この記事は obniz Advent Calendar 2020 4日目の記事です。
obniz に Grove Water Level Sensor をつなげるメモ をベースにNode-RED から obniz ノードをつかって Grove Water Level Sensor をつなげてみます。
裏テーマとしては、 I2C に関する複雑なコードでも obniz ノードで行けるのかどうかを試してみたいのです!
元のソース
もともと obniz クラウドで動いていたソースはこちらです。
var obniz = new Obniz("Obniz_ID");
obniz.onconnect = async function() {
obniz.display.clear();
obniz.display.print("Hello World");
// 黒:0番
obniz.io0.output(false)
// 赤:1番
obniz.io1.output(true)
obniz.i2c0.onerror = function(err) {
console.error(err)
}
// 接続が整うまでに待ち時間が必要
await obniz.wait(100);
// I2C接続
obniz.i2c0.start({mode:"master", sda:2, scl:3, clock:400000 });
// https://wiki.seeedstudio.com/Grove-Water-Level-Sensor/#software を参考
let low_data = [];
let high_data = [];
let THRESHOLD = 100;
let ATTINY1_HIGH_ADDR = 0x78;
let ATTINY2_LOW_ADDR = 0x77;
while(true){
let touch_val = 0;
let trig_section = 0;
low_count = 0;
high_count = 0;
high_data = await obniz.i2c0.readWait(ATTINY1_HIGH_ADDR, 12);
low_data = await obniz.i2c0.readWait(ATTINY2_LOW_ADDR, 8);
let i;
for (i = 0 ; i < 8; i++) {
if (low_data[i] > THRESHOLD) {
touch_val |= 1 << i;
}
}
for (i = 0 ; i < 12; i++) {
if (high_data[i] > THRESHOLD) {
touch_val |= 1 << (8 + i);
}
}
while (touch_val & 0x01)
{
trig_section++;
touch_val >>= 1;
}
let water_level_mm = trig_section * 5;
// console.log("c = " + water_level_mm + "%");
// HTML に反映
$("#waterlevel").html("[water level] " + water_level_mm + " %");
// ディスプレイに反映
obniz.display.clear();
obniz.display.font(null,16)
obniz.display.print("[water level]");
obniz.display.font(null,30)
obniz.display.print(water_level_mm + " mm");
await obniz.wait(1000);
}
};
Node-RED のフローに書き換えてみる
自分は Node-RED を手元のPCのローカルで立ち上げています。
書き換えてみて、ちゃんと動きました。
このようなフローになりました。
[{"id":"782b6ae2.9d4a24","type":"inject","z":"87a7ba2e.1c34b8","name":"起動時","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"","payloadType":"date","x":400,"y":560,"wires":[["c704cf1f.e04c7"]]},{"id":"c704cf1f.e04c7","type":"obniz-function","z":"87a7ba2e.1c34b8","obniz":"e1f5c3f5.3c7f5","name":"obniz 準備","code":"obniz.display.clear();\r\nobniz.display.print(\"Hello World\");\r\n\r\n// 黒:0番\r\nobniz.io0.output(false)\r\n// 赤:1番\r\nobniz.io1.output(true)\r\n\r\nobniz.i2c0.onerror = function(err) {\r\nconsole.error(err)\r\n}\r\n\r\n// 接続が整うまでに待ち時間が必要\r\nawait obniz.wait(100);\r\n\r\n// I2C接続\r\nobniz.i2c0.start({mode:\"master\", sda:2, scl:3, clock:400000 });","x":610,"y":560,"wires":[[]]},{"id":"af578fce.de206","type":"obniz-repeat","z":"87a7ba2e.1c34b8","obniz":"e1f5c3f5.3c7f5","name":"","interval":"1000","code":"// https://wiki.seeedstudio.com/Grove-Water-Level-Sensor/#software を参考\r\n\r\nlet low_data = [];\r\nlet high_data = [];\r\n\r\nlet THRESHOLD = 100;\r\nlet ATTINY1_HIGH_ADDR = 0x78;\r\nlet ATTINY2_LOW_ADDR = 0x77;\r\n\r\n// while(true){\r\n\r\n let touch_val = 0;\r\n let trig_section = 0;\r\n low_count = 0;\r\n high_count = 0;\r\n\r\n high_data = await obniz.i2c0.readWait(ATTINY1_HIGH_ADDR, 12);\r\n low_data = await obniz.i2c0.readWait(ATTINY2_LOW_ADDR, 8);\r\n\r\n let i;\r\n\r\n for (i = 0 ; i < 8; i++) {\r\n if (low_data[i] > THRESHOLD) {\r\n touch_val |= 1 << i;\r\n }\r\n }\r\n for (i = 0 ; i < 12; i++) {\r\n if (high_data[i] > THRESHOLD) {\r\n touch_val |= 1 << (8 + i);\r\n }\r\n }\r\n \r\n while (touch_val & 0x01)\r\n {\r\n trig_section++;\r\n touch_val >>= 1;\r\n }\r\n\r\n let water_level_mm = trig_section * 5;\r\n \r\n // ディスプレイに反映\r\n obniz.display.clear();\r\n // obniz.display.font(null,16)\r\n obniz.display.print(\"[water level]\\n\");\r\n // obniz.display.font(null,30)\r\n obniz.display.print(water_level_mm + \" mm\");\r\n\r\n // await obniz.wait(1000); // この 1000 ミリ秒を interval(ms) に反映\r\n//}\r\n\r\n// 後に続くノードに水のレベル water_level_mm を伝える\r\nmsg.payload = water_level_mm;\r\n\r\nreturn msg;\r\n","x":610,"y":660,"wires":[["95074474.3c46b8"]]},{"id":"95074474.3c46b8","type":"debug","z":"87a7ba2e.1c34b8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":810,"y":660,"wires":[]},{"id":"e1f5c3f5.3c7f5","type":"obniz","z":"","obnizId":"0000-0000","deviceType":"obnizboard","name":"","accessToken":"","code":""}]
インポートして使えるフローJSONデータなこちらです。
obniz のIDは 0000-0000
にしています。
自分の obniz のIDと device type を指定して使いましょう。
起動時の動作
起動時は inject ノードで1秒後に obniz ノードが実行されます。
obniz ノードの中身です。
obniz.display.clear();
obniz.display.print("Hello World");
// 黒:0番
obniz.io0.output(false)
// 赤:1番
obniz.io1.output(true)
obniz.i2c0.onerror = function(err) {
console.error(err)
}
// 接続が整うまでに待ち時間が必要
await obniz.wait(100);
// I2C接続
obniz.i2c0.start({mode:"master", sda:2, scl:3, clock:400000 });
元のソースの while ループを回す前の処理を移植しています。
ループ処理 obniz repeat
while ループの中身は obniz repeat のほうに任せます。
obniz ノードの中身です。
// https://wiki.seeedstudio.com/Grove-Water-Level-Sensor/#software を参考
let low_data = [];
let high_data = [];
let THRESHOLD = 100;
let ATTINY1_HIGH_ADDR = 0x78;
let ATTINY2_LOW_ADDR = 0x77;
// while(true){
let touch_val = 0;
let trig_section = 0;
low_count = 0;
high_count = 0;
high_data = await obniz.i2c0.readWait(ATTINY1_HIGH_ADDR, 12);
low_data = await obniz.i2c0.readWait(ATTINY2_LOW_ADDR, 8);
let i;
for (i = 0 ; i < 8; i++) {
if (low_data[i] > THRESHOLD) {
touch_val |= 1 << i;
}
}
for (i = 0 ; i < 12; i++) {
if (high_data[i] > THRESHOLD) {
touch_val |= 1 << (8 + i);
}
}
while (touch_val & 0x01)
{
trig_section++;
touch_val >>= 1;
}
let water_level_mm = trig_section * 5;
// ディスプレイに反映
obniz.display.clear();
// obniz.display.font(null,16)
obniz.display.print("[water level]\n");
// obniz.display.font(null,30)
obniz.display.print(water_level_mm + " mm");
// await obniz.wait(1000); // この 1000 ミリ秒を interval(ms) に反映
//}
// 後に続くノードに水のレベル water_level_mm を伝える
msg.payload = water_level_mm;
return msg;
元のソースを見比べてみると、 while ループと待ち処理をしている await obniz.wait(1000);
をコメントアウトして、この 1000 ミリ秒を interval(ms) に反映しています。
return msg;
で後に続くノードに水のレベル water_level_mm を伝えるので、debugノードに伝わります。
スピーカーで水位に合わせて音を変える
Node-RED から obniz ノード使って Grove Water Level Sensor 動かせた。スピーカーも連動させて指で触れて動かすと音が変わります!#obniz #nodered #noderedjp #seeed pic.twitter.com/dzlaZ6uqiB
— Tanaka Seigo (@1ft_seabass) December 3, 2020
このようにスピーカーのドレミファソラシドの音と、水位を連動させてみました。
このようなフローになります。
[{"id":"782b6ae2.9d4a24","type":"inject","z":"87a7ba2e.1c34b8","name":"起動時","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"","payloadType":"date","x":400,"y":560,"wires":[["c704cf1f.e04c7"]]},{"id":"c704cf1f.e04c7","type":"obniz-function","z":"87a7ba2e.1c34b8","obniz":"e1f5c3f5.3c7f5","name":"obniz 準備","code":"obniz.display.clear();\r\nobniz.display.print(\"Hello Water!\");\r\n\r\n// 黒:0番\r\nobniz.io0.output(false)\r\n// 赤:1番\r\nobniz.io1.output(true)\r\n\r\nobniz.i2c0.onerror = function(err) {\r\n console.error(err)\r\n}\r\n\r\n// I2C接続\r\nobniz.i2c0.start({mode:\"master\", sda:2, scl:3, clock:400000 });\r\n\r\n// スピーカー\r\nobnizParts.speaker = obniz.wired(\"Speaker\", {signal:9, gnd:11});","x":610,"y":560,"wires":[[]]},{"id":"af578fce.de206","type":"obniz-repeat","z":"87a7ba2e.1c34b8","obniz":"e1f5c3f5.3c7f5","name":"","interval":"1000","code":"// https://wiki.seeedstudio.com/Grove-Water-Level-Sensor/#software を参考\r\n\r\nlet low_data = [];\r\nlet high_data = [];\r\n\r\nlet THRESHOLD = 100;\r\nlet ATTINY1_HIGH_ADDR = 0x78;\r\nlet ATTINY2_LOW_ADDR = 0x77;\r\n\r\n// while(true){\r\n\r\n let touch_val = 0;\r\n let trig_section = 0;\r\n low_count = 0;\r\n high_count = 0;\r\n\r\n high_data = await obniz.i2c0.readWait(ATTINY1_HIGH_ADDR, 12);\r\n low_data = await obniz.i2c0.readWait(ATTINY2_LOW_ADDR, 8);\r\n\r\n let i;\r\n\r\n for (i = 0 ; i < 8; i++) {\r\n if (low_data[i] > THRESHOLD) {\r\n touch_val |= 1 << i;\r\n }\r\n }\r\n for (i = 0 ; i < 12; i++) {\r\n if (high_data[i] > THRESHOLD) {\r\n touch_val |= 1 << (8 + i);\r\n }\r\n }\r\n \r\n while (touch_val & 0x01)\r\n {\r\n trig_section++;\r\n touch_val >>= 1;\r\n }\r\n\r\n let water_level_mm = trig_section * 5;\r\n \r\n // ディスプレイに反映\r\n obniz.display.clear();\r\n // obniz.display.font(null,16)\r\n obniz.display.print(\"[water level]\\n\");\r\n // obniz.display.font(null,30)\r\n obniz.display.print(water_level_mm + \" mm\");\r\n\r\n // await obniz.wait(1000); // この 1000 ミリ秒を interval(ms) に反映\r\n//}\r\n\r\n// 後に続くノードに水のレベル water_level_mm を伝える\r\nmsg.payload = water_level_mm;\r\n\r\nreturn msg;\r\n","x":610,"y":660,"wires":[["b5d68dcd.60277"]]},{"id":"95074474.3c46b8","type":"debug","z":"87a7ba2e.1c34b8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1150,"y":660,"wires":[]},{"id":"b5d68dcd.60277","type":"obniz-function","z":"87a7ba2e.1c34b8","obniz":"e1f5c3f5.3c7f5","name":"水のレベルによって音を変える","code":"/*\r\nド 523.23\r\nレ 587.34\r\nミ 659.25\r\nファ 698.45\r\nソ 783.98\r\nラ 879.99\r\nシ 987.75\r\nド 1046.5\r\n*/\r\n\r\n// 水のレベルによって音を変える\r\nif(msg.payload >= 40){\r\n // ド\r\n obnizParts.speaker.play(1046);\r\n} else if(msg.payload >= 35){\r\n // シ\r\n obnizParts.speaker.play(987);\r\n} else if(msg.payload >= 30){\r\n // ラ\r\n obnizParts.speaker.play(879);\r\n} else if(msg.payload >= 25){\r\n // ソ\r\n obnizParts.speaker.play(783);\r\n} else if(msg.payload >= 20){\r\n // ファ\r\n obnizParts.speaker.play(698);\r\n} else if(msg.payload >= 15){\r\n // ミ\r\n obnizParts.speaker.play(659);\r\n} else if(msg.payload >= 10){\r\n // レ\r\n obnizParts.speaker.play(587);\r\n} else if(msg.payload >= 5){\r\n // ド\r\n obnizParts.speaker.play(523);\r\n} else {\r\n obnizParts.speaker.stop();\r\n}","x":890,"y":660,"wires":[["95074474.3c46b8"]]},{"id":"e1f5c3f5.3c7f5","type":"obniz","z":"","obnizId":"0000-0000","deviceType":"obnizboard","name":"","accessToken":"","code":""}]
インポートして使えるフローJSONデータなこちらです。先ほどのフローと同様に使うときは、自分の obniz のIDと device type を指定して使いましょう。
起動時の動作
こちらの obniz ノードではスピーカーの呼び出しだけ加えています。
obniz.display.clear();
obniz.display.print("Hello Water!");
// 黒:0番
obniz.io0.output(false)
// 赤:1番
obniz.io1.output(true)
obniz.i2c0.onerror = function(err) {
console.error(err)
}
// I2C接続
obniz.i2c0.start({mode:"master", sda:2, scl:3, clock:400000 });
// スピーカー
obnizParts.speaker = obniz.wired("Speaker", {signal:9, gnd:11});
ループ処理 obniz repeat
obniz repeat ノードのほうは内容に変更はなく、「水のレベルによって音を変える」となっている obniz ノードで、スピーカーのドレミファソラシドの音を水位と合わせるプログラムを書いています。
/*
ド 523.23
レ 587.34
ミ 659.25
ファ 698.45
ソ 783.98
ラ 879.99
シ 987.75
ド 1046.5
*/
// 水のレベルによって音を変える
if(msg.payload >= 40){
// ド
obnizParts.speaker.play(1046);
} else if(msg.payload >= 35){
// シ
obnizParts.speaker.play(987);
} else if(msg.payload >= 30){
// ラ
obnizParts.speaker.play(879);
} else if(msg.payload >= 25){
// ソ
obnizParts.speaker.play(783);
} else if(msg.payload >= 20){
// ファ
obnizParts.speaker.play(698);
} else if(msg.payload >= 15){
// ミ
obnizParts.speaker.play(659);
} else if(msg.payload >= 10){
// レ
obnizParts.speaker.play(587);
} else if(msg.payload >= 5){
// ド
obnizParts.speaker.play(523);
} else {
obnizParts.speaker.stop();
}
もっと良く書ける気もしますが、まずは動くの重視でとってもストレートに書いています。
Node-RED から obniz ノードをつかって Grove Water Level Sensor とスピーカーの連動!実際に水入れて試してみた。ドレミファソラシド~♪って奏でてくれる!#obniz #nodered #noderedjp #seeed pic.twitter.com/ET7BG5sAP8
— Tanaka Seigo (@1ft_seabass) December 3, 2020
明日は
明日の obniz Advent Calendar 2020 の担当は @tmisuoka0423 さんの「冬 x 睡眠 x obniz」です!
楽しみです!