M5StickCでUART(Serial)を使う

2本線での通信ですので、簡単に試せます。

※現時点の情報ですので、最新情報はM5StickC非公式日本語リファレンスを確認してください。

UARTとは

送信と受信の通信線をクロスにつないだ通信方式です。2本の通信線で通信ができるので簡単ですが、フロー制御などがないので文字化けとか通信データの欠落などがでてきます。

ESP32では3系統のUARTが利用することができます。

  • Serial : USB接続した場合にPCと通信するシリアル
  • Serial1 : 2系統目のシリアル
  • Serial2 : 3系統目のシリアル

1系統目はPCとの接続に利用しています。2系統目と3系統目はどちらを使ってもいいですし、両方同時に使うことも可能です。

また、Bluetooth経由でワイヤレス接続のBluetoothSerialってのもありますが、こちらは後日紹介します。

ピン配置

入力専用のIO36を除き、外部接続可能な4PINはどんな組み合わせでもI2Cで通信が可能でした。

サンプル配線

M5StickCのみで実験をする場合の配線です。特徴としてはRXとTXを接続することです。RXがReciverで受信、TXはTransmitterで送信を意味しています。

  • Serial1 : RX(0), TX(26)
  • Serial2 : RX(32), TX(33)

上記にアサインしています。Groveは32,33の順で割り当てるのが標準ですが、EXT IOの0と26は逆でも構いませんが、I2Cと同じように若い番号から割り当てています。

サンプルコード

#include <M5StickC.h>
void setup() {
  M5.begin();
  Serial1.begin(115200, SERIAL_8N1, 0, 26); // EXT_IO
  Serial2.begin(115200, SERIAL_8N1, 32, 33);// Grove
}
void loop() {
  if (Serial.available()) {
    // Serial(PC) to Serial1(EXT_IO)
    int inByte = Serial.read();
    Serial1.write(inByte);
  }
  if (Serial1.available()) {
    // Serial1(EXT_IO) to Serial2(Grove)
    int inByte = Serial1.read();
    Serial2.write(inByte);
  }
  if (Serial2.available()) {
    // Serial2(Grove) to Serial(PC)
    int inByte = Serial2.read();
    Serial.write(inByte);
  }
}

Arduinoのシリアルモニタから送信したデータをSerial(USB)で受信して、Serial1(EXT_IO)に出力。Serial1(EXT_IO)で受信したデータをSerial2(Grove)に送信。Serial2(Grove)で受信したデータをSerial(USB)に送信というサンプルです。

通信速度について

サンプルは一番最速の「115200」を指定していますが、速度が早いとエラーになる可能性があがるので、もう少し遅い速度を指定したほうが安定すると思います。

  • 300
  • 1200
  • 2400
  • 4800
  • 9600
  • 14400
  • 19200
  • 28800
  • 38400
  • 57600
  • 115200

あたりが指定できますが、相手と同じ速度を指定する必要があるので、相手が固定値だと選択肢がないですが、速度が必要ない場合には9600あたりか、少し早い19200あたりの方がエラーは少ないと思います。

遅めで通信をして、速度が必要になった場合に早くするほうが苦労が少ないと思います。

外部装置との接続例

ESP32の開発ボードとM5StickCをUARTで接続する実験をしてみました。

M5StickC側サンプルスケッチ

#include <M5StickC.h>
void setup() {
  M5.begin();
  Serial2.begin(115200, SERIAL_8N1, 32, 33);
}
void loop() {
  if (Serial2.available()) {
    int inByte = Serial2.read();
    Serial.write(inByte);
  }
  if (Serial.available()) {
    int inByte = Serial.read();
    Serial2.write(inByte);
  }
}

Grove側の32と33を使って接続しています。ESP32から送信されたデータをSerial2(Grove)で受信して、Serial(USB)に送信しています。また、Serial(USB)から送信されたデータをSerial2(Grove)に送信しています。

ESP32開発ボード側サンプルスケッチ

void setup() {
  Serial.begin(115200);  
  Serial2.begin(115200, SERIAL_8N1, 32, 33);
}
void loop() {
  if (Serial2.available()) {
    int inByte = Serial2.read();
    Serial.write(inByte);
  }
  if (Serial.available()) {
    int inByte = Serial.read();
    Serial2.write(inByte);
  }
}

ほぼ一緒ですが、M5.begin()の中で実行されているSerial.begin()を自分で呼び出す必要があります。

実行の注意点

Arduino IDEのシリアルモニタは1つのシリアルしか開けません。開発ボードを変更すると他の設定も一度に変わってしまうので、別のパソコンとかに繋げて実験するのが一番動かしやすいです。

UARTの注意点

電圧差異

M5StickCは内部は3.3Vで動いていますが、一般的なArduinoボードは5Vで動いています。そのまま接続すると電圧が異なりますので、繋げないほうがいいです。

M5StickCは実際には5V信号を入力しても、壊れない気がしますがデータシート上は5Vは許容していませんので、電圧変換の回路やICを間にいれて接続する必要があります。

物によっては12Vとかを流してくるシリアルもあるので、安易に繋ぐのはやめましょう。

速度差異

お互いに同じ速度を設定しないと通信ができません。複数機材を接続するのであれば、お互いに同じ値を設定するればよいのですが、LTEモジュールなどの通信機器と接続するためには、相手側の初期値で接続してから、変更するなどをする必要があるので注意しましょう。

エラー処理

基本的に1バイト単位で送受信する機能しかありませんので、途中でエラーが発生したり、相手側が受信を失敗しているかを確認する方法が標準では提供されていません。

エラー処理は自分で考慮する必要があるので注意して使ってください。

まとめ

UART自体は簡単に利用することができます。ただし長い距離を通信させることや、高速で通信する場合にはエラー処理などがちょっと不安ですので、注意が必要そうです。

パソコンと接続するのであればBluetoothSerialを使うことで、速度とエラー処理などの心配がほぼ無くなるのでおすすめです。

コメント

  1. にがうりジュース より:

    こんにちは。いつも大変丁寧で参考になる記事をありがとうございます。M5StickCについて色々と学びたいと思っていて、頻繁に拝見しています。
    現在、以下のKT403AというGroveターミナルを持つMP3 playerを、M5StickCで操作したいと思っています。他のArduinoでは問題なく動かせますが、M5StickCのGroveを使った動作がうまくいきません。素人の私ですが、何かヒントでも頂けるとありがたいです。
    http://wiki.seeedstudio.com/Grove-MP3_v2.0/
    よろしくお願いします

  2. にがうりジュース より:

    返事が遅くてすみません。以下のSampleをArduino Nanoで試したところ問題なく動作しました。

    https://github.com/Seeed-Studio/Seeed_Serial_MP3_Player/tree/master/examples/KT403A_Terminal_player

    これを使ってM5StickCでも動かせるように、以下のようなことを試しました。ちなみにM5StickCとPCとはUSBで、MP3 playerとはGroveのケーブルで接続しています。まだプログラミングの知識が乏しく、見よう見まね、かつ力づくでやっていますので、その点はご容赦ください。
    1)36行目の下に#include 追加
    2)40行目のSoftwareSerialは使い方がよくわからないので、とりあえずGroveの(32, 33)に変更。
    まずはこれだけでどうなるか見ましたが、以下のエラーが出ます。
    ‘ShowSerial’ was not declared in this scope
    これは38行目以降の#ifdef …という部分がすべてパスしてしまって、ShowSerialが定義されていないのでは、と思いました。#ifdefの部分にはAVR, ARDUINO_SAMD_VARIANT_COMPLIANCE, ARDUINO_ARCH_STM32F4の3種類がありますが、M5StickCはこれらに該当しないのでしょうか。それとも何か別の理由でしょうか。
    そもそもSoftwareSerialを使わなくても、ここのトピックにあるSerial2を使えばできるような気もしますが、特にSetupまでの部分をどう改良すればよいかがわかりません。
    よろしくお願いします。

    • たなかまさゆき より:

      現物がないので、コンパイル通すところまでは試しました。
      宣言部分は3行追加です。ShowSerialはSerial1かSerial2のどっちでも大丈夫ですが、ブログで紹介しているGrove側は2にとりあえずしました。
      Serial1もSerial2も中身はHardwareSerialなので、LT403Aクラスの宣言も追加。

      初期化部分はCOMSerialはM5.begin();で初期化されるので入れ替える。
      ShowSerial.begin()の初期化はSerial2の初期化なので転送速度だけはサンプルに合わせました。

      #define COMSerial Serial
      #define ShowSerial Serial2
      KT403A < HardwareSerial > Mp3Player;

      void setup() {
      // put your setup code here, to run once:
      M5.begin();
      ShowSerial.begin(9600, SERIAL_8N1, 32, 33);// Grove

      while (!ShowSerial);
      while (!COMSerial);
      Mp3Player.init(COMSerial);
      printMenu();
      }
      (以下略)

      • たなかまさゆき より:

        <>が消えてしまうのですね、、、
        KT403A< HardwareSerial > Mp3Player;

        上記のHardwareSerialが消えていますので、スケッチの上に方に同じ行があるのでそこからコピーしてきてください。

  3. にがうりジュース より:

    1)36行目の下に#include 追加 —> #include でした。

  4. にがうりジュース より:

    iPhoneではカッコが見えないので、へんなメッセージを送ってしまいました。失礼しました。

  5. にがうりジュース より:

    何度もすみません。結局伝えたかったのは、#include (小なり) M5StickC.h (大なり)ということでした。

  6. にがうりジュース より:

    ありがとうございます。余計なものを削除して以下のようなスケッチで試したところ(Setupまでの記載)、エラーもなく書き込むことができました(これでも自分にとっては大きな進歩です)。しかし音が鳴らず、シリアルモニタを見ると、Menuは出ているので各Serialは動作を始めているようなのですが、文字化けや長いエラーメッセージが出たりなど、挙動不審です。

    スケッチの最初の部分

    #include “KT403A_Player.h”
    #include< M5StickC.h >

    #define COMSerial Serial
    #define ShowSerial Serial2
    KT403A< HardwareSerial > Mp3Player;

    static uint8_t recv_cmd[8] = {};

    void setup() {
    // put your setup code here, to run once:

    M5.begin();
    ShowSerial.begin(9600, SERIAL_8N1, 32, 33);// Grove

    while (!ShowSerial);
    while (!COMSerial);
    Mp3Player.init(COMSerial);
    printMenu();
    }

    シリアルモニタ(115.2kbps設定)のエラーメッセージの例

    23:38:33.892 -> Guru Meditation Error: Core 1 panic’ed (LoadProhibited). Exception was unhandled.
    23:38:33.927 -> Core 1 register dump:
    23:38:33.927 -> PC : 0x400d0f49 PS : 0x00060f30 A0 : 0x800d1198 A1 : 0x3ffb1f30
    23:38:33.927 -> A2 : 0x3ffc0154 A3 : 0x00000004 A4 : 0x00000000 A5 : 0x00000014
    23:38:33.927 -> A6 : 0x00000006 A7 : 0x00000008 A8 : 0x800d54fc A9 : 0x3ffb1f00
    23:38:33.927 -> A10 : 0xffffffff A11 : 0x00000000 A12 : 0x3ffb8274 A13 : 0x00000000
    23:38:33.961 -> A14 : 0x3ffb8280 A15 : 0x00000000 SAR : 0x0000000a EXCCAUSE: 0x0000001c
    23:38:33.961 -> EXCVADDR: 0xffffffff LBEG : 0x4000c28c LEND : 0x4000c296 LCOUNT : 0x00000000
    23:38:33.961 ->
    23:38:33.961 -> Backtrace: 0x400d0f49:0x3ffb1f30 0x400d1195:0x3ffb1f70 0x400d0eba:0x3ffb1f90 0x400d4e81:0x3ffb1fb0 0x400889d9:0x3ffb1fd0
    23:38:33.961 ->
    23:38:33.961 -> Rebooting…
    23:38:34.171 -> M5StickC initializing…OK

    もしかしてどこかのSerial通信がうまくいっていないように見えますが、もう少しでまともに動かせるような気がします。お手数ではありますが、もしもアドバイスがあればお願いたします。

    • たなかまさゆき より:

      M5StickC.hを一番上に移動してみてもらえますか?
      ここの中でいろいろな宣言がされているはずです
      初期化より前にエラーがでているのが怪しいです

  7. にがうりジュース より:

    一番上に持っていっても状況は変わりませんでした。また、Serial1(G0とG26)に替えても変わらずでした。うまくいったらM5StickCのボタンを使って小さなMP3 Playerを作ってみたいと思っていましたが、なかなか思うようにはさせてくれませんね。
    3/2にはMenuは出てくる、と書きましたが、結局書き込んだ後にシリアルモニタを立ち上げても、出てくるはずのMenu(サンプルプログラムの一番下の関数)が現れず、一度Returnキーを押すと現れることが分かりました。PCとの通信が滞っているのでしょうか。もう少しできそうなことを試そうと思います。

    • たなかまさゆき より:

      すみません、よく確認したらCOMSerialがMp3につながっている方でした
      ShowSerial は使っていない?
      下の方の表示はSerial使っていますし、、、
      ===============================
      #define COMSerial Serial2
      #define ShowSerial Serial
      KT403A < HardwareSerial > Mp3Player;

      void setup() {
      // put your setup code here, to run once:
      M5.begin();
      COMSerial.begin(9600, SERIAL_8N1, 32, 33);// Grove

  8. にがうりジュース より:

    度々ありがとうございます。上記を試しましたが、やはり動作しません。念のため、CardKeyboardでGroveの接続を確認したところ、(IICですが)やり取りはしているようです。Serial2をSerial1に替えて試しましたが、状況は変わらずでした。Hardwareは壊れていないと思うのですが、通常のESP32やArduinoと何らかの作法が異なるのでしょうか。

    • たなかまさゆき より:

      どんな感じで動いていませんか?
      メニューが動いていない感じでした?

      COMSerial.begin(9600, SERIAL_8N1, 32, 33);// Grove

      もしかしたらTXとRXが逆になっている可能性があるので、32と33を逆にしてみるといいのかもしれません

  9. にがうりジュース より:

    いつもありがとうございます。おっしゃる通り、32と33を入れ替えたら通信できました。デバッグの基本ですね。もっと早く気付くべきだったと反省しております。
    ちなみにSrial2をSerial1に替えた場合でも、(26, 0)の順番にすれば通信できることを確認しました。
    ただしPCでSerialのWindowを立ち上げてもMenuが出て来ず、一度Returnを叩く必要があります(Arduinoで試した場合には、きれいに現れたと思います)。またその場合でも文字化けらしきものが一瞬見えます。ただしまだ詳しく解析できていないので、これから試行錯誤しようと考えています。
    いずれにしても、これで次のStepに進めそうです。的確なアドバイス、ありがとうございました。