Moddable SDKのターゲットデバイスにM5StickC Plusを追加してみた

# はじめに

Node-RED MCU Editionの動作確認用のデバイスとしてM5StickCをメインに使っていますが、M5StickCはEOL(製造・販売終了)で入手が困難です。
後継のM5StickC PlusはModdable SDKのターゲットデバイスとしてサポートされていないため、どうしようかなぁーと悩んでいました。
M5StickC Plusをターゲットデバイスに追加しようと決意したきっかけがありました。

## きっかけ ①

Moddable SDKのballsのサンプルデモを動かそうと思って、M5StickCではなく、間違えてM5StickC Plusを接続したところ、表示色がおかしいものの、画面の左端で動くことを確認しました。

![IMG_2338.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/c31fd692-4a6e-ed25-ba4c-9179b37ccc1d.jpeg)

https://qiita.com/kitazaki/items/15cba2f4622bf3682083

```bash
$ cd $MODDABLE/examples/piu/balls
$ UPLOAD_PORT=/dev/cu.usbserial-C152AAC745 mcconfig -d -m -p esp32/m5stick_c
```

液晶ドライバや画面サイズが異なるものの、その他のスペックはほぼ同じなので、液晶の設定を変更すればうまく動作するのではないか、と思いました。

### スペック比較

| |M5StickC	|M5StickC Plus |
|--|--|--|
|TFTコントローラー |ST7735S |ST7789V2 |
|液晶サイズ |0.96インチ |1.14 インチ |
|画面サイズ |80×160 |135×240 |
|6軸IMU |SH200Q or MPU6886 |MPU6886 |
|マイク |SPM1423 |SPM1423 |
|ブザー |なし |GPIO2 |
|PMIC(電源管理) |AXP192 |AXP192 |
|バッテリー |95mAh/3.7V |120mAh/3.7V |
|HATピンポート |G0, G36, G26 |G0, G25/G36, G26, |
|Groveピンポート |G32, G33 |G32, G33 |

## きっかけ ②

Node-RED MCU EditionのSensorノードで、M5StickCのHATモジュール(M5 ENV Hatなど)を使用する方法を調べていた時にターゲットデバイスの設定ファイルを知ることができました。

https://qiita.com/kitazaki/items/da3a2e4a16b8a971b840

```bash
$ cd $MODDABLE/build/devices/esp32/targets/m5stick_c
$ tree
.
├── host
│   └── provider.js
├── manifest.json
└── setup-target.js

1 directory, 3 files
```

HAT用のI2C(SDA:G0, SCL:G26)の設定はhost/provider.jsファイルへ追加しました。
setup-target.jsファイルはボタン、電源管理、6軸IMUの設定、manifest.jsonで液晶ディスプレイとマイクの設定をしていることを確認しました。

# 対応方針

- setup-target.js
  - 6軸IMUはSH200Qの判定ロジックを削除 (MPU6886のみに対応)
- manifest.json
  - 液晶ディスプレイ(ST7789、画面サイズ)に対応

## 6軸IMUの対応

SH200Qの判定ロジックを削除するだけでしたので、難しくありませんでした。

### 差分情報

```bash
$ diff setup-target.js ../m5stick_c/setup-target.js 
3a4
> import SH200Q from "sh200q";
31c32
<   state.accelerometerGyro = new MPU6886;
---
>   state.accelerometerGyro = new IMU();
170a172,183
> class IMU {
> 	constructor() {
> 		const probe = new I2C({
> 			address: 0x68,		// MPU6886
> 			throw: false
> 		});
> 		const result = probe.write(0x75);
> 		probe.close();
> 
> 		return (result instanceof Error) ? new SH200Q : new MPU6886;
> 	}
> }
```

## 液晶ディスプレイの対応

Moddable SDKのGithubで液晶ディスプレイの情報を調べたところ、ST7735の液晶ドライバ(ライブラリ)はILI9341のサブセットとして動作させていることがわかりました。

https://github.com/Moddable-OpenSource/moddable/blob/public/documentation/drivers/st7735/st7735.md

https://github.com/Moddable-OpenSource/moddable/blob/public/documentation/drivers/ili9341/ili9341.md

ST7789もソースコードを見て同様とわかりました。

https://github.com/Moddable-OpenSource/moddable/tree/public/modules/drivers/st7789

### 液晶ディスプレイの仕様調査

TFTコントローラーの仕様書とM5Stack社の実装(ソースコード)を調査し、設定パラメータを見比べました。

https://docs.google.com/spreadsheets/d/18JTX6W-u1tB2hhPPjuPoj_gWdk9RbC4BrG5XwN69Fkw/edit?usp=sharing

### 既知の未解決問題

widthに135(奇数)、heightに240を設定するとM5StickC Plusがリセット動作する症状になりました。
widthに134、または、136(偶数)を指定すると正常に動作することがわかったため、widthに136を設定し、1ラインを諦めてPR(プルリク)を送りました。

https://github.com/Moddable-OpenSource/moddable/pull/947

![スクリーンショット 2022-10-25 11.32.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/06aea585-71ed-77aa-8187-081e0ed705ff.png)

STC ( https://twitter.com/stc1988 ) さんから情報をいただき、widthに135(奇数)を設定するとデバイスがリセット動作するのは既知の未解決問題だったということがわかりました。

![スクリーンショット 2022-10-25 11.32.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/6bee080d-c997-e4ab-8994-7637023f48e4.png)

https://github.com/Moddable-OpenSource/moddable/discussions/708

### 回避方法の検討

実際の液晶ディスプレイの画面サイズと異なる値を設定するのは気持ちが悪いので、回避方法を検討することにしました。
まずはM5StickC Plusと同じ液晶ディスプレイを使用している他のデバイスの調査から始めました。

- 液晶ドライバにST7789を使用しているデバイス
- 画面サイズが135x240のデバイス

LILYGO® TTGOが検索結果に出てきました。

http://www.lilygo.cn/prod_view.aspx?TypeId=50062&Id=1400&FId=t3:50062:3

Moddable SDKのターゲットデバイスとして既に登録されており、widthとheightのサイズを逆に設定していることがわかりました。

https://github.com/Moddable-OpenSource/moddable/tree/public/build/devices/esp32/targets/lilygo_ttgo

```json:manifest.json (一部抜粋)
                "ili9341": {
                        "hz": 27000000,
                        "width": 240,
                        "height": 135,
```

widthとheightの指定を逆にするため、レジスタパラメータ(0x36: Memory Access Data Control)の設定を0x08→0x78へ変更します。
結果、heightは奇数を設定してもリセット動作は発生しませんでした。

![スクリーンショット 2022-10-25 11.42.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/47851bba-38c2-644b-5a3e-3cbe39c1de58.png)

### 回転(rotation)の確認

mcconfigコマンドで画面を回転させた時もリセット動作が発生しないことを確認しました。

![スクリーンショット 2022-10-25 11.41.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/bb5322b2-6562-016b-6fbb-8210c664ede0.png)

![スクリーンショット 2022-10-25 11.41.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/99e8d52e-788f-dd09-b8e8-bc0e4c8ba016.png)

### 差分情報

```bash
$ diff manifest.json ../m5stick_c/manifest.json 
7c7
< 		"$(MODDABLE)/modules/drivers/st7789/manifest.json",
---
> 		"$(MODDABLE)/modules/drivers/st7735/manifest.json",
12c12
< 		"screen": "st7789",
---
> 		"screen": "st7735",
26,27c26,27
< 			"width": 240,
< 			"height": 135,
---
> 			"width": 80,
> 			"height": 160,
31,32c31,32
< 			"column_offset": 40,
< 			"row_offset": 53,
---
> 			"column_offset": 26,
> 			"row_offset": 1,
39,42c39,41
<                  "0xB2, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33,",
<                  "0xB6, 2, 0x0A, 0x82,",
<                  "0xB7, 1, 0x35,",
<                  "0xBB, 1, 0x28,",
---
>                  "0xB1, 3, 0x01, 0x2C, 0x2D,",
>                  "0xB2, 3, 0x01, 0x2C, 0x2D,",
>                  "0xB3, 6, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D,",
44,48c43,49
<                  "0xC0, 1, 0x0C,",
<                  "0xC2, 2, 0x01, 0xFF,",
<                  "0xC3, 1, 0x10,",
<                  "0xC4, 1, 0x20,",
<                  "0xC6, 1, 0x0F,",
---
>                  "0xB4, 1, 0x07,",
>                  "0xC0, 3, 0xA2, 0x02, 0x84,",
>                  "0xC1, 1, 0xC5,",
>                  "0xC2, 2, 0x0A, 0x00,",
>                  "0xC3, 2, 0x8A, 0x2A,",
>                  "0xC4, 2, 0x8A, 0xEE,",
>                  "0xC5, 1, 0x0E,",
50,53c51,54
<                  "0x36, 1, 0x78,",
<                  "0x3A, 1, 0x55,",
<                  "0x2A, 4, 0x00, 0x00, 0x01, 0x3F,",
<                  "0x2B, 4, 0x00, 0x00, 0x00, 0xE5,",
---
>                  "0x36, 1, 0xC8,",
>                  "0x3A, 1, 0x05,",
>                  "0x2A, 4, 0x00, 0x02, 0x00, 0x81,",
>                  "0x2B, 4, 0x00, 0x01, 0x00, 0xA0,",
55,56c56,57
<                  "0xE0, 14, 0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x32, 0x44, 0x42, 0x06, 0x0E, 0x12, 0x14, 0x17,",
<                  "0xE1, 14, 0xD0, 0x00, 0x02, 0x07, 0x0a, 0x28, 0x31, 0x54, 0x47, 0x0E, 0x1C, 0x17, 0x1B, 0x1E,",
---
>                  "0xE0, 16, 0x02, 0x1C, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2D, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10,",
>                  "0xE1, 16, 0x03, 0x1D, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10,",
61c62
<                  "kDelayMS, 0"
---
> 				 "kDelayMS, 0"
78c79,81
< 			"$(MODULES)/drivers/mpu6886/*"
---
> 			"$(MODULES)/drivers/mpu6886/*",
> 			"$(MODULES)/drivers/sh200q/*"
> 			
85c88,89
< 		"mpu6886"
---
> 		"mpu6886",
> 		"sh200q"
```

# さいごに

無事にballsのデモを動かすことができました。

![IMG_2340.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/9f3e4f27-9202-fb59-392b-3e89218e942b.jpeg)