M5stack Bluetoothでのmacとの送受信

プロトコルの煩雑なbleではなく、シンプルなbluetoothで接続してみます。

<環境>

・M5stack gray

・Macbook Monterey

・Python3 : mac側の動作確認用スクリプト記述

<ライブラリ>

bluetoothをあたかもシリアルのように見せるライブラリ(M5stack :

BluetoothSerial.h)とpythonでシリアルインターフェースを扱うための pyserialがあるのでそれらを使います。

 

・pyserialをインストールする

pip3 install pyserial

 

BluetoothSerial.hのインストール

BluetoothSerial.hはM5stackのライブラリから選択、インストールできます

 

<main.cpp>

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

BluetoothSerial bts;

void setup() {
  M5.begin();
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 0, 2);
  M5.Lcd.println("Bluetooth TEST");
  bts.begin("ESP32test");
}

void loop() {
  M5.Lcd.setCursor(0, 20, 2);
  M5.Lcd.printf("Start Bluetooth");
  bts.println("message from the M5stack");
  //Serial.println("loop");
  String str = bts.readString();
  M5.Lcd.setCursor(0, 60, 2);
  M5.Lcd.println(str);

  delay(1000);
}

 

・シリアルポートを調べる

M5stackにコード書き込んで、シリアルポートを調べます。

% ls -l /dev/tty.*

crw-rw-rw-  1 root  wheel  0x9000008  5  4 18:03 /dev/tty.ESP32test

 

<write.py>

該当のポート情報に上の情報を設定します。

import serial

mes = "messge from my Mac"
# initialize bt serial
m5data = serial.Serial('/dev/tty.ESP32test',115200, timeout=3)
# read from bt
line = m5data.readline()
print(line.decode()[:-2:])
# write to bt
m5data.write(mes.encode())
m5data.close()

・実行

% python write.py 

b”

‘’に何も表示されないということはタイムアウトしている。

・macをリブートして、USBシリアル経由でpyserialの機能を確認してみる。

% ls -l /dev/tty.*

crw-rw-rw-  1 root  wheel  0x9000008  5  5 09:39 /dev/tty.SLAB_USBtoUART

write.pyに以下のデバッグ行追加

Serial.println(“loop”);

% python write.py

b’loop\r\n’

デバッグ用にコンソールに出力させて、シリアルポート切り替えると受信できるからpyserialそのものは動いているんだろう。

じゃ、本来のbluetoothでどうなの?この時MacからESPtestのbluetoothに接続はしない。

% python write.py

b’message from the M5stack\r\n’

ちゃんと受信できるから、どこかクリアされない状態があった?

リブートで状態がクリアされているようで、結果としてmacの設定でbluetoothの接続をしてしまうとダメ。一度接続選択すると接続選択解除してもpythonスクリプトからは使えなくなるから接続の権利(と言ってもmacに変わりはないけど)を他でキープしているようだ。

P.S ここでは接続しないと、% ls -l /dev/tty.*で見えないからダメだけど、

ここでは接続してはいけない。

 

読み出し文字列中のb’’はバイトストリングと言うことらしいけど、実際には必要ないからdecode()して普通の文字列に変換してやります。また書き込みの時にはencode()が必要です。

M5stackのスクショってどうすれば取れるんだろう?

bluetooth経由でデータの送受信ができるとやれることが広がります。

 

admin

Unit V2カメラ Mac OSでの接続

今までMac OSだと、ドライバー必要と思っていたけど、以下のやり方でドライバーは入れなくても使えます。

Linuxのように完全に手続き不要ではなく、ifconfigで接続先を切り替える方法です。

ifconfig(部分)で以下のようなメッセージが出てますが、

en6: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
	options=6467<RXCSUM,TXCSUM,VLAN_MTU,TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZE
 % sudo ifconfig en6 down
 % sudo ifconfig en6 up  

のように入力するとUnit V2に接続されるので、Chromeからhttp://10.254.239.1/

を入力すれば管理画面が出てきます。

参考は、https://qiita.com/kowloon/items/99073be408f6432711aad

です。

 

admin

Unit V2カメラにsshログオン

Linuxで動いているので、当然sshも使えます。

ユーザー権限では色々情報ありますが、root権限では以下のような長ったらしいパスワード入れれば良さそうです。

https://notenoughtech.com/home-automation/m5stack-unitv2/

10.254.239.1はavahiが動いていればunitv2.pyで問題ありません。



<user login>

ssh m5stack@10.254.239.1 
//user: m5stack
//pwd: 12345678

------------------------------------

<file>

unitv2% df
Filesystem           1K-blocks      Used Available Use% Mounted on
ubi:rootfs              419980    297980    122000  71% /
devtmpfs                 57344         0     57344   0% /dev
tmpfs                    58368         0     58368   0% /dev/shm
tmpfs                    58368        92     58276   0% /tmp
tmpfs                    58368        44     58324   0% /run
cgroup                   58368         0     58368   0% /sys/fs/cgroup
/dev/mmcblk0p1        15549952        32  15549920   0% /media/sdcard


<root pw>

7d219bec161177ba75689e71edc1835422b87be17bf92c3ff527b35052bf7d1f

unitv2# who
root            pts/0           00:00   Jan  1 00:07:12  10.254.239.104
unitv2# 


<python>

unitv2# python --version
Python 3.8.6


<network>

unitv2# ifconfig
br0       Link encap:Ethernet  HWaddr 00:30:1B:BA:02:DB  
          inet addr:10.254.239.1  Bcast:10.254.239.255  Mask:255.255.255.0

ログインして幾つかのコマンドを実行してみましたが、外付けのsdカードには何も入っていないようですね。何に使うんだろう?

 

admin

 

 

M5Stack UnitV2 AI カメラ(SSD202D)つないでみる

いつもの通り、switch scienceで購入。これは宅急便(送料650円)扱いだけれども、8,000円以上は送料無料とかで支払い一万円は超えず。

 

macに接続、最初Mac OSでは機能有効にするとインターネット使えなくなった(Linuxでも同じ)ですが、ドライバ不要のLinux(VMware Ubuntu)で使うのが良さそう。

デフォルトのアプリで物体認識してみる。写真にカップ写っているからかdinning tableと認識されてます。

まずは、疎通確認だけですね。

<ドキュメント>

https://docs.m5stack.com/en/unit/unitv2?id=description

https://docs.m5stack.com/en/quick_start/unitv2/v_training

 

admin

M5Stack UnitV2 AI カメラ(SSD202D)

M5stackにつながるカメラにそれほどの選択肢はありませんが、これは面白そうです。A.I機能がビルトインされてて購入時点で使えるとのことなので、画像認識系のアプリを簡単に作成するには便利そうです。

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

Linux系らしいので、多少モディファイするのも難易度は高くはなさそうです。

(画像はスイッチサイエンスのwebから)

 

admin

M5stack grayでBLEを使う

M5stackには色々な機能が含まれているので、順次の機能確認でまずはBLE機能を使ってみます。

ソースはPlatformIOのライブラリでBuil-in中のESP32 BLE ArduinoのRevealから見えるBLE_scanから持ってきています。ライブラリーにM5stack追加とソースに

#include <m5stack.h> はM5stack使う時には必ず追加必要です。

#include <m5stack.h>    // this is needed to the original source code.

/*
   Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
   Ported to Arduino ESP32 by Evandro Copercini
*/

#include <bledevice.h>
#include <bleutils.h>
#include <blescan.h>
#include <bleadvertiseddevice.h>

int scanTime = 5; //In seconds
BLEScan* pBLEScan;

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
    }
};

void setup() {
  Serial.begin(115200);
  Serial.println("Scanning...");

  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan(); //create new scan
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99);  // less or equal setInterval value
  M5.begin();       //Init M5Core.  
  M5.Power.begin(); //Init Power module.  
  M5.Lcd.fillScreen(BLACK); //Set the screen background color to black. 
  M5.Lcd.setTextColor(GREEN , BLACK); //Sets the foreground color and background color of the displayed text.  
  M5.Lcd.setTextSize(2);    //Set the font size.  
  M5.Lcd.setCursor(0, 70);
  M5.Lcd.printf("pow on");
}

void loop() {
  // put your main code here, to run repeatedly:
  BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
  Serial.print("Devices found: ");
  Serial.println(foundDevices.getCount());
  Serial.println("Scan done!");
  pBLEScan->clearResults();   // delete results fromBLEScan buffer to release memory
  delay(2000);
}
このソースは関数をポインタにして、onResult()はイベント待ちだろうからコールバック関数使ってます。コールバック関数使うために関数をポインタ化するんでしょうが。
シリアル出力を使っているので、platformio.iniで速度指定をしないと文字化けします。

monitor_speed = 115200

を追加して実行させると、メッセージを吐き出しますが以下はその一部です。

Devices foundが0というのは不思議ですが。

 

admin

M5stack IMUとサーボモーターの連動(二軸)ーーその2

二つ前の記事でIMUとサーボモーターを連動させたので、構造部分を作成してM5stackの直立状態を保持できるような姿勢制御を作ってみました。

M5stack IMUとサーボモーターの連動(二軸)

前回からの変更点はサーボ制御部分のみです。


#include <imu.h>

int servopinx=2;    // dio port definition for X axis
int servopiny=5;    // dio port definition for Y axis
int pulsewidth;     // pwm on time
int repeat=1;       // number of pwm write cycle repetition
int ax;             // angle for servo motors
int ay;
int az;
float p_accX;       // to hold IMU read data
float p_accZ;
int p_angX;         // to hold servo motor angles
int p_angZ;
boolean first = true;   // check if it is first cycle or not.
//
void servo(int myangle, int motor)      // servo motor pwm contorol. motor 0 : servoponx , motor 1 : servopiny
{
 for (int i = 0; i < repeat; i ++)      // i is used to keep a moving time, since this source does not use the library.
 {
    int port;
    if (motor == 0){
        port = servopinx;
         }
        else{
        port = servopiny;
    }
    pulsewidth=map(myangle,0,180,500,2500);
    digitalWrite(port,HIGH);
    delayMicroseconds(pulsewidth);
    digitalWrite(port,LOW);
    delay(20-pulsewidth/1000);
 }
}
void init_sm()
{
    servo(90, 0);
    servo(90, 1);
    p_angX = 90;
    p_angZ = 90;
    delay(600);
}
void setup()
{
 setup_imu();
 pinMode(servopinx,OUTPUT);
 pinMode(servopiny,OUTPUT);
}
void loop()
{
 loop_imu();
 if (first == true){
    init_sm();                  // initialize to 90 degree
    first = false;
    }
// the angle change is needed? & the change is reflected? & check the angle limit
 if (accX < -0.05 && abs(p_accX - accX) > 0.02 && p_angX < 180){ p_angX ++; } else{ if (accX > 0.05 && abs(p_accX - accX) > 0.02 && p_angX > 0){
             p_angX --;
         }
     }
 if (accZ < -0.05 && abs(p_accZ - accZ) > 0.02 && p_angZ < 180){ p_angZ ++; } else{ if (accZ > 0.05 && abs(p_accZ - accZ) > 0.02 && p_angZ > 0){
             p_angZ --;
         }
     }
 p_accX = accX;                 // set to the previous values
 p_accZ = accZ;
 servo(p_angZ , 0);             // drive the servo motor
 servo(p_angX , 1);
 delay(1);
 M5.Lcd.setCursor(0, 114);
 M5.Lcd.printf("%3d  %3d ", p_angX, p_angZ);
}

 

imu.hには変更ありません。

今回はM5stackを垂直で使うので、二次元を水平に保つのはX軸とZ軸になるので、IMUからの読み出しデータはそれらを使います。

M5stackが移動するときには重力加速度以外の加速度も加算されますが、このアプリではそれを勘案する必要はないでしょう。多少動きがオーバーシュートするのはそのせいかもしれませんが。

写真は以下の通り。

サーボーモーターの電源入れない状態でM5stackを動かしているので、画面のアングル表示は変な値になっています。初期値がどうであっても、最後は収束しますが。

コードと3dプリンタ用のstlファイルは、

https://github.com/chateight/servo_

に置いてあります。M5stack追従のロジックは改善の余地があるように思います。

M5stack本体との接続は、DIO2,5とGNDの三本だけ。

 

admin

M5stack IMUとサーボモーターの連動(二軸)

M5stackの加速度計(重力加速度)とサーボモーターを組み合わせることで、M5stackの姿勢をフォローするものを作ります。

M5stackの加速度測定数値は結構揺らぎがあるので、平滑化と有効ビットの削減をおこなっています。

<servo.cpp>


#include <imu.h>

int servopinx=2;    // dio port definition
int servopiny=5;
int pulsewidth;     // pwm on time
int repeat=1;       // number of pwm write cycle repetition
int ax;             // angle for servo motors
int ay;
int az;
float comp = 1.1;  // compensate not to show minus value
float adj = 30;    // adjuct the angle with map() function not to exceed the range upper limit 180 & eliminate the noise
float p_accX = 0.5;
float p_accY = 0.5;
float p_accZ = 0.5;
boolean first = true;   // check if it is first cycle or not.
float smooth = 0.6;     // smoothing the angle value. it causes a delay time to the angle change.
//
void servo(int myangle, int motor)      // servo motor pwm contorol. motor 0 : servoponx , motor 1 : servopiny
{
 for (int i = 0; i < repeat; i ++)      // i is used to keep a moving time, since this source does not use the library.
 {
    int port;
    if (motor == 0){
        port = servopinx;
         }
        else{
        port = servopiny;
    }
    pulsewidth=map(myangle,0,180,500,2500);
    digitalWrite(port,HIGH);
    delayMicroseconds(pulsewidth);
    digitalWrite(port,LOW);
    delay(20-pulsewidth/1000);
 }
}
void setup()
{
 setup_imu();
 pinMode(servopinx,OUTPUT);
 pinMode(servopiny,OUTPUT);
}
void loop()
{
 loop_imu();
 if (first == true){        // smoothing
     p_accX = accX;
     p_accY = accY;
     p_accZ = accZ;
     first = false;
    }
    else{
        p_accX = p_accX*smooth + accX*(1-smooth);       
        p_accY = p_accY*smooth + accY*(1-smooth);
        p_accZ = p_accZ*smooth + accZ*(1-smooth);
    }
 ax = map((int)((p_accX+comp)*adj), 0, 66, 0, 180);     // translate acc values to the range 0 to 180
 ay = map((int)((p_accY+comp)*adj), 0, 66, 0, 180);
 az = map((int)((p_accZ+comp)*adj), 0, 66, 0, 180);
 M5.Lcd.setCursor(0, 114);
 M5.Lcd.printf("%3d  %3d  %3d ", ax, ay, az);
 servo(ax , 0);             // drive the servo motor
 servo(ay , 1);
 delay(5);
}

本来的なヘッダーファイルの使い方とは多少異なりますが、M5stack IMUの加速度数値を読み取るルーチンです。

<imu.h>

/*
*******************************************************************************
* Copyright (c) 2021 by M5Stack
*                  Equipped with M5Core sample source code
* Visit the website for more information:https://docs.m5stack.com/en/core/gray
*
* describe:MPU6886 example. 
* date:2021/7/21
*******************************************************************************
*/
#define M5STACK_MPU6886
#include <m5stack.h>

float accX = 0.0F;  // Define variables for storing inertial sensor data
float accY = 0.0F;
float accZ = 0.0F;

void setup_imu(){
  M5.begin(); //Init M5Core.  
  M5.Power.begin(); //Init Power module.  

  M5.IMU.Init();  //Init IMU sensor.  

  M5.Lcd.fillScreen(BLACK); //Set the screen background color to black. 
  M5.Lcd.setTextColor(GREEN , BLACK); //Sets the foreground color and background color of the displayed text.  
  M5.Lcd.setTextSize(2);  //Set the font size.  
}

void loop_imu() {
  M5.IMU.getAccelData(&accX,&accY,&accZ); //Stores the triaxial accelerometer. 

  // Accelerometer output is related
  M5.Lcd.setCursor(0, 70);
  M5.Lcd.printf("accX,   accY,  accZ");
  M5.Lcd.setCursor(0, 92);
  M5.Lcd.printf("%5.2f  %5.2f  %5.2f G", accX, accY, accZ);
}

やりたいことにはもう一工夫がいるので、それを追加して3Dプリンタで構造を作れば完成型になります。

P.S.

傾き(θ)に対して重力加速度はリニアではなく、重力加速度からarcsin()で傾きを計算しないといけないわけですが、大まかな振る舞いを見るには直線性は無視しても構わないと思います。

 

P.S.2

Switch Science記載のサーボモーターのピン番号は間違っている(PowとSig入れ違い)ので、正しいピン番号を記載しておきます。

 

admin

VScodeでソースを複数ファイルに分割時の留意点

IMUとservo機能を連携させようとしますが、ソースファイルを一個にするのは汚い管理なので、機能ごとに分割します。

この時にArduino IDEだとバックエンドで無意識に処理をしてくれますが、VScodeを使う時にはほぼcの作法に従う必要があります。つまり、

・他のソースを使う時には、xxx.hとしてインクルードする。

今回のケースではservo.cppからimu.hを呼び出しています。

しかし、imu.hの組み込みで”M5にIMUは見つからないよ”とコンパイルエラーになります。

で調べると、

トホホな疑問(31) M5Stack、IMUの種類と取り扱いに戸惑う

と、

https://github.com/m5stack/M5Stack/blob/master/src/M5Stack.h

にあるようにimu.hの先頭部分は、

#define M5STACK_MPU6886
#include <M5Stack.h>
ですが、ここで#defineの”M5STACK_MPU6886″がM5stack.h中で展開されてIMUオブジェクトが生成されるので、#include <M5Stack.h>文が後に来ないと機能しません。

従ってservo.cppからは、#include <M5Stack.h>を削除してimu.hの記述を使うようにするとうまくいきました。二重インクルードはこの場合にはエラーにならず、最初の#include <m5stack.h>が有効になるからでしょう。


== top of the servo.cpp ==
//#include <m5stack.h>    // M5stack needs this module
#include <imu.h>

== top of the imu.h ==
#define M5STACK_MPU6886
#include <m5stack.h>

ということなので、Arduino IDEと違ってVScodeは普通のc開発に近いよということでした。

 

admin

サーボモーターの動作確認(M5stack gray)

サーボモーター(LFD-01M)到着したので早速動作確認。制御は標準的なPWM方式。PWMの周波数も標準的な50Hzです。

接続はモーターへの電源、そしてM5stackからのPWM制御信号です。GNDの共有を忘れずに。当然モーターの電源は大電流なので外部電源で5Vを使います。PWMは多くの他の信号がそうであるように3.3V振幅ですが、入力はどうせC-MOSだろうから問題なし。

https://coskxlabsite.stars.ne.jp/html/for_students/M5Stack/RCServo/RCServo.html

PWMピンは上記を参照してDIOの2ピン目を使用。

動作確認用のソースはHiwonderのwebページからですが、サーボモーター用のライブラリは使ってません。存在しないのかもしれませんが、直接コードを書く方が動作の理解には助けになります。

https://drive.google.com/drive/folders/1Bgf1HGrfhB8N8XIxlRpz-U9_2oxVurDv

Arduino用なので多少の修正が必要なのと、画面表示がないと電源オンもわからないのでメッセージを出すようにしてます。ライブラリを使わないと、動作時間制御もコードで指定(この場合には20ms*20 = 400ms)しています。試しに9行目の20を10にすると90度ぐらいしか回転しません。

/*******舵机测试程序*******
 * Arduino型号:Arduino UNO
 **************************/
#include <m5stack.h>    // M5stack needs this module
int servopin=2;
int pulsewidth;
void servo(int myangle)
{
 for (int i = 0; i < 20; i ++)  // i is used to make moving time, since this source does not use library.
 {
pulsewidth=map(myangle,0,180,500,2500);
digitalWrite(servopin,HIGH);
delayMicroseconds(pulsewidth);
digitalWrite(servopin,LOW);
delay(20-pulsewidth/1000);
 }
}
void setup()
{
 pinMode(servopin,OUTPUT);
 M5.begin();
 M5.Lcd.fillScreen(BLACK); //Set the screen background color to black. 
 M5.Lcd.setTextColor(GREEN , BLACK); //Sets the foreground color and background color of the displayed text.  
 M5.Lcd.setTextSize(2);  //Set the font size.  
}
void loop()
{
 M5.Lcd.setCursor(0, 70);
 M5.Lcd.printf("servo_motor drive");
 servo(0);
 delay(500);
 servo(180);
 delay(500);
}

駆動波形はこんな感じ。

動作時の動画は、

となります。180度以上振れてるようなので、キャリブレーションは必要そうです。

 

admin