<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aaron Qian</title>
    <description>The latest articles on DEV Community by Aaron Qian (@aq1018).</description>
    <link>https://dev.to/aq1018</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F110125%2F295476a5-bcc7-4e9a-8b47-33950bc265e3.jpeg</url>
      <title>DEV Community: Aaron Qian</title>
      <link>https://dev.to/aq1018</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aq1018"/>
    <language>en</language>
    <item>
      <title>tinyboot v0.4.0 Released — The API is Stable</title>
      <dc:creator>Aaron Qian</dc:creator>
      <pubDate>Thu, 23 Apr 2026 05:10:00 +0000</pubDate>
      <link>https://dev.to/aq1018/tinyboot-v040-released-the-api-is-stable-2h76</link>
      <guid>https://dev.to/aq1018/tinyboot-v040-released-the-api-is-stable-2h76</guid>
      <description>&lt;p&gt;If you've been following tinyboot, you might have noticed there was no&lt;br&gt;
announcement for v0.3.0. That release added CH32V103 support, but things were&lt;br&gt;
still in flux. Crate structure was shifting, APIs were changing, the protocol&lt;br&gt;
was being reworked. I didn't want to write up something that'd be outdated in&lt;br&gt;
two weeks. I wanted to wait until the dust settled. And with v0.4.0, we have&lt;br&gt;
finally arrived at this point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/OpenServoCore/tinyboot" rel="noopener noreferrer"&gt;OpenServoCore/tinyboot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Handbook: &lt;a href="https://openservocore.github.io/tinyboot" rel="noopener noreferrer"&gt;openservocore.github.io/tinyboot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Changelog: &lt;a href="https://github.com/OpenServoCore/tinyboot/blob/main/CHANGELOG.md" rel="noopener noreferrer"&gt;CHANGELOG.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is tinyboot
&lt;/h2&gt;

&lt;p&gt;tinyboot is a minimal bootloader for resource constrained MCUs. It is written&lt;br&gt;
in Rust, and it fits in 1920 bytes of system flash, leaving all user flash free&lt;br&gt;
for your application, except a small page (64 bytes on V003) of user flash to&lt;br&gt;
store boot metadata. It gives you CRC-validated firmware updates over UART with&lt;br&gt;
trial boot and automatic fallback to bootloader service mode when trials run out.&lt;br&gt;
The kind of safe OTA update story you'd expect from a much larger bootloader,&lt;br&gt;
squeezed into the constraints of a $0.22 MCU.&lt;/p&gt;

&lt;p&gt;It's currently focused on the CH32 family, but the core is chip-agnostic and&lt;br&gt;
designed to be portable. If you're interested in bringing tinyboot to another&lt;br&gt;
chip family, see the porting section of the&lt;br&gt;
&lt;a href="https://openservocore.github.io/tinyboot/porting.html" rel="noopener noreferrer"&gt;handbook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm building it as part of &lt;a href="https://dev.to/projects/open-servo-core/"&gt;OpenServoCore&lt;/a&gt;, an&lt;br&gt;
open-source smart servo platform. tinyboot handles the OTA updates via the&lt;br&gt;
existing single wire UART (Dynamixel TTL), so you don't have to tear your robot&lt;br&gt;
apart and open up each servo, unsolder the board just to flash a new firmware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Platform Support
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Family&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CH32V003&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CH32V00x&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;New in v0.4.0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CH32V103&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CH32X03x&lt;/td&gt;
&lt;td&gt;Planned&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  V00x Support
&lt;/h2&gt;

&lt;p&gt;The biggest addition in v0.4.0 is full support for the CH32V00x family: V002,&lt;br&gt;
V004, V005, V006, and V007. This is the release that matters most for&lt;br&gt;
OpenServoCore, because the OSC dev board runs on the CH32V006. Hardware&lt;br&gt;
validation for this release was done on the actual dev board.&lt;/p&gt;

&lt;p&gt;Getting here wasn't entirely smooth. I ran into a hardware issue where the RX&lt;br&gt;
line wouldn't work without driving the inverse TX_EN line, which took a scope&lt;br&gt;
session to figure out. But that's a story for another post. I'm just glad I was&lt;br&gt;
able to finally get RX to work and complete the hardware test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything Fits
&lt;/h2&gt;

&lt;p&gt;TX_EN support used to overflow system flash on some chip variants. That's not&lt;br&gt;
acceptable. TX_EN isn't optional for OpenServoCore. It's required for&lt;br&gt;
half-duplex RS-485 / DXL TTL communication.&lt;/p&gt;

&lt;p&gt;As of v0.4.0, all chip variants compile with TX_EN enabled and fit in system&lt;br&gt;
flash. The V103 was the outlier. It has a split flash layout, and I moved the&lt;br&gt;
UART transport into the second region to make it fit. That trick deserves its&lt;br&gt;
own post. Down the road, that second region also has enough room for a USB&lt;br&gt;
transport, so flashing via USB on V103 is a real possibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crate Restructure
&lt;/h2&gt;

&lt;p&gt;The previous three separate crates (&lt;code&gt;tinyboot-ch32-boot&lt;/code&gt;, &lt;code&gt;-app&lt;/code&gt;, &lt;code&gt;-hal&lt;/code&gt;) are&lt;br&gt;
now merged into a single &lt;code&gt;tinyboot-ch32&lt;/code&gt; crate with &lt;code&gt;boot&lt;/code&gt;, &lt;code&gt;app&lt;/code&gt;, and &lt;code&gt;hal&lt;/code&gt;&lt;br&gt;
modules. There's also a new &lt;code&gt;tinyboot-ch32-rt&lt;/code&gt;, a minimal runtime because&lt;br&gt;
&lt;code&gt;qingke-rt&lt;/code&gt; is too large for system flash.&lt;/p&gt;

&lt;p&gt;This wasn't a planned refactor. It was the natural result of continuous&lt;br&gt;
iteration during tinyboot's early development. I'd rather get the architecture&lt;br&gt;
right early by sacrificing API stability than lock in the wrong abstractions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tinyboot-ch32&lt;/code&gt; is currently git-only and not published to crates.io. It depends&lt;br&gt;
on a git version of &lt;code&gt;ch32-metapac&lt;/code&gt; that includes flash fixes for the V00x family&lt;br&gt;
that haven't been released yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protocol and API Changes
&lt;/h2&gt;

&lt;p&gt;The wire protocol now uses 24-bit addresses instead of 32-bit, freeing the&lt;br&gt;
fourth byte for per-command flags. 24 bits is still 16MB of addressable space,&lt;br&gt;
more than enough for these MCUs. The standalone &lt;code&gt;Flush&lt;/code&gt; command is gone; it's&lt;br&gt;
now a flag on the final &lt;code&gt;Write&lt;/code&gt;. Cleaner on the wire, simpler in the dispatcher,&lt;br&gt;
improves the developer experience, and best of all no size increase due to&lt;br&gt;
zero-cost abstractions via Rust's union types.&lt;/p&gt;

&lt;p&gt;On the API side: &lt;code&gt;BootMode&lt;/code&gt; became &lt;code&gt;RunMode&lt;/code&gt; to separate the concept of boot&lt;br&gt;
flash region selection vs bootloader run mode (handoff or service).&lt;br&gt;
&lt;code&gt;BootClient&lt;/code&gt; was also removed after the crates merge. Boolean parameters in&lt;br&gt;
the public API became semantic enums (&lt;code&gt;Duplex::Half&lt;/code&gt; instead of&lt;br&gt;
&lt;code&gt;half_duplex: true&lt;/code&gt;). Last but not least, flash lock/unlock is now scoped per&lt;br&gt;
operation instead of manual.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug Fixes
&lt;/h2&gt;

&lt;p&gt;Two nasty half-duplex communication bugs, both found during hardware validation&lt;br&gt;
on the dev board:&lt;/p&gt;

&lt;p&gt;The dispatcher wasn't flushing the transport after sending a response. On a&lt;br&gt;
full-duplex UART you'd never notice, but on RS-485 / DXL TTL half-duplex the&lt;br&gt;
data just sits in the buffer and never goes out on the wire.&lt;/p&gt;

&lt;p&gt;The ring buffer wasn't resetting its head/tail pointers after flushing buffered&lt;br&gt;
writes to flash. The buffer was logically empty but the pointers were advanced,&lt;br&gt;
so subsequent writes would eventually wrap incorrectly. I didn't notice this&lt;br&gt;
before because I didn't do back-to-back &lt;code&gt;flash&lt;/code&gt; commands with the &lt;code&gt;tinyboot&lt;/code&gt;&lt;br&gt;
CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docs Overhaul
&lt;/h2&gt;

&lt;p&gt;Documentation has been completely rewritten for users instead of maintainers,&lt;br&gt;
and a user handbook has been created. When the architecture is changing every&lt;br&gt;
release, writing user-facing docs is a losing game. Now that things have&lt;br&gt;
stabilized, it actually makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Stable" Means
&lt;/h2&gt;

&lt;p&gt;To be clear, tinyboot is not production-grade. But as of v0.4.0, it's stable in&lt;br&gt;
the ways that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Architecture stable&lt;/strong&gt;: no more big crate restructures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol stable&lt;/strong&gt;: no more wire format changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Features stable&lt;/strong&gt;: all core features compile and fit on all supported chips.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior stable&lt;/strong&gt;: no obvious bugs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means I'm shifting focus. tinyboot satisfies all of OpenServoCore's needs,&lt;br&gt;
and my attention is moving to rewriting the OSC firmware. I'll still maintain&lt;br&gt;
tinyboot, and issues and PRs are welcome, but active feature development is&lt;br&gt;
pausing for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;USB transport for V103 (the second flash region has room).&lt;/li&gt;
&lt;li&gt;CH32X03x support eventually.&lt;/li&gt;
&lt;li&gt;But the immediate priority is OpenServoCore firmware.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>embedded</category>
      <category>bootloader</category>
      <category>ch32</category>
    </item>
  </channel>
</rss>
