LoginSignup
10
3

More than 3 years have passed since last update.

Node-RED から obniz ノードをつかって Grove Water Level Sensor をつなげてみる

Last updated at Posted at 2020-12-03

この記事は 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のローカルで立ち上げています。

2020-12-03_20h46_35.jpg (280.4 kB)

書き換えてみて、ちゃんと動きました。

image.png (17.5 kB)

このようなフローになりました。

[{"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データなこちらです。

image.png (14.0 kB)

obniz のIDは 0000-0000 にしています。

image.png (21.5 kB)

自分の obniz のIDと device type を指定して使いましょう。

起動時の動作

image.png (7.8 kB)

起動時は inject ノードで1秒後に obniz ノードが実行されます。

image.png (35.9 kB)

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

image.png (5.7 kB)

while ループの中身は obniz repeat のほうに任せます。

image.png (39.9 kB)

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) に反映しています。

image.png (12.2 kB)

return msg; で後に続くノードに水のレベル water_level_mm を伝えるので、debugノードに伝わります。

スピーカーで水位に合わせて音を変える

このようにスピーカーのドレミファソラシドの音と、水位を連動させてみました。

image.png (27.2 kB)

このようなフローになります。

[{"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 を指定して使いましょう。

起動時の動作

image.png (7.8 kB)

こちらの 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

image.png (9.8 kB)

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();
}

もっと良く書ける気もしますが、まずは動くの重視でとってもストレートに書いています。

明日は

明日の obniz Advent Calendar 2020 の担当は @tmisuoka0423 さんの「冬 x 睡眠 x obniz」です!

楽しみです!

10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3