M5STACKのボタンを押すと値をBLE経由でM5STICKPlusに送信し、 M5StickPlusでは受信した内容を画面に表示する
構成
プロジェクト名:BLE_Sample_M5Stack.ino/BLE_Sample_M5StickPlus.ino
デバイス:M5STACK、M5STICK CPLUS
動作確認
M5STACK側
接続が完了で以下の情報がシリアル出力されることを確認。
17:38:01.038 -> Device Connected!
M5STICK側
接続を完了し、受信したボタン情報がシリアル出力されることを確認。
18:03:19.627 -> Forming a connection to 08:3a:f2:43:ab:06
18:03:20.150 -> - Created client
18:03:20.222 -> - Connected to server
18:03:20.690 -> - Found our service
18:03:20.690 -> - Found our characteristic
18:03:20.730 -> We are now connected to the BLE Server.
18:03:25.598 -> Received: A=1, B=0, C=0
18:03:26.853 -> Received: A=1, B=2, C=0
18:03:27.728 -> Received: A=1, B=2, C=3
プログラム
M5STACK
//--------------------------------------------
// ライブラリ
//--------------------------------------------
#include <M5Stack.h>
//BLE関連
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
// BLEサーバー本体のオブジェクト。クライアント(M5StickCなど)からの接続を受け付ける中心的な役割です。
BLEServer* pServer = NULL;
// データを読み書きするための「特性(窓口)」。ここに値をセットしてNotify(通知)を飛ばします。
BLECharacteristic* pCharacteristic = NULL;
// 現在、デバイス(クライアント)が接続されているかどうかを保持するフラグです。
bool deviceConnected = false;
// 1つ前のループ時点での接続状態。これと比較することで「接続された瞬間」や「切断された瞬間」を判定します。
bool oldDeviceConnected = false;
// 送信データを格納するバッファ(情報の入れ物)。ボタンの状態などをこの配列に書き込んで送信します。
uint8_t buf[100];
#define SERVICE_UUID "6f3b8a12-4d5e-4b9a-9c2f-7a1b3c5d8e01"
#define CHARACTERISTIC_UUID "a1b2c3d4-e5f6-4a5b-bc6d-7e8f9a0b1c2d"
//--------------------------------------------
//コールバック関数
//--------------------------------------------
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
//--------------------------------------------
//初期化処理
//--------------------------------------------
void setup()
{
M5.begin();
WiFi.mode(WIFI_OFF);
Serial.print("");
// Create the BLE Device
// 初期化処理を行ってBLEデバイスを初期化する
// Device Local Name
BLEDevice::init("BLEMaster");
// Serverオブジェクトを作成してコールバックを設定する
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Serviceオブジェクトを作成して準備処理を実行する
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
// Create a BLE Descriptor
pCharacteristic->addDescriptor(new BLE2902());
// --- Master側の setup() 内の修正 ---
pService->start(); // サービスを開始
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
// 重要:ここでサービスUUIDをアドバタイジングデータに確実に追加する
pAdvertising->addServiceUUID(SERVICE_UUID);
// Scan Responseをtrueに設定(名前やUUIDをより確実に送るため)
pAdvertising->setScanResponse(true);
// iPhoneなどでも見つけやすくするための推奨設定
pAdvertising->setMinPreferred(0x06);
pAdvertising->setMaxPreferred(0x12);
// アドバタイジングの開始
BLEDevice::startAdvertising();
//画面初期化
M5.Lcd.clear(WHITE);
M5.Lcd.setCursor(90, 100);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(BLUE);
M5.Lcd.println("BLEMaster");
}
//--------------------------------------------
//メイン処理
//--------------------------------------------
void loop()
{
M5.update();
if (M5.BtnA.wasPressed()) { //Aボタン押下時
M5.Lcd.clear(RED);
if(buf[0]==0x00)
buf[0]=(uint8_t)(0x01);
else
buf[0]=(uint8_t)(0x00);
pCharacteristic->setValue(buf,sizeof buf);
pCharacteristic->notify();
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
}
if (M5.BtnB.wasPressed()) { //Bボタン押下時
M5.Lcd.clear(GREEN);
if(buf[1]==0x00)
buf[1]=(uint8_t)(0x02);
else
buf[1]=(uint8_t)(0x00);
pCharacteristic->setValue(buf,sizeof buf);
pCharacteristic->notify();
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
}
if (M5.BtnC.wasPressed()) { //Cボタン押下時
if(buf[2]==0x00)
buf[2]=(uint8_t)(0x03);
else
buf[2]=(uint8_t)(0x00);
pCharacteristic->setValue(buf,sizeof buf);
pCharacteristic->notify();
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
M5.Lcd.clear(BLACK);
}
delay(300);
// BLEの接続状態が変わった瞬間の処理(管理)
// notify changed value
//接続の維持(Connecting): oldDeviceConnected という変数を使って、
//前回は切れていたけど、今はつながっている」という変化の瞬間を捉えています。
//ここで「Connected!」と表示させることで、Slave側(StickC)が
//正しく見つけて接続しに来たことが確認できます。
if (deviceConnected) {
// ここはボタンを押した時以外に、自動でデータを送り続けたい場合に記述します。
// 今はボタン押下時に送信しているので、空のままでOKです。
}
// 【切断された瞬間】の処理
//自動再開(Disconnecting): BLEは一度切れると、Master側が再び「募集中(Advertising)」を
//始めないとSlaveから見えなくなります。
//このコードがあるおかげで、StickCの電源を入れ直したり、
//距離が離れて切れたりしても、自動で再接続できる状態に戻ります。
if (!deviceConnected && oldDeviceConnected) {
delay(500);
pServer->startAdvertising(); // アドバタイジング(再募集)を再開
// --- デバッグ表示を追加 ---
Serial.println("Device Disconnected. Restarting advertising...");
M5.Lcd.clear(BLACK);
M5.Lcd.setCursor(0, 0);
M5.Lcd.println("Disconnected");
// -----------------------
oldDeviceConnected = deviceConnected;
}
// 【接続された瞬間】の処理
if (deviceConnected && !oldDeviceConnected) {
// --- デバッグ表示を追加 ---
Serial.println("Device Connected!");
M5.Lcd.clear(GREEN);
M5.Lcd.setCursor(0, 0);
M5.Lcd.println("Connected!");
// -----------------------
oldDeviceConnected = deviceConnected;
}
}
M5STICK CPLUS
//--------------------------------------------
// ライブラリ
//--------------------------------------------
#include <M5StickCPlus.h>
#include "BLEDevice.h"
// 接続先(M5Stack)が提供している「サービス」の固有ID。Master側と一致させる必要があります。
static BLEUUID serviceUUID("6f3b8a12-4d5e-4b9a-9c2f-7a1b3c5d8e01");
// 読み書きや通知を受け取る「窓口(特性)」の固有ID。Master側と一致させる必要があります。
static BLEUUID charUUID("a1b2c3d4-e5f6-4a5b-bc6d-7e8f9a0b1c2d");
// 目的のデバイスが見つかり、「今から接続処理を開始していいですよ」という合図(フラグ)です。
static boolean doConnect = false;
// 現在、実際にMasterとの通信が確立されているかどうかを保持します。
static boolean connected = false;
// 一度切断された後などに、再び周囲のデバイスを探す(スキャンする)必要があるかを示します。
static boolean doScan = false;
// 接続先のMasterが持つデータの窓口を操作するためのオブジェクトです。ここを通じて値を取得します。
static BLERemoteCharacteristic* pRemoteCharacteristic;
// スキャンで見つかったMasterの情報を一時的に保存しておく場所です。
static BLEAdvertisedDevice myDevice;
// Masterから受信した数値を保存する変数。これをLCDに表示します。
uint16_t val;
//--------------------------------------------
// notifyCallback 関数は、**「Master(M5Stack)から
//新しいデータが送られてきた瞬間に、自動で呼び出される処理
//--------------------------------------------
static void notifyCallback(
BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
//pData: 届いたデータの塊(配列)の先頭アドレスです。
//length: 届いたデータの長さ(今回の場合は100バイト)です。
// 配列の各要素をデバッグ表示
Serial.printf("Received: A=%d, B=%d, C=%d\n", pData[0], pData[1], pData[2]);
// 画面表示用の変数 val に何を代入するか決めます
// 例:最後に変化があった値を表示する場合
if (pData[0] != 0) val = pData[0];
else if (pData[1] != 0) val = pData[1];
else if (pData[2] != 0) val = pData[2];
else val = 0;
}
//--------------------------------------------
//Slave(M5StickC)側から見た、接続状態の変化を監視する
//--------------------------------------------
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) {
}
void onDisconnect(BLEClient* pclient) {
connected = false;
Serial.println("onDisconnect");
}
};
//--------------------------------------------
//M5StickCがMaster(M5Stack)を見つけた後、**「実際に通信ができる状態までセットアップを完了させる
//--------------------------------------------
bool connectToServer() {
Serial.print("Forming a connection to ");
Serial.println(myDevice.getAddress().toString().c_str());
//「BLE通信専用の窓口(クライアント)」を作ります
BLEClient* pClient = BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback());
//スキャンで見つけておいたMaster(myDevice)に対して接続を試みます。
//重要点: ここで成功すると、Master側の画面には「Connected!」と表示されます。
pClient->connect(&myDevice);
Serial.println(" - Connected to server");
//今後、ボタンの値を受け取る相手はこの pRemoteCharacteristic
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
Serial.print("Failed to find our service UUID: ");
Serial.println(serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our service");
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
//これが非常に重要です。Masterに対して**「値が変わったら(ボタンが押されたら)、
//その都度勝手に知らせて(Notify)ください」**と予約します。
//これにより、Masterでボタンが押されるたびに、
//自動的に notifyCallback 関数が動くようになります。
if (pRemoteCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our characteristic");
if(pRemoteCharacteristic->canNotify())
pRemoteCharacteristic->registerForNotify(notifyCallback);
connected = true;
return true; // 戻り値もお忘れなく
}
//--------------------------------------------
//周囲のBluetoothデバイスをスキャン
//--------------------------------------------
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
//デバイスを1つ見つけるたびに、この関数が自動で実行されます
void onResult(BLEAdvertisedDevice advertisedDevice) {
//まず、相手の名前が "BLEMaster" かどうかを確認します。
if (advertisedDevice.getName() == "BLEMaster") {
if (advertisedDevice.isAdvertisingService(serviceUUID)) {
//目的の相手が見つかったので、バッテリーとメモリを節約するために
//スキャン(検索)を停止します。
BLEDevice::getScan()->stop();
//見つかった相手の情報を myDevice という変数にコピーして保存します。
myDevice = advertisedDevice;
doConnect = true;
doScan = true;
}
}
}
};
//--------------------------------------------
// 初期化
//--------------------------------------------
void setup() {
M5.begin();
Serial.begin(115200);
Serial.println("Starting Arduino BLE Client application...");
BLEDevice::init("");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false);
} // End of setup.
//--------------------------------------------
//メイン処理
//--------------------------------------------
// This is the Arduino main loop function.
void loop() {
M5.update(); // ボタン状態などの更新(必要に応じて)
if (connected) {
// 値が変わったとき、または一定時間ごとに画面を更新
// ここでは単純化のため毎回描画しますが、fillScreenを毎回しない工夫
M5.Lcd.setCursor(0, 50);
M5.Lcd.setTextSize(5);
M5.Lcd.setTextColor(RED, BLACK); // 第2引数に背景色を入れると、上書き時にチラつきません
M5.Lcd.printf("%d ", val); // 桁数が変わった時のために空白を入れる
} else {
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0, 10);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.println("Scanning...");
}
if (doConnect == true) {
if (connectToServer()) {
Serial.println("We are now connected to the BLE Server.");
} else {
Serial.println("We have failed to connect to the server; there is nothin more we will do.");
}
doConnect = false;
}
if (connected) {
}else if(doScan){
BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino
}
delay(1000); // Delay a second between loops.
} // End of loop



コメント