IoT何をいまさら(32) M5Stackへ, Nucleoから移植

ロガーが必要だな、などと思い至りました。そのお役目はM5Stackへお願いしようと考えているのであります。小なりとは言え、ロガーに必須な記憶装置のスロットあり、自前の表示パネルあり、無線も使えて、なんとなれば自前のバッテリーも使えると至れり尽くせり。ハード的には、センサをI2Cで接続してやればOK。後はソフトを書くだけ。しかしね、ドキュメントが無い分けじゃない(結構いろいろある)割に、私が気になることは見つからなかったりする。一歩一歩確かめていくしかありますまい。

ま、最初に随分迷ったのが、I2Cの信号電圧(21番と22番端子に出ている)でした。M5Stackから取り出せる信号の端子名などはアチコチに書かれているのですが、AC/DC仕様みたいなものが見当たりません。それではということで、BasicというCoreボードの回路図があったので見てみたですが、ESP32のチップの端子がM5Stackのコネクタに直接出ているように見えます。ESP32は3.3V電源のチップなのでそうであれば、電圧3.3Vでしょう。なお、M5StackのWebサイトからたどれるESP32のデータシートは中文版なので、別途ダウンロードしてあった英文版を参照しました。手元にあるのはBasicというCoreボードにモーションセンサを搭載したGrayというバージョン。センサはI2C接続であります。オンボードで搭載可能ないくつかのセンサのデータシートを当たってみれば、やはり電源電圧、IO電圧とも3.3V系でした。当然、I2Cの信号も3.3V電圧に対して定義されています。ところが、M5Stackのドキュメントのページには、それらセンサに接続するSDA, SCLの隣にGroveコネクタの5Vという電圧がビシビシ書いてある。

え、3.3Vじゃないの?それともGroveコネクタのところにレベル変換入っているの?

レベル変換らしいものはBasicの回路図には見当たらず、どきどきするばかり。仕方がないので、現物に問いかけました。内部にI2Cのチップが搭載されているならば、I2Cの信号はきっとプルアップされている筈。そうであれば、SCLやSDAの電圧を測ってやれば、3.3Vなのか、5Vなのか分かるでしょう。結果

3.3Vが見えました。

これは個人的な「経験」で、それを保証するものではありません。I2CバスにSCANをかけた波形も観察してみましたが、3.3Vと0Vの間で信号が動いていました。Webサイトの文書を読めば読むほど、雰囲気的には5Vのような気がしてくるのですが自分が見た現物を信じましょう。ええい、ままよ、ということで、3.3Vで接続して動かしてしまいました。その上、多分内部にプルアップがある筈なのに、外にも10kと大きめではありますがプルアップつけてあったのをそのままつなげてしまいました(今は外しました)。M5Stackに最初につなげてみたのは、外付けのI2C接続の白黒LCDパネル(AQM1602)です。3.3Vでも5Vでも動作できるもの。I2Cが正常に動けば表示が出るので分かり易い。こんな感じ。

どきどきしながらも Hello World! を見て一安心。そして、M5Stack本体のLCDパネルには、I2Cバスをスキャンした結果を表示させてみました。文字があまりに小さいので下に拡大写真を載せておきます。本当は、0x00から0x7Fまでスキャン結果を表示させるつもりが、一行すくない0x6Fまでになってました(バグだね)。行方向が、上の4ビットで、列方向が下の4ビットです。SCANに応答があれば1, 応答がなければ0です。4行目、0x3Eのところにある1が、外付けLCDパネル。0x68のところにある1が、InvenSenseの6軸モーションセンサだと思われます。

私の個人的な見解としては、3.3Vということで「とりあえず」決定しました。このAQM1602の接続用のコードは、

Nucleo用にMbed OS上で自作したファイル

からコピペでArduino IDEに持ってきて改造したのです。最初、2点ほど失敗し表示が出ませんでした。反省を込めて列挙すると

  1. Mbed OS上のI2Cインタフェース関数は、スレーブアドレスの最下位ビット(R/Wの方向を示す)まで「アドレス」として含めるスタイルだが、Arduino系のM5Stackは、最下位ビットを取り除いて上位7ビットだけ使うスタイル(AQM1602の秋月製のデータシートにしっかり書いてあったですが、そこに戻らなかった私が悪い)なことに最初気付かなかった。(I2Cのスキャン結果みて気付いた)
  2. MbedOS上のI2Cインタフェース関数は、成功すると0、失敗すると1を返すスタイルだったのですが、M5Stack用の関数は戻り値bool型で成功するとtrueを返す(数字で見れば1)のでした。この真偽値が逆転していたことにしばらく気付かずデバッグしてました。

環境の違うところにコード移植するときの典型的?な落とし穴を二つも踏んづけてしまいました。

ついでに蛇足ながら秋月のAQM1602のデータシートの矛盾点もひとつ見つけましたぜ。初期化例では、最初40mS待ち、途中はコマンドとコマンドの間で26.3μS待ちを続けるのですが、1箇所だけ電源をいじるコマンドの直後で、200mS待ちが挟まるのであります。ところが、別に掲載されているArduinoとの接続例のコードを見ると、そこのステップで200mSも待ってない。(それほど待たなくても動くみたいだけれども。。。細かい話か)

なお、Arduino系では、I2C通信するのにWire.hというヘッダを持ってきて通信するのが普通のようですが、M5StackのAPIを読んでいると

M5Stack.h

をインクルードして、M5.beginの第四引数をtrue(これがI2Cのインタフェースイネーブル。デフォルトだとディセーブルらしい。)にしておけば

M5.I2C.チョメチョメ

というスタイルでM5Stack式のI2Cのインタフェース関数を呼び出せました。元からあるArduinoのインタフェース関数の上に一枚被せた感じで、こちらの方が簡単に書けるのでM5Stack式を採用。ま、動けば何でもよいか。

IoT何をいまさら(31) Raspberry Pi Zero Wがやってきた へ戻る

IoT何をいまさら(33) Raspberry Pi Camera へ進む