4
2

More than 3 years have passed since last update.

M5StickCでNimBLE-Arduinoのシンプルなペリフェラルを作ってみました

Last updated at Posted at 2021-08-01

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
4
2
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
4
2