ATOM TailBAT 190 mAh のバッテリーがどれくらい継続給電できるか簡単に検証したメモ

ATOM TailBAT 190 mAh のバッテリーがどれくらい継続給電できるか簡単に検証したメモです。

背景

atom-tailbat-battery-simple-test_01.jpg

最近、ようやく ATOMS3 が使いこなせるようになってきて ATOM TailBAT 190 mAh のバッテリーがどれくらい継続給電できるかが知りたくなりました。

展示などでどういった充電サイクルと個数で臨めばいいかも見えてくるのでやってみます。

AtomS3 のプログラム

ということで、

  • Wi-Fi を常時接続して
  • ローカルネットワークの MQTT ブローカーに対して接続
  • その MQTT に 60 秒に一度データを送り
  • ディスプレイにその進捗状況を表示し
  • どこまでデータを送り続けられるかをやってみる

という流れで、以下のようなプログラムを書きました。

#include "M5AtomS3.h"

#include <WiFi.h>
#include <WiFiMulti.h>
WiFiMulti wifiMulti;

#include <ArduinoJson.h> // バージョンは v6 で
#include <WiFiClient.h>
#include <PubSubClient.h>

const char* ssid     = "ssid";
const char* password = "password";

// MQTTの接続先のIP
const char *endpoint = "endpoint";
// MQTTのポート
const int port = 1883;
// 今回使いたい MQTT のユーザー名
const char *mqttUsername = "mqttUsername";
// 今回使いたい MQTT のパスワード
const char *mqttPassword = "mqttPassword";

// デバイスID
char deviceID[50];

// トピックを保存するためのバッファを確保
char pubTopic[50];
char subTopic[50];

// プログラム名
#define FW_NAME "AtomS3_Wifi_Simple_Test"
// ファームウェアバージョン
#define FW_VERSION "1.0.1"

// ArduinoJson v6 の記述で JSON を格納する StaticJsonDocument を準備
StaticJsonDocument<512> root;

WiFiClient httpsClient;
PubSubClient mqttClient(httpsClient);
char pubJson[512];

// セットアップ管理
int setupFlag = 0;

// 定期実行時間(ミリ秒)
int executeTime = 60000;

// 待ち時間処理
int previoueExecuteTime = 0;

// 送信回数
int executeCount = 0;

void setup() {
    auto cfg = M5.config();
    AtomS3.begin(cfg);

    wifiMulti.addAP(ssid, password);

    AtomS3.Display.setTextDatum(TL_DATUM);
    AtomS3.Display.setCursor(0,0);
    AtomS3.Display.setTextSize(1);
    AtomS3.Display.println("[TEST]");
    AtomS3.Display.println(FW_NAME);
    AtomS3.Display.println(FW_VERSION);
    
    delay(3000);
    
    Serial2.begin(9600, SERIAL_8N1, 5, -1);

    WiFi.mode(WIFI_STA);
    WiFi.setSleep(false);

    
    Serial.print("Connecting...");
    AtomS3.Display.println("WiFi ");
    AtomS3.Display.print(".");
    
    while (wifiMulti.run() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
      AtomS3.Display.print(".");
    }

    Serial.println("");
    Serial.print("Connected to ");
    Serial.println(WiFi.SSID());
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    Serial.print("MAC address: ");
    Serial.println(WiFi.macAddress());

    AtomS3.Display.setCursor(0,0);
    AtomS3.Display.clear();
    AtomS3.Display.println("[TESTER]");
    AtomS3.Display.println(FW_NAME);
    AtomS3.Display.println(FW_VERSION);
    AtomS3.Display.println("WiFi OK!");
  
    // deviceID
    String macStr = String(WiFi.macAddress());
    macStr.replace(":", "");
    sprintf(deviceID, "%s", macStr);
    
    AtomS3.Display.print("ID ");
    AtomS3.Display.println(deviceID);
    AtomS3.Display.print("IP ");
    AtomS3.Display.println(WiFi.localIP());
    
    // メッセージを知らせるトピック
    sprintf(pubTopic, "device/%s/pub", deviceID);
    // メッセージを待つトピック
    sprintf(subTopic, "device/%s/sub", deviceID);

    AtomS3.Display.println("----");
    AtomS3.Display.print("pubTopic ");
    AtomS3.Display.println(pubTopic);
    AtomS3.Display.println("----");
  
    mqttClient.setServer(endpoint, port);
    mqttClient.setCallback(mqttCallback);
    mqttClient.setBufferSize(512);
    
    connectMQTT();

    delay(1000);
    AtomS3.Display.print("... 5 ");
    delay(1000);
    AtomS3.Display.print("4 ");
    delay(1000);
    AtomS3.Display.print("3 ");
    delay(1000);
    AtomS3.Display.print("2 ");
    delay(1000);
    AtomS3.Display.print("1 > GO!");
    delay(1000);

    setupFlag = 1;
    
}

void connectMQTT() {
  
  // 起動してからの経過時間でシードを設定
  randomSeed(millis());
  
  // MQTT clientID のランダム化(名称重複対策)
  char clientID[40] = "clientID";
  String rndNum = String(random(0xffffff), HEX);
  String deviceIDRandStr = String("client-");
  deviceIDRandStr.concat(deviceID);
  deviceIDRandStr.concat("-");
  deviceIDRandStr.concat(rndNum);
  deviceIDRandStr.toCharArray(clientID, 40);
  Serial.print("clientID ");
  Serial.println(clientID);

  AtomS3.Display.print("MQTT");
  
  while (!mqttClient.connected()) {
    if (mqttClient.connect(clientID,mqttUsername,mqttPassword)) {
      Serial.println("MQTT Connected.");
      int qos = 0;
      mqttClient.subscribe(subTopic, qos);
      Serial.println("MQTT Subscribed.");
      // Connected message
      DynamicJsonDocument doc(512);
      doc["type"] = "connected";
      doc["localIP"] = WiFi.localIP();
      serializeJson(doc, pubJson);
      mqttClient.publish(pubTopic, pubJson);
      AtomS3.Display.println(" > OK!");
    } else {
      Serial.print("Failed. Error state=");
      Serial.println(mqttClient.state());
      // Wait 5 seconds before retrying
      delay(5000);
    }
    
  }
}

void mqttCallback (char* topic, byte* payload, unsigned int length) {
  
    String str = "";
    Serial.print("Received. topic=");
    Serial.println(topic);
    for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
        str += (char)payload[i];
    }
    Serial.print("\n");
 
    // Deserialize the JSON dsocument
    DeserializationError error = deserializeJson(root, str);
 
    // Test if parsing succeeds.
    if (error) {
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(error.f_str());
      return;
    }

    //
}

void mqttLoop() {
    if (!mqttClient.connected()) {
        connectMQTT();
    }
    mqttClient.loop();
}

void loop() {
    // 常にチェックして切断されたら復帰できるように
    mqttLoop();

    // millis() を秒に変換
    unsigned long ms = millis();
    float seconds = ms / 1000.0;

    // 小数点以下2桁にフォーマットして表示
    char buffer[10];
    snprintf(buffer, sizeof(buffer), "%.2f", seconds);

    AtomS3.update();
    
    AtomS3.Display.setCursor(0,0);
    AtomS3.Display.clear();

    AtomS3.Display.println("[TESTER]");
    AtomS3.Display.println("waiting...");
    AtomS3.Display.print(buffer);
    AtomS3.Display.print(" sec");

    // 定期実行
    if(millis() - previoueExecuteTime > executeTime){
      
      AtomS3.Display.println("---");

      executeCount++;

      // Connected message
      DynamicJsonDocument doc(512);
      doc["type"] = "execute";
      doc["localIP"] = WiFi.localIP();
      doc["millis"] = millis();
      doc["executeCount"] = executeCount;
      serializeJson(doc, pubJson);
      mqttClient.publish(pubTopic, pubJson);
      
      AtomS3.Display.println("---> send!");
      AtomS3.Display.print("count :");
      AtomS3.Display.println(executeCount);

      previoueExecuteTime = millis();
      
      delay(1000);
    }
    
    delay(1000);
    
}

受け取る Node-RED の仕組み

atom-tailbat-battery-simple-test_04.png

データを計測するのは、このようなフローです。

Aedes MQTT ブローカーのノードでローカルネットワークの MQTT ブローカーを作りつつ、購読したトピックからデータを受け取り、それをダッシュボードに表示する仕組みです。

atom-tailbat-battery-simple-test_05.png

たとえばこれは 8 回、つまり 8 分送った状況です。

実際どうだったか

atom-tailbat-battery-simple-test_02.jpg

さて、あとは TailBat を満充電して放置するだけです。

atom-tailbat-battery-simple-test_06.png

こんな結果になりました。最小値で 47 分。

atom-tailbat-battery-simple-test_00.png

だいたい最大値で 65 分という結果です。

10 回くらい複数の TailBat で試してこんな感じで、おおよそ 50 分程度は電池が持つというところになりそうです。

勝手に体感として「15 分くらいしかもたないでしょ」と思っていたので、良い意味で裏切られた結果になりました。

もちろん、これでサーボを動かしたり、他のデバイスと連携したり、ネットワークへのアクセス
頻度を高めたら変わってくるかとは思いますが、一つの基準が持てて良かったです。