M5StickCのdefine宣言を調べる

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

概要

M5StickCをArduino IDEで開発する場合に、最初から定義されているdefineを調べてみました。

defineの階層

  • M5StickCライブラリ
  • M5StickCボード
  • ESP32プラットフォーム

Arduino IDEでは、上記の3層にわかれています。一番上のライブラリはM5StickC.hのファイルで、自分で読み込む必要があるライブラリです。

真ん中のボードは、Ardiono IDEで選択するボードで「M5Stick-C」という表記になっていると思います。ボードごとにコンパイルや転送オプションを変更しています。

一番下にあるプラットフォームはESP32で、基本的な関数はすべてここで定義されています。

全部出してみる

defineの宣言を知るためには、コンパイラのオプションでプリプロセッサの結果を出力するのが一番正確です。

調べたいスケッチを環境設定の「より詳細な情報を表示する」でコンパイルを選択してから、コンパイルを実行するとコンソールにコンパイル時のコマンドラインが表示されると思います。

スケッチをコンパイルしています...

上記の次の行がスケッチのコンパイルですので、コピーしてきて引数を書き換えます。

  • 「-c」を「-dM -E -x c++」に書き換え
  • 最後の-o以降を削る

上記の変更をして実行してみるとプロンプトにdefineの一覧が出力されます。ただしWindows環境で試すと、コマンドラインの文字数制限にひっかかるのでそのままだと実行できません。

バッチファイルにコマンドを書いて実行するのが、かんたんだと思います。

ただし、この結果出力を見たところ1万行以上出力されたので、とてもじゃないですがぜんぶ見ることができません。

ざっとみたところ、気になったものを紹介します。

#define B0 0
#define B00 0
#define B000 0
#define B0000 0
#define B00000 0
#define B000000 0
#define B0000000 0
#define B00000000 0
#define B00000001 1
#define B0000001 1

2進数でこんな感じのがずらっと255まで並んでいました。なにに使うんだろう。。。

あとは関数系のマクロが大量に入っていました。

#define GPIO_PIN0_CONFIG 0x00000003
#define GPIO_PIN0_CONFIG_M ((GPIO_PIN0_CONFIG_V)<<(GPIO_PIN0_CONFIG_S))
#define GPIO_PIN0_CONFIG_S 11
#define GPIO_PIN0_CONFIG_V 0x3
#define GPIO_PIN0_INT_ENA 0x0000001F
#define GPIO_PIN0_INT_ENA_M ((GPIO_PIN0_INT_ENA_V)<<(GPIO_PIN0_INT_ENA_S))
#define GPIO_PIN0_INT_ENA_S 13
#define GPIO_PIN0_INT_ENA_V 0x1F
#define GPIO_PIN0_INT_TYPE 0x00000007
#define GPIO_PIN0_INT_TYPE_M ((GPIO_PIN0_INT_TYPE_V)<<(GPIO_PIN0_INT_TYPE_S))
#define GPIO_PIN0_INT_TYPE_S 7
#define GPIO_PIN0_INT_TYPE_V 0x7
#define GPIO_PIN0_PAD_DRIVER (BIT(2))
#define GPIO_PIN0_PAD_DRIVER_M (BIT(2))
#define GPIO_PIN0_PAD_DRIVER_S 2
#define GPIO_PIN0_PAD_DRIVER_V 0x1
#define GPIO_PIN0_REG (DR_REG_GPIO_BASE + 0x0088)

こんなのが0から99まで入っていて、似たようなのも大量にありました。

参考ページ

ただし、上記はgccでコンパイルしていますが、esp32はg++なのでコマンドが若干違います。

気になった単語抜き出し

M5

#define ARDUINO_BOARD "M5Stick_C"
#define ARDUINO_M5Stick_C 1
#define ARDUINO_VARIANT "m5stick_c"
#define M5_BUTTON_HOME 37
#define M5_BUTTON_RST 39
#define M5_IR 9
#define M5_LED 10
#define _M5DISPLAY_H_ 
#define _M5STICKC_H_ 
#define m5 M5

ボードとライブラリで宣言されたdefineだと思います。どこでなにを宣言しているのかは後ほど調べたいと思います。

ESP32

#define ARDUINO_ARCH_ESP32 1
#define ESP32 1
#define ESP_FAIL -1
#define ESP_OK 0
#define ESP_PLATFORM 1

たくさんあったので、抜粋です。

ESP32のESP-IDFライブラリの実行結果は0だと正常終了で、それ以外だとエラーみたいで、これ以外にもエラーコードがたくさん定義されていました。

宣言場所を探す

libraries\M5StickC\src\utility\Config.h

#define M5_IR      9
#define M5_LED     10
#define M5_BUTTON_HOME 37
#define M5_BUTTON_RST  39

#define BUTTON_A_PIN 37
#define BUTTON_B_PIN 39

// UART
#define USE_SERIAL Serial

上記がM5StickCライブラリの宣言です。BUTTON_A_PINってのも宣言されているんですね。

packages\esp32\hardware\esp32\1.0.4\boards.txt

m5stick-c.build.variant=m5stick_c
m5stick-c.build.board=M5Stick_C

上記の設定値が自動的にdefine宣言されるようです。

#define ARDUINO_BOARD "M5Stick_C"
#define ARDUINO_M5Stick_C 1
#define ARDUINO_VARIANT "m5stick_c"

これに対応しているはずですが、ARDUINO_M5Stick_Cはm5stick-c.build.boardの値で自動宣言されるようです。

packages\esp32\hardware\esp32\1.0.4\variants\m5stick_c\pins_arduino.h

#define EXTERNAL_NUM_INTERRUPTS 16
#define NUM_DIGITAL_PINS        40
#define NUM_ANALOG_INPUTS       16

#define analogInputToDigitalPin(p)  (((p)<20)?(esp32_adc2gpio[(p)]):-1)
#define digitalPinToInterrupt(p)    (((p)<40)?(p):-1)
#define digitalPinHasPWM(p)         (p < 34)

static const uint8_t TX = 1;
static const uint8_t RX = 3;

static const uint8_t SDA = 32;
static const uint8_t SCL = 33;

static const uint8_t SS    = 5;
static const uint8_t MOSI  = 15;
static const uint8_t MISO  = 36;
static const uint8_t SCK   = 13;

static const uint8_t G9 = 9;
static const uint8_t G10 = 10;
static const uint8_t G37 = 37;
static const uint8_t G39 = 39;
static const uint8_t G32 = 32;
static const uint8_t G33 = 33;
static const uint8_t G26 = 26;
static const uint8_t G36 = 36;
static const uint8_t G0 = 0;

static const uint8_t DAC1 = 25;
static const uint8_t DAC2 = 26;

static const uint8_t ADC1 = 35;
static const uint8_t ADC2 = 36;

あとM5StickCに関係あるのが、上記ファイルです。SDAとSCLのデフォルトなどはこの値を取得していると思います。

基本的にはここの値は使わないで、自分で指定するほうが安全だと思います。

環境判定用define

ESP32プラットフォーム

#ifdef ARDUINO_ARCH_ESP32
#include "???.h"
#endif

もしくは

#if defined(ARDUINO_ARCH_ESP32)
#include "???.h"
#endif

ARDUINO_ARCH_ESP32が宣言されているかで確認するのが一般的みたいです。AdafruitのライブラリだとESP32で確認していますが、公式ライブラリはARDUINO_ARCH_ESP32を使っていました。

M5StickCボード

#ifdef ARDUINO_M5Stick_C
#include "???.h"
#endif

もしくは

#if defined(ARDUINO_M5Stick_C)
#include "???.h"
#endif

ARDUINO_M5Stick_Cが宣言されているかで確認できますが、あまりボードの判定はしていないみたいです。

M5StickCライブラリ

#ifdef _M5STICKC_H_
#include "???.h"
#endif

もしくは

#if defined(_M5STICKC_H_)
#include "???.h"
#endif

_M5STICKC_H_の宣言で確認できますが、こちらもあまり使わないかな?

まとめ

実際のところESP32かの確認は必要ですが、あまりボードの違いは少ないので判定している例は少なかったです。

しかしながら1万以上もdefineがされていたとは。。。

2進数のはArduino本家のbinary.hをそのままESP32に持ってきているから、入っているみたいです。

M5StickCでUIFlow入門 その4 条件分岐

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

概要

前回はボタンを押したらLEDをつける制御をやりました。途中にでてきた条件分岐を今回説明したいと思います。

条件分岐とは?

一般的にはif文という名前で呼ばれている分岐です。

前回のブロックだと、この青い「もし」というブロックになります。条件に合致した場合に、特定の処理を行うという処理です。

条件分岐で重要なのは、すべての条件をもれなくもうらしているかと、その条件が同時に発生することがあるかを意識することです。

ちょっとむずかしいので、個別に説明をします。

条件にもれがないか?

このAボタンの状態は、pressedの押しているか、releasedの押していないの2種類です。前回はwasPressedとwasがついて4種類ありましたが簡易的な方を使っています。

pressedの条件のみにすると、releasedのときの処理がないことになります。もちろん、スタートボタンなどの用途だと押したことだけが重要で、離したことの検知が必要ない場合もあります。

このような場合に、考慮もれで条件が抜けているのか、必要ないから入れていないのかは一度考えてからプログラムを組むようにしてください。

また、この例だと「もし」を2つ並べて使っていましたが他の方法もあります。

「もし」の左にある設定ボタンを押すと、下に引き出しが表示されます。

「else if」のブロックを、右側にあるifの下にくっつけてみました。するともとのブロックが変化します。もう一度「もし」の左にある設定ボタンを押すと、吹き出しが消えます。

「そうでなくもし」という条件があったので、そこにAボタンが押されていない場合にはLEDを消すという処理を追加しました。

これで、最初と同じ動きのプログラムができました。「もし」を並べるのと、「そうでなくてもし」を使うので動きが同じであれば、どちらを使っても構いません。

上記は、さらに「else」というブロックを設定から追加しました。「そうでなければ」というのが増えて、「もし」と「そうでなくもし」に該当しない場合には、ここの処理が動きます。

この場合Aボタンを押している場合にはLEDがON、Aボタンを押していない場合にはLEDがOFF、それ以外の場合には画面の背景色を赤に設定するという動きになります。

Aボタンの状態は押しているか、押していないかの2種類ですので、画面が赤くなることはないと思います。

しかし、悲しいですがこのプログラムを実行して、ものすごくAボタンを連打すると赤くなります。

連打していると、Aボタンの状態が素早く変わります。「もし」の瞬間にはAボタンが押されていなく、「そうでなくもし」のときにAボタンが押している場合に、画面が赤くなります。

これを防ぐのには、上記のように「もし」とelseの「そうでなければ」の組み合わせにすることで、「もし」のAボタンが押されている場合以外はすべて、「そうでなければ」でLEDが消えます。

ただし、これを防ぐ必要のないプログラムもあると思います。たんにLEDをつけるか消すかだけであれば「もし」を複数並べていても、それほど問題にはならないです。

簡易的にプログラムをするのか、なるべく厳密にプログラムをするのかは意識して考えて選ぶ必要があると思います。最初はまずは動かして楽しみ、慣れてきたらより厳密に組んだほうがトラブルが少ないと思います。

条件の同時発生

こんどは、Aボタンを押すとLEDをつけて、Bボタンを押すとLEDが消えます。

動かしてみると、たしかに意図通りに動くと思います。さて、AボタンとBボタンを同時に押した場合にどうなるでしょうか?

試してみるとLEDが点滅します。

このように、スタートとストップが同時に動いた場合に、どのような処理が好ましいかは状況により異なります。一般的に点滅するってのは、好ましくない動きだと思います。

この場合の考え方としては、安全側に倒すという処理になります。この安全側というのは、どっちの状態がより安全かという意味です。一般的にはスタートとストップだとストップの優先順位を高くします。

最近の車などの場合にはアクセルとブレーキを同時に踏むと、ブレーキが優先されます。それでも暴走する車が多いのは、ブレーキと思ってアクセルを踏んでいるからみたいですね。

さて、常にストップを優先したほうがいいかというと違っていまして、警告灯のスタート、ストップの場合には、安全なのは警告灯がついている方です。赤信号と青信号どちらを表示すればいいか迷ったときには、とりあえず赤信号を表示したほうが安全だと思います。

今回はAボタンを優先した場合、対策したプログラムは上記になります。Aボタンが押しているとLEDがついて、Aボタンが押されていなくBボタンが押されている場合のみLEDがOFFになります。

これで、AボタンとBボタンを同時に押した場合でもLEDがONになります。

ちなみに、else ifの「そうでなくもし」は複数つなげることも可能です。

変数

UIFlowでは変数と呼ばれる数値や文字を保存しておく仕組みがあります。

変数を使うためには、最初に作る必要があります。メニューから変数を選び、一番上にある「変数の作成…」を選び変数名を入力します。

ここの画面では日本語が入力できますが、現在日本語の変数は利用できないので注意してください。ここでは「LED」という変数を作成しています。

上記がいきなり完成図ですが、変数を作ると作った変数を変更する「○○を■にする」ブロックと、変数を増やすブロックと、変数自体のブロックが追加されます。

Setupで最初にLEDという変数を0に設定しています。その後にAボタンが押されると「もし」ブロックの中で変数を比較するブロックで、LED=0の場合(LEDが0の場合)にはLEDをつけて、変数を1にしています。

Aボタンを押すたびにLEDの点灯状態が変わります。

上記のように0=LEDと逆にしても、動きます。

UIFlowの表記プログラムの表記条件
==同じ値の場合
!=違う値の場合
<右辺が大きい場合
<=, =<同じ値か右辺が大きい場合
>左辺が大きい場合
>=, =>同じ値か左辺が大きい場合

ここの条件は上記の設定があります。UIFlowは左側の表記を使っていますが、プログラムでは真ん中の表記を使うことが多いです。

プログラムの場合には、変数に数値を代入するときに=を利用します。LED=0と書いた場合には、等しいかを比べるのではなくLEDという変数に0を代入して、結果的にLEDと0が等しくなります。

そのため、変数と数値が等しいかを比べる場合にはLED==0などのように、他の記号を使っている場合が多いです。

さきほどは数値で判定していましたが、文字列でも構いません。上記は真理値や論理値と呼ばれるtrue(真)、false(偽)を使った例です。

論理値の場合には、「もし」の条件に直接置くことができます。trueのときには条件に合致していることになります。LED=trueと値を比べることもできます。

変数に入れる値は、数値でも文字列でも真理値でも構いません。ただし真理値はtrueとfalseの2つの状態しかありませんので注意してください。

おそらくは、文字列にしたほうが何をしようとしているプログラムなのかを把握するのにはいいと思います。

気をつけないといけないのは、値の初期化を忘れないことです。上記のプログラムの場合にはLEDをOFFで初期化していますが、この初期化を忘れても「そうでなければ」の処理に入り、値が正常に戻ります。

上記のようなコードの場合に初期化を忘れると、LEDの中身がONでもOFFでもないので画面が赤くなり、処理が動きません。

このようなプログラムが悪いかというと、初期化漏れがすぐにわかるのでちゃんと動作検証をする場合には好ましいと思います。

初期化していないおかしな状態でも、なんとなく安全と思われる動きをするプログラムでも、想定外のことが起きたらすぐにわかるようにして、動作はしないプログラムもどちらでも構いません。

そのときに作ろうとしているプログラムが、どのような動きの方が好ましいかを考えながら作ってみてください。

一般的に人命やお金に絡む処理に関しては、想定外のことが起こるとストップするか、安全な動きになるように作ることが多いです。

まとめ

もしのif文だけで終わってしまいました。たんに使い方を書いたUIlowの入門ではなく、UIFlowを使ったプログラム入門として、気をつけないといけないことはちょっと、くどくなりますが多めに書いてあります。

ESP32のULPアセンブリ言語入門 その8 ulptool

概要

前回までで、Cマクロを使ったULPの解説が終わりました。今回はCマクロではなく、Arduino IDEから直接ULPアセンブラを使うことができるulptoolの紹介をしたいと思います。

ulptoolとは?

ulptoolとはArduino IDEからULPアセンブラを呼び出すことができるツール群です。オフィシャルのツールではなく、有志が作ったツールになります。

ulptool自体にはArduino IDEからビルドツールを呼び出す仕組みのみが提供されており、実際のビルドツールは別にあります。

Binutils fork with support for the ESP32 ULP co-processor

Binutilsとは、各種バイナリファイルを作成するツール群です。多くの場合gccを改造して、特殊なCPUなどに対応した実行ファイルを作成することができます。

BinutilsはESP32の開発元であるEspressif社が提供しています。

セットアップ方法

基本的にはulptoolのトップに書いてありますが、英語でちょっとわかりにくいところがあるので、説明したいと思います。

Python2.7のセットアップ

ulptoolはPython2.7で動きますので、あらかじめPython2.7を入れておく必要があります。

注意しておく点として、現在Pythonは3.x系が主流であり、2.7は過去のプログラムを動かすためにだけ存在しており、2.xと3.xはプログラムの互換性はありません。

すでに3.xが入っている環境の場合、2.7も入れることができますが標準で利用するのは3.xにしたほうがいいと思います。

上記がPython2.7の最新版のダウンロードページです。Windowsの場合「Windows x86-64 MSI installer」あたりが、一番一般的だと思います。

2.7の場合、もう新しいバージョンはできないとは思いますが以下のページで確認ができます。

上記のようにプラットフォーム別のダウンロードページにいくと、最新バージョンが書いてあるので、一応確認してから入れてみてください。

セットアップする場所は好みの場所でよいのですが、「C:\Python\Python27」に私は入れました。デフォルトのセットアップ先の場合には書き込み権限がなかったりして、追加ツールを入れるのに失敗する可能性があるので、他の場所にしたほうがおすすめです。

また、Windowsの場合にはPathに追加するというオプションがありますが、2.7系はPathの追加を行わず、3.x系の最新バージョンをそのあと入れて、そちらのPathを追加するのをおすすめします。

ulptoolのダウンロード

上記のリリースページから最新版のソースコードをダウンロードします。

ulptoolの展開

ダウンロードしたzipファイルを展開します。そのまま展開するとulptool-2.4.1などのようにバージョン番号ついたフォルダができあがるので、バージョン番号を消してulptoolと名前を変更します。

ulptoolのコピー

  • (Mac OS) ~/Library/Arduino15/packages/esp32
  • (Windows) C:\Users\\AppData\Local\Arduino15\packages\esp32
  • (Linux) ~/.arduino15/packages/esp32

コピー先は環境によって異なるので注意してください。

Arduino IDEの環境設定から、上記の赤で囲われたpreferences.txtをクリックすると、該当フォルダのArduino15が開くと思います。

packages\esp32\toolsと開いて、展開したulptoolをコピーします。

「Arduino15\packages\esp32\tools\ulptool」などのフォルダ構成になると思います。

Binutilsのダウンロード

上記から最新バージョンのBinutilsをダウンロードします。環境ごとにダウンロードするファイルが異なるので注意してください。

また、esp32ulpとesp32s2ulpがあるので注意してください。esp32s2ulpはESP32の独自ULPではなく、RISC-Vベース新しいチップ用のツールになります。

Windowsの場合には「binutils-esp32ulp-win32-x.xx.xx-esp-xxxxxxxx.zip」などのファイルになると思います。

Binutilsの展開

ダウンロードしたzipファイルを展開します。esp32ulp-elf-binutilsフォルダに展開されると思います。

Binutilsのコピー

先程のulptoolの中にあるsrcフォルダにesp32ulp-elf-binutilsをコピーします。

「Arduino15\packages\esp32\tools\ulptool\src\esp32ulp-elf-binutils」などのフォルダ構成になると思います。

platform.local.txtのコピー

このままでは、コピーしたulptoolが使われないので、追加の設定ファイルである「platform.local.txt」を指定フォルダにコピーします。

こちらも環境により異なりますが「Arduino15\packages\esp32\hardware\esp32\1.0.4」などの場所にあります。「platform.txt」があるフォルダです。

最後のバージョン番号は今後変わるとおもいますので、今入っているバージョン番号のフォルダに入れます。ここに入れるので、ESP32のライブラリのバージョンが変わるとulptoolも消えてしまうのでセットアップし直しになります。

また、すでに「platform.local.txt」がある場合には上書きすると既存の設定が消えてしまうので、ファイルの中身をコピーして追記する必要があると思います。

「platform.local.txt」の編集

version=2.3.0

## paths
compiler.ulp.path={runtime.tools.ulptool.path}/esp32ulp-elf-binutils/bin/
tools.ulptool.path={runtime.tools.ulptool.path}/

## tool name
tools.ulptool.cmd=esp32ulp_build_recipe.py

## ulp build tool
#compiler.s.cmd=python "{tools.ulptool.path}{tools.ulptool.cmd}"
compiler.s.cmd=C:\Python\Python27\python "{tools.ulptool.path}{tools.ulptool.cmd}"

## ulp_main.ld file address
compiler.c.elf.extra_flags="-L{build.path}/sketch/" -T ulp_main.ld "{build.path}/sketch/ulp_main.bin.bin.o"
(以下略)

上記が私の環境に合わせた設定ファイルです。「python」とPath指定無しだったプログラムに対して、「C:\Python\Python27\python」と明示的に2.7のPythonを使うように指定しています。

私の環境は「C:\Python\Python38\python」は標準で使う最新版のPythonで、3.x系で動かない古いスクリプトのみ2.7で動かすようにしています。

動作確認

「ulptool\src\ulp_examples\ulp_README」

上記にサンプルスケッチがあるので開いてみます。Arduino IDEのメニューのスケッチ例からは探しに行かない場所になるので注意してください。

ulp count: 0
ulp count: 1
ulp count: 2
ulp count: 3
ulp count: 4

スケッチを動かしてみて、上記のような出力がシリアルモニタに出力されていれば環境構築は完了です。

ESP32 ULP Debugger組み込み

ちょっとこのままだとわかりにくいと思うので、ESP32 ULP Debuggerも同時に使ってみたいと思います。

#include "esp32/ulp.h"
// include ulp header you will create
#include "ulp_main.h"
// include ulptool binary load function
#include "ulptool.h"
#include "UlpDebug.h"

// Unlike the esp-idf always use these binary blob names
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[]   asm("_binary_ulp_main_bin_end");

static void init_run_ulp(uint32_t usec);

void setup() {
    Serial.begin(115200);
    memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM);
    delay(1000);
    init_run_ulp(100 * 1000); // 100 msec
}

void loop() {
    // ulp variables data is the lower 16 bits
    Serial.printf("ulp count: %u\n", ulp_count & 0xFFFF);
    ulpDump();
    delay(100);
}
//hash: 5c389934265d8016df226704091cd30a
static void init_run_ulp(uint32_t usec) {
    // initialize ulp variable
    ulp_set_wakeup_period(0, usec);
    esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
    // ulp coprocessor will run on its own now
    ulp_count = 0;
    err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
    if (err) Serial.println("Error Starting ULP Coprocessor");
}

色がついている行が追記した行です。UlpDebug.hを読み込んで、memsetでULPで使うRTC_SLOW_MEM領域を0クリアしています。

loop()の中でulpDump()を呼び出すことで、ULPの逆アセンブラ結果を出力しています。

ちなみに、このままだと100ミリ秒ごとに出力されるので、delay(100)の数は大きくしたほうが見やすいと思います。

ulp count: 2
====================================
0000 : 72800053 PROG MOVE   R3,   5			// R3 = 5
0001 : D000000C PROG LD     R0, R3,   0			// R0 = MEM[R3+0]
0002 : 72000010 PROG ADD    R0, R0,   1			// R0 = R0 + 1
0003 : 6800000C PROG ST     R0, R3,   0			// MEM[R3+0] = R0
0004 : B0000000 PROG HALT				// HALT
0005 : 00630002 DATA     2				// ST ADDR:0x0003

上記のような出力結果になれば成功です。

/* Define variables, which go into .bss section (zero-initialized data) */
    .data
/* Store count value */
    .global count
count:
    .long 0
/* Code goes into .text section */
    .text
    .global entry
entry:
    move    r3, count
    ld      r0, r3, 0
    add     r0, r0, 1
    st      r0, r3, 0
    halt

上記が元のULPアセンブリコードになります。逆アセンブラの結果と比べてみると、データが一番上で「.global count」を宣言していますが、プログラムの後ろに実際は配置されているのがわかります。

ulptoolの変数アクセス

これまでのESP32 ULP Debuggerを使ったCマクロでは、最初にenumで変数を宣言し、RTC_SLOW_MEM[SLOW_COUNT]などの形でアクセスしていました。

しかし通常のアセンブラではプログラムの後ろに変数が置かれているので、実際にアセンブラでマシン語にしたあとでないと、変数のアドレスが確定しません。

そこで、ulptoolではちょっと特殊なことをしています。

/*
    Put your ULP globals here you want visibility
    for your sketch. Add "ulp_" to the beginning
    of the variable name and must be size 'uint32_t'
*/
#include "Arduino.h"

extern uint32_t ulp_entry;
extern uint32_t ulp_count;

上記はulp_main.hというファイルです。ここで、externを使って外部で変数が確保されていると宣言しています。

ulp_entryとulp_countの2つありますが、これはULPアセンブリコードの「.global count」と「.global entry」の行に対応しています。

内部のコードを見てみたところ、マップファイルを分析して、シンボルをulp_main.ldに定義する処理がesp32ulp_mapgen.pyで実行されていました。

/* Variable definitions for ESP32ULP linker
 * This file is generated automatically by esp32ulp_mapgen.py utility.
 */

PROVIDE ( ulp_count = 0x50000014 );
PROVIDE ( ulp_entry = 0x50000000 );

上記が実際に生成されていたulp_main.ldファイルでulp_entryがULPが実行開始されるアドレスです。0x50000000はRTC_SLOW_MEMのアドレスなので、先頭から開始。

0x50000014は先頭から0x14(20)バイト進んだところで、RTC_SLOW_MEMは4バイト(32ビット)単位なので、5個進んだ場所である、RTC_SLOW_MEM[5]になります。

逆アセンブラの結果と一致しています。

そのため、Arduino側からはulp_countと書くことで、RTC_SLOW_MEM[5]と同じ値にアクセスすることができます。

ちなみに、処理の中でulp_main.h自体も作成してくれているのですが、コンパイルはすでに終わっているので読み込んでいません。そして、そのファイルは実際のプロジェクトファイルを更新していないので意味がないファイルとなっています。

その他のスケッチを確認

よく見るとADCなど苦労したところのスケッチ例がありました。ほぼ同じことをしていたので良かったです。

I2CのサンプルもあるのですがI2Cの専用命令を使っていません。自分でGPIOを直接叩いてソフトウエア的にI2Cプロトコルを実装しています。

ちょっと調べてみたところ、ULP標準のI2CコマンドはGPIO4,GPIO0かGPIO2,GPIO15の組み合わせでしか使えないようです。

ESP32テクニカルマニュアルの29.6 RTC_I2C Controllerからの項目で解説があり、4.11 RTC_MUX Pin Listに使えるPINの一覧が書いてあります。

実際のところ、GPIO0もGPIO2もブートに関するPINなので、あまり外部接続したくありません。そのためULPハードウエアのI2Cプロトコルは使わずに、ソフトウエア実装のI2Cをほとんどの人が使っているようでした。

ulp_i2c_bitbangというスケッチ例が入っており、こちらを参考に実装している人が多いです。しかしながらかなり面倒な処理が大量にあるので、これをCマクロで実装するのはちょっと大変そうです。

ESP32 ULP Debuggerあたりにヘルパーマクロを用意して、気軽に使えるようにしたほうがいいのかな、、、

まとめ

かなりディープな内容になってきていますので、普通の人が読んでみ意味がない記事になってきているような気がします。

ESP32-S2が販売されたら、RISC-V版のULPを使っての一般的なアセンブラ解説を書こうと思いますが、この記事はどんどんディープな方向に進んでいきたいと思います。

次はESP32 ULP Debuggerをそろそろ修正したいと思いますので、少し時間があくかもしれません。

M5StickC Type-C認識検証

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

概要

M5StickCとType-Cの組み合わせで認識しないとの度々SNSでみますので、検証してみました。とくに最近のMacBookなどはType-Cポートしかないので、認識が大変みたいでした。

検証環境

手元にはType-Cポートも持つMacがなかったので、Windowsノートで検証しています。結果が違う可能性があるのでご注意ください。

M5StickCのバージョン確認

M5StickC本体の製造時期によって、何種類かバージョンがあります。左側の本体が新しいのですが、青いRTCの表示があるバージョンであれば、Type-C to Type-Cのケーブルで接続しても問題なく認識します。

それ以前のバージョンでは、そのままでは認識しないのでひと手間必要になります。

Type-AからType-Cへの変換

購入したのは上の商品です。Type-Cポートに普通のType-Aケーブルを変換です。100円ショップでも購入できると思います。

認識手順(電源オフか他のデバイスを認識させる)

RTC表示のあるM5StickCの場合にはこれで認識します。しかし、古いバージョンのM5StickCを本体付属ケーブルで接続しても、認識しません。本体の電源を切るか、一度他のデバイスを認識させる必要があります。

M5StickCの電源をOFFにしてから差し込むか、差し込んでいる状態でOFFにしてください。M5StickCの電源が自動で入れば認識しました。

おすすめ構成

あいだにUSB Hubを入れることで、安定して認識するようになります。

とはいえ、最初からType-CのHubを購入したほうが安心です。

まとめ

M5StickCはType-Cコネクタを採用していますが、最近までちゃんとした実装をしていなかったようです。パソコンでは上記の方法で認識するようになりますが、充電専用のType-Cケーブルからは充電もできませんので注意してください。

M5StickCでUIFlow入門 その3 LED制御とブロックの種類

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

概要

前回はCloud版とDesktop版でわけましたが、今回からは共通です。画面キャプチャがやりやすいDesktop版を利用していますが、利用しているバージョンは同じなので機能に差はありません。

ボタンを押したらLEDをつける

今回からすこし実践的なプログラムをしてみたいと思います。M5StickCにはボタンとLEDが内蔵されているので、使ってみたいと思います。

上記がイベントを選択した画面です。前回ずっとのループ処理を使ったと思います。その下にはボタン系のイベントと、タイマー系のイベントがあります。

今回はボタン系のイベントを使って、ボタンを押したときにLEDをつける処理を作ってみたいと思います。

ボタンを押したときの処理は、上記のブロックを使います。ボタンは真ん中のAボタンと右側にあるBボタンが選択できます。

条件メモ
wasPressedボタンを押したとき
wasReleasedボタンを離したとき
longPress長押ししたとき
wasDoublePressすばやく2度押ししたとき

通常は押したときのwasPressedと、離したときのwasReleasedを使います。それ以外の操作をしたい場合に長押しと2度押しを使いますが操作が難しいので、なるべく使わない方がいいと思います。

今回はAボタンを押したときにLEDをつけて、離したときにLEDを消したいと思います。

ハードウエアのLEDの中にLEDのONとOFFがあるので使ってみます。wasPressedで押したときにLED ON、wasReleasedで離したときにLED OFFにします。

今回はSetupのブロックには何もつけません。ボタンのブロックもどこにおいても大丈夫です。

この状態で再生ボタンを押して実行してみてください。Aボタンを押している時だけLEDがつきましたでしょうか?

ブロックの種類

細かいブロックの説明は今後していきますが、ブロックの形によって種類がわかれているので、見分けることができるようになってください。

上下にくっつくブロック

上記のような形のブロックが上下にくっついてつながるブロックになります。一番したの「画面をクリア」ブロックのように、上がへこんでいて、下がでっぱっています。どこにもくっついていないブロックは色が灰色になって、無効であることがわかるようになっています。

独立して動くブロック

ボタンのように独立して動くブロックは、外側にでっぱりもへこみもありません。

上のようなブロックが、独立して動くブロックになります。

値のブロック

左側がでっぱっているブロックは値のブロックになります。他のブロックに、その形のへこみがありますので、くっつけることで数値や文字を指定することができます。

内部処理があるブロック

複数のブロックの種類が複合しているものがあります。もしというブロックは、右側に条件の値のブロックがあり、その下には上下につながるブロックもくっつけることができます。

上記のプログラムでも、ボタンを押したらLEDがつき、ボタンを離したらLEDがきえる処理にもなります。

ずっとでループを作る組み方は古典的なプログラム方法ですが、UIFlowの場合にはイベントの独立したブロックを使うほうがスッキリした画面になると思います。

プログラムには唯一の正解はありませんので、最終的に動いていればどんな形になっても構わないと思います。

ただし、みやすさなどの差はでてくるので、なるべくシンプルな処理の方が好ましいとされています。

LEDチカチカ(Lチカ)

電子工作では、最初にLEDをチカチカ点灯させるプログラムを作ることが多いので、作ってみたいと思います。

今回は0.5秒間LEDをつけてから、0.5秒間LEDを消すを繰り返すLチカにしたいと思います。1秒は1000ミリ秒なので、プログラムでは少数点がある0.5秒ではなく、500ミリ秒として処理する場合が多いです。

上記が一例です。ずっとブロックでループさせて、LEDつけてから0.5秒停止して、その後LEDを消してから0.5秒停止しています。

最後の0.5秒停止を忘れると、LEDがほぼ消えていない状態になると思います。

上記がちょっと変わった処理になります。細かい処理は今度説明しますが、1秒間隔で呼び出すタイマーをつかっています。タイマーが呼び出されたときブロックの中で、LEDをつけてから0.5秒間停止してLEDを消しています。

こちらの処理ではLEDを消してからの停止していません。しかしこの処理でも0.5秒間LEDがついて、0.5秒間LEDが消えているLチカが正しく動きます。

わかりやすさのために、LED OFFブロックの下に500ミリ秒停止を追加してもかまいません。

なくても良いブロックは削除するのでも、わかりやすさのために追加するのもどちらも正しい考え方です。プログラムは自由に組めるので、好きなように組んでいってください。

とはいえ、考え方はプログラムの中では統一したほうが好ましいです。同じような処理でこっちは消して、こっちは追加してと混在しているのは好ましくありませんので、どちらかに統一してください。

まとめ

今回も概要的な説明になってしまいましたが、プログラムに正解はないので自由に試してみてください。

次回からはもう少しブロックの動きについて説明をしていければと思います。

続編

RISC-Vボードを調べる

概要

いまさらですが、RISC-Vのボードと開発環境まわりを調べてみました。

Arduino IDE環境

Arduino IDEで開発する場合には、CPUの系統により2種類ありました。Arduino環境なので独自拡張のC++言語での開発になります。

SiFive

こちらがRISC-V本家用のボードで、標準的なCPUですが、非常に高いです。HiFive1 RevBなどが代表的なボードです。

上記をArduino IDEの設定に追加すれば利用できますが現在LinuxとMacの環境でないと動きません。Windowsで開発する場合には利用できないので注意しましょう。

Kendryte K210

一番手に入りやすいCPUシリーズです。Sipeed Maix M1 Dockなどが代表的なボードです。Sipeed Longan Nanoなどは違うシリーズなので注意してください。

PlatformIO環境

Arduino IDEと似ていますが、エディタが自由に使える環境です。一番多くのCPUに対応しています。

ボードにより、利用できるフレームワークが違うので注意してください。Arduino環境の使えるボードと、独自フレームワークにしか対応していないボードがあります。

基本的には系統はなく、すべてのボードが利用可能なのですが、上記のページでグループ分けされていましたのでグループ別に紹介します。

SiFive

Freedom系の他に、SiFive E31の標準的なRISC-Vに対応しています。全般的に高めのボードが多いです。Freedom E SDKとZephyrRTOSに対応しているボードが多いようです。

RISC-V GAP

GAPuino GAP8というボードに対応しています。日本でも取り扱いがないのでちょっと特殊です。

Kendryte K210

こちらも一番入手しやすいK210には対応していました。Arduino環境とKendryte独自環境での開発に対応していました。

GigaDevice GD32V

GD32VF103向けの開発環境です。一番安価で手に入りやすいSipeed Longan Nanoはこの環境になります。

MaixPy環境

MicropythonをK210に対応させた環境になります。そのためK210での標準的な開発環境となります。

MaixPy IDEのダウンロードURLは、どこにあるのかがわかりにくいです。上記のM5StickVのページから落とすのが一番わかりやすいと思います。

ちなみに、上記が本家の置き場です。どこから落とすのかわかりますか?

なんと、このアンダーバー(_)の中に入っています。。。

上記が、0.2.4のダウンロードページになります。

Freedom Studio環境

HiFive1などのRISC-V標準の開発環境になります。StudioはEclipseベースの統合環境なので、CLIベースのFreedom-E-SDKを直接使うか、PlatformIO経由で使ったほうが使いやすいかもしれません。

下の方にFreedom E SDKとFreedom Studioのダウンロードリンクがあります。

Zephyr環境

Linux FoundationがすすめているリアルタイムOS(RTOS)の一種です。非常にシンプルなOSですが、Freedom-E-SDKと使える環境は同じなのでまずはFreedom環境の方がいいのかもしれません。

上記にサポートしているボードの一覧があります。

Tang Dynasty IDE環境

Sipeed Tang PriMER FPGA用の開発環境でTD IDEとも呼ばれています。

上記からダウンロードできるようです。

RISC-V GNU Compiler Toolchain環境

GCCの環境もあるようです。しかし、いろいろ環境構築が大変そうです、、、

おすすめボード

まだ何がおすすめなのかわかっていません。。。

HiFive1 RevB

リファレンスボードなので、これ持っておいたほうがいい気がしますが、結構高いです。

Sipeed Maix Bit with Mic

Kendryte K210だと一番安いのかもしれません。(Shigezoneさんの価格なんでちょっと安すぎますが、、、)

Arduino IDEで使わないんだったらM5StickVも楽しそうです。ただMaix Bitが動くってことは、M5StickVも頑張ればArduino IDEで動きそうなんですけれどね、、、

Sipeed Longan Nano

圧倒的な安さなので、惹かれますがちょっと環境が特殊です。個人的にはPlatformIOは便利なんですが、重い印象があります。JTAGつけてのデバッグなどの環境構築は一番ラクだとは思います。

RISC-Vボード一覧

スイッチサイエンス秋月電子Shigezone商品名CPU価格ArduinoPlatformIOMaixPyFreedom StudioZephyrTang Dynasty IDE
HiFive1 RevBFreedom E3106600SiFiveSiFiveFreedom StudioRISCV
SparkFun RED-V RedBoardSiFive E31 CPU Coreplex4594SiFiveFreedom StudioRISCV
SparkFun RED-V Thing PlusSiFive E31 CPU Coreplex4043SiFiveFreedom StudioRISCV
HiFive1Freedom E310SiFiveSiFiveFreedom StudioRISCV
HiFive1 RevBFreedom U540SiFiveFreedom Studio
GAPuino GAP8GAP8RISC-V GAP
Sipeed Maix Bit with MicKendryte K2102500K210Kendryte K210MaixPy
Sipeed Maix GoKendryte K2105800K210Kendryte K210MaixPy
Sipeed MaixFace(MF1)Kendryte K2104800Kendryte K210
Sipeed Maix BitKendryte K2103520K210Kendryte K210MaixPy
Sipeed Maix M1 DockKendryte K2103300K210Kendryte K210MaixPy
Sipeed MaixduinoKendryte K2103960K210Kendryte K210MaixPy
Sipeed Longan NanoGD32VF103CBT6715GigaDevice GD32V
LamLoei AIoT DaaNKendryte K210K210
Kendryte KD233Kendryte KD233MaixPy
M5Stack M5StickVKendryte K2103080MaixPy
Sipeed Tang PriMER FPGA Dev.BoardAnlogic EG4S20 FPGA2580TD IDE
M5Stack UnitV AI CameraKendryte K2102145MaixPy
Wio Lite RISC-VGD32VF103CBT61000GigaDevice GD32V

ざっくり国内で入手しやすそうなボードと開発環境の一覧です。価格はスイッチサイエンスさん、秋月電子さん、Shigezoneさんの順で埋めています。

まとめ

結局よくわからん。

てなことで、RISC-V原典とInterfaceを購入して読んで見るところからやってみたいと思います。。。

ESP32のULPアセンブリ言語入門 その7 マシン語の構造と逆アセンブラ

概要

前回まででCマクロで定義されている命令をすべて説明しました。今回はマシン語の構造と、逆アセンブラの説明をしたいと思います。

命令の概要

ESP32のULPは、1命令が4バイト固定のRISCアーキテクチャです。一般的なパソコンに使われているIntelのCPUはCISCアーキテクチャで、単純な命令は1命令が1バイトと、命令によってバイト数が異なります。

そのため、CISCの場合にはレジスタに0を代入するのでも、 MOVE命令で0を代入すると2バイトになってしまうので、同じレジスタ同士XORをして0クリアすると1バイトになるなどのテクニックがありました。

ULPの場合には、各命令でサイズは共通ですのであまり気にする必要はありません。ただし、命令によって実行に必要なサイクル数が異なります。

MOVEで数値を代入するのはサイクル数は2ですが、LDで外部メモリからデータを取得するサイクル数は4と、より多くの時間が必要になります。とはいえ、ULPの場合にはそれほどサイクル数を気にする必要はないと思います。

マシン語の構造

ULPの命令は4バイトですので32ビット固定です。先頭4ビットをオペレーションコード(OpCode)とよび、大まかな命令の種類を定義しています。

OpCodeコード値(10進数)コード値(2進数)
REG_WR10001
REG_RD20010
I2C_RD30011
I2C_WR30011
WAIT40100
ADC50101
ST60110
演算系(ALU)70111
JUMP系81000
WAKE/SLEEP91001
HALT111011
LD131101

おそらく0がNOPになると思いますが、リファレンスでは定義されていませんでした。STとLDの番号が離れているのがちょっと気になりますが、OpCode自体の数値にあまり意味はないと思います。

4桁ですので16種類のコードが定義できますが、実装順で割り振っている気もします。ここで気をつけないといけないのがデータを表すOpCodeが定義されていません。この実装は失敗で、データにもOpCodeを割り振った方が良かったと思います。

それでは実際の命令の構造を何個か見ていきたいと思います。

演算系(ALU)

上記のテクニカルリファレンスにULPの命令が記載されています。OpCode7が一番複雑なので、こちらを解説したいと思います。

まず一番最初の図が32ビットの命令に対して、どこのどの役割を割り当てているのかを書いてあります。

上の画像です。左側の上位から見ていくと、28ビット目から31ビット目までは4’d7と書いてあります。これは4ビットで10進数(d)の7が入ることを表します。一番左の4ビットがOpCodeになります。

次が25ビットから27ビット目に3’b0をあるので、3ビットで2進数の0が入ることを表します。ここの項目は次のページを見ないとわからないのですが、0の場合には引数がレジスタになります。I_ADDRなどのCマクロですね。1の場合には数値を引数にするIがつくI_ADDIなどのCマクロに相当します。2の場合にはCマクロで定義されていないステージカウンタの命令になります。

3番目が21ビットから24ビットでALU_selとあります。

ALU_selでどの演算になるのかを設定しています。

6ビットから20ビットまでは何もかかれていませんので、未使用です。

最後に2ビットずつ3つのレジスタを指定する場所があります。R0は0、R1は1、R2は2、R3は3の数値が対応しています。

こちらが、一番左のOpCodeが7で、2つ目のSubOpCodeが1で、引数に数値をとる命令のリファレンスになります。

Rsrc2の変わりに、Immで16ビットの数値を指定するように変わっています。

ST

ちょっと特徴的なのがSTです。

命令の構造的には普通なのですが、中央に以下の表記があります。

Mem [ Rdst + Offset ]{31:0} = {PC[10:0], 5’b0, Rsrc[15:0]}

STで代入する数値は16ビット分です。上位16ビットにはプログラムカウンタ(PC)を5ビットシフトした数値が入っていると記載されています。

データ領域は32ビットですが、実際に使えるのは下位16ビットのみとなります。また、PCとはSTが実行されたときのアドレスになります。

つまり、データをみることで、このアドレスのSTから書き込まれたのかがわかるようになっています。この機能自体は便利なのですが、OpCodeのエリアにも書き込んでしまうため、データと命令の区別がつきません。後ろにある5ビット分の00000を先頭に書き込んでくれればOpCodeの0はデータかNOPのどちらかと区別がつくのですが。。。

ちなみに5ビット分の0は、実機で動かすと00000ではなく、00011と仕様書と違った数値が代入されています。

逆アセンブラの仕組み

逆アセンブラとは、マシン語に変換されている命令を、アセンブリ言語に戻す処理のことです。アセンブリ言語からマシン語に変換するアセンブラとは逆の処理ですので、逆アセンブラと呼ばれます。

自作ライブラリのESP32 ULP Debuggerでは、32ビットのメモリ上の命令を読み込み、OpCodeなどの情報から、どんな命令かを解析してシリアルに出力する処理を行っています。

上記が解析したときに利用したデータです。この他にCPU温度を取得するTSENSがCマクロではOpCode10であるのですが、未実装と書いてあり、テクニカルリファレンスにも記述していませんので、除いてあります。

Cマクロの仕組み

Cマクロでは、ulp_insn_tという共用体(union)を利用しています。共用体よりはunionの方がよく使う呼び方な気がします。

unionは、変数を複数の使い方でアクセスすることができます。

typedef union {
    struct {
        uint32_t cycles : 16;       /*!< Number of cycles to sleep */
        uint32_t unused : 12;       /*!< Unused */
        uint32_t opcode : 4;        /*!< Opcode (OPCODE_DELAY) */
    } delay;                        /*!< Format of DELAY instruction */

    struct {
        uint32_t unused : 28;       /*!< Unused */
        uint32_t opcode : 4;        /*!< Opcode (OPCODE_HALT) */
    } halt;                         /*!< Format of HALT instruction */
} ulp_insn_t;

かなりの命令を省略していますが、上記のような使い方ができます。

    struct {
        uint32_t unused : 28;       /*!< Unused */
        uint32_t opcode : 4;        /*!< Opcode (OPCODE_HALT) */
    } halt;                         /*!< Format of HALT instruction */

HALTの命令はOpCodeが4ビットで、残り28ビットは利用していません。unionの場合は下位から何ビット使うかを指定するので、下位から28ビットは未使用で、上位4ビットをopcodeに使うと定義されています。

    struct {
        uint32_t cycles : 16;       /*!< Number of cycles to sleep */
        uint32_t unused : 12;       /*!< Unused */
        uint32_t opcode : 4;        /*!< Opcode (OPCODE_DELAY) */
    } delay;                        /*!< Format of DELAY instruction */

上記はスリープをするDELAY命令です。下位16ビットでスリープするサイクル数を指定して、上位4ビットがDELAYのOpCodeが入ります。

#define I_DELAY(cycles_) { .delay = {\
    .cycles = cycles_, \
    .unused = 0, \
    .opcode = OPCODE_DELAY } }

実際のCマクロが上記になります。OPCODE_DELAYはDELAYのOpCodeである4を代入し、cyclesに16ビットのサイクル数を代入しています。

#define M_LABEL(label_num) { .macro = { \
    .label = label_num, \
    .unused = 0, \
    .sub_opcode = SUB_OPCODE_MACRO_LABEL, \
    .opcode = OPCODE_MACRO } }

ちょっと特徴的なのがM_LABEL()のマクロです。OPCODE_MACROは15なのですが、実際のULPで15は未定義の命令です。

ジャンプ先のラベルとして、未定義の命令をとりあえず入れておき、ulp_process_macros_and_load()関数で転送するときに、実際のアドレスが決まるので置換しています。

この仕組があるのでラベルなども使えるよになっているのですが、転送時に変換している関係で、一度に転送できる命令がArduino IDEでは128命令に制限されています。

この数値は本来設定ファイルで変更できるのですが、ライブラリ内部でコンパイル済みの値が保存されているため変更できませんので注意してください。

ネクストステップ

ここまでで、一般的なULP入門の内容は完了です。記事自体はCマクロで定義されていないI2Cなどを自分で定義して動かすなどの内容を続けたいと思いますが、これまでに書いてあったことをなんとなく理解できていれば、他のアセンブリ言語を理解する足場になると思います。

一夜漬けCASL

私はこの本でアセンブラ言語の基礎を学びました。かなり古い本なので図書館などで借りて見るのがいいと思います。ざっくりとアセンブリ言語ってどんなものかを理解するのに適しています。

仕様的にはULPよりちょっとだけ複雑ですが、非常に単純な処理系です。ただし、この記事を読んでいるのであればCASLを勉強する必要はあまりないと思います。

LASM体験版(8086)

Windowsを使っている人には、上記をおすすめします。100行までしかアセンブリできませんが、Intel系CPUの8086アセンブリが無料で使うことができます。

ヘルプファイルなどが提供されていますので、できれば全部のページを見てほしいと思います。

参考図書としては、上記あたりでしょうか?

みんな絶版ですね。こちらも図書館で探すのがよいと思います。一番重要なのはアセンブリ言語自体を理解するよりは、アセンブラやリンカの使い方を含めて学ぶことです。

LSI C試食版

こちらもWindows版ですが、アセンブリではなくC言語の開発環境です。こちらのアプリもマニュアルが充実しており、中身を読むとかなり勉強になります。

ゴールとしてはLASMで作った関数を、LSI Cのmain関数から呼び出してみるところになります。アセンブラで行う処理自体は単純で構いません。引数をインクリメントして返すぐらいの処理でも構わないので、makefileからオブジェクトファイルを作成して、リンクして実行ファイルにするところまでできると、メモリマップとかも意識できます。

makeを含めた低レベルなコマンドラインの仕組みを勉強するのは非常に難しいのと、いま勉強している人は少ないのでおすすめです。

RISC-V

リスクファイブと読むそうです。オープンソースのCPUで、ESP32-S2のULPにも採用されています。

非常に伸びてきており、アセンブリ言語で実際に使う確率が高いのが、このRISC-Vになると思います。

ただ、敷居がものすごい高いので、予備知識なしでは歯が立たないと思います。

その他

大きめの書店などで中身を見てみるのがよいと思いますが、適当に目についた書籍をあげておきます。

RISC-Vの前に、なにか1つか2つ他のアーキテクチャを勉強してから勉強することをおすすめします。定番であればZ80などですが、実際に動かしたい場合にはPICやIntel系(8086, 486など)が動かしやすいと思います。

ESP32のメインコアにも、Arduino UNOのAVRなどにもアセンブリでプログラミングすることはできますが、絶対的に資料が少ないのでおすすめしません。

勉強であれば、一般的で資料が多いアーキテクチャでチャレンジするのがよいと思います。

まとめ

ULPは入門用としてはかなりシンプルでおすすめです。しかしながらより深く勉強するほどの価値はありませんので、概要までわかったら他の環境を勉強することをおすすめします。

ここまでで、他のCPUを理解するための基礎力はついているはずです。ぜひRISC-Vにチャレンジして、撃沈してみてください。

続編

Gigabits IoT Platform(ベータ版)にESP32でデータ送信してみた

概要

Gigabitsという新しいIoTプラットフォームができていたので、ベータ版を触ってみました。

ESP32の開発元であるEspressifと共同でリリースを出していました。

gitabits.ioの基本機能

ざっくりいうとMQTTでデータを投げるとグラフ化してくれるサービスです。kibanaっぽいグラフィックなので、きれいなダッシュボードができそうです。

ただし、まだまだ作り途中って感じですし、データのフィルタリングもそれほどできません。

gitabits.io社とは?

ここを見ると2019年に設立したばかりの会社で、スタートアップみたいですね。

gitabits.ioの参加方法

上記のページから参加申し込みをすると、翌日ぐらいのメールが届きます。その中に書いてあるキーを使ってアカウント登録をします。

上記で登録すると利用できるようになります。

使い方

メインページの下の方に、PDFで使い方が書いてありました。サンプルはセンサーモジュールを使っていますが、とりあえず外部センサーを使わないで動かしてみました。

プロジェクト作成

ログインした直後の画面からProjectを追加します。プラスボタンを押すと問答無用でNew Projectが増えます。

メニューからEdit Projectを選びます。

プロジェクト名を適当につけます。Zip Codeは入れなくても大丈夫です。ただし、アメリカの郵便番号なので、実質日本では利用できません。Saveで保存します。

デバイス追加

右側にあるAdd Devicesボタンを押します。

追加するデバイス一覧がでるので、ESP32を選びます。

画面下にAddedと表示されるだけで、この画面から変わりません。一つ前の画面に戻ってくれたほうがわかりやすいと思います。。。

左上のパンくずリストから、Editを押して一つ前の画面に戻るとデバイスが追加されています。Device KeyとSecretをこのあと使うので確認しましょう。本当はデバイス名とかを編集したほうがいいです。

保存するデータの設定もこのデバイスで行いますが、いまのところ設定しなくても保存はされるみたいです。

環境構築

  • Arduino IDE
  • Gigabitsライブラリ
  • ArduinoJson(Gigabitsライブラリを入れると依存として一緒に入る)

上記で動くと思います。標準ライブラリマネージャでGigabitsライブラリを入れれば、依存も含めて一緒に入るようになっています。

Wi-Fi設定

#include <WiFi.h>

void setup() {
  WiFi.begin("SSID", "PASSWORD");
}

void loop() {
}

ESP32は最後に接続したWi-Fi情報を覚えています。そのため、このブロクでは各スケッチでSSIDとパスワードを記述するのを非推奨としています。

上記のようなスケッチで一度接続を試しておくと、以後はWiFi.begin()のように引数を省略することで、Wi-Fi接続ができるようになります。

M5StickCの人は上記のような設定初期化スケッチを利用してもよいと思います。

最小スケッチ

/*
 *  This sketch demonstrates using the Gigabits device library 
 *  with an ESP32 using an unencrypted connection
 */

#include <WiFi.h>
#include <Gigabits.h>

WiFiClient net;
Gigabits gigabits;

unsigned long lastMillis = 0;

void setup() {
  Serial.begin(115200);
  
  WiFi.begin();

  // Add a listener to call when commands are received over Sensor Index 3
  gigabits.addCommandListener(3, [](int32_t *data, size_t sz) {
    for (size_t i = 0; i < sz; i++) {
      int32_t command = data[i];
      Serial.print("Received a: ");Serial.println(command);
      // Do something with the command
    }
  });
  Serial.println("Connecting..");
  
  // Start gigabits with the device key and a Client type object
  gigabits.begin("yourdevicekey", "yoursecret", net, "mqtt.gigabits.io", 1883);
}

void loop() {
  // Call this frequently
  gigabits.run();

  if (millis() - lastMillis > 10000) {
    lastMillis = millis();
    Serial.println("Sending data...");
    // Send data to Sensor Index 1
    gigabits.sendRecord(1, (uint32_t)10);
    // Send data to Sensor Index 2
    gigabits.sendRecord(2, (uint32_t)(millis() % 100));
  }
}

上記はGigabitsライブラリに入っていたESP32Exampleスケッチです。WiFi.begin()のところだけ引数をなくしてあります。

“yourdevicekey”, “yoursecret”のところをプロジェクトのデバイスで割り当てられた文字列に置換してあげてください。

このスケッチだとデータを2個転送しています。

    gigabits.sendRecord(1, (uint32_t)10);
    gigabits.sendRecord(2, (uint32_t)(millis() % 100));

一個目が固定値の10で、二個目が起動経過ミリ秒の100で割ったあまりですね。

デバイスにセンサー追加

アップするデータの個数が確定したので、センサーを追加します。このセンサーってのが実質データみたいですね。

この画面でAddを押すと、押した回数分センサーが追加されます。今回は2回押します。例によって画面は変わらないのでわかりにくいです。

パンくずリストから「My New Device」を選んで元の画面に戻ってください。

増えました。デバイスの名前も本当は変えたほうがわかりやすいですね。センサーの情報をEditを押して編集画面を開いてみましょう。

現在は整数型のGeneral Sensorしかないようです。グラフで表示するようの名前と色はここで変更可能です。

ダッシュボード編集

プロジェクトトップまでパンくずリストで戻ると、ものすごく寂しい画面が開くと思います。左からNew Chartを選んでグラフを追加します。

右下の角で大きさを変更できます。右上のメニューから編集ができます。

グラフの設定をします。変更するのは名前だけかな?

描画するセンサーのデータを選びます。Lineで既存のグラフかNew Lineを選択できます。追加なのでCreate New Lineを選び、デバイスとセンサーを選択します。

ここでデバイスやセンサーに名前をつけていないと上記のように非常にわかりにくいですが、データの並びで区別はできます。

さすがにわかりにくかったので、センサー1と2と名前をつけてきてから、Create New Lineを2度繰り返してみました。2個のグラフが描画されているのがわかるのでしょうか?

特徴的な機能として、外部データも一緒に描画することができます。この例だと設定した郵便番号の場所の気温を描画する設定になります。

数分待つと追加されます。華氏53.01度だから摂氏12度ぐらい?

予想としては過去データも表示してくれるのかと思ったら、画面上のリアルタイム取得で描画していて、どこにも保存していないっぽいですね。ちょっと予想と違う動きだったので、グラフ描画できていないと思ってしまいました。

リアリタイムデータ確認

100ミリ秒間隔で送信してみました。グラフの横1マスが1秒なので、10ポイント近くデータはありそうです。

ただこのデータエクスポートできないんですよね。。。

長時間データ

昨晩から接続していたプロジェクトのグラフです。M5StickCのCPU温度とバッテリー温度を保存しています。途中でバッテリー充電をやめたので下がっていって、データがなくなっています。その後充電したので復活していますが、あいだのデータはなんとなくのグラフで補間されていますね。

Live Dataのチェックを外すと、範囲指定を自由に変更できるのですが、現状は3時間ぐらいのデータしか保存していないみたいです。

Liveと同じ範囲しか見えません。。。

まとめ

これから面白くなりそうなプラットフォームですが、現状はまだまだ荒削りです。標準的な機能と料金体系や、保存されるデータ期間や最小間隔が気になります。

M5StickCでUIFlow入門 その2 Desktop版最初の一歩

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

概要

前回は環境構築まで完了させました。今回は実際にUIFlowを動かしながら、基本的な動かし方を説明したいと思います。

ブラウザで動かすCloud版と、アプリで動かすDesktop版で説明が違うので同じ内容のものを別ページで作成しています。

こちらの記事はアプリで動かすDesktop版になります。

ブラウザで動かすCloud版の場合には上記ページに同じ内容のものがあります。

動作環境

環境構築と同じく、WindowsかLinux、Macで動かすことができます。

M5StickCの説明

M5StickCにはボタンが3つあります。左側にあるのが電源ボタンで一秒以上押すと電源が入ります。真ん中のM5と書いてあるところがAボタンで、右側面にあるのがBボタンです。

現在UIFlowからは使えませんが、Aボタンの隙間にマイクがあります。また、画面の左上に赤色のLEDと、赤外線送信があります。

M5StickCの起動

電源ボタンを1秒以上押して、M5StickCを起動させます。

上記のようなUSBケーブルが表示されていることを確認してください。この画面以外ではプログラミングができません。

もしUSBケーブル以外が表示されていない場合には、M5と書かれたBボタンを押しながら、電源ボタンを押してください。

上記のProgramと書かれている設定画面で起動するはずです。

右側にあるBボタンを押すことで、メニューがアプリ選択に変わります。もう一度Bボタンを押して次の画面に変更します。

セットアップ画面にきたら、Aボタンを押して選択してください。

設定画面になったら、Bボタンを一度押してUSBモードに選択されていることを確認してから、Aボタンで選択します。

以上の操作をすると、上の画像のようにUSBケーブルの画面が表示されるはずです。

この画面になったら、M5StickCにUSBケーブルが接続されている場合には一度抜いてください。

UIFlow Desktopを起動する

私の環境の場合、上記画面が毎回表示されますが、Skipを押してください。

上記は起動直後の画面です。言語が日本語でなかったら日本語を選択、DeviceがStick-CでなかったらStick-Cを選択してください。

この状態でCOMのリストを確認してください。ここの一覧はパソコンによって表示が違うので注意してください。

私のパソコンはCOM1しか選択できませんでした。COMの中身を確認したら、M5StickCをパソコンに接続してみてください。

接続して増えたCOMがあれば、それがM5StickCになります。私のパソコンではCOM4が増えたので、COM4を選択して、OKボタンを押します。

接続確認

アプリの左下にある表示が、接続済みになっていることを確認してください。Disconnectedと表示されている場合には、USBケーブルが接続されていることの確認と、M5StickCの電源を入れ直して、USBケーブルの画面が表示されていることを確認してから、回転マークを押してみてください。

上記のように接続済みとなれば、プログラミングの準備が完了しました。

画面の説明

画面は横に3つの場所わけがされています。左側のM5StickCの画像がでている場所が画面表示のデザインをする場所。真ん中のイベントなどが並んでいる場所がブロックの選択場所。右側がブロックを並べてプログラミングする場所になります。

左側のデザインが必要ない場合には、左下にある隠すボタンを押すとデザイン部分が隠れるので、より広い画面で利用できます。

隠した場合には上記のように右側が広がります。使いやすい画面でプログラミングをしてみてください。

あとで説明しますが、右上にある再生ボタンを押すとプログラムがM5StickCに転送されて実行されます。

一番右上のボタンを押すと、プログラムの保存や読み込みができます。

プログラミングのはじめかた

画面の向き決定

最初に決めるのは画面の向きです。

どの画面方向で行っても構いませんが、縦画面の場合には標準の0。横画面の場合には3を選ぶことが多いです。しかしながら、ボタンの押しやすさで選んでもらって構いません。

今回は標準的な縦画面の0で使いたいと思います。

画面デザイン

次に画面デザインを決めます。

背景色

背景色は左側のデザイン部分の画面をクリックすると、画面中央にScreenという設定項目がでてきて、backgroundColorをクリックすると色が選択できます。

今回は赤くしてみました。この状態で右上にある再生ボタンを押してください。

M5StackCの画面が赤くなったと思います。

タイトル

タイトル表示は必須ではありませんが、アプリの名前を画面上に出しておくとわかりやすいです。

表示する場合には左上のTitleという場所をクリックしたままドラッグして、M5StickCの画面にドロップします。

成功すると、画面上にTitleという帯が表示されます。

Titleの帯をクリックすると、設定画面が開きますのでtextのところに表示したいアプリ名を入力します。画面上では日本語も入力されますが、実際には英数文字しか表示されないので注意してください。

ラベル

画面上に文字を書きたい場合にはLabelを利用します。Titleと同じように左上のLabelからドラッグして、表示したい場所にドロップしてください。位置はあとでも修正できるので場所はアバウトでもかまいません。

Titleと同じようにTextもクリックすると設定画面がでてきます。Titleより設定できる項目が多いです。

fontをラベルのフォントを設定できます。

ちょっとはみ出ていますが、上記がフォントの一覧です。DejaVuSansは18から72まで大きさの異なるフォントが入っています。こちらも日本語は実際には表示されないので注意してください。

rotationは文字の回転角度になります。基本的には0以外はあまり使わないと思います。

layerは画面の重なり順で、数字が大きいほど重なった場合に手前に来ます。

不要なオブジェクトを消す

画面上に配置してけれど、要らなくなったオブジェクトは画面右上のゴミ箱に持っていくことで消すことができます。

ブロックプログラミング

画面デザインは上のように、タイトルとラベルを配置したものとします。

バッテリー電圧をラベルに表示する

UIFlowの場合、M5StickCのバッテリー残量を確認することができません。そのため、表示する場所があるのであれば常に表示しておくことが好ましいです。

まずはラベルを変更するためのブロックを配置します。UIの中のラベルを選択すると、ブロックの一覧が表示されます。一番上のブロックがラベルに任意の文字を表示するブロックになります。

一番上のブロックをクリックしたままドラッグして、右側のブロックプログラミングエリアのSetupの下でドロップします。

上記のようにブロックがくっつけば成功です。

次のバッテリーの電圧を取得します。ハードウエアの中のPowerを選ぶと、ブロックの一覧が表示され、「Get battery voltage」ブロックを選びます。

上記のように、ラベルの右側にある隙間にブロックを入れると完成です。

実行

右上にある再生ボタンを押して、実行してみてください。

画像のように、表示されれば問題ありません。表示されている数値はバッテリーの電圧で、3.9V弱です。目安として3V以下になるとバッテリー切れで電源が落ちます。4V以上で概ね満充電になります。

充電ケーブルを外した状態で少し動かしてからまたケーブルを接続し、再度再生ボタンを押してください。さっきの数値とは違う電圧に変化しているはずです。

保存する

UIFlowはブラウザを閉じてしまうと、プログラムの内容がなくなってしまうので定期的に保存することをおすすめします。

右上のメニューを開いて、保存ボタンを押しましょう。保存ボタンを押すと、ファイルの保存画面が開きます。この保存したファイルを読み込むことで、元の状態に戻すことができます。

保存ボタンではなく、間違えてダウンロードボタンを押すとM5StickCの中にアプリが保存されます。一見同じようにみえますが、一度ダウンロードボタンを押してしまうとプログラムモードではなく、アプリモードにM5StickCが変わってしまいます。

ダウンロードしたアプリしか動かなくなりますので、Aボタンを押しながら電源ボタンを押して、プログラムモードに戻す必要がありますので注意してください。

アプリモードになっていると、再生ボタンを押してもプログラムが転送されません。

定期更新をするように変更

さっきのブロックは起動時に一度しかラベルを更新していませんでした。電圧が更新されるように改造してみたいと思います。

定期的にラベルの電圧を更新するために、定期的に処理をするループブロックを設置したいと思います。

今動いているブロックが邪魔なので、一度右下に移動して外します。

どこにもくっついていないブロックは動かないので、一時的においておくのはいいですが邪魔なので使わないブロックは右下にあるゴミ箱か、Deleteボタンを押して削除してください。

イベントの一番上にある、ずっとブロックを選んで設置します。

Setupにくっつけたあとに、ラベルを表示するブロックをその間に入れます。

これで、定期的にラベルの電圧が更新されます。再生ボタンを押してプログラムを転送してみてください。

転送できない場合には、まちがってダウンロードをしてしまった可能性があるので、Aボタンを押しながら電源ボタンをいれて、Programの画面でAボタンで選択してください。

うまく動くと、電圧が激しく動いているのがわかると思います。USBの充電ケーブルを外している状態の方が動きがわかりやすいと思います。

このままだと動きが多すぎてうるさいので、更新頻度をさせてみたいと思います。

1秒間隔で更新する

タイマーの中に「秒停止」というブロックがあるので設置します。

いまある「ラベルに表示」ブロックの下に「秒停止」ブロックを持っていくと下に追加することができます。

この画面のように色が付けばブロックが正しく設置されています。この状態でもう一度再生ボタンを押して実行してみてください。1秒間隔でバッテリー電圧が更新されているでしょうか?

バッテリー電圧は3Vでバッテリー切れになるので、下がっていたら充電するように気をつけてください。

完成図

本日作ったプログラムの完成図です。

ダウンロード(uiflow_l02.m5f)

上記から今回のファイルがダウンロードできます。

まとめ

最低限最初に知っておいた方がいい項目を説明しました。特にダウンロードをしてしまった場合にアプリモードになってしまうと、知らないと元のプログラムモードに戻せないので注意してください。

あと、赤い背景は非常に目に悪いので他の色がいいと思いました。

次回はもうちょっと動きにあることを解説したいと思います。

続編

M5StickCでUIFlow入門 その2 Cloud版最初の一歩

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

概要

前回は環境構築まで完了させました。今回は実際にUIFlowを動かしながら、基本的な動かし方を説明したいと思います。

ブラウザで動かすCloud版と、アプリで動かすDesktop版で説明が違うので同じ内容のものを別ページで作成しています。

こちらの記事はブラウザで動かすCloud版になります。

アプリで動かすDesktop版を利用する場合には上記ページに同じ内容のものがあります。

動作環境

上記のURLをブラウザで開くことで、プログラミングができます。基本的にはブラウザが動く最近の環境であれば利用できるのですが、パソコン以外から使った場合には画面操作感が悪いので、なるべくパソコンから利用してください。

URLがhttpsではなく、httpなのでブラウザ上では警告がでます。

通信が暗号化されていない警告なので、現状は無視しても問題がありません。httpsにしてほしいと公式にはお願いしましたので、そのうちhttpsでの接続ができるようになるかもしれません。

手元環境で調べたところ、iPhoneやAndroidなどのブラウザから動かすことも可能ですが、非常に操作感が悪いので実用的には利用できないと思います。

上記はAmazon Fire HD 8で試したところです。一部の機能は利用できなかったりすので、おすすめしません。

ChromebookなどのPCと同じブラウザが使えるものであれば、動きそうですが未検証です。

M5StickCの説明

M5StickCにはボタンが3つあります。左側にあるのが電源ボタンで一秒以上押すと電源が入ります。真ん中のM5と書いてあるところがAボタンで、右側面にあるのがBボタンです。

現在UIFlowからは使えませんが、Aボタンの隙間にマイクがあります。また、画面の左上に赤色のLEDと、赤外線送信があります。

M5StickCの起動

電源ボタンを1秒以上押して、M5StickCを起動させます。

上記のような青い丸が表示されていることを確認してください。この画面以外ではプログラミングができません。写真である下についているものは、USBケーブルをマグネットで接続するためのアダプターですので、気にしないでください。

もし緑の丸が表示されていない場合には、M5と書かれたBボタンを押しながら、電源ボタンを押してください。

上記のProgramと書かれている設定画面で起動するはずです。M5StickCの内部に保存したアプリを実行した場合に再起動すると、同じアプリが起動してしまうので、上記の設定画面に入り、Programの画面でAボタンで決定すると丸が表示されている画面が表示されると思います。

ブラウザでUIFlowページを開く

上記は起動直後の画面です。

API KeyにM5StickCの画面に表示されているキーを入力します。この時M5StickCの下にあるマークが丸で、緑になっていることを再度確認してください。赤色だったりエラーがでているとインターネットに接続できない状況です。

無線環境を見直すか、その1の環境構築の項目を再確認してください。

上の画像が入力した画面です。Api Keyの他に言語を日本語にして、DeviceをStick-Cを選択してからOKボタンを押してください。

接続確認

ブラウザの左下にある表示が、接続済みになっていることを確認してください。Disconnectedと表示されている場合にはM5StickCの電源を入れ直して、緑の丸が表示されていることを確認してから、回転マークを押してみてください。

上記のように接続済みとなれば、プログラミングの準備が完了しました。

画面の説明

画面は横に3つの場所わけがされています。左側のM5StickCの画像がでている場所が画面表示のデザインをする場所。真ん中のイベントなどが並んでいる場所がブロックの選択場所。右側がブロックを並べてプログラミングする場所になります。

左側のデザインが必要ない場合には、左下にある隠すボタンを押すとデザイン部分が隠れるので、より広い画面で利用できます。

隠した場合には上記のように右側が広がります。使いやすい画面でプログラミングをしてみてください。

あとで説明しますが、右上にある再生ボタンを押すとプログラムがM5StickCに転送されて実行されます。

一番右上のボタンを押すと、プログラムの保存や読み込みができます。

プログラミングのはじめかた

画面の向き決定

最初に決めるのは画面の向きです。

どの画面方向で行っても構いませんが、縦画面の場合には標準の0。横画面の場合には3を選ぶことが多いです。しかしながら、ボタンの押しやすさで選んでもらって構いません。

今回は標準的な縦画面の0で使いたいと思います。

画面デザイン

次に画面デザインを決めます。

背景色

背景色は左側のデザイン部分の画面をクリックすると、画面中央にScreenという設定項目がでてきて、backgroundColorをクリックすると色が選択できます。

今回は赤くしてみました。この状態で右上にある再生ボタンを押してください。

M5StackCの画面が赤くなったと思います。

タイトル

タイトル表示は必須ではありませんが、アプリの名前を画面上に出しておくとわかりやすいです。

表示する場合には左上のTitleという場所をクリックしたままドラッグして、M5StickCの画面にドロップします。

成功すると、画面上にTitleという帯が表示されます。

Titleの帯をクリックすると、設定画面が開きますのでtextのところに表示したいアプリ名を入力します。画面上では日本語も入力されますが、実際には英数文字しか表示されないので注意してください。

ラベル

画面上に文字を書きたい場合にはLabelを利用します。Titleと同じように左上のLabelからドラッグして、表示したい場所にドロップしてください。位置はあとでも修正できるので場所はアバウトでもかまいません。

Titleと同じようにTextもクリックすると設定画面がでてきます。Titleより設定できる項目が多いです。

fontをラベルのフォントを設定できます。

ちょっとはみ出ていますが、上記がフォントの一覧です。DejaVuSansは18から72まで大きさの異なるフォントが入っています。こちらも日本語は実際には表示されないので注意してください。

rotationは文字の回転角度になります。基本的には0以外はあまり使わないと思います。

layerは画面の重なり順で、数字が大きいほど重なった場合に手前に来ます。

不要なオブジェクトを消す

画面上に配置してけれど、要らなくなったオブジェクトは画面右上のゴミ箱に持っていくことで消すことができます。

ブロックプログラミング

画面デザインは上のように、タイトルとラベルを配置したものとします。

バッテリー電圧をラベルに表示する

UIFlowの場合、M5StickCのバッテリー残量を確認することができません。そのため、表示する場所があるのであれば常に表示しておくことが好ましいです。

まずはラベルを変更するためのブロックを配置します。UIの中のラベルを選択すると、ブロックの一覧が表示されます。一番上のブロックがラベルに任意の文字を表示するブロックになります。

一番上のブロックをクリックしたままドラッグして、右側のブロックプログラミングエリアのSetupの下でドロップします。

上記のようにブロックがくっつけば成功です。

次のバッテリーの電圧を取得します。ハードウエアの中のPowerを選ぶと、ブロックの一覧が表示され、「Get battery voltage」ブロックを選びます。

上記のように、ラベルの右側にある隙間にブロックを入れると完成です。

実行

右上にある再生ボタンを押して、実行してみてください。

画像のように、表示されれば問題ありません。表示されている数値はバッテリーの電圧で、3.9V弱です。目安として3V以下になるとバッテリー切れで電源が落ちます。4V以上で概ね満充電になります。

充電ケーブルを外した状態で、再度再生ボタンを押してください。さっきの数値とは違う電圧に変化しているはずです。

保存する

UIFlowはブラウザを閉じてしまうと、プログラムの内容がなくなってしまうので定期的に保存することをおすすめします。

右上のメニューを開いて、保存ボタンを押しましょう。保存ボタンを押すと、ファイルのダウンロードがはじまります。このダウンロードしたファイルを読み込むことで、元の状態に戻すことができます。

保存ボタンではなく、間違えてダウンロードボタンを押すとM5StickCの中にアプリが保存されます。一見同じようにみえますが、一度ダウンロードボタンを押してしまうとプログラムモードではなく、アプリモードにM5StickCが変わってしまいます。

ダウンロードしたアプリしか動かなくなりますので、Aボタンを押しながら電源ボタンを押して、プログラムモードに戻す必要がありますので注意してください。

アプリモードになっていると、再生ボタンを押してもプログラムが転送されません。

定期更新をするように変更

さっきのブロックは起動時に一度しかラベルを更新していませんでした。電圧が更新されるように改造してみたいと思います。

定期的にラベルの電圧を更新するために、定期的に処理をするループブロックを設置したいと思います。

今動いているブロックが邪魔なので、一度右下に移動して外します。

どこにもくっついていないブロックは動かないので、一時的においておくのはいいですが邪魔なので使わないブロックは右下にあるゴミ箱か、Deleteボタンを押して削除してください。

イベントの一番上にある、ずっとブロックを選んで設置します。

Setupにくっつけたあとに、ラベルを表示するブロックをその間に入れます。

これで、定期的にラベルの電圧が更新されます。再生ボタンを押してプログラムを転送してみてください。

転送できない場合には、まちがってダウンロードをしてしまった可能性があるので、Aボタンを押しながら電源ボタンをいれて、Programの画面でAボタンで選択してください。

うまく動くと、電圧が激しく動いているのがわかると思います。USBの充電ケーブルを外している状態の方が動きがわかりやすいと思います。

このままだと動きが多すぎてうるさいので、更新頻度をさせてみたいと思います。

1秒間隔で更新する

タイマーの中に「秒停止」というブロックがあるので設置します。

いまある「ラベルに表示」ブロックの下に「秒停止」ブロックを持っていくと下に追加することができます。

この画面のように色が付けばブロックが正しく設置されています。この状態でもう一度再生ボタンを押して実行してみてください。1秒間隔でバッテリー電圧が更新されているでしょうか?

バッテリー電圧は3Vでバッテリー切れになるので、下がっていたら充電するように気をつけてください。

完成図

本日作ったプログラムの完成図です。

ダウンロード(uiflow_l02.m5f)

上記から今回のファイルがダウンロードできます。

まとめ

最低限最初に知っておいた方がいい項目を説明しました。特にダウンロードをしてしまった場合にアプリモードになってしまうと、知らないと元のプログラムモードに戻せないので注意してください。

あと、赤い背景は非常に目に悪いので他の色がいいと思いました。

次回はもうちょっと動きにあることを解説したいと思います。

続編