M5StickCとATTiny85でのUSBキーボード制御

現時点の情報です。最新情報はM5StickC非公式日本語リファレンスを確認してみてください。

概要

M5StickC(ESP32)とATTiny85を接続し、USBキーボード制御を作りました。ATTiny85をパソコンに接続することで、M5StickC経由で好きな文字列を入力することができます。

今回はM5StickCと接続していますが、ESP32や他のArduino、Raspberry PiなどI2Cが使えるものであれば問題ないと思います。

ATTiny85とは?

下の記事でちょっとだけ触ったことがある、小さなArduino端末です。特徴としてUSB端子を持っていて、パソコンに差すとキーボードやマウスとして振る舞うことが可能です。

接続方法

今回はI2C接続にしました。キーボードとして動かすのでUARTでもいいのですが、複数接続できるI2Cの方が流用しやすいかなと、、、

端子M5StickCATTiny85
GNDGNDGND
SDAGPIO0P0
SCKGPIO26P2

上記の3本接続しました。M5StickCがI2C Master、ATTiny85がI2C Slaveとして動かします。また、ATTiny85の5VからM5StickCの5V inに追加で接続することで、給電しながら動かすこともできます。

接続に利用しているのは秋月電子で購入した「スルホール用テストワイヤ TP-200 (10本入)」です。はじめて利用しましたが、便利ですね。

アマゾンだと結構高いのでちょっと手が出ませんでした。

ATTiny85側スケッチ

#include "DigiKeyboard.h"
#include "TinyWireS.h"

#define SLAVE_ADDR 0x50

void receiveEvent(uint8_t howMany) {
  // LED ON
  digitalWrite(1, HIGH);

  // Receive
  char sendText[256];
  memset(sendText, 0, sizeof(sendText));
  for ( int i = 0 ; i < howMany ; i++ ) {
    sendText[i] = TinyWireS.receive();
  }

  // Keyboard Send
  DigiKeyboard.sendKeyStroke(0);
  DigiKeyboard.println(sendText);

  // LED OFF
  digitalWrite(1, LOW);
}

void setup() {
  TinyWireS.begin(SLAVE_ADDR);
  TinyWireS.onReceive(receiveEvent);
}

void loop() {
  TinyWireS_stop_check();
  DigiKeyboard.delay(100);
}

利用ライブラリ

開発環境は『USB直挿しマイコン Digispark(ATTINY85)』を参考に構築してください。I2C Slaveとして動かしますので、TinyWireSをライブラリフォルダに追加します。

コード自体は非常にシンプルです。受信した文字列をキーボード出力しているだけです。DigiKeyboard.delay()を定期的に呼び出さないと、パソコン側からキーボードとして認識しなくなるので注意しましょう。

LEDは必須ではないですが、本当に送れているのかがわからないので、光らせた方がいいです。また、キーボードの送信が完了したらI2Cで完了を送信したほうが、きれいな制御になると思います。

M5StickC側スケッチ

#include <M5StickC.h>
#include <Wire.h>

#define SLAVE_ADDR 0x50

void setup() {
  M5.begin();
  Wire.begin(0,26);
}

const char str[] = "lang-ship.com";

void loop() {
  M5.update();
  if (M5.BtnA.wasPressed()) {
    Wire.beginTransmission(SLAVE_ADDR);
    for ( int i = 0 ; i < strlen(str) ; i++ ) {
      Wire.write(str[i]);
    }
    Wire.endTransmission();
  }
}

BtnAを押すと、固定文字列をI2Cで送信するサンプルです。本当はこのスケッチを動かす前にI2C Scanner的なのを動かして、I2CとしてATTiny85が認識しているかを確認したほうがいいです。

まとめ

できそうだなと思ったので、目的もなく作ってみました。M5StickCに他のマイコンを接続するのは反則っぽくて、あまり好きじゃないのですが、ATTiny85だったら安いからいいかな。。。

M5StickCからUSBを制御できると、MQTTとかと連携することでリモートのパソコンなどのキーボードを遠隔から操作できるようになります。

ちなみに純正M5StickC Proto Hatだとちょっとだけ長さが足りなくて、入りませんでした。。。

M5StickCの回路図が公開されました

現時点の情報です。最新情報はM5StickC非公式日本語リファレンスを確認してみてください。

概要

開発元のM5Stackより、M5StickCの情報ページが更新され、新規に回路図が追加されました。

回路図

解析

ごめんなさい、まだできていません、、、

ただしこのページがかなり充実したので、いろいろ推測でしかなかったことがはっきりするはずです。

この図の通りだとGPIO35はやっぱり配線されているけれど、プルアップされているかがわかりません。そしてGPIO35なのでESP32側のプルアップは使えない、、、

AXP192の通知ってやっぱり使えないのかな?

まとめ

ちょっと解析がまにあっていません。Twitter上でしかこの情報がないと思いますので、Googleで検索しやすいようにブログにアップしておきます。

M5StickCの新型スクリュー追加版レビュー

現時点の情報です。最新情報はM5StickC非公式日本語リファレンスを確認してみてください。本体仕様は本体バージョンページにまとめてあります。

概要

11/11にAliexpressで購入したものが届いたので、使ってみました。

新型M5StickC

事前情報で、新型は5V INの色が青になっているのがわかったので、それが欲しかったのです。

しかしながら、色が赤くて落胆したのですが、後日よく見るとネジ穴が追加されていました。

ネジ穴の仕様

ネジ穴はM2でして、右側の深さが5ミリ、左側の深さが8ミリでした。しかしながら、長めのネジを使うと壊れる恐れがあるので、通常はM2の4ミリぐらいを利用するのがおすすめです。

固定方法

比較的最近のHATは固定用のネジ穴が空いています。この場合固定する対象物の厚み分を追加したネジを使って固定します。

本体に4ミリ、基板が約1ミリとしてM2の5ミリネジを利用して固定してみました。ただしHATにはネジが付属しないので、小さめのサイズのネジセットはあらかじめ用意したほうがいいかもしれません。

まとめ

まだネジ穴付き本体が日本に流通するのは先になると思います。そしてBtnBを押したときに100mA流れるバグも残ったままです。

そして、同時にかったJOYSTICK HATを開封もしていないのに気が付きました、、、サーボハットは別便で頼んでいるサーボがまだ届いていません、、、

技適未取得機器実験等特例を利用して、ESP32-PICO-KITを触る

概要

技適取得済みですが、有効な表記がないESP32-PICO-KITを、技適未取得機器実験等特例の届け出をして、合法的に利用してみました。

技適未取得機器実験等特例とは?

上記のページから届け出することで、最大半年間技適の無い機器を実験することができる精度です。

# 開設届出日:2019年11月21日
# 
# 目的:
#   ESP32-PICO-KITを用いたリファレンスボードでの動作試験
# 
# 規格:
#   IEEE802.11b
#   IEEE802.11g
#   IEEE802.11n
#   Bluetooth Version 4.2

上記みたいな内容で申請してみました。開始日は12月1日にしたのでやっと使える状態になりました。

基本的に届け出なので、不備がなければ受理されるみたいです。シリアルは区別できればいいので「ESP32-PICO-KIT-001」で私は申請しています。

Ai-ThinkerとかのESP32とかのったボードで楽しいのが結構あるので、今度チャレンジしてみたいですね。

ESP32-PICO-KITとは?

M5StickCの中身に使われているESP32-PICO-D4を搭載しているボードです。ESP32-PICO-D4は単体だとアンテナの無いチップなので、ボード単位で技適を取得する必要があります。

ESP32-PICO-KITで技適を取得しているのですが、ボード上に表記されているバージョンが販売されていません。あんまり売れていないか、FCCだけ表記したボードを作りすぎたんですかね?

実際の価格はM5StickCと同じぐらいなので、基本的にはM5StickCを買ったほうがいいと思います、、、

今回はM5StickCの省電力検証用にこのボードを準備しました!

やっぱりバッテリーがつながっていると、消費電力がよくわからないです、、、

IOが外に出ているの以外はM5StickCとほぼ同じだと思います。ただUSB-シリアルが「Silicon Labs CP210x USB to UART Bridge」と違うので、別シリアルポートになります。

M5StickCの設定のままでもプログラム転送も可能です。

まとめ

まだちゃんと動かしていませんが、これでM5StickCの細かい消費電力調査をしたいと思います。本当はAXP192もほしいのですが6mm x 6mmの48-pin QFNって手半田つらいよね?

んー、ブレイクアウト基板でも作って、実装サービスでくっつけてもらうしかないんだろうか、、、

M5StickCの画面制御ST7735のソース解析

現時点の情報です。最新情報はM5StickC非公式日本語リファレンスを確認してみてください。

概要

M5StickCの画面制御で使われているST7735関連のソースコードを読んでみました。

ST7735とは?

ST7735とST7735Sがあるようですが、M5Stack社が公開しているデータシートからM5StickCで使われているのはSがついている方だと思います。(違いはわかりません、、、最近はS付きばかり売られている?)

ST7735は132×162ドットのフレームバッファを内蔵しており、フレームバッファに16ビットの色情報を書き込むことで、いい感じに液晶に表示してくれるコントローラーです。

液晶とコントローラーは個別に選択することができますが、個人で買えるモジュールはセットで販売されていると思います。

ST7735_Defines.h

使っている液晶などの設定をするファイルです。

液晶のサイズ指定

// Change the width and height if required (defined in portrait mode)
// or use the constructor to over-ride defaults
#ifndef TFT_WIDTH
  #define TFT_WIDTH  80
#endif
#ifndef TFT_HEIGHT
  #define TFT_HEIGHT 160
#endif

M5StickCの液晶は縦長なので80×160と定義されています。この値が画面用のクラスであるTFT_eSPIのコンストラクタの引数として指定されています。

他のサイズの液晶を使う場合には、宣言を書き換えるかコンストラクタの引数の引数で与えろとコメントがありますね。

class TFT_eSPI : public Print {

 public:

  TFT_eSPI(int16_t _W = TFT_WIDTH, int16_t _H = TFT_HEIGHT);

利用している液晶選択

//#define ST7735_INITB
// #define ST7735_INITB
// #define ST7735_GREENTAB
// #define ST7735_GREENTAB2
//#define ST7735_GREENTAB3
// #define ST7735_GREENTAB128    // For 128 x 128 display
#define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
 //#define ST7735_REDTAB
//#define ST7735_BLACKTAB
 //#define ST7735_REDTAB160x80  
// Enumerate the different configurations

ここではM5StickCで利用しているST7735_GREENTAB160x80だけコメントが外されており、それ以外はコメントアウトされています。

グリーンタブというのが、わかりにくいのですが新品の液晶を購入した場合の液晶保護シートについているタブの色です。上記が緑で80×160なのでM5StickCと同じものだと思います。

いまは緑ばかりですが、上記のようにREDTABもあります。

液晶の列挙

#define INITR_GREENTAB       0x0
#define INITR_REDTAB         0x1
#define INITR_BLACKTAB       0x2 // Display with no offsets
#define INITR_GREENTAB2      0x3 // Use if you get random pixels on two edges of green tab display
#define INITR_GREENTAB3      0x4 // Use if you get random pixels on edge(s) of 128x128 screen
#define INITR_GREENTAB128    0x5 // Use if you only get part of 128x128 screen in rotation 0 &amp; 1
#define INITR_GREENTAB160x80 0x6 // Use if you only get part of 128x128 screen in rotation 0 &amp; 1
#define INITR_REDTAB160x80   0x7 // Added for https://www.aliexpress.com/item/ShengYang-1pcs-IPS-0-96-inch-7P-SPI-HD-65K-Full-Color-OLED-Module-ST7735-Drive/32918394604.html
#define INITB                0xB

上記の選択に近いですが、ここは定義されている液晶が列挙されています。INITR_REDTAB160x80とかのURLを見ると、中身は緑に変わっていたりと商品はどんどん入れ替わるので気をつけましょう!

ここに無いものは自分で定義することになると思います。

液晶の確定

#if defined (ST7735_INITB)
  #define TAB_COLOUR INITB
...(略)
#elif defined (ST7735_GREENTAB160x80)
  #define TAB_COLOUR INITR_GREENTAB160x80
  #define CGRAM_OFFSET

#elif defined (ST7735_REDTAB160x80)
  #define TAB_COLOUR INITR_REDTAB160x80
  #define CGRAM_OFFSET

ST7735_GREENTAB160x80を指定したので、内部で使う設定はINITR_GREENTAB160x80になります。また、CGRAM_OFFSETという宣言が追加されています。

この宣言がある液晶と、ない液晶があるので、ST7735_*で選択したものをここの#IFで切り分けています。

M5StickCの液晶は80×160ですので、フレームバッファには収まっています。

上記はちょっと大げさに書いてありますが、フレームバッファのどこを画面に表示するのかは、液晶によって違います。上記の場合液晶は右上に寄っています。この場合左側には使われない領域があります。(説明用の図なので実際とは違います!)

初期化を間違えてしまうと左上から描画してしまうので、一番端の列は変な色になってしまいます。obnizの初期ライブラリはこの状態なので、全面塗りつぶしても、よく見ると端っこの色が変です。

上記のように、描画する座標を調整する必要があるって宣言がCGRAM_OFFSETみたいです。

色の宣言

// Color definitions for backwards compatibility with old sketches
// use colour definitions like TFT_BLACK to make sketches more portable
#define ST7735_BLACK       0x0000      /*   0,   0,   0 */
#define ST7735_NAVY        0x000F      /*   0,   0, 128 */
#define ST7735_DARKGREEN   0x03E0      /*   0, 128,   0 */
#define ST7735_DARKCYAN    0x03EF      /*   0, 128, 128 */
#define ST7735_MAROON      0x7800      /* 128,   0,   0 */
#define ST7735_PURPLE      0x780F      /* 128,   0, 128 */
#define ST7735_OLIVE       0x7BE0      /* 128, 128,   0 */
#define ST7735_LIGHTGREY   0xC618      /* 192, 192, 192 */
#define ST7735_DARKGREY    0x7BEF      /* 128, 128, 128 */
#define ST7735_BLUE        0x001F      /*   0,   0, 255 */
#define ST7735_GREEN       0x07E0      /*   0, 255,   0 */
#define ST7735_CYAN        0x07FF      /*   0, 255, 255 */
#define ST7735_RED         0xF800      /* 255,   0,   0 */
#define ST7735_MAGENTA     0xF81F      /* 255,   0, 255 */
#define ST7735_YELLOW      0xFFE0      /* 255, 255,   0 */
#define ST7735_WHITE       0xFFFF      /* 255, 255, 255 */
#define ST7735_ORANGE      0xFD20      /* 255, 165,   0 */
#define ST7735_GREENYELLOW 0xAFE5      /* 173, 255,  47 */
#define ST7735_PINK        0xF81F

#define BLACK               0x0000      /*   0,   0,   0 */
#define NAVY                0x000F      /*   0,   0, 128 */
#define DARKGREEN           0x03E0      /*   0, 128,   0 */
#define DARKCYAN            0x03EF      /*   0, 128, 128 */
#define MAROON              0x7800      /* 128,   0,   0 */
#define PURPLE              0x780F      /* 128,   0, 128 */
#define OLIVE               0x7BE0      /* 128, 128,   0 */
#define LIGHTGREY           0xC618      /* 192, 192, 192 */
#define DARKGREY            0x7BEF      /* 128, 128, 128 */
#define BLUE                0x001F      /*   0,   0, 255 */
#define GREEN               0x07E0      /*   0, 255,   0 */
#define CYAN                0x07FF      /*   0, 255, 255 */
#define RED                 0xF800      /* 255,   0,   0 */
#define MAGENTA             0xF81F      /* 255,   0, 255 */
#define YELLOW              0xFFE0      /* 255, 255,   0 */
#define WHITE               0xFFFF      /* 255, 255, 255 */
#define ORANGE              0xFD20      /* 255, 165,   0 */
#define GREENYELLOW         0xAFE5      /* 173, 255,  47 */
#define PINK                0xF81F

色を宣言しています。ST7735は16ビットで色を制御しています。RGB順に並んでいますがRが5ビット、Gが6ビット、Bが5ビットで制御しています。

01234567 01234567
RRRRRGGG GGGBBBBB

16ビットの場合にはこの他に透明色のAを追加してRGB各5ビットの場合もあります。

コマンドと設定値

// Delay between some initialisation commands
#define TFT_INIT_DELAY 0x80

// Generic commands used by TFT_eSPI.cpp
#define TFT_NOP     0x00
#define TFT_SWRST   0x01

#define TFT_CASET   0x2A
#define TFT_PASET   0x2B
#define TFT_RAMWR   0x2C

#define TFT_RAMRD   0x2E
#define TFT_IDXRD   0x00 //0xDD // ILI9341 only, indexed control register read

#define TFT_MADCTL  0x36
#define TFT_MAD_MY  0x80
#define TFT_MAD_MX  0x40
#define TFT_MAD_MV  0x20
#define TFT_MAD_ML  0x10
#define TFT_MAD_BGR 0x08
#define TFT_MAD_MH  0x04
#define TFT_MAD_RGB 0x00

#define TFT_INVOFF  0x20
#define TFT_INVON   0x21

// ST7735 specific commands used in init
#define ST7735_NOP     0x00
#define ST7735_SWRESET 0x01
#define ST7735_RDDID   0x04
#define ST7735_RDDST   0x09

#define ST7735_SLPIN   0x10
#define ST7735_SLPOUT  0x11
#define ST7735_PTLON   0x12
#define ST7735_NORON   0x13

#define ST7735_INVOFF  0x20
#define ST7735_INVON   0x21
#define ST7735_DISPOFF 0x28
#define ST7735_DISPON  0x29
#define ST7735_CASET   0x2A
#define ST7735_RASET   0x2B // PASET
#define ST7735_RAMWR   0x2C
#define ST7735_RAMRD   0x2E

#define ST7735_PTLAR   0x30
//Add
#define ST7735_VSCRDEF 0x33
#define ST7735_COLMOD  0x3A
#define ST7735_MADCTL  0x36
#define ST7735_VSCRSADD 0x37

#define ST7735_FRMCTR1 0xB1
#define ST7735_FRMCTR2 0xB2
#define ST7735_FRMCTR3 0xB3
#define ST7735_INVCTR  0xB4
#define ST7735_DISSET5 0xB6

#define ST7735_PWCTR1  0xC0
#define ST7735_PWCTR2  0xC1
#define ST7735_PWCTR3  0xC2
#define ST7735_PWCTR4  0xC3
#define ST7735_PWCTR5  0xC4
#define ST7735_VMCTR1  0xC5

#define ST7735_RDID1   0xDA
#define ST7735_RDID2   0xDB
#define ST7735_RDID3   0xDC
#define ST7735_RDID4   0xDD

#define ST7735_PWCTR6  0xFC

#define ST7735_GMCTRP1 0xE0
#define ST7735_GMCTRN1 0xE1

いろいろコマンドとかが列挙されていますが、ここだけみてもわからないので実際のコードのところで理解したいと思います。

ST7735_Init.h

液晶の初期化をしているファイルです。

初期化コマンド

	// Initialization commands for ST7735 screens
  static const uint8_t PROGMEM
  Bcmd[] = {                  // Initialization commands for 7735B screens
    18,                       // 18 commands in list:
    ST7735_SWRESET,   TFT_INIT_DELAY,  //  1: Software reset, no args, w/delay
      50,                     //     50 ms delay
    ST7735_SLPOUT ,   TFT_INIT_DELAY,  //  2: Out of sleep mode, no args, w/delay
      255,                    //     255 = 500 ms delay
    ST7735_COLMOD , 1+TFT_INIT_DELAY,  //  3: Set color mode, 1 arg + delay:
      0x05,                   //     16-bit color
      10,                     //     10 ms delay
...(略)

この辺は定形の決り文句みたいなので、無視します。データシートをみると何をやっているかはわかると思いますが、定形なので順番に必要なパラメータをセットしているだけのようです。

液晶初期化

     if (tabcolor == INITB)
     {
       commandList(Bcmd);
     }
     else 
     {
	  commandList(Rcmd1);
       if (tabcolor == INITR_GREENTAB)
       {
         commandList(Rcmd2green);
         colstart = 2;
         rowstart = 1;
       }
       else if (tabcolor == INITR_GREENTAB2)
       {
         commandList(Rcmd2green);
         writecommand(ST7735_MADCTL);
         writedata(0xC0);
         colstart = 2;
         rowstart = 1;
       }
       else if (tabcolor == INITR_GREENTAB3)
       {
         commandList(Rcmd2green);
         colstart = 2;
         rowstart = 3;
       }
       else if (tabcolor == INITR_GREENTAB128)
       {
         commandList(Rcmd2green);
         colstart = 0;
         rowstart = 32;
       }
       else if (tabcolor == INITR_GREENTAB160x80)
       {
         commandList(Rcmd2green);
         writecommand(TFT_INVON);
         colstart = 26;
         rowstart = 1;
       }
       else if (tabcolor == INITR_REDTAB160x80)
       {
         commandList(Rcmd2green);
         colstart = 24;
         rowstart = 0;
       }
       else if (tabcolor == INITR_REDTAB)
       {
         commandList(Rcmd2red);
       }
       else if (tabcolor == INITR_BLACKTAB)
       {
         writecommand(ST7735_MADCTL);
         writedata(0xC0);
       }
       commandList(Rcmd3);
     }

上記で初期化しています。M5StickCはINITR_GREENTAB160x80なので、ハイライトされている場所が実行されます。

commandList(Rcmd1)

ブラックタブ以外の共通初期化処理みたいです。

commandList(Rcmd2green)

グリーンタブの共通初期化処理。

writecommand(TFT_INVON)

他で利用していないコマンドがでてきました!

宣言を調べると、INVのONとOFFがあります。

#define TFT_INVOFF  0x20
#define TFT_INVON   0x21

ここからは20とか21を参考にデータシートを調べます。

INVOFFはデフォルトの状態で、フレームバッファのメモリをそのまま表示させるモードみたいです。

INVONはフレームバッファのメモリの輝度を反転させて転送するモードかな?

液晶画面によって0が一番暗い状態が一般的ですが、たまに反転していて0が一番明るい状態の物があります。

M5Stackだと途中で使っている液晶モジュールが変わって、古いライブラリだと色がおかしくなった事象があったと思います。液晶の色が反転してみる場合には、この設定も疑いましょう。

colstart = 26, rowstart = 1

この設定がフレームバッファと液晶の表示座標のズレを指定しています。横1ドット、縦26ドットずれた場所から描画されているみたいです。

commandList(Rcmd3)

こちらもブラックタブ以外の共通初期化処理みたいです。

ST7735_Rotation.h

画面の回転をした場合の処理です。

入力値チェック

  rotation = m % 4; // Limit the range of values to 0-3

画面の方向は0から3までなので、変な値が来ても大丈夫なように4のあまりで0-3の値に変更しています。

writecommand(TFT_MADCTL)

コマンドを投げていますね。データシートを調べます。

上記が定義です。ちょっとこれだけだと、よくわからないですね。

ビット概要
MYY軸反転
MXX軸反転
MVX軸とY軸を入れ替える
ML横方向の描画方向
MH縦方向の描画方向
RGBRGBかBGR

上記がどのように使われているのかを確認します。

    case 0:
     if (tabcolor == INITR_BLACKTAB) {
       writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB);
     } else if(tabcolor == INITR_GREENTAB2) {
       writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB);
       colstart = 2;
       rowstart = 1;
     } else if(tabcolor == INITR_GREENTAB3) {
       writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR);
       colstart = 2;
       rowstart = 3;
     } else if(tabcolor == INITR_GREENTAB128) {
       writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR);
       colstart = 0;
       rowstart = 32;
     } else if(tabcolor == INITR_GREENTAB160x80) {
       writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR);
       colstart = 26;
       rowstart = 1;
     } else if(tabcolor == INITR_REDTAB160x80) {
       writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR);
       colstart = 24;
       rowstart = 0;
     } else if(tabcolor == INITB) {
       writedata(TFT_MAD_MX | TFT_MAD_RGB);
     } else {
       writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR);
     }
      _width  = _init_width;
      _height = _init_height;
      break;

方向が0なので、M5StickCだと縦においた場合の画面方向です。

M5StickCはINITR_GREENTAB160x80なので、TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGRが指定されていました。

MXとMYはだいたい入っているので、よくわからないですね。M5StickCのだけ抜粋したコードを見てみます。

  switch (rotation) {
    case 0:
       writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR);
       colstart = 26;
       rowstart = 1;
      _width  = _init_width;
      _height = _init_height;
      break;
    case 1:
       writedata(TFT_MAD_MV | TFT_MAD_MY | TFT_MAD_BGR);
       colstart = 1;
       rowstart = 26;
      _width  = _init_height;
      _height = _init_width;
      break;
    case 2:
       writedata(TFT_MAD_BGR);
       colstart = 26;
       rowstart = 1;
      _width  = _init_width;
      _height = _init_height;
      break;
    case 3:
       writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR);
       colstart = 1;
       rowstart = 26;
      _width  = _init_height;
      _height = _init_width;
      break;
  }

えーっと、画面の回転は以下の図です。

一番シンプルなのが2の逆さですね。TFT_MAD_BGRのみの指定です。おそらくこの向きが液晶の正しい向きな気がします。

このBGRは液晶の色の並び方を示しています。他の液晶はRGBの指定もありましたが、この液晶はBGRの順に色が並んでいます。デフォルトはRGBなのでBGRを指定しないと色が変になってしまいます。

obnizの初期ライブラリは未指定だったみたいで、デフォルトのRGBモードで動いていたので、色がおかしかったです。

rotation方向MXMYMVMHBGR
0
2
1
3

各指定をまとめたのが上の図です。画面を横にしているときには、XとY軸が逆になるのでMVを指定する必要があります。

MXとMYで反転させて、回転方向を決めます。0は逆さに回転するので、MXとMYの両方をしていします。

MHは画面の転送順番を指定するのですが、実際のところ指定しなくても見た目はほとんど変わりません。画面の上から描画されていくか、下から描画されていくかの違いです。同じくMLで右からか左からかも指定できますが、転送が非常に遅い場合か、スローカメラで撮影しないと差はわからないと思います。

1と3はどっちの方向に回転するのかでMXとMYを選択しています。

左右反転の鏡面表示や、上下反転表示をしたい場合には、上記のデフォルト値から反転したい方向のフラグを逆に設定します。

    // 縦向きのデフォルト値
    M5.Lcd.setRotation(2);
    M5.Lcd.writecommand(TFT_MADCTL);
    M5.Lcd.writedata(TFT_MAD_BGR);

    // 縦向きの左右反転(TFT_MAD_MXを追加)
    M5.Lcd.setRotation(2);
    M5.Lcd.writecommand(TFT_MADCTL);
    M5.Lcd.writedata(TFT_MAD_MX | TFT_MAD_BGR);

    // 縦向きの上限反転(TFT_MAD_MYを追加)
    M5.Lcd.setRotation(2);
    M5.Lcd.writecommand(TFT_MADCTL);
    M5.Lcd.writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR);

    // 横向きのデフォルト値
    M5.Lcd.setRotation(3);
    M5.Lcd.writecommand(TFT_MADCTL);
    M5.Lcd.writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR);

    // 横向きの左右反転(TFT_MAD_MYを追加)
    M5.Lcd.setRotation(3);
    M5.Lcd.writecommand(TFT_MADCTL);
    M5.Lcd.writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR);

    // 横向きの上限反転(TFT_MAD_MXを取る)
    M5.Lcd.setRotation(3);
    M5.Lcd.writecommand(TFT_MADCTL);
    M5.Lcd.writedata(TFT_MAD_MV | TFT_MAD_BGR);

上記の関係になります。横になった場合はMVによってXとYが逆になっているので、反転方向も逆になります。

#include <M5StickC.h>

int mode = -1;

void setup() {
  M5.begin();
}

void loop() {
  M5.update();
  if ( M5.BtnA.wasPressed() ) {
    mode++;
    mode = mode % 10;
    if ( mode == 0 ) {
      M5.Lcd.setRotation(0);
      M5.Lcd.writecommand(TFT_MADCTL);
      M5.Lcd.writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR);
    } else if ( mode == 1 ) {
      M5.Lcd.setRotation(0);
      M5.Lcd.writecommand(TFT_MADCTL);
      M5.Lcd.writedata(TFT_MAD_MY | TFT_MAD_MH | TFT_MAD_BGR);
    } else if ( mode == 2 ) {
      M5.Lcd.setRotation(0);
      M5.Lcd.writecommand(TFT_MADCTL);
      M5.Lcd.writedata(TFT_MAD_MX | TFT_MAD_MH | TFT_MAD_BGR);
    } else if ( mode == 3 ) {
      M5.Lcd.setRotation(0);
      M5.Lcd.writecommand(TFT_MADCTL);
      M5.Lcd.writedata(TFT_MAD_MH | TFT_MAD_BGR);
    } else if ( mode == 4 ) {
      M5.Lcd.setRotation(0);
      M5.Lcd.writecommand(TFT_MADCTL);
      M5.Lcd.writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_BGR);
    } else if ( mode == 5 ) {
      M5.Lcd.setRotation(0);
      M5.Lcd.writecommand(TFT_MADCTL);
      M5.Lcd.writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_ML | TFT_MAD_BGR);
    } else if ( mode == 6 ) {
      M5.Lcd.setRotation(3);
      M5.Lcd.writecommand(TFT_MADCTL);
      M5.Lcd.writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_BGR);
    } else if ( mode == 7 ) {
      M5.Lcd.setRotation(3);
      M5.Lcd.writecommand(TFT_MADCTL);
      M5.Lcd.writedata(TFT_MAD_MV | TFT_MAD_BGR);
    } else if ( mode == 8 ) {
      M5.Lcd.setRotation(3);
      M5.Lcd.writecommand(TFT_MADCTL);
      M5.Lcd.writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR);
    } else if ( mode == 9 ) {
      M5.Lcd.setRotation(3);
      M5.Lcd.writecommand(TFT_MADCTL);
      M5.Lcd.writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_BGR);
    }
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(5, 10);
    M5.Lcd.print("mode:");
    M5.Lcd.print(mode);
  }
}

上記サンプルスケッチを動かして関係性を調べてみてください。

その他のコマンド

Sleepなど

M5StickCだとAXP192で制御するので、あまり使わないはずですが液晶のON、OFFやスリープ設定があります。

GAMSET (26h): Gamma Set

画像などで、ガンマ値を変更して表示したい場合に使えそうです。

VSCSAD: Vertical Scroll Start Address of RAM (37h)

特定エリアの画像などを縦スクロールする機能。かなり難易度が高そうなので普通は使わないと思います。

その他

フレームバッファへのアクセス方法がいろいろありますが、通常はTFT_eSPIクラスやTFT_eSpriteクラス経由でアクセスしたほうがよいと思います。

まとめ

結構長くなってしまいましたが、ST7735を使う上でのアウトラインは理解できたきがします。TFT_eSPIクラスが実際の描画まわりのラッパークラスになります。ただTFT_eSPIクラスは操作単位でST7735につど転送しているので、処理がかなり重いです。

画面のチラツキも出てくるので、高速描画ではダブルバッファリングという処理を行います。これは描画途中のフレームバッファを用意して、その画面にすべての描画が終了してから、実際のフレームバッファに転送する方法です。

おそらくTFT_eSpriteがダブルバッファリングをおこなっている描画クラスな気がするのですが、らびやんさんが最新版のTFT_eSpriteに入れ替えるプルリクエストを出しているので、取り込まれたバージョンがでたら、もう少し検証を進めたいと思います。

電子工作での消費電流測定方法

概要

電子工作で利用する主に数Aまでの消費電流の測定方法を調べてみました。

商用機材(数十万円以上)

上記が非常にまとまった資料です。

デバイス電流波形アナライザ

一番細かいデータが取得できる機材。電流測定専用のオシロスコープみたいな機能です。回路の任意の場所に流れる電流を複数箇所測定できます。

DC電源アナライザ

直流安定化電源に、電流測定機能がついているもの。それなりの精度で電流測定が可能ですが、電源なので機材全体の電流しか測定できません。

高感度電流プローブ+オシロスコープ

オシロスコープを利用して、電流を測定する特別な電流プローブを利用して測定します。電流プローブが非常に高価で、高感度のものは30万円以上します。

エントリー機材(10万円前後まで)

プログラマブル電源

DC電源アナライザに近いですが、安定化電源のデータが取得できるものです。電源自体にもなって、測定もできますが、スペックがいまいちわかりにくいです。

電源制御をすることがメインで、測定目的で使うのはちょっと違うのかもしれません。

ベンチ型デジタルマルチメータ

高精度のテスターです。測定に特化しているので、時間分解能も電流分解能も高いです。電源は別に準備する必要がありますが、ちょっと個人だとベンチ型は手が出にくいです。

上記サイトですとベンチ型を使ってグラフ表示していました。

個人向け機材(1万円以下ぐらいまで)

シャント抵抗(千円以下)+オシロスコープ

オシロスコープはすでに持っているものとします!

上記が詳しいですが、電源ラインのどこかにシャント抵抗と呼ばれる抵抗を入れ、その電圧をはかることで、電流を測定します。

ハイサイド測定

電源のプラス側にシャント抵抗を入れて測定する方法です。この方法だとオシロスコープが絶縁されているか、2chのプローブでの差分で測定する必要があります。

失敗すると回路が壊れるので、よくわからない場合にはおすすめしません。

ローサイド測定

電源のGND側にシャント抵抗を入れて測定する方法です。この方法だと1chのオシロスコープでも測定が可能です。比較的失敗しにくいのでおすすめです。

シャント抵抗の選び方

シャント抵抗は流れる電流と電圧に応じて、抵抗値を選択する必要があります。基本的には電流×抵抗値=電圧という計算式になります。この電圧を測定するのですが、その電圧分回路の電圧が低下してしまいます。

5Vで動いている回路に、1Ωのシャント抵抗を使って1A流れると、1Vの電圧がシャント抵抗に流れ、回路は1V低下して4Vで動作することになります。ちょっと電圧低下しすぎで回路が動かない可能性があります。

ESP32のWi-Fi利用時は0.2A程度ですので、1Ωのシャント抵抗では0.2Vの電圧低下になります。これぐらいだったら大丈夫そうですが、瞬間電力だともっと流れていそうですので注意が必要です。

mgo-tecさんの場合には0.4Ωのシャント抵抗を利用していました。ESP32直接なので3.3V動作の回路で、1A流れても2.9Vで動く計算です。

上記がおすすめの抵抗です。チップ抵抗を必要数だけつなげるのはちょっと難易度が高いので、個人的には1Ωの抵抗モジュールを2つぐらい購入するのがおすすめです。

並列に使えば0.5Ω、直列で使えば2Ωも作れます。あと、Wが重要で5Vの回路で、1Ωに1Aを流すと抵抗には1Vが1A流れるので1Wになります。チップ抵抗の定格には収まっていますが発熱が心配です。大きいものを使っておけばとりあえず燃える心配はしなくても良さそうなので、思いっきり余裕を持ったものが安心です。

基本的には抵抗値は低いほうが回路に与える影響は少ないのですが、出力電圧が低くなります。そのため測定側のオシロスコープで、低電圧になった場合の測定精度が落ちてしまう問題があります。

抵抗値の選択は結構難しく、測定機材の精度が高ければ小さい方がいいと思いますが、流れる電流にも依存しそうです。

マルチメータ(1万円前後)

少しいい値段のするテスターを利用した測定方法です。ただし、電流測定精度は高いのですが時間的な解像度が足りません。毎秒単位での電流測定を高精度に行いたい場合には、オシロスコープより精度が高い気がします。

個人的にはよく調査してから購入すべきだと思います。ちょっと情報が少ないのと、接続に追加機材が必要になったり、無線がついているものは日本正規代理店から買わないと技適がなかったりします。

USBテスター(数千円)

一番お手軽ですが、注意が必要です。無線が使えるものは技適がないものばかりです。また、パソコンには接続できないものがあったり、画面上とデータ取得でデータ精度が違っている場合などがあります。

時間解像度も1秒単位や、15秒など瞬間よりも長時間動作させる前提の物がおおいように思えます。

電流センサー(1000円前後)

シャント抵抗+電圧測定モジュールがモジュールになっているものです。高価なモジュールはシャント抵抗以外のものもありますが、回路の途中に入れることで電流を測定できるモジュールです。

I2Cなどで通信するものが多く、測定対象の回路外の機材からも測定が可能です。

上記ではPAC1710を利用した測定を行っています。INA219とかも定番だと思います。数ms単位での数mA精度の電流測定が可能です。

まとめ

M5StickCの消費電流をもう少し高精度で計測したくて、いろいろ調べましたが結構難しいです。M5StickCの場合には電源制御ICのAXP192でも消費電流が取得できますが、測定自体が消費電力を引き上げてしまうので、ちょっと嫌な感じがします。

手持ちのオシロスコープは絶縁タイプなので、シャント抵抗もチャレンジしたいですが手持ちにないので、入手からになります。いろいろ組み合わせながら、どんな構成がいいのか検討したいと思います。

オープンソースのコード分析ツールSourcetrailを使ってみた

概要

Sourcetrailという、ソースコード分析ツールがオープンソース化して、無料で使えるようになったので、Arduino core for the ESP32を分析してみました。

セットアップ

上記からSourcetrail_2019_4_61_Windows_64bit_Portable.zipをダウンロードしてきて、展開しました。

セットアップしなくても使えるポータブル版があるのは便利です。

使い方

New Project

プロジェクトの名前と、保存場所を設定します。プロジェクト名はなんでも大丈夫で、保存場所は環境変数を入れるとおかしな動きをしたので、絶対PATHで指定してください。

標準的な保存場所は実行ファイルがあるPATH直下の「user\projects\プロジェクト名」です。

Add Source Group

Empty C++を選びました。

C++のバージョンは、Arduino IDEのコンパイルオプションから確認して「gnu++11」を選択。

ソースファイルは「%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/」を指定しました。セットアップしているバージョンに合わせて変更してください。

「show file」ボタンで対象となるファイルが含まれているかを確認してから、次に進みます。不要なファイルが含まれていたらExcluded設定で除外します。

「%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/ESP32/examples/Camera/CameraWebServer/app_httpd.cpp」は除外してもいいかもしれません。

ここが一番面倒で、ワイルドカードが使えないので、Include Pathを右下の鉛筆マークを押してから、テキストで流し込みます。

C:/Program Files (x86)/Arduino/hardware/tools/avr/lib/gcc/avr/7.3.0/include
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/xtensa-esp32-elf/include/c++/5.2.0
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/cores/esp32
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/config
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/app_trace
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/app_update
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/asio
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/bootloader_support
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/bt
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/coap
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/console
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/driver
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-tls
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp32
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_adc_cal
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_event
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_http_client
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_http_server
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_https_ota
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_ringbuf
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/ethernet
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/expat
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/fatfs
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/freemodbus
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/freertos
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/heap
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/idf_test
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/jsmn
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/json
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/libsodium
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/log
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/lwip
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mbedtls
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mdns
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/micro-ecc
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mqtt
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/newlib
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/nghttp
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/nvs_flash
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/openssl
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/protobuf-c
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/protocomm
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/pthread
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/sdmmc
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/smartconfig_ack
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/soc
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/spi_flash
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/spiffs
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/tcp_transport
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/tcpip_adapter
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/ulp
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/vfs
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wear_levelling
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wifi_provisioning
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wpa_supplicant
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/xtensa-debug-module
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-face
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp32-camera
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-face
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/fb_gfx
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/ArduinoOTA/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/AsyncUDP/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/AzureIoT/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/BLE/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/BluetoothSerial/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/DNSServer/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/EEPROM/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/ESP32/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/ESPmDNS/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/FFat/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/FS/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/HTTPClient/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/HTTPUpdate/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/NetBIOS/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/Preferences/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/SD/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/SD_MMC/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/SimpleBLE/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/SPI/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/SPIFFS/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/Ticker/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/Update/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/WebServer/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/WiFi/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/WiFiClientSecure/src
%USERPROFILE%/AppData/Local/Arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/Wire/src

私が指定したのは、上記です。M5StickCなど他のライブラリを解析するときにも、上記のリストをベースに増減するとうまくいくと思います。

こちらも設定したら「validate include directives」ボタンで、エラーが発生していないか調べます。何個か他の環境用のファイルが見つかっていませんが、必要そうなのが揃っていたら無視しても構わないと思います。

「mbedtls/esp_config.h」を読み込ませる設定が入っていないからな気もします。

Globalの方は、たぶんいらないので消せるのは消します。

Compiler Flagsはちゃんと指定したほうがいいかもしれませんが、全部削除。

New Project

設定が完了すると、最初の画面に戻ってきますので「Create」で作成します。この画面は作成したあとでも、ProjectメニューEdit Projectで開くことができます。

Start Indexing

Startで分析を開始します。この画面はEditメニューからRefreshで開けます。

初期画面

エラーが何個かでていますが、分析完了しました。

HardwareSerialを見てみる

こんな感じの構成が見えます。

HardwareSerial::writeを見てみる

こんな感じで、関係と右側にソースと宣言が開きます。

Doxygenと比べる

Doxygenはテキストがメインで、構造は図ではなく情報から自分で組み立てる必要があります。

Sourcetrailはきれいですが、Doxygenなどに使うドキュメントブロックは認識しません。Doxygenできれいに管理されているのであれば、 Doxygenで見たほうが楽かもしれません。

まとめ

人によってデータ解析の方法が違うので、図で認識する人はSourcetrailを使ったほうが便利だと思います。文字で認識する人はDoxygenかなー。

DB定義書がER図じゃないと構造が頭に入りにくい人と、脳内デバッグするからSQL文とサンプルデータの方がいい人がいるみたいな感じだと思います。

Arduino IDEでESP32フラッシュ削除プラグインを作ってみた

概要

Arduino IDEでメニューから「ESP32 Erase Flash」を選択すると、ESP32のフラッシュをすべて削除するプラグインを作ってみました。

開発環境構築

Arduino IDEはJavaで開発されているので、Javaの開発環境を準備する必要があります。

今回はArduio IDE自体をビルドできる環境を構築し、その環境で開発を行います。

上記オフィシャルWikiを参考に、CentOS7上で実験してみました。必要なパッケージをyumで導入すればビルド自体はすんなりできると思います。

プラグイン作成のサンプル解析

これ以外のプラグインを見たことがないのですが、ESP32のフラッシュ領域にdataフォルダに入っているファイルをアップするプラグインです。

これをコピーしてきて、見様見真似でコードを編集します。make.shの中身はちょっと編集しないと../../../と三階層上までjarファイルを探しに行っているところがあり、findが終わらなくなります。そして複数のファイルがあるとおかしな動きをします。。。

仕組み

フラッシュを削除する実行ファイルはesptoolで、オプションerase_flashを指定すると内容を削除してくれます。シリアルポートだけが必須で、転送速度はおそらく指定しなくても大丈夫だと思いますが、念の為指定しておきます。

esptool.py --port COM3 -b 1500000 erase_flash

作成物

上記が作成物です。使い方はArduino ESP32 filesystem uploaderとほぼ同じです。

正しく設置してからArduino IDEを再起動すると、上記のようにメニューに追加されます。

「ESP32 Erase Flash」を選択すると、ESP32のフラッシュが消されます!

esptool.py v2.6
Serial port COM3
Connecting....
Chip is ESP32-PICO-D4 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, Embedded Flash, VRef calibration in efuse, Coding Scheme None
MAC: ??:??:??:??:??:??
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 1500000
Changed.
Erasing flash (this may take a while)...
Chip erase completed successfully in 1.7s
Hard resetting via RTS pin...

まとめ

プラグインは結構かんたんに作ることができました。このプラグイン自体はあまり使うことがないのですが、プラグイン作成の流れがわかりました。

今後もう少し実用的なプラグインを作ってみたいと思っているので、その事前検証として実施してみました。

M5StickC放電実験

現時点の情報です。最新情報はM5StickC非公式日本語リファレンスを確認してみてください。

概要

M5StickCを電源OFFにした状態で放置して、BAT端子の電圧を測定して、バッテリー放電を確認しました。

満充電からの放電

時間電圧
開始4.179688
8時間後4.047852-0.13184
1日後3.955078-0.22461
4日後3.657227-0.52246
5日後3.588867-0.59082
7日半後0

まだ実験途中です。もっと早く放電するかと思ったら途中で放電がすくなくなったので、一週間以上は持ちそうかな?

11/21追記 すみません、ぼーっとしてたら電源切れていました。おそらく一週間ぐらいで完全放電すると思います。。。

放電の最後

秒数電圧
12.402
22.402
32.397
42.397
52.397
60.537
70.259
80.161
90.107
100.078
110.054
120.034
130.024
140.015
150.010
160.005
170.005
180.000

断末魔の叫びです。。。

放置してあったM5StickCは電圧が出力されていなかったので、一瞬だけ電源を接続してから、放電させました。

2.4ボルトぐらいで、一気に電圧が落ちます。ただLi-Poって2.7ボルト以下だと過放電ですよね?

バッテリー保護ICを経由していない、バッテリー直結端子ですね。

5V OUTとBAT端子の違い

電源ONの場合には5V OUTにはDCDCで作られた5Vが出力されます。電源OFFのときにはバッテリー電圧が出力されます。

が、ちゃんと測定すると5V OUTは逆流防止でおそらくダイオードが入っているので、0.1ボルトぐらいバッテリー電圧より低下していました。

まとめ

おもったより、電源OFFでも電圧落ちるんですね。RTCが搭載しているので仕方ないですが、充電していないM5StickCは時計がクリアされているのでちょっと不便です。

あと過放電でバッテリーが死にやすい気がするので、気になります。。。

常に測定していると、測定が原因でバッテリーライフに影響でそうなんで、たまーに測定しているのでもう少しデータが取れたら追記したいと思います。

M5StickCバッテリーライフ検証 その4

現時点の情報です。最新情報はM5StickC非公式日本語リファレンスを確認してみてください。

概要

その1その2その3に引き続き、バッテリー駆動時間の計測をしてみました。

ディープスリープ

#include <M5StickC.h>

void setup() {
  setCpuFrequencyMhz(240);
  M5.begin();
  M5.Axp.ScreenBreath(12);
  M5.Lcd.fillScreen(WHITE);
  Serial2.begin(115200, SERIAL_8N1, 0, 26);

  Serial2.println(M5.Axp.GetBatVoltage());

  // 画面を消す
  M5.Axp.ScreenBreath(0);

  // 60000000us = 60sのタイマー設定
  esp_sleep_enable_timer_wakeup(60000000);
 
  // ディープスリープ
  esp_deep_sleep_start();
}

void loop() {
}
条件動作時間
1分間隔タイマー10.5
5分間隔タイマー12.3117%

上記のコードだと半日しかバッテリーが持ちませんでした。1分と5分であまり動作時間に変わりがないのでCPU動作よりも、他の要因でバッテリーがなくなっていると推測することができます。

おそらく画面だけ一番暗くしていますが、AXP192が動いたままなのが原因な気がしますが未検証です。

AXP192側の条件を変えた検証を今後したいと思いますが、現状のバージョンだとAXP192クラスの動きがおかしいので、そこから検証しないといけない気もします。。。

まとめ

予想以上に長期稼働は難しそうに思えました。

ただ「M5StickCの消費電流 その2」で電源オフと、タイマーでのディープスリープで4.5mAの差があったので、80mAhのバッテリーだと長くても17時間程度しか動かないことになります。

タイマー起動だと、ESP32には電源供給せずにAXP192上のタイマーだけで管理したほうが省電力になりそうです。ちょっとAXP192のデータシート見直して、実験計画を組み直したいと思います。

これとは別に自然放電も測定していますが、一時間で0.02ボルトぐらい低下していますので、数日はもちそうです。

ただ、実験時間が長くなりすぎたので、データの取りまとめはしたいと思いますが、中国から取寄中の電流計を使った、消費電流測定を今後行っていきたいと思います。