1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

M5Atomic Echo BaseをModdable SDKで動かす (格闘編)

Posted at

はじめに

M5Atomシリーズ(M5AtomS3)とM5Atomic Echo Baseを組み合わせて、小さいスタックチャンを動かすまでの過程を備忘メモに残します。

Moddable SDKでターゲットデバイスを追加する時の一例として参考にしていただければ幸いです。

ゴール

Node-RED MCU版(Moddable SDK版)の小さいスタックチャンを動かすことが目的です。

サンプルコードを動かす

Arduinoのサンプルコード

Arduinoライブラリとサンプルコードが用意されています。

サンプルコードを動かす際に注意点がありました。

  • ESP32のArduinoライブラリは2.0.x系を使用する(ESP-IDF 4.4系を使用する)
    • 3.0.x系はESP-IDF 5.x系が使用されているため、ビルドでエラーが出る
  • M5Atomic-EchoBaseのライブラリ(1.0.0)の内容の一部(PI4IOESV6408チップのI2Cアドレス)が間違っている
    • GitHubの内容(最新)は修正されているが、リリースパッケージが更新されていない



UIFlow2のサンプルコード

UIFlow2(Micropython)のサンプルコードが用意されています。

実装はGitHubのソースコードを参考にしました。

Moddable SDKで実装する

過去にターゲットデバイスの追加や、液晶ディスプレイドライバの初期化処理のデバッグを行ったことがあったので、安直に考えていましたが間違いでした。
サンプルコードを参考に実装しましたが、デバイスから音が鳴りません。

ロジアナで解析する

まずはI2Cの実装が合っているか、ロジアナ(激安USBロジアナ、sigrok、PulseView)を使用して確認しました。

(参考)
https://qiita.com/nanbuwks/items/9069165bfb66d8836583

ES8311とPI4IOE5V6408のコマンドシーケンスを追いかけます。

I2Cのコマンドシーケンスは同じでも音が出ませんでした。
(たまに音が鳴っても、虫の音ほど微かに聞こえる程度)

次にI2Sの信号も確認しました。

デバッグ作業

ES8311はモノラルオーディオデコーダーなのでR(右)の音声データの有無は関係ないと先入観を持っていましたが、最終的に気がついたのはI2Sの信号で、送信される音声データに差分(LRのデータと、Lのみのデータ)がありました。

ok.jpg

また、後になって気づいたことですが、起動(初期化)後の数秒(1〜2秒)は音が出ない仕様のようです。

init_muon.jpg

動いた

L(左)の音声データをR(右)にも送信することで音が鳴るようになりました。

ESP-IDFのソースコードでは、I2SのslotはI2S_STD_SLOT_LEFTが指定されていることを確認しました。
これをI2S_STD_SLOT_BOTHに変更すればLR(左右)のデータとして出力されそうです。
Moddable SDKではmanifest.jsonで設定できることを確認しました。

manifest.json
"i2s": {
    "slot": "I2S_STD_SLOT_BOTH"
}

コードの整理

起動(初期化)後の数秒(1〜2秒)は音が出ない仕様については、最初に1秒間の無音データ(16000サンプルデータ)を挿入することで問題を回避しました。

M5AtomS3 + M5Atomic Echo Baseのターゲットファイル(m5atom_s3_echo_base)を例に差分コードを示します。

manifest.json
% diff manifest.json ../m5atom_s3/manifest.json 
19,21d18
< 		"setup/target": "./setup-target",
< 		"pins/audioout": "$(MODULES)/pins/i2s/*",
< 		"pins/smbus": "$(MODULES)/pins/smbus/smbus",
24,25d20
< 			"$(MODULES)/pins/audioin/*",
< 			"$(MODULES)/pins/audioin/esp32/*",
27c22,23
< 		]
---
> 		],
> 		"setup/target": "./setup-target"
30,31d25
< 		"pins/audioout",
< 		"audioin",
38,42c32
< 		"touch": "",
< 		"startupSound": "bflatmajor.maud",
< 		"es8311": {
< 			"volume": 160
< 		}
---
> 		"touch": ""
44,46d33
< 	"resources": {
< 		"*": "$(MODDABLE)/examples/assets/sounds/bflatmajor"
< 	},
111,135d97
< 		},
< 		"audioOut": {
< 			"bitsPerSample": 16,
< 			"numChannels": 1,
< 			"sampleRate": 11025,
< 			"volume_divider": 1,
< 			"i2s": {
< 				"num": 1,
< 				"slot": "I2S_STD_SLOT_BOTH",
< 				"bitsPerSample": 16,
< 				"bck_pin": 8,
< 				"lr_pin": 6,
< 				"dataout_pin": 5
< 			}
< 		},
< 		"audioIn": {
< 			"sampleRate": 11025,
< 			"bitsPerSample": 16,
< 			"i2s": {
< 				"num": 0,
< 				"bck_pin": 8,
< 				"lr_pin": 6,
< 				"datain": 7,
< 				"pdm": 1
< 			}
setup-target.js
1c1
< //import Digital from "pins/digital";
---
> import Digital from "pins/digital";
9,11d8
< import AudioOut from "pins/audioout";
< import Resource from "Resource";
< import SMBus from "pins/smbus";
13,18d9
< const INTERNAL_I2C = Object.freeze({
< 	sda: 38,
< 	scl: 39,
< 	hz: 100000
< });
< 
41,64d31
<         // start-up sound
< 	if (config.startupSound) {
< 		const speaker = new AudioOut({streams: 1});
< 
<                 speaker.callback = function () {
< 			this.stop();
< 			this.close();
< 			Timer.set(this.done);
< 		};
< 		speaker.done = done;
< 		done = undefined;
< 
<                 new ES8311();
<                 new PI4IOE5V6408();
< 
< 		speaker.enqueue(0, AudioOut.Silence, 16000); // enqueue silence for no audio output (1 sec) of ES8311 I2S initial setup
<        		speaker.enqueue(0, AudioOut.Samples, new Resource(config.startupSound), 1);
< 		speaker.enqueue(0, AudioOut.Callback, 0);
< 		speaker.start();
< 
<         }
< 
< 
< 	// accelerometer and gyrometer
69c36
<         done?.();
---
>         done();
72,149d38
< class ES8311 {
< 	// initialize ES8311(0x18)
< 	// https://github.com/m5stack/uiflow-micropython/blob/master/m5stack/libs/driver/es8311/__init__.py
<         constructor(options) {
<                 const es = new SMBus({
<         		...INTERNAL_I2C,
<         		address: 0x18,
<         	});
<         	es.writeByte(0x00, 0x1F); // ES8311_RESET_REG00
<         	Timer.delay(20);
<         	es.writeByte(0x00, 0x00); // ES8311_RESET_REG00
<         	es.writeByte(0x00, 0x80); // ES8311_RESET_REG00
< 
<         	// clock config
<         	es.writeByte(0x01, 0xBF); // ES8311_CLK_MANAGER_REG01
<         	es.readByte(0x06); // ES8311_CLK_MANAGER_REG06
<         	es.writeByte(0x06, 0x03); // ES8311_CLK_MANAGER_REG06
<         	es.readByte(0x02); // ES8311_CLK_MANAGER_REG02
<         	es.writeByte(0x02, 0x10); // ES8311_CLK_MANAGER_REG02
<         	es.writeByte(0x03, 0x10); // ES8311_CLK_MANAGER_REG03
<         	es.writeByte(0x04, 0x10); // ES8311_CLK_MANAGER_REG04
<         	es.writeByte(0x05, 0x00); // ES8311_CLK_MANAGER_REG05
<         	es.readByte(0x06); // ES8311_CLK_MANAGER_REG06
<         	es.writeByte(0x06, 0x03); // ES8311_CLK_MANAGER_REG06
<         	es.readByte(0x07); // ES8311_CLK_MANAGER_REG06
<         	es.writeByte(0x07, 0x00); // ES8311_CLK_MANAGER_REG07
<         	es.writeByte(0x08, 0xFF); // ES8311_CLK_MANAGER_REG08
< 
<         	// format config
<         	es.readByte(0x00); // ES8311_RESET_REG00
<         	es.writeByte(0x00, 0x80); // ES8311_RESET_REG00
<         	es.writeByte(0x09, 0x10); // ES8311_SDPIN_REG09
<         	es.writeByte(0x0A, 0x10); // ES8311_SDPOUT_REG0A
< 
<                 //
<         	es.writeByte(0x0D, 0x01); // ES8311_SYSTEM_REG0D
<         	es.writeByte(0x0E, 0x02); // ES8311_SYSTEM_REG0E
<         	es.writeByte(0x12, 0x00); // ES8311_SYSTEM_REG12
<                 es.writeByte(0x13, 0x10); // ES8311_SYSTEM_REG13
<                 es.writeByte(0x1C, 0x6A); // ES8311_ADC_REG1C
<                 es.writeByte(0x37, 0x08); // ES8311_DAC_REG37
< 
<         	// set volume (0 - 256)
< 		let volume = config.es8311.volume ?? 128;
< 		if (volume < 0) volume = 0;
< 		if (volume > 255) volume = 255;
<         	es.writeByte(0x32, volume); // ES8311_DAC_REG32
< 
<         	// microphone
<                 es.writeByte(0x17, 0xFF); // ES8311_ADC_REG17
<                 es.writeByte(0x14, 0x1A); // ES8311_SYSTEM_REG14
< 
<         	es.close();
<         }
< }
< 
< class PI4IOE5V6408 {
< 	// initialize PI4IOE5V6408(0x43)
< 	// https://github.com/m5stack/uiflow-micropython/blob/master/m5stack/libs/base/echo.py
<         constructor(options) {
<                 const pi = new SMBus({
<         		...INTERNAL_I2C,
<                         address: 0x43,
<                 });
< 	        pi.readByte(0x00); // PI4IOE_REG_CTRL
<         	pi.writeByte(0x07, 0x00); // PI4IOE_REG_IO_PP
<         	pi.readByte(0x07);
<         	pi.writeByte(0x0D, 0xFF); // PI4IOE_REG_IO_PULLUP
<         	pi.writeByte(0x03, 0x6E); // PI4IOE_REG_IO_DIR
<         	pi.readByte(0x03);
<         	pi.writeByte(0x05, 0xFF); // PI4IOE_REG_IO_OUT
<         	pi.readByte(0x05);
< 
<         	pi.close();
<         }
< }

プルリク

2025年5月11日、プルリク(Pull Request)を送りました。

次回のリリース(5.7.0)にマージされると普通に利用できるようになります。

それまで待てないという方は
$MODDABLE/build/devices/esp32/targets
ディレクトリにターゲットファイルを配置(ディレクトリを作成)してください。

最後に

Node-RED MCUでも動くことを確認しました。

node-red-mcu-pluginの修正(ターゲットデバイスの定義追加)が必要です。
プルリクがマージされるまでお待ちください!

mcu_plugin.js
...
   let platform_identifiers = [
   (以下に行を追加)
     'esp32/m5atom_s3_echo_base',

noderedmcu.jpg

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?