初めてのBLE (22) Feather nRF52840 Sense④BLE その3
前回作った湿度センサSHT30、気圧センサBMP280のペリフェラルの送ってくるデータを受けるセントラルを、Feather nRF52840 SenseのセンサなしのAdafruit Feather nRF52840 Expressボード(写真右)で作ります。
●Arduino IDEで使う
USBケーブルでPCと接続します。メニューのツールから、ボードは、次のようにAdafruit Feather nRF52840 Expressを選択しました。続いてシリアルポートも設定します。
サンプルからセントラルのcentral_scanを選び、コンパイル、書き込みをして実行します。
シリアルモニタの様子です。見つけてきました。
今度は、central_scan_advancedを動かします。アドレス以外にnameも表示しています。
●custom_hrmをベースに修正
温度だけを取り上げます。温度のサービスのUUIDがADAF0100-C332-42A8-93BD-25E905756CB8、同じくキャラクタリスティックのUUIDがADAF0101-C332-42A8-93BD-25E905756CB8です。
temp_notify_callback()でデータが更新されたら新しい値を受けています。送られてくるデータは32ビットで、リトル・エンディアンの浮動小数点形式です。これを実数に変換します。
/********************************************************************* This is an example for our nRF52 based Bluefruit LE modules Pick one up today in the adafruit shop! Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! MIT license, check LICENSE for more information All text above, and the splash screen below must be included in any redistribution *********************************************************************/ /* This sketch show how to use BLEClientService and BLEClientCharacteristic * to implement a custom client that is used to talk with Gatt server on * peripheral. * * Note: you will need another feather52 running peripheral/custom_HRM sketch * to test with. */ #include <bluefruit.h> uint8_t bmp280_SERVICE_UUID[] = {0xB8, 0x6c, 0x75, 0x05, 0xE9, 0x25, 0xBD, 0x93, 0xA8, 0x42, 0x32, 0xC3, 0x00, 0x01, 0xAF, 0xAD}; uint8_t temp_CHARACTERISTIC_UUID[] = {0xB8, 0x6c, 0x75, 0x05, 0xE9, 0x25, 0xBD, 0x93, 0xA8, 0x42, 0x32, 0xC3, 0x01, 0x01, 0xAF, 0xAD}; BLEClientService bmp280Service(bmp280_SERVICE_UUID); BLEClientCharacteristic tempCharacteristic(temp_CHARACTERISTIC_UUID); void setup(){ Serial.begin(115200); while ( !Serial ) delay(10); // for nrf52840 with native usb Serial.println("\nFeather nRF52840 Sense Central"); Serial.println("--------------------------------------\n"); // Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1 // SRAM usage required by SoftDevice will increase dramatically with number of connections Bluefruit.begin(0, 1); Bluefruit.setName("Feather nRF52840 Sense Central"); // Initialize HRM client bmp280Service.begin(); // set up callback for receiving measurement tempCharacteristic.setNotifyCallback(temp_notify_callback); tempCharacteristic.begin(); // Increase Blink rate to different from PrPh advertising mode Bluefruit.setConnLedInterval(150); // Callbacks for Central Bluefruit.Central.setDisconnectCallback(disconnect_callback); Bluefruit.Central.setConnectCallback(connect_callback); /* Start Central Scanning * - Enable auto scan if disconnected * - Interval = 100 ms, window = 80 ms * - Don't use active scan * - Filter only accept HRM service * - Start(timeout) with timeout = 0 will scan forever (until connected) */ Bluefruit.Scanner.setRxCallback(scan_callback); Bluefruit.Scanner.restartOnDisconnect(true); Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms //Bluefruit.Scanner.filterUuid(bmp280Service.uuid); Bluefruit.Scanner.useActiveScan(false); Bluefruit.Scanner.start(0); // // 0 = Don't stop scanning after n seconds } void loop(){ // do nothing } /** * Callback invoked when scanner pick up an advertising data * @param report Structural advertising data */ void scan_callback(ble_gap_evt_adv_report_t* report){ PRINT_LOCATION(); uint8_t len = 0; uint8_t buffer[32]; memset(buffer, 0, sizeof(buffer)); /* RSSI value */ Serial.printf("%10s %d dBm\n", "RSSI", report->rssi); // MAC is in little endian --> print reverse Serial.printf("Address ");Serial.printBufferReverse(report->peer_addr.addr, 6, ':'); Serial.print("\n"); if(Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, buffer, sizeof(buffer))){ Serial.printf("%10s %s\n", "SHORT NAME", buffer); memset(buffer, 0, sizeof(buffer)); Serial.print("\n"); } Bluefruit.Central.connect(report); } /** * Callback invoked when an connection is established * @param conn_handle */ void connect_callback(uint16_t conn_handle){ Serial.println("Connected"); Serial.print("Discovering BMP280 Service ... "); // If HRM is not found, disconnect and return if ( !bmp280Service.discover(conn_handle) ){ Serial.println("Found NONE"); // disconnect since we couldn't find HRM service Bluefruit.disconnect(conn_handle); return; } // Once BMP280 service is found, we continue to discover its characteristic Serial.println("Found it"); Serial.print("Discovering temp characteristic ... "); if ( !tempCharacteristic.discover() ) { // Measurement chr is mandatory, if it is not found (valid), then disconnect Serial.println("not found !!!"); Serial.println("Measurement characteristic is mandatory but not found"); Bluefruit.disconnect(conn_handle); return; } Serial.println("Found it"); // Reaching here means we are ready to go, let's enable notification on measurement chr if ( tempCharacteristic.enableNotify() ) { Serial.println("\nReady to receive temp Measurement value..."); }else { Serial.println("Couldn't enable notify for HRM Measurement. Increase DEBUG LEVEL for troubleshooting"); } } /** * Callback invoked when a connection is dropped * @param conn_handle * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h */ void disconnect_callback(uint16_t conn_handle, uint8_t reason){ (void) conn_handle; (void) reason; Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX); } /** * Hooked callback that triggered when a measurement value is sent from peripheral * @param chr Pointer client characteristic that even occurred, * in this example it should be hrmc * @param data Pointer to received data * @param len Length of received data */ void temp_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len){ Serial.print("temp data: 0x"); uint32_t temp = (uint32_t)data[3]<<24 |(uint32_t)data[2]<<16 |(uint16_t)data[1]<<8 | data[0]; Serial.println(temp,HEX); // convert real int8_t f = pow(-1, int(bitRead(temp,31))); double k = 1 + ((((temp<<1)<<8)>>9))/pow(2,23); int32_t s = pow(2,((((temp<<1)>>24))-127)); Serial.print("Temperature: ");Serial.println(f*k*s,2); //delay(2000); }
実行中の様子です。
filterUuidのフィルタをかけるとscanで探してこないので、コメントアウトしています。したがって、現状では、ほかのBLEも受けてしまいます。
●湿度と気圧を追加
三つのサービスを、三つのキャラクタリスティックを記述して実行しましたが、うまく動きませんでした。いろいろな部分をコメントアウトしながら動作を確認していくと、サービスは一つししか生かせないことがわかりました。
次に示すのは、実験で使ったスケッチです。サービスを一つにし、connect_callback()も、そのサービスに対する記述を生かせば、温度、湿度、気圧のいずれも動きます。複数のサービスが動く記述方法があるかもしれませんが、見つかっていません。
/********************************************************************* This is an example for our nRF52 based Bluefruit LE modules Pick one up today in the adafruit shop! Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! MIT license, check LICENSE for more information All text above, and the splash screen below must be included in any redistribution *********************************************************************/ /* This sketch show how to use BLEClientService and BLEClientCharacteristic * to implement a custom client that is used to talk with Gatt server on * peripheral. * * Note: you will need another feather52 running peripheral/custom_HRM sketch * to test with. */ #include <bluefruit.h> uint8_t bmp280_SERVICE_UUID[] = {0xB8, 0x6c, 0x75, 0x05, 0xE9, 0x25, 0xBD, 0x93, 0xA8, 0x42, 0x32, 0xC3, 0x00, 0x01, 0xAF, 0xAD}; uint8_t temp_CHARACTERISTIC_UUID[] = {0xB8, 0x6c, 0x75, 0x05, 0xE9, 0x25, 0xBD, 0x93, 0xA8, 0x42, 0x32, 0xC3, 0x01, 0x01, 0xAF, 0xAD}; BLEClientService bmp280Service(bmp280_SERVICE_UUID); BLEClientCharacteristic tempCharacteristic(temp_CHARACTERISTIC_UUID); uint8_t sht30_SERVICE_UUID[] = {0xB8, 0x6c, 0x75, 0x05, 0xE9, 0x25, 0xBD, 0x93, 0xA8, 0x42, 0x32, 0xC3, 0x00, 0x07, 0xAF, 0xAD}; uint8_t humi_CHARACTERISTIC_UUID[] = {0xB8, 0x6c, 0x75, 0x05, 0xE9, 0x25, 0xBD, 0x93, 0xA8, 0x42, 0x32, 0xC3, 0x01, 0x07, 0xAF, 0xAD}; BLEClientService sht30Service(sht30_SERVICE_UUID); BLEClientCharacteristic humiCharacteristic(humi_CHARACTERISTIC_UUID); uint8_t bmp280P_SERVICE_UUID[] = {0xB8, 0x6c, 0x75, 0x05, 0xE9, 0x25, 0xBD, 0x93, 0xA8, 0x42, 0x32, 0xC3, 0x00, 0x08, 0xAF, 0xAD}; uint8_t baro_CHARACTERISTIC_UUID[] = {0xB8, 0x6c, 0x75, 0x05, 0xE9, 0x25, 0xBD, 0x93, 0xA8, 0x42, 0x32, 0xC3, 0x01, 0x08, 0xAF, 0xAD}; BLEClientService bmp280PService(bmp280P_SERVICE_UUID); BLEClientCharacteristic baroCharacteristic(baro_CHARACTERISTIC_UUID); void setup(){ Serial.begin(115200); while ( !Serial ) delay(10); // for nrf52840 with native usb Serial.println("\nFeather nRF52840 Sense Central"); Serial.println("--------------------------------------\n"); // Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1 // SRAM usage required by SoftDevice will increase dramatically with number of connections Bluefruit.begin(0, 1); Bluefruit.setName("Feather nRF52840 Sense Central"); // Initialize client // bmp280Service.begin(); // sht30Service.begin(); bmp280PService.begin(); // set up callback for receiving measurement tempCharacteristic.setNotifyCallback(temp_notify_callback); tempCharacteristic.begin(); humiCharacteristic.setNotifyCallback(humi_notify_callback); humiCharacteristic.begin(); baroCharacteristic.setNotifyCallback(baro_notify_callback); baroCharacteristic.begin(); // Increase Blink rate to different from PrPh advertising mode Bluefruit.setConnLedInterval(150); // Callbacks for Central Bluefruit.Central.setDisconnectCallback(disconnect_callback); Bluefruit.Central.setConnectCallback(connect_callback); /* Start Central Scanning * - Enable auto scan if disconnected * - Interval = 100 ms, window = 80 ms * - Don't use active scan * - Filter only accept HRM service * - Start(timeout) with timeout = 0 will scan forever (until connected) */ Bluefruit.Scanner.setRxCallback(scan_callback); Bluefruit.Scanner.restartOnDisconnect(true); Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms //Bluefruit.Scanner.filterUuid(bmp280Service.uuid); //not work Bluefruit.Scanner.useActiveScan(false); Bluefruit.Scanner.start(0); // // 0 = Don't stop scanning after n seconds } void loop(){ // do nothing } /** * Callback invoked when scanner pick up an advertising data * @param report Structural advertising data */ void scan_callback(ble_gap_evt_adv_report_t* report){ PRINT_LOCATION(); uint8_t len = 0; uint8_t buffer[32]; memset(buffer, 0, sizeof(buffer)); /* RSSI value */ Serial.printf("%10s %d dBm\n", "RSSI", report->rssi); // MAC is in little endian --> print reverse Serial.printf("Address ");Serial.printBufferReverse(report->peer_addr.addr, 6, ':'); Serial.print("\n"); if(Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, buffer, sizeof(buffer))){ Serial.printf("%10s %s\n", "SHORT NAME", buffer); memset(buffer, 0, sizeof(buffer)); Serial.print("\n"); } Bluefruit.Central.connect(report); } /** * Callback invoked when an connection is established * @param conn_handle */ void connect_callback(uint16_t conn_handle){ Serial.println("Connected"); Serial.print("Discovering BMP280 Service ... "); // If bmp280Service is not found, disconnect and return if ( !bmp280Service.discover(conn_handle) ){ Serial.println("Found NONE"); // disconnect since we couldn't find BMP280 service Bluefruit.disconnect(conn_handle); return; } // Once BMP280 service is found, we continue to discover its characteristic Serial.println("Found it"); Serial.print("Discovering temp characteristic ... "); if ( !tempCharacteristic.discover() ) { // Measurement chr is mandatory, if it is not found (valid), then disconnect Serial.println("not found !!!"); Serial.println("temp characteristic is mandatory but not found"); Bluefruit.disconnect(conn_handle); return; } Serial.println("Found tempCharacteristic"); if ( !sht30Service.discover(conn_handle) ){ Serial.println("sht30Service Found NONE"); // disconnect since we couldn't find SHT30 service Bluefruit.disconnect(conn_handle); return; } Serial.print("Discovering humi Sensor Location characteristic ... "); if ( humiCharacteristic.discover() ) { Serial.println("Found humiCharacteristic"); }else { Serial.println("Found NONE"); } if ( !bmp280PService.discover(conn_handle) ){ Serial.println("bmp280PService Found NONE"); // disconnect since we couldn't find BMP280P service Bluefruit.disconnect(conn_handle); return; } Serial.print("Discovering baro Sensor Location characteristic ... "); if ( baroCharacteristic.discover() ) { Serial.println("Found baroCharacteristic"); }else { Serial.println("Found NONE"); } // Reaching here means we are ready to go, let's enable notification on measurement chr if ( tempCharacteristic.enableNotify() ) { Serial.println("\nReady to receive temp Measurement value..."); }else { Serial.println("Couldn't enable notify for temp Measurement. Increase DEBUG LEVEL for troubleshooting"); } // Reaching here means we are ready to go, let's enable notification on measurement chr if ( humiCharacteristic.enableNotify() ) { Serial.println("\nReady to receive humi Measurement value..."); }else { Serial.println("Couldn't enable notify for humi Measurement. Increase DEBUG LEVEL for troubleshooting"); } // Reaching here means we are ready to go, let's enable notification on measurement chr if ( baroCharacteristic.enableNotify() ) { Serial.println("\nReady to receive baro Measurement value..."); }else { Serial.println("Couldn't enable notify for baro Measurement. Increase DEBUG LEVEL for troubleshooting"); } } /** * Callback invoked when a connection is dropped * @param conn_handle * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h */ void disconnect_callback(uint16_t conn_handle, uint8_t reason){ (void) conn_handle; (void) reason; Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX); } /** * Hooked callback that triggered when a measurement value is sent from peripheral * @param chr Pointer client characteristic that even occurred, * in this example it should be hrmc * @param data Pointer to received data * @param len Length of received data */ void temp_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len){ Serial.print("temp data: 0x"); uint32_t temp = (uint32_t)data[3]<<24 |(uint32_t)data[2]<<16 |(uint16_t)data[1]<<8 | data[0]; Serial.println(temp,HEX); // convert real int8_t f = pow(-1, int(bitRead(temp,31))); double k = 1 + ((((temp<<1)<<8)>>9))/pow(2,23); int32_t s = pow(2,((((temp<<1)>>24))-127)); Serial.print("Temperature: ");Serial.println(f*k*s,2); //delay(2000); } void humi_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len){ Serial.print("humi data: 0x"); uint32_t humi = (uint32_t)data[3]<<24 |(uint32_t)data[2]<<16 |(uint16_t)data[1]<<8 | data[0]; Serial.println(humi,HEX); // convert real int8_t f = pow(-1, int(bitRead(humi,31))); double k = 1 + ((((humi<<1)<<8)>>9))/pow(2,23); int32_t s = pow(2,((((humi<<1)>>24))-127)); Serial.print("Humidity: ");Serial.println(f*k*s,2); //delay(2000); } void baro_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len){ Serial.print("baro data: 0x"); uint32_t baro = (uint32_t)data[3]<<24 |(uint32_t)data[2]<<16 |(uint16_t)data[1]<<8 | data[0]; Serial.println(baro,HEX); // convert real int8_t f = pow(-1, int(bitRead(baro,31))); double k = 1 + ((((baro<<1)<<8)>>9))/pow(2,23); int32_t s = pow(2,((((baro<<1)>>24))-127)); Serial.print("Barometric : ");Serial.println(f*k*s,2); //delay(2000); }
●UUIDのフィルタが動作しないので
filterUuidのフィルタが動作しないので、アドレスまたはnameの一致でペリフェラルを選択できます。local nameの一致したら、実際につないで以降の処理を行う事例が、
にありましたが、うまく実装できませんでした。