M5Stamp C3 Mateを試してみました【Arduino使用】

Raspberry Pi/電子工作
スポンサーリンク

こんにちは、あろっちです。

M5Stamp C3 Mateを入手したので試してみました。

参考URL:

m5-docs
The reference docs for M5Stack products. Quick start, get the detailed information or instructions such as IDE,UIFLOW,Arduino. The tutorials for M5Burner, Firmw...

M5Stamp C3U Mateも記事にしました。スケッチは本記事に掲載したもので試しています。

Seeed Studio XIAO ESP32C3(参考サイト)

本記事では、プログラムにArduino(C/C++)を使っています。

ピン配列

LEDG2
ボタンG3

I2C

デフォルトのピン番号を記載します。

ピンGPIO
SDA8
SCL9

Arduinoでは、SDA、SCLをWire.begin(SDA, SCL);という形式で任意のピン番号に変更できます。
以下は、SDA=0、SCL=1に変更する例です。

Wire.begin(0, 1);

SPI

デフォルトのピン番号を記載します。

ピンGPIO
SCK4
MISO5
MOSI6
SS7

Arduinoでは、SPI.begin(SCK, MISO, MOSI, SS);という形式で任意のピン番号に変更できます。

この動画はSPIのTFTディスプレイ ILI9341の表示例です。

LovyanGFX 1.1.5 コンパイルエラー対処方法

ESP32-C3ボードでBus_SPI.cppのコンパイルエラーが発生するようです。

1.1.5以外のバージョンではコンパイルエラーは発生しないようです。

応急処置的な対処法を記載します。

修正ファイル

[スケッチ保存フォルダ]/libraries/LovyanGFX/src/lgfx/v1/platforms/esp32/Bus_SPI.cpp

修正前

#include <soc/dport_reg.h>

修正方法は以下の通りです。

#if __has_include (<soc/dport_reg.h>)
#include <soc/dport_reg.h>
#endif

Ethernetモジュールの使用

SPI接続のEthernetモジュール(W5500)も動作しました。

W5500 Liteでも動作確認できました。

スケッチ等は以下の記事をご覧ください。

回路図

内容物

1 x M5Stamp C3
1 x 耐熱ステッカー
1 x M2 六角レンチ
2 x HY2.0-4Pコネクタ(90° Grove互換コネクタ)
1 x ピンヘッダ 10ピン
1 x ピンソケット 10ピン
1 x ピンヘッダ 12ピン
1 x ピンソケット 12ピン

技適マークは添付のステッカーに印刷されています。

最初は、ケースがついた状態で、ケースにMACアドレスが記載されたシールが貼り付けられていました。

MACアドレスのシールをはがした状態

ケースを取り外すにはMACアドレスのシールをはがし、中央のネジを付属の六角レンチで外します。

ピンヘッダー装着後

USBシリアルについて

M5Stamp C3はシリアル変換IC CH9102Fを搭載しています。

Mac シリアルドライバ

M5Stackのサイトから、CH9102_VCP_SER_MacOS v1.7をダウンロードできます。

https://docs.m5stack.com/en/download

※2022年6月26日
v1.7以前のドライバでは環境によってエラーが発生してインストールできないという不具合らしき現象が確認されていましたが、v1.7では解消されていることを確認しました。

以下は、上述の不具合の代替案として本記事執筆時に記述した情報となりますが、参考情報として残しておきます。

以下からWCH公式最新のUSBシリアルドライバをダウンロードできます。

GitHub

https://github.com/WCHSoftGroup/ch34xser_macos

Windows シリアルドライバ

M5Stackサイトから、CH9102_VCP_SER_Windowsをダウンロードできます。
ダブルクリックでインストーラが起動し、[INSTALL]をクリックするとドライバがインストールできます。

https://docs.m5stack.com/en/download

Arduinoボード設定

M5Stamp C3では、次のボードが使用できます。

  • M5Stackボード STAMP-C3
  • ESP32ボード ESP32C3 Dev Module

本記事に掲載のスケッチは、いずれのボードでも動作することを確認しました。

M5Stackボード STAMP-C3を使用する場合

Arduino IDEにM5Stackボードを追加しておきます。

追加方法については、以下の記事をご参照ください。

  • ボードインストール

[ツール] > [ボード] > [ボードマネージャ]からM5Stackボードをインストールします。

  • ボードの選択

[ツール] > [ボード] > [M5Stack Arduino] > [STAMP-C3]を選択します。

  • シリアルポートの選択

シリアルポートは、Macの場合、/dev/cu.wchusbserial*から始まるポートを選択します。

Windowsの場合は、COM*で認識されますので、それを選択します。

ESP32ボード ESP32C3 Dev Moduleを使用する場合

Arduino IDEにESP32ボードを追加しておきます。安定リリース版でOKです。

追加方法については、以下の記事をご参照ください。

  • ボードインストール

[ツール] > [ボード] > [ボードマネージャ]からESP32ボード(Ver2.0.0以降)をインストールします。

  • ボードの選択

[ツール] > [ボード] > [ESP32 Arduino] > [ESP32C3 Dev Module]を選択します。

  • シリアルポートの選択

シリアルポートは、Macの場合、/dev/cu.wchusbserial*から始まるポートを選択します。

Windowsの場合は、COM*で認識されますので、それを選択します。

書き込みモード(Download Boot)

G9とGNDを短絡することで、書き込みモード(Download Boot)にできるようです。

G9とGNDをジャンパーピンで短絡した状態

シリアルモニターには以下のように表示されます。
※確認できない場合はリセットボタンを押してみてください。

ESP32-C3 Technical Reference Manual(7.2 Boot Mode Control)および回路図を参考にしました。

書き込みがうまくいかない場合に試すとよいかと思います。

以下はピンヘッダー未装着のときにスルーホール用テストワイヤで試した画像です。

以下のようにUSBを接続しArduino IDE(1.8.15 Mac版)からスケッチが書き込めるのを確認しました。

スルーホール用テストワイヤ(TT-200)は秋月電子通商さんで購入しました

参考(pdf):

ESP32-C3 Technical Reference Manual
ESP32-C3 Datasheet

サンプルプログラム(Arduinoスケッチ)

Lチカ

カラフルにLチカしてみました。

Adafruit NeoPixelライブラリを使用しています。

#include <Adafruit_NeoPixel.h>

#define LED_PIN 2

#define COLOR_REPEAT 2

// create a pixel strand with 1 pixel on PIN_NEOPIXEL
Adafruit_NeoPixel pixels(1, LED_PIN);

uint8_t color = 0, count = 0;
uint32_t colors[] = {pixels.Color(125, 0, 0), pixels.Color(0, 125, 0), pixels.Color(0, 0, 125), pixels.Color(125, 125, 125)};
const uint8_t COLORS_LEN = (uint8_t)(sizeof(colors) / sizeof(colors[0]));

void setup() {
  pixels.begin();  // initialize the pixel
}

void loop() {
  pixels.setPixelColor(0, colors[color]);
  pixels.show();
  
  delay(1000);
  
  pixels.clear();
  pixels.show();
  
  delay(1000);
  
  count++;

  if(count >= COLOR_REPEAT) {
    count = 0;
    color++;
    if(color >= COLORS_LEN) {
      color = 0;
    }
  }
}

レインボー

#include <Adafruit_NeoPixel.h>

#define LED_PIN 2
#define MAX_BRIGHTNESS 255

Adafruit_NeoPixel pixels(1, LED_PIN);

int rgbValues[] = {MAX_BRIGHTNESS, 0, 0}; // 0=Red, 1=Green and 2=Blue
int upIndex = 0, downIndex = 1;

void setup()
{
  pixels.begin(); // initialize the pixel
}

void loop()
{
  rgbValues[upIndex] += 1;
  rgbValues[downIndex] -= 1;

  if (rgbValues[upIndex] > MAX_BRIGHTNESS)
  {
    rgbValues[upIndex] = MAX_BRIGHTNESS;
    upIndex = upIndex + 1;

    if (upIndex > 2)
    {
      upIndex = 0;
    }
  }

  if (rgbValues[downIndex] < 0)
  {
    rgbValues[downIndex] = 0;
    downIndex = downIndex + 1;

    if (downIndex > 2)
    {
      downIndex = 0;
    }
  }

  pixels.setPixelColor(0, pixels.Color(MAX_BRIGHTNESS - rgbValues[0], MAX_BRIGHTNESS - rgbValues[1], MAX_BRIGHTNESS - rgbValues[2]));
  pixels.show();

  delay(5);
}

一撃入魂

内蔵ボタンを押すと、1/2でレインボーに光るというスケッチです。

ルール
チャンスは1回、ボタン押下時に抽選

ということで、今回のスケッチは、あえてボタン入力の受付は1回のみとしてみました。
再チャレンジしたい場合は、リセットボタンを押してみてください。

RGB LEDの状態について

ボタン入力受付は、白点滅
抽選演出は、赤で表現
抽選結果は、当選時 > レインボー、落選時 > フェードアウト

#include <Adafruit_NeoPixel.h>

#define BTN 3

#define LED_PIN 2

#define TOGGLE_PERIOD (1000u)

#define LED_STAND_BY_COLOR 0xffffff
#define LED_READY_COLOR 0xff0000

Adafruit_NeoPixel pixels(1, LED_PIN);

uint8_t rgbValues[7][3] = {
    {255, 0, 0}, {255, 165, 0}, {255, 255, 0}, {0, 128, 0}, {0, 255, 255}, {0, 0, 255}, {128, 0, 128}};

int16_t randNumber;

int16_t brightness = 0;
int16_t fadeAmount = 10;

void toggleLED_nb(void)
{
  static bool toggle = true;
  static auto lastToggle = millis(); // saved between calls
  auto now = millis();

  if (now - lastToggle > TOGGLE_PERIOD)
  {
    if (toggle)
    {
      pixels.setPixelColor(0, LED_STAND_BY_COLOR);
      pixels.setBrightness(18);
      pixels.show();
    }
    else
    {
      pixels.clear();
      pixels.show();
    }
    toggle = !toggle;
    lastToggle = now;
  }
}

void readyLED()
{
  for (uint8_t i = 0; i < 3; i++)
  {
    brightness = 0;
    pixels.clear();
    pixels.show();
    delay(100);

    for (uint8_t j = 0; j < (100 / fadeAmount); j++)
    {
      pixels.setPixelColor(0, LED_READY_COLOR);
      pixels.setBrightness(brightness);
      pixels.show();

      brightness = brightness + fadeAmount;
      delay(80);
    }

    delay(1000);
  }
  brightness = 150;
  pixels.setPixelColor(0, LED_READY_COLOR);
  pixels.setBrightness(brightness);
  pixels.show();
  delay(3000);
}

void successLED()
{
  for (uint8_t i = 0; i < 8; i++)
  {
    for (uint8_t j = 0; j < 7; j++)
    {
      pixels.setPixelColor(0, pixels.Color(rgbValues[j][0], rgbValues[j][1], rgbValues[j][2]));
      pixels.setBrightness(18);
      pixels.show();
      delay(50);
    }
  }
}

void failureLED()
{
  while (0 < brightness)
  {
    pixels.setPixelColor(0, LED_READY_COLOR);
    pixels.setBrightness(brightness);
    pixels.show();

    brightness = brightness - fadeAmount;
    delay(80);
  }

  pixels.clear();
  pixels.show();
  delay(200);
  pixels.setPixelColor(0, LED_READY_COLOR);
  pixels.setBrightness(10);
  pixels.show();
  delay(150);
}

void setup()
{
  pinMode(BTN, INPUT_PULLUP);

  pixels.begin();
  while (digitalRead(BTN) == HIGH)
  {
    toggleLED_nb();
    delay(10);
  }

  // 抽選
  randomSeed(analogRead(0));
  randNumber = (int16_t)random(0xffff + 1);

  readyLED();

  if (randNumber < 0)
  {
    // おめでとう
    successLED();
  }
  else
  {
    // 残念
    failureLED();
  }

  pixels.clear();
  pixels.show();
}

void loop()
{
}

HelloServer

[スケッチ例] > [WebServer] > [HelloServer]のコードをM5Stamp C3用に修正したものです。
LEDにNeoPixelを使うように修正しています。

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Adafruit_NeoPixel.h>

const char* ssid = "........";
const char* password = "........";

WebServer server(80);

#define LED_PIN 2
Adafruit_NeoPixel pixels(1, LED_PIN);

void led_on() {
  pixels.setPixelColor(0, pixels.Color(125, 0, 0));
  pixels.show();
}

void led_off() {
  pixels.clear();
  pixels.show();  
}

void handleRoot() {
  led_on();
  server.send(200, "text/plain", "hello from esp32!");
  led_off();
}

void handleNotFound() {
  led_on();
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
  led_off();
}

void setup(void) {
  pixels.begin();
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp32")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);

  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });

  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  server.handleClient();
  delay(2);//allow the cpu to switch to other tasks
}

スケッチを書き込む場合、ssidとpasswordを接続するWi-Fiの情報に書き換えてください。

シリアルモニターを開くと、HelloServerのIPアドレスが表示されますので、ブラウザからアクセスします。

以下のように表示されます。

http://IPアドレス

http://IPアドレス/inline

http://IPアドレス/適当な文字列

BLE UART

BLEの例を紹介します。
[スケッチ例] > [ESP32 BLE Arduino] > [BLE_uart]をペリフェラル(M5Stamp C3)のシリアルを通してBLEクライアントにメッセージ通知(notify)できるように改修してみました。

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;

#define TXVALUE_SIZE 64
uint8_t txValue[TXVALUE_SIZE];
size_t size;

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID           "6b02758a-24f2-11ed-861d-0242ac120002" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6b027832-24f2-11ed-861d-0242ac120002"
#define CHARACTERISTIC_UUID_TX "6b02797c-24f2-11ed-861d-0242ac120002"

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string rxValue = pCharacteristic->getValue();

      if (rxValue.length() > 0) {
        Serial.println("*********");
        Serial.print("Received Value: ");
        for (int i = 0; i < rxValue.length(); i++)
          Serial.print(rxValue[i]);

        Serial.println();
        Serial.println("*********");
      }
    }
};


void setup() {
  Serial.begin(115200);

  // Create the BLE Device
  BLEDevice::init("ESP32-C3 UART Service");

  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create a BLE Characteristic
  pTxCharacteristic = pService->createCharacteristic(
										CHARACTERISTIC_UUID_TX,
										BLECharacteristic::PROPERTY_NOTIFY
									);
                      
  pTxCharacteristic->addDescriptor(new BLE2902());

  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
											 CHARACTERISTIC_UUID_RX,
											BLECharacteristic::PROPERTY_WRITE
										);

  pRxCharacteristic->setCallbacks(new MyCallbacks());

  // Start the service
  pService->start();

  // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("Waiting a client connection to notify...");
}

void loop() {

    if (deviceConnected) {
      while (Serial.available())
      {
        size = Serial.readBytes(txValue, TXVALUE_SIZE);
        pTxCharacteristic->setValue(txValue, size);
        pTxCharacteristic->notify();
      }
      delay(10); // bluetooth stack will go into congestion, if too many packets are sent
    }

    // disconnecting
    if (!deviceConnected && oldDeviceConnected) {
        delay(500); // give the bluetooth stack the chance to get things ready
        pServer->startAdvertising(); // restart advertising
        Serial.println("start advertising");
        oldDeviceConnected = deviceConnected;
    }
    // connecting
    if (deviceConnected && !oldDeviceConnected) {
		// do stuff here on connecting
        oldDeviceConnected = deviceConnected;
    }
}

動作テスト

BLEクライアントの通信アプリケーションとして、ここでは「LightBlue」を使用します。
「LightBlue」はAndroidやiOSのアプリです。操作性(画面)はAndroidとiOSで若干異なります。

ここではAndroid版を使用します。
iOS版の操作方法は後述の「ESP32_BleSerialライブラリを使う」の動作テストを参考にしてください。

ペリフェラル側(PC)は「Arduino IDE」のシリアルモニターを使用します。

BLE接続(LightBlue for Android)

はじめにLightBlueからM5Stamp C3(ESP32-C3 UART Service)にBLE接続します。

接続に成功すると次のような画面になります。

ペリフェラル(PC) → BLEクライアント (Notify)

LightBlueのNotifyをタップします。

Data formatをUTF-8 Stringに変更して、SUBSCRIBEをタップします。

Arduino IDEのシリアルモニターからメッセージを送信します。

メッセージ「あろしーど」を送信

LightBlueに受信データ(今回は「あろしーど」)が表示されます。

BLEクライアント → ペリフェラル(PC) (Writable)

LightBlueのWritableをタップします。

Data formatをUTF-8 Stringにします。

WRITTEN VALUESにメッセージを入力し「WRITE」をタップします。

メッセージ「あろしーど」をWRITE(送信)

Arduino IDEのシリアルモニターにLightBlueから送信したメッセージが表示されます。

ESP32_BleSerialライブラリを使う

上記のBLE UARTと同じ様な処理をESP32_BleSerialライブラリで実装してみました。

ESP32_BleSerialライブラリはArduino IDEのライブラリマネージャーから検索してインストールできます。

ライブラリ修正

ライブラリのソースファイル場所Arduinoスケッチ保存フォルダ内のlibraries/ESP32_BleSerial/src
修正ファイルBleSerial.cpp

void BleSerial::SetupSerialService() 関数内の以下の箇所

	TxCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED);
	RxCharacteristic->setAccessPermissions(ESP_GATT_PERM_WRITE_ENCRYPTED);

こちらを以下のように#ifdef#endifで囲むよう修正します。

#ifdef CONFIG_IDF_TARGET_ESP32
	TxCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED);
	RxCharacteristic->setAccessPermissions(ESP_GATT_PERM_WRITE_ENCRYPTED);
#endif

この修正はESP32-C3やESP32-S3の場合、この箇所を有効にした状態だとうまく動作しないために実施する処置になります。

スケッチ

パターン1

シリアルの送受信(Read, Write)にバッファを使うパターン

#include <BleSerial.h>

BleSerial SerialBT;

#define DEVICE_NAME "ESP32 BLE Serial"

const int BUFFER_SIZE = 8192;

uint8_t bleReadBuffer[BUFFER_SIZE];
uint8_t serialReadBuffer[BUFFER_SIZE];

void setup() {
  Serial.begin(115200);

  SerialBT.begin(DEVICE_NAME); //Bluetooth device name
  SerialBT.setTimeout(10);
  
  Serial.printf("The device with name \"%s\" is started.\nNow you can pair it with Bluetooth!\n", DEVICE_NAME);
}

void loop() {
  if (Serial.available()) {
    auto count = Serial.readBytes(serialReadBuffer, BUFFER_SIZE);
    SerialBT.write(serialReadBuffer, count);
  }
  if (SerialBT.available()) {
    auto count = SerialBT.readBytes(bleReadBuffer, BUFFER_SIZE);
    Serial.write(bleReadBuffer, count);
  }
  yield();
}

パターン2

シリアルの送受信(Read, Write)用バッファを使わずそのまま送信するパターン

#include <BleSerial.h>

BleSerial SerialBT;

#define DEVICE_NAME "ESP32 BLE Serial"

void setup() {
  Serial.begin(115200);

  SerialBT.begin(DEVICE_NAME);  //Bluetooth device name
  SerialBT.setTimeout(10);

  Serial.printf("The device with name \"%s\" is started.\nNow you can pair it with Bluetooth!\n", DEVICE_NAME);
}

void loop() {
  static bool serialBTWrote = false;

  if (Serial.available()) {
    SerialBT.write(Serial.read());
    if (!serialBTWrote) {
      serialBTWrote = true;
    }
  } else if (!Serial.available() && serialBTWrote) {
    SerialBT.flush();
    serialBTWrote = false;
  }
  if (SerialBT.available()) {
    Serial.write(SerialBT.read());
  }
  yield();
}

パターン1、パターン2いずれもBLE UARTと同じような動作をしますが、ソースコードはよりシンプルに実装できているかと思います。

動作テスト

BLEクライアントの通信アプリケーションとして、ここでは「LightBlue」を使用します。
「LightBlue」はAndroidやiOSのアプリです。操作性(画面)はAndroidとiOSで若干異なります。
今回はiOS版で解説していきます。Androidをご使用の場合「BLE UART」の動作テストを参考にしてください。

ペリフェラル側(PC)は「Arduino IDE」のシリアルモニターを使用します。

BLE接続(LightBlue for iOS)

LightBlueからM5Stamp C3(ESP32 BLE Serial)にBLE接続します。

接続に成功すると次のような画面になります。

ペリフェラル(PC) → BLEクライアント (Notify)

LightBlueのRead Notifyをタップします。

Data formatをUTF-8 Stringに変更します。

「Listen for notifications」をタップします。

Arduino IDEのシリアルモニターからメッセージを送信します。

メッセージ「あろしーど」を送信

LightBlueに受信データ(今回は「あろしーど」)が表示されます。

BLEクライアント → ペリフェラル(PC) (Writable)

LightBlueのWriteをタップします。

Data formatをUTF-8 Stringにします。

「Write new value」をタップします。

メッセージ「あろしーど」をWrite(送信)
メッセージ送信後画面

Arduino IDEのシリアルモニターにLightBlueから送信したメッセージが表示されます。

まとめ

小さくて高機能。USBシリアルポート搭載で、すぐ試せるというのが嬉しいですね。

お試し程度でしか触れていませんが、ご参考になればと思います。

当ブログのマイコン記事です。ぜひご覧ください。

この記事を書いた人
あろっち

元ITエンジニア
エンジニア時代は大手企業などでSE・プログラマを経験してきました。

当ブログでは、経験や日々の暮らしの中で、興味があること、役に立ちそうなこと、気になったことを発信していきます。

IT関係(技術的な内容もあります) / スマホ・PC / ガジェット / 生活に役立ちそうなこと ... etc

あろっちをフォローする
Raspberry Pi/電子工作IT
あろっちをフォローする
あろしーど

コメント

タイトルとURLをコピーしました