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

 BLEは、一番外側がGATTで、その中に複数のServiceがあります。Serviceの中に複数のCharacteristicsがあるという構造をしています。ServiceとCharacteristicsにはユニークなUUIDが振られています。バッテリ・サービスのように決まった(登録されている)UUIDもありますが、多くは、各社各自、自由に使っています。
 ここでは、Adafruitが定義したUUIDをそのまま使います。

 Feather nRF52840 Senseボードにはたくさんのセンサやボタン、LEDがあるので、Characteristicsは10個以上とたくさんあります。ここでは、温湿度センサSHT30と気圧センサBMP280だけを取り上げます。

<センサの読み取り>

  • SHT30のライブラリはAdafruit_SHT31で、湿度をsht30.readHumidity()で読み出す
  • BMP280のライブラリはAdafruit_BMP280で、温度をgetTemperatureSensor()もしくはbmp280.readTemperature()で、気圧をbmp280.getPressureSensor()もしくはbmp280.readPressure()で読み出す

<BLEのUUID>黄色の数字が異なるだけで、ほかはみな同じ。

  • 湿度のServiceのUUIDはADAF0700-C332-42A8-93BD-25E905756CB8、湿度のCharacteristicsはADAF0701-C332-42A8-93BD-25E905756CB8
  • 温度のServiceのUUIDはADAF0100-C332-42A8-93BD-25E905756CB8、温度のCharacteristicsはADAF0101-C332-42A8-93BD-25E905756CB8
  • 気圧のServiceのUUIDはADAF0800-C332-42A8-93BD-25E905756CB8、気圧のCharacteristicsはADAF0801-C332-42A8-93BD-25E905756CB8

スケッチ

 例えば、バッテリ・サービスであれば、blebas.write(100);のように、書き込みを行いますが、Adafruitのセンサ・ライブラリでは、リード/ライトおよびNotify の属性、測定間隔は1秒?(単位が不明;100ms?)間隔と設定されています。Writeの属性はありますが、何か書き込まないと、スリープ状態から抜けてセンサからデータを読み出せないというわけではありません。

* Shared Characteristics
* - Measurement Period 0001 | int32_t | Read + Write |
* ms between measurements, -1: stop reading, 0: update when changes
* * Barometric service 0800
* - Pressure 0801 | float[3] | Read + Notify | x, y, z
* - Measurement Period 0001

 スケッチです。アドバタイジングをするときの設定は、元のスケッチから変更していません。

/*********************************************************************
 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 demonstrate the BLE Adafruit Service that is used with
 * "Adafruit Bluefruit Playground" app. Supported boards are
 *  - Feather Sense : https://www.adafruit.com/product/4516
 */

#include <Wire.h>
#include <bluefruit.h>
#include <BLEAdafruitService.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_SHT31.h>
#include <Adafruit_AHRS.h>

#define DEVICE_NAME     "Feather nRF52840 Sense"

BLEAdafruitBaro       bleBaro;     // UUID 0800
BLEAdafruitHumid      bleHumid;    // UUID 0700

Adafruit_BMP280   bmp280;   // Temperature, Barometric
Adafruit_SHT31    sht30;    // Humid

uint16_t measure_humid(uint8_t* buf, uint16_t bufsize){
  float humid = sht30.readHumidity();
  memcpy(buf, &humid, 4);
  return 4;
}

//--------------------------------------------------------------------+
// Common for all Boards
//--------------------------------------------------------------------+

// BLE Service
BLEDfu  bledfu;  // OTA DFU service
BLEDis  bledis;  // device information
BLEUart bleuart; // uart over ble

// Adafruit Service: ADAFxx-C332-42A8-93BD-25E905756CB8
BLEAdafruitTemperature        bleTemp; // UUID 0100

//--------------------------------------------------------------------+
// Codes
//--------------------------------------------------------------------+
void setup(){
  // Increase I2C speed to 400 Khz
  Wire.begin();
  Wire.setClock(400000);

  Serial.begin(115200);
  while(!Serial) delay(10); // wait for native USB
  bmp280.begin();
  sht30.begin(0x44);

  Serial.println("Bluefruit Playground Example");
  Serial.println("---------------------------\n");

  // Setup the BLE LED to be enabled on CONNECT
  // Note: This is actually the default behaviour, but provided
  // here in case you want to control this LED manually via PIN 19
  //  Bluefruit.autoConnLed(false); D4<-Feather nRF52840 Sense

  // Config the peripheral connection with maximum bandwidth 
  // more SRAM required by SoftDevice
  // Note: All config***() function must be called before begin()
  Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);

  Bluefruit.begin();
  Bluefruit.setTxPower(8);    // Check bluefruit.h for supported values
  Bluefruit.setName(DEVICE_NAME);
  //Bluefruit.setName(getMcuUniqueID()); // useful testing with multiple central connections
  Bluefruit.Periph.setConnectCallback(connect_callback);
  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);

  // To be consistent OTA DFU should be added first if it exists
  bledfu.begin();

  // Configure and Start Device Information Service
  bledis.setManufacturer("Adafruit Industries");
  bledis.begin();

  // Configure and Start BLE Uart Service
  bleuart.begin();

  //------------- Adafruit Service -------------//
  bleBaro.begin(bmp280.getPressureSensor(), 100);  // 100ms
  bleHumid.begin(measure_humid, 100);
  bleTemp.begin(bmp280.getTemperatureSensor(), 100);

  // Set up and start advertising
  startAdv();

  Serial.println("Please use Adafruit's Bluefruit LE app to connect in UART mode");
  Serial.println("Once connected, enter character(s) that you wish to send");
}

void startAdv(void){
  // Advertising packet
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);

  // Advertising with only board ID
  struct ATTR_PACKED {
    uint16_t mfr_id;
    
    uint8_t  field_len;
    uint16_t field_key;
    uint16_t field_value;
  } mfr_adv;

  mfr_adv.mfr_id = UUID16_COMPANY_ID_ADAFRUIT;
  mfr_adv.field_len = 4;
  mfr_adv.field_key = 1; // board id
  mfr_adv.field_value = USB_PID;

  Bluefruit.Advertising.addManufacturerData(&mfr_adv, sizeof(mfr_adv));

  // Add name to advertising, since there is enough room
  Bluefruit.Advertising.addName();
  
  /* Start Advertising
   * - Enable auto advertising if disconnected
   * - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
   * - Timeout for fast mode is 30 seconds
   * - Start(timeout) with timeout = 0 will advertise forever (until connected)
   * 
   * For recommended advertising interval
   * https://developer.apple.com/library/content/qa/qa1931/_index.html   
   */
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);    // in unit of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds  
}

void loop(){
  float  temperature = bmp280.readTemperature();
  float pressure = bmp280.readPressure()/100.0;
  float  humidity = sht30.readHumidity();
  Serial.print("Temperature: ");
  Serial.print(temperature);
  Serial.println(" C");
  Serial.print("Barometric pressure: ");
  Serial.println(pressure);
  Serial.print("Humidity: ");
  Serial.print(humidity);
  Serial.println(" %");
  delay(1000);
}

// callback invoked when central connects
void connect_callback(uint16_t conn_handle){
  // Get the reference to current connection
  BLEConnection* connection = Bluefruit.Connection(conn_handle);

  char central_name[32] = { 0 };
  connection->getPeerName(central_name, sizeof(central_name));

  Serial.print("Connected to ");
  Serial.println(central_name);
}

/**
 * Callback invoked when a connection is dropped
 * @param conn_handle connection where this event happens
 * @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.println();
  Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
}

 実行中のシリアルモニタです。UART関連のメッセージが残っていますが、今回は使っていません。

 オン・セミコンダクターのBLEであるRSL10 Bluetooth Low Enaergy Exploerを使って情報を見ます。セントラルの立場です。connectで接続し、Discover Serviceでサービスを表示しました。

 読み出したデータは、Floating Point to Hex Converterサイトで変換しました。4桁の固定小数点はリトル・エンディアンなので、逆順にします。

データの更新

 バッテリ・サービスのソースを読むと、writeが記述されているので、データを定期的にwriteすれば、BLE経由でセントラルへ新しい値が送られます。この記述は、#include <bluefruit.h>に、ソースは、https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/libraries/Bluefruit52Libにあります。

 センサ関連は、#include <BLEAdafruitService.h>に、ソースは、https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/libraries/BLEAdafruitServiceにあります。

 たとえば、気圧のソースはここにあります。

https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/libraries/BLEAdafruitService/src/services/BLEAdafruitBaro.cpp

 ほかの温度などもほぼ同じ記述内容で、UUID、read/write/notifyの属性が記述されています。そして、BLEAdafruitSensor.cppを継承しています。この元となるプログラムでは、タイマの設定値(period_ms)に合わせて、32ビットのデータをwriteします。32ビットのデータ形式は固定小数点だと思われます。

 period_ms=0つまり最初は、センサのデータを読みます。Notifyが有効になったら、period_msの間隔でセンサのデータを読みに行きます。センサの値が更新されたら、writeします。

 Adafruit_BMP280_Library/Adafruit_BMP280.cppでは、温度を読み出す関数名はreadTemperature()で、getTemperatureSensor()は、そのポインタと記述されています。同様に、readPressure()関数で気圧を取得し、getPressureSensor(void) はそのポインタです。

 関数のポインタを指定しているため、

  bleBaro.begin(bmp280.getPressureSensor(), 100);
  bleTemp.begin(bmp280.getTemperatureSensor(), 100);

によって、100ms(period_ms)ごとに、getTemperatureSensor()で温度を読んできて、その読み取り値がbufにあるので、それをBLEのwriteしています。

 しかし、adafruit/Adafruit_SHT31のAdafruit_SHT31.cppにはreadHumidity()関数はありますが、ポインタは記述されていません。そこで、このプログラム内でmeasure_humid()関数を記述し、bufにreadHumidity()関数で読み取った湿度の値を書き込んでwriteします。

  bleHumid.begin(measure_humid, 100);

前へ

初めてのBLE (20) Feather nRF52840 Sense②BLE その1

次へ

初めてのBLE (22) Feather nRF52840 Sense④BLE その3