2018の17日目の記事です。ときどき勉強会で、司会者と参加者でじゃんけんして勝ち残った人にプレゼントが贈呈されることがあります。このじゃんけんをobnizでやるプログラムを試作してみました。作成のきっかけスイッチサイエンスの動画「micro:bitファンミーティングの話」を見ていたら、5分3秒ぐらいからmicro:bitを43台使って抽選を行った話が出てきました。obnizを使って似たようなことができないかなと思いました。司会者と参加者が各1台ずつobnizを持ち、ダイアルスイッチを使ってじゃんけんの手(?)「グー」「チョキ」「パー」を入力します。司会者に勝った人だけが、次のじゃんけんに参加できます。試作品なので最低限の情報しか画面に表示していません。起動直後は、各obnizに親か子の情報が表示されています。じゃんけんの手を全員が入力し終わると、子供には勝敗が表示されます。勝敗決定後のブラウザ画面はこうなります。2回戦に進んだ画面はこうなります。2は1回戦で負けたため2回戦には参加できません。プログラムを書いて気づいたこと複数台のobnizの制御obnizサイトのドキュメントにある「obniz2つを連携」を真似して作り始めました。ここで解説されている方法は、obniz.onconnectに渡す関数を入れ子にする方法です。obnizAのonconnect関数内にobnizBのインスタンス作成とonconnect関数が記載されています。here}}この方法には次の点でじゃんけんプログラムには適しません。参加人数が多くなると入れ子がどんどん深くなる。onlineにならないobnizがあると、それより先のobnizは接続できない。そこで、入れ子にするのはやめて、単純にループで回すようにしました。createChildOnCloseFunction(i,child)children.push(child);}子供のonconnect関数内で、自分が何番目の子供であるかのインデックス番号と自分自身を表すobnizオブジェクトを使うので、それらが参照できるようにしなければなりません。そこで「関数を返す関数」を用意しました。index);}}同様に、obniz.switch.onchangeも「関数を返す関数」を使って設定しています。handName[state];updateBrowser();}}}}こういったコーディングをすることで、手持ちの4台のobnizでプログラムが動作するようになりました。考えてみると数十台のobnizとの接続は、OSやブラウザの限界を超えてしまう可能性があることに気づきました。)関数が一斉に発火します。それらの関数が1個ずつ処理されるのか並行に処理されるのかは、obnizのJavaScriptライブラリの実装方法や、ブラウザのJavaScriptエンジンの実行方法に依存します。obnizのドキュメントを見た限りではその辺の解説は見つかりませんでした。今回は「逐次実行だ!」と信じて排他制御は考えませんでした。設計せずにいきなりコードを書き始めたのでかなり手戻りがありました。最初は単純に次のような状態遷移を考えていました。プログラムを書いて動かしてみたら、次のような問題があることに気がつきました。3の遷移はこんなに単純ではない手を入力しない人がいるかもしれない何らかの原因でオフラインになったobnizからは手が入力されないこの問題は、全員の入力を待つことなく強制的に「勝敗を判定」に遷移する方法を用意することで回避しました。obnizのオフライン判定は一定のタイムアウト時間後になるので即時性がないため自動判定にはしませんでした。複数台のobnizを制御するプログラムの難しさがここに潜んでいます。4の遷移はこんなに単純ではない。勝った人がいない場合は、同じ参加者でじゃんけんをやり直す賞品が3つあって勝者が2人の場合、勝った2人を除いた人達でじゃんけんを再開したい賞品をゲットした人は除いて次のじゃんけんをはじめたいその他いろいろこの問題の1つ目は単純な考慮漏れだったのですぐ直せましたが、2つ目以降はやりたいことが多岐にわたるので対応を諦めました。IDを入力する必要があるが、現状ではプログラムに直書きしているため、実用には程遠いという問題があります。複数のobnizを使う場合に注意すべき点もまだまだありそうです。修行を続けたいと思います。WROOM-32につないでみた水中ポンプ4台のOn/Offを制御したくてWROOMで動作しそうなリレーを探していたら、amazonでこんなのが見つかりました。500円というお買い得商品です。4連リレーamazonで安い電子部品を買うと、中国からの発送になって1〜2週間かかることが多いのですが、こちらの商品はamazon倉庫からの発送で翌日に届きました。電源と信号線を接続する端子と、リレー駆動電源を選択するジャンパー端子があります。ジャンパーを取り付けている状態では、VCCに供給した電源をリレー駆動に使います。ジャンパーを取り外してJD-VCCピンに別の電源を接続するとVCCとは違う電源でリレーを駆動できるようになります。WROOMのGPIOは3.3VなのでVCCには3.3Vを供給します。リレーは5V仕様なので3.3Vだと動作が不安定になります。1つのリレーに対して3つ用意されています。中央と左側の端子に接続すると、リレーが動作していないときは回路が切断状態になり、リレーが動作しているときは回路が接続状態になります。中央と右側の端子に接続すると、逆の動作になります。信号線がHIGHのときリレーは動作せず、LOWのときリレーが動作します。sleepさせたときGPIOの出力がLOWになってリレーが動いてしまうのではないかという心配があります。sleep時にはリレーはOffのままでした。実験に使用した開発ボードは下記のものです。SHT31温度湿度計をM5Stackにつないでみました。接続はGroveケーブルを使ってM5StackのGroveソケットに挿しました。WikiのSHT31のページにあるライブラリとサンプルコードを使いました。追加12行目のボーレートを115200に変更29行目にLCDへの表示を追加だけです。LCD表示はスクロールのコードを書いていないので、画面最後の行まで到達したらそれ以降画面更新はありません。Groveソケットを使わないで、GPIO端子に接続するのも試してみました。GPIO端子に接続こちらも問題なく動作しました。v4.0というモジュールです。スイッチサイエンスさんの単体販売ページに掲載されているものと同じ製品だと思われますが、スイッチサイエンスの製品写真だとv2.0になっているので、バージョンが異なる可能性があります。RGBバックライト液晶モジュール(裏)RGBバックライト液晶モジュールの動作この液晶は、文字の色は黒しかありませんが背景の色を変えられます。表示文字数は16文字x2行です。このモジュールとIoTボード間の通信はI2Cを使います。このモジュールは5Vを必要とします。BTのGroveソケットに接続した場合、3.3Vしか供給されないので正常に動作しません。BTにはGPIOのピンソケットが搭載されていて、5V電源のピンも出ているので、ピンソケットに接続すればこのモジュールを使えます。接続には一方がバラになっているGroveケーブルを使います。スイッチサイエンスさんではこんなGroveケーブルが販売されています。BTのGPIOソケットに接続する場合は、オス-オスのジャンパーケーブルを継ぎ足して使うとこなります。Studioのサイトには、オス端子が付いた先バラケーブルが売られていますが、国内では販売店を見つけられませんでした。ピンが付いている先バラGroveケーブル私は短いケーブルが欲しかったので、通常のGroveケーブルを2分割してピン端子を取り付けました。雑な工作なので写真を載せるのが恥ずかしいです。R2の場合、先バラケーブルは下図のように接続します。BT無印の場合は、5Vのソケット位置が異なるので注意してください。Wikiページに掲載されているライブラリとサンプルプログラムを動かしてみます。Library」というリンクをクリックしてプログラム一式のzipファイルをダウンロードします。[.ZIP形式のライブラリをインストール...]を選んでインストールします。[HelloWorld]を選択してサンプルプログラム「HelloWrold.ino」を開きます。HelloWorldを開くHelloWorld.inoの中身はこうなっています。reset:lcd.print(millis()/1000);delay(100);}このままのプログラムで動作するはずですが、なぜか動作しませんでした。追加したら、なぜか動きました。理由はまだ解明できていません。サーボモータサーボモータモジュール私が購入したGROVEスターターキットV3に入っていたサーボモータモジュールには、型番や定格が記載されていませんでした。形状や付属品はスイッチサイエンスさんの単体販売ページに掲載されているものと同じに見えます。Groveサーボモータモジュールサーボモータの動作サーボモーターはPWMで指定された値に応じて軸の角度が変わる特殊なモーターです。通常180度程度の可動域があります。PWMについては別記事に解説を記載しているので参照してください。サーボモータの可動域の例サーボモータのPWMパラメータは、一般的には、周期は10〜20msでパルス幅は0.5ms〜2.5msぐらいのようですが、機種による差が大きく、また個体による差もあるようです。筆者が持っているサーボモーターでは、次のようなパラメーターで動作するようです。BTに装備されているスイッチを押す度に、0°、90°、180°の位置へ移動を繰り返すプログラムです。Motor//Groveケーブルを接続するGroveコネクターを1つ選んで#define文に記載してください。}12345678910111213141516171819202122232425262728293031323334//Groveケーブルを接続するGroveコネクターを1つ選んで#define文に記載してください。pulse[3]で、0°、90°、180°のパルス幅を覚える配列を定義しています。)でGPIOピンとチャンネルを結びつけます。)で0°のパルス幅をサーボモーターに設定しています。)}ルーチン内では、スイッチが押される度に90°、180°、0°のパルス幅を順に設定しています。IDEを使ってESP-WROOM-32(ESP32)のPWMを制御するプログラムを書く方法を記載します。Modulation)は、次の図のような周期的なパルス信号を生成し、1になっている時間と0になっている時間の比率(デューティー比)を変えることで送信する電力を変化させます。PWMの仕組みLEDの点灯にPWMを使うと、1になっている時間が短いと人間の目にはLEDが暗くなったように見え、時間が長いと明るく見えます。PWMを使う場合、2つの値を指定します。IDEでプログラムする場合、LEDCライブラリを使ってPWMを制御します。LEDCでは上記2つの値を指定するために3つの値を使います。パルスの周期(Hzで指定)1周期に含まれる目盛の数(ビット数で指定。10bitなら1024目盛。)1になっている時間の長さ(上記の目盛で指定。10bit目盛の場合は0〜1023の値。)また、LEDCではどのチャンネルを使うかを指定します。チャンネルは0〜15の16個あります。LEDCライブラリのパラメータLEDの明るさを制御するプログラム例LEDをだんだん明るくし、輝度が最大になったら、今度はだんだん暗くする動作を繰り返すプログラムです。)関数で使用するピンを指定しています。)関数でLEDの明るさを変更しています。v1.4というモジュールです。スイッチサイエンスさんの単体販売ページに掲載されているものとは、基板部分は同じですが、付属するLEDの個数に違いがあります。スイッチサイエンスさんで販売されているものはLEDが1個しかついていませんが、GROVEスターターキットV3には赤、青、緑の3種類のLEDが付いています。LEDモジュールとLED3種GroveLEDモジュール基板の右下にあるのがLEDを挿すソケットです。右上にあるの黄色いプラスチックが付いた箱はLEDの明るさを調整する可変抵抗です。黄色い部分をドライバで回して明るさを調整します。LEDモジュール(裏)LEDモジュールの動作1(HIGH)を送るとLEDが点灯し、0(LOW)を送るとLEDが消灯します。LEDの明るさは基板についている可変抵抗で調整できるようになっています。LEDが暗い時や点灯しない時は可変抵抗を回してみてください。LEDをソケットに挿す際に、LEDの極性を合わせる必要があります。円の一部分が欠けて直線になっている方がマイナスです。モジュールのLEDソケット部にも円が欠けている絵が描いてあるので、それに合わせてLEDを差し込みます。(注:A0を使うときはA1を記載します。(注:A0を使うときはA1を記載します。LOW);Serial.println(LOW);delay(1000);}1秒ごとに点灯、消灯を繰り返すプログラムです。使ってHIGHとLOWを交互に設定しています。Modulation)を使って明るさを変化させます。BTでPWMを使う場合は、LEDCライブラリを使います。PWMとLEDCライブラリについては別記事に解説を書いていますので参照ください。PWM//GROVEケーブルを接続するGROVEコネクターを1つ選んで#define文に記載してください。}1234567891011121314151617181920212223242526272829303132333435//GROVEケーブルを接続するGROVEコネクターを1つ選んで#define文に記載してください。(uint32_t)i);delay(50);}}LEDをだんだん明るくし、輝度が最大になったら、今度はだんだん暗くする動作を繰り返すプログラムです。)関数で使用するピンを指定しています。)関数でLEDの明るさを変更しています。V1.2というモジュールです。スイッチサイエンスさんの単体販売ページに掲載されているものと同じです。温度センサモジュールはアナログモジュールです。温度の高い低いに応じて出力される電圧が変化します。analogRead(PIN)関数を使うと電圧の高さが0〜4095の数値として得られます。この数値だと何度かわからないので、数値を温度に変換するプログラムが必要になります。変換方法は、Groveモジュールの販売サイトや、Groveモジュールで使用している温度センサ部品のサイトに掲載されていますが、かなり難解です。Groveモジュールを使うだけなら、販売店サイトで公開されているプログラムを利用するのが手軽です。Studio社の商品紹介ページにあるArduino用のプログラムを使ってみます。無印の場合はA2に変更します。R2の場合はA1に変更してください。不要なので削除します。INPUT);を追加します。aはanalogRead(pinTempSensor)で取得した値です。)関数は0〜4095の値を返すので、1023.0となっている部分を4095.0に変更します。この変更を加えたプログラムは次のようになります。");Serial.println(temperature);delay(100);}このプログラムを動作させると、シリアルモニタに次のような結果が出力されます。温度計測定値のシリアルモニタ出力例なんとなくそれらしい温度が表示されていますが、Arduinoにこの温度センサーを接続した時より1〜2度低いようです。Arduinoのプログラムは5Vでの動作を前提としているので、3.3Vだと少し定数の変更が必要なのかもしれません。スイッチサイエンスさんの単体販売ページの中央に写っているものと同じです。同じページの右上の画像のモジュールはボリュームパーツが若干異なるようです。Groveボリュームモジュール(表)Groveボリュームモジュール(裏)ボリュームモジュールの動作ボリュームモジュールはアナログモジュールです。3.3Vの電源に接続している場合、ノブを回すと0Vから3.3Vまで電圧が連続的に変化します。ノブを時計回りにいっぱい回すと0Vで反時計回りにいっぱい回すと3.3Vが出力されます。なんとなく逆のイメージがありますが、このような動作になっています。プログラムボリュームモジュールから読み込んだ値をシリアルコンソールに表示するプログラムです。値の読み込みはanalogRead(PIN)関数で行なっています。//Groveケーブルを接続するGroveソケットを1つ選んで#define文に記載してください。(注:A0を使うときはA1を記載します。<Nefry.h>//Groveケーブルを接続するGroveソケットを1つ選んで#define文に記載してください。(注:A0を使うときはA1を記載します。{Serial.println(analogRead(PIN));delay(300);}プログラムを動作させノブを左右に回すとこのように0〜4095の数字が表示されます。右にいっぱい回すと0、左にいっぱい回すと4095が表示されます。コンソール出力例Groveソケットごとの動作状況4箇所のGroveソケットにボリュームモジュールを順に接続して、動作をテストしてみました。無印の場合はこうなります。起動してから接続すると、常に4095が表示される。常に4095が表示される。R2の場合はこうなります。