ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【M5StickC Plus/Arduino】M5StickC Plusでバッテリー残量を表示する

 M5StickC Plusには容量が小さいながらもバッテリーを搭載しているので、USBからの給電がない状態でも動かすことができます。
 バッテリーを内蔵しているのならば残量が知りたくなると思い調べてみると、バッテリーの電圧から電池残量を計算して出すことができるらしいことがわかりました。

 今回は電圧から電池残量を計算してそれっぽい表示までやってみた備忘録です。

 では、始めます。


1:電源に関する関数
 M5StickCには電源に関する関数がいくつか実装されています。

 今回のバッテリー残量を表示するために使用する関数は以下の3つです。

// 1:電圧を取得する関数(3.0~4.2Vの範囲で3.0V以下になるとシャットダウンするらしい)
float _vbat = M5.Axp.GetBatVoltage();

// 2:低電圧状態をチェックする関数(3.4V以下だと1, それ以外は0を返す)
uint8_t _low_bat = M5.Axp.GetWarningLeve();

// 3:バッテリー稼働か充電中かを判定する関数(プラスの値なら充電、マイナスの値ならバッテリー稼働)
float _ibat = M5.Axp.GetBatCurrent();

 この他にも色々と関数が実装されているので、詳しく知りたい場合は以下のページ様を参照してください。


2:バッテリー表示のサンプル
 電源に関する関数がわかったので、これらを使ってバッテリー表示のサンプルを作ってみました。
 できるだけヘッダーファイル「Battery.h」に機能をまとめており、それを読み込んで「M5StickCPlus_battery.ino」で実際に表示しています。

・Battery.h

/**
 * 電池残量表示
 */
#include <M5StickCPlus.h>

/**
 * Batteryクラス
 * 
 * 電池残量を表示する。
 */
class Battery {
  public:
    Battery();
    void setPosAndSize(int posX, int posY, int showSizeNum);
    void setDeleteBgColor(uint32_t color);
    void setTextColor(uint32_t color);
    void showBattery();
    void deleteBattery();
    void batteryUpdate(int percent);

  private:
    const float MAX_BATTERY_VOLTAGE = 4.2f;
    const float MIN_BATTERY_VOLTAGE = 3.0f;
    const int8_t BITS_PER_PIXEL     = 1;
    const int TRANS_PARENTS         = 0;
    const int MAX_SHOW_SIZE         = 7;
    const int MIN_SHOW_SIZE         = 1;

    TFT_eSprite _sprite = TFT_eSprite(&M5.Lcd);
    bool _showFlg;
    int _x;
    int _y;
    int _showSize;
    int _width;
    int _height;
    int _top_width;
    uint32_t _bg_color;
    uint32_t _line_color;
    uint32_t _text_color;

    void drawBatteryLines();
    int calcBatteryPercent();
    uint32_t getBatteryColor();
    void showBatteryPercent(int i_percent);
    bool isLowBattery();
    bool isUsingBattery();
};

/**
 * コンストラクタ
 */
Battery::Battery() {
  // 初期化
  _showFlg = true;
  _x = 0;
  _y = 0;
  _showSize = 1;
  _width     = 28*_showSize;
  _height    = 10*_showSize;
  _top_width =  2*_showSize;
  _bg_color   = TFT_BLACK;
  _line_color = TFT_WHITE;
  _text_color = TFT_WHITE;
}

/**
 * 表示位置(x, y)と表示サイズを設定する関数
 */
void Battery::setPosAndSize(int posX, int posY, int showSizeNum) {
  _x = posX;
  _y = posY;
  
  if (MAX_SHOW_SIZE < showSizeNum) {
    _showSize = MAX_SHOW_SIZE;
  } else if (showSizeNum < MIN_SHOW_SIZE) {
    _showSize = MIN_SHOW_SIZE;
  } else {
    _showSize  = showSizeNum;
  }
  
  _width     = 28*_showSize;
  _height    = 10*_showSize;
  _top_width =  2*_showSize;
}

/**
 * 削除時の背景色を設定する関数
 */
void Battery::setDeleteBgColor(uint32_t color) {
  _bg_color  = color;
}

/**
 * 電池図形と文字の色を設定する関数
 */
void Battery::setTextColor(uint32_t color) {
  _line_color = color;
  _text_color = color;
}

/**
 * 電池残量を表示する関数
 */
void Battery::showBattery() {
  _showFlg = true;
  drawBatteryLines();
  _sprite.setColorDepth(16);
  _sprite.createSprite(_width-1, _height-1);
}

/**
 * 電池残量を非表示(塗りつぶし)する関数
 */
void Battery::deleteBattery() {
  // スプライト全体を_bg_colorで塗りつぶしてメモリ開放
  _sprite.deleteSprite();
  _sprite.createSprite(_width+_top_width, _height+1);
  _sprite.fillSprite(_bg_color);
  _sprite.pushSprite(_x, _y);
  _sprite.deleteSprite();
  _showFlg = false;
}

/**
 * バッテリー残量の表示を更新する関数
 */
void Battery::batteryUpdate(int percent=-1) {
  if (!_showFlg) { return; }

  // 電池図形内部背景塗りつぶし
  _sprite.fillRect(0, 0, _width-1, _height-1, TFT_BLACK);

  // 実際にバッテリーの値を計算するか判定
  int i_percent = percent;
  if (i_percent == -1) {
    i_percent = calcBatteryPercent();
  }

  // バッテリー残量の割合を計算して背景色塗りつぶし
  int b_width = int((_width-2) * (i_percent/100.0f));
  _sprite.fillRect(0, 0, b_width+1, _height-1, getBatteryColor());

  showBatteryPercent(i_percent); // バッテリー数値を表示
  _sprite.pushSprite(_x+1, _y+1); // ディスプレイに表示
}

/**
 * 電池の図形を作成する関数
 */
void Battery::drawBatteryLines() {
  // 透明部分も作れるスプライト範囲を作成
  _sprite.setColorDepth(BITS_PER_PIXEL);
  _sprite.createSprite(_width+_top_width, _height+1);
  _sprite.fillSprite(TRANS_PARENTS);

  // 電池図形を作成
  _sprite.fillRect(0, 0, _width+1, _height+1, _line_color);
  _sprite.fillRect(1, 1, _width-1, _height-1, TRANS_PARENTS);
  _sprite.fillRect(_width+1, _top_width, _top_width, _height-(_top_width*2)+1, _line_color);

  _sprite.setBitmapColor(_line_color, TRANS_PARENTS); // 色を設定
  _sprite.pushSprite(_x, _y, TRANS_PARENTS); // 表示
  _sprite.deleteSprite(); // メモリ開放
}

/**
 * バッテリー残量%を計算する関数(戻り値は0~100)
 */
int Battery::calcBatteryPercent() {
  float _vbat = M5.Axp.GetBatVoltage();
  float percent = (_vbat - MIN_BATTERY_VOLTAGE) / (MAX_BATTERY_VOLTAGE - MIN_BATTERY_VOLTAGE);
  return roundf(percent * 100.0f);
}

/**
 * バッテリーの状態に応じて色を取得する関数
 */
uint32_t Battery::getBatteryColor() {
  // バッテリー稼働中は緑、電圧が低い時は赤色、充電中は青色
  uint32_t color = TFT_DARKGREEN;
  if (isLowBattery()) {
    color = TFT_RED;
  } else if (!isUsingBattery()) {
    color = TFT_BLUE;
  }
  return color;
}

/**
 * バッテリー残量を表示する関数
 */
void Battery::showBatteryPercent(int i_percent) {
  // バッテリー数値を表示
  _sprite.setCursor(0+_showSize, 0+_showSize);
  _sprite.setTextFont(1);
  _sprite.setTextColor(_text_color);
  _sprite.setTextSize(_showSize);
  _sprite.print(i_percent);
  _sprite.print("%");
}

/**
 * 低電圧状態かを判定する関数
 */
bool Battery::isLowBattery() {
  // 低電圧状態(3.4V以下)だと1それ以外は0
  uint8_t _low_bat = M5.Axp.GetWarningLeve();
  if (_low_bat == 0) {
    return false;
  } else {
    return true;
  }
}

/**
 * バッテリー稼働か充電中かを判定する関数
 */
bool Battery::isUsingBattery() {
  // プラスが充電、マイナスがバッテリー稼働
  float _ibat = M5.Axp.GetBatCurrent();
  if (_ibat < 0.0f) {
    return true;
  } else {
    return false;
  }
}


・M5StickCPlus_battery.ino

/**
 * バッテリー残量を表示する
 */
#include <M5StickCPlus.h>
#include "Battery.h"

Battery battery = Battery();

void setup() {
  Serial.begin(115200);
  
  M5.begin();
  M5.Axp.ScreenBreath(10); // 画面の明るさ(7-12)
  M5.Lcd.setRotation(3);   // 画面を横向きに(0-3)

  // 表示左上の位置x, yと表示サイズ(1-7)を設定
  battery.setPosAndSize(5, 5, 2);

  // deleteBattery()時の塗りつぶし色を設定
  battery.setDeleteBgColor(TFT_BLACK); 

  // 電池図形と%表示の色を設定
  battery.setTextColor(TFT_WHITE);

  // バッテリーを表示
  battery.showBattery();
  Serial.println("setup end!");
}

int count = 0;
bool showFlg = true;

void loop() {
  M5.update();

  if (count > 100) {
    count = 0;
    showFlg = !showFlg;
    if (showFlg) {
      battery.showBattery();
    } else {
      battery.deleteBattery();
    }
  }

//  // 以下の関数だと電池の電圧から計算して表示(通常はこちらを使う)
//  battery.batteryUpdate();

  // 引数の値を表示
  battery.batteryUpdate(count);
  count++;
  delay(100);
}

 このサンプルを実行すると、電池残量のサンプルとしてcountの値が表示されます。100カウント毎に電池表示が消えたり表示されたりします。

 少しだけ解説すると、充電中はパーセント表示している後ろのバーが青色で表示され、バッテリー稼働中になると緑色になります。3.4V以下の低電圧状態になるとバーは赤色になります。 「setPosAndSize」で表示位置と表示サイズ、「setDeleteBgColor」でdelete時の塗りつぶし色、「setTextColor」でパーセント表示の文字と電池の図形部分の色をそれぞれ設定できるようにしています。
 また「batteryUpdate」の引数をなしにすると実際のバッテリーの状態を表示し、引数に数値(0~100)を設定するとその数値がそのまま表示するようにしています。
 ちなみに表示にスプライトを使用しているのはスプライトの方が表示速度が速かったからです。


 以上がM5StickC Plusでバッテリー残量を表示してみた内容です。

 残量表示自体は割とすぐにできたのですが、表示に使ったスプライトの仕様がいまいちわからずに色々と苦戦しました。ただスプライトの方が画面自体に表示させるよりも高速にできることがわかったので、簡単なゲームみたいなものを作りたい場合はスプライトで表示させる方が良いかと思いました。

 今回ヘッダーファイルに分けたことでスマホの画面のように常に表示させるみたいなこともやりやすくなったかと思うので、色々有効活用していきたいです。
 

・参考資料