M5Stack でシリアル通信

日曜電子工作の続き。

M5StackとMacとの間でシリアル通信を試してみる。
M5Stackの開発ではホストPCとUSBケーブルで接続してシリアル通信をプログラムの転送を行うので、その環境をそのまま利用する。

M5Stack側

M5Stack側のプログラムは前回写経した内蔵加速度センサのサンプルを少し改造して取得したデータをシリアルポートに出力するようにする。

#define M5STACK_MPU6886 

#include <M5Stack.h>
#include "utility/MPU6886.h"

#define MULTISAMPLE 20

void readAccelMulti(float *ax, float *ay, float *az, int multi) {
  float x, y, z;
  float _x, _y, _z;

  x = y = z = 0;
  for (int i = 0; i < multi; i++) {
    M5.IMU.getAccelData(&_x, &_y, &_z);
    x += _x;
    y += _y;
    z += _z;
  }
  *ax = x / multi;
  *ay = y / multi;
  *az = z / multi;
}

void drawGrid() {
  M5.Lcd.drawLine(41, 120, 279, 120, CYAN);
  M5.Lcd.drawLine(160, 1, 160, 239, CYAN);
  M5.Lcd.drawCircle(160, 120, 119, CYAN);
  M5.Lcd.drawCircle(160, 120, 60, CYAN);
}

int oldX = 0;
int oldY = 0;

void drawSpot(int ax, int ay) {
  int x, y;
  x = map(constrain(ax, -300, 300), -300, 300, 40, 280);
  y = map(constrain(ay, -300, 300), -300, 300, 240, 0);
  M5.Lcd.fillCircle(oldX, oldY, 7, BLACK);
  drawGrid();
  M5.Lcd.fillCircle(x, y, 7, WHITE);
  oldX = x;
  oldY = y;
}

float offsetX, offsetY, offsetZ;
#define MOVINGAVG 10
float movingavgx[MOVINGAVG], movingavgy[MOVINGAVG];
int _index = 0;

void setup() {
  M5.begin();         // INIT M5Stack
  M5.Power.begin();   // 
  M5.IMU.Init();     // Init MPU6886

  readAccelMulti(&offsetX, &offsetY, &offsetZ, MULTISAMPLE);
  for (int i = 0; i < MOVINGAVG; i++) {
    movingavgx[i] = offsetX;
    movingavgy[i] = offsetY;
  }
}

int serialStart = 0;

void loop() {
  float x, y, z;
  float ax, ay;

  M5.update();

  readAccelMulti(&x, &y, &z, MULTISAMPLE);
  movingavgx[_index] = x;
  movingavgy[_index] = y;
  _index = (_index + 1) % MOVINGAVG;

  ay = ax = 0;
  for (int i = 0; i < MOVINGAVG; i++) {
    ax += movingavgx[i];
    ay += movingavgy[i];
  }
  ax = ax / MOVINGAVG;
  ay = ay / MOVINGAVG;

  if (M5.BtnA.wasPressed()) {
    offsetX = ax;
    offsetY = ay;
  }  
  if (M5.BtnC.wasPressed()) {
    serialStart = (serialStart == 0) ? 1 : 0;
  }

  if (serialStart == 1) {
    Serial.printf("{x:%f, y:%f}¥n", ax- offsetX, ay - offsetY);
  }
  
  drawSpot((int)((ax - offsetX) * 1000), (int)((ay - offsetY) * 1000));
  delay(100);
}

シリアルポートのオープンは M5.update()の中で既に行われているのでデフォルトの通信速度(115,200bps)で問題なければ何もする必要はないみたい。
シリアルポートへの読み書きは、Serial オブジェクトのメソッドを利用して行う。ここらへんは普通に Arduino 互換な感じでいける。リファレンスにはprintf()は存在しないがなんか使えるっぽい。

Mac

親機であるMac用のプログラムはPythonで書いてみた。といってもたったの5行。

import serial
ser = serial.Serial("/dev/cu.SLAB_USBtoUART", 115200, timeout=0.1)
while True:
    line = ser.readline()
    print(line)

シリアル通信にはpyserialを使用するので、事前にpipを利用してインストールしておく。
通信ポートは開発環境をセットアップするときにインストールしたUSBのシリアルドライバによって生成されたデバイスを指定する。

$ pip3 install pyserial

実行してみる

M5StackとMacをUSBケーブルで接続、プログラムを転送してM5Stackの電源を入れてから、Mac側の受信プログラムを実行する。

$ python3 serial_test.py
b'{x:-0.019182, y:-0.096029}\xc2\xa5n'
b'{x:-0.003403, y:-0.126227}\xc2\xa5n'
b'{x:-0.031735, y:-0.192700}\xc2\xa5n'
b'{x:-0.045695, y:-0.215953}\xc2\xa5n'
b'{x:-0.048987, y:-0.247052}\xc2\xa5n'
b'{x:-0.049235, y:-0.274598}\xc2\xa5n'
b'{x:-0.048007, y:-0.308534}\xc2\xa5n'

とりあえずデータの受信はできているようだけど、前後に謎のデータが付与されている。 これらはなんじゃろかい?