moddable-m5chain is a Moddable SDK module for controlling M5Chain devices over UART.
It handles device enumeration, initialization, event dispatch, and polling.
| Device | Type ID | HasLed |
HasKey |
CanSample |
Sample Event (onSample) |
API Guide |
|---|---|---|---|---|---|---|
| Encoder | 0x0001 |
Yes | Yes | Yes | Yes (delta value) | Encoder API |
| Angle | 0x0002 |
Yes | No | Yes | Yes (normalized 0.00-1.00) |
Angle API |
| Key | 0x0003 |
Yes | Yes | No | No | Key API |
| JoyStick | 0x0004 |
Yes | Yes | Yes | Yes ({ x, y } in -128 to 127) |
JoyStick API |
| ToF | 0x0005 |
Yes | No | Yes | Yes (distance in mm) | ToF API |
- Packet transport and matching (
sendPacket/sendAndWait) - Automatic scan on startup
- Automatic re-scan when
ENUM_PLEASE (0xFC)is received (debounced) - Feature composition with mixins (LED, Key, Sample)
- Poll loop runs only when at least one device has
onSampleset
In your app's manifest.json, include this module's manifest.
{
"include": [
{
"git":"https://github.com/stc1988/moddable-m5chain.git"
}
]
}For M5Stack products, the default UART pins are set to the Grove port.
If you use an M5Atom series device with Atom Chain Base, automatically provides a config.m5chain pin configuration.
See Minimal Usage for the concrete usage pattern.
import M5Chain from "m5chain";
import config from "mc/config";
const m5chain = new M5Chain({
transmit: config.m5chain.transmit,
receive: config.m5chain.receive,
debug: false,
pollingInterval: 30, // ms
});
m5chain.onDeviceListChanged = (devices) => {
for (const device of devices) {
trace(`id=${device.id} type=0x${device.type.toString(16)} uid=${device.uuid}\n`);
}
};
await m5chain.start();- Called after the initial scan completes in
start() - Called again after re-scan when the chain sends
ENUM_PLEASE devicesis the current connected device list
Available on devices with HasKey (Encoder / Key / JoyStick).
statusis a key event, not the pressed/released state- Use
KEY_EVENT.SINGLE_CLICK,KEY_EVENT.DOUBLE_CLICK, orKEY_EVENT.LONG_PRESS
import M5Chain, { KEY_EVENT } from "m5chain";
device.onPush = async (keyEvent) => {
if (keyEvent === KEY_EVENT.SINGLE_CLICK) {
await device.setLedColor(255, 0, 0);
}
};KEY_EVENT, KEY_MODE, KEY_STATUS, and their TypeScript types are also exported from the key-capable device modules:
m5chainEncoder, m5chainKey, and m5chainJoyStick.
Available on devices with CanSample (Encoder / Angle / JoyStick / ToF).
If any device has onSample set, bus polling starts. It stops when all onSample handlers are null.
Use device.sample() to read the latest sample. Inside onSample, this is bound to the device:
device.onSample = function () {
const sample = this.sample();
};Angle, JoyStick, and ToF dispatch onSample with the latest sampled value on every poll. Encoder dispatches onSample with the delta from the previous encoder value and skips dispatch while the value is unchanged.
new M5Chain({ transmit, receive, debug = false, pollingInterval = 30 })await m5chain.start()m5chain.devicescurrent device array
device.iddevice.typedevice.uuid(afterinit())await device.configure(options)applies device and feature settingsawait device.readConfiguration()reads current device and feature settings from the chain deviceawait device.getUID(uidType = 1)(uidType: 0 | 1)await device.getBootloaderVersion()await device.getFirmwareVersion()
Available on: Encoder / Angle / Key / JoyStick / ToF
See HasLed API.
Available on: Encoder / Key / JoyStick
See HasKey API.
Available on: Encoder / Angle / JoyStick / ToF
See CanSample API.
Device-specific usage, TypeScript exports, and method details are split into focused pages:
Feature mixin details are also split into focused pages:
README intentionally keeps only the setup, event model, and shared API surface so device and feature pages can grow without making the first-read path hard to scan.
examples/basic: device discovery, info read, and event subscriptionexamples/led: LED control for Encoder/Angle/Key/JoyStick/ToFexamples/ble-hid/keyboard: BLE HID keyboard example that sends Enter from M5Chain Key events (docs)examples/ble-hid/mediaControl: BLE HID media control example that sends Play/Pause, Next Track, and Previous Track from M5Chain Key events (docs)examples/hotplug: re-scan verification after device reconnect, usinguuid, LED blink, key events, and sampled values
examples/ble-hid/keyboard/bleKeyboard.ts exposes a small keyboard peripheral helper. It supports single keys, modifier combinations, text typing, connection state, up to six simultaneous normal keys in one HID report, manual press/release, host LED indicators such as Caps Lock, and manual BLE advertising control. See BLE HID Keyboard Example for the full API.
keyboard.notifyKey({
keyCode: BLEKeyboard.KEY_CODE.A,
modifiers: BLEKeyboard.MODIFIER.LEFT_SHIFT,
});
keyboard.notifyKeyCodes([BLEKeyboard.KEY_CODE.A, BLEKeyboard.KEY_CODE.B]);
keyboard.typeText("hello\n");
keyboard.pressKeyCodes([BLEKeyboard.KEY_CODE.DELETE], BLEKeyboard.MODIFIER.LEFT_CONTROL | BLEKeyboard.MODIFIER.LEFT_ALT);
keyboard.releaseAll();
keyboard.stopAdvertising();
keyboard.startAdvertising();
keyboard.onIndicatorsChanged = (indicators) => {
trace(`caps lock=${(indicators & BLEKeyboard.INDICATOR.CAPS_LOCK) !== 0}\n`);
};examples/ble-hid/mediaControl/bleMediaControl.ts exposes a small media control peripheral helper. It sends HID Consumer Control usages such as Play/Pause, Next Track, Previous Track, Volume Up, Volume Down, and Mute. See BLE HID Media Control Example for the full API.
mediaControl.notifyUsage(BLEMediaControl.USAGE.PLAY_PAUSE);
mediaControl.notifyUsage(BLEMediaControl.USAGE.SCAN_NEXT_TRACK);
mediaControl.notifyUsage(BLEMediaControl.USAGE.VOLUME_UP);Format and lint:
npm run format
npm run lintMIT