Log
Build logs and progress updates across all projects.
CH32V006 Servo Controller Firmware - DXL 2.0 RX Timing
This is a design note for anyone bringing up a Dynamixel 2.0 servo on the CH32V006. You don’t need prior DXL or RISC-V expertise; read it top to bottom. Protocol reference throughout is the DYNAMIXEL Protocol 2.0 spec . If you’re new here, OpenServoCore is my effort to turn cheap SG90 / MG90 servos into networked smart actuators with sensor feedback, cascade control, and a DYNAMIXEL-style TTL bus. This is the first post in a series on how the CH32V006 firmware meets DXL 2.0’s timing budget on a $0.16 chip (qty. 1000+). Later posts will be linked here as they go live.
CH32V006 Servo Controller Dev Board (Rev. B) Bring-Up - One Trace Cut for a Fresh-Chip Bootstrap
When the first Rev B board came back from PCBWay, I hooked up the WCH-LinkE, fired up the debugger, and tried to connect. The debugger reported no target at all. The board was clearly powered (3.3 V rail LED on, no heat, no smoke), and the LinkE itself was alive, but whatever was on the other end of the SWD lines wasn’t answering. Unlike Rev A where I failed my way to debug success, I didn’t actually have to probe to know what was going on this time. It immediately hit me that the Rev B schematic puts nRST and OPN2 on the same physical pin, and is tied to ground. This means the chip is held in reset forever, no way of changing nRST to GPIO via LinkE… If you’re new here, OpenServoCore is my effort to turn cheap MG90S-class servos into networked smart actuators with sensor feedback, cascade control, and a DYNAMIXEL-style TTL bus. The CH32V006 dev board is the firmware development platform for this project. Rev B is the second revision, announced in April and fabricated and assembled by PCBWay as a sponsored run.
CH32V006 Servo Controller Dev Board (Rev. B) Designed
Rev A took board surgery to power on. Then I hit an RX line that refused to go LOW . Then I noticed a third defect I never wrote up: the differential current sensing on the OPA wasn’t actually differential. Rev B is the respin that fixes all three, plus a handful of features I was going to need anyway. If you’re new here, OpenServoCore is my effort to turn cheap MG90S-class servos into networked smart actuators with sensor feedback, cascade control, and a DYNAMIXEL-style TTL bus. The CH32V006 dev board is the firmware development platform for this project. Rev B is the second revision of that board, routed this week and ready to fab.
CH32V006 Servo Controller Dev Board (Rev. A) - UART RX Stuck High (TTL Buffer / TX_EN Gotcha)
RX wouldn’t go low. The scope showed a perfectly shaped square wave, riding on top of 3.3 V with a whopping 180 mV of swing. Something was holding the line near the rail so hard my USB UART adapter could only nudge it down a couple hundred millivolts. If you’re new here, OpenServoCore is my effort to turn cheap MG90S-class servos into networked smart actuators with sensor feedback, cascade control, and a DYNAMIXEL-style TTL bus. tinyboot is the Rust bootloader for those boards, and this post is what happened the first time I tried to bring it up on the Rev A CH32V006 dev board. The culprit turned out to be the half-duplex TTL front-end. TX_EN isn’t a transmit enable, it’s a mux select, and with it low the buffer was actively pushing 3.3 V back into RX through a 24 mA CMOS output stage. Inside: scope photos, schematic walkthrough, the absurd workaround (assert TX_EN to receive), and why Rev B gets a jumper instead of a firmware fix.
tinyboot v0.4.0 Released — The API is Stable
You may have noticed v0.3.0 came and went without an announcement. That was deliberate. Crates were merging, APIs were shifting, the wire protocol was being reworked, and I didn’t want to ship a writeup that’d be stale in two weeks. v0.4.0 is the release where the dust finally settles. If you’re new here, tinyboot is a minimal Rust bootloader for resource-constrained MCUs. It fits in 1920 bytes of system flash and gives you CRC-validated firmware updates over UART, with trial boot and automatic fallback to service mode. I’m building it as part of OpenServoCore so you can OTA-update servo firmware over the same single-wire DXL TTL bus, without tearing the robot apart. What’s in v0.4.0: full CH32V00x family support (V002 through V007, including the V006 that the OSC dev board runs on), TX_EN now fits in system flash on every supported variant, the three tinyboot-ch32-* crates collapsed into one, 24-bit protocol addresses with per-command flag bits, two nasty half-duplex bugs squashed during dev-board validation, and a docs rewrite aimed at users instead of maintainers. Plus a quiet announcement: with the API stable, active feature work pauses while my attention shifts to the OSC firmware rewrite.
CH32V006 Servo Controller Dev Board (Rev. A) First Spin - Lessons from a Faulty Revision
Three embarrassing mistakes, two failed surgeries, one working board. The 3.3V rail sat at 0.84V, I fed reverse voltage into a $0.22 MCU across multiple power-on cycles, and somehow it still came back to life. If you’re new here, OpenServoCore is my effort to turn cheap MG90S-class servos into networked smart actuators with sensor feedback, cascade control, and a DYNAMIXEL-style TTL bus. The CH32V006 dev board is the firmware development platform for this project. This is the first-spin bringup of Rev A, generously sponsored by PCBWay for both PCB and assembly. What’s inside: PCBWay catching footprint and BOM mistakes I missed (and KiCad’s DRC missed), a wrong house number that sent the boards to my neighbor, mislabeled test points that fed 3.3V into the EN pin, the moment I realized I’d swapped VDD and VCC on the schematic, and an hour of magnet-wire surgery under a magnifier to bring one board back. The CH32V006 is, it turns out, surprisingly tough.
tinyboot v0.2.1 Released - User-Flash Path Fixes
Turns out shipping a bootloader and actually booting real firmware through it are two different things. The user-flash path was quietly broken, and the week after the initial release was mostly spent hunting down the bugs that only show up once an application is sitting behind the bootloader. If you’re new here, tinyboot is my minimal serial bootloader for the CH32V003 and friends, squeezed into 1920 bytes of system flash. The 0.2.0 release got the protocol and the system-flash variant solid. 0.2.1 is the follow-up that makes the user-flash variant actually usable. The headline fixes: a hardcoded mtvec in qingke-rt that pointed the interrupt vector table at the wrong place, APB2 peripherals leaking state from the bootloader into the app, a broken app_version read, an alignment UB in the boot metadata path, and a defmt panic on reset-into-bootloader. Plus another ~180 bytes shaved off the system-flash build, because every byte matters when you have 1920 of them.
tinyboot - Initial Release for CH32V003
While I was waiting for the CH32V006 dev board to arrive, I ended up going on a side quest: making firmware updates less painful for future OpenServoCore-based servos. Once a servo is built into a system, it is not really practical to pull it back out, open the case, desolder the board, and hook up a debugger just to flash new firmware. Updating over the existing one-wire UART bus is a must-have feature if I wanted to keep both you and me sane. And that is how tinyboot was born. If you’re new here, OpenServoCore is my effort to turn cheap MG90S-class servos into networked smart actuators with sensor feedback, cascade control, and a DYNAMIXEL-style TTL bus. tinyboot is the bootloader half of that story: a small Rust bootloader that lives where the WCH factory bootloader normally sits, so a deployed servo can take firmware updates over the same one-wire UART bus it already speaks on. This first release targets the CH32V003, fits in 1920 bytes of system flash, and ships with CRC validation, a trial-boot counter that rolls back bad updates on its own, a modular transport layer (standard UART plus DXL TTL with TX_EN), and a tinyboot-cli for driving updates from the host. It is, in the author’s words, a little unhinged on the inside. But it works.
tinyboot - A Minimal Rust Bootloader for CH32
A Rust bootloader that squeezes into the CH32V003’s 1920-byte system flash, with CRC16 validation and trial boot, and still leaves the entire 16KB user flash free for your application. tinyboot is the bootloader half of OpenServoCore , my effort to turn cheap MG90S-class servos into networked smart actuators. Servos need field updates over the same TTL bus they take commands on, and they have very little flash to spare. So tinyboot has to be tiny, robust enough to never brick the part, and portable across the CH32 family. This post is the project overview: what tinyboot is, the feature set, the chips it targets (V003 working today, V103 in progress, the rest of the V0/V2/V3/X line planned), and the transports it speaks (UART and RS485 working, USB planned). If “Rust in 1920 bytes” sounds like a typo, that’s fair, it surprised me too.
CH32V006 Servo Controller Dev Board - Initial Design
Swapping a $1.85 STM32F301 for a $0.22 CH32V006 sounds like a one-line BOM edit. It turned into the most complex board I’ve designed yet, weeks in KiCad, and several rounds of Reddit PCB review before I felt good about sending it out. If you’re new here, OpenServoCore is my effort to turn low-cost servos like the MG90S into smart actuators with cascade control and DYNAMIXEL-style communication over single-wire UART (DXL TTL). This board is the firmware development platform for that move, and the first dev board to actually exercise the new MCU end to end. Inside: three-way power ORing for USB-C, bench supply, and 1S/2S LiPo direct, low-side current sensing through CH32V006’s internal PGA op-amps, motor terminal voltage sensing for back-EMF, an NTC to replace the STM32’s internal temperature sensor, single-wire DXL TTL through a 74LVC2G241 buffer, and probably more test points than strictly necessary. Off to PCBWay, since JLCPCB was out of CH32V006F8P6 the day I hit submit.
Thanksgiving Night Build: Dual ITR1204 IR Sensor Test Module
The house got quiet on Thanksgiving night, so I opened KiCad and finally started the encoder board that’s been sitting in my backlog: a $0.60, dual-channel reflective IR sensor that’s the first step toward closing the loop on cheap servos. If you’re new here, OpenServoCore is my effort to turn cheap MG90S-class servos into networked smart actuators with sensor feedback, cascade control, and a DYNAMIXEL-style TTL bus. To get there I need a reliable, low-cost way to measure motor motion, and this board is Part 1 of that: a tiny ITR1204-based PCB that handles the detection stage. Part 2 will be the MCU and comparator front-end once these come back from fab. What’s in this post: the schematic and 4-layer layout, a full walk through four operating modes (5 V and 3.3 V, digital and analog) with the math behind each resistor pair, a BOM that lands at roughly $0.60 per board, and JLCPCB ordering notes for both bare-PCB and full PCBA paths. The board is intentionally retunable by swapping two resistor pairs, so the same footprint serves as a quadrature edge sensor or a direct-to-ADC analog encoder.
OpenServoCore - Cascaded PID Loop Working
Finally got cascaded PID stable. Overshoot down from 15% to 2%, settling time 120ms.
OpenServoCore - Migration from STM32 to CH32 RISC-V
Switching from STM32F301 to CH32V303 to cut BOM cost.
OpenServoCore - Open-Source Smart Servo Platform (CH32V006)
Democratize Robotics For Everyone