第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. 数値を表示する準備

ブロックを組む

  1. 新規プロジェクトを作り、画面の背景を黒にします。
  2. UIの部品からLabelを3個配置し、名前を label0、label1、label2 にします。
  3. 位置をそれぞれ (8, 35)、(8, 85)、(8, 135)、文字色を白、背景色を黒、フォントをDejaVu18に設定します。
  4. Setup側で label0 の文字を Ready にします。Loop側には「待つ 100 ms」を置きます。秒単位のブロックなら0.1秒です。
  5. 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には、録音と再生に使うブロックが掲載されています。バッファを作るブロックが見つからない場合は、そのサンプルを開き、対応するブロックを確認してください。

  1. Variablesで rec_data を作ります。bytearrayを作成するブロックで長さを 8000 にし、変数へ入れます。これは文字列や数値のリストではなく、録音先のバイト配列です。
  2. HardwareのSpeakerとMicから、開始・終了・録音のブロックを置きます。録音のデータ欄へ rec_data、サンプルレートへ 8000、ステレオへFalseを指定します。
  3. 「録音中」の判定と「条件が真の間繰り返す」を組み合わせます。内側に「20 ms待つ」を置きます。
  4. 録音後、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」からブロック例も確認できます。

  1. Setupにカメラ初期化を置き、形式をRGB565、サイズをQVGA(320×240)にします。
  2. Variablesで img を作ります。
  3. Loopで「カメラの画像を取得(snapshot)」を img へ入れます。
  4. 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で各実習のチェック項目を確かめてください。

読み取りができたら、センサーアプリで記録と可視化へ進むか、ゲーム作りでタッチや傾きを操作に利用しましょう。