センサデバイス(クライアント)の全体動作は、下記の手順となる。消費電力の観点から考えると、アドバタイジング後に、いかに早くマスター(サーバー)に見つけてもらうか、つまり接続要求が早急に来るかが鍵になるだろう。また、スリープ時間を固定すれば、項目4の「スリープ時間受信」は省略できるが、ここでは実利用を考慮して双方向通信とした。

一方、センサデバイスのデバイス番号は、現状ではプログラム内で定義しているため、デバイスごとにプログラムを書き換えるのは、手間が掛かり間違いも生じやすい。実際の運用では、初回のマスターとの接続時に、マスターからユニークな番号を受け取りEEPROMに書き込んで、その番号で次回から起動する方法などが考えられる。

  1. 起動後のアドバタイジング(サービスUUID提供)により、マスターからの接続を待つ
  2. 一定時間(現状5秒)内に接続要求がなければ、ディープスリープモードへ
  3. 接続要求があれば、デバイス番号とセンサ情報(現在はブート回数)を送信
  4. マスターからのスリープ時間を受信
  5. マスターからの切断要求によりディープスリープモードへ(スリープ後に再起動)
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLE2902.h>
#define DEVICE_NAME "BVNET"
#define deviceNumber 1 // 1, 2, 3, ... (連番で設定)
// サービスUUID
#define SERVICE_UUID "19ce426e-6c37-4cd2-b8b4-b7f51b065a3c"
// 送信用UUID
#define CHARACTERISTIC_UUID_TX "76ac6030-b243-439f-b96e-b964c45e4180"
// 受信用UUID
#define CHARACTERISTIC_UUID_RX "4fe2d1f9-c424-4124-8dc5-6f06b73ad9c5"
// 送受信キャラクタリスティック
BLECharacteristic *pCharacteristicTX;
BLECharacteristic *pCharacteristicRX;
// RTCスローメモリ変数:ディープスリープ保持
RTC_DATA_ATTR uint32_t bootCount = 0; // ブート回数
RTC_DATA_ATTR uint8_t sleepTime = 30; // デフォルト・スリープ時間(秒単位) 
uint16_t timeOut = 50;           // マスター不在タイムアウト時間(100msec単位)
int unitTime = 100;              // 100msec基本割込み
bool sleepFlag = false;          // ディープスリープ突入フラグ
BLEAdvertising *pAdvertising;    // アドバタイズクラス
bool masterConnected = false;    // マスター接続フラグ
bool masterDisconnected = false; // マスター切断フラグ
uint8_t notifyData[5];           // Notifyデータ(デバイス番号1byte, ブート回数4byte)
// マスター接続・切断イベント
class masterNetWorkEvents: public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    masterConnected = true;
  }
  void onDisconnect(BLEServer* pServer) {
    masterDisconnected = true;
  }
};
// マスター受信イベント
class masterReceiveEvent: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pCharacteristicRX) {
    std::string rxValue = pCharacteristicRX->getValue();
    sleepTime = rxValue[0];
    Serial.printf("Sleep Time %d\n", sleepTime);
  }
};
// 起動方法取得(ディープスリープからのウェイクアップ状態)
void getWakeupReason() { 
  esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  switch(wakeup_reason) {
    case ESP_SLEEP_WAKEUP_EXT0     : break; // 外部割り込み
    case ESP_SLEEP_WAKEUP_EXT1     : break; // 外部割り込み
                                            // タイマー割り込み
    case ESP_SLEEP_WAKEUP_TIMER    : Serial.printf("\nWake Up %d\n", ++bootCount); break; 
    case ESP_SLEEP_WAKEUP_TOUCHPAD : break; // タッチ割り込み
    case ESP_SLEEP_WAKEUP_ULP      : break; // ULPプログラム
    case ESP_SLEEP_WAKEUP_GPIO     : break; // ライトスリープからGPIO割り込み
    case ESP_SLEEP_WAKEUP_UART     : break; // ライトスリープからUART割り込み
    default                        : Serial.println("\nPower ON"); break;
  }
}
// タイマー管理用の構造体ポインター
hw_timer_t *timer = NULL;
// 基本割込みサービスルーチン(100msec間隔)
void IRAM_ATTR baseInterrupt() {
  static int loop = 0;
  static bool flush = true;
  // マスター不在タイムアウト(ディープスリープ突入確認)
  loop++;
  if (loop == timeOut) sleepFlag = true;
}
// 遅延処理(単位:ミリ秒)
void delayMSec(int mSecond) {
  vTaskDelay(mSecond / portTICK_RATE_MS);
}
// スリープモード突入
void enterSleepMode() {
  esp_sleep_enable_timer_wakeup(1000 * 1000 * sleepTime); // 秒からマイクロ秒へ変換
  esp_deep_sleep_start();
}
// マスターとの双方向通信(Notify後にスリープ時間受信イベント発生)
void handshakeMaster() {
  notifyData[0] = (uint8_t)deviceNumber;
  notifyData[1] = bootCount >> 24 & 0xFF;
  notifyData[2] = bootCount >> 16 & 0xFF;
  notifyData[3] = bootCount >> 8 & 0xFF;
  notifyData[4] = bootCount & 0xFF;
  pCharacteristicTX->setValue((uint8_t*)¬ifyData, sizeof(notifyData));
  pCharacteristicTX->notify();
}
// セットアップ
void setup() {
  Serial.begin(115200);
  while(!Serial) {}   // シリアルポート準備待ち  
  Serial.println("");
  getWakeupReason();  // ウェイクアップ要因取得(ブート回数更新)
  // デバイス初期化
  BLEDevice::init(DEVICE_NAME);
  // サーバーオブジェクト生成
  BLEServer *pServer = BLEDevice::createServer();
  // マスター接続・切断イベント登録
  pServer->setCallbacks(new masterNetWorkEvents());
  // サービスオブジェクト生成
  BLEService *pService = pServer->createService(SERVICE_UUID);
  // Notifyキャラクタリスティック生成
  pCharacteristicTX = pService->createCharacteristic(
    CHARACTERISTIC_UUID_TX,
    BLECharacteristic::PROPERTY_NOTIFY
  );
  pCharacteristicTX->addDescriptor(new BLE2902());
  // 受信キャラクタリスティック生成
  pCharacteristicRX = pService->createCharacteristic(
    CHARACTERISTIC_UUID_RX,
    BLECharacteristic::PROPERTY_WRITE
  );
  // マスター受信イベント登録
  pCharacteristicRX->setCallbacks(new masterReceiveEvent());
  // サービス開始
  pService->start();
  // アドバタイジング開始
  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->start();
  // タイマー割込み設定
  timer = timerBegin(0, 80, true); // timer = 1usec
  timerAttachInterrupt(timer, &baseInterrupt, true);
  timerAlarmWrite(timer, unitTime * 1000, true);
  timerAlarmEnable(timer);
}
// メインループ
void loop() {
  static bool advertisingStop = false;
  // マスター接続直後にアドバタイジング停止
  if (masterConnected && !advertisingStop) {
    advertisingStop = true;
    pAdvertising->stop();
  }
  // 「マスターとの双方向通信(接続中 and 切断前)」
  if (masterConnected && !masterDisconnected) {
    delayMSec(100); // Notify連打防止(100msec間隔)
    handshakeMaster();
  }
  // 「タイムアウト and 非接続状態」 or 「接続・切断プロセス」
  if ((sleepFlag && !masterConnected) || (masterConnected && masterDisconnected)) {
    enterSleepMode(); // ディープスリープ突入
  }
}
// End of File

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です