Bluetooth Beaconプロジェクトの失敗の多くは、ハードウェアの問題ではなくファームウェアに起因します。電池を数年ではなく数週間で消費し、RF干渉下でクラッシュし、ブラウンバウト後にサイレントに広告を停止するファームウェアです。同じCR2032で3ヶ月しか持たないビーコンと3年持つビーコンの違いは、ファームウェアが無線イベントをどうスケジュールし、電力状態をどう管理し、障害からどう復旧するかにあります。本記事では、nRF52 + SoftDeviceを参照プラットフォームとして使用し、量産レベルのビーコンファームウェアのアーキテクチャを解説します。nRF52は現在の商用ビーコンの約70%で使用されています。
1. 全体像:ファームウェアアーキテクチャが電池寿命を決定する理由
ビーコンは寿命の99.97%をスリープ状態で過ごします。1秒の広告間隔と3チャネル3バイトペイロードの場合、無線は1秒あたり約300マイクロ秒しかアクティブになりません。つまり1日86,400秒のうち、無線が送信するのは合計約26秒です。残りの86,374秒はスリープであり、どれだけ深くスリープできるかがすべてを決定します。
| 状態 | nRF52832消費電流 | 時間/イベント(1s) | 平均電流寄与 |
|---|---|---|---|
| ディープスリープ(RAM保持) | 1.5 uA | ~999.6 ms | 1.499 uA |
| ランプアップ+クリスタル起動 | 3.2 mA | ~130 us | 0.416 uA |
| Ch37送信(0 dBm) | 4.6 mA | ~80 us (3バイト) | 0.368 uA |
| チャネル間ギャップ | 1.8 mA | ~150 us | 0.270 uA |
| Ch38+Ch39送信 | 4.6 mA | ~160 us | 0.736 uA |
| ランプダウン+RTC処理 | 2.1 mA | ~90 us | 0.189 uA |
| 合計平均(1s間隔、0 dBm、3バイト) | 3.478 uA | ||
CR2032(公称220 mAh、3Vパルス負荷で使用可能約160 mAh):160,000 uAh / 3.478 uA = 46,000時間 = 5.2年。これが理論上限です。実際のファームウェアでは、センサーのポーリング、LED点滅、ボタンスキャン、ウォッチドッグ割り込み、RTCドリフト補正が追加され、それぞれが平均5〜50 uAを追加します。1秒あたり1回の不要な1 msウェイクアップが3 mAで3 uAを追加します。スリープ予算のほぼ2倍です。
2. 無線スケジューラ:広告イベントの実際のスケジューリング
SoftDevice(NordicのBLEスタック)は、プリエンプティブな無線スケジューラとして機能します。アプリケーションコードは無線に直接アクセスしません。代わりに広告パラメータを設定すると、SoftDeviceが無線イベントを内部タイムラインに配置します。各無線イベントにはセットアップオーバーヘッドがあるため、このタイムラインを理解することが重要です。
2.1 SoftDeviceイベントタイムライン
SoftDevice無線タイムライン(広告間隔ごと)
[RC] Tx37 [RD] gap [RC] Tx38 [RD] gap [RC] Tx39 [RD]
130us 80us 20us 130us 80us 20us 130us 80us 20us
RC = 無線ランプアップ+クリスタル安定化
RD = 無線ランプダウン
総アクティブ時間: ~610 us(3バイト、0 dBm)
チャネル間ギャップはユーザー設定不可です。内部的に最小150 usに設定されています。広告と並行してスキャンや接続モードを有効にすると、SoftDeviceがこれらのイベントをインターリーブし、広告タイミングが最大+/-200 usジッターします。
2.2 広告間隔とデューティサイクル
| 間隔 | イベント/時間 | 無線アクティブ/時間 | デューティサイクル | 平均無線電流 |
|---|---|---|---|---|
| 100 ms | 36,000 | 22.0 s | 0.0061% | 28.0 uA |
| 200 ms | 18,000 | 11.0 s | 0.0030% | 14.0 uA |
| 500 ms | 7,200 | 4.4 s | 0.0012% | 5.6 uA |
| 1000 ms | 3,600 | 2.2 s | 0.0006% | 2.8 uA |
| 2000 ms | 1,800 | 1.1 s | 0.0003% | 1.4 uA |
| 5000 ms | 720 | 0.44 s | 0.0001% | 0.56 uA |
0 dBmのTX電流はnRF52832で4.6 mAです。100 ms間隔では無線だけで平均28 uAを消費し、2年のCR2032寿命を目指す場合のスリープ予算3 uAを既に超えています。100 ms間隔はUSB給電ビーコンまたはAA電池パック(2500+ mAh)のユニットでのみ実現可能です。
2.3 非接続広告と接続可能広告:レイテンシの罠
非接続広告(ADV_NONCONN_IND)はファイアアンドフォーゲットです。接続可能広告(ADV_IND)は各送信後に設定可能なリッスンウィンドウの間無線をRxモードに維持する必要があります。スキャンレスポンスを有効にすると、無線はチャネルあたり最大10 ms Rxモードに留まり、1間隔あたり30 msの5.4 mA Rx電流です。
| モード | 追加Rx時間/間隔 | 1s追加電流 | 5s追加電流 |
|---|---|---|---|
| ADV_NONCONN(スキャンレスポンスなし) | 0 ms | 0 uA | 0 uA |
| ADV_IND + スキャンレスポンス(10ms) | 30 ms | 162 uA | 32.4 uA |
| ADV_IND + 接続(1s間隔) | 継続 | ~800 uA | ~800 uA |
スキャンレスポンスデータを広告する場合、1s間隔での162 uAオーバーヘッドはスリープ電流の47倍です。多くの量産ビーコンがスキャンレスポンスを完全に廃止し、すべてを31バイトのメーカー固有データペイロードに詰め込む理由です。
3. 電力状態マシン:ビーコンファームウェアの心臓
よく設計されたビーコンファームウェアには、明示的で文書化された電力状態マシンがあります。各状態には定義された消費電流、入出力条件、最大滞在時間があります。以下は大手OEMでの量産ビーコンファームウェアで使用される状態マシンです:
3.1 電力状態定義
ビーコン電力状態マシン
[DEEP SLEEP] --RTC割込--> [ADV EVENT (Active)]
1.5 uA 5.2 mA
| |
| ボタン割込 | センサー保留?
v v
[BUTTON HANDLER] [SENSOR READ]
3.0 mA 1.2 mA
| |
| 500msタイムアウト | 2ms読取
v v
[CONFIG MODE] [DEEP SLEEP]
8.5 mA 1.5 uA
障害パス: WDTタイムアウト -> RESET -> INIT -> SLEEP
3.2 状態遷移予算
| 状態 | 電流 | 典型時間 | エネルギー/イベント | イベント/日 | 日次エネルギー |
|---|---|---|---|---|---|
| ディープスリープ(RAM保持) | 1.5 uA | 999.4 ms | 1.499 uJ | 86,400 | 129.5 mJ |
| 広告イベント | 5.2 mA | 610 us | 9.52 uJ | 86,400 | 822.5 mJ |
| センサー読取(LIS2DH12) | 1.2 mA | 2 ms | 7.2 uJ | 2,880(30s) | 20.7 mJ |
| ボタンスキャン(GPIO) | 3.0 mA | 0.1 ms | 0.9 uJ | 86,400 | 77.8 mJ |
| 設定モード(GATT) | 8.5 mA | ~5 s | 127.5 mJ | ~2 | 255 mJ |
| RTC+イベント処理 | 3.2 mA | 50 us | 0.48 uJ | 86,400 | 41.5 mJ |
| 日次合計エネルギー | 1,347 mJ | ||||
CR2032エネルギー:220 mAh x 3.0V = 2,376 J。使用可能(80%):約1,520 J。日次予算:1,347 mJ -> 1,520,000 / 1,347 = 1,128日 = 3.1年。1s間隔、30s加速度センサー、週次設定接続のビーコンのフィールドデータと一致します。
3.3 System OFF vs System ONの決定
nRF52は2つの低電力モードを提供します。間違った選択はビーコン設計で最も一般的なファームウェアのミスです。
| パラメータ | System ON (WFE) | System OFF |
|---|---|---|
| 電流 | 1.5 uA(RAM保持) | 0.4 uA(RAM保持なし) |
| ウェイクアップレイテンシ | ~2 us | ~300 us(リセット+init) |
| RAM保持 | あり | なし |
| RTC動作 | あり | なし(GPIOセンス使用) |
| SoftDevice状態 | 保持 | 破棄(再init必要) |
SoftDeviceを使用するビーコンではSystem ONが必須です。SoftDeviceは次の広告イベントをスケジュールするためにRTCを必要とします。System OFFは出荷/保管モードでのみ有用です。
4. タイマーアーキテクチャ:RTC vs SysTick vs アプリケーションタイマー
ビーコンファームウェアは通常3層のタイミングを使用し、それぞれ異なる精度と電力への影響があります:
| 層 | ハードウェア | クロック源 | 精度 | 電力 | 分解能 |
|---|---|---|---|---|---|
| BLEスタック | RTC1 | 32.768 kHz LFXO | +/-20 ppm | 0.3 uA | 30.5 us |
| アプリタイマー | RTC2 | 32.768 kHz LFXO | +/-20 ppm | 0.1 uA | 30.5 us |
| 高分解能遅延 | TIMER0/1/2 | 16 MHz HCLK | +/-50 ppm | 5.5 mA | 62.5 ns |
SoftDeviceはRTC1を独占します。アプリタイマーはソフトウェアコールバックキューでRTC1を共有し、最大1 RTCティック(30.5 us)のジッターが発生します。ビーコンファームウェアでは無関係ですが、正確な100 Hzサンプリングにはハードウェアタイマーが必要です。
4.2 クロックドリフトの影響
+/-20 ppmのクリスタルは、RTCが1秒あたり最大20 usドリフトすることを意味します。24時間で1.728秒です。SoftDeviceは内部補償しますが、アプリレベルのタイマーはドリフトします:
#define CRYSTAL_PPM 20
#define SECONDS_PER_DAY 86400
float drift_per_day = (float)CRYSTAL_PPM * SECONDS_PER_DAY / 1e6; // 1.728 s/day
4.3 タイマーコアレシング:ウェイクアップ削減
app_timerライブラリはコアレシングをサポートします。2つのタイマーが同じRTCティックウィンドウで期限切れになる場合、単一のウェイクアップにマージされます。最適化戦略:タイマーイベントをバッチ化します。30秒ごとに加速度センサーをポーリングし、かつ30秒ごとに温度センサーをポーリングする(2ウェイクアップ)代わりに、同じ30秒境界に揃えます(1ウェイクアップで両方を読む)。センサー読取エネルギー予算を半減します。
5. イベント駆動アーキテクチャ:割込み -> キュー -> ハンドラ
量産ビーコンファームウェアはビジーウェイトループを使用すべきではありません。すべての操作はイベント駆動であるべきです:
typedef enum {
EVT_ADV_COMPLETE = 0,
EVT_SENSOR_TIMER,
EVT_BUTTON_PRESS,
EVT_BATTERY_LOW,
EVT_GATT_CONNECT,
EVT_OTA_START,
EVT_WATCHDOG,
EVT_FAULT,
} beacon_event_t;
#define EVENT_QUEUE_SIZE 16
static event_t event_queue[EVENT_QUEUE_SIZE];
static volatile uint8_t eq_head = 0, eq_tail = 0;
void event_post(beacon_event_t type, uint32_t data) {
uint8_t next = (eq_head + 1) % EVENT_QUEUE_SIZE;
if (next == eq_tail) { fault_set(FAULT_EVENT_QUEUE_OVERFLOW); return; }
event_queue[eq_head].type = type;
event_queue[eq_head].data = data;
event_queue[eq_head].timestamp = rtc_get_ticks();
eq_head = next;
}
int main(void) {
beacon_init();
advertising_start();
event_t evt;
while (1) {
if (event_get(&evt)) event_handler(&evt);
else __WFE();
}
}
設計原則:(1) ISRはイベントをポストするのみ。(2) メインループはFIFO順で処理。(3) 保留イベントがない場合、CPUはWFEに入り1.5 uAに低下。(4) キューオーバーフローはフォールトフラグを設定するがブロックしない。
6. ウォッチドッグと障害復旧
| 層 | トリガー | 応答 | 復旧時間 |
|---|---|---|---|
| L1: ソフト障害 | キュー溢れ、センサータイムアウト | 記録、スキップ、継続 | 0 ms |
| L2: ハード障害 | 無線停止(60s)、SPIロック | ペリフェラルリセット | 50-200 ms |
| L3: システム障害 | WDTタイムアウト(8s) | フルリセット | 500ms-2s |
#define WDT_TIMEOUT_MS 8000
void wdt_init(void) {
NRF_WDT->CONFIG = (WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos);
NRF_WDT->CRV = 32768 * (WDT_TIMEOUT_MS / 1000);
NRF_WDT->RREN = (1 << 0) | (1 << 1);
NRF_WDT->TASKS_START = 1;
}
void wdt_feed(void) { NRF_WDT->RR[0] = 0x6E524635; }
6.3 ブラウンバウト復旧
CR2032の内部抵抗は新品5-15 ohm、寿命末期30+ ohmです。4.6 mA送信時、4.6 mA x 10 ohm = 46 mV低下します。2.3V電池では2.254Vに低下します。
| 電池電圧 | 内部抵抗 | Vdrop | Vmin | BORリスク |
|---|---|---|---|---|
| 3.0V | 5 ohm | 23 mV | 2.977V | なし |
| 2.6V | 10 ohm | 46 mV | 2.554V | なし |
| 2.3V | 15 ohm | 69 mV | 2.231V | 低 |
| 2.1V | 30 ohm | 138 mV | 1.962V | 高 |
7. メモリレイアウトと永続ストレージ
| 領域 | サイズ | 内容 |
|---|---|---|
| SoftDevice (MBR + S132) | 160 KB | MBR + BLEスタック |
| アプリケーション | 308 KB | メインファームウェア |
| 設定ページ(NVS) | 16 KB | 広告間隔、TX電力、UUID |
| アプリデータ(NVS) | 16 KB | センサーログ、障害履歴 |
| ブートローダ | 16 KB | DFU + CRC |
nRF52832はデュアルバンクOTAに十分なフラッシュがありません。安全なロールバックが必要な場合は1 MBのnRF52840が必要です。
8. BLEスタック統合
| パラメータ | 値 | 根拠 |
|---|---|---|
| 広告タイプ | ADV_NONCONN_IND | 通常運用時は接続不可 |
| 間隔 | 1000 ms | 発見性と電池寿命のバランス |
| TX電力 | 0 dBm | ~30m視距 |
| チャネルマップ | 37+38+39 | 全チャネル使用 |
設定モードでは100 ms接続可能間隔で~300 uAに跳ね上がります。5分タイムアウトで電池ドレインを制限:300 uA x 5分 = 25 uAh、CR2032の0.02%未満/セッション。
9. OTAファームウェア更新
| パラメータ | 値 | 備考 |
|---|---|---|
| MTU | 247バイト | ネゴシエーション |
| 接続間隔 | 15 ms | スループットと電力のバランス |
| ファームウェアサイズ | ~120 KB | アプリ領域のみ |
| 転送時間 | ~8秒 | 508 x 15ms |
| OTA中電流 | ~8.5 mA | 接続+フラッシュ書込 |
| 電池影響 | ~28 uAh | 0.017%/更新 |
10. 量産指標とフィールドデータ
| 指標 | 目標 | フィールド結果(n=2,847) |
|---|---|---|
| リセット間平均時間 | > 6ヶ月 | 11.3ヶ月 |
| WDTリセット | < 1% | 0.3% |
| OTA成功率 | > 99% | 99.7% |
| 電池寿命(1s, 23C) | > 24ヶ月 | 28.4ヶ月 |
| 電池寿命(1s, 0C) | > 18ヶ月 | 19.7ヶ月 |
11. 一般的な落とし穴と修正
| 落とし穴 | 症状 | 修正 |
|---|---|---|
| フローティングGPIO | +8-15 uA | プルダウンor出力Low |
| SPIバス解放忘れ | +0.5 mA | 各トランザクション後に無効化 |
| SAADC有効残留 | +0.2 uA | uninit()呼出 |
| 過密フラッシュログ | 早期摩耗 | RAM蓄積5分毎フラッシュ |
| ISR内ブロッキング | BLEタイミング違反 | イベントポスト |
| スタックオーバーフロー | ランダムフォールト | 静的バッファ+MPUガード |
13. まとめ
ビーコンファームウェアは本質的に、ほとんど何もしないことをほぼ常に続け、決して故障しないことに関するものです。スリープモードで節約する1マイクロアンペアごとに、フィールド寿命の数ヶ月が追加されます。カバーする障害パスごとに、ビーコン自体のコストを超えるサポートチケットを防ぎます。アーキテクチャを正しく設計すれば、Bluetooth Beaconは電池保証期間を超えて稼働し、フィールド返品ゼロを達成できます。