chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

M5STACK AtomS3 (ESP32 S3)にMicroPythonを入れる

やりたいこと:手元にM5STACK AtomS3 (LCD付き)*1があったのでこれにMicroPythonを入れてGameControllerに仕立てる。
まずやること:MicroPythonを入れる、LCDを使えるようにする
結論:MicroPythonを入れて、LCDが動くところまでは確認
詳細:

M5STACK AtomS3

ファームを焼く

以下からファームを取ってくる(ATOMS3 Genericといのもあるかもだけど、M5Stack ATOM S3 Lite用にポーティングされているのを使ってみる)
https://micropython.org/download/M5STACK_ATOMS3_LITE/
UTF2版はesptool.pyでは焼けないので(多分)、bin版をDL

以下の手順で焼く

esptool.py --port <PORTNAME> erase_flash
esptool.py --port <PORTNAME> write_flash 0 <firm_file>

上記の手順で焼くつもりだったが、ESP32 S3をUSB-Cで接続すると、ドライブとして出現したので、UTF2形式のファームをDrag&Dropで焼いてみる。
コピーしたところ、単にファイルとして保存されただけだった。

Stack ATOM S3の横のボタンを押しながらUSB接続*2、Download Boot modeになっているのを期待して、シリアルから操作

$ esptool.py --port /dev/ttyS27  chip_id
esptool.py v4.7.0
Serial port /dev/ttyS27
Connecting...
Detecting chip type... ESP32-S3
Chip is ESP32-S3 (QFN56) (revision v0.1)
Features: WiFi, BLE, Embedded Flash 8MB (GD)
Crystal is 40MHz
MAC: dc:54:75:xx:xx:xx
Uploading stub...
Running stub...
Stub running...
Warning: ESP32-S3 has no Chip ID. Reading MAC instead.
MAC: dc:54:75:cb:bb:8c
Hard resetting via RTS pin...
$ esptool.py --port /dev/ttyS27  erase_flash
esptool.py v4.7.0
Serial port /dev/ttyS27
Connecting...
Detecting chip type... ESP32-S3
Chip is ESP32-S3 (QFN56) (revision v0.1)
Features: WiFi, BLE, Embedded Flash 8MB (GD)
Crystal is 40MHz
MAC: dc:54:75:xx:xx:xx
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 5.6s
Hard resetting via RTS pin...
$ esptool.py --port /dev/ttyS27  write_flash 0 M5STACK_ATOMS3_LITE-20250809-v1.26.0.bin
esptool.py v4.7.0
Serial port /dev/ttyS27
Connecting...
Detecting chip type... ESP32-S3
Chip is ESP32-S3 (QFN56) (revision v0.1)
Features: WiFi, BLE, Embedded Flash 8MB (GD)
Crystal is 40MHz
MAC: dc:54:75:xx:xx:x
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Flash will be erased from 0x00000000 to 0x001a5fff...
Compressed 1725072 bytes to 1131254...
Wrote 1725072 bytes (1131254 compressed) at 0x00000000 in 10.8 seconds (effective 1278.4 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...

シリアルで接続すると以下のメッセージであった

>>>
MPY: soft reboot
MicroPython v1.26.0 on 2025-08-09; M5Stack AtomS3 Lite with ESP32S3
Type "help()" for more information.
>>> help('modules')
__main__          btree             io                ssl
_asyncio          builtins          json              struct
_boot             cmath             machine           sys
_espnow           collections       machine           time
_onewire          cryptolib         math              tls
_thread           deflate           micropython       uasyncio
_webrepl          dht               mip/__init__      uctypes
aioespnow         ds18x20           neopixel          umqtt/robust
apa106            errno             network           umqtt/simple
array             esp               ntptime           upysh
asyncio/__init__  esp32             onewire           urequests
asyncio/core      espnow            os                vfs
asyncio/event     flashbdev         platform          webrepl
asyncio/funcs     framebuf          random            webrepl_setup
asyncio/lock      gc                re                websocket
asyncio/stream    hashlib           requests/__init__
binascii          heapq             select
bluetooth         inisetup          socket
Plus any modules on the filesystem
>>> import os
>>> os.uname()
(sysname='esp32', nodename='esp32', release='1.26.0', version='v1.26.0 on 2025-08-09', machine='M5Stack AtomS3 Lite with ESP32S3')
>>> import machine
>>> machine.freq()
160000000
>>> machine.freq(240000000)
>>> machine.freq()
240000000

LCDを制御する(先人のドライバを使わせていただく)

下記のRepositoryから、
https://github.com/russhughes/st7789py_mpy/tree/master
下記2つのファイルを取得

import tft_config
import ss7789py as st7789
tft = tft_config.config(tft_config.WIDE)
tft.fill(st7789.WHITE)
tft.fill(st7789.BLACK)
tft.line(0,0,128,128,st7789.WHITE)
tft.line(0,128,128,0,st7789.WHITE)

上記で画面に×を描画できた

Hello Worldを表示してみる
fontデータを入手

  • st7789py_mpy/romfonts/vga1_8x8.py
  • st7789py_mpy/romfonts/vga1_16x16.py
import vga1_16x16
import vga1_8x8
tft.fill(st7789.BLACK)
tft.text(vga1_8x8, 'Hello, World', 0, 0, st7789.WHITE, st7789.BLACK)
tft.text(vga1_16x16, 'hello', 0, 60, st7789.WHITE, st7789.BLACK)
tft.text(vga1_16x16, 'world', 40, 77, st7789.WHITE, st7789.BLACK)

8x8のフォントでも十分読める

■追記(2025/8/17)
ATOM(ESP32S3)がAWS IoT CoreとMQTT通信するコード例

  • 以下は旧引数仕様(後方互換性確保版の仕様)
  • 証明書はDER形式に変換せずともPEM形式でよい (DER形式でも処理できる)
  • Client証明書、秘密鍵はファイル名指定で良い
  • 接続先サーバのroot CAはファイルを読み込んでデータとして渡す
  • 接続先サーバの証明書の中間証明書は不要(ライブラリが処理してくれる)
#
#
from umqtt.simple import MQTTClient
import ssl
import json

MQTT_BROKER = 'axxxxxxxxxxxxs.iot.ap-northeast-1.amazonaws.com'
MQTT_PORT = 8883             # 8883 : MQTT, encrypted

MQTT_TOPIC = b'test/upy_publish_test'
MQTT_CLIENT_ID = "client_ESP32_PICO_001"

CLIENT_KEY_FILE = '/cert/private.key'
CLIENT_CRT_FILE = '/cert/client.crt'
SERVER_CRT_FILE = '/cert/AmazonRootCA1.pem'

# read Server side ROOT CA (PEM format)
with open(SERVER_CRT_FILE, "r") as f:
     cadata = f.read()

# parameters for mTLS
ssl_params = {
    "key" : CLIENT_KEY_FILE,
    "cert" : CLIENT_CRT_FILE,
    'cadata' : cadata,
    "cert_reqs" : ssl.CERT_REQUIRED,
    'server_hostname' : MQTT_BROKER,
}
client = MQTTClient( MQTT_CLIENT_ID, MQTT_BROKER, MQTT_PORT, ssl = True, ssl_params = ssl_params )
client.connect()
message = {'client_id': MQTT_CLIENT_ID, 'security settings' : 'with TLS and auth by client crt (pem format)' }
payload = json.dumps(message).encode('utf-8')
print("publish:", payload)
client.publish(MQTT_TOPIC, payload)
client.disconnect()

注意点:AWS側でClient証明書を発行した場合、Activateする必要がある(多分)


RP2350(RPi Pico 2 W)上のMicroPythonとMicroPythonライブラリが入手したumqtt.simpleを使ってAWS IoT Coreと接続する時のソースは以下
PEM形式のファイルではエラーになるため、DER形式に変換する必要あり。

import time
import ssl
import json
from umqtt.simple import MQTTClient

MQTT_BROKER = 'a3xxxxxxxx.iot.ap-northeast-1.amazonaws.com'
MQTT_PORT = 8883             # 8883 : MQTT, encrypted

MQTT_TOPIC = b'test/upy_publish_test'
MQTT_CLIENT_ID = "client_RP_Pico2W_0001"

CLIENT_KEY_FILE = '/cert/private.der.key'
CLIENT_CRT_FILE = 'cert/client.der.crt'
SERVER_CRT_FILE = 'cert/AmazonRootCA1.der.pem'

# read Server side ROOT CA (DER format)
with open(SERVER_CRT_FILE, "rb") as f:
     cadata = f.read()

# parameters for mTLS
ssl_params = {
    "key" : CLIENT_KEY_FILE,
    "cert" : CLIENT_CRT_FILE,
    'cadata' : cadata,
    "cert_reqs" : ssl.CERT_REQUIRED,
    'server_hostname' : MQTT_BROKER,
}
client = MQTTClient( MQTT_CLIENT_ID, MQTT_BROKER, MQTT_PORT, ssl = True, ssl_params = ssl_params )
client.connect()

■関連URL
https://docs.m5stack.com/ja/core/AtomS3

russhughes氏によるLCDドライバ(一発で動きました。感謝)
https://github.com/russhughes/st7789py_mpy/tree/master

*1:今はもう売っていないかもしれない

*2:2秒押し続けるとG0がGNDに設定される