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のSoftwareSerialをPIN_D02とPIN_D04で使用するものとして、SPRESENSEとM5NanoC6を下表のように結線します。(今回はSoftwareSerialを使いますが、もちろんSerial2に接続するのもアリです。)
| M5NanoC6 (Groveコネクタ) | SPRESENSE (拡張ボード) |
|---|---|
| G1 | D04 |
| G2 | D02 |
| 5V | Vout 5V |
| G | GND |
M5NanoC6のファームウェア
簡易的なUART-NUSブリッジを実装しました。
長いコード (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の動作確認用スケッチ
#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 |
D00とD01
|
