ESP32マイコンの電池駆動を考える(3)

PC/デジモノ

ESP32-C3を電池のみで長時間駆動させる検証を行っています。

BME280などのセンサーで取得した環境情報などをWifiを通してSQLサーバーにアップロードする方法で、
データ送信時以外はDeepSleepさせることで電力消費量の削減をしました。この結果、18650型のLFPバッテリー1個を用いて、30秒間隔でデータ送信をするという条件で500時間弱の駆動を実現しました。

今回はさらなる消費電力低減を目指して、BluetoothLowEnergyを用いた方法を検証してみました。

BluetoothLowEnergyとは?

BluetoothLowEnergy(以下BLE)は低消費電力でデータを送信する仕組みです。

Bluetooth Low Energy - Wikipedia

XBOXのゲームコントローラーやSwitchBotの各種センサーなどで使用されています。

BLEには、従来のBluetooth(BluetoothClassic)のように2つの機器間で接続を行う
通信方式(コネクション)もありますが、今回注目するのはブロードキャストという通信方式です。

この方式は、いわばデータを垂れ流しているだけの状態(*)で相手が受け取っているかどうかは確認しません。接続確認などのオーバーヘッドがないため、データ送信側の負荷を小さくできます。


*厳密には、垂れ流されているデータをただ受信するだけのPassiveScanと、追加データの送信を要求するActiveScanがあります。

全体の仕組み

システム全体の仕組みは以下のようなイメージです。上が前回紹介したWiFiを使用したシステム、下が今回検証したBLEを使用したシステムです。

BLEを利用する場合、SQLサーバーを直接叩けないため、橋渡し役のゲートウェイが必要になります。
今回はこのゲートウェイにはRaspberryPi3Aを使用しました。

なお、SQLサーバー側にはPHPで動作するスクリプトを用意し、
ゲートウェイがこれを叩くことでSQLサーバーにデータが登録されるようにしてあります。
詳細は過去の記事をご覧ください。

回路構成

回路構成は以前のWiFiの検証で使用したものと同じものを使用しています。

ESP32C3スケッチ

15秒間隔で1回につき200msecの送信を行います。

#include <Wire.h>
#include <esp_sleep.h>
#include <Adafruit_BME280.h>

// https://github.com/nkolban/ESP32_BLE_Arduino
#include <BLEDevice.h>
#include <BLEServer.h>

#define DEVICE_NAME "ESP32_BLE"
#define SCL 7
#define SDA 6
#define ADCPIN 1
#define I2CPIN 5
#define LEDPIN 8

// https://www.uuidgenerator.net/
//上記サイトでUUIDを生成して使う
#define SERVICE_UUID "de2ee0cd-bdd4-467c-adf2-8d86b1d3cecf"
#define CHARACTERISTIC_UUID "80b30b49-9832-4cf1-c2f4-2fa2df1e73a1"

const int SLEEP_TIME = 15; // sleep time (second)
const int ADVERTISING_TIME = 200; // advertising time (mili second)

Adafruit_BME280 bme;

void setAdvertisementData(BLEAdvertising* pAdvertising)
{
  double t = 0;
  double h = 0;
  double p = 0;
  double lux = 0;
  int v=0;

  digitalWrite(LEDPIN, HIGH);

  //電圧測定
  v = analogRead(ADCPIN); delay(1);
  v += analogRead(ADCPIN); delay(1);
  v += analogRead(ADCPIN); delay(1);
  double volt = (float)v * 1.41334 / 3 / 1000;

  //気圧気温湿度
  digitalWrite(I2CPIN, HIGH); delay(10);
  if (!bme.begin(0x76))
  {
    goDeepSleep();
  }

  bme.setSampling(Adafruit_BME280::MODE_FORCED,
  Adafruit_BME280::SAMPLING_X1, // temperature
  Adafruit_BME280::SAMPLING_X1, // pressure
  Adafruit_BME280::SAMPLING_X1, // humidity
  Adafruit_BME280::FILTER_OFF);
  bme.takeForcedMeasurement();

  t = bme.readTemperature();
  p = bme.readPressure() / 100.0F;
  h = bme.readHumidity();

  digitalWrite(I2CPIN, LOW);

  uint16_t temp = (uint16_t)(t * 100);
  uint16_t humi = (uint16_t)(h * 100);
  uint16_t pres = (uint16_t)(p * 50);
  uint32_t illum = (uint32_t)(lux * 100);
  uint16_t vbat = (uint16_t)(volt * 1000);

  std::string strData = "";
  strData += (char)0xff; // Manufacturer specific data
  strData += (char)0xff; // manufacturer ID low byte
  strData += (char)0xff; // manufacturer ID high byte

  strData += (char)(temp & 0xff);
  strData += (char)((temp >> 8) & 0xff);
  strData += (char)(humi & 0xff);
  strData += (char)((humi >> 8) & 0xff);
  strData += (char)(pres & 0xff);
  strData += (char)((pres >> 8) & 0xff);

  strData += (char)(illum & 0xff);
  strData += (char)((illum >> 8) & 0xff);
  strData += (char)((illum >> 16) & 0xff);
  strData += (char)((illum >> 24) & 0xff);
  strData += (char)(vbat & 0xff);
  strData += (char)((vbat >> 8) & 0xff);

  strData = (char)strData.length() + strData;

  BLEAdvertisementData AdvertisementData = BLEAdvertisementData();
  AdvertisementData.setName(DEVICE_NAME);
  AdvertisementData.setFlags(0x06);
  AdvertisementData.addData(strData);
  pAdvertising->setAdvertisementData(AdvertisementData);
}

void setup() {
  Wire.begin(SDA, SCL);
  pinMode(I2CPIN, OUTPUT);
  pinMode(ADCPIN, ANALOG);
  pinMode(LEDPIN, OUTPUT);
  digitalWrite(LEDPIN, HIGH);
  analogSetAttenuation(ADC_11db);

  //BLEオブジェクト初期化
  BLEDevice::init(DEVICE_NAME);

  //アドバタイズオブジェクトを取得する
  BLEServer *pServer = BLEDevice::createServer();
  BLEAdvertising *pAdvertising = pServer->getAdvertising();

  setAdvertisementData(pAdvertising);

  // Advertising
  pAdvertising->start();
  delay(ADVERTISING_TIME);
  pAdvertising->stop();

  goDeepSleep();
}

void goDeepSleep()
{
  digitalWrite(LEDPIN, LOW);
  digitalWrite(I2CPIN, LOW);
  esp_deep_sleep(SLEEP_TIME * 1000000LL);
}

void loop() {
}

ゲートウェイ側のプログラム

PythonでBLEのアドバタイズデータを読み取り、結果をBASHスクリプトに渡して、データをSQLサーバーに送信しています。

ゲートウェイのHWはRaspberryPi3Aですが、bluepyの都合で最新のBookwormではなく、Bullseyeを使用しました。

以下のコマンドでpython3とbluepyをインストールしてください。

$sudo apt -y install python3-dev
$sudo apt -y install python3-pip
$sudo apt -y install libglib2.0-dev
$sudo pip3 install bluepy

python(ble.py)

from bluepy.btle import Scanner, DefaultDelegate
import struct
import subprocess

# 対象のMACアドレス(小文字で統一)
macaddr = "aa:aa:aa:aa:aa:aa"

class ScanDelegate(DefaultDelegate):
  def __init__(self):
    DefaultDelegate.__init__(self)

  def handleDiscovery(self, dev, isNewDev, isNewData):
    if dev.addr.lower() != macaddr.lower():
      return
    for (adtype, desc, value) in dev.getScanData():
      if adtype == 255:
        try:
          raw = bytes.fromhex(value)
          payload = raw[2:] # ManufacturerID除去 3:じゃなくて2:が正解

          temp = struct.unpack('<H', payload[0:2])[0] / 100.0
          humi = struct.unpack('<H', payload[2:4])[0] / 100.0
          pres = struct.unpack('<H', payload[4:6])[0] / 50.0
          illum = struct.unpack('<I', payload[6:10])[0] / 100.0
          vbat = struct.unpack('<H', payload[10:12])[0]

          subprocess.run([
            "bash","ble.sh",
            f"{temp:.2f}",
            f"{humi:.2f}",
            f"{pres:.2f}",
            f"{illum:.2f}",
            f"{vbat:.2f}"
          ])

        except Exception as e:
          pass # 出力のみ行うのでエラーは黙殺

scanner = Scanner().withDelegate(ScanDelegate())
while True:
  scanner.scan(5.0)

BASHスクリプト(ble.sh)

#!/bin/bash
SCRIPT_DIR=`dirname $0`
cd $SCRIPT_DIR

TB="tb1111"
URL="http://192.168.0.111/insdata.php?table="$TB

n=0
for i in "$@"; do
  eval "val$n='$i'"
  ((n++))
done

URL=$URL"&d0="$val0"&d1="$val1"&d2="$val2"&d3="$val3"&d4="$val4
curl $URL

結果

BLEでデータ送出を行ったとき、以下のような結果が得られました。

・15秒間隔の送信で460時間(平均消費電流3.91mA)

ちなみに、Wifiでデータ送信した場合、以下のような駆動時間でした。

・10秒間隔の送信で166時間(平均消費電流10.8mA)

・30秒間隔の送信で489時間(平均消費電流平均3.68mA)

これらと比較すると、概ね半分程度の消費電力になっていると思われます(*)。BLEデータを連続送信した場合には80mA程度の消費電流になっており、これはWifiとさほど変わらないイメージです。しかしながら、Wifiの場合にはコネクションの確立だけでも数百ミリ秒はかかっていましたので、200~300msecでデータ送信まで終わってしまう点が消費電力の削減につながっていると思われます。

データのドロップも1日に数回程度で、比較対象としたWifi接続と同じか少ないぐらいでした。


(*)Wifiの処理についても設定が多岐にわたりますし、同条件での比較ではないので単純には決められませんが・・・

まとめ

今回の方法ではWiFiを使用した場合よりもより低消費電力であることを確認できました。ただ、複数台使用時の衝突防止やデータのドロップ防止などまだまだ考えることも多いです。

ゲートウェイ側の処理が重くなってしまうのが難点ですが電力制限が厳しいIOT機器側の消費電力を削減できるのは大きいですね。

参考・イラスト使用

以下のサイト様を参考・使用させていただきました。

BLEについて

BLEデバイスとラズパイで温湿度を計測する話 | Bowreso
本稿では、ESP32マイコンを使って温湿度を計測するBLE(Bluetooth Low Energy)デバイスを作り、ラズパイでBLEデバイスからの温湿度データを受け取ってクラウドサービスに送信するシステムの作り方を紹介します。BLEデバイ...

イラスト使用

かわいいフリー素材集
いらすとやは季節のイベント・動物・子供などのかわいいイラストが沢山見つかるフリー素材サイトです。
「ブンブンざむらい」のブログ
撮影画像で実物をレビュー!
無料のAi・PNG白黒シルエットイラスト | シルエットイラスト一覧
無料で使える白黒シルエットイラスト素材の専門サイト。WEBやDTPデザインに無料で使えるシルエットが満載。
塗れるイラスト素材-ぬれよん
ビジソザ
商用フリーのイラスト素材提供サイト「ビジソザ」は、(1)利用無料(2)クレジット表記不要(3)連絡不要(4)登録不要で、 プレゼン、ポスターはもちろんホームページや放映、販売商品にもご利用可能です。

ありがとうございます。

 

コメント