1 はじめに
ardino-esp32のv2.0.0でnimbleが採用されるようです
ちょっと予習を兼ねて、超シンプルなベースを作ってみました!!
2 接続環境
central(client):Android BLE Scannerアプリ
https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner&hl=ja&gl=US
peripheral(server):M5StickC 今回の開発対象
3 開発環境
PlatformIO+vscodeで開発します
4 手順
4.1 プロジェクト作成
project 好きな名前
board M5Stickc
framework Arduino
4.2 platform.iniを修正します
ライブラリにm5stick-cとNimBLEを追加します
ビルドするとライブラリをダウンロードしてくれます
[env:m5stick-c]
platform = espressif32
+ lib_deps =
+ M5StickC
+ h2zero/NimBLE-Arduino
board = m5stick-c
framework = arduino
+monitor_speed = 115200
4.3 サンプルを修正します
ベースは下記のソース
https://github.com/h2zero/NimBLE-Arduino/blob/master/examples/NimBLE_Server/NimBLE_Server.ino
4.3.1 ヘッダを追加します
/** NimBLE_Server Demo:
*
*
* Demonstrates many of the available features of the NimBLE server library.
*
* Created: on March 22 2020
* Author: H2zero
*
*/
+ #include<M5StickC.h>
#include <NimBLEDevice.h>
4.3.2 UUIDを定義します
static NimBLEServer* pServer;
+ #define MACHINE_NAME "M5-Stickc-0"
+ #define SERVICE_UUID "your_serviceUUID"
+ #define CHARACTERISTIC_UUID "your_characteristicUUID"
4.3.3 setupを修正します
- sampleでは複数のUUIDを利用しています
シンプルに1つのUUIDを利用するように変更します - M5の初期化を追加します
void setup() {
Serial.begin(115200);
M5.begin();
Serial.println("Starting NimBLE Server");
/** sets device name */
NimBLEDevice::init("NimBLE-Arduino");
/** Optional: set the transmit power, default is 3db */
NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */
//NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks());
NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic* pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY
);
pCharacteristic->setValue("Burger");
pCharacteristic->setCallbacks(&chrCallbacks);
/** Start the services when finished creating all Characteristics and Descriptors */
pService->start();
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/** Add the services to the advertisment data **/
pAdvertising->addServiceUUID(pService->getUUID());
/** If your device is battery powered you may consider setting scan response
* to false as it will extend battery life at the expense of less data sent.
*/
pAdvertising->setScanResponse(true);
pAdvertising->start();
Serial.println("Advertising Started");
}
4.3.4 loopを修正します
- Aボタンの押し検知を追加します
if (M5.BtnA.wasPressed()) {
- "button 押した回数"をsetValueにセットします
pChr->setValue(str);
- loop全体
static uint8_t cnt = 0;
void loop() {
/** Do your thing here, this just spams notifications to all connected clients */
if(pServer->getConnectedCount()) {
NimBLEService* pSvc = pServer->getServiceByUUID(SERVICE_UUID);
if(pSvc) {
NimBLECharacteristic* pChr = pSvc->getCharacteristic(CHARACTERISTIC_UUID);
if(pChr) {
+ if (M5.BtnA.wasPressed()) {
+ char str[32];
+ sprintf(str,"button %d", cnt );
+ pChr->setValue(str);
+ cnt++;
+ }
pChr->notify(true);
}
}
}
+ M5.update();
delay(100);
}
5 コード全体
/** NimBLE_Server Demo:
*
* Demonstrates many of the available features of the NimBLE server library.
*
* Created: on March 22 2020
* Author: H2zero
*
*/
#include<M5StickC.h>
#include <NimBLEDevice.h>
static NimBLEServer* pServer;
#define MACHINE_NAME "M5-Stickc-0"
#define SERVICE_UUID "your_serviceUUID"
#define CHARACTERISTIC_UUID "your_characteristicUUID"
/** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */
class ServerCallbacks: public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer) {
Serial.println("Client connected");
Serial.println("Multi-connect support: start advertising");
NimBLEDevice::startAdvertising();
};
/** Alternative onConnect() method to extract details of the connection.
* See: src/ble_gap.h for the details of the ble_gap_conn_desc struct.
*/
void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
Serial.print("Client address: ");
Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str());
/** We can use the connection handle here to ask for different connection parameters.
* Args: connection handle, min connection interval, max connection interval
* latency, supervision timeout.
* Units; Min/Max Intervals: 1.25 millisecond increments.
* Latency: number of intervals allowed to skip.
* Timeout: 10 millisecond increments, try for 5x interval time for best results.
*/
pServer->updateConnParams(desc->conn_handle, 24, 48, 0, 60);
};
void onDisconnect(NimBLEServer* pServer) {
Serial.println("Client disconnected - start advertising");
NimBLEDevice::startAdvertising();
};
void onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc) {
Serial.printf("MTU updated: %u for connection ID: %u\n", MTU, desc->conn_handle);
};
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){
Serial.println("Server Passkey Request");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456;
};
bool onConfirmPIN(uint32_t pass_key){
Serial.print("The passkey YES/NO number: ");Serial.println(pass_key);
/** Return false if passkeys don't match. */
return true;
};
void onAuthenticationComplete(ble_gap_conn_desc* desc){
/** Check that encryption was successful, if not we disconnect the client */
if(!desc->sec_state.encrypted) {
NimBLEDevice::getServer()->disconnect(desc->conn_handle);
Serial.println("Encrypt connection failed - disconnecting client");
return;
}
Serial.println("Starting BLE work!");
};
};
/** Handler class for characteristic actions */
class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
void onRead(NimBLECharacteristic* pCharacteristic){
Serial.print(pCharacteristic->getUUID().toString().c_str());
Serial.print(": onRead(), value: ");
Serial.println(pCharacteristic->getValue().c_str());
};
void onWrite(NimBLECharacteristic* pCharacteristic) {
Serial.print(pCharacteristic->getUUID().toString().c_str());
Serial.print(": onWrite(), value: ");
Serial.println(pCharacteristic->getValue().c_str());
};
/** Called before notification or indication is sent,
* the value can be changed here before sending if desired.
*/
void onNotify(NimBLECharacteristic* pCharacteristic) {
Serial.println("Sending notification to clients");
};
/** The status returned in status is defined in NimBLECharacteristic.h.
* The value returned in code is the NimBLE host return code.
*/
void onStatus(NimBLECharacteristic* pCharacteristic, Status status, int code) {
String str = ("Notification/Indication status code: ");
str += status;
str += ", return code: ";
str += code;
str += ", ";
str += NimBLEUtils::returnCodeToString(code);
Serial.println(str);
};
void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue) {
String str = "Client ID: ";
str += desc->conn_handle;
str += " Address: ";
str += std::string(NimBLEAddress(desc->peer_ota_addr)).c_str();
if(subValue == 0) {
str += " Unsubscribed to ";
}else if(subValue == 1) {
str += " Subscribed to notfications for ";
} else if(subValue == 2) {
str += " Subscribed to indications for ";
} else if(subValue == 3) {
str += " Subscribed to notifications and indications for ";
}
str += std::string(pCharacteristic->getUUID()).c_str();
Serial.println(str);
};
};
/** Handler class for descriptor actions */
class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
void onWrite(NimBLEDescriptor* pDescriptor) {
std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength());
Serial.print("Descriptor witten value:");
Serial.println(dscVal.c_str());
};
void onRead(NimBLEDescriptor* pDescriptor) {
Serial.print(pDescriptor->getUUID().toString().c_str());
Serial.println(" Descriptor read");
};
};
/** Define callback instances globally to use for multiple Charateristics \ Descriptors */
static DescriptorCallbacks dscCallbacks;
static CharacteristicCallbacks chrCallbacks;
void setup() {
Serial.begin(115200);
M5.begin();
Serial.println("Starting NimBLE Server");
/** sets device name */
NimBLEDevice::init(MACHINE_NAME);
/** Optional: set the transmit power, default is 3db */
NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */
/** 2 different ways to set security - both calls achieve the same result.
* no bonding, no man in the middle protection, secure connections.
*
* These are the default values, only shown here for demonstration.
*/
//NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks());
NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic* pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY
);
pCharacteristic->setValue("Burger");
pCharacteristic->setCallbacks(&chrCallbacks);
/** Start the services when finished creating all Characteristics and Descriptors */
pService->start();
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/** Add the services to the advertisment data **/
pAdvertising->addServiceUUID(pService->getUUID());
/** If your device is battery powered you may consider setting scan response
* to false as it will extend battery life at the expense of less data sent.
*/
pAdvertising->setScanResponse(true);
pAdvertising->start();
Serial.println("Advertising Started");
}
static uint8_t cnt = 0;
void loop() {
/** Do your thing here, this just spams notifications to all connected clients */
if(pServer->getConnectedCount()) {
NimBLEService* pSvc = pServer->getServiceByUUID(SERVICE_UUID);
if(pSvc) {
NimBLECharacteristic* pChr = pSvc->getCharacteristic(CHARACTERISTIC_UUID);
if(pChr) {
if (M5.BtnA.wasPressed()) {
char str[32];
sprintf(str,"button %d", cnt );
pChr->setValue(str);
Serial.println(str);
cnt++;
}
pChr->notify(true);
}
}
}
M5.update();
delay(100);
}
6 結果
Client connected
Multi-connect support: start advertising
Client address: 4c:45:60:eb:5c:84
0x000c: onRead(), value: button 3
Client ID: 0 Address: 4c:45:60:eb:5c:84 Subscribed to notfications for 0x000c
Sending notification to clients
Notification/Indication status code: 1, return code: 0,
button 4
Sending notification to clients
Notification/Indication status code: 1, return code: 0,
button 5
Sending notification to clients
Notification/Indication status code: 1, return code: 0,
Sending notification to clients
Notification/Indication status code: 1, return code: 0,
Client ID: 0 Address: 4c:45:60:eb:5c:84 Unsubscribed to 0x000c
Client disconnected - start advertising