[{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eFollowing my previous exploration with the Adafruit RP2040 Feather and custom ThinkInk e-paper drivers, I wanted to take the next logical step in embedded Rust: building cross-platform, hardware-agnostic drivers that can jump between entirely different architectures without breaking a sweat.\u003c/p\u003e\n\u003cp\u003eI recently received the \u003cstrong\u003eAdafruit ESP32-C6 Feather (PID 5933)\u003c/strong\u003e, featuring a single-core RISC-V SoC. To pair with it, I developed the \u003ca class=\"ng-star-inserted\" href=\"https://crates.io/crates/modulino\" target=\"_blank\"\u003emodulino\u003c/a\u003e Rust library—a pure \u003ccode\u003eno_std\u003c/code\u003e driver that currently supports all \u003cstrong\u003e12 modules\u003c/strong\u003e in the Arduino Modulino family.\u003c/p\u003e\n\u003cp\u003eThanks to the Feather's built-in \u003cstrong\u003eSTEMMA QT / Qwiic\u003c/strong\u003e connector, getting this setup running requires absolutely zero soldering. You can hardware-verify your embedded apps by simply daisy-chaining the modules together over a standard 100kHz I2C bus.\u003c/p\u003e\n\u003ch2\u003e1. Setting Up the RISC-V Environment\u003c/h2\u003e\n\u003cp\u003eUnlike ARM Cortex-M or RP2040 targets, the ESP32-C6 utilizes a 32-bit RISC-V core. We can easily configure our environment using standard Rust toolchains and \u003ccode\u003eespflash\u003c/code\u003e:\u003c/p\u003e\n\u003cp\u003e\u003cspan class=\"ng-tns-c2725152751-51\"\u003e\u003c/span\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"# Add the bare-metal RISC-V target toolchain\nrustup target add riscv32imac-unknown-none-elf\n\n# Install the flashing tool for Espressif chips\ncargo install espflash","metadata":{"language":"terminal","linenums":false,"filename":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eMake sure your project's \u003ccode\u003e.cargo/config.toml\u003c/code\u003e is configured to leverage \u003ccode\u003eespflash\u003c/code\u003e for seamless flashing and serial monitoring over the built-in USB-JTAG connection:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"[target.riscv32imac-unknown-none-elf]\nrunner = \"espflash flash --monitor\"","metadata":{"language":"auto","linenums":false,"filename":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n        \u003ch2\u003e2. Cargo Dependencies (\u003ccode\u003eCargo.toml\u003c/code\u003e)\u003c/h2\u003e\n\u003cp\u003eHere is the setup for your dependencies. Note that we are using the modern \u003cstrong\u003e0.2.0\u003c/strong\u003e release of the \u003ccode\u003emodulino\u003c/code\u003e crate, which natively interfaces with any hardware layer implementing the \u003ccode\u003eembedded-hal\u003c/code\u003e 1.0 I2C traits.\u003c/p\u003e\n      \n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"[package]\nname = \"esp32c6-modulino-examples\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\n# Espressif bare-metal hardware abstraction layer\nesp-hal = { version = \"0.22.0\", features = [\"esp32c6\"] } \nesp-backtrace = { version = \"0.14.0\", features = [\"esp32c6\", \"panic-handler\", \"print-uart\"] }\nesp-println = { version = \"0.12.0\", features = [\"esp32c6\", \"log\"] }\n\n# The hardware-agnostic Modulino driver crate\nmodulino = \"0.2.0\"","metadata":{"language":"auto","linenums":false,"filename":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n        \u003ch2\u003e3. Initializing the Shared I2C Bus on the C6\u003c/h2\u003e\n\u003cp\u003eOn the Adafruit ESP32-C6 Feather, the hardware layout maps the STEMMA QT connector directly to \u003cstrong\u003eGPIO 19 (SDA)\u003c/strong\u003e and \u003cstrong\u003eGPIO 20 (SCL)\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eBecause the \u003ccode\u003emodulino\u003c/code\u003e driver is fully decoupled from the hardware, you simply initialize the standard \u003ccode\u003eI2c\u003c/code\u003e peripheral via \u003ccode\u003eesp-hal\u003c/code\u003e at 100kHz and hand it directly over to the driver constructors.\u003c/p\u003e\n\u003cp\u003eBelow is an operational template showcasing how to spin up a couple of different modules sequentially or concurrently from the same hardware initialization block:\u003c/p\u003e\n      \n\n\n\n","metadata":{}},{"element_type":"code","content":"#![no_std]\n#![no_main]\n\nuse esp_backtrace as _;\nuse esp_hal::{\n    clock::ClockControl,\n    gpio::Io,\n    i2c::I2c,\n    peripherals::Peripherals,\n    prelude::*,\n    system::SystemControl,\n    delay::Delay,\n};\nuse esp_println::println;\n\n// Import your chosen modules from the crate\nuse modulino::{Pixels, Buttons, Thermo, Color};\n\n#[entry]\nfn main() -\u003e ! {\n    // 1. Core peripheral and clock configuration\n    let peripherals = Peripherals::take();\n    let system = SystemControl::new(peripherals.SYSTEM);\n    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();\n    let mut delay = Delay::new(\u0026clocks);\n\n    // 2. Configure I2C Pins via IO MUX\n    let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);\n    let sda = io.pins.gpio19; // Adafruit Feather C6 STEMMA QT SDA\n    let scl = io.pins.gpio20; // Adafruit Feather C6 STEMMA QT SCL\n\n    // 3. Initialize the I2C Bus at the standard 100 kHz Modulino frequency\n    let i2c = I2c::new(\n        peripherals.I2C0,\n        sda,\n        scl,\n        100_u32.kHz(),\n        \u0026clocks,\n    );\n\n    // 4. Instantiating drivers is plug-and-play!\n    // For single-ownership testing, move the bus directly in.\n    // (Note: For multi-module setups, wrap the bus in a shared manager like embedded-hal-bus)\n    let mut pixels = Pixels::new(i2c).unwrap();\n\n    println!(\"Modulino initialization successful! Starting main loop...\");\n\n    loop {\n        // Blink the 8 RGB LEDs on the Pixels module\n        pixels.set_all_color(Color::RED, 25).unwrap();\n        pixels.show().unwrap();\n        delay.delay_ms(500_u32);\n\n        pixels.set_all_color(Color::BLUE, 25).unwrap();\n        pixels.show().unwrap();\n        delay.delay_ms(500_u32);\n    }\n}","metadata":{"language":"javascript","linenums":false,"filename":""}},{"element_type":"text","content":"\n        \u003ch2\u003e4. Supporting the Complete 12-Module Ecosystem\u003c/h2\u003e\n\u003cp\u003eThe ultimate strength of the \u003ccode\u003emodulino\u003c/code\u003e architecture is that it brings uniform, strongly typed Rust patterns to all 12 modules over the daisy-chained STEMMA QT line.\u003c/p\u003e\n\u003cp\u003eDepending on which hardware you chain to your Feather, swapping modules into your logic takes only a few lines of clean code:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eButtons (\u003ccode\u003eButtons\u003c/code\u003e):\u003c/strong\u003e Instantly intercept tactile input and toggle the three integrated hardware indicator LEDs cleanly via \u003ccode\u003ebuttons.read()?\u003c/code\u003e and \u003ccode\u003ebuttons.update_leds()?\u003c/code\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eThermo (\u003ccode\u003eThermo\u003c/code\u003e):\u003c/strong\u003e Reads environment data directly via an underlying \u003ccode\u003ehs3003\u003c/code\u003e implementation using a provided hardware delay.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eMovement (\u003ccode\u003eMovement\u003c/code\u003e):\u003c/strong\u003e Exposes 3-axis accelerometer and gyroscope metrics from the on-board LSM6DSOX IMU sensor mapped directly into clear coordinate structs.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eKnob, Joystick, Buzzer, Distance, Vibro, LatchRelay, and more:\u003c/strong\u003e Every single one maps directly to native structs implementing zero-overhead drivers.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2\u003eWhy This Architecture Wins\u003c/h2\u003e\n\u003cp\u003eBy targeting the \u003ccode\u003eembedded-hal\u003c/code\u003e ecosystem abstraction layer rather than pinning code to a specific register layout, we achieve true portability. The exact same application layer code can be compiled for an RP2040, a bare-metal Cortex-M microcontroller, or an Espressif RISC-V core like the ESP32-C6.\u003c/p\u003e\n\u003cp\u003eCheck out the full suite of hardware-tested examples, browse the source code, or grab the crate for your next plug-and-play project:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eCrates.io:\u003c/strong\u003e \u003ca class=\"ng-star-inserted\" href=\"https://crates.io/crates/modulino\" target=\"_blank\"\u003ecrates.io/crates/modulino\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eGitHub Repository:\u003c/strong\u003e \u003ca class=\"ng-star-inserted\" href=\"https://github.com/melastmohican/modulino-rs\" target=\"_blank\"\u003egithub.com/melastmohican/modulino-rs\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eGithub Repository\u003c/strong\u003e: \u0026nbsp;\u003ca href=\"https://github.com/melastmohican/adafruit-feather-esp32c6-discovery\"\u003egithub.com/melastmohican/adafruit-feather-esp32c6-discovery\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n      ","metadata":{}}]