初めての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の一致したら、実際につないで以降の処理を行う事例が、

  https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/libraries/Bluefruit52Lib/examples/Central/central_ti_sensortag_optical/central_ti_sensortag_optical.ino

にありましたが、うまく実装できませんでした。

前へ

初めてのBLE (21) Feather nRF52840 Sense③BLE その2

次へ

STEMMAで広がるArduinoの世界①Step1 温度センサ±0.1°C TMP117