From e089ab6874b001d291e1f36a2f6eab6aecfe4d8c Mon Sep 17 00:00:00 2001 From: STC Date: Sun, 10 May 2026 18:06:45 +0900 Subject: [PATCH] M5StickC and M5StickCplus migrate drives from pins/i2c to Ecma-419 --- .../esp32/targets/m5stick_c/host/provider.js | 92 ++++++++- .../esp32/targets/m5stick_c/manifest.json | 23 +-- .../esp32/targets/m5stick_c/setup-target.js | 184 ++---------------- .../targets/m5stick_cplus/host/provider.js | 68 ++++++- .../esp32/targets/m5stick_cplus/manifest.json | 26 ++- .../targets/m5stick_cplus/setup-target.js | 35 ++++ examples/drivers/m5stickc-axp192/main.js | 24 +-- examples/drivers/m5stickc-imu/main.js | 66 +++---- examples/drivers/m5stickc-pedometer/main.js | 32 +-- examples/drivers/m5stickc-rtc/main.js | 62 +++--- examples/drivers/m5stickc-rtc/manifest.json | 11 +- modules/drivers/peripherals/axp192/axp192.js | 180 +++++++++++++++++ .../drivers/peripherals/axp192/manifest.json | 8 + modules/drivers/sensors/sh200q/manifest.json | 9 + modules/drivers/sensors/sh200q/sh200q.js | 184 ++++++++++++++++++ 15 files changed, 700 insertions(+), 304 deletions(-) create mode 100644 build/devices/esp32/targets/m5stick_cplus/setup-target.js create mode 100644 modules/drivers/peripherals/axp192/axp192.js create mode 100644 modules/drivers/peripherals/axp192/manifest.json create mode 100644 modules/drivers/sensors/sh200q/manifest.json create mode 100644 modules/drivers/sensors/sh200q/sh200q.js diff --git a/build/devices/esp32/targets/m5stick_c/host/provider.js b/build/devices/esp32/targets/m5stick_c/host/provider.js index 322e03b724..ea42177d89 100644 --- a/build/devices/esp32/targets/m5stick_c/host/provider.js +++ b/build/devices/esp32/targets/m5stick_c/host/provider.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Moddable Tech, Inc. + * Copyright (c) 2021-2026 Moddable Tech, Inc. ,Satoshi Tanaka * * This file is part of the Moddable SDK Runtime. * @@ -27,6 +27,11 @@ import PWM from "embedded:io/pwm"; import Serial from "embedded:io/serial"; import SMBus from "embedded:io/smbus"; import SPI from "embedded:io/spi"; +import PulseWidth from "embedded:io/pulsewidth"; +import AXP192 from "embedded:peripheral/Power/axp192"; +import SH200Q from "embedded:sensor/Accelerometer-Gyroscope/SH200Q"; +import MPU6886 from "embedded:sensor/Accelerometer-Gyroscope/MPU6886"; +import RTC from "embedded:RTC/BM8563"; const device = { I2C: { @@ -68,12 +73,95 @@ const device = { pin: 36 } }, - io: {Analog, Digital, DigitalBank, I2C, PulseCount, PWM, Serial, SMBus, SPI}, + io: { Analog, Digital, DigitalBank, I2C, PulseCount, PWM, Serial, SMBus, SPI, PulseWidth }, pin: { button: 37, led: 10, displayDC: 23, displaySelect: 5 + }, + sensor: { + IMU: class { + constructor(options) { + let isMPU6886 = true; + let probe; + try { + probe = new SMBus({ + ...device.I2C.internal, + io: SMBus, + hz: 400_000, + address: 0x68, // MPU6886 + }); + probe.readUint8(0x74); + } catch { + isMPU6886 = false; + } finally { + probe?.close(); + } + + const IMUBase = isMPU6886 ? MPU6886 : SH200Q; + const IMUClass = class extends IMUBase { + sample() { + const result = super.sample(); + result.accelerometer.x *= -1; + result.accelerometer.y *= -1; + result.gyroscope.y *= -1; + return result; + } + }; + + return new IMUClass({ + ...options, + sensor: { + ...device.I2C.internal, + io: device.io.SMBus + } + }); + } + } + }, + peripheral: { + Power: class extends AXP192 { + constructor(options) { + super({ + ...options, + peripheral: { + ...device.I2C.internal, + io: device.io.SMBus + } + }); + this.writeByte(0x10, 0xff); // OLED VPP Enable + this.writeByte(0x28, 0xff); // Enable LDO2&LDO3, LED&TFT 3.3V + this.writeByte(0x82, 0xff); // Enable all the ADCs + this.writeByte(0x33, 0xc0); // Enable Charging, 100mA, 4.2V End at 0.9 + this.writeByte(0xb8, 0x80); // Enable Colume Counter + this.writeByte(0x12, 0x4d); // Enable DC-DC1, OLED VDD, 5B V EXT + this.writeByte(0x36, 0x5c); // PEK + this.writeByte(0x91, 0xA0); // Set MIC voltage to 2.8V + this.writeByte(0x90, 0x02); // gpio0 + } + // value 0 - 100 % + set brightness(value) { + if (value <= 0) + value = 7; + else if (value >= 100) + value = 15; + else + value = (value / 100) * 8 + 7; + this.writeByte(0x28, (this.readByte(0x28) & 0x0F) | (value << 4)); + } + }, + RTC: class { + constructor(options) { + return new RTC({ + ...options, + clock: { + ...device.I2C.internal, + io: SMBus, + }, + }); + } + }, } }; diff --git a/build/devices/esp32/targets/m5stick_c/manifest.json b/build/devices/esp32/targets/m5stick_c/manifest.json index e2e46af543..cc4576db84 100644 --- a/build/devices/esp32/targets/m5stick_c/manifest.json +++ b/build/devices/esp32/targets/m5stick_c/manifest.json @@ -4,19 +4,19 @@ "DEBUGGER_SPEED": "1500000" }, "include": [ - "$(MODDABLE)/modules/drivers/st7735/manifest.json", - "$(MODDABLE)/modules/drivers/axp192/manifest.json", - "$(MODDABLE)/modules/pins/smbus/manifest.json" + "$(MODDABLE)/modules/io/manifest.json", + "$(MODULES)/pins/digital/monitor/manifest.json", + "$(MODULES)/drivers/st7735/manifest.json", + "$(MODULES)/drivers/sensors/mpu6886/manifest.json", + "$(MODULES)/drivers/sensors/sh200q/manifest.json", + "$(MODULES)/drivers/peripherals/axp192/manifest.json", + "$(MODULES)/drivers/peripherals/bm8563/manifest.json" ], "config": { "screen": "st7735", "touch": "" }, "defines": { - "i2c": { - "sda_pin": 21, - "scl_pin": 22 - }, "spi": { "mosi_pin": 15, "sck_pin": 13 @@ -75,19 +75,14 @@ }, "modules": { "*": [ - "$(MODULES)/pins/digital/monitor/*", - "$(MODULES)/pins/digital/monitor/esp32/*", - "$(MODULES)/drivers/mpu6886/*", - "$(MODULES)/drivers/sh200q/*" - + "../m5stack_fire/m5button" ], "setup/target": "./setup-target" }, "preload": [ "monitor", "setup/target", - "mpu6886", - "sh200q" + "m5button" ], "creation": { "static": 0, diff --git a/build/devices/esp32/targets/m5stick_c/setup-target.js b/build/devices/esp32/targets/m5stick_c/setup-target.js index 61e393f38b..df16b44d2d 100644 --- a/build/devices/esp32/targets/m5stick_c/setup-target.js +++ b/build/devices/esp32/targets/m5stick_c/setup-target.js @@ -1,183 +1,37 @@ -import Digital from "pins/digital"; -import Monitor from "monitor"; -import AXP192 from "axp192"; -import SH200Q from "sh200q"; -import MPU6886 from "mpu6886"; -import I2C from "pins/i2c"; -import Timer from "timer"; - +import M5Button from "m5button"; import config from "mc/config"; - -const state = { - handleRotation: nop -}; +import Timer from "timer"; export default function (done) { - global.button = { - a: new Monitor({ - pin: 37, - mode: Digital.InputPullUp, - edge: Monitor.Rising | Monitor.Falling - }), - b: new Monitor({ - pin: 39, - mode: Digital.InputPullUp, - edge: Monitor.Rising | Monitor.Falling - }), + globalThis.button = { + a: new M5Button(37), + b: new M5Button(39) }; - button.a.onChanged = button.b.onChanged = nop; - - global.power = new Power(); - - state.accelerometerGyro = new IMU(); - - global.accelerometer = { - onreading: nop - } - global.gyro = { - onreading: nop - } - - //trace('The Temp:', state.accelerometerGyro.sampleTemp(), '\n'); - - accelerometer.start = function (frequency) { - accelerometer.stop(); - state.accelerometerTimerID = Timer.repeat(id => { - state.accelerometerGyro.configure({ - operation: "accelerometer" - }); - const sample = state.accelerometerGyro.sample(); - if (sample) { - sample.y *= -1; - sample.z *= -1; - state.handleRotation(sample); - accelerometer.onreading(sample); - } - }, frequency); - } - - gyro.start = function (frequency) { - gyro.stop(); - state.gyroTimerID = Timer.repeat(id => { - state.accelerometerGyro.configure({ - operation: "gyroscope" - }); - const sample = state.accelerometerGyro.sample(); - if (sample) { - let { - x, - y, - z - } = sample; - const temp = x; - x = y * -1; - y = temp * -1; - z *= -1; - gyro.onreading({ - x, - y, - z - }); - } - }, frequency); - } - - accelerometer.stop = function () { - if (undefined !== state.accelerometerTimerID) - Timer.clear(state.accelerometerTimerID); - delete state.accelerometerTimerID; - } - - gyro.stop = function () { - if (undefined !== state.gyroTimerID) - Timer.clear(state.gyroTimerID); - delete state.gyroTimerID; - } - - if (config.autorotate && global.Application) { - state.handleRotation = function (reading) { - if (Math.abs(reading.y) > Math.abs(reading.x)) { - if (reading.y < -0.7 && application.rotation != 90) { - application.rotation = 90; - } else if (reading.y > 0.7 && application.rotation != 270) { + globalThis.power = new device.peripheral.Power(); + + if (config.autorotate && globalThis.Application) { + const imu = new device.sensor.IMU(); + Timer.repeat(id => { + const sample = imu.sample(); + const {x, y } = sample.accelerometer; + if (Math.abs(y) > Math.abs(x)) { + if (y < -0.7 && application.rotation !== 270) { application.rotation = 270; + } else if (y > 0.7 && application.rotation !== 90) { + application.rotation = 90; } } else { - if (reading.x < -0.7 && application.rotation != 180) { + if (x < -0.7 && application.rotation !== 180) { application.rotation = 180; - } else if (reading.x > 0.7 && application.rotation != 0) { + } else if (x > 0.7 && application.rotation !== 0) { application.rotation = 0; } } - } - accelerometer.start(300); + }, 300); } done(); } -function nop() {} - -class Power extends AXP192 { - constructor() { - super({ - sda: 21, - scl: 22, - }); - // TODO: Use class method rather than directly accessing register - this.write(0x10, 0xff); // OLED VPP Enable - this.write(0x28, 0xff); // Enable LDO2&LDO3, LED&TFT 3.3V - this.write(0x82, 0xff); // Enable all the ADCs - this.write(0x33, 0xc0); // Enable Charging, 100mA, 4.2V End at 0.9 - this.write(0xb8, 0x80); // Enable Colume Counter - this.write(0x12, 0x4d); // Enable DC-DC1, OLED VDD, 5B V EXT - this.write(0x36, 0x5c); // PEK - this.write(0x91, 0xA0); // Set MIC voltage to 2.8V - this.write(0x90, 0x02); // gpio0 - } - // value 0 - 100 % - set brightness(value) { - if (value <= 0) - value = 7; - else if (value >= 100) - value = 15; - else - value = (value / 100) * 8 + 7; - this.writeByte(0x28, (this.readByte(0x28) & 0x0F) | (value << 4)); - } - - /** - * sets the screen brightness - * @param {*} brightness brightness between 7-15 - * @deprecated Use setter - */ - setBrightness(brightness) { - brightness=Math.floor((brightness-6)*12.5); - trace(`WARNING: AXP192#setBrightness is deprecated. use setter, range now 0-100, now ${brightness}\n`); - this.brightness = brightness; - } - - /** - * @deprecated - */ - initialize() { - trace( - "WARNING: AXP192#initialize is deprecated. no need to initialize explicitly" - ); - } -} - -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; - } -} diff --git a/build/devices/esp32/targets/m5stick_cplus/host/provider.js b/build/devices/esp32/targets/m5stick_cplus/host/provider.js index 4d2f3b408d..e96f37218b 100644 --- a/build/devices/esp32/targets/m5stick_cplus/host/provider.js +++ b/build/devices/esp32/targets/m5stick_cplus/host/provider.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Moddable Tech, Inc. + * Copyright (c) 2021-2026 Moddable Tech, Inc. * * This file is part of the Moddable SDK Runtime. * @@ -28,6 +28,9 @@ import Serial from "embedded:io/serial"; import SMBus from "embedded:io/smbus"; import SPI from "embedded:io/spi"; import PulseWidth from "embedded:io/pulsewidth"; +import AXP192 from "embedded:peripheral/Power/axp192"; +import MPU6886 from "embedded:sensor/Accelerometer-Gyroscope/MPU6886"; +import RTC from "embedded:RTC/BM8563"; const device = { I2C: { @@ -75,6 +78,69 @@ const device = { led: 10, displayDC: 23, displaySelect: 5 + }, + sensor: { + IMU: class extends MPU6886 { + constructor(options) { + super({ + ...options, + sensor: { + ...device.I2C.internal, + io: device.io.SMBus + } + }); + } + sample() { + const sample = super.sample(); + sample.accelerometer.x *= -1; + sample.accelerometer.y *= -1; + sample.gyroscope.y *= -1; + return sample; + } + } + }, + peripheral: { + Power: class extends AXP192 { + constructor(options) { + super({ + ...options, + peripheral:{ + ...device.I2C.internal, + io: device.io.SMBus + } + }); + this.writeByte(0x10, 0xff); // OLED VPP Enable + this.writeByte(0x28, 0xff); // Enable LDO2&LDO3, LED&TFT 3.3V + this.writeByte(0x82, 0xff); // Enable all the ADCs + this.writeByte(0x33, 0xc0); // Enable Charging, 100mA, 4.2V End at 0.9 + this.writeByte(0xb8, 0x80); // Enable Colume Counter + this.writeByte(0x12, 0x4d); // Enable DC-DC1, OLED VDD, 5B V EXT + this.writeByte(0x36, 0x5c); // PEK + this.writeByte(0x91, 0xA0); // Set MIC voltage to 2.8V + this.writeByte(0x90, 0x02); // gpio0 + } + // value 0 - 100 % + set brightness(value) { + if (value <= 0) + value = 7; + else if (value >= 100) + value = 15; + else + value = (value / 100) * 8 + 7; + this.writeByte(0x28, (this.readByte(0x28) & 0x0F) | (value << 4)); + } + }, + RTC: class { + constructor(options) { + return new RTC({ + ...options, + clock: { + ...device.I2C.internal, + io: SMBus, + }, + }); + } + }, } }; diff --git a/build/devices/esp32/targets/m5stick_cplus/manifest.json b/build/devices/esp32/targets/m5stick_cplus/manifest.json index b571f27e85..4fc7310219 100644 --- a/build/devices/esp32/targets/m5stick_cplus/manifest.json +++ b/build/devices/esp32/targets/m5stick_cplus/manifest.json @@ -4,20 +4,20 @@ "DEBUGGER_SPEED": "1500000" }, "include": [ - "$(MODDABLE)/modules/drivers/st7789/manifest.json", - "$(MODDABLE)/modules/drivers/axp192/manifest.json", - "$(MODDABLE)/modules/pins/smbus/manifest.json" + "$(MODDABLE)/modules/io/manifest.json", + "$(MODULES)/pins/digital/monitor/manifest.json", + "$(MODULES)/drivers/st7789/manifest.json", + "$(MODULES)/drivers/sensors/mpu6886/manifest.json", + "$(MODULES)/drivers/peripherals/axp192/manifest.json", + "$(MODULES)/drivers/peripherals/bm8563/manifest.json" ], "config": { "screen": "st7789", "touch": "", - "rotation": 270 + "rotation": 270, + "autorotate": true }, "defines": { - "i2c": { - "sda_pin": 21, - "scl_pin": 22 - }, "spi": { "mosi_pin": 15, "sck_pin": 13 @@ -75,18 +75,14 @@ }, "modules": { "*": [ - "$(MODULES)/pins/digital/monitor/*", - "$(MODULES)/pins/digital/monitor/esp32/*", - "$(MODULES)/drivers/mpu6886/*", - "$(MODULES)/drivers/sh200q/*" + "../m5stack_fire/m5button" ], - "setup/target": "../m5stick_c/setup-target" + "setup/target": "./setup-target" }, "preload": [ "monitor", "setup/target", - "mpu6886", - "sh200q" + "m5button" ], "creation": { "static": 0, diff --git a/build/devices/esp32/targets/m5stick_cplus/setup-target.js b/build/devices/esp32/targets/m5stick_cplus/setup-target.js new file mode 100644 index 0000000000..52b60c2645 --- /dev/null +++ b/build/devices/esp32/targets/m5stick_cplus/setup-target.js @@ -0,0 +1,35 @@ +import M5Button from "m5button"; +import config from "mc/config"; +import Timer from "timer"; + +export default function (done) { + globalThis.button = { + a: new M5Button(37), + b: new M5Button(39) + }; + + globalThis.power = new device.peripheral.Power(); + + if (config.autorotate && globalThis.Application) { + const imu = new device.sensor.IMU(); + Timer.repeat(id => { + const sample = imu.sample(); + const {x, y } = sample.accelerometer; + if (Math.abs(y) > Math.abs(x)) { + if (y < -0.7 && application.rotation !== 270) { + application.rotation = 270; + } else if (y > 0.7 && application.rotation !== 90) { + application.rotation = 90; + } + } else { + if (x < -0.7 && application.rotation !== 180) { + application.rotation = 180; + } else if (x > 0.7 && application.rotation !== 0) { + application.rotation = 0; + } + } + }, 300); + } + + done(); +} diff --git a/examples/drivers/m5stickc-axp192/main.js b/examples/drivers/m5stickc-axp192/main.js index 3d7def294c..1681f1574b 100644 --- a/examples/drivers/m5stickc-axp192/main.js +++ b/examples/drivers/m5stickc-axp192/main.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Moddable Tech, Inc. + * Copyright (c) 2016-2026 Moddable Tech, Inc. , Satoshi Tanaka * * This file is part of the Moddable SDK. * @@ -18,21 +18,21 @@ import Poco from "commodetto/Poco"; import Resource from "Resource"; import config from "mc/config"; -let render = new Poco(screen, {rotation:config?.rotation}); +const render = new Poco(screen, {rotation:config?.rotation}); -let white = render.makeColor(255, 255, 255); -let blue = render.makeColor(0, 0, 255); +const white = render.makeColor(255, 255, 255); +const blue = render.makeColor(0, 0, 255); -let font = parseBMF(new Resource("OpenSans-Semibold-18.bf4")); -let text = "Press button A to change brightness... "; -let textWidth = render.getTextWidth(text, font); +const font = parseBMF(new Resource("OpenSans-Semibold-18.bf4")); +const text = "Press button A to change brightness... "; +const textWidth = render.getTextWidth(text, font); let x = render.width; let y = (render.height - font.height) >> 1; -let loop = true; // set false to scroll text once across the screen +const loop = true; // set false to scroll text once across the screen let brightness = 50; // screen brightness % -button.a.onChanged = function() { - if (button.a.read()) { +globalThis.button.a.onChanged = () =>{ + if (globalThis.button.a.read()) { return; } brightness+=10; @@ -51,14 +51,14 @@ Timer.repeat(id => { render.fillRectangle(blue, 0, 0, render.width, render.height); render.drawText(text, font, white, x, y); if (!loop) { - if (x + textWidth == 0) + if (x + textWidth === 0) Timer.clear(id); } else { if (x + textWidth < render.width) render.drawText(text, font, white, x + textWidth, y); } - if (x + textWidth == 0) + if (x + textWidth === 0) x = 0; else --x; diff --git a/examples/drivers/m5stickc-imu/main.js b/examples/drivers/m5stickc-imu/main.js index e4b8e3b166..5d06892792 100644 --- a/examples/drivers/m5stickc-imu/main.js +++ b/examples/drivers/m5stickc-imu/main.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2022 Moddable Tech, Inc. + * Copyright (c) 2016-2026 Moddable Tech, Inc. , Satoshi Tanaka * * This file is part of the Moddable SDK. * @@ -17,15 +17,16 @@ import parseBMP from "commodetto/parseBMP"; import Poco from "commodetto/Poco"; import Resource from "Resource"; import config from "mc/config"; +import Timer from "timer"; const GYRO_SCALER = 0.002; -let render = new Poco(screen, {rotation: config?.rotation ?? screen.rotation}); +const render = new Poco(screen, {rotation: config?.rotation ?? screen.rotation}); const width = render.width, height = render.height; -let font = parseBMF(new Resource("OpenSans-Semibold-16.bf4")); +const font = parseBMF(new Resource("OpenSans-Semibold-16.bf4")); -let ball = parseBMP(new Resource("ball-color.bmp")); +const ball = parseBMP(new Resource("ball-color.bmp")); ball.alpha = parseBMP(new Resource("ball-alpha.bmp")); ball.x = width >> 1; ball.y = height >> 1; @@ -43,49 +44,48 @@ render.begin(); render.fillRectangle(ball.backgroundColor, 0, ball.yMin, width, height); render.end(); -accelerometer.onreading = function(values){ - onReading(values, "a"); -} - -gyro.onreading = function(values){ - let {x, y, z} = values; - x *= GYRO_SCALER; - y *= GYRO_SCALER; - z *= GYRO_SCALER; - onReading({x,y,z}, "g"); -} +const imu = new device.sensor.IMU() +imu.configure({ + "GYRO_SCALER": (2000.0 / 32768.0) * GYRO_SCALER +}) + +Timer.repeat(() => { + const sample = imu.sample(); + if(flag) { + onReading(sample.accelerometer, "a"); + } else { + onReading(sample.gyroscope, "g"); + } +}, 17) -accelerometer.start(17); +let flag = true; -button.a.onChanged = function(){ - let value = button.a.read(); +globalThis.button.a.onChanged = () =>{ + const value = globalThis.button.a.read(); if (value) { - accelerometer.stop(); - gyro.stop(); - accelerometer.start(17); + flag = true } } -button.b.onChanged = function(){ - let value = button.b.read(); - if (value){ - accelerometer.stop(); - gyro.stop(); - gyro.start(17); +globalThis.button.b.onChanged = () =>{ + const value = globalThis.button.b.read(); + if (value) { + flag = false } } function onReading(values, labelPrefix){ - let { x, y, z } = values; + const { x, y, z } = values; + trace(`${labelPrefix}X: ${formatValue(x)} - ${labelPrefix}Y: ${formatValue(y)} - ${labelPrefix}Z: ${formatValue(z)}\n`); render.begin(0, 0, width, ball.yMin); render.fillRectangle(backgroundColor, 0, 0, width, height); - drawBar(labelPrefix + "X", x, 0, 0, width, font.height); - drawBar(labelPrefix + "Y", y, 0, font.height, width, font.height); - drawBar(labelPrefix + "Z", z, 0, font.height * 2, width, font.height); + drawBar(`${labelPrefix}X`, x, 0, 0, width, font.height); + drawBar(`${labelPrefix}Y`, y, 0, font.height, width, font.height); + drawBar(`${labelPrefix}Z`, z, 0, font.height * 2, width, font.height); render.end(); - ball.vx = (ball.vx - x) * 0.98; + ball.vx = (ball.vx + x) * 0.98; ball.vy = (ball.vy - y) * 0.98; let nx = ball.x + ball.vx; let ny = ball.y + ball.vy; @@ -114,7 +114,7 @@ function formatValue(value) { return value; if (value < 0) return value.toFixed(3); - return "+" + value.toFixed(3); + return `+${value.toFixed(3)}`; } function drawBar(label, value, x, y, width, height) { diff --git a/examples/drivers/m5stickc-pedometer/main.js b/examples/drivers/m5stickc-pedometer/main.js index 5c1ea47ddc..09e8e787e3 100644 --- a/examples/drivers/m5stickc-pedometer/main.js +++ b/examples/drivers/m5stickc-pedometer/main.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Moddable Tech, Inc. + * Copyright (c) 2022-2026 Moddable Tech, Inc., Satoshi Tanaka * * This file is part of the Moddable SDK. * @@ -24,7 +24,7 @@ const STARTING_STEPS = 123; const GOAL = 200; const SHAKEVALUE = 3; -let render = new Poco(screen, {rotation: config.rotation}); +const render = new Poco(screen, {rotation: config.rotation}); const black = render.makeColor(0, 0, 0); const white = render.makeColor(255, 255, 255); @@ -59,7 +59,7 @@ function redraw() { let offset = (render.width-32)/2; render.drawGray(steps, stepColor, 20, 7); render.fillRectangle(stepColor, 10, render.height-17, render.width-20, render.height-17); - let fraction = stepCount / GOAL; + const fraction = stepCount / GOAL; render.fillRectangle(barColor, 10, render.height-17, (render.width-20)*fraction, render.height-17); render.drawGray(progressOverlay, backgroundColor, 0, render.height-17); offset = (render.width-(render.getTextWidth(stepCount, bigFont)))/2; @@ -69,30 +69,31 @@ function redraw() { } -accelerometer.oldData = {x: 0, y: 0, z: 0, init: false}; -accelerometer.onreading = function(values) { +const imu = new device.sensor.IMU(); +let oldData = {x: 0, y: 0, z: 0, init: false}; +function onReading(values) { const {x,y,z} = values; - if (this.oldData.init){ - const delta = Math.abs(x - this.oldData.x) + Math.abs(y - this.oldData.y) + Math.abs(z - this.oldData.z); + if (oldData.init){ + const delta = Math.abs(x - oldData.x) + Math.abs(y - oldData.y) + Math.abs(z - oldData.z); if (delta > SHAKEVALUE) { stepCount++; redraw(); } } - this.oldData.x = x; - this.oldData.y = y; - this.oldData.z = z; - this.oldData.init = true; + oldData.x = x; + oldData.y = y; + oldData.z = z; + oldData.init = true; } -button.a.onChanged = function() { +globalThis.button.a.onChanged = function() { if (this.read()) { lightMode = !lightMode; redraw(); } } -button.b.onChanged = function() { +globalThis.button.b.onChanged = function() { if (this.read()) { stepCount = 0; redraw(); @@ -100,4 +101,7 @@ button.b.onChanged = function() { } redraw(); -accelerometer.start(17); +Timer.repeat(() => { + const sample = imu.sample(); + onReading(sample.accelerometer) +}, 17) diff --git a/examples/drivers/m5stickc-rtc/main.js b/examples/drivers/m5stickc-rtc/main.js index 46da9f59ba..2e0bd213ce 100644 --- a/examples/drivers/m5stickc-rtc/main.js +++ b/examples/drivers/m5stickc-rtc/main.js @@ -12,43 +12,29 @@ * */ -import BM8563 from "bm8563"; import Time from "time"; +import Timer from "timer"; +import parseBMF from "commodetto/parseBMF"; +import Poco from "commodetto/Poco"; +import Resource from "Resource"; +import config from "mc/config"; +const rtc = new device.peripheral.RTC(); -let rtc = new BM8563; -let enabled = 1; - -// Main button: enable/disable RTC -button.a.onChanged = function () { - if (button.a.read()) { - return; - } - enabled = !enabled; - rtc.enabled = enabled; - globalThis.power.brightness = 20 + enabled * 70; -} - -// Side button: set time from sntp -button.b.onChanged = function () { - if (button.b.read()) { +// Main button: set time from sntp +globalThis.button.a.onChanged = () => { +if (globalThis.button.a.read()) { return; } setRTCTimeLocal(); } -import Timer from "timer"; -import parseBMF from "commodetto/parseBMF"; -import Poco from "commodetto/Poco"; -import Resource from "Resource"; -import config from "mc/config"; - function setRTCTimeLocal() { - let d = new Date(); + const d = new Date(); trace(`Set time: ${d.getTime()} ${d.toString()} tz:${Time.timezone} dst:${Time.dst}\n`); - rtc.seconds = d.getTime() / 1000; - let read = rtc.seconds; - let s = new Date(read * 1000); + rtc.time = d.getTime(); + const read = rtc.time; + const s = new Date(read); trace(`Get time: ${read} ${s.toString()}\n`) } @@ -56,29 +42,27 @@ const render = new Poco(screen, { rotation: config.rotation }); -let white = render.makeColor(255, 255, 255); -let grey = render.makeColor(170, 170, 170); -let blue = render.makeColor(0, 0, 255); +const white = render.makeColor(255, 255, 255); +const grey = render.makeColor(170, 170, 170); +const blue = render.makeColor(0, 0, 255); -let font = parseBMF(new Resource("OpenSans-Semibold-16.bf4")); -let text = "Press button A to set Time"; -let textWidth = render.getTextWidth(text, font); -let x = 1; -let y = 1; +const font = parseBMF(new Resource("OpenSans-Semibold-16.bf4")); +const x = 1; +const y = 1; render.begin(); render.fillRectangle(blue, 0, 0, render.width, render.height); render.end(); -Timer.repeat(id => { +Timer.repeat(_id => { let now = 'setting..'; try { - now = rtc.seconds; + now = rtc.time; } catch (e) { trace(e); } - let rtc_clock = new Date(now * 1000); - let actual_clock = new Date(); + const rtc_clock = new Date(now); + const actual_clock = new Date(); render.begin(0, y, render.width, render.height); render.fillRectangle(blue, 0, 0, render.width, render.height); render.drawText(rtc_clock.toString().slice(4, 24), font, white, x, y); diff --git a/examples/drivers/m5stickc-rtc/manifest.json b/examples/drivers/m5stickc-rtc/manifest.json index 7dec6a3df4..f940821939 100644 --- a/examples/drivers/m5stickc-rtc/manifest.json +++ b/examples/drivers/m5stickc-rtc/manifest.json @@ -4,15 +4,11 @@ }, "include": [ "$(MODDABLE)/examples/manifest_base.json", - "$(MODDABLE)/examples/manifest_net.json", - "$(MODULES)/pins/i2c/manifest.json", + "$(MODDABLE)/examples/manifest_net.json", "$(MODDABLE)/examples/manifest_commodetto.json" ], "modules": { - "pins/smbus": "$(MODULES)/pins/smbus/smbus", "*": [ - "$(MODULES)/drivers/rtc/rtc", - "$(MODULES)/drivers/rtc/bm8563", "$(MODULES)/network/sntp/*", "./main" ] @@ -21,10 +17,7 @@ "*-mask": "$(MODDABLE)/examples/assets/fonts/OpenSans-Semibold-16" }, "preload": [ - "sntp", - "bm8563", - "rtc", - "smbus" + "sntp" ], "platforms": { "esp32/m5stick_c" :{ diff --git a/modules/drivers/peripherals/axp192/axp192.js b/modules/drivers/peripherals/axp192/axp192.js new file mode 100644 index 0000000000..325a200113 --- /dev/null +++ b/modules/drivers/peripherals/axp192/axp192.js @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2019-2026 Shinya Ishikawa, Satoshi Tanaka + * + * This file is part of the Moddable SDK Runtime. + * + * The Moddable SDK Runtime is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Moddable SDK Runtime is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the Moddable SDK Runtime. If not, see . + * + */ + +class DCDC { + #parent; + #register; + constructor({ register, parent }) { + this.#parent = parent; + this.#register = register; + } + set voltage(v) { + const vdata = v < 700 ? 0 : (v - 700) / 25; + this.#parent.writeByte( + this.#register, + (this.#parent.readByte(this.#register) & 0x80) | (vdata & 0x7f) + ); + } + get voltage() { + return (this.#parent.readByte(this.#register) & 0x7f) * 25 + 700; + } +} + +class LDO { + #parent; + #register; + #offsetV; + #offsetEn; + constructor({ register, parent, offsetV, offsetEn }) { + this.#parent = parent; + this.#register = register; + this.#offsetV = offsetV; + this.#offsetEn = offsetEn; + } + + set voltage(v) { + const vdata = v > 3300 ? 15 : v / 100 - 18; + const mask = ~(0xff << this.#offsetV); + this.#parent.writeByte( + this.#register, + (this.#parent.readByte(this.#register) & mask) | (vdata << this.#offsetV) + ); + } + + get voltage() { + return ( + ((this.#parent.readByte(this.#register) >> this.#offsetV) + 18) * 100 + ); + } + + set enable(enable) { + const mask = 0x01 << this.#offsetEn; + if (enable) { + this.#parent.writeByte(0x12, this.#parent.readByte(0x12) | mask); + } else { + this.#parent.writeByte(0x12, this.#parent.readByte(0x12) & ~mask); + } + } + + get enable() { + return Boolean((this.#parent.readByte(0x12) >> this.#offsetEn) & 1); + } +} + +class GPIO { + #register; + #parent; + #mask; + constructor({ register, parent, offset }) { + this.#parent = parent; + this.#register = register; + this.#mask = 0x01 << offset; + } + + get enable() { + return Boolean(this.#parent.readByte(this.#register) & this.#mask); + } + + set enable(enable) { + let data = this.#parent.readByte(this.#register); + if (enable) { + data |= this.#mask; + } else { + data &= ~this.#mask; + } + this.#parent.writeByte(this.#register, data); + } +} + +export default class AXP192 { + #io; + constructor(options) { + this.#io = new options.peripheral.io({ + hz: 400_000, + address: 0x34, + ...options.peripheral, + }); + + this._dcdc1 = new DCDC({ register: 0x26, parent: this }); + this._dcdc2 = new DCDC({ register: 0x23, parent: this }); + this._dcdc3 = new DCDC({ register: 0x27, parent: this }); + this._ldo2 = new LDO({ + register: 0x34, + parent: this, + offsetV: 4, + offsetEn: 2, + }); + this._ldo3 = new LDO({ + register: 0x34, + parent: this, + offsetV: 0, + offsetEn: 3, + }); + this._gpio0 = new GPIO({ register: 0x94, parent: this, offset: 0 }); + this._gpio1 = new GPIO({ register: 0x94, parent: this, offset: 1 }); + this._gpio2 = new GPIO({ register: 0x94, parent: this, offset: 2 }); + this._gpio3 = new GPIO({ register: 0x96, parent: this, offset: 0 }); + this._gpio4 = new GPIO({ register: 0x96, parent: this, offset: 1 }); + } + + readByte(address) { + return this.#io.readUint8(address); + } + + writeByte(address, value) { + return this.#io.writeUint8(address, value); + } + + set chargeCurrent(state) { + this.writeByte(0x33, (this.readByte(0x33) & 0xf0) | (state & 0x0f)); + } + get batteryVoltage() { + let data = this.readByte(0x78) << 4; + data |= this.readByte(0x79); + return data * 1.1 / 1000; + } + get batteryCurrent() { + let currentIn = this.readByte(0x7a) << 5; + currentIn |= this.readByte(0x7b); + let currentOut = this.readByte(0x7c) << 5; + currentOut |= this.readByte(0x7d); + + return (currentIn - currentOut) * 0.5; + } +} + +AXP192.CHARGE_CURRENT = { + Ch_100mA: 0b0000, + Ch_190mA: 0b0001, + Ch_280mA: 0b0010, + Ch_360mA: 0b0011, + Ch_450mA: 0b0100, + Ch_550mA: 0b0101, + Ch_630mA: 0b0110, + Ch_700mA: 0b0111, + Ch_780mA: 0b1000, + Ch_880mA: 0b1001, + Ch_960mA: 0b1010, + Ch_1000mA: 0b1011, + Ch_1080mA: 0b1100, + Ch_1160mA: 0b1101, + Ch_1240mA: 0b1110, + Ch_1320mA: 0b1111, +}; diff --git a/modules/drivers/peripherals/axp192/manifest.json b/modules/drivers/peripherals/axp192/manifest.json new file mode 100644 index 0000000000..553d194599 --- /dev/null +++ b/modules/drivers/peripherals/axp192/manifest.json @@ -0,0 +1,8 @@ +{ + "modules": { + "embedded:peripheral/Power/axp192": "./axp192" + }, + "preload": [ + "embedded:peripheral/Power/axp192" + ] +} diff --git a/modules/drivers/sensors/sh200q/manifest.json b/modules/drivers/sensors/sh200q/manifest.json new file mode 100644 index 0000000000..5df9585837 --- /dev/null +++ b/modules/drivers/sensors/sh200q/manifest.json @@ -0,0 +1,9 @@ +{ + "modules": { + "embedded:sensor/Accelerometer-Gyroscope/SH200Q": "$(MODDABLE)/modules/drivers/sensors/sh200q/sh200q" + }, + "preload": [ + "embedded:sensor/Accelerometer-Gyroscope/SH200Q" + ] +} + diff --git a/modules/drivers/sensors/sh200q/sh200q.js b/modules/drivers/sensors/sh200q/sh200q.js new file mode 100644 index 0000000000..006be6f2f1 --- /dev/null +++ b/modules/drivers/sensors/sh200q/sh200q.js @@ -0,0 +1,184 @@ +/* + * Copyright (c) Wilberforce + + * Copyright (c) 2019-2020 Moddable Tech, Inc. + * + * This file is part of the Moddable SDK Runtime. + * + * The Moddable SDK Runtime is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Moddable SDK Runtime is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the Moddable SDK Runtime. If not, see . + * + */ +/* + sh200q Accelerometer + Gyro + Datasheet: http://senodia.com/Uploads/Product/5b2b6ef1216e8.pdf + Register Map: https://github.com/m5stack/M5StickC/blob/master/src/utility/SH200Q.h#L7-L22 + https://github.com/m5stack/M5StickC/blob/master/src/utility/SH200Q.cpp + +*/ + +import Timer from "timer"; + +const REGISTERS = { + ACCEL_XOUT: 0x00, + TEMP_OUT: 0x0C, + GYRO_XOUT: 0x06, + WHO_AM_I: 0x30, + + ACC_CONFIG: 0x0E, + GYRO_CONFIG: 0x0F, + GYRO_DLPF: 0x11, + FIFO_CONFIG: 0x12, + ACC_RANGE: 0x16, + GYRO_RANGE: 0x2B, + OUTPUT_ACC: 0x00, + OUTPUT_GYRO: 0x06, + OUTPUT_TEMP: 0x0C, + REG_SET1: 0xBA, + REG_SET2: 0xCA, //ADC reset + ADC_RESET: 0xC2, //drive reset + SOFT_RESET: 0x7F, + RESET: 0x75 +}; +Object.freeze(REGISTERS); + +const EXPECTED_WHO_AM_I = 0x18; + +const GYRO_SCALER = { + GFS_125DPS: (125.0 / 32768.0), + GFS_250DPS: (250.0 / 32768.0), + GFS_500DPS: (500.0 / 32768.0), + GFS_1000DPS: (1000.0 / 32768.0), + GFS_2000DPS: (2000.0 / 32768.0) +}; +Object.freeze(GYRO_SCALER); + +const ACCEL_SCALER = { + AFS_2G: (2.0 / 32768.0), + AFS_4G: (4.0 / 32768.0), + AFS_8G: (8.0 / 32768.0), + AFS_16G: (16.0 / 32768.0) +}; +Object.freeze(ACCEL_SCALER); + +class SH200Q { + #io; + #view; + #gyroScale = GYRO_SCALER.GFS_2000DPS; + #accelScale = ACCEL_SCALER.AFS_8G; + + constructor(options) { + const io = this.#io = new options.sensor.io({ + hz: 400_000, + address: 0x6C, + ...options.sensor + }); + + this.#view = new DataView(new ArrayBuffer(6)); + + if (io.readUint8(REGISTERS.WHO_AM_I) !== EXPECTED_WHO_AM_I) + throw new Error("unrecognized"); + + this.enable(); + } + + close() { + this.#io?.close(); + this.#io = undefined; + } + + configure(dictionary) { + for (const property in dictionary) { + switch (property) { + case "GYRO_SCALER": + this.#gyroScale = dictionary.GYRO_SCALER; + break; + case "ACCEL_SCALER": + this.#accelScale = dictionary.ACCEL_SCALER; + break; + } + } + } + + enable() { + const io = this.#io; + + Timer.delay(1); + + // set acc odr 256hz + // 0x81 1024hz //0x89 512hz //0x91 256hz + io.writeUint8(REGISTERS.ACC_CONFIG, 0x91); + + // set gyro odr 500hz + // 0x11 1000hz //0x13 500hz //0x15 256hz + io.writeUint8(REGISTERS.GYRO_CONFIG, 0x13); + + // set gyro dlpf 50hz + // 0x00 250hz //0x01 200hz //0x02 100hz //0x03 50hz //0x04 25hz + io.writeUint8(REGISTERS.GYRO_DLPF, 0x03); + + // set no buffer mode + io.writeUint8(REGISTERS.FIFO_CONFIG, 0x00); + + // set acc range +-8G + io.writeUint8(REGISTERS.ACC_RANGE, 0x01); + + // set gyro range +-2000/s + io.writeUint8(REGISTERS.GYRO_RANGE, 0x00); + + io.writeUint8(REGISTERS.REG_SET1, 0xC0); + + // ADC Reset + const temp = io.readUint8(REGISTERS.REG_SET2); + io.writeUint8(REGISTERS.REG_SET2, temp | 0x10); + + Timer.delay(1); + + io.writeUint8(REGISTERS.REG_SET2, temp & 0xEF); + + Timer.delay(10); + } + + sample() { + const io = this.#io; + const view = this.#view; + + io.readBuffer(REGISTERS.ACCEL_XOUT, view); + const accelerometer = { + x: view.getInt16(0) * this.#accelScale, + y: view.getInt16(2) * this.#accelScale, + z: view.getInt16(4) * this.#accelScale + }; + + io.readBuffer(REGISTERS.GYRO_XOUT, view); + const gyroscope = { + x: view.getInt16(0) * this.#gyroScale, + y: view.getInt16(2) * this.#gyroScale, + z: view.getInt16(4) * this.#gyroScale + }; + + io.readBuffer(REGISTERS.TEMP_OUT, 2, view.buffer); + const thermometer = { + temperature: view.getInt16(0, true) / 333.87 + 21.0 + }; + + return { + accelerometer, + gyroscope, + thermometer + }; + } +} +Object.freeze(SH200Q.prototype); + +export default SH200Q;