Arduino IDEでM5stackを扱う

今更のメモですが、

簡易的にM5stackを扱うのにはArduino IDEが便利です、その理由はI2Cデバイスのライブラリが整っているので、わざわざライブラリを組み込む手間が省けると言うのが一番大きいし、VScodeはビルドの時にパスを通すように設定とか必要になるから。

あと、シリアルポートの出力結果を簡単にグラフ表示できると言うのもありますね。

環境のセットアップ手順は既に作成されている資料で完璧です。

https://qiita.com/hmmrjn/items/2b2da09eecffcbdbad85

USBドライバーは最近新しいバージョンになっていますが。

 

admin

レーザー距離測定でLCDは光学的に特別の性質なのか

色々なターゲットで距離測定しつつ反射光の検出強度を測定してみた。

環境はArduino IDEでも動かせるし、以前のようにM5stackのビルドも遅くないから、使ってみた。

Arduino IDEで使うライブラリはこれ、

距離値はビジュアル化できる、

APIで反射光のセンサー値(検出強度)を読み出せる、強さが1,000以下は不安定と判断してfailにされている

色々なターゲットでの検出強度、特徴的なのはターゲットがLCD面だと値が大きく変動することで、おそらく偏向特性が関連しているように思う

結構検出精度は高いように思います、検出エリアは公称18度ぐらいですが。可視光と赤外線では見え方が違うだろうから、人の目には明らかでもレーザー光では反射率が低下するようなターゲットもあるでしょう。

P.S. 2022/9/26

鏡でやってみても同じ特性を持つから、単にLCDや鏡は反射特性が指向性を持つからだろう。

 

admin

Laser距離センサーを動かしてみた

<距離センサー>

熱中症指数計も要らなくなったので、M5stackの転用先を考えてみた。距離センサーで超音波方式はそれほど精度も出ないし、速度も遅いからレーザー方式を試してみようと。

デバイスは新しめの、

https://www.switch-science.com/catalog/8183/

上記のリンク先は、

https://learn.adafruit.com/adafruit-vl53l4cx-time-of-flight-distance-sensor/arduino

対象の数まで検出可能というところが優れもの。i2cデバイスで、シリアルにデバイスを拡張できるから基板にコネクタが二個付いている。

しかし、VL53L4のライブラリとサンプルコードでは動かない、デバイスの初期化でエラーが帰る。実はQwiicの接続だけでは不足しているような気がする、故に以下のサンプルコードとライブラリで動かした(VL53L1用だけどL4でも機能限定で動くんだとあったから)

https://learn.sparkfun.com/tutorials/qwiic-distance-sensor-vl53l1x-vl53l4cd-hookup-guide/all

APIが載っているので、こちらは使いやすい。

コードは比較的単純、

https://github.com/chateight/PlatformIO/tree/master/laser_dist_01/src

バッテリー残量表示はデフォルトで必要だと思うから入れてます。

 

距離測定中、タブレットの画面までの距離を表示してます。

アタッチメント、モジュールはいつもの両面テープ貼り付け

動いてるとこ、

Fusion360で作成したピース、

<おまけ>

VL53L4のドキュメントを見ると追加ピンが2本あるのでその役割

・GPIOとXSHUTの役割(i2cのアドレス変更で使う)

Qwiic以外の追加のインターフェースです。

The host hardware design must ensure the sensor XSHUT pins can be controlled individually. Each XSHUT pin must be connected to a host MCU GPIO pin.

To change the I2C address, the host must :

1. Put the device in HW standby by setting the XSHUT pin low on all the VL53L4CD devices

2. Raise the XSHUT pin of one of the VL53L4CD (e.g. current_tof)

3. Call VL53LX_SetDeviceAddress(current_tof, newAddress) to program the new address

4. Repeat the steps from [2 to 3] to change the address of all ToFs.

 

admin

M5stackって色指定が16bit colorだったんだ

https://qiita.com/nnn112358/items/ea6b5e81623ba690343c

などで、M5stackの色指定は3バイトではなくuint16_tで指定されるので何でかなと思ったら、r/b : 5bit, g : 6bitの2バイトで表現されるんですね。

M5stackのディスプレイならば、それで問題ないだろうと思うし、メモリ使用量を少なくする考慮が種々なされてるから、それもアリなのでしょう。

 

admin

熱中症簡易アラーム(@M5stack + sensor)

そろそろ夏も本番に近づいて、散歩も一時間が限界になりますが、暑さを客観的な数値にするにはWGBT指数(湿度と温度で判定)ですが、無論風のあるなし、直射日光下、人間の暑さ耐性、あるいは運動強度によって変わってくるので目安の数値ですが、i2cインターフェースの温湿度(気圧もある)センサーとM5stackで実現します。M5stackも電源入れっぱなしでなくて、時々チェックするぐらいなら電池120mAhでも使えると思う。

 

・用意したもの

M5Stack Basic V2.6

M5Stack用温湿度気圧センサユニット Ver.3(ENV Ⅲ)

いずれもいつものスイッチサイエンスで購入。

 

センサーのライブラリとサンプルコードは以下からダウンロード。

・サンプルコード@GitHub

https://github.com/m5stack/UNIT_ENV/blob/master/examples/UNIT_ENV_III_M5Stack/UNIT_ENV_III_M5Stack.ino

・unit_env

以下をVScodeのソースディレクトリに展開します。

https://github.com/m5stack/UNIT_ENV

 

・USBドライバー

M5stackへのバイナリのアップロードで以下のようなエラー発生、これはM5Stack Basic V2.6でUSB-serialのICが変わっていて、標準のOSではまだサポートしていないからだと、スイッチサイエンスのページにも記載あります。

Uploading stub…

A fatal error occurred: Failed to write to target RAM (result was 01070000: Operation timed out)

*** [upload] Error 2

しかし、

https://mag.switch-science.com/2021/11/01/m5stack-v2-6-changes/

に書いてある様にやってみてもダメで、

platformio.iniに以下のようなポート決めうち指定で解決しました。

upload_port_set.jpg

“monitor_speed = 115200″と合わせて、platformio.iniへのデフォルト設定になると思います。

ポート番号は環境で変わると思うので、wch以降は、

/devディレクトリで

% ls tty.wch* を実行して確認しましょう。PIO HomeのDevicesではそのままの値が確認できますが。

tty.wchusbserial54260147311

 

・WBGT指数

https://healthy-ecohouse.com/wp-content/uploads/2019/08/190804-4.jpg

からダウンロード

このテーブルをソースの中に二次元配列で埋め込んで、WBGT値を獲得しています。

画面はこんな感じです。

 

全部のコードは、

https://github.com/chateight/PlatformIO/tree/master/heat_alarm/src

heat_alarm.cpp以外はセンサーアクセス用のライブラリファイルです。

 

P.S. @2022/6/22

表示方法変更しました、テキストよりもカラーブロックで表示がわかりやすいから。

気圧の使い道は何なのかなと考えると、一番分かりやすいのは標高を簡易的に知るということになると思う。およそ100m高くなると10hPa気圧は低下するのだから。

P.S.2 @2022/6/25

ということで、大まかな高度も表示するようにしました。気圧は揺れ動くから、刻みは200m刻みで山に行ったときぐらいには使えるレベル。正確に出すならばGPSモジュール使うのだろうけど、そこまでは不要でセンサーの機能があるから使ってみようぐらいの話。

 

admin

M5stackのバッテリー残量表示

デフォルトでは小容量のLi-ion電池が内蔵されていますが、残量を知る方法が必要な時があります。完全放電してしまうと電池は再生不能になるし。

https://m5stack-build.hatenablog.com/entry/2019/10/05/233914

で電池残量表示していたので、該当部分だけのコードを引き出して、既存のプロジェクト、

https://isehara-3lv.sakura.ne.jp/blog/2022/02/04/1095/

に埋め込みました。

コア部分だけ抜き出すと以下の通り。setup_batt()が初期化、read_batt()が残量読み取りです。


void setup_batt()
{  
  M5.begin();
  M5.Power.begin();
}

int read_batt()
{
  int l_battery = M5.Power.getBatteryLevel();
  return l_battery;
}

ソースコードは、

https://github.com/chateight/servo_

になります。ファイル追加のついでに、汚かった構造(ヘッダーファイルにソースコード混在など)を書き直してます。

おそらく、どのプロジェクトでも電池残量表示はデフォルトで必要だろうと思う。

今は満充電のようですが、表示はおそらくそれほどの精度はなくて目安だろうと思う。

 

P.S.

2日ぐらい経過してみてみたら75%表示になっているから機能はしている。ただし分解能は25%らしい。

 

admin

UnitV2 AIカメラとM5stackをgroveコネクタで接続して認識結果を受け取る

UnitV2 AIカメラからは認識結果が垂れ流しで出力されてくるようなので、それをM5stackで受け取ってみます。

M5stackのgroveは標準ではI2Cなので、シリアルで使うためにSoftwareSerialライブラリを使います。

参考は、

https://qiita.com/ma2shita/items/37d403fb7a79814d4d4c

ですが、SoftwareSerialの設定(インスタンス作成)では上のリンクのソースと21 <=> 22を入れ替え必要でした。

groveコネクタというのは物理的な形状や接続しか定義していないので、中を通る信号線は自由に定義できるから。

以下の画像から、

ピン21 — SDA — Tx(左端)

ピン22 — SCL — Rx(左端から2番目)

となるので、シリアルのツイスト対抗ピン設定すると”Grove(22, 21); // define rx/tx”となります。

#include <M5Stack.h>
#include <ArduinoJson.h>
#include <SoftwareSerial.h>

SoftwareSerial Grove(22, 21);     // define rx/tx connecting to the UnitV2 camera
                                  // SoftwareSerial(rxPin, txPin, inverse_logic)

int x;
int y;
long k;
char data[50];

void setup() {
  M5.begin();
  M5.Lcd.setCursor(20, 40);
  M5.Lcd.setTextSize(2);
  Serial.begin(115200);
  Grove.begin(115200);
  M5.Lcd.print("--initialized--");      // display M5 Lcd message
  Serial.print("---initialized---");    // output serial line
}


void loop_(){

  if(Grove.available()) {
    String recvStr = Grove.readStringUntil('\n');
    if(recvStr[0] == '{'){
      Serial.print(recvStr);
    }
  }
}

void readJSON(void){
  String recvStr = Grove.readStringUntil('\n');
  StaticJsonDocument<128> doc;
  DeserializationError error = deserializeJson(doc, recvStr);

  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.f_str());
    return;
  }

  x = doc["x"];
  y = doc["y"];
  k = doc["k"];
  const char* running = doc["running"]; // "Lane Line Tracker"  
}

void loop(){
  if(Grove.available()) {
    readJSON();
    sprintf(data,"x = %d , y = %d , k = %ld",x,y,k);
    Serial.println(data);
  }
}

AIカメラの電源はgrove経由でM5stackから供給されるので、一度ブラウザでAIカメラの動作モードを指定してやれば、あとはAIカメラのUSBケーブルは抜いても引き続き動作します。

AIカメラから送信されるデータはjson形式なので、生のデータはこんな感じ。

これを、ArduinoJsont使って、解析します。

サンプルの事例ではLaneLineTrackerだったので同じコードを流用してますが、いかようにも変えられるでしょう。

生データ出力と解析データ表示は、ソース中ではloop()とloop_()関数で埋め込んで切り替えています。

また結果はUSBシリアルへの出力ですが、M5stackに表示させるだけで十分ならば画面表示させれば良いし、さらにパソコンやラズパイなどに無線で飛ばしても良いでしょう。

時々deserializeがエラーしているので、これはシリアルの転送速度を落としてやらないとダメかもしれません。

 

admin

M5stack unitV2 A.I cameraのmicroSDスロット

TFカードと呼んでますが、実態はmacroSDとほぼ同じだろう。本体にスロットがあり、取り出し簡単ではなかったけれども抜いてみたら、SunDiskの16GBのカードが入っていて中はカラっぽ。

すでにsshコマンドで確認済みですが。

https://isehara-3lv.sakura.ne.jp/blog/2022/03/07/unit-v2カメラにsshログオン/

A.Iカメラの基本機能でそれなりのことはできるけれども、何かやろうとするとA.Iカメラ側にコード書かなければいけないけれども、関連情報はこれからサーチ。まあ、Pythonも3.0系が入っているし、c/c++も使えばやりたいことはできるでしょう。

Groveインターフェース介して、電源供給とシリアル通信使うのが現実的なアプリケーションになると思う。

 

admin

M5stackでstepping motorを動かす

Arduinoのステッピングモーター駆動は挙動不審、どうも脱調しているかの如く、同じ動作の繰り返しでも段々位置ずれするから、M5stackでも動かしてみる。

ライブラリ使うとブラックボックスになるので、直接ソースで動作を指定しました。

#include <M5Stack.h>

// motor/driver 28BYJ-48/ULN2003
//
// original source https://blog.takumus.io/entry/2016/09/13/190510
//

const int IN1 = 2;
const int IN2 = 16;
const int IN3 = 5;
const int IN4 = 17;

const int d_hs[8] = {   // 1-2 drive, 4096 cycles/turn ; gear ration 64:1
  0B00001000, 
  0B00001100,
  0B00000100,
  0B00000110,
  0B00000010,
  0B00000011,
  0B00000001,
  0B00001001
};

int i = 0;

void step(int d);

void setup()
{
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
}

void loop()
{
  for(int i=0; i < 8*256; ++i)
  { 
    step(1);
    delayMicroseconds(2000);    // it does not work properly lower than 2 ms 
  }
  for(int i=0; i < 8*512; ++i)
  {
    step(-1);
    delayMicroseconds(2000);
  }
  for(int i=0; i < 8*256; ++i) 
  { step(1); delayMicroseconds(2000); 
  } 
}
 
void step(int d) // d = -1 : anticlockwise, d = 1 : clockwise { i += d; if(i > 7) i = 0;
  if(i < 0) i = 7;
  byte b = d_hs[i];
  digitalWrite(IN1, bitRead(b, 0));     // bit position 0 ; LSB
  digitalWrite(IN2, bitRead(b, 1));
  digitalWrite(IN3, bitRead(b, 2));
  digitalWrite(IN4, bitRead(b, 3));
}

M5stackは外部に出ているGPIOピンが少ないので、使用するピンは限定されます。

以下の画像で、3-R0, 18-SCKなどは同じピンがオスメスの違いで出力されているだけです。

最初、2/5/35/36使ったら動かない、35/36は入力専用だから。次に2/5/25/26使うと動いたけど、スピーカーと兼用らしくて五月蝿い。で最終的に2/5/16/17を使用。

 

<結論>

M5stackでは正確に角度を刻める、ただし時間待ちを1,500μsぐらいに小さくすると抜けが出るようです。値を大きく取って、連続して動かしていてもターンテーブルの位置は変わらないから、時間待ちがどうも問題の本質のようです。

あとArduinoIDEはイマイチだから、Arduinoの開発環境もVScodeにしたほうがいいかもしれない。

 

P.S. (2022/5/22)

Arduinoから線引き出して、同じ条件で二つのモーターの動作比較して、

・本体のモーターが片方向しか回転しないのは、モータードライバ基板へのArduino側の配線が一本外れてた。-> これは該当問題とは無縁の突発事象

・外部モーターもライブラリ使わないとFreeRTOS配下ではうまく動作しない。圧電スピーカーでもそうだったけど、どうも時間待ちという処理でFreeRTOSとリソース競合が起こるように見える。

・ライブラリ使っても、ライブラリなしと同様に回転速度を落とさないとパルスが抜けるように見える。

と言うことで、回転速度を” myStepper.setSpeed(5); // 10 -> 5回転/1分に設定、つまりライブラリなしでの時間待ちを長くするに相当、に落とすとまともに動いてるように見えるから、高速では動けないモーターだったということ。

アップデートは、

https://github.com/chateight/arduino/tree/master/smart_car_RTOS

にアップロードしました。

 

admin

PyGameのコントローラーにM5stack via Bluetooth

PyGameの迷路のコントローラーとしてM5stackを使ってみた。

M5stackとMacとの通信は以下の方法で。

https://isehara-3lv.sakura.ne.jp/blog/2022/05/05/m5stack-bluetoothでのmacとの送受信/

迷路ゲームについては、迷路作成は壁のばし法

https://yottagin.com/?p=1579

のクラスファイル流用して、ゲーム本体は、

https://news.mynavi.jp/techplus/article/zeropython-90/

から迷路作成ロジックだけ入れ替えですが、ほぼそのまま使えました。

M5stackのボタン処理は、ボタンが三つしか無いから、そのままではL/R/U/Dの処理ができないから、M5stackを傾けてボタンA/Cを操作したらU/Dになるように重力加速度の値で読み替えしてます。

ゲーム中にボタンBを押すとゲーム終了します。

#define M5STACK_MPU6886
#include "BluetoothSerial.h"
#include <M5Stack.h>

BluetoothSerial bts;
String btn;
float accX = 0.0F;  // Define variables for storing inertial sensor data
float accY = 0.0F;
float accZ = 0.0F;
float angle_th = 0.5F;  // r/l or u/l decision threashold

void read_imu() {
  M5.IMU.getAccelData(&accX,&accY,&accZ); //Stores the triaxial accelerometer. 
  M5.Lcd.setCursor(0, 90);
  M5.Lcd.printf(" accX");
  M5.Lcd.setCursor(0, 130);
  M5.Lcd.printf("%5.2f G", accX);
}

void setup() {
  M5.begin();
  M5.Power.begin(); //Init Power module.  
  M5.IMU.Init();    //Init IMU sensor.  
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 0, 2);
  M5.Lcd.println("Maze game");
  bts.begin("ESP32test");
}

void loop() {
  btn = "";                           // button info. clear
  
  M5.update();
  if(M5.BtnA.wasPressed())
  {
    btn = "r";
  }
    if(M5.BtnB.wasPressed())
  {
    btn = "t";
  }
  if(M5.BtnC.wasPressed())
  {
    btn = "l";
  }

  read_imu();   // imu data read

  if(btn != "")
  {
    if (btn != "t")       // t(ButtonB) key terminate the Maze game
    {
      if (accX < -angle_th | accX > angle_th)       // if M5stack is tilted, r/l keys are changed to u/d
      {
        if (accX < -angle_th){ if (btn == "r") { btn = "u"; } else { btn = "d"; } } if (accX > angle_th)
        {
          if (btn == "r")
          {
            btn = "d";
          }
          else
          {
            btn = "u";
          }
        }
      }
    }

    bts.println(btn);      // send pressed key info.
    M5.Lcd.setCursor(0, 50, 2);
    M5.Lcd.println(btn);
  }

  delay(100);

}

Bluetoothシリアルがコードを弄ってると繋がらなくなることが頻繁に起きて、その都度リブートしてるのは面倒です。本質的な原因はなんなんだろう?

全体のコードは、

https://github.com/chateight/PlatformIO/tree/master/bte_serial/src

にあります。

pythonのmaze.pyがメイン処理、make_maze.pyが迷路作成コード、receive.pyはM5stackのキー入力確認用のスクリプトでゲームと直接は関係ありません。

 

admin