diff --git a/BoardSupport.md b/BoardSupport.md index e6787f1..881edbe 100644 --- a/BoardSupport.md +++ b/BoardSupport.md @@ -113,6 +113,24 @@ static inline void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callb #define PIN_TO_SERVO(p) (p) ``` +## Arduino UNO Q + +The Arduino UNO Q is a Zephyr RTOS-based board built on the STMicroelectronics STM32U585AI (Cortex-M33). It uses the [arduino/ArduinoCore-zephyr](https://github.com/arduino/ArduinoCore-zephyr) core (FQBN `arduino:zephyr:unoq`), which is installed through the Arduino IDE Boards Manager after adding the package index `https://downloads.arduino.cc/packages/package_zephyr_index.json`. + +Unlike the classic Uno, sketches on the UNO Q are compiled to a freestanding ELF that is loaded dynamically by a precompiled Zephyr firmware. Any pin that does not appear in the variant's `pwm-pin-gpios`, `adc-pin-gpios`, `i2cs`, or `spis` devicetree properties will not be driven by the corresponding peripheral, so Firmata only exposes the Arduino R3 header pins plus the onboard RGB LED. + +| Property | Value | +| ----------- | ------------------------------------ | +| Number of Pins | 22 user-addressable (14 digital D0-D13 + 6 analog A0-A5 as D14-D19 + 2 dedicated I2C D20/D21), plus the onboard RGB LED at `LED_BUILTIN` (pin 51) | +| Number of analog inputs | 6 (14-bit ADC) | +| Flash Memory | 2 MB | +| RAM | 786 KB | +| PWM capable pins | D2, D3, D5-D13, D20, D21, `LED_BUILTIN` (D0/D1 are reserved for USART1 and not PWM-enabled in the default variant) | +| Built-in LED | Yes, the `led3_green` RGB channel exposed as `LED_BUILTIN`; no LED is wired to D13 | +| I2C, Bus 0 | D20 SDA (PB11), D21 SCL (PB10) on I2C2 | +| SPI, Bus 0 | D10 SS (PB9), D11 MOSI (PB15), D12 MISO (PB14), D13 SCK (PB13) on SPI2 | +| Serial1 | D0 RX (PB7), D1 TX (PB6) on USART1 | + ### I2C The Raspberry Pi Pico [datasheet](https://datasheets.raspberrypi.org/pico/Pico-R3-A4-Pinout.pdf) states that the diff --git a/examples/ConfigurableFirmata/ConfigurableFirmata.ino b/examples/ConfigurableFirmata/ConfigurableFirmata.ino index 179a527..17efc01 100644 --- a/examples/ConfigurableFirmata/ConfigurableFirmata.ino +++ b/examples/ConfigurableFirmata/ConfigurableFirmata.ino @@ -16,9 +16,10 @@ const int NETWORK_PORT = 27016; // #define ENABLE_ONE_WIRE -// Note that the SERVO module currently is not supported on ESP32. So either disable this or patch the library -#ifndef ESP32 -#define ENABLE_SERVO +// Note that the SERVO module currently is not supported on ESP32 or on the Zephyr-based Arduino cores +// (e.g. Arduino UNO Q), which do not ship a Servo library. So either disable this or patch the library. +#if !defined(ESP32) && !defined(ARDUINO_ARCH_ZEPHYR) +#define ENABLE_SERVO #endif // #define ENABLE_ACCELSTEPPER @@ -166,7 +167,12 @@ void initTransport() } Firmata.begin(serverStream); Firmata.blinkVersion(); // Because the above doesn't do it. -#else +#elif defined(ARDUINO_UNO_Q) + // On the Arduino UNO Q, the STM32's Serial1 is bridged to /dev/ttyHS1 on the + // Linux side. The default `Serial` is USB-CDC and is not reachable from Linux. + Serial1.begin(115200); + Firmata.begin(Serial1); +#else Firmata.begin(115200); #endif } diff --git a/src/ConfigurableFirmata.cpp b/src/ConfigurableFirmata.cpp index a7b2e6d..0967e18 100644 --- a/src/ConfigurableFirmata.cpp +++ b/src/ConfigurableFirmata.cpp @@ -24,6 +24,40 @@ extern "C" { #include } +#if defined(ARDUINO_ARCH_ZEPHYR) +// The Zephyr Arduino core builds sketches as LLEXTs against picolibc. libc's vsnprintf +// is not reliably resolvable by the LLEXT loader, but Zephyr's cbvprintf is exported +// (see ArduinoCore-zephyr loader/llext_exports.c). Wrap it to provide a drop-in +// vsnprintf with matching semantics. +#include + +static int firmata_zephyr_vsnprintf_cb(int c, void *ctx_raw) +{ + struct { char *buf; size_t size; size_t pos; } *ctx = + (decltype(ctx))ctx_raw; + if (ctx->pos + 1 < ctx->size) { + ctx->buf[ctx->pos] = (char)c; + } + ctx->pos++; + return c; +} + +static int firmata_zephyr_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) +{ + struct { char *buf; size_t size; size_t pos; } ctx = { str, size, 0 }; + // Older Zephyr headers shipped with the Arduino core declare cbprintf_cb as a + // K&R-style (untyped) function pointer, so cast explicitly to satisfy C++. + int written = cbvprintf((cbprintf_cb)firmata_zephyr_vsnprintf_cb, &ctx, fmt, ap); + if (size > 0) { + size_t term = ctx.pos < size ? ctx.pos : size - 1; + str[term] = '\0'; + } + return written; +} + +#define vsnprintf firmata_zephyr_vsnprintf +#endif + //****************************************************************************** //* Support Functions //****************************************************************************** diff --git a/src/utility/Boards.h b/src/utility/Boards.h index e69d544..e9171a1 100644 --- a/src/utility/Boards.h +++ b/src/utility/Boards.h @@ -802,6 +802,37 @@ static inline void attachInterrupt(pin_size_t interruptNumber, voidFuncPtr callb #define PIN_TO_PWM(p) (p) #define PIN_TO_SERVO(p) (p) +// Arduino UNO Q (Zephyr-based, STM32U585AI, Arduino R3 pinout) +// FQBN: arduino:zephyr:unoq (arduino/ArduinoCore-zephyr) +#elif defined(ARDUINO_UNO_Q) +#define TOTAL_ANALOG_PINS 6 +// LED_BUILTIN (led3_green on PH11) is index 51 in the variant's digital-pin-gpios list, +// so TOTAL_PINS is sized to cover it even though only a subset are user-addressable. +#define TOTAL_PINS 52 +#define TOTAL_DEFAULT_PINS 22 // 14 digital (D0-D13) + 6 analog (A0-A5 as D14-D19) + 2 I2C (D20 SDA, D21 SCL) +#define VERSION_BLINK_PIN LED_BUILTIN +#define PIN_SERIAL1_RX 0 +#define PIN_SERIAL1_TX 1 +#define IS_PIN_DIGITAL(p) (((p) >= 2 && (p) < TOTAL_DEFAULT_PINS) || (p) == LED_BUILTIN) +#define FIRMATA_IS_PIN_ANALOG(p) ((p) >= 14 && (p) < 14 + TOTAL_ANALOG_PINS) +// PWM-capable pins per the variant overlay's pwm-pin-gpios; D0/D1 are disabled due to the USART1 conflict. +#define FIRMATA_IS_PIN_PWM(p) ((p) == 2 || (p) == 3 || ((p) >= 5 && (p) <= 13) || (p) == 20 || (p) == 21 || (p) == LED_BUILTIN) +#define IS_PIN_SERVO(p) (IS_PIN_DIGITAL(p) && (p) - 2 < MAX_SERVOS) +#define IS_PIN_I2C(p) ((p) == 20 || (p) == 21) // SDA = D20 (PB11), SCL = D21 (PB10) on I2C2 +// The Zephyr Arduino core does not expose the standard PIN_SPI_* / SS / MOSI / MISO / SCK symbols, +// so provide them here pointing at SPI2 (the bus exposed on the R3 header). +#define PIN_SPI_SS 10 +#define PIN_SPI_MOSI 11 +#define PIN_SPI_MISO 12 +#define PIN_SPI_SCK 13 +#define IS_PIN_SPI(p) ((p) == PIN_SPI_SS || (p) == PIN_SPI_MOSI || (p) == PIN_SPI_MISO || (p) == PIN_SPI_SCK) +#define IS_PIN_SERIAL(p) ((p) == 0 || (p) == 1) // USART1 on D0 (RX) / D1 (TX) +#define PIN_TO_DIGITAL(p) (p) +#define PIN_TO_ANALOG(p) ((p) - 14) +#define PIN_TO_PWM(p) PIN_TO_DIGITAL(p) +#define PIN_TO_SERVO(p) ((p) - 2) +#define DEFAULT_ADC_RESOLUTION 14 // STM32U5 ADC runs at 14 bits per the variant devicetree + // Arduino UNO R4 Minima and Wifi // The pinout is the same as for the classical UNO R3 #elif defined(ARDUINO_UNOR4_MINIMA) || defined(ARDUINO_UNOR4_WIFI)