ESP32を使用してBLEスキャンアプリを作成します。その後、アルプスのセンサネットワークモジュールから環境データ、CC2541 SensorTagから温度データを取得します。WindowsでC#により作成したプログラムは、「C# .NET Frameworkを使ってアルプスのセンサネットワークモジュールからモーションデータの取得」、「C# .NET Frameworkを使ってCC2541 SensorTagから温度データの取得」にそれぞれ示します。なお、各データの取得のプログラムは「BLE_client」を参考にして作成しました。

BLEスキャンアプリのコンパイル・実行

次のようにスケッチ例から「BLE_scan」を選択して実行します。

実行した結果を次に示します。アルプスのセンサネットワークモジュールは、「Advertised Device: Name: , Address: 28:a1:83:e1:58:96」で表示されました。名称が検出されず、アドレスだけ表示されます。

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
Scanning...
Advertised Device: Name: , Address: 28:a1:83:e1:58:96
Advertised Device: Name: , Address: 69:b9:db:d9:dc:e5, manufacturer data: 060001092002df9ca2d6b42bbd48463354bb4703b029d9ada2d70543a0
Advertised Device: Name: , Address: 4a:8e:f0:7d:43:75, manufacturer data: 4c00100521188399b5, txPower: 8
Devices found: 3
Scan done!
Advertised Device: Name: , Address: 69:b9:db:d9:dc:e5, manufacturer data: 060001092002df9ca2d6b42bbd48463354bb4703b029d9ada2d70543a0
Advertised Device: Name: , Address: 4a:8e:f0:7d:43:75, manufacturer data: 4c00100521188399b5, txPower: 8
Advertised Device: Name: , Address: 28:a1:83:e1:58:96
Devices found: 3

CC2541 SensorTagからの温度データ取得

CC2541 SensorTagから温度データを取得するプログラムを次に示します。

  • 4行目でSensorTagのアドレスを指定し、検出したBLE機器のアドレスと137行目で比較し、SensorTagを検出します。
  • 103行目でのNotifyのDescriptorの指定は必要ありませんでした。
  • 109-110行目で温度データの出力要求を指示します。

SensortagCentral.ino

#include "BLEDevice.h"

// sensor tab
#define BLEADDRESS "b4:99:4c:64:cd:df"

// The remote service we wish to connect to.
static BLEUUID serviceUUID("F000AA00-0451-4000-B000-000000000000");
// The characteristic of the remote service we are interested in.
static BLEUUID    charUUID("F000AA01-0451-4000-B000-000000000000");
static BLEUUID    charcmdUUID("F000AA02-0451-4000-B000-000000000000");

static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLERemoteCharacteristic* pRemoteCmdCharacteristic;
static BLEAdvertisedDevice* myDevice;

static boolean initflg = false;
const uint8_t notifyOn[] = {0x1, 0x0};

static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
  Serial.print("Notify callback for characteristic ");
  Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
  Serial.print(" of data length ");
  Serial.println(length);
  Serial.print("data: ");
  for (int i = 0; i <= length - 1; i++) {
    Serial.print(String(*(pData + i), HEX));
    Serial.print(" ");
  }
  Serial.println("");
}

class MyClientCallback : public BLEClientCallbacks {
    void onConnect(BLEClient* pclient) {
    }

    void onDisconnect(BLEClient* pclient) {
      connected = false;
      Serial.println("onDisconnect");
    }
};

bool connectToServer() {
  Serial.print("Forming a connection to ");
  Serial.println(myDevice->getAddress().toString().c_str());

  BLEClient*  pClient  = BLEDevice::createClient();
  Serial.println(" - Created client");

  pClient->setClientCallbacks(new MyClientCallback());

  // Connect to the remove BLE Server.
  pClient->connect(myDevice);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
  Serial.println(" - Connected to server");

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");


  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(charUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");

  pRemoteCmdCharacteristic = pRemoteService->getCharacteristic(charcmdUUID);
  if (pRemoteCmdCharacteristic == nullptr) {
    Serial.print("Failed to find our cmdcharacteristic UUID: ");
    Serial.println(charcmdUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our cmdcharacteristic");

  // Read the value of the characteristic.
  if (pRemoteCharacteristic->canRead()) {
    //std::string value = pRemoteCharacteristic->readValue();
    //Serial.println(value.c_str());
    Serial.print("The characteristic value was: ");
    uint8_t value = pRemoteCharacteristic->readUInt8();
    Serial.println(String(value, HEX));
  }

  if (pRemoteCharacteristic->canNotify()) {

//    pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notifyOn, 2, true);
    pRemoteCharacteristic->registerForNotify(notifyCallback);

    Serial.println("canNotify!!");
  }

  byte comm1[] = { 0x01 };
  pRemoteCmdCharacteristic->writeValue(comm1, 1);

  connected = true;

}
/**
   Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    /**
        Called for each advertising BLE server.
    */
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      Serial.print("BLE Advertised Device found: ");
      Serial.println(advertisedDevice.toString().c_str());
      //Serial.println(advertisedDevice.getName().c_str());
      if (advertisedDevice.haveServiceUUID()) {
        Serial.println(advertisedDevice.getServiceUUID().toString().c_str());
      }
      else {
        Serial.println("No ServiceUUID ");
      }

      Serial.println(advertisedDevice.getAddress().toString().c_str());

      // We have found a device, let us now see if it contains the service we are looking for.
      //if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
      if (advertisedDevice.getAddress().toString() == BLEADDRESS) {
        Serial.println("Sensortag!! ");

        BLEDevice::getScan()->stop();
        myDevice = new BLEAdvertisedDevice(advertisedDevice);
        doConnect = true;
        doScan = true;

      } // Found our server
    } // onResult
}; // MyAdvertisedDeviceCallbacks

void setup() {
  Serial.begin(115200);
  Serial.println("Starting Arduino BLE Client application...");
  BLEDevice::init("");

  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 5 seconds.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setInterval(1349);
  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5, false);
} // End of setup.


// This is the Arduino main loop function.
void loop() {

  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
  if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("We are now connected to the BLE Server.");
    } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
    }
    doConnect = false;
  }

  delay(1000); // Delay a second between loops.
} // End of loop

実行した結果を次に示します。名称は「SensorTag」と表示され、アドレスは「b4:99:4c:64:cd:df」となります。Notify callbackにより、温度データが表示されます。

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
Starting Arduino BLE Client application...
BLE Advertised Device found: Name: , Address: 52:18:13:48:eb:aa, manufacturer data: 4c0010052118944155, txPower: 8
No ServiceUUID 
52:18:13:48:eb:aa
BLE Advertised Device found: Name: SensorTag, Address: b4:99:4c:64:cd:df, txPower: 0
No ServiceUUID 
b4:99:4c:64:cd:df
Sensortag!! 
Forming a connection to b4:99:4c:64:cd:df
 - Created client
 - Connected to server
 - Found our service
 - Found our characteristic
 - Found our cmdcharacteristic
The characteristic value was: f0
canNotify!!
We are now connected to the BLE Server.
Notify callback for characteristic f000aa01-0451-4000-b000-000000000000 of data length 4
data: e1 fe b0 8 
Notify callback for characteristic f000aa01-0451-4000-b000-000000000000 of data length 4
data: e3 fe b0 8 

アルプスのセンサネットワークモジュールからの環境データ取得

アルプスのセンサネットワークモジュールからの環境データ(Pressure、Humidity、Temperature)を取得するプログラムを次に示します。

  • 186-204行目で環境データの出力を要求します。「delay(100)」を設定する必要があります。設定しない場合、ステータス「0xE0」のみが出力されます。

AlpsCentral.ino

/**
   A BLE client example that is rich in capabilities.
   There is a lot new capabilities implemented.
   author unknown
   updated by chegewara
*/

#include "BLEDevice.h"
//#include "BLEScan.h"

// ALPS
#define BLEADDRESS "28:a1:83:e1:58:96"

// The remote service we wish to connect to.
static BLEUUID serviceUUID("47FE55D8-447F-43EF-9AD9-FE6325E17C47");
// The characteristic of the remote service we are interested in.
static BLEUUID    charUUID("686A9A3B-4C2C-4231-B871-9CFE92CC6B1E");
//static BLEUUID    charUUID("078FF5D6-3C93-47F5-A30C-05563B8D831E");
static BLEUUID    charcmdUUID("B962BDD1-5A77-4797-93A1-EDE8D0FF74BD");


static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLERemoteCharacteristic* pRemoteCmdCharacteristic;
static BLEAdvertisedDevice* myDevice;

static boolean initflg = false;
const uint8_t notifyOn[] = {0x1, 0x0};

void dataana(uint8_t* pData){
  uint8_t test[20] = { 0xf3, 0x14, 0x79, 0xe3, 0x40, 0x16, 0x23, 0xc, 1, 0, 3, 0, 0, 0, 0, 0, 1, 0xa, 0xf, 0x74 }; 

  //pData = test;
  
  float Pressure = (float)((uint32_t)(*(pData + 3))<<8);
  Pressure = Pressure + *(pData + 2);
  float Humidity = (float)((uint32_t)(*(pData + 5))<<8);
  Humidity = Humidity + *(pData + 4);
  float Temperature = (float)((uint32_t)(*(pData + 7))<<8);
  Temperature = Temperature + *(pData + 6);
  
  Pressure = Pressure*860/65535+250;
  Humidity = (Humidity- 896)/64;
  Temperature = (Temperature-2096)/50;
  
  Serial.print("Pressure: ");
  Serial.println(String(Pressure));
  Serial.print("Humidity: ");
  Serial.println(String(Humidity));
  Serial.print("Temperature: ");
  Serial.println(String(Temperature));

}

static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
  Serial.print("Notify callback for characteristic ");
  Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
  Serial.print(" of data length ");
  Serial.println(length);
  Serial.print("data: ");
  for (int i = 0; i <= length - 1; i++) {
    Serial.print(String(*(pData + i), HEX));
    Serial.print(" ");
  }
  Serial.println("");
  if(*(pData) == 0xf3){
    dataana(pData);
  }
}

class MyClientCallback : public BLEClientCallbacks {
    void onConnect(BLEClient* pclient) {
    }

    void onDisconnect(BLEClient* pclient) {
      connected = false;
      Serial.println("onDisconnect");
    }
};

bool connectToServer() {
  Serial.print("Forming a connection to ");
  Serial.println(myDevice->getAddress().toString().c_str());

  BLEClient*  pClient  = BLEDevice::createClient();
  Serial.println(" - Created client");

  pClient->setClientCallbacks(new MyClientCallback());

  // Connect to the remove BLE Server.
  pClient->connect(myDevice);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
  Serial.println(" - Connected to server");

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");


  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(charUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");
  
  pRemoteCmdCharacteristic = pRemoteService->getCharacteristic(charcmdUUID);
  if (pRemoteCmdCharacteristic == nullptr) {
    Serial.print("Failed to find our cmdcharacteristic UUID: ");
    Serial.println(charcmdUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our cmdcharacteristic");

  // Read the value of the characteristic.
  if (pRemoteCharacteristic->canRead()) {
    //std::string value = pRemoteCharacteristic->readValue();
    //Serial.println(value.c_str());
    Serial.print("The characteristic value was: ");
    uint8_t value = pRemoteCharacteristic->readUInt8();
    Serial.println(String(value, HEX));
  }

  if (pRemoteCharacteristic->canNotify()) {

    //pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notifyOn, 2, true);
    pRemoteCharacteristic->registerForNotify(notifyCallback);
    
    Serial.println("canNotify!!");
  }
  
  //setcommand();

  connected = true;

}
/**
   Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    /**
        Called for each advertising BLE server.
    */
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      Serial.print("BLE Advertised Device found: ");
      Serial.println(advertisedDevice.toString().c_str());
      //Serial.println(advertisedDevice.getName().c_str());
      if (advertisedDevice.haveServiceUUID()) {
        Serial.println(advertisedDevice.getServiceUUID().toString().c_str());
      }
      else {
        Serial.println("No ServiceUUID ");
      }

      Serial.println(advertisedDevice.getAddress().toString().c_str());

      // We have found a device, let us now see if it contains the service we are looking for.
      //if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
      if (advertisedDevice.getAddress().toString() == BLEADDRESS) {
        Serial.println("ALPS!! ");

        BLEDevice::getScan()->stop();
        myDevice = new BLEAdvertisedDevice(advertisedDevice);
        doConnect = true;
        doScan = true;

      } // Found our server
    } // onResult
}; // MyAdvertisedDeviceCallbacks

void setcommand() {
      byte comm1[3] = { 0x2F, 0x03, 0x03 };    
      pRemoteCmdCharacteristic->writeValue(comm1, 3);
      delay(100);
      byte comm2[3] = { 0x01, 0x03, 0x7c};    
      pRemoteCmdCharacteristic->writeValue(comm2, 3);
      delay(100);
      byte comm3[3] = { 0x04, 0x03, 0x00 };    
      pRemoteCmdCharacteristic->writeValue(comm3, 3);
      delay(100);
      byte comm4[4] = { 0x05, 0x04, 0x05, 0x00 };    
      pRemoteCmdCharacteristic->writeValue(comm4, 4);
      delay(100);
      byte comm5[3] = { 0x2F, 0x03, 0x01 };    
      pRemoteCmdCharacteristic->writeValue(comm5, 3);
      delay(100);
      byte comm6[3] = { 0x20, 0x03, 0x01 };    
      pRemoteCmdCharacteristic->writeValue(comm6, 3);
}
void setup() {
  Serial.begin(115200);
  Serial.println("Starting Arduino BLE Client application...");
  BLEDevice::init("");

  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 5 seconds.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setInterval(1349);
  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5, false);
} // End of setup.


// This is the Arduino main loop function.
void loop() {

  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
  if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("We are now connected to the BLE Server.");
    } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
    }
    doConnect = false;
  }
  if (connected) {
    setcommand();
    connected = false;
  }
  delay(1000); // Delay a second between loops.
} // End of loop

実行した結果を次に示します。Notify callbackにより、「Pressure:」「Humidity:」「Temperature: 」として計測した環境データが表示されます。

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:9720
ho 0 tail 12 room 4
load:0x40080400,len:6352
entry 0x400806b8
Starting Arduino BLE Client application...
BLE Advertised Device found: Name: , Address: 28:a1:83:e1:58:96
No ServiceUUID
28:a1:83:e1:58:96
ALPS!!
Forming a connection to 28:a1:83:e1:58:96
 - Created client
 - Connected to server
 - Found our service
 - Found our characteristic
 - Found our cmdcharacteristic
The characteristic value was: f3
canNotify!!
We are now connected to the BLE Server.
Notify callback for characteristic 686a9a3b-4c2c-4231-b871-9cfe92cc6b1e of data length 20
data: f2 14 0 80 0 80 0 80 0 80 0 80 0 80 0 0 1c 1 0 d
Notify callback for characteristic 686a9a3b-4c2c-4231-b871-9cfe92cc6b1e of data length 20
data: f3 14 b e3 28 18 fd b 0 0 3 0 0 0 0 0 1 a f d
Pressure: 1012.73
Humidity: 82.62
Temperature: 19.46
Notify callback for characteristic 686a9a3b-4c2c-4231-b871-9cfe92cc6b1e of data length 20
data: e0 14 0 0 0 0 c7 b5 9 0 1 0 0 0 0 0 0 0 0 0
Notify callback for characteristic 686a9a3b-4c2c-4231-b871-9cfe92cc6b1e of data length 20
data: f2 14 0 80 0 80 0 80 0 80 0 80 0 80 0 0 24 1 0 0