コード

以下のようなコードで試した。概要としては

  • 2.5秒に1度起動する (それ以外は deep sleep)
  • 起動時にデータをRTCメモリに書きむ (センサーデータ取得を想定)
  • 4回に1度 WiFi に接続し、UDP でセンサーデータを送信する

というもの

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include "rtc_memory.hpp"
#include "config.h"
WiFiClient wifiClient;
struct {
char SSID[255] = WIFI_SSID;
char Password[255] = WIFI_PASS;
} wifi_config;
bool startWifi(int timeout) {
WiFi.disconnect();
WiFi.mode(WIFI_STA);
Serial.println("Reading wifi_config");
Serial.println("wifi_config:");
Serial.print("SSID: ");
Serial.print(wifi_config.SSID);
Serial.print("\n");
if (strlen(wifi_config.SSID) == 0) {
Serial.println("SSID is not configured");
return false;
}
WiFi.begin(wifi_config.SSID, wifi_config.Password);
int time = 0;
for (;;) {
switch (WiFi.status()) {
case WL_CONNECTED:
Serial.println("connected!");
WiFi.printDiag(Serial);
Serial.print("IPAddress: ");
Serial.println(WiFi.localIP());
return true;
case WL_CONNECT_FAILED:
Serial.println("connect failed");
return false;
}
delay(500);
Serial.print(".");
time++;
if (time >= timeout) {
break;
}
}
return false;
}
struct deep_sleep_data_t {
uint16_t count = 0;
uint8_t send = 0;
uint16_t data[12];
void add_data(uint16_t n) {
data[count] = n;
}
template <class T>
void run_every_count(uint16_t n, T func) {
count++;
if (!send) {
send = count % (n - 1) == 0;
} else {
send = 0;
count = 0;
func();
}
}
};
rtc_memory<deep_sleep_data_t> deep_sleep_data;
void post_sensor_data();
void setup() {
pinMode(13, OUTPUT);
Serial.begin(74880);
Serial.println("Initializing...");
// データ読みこみ
if (!deep_sleep_data.read()) {
Serial.println("system_rtc_mem_read failed");
}
Serial.print("deep_sleep_data->count = ");
Serial.println(deep_sleep_data->count);
// データの変更処理(任意)
deep_sleep_data->add_data(deep_sleep_data->count);
deep_sleep_data->run_every_count(4, [&]{
Serial.println("send data");
// なんか定期的に書きこみたい処理
post_sensor_data();
});
if (!deep_sleep_data.write()) {
Serial.print("system_rtc_mem_write failed");
}
if (deep_sleep_data->send) {
ESP.deepSleep(2.5e6, WAKE_RF_DEFAULT);
} else {
// sendしない場合は WIFI をオフで起動させる
ESP.deepSleep(2.5e6, WAKE_RF_DISABLED);
}
}
void loop() {
}
// dummy
void post_sensor_data() {
if (startWifi(30)) {
// do http etc...
WiFiUDP udp;
IPAddress serverIP(192, 168, 0, 5);
udp.beginPacket(serverIP, 5432);
udp.write((uint8_t*)deep_sleep_data->data, sizeof(deep_sleep_data->data));
udp.endPacket();
// wait for sending packet
delay(1000);
} else {
Serial.println("failed to start wifi");
ESP.restart();
}
}
view raw main.cpp hosted with ❤ by GitHub
extern "C" {
#include <user_interface.h>
};
template <class T>
class rtc_memory {
// 4 bytes aligned memory block address in rtc memory.
// user data must use 64 or larger block.
// but system_rtc_mem_read() is failed for 64 so use 65.
static constexpr uint32_t USER_DATA_ADDR = 65;
static constexpr uint32_t USER_DATA_SIZE = 512 - ((USER_DATA_ADDR - 64) * 4);
static uint32_t fnv_1_hash_32(uint8_t *bytes, size_t length) {
static const uint32_t FNV_OFFSET_BASIS_32 = 2166136261U;
static const uint32_t FNV_PRIME_32 = 16777619U;
uint32_t hash = FNV_OFFSET_BASIS_32;
for (size_t i = 0 ; i < length ; ++i) hash = (FNV_PRIME_32 * hash) ^ (bytes[i]);
return hash;
}
uint32_t calc_hash(T& data) const {
return fnv_1_hash_32((uint8_t*)&data, sizeof(data));
}
public:
uint32_t hash;
T data;
static_assert(sizeof(T) <= (USER_DATA_SIZE - sizeof(hash)), "sizeof(T) it too big");
bool read() {
// Read memory to temporary variable to retain initial values in struct T.
rtc_memory<T> read;
// An initial rtc memory is random.
bool ok = system_rtc_mem_read(USER_DATA_ADDR, &read, sizeof(read));
if (ok) {
// Only hashes are matched and copy to struct.
if (read.hash == calc_hash(read.data)) {
memcpy(this, &read, sizeof(read));
}
}
return ok;
}
bool write() {
hash = calc_hash(data);
return system_rtc_mem_write(USER_DATA_ADDR, this, sizeof(*this));
}
T* operator->() {
return &data;
}
};
view raw rtc_memory.cpp hosted with ❤ by GitHub

実測

説明をいれると

約5秒でAPに接続できている。その後UDPで送信し、送信が終わるのをちょっと無駄に1秒待っている。

V: 31.72mA となっているのが画面内の平均だけど、WiFi 接続が2回起きているので、1サイクルの平均ではない。

1サイクル(約16秒)あたりは、WiFi に 80mA 6秒、スリープ 20uA 10秒とし平均30mA。

もし単三 NiMH 2400mAh 3本(公称1.2V * 3 = 3.6V)をLDOで3.3Vにして電源にしたとすると、バッテリ容量は2400mAh * 1.2V * 3 = 8640mWh。このケースのESP8266の平均消費電力は 3.6V * 30mA = 108mW (LDO損失こみ)。8640 / 108 = 80時間。

支配的なのは圧倒的にWiFi接続時間なので、とにかく周期を長くとれば、それだけ電池寿命は伸びる。実際のところ電池3本で実用的なのは5分以上かなあという気がする (5分間隔でも2ヶ月ぐらい)。

これよりも短い間隔でデータのやりとりをする場合、電池駆動は考えないほうがよさそう。どうしても電池駆動したい場合WiFi 使うのが間違っているので ZigBee とか使うべきなんだと思う。とはいえ ESP8266 が圧倒的に安すぎるので、なんでも WiFi でやりたくなってしまう。

  1. トップ
  2. tech
  3. メモ: ESP8266 での実測電流
▲ この日のエントリ