M5Stackであそぼう

M5Stackの概要

ようやくM5Stackが入荷しました。昨年から注目されていますので、よくご存知の方も多いかと思いますが、まず簡単にM5Stackを紹介します。

M5StackはEspressifESP32と、SDカードスロット、ボタン、USBやGroveのコネクタを5cm四方の基板に搭載し、ディスプレイや電源までを樹脂のケースに詰め込んであるモジュールです。

M5StackはArduinoのシールドのように、センサー等の載った拡張基板をメインモジュール(CORE)に積み重ねることができ、それぞれのモジュールが約5 cm四方のケースに入っているところが特徴の1つです。

拡張基板には、いまのところGPS受信機や無線モジュール、DIYのためのユニバーサル基板などが用意されています。

EspressifのESP-IDFの他、Arduino IDEや、MicroPythonなどを利用することができます。

Wi-FiとBluetoothを日本国内でも適法に利用できる低価格なモジュールとして定番となっているESP32ベースなため、M5Stackにも利用できる様々なリソースがウェブに見つかることと思います。

スイッチサイエンスで買えるM5Stack関連商品

今回は、第一弾として以下の商品を入荷しました。

今後登場が予定されている4MB PSRAMの載ったCoreモジュールについては、技適の取得を待って入荷したいと考えています。

入荷したものの1つ、Basicの中を見てみると、専用の樹脂ケースに、USBケーブルや簡易な(とはいえ案外詳しい)説明書が同梱されています。

本体は、ディスプレイなどがあるメイン基板と、バッテリーの入った底側の基板に分割することができます。この2つは30ピン(15 x 2)の低いピンヘッダでつながっていますが、M3のネジで強く固定することもできます。

底面のケースには電池やGPIOなどのピンヘッダの載った基板に加えて、強い磁石が4つ入っているため、金属の壁などに貼り付けておくことができます。

使い方

とりあえず簡単に試すには、Arduino IDEで書いたプログラムをBasicモジュールに書き込むのが良いかもしれません。

Arduinoを使った経験があれば、M5Stackのためにやることは、USBのドライバのインストールと、M5Stack用のArduinoのライブラリをインストールするだけです。

M5StackのサイトにはMac/Win/Linuxへのインストール方法が解説されています。

注意点として、MacOSXの10.13.3(Hight Sierra)ではUSBドライバのインストールをしても、USBデバイス(/dev/tty.SLAB_USBtoUART)が出現しません。実はドライバをアクティベートするには、システム環境設定のセキュリティーとプライバシーのところで「許可」ボタンを押す必要があることにしばらくして気がつきました。みなさんもお気をつけください。

リソース

M5StackのGithubのリポジトリから入手できるもの。

  • Arduinoのライブラリやその利用法サンプル
  • 回路図、Protoモジュールの設計ファイル(Altium Designer用)、ケースのSTLファイル
  • M5CloudというMicroPythonのファームウエア

試してみた

MicroPythonで当社のConta サーモグラフィーの画像を表示させてみました。

プログラムはこちら。

# MicroPython sample program for M5Stack with AMG8833
# M5Stack
# http://www.m5stack.com/
# Panasonic Grid-EYE AMG8833
# https://industrial.panasonic.com/jp/products/sensors/built-in-sensors/grid-eye
# Usage
# Save this code as main.py then restart.
# Button A : Start drawing grid matrix of thermal data.
# Button B : Caribration
# Button C : Stop drawing
from m5stack import lcd, BtnA, BtnB, BtnC
from machine import I2C
import ustruct
colors = (0x0000FF, 0x0008FF, 0x0010FF, 0x0018FF, 0x0020FF, 0x0028FF, 0x0030FF, 0x0038FF, 0x0040FF, 0x0048FF, 0x0050FF, 0x0058FF, 0x0060FF, 0x0068FF, 0x0070FF, 0x0078FF, 0x0080FF, 0x0088FF, 0x0090FF, 0x0098FF, 0x00A0FF, 0x00A8FF, 0x00B0FF, 0x00B8FF, 0x00C0FF, 0x00C8FF, 0x00D0FF, 0x00D8FF, 0x00E0FF, 0x00E8FF, 0x00F0FF, 0x00F8FF, 0x00FFFF, 0x00FFF7, 0x00FFEF, 0x00FFE7, 0x00FFDF, 0x00FFD7, 0x00FFCF, 0x00FFC7, 0x00FFBF, 0x00FFB7, 0x00FFAF, 0x00FFA7, 0x00FF9F, 0x00FF97, 0x00FF8F, 0x00FF87, 0x00FF7F, 0x00FF77, 0x00FF6F, 0x00FF67, 0x00FF5F, 0x00FF57, 0x00FF4F, 0x00FF47, 0x00FF3F, 0x00FF37, 0x00FF2F, 0x00FF27, 0x00FF1F, 0x00FF17, 0x00FF0F, 0x00FF07, 0x00FF00, 0x08FF00, 0x10FF00, 0x18FF00, 0x20FF00, 0x28FF00, 0x30FF00, 0x38FF00, 0x40FF00, 0x48FF00, 0x50FF00, 0x58FF00, 0x60FF00, 0x68FF00, 0x70FF00, 0x78FF00, 0x80FF00, 0x88FF00, 0x90FF00, 0x98FF00, 0xA0FF00, 0xA8FF00, 0xB0FF00, 0xB8FF00, 0xC0FF00, 0xC8FF00, 0xD0FF00, 0xD8FF00, 0xE0FF00, 0xE8FF00, 0xF0FF00, 0xF8FF00, 0xFFFF00, 0xFFF700, 0xFFEF00, 0xFFE700, 0xFFDF00, 0xFFD700, 0xFFCF00, 0xFFC700, 0xFFBF00, 0xFFB700, 0xFFAF00, 0xFFA700, 0xFF9F00, 0xFF9700, 0xFF8F00, 0xFF8700, 0xFF7F00, 0xFF7700, 0xFF6F00, 0xFF6700, 0xFF5F00, 0xFF5700, 0xFF4F00, 0xFF4700, 0xFF3F00, 0xFF3700, 0xFF2F00, 0xFF2700, 0xFF1F00, 0xFF1700, 0xFF0F00, 0xFF0700)
i2c_address = 0x68
register = 0x80
ison = False
offset = 0
rate = 1
i2c = I2C()
def getval(val):
absval = (val & 0x7FF)
if val & 0x800:
return - float(0x800 - absval) * 0.25
else:
return float(absval) * 0.25
def minmax():
global offset
global rate
reg = register
min_temp = 1000
max_temp = -1000
for i in range(0, 64):
val = ustruct.unpack('<h', i2c.readfrom_mem(i2c_address, reg, 2))[0]
tmp = getval(val)
if tmp < min_temp:
min_temp = tmp
if max_temp < tmp:
max_temp = tmp
reg += 2
diff = max_temp - min_temp
# add some margin
diff *= 1.4
rate = len(colors) / diff
offset = min_temp * 0.8
lcd.clear()
lcd.print('min: ' + '{:.2f}'.format(min_temp), 0, 0)
lcd.print('max: ' + '{:.2f}'.format(max_temp), 0, 10)
lcd.print('rate: ' + '{:.2f}'.format(rate), 0, 20)
lcd.print('offset: ' + '{:.2f}'.format(offset), 0, 30)
def graph():
global offset
global rate
reg = register
for ic in range(0, 8):
for ir in range(0, 8):
val = ustruct.unpack('<h', i2c.readfrom_mem(i2c_address, reg, 2))[0]
tmp = (getval(val) - offset) * rate
if tmp < 0:
tmp = 0
if 127 < tmp:
tmp = 127
lcd.rect(ic * 30 + 40, ir * 30, 30, 30, 0, colors[int(tmp)])
reg += 2
def loop():
global ison
while True:
if BtnA.press():
ison = True
if BtnB.press():
minmax()
if BtnC.press():
ison = False
if ison:
graph()
loop()

MicroPythonファームウエア

M5CloudのGithubリポジトリに書かれた手順の通り、MicroPythonのファームウエアをダウンロードして、USB Type-Cのケーブル(同梱)でパソコンと繋ぎ、esptoolで書き込みます。ファームウエアには、ネット経由でプログラムできるタイプと、オフラインで書き込むのものがありますが、私は後者の方が扱いやすいと思ったのでそちらを使っています。ファームウエアは頻繁に代わりますが、私は現在最新のm5stack-20180206-v0.3.3.binを利用しました。

Pythonのプログラムを書き込む

例えばscreenコマンド

screen /dev/tty.SLAB_USBtoUART 115200

やCoolTerm、PuttyなどでPCとM5Stack同士でシリアル通信を開始したら、何度かパソコンのReturnキーを押すとプロンプト入力の表示になると思います。

ここでPythonプログラムを直接タイプして実行できるのがMicroPythonの良いところですが、今回はmain.pyのコードを書き込んで本体に保存するために、以下のようにします。

Ctrl + Eキーを押してペーストモードに入ってから、プログラムをコピペします。例えば、このようなプログラムをペーストすると、main.pyが保存され、再起動するとディスプレイに「Hello World!」が表示されます。

str = '''
from m5stack import lcd
lcd.clear()
lcd.print('Hello World!')
'''
f = open('main.py', 'w')
f.write(str)
f.close()
view raw gistfile1.txt hosted with ❤ by GitHub

2行目から4行目のプログラム部分を前出のプログラムにすれば良いわけです。

ちなみにペーストモードを脱出するにはCtrl + Dです。

今回エンクロージャーとしてTinkercadで3Dデータを作ってMakerbotで出力してみました。その他今回は使わなかったデータもGithubに置いておきます

TIPS: バッテリー駆動時は電源ボタンの2回押しで電源が切れるのと、起動時にAボタンを押しておくとmain.pyを実行しない。

まとめ

M5Stackはケースに入っており、電池も含めて程よいサイズに収まっているので、気軽にポケットなどに入れられるところが良いです。もちろん拡張性もあるので、自分の使いたいものに近づけていくことができます。ディスプレイやボタンやスピーカーがあらかじめ備わっていて、ArduinoやMicroPythonでプログラムできるので簡単に楽しめます。