第3章
CoreS3のセンサーをブロックで使う
UIFlow 2.0のブロックでCoreS3のセンサーを動かし、対応するMicroPythonコードを読んで仕組みを学びます。
この章の進め方
まずUIFlow 2.0のブロックを自分で組み、実機で確かめます。動いた後でPython表示へ切り替え、どの命令に変換されたかを調べましょう。完成コードの入力から始める必要はありません。
各実習は「ブロックを組む → 動作確認 → 対応するMicroPython」の順です。実習ごとにプロジェクトを別名で保存し、一度に一つのセンサーを動かします。コード例は同じ処理を読みやすく書き直したものです。UIFlowが自動生成する setup()・loop() や変数宣言とは見た目が異なりますが、初期設定と繰り返し処理を対応させて読めます。
準備と使うセンサー
対象は**CoreS3(K128)**です。環境構築を済ませ、UIFlow 2.0でCoreS3を選び、USBまたはネットワーク経由で実行できる状態にします。追加配線や外付けユニットは使いません。使用したファームウェアのバージョンをメモしてください。
| 実習 | 入力 | 確かめること |
|---|---|---|
| 1 | タッチパネル | 触れた位置と、指を離した状態 |
| 2 | 環境光センサー | 手で覆ったときの明るさの変化 |
| 3 | 加速度センサー | 傾けたときの3軸の変化 |
| 4 | ジャイロセンサー | 回している間の変化 |
| 5 | 地磁気センサー | 本体の向きを変えたときの変化 |
| 6 | マイク | 音を録音して聞き返す |
| 7 | カメラ | 撮影した画像を画面に表示する |
CoreS3はBMI270(加速度・ジャイロ)、BMM150(地磁気)、LTR-553(環境光・近接)、マイク、カメラを搭載しています。CoreS3-LiteやCoreS3 SEなどは別モデルなので、機種名だけでなく搭載部品も確認してください。特にカメラ実習はカメラ搭載のCoreS3が必要です。CoreS3の公式仕様
近接機能は明るさとは別です。この章では標準のALSブロックで明るさを扱います。近接の直接読み取りは、次のセンサーアプリ教材の発展内容として扱います。温度・湿度などを測る場合は、別途対応する外付けUnitが必要です。
0. 数値を表示する準備
ブロックを組む
- 新規プロジェクトを作り、画面の背景を黒にします。
- UIの部品からLabelを3個配置し、名前を
label0、label1、label2にします。 - 位置をそれぞれ
(8, 35)、(8, 85)、(8, 135)、文字色を白、背景色を黒、フォントをDejaVu18に設定します。 - Setup側で
label0の文字をReadyにします。Loop側には「待つ 100 ms」を置きます。秒単位のブロックなら0.1秒です。 - Run Onceで実行し、画面に
Readyが出ることを確認します。sensor-baseという名前で保存します。
以降の「ブロックの並び」は配置を説明する文章で、貼り付けて実行するコードではありません。UIFlowの表示言語・バージョンによってブロック名が変わるため、英語の機能名と対応APIも併記します。標準のSetup/Loopを使う場合、初期化と入力更新は自動生成されます。Python表示で M5.begin() と、繰り返し内の M5.update() を確認してください。
| ブロックを探す場所・機能 | Pythonでの対応 |
|---|---|
| HardwareのTouch、ALS、IMU、Mic | M5.Touch、M5.Als、M5.Imu、M5.Mic |
| UIのLabel → 文字を設定 | label0.setText(...) |
| Variables → 変数を設定 | value = ... |
| Text → 文字列へ変換/つなぐ | str(value)/"値: " + str(value) |
| Logic → 比較、もし/そうでなければ | >、if/else |
| 待つ(ミリ秒) | time.sleep_ms(...) |
ブロックが見つからないときは、各実習の公式資料にあるブロック画像とUIFlow2 Exampleを開いて確認します。別機種や外付けUnitの似た名前のブロックで置き換えないでください。
対応するMicroPython
以下の例を読むときは、M5.begin() からラベル作成までが初期設定、while True: 以下がLoopに対応します。以降のコード例はそれぞれ単独で実行できます。Python側を編集する前にブロック版を保存してください。
import M5
from M5 import Widgets
import time
M5.begin()
Widgets.fillScreen(0x000000)
label0 = Widgets.Label("", 8, 35, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label1 = Widgets.Label("", 8, 85, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label2 = Widgets.Label("", 8, 135, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label0.setText("Ready")
while True:
M5.update()
time.sleep_ms(100)
1. タッチ:触れた位置を表示する
ブロックを組む
sensor-base を別名 sensor-touch で保存します。HardwareのTouchから「タッチ数」「X座標」「Y座標」を使います。まずタッチ数だけを label0 に表示して実行し、指を触れたり離したりしてみましょう。その後、条件分岐と座標表示を足します。
Loop
タッチ数を変数 count に入れる
label0 の文字を「Count: 」+ countの文字列 にする
もし count > 0 なら
label1 の文字を「X: 」+ タッチX座標の文字列 にする
label2 の文字を「Y: 」+ タッチY座標の文字列 にする
そうでなければ
label1 の文字を「No touch」にする
label2 の文字を空にする
100 ms待つ
動作確認
画面の左上・右下・中央を順に触り、座標を比べます。指を離すと No touch に戻れば成功です。触れていないときに古い座標を使わないよう、最初にタッチ数を確認します。TouchのブロックとAPI
対応するMicroPython
import M5
from M5 import Widgets
import time
M5.begin()
Widgets.fillScreen(0x000000)
label0 = Widgets.Label("", 8, 35, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label1 = Widgets.Label("", 8, 85, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label2 = Widgets.Label("", 8, 135, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
while True:
M5.update()
count = M5.Touch.getCount()
label0.setText("Count: " + str(count))
if count > 0:
label1.setText("X: " + str(M5.Touch.getX()))
label2.setText("Y: " + str(M5.Touch.getY()))
else:
label1.setText("No touch")
label2.setText("")
time.sleep_ms(100)
2. 明るさ:手で覆った変化を調べる
ブロックを組む
基本プロジェクトを別名 sensor-light で保存します。ALSの「環境光の値を取得」(getLightSensorData)を使います。
Loop
ALSの値を変数 light に入れる
label0 の文字を「Light: 」+ lightの文字列 にする
200 ms待つ
まずこの3つを動かしてから、次の判定を「待つ」の前に追加します。
もし light < threshold なら
label1 の文字を「DARK」にする
そうでなければ
label1 の文字を「BRIGHT」にする
Variablesで threshold を作り、Setupで数値を設定します。値は動作確認で決めます。
動作確認
通常の明るさとセンサー部分を手で覆った状態を、それぞれ数秒観察して記録します。暗いときの値が小さいことを確認し、その間の値を threshold にします。例えば暗いとき40、明るいとき260なら150です。150は説明用で、全員共通の正解ではありません。
画面の明るさ設定を変える実習ではなく、周囲の光を測る実習です。ここでは取得値を比較し、照度計の校正済みluxとしては扱いません。ALSのブロックとAPI
対応するMicroPython
import M5
from M5 import Widgets
import time
M5.begin()
Widgets.fillScreen(0x000000)
label0 = Widgets.Label("", 8, 35, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label1 = Widgets.Label("", 8, 85, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label2 = Widgets.Label("", 8, 135, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
threshold = 150 # 自分で観察した値に変更
while True:
M5.update()
light = M5.Als.getLightSensorData()
label0.setText("Light: " + str(light))
if light < threshold:
label1.setText("DARK")
else:
label1.setText("BRIGHT")
time.sleep_ms(200)
3. 加速度:傾きを数値で見る
ブロックを組む
基本プロジェクトを別名 sensor-accel で保存します。IMUの「加速度の値を取得」を使い、軸の選択欄をXにします。最初はXだけを label0 に表示して実行します。値が変わったら同じブロックを2個複製し、軸をY・Z、表示先を label1・label2 に変更します。
Loop
label0 の文字を「AX: 」+ 加速度Xの文字列 にする
label1 の文字を「AY: 」+ 加速度Yの文字列 にする
label2 の文字を「AZ: 」+ 加速度Zの文字列 にする
100 ms待つ
動作確認
平らに置いた状態、右へ傾けた状態、前へ傾けた状態を比べます。静止していても重力があるため、3軸全部が0になるとは限りません。加速度はg単位で、傾き角度そのものではありません。
軸の値を1個取得するブロックは、Pythonでは3軸の組から [0](X)、[1](Y)、[2](Z)を取り出す処理に対応します。 IMUのブロックとAPI
対応するMicroPython
表示を読みやすくするため、コード例では round(値, 2) で小数第2位までに丸めています。ブロックでも同じ表示にしたい場合は、数値を「100倍 → 四捨五入 → 100で割る」の計算にしてから文字列へ変換します。
import M5
from M5 import Widgets
import time
M5.begin()
Widgets.fillScreen(0x000000)
label0 = Widgets.Label("", 8, 35, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label1 = Widgets.Label("", 8, 85, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label2 = Widgets.Label("", 8, 135, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
while True:
M5.update()
x, y, z = M5.Imu.getAccel()
label0.setText("AX: " + str(round(x, 2)))
label1.setText("AY: " + str(round(y, 2)))
label2.setText("AZ: " + str(round(z, 2)))
time.sleep_ms(100)
4. ジャイロ:回転している間を調べる
ブロックを組む
基本プロジェクトを別名 sensor-gyro で保存します。IMUの「ジャイロの値を取得」を使い、軸の選択欄をXにします。最初はXだけを label0 に表示して実行します。値が変わったら同じブロックを2個複製し、軸をY・Z、表示先を label1・label2 に変更します。
Loop
label0 の文字を「GX: 」+ ジャイロXの文字列 にする
label1 の文字を「GY: 」+ ジャイロYの文字列 にする
label2 の文字を「GZ: 」+ ジャイロZの文字列 にする
100 ms待つ
動作確認
机に置いて静止した状態と、手でゆっくり回している間を比べます。回転を止めると値が0付近に戻るか確かめます。回した後の角度を保持するセンサーではなく、回転の速さ(角速度、°/s)を測ります。
加速度との違いを確かめるため、「傾けたまま静止する」実験も行いましょう。 IMUのブロックとAPI
対応するMicroPython
表示を読みやすくするため、コード例では round(値, 2) で小数第2位までに丸めています。ブロックでも同じ表示にしたい場合は、数値を「100倍 → 四捨五入 → 100で割る」の計算にしてから文字列へ変換します。
import M5
from M5 import Widgets
import time
M5.begin()
Widgets.fillScreen(0x000000)
label0 = Widgets.Label("", 8, 35, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label1 = Widgets.Label("", 8, 85, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label2 = Widgets.Label("", 8, 135, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
while True:
M5.update()
x, y, z = M5.Imu.getGyro()
label0.setText("GX: " + str(round(x, 2)))
label1.setText("GY: " + str(round(y, 2)))
label2.setText("GZ: " + str(round(z, 2)))
time.sleep_ms(100)
5. 地磁気:向きを変えて比べる
ブロックを組む
基本プロジェクトを別名 sensor-mag で保存します。IMUの「地磁気の値を取得」を使い、軸の選択欄をXにします。最初はXだけを label0 に表示して実行します。値が変わったら同じブロックを2個複製し、軸をY・Z、表示先を label1・label2 に変更します。
Loop
label0 の文字を「MX: 」+ 地磁気Xの文字列 にする
label1 の文字を「MY: 」+ 地磁気Yの文字列 にする
label2 の文字を「MZ: 」+ 地磁気Zの文字列 にする
100 ms待つ
動作確認
同じ場所で本体を水平に保ち、向きをゆっくり変えます。机の金属やスピーカーなどの近くと、少し離れた場所でも値を比較します。
ここでは3軸の磁気の読み取り値を観察します。数値だけで北を断定せず、方位計を作るときは校正や傾き補正を別途学びます。 IMUのブロックとAPI
対応するMicroPython
表示を読みやすくするため、コード例では round(値, 2) で小数第2位までに丸めています。ブロックでも同じ表示にしたい場合は、数値を「100倍 → 四捨五入 → 100で割る」の計算にしてから文字列へ変換します。
import M5
from M5 import Widgets
import time
M5.begin()
Widgets.fillScreen(0x000000)
label0 = Widgets.Label("", 8, 35, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label1 = Widgets.Label("", 8, 85, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label2 = Widgets.Label("", 8, 135, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
while True:
M5.update()
x, y, z = M5.Imu.getMag()
label0.setText("MX: " + str(round(x, 2)))
label1.setText("MY: " + str(round(y, 2)))
label2.setText("MZ: " + str(round(z, 2)))
time.sleep_ms(100)
6. マイク:録音した音を聞き返す
ここでは音を1個の数値として読む代わりに、音声データを一時的に記憶して再生します。スピーカーは入力センサーではなく、確認用の出力です。
ブロックを組む
別名 sensor-mic で保存します。Micの公式資料のUIFlow2 Exampleには、録音と再生に使うブロックが掲載されています。バッファを作るブロックが見つからない場合は、そのサンプルを開き、対応するブロックを確認してください。
- Variablesで
rec_dataを作ります。bytearrayを作成するブロックで長さを8000にし、変数へ入れます。これは文字列や数値のリストではなく、録音先のバイト配列です。 - HardwareのSpeakerとMicから、開始・終了・録音のブロックを置きます。録音のデータ欄へ
rec_data、サンプルレートへ8000、ステレオへFalseを指定します。 - 「録音中」の判定と「条件が真の間繰り返す」を組み合わせます。内側に「20 ms待つ」を置きます。
- 録音後、Micを終了してSpeakerを開始し、生データ再生(play raw)に同じ
rec_dataとレート8000を渡します。
Setup(1回だけ)
rec_data を bytearray(8000) にする
Speakerを終了
Micを開始
label0 を「Recording...」にする
rec_data に録音(8000 Hz、ステレオFalse)
録音中の間、20 ms待つ
Micを終了
Speakerを開始
音量を10%にする
label0 を「Playing...」にする
rec_data を生データ再生(8000 Hz)
再生中の間、20 ms待つ
Speakerを終了
label0 を「Done」にする
Loop
100 ms待つ
動作確認
実行直後に短く「あ」と声を出します。録音が終わると聞き返せます。もう一度録音するときはRun Onceで再実行します。静かな状態と発声した状態で比べましょう。録音と再生を同時に動かさず、順番に切り替える点がポイントです。
この実習は録音の確認です。音量を測るdBメーターではありません。音の大きさを判定するには、音声サンプルから振幅やRMSを計算する処理を後で加えます。
対応するMicroPython
import M5
from M5 import Widgets
import time
M5.begin()
Widgets.fillScreen(0x000000)
label0 = Widgets.Label("", 8, 35, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label1 = Widgets.Label("", 8, 85, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
label2 = Widgets.Label("", 8, 135, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.DejaVu18)
rec_data = bytearray(8000)
M5.Speaker.end()
M5.Mic.begin()
label0.setText("Recording...")
M5.Mic.record(rec_data, 8000, False)
while M5.Mic.isRecording():
M5.update()
time.sleep_ms(20)
M5.Mic.end()
M5.Speaker.begin()
M5.Speaker.setVolumePercentage(10)
label0.setText("Playing...")
M5.Speaker.playRaw(rec_data, 8000)
while M5.Speaker.isPlaying():
M5.update()
time.sleep_ms(20)
M5.Speaker.end()
label0.setText("Done")
while True:
M5.update()
time.sleep_ms(100)
7. カメラ:画像をセンサーデータとして扱う
ブロックを組む
新しいプロジェクト sensor-camera を作ります。この実習ではLabelを配置せず、画像で画面全体を使います。AdvancedのCameraを探します。カメラの公式資料の「capture display」からブロック例も確認できます。
- Setupにカメラ初期化を置き、形式をRGB565、サイズをQVGA(320×240)にします。
- Variablesで
imgを作ります。 - Loopで「カメラの画像を取得(snapshot)」を
imgへ入れます。 - Displayの画像表示(show)へ
imgを渡し、位置を(0, 0)、幅320、高さ240にします。「100 ms待つ」を続けます。
Setup
Cameraを初期化(RGB565、QVGA)
Loop
img にCameraのsnapshotを入れる
Displayでimgを表示(X=0、Y=0、幅=320、高さ=240)
100 ms待つ
動作確認
カメラの前に色のついた紙などを置き、動かすと表示も変わることを確認します。次に待ち時間を500 msへ変え、更新の速さを比べます。撮影結果は1個の数値ではなく、画素が集まった画像です。このプログラムは画面表示だけで、ファイル保存や通信は行いません。
対応するMicroPython
import M5
import camera
import time
M5.begin()
camera.init(pixformat=camera.RGB565, framesize=camera.QVGA)
while True:
M5.update()
img = camera.snapshot()
M5.Lcd.show(img, 0, 0, 320, 240)
time.sleep_ms(100)
実験の記録と振り返り
次の表をノートに作り、操作前と操作後の結果を記録します。値が揺れる場合は1個だけでなく、最小値と最大値も残しましょう。
| 入力 | 試した操作 | 操作前 | 操作後 | 分かったこと |
|---|---|---|---|---|
| タッチ | 指を触れる・離す | |||
| 明るさ | センサー部分を覆う | |||
| 加速度 | 傾けて静止する | |||
| ジャイロ | 回してから止める | |||
| 地磁気 | 水平に向きを変える | |||
| マイク | 静かな状態・発声 | |||
| カメラ | 紙を動かす |
- ブロックの「変数へ入れる」は、Pythonのどの行に対応しますか。
- 初期設定を1回だけ行い、読み取りをLoopへ置くのはなぜですか。
- 加速度とジャイロは、傾けたまま止めたときにどう違いましたか。
- 明るさの判定境界付近で表示が切り替わり続けるなら、しきい値や待ち時間を変えるとどうなりますか。
うまく動かないとき
| 症状 | 確認すること |
|---|---|
| ブロックが見つからない | 選択機種がCoreS3か確認し、各公式資料のブロック例と比較する |
Ready のまま |
実行先の機器と接続状態、ブロックがLoopへ接続されているか確認する |
| 入力が更新されない | 読み取りがLoop内か、Python表示で M5.update() があるか確認する |
| 数字が長くて読めない | 表示桁数を減らし、ラベル同士の間隔を広げる |
| ALSやIMUの属性エラー | 別機種のファームウェアでないか確認し、使用バージョンと公式資料を照合する |
| 録音が再生されない | バッファ、録音・再生のレート、完了待ち、MicとSpeakerの切り替え順を確認する |
| カメラを初期化できない | カメラ搭載機種と対応ファームウェアか確認し、再起動してカメラ実習だけを実行する |
本教材のコードはUIFlow 2.0用です。PC上のPythonでは M5 などを読み込めません。センサーの反応や表示は、手元のCoreS3で各実習のチェック項目を確かめてください。