第二世代　外部給電のためのスクリプト

「aibo ers-11x/210で遊ぶ Advent Calendar 2023」の24日目です。

ERS-210のバッテリーについて、Loïc POLLIERさんのレポジトリbattmonを参考に、一から勉強したメモをまとめています。
https://github.com/lpollier/battmon/tree/master

23日目でのERS-210のバッテリーの保護回路の改造で外部給電で動かせる改造を行いました。
今回はSMBusのaibo本体への応答をBQ2040に代わって行うESP32のスクリプトの作成を試みました。

:::note alert
警告
この記事について作者はいかなる責任も負いません。
バッテリーの分解、改造は非常に危険な行為です。
:::

:::note
業務外の個人的な検討活動です。
:::

#### aibo本体とバッテリーの間の通信内容の監視
まず正解を調べるため、aibo本体とバッテリーの間の通信内容を調べました。
19日目のスニファー機能を使い、22日目の電池を使いました。

https://qiita.com/nakmura/private/633afab831040a71b81a

https://qiita.com/nakmura/private/05c10af6808d94ee6287

19日目の記事に合ったように、この時のaibo本体からの出力とBQ2040からの出力の組み合わせを調べたところ、バッテリーを入れた直後はバッテリーのデバイス名などの問い合わせが出ていましたが、そのあとのaibo本体とBQ2040は下記の応答を繰り返していました。

```
S0001011W+00010110+1S0001011R+11000111+00000000-s   ステータス
S0001011W+00001000+1S0001011R+10010010+00001011-s   温度 92 0B; 296.2K
S0001011W+00001001+1S0001011R+10111100+00011111-s   電圧 BA 1F; 8122
S0001011W+00001010+1S0001011R+00000000+00000000-s   電流
S0001011W+00010000+1S0001011R+11111100+00000111-s   容量　FC 08
S0001011W+00001101+1S0001011R+01100011+00000000-s   相対電圧 63 00; 99 (%?) 
S0001011W+00001110+1S0001011R+01011000+00000000-s   絶対電圧 58 00; 88
```
行末は手動で追加したコメントです。
BQ2040の仕様書を見ると、それぞれバッテリーステータス、温度、電圧、電流、バッテリー設計容量、相対電圧、絶対電圧を出力する命令のようです。

#### BQ2040をエミュレーションするESP32のスクリプトの作成
ESP32がaibo本体からの問い合わせに対してBQ2040と同じような回答をするようにスクリプトを作成しました。ChatGPT先生とやり取りしながら作成しました。

```
#include <Wire.h>

#define SLAVE_ADDRESS 0x0B

#define START_CONDITION 0x01 // Replace 0x01 with the actual value you want
#define STOP_CONDITION 0x02  // Replace 0x02 with the actual value you want

void setup() {
  Serial.begin(115200);
  Wire.begin(SLAVE_ADDRESS); // ESP32をスレーブアドレス0x0Bで初期化
  Wire.onReceive(receiveEvent); // データ受信時のコールバック関数を設定
  Wire.onRequest(requestEvent); // データ送信要求時のコールバック関数を設定
}

void loop() {
    // スタートコンディションのチェック
    if (Serial.available() > 0) {
        // データの読み込み
        byte received = Serial.read();

        // スタートコンディションの確認
        if (received == START_CONDITION) {
            Serial.print("Start Condition Received: ");
            Serial.println(received, HEX); // ここで改行
        } else if (received == STOP_CONDITION) {
            // ストップコンディションの確認
            Serial.print("Stop Condition Received: ");
            Serial.println(received, HEX); // ここで改行
        } else {
            // 通常のデータ処理
            Serial.print("Received: 0x");
            Serial.print(received, HEX);
            Serial.println(" Sent: 0xC000"); // ここで改行
        }
    }
}

// 応答データとその長さを保持するための変数
byte response[3];
int responseLength = 2; // デフォルトの応答長

void receiveEvent(int howMany) {
  if (Wire.available()) {
    char received = Wire.read(); // データを読み取る
    if (received == 0x16) {
      // ホストからの特定のコマンド（0x16）に対する応答を設定
      response[0] = 0x0B;
      // responseLength = 1; // 
      response[1] = 0x00;
      response[1] = 0x00; //C0;
      response[2] = 0xB0;
      responseLength = 3; // 応答長を3バイトに設定
    } 
    else if (received == 0x08) {
      // ホストからの特定のコマンド（0x08）に対する応答を設定
      response[0] = 0x0B;
      response[1] = 0x92;
      response[2] = 0x0B; // 296.2K
      responseLength = 3; // 応答長を3バイトに設定
    } 
    else if (received == 0x09) {
      // ホストからの特定のコマンド（0x09）に対する応答を設定
      response[0] = 0x0B;
      response[1] = 0x00;
      responseLength = 2; // 応答長を3バイトに設定
      // response[2] = 0x00; //  mV
      // responseLength = 3; // 応答長を3バイトに設定
    } 
    else if (received == 0x0A) {
      // ホストからの特定のコマンド（0x0A）に対する応答を設定
      response[0] = 0x0B;
      response[1] = 0xA9;
      response[2] = 0xFF; // 0 mA
      responseLength = 3; // 応答長を3バイトに設定
    } 
    else if (received == 0x10) {
      // ホストからの特定のコマンド（0x10）に対する応答を設定
      response[0] = 0x0B;
      response[1] = 0xFC;
      response[2] = 0x08; // 2300 mAh
      responseLength = 3; // 応答長を3バイトに設定
    } 
    else if (received == 0x0D) {
      // ホストからの特定のコマンド（0x0D）に対する応答を設定
      response[0] = 0x0B;
      response[1] = 0x5A;
      response[2] = 0x00; //90 %
      responseLength = 3; // 応答長を3バイトに設定
    } 
    else if (received == 0x0E) {
      // ホストからの特定のコマンド（0x0E）に対する応答を設定
      response[0] = 0x0B;
      response[1] = 0x5A;
      response[2] = 0x00; // 90 %
      responseLength = 3; // 応答長を3バイトに設定
    } 
    else if (received == 0x21) {
      // ホストからの特定のコマンド（0x21）に対する応答を設定
      response[0] = 0x0B;
      response[1] = 0x07;
      response[2] = 0x45;
      response[3] = 0x52;
      response[4] = 0x41;
      response[5] = 0x32;
      response[6] = 0x30;
      response[7] = 0x31;
      response[8] = 0x42;
      responseLength = 9; // 応答長を9バイトに設定
    } 
    else {
      // 他のデータに対する標準的な応答
      response[0] = 0x0B;
      response[1] = 0x11;
      response[2] = 0x00;
      responseLength = 3; // 応答長を3バイトに設定
    }

    // responseLength = 0;

    // シリアルモニタに受信データを表示
    Serial.print("Received: 0x");
    Serial.println(received, HEX); // ここで改行
  }
}


void requestEvent() {
  Wire.write(response, responseLength); // 応答データを送信

  // シリアルモニタに送信データを表示
  Serial.print("Data Sent: ");
  for (int i = 0; i < responseLength; i++) {
    Serial.print("0x");
    if (response[i] < 0x10) Serial.print("0"); // 1桁の場合は0で埋める
    Serial.print(response[i], HEX);
    if (i < responseLength - 1) Serial.print(", "); // 最後のバイト以外はカンマで区切る
  }
  Serial.println(); // 改行
}
```

#### 動作確認
こちらの動画の様に外部給電でers-220を起動できました。

https://youtu.be/YL1k9D4UM-4

#### 但し書き
- 動画の最後にあるように、使用している機体では途中で警報が鳴りながら止まります。リフィルしたバッテリーでも同じ事象が起きるのでaibo本体の問題かもしれません。
- コマンド（0x09）については無効化しています。実験でここを無効化しないとaiboが起動できませんでした。

#### 警告をもう一度
今回のAdvent Calenderでバッテリーセルの交換について考察しますが、非常に危険ですので知識と教養を深める目的で調べたことをまとめました。
十分な安全対策を講じなければ、実行することはお勧めできません。
重要なことなので、もう一度書きます。

:::note alert
警告
この記事について作者はいかなる責任も負いません。
バッテリーの分解、改造は非常に危険な行為です。
:::