diff --git a/build/devices/pico/targets/ws_round/host/provider.js b/build/devices/pico/targets/ws_round/host/provider.js
index 479c62ec37..91f216dd9d 100644
--- a/build/devices/pico/targets/ws_round/host/provider.js
+++ b/build/devices/pico/targets/ws_round/host/provider.js
@@ -26,6 +26,7 @@ import PulseCount from "embedded:io/pulsecount";
import PWM from "embedded:io/pwm";
import SMBus from "embedded:io/smbus";
import SPI from "embedded:io/spi";
+import QMI8658 from "embedded:sensor/Accelerometer-Gyroscope/QMI8658";
class Backlight {
#io;
@@ -92,6 +93,27 @@ const device = {
return new Backlight({pin: device.pin.backlight });
}
}
+ },
+ sensor: {
+ IMU: class extends QMI8658 {
+ constructor(options) {
+ super({
+ ...options,
+ sensor: {
+ ...device.I2C.default,
+ io: device.io.SMBus
+ }
+ });
+ }
+ sample() {
+ const sample = super.sample();
+ [sample.accelerometer.x, sample.accelerometer.y] = [sample.accelerometer.y, sample.accelerometer.x];
+ sample.accelerometer.z *= -1;
+ [sample.gyroscope.x, sample.gyroscope.y] = [sample.gyroscope.y, sample.gyroscope.x];
+ sample.gyroscope.z *= -1;
+ return sample;
+ }
+ }
}
};
diff --git a/build/devices/pico/targets/ws_round/manifest.json b/build/devices/pico/targets/ws_round/manifest.json
index e848870169..3fd7abf0db 100644
--- a/build/devices/pico/targets/ws_round/manifest.json
+++ b/build/devices/pico/targets/ws_round/manifest.json
@@ -2,9 +2,7 @@
"include": [
"$(MODDABLE)/modules/io/manifest.json",
"$(MODDABLE)/modules/drivers/gc9a01/manifest.json",
- "$(MODULES)/drivers/led/manifest.json",
- "$(MODULES)/drivers/button/manifest.json",
- "$(MODULES)/pins/pwm/manifest.json"
+ "$(MODDABLE)/modules/drivers/sensors/qmi8658/manifest.json"
],
"modules": {
"setup/target": "./setup-target"
@@ -19,11 +17,6 @@
"lcd_cs_pin": 9
},
"defines": {
- "i2c": {
- "sda_pin": 6,
- "scl_pin": 7,
- "port": 1
- },
"spi": {
"mosi_pin": 11,
"sck_pin": 10
diff --git a/build/devices/pico/targets/ws_round/setup-target.js b/build/devices/pico/targets/ws_round/setup-target.js
index 7de3913958..b6c11db6ad 100644
--- a/build/devices/pico/targets/ws_round/setup-target.js
+++ b/build/devices/pico/targets/ws_round/setup-target.js
@@ -1,45 +1,20 @@
-import Digital from "pins/digital";
import config from "mc/config";
import Timer from "timer";
-import PWM from "pins/pwm";
-
-class Backlight extends PWM {
- constructor(brightness = 100) {
- super({pin: config.backlight});
- this.write(brightness);
- }
- write(value) {
- if (value <= 0)
- value = 0;
- else if (value >= 100)
- value = 1023;
- else
- value = (value / 100) * 1023;
- super.write(value);
- }
-}
-
-globalThis.Host = Object.freeze({
- Backlight
-}, true);
export default function (done) {
- if ((undefined == config.brightness) || ("none" === config.brightness))
- Digital.write(config.backlight, 0);
- else if ("off" === config.backlight)
- Digital.write(config.backlight, 1);
- else {
- globalThis.backlight = new device.peripheral.Backlight;
- backlight.brightness = parseInt(config.brigtness) / 100;
- }
+ const Digital = device.io.Digital;
+
+ const displayResetPin = new Digital({ pin: config.lcd_rst_pin, mode: Digital.Output });
+ const displayCSPin = new Digital({ pin: config.lcd_cs_pin, mode: Digital.Output });
- Digital.write(config.lcd_rst_pin, 1);
+ displayResetPin.write(1);
Timer.delay(100);
- Digital.write(config.lcd_rst_pin, 0);
+ displayResetPin.write(0);
Timer.delay(100);
- Digital.write(config.lcd_rst_pin, 0);
- Digital.write(config.lcd_cs_pin, 0);
+ displayCSPin.write(0);
Timer.delay(100);
+ displayResetPin.close();
+ displayCSPin.close();
done();
}
diff --git a/build/devices/pico/targets/ws_round_touch/host/provider.js b/build/devices/pico/targets/ws_round_touch/host/provider.js
index e7b6d04b06..fae6c42c9b 100644
--- a/build/devices/pico/targets/ws_round_touch/host/provider.js
+++ b/build/devices/pico/targets/ws_round_touch/host/provider.js
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022-2025 Moddable Tech, Inc.
+ * Copyright (c) 2022-2026 Moddable Tech, Inc.
*
* This file is part of the Moddable SDK Runtime.
*
@@ -27,6 +27,7 @@ import PWM from "embedded:io/pwm";
import SMBus from "embedded:io/smbus";
import SPI from "embedded:io/spi";
import Touch from "embedded:sensor/Touch/CST816";
+import QMI8658 from "embedded:sensor/Accelerometer-Gyroscope/QMI8658";
class Backlight {
#io;
@@ -117,6 +118,25 @@ const device = {
result.configure({ });
return result;
}
+ },
+ IMU: class extends QMI8658 {
+ constructor(options) {
+ super({
+ ...options,
+ sensor: {
+ ...device.I2C.default,
+ io: device.io.SMBus
+ }
+ });
+ }
+ sample() {
+ const sample = super.sample();
+ [sample.accelerometer.x, sample.accelerometer.y] = [sample.accelerometer.y, sample.accelerometer.x];
+ sample.accelerometer.z *= -1;
+ [sample.gyroscope.x, sample.gyroscope.y] = [sample.gyroscope.y, sample.gyroscope.x];
+ sample.gyroscope.z *= -1;
+ return sample;
+ }
}
}
};
diff --git a/build/devices/pico/targets/ws_round_touch/manifest.json b/build/devices/pico/targets/ws_round_touch/manifest.json
index 86d49d7a98..b1297660e2 100644
--- a/build/devices/pico/targets/ws_round_touch/manifest.json
+++ b/build/devices/pico/targets/ws_round_touch/manifest.json
@@ -1,10 +1,8 @@
{
"include": [
"$(MODDABLE)/modules/io/manifest.json",
- "$(MODDABLE)/modules/drivers/gc9a01/manifest.json",
- "$(MODULES)/drivers/led/manifest.json",
- "$(MODULES)/drivers/button/manifest.json",
- "$(MODULES)/pins/pwm/manifest.json",
+ "$(MODULES)/drivers/gc9a01/manifest.json",
+ "$(MODULES)/drivers/sensors/qmi8658/manifest.json",
"$(MODULES)/drivers/sensors/cst816/manifest.json"
],
"modules": {
@@ -20,11 +18,6 @@
"lcd_cs_pin": 9
},
"defines": {
- "i2c": {
- "sda_pin": 26,
- "scl_pin": 27,
- "port": 1
- },
"spi": {
"mosi_pin": 11,
"sck_pin": 10
diff --git a/documentation/devices/pico.md b/documentation/devices/pico.md
index 8f1cd0f835..032ea26cb6 100644
--- a/documentation/devices/pico.md
+++ b/documentation/devices/pico.md
@@ -88,7 +88,7 @@ The Moddable SDK supports devices built with the Pico. The following table lists
| 
Adafruit
QT Py | `pico/qtpy` | STEMMA/Qwiic connector, Neopixel, 1 button |
[Adafruit product page](https://www.adafruit.com/product/4900) |
| 
Adafruit
Trinkey QT2040 | `pico/qt_trinkey` | STEMMA/Qwiic connector, Neopixel, 1 button | [Adafruit product page](https://www.adafruit.com/product/5056) |
| 
Pimoroni
Tiny 2040 | `pico/tiny2040` | RGB LED, 1 button| [Pimoroni product page](https://shop.pimoroni.com/products/tiny-2040?variant=39560012234835) |
-| 
WAVESHARE
1.28inch Round LCD | `pico/ws_round`
`pico/ws_round_touch` | 1.28" IPS 240×240 Round Display| [WAVESHARE product page](https://www.waveshare.com/rp2040-lcd-1.28.htm)[touch LCD version](https://www.waveshare.com/product/rp2040-touch-lcd-1.28.htm) |
+| 
WAVESHARE
1.28inch Round LCD | `pico/ws_round`
`pico/ws_round_touch` | 1.28" IPS 240×240 Round Display
IMU| [WAVESHARE product page](https://www.waveshare.com/rp2040-lcd-1.28.htm)[touch LCD version](https://www.waveshare.com/product/rp2040-touch-lcd-1.28.htm) |
| 
Seeed Studio
XIAO RP2040 | `pico/xiao_rp2040` | Neopixel | [Seeed Studio product page](https://www.seeedstudio.com/XIAO-RP2040-v1-0-p-5026.html) |
| 
ili9341 | `pico/xiao_ili9341` | ili9341 QVGA display
320 x 240
16-bit color | [Wiring Guide - Pico](../displays/images/xiao-qtpy-ili9341-wiring.png) |
| 
ili9341 | `pico/ili9341` | ili9341 QVGA display
320 x 240
16-bit color | [Generic 2.4" & 2.8" Displays (Resistive Touch) Wiring Guide - Pico](../displays/wiring-guide-generic-2.4-spi-pico.md) |
diff --git a/examples/drivers/sensors/qmi8658/bounce/ball.png b/examples/drivers/sensors/qmi8658/bounce/ball.png
new file mode 100644
index 0000000000..e0d0f0cca6
Binary files /dev/null and b/examples/drivers/sensors/qmi8658/bounce/ball.png differ
diff --git a/examples/drivers/sensors/qmi8658/bounce/main.js b/examples/drivers/sensors/qmi8658/bounce/main.js
new file mode 100644
index 0000000000..ff94d6f6ef
--- /dev/null
+++ b/examples/drivers/sensors/qmi8658/bounce/main.js
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016-2017 Moddable Tech, Inc.
+ *
+ * This file is part of the Moddable SDK.
+ *
+ * This work is licensed under the
+ * Creative Commons Attribution 4.0 International License.
+ * To view a copy of this license, visit
+ * .
+ * or send a letter to Creative Commons, PO Box 1866,
+ * Mountain View, CA 94042, USA.
+ *
+ */
+
+import config from "mc/config";
+import Timer from "timer";
+import parseBMF from "commodetto/parseBMF";
+import parseBMP from "commodetto/parseBMP";
+import Poco from "commodetto/Poco";
+import Resource from "Resource";
+import ILI9341 from "ili9341";
+import device from "embedded:provider/builtin";
+import Accelerometer from "embedded:sensor/Accelerometer-Gyroscope/MPU6050";
+
+let pixelsOut = new ILI9341({});
+const width = pixelsOut.width;
+const height = pixelsOut.height;
+
+let render = new Poco(pixelsOut);
+let font = parseBMF(new Resource("OpenSans-Semibold-18.bf4"));
+
+let ball = parseBMP(new Resource("ball-color.bmp"));
+ball.alpha = parseBMP(new Resource("ball-alpha.bmp"));
+ball.x = width >> 1;
+ball.y = height >> 1;
+ball.vx = 0;
+ball.vy = 0;
+ball.yMin = font.height * 3;
+ball.backGroundColor = render.makeColor(0, 0, 0);
+
+const textColor = render.makeColor(255, 255, 255);
+const backgroundColor = render.makeColor(64, 64, 64);
+const barColor = render.makeColor(128, 128, 128);
+
+render.begin();
+ render.fillRectangle(backgroundColor, 0, 0, width, ball.yMin);
+ render.fillRectangle(ball.backgroundColor, 0, ball.yMin, width, height);
+render.end();
+
+let sensor = new Accelerometer({
+ sensor: {
+ ...device.I2C.default,
+ io: device.io.SMBus
+ }
+});
+
+Timer.repeat(() => {
+ let values = sensor.sample();
+
+ if (180 === parseInt(config.orientation)) {
+ values.accelerometer.x = -values.accelerometer.x;
+ }
+
+ render.begin(0, 0, width, ball.yMin);
+ render.fillRectangle(backgroundColor, 0, 0, width, height);
+
+ drawBar("X", values.accelerometer.x, 0, 0, width, font.height);
+ drawBar("Y", values.accelerometer.y, 0, font.height, width, font.height);
+ drawBar("Z", values.accelerometer.z, 0, font.height * 2, width, font.height);
+ render.end();
+
+ ball.vx = (ball.vx + values.accelerometer.y) * 0.98;
+ ball.vy = (ball.vy + values.accelerometer.x) * 0.98;
+ let x = ball.x + ball.vx;
+ let y = ball.y + ball.vy;
+ if (x < 0) {
+ x = -x;
+ ball.vx = -ball.vx;
+ }
+ else if (x > (width - ball.width)) {
+ x = width - ball.width;
+ ball.vx = -ball.vx;
+ }
+ if (y < ball.yMin) {
+ y = ball.yMin;
+ ball.vy = -ball.vy;
+ }
+ else if (y > (height - ball.height)) {
+ y = height - ball.height;
+ ball.vy = -ball.vy;
+ }
+ moveBallTo(x, y)
+}, 17);
+
+function formatValue(value) {
+ if (!value)
+ return value;
+ if (value < 0)
+ return value.toFixed(3);
+ return "+" + value.toFixed(3);
+}
+
+function drawBar(label, value, x, y, width, height) {
+ const halfWidth = width >> 1;
+ const barWidth = (value * halfWidth) | 0;
+
+ if (value > 0)
+ render.fillRectangle(barColor, x + halfWidth, y, barWidth, height);
+ else
+ render.fillRectangle(barColor, x + halfWidth + barWidth, y, -barWidth, height);
+
+ render.drawText(label + " " + formatValue(value), font, textColor, x + 50, y);
+}
+
+function moveBallTo(x, y) {
+ const w = ball.width, h = ball.height;
+
+ if ((Math.abs(ball.x - x) <= w) && (Math.abs(ball.y - y) <= h))
+ render.begin(Math.min(ball.x, x), Math.min(ball.y, y), w << 1, h << 1); // often overdrawing
+ else {
+ render.begin(ball.x, ball.y, w, h);
+ render.fillRectangle(ball.backgroundColor, 0, 0, width, height);
+ render.continue(x, y, w, h);
+ }
+
+ render.fillRectangle(ball.backgroundColor, 0, 0, width, height);
+ render.drawMasked(ball, x, y, 0, 0, w, h, ball.alpha, 0, 0);
+ render.end();
+
+ ball.x = x;
+ ball.y = y;
+}
diff --git a/examples/drivers/sensors/qmi8658/bounce/manifest.json b/examples/drivers/sensors/qmi8658/bounce/manifest.json
new file mode 100644
index 0000000000..0404bcd7cd
--- /dev/null
+++ b/examples/drivers/sensors/qmi8658/bounce/manifest.json
@@ -0,0 +1,24 @@
+{
+ "include": [
+ "$(MODDABLE)/examples/manifest_base.json",
+ "$(MODDABLE)/examples/manifest_commodetto.json",
+ "$(MODDABLE)/modules/drivers/ili9341/manifest.json",
+ "$(MODDABLE)/modules/drivers/sensors/mpu6050/manifest.json",
+ "$(MODDABLE)/modules/io/manifest.json"
+ ],
+ "modules": {
+ "*": [
+ "./main"
+ ]
+ },
+ "config": {
+ "orientation": "180"
+ },
+ "preload": [
+ "pins/*"
+ ],
+ "resources": {
+ "*": "./ball",
+ "*-mask": "$(MODDABLE)/examples/assets/fonts/OpenSans-Semibold-18"
+ }
+}
diff --git a/examples/drivers/sensors/qmi8658/poll/main.js b/examples/drivers/sensors/qmi8658/poll/main.js
new file mode 100644
index 0000000000..53b5dfe8a7
--- /dev/null
+++ b/examples/drivers/sensors/qmi8658/poll/main.js
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2026 Moddable Tech, Inc. ,Satoshi Tanaka
+ *
+ * This file is part of the Moddable SDK.
+ *
+ * This work is licensed under the
+ * Creative Commons Attribution 4.0 International License.
+ * To view a copy of this license, visit
+ * .
+ * or send a letter to Creative Commons, PO Box 1866,
+ * Mountain View, CA 94042, USA.
+ *
+ */
+
+import QMI8658 from "embedded:sensor/Accelerometer-Gyroscope/QMI8658";
+import Timer from "timer";
+
+const sensor = new QMI8658({
+ sensor: {
+ ...device.I2C.default,
+ io: device.io.SMBus
+ }
+});
+
+sensor.configure({
+ ACCEL_SCALE: 8.0 / 32768.0, // AFS_8G
+ GYRO_SCALE: 1024.0 / 32768.0 // GFS_1024DPS
+});
+
+Timer.repeat(() => {
+ const sample = sensor.sample();
+ trace(`Accel: [${sample.accelerometer.x?.toFixed(2)}, ${sample.accelerometer.y?.toFixed(2)}, ${sample.accelerometer.z?.toFixed(2)}] - `);
+ trace(`Gyro: [${sample.gyroscope.x?.toFixed(2)}, ${sample.gyroscope.y?.toFixed(2)}, ${sample.gyroscope.z?.toFixed(2)}]\n`);
+}, 2000);
+
diff --git a/examples/drivers/sensors/qmi8658/poll/manifest.json b/examples/drivers/sensors/qmi8658/poll/manifest.json
new file mode 100644
index 0000000000..f65a323cb7
--- /dev/null
+++ b/examples/drivers/sensors/qmi8658/poll/manifest.json
@@ -0,0 +1,9 @@
+{
+ "include": [
+ "$(MODDABLE)/examples/manifest_base.json",
+ "$(MODDABLE)/modules/drivers/sensors/qmi8658/manifest.json"
+ ],
+ "modules": {
+ "*": "./main"
+ }
+}