BLE Nano を光センサー CdS と組み合わせる

 前回までは BLE Nano をそのまま USB のライター DAPLink にさして使っていましたが、このままだと他のセンサー類と組み合わせることができないので、 BLE Nano をブレッドボードにさして使ってみます。まずは BLE Nano をブレッドボードにさして動作させられることを確認し、その後実際のセンサーと組み合わせて値を取得してみます。

ブレッドボードで配線

 写真だとちょっと分かりづらいですが、今回は下記のように配線しました。基本的には BLE Nano をブレッドボードにさして、DAPLink の対応するピンとオス-オスのジャンパーワイヤーで接続しています。

f:id:akanuma-hiroaki:20170918144703j:plain:w450 f:id:akanuma-hiroaki:20170918144714j:plain:w450

 今回は下記のピンを接続しました。

  • SWCLK
  • SWDIO
  • VIN
  • GND
  • TXD
  • RXD

 シリアル接続でのデバッグ等を行わなければ TXD, RXD は不要かもしれません。

 また、今回は LED と抵抗を接続して、前回のプログラムで BLE Nano のオンボードLEDを点滅させていたところを、ブレッドボード上の LED を点滅させてみます。 BLE Nano の P0_4 ピンから 1kΩ の抵抗を経由して LED のアノード側に接続し、カソード側と BLE Nano の GND ピンに接続しています。

ソースコードを変更してコンパイル&実行

 前回実装したコードの LED のピンの指定を下記のように変更します。

DigitalOut led(LED1, 0);
 ↓
DigitalOut led(P0_4, 0);

 そして mbed compile して hex ファイルを BLE Nano に転送すると、ブレッドボード上の LED が点滅を開始し、 LightBlue などでは前回同様にデバイスが検知され、 Characteristic の値が確認できます。

ボタン電池で動かす

 開発・デバッグ時は DAPLink に接続しているので電源もそこから取っていますが、実際に何か BLE デバイスを作成した時には単体で動くようにする必要があるので、ボタン電池で動かしてみます。配線は下記の写真のようにしました。

f:id:akanuma-hiroaki:20170923015011j:plain:w450

 DAPLink の VIN と GND にさしていたピンをそれぞれボタン電池ホルダーの + 側と - 側に繋いで、それ以外のコードを取り外しています。今回はスイッチなどはつけてないので、ボタン電池をつけるとすぐにプログラムが動き始めます。 LightBlue などで接続してみると今まで同様に動作していることが確認できます。

実際のセンサーと組み合わせる

 先ほどまではダミーの値をセンサー値として使用していましたが、今度は実際のセンサーと組み合わせてみます。今回は下記の光センサー CdSセルを使用してみました。

www.aitendo.com

 これを 4.7kΩの抵抗と組み合わせて下記の写真のように配線しました。

f:id:akanuma-hiroaki:20170923015435j:plain:w450 f:id:akanuma-hiroaki:20170923015458j:plain:w450

 コードも実際のセンサー値を使用するように変更します。 main.cpp は下記のようにしました。

#include "mbed.h"
#include "ble/BLE.h"
#include "LuxService.h"

Serial pc(USBTX, USBRX);
DigitalOut led(P0_4, 0);
AnalogIn cds(P0_5);

const static char DEVICE_NAME[] = "LUXSample";
static const uint16_t uuid16_list[] = {LuxService::LUX_SERVICE_UUID};

static volatile bool triggerSensorPolling = false;

unsigned short luxValue;
const int threshold = 500;

LuxService *luxServicePtr;

Ticker ticker;

BLE ble;

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
  BLE::Instance().gap().startAdvertising();
}

void periodicCallback(void)
{
  luxValue = cds.read_u16();
  if (luxValue >= threshold) {
    led = 0;
  } else {
    led = 1;
  }

  triggerSensorPolling = true;
}

void onBleInitError(BLE &ble, ble_error_t error)
{
}

void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
  BLE& ble = params->ble;
  ble_error_t error = params->error;

  if (error != BLE_ERROR_NONE) {
    onBleInitError(ble, error);
    return;
  }

  if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
    return;
  }

  ble.gap().setDeviceName((const uint8_t *) DEVICE_NAME);
  ble.gap().onDisconnection(disconnectionCallback);

  uint16_t initialValueForLUXCharacteristic = 100;
  luxServicePtr = new LuxService(ble, initialValueForLUXCharacteristic);

  ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
  ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
  ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
  ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
  ble.gap().setAdvertisingInterval(1000);
  ble.gap().startAdvertising();
}

int main(void)
{
  pc.baud(115200);
  pc.printf("Starting BLE_LUX Sample...\r\n");

  ticker.attach(periodicCallback, 1);

  pc.printf("Initializing BLE Controller...\r\n");
  ble.init(bleInitComplete);
  pc.printf("Initialized.\r\n");

  while (ble.hasInitialized() == false) { /* spin loop */ }

  pc.printf("Starting loop...\r\n");
  while (true) {
    if (triggerSensorPolling && ble.getGapState().connected) {
      triggerSensorPolling = false;
      pc.printf("%u\r\n", luxValue);
      luxServicePtr->updateLuxValue(luxValue);
    } else {
      ble.waitForEvent();
    }
  }
}

 今までダミーの値を生成していた部分を削除し、実際のセンサーデータを読み出す処理を追加しています。 CdSセルからのデータはアナログ入力として受け取ります。また、今までは一秒間隔で点滅させていたLEDを、センサーデータが閾値より小さかったら(暗かったら)点灯し、大きかったら(明るかったら)消すという処理にしています。

void periodicCallback(void)
{
  luxValue = (cds.read_u16() & 0xffff);
  if (luxValue >= threshold) {
    led = 0;
  } else {
    led = 1;
  }

  triggerSensorPolling = true;
}

 LuxService.h の方も全体を掲載しておきますが、前回との変更は受け取るセンサー値の型を unsigned short に変更しただけです。

#ifndef __BLE_LUX_SERVICE_H__
#define __BLE_LUX_SERVICE_H__

class LuxService {
public:
  const static uint16_t LUX_SERVICE_UUID = 0xA000;
  const static uint16_t LUX_VALUE_CHARACTERISTIC_UUID = 0xA001;

  LuxService(BLE &_ble, uint16_t initialValueForLUXCharacteristic) :
    ble(_ble), luxValue(LUX_VALUE_CHARACTERISTIC_UUID, &initialValueForLUXCharacteristic, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY)
  {
    GattCharacteristic *charTable[] = {&luxValue};
    GattService luxService(LUX_SERVICE_UUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
    ble.gattServer().addService(luxService);
  }

  void updateLuxValue(unsigned short newValue) {
    ble.gattServer().write(luxValue.getValueHandle(), (uint8_t *)&newValue, sizeof(bool));
  }

private:
  BLE &ble;
  ReadOnlyGattCharacteristic<uint16_t> luxValue;
};

#endif

 これで実際のセンサーの計測値をBLEで受け取ることができるようになります。 LightBlue で見てみると下記のように値が取得できます。

f:id:akanuma-hiroaki:20170923134018p:plain:w300

まとめ

 BLE Nano とボタン電池を使うことで簡単にワイヤレスで動作するデバイスが試作できました。どんなセンサーと組み合わせて何をするかはアイディア次第だと思いますので、日常的に役に立ちそうなものを考えてプロトタイプを作ってみたいと思います。

 ちなみに BLE Nano は日本でも V2 が発売されましたね。スペックも上がってるようですし、 DAPLink のバージョンも 1.0 から 1.5 に上がっているようです。DAPLink 1.0 でも BLE Nano V2 が使えるのかなどは気になるところです。

mag.switch-science.com