3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

M5NanoC6をSPRESENSEの通信モジュールに

3
Last updated at Posted at 2025-12-10

SPRESENSEには2つのシリアルポート (UART1, UART2) があり、Arduino環境ではSerialおよびSerial2として利用できます。このうち Serial (UART1) はUSBシリアル通信専用です。いっぽう Serial2 (UART2) は PIN_D00, PIN_D01 に外部ハードウェアを接続できます。

クレイン電子のBLE1507 (BLE for Spresense)や、スイッチサイエンスのSPRESENSE用 BLEベースボードは、このUART2にBLEチップが接続されています。

もう一つ別のデバイス、例えばシリアルサーボと通信をしたいときは、SoftwareSerialを使うしかありません。もしシリアルサーボをUART2に接続する必要がある場合、前述の BLE1507 や BLE ベースボードは利用できなくなります。

M5NanoC6でBLEシリアル通信

M5NanoC6は、安価で超小型のIoTマイコンモジュールです。BLEやWiFiなどの通信が可能です。このM5NanoC6をSPRESENSEの通信モジュールとして使おうと思います。

SPRESENSEとM5NanoC6をシリアル通信で接続し、M5NanoC6には送受信データへBLEサービスにブリッジさせます。BLEサービスは NUS (Nordic UART Service) を使用します。これは前述のBLE1507やBLEベースボードでも使われているものです。(BLE1507ではファームウェア書き換えが必要。)

SPRESENSEとM5NanoC6の接続

SPRESENSEのSoftwareSerialPIN_D02PIN_D04で使用するものとして、SPRESENSEとM5NanoC6を下表のように結線します。(今回はSoftwareSerialを使いますが、もちろんSerial2に接続するのもアリです。)

M5NanoC6 (Groveコネクタ) SPRESENSE (拡張ボード)
G1 D04
G2 D02
5V Vout 5V
G GND

IMG_6531.JPG

M5NanoC6のファームウェア

簡易的なUART-NUSブリッジを実装しました。

長いコード (NUS_NanoC6.ino) を見る
NUS_NanoC6.ino
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h> // Notifyに必要

// デバッグ用マクロ
#define DEBUG_LOG
#if defined(DEBUG_LOG)
  #define LOG_BEGIN(x)      Serial.begin(x)
  #define LOG_PRINTLN(x)    Serial.println(x)
  #define LOG_PRINT(x)      Serial.print(x)
#else
  #define LOG_BEGIN(x)
  #define LOG_PRINTLN(x)
  #define LOG_PRINT(x)
#endif

// シリアルポートのピン定義 (Groveコネクタ)
#define PIN_RX 1
#define PIN_TX 2

// Nordic UART Service (NUS) のUUID
#define SERVICE_UUID  "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHAR_UUID_RX  "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" // M5からみたRX
#define CHAR_UUID_TX  "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" // M5がらみたTX

// キャラクタリスティック
BLECharacteristic *pCharRX; // M5からみたRX
BLECharacteristic *pCharTX; // M5がらみたTX

// 接続状態管理用フラグ
bool isConnected = false;
bool wasConnected = false;

// 接続時/切断時のコールバック
class MyServerCallbacks: public BLEServerCallbacks {
    // 接続時
    void onConnect(BLEServer* pServer) {
      isConnected = true;
      LOG_PRINTLN("Connected");
    }
    // 切断時
    void onDisconnect(BLEServer* pServer) {
      isConnected = false;
      LOG_PRINTLN("Disconnected");
    }
};

// データ受信時のコールバック
class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharRX) {
      String rxValue = pCharRX->getValue();

      if (rxValue.length() > 0) {
        LOG_PRINT("Received: ");
        LOG_PRINTLN(rxValue);
        Serial1.print(rxValue); 
      }
    }
};

// 初期化
void setup()
{
  // デバッグ用USBシリアル
  LOG_BEGIN(115200);

  // ハードウェアシリアルポート (Groveコネクタ)
  Serial1.begin(19200, SERIAL_8N1, PIN_RX, PIN_TX);

  // BLEデバイスを初期化
  BLEDevice::init("M5 NUS");

  // GATTサーバー
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // NUSサービス
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // TXキャラクタリスティック (Notify : M5 -> Central)
  pCharTX = 
      pService->createCharacteristic(
        CHAR_UUID_TX,
        BLECharacteristic::PROPERTY_NOTIFY
      );
  pCharTX->addDescriptor(new BLE2902()); // Notifyを有効にするためのデスクリプタ追加

  // RXキャラクタリスティック (Write : Central -> M5)
  pCharRX = 
      pService->createCharacteristic(
        CHAR_UUID_RX,
        BLECharacteristic::PROPERTY_WRITE
      );
  pCharRX->setCallbacks(new MyCallbacks());

  // サービスを開始
  pService->start();

  // アドバタイジングの設定
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x0); // iOSとの接続のために推奨
  
  // アドバタイジング周期の設定 (100ms)
  uint16_t advInterval = 160; // 100ms / 0.625ms = 160
  pAdvertising->setMinInterval(advInterval);
  pAdvertising->setMaxInterval(advInterval);

  // アドバタイジングを開始
  BLEDevice::startAdvertising();
}

// メインループ
void loop()
{
  // 接続状態の変化をチェック
  if (isConnected && !wasConnected) {
    wasConnected = isConnected;
  }
  if (!isConnected && wasConnected) {
      wasConnected = isConnected;
      // 切断されたらアドバタイジングを再開
      BLEDevice::startAdvertising(); 
      LOG_PRINTLN("Restart Advertising");
  }

  // シリアル受信データをBLE送信
  if (isConnected) {
    int len = Serial1.available();
    if (len > 0) {
      if(len > 255) len = 255;
      static char rxBuffer[256];
      int len2 = Serial1.readBytes(rxBuffer, len);
      rxBuffer[len2] = '\0';
      String txString = String(rxBuffer);
      // String txString = Serial.readString(); // <-- こっちだと遅延が大きい
      pCharTX->setValue(txString);
      pCharTX->notify();
    }
  }
  delay(5);
}

https://gist.github.com/lipoyang/0ac910bb2754f7988fee5cf77439d82a

SPRESENSEの動作確認用スケッチ

test.ino
#include <Arduino.h>
#include <SoftwareSerial.h>

#define PIN_RX 2
#define PIN_TX 4

SoftwareSerial softSerial(PIN_RX, PIN_TX);

void setup()
{
  Serial.begin(115200);
  softSerial.begin(19200);
}

void loop()
{
  while (softSerial.available())
  {
    int c = softSerial.read();
    Serial.write(c);
  }

  while (Serial.available())
  {
    int c = Serial.read();
    softSerial.write(c);
  } 
}

動作確認用の通信相手 (Webアプリ)

ブラウザ上でNUSの送受信を確認できる簡単なアプリを用意しました。Web Bluetooth API に対応したChromeなどのブラウザで利用できます。(iOSのChromeは非対応)

結果

送受信とも動作確認できました。

補足

じつはSPRESENSE (というかCXD5602マイコン) にはもう一つ UART0 というシリアルポートがあるようです。ただしUART0は通常の使い方は出来ないようです。『CXD5602 User Manual』を読んでも詳しくは分かりませんでした。

ハードウェア名 Arduino名 備考
UART0 なし ふつうは使えない
UART1 Serial USBシリアル通信専用
UART2 Serial2 D00D01
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?