両刀遣い...ぃゃぁ
はじめに
- 以前、applewatchの心拍数をサイクルコンピューターに飛ばすこと考え、中継としてM5StickCPlus2(以下、cp2)を使うことを検討していたときの検証内容を示す。
- bleのセントラルとペリフェラルを同時に実行できないだろうと認識はしていたけど、bleとbluetoothクラシック(sppプロファイル)の機能は、もしかしたら同時に使えるかも?と思って検証してしまった。
- 結果としては、同時に使えた。同時に使えた理屈はよく分かってないけど、使えたことが重要だと思う。
実際の動作
- cp2のスイッチを入れた後、macのbluetooth設定でペアリングを済ませ、arduinoIDEのシリアルモニタをbluetooth経由でcp2と接続しておく。
- サイクルコンピューターの電源を入れると勝手に接続する。
- シリアルモニタで適当な数値(0-255)を入力し、サイクルコンピュータの心拍欄に表示させる(下の動画)
コード(arduinoIDE)
#include "BluetoothSerial.h"
#include <M5Unified.h>
#include <BLEDevice.h>
#include <BLE2902.h> //CCCD(Client Characteristic Configuration Descriptor)のuuid:0x2902より
BluetoothSerial bts;
#define SERVICE_UUID "180d"
//notify用を追加
#define NOTIFY_CHARACTERISTIC_UUID "2a37"
BLECharacteristic *pNotifyCharacteristic;
//コネクト状態のフラグ追加
static bool connected = false;
void startService(BLEServer *pServer) //③で呼び出す関数
{
BLEService *pService = pServer->createService(SERVICE_UUID);
pNotifyCharacteristic = pService->createCharacteristic(
NOTIFY_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_NOTIFY);
pNotifyCharacteristic->addDescriptor(new BLE2902());
pService->start(); //③-5 サービス開始
}
void startAdvertising() //④で呼び出す関数
{
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
BLEDevice::startAdvertising();
}
//コールバック関数の生成
// Serverのコールバックで接続に対する処理を行う
class ServerCallbacks : public BLEServerCallbacks {
// 接続時に呼び出される
void onConnect(BLEServer *pServer) {
M5.Lcd.clearDisplay();
M5.Lcd.setCursor(5, 5);
M5.Lcd.println("接続!");
connected = true;
// 接続されたらAdvertisingを停止する
BLEDevice::stopAdvertising();
}
// 切断された時に呼び出される
void onDisconnect(BLEServer *pServer) {
M5.Lcd.clearDisplay();
M5.Lcd.setCursor(5, 5);
M5.Lcd.println("切断!");
connected = false;
delay(1000);
// 再接続のためにもう一度Advertisingする
BLEDevice::startAdvertising();
M5.Display.println("アドバタイズ中!");
}
};
void setup() {
auto cfg = M5.config();
M5.begin(cfg);
M5.Display.setFont(&lgfxJapanGothic_24);
BLEDevice::init("stickCP2_BLE"); //①BLEモジュールの初期化
BLEServer *pServer = BLEDevice::createServer(); //②BLEサーバーの設定
pServer->setCallbacks(new ServerCallbacks()); //②' コールバック関数の追加
startService(pServer); //③サービス設定
startAdvertising(); //④アドバタイズ実行
M5.Display.println("アドバタイズ中");
bts.begin("BTserialtest");
M5.Display.println("BTシリアル開始");
}
//0-255の数字を受けて、ble心拍計キャラクタリスティックのnotifyする関数
void sendHR(uint8_t value) {
M5.Lcd.println(value);
char data[] = { 0, value };
std::string myStringForUnit16((char *)&data, 2);
pNotifyCharacteristic->setValue(myStringForUnit16);
pNotifyCharacteristic->notify();
}
void loop() {
M5.update();
if (M5.BtnA.wasClicked()) {
if (!connected) { return; } //コネクトしてないときは、クリック無視
uint8_t value = random(80, 170); //適当に心拍データを作成
sendHR(value);
}
// macのシリアルモニタから送信されたものを受け取り表示したのち、0-255の整数なら心拍データを送信する
if (bts.available()) {
String data = bts.readStringUntil('\n');
M5.Lcd.println("受信:" + data);
bts.println("cp2受信:" + data);
int num = data.toInt();
auto hr = static_cast<uint8_t>(num);
if (hr > 0 && hr < 256) {
sendHR(hr);
}
}
}
コードの解説
- bleについては、以前の投稿を参照。
- bluetooth_classicといっても、arduinoIDEではおなじみのbluetoothシリアルのことだから、
BluetoothSerial bts
とbts.begin("BTserialtest")
をしただけの楽ちん仕様。bleにも見習って貰いたい! - ちなみに、bluetoothシリアルはM5atomS3では使えないから注意。M5atomS3は今どきの子なので、classic非対応で、bleのみ対応。
さいごに
- 以前の投稿で書いた、apple watchからサイコンへの心拍数飛ばしは結構苦労したから、そのときに書いたコードをこの数日でいくつか小出ししてみた。誰かの参考になればうれしい。