<?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: Ishu1519</title>
    <description>The latest articles on DEV Community by Ishu1519 (@ishu1519).</description>
    <link>https://dev.to/ishu1519</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4079020%2F2bc39360-8979-4d45-9bba-34ffa687952f.png</url>
      <title>DEV Community: Ishu1519</title>
      <link>https://dev.to/ishu1519</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ishu1519"/>
    <language>en</language>
    <item>
      <title>Reverse-Engineering SWIO: Why Existing CH32V003 Programmers Fail and How I Built One That Works</title>
      <dc:creator>Ishu1519</dc:creator>
      <pubDate>Sun, 16 Aug 2026 06:42:27 +0000</pubDate>
      <link>https://dev.to/ishu1519/i-built-an-esp32-s3-ch32v003-swio-programmer-51p8</link>
      <guid>https://dev.to/ishu1519/i-built-an-esp32-s3-ch32v003-swio-programmer-51p8</guid>
      <description>&lt;p&gt;I wanted to see if an ESP32-S3 could be used as a programmer for the WCH CH32V003 instead of using a dedicated WCH-Link.&lt;/p&gt;

&lt;p&gt;The final setup is:&lt;/p&gt;

&lt;p&gt;PC → ESP32-S3 → SWIO → CH32V003&lt;/p&gt;

&lt;p&gt;The ESP32-S3 handles the timing-sensitive SWIO communication and the CH32 debug/DMI interface. On top of that I implemented target detection, memory access, flash unlock, page erase, programming, read-back verification and reset/run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;ESP32-S3 N8R2&lt;/li&gt;
&lt;li&gt;CH32V003A4M6 (SOP-16)&lt;/li&gt;
&lt;li&gt;4.7kΩ–10kΩ SWIO pull-up&lt;/li&gt;
&lt;li&gt;CP6208 motor driver&lt;/li&gt;
&lt;li&gt;Small DC motor&lt;/li&gt;
&lt;li&gt;3.7V Li-ion battery&lt;/li&gt;
&lt;li&gt;Breadboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important connections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ESP32-S3 GPIO10 → CH32V003 SWIO&lt;/li&gt;
&lt;li&gt;ESP32-S3 3.3V → CH32V003 VDD&lt;/li&gt;
&lt;li&gt;Common GND&lt;/li&gt;
&lt;li&gt;External pull-up on SWIO&lt;/li&gt;
&lt;li&gt;CH32V003 PC4 → CP6208 control input&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The software stack
&lt;/h2&gt;

&lt;p&gt;The programmer is split into several layers:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
text
PC
 │
 │ Python host tool
 ▼
ESP32-S3
 │
 │ SWIO
 ▼
WCH DMI
 │
 ▼
CH32V003 debug module
 │
 ▼
Abstract commands / program buffer
 │
 ▼
Flash controller

The ESP32-S3 is doing the SWIO timing directly rather than relying on a separate programmer IC.

I used existing open-source CH32/SWIO implementations as references, particularly CNLohr's CH32V003 work and BlueSyncLine's SWIO implementation.

Getting SWIO working

The first versions did not work.

One of the early failures was:

SWIO sync: FAILED
DMCFGR = 0xFFFFFFFF

I had to work through the SWIO startup sequence, timing, receive behavior and physical wiring before getting reliable target responses.

Once it was working, the programmer reported:

SWIO sync: OK
DMI communication: OK
Target detect: OK
CH32 ID = 0x0713BB91
Target memory read: OK

That gave me a stable base for the flash implementation.

Flash programming

I then added the flash controller operations:

flash unlock
64-byte page erase
fast page programming
read-back verification
target reset/run

One useful milestone was observing the flash lock transition:

FLASH_CTLR before unlock: 0x00008080
FLASH_CTLR after unlock:  0x00000200

After that I tested programming and verification using deterministic data.

The programmer was able to write data to the CH32V003 flash and read it back with zero mismatches.

The first real application test

For the first application I built a tiny bare-metal CH32V003 firmware that drives PC4.

Instead of an LED, I used a CP6208 motor driver and a small DC motor.

The first attempt failed even though the firmware had been programmed and verified.

The CH32 was actually running and PC4 was at about 3.3V.

The problem turned out to be the CP6208 input state: the second input was HIGH instead of LOW. That put the driver into the wrong state.

After grounding that input, the motor started.

That was probably the most useful debugging lesson from the entire project: the programming path can be completely correct while the final hardware application is still wrong.

Final host workflow

The programmer now has a Python command-line tool.

For example:

python tools/flash_tool.py --port COM10 --bin firmware/ch32_blink/blink.bin --addr 0x08000000 --reset

The final test performed on real hardware was:

Target detect: OK
Programming: OK
Verification: OK
Reset/run: OK

The programmed CH32V003 then executed the application and drove the motor.

Source code

GitHub:

https://github.com/Ishu1519/esp32s3-ch32-programmer

The repository includes:

ESP32-S3 programmer firmware
CH32V003 target example
Python host flasher
hardware documentation
SWIO/DMI documentation
build instructions
What's next

The current implementation is focused on the CH32V003.

The next interesting question is how far the same ESP32-S3 transport can be extended to other WCH RISC-V devices, and whether more debugging functionality can be added beyond programming.
![ ](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/yfnvocz4re8r801rjvcv.jpeg)

![ ](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/a16hgrlxfi4s5nwi8768.png)# I Built an ESP32-S3 CH32V003 SWIO Programmer

![ ](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/4ubjfdmk7mrzrma7sfz1.jpeg)

![ ](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/eiwbti5i4hotix15pgfu.png)

![ ](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/lb286a1aygcglcaten7f.png)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>esp32</category>
      <category>embedded</category>
      <category>hardware</category>
      <category>riscv</category>
    </item>
  </channel>
</rss>
