From 925726b2e4346c1583204f303248e27aa615bb35 Mon Sep 17 00:00:00 2001 From: Ayachika Kitazaki Date: Thu, 7 May 2026 22:56:08 +0900 Subject: [PATCH 1/6] Add rotation support to CO5300 display driver ## Summary Add rotation support to the CO5300 display driver used by `esp32/waveshare_amoled_206`. The driver now supports `rotation` values of `0`, `90`, `180`, and `270`, consistent with other Moddable display drivers. ## Changes - Added a `rotation` getter/setter to `modules/drivers/co5300/co5300.js`. - Added native rotation state handling in `modCo5300.c`. - Updated `width` and `height` getters for 90/270 degree rotation. - Added validation for unsupported rotation values. - Added software pixel rearrangement for 90, 180, and 270 degree rotation. - Preserved the existing fast path for 0 degree rotation. ## Notes The CO5300 does not appear to provide a MADCTL-style row/column exchange mode suitable for 90/270 degree rotation. Non-zero rotations are therefore handled in software before pixel data is sent to the display. Rotated modes may be slower than the default 0 degree mode, but existing behavior is unchanged when `rotation` is not set. ## Testing Tested on `esp32/waveshare_amoled_206`, which uses the CO5300 display driver. Verified the following rotation values with `examples/piu/clock`: - `0` - `90` - `180` - `270` --- modules/drivers/co5300/co5300.js | 2 + modules/drivers/co5300/modCo5300.c | 183 +++++++++++++++++++++++++---- 2 files changed, 165 insertions(+), 20 deletions(-) diff --git a/modules/drivers/co5300/co5300.js b/modules/drivers/co5300/co5300.js index 03057c3c9..61c66eb48 100644 --- a/modules/drivers/co5300/co5300.js +++ b/modules/drivers/co5300/co5300.js @@ -37,6 +37,8 @@ export default class CO5300 @ "xs_co5300_destructor" { get pixelFormat() @ "xs_co5300_get_pixelFormat"; get width() @ "xs_co5300_get_width"; get height() @ "xs_co5300_get_height"; + get rotation() @ "xs_co5300_get_rotation"; + set rotation(value) @ "xs_co5300_set_rotation"; get async() {return true;} get c_dispatch() @ "xs_co5300_get_c_dispatch"; diff --git a/modules/drivers/co5300/modCo5300.c b/modules/drivers/co5300/modCo5300.c index 04f2112ed..9c0afbdb2 100644 --- a/modules/drivers/co5300/modCo5300.c +++ b/modules/drivers/co5300/modCo5300.c @@ -76,6 +76,7 @@ */ #define CO5300_CMD(reg) ((0x02 << 24) | ((reg) << 8)) #define CO5300_COLOR_CMD ((0x32 << 24) | (0x2C << 8)) +#define CO5300_ROTATE_BUFFER_PIXELS ((((MODDEF_CO5300_WIDTH) > (MODDEF_CO5300_HEIGHT)) ? (MODDEF_CO5300_WIDTH) : (MODDEF_CO5300_HEIGHT)) * 2) typedef struct { PixelsOutDispatch dispatch; @@ -85,6 +86,9 @@ typedef struct { #endif int updateWidth; + int updateHeight; + int updateX; + int updateY; int updateLinesRemaining; int yMin; int yMax; @@ -92,6 +96,8 @@ typedef struct { uint8_t nothingSent; uint8_t firstFrame; uint8_t brightnessValue; + uint8_t rotation; // 0, 1, 2, 3 => 0, 90, 180, 270 + uint8_t colorSlotReserved; SemaphoreHandle_t colorsInFlight; esp_lcd_panel_io_handle_t io_handle; @@ -100,9 +106,14 @@ typedef struct { int opZero; uint8_t data[32]; + uint16_t *rotateBuffer; } co5300DisplayRecord, *co5300Display; static void co5300Init(co5300Display sd); +static void co5300SetWindow(co5300Display sd, uint16_t x, uint16_t y, uint16_t w, uint16_t h); +static void co5300WaitForColors(co5300Display sd); +static void co5300SendColorSync(co5300Display sd, const void *pixels, int byteLength); +static void co5300SendRotated(co5300Display sd, uint16_t *pixels, int byteLength); #define co5300Command(sd, command, data, count) \ (esp_lcd_panel_io_tx_param(sd->io_handle, command, data, count)) @@ -143,6 +154,9 @@ void xs_co5300_destructor(void *data) if (sd->colorsInFlight) vSemaphoreDelete(sd->colorsInFlight); + if (sd->rotateBuffer) + c_free(sd->rotateBuffer); + c_free(data); } @@ -177,6 +191,13 @@ void xs_co5300(xsMachine *the) xsmcSetHostData(xsThis, sd); + sd->rotateBuffer = c_malloc(CO5300_ROTATE_BUFFER_PIXELS * sizeof(uint16_t)); + if (!sd->rotateBuffer) { + c_free(sd); + xsmcSetHostData(xsThis, NULL); + xsUnknownError("no memory"); + } + spi_bus_config_t buscfg = { .sclk_io_num = MODDEF_CO5300_SCK_PIN, .data0_io_num = MODDEF_CO5300_DATA0_PIN, @@ -189,6 +210,7 @@ void xs_co5300(xsMachine *the) int err = spi_bus_initialize(MODDEF_CO5300_SPI_PORT, &buscfg, SPI_DMA_CH_AUTO); if (err) { + c_free(sd->rotateBuffer); c_free(sd); xsmcSetHostData(xsThis, NULL); xsUnknownError("spi_bus_initialize failed"); @@ -212,6 +234,7 @@ void xs_co5300(xsMachine *the) err = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)MODDEF_CO5300_SPI_PORT, &io_config, &sd->io_handle); if (err) { spi_bus_free(MODDEF_CO5300_SPI_PORT); + c_free(sd->rotateBuffer); c_free(sd); xsmcSetHostData(xsThis, NULL); xsUnknownError("esp_lcd_new_panel_io_spi failed"); @@ -295,12 +318,14 @@ void xs_co5300_get_pixelFormat(xsMachine *the) void xs_co5300_get_width(xsMachine *the) { - xsmcSetInteger(xsResult, MODDEF_CO5300_WIDTH); + co5300Display sd = xsmcGetHostData(xsThis); + xsmcSetInteger(xsResult, (sd->rotation & 1) ? MODDEF_CO5300_HEIGHT : MODDEF_CO5300_WIDTH); } void xs_co5300_get_height(xsMachine *the) { - xsmcSetInteger(xsResult, MODDEF_CO5300_HEIGHT); + co5300Display sd = xsmcGetHostData(xsThis); + xsmcSetInteger(xsResult, (sd->rotation & 1) ? MODDEF_CO5300_WIDTH : MODDEF_CO5300_HEIGHT); } void xs_co5300_get_c_dispatch(xsMachine *the) @@ -308,6 +333,23 @@ void xs_co5300_get_c_dispatch(xsMachine *the) xsResult = xsThis; } +void xs_co5300_get_rotation(xsMachine *the) +{ + co5300Display sd = xsmcGetHostData(xsThis); + xsmcSetInteger(xsResult, sd->rotation * 90); +} + +void xs_co5300_set_rotation(xsMachine *the) +{ + co5300Display sd = xsmcGetHostData(xsThis); + int32_t rotation = xsmcToInteger(xsArg(0)); + + if ((0 != rotation) && (90 != rotation) && (180 != rotation) && (270 != rotation)) + xsRangeError("invalid rotation"); + + sd->rotation = (uint8_t)(rotation / 90); +} + void xs_co5300_command(xsMachine *the) { co5300Display sd = xsmcGetHostData(xsThis); @@ -365,8 +407,14 @@ void co5300Send(PocoPixel *pixels, int byteLength, void *refcon) } #endif + if (sd->rotation) { + co5300SendRotated(sd, (uint16_t *)pixels, byteLength); + return; + } + { int one = 1; + sd->colorSlotReserved = 0; xQueueSend(sd->ops, &one, portMAX_DELAY); esp_lcd_panel_io_tx_color(sd->io_handle, CO5300_COLOR_CMD, pixels, byteLength); @@ -442,6 +490,105 @@ void co5300Init(co5300Display sd) sd->brightnessValue = 0; } +static void co5300SetWindow(co5300Display sd, uint16_t x, uint16_t y, uint16_t w, uint16_t h) +{ + uint16_t xMin = x + MODDEF_CO5300_COLUMN_OFFSET; + uint16_t yMin = y + MODDEF_CO5300_ROW_OFFSET; + uint16_t xMax = xMin + w - 1; + uint16_t yMax = yMin + h - 1; + uint8_t data[4]; + + data[0] = (xMin >> 8) & 0xff; + data[1] = xMin & 0xff; + data[2] = (xMax >> 8) & 0xff; + data[3] = xMax & 0xff; + co5300Command(sd, CO5300_CMD(0x2a), data, 4); + + data[0] = (yMin >> 8) & 0xff; + data[1] = yMin & 0xff; + data[2] = (yMax >> 8) & 0xff; + data[3] = yMax & 0xff; + co5300Command(sd, CO5300_CMD(0x2b), data, 4); +} + +static void co5300WaitForColors(co5300Display sd) +{ + xSemaphoreTake(sd->colorsInFlight, portMAX_DELAY); + xSemaphoreTake(sd->colorsInFlight, portMAX_DELAY); + xSemaphoreGive(sd->colorsInFlight); + xSemaphoreGive(sd->colorsInFlight); +} + +static void co5300SendColorSync(co5300Display sd, const void *pixels, int byteLength) +{ + int one = 1; + + if (sd->colorSlotReserved) + sd->colorSlotReserved = 0; + else + xSemaphoreTake(sd->colorsInFlight, portMAX_DELAY); + + xQueueSend(sd->ops, &one, portMAX_DELAY); + esp_lcd_panel_io_tx_color(sd->io_handle, CO5300_COLOR_CMD, pixels, byteLength); + co5300WaitForColors(sd); +} + +static void co5300SendRotated(co5300Display sd, uint16_t *pixels, int byteLength) +{ + int lines = (byteLength >> 1) / sd->updateWidth; + int row = sd->updateHeight - sd->updateLinesRemaining; + uint16_t *src = pixels; + + while (lines > 0) { + int rows = (lines > 1) ? 2 : 1; + uint16_t *out = sd->rotateBuffer; + uint16_t x, y, w, h; + + switch (sd->rotation) { + case 1: + for (int col = 0; col < sd->updateWidth; col++) { + for (int r = rows - 1; r >= 0; r--) + *out++ = src[(r * sd->updateWidth) + col]; + } + x = MODDEF_CO5300_WIDTH - (sd->updateY + row + rows); + y = sd->updateX; + w = rows; + h = sd->updateWidth; + break; + + case 2: + for (int r = rows - 1; r >= 0; r--) { + for (int col = sd->updateWidth - 1; col >= 0; col--) + *out++ = src[(r * sd->updateWidth) + col]; + } + x = MODDEF_CO5300_WIDTH - (sd->updateX + sd->updateWidth); + y = MODDEF_CO5300_HEIGHT - (sd->updateY + row + rows); + w = sd->updateWidth; + h = rows; + break; + + default: // 270 degrees + for (int col = sd->updateWidth - 1; col >= 0; col--) { + for (int r = 0; r < rows; r++) + *out++ = src[(r * sd->updateWidth) + col]; + } + x = sd->updateY + row; + y = MODDEF_CO5300_HEIGHT - (sd->updateX + sd->updateWidth); + w = rows; + h = sd->updateWidth; + break; + } + + co5300SetWindow(sd, x, y, w, h); + co5300SendColorSync(sd, sd->rotateBuffer, (int)((out - sd->rotateBuffer) * sizeof(uint16_t))); + + src += rows * sd->updateWidth; + row += rows; + lines -= rows; + sd->updateLinesRemaining -= rows; + } +} + void co5300AdaptInvalid(void *refcon, CommodettoRectangle r) { // CO5300 requires even-aligned coordinates for partial updates @@ -460,37 +607,30 @@ void co5300AdaptInvalid(void *refcon, CommodettoRectangle r) void co5300Begin(void *refcon, CommodettoCoordinate x, CommodettoCoordinate y, CommodettoDimension w, CommodettoDimension h) { co5300Display sd = refcon; - uint16_t xMin, xMax, yMin, yMax; + uint16_t yMin, yMax; - if (sd->nothingSent) + if (sd->nothingSent && sd->colorSlotReserved) { xSemaphoreGive(sd->colorsInFlight); + sd->colorSlotReserved = 0; + } sd->nothingSent = 1; - xMin = x + MODDEF_CO5300_COLUMN_OFFSET; yMin = y + MODDEF_CO5300_ROW_OFFSET; - - xMax = xMin + w - 1; yMax = yMin + h - 1; + sd->updateX = x; + sd->updateY = y; sd->updateWidth = w; + sd->updateHeight = h; sd->updateLinesRemaining = h; sd->yMin = yMin; sd->yMax = yMax; - uint8_t data[4]; - data[0] = (xMin >> 8) & 0xff; - data[1] = xMin & 0xff; - data[2] = (xMax >> 8) & 0xff; - data[3] = xMax & 0xff; - co5300Command(sd, CO5300_CMD(0x2a), data, 4); - - data[0] = (yMin >> 8) & 0xff; - data[1] = yMin & 0xff; - data[2] = (yMax >> 8) & 0xff; - data[3] = yMax & 0xff; - co5300Command(sd, CO5300_CMD(0x2b), data, 4); + if (!sd->rotation) + co5300SetWindow(sd, x, y, w, h); xSemaphoreTake(sd->colorsInFlight, portMAX_DELAY); + sd->colorSlotReserved = 1; } void co5300Continue(void *refcon) @@ -512,7 +652,10 @@ void co5300End(void *refcon) } if (sd->nothingSent) { - xSemaphoreGive(sd->colorsInFlight); + if (sd->colorSlotReserved) { + xSemaphoreGive(sd->colorsInFlight); + sd->colorSlotReserved = 0; + } sd->nothingSent = 0; } } From ddb94a71439a93169f0a017e35d50c8711189300 Mon Sep 17 00:00:00 2001 From: Ayachika Kitazaki Date: Fri, 8 May 2026 21:42:48 +0900 Subject: [PATCH 2/6] Add BMI270 IMU support for M5AtomS3R targets ## Summary This PR adds BMI270 IMU support to the M5AtomS3R target family. Moddable SDK 8.1.0 added support for the BMI270 sensor device. Since the M5AtomS3R family uses the same BMI270 IMU, this PR updates the corresponding target definitions. Updated targets: - `esp32/m5atom_s3r` - `esp32/m5atom_s3r_cam` - `esp32/m5atom_s3r_m12` ## Changes - Added the BMI270 sensor driver manifest to the M5AtomS3R target manifests. - Added `device.sensor.IMU` definitions for M5AtomS3R targets. - Added axis correction for `m5atom_s3r` so the IMU orientation matches `m5atom_s3`. - Scaled BMI270 accelerometer values from `m/s^2` to `g` for compatibility with existing AtomS3 IMU(MPU6886) examples. - Updated `examples/drivers/atoms3-imu` to handle samples that may not include accelerometer or gyroscope data immediately. - Added `examples/drivers/atoms3r-imu-console` for console-based IMU verification on displayless M5AtomS3R variants. ## Verification - `examples/drivers/atoms3-imu` with `esp32/m5atom_s3r` - `examples/drivers/atoms3r-imu-console` with `esp32/m5atom_s3r` - `examples/drivers/atoms3r-imu-console` with `esp32/m5atom_s3r_cam` - `examples/drivers/atoms3r-imu-console` with `esp32/m5atom_s3r_m12` ## Note M5Stack Core2 v1.3 also uses the BMI270 IMU. However, the Core2 hardware configuration changed between v1.1 and v1.3: the IMU changed from MPU6886 to BMI270, and the PMU changed from AXP2101 to AXP192. I plan to submit a separate PR for Core2 v1.3 after verifying the detection logic for these hardware differences. --- .../esp32/targets/m5atom_s3r/host/provider.js | 141 ++++++++++++++++++ .../esp32/targets/m5atom_s3r/manifest.json | 94 ++++++++++++ .../targets/m5atom_s3r_cam/host/provider.js | 84 +++++++++++ .../targets/m5atom_s3r_cam/manifest.json | 63 ++++++++ .../targets/m5atom_s3r_m12/host/provider.js | 84 +++++++++++ .../targets/m5atom_s3r_m12/manifest.json | 64 ++++++++ examples/drivers/atoms3-imu/main.js | 9 +- examples/drivers/atoms3r-imu-console/main.js | 55 +++++++ .../drivers/atoms3r-imu-console/manifest.json | 16 ++ 9 files changed, 605 insertions(+), 5 deletions(-) create mode 100644 build/devies/esp32/targets/m5atom_s3r/host/provider.js create mode 100644 build/devies/esp32/targets/m5atom_s3r/manifest.json create mode 100644 build/devies/esp32/targets/m5atom_s3r_cam/host/provider.js create mode 100644 build/devies/esp32/targets/m5atom_s3r_cam/manifest.json create mode 100644 build/devies/esp32/targets/m5atom_s3r_m12/host/provider.js create mode 100644 build/devies/esp32/targets/m5atom_s3r_m12/manifest.json create mode 100644 examples/drivers/atoms3r-imu-console/main.js create mode 100644 examples/drivers/atoms3r-imu-console/manifest.json diff --git a/build/devies/esp32/targets/m5atom_s3r/host/provider.js b/build/devies/esp32/targets/m5atom_s3r/host/provider.js new file mode 100644 index 000000000..a7a99c87c --- /dev/null +++ b/build/devies/esp32/targets/m5atom_s3r/host/provider.js @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2025 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 . + * + */ + +import Analog from "embedded:io/analog"; +import Digital from "embedded:io/digital"; +import DigitalBank from "embedded:io/digitalbank"; +import I2C from "embedded:io/i2c"; +import PulseCount from "embedded:io/pulsecount"; +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 Timer from "timer"; +import IMU from "embedded:sensor/Accelerometer-Gyroscope-Magnetometer/BMI270"; + +const ACCELERATION_SCALER = 1 / 9.80665; + +class Backlight { + #io; + + constructor(options) { + const io = this.#io = new SMBus({ + ...device.I2C.internal, + hz: 400_000, + address:48, + }); + + io.writeUint8(0x00, 0b01000000) + Timer.delay(1) + io.writeUint8(0x08, 0b00000001) + io.writeUint8(0x70, 0b00000000) + } + close() { + this.#io?.close(); + this.#io = undefined; + } + set brightness(value) { + if (value <= 0) value = 0; + else if (value >= 1) value = 255; + else value *= 255; + this.#io.writeUint8(0x0e, value) + } +} + +const device = { + I2C: { + default: { + io: I2C, + data: 2, + clock: 1, + }, + internal: { + io: I2C, + data: 45, + clock: 0, + }, + }, + SPI: { + default: { + io: SPI, + port: 3, + clock: 15, + out: 21, + }, + }, + Analog: { + default: { + io: Analog, + pin: 8, + }, + }, + io: { + Analog, + Digital, + DigitalBank, + I2C, + PulseCount, + PWM, + Serial, + SMBus, + SPI, + }, + pin: { + button: 41, + displaySelect: 14, + }, + peripheral: { + Backlight: class { + constructor() { + return new Backlight(); + } + }, + }, + sensor: { + IMU: class extends IMU { + constructor(options) { + super({ + ...options, + sensor: { + ...device.I2C.internal, + address: 0x68, + io: device.io.SMBus, + }, + }); + } + sample() { + const sample = super.sample(); + + if (sample.accelerometer) { + [sample.accelerometer.x, sample.accelerometer.y] = [-sample.accelerometer.y * ACCELERATION_SCALER, sample.accelerometer.x * ACCELERATION_SCALER]; + sample.accelerometer.z *= -ACCELERATION_SCALER; + } + if (sample.gyroscope) { + [sample.gyroscope.x, sample.gyroscope.y] = [-sample.gyroscope.y, sample.gyroscope.x]; + sample.gyroscope.z *= -1; + } + + return sample; + } + }, + }, +}; + +export default device; diff --git a/build/devies/esp32/targets/m5atom_s3r/manifest.json b/build/devies/esp32/targets/m5atom_s3r/manifest.json new file mode 100644 index 000000000..51a1ac744 --- /dev/null +++ b/build/devies/esp32/targets/m5atom_s3r/manifest.json @@ -0,0 +1,94 @@ +{ + "build":{ + "ESP32_SUBCLASS": "esp32s3", + "USE_USB": "2", + "SDKCONFIGPATH": "./sdkconfig", + "PARTITIONS_FILE_FOR_TARGET": "./sdkconfig/partitions.csv", + "PROGRAMMING_MODE_MESSAGE": "INSTRUCTIONS: Press and hold the button until the LED lights.", + "BEFORE_DEBUGGING_MESSAGE": "Press and release the Reset button." + }, + "include": [ + "$(MODDABLE)/modules/io/manifest.json", + "$(MODDABLE)/modules/drivers/ili9341/manifest.json", + "$(MODDABLE)/modules/drivers/sensors/bmi270/manifest.json", + "$(MODULES)/drivers/button/manifest.json" + ], + "modules": { + "*": [ + "../m5stack_fire/m5button" + ], + "setup/target": "./setup-target" + }, + "preload": [ + "setup/target", + "m5button" + ], + "config": { + "screen": "ili9341", + "touch": "" + }, + "creation": { + "static": 0, + "chunk": { + "initial": 78848, + "incremental": 0 + }, + "heap": { + "initial": 4928, + "incremental": 0 + }, + "stack": 512 + }, + "defines": { + "i2c": { + "sda_pin": 45, + "scl_pin": 0 + }, + "spi": { + "mosi_pin":21, + "sck_pin": 15 + }, + "ili9341": { + "hz": 27000000, + "width": 128, + "height": 128, + "cs_pin": 14, + "rst_pin": 48, + "dc_pin": 42, + "column_offset": 0, + "row_offset": 32, + "spi_port": "SPI3_HOST", + "registers": [ + "0xFE, 0,", + "kDelayMS, 10,", + "0xEF, 0,", + "kDelayMS, 10,", + "0x36, 1, 0x08,", + "0xB0, 1, 0xC0,", + "0xB2, 1, 0x2F,", + "0xB3, 1, 0x03,", + "0xB6, 1, 0x19,", + "0xB7, 1, 0x01,", + "0xAC, 1, 0xCB,", + "0xAB, 1, 0x0E,", + "0xB4, 1, 0x04,", + "0xA8, 1, 0x19,", + "0x3A, 1, 0x05,", + "0xB8, 1, 0x08,", + "0xE8, 1, 0x24,", + "0xE9, 1, 0x48,", + "0xEA, 1, 0x22,", + "0xC6, 1, 0x30,", + "0xC7, 1, 0x18,", + "0xF0, 14, 0x1F,0x28,0x04,0x3E,0x2A,0x2E,0x20,0x00,0x0C,0x06,0x00,0x1C,0x1F,0x0F,", + "0xF1, 14, 0x00,0x2D,0x2F,0x3C,0x6F,0x1C,0x0B,0x00,0x00,0x00,0x07,0x0D,0x11,0x0F,", + "0x20, 0,", + "0x11, 0,", + "kDelayMS, 120,", + "0x29, 0,", + "kDelayMS, 20,", + "kDelayMS, 0" + ] + } + } +} diff --git a/build/devies/esp32/targets/m5atom_s3r_cam/host/provider.js b/build/devies/esp32/targets/m5atom_s3r_cam/host/provider.js new file mode 100644 index 000000000..60dbb1a12 --- /dev/null +++ b/build/devies/esp32/targets/m5atom_s3r_cam/host/provider.js @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021-2024 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 . + * + */ + +import Analog from "embedded:io/analog"; +import Digital from "embedded:io/digital"; +import DigitalBank from "embedded:io/digitalbank"; +import I2C from "embedded:io/i2c"; +import PulseCount from "embedded:io/pulsecount"; +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 IMU from "embedded:sensor/Accelerometer-Gyroscope-Magnetometer/BMI270"; + +const device = { + I2C: { + default: { + io: I2C, + data: 45, + clock: 0, + }, + internal: { + io: I2C, + data: 45, + clock: 0, + } + }, + Serial: { + default: { + io: Serial, + port: 1, + receive: 44, + transmit: 43 + } + }, + SPI: { + default: { + io: SPI, + clock: 36, + in: 37, + out: 35, + port: 2 + } + }, + io: {Analog, Digital, DigitalBank, I2C, PulseCount, PulseWidth, PWM, Serial, SMBus, SPI}, + pin: { + //@@ button + button: 0 + }, + sensor: { + IMU: class { + constructor(options) { + return new IMU({ + ...options, + sensor: { + ...device.I2C.internal, + address: 0x68, + io: device.io.SMBus + } + }); + } + } + } +}; + +export default device; diff --git a/build/devies/esp32/targets/m5atom_s3r_cam/manifest.json b/build/devies/esp32/targets/m5atom_s3r_cam/manifest.json new file mode 100644 index 000000000..f5b6e6ede --- /dev/null +++ b/build/devies/esp32/targets/m5atom_s3r_cam/manifest.json @@ -0,0 +1,63 @@ +{ + "build": { + "SDKCONFIGPATH": "./sdkconfig", + "PARTITIONS_FILE_FOR_TARGET": "./sdkconfig/partitions.csv", + "ESP32_SUBCLASS": "esp32s3", + "USE_USB": "2" + }, + "include": [ + "$(MODDABLE)/modules/io/manifest.json", + "$(MODDABLE)/modules/drivers/sensors/bmi270/manifest.json", + "$(MODULES)/drivers/button/manifest.json" + ], + "modules": { + "setup/target": "./setup-target" + }, + "preload": [ + "setup/target" + ], + "config": { + "Screen": "", + "button_pin": 41, + "power_pin": 18, + "ir_pin": 47 + }, + "creation": { + "static": 0, + "chunk": { + "initial": 78848, + "incremental": 0 + }, + "heap": { + "initial": 4928, + "incremental": 0 + }, + "stack": 512 + }, + "defines": { + "i2c": { + "sda_pin": 45, + "scl_pin": 0 + }, + "camera": { + "powerdown": -1, + "reset": -1, + "xclk": 21, + "pclk": 40, + "href": 14, + "vsync": 10, + "scl": 9, + "sda": 12, + "i2c_port": 1, + "d0": 3, + "d1": 42, + "d2": 46, + "d3": 48, + "d4": 4, + "d5": 17, + "d6": 11, + "d7": 13 + } + } +} + diff --git a/build/devies/esp32/targets/m5atom_s3r_m12/host/provider.js b/build/devies/esp32/targets/m5atom_s3r_m12/host/provider.js new file mode 100644 index 000000000..60dbb1a12 --- /dev/null +++ b/build/devies/esp32/targets/m5atom_s3r_m12/host/provider.js @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021-2024 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 . + * + */ + +import Analog from "embedded:io/analog"; +import Digital from "embedded:io/digital"; +import DigitalBank from "embedded:io/digitalbank"; +import I2C from "embedded:io/i2c"; +import PulseCount from "embedded:io/pulsecount"; +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 IMU from "embedded:sensor/Accelerometer-Gyroscope-Magnetometer/BMI270"; + +const device = { + I2C: { + default: { + io: I2C, + data: 45, + clock: 0, + }, + internal: { + io: I2C, + data: 45, + clock: 0, + } + }, + Serial: { + default: { + io: Serial, + port: 1, + receive: 44, + transmit: 43 + } + }, + SPI: { + default: { + io: SPI, + clock: 36, + in: 37, + out: 35, + port: 2 + } + }, + io: {Analog, Digital, DigitalBank, I2C, PulseCount, PulseWidth, PWM, Serial, SMBus, SPI}, + pin: { + //@@ button + button: 0 + }, + sensor: { + IMU: class { + constructor(options) { + return new IMU({ + ...options, + sensor: { + ...device.I2C.internal, + address: 0x68, + io: device.io.SMBus + } + }); + } + } + } +}; + +export default device; diff --git a/build/devies/esp32/targets/m5atom_s3r_m12/manifest.json b/build/devies/esp32/targets/m5atom_s3r_m12/manifest.json new file mode 100644 index 000000000..19781b65f --- /dev/null +++ b/build/devies/esp32/targets/m5atom_s3r_m12/manifest.json @@ -0,0 +1,64 @@ +{ + "build": { + "SDKCONFIGPATH": "./sdkconfig", + "PARTITIONS_FILE_FOR_TARGET": "./sdkconfig/partitions.csv", + "ESP32_SUBCLASS": "esp32s3", + "USE_USB": "2" + }, + "include": [ + "$(MODDABLE)/modules/io/manifest.json", + "$(MODDABLE)/modules/drivers/sensors/bmi270/manifest.json", + "$(MODULES)/drivers/button/manifest.json" + ], + "modules": { + "setup/target": "./setup-target" + }, + "preload": [ + "setup/target" + ], + "config": { + "Screen": "", + "button_pin": 41, + "power_pin": 18, + "ir_pin": 47 + }, + "creation": { + "static": 0, + "chunk": { + "initial": 78848, + "incremental": 0 + }, + "heap": { + "initial": 4928, + "incremental": 0 + }, + "stack": 512 + }, + "defines": { + "i2c": { + "sda_pin": 45, + "scl_pin": 0 + }, + "camera": { + "powerdown": -1, + "reset": -1, + "xclk": 21, + "xclk_freq_hz": 20000000, + "pclk": 40, + "href": 14, + "vsync": 10, + "scl": 9, + "sda": 12, + "i2c_port": 1, + "d0": 3, + "d1": 42, + "d2": 46, + "d3": 48, + "d4": 4, + "d5": 17, + "d6": 11, + "d7": 13 + } + } +} + diff --git a/examples/drivers/atoms3-imu/main.js b/examples/drivers/atoms3-imu/main.js index db59f5d0e..ee6ee1e0e 100644 --- a/examples/drivers/atoms3-imu/main.js +++ b/examples/drivers/atoms3-imu/main.js @@ -50,11 +50,10 @@ imu.configure({ }) Timer.repeat(() => { const sample = imu.sample(); - if(flag) { - onReading(sample.accelerometer, "a"); - } else { - onReading(sample.gyroscope, "g"); - } + const values = flag ? sample.accelerometer : sample.gyroscope; + + if (values) + onReading(values, flag ? "a" : "g"); }, 17) let flag = true; diff --git a/examples/drivers/atoms3r-imu-console/main.js b/examples/drivers/atoms3r-imu-console/main.js new file mode 100644 index 000000000..4c39f2adc --- /dev/null +++ b/examples/drivers/atoms3r-imu-console/main.js @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2026 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 Timer from "timer"; + +const SAMPLE_INTERVAL = 1000; + +let acceleration; +let rotationRate; + +if (!device?.sensor?.IMU) + throw new Error("IMU not available"); + +const imu = new device.sensor.IMU(); + +Timer.repeat(() => { + const sample = imu.sample(); + + acceleration = sample.accelerometer; + rotationRate = sample.gyroscope; + + trace("accelerometer "); + traceVector(acceleration); + trace(" gyroscope "); + traceVector(rotationRate); + trace("\n"); +}, SAMPLE_INTERVAL); + +function traceVector(values) { + if (!values) { + trace("(pending)"); + return; + } + + trace(`x:${format(values.x)} y:${format(values.y)} z:${format(values.z)}`); +} + +function format(value) { + if (undefined === value) + return "n/a"; + if (0 <= value) + return `+${value.toFixed(3)}`; + return value.toFixed(3); +} diff --git a/examples/drivers/atoms3r-imu-console/manifest.json b/examples/drivers/atoms3r-imu-console/manifest.json new file mode 100644 index 000000000..aebb5176d --- /dev/null +++ b/examples/drivers/atoms3r-imu-console/manifest.json @@ -0,0 +1,16 @@ +{ + "include": [ + "$(MODDABLE)/examples/manifest_base.json" + ], + "modules": { + "*": "./main" + }, + "platforms": { + "esp32/m5atom_s3r": {}, + "esp32/m5atom_s3r_cam": {}, + "esp32/m5atom_s3r_m12": {}, + "...": { + "error": "esp32/m5atom_s3r, esp32/m5atom_s3r_cam, and esp32/m5atom_s3r_m12 only" + } + } +} From 823ddcded694619d86b4c45e7041350400f249cc Mon Sep 17 00:00:00 2001 From: Ayachika Kitazaki Date: Sat, 9 May 2026 13:38:47 +0900 Subject: [PATCH 3/6] Updated. uses MADCTL for 180 degree rotation ## Summary Add rotation support to the CO5300 display driver. The driver now supports `rotation` values of `0`, `90`, `180`, and `270`. ## Changes - Add `rotation` getter/setter to the CO5300 JavaScript binding. - Use CO5300 `MADCTL` (`0x36`) with `MY | MX` for 180 degree rotation. - Use software pixel rearrangement for 90 and 270 degree rotation, where `MADCTL` cannot swap rows and columns. - Update width/height getters for 90/270 degree rotation. - Preserve the existing fast path for 0 degree rotation. ## Testing - `examples/piu/cards` for `esp32/waveshare_amoled_206`. - Verified 0, 90, 180, and 270 degree rotation on device. --- modules/drivers/co5300/co5300.js | 2 +- modules/drivers/co5300/modCo5300.c | 92 +++++++++++++++++------------- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/modules/drivers/co5300/co5300.js b/modules/drivers/co5300/co5300.js index 61c66eb48..b5e66843f 100644 --- a/modules/drivers/co5300/co5300.js +++ b/modules/drivers/co5300/co5300.js @@ -37,9 +37,9 @@ export default class CO5300 @ "xs_co5300_destructor" { get pixelFormat() @ "xs_co5300_get_pixelFormat"; get width() @ "xs_co5300_get_width"; get height() @ "xs_co5300_get_height"; + get async() {return true;} get rotation() @ "xs_co5300_get_rotation"; set rotation(value) @ "xs_co5300_set_rotation"; - get async() {return true;} get c_dispatch() @ "xs_co5300_get_c_dispatch"; diff --git a/modules/drivers/co5300/modCo5300.c b/modules/drivers/co5300/modCo5300.c index 9c0afbdb2..312438a5b 100644 --- a/modules/drivers/co5300/modCo5300.c +++ b/modules/drivers/co5300/modCo5300.c @@ -76,6 +76,8 @@ */ #define CO5300_CMD(reg) ((0x02 << 24) | ((reg) << 8)) #define CO5300_COLOR_CMD ((0x32 << 24) | (0x2C << 8)) +#define CO5300_MADCTL_MY (0x80) +#define CO5300_MADCTL_MX (0x40) #define CO5300_ROTATE_BUFFER_PIXELS ((((MODDEF_CO5300_WIDTH) > (MODDEF_CO5300_HEIGHT)) ? (MODDEF_CO5300_WIDTH) : (MODDEF_CO5300_HEIGHT)) * 2) typedef struct { @@ -110,10 +112,12 @@ typedef struct { } co5300DisplayRecord, *co5300Display; static void co5300Init(co5300Display sd); +static void co5300SetMADCTL(co5300Display sd); static void co5300SetWindow(co5300Display sd, uint16_t x, uint16_t y, uint16_t w, uint16_t h); static void co5300WaitForColors(co5300Display sd); static void co5300SendColorSync(co5300Display sd, const void *pixels, int byteLength); static void co5300SendRotated(co5300Display sd, uint16_t *pixels, int byteLength); +static void co5300SendHardwareRotated180(co5300Display sd, uint16_t *pixels, int byteLength); #define co5300Command(sd, command, data, count) \ (esp_lcd_panel_io_tx_param(sd->io_handle, command, data, count)) @@ -328,11 +332,6 @@ void xs_co5300_get_height(xsMachine *the) xsmcSetInteger(xsResult, (sd->rotation & 1) ? MODDEF_CO5300_WIDTH : MODDEF_CO5300_HEIGHT); } -void xs_co5300_get_c_dispatch(xsMachine *the) -{ - xsResult = xsThis; -} - void xs_co5300_get_rotation(xsMachine *the) { co5300Display sd = xsmcGetHostData(xsThis); @@ -348,6 +347,12 @@ void xs_co5300_set_rotation(xsMachine *the) xsRangeError("invalid rotation"); sd->rotation = (uint8_t)(rotation / 90); + co5300SetMADCTL(sd); +} + +void xs_co5300_get_c_dispatch(xsMachine *the) +{ + xsResult = xsThis; } void xs_co5300_command(xsMachine *the) @@ -407,11 +412,16 @@ void co5300Send(PocoPixel *pixels, int byteLength, void *refcon) } #endif - if (sd->rotation) { + if (1 == (sd->rotation & 1)) { co5300SendRotated(sd, (uint16_t *)pixels, byteLength); return; } + if (2 == sd->rotation) { + co5300SendHardwareRotated180(sd, (uint16_t *)pixels, byteLength); + return; + } + { int one = 1; sd->colorSlotReserved = 0; @@ -449,6 +459,7 @@ static const uint8_t gInit[] ICACHE_RODATA_ATTR = { 0xFE, 1, 0x00, // Page select (page 0) 0xC4, 1, 0x80, // Set interface (QSPI) 0x3A, 1, 0x55, // Pixel format: 16-bit RGB565 + 0x36, 1, 0x00, // Memory data access control 0x44, 2, 0x01, 0xD1, // Set tear scanline 0x35, 1, 0x00, // Tearing effect line on 0x53, 1, 0x20, // Write CTRL display value @@ -488,6 +499,13 @@ void co5300Init(co5300Display sd) sd->firstFrame = true; sd->brightnessValue = 0; + sd->rotation = 0; +} + +static void co5300SetMADCTL(co5300Display sd) +{ + uint8_t value = (2 == sd->rotation) ? (CO5300_MADCTL_MY | CO5300_MADCTL_MX) : 0; + co5300Command(sd, CO5300_CMD(0x36), &value, 1); } static void co5300SetWindow(co5300Display sd, uint16_t x, uint16_t y, uint16_t w, uint16_t h) @@ -544,40 +562,24 @@ static void co5300SendRotated(co5300Display sd, uint16_t *pixels, int byteLength uint16_t *out = sd->rotateBuffer; uint16_t x, y, w, h; - switch (sd->rotation) { - case 1: - for (int col = 0; col < sd->updateWidth; col++) { - for (int r = rows - 1; r >= 0; r--) - *out++ = src[(r * sd->updateWidth) + col]; - } - x = MODDEF_CO5300_WIDTH - (sd->updateY + row + rows); - y = sd->updateX; - w = rows; - h = sd->updateWidth; - break; - - case 2: - for (int r = rows - 1; r >= 0; r--) { - for (int col = sd->updateWidth - 1; col >= 0; col--) - *out++ = src[(r * sd->updateWidth) + col]; - } - x = MODDEF_CO5300_WIDTH - (sd->updateX + sd->updateWidth); - y = MODDEF_CO5300_HEIGHT - (sd->updateY + row + rows); - w = sd->updateWidth; - h = rows; - break; - - default: // 270 degrees - for (int col = sd->updateWidth - 1; col >= 0; col--) { - for (int r = 0; r < rows; r++) - *out++ = src[(r * sd->updateWidth) + col]; - } - x = sd->updateY + row; - y = MODDEF_CO5300_HEIGHT - (sd->updateX + sd->updateWidth); - w = rows; - h = sd->updateWidth; - break; + if (1 == sd->rotation) { + for (int col = 0; col < sd->updateWidth; col++) { + for (int r = rows - 1; r >= 0; r--) + *out++ = src[(r * sd->updateWidth) + col]; + } + x = MODDEF_CO5300_WIDTH - (sd->updateY + row + rows); + y = sd->updateX; + } + else { + for (int col = sd->updateWidth - 1; col >= 0; col--) { + for (int r = 0; r < rows; r++) + *out++ = src[(r * sd->updateWidth) + col]; + } + x = sd->updateY + row; + y = MODDEF_CO5300_HEIGHT - (sd->updateX + sd->updateWidth); } + w = rows; + h = sd->updateWidth; co5300SetWindow(sd, x, y, w, h); co5300SendColorSync(sd, sd->rotateBuffer, (int)((out - sd->rotateBuffer) * sizeof(uint16_t))); @@ -589,6 +591,18 @@ static void co5300SendRotated(co5300Display sd, uint16_t *pixels, int byteLength } } +static void co5300SendHardwareRotated180(co5300Display sd, uint16_t *pixels, int byteLength) +{ + int lines = (byteLength >> 1) / sd->updateWidth; + int row = sd->updateHeight - sd->updateLinesRemaining; + uint16_t x = MODDEF_CO5300_WIDTH - (sd->updateX + sd->updateWidth); + uint16_t y = MODDEF_CO5300_HEIGHT - (sd->updateY + row + lines); + + co5300SetWindow(sd, x, y, sd->updateWidth, lines); + co5300SendColorSync(sd, pixels, byteLength); + sd->updateLinesRemaining -= lines; +} + void co5300AdaptInvalid(void *refcon, CommodettoRectangle r) { // CO5300 requires even-aligned coordinates for partial updates From 11b3c2c9ea7de46a64d850174941617365627166 Mon Sep 17 00:00:00 2001 From: Ayachika Kitazaki Date: Sat, 9 May 2026 13:46:49 +0900 Subject: [PATCH 4/6] Delete examples/drivers/atoms3r-imu-console/main.js --- examples/drivers/atoms3r-imu-console/main.js | 55 -------------------- 1 file changed, 55 deletions(-) delete mode 100644 examples/drivers/atoms3r-imu-console/main.js diff --git a/examples/drivers/atoms3r-imu-console/main.js b/examples/drivers/atoms3r-imu-console/main.js deleted file mode 100644 index 4c39f2adc..000000000 --- a/examples/drivers/atoms3r-imu-console/main.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2026 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 Timer from "timer"; - -const SAMPLE_INTERVAL = 1000; - -let acceleration; -let rotationRate; - -if (!device?.sensor?.IMU) - throw new Error("IMU not available"); - -const imu = new device.sensor.IMU(); - -Timer.repeat(() => { - const sample = imu.sample(); - - acceleration = sample.accelerometer; - rotationRate = sample.gyroscope; - - trace("accelerometer "); - traceVector(acceleration); - trace(" gyroscope "); - traceVector(rotationRate); - trace("\n"); -}, SAMPLE_INTERVAL); - -function traceVector(values) { - if (!values) { - trace("(pending)"); - return; - } - - trace(`x:${format(values.x)} y:${format(values.y)} z:${format(values.z)}`); -} - -function format(value) { - if (undefined === value) - return "n/a"; - if (0 <= value) - return `+${value.toFixed(3)}`; - return value.toFixed(3); -} From 9ae4b8cc462b1a05baacc6961d9d2003d595f673 Mon Sep 17 00:00:00 2001 From: Ayachika Kitazaki Date: Sat, 9 May 2026 13:47:02 +0900 Subject: [PATCH 5/6] Delete examples/drivers/atoms3r-imu-console/manifest.json --- .../drivers/atoms3r-imu-console/manifest.json | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 examples/drivers/atoms3r-imu-console/manifest.json diff --git a/examples/drivers/atoms3r-imu-console/manifest.json b/examples/drivers/atoms3r-imu-console/manifest.json deleted file mode 100644 index aebb5176d..000000000 --- a/examples/drivers/atoms3r-imu-console/manifest.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "include": [ - "$(MODDABLE)/examples/manifest_base.json" - ], - "modules": { - "*": "./main" - }, - "platforms": { - "esp32/m5atom_s3r": {}, - "esp32/m5atom_s3r_cam": {}, - "esp32/m5atom_s3r_m12": {}, - "...": { - "error": "esp32/m5atom_s3r, esp32/m5atom_s3r_cam, and esp32/m5atom_s3r_m12 only" - } - } -} From b8a667c71e1460e734f0a122a6ef96812c0a0ce3 Mon Sep 17 00:00:00 2001 From: Ayachika Kitazaki Date: Sat, 9 May 2026 14:01:28 +0900 Subject: [PATCH 6/6] Add BMI270 polling example ## Summary Add a generic BMI270 polling example under `examples/drivers/sensors/bmi270`. IMU console example with a driver-level sample that can be used from any device with a BMI270 connected over I2C. ## Changes - Add `examples/drivers/sensors/bmi270/poll`. - Import the BMI270 driver directly instead of using a device-specific `device.sensor.IMU` wrapper. - Poll accelerometer, gyroscope, and thermometer samples once per second. - Use `device.I2C.internal` when available, falling back to `device.I2C.default`. ## Testing - `esp32/m5atom_s3r` - `esp32/m5atom_s3r_cam` - `esp32/m5atom_s3r_m12` --- examples/drivers/sensors/bmi270/poll/main.js | 52 +++++++++++++++++++ .../drivers/sensors/bmi270/poll/manifest.json | 10 ++++ 2 files changed, 62 insertions(+) create mode 100644 examples/drivers/sensors/bmi270/poll/main.js create mode 100644 examples/drivers/sensors/bmi270/poll/manifest.json diff --git a/examples/drivers/sensors/bmi270/poll/main.js b/examples/drivers/sensors/bmi270/poll/main.js new file mode 100644 index 000000000..d655a5951 --- /dev/null +++ b/examples/drivers/sensors/bmi270/poll/main.js @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026 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 BMI270 from "embedded:sensor/Accelerometer-Gyroscope-Magnetometer/BMI270"; +import Timer from "timer"; + +const i2c = device.I2C.internal ?? device.I2C.default; + +const sensor = new BMI270({ + sensor: { + ...i2c, + io: device.io.SMBus + } +}); + +Timer.repeat(() => { + const sample = sensor.sample(); + + trace("Accel: "); + traceVector(sample.accelerometer); + trace(" - Gyro: "); + traceVector(sample.gyroscope); + + if (sample.thermometer) + trace(` - Temp: ${sample.thermometer.temperature.toFixed(2)} C`); + + trace("\n"); +}, 1000); + +function traceVector(values) { + if (!values) { + trace("[pending]"); + return; + } + + trace(`[${format(values.x)}, ${format(values.y)}, ${format(values.z)}]`); +} + +function format(value) { + return (undefined === value) ? "n/a" : value.toFixed(3); +} diff --git a/examples/drivers/sensors/bmi270/poll/manifest.json b/examples/drivers/sensors/bmi270/poll/manifest.json new file mode 100644 index 000000000..4fa271040 --- /dev/null +++ b/examples/drivers/sensors/bmi270/poll/manifest.json @@ -0,0 +1,10 @@ +{ + "include": [ + "$(MODDABLE)/examples/manifest_base.json", + "$(MODDABLE)/modules/io/manifest.json", + "$(MODDABLE)/modules/drivers/sensors/bmi270/manifest.json" + ], + "modules": { + "*": "./main" + } +}