BLEでデータ送信をする場合に小型で電池を長持ちさせる方法のテストです。電池を使い昇圧モジュールを使う方法が簡単ですが、昇圧モジュールにもいろいろあります。そこで簡単に手に入る昇圧モジュール6種類を使い実際に試してみました。
実験にはESP32 Dev kitを2台使いました。1台は10秒間隔でdeep Sleep。sleepから覚めるとi2cのセンサーから値を読み取りBLEで値をブロードキャストします。この辺りはこちらのサイトを参考にさせてもらいました。
ESP32による近距離無線通信の実験② BLE通信
i2cで接続したセンサーはmax30205。信号線は10kの抵抗でプルアップしています。その他にLEDをつけてブロードキャストしている間は光らせています。これは動作確認用。
そしてもう1台のESP32でブロードキャストを受信したらそのデータをwi-fiでサーバに送信します。サーバではPHPがデータを受け取りログとして記録します。このログが記録できている時間を動作時間としています。
コード
ブロードキャストするESP32のコードは次の通り。
//BLE送信側(ブロードキャスト側)のコード
#include <BLEDevice.h>
#include <BLEServer.h>
#include <esp_deep_sleep.h>
#include "ClosedCube_MAX30205.h"
ClosedCube_MAX30205 max30205;
/* 基本属性定義 */
#define DEVICE_NAME "ESP32" // デバイス名
#define DEVICE_NUMBER 1 // デバイス識別番号(1~8)
#define SPI_SPEED 115200 // SPI通信速度
const int advertising_time = 1; // アドバタイジング時間(秒)
const int ledPin = 12; // LEDの接続ピン
const int max30205Pin = 19; //max30205の電源にするためのピン
float tempValue;
void setup() {
doInitialize();
digitalWrite(ledPin, HIGH);
digitalWrite(max30205Pin, HIGH);
BLEDevice::init(DEVICE_NAME); // BLEデバイスを初期化する
//i2cの準備 測定
max30205.begin(0x48);
tempValue = max30205.readTemperature();
digitalWrite(max30205Pin, LOW);
Serial.print("temp="); Serial.println(tempValue);
// BLEサーバーを作成してアドバタイズオブジェクトを取得する
BLEServer *pServer = BLEDevice::createServer();
BLEAdvertising *pAdvertising = pServer->getAdvertising();
// 送信情報を設定してシーケンス番号をインクリメントする
setAdvertisementData(pAdvertising);
// 所定の時間だけアドバタイズする
pAdvertising->start();
Serial.println("Advertising started!");
delay(advertising_time * 1000);
pAdvertising->stop();
digitalWrite(ledPin, LOW);
// 外部ウェイクアップを設定してディープスリープに移行する
esp_sleep_enable_ext0_wakeup(GPIO_NUM_32, 1);
Serial.println("... in deep sleep!");
esp_deep_sleep(10 * 1000 * 1000);//10sec
}
void loop() {
}
void doInitialize() {
Serial.begin(SPI_SPEED);
pinMode(ledPin, OUTPUT);
pinMode(max30205Pin, OUTPUT);
}
/*
アドバタイズデータに送信情報を設定する
*/
void setAdvertisementData(BLEAdvertising* pAdvertising) {
//体温をセットする
Serial.print("tempValue="); Serial.println(tempValue);
uint16_t temp = tempValue * 100;//100倍して整数にする
Serial.print("temp="); Serial.println(temp);
// string領域に送信情報を連結する
std::string strData = "";
strData += (char)0xff; // Manufacturer specific data
strData += (char)0xff; // manufacturer ID low byte 0
strData += (char)0xff; // manufacturer ID high byte 1
strData += (char)DEVICE_NUMBER; // サーバー識別番号 2
strData += (char)(temp & 0xff); // 温度の下位バイト 3
strData += (char)((temp >> 8) & 0xff); // 温度の上位バイト 4
Serial.print("low bit:"); Serial.println((char)(temp & 0xff));
Serial.print("high bit:"); Serial.println((char)((temp >> 8) & 0xff));
strData = (char)strData.length() + strData; // 先頭にLengthを設定
Serial.print("data length:"); Serial.println(strData.length());
// デバイス名とフラグをセットし、送信情報を組み込んでアドバタイズオブジェクトに設定する
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
oAdvertisementData.setName(DEVICE_NAME);
oAdvertisementData.setFlags(0x06); // LE General Discoverable Mode | BR_EDR_NOT_SUPPORTED
oAdvertisementData.addData(strData);
pAdvertising->setAdvertisementData(oAdvertisementData);
}
BLEの受信(オブザーバー)側の ESP32のコードは次の通り。
/*
2021.03.26 BLE+wifiデータ送信テスト
ESP32 devkit
BLEを受信してデータを受け取った場合にwi-fiに接続してデータをサーバに送信する。
測定間隔はBroadcaster側で決まる
*/
#include <Arduino.h>
#include <BLEDevice.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
const char* ssid = "あなたのwi-fiのssid";
const char* password = "あなたのwi-fiのパスワード";
WiFiMulti wifiMulti;
/* 基本属性定義 */
#define SPI_SPEED 115200 // SPI通信速度
/* スキャン制御用 */
#define DEVICE_NAME "ESP32" // 対象デバイス名
const int scanning_time = 3; // スキャン時間(秒)
BLEScan* pBLEScan; // Scanオブジェクトへのポインター
/* 受信データ構造体 */
struct tmpData {
float temperature; // 温度
};
const int ledPin = 12; // LEDの接続ピン
void setup() {
doInitialize(); // 初期化処理をして
BLEDevice::init(""); // BLEデバイスを作成する
Serial.println("Client application start...");
pBLEScan = BLEDevice::getScan(); // Scanオブジェクトを取得して、
pBLEScan->setActiveScan(false); // パッシブスキャンに設定する
WiFi.begin(ssid, password);
wifiMulti.addAP(ssid, password);
}
void loop() {
struct tmpData td;
// 所定時間だけスキャンして、見つかったデバイス数を取得する
BLEScanResults foundDevices = pBLEScan->start(scanning_time);
int count = foundDevices.getCount();
for (int i = 0; i < count; i++) { // 受信したアドバタイズデータを調べて
BLEAdvertisedDevice dev = foundDevices.getDevice(i);
std::string device_name = dev.getName();
if (device_name == DEVICE_NAME && dev.haveManufacturerData()) {
// デバイス名が一致しManufacturer dataがあって
digitalWrite(ledPin, HIGH);
std::string data = dev.getManufacturerData();
Serial.print("0:"); Serial.println(data[0]);
Serial.print("1:"); Serial.println(data[1]);
Serial.print("2:"); Serial.println(data[2]);
Serial.print("3:"); Serial.println(data[3]);
Serial.print("4:"); Serial.println(data[4]);
Serial.print("5:"); Serial.println(data[5]);
// 受信データを取り出す
td.temperature = (float)(data[4] << 8 | data[3]) / 100.00;
//データを送信する
setndDataToServer(&td);
digitalWrite(ledPin, LOW);
}
}
}
/* 初期化処理 */
void doInitialize() {
Serial.begin(SPI_SPEED);
pinMode(ledPin, OUTPUT); // GPIO設定:LED
digitalWrite(ledPin, LOW);
Serial.println("BLE Client start ...");
}
/* wi-fiに接続してデータをサーバに送信 */
void setndDataToServer(struct tmpData* td) {
if ((wifiMulti.run() == WL_CONNECTED)) {
HTTPClient http;
//体温をStringとして変数に入れる
String tempString = String(td->temperature);
Serial.print("tempString: "); Serial.println(tempString);
//送信するテキスト
String urlString = "サーバーのPHPへのパス/upload.php?mv=" + tempString;
Serial.print("[HTTP] begin...\n");
http.begin(urlString); //HTTP
Serial.print("[HTTP] GET...\n");
// start connection and send HTTP header
int httpCode = http.GET();
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been send and Server response header has been handled
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
//Serial.println(payload);
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
}
サーバで送られてきたデータを受信するためのPHPは次の通りです。私はAWSを使ってますが、PHPが使えればどこのサーバでも同じような物だと思います。
<?php
date_default_timezone_set('Asia/Tokyo');//タイムゾーンの設定
//文字コードの設定
mb_internal_encoding("UTF-8");
$mv = $_GET["mv"];
$fhandle = fopen("./log.csv", "a+");//読み込み/書き出し用でオープンします。 ファイルポインタをファイルの終端
//現在時刻の取得
fwrite($fhandle,date("Y/m/d G:i"));
fwrite($fhandle,",");
fwrite($fhandle,$mv);
fwrite($fhandle, "\n");
//レスポンス
header("Content-type: text/html");
echo "data=";
echo $mv;
echo "C\n";
昇圧モジュール
今回用意した昇圧モジュールは次のものになります。
Pololu 3.3Vステップアップ電圧レギュレータ NCP1402
TPS610986 超低消費電力 3.3V出力昇圧モジュール
TPS61200 超低電圧DC-DCコンバータモジュール(3.3V/5V)
TPS61291 超ローパワー昇圧コンバータ(2.5V/3V/3.3V)
TPS63000 昇降圧DC-DCコンバータモジュール(3.3V/5V)
3.3V出力昇圧DCDCコンバータ XCL102
他にTPS63020 昇降圧DC-DCコンバータモジュール(3.3V/5V)もありましたが、ストロベリーリナックスの商品説明ではTPS63000とほぼ同等の性能とのことで今回は購入しませんでした。
電池はAmazonで購入できる単6電池を使いました。
Amazonベーシック 乾電池 単6形 アルカリ
この電池を2本直列に接続し昇圧コンバータに入力し3.3Vの電源にしています。
実験結果
実験結果は次の通りです。
昇圧を使わない。電池を直接ESP32に接続した物。
電圧記録は1分毎に行ってます。所々低い電圧が出てきていますが、ESP32が動作中などの理由で一時的に電圧が下がった部分でしょう。
稼働時間:5時間53分
Pololu 3.3Vステップアップ電圧レギュレータ NCP1402
稼働時間:3時間
TPS610986 超低消費電力 3.3V出力昇圧モジュール
稼働時間:3時間21分
TPS61200 超低電圧DC-DCコンバータモジュール(3.3V/5V)
パワーセーブモード有効
稼働時間:4時間20分
TPS61291 超ローパワー昇圧コンバータ(2.5V/3V/3.3V)
3.3V出力
稼働時間:13時間52分
TPS63000 昇降圧DC-DCコンバータモジュール(3.3V/5V)
3.3V出力
パワーセーブモード
稼働時間:9時間32分
3.3V出力昇圧DCDCコンバータ XCL102
稼働時間:7時間43分
というわけで
TPS61291 超ローパワー昇圧コンバータ(2.5V/3V/3.3V)
が一番電池寿命を伸ばすことができました。残念ながら電池単体での駆動よりも短くなってしまうものもありました。負荷の条件により性能が活かされてないのでしょう。