This commit is contained in:
David Eisinger
2025-10-04 14:02:39 -04:00
parent 5ed14b56e1
commit 92d64da169
6 changed files with 1868 additions and 6 deletions

View File

@@ -0,0 +1,203 @@
[1]
BogdanTheGeek's Blog
• Menu ▾
□ [2]About
□ [3]Insights
□ [4]Projects
□ [5]Thoughts
• [6]About
• [7]Insights
• [8]Projects
• [9]Thoughts
[10]Hosting a WebSite on a Disposable Vape
2025-09-13Bogdan Ionescu6 min read (1266 words)[11]source [12]report issue
#[13]programming #[14]arm #[15]tools #[16]electronics Hosting a WebSite on
a Disposable Vape
Preface[17]#
This article is NOT served from a web server running on a disposable vape. If
you want to see the real deal, click [18]here. The content is otherwise
identical.
Background[19]#
For a couple of years now, I have been collecting disposable vapes from friends
and family. Initially, I only salvaged the batteries for “future” projects
(Its not hoarding, I promise), but recently, disposable vapes have gotten more
advanced. I wouldnt want to be the lawyer who one day will have to argue how a
device with USB C and a rechargeable battery can be classified as “disposable”.
Thankfully, I dont plan on pursuing law anytime soon.
Last year, I was tearing apart some of these fancier pacifiers for adults when
I noticed something that caught my eye, instead of the expected black blob of
goo hiding some ASIC (Application Specific Integrated Circuit) I see a little
integrated circuit inscribed “PUYA”. I dont blame you if this name doesnt
excite you as much it does me, most people have never heard of them. They are
most well known for their flash chips, but I first came across them after
reading Jay Carlsons blog post about [20]the cheapest flash microcontroller
you can buy. They are quite capable little ARM Cortex-M0+ micros.
Over the past year I have collected quite a few of these PY32 based vapes, all
of them from different models of vape from the same manufacturer. Its not my
place to do free advertising for big tobacco, so I wont mention the brand I
got it from, but if anyone who worked on designing them reads this, thanks for
labeling the debug pins!
What are we working with[21]#
The chip is marked PUYA C642F15, which wasnt very helpful. I was pretty sure
it was a PY32F002A, but after poking around with [22]pyOCD, I noticed that the
flash was 24k and we have 3k of RAM. The extra flash meant that it was more
likely a PY32F002B, which is actually a very different chip.^[23]1
So here are the specs of a microcontroller so bad, its basically disposable:
• 24MHz Coretex M0+
• 24KiB of Flash Storage
• 3KiB of Static RAM
• a few peripherals, none of which we will use.
You may look at those specs and think that its not much to work with. I dont
blame you, a 10y old phone can barely load google, and this is about 100x
slower. I on the other hand see a blazingly fast web server.
Getting online[24]#
The idea of hosting a web server on a vape didnt come to me instantly. In
fact, I have been playing around with them for a while, but after writing my
post on [25]semihosting, the penny dropped.
If you dont feel like reading that article, semihosting is basically syscalls
for embedded ARM microcontrollers. You throw some values/pointers into some
registers and call a breakpoint instruction. An attached debugger interprets
the values in the registers and performs certain actions. Most people just use
this to get some logs printed from the microcontroller, but they are actually
bi-directional.
If you are older than me, you might remember a time before Wi-Fi and Ethernet,
the dark ages, when you had to use dial-up modems to get online. You might also
know that the ghosts of those modems still linger all around us. Almost all USB
serial devices actually emulate those modems: a 56k modem is just 57600 baud
serial device. Data between some of these modems was transmitted using a
protocol called SLIP (Serial Line Internet Protocol).^[26]2
This may not come as a surprise, but Linux (and with some tweaking even macOS)
supports SLIP. The slattach utility can make any /dev/tty* send and receive IP
packets. All we have to do is put the data down the wire in the right format
and provide a virtual tty. This is actually easier than you might imagine,
pyOCD can forward all semihosting through a telnet port. Then, we use socat to
link that port to a virtual tty:
pyocd gdb -S -O semihost_console_type=telnet -T $(PORT) $(PYOCDFLAGS) &
socat PTY,link=$(TTY),raw,echo=0 TCP:localhost:$(PORT),nodelay &
sudo slattach -L -p slip -s 115200 $(TTY) &
sudo ip addr add 192.168.190.1 peer 192.168.190.2/24 dev sl0
sudo ip link set mtu 1500 up dev sl0
Ok, so we have a “modem”, but thats hardly a web server. To actually talk TCP/
IP, we need an IP stack. There are many choices, but I went with [27]uIP
because its pretty small, doesnt require an RTOS, and its easy to port to
other platforms. It also, helpfully, comes with a very minimal HTTP server
example.
After porting the SLIP code to use semihosting, I had a working web server&
mldr;half of the time. As with most highly optimised libraries, uIP was
designed for 8 and 16-bit machines, which rarely have memory alignment
requirements. On ARM however, if you dereference a u16 *, you better hope that
address is even, or youll get an exception. The uip_chksum assumed u16
alignment, but the script that creates the filesystem didnt. I actually
decided to modify a bit the structure of the filesystem to make it a bit more
portable. This was my first time working with perl and I have to say, its
quite well suited to this kind of task.
Blazingly fast[28]#
So how fast is a web server running on a disposable microcontroller. Well,
initially, not very fast. Pings took ~1.5s with 50% packet loss and a simple
page took over 20s to load. Thats so bad, its actually funny, and I kind of
wanted to leave it there.
However, the problem was actually between the seat and the steering wheel the
whole time. The first implementation read and wrote a single character at a
time, which had a massive overhead associated with it. I previously benchmarked
semihosting on this device, and I was getting ~20KiB/s, but uIPs SLIP
implementation was designed for very low memory devices, so it was serialising
the data byte by byte. We have a whopping 3kiB of RAM to play with, so I added
a ring buffer to cache reads from the host and feed them into the SLIP poll
function. I also split writes in batches to allow for escaping.
Now this is what I call blazingly fast! Pings now take 20ms, no packet loss and
a full page loads in about 160ms. This was using almost all of the RAM, but I
could also dial down the sizes of the buffer to have more than enough headroom
to run other tasks. The project repo has everything set to a nice balance
latency and RAM usage:
Memory region Used Size Region Size %age Used
FLASH: 5116 B 24 KB 20.82%
RAM: 1380 B 3 KB 44.92%
For this blog however, I paid for none of the RAM, so Ill use all of the RAM.
As you may have noticed, we have just under 20kiB (80%) of storage space. That
may not be enough to ship all of React, but as you can see, its more than
enough to host this entire blog post. And this is not just a static page
server, you can run any server-side code you want, if you know C that is.
Just for fun, I added a json api endpoint to get the number of requests to the
main page (since the last crash) and the unique ID of the microcontroller.
Resources[29]#
• [30]Code for this project
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. While getting things together for this post, I came across [31]this project
that correctly identified these MCUs as PY32C642, which are pretty much
identical to the 002B. [32]↩︎
2. Later modems used PPP (Point-to-Point Protocol) [33]↩︎
© 2025 Bogdan Ionescu
References:
[1] https://bogdanthegeek.github.io/blog/
[2] https://bogdanthegeek.github.io/blog/about
[3] https://bogdanthegeek.github.io/blog/insights
[4] https://bogdanthegeek.github.io/blog/projects
[5] https://bogdanthegeek.github.io/blog/thoughts
[6] https://bogdanthegeek.github.io/blog/about
[7] https://bogdanthegeek.github.io/blog/insights
[8] https://bogdanthegeek.github.io/blog/projects
[9] https://bogdanthegeek.github.io/blog/thoughts
[10] https://bogdanthegeek.github.io/blog/projects/vapeserver/
[11] https://github.com/BogdanTheGeek/blog/tree/main/content/projects/vapeserver.md
[12] https://github.com/BogdanTheGeek/blog/issues/new?template=corrections.md&title=[Correction]:%20Hosting%20a%20WebSite%20on%20a%20Disposable%20Vape
[13] https://bogdanthegeek.github.io/blog/tags/programming/
[14] https://bogdanthegeek.github.io/blog/tags/arm/
[15] https://bogdanthegeek.github.io/blog/tags/tools/
[16] https://bogdanthegeek.github.io/blog/tags/electronics/
[17] https://bogdanthegeek.github.io/blog/projects/vapeserver/#preface
[18] http://ewaste.fka.wtf/
[19] https://bogdanthegeek.github.io/blog/projects/vapeserver/#background
[20] https://jaycarlson.net/2023/02/04/the-cheapest-flash-microcontroller-you-can-buy-is-actually-an-arm-cortex-m0/
[21] https://bogdanthegeek.github.io/blog/projects/vapeserver/#what-are-we-working-with
[22] http://pyocd.io/
[23] https://bogdanthegeek.github.io/blog/projects/vapeserver/#fn:1
[24] https://bogdanthegeek.github.io/blog/projects/vapeserver/#getting-online
[25] https://bogdanthegeek.github.io/blog/insights/jlink-rtt-for-the-masses/
[26] https://bogdanthegeek.github.io/blog/projects/vapeserver/#fn:2
[27] https://github.com/adamdunkels/uip/tree/uip-0-9
[28] https://bogdanthegeek.github.io/blog/projects/vapeserver/#blazingly-fast
[29] https://bogdanthegeek.github.io/blog/projects/vapeserver/#resources
[30] https://github.com/BogdanTheGeek/semihost-ip
[31] https://github.com/grahamwhaley/py32c642_vape
[32] https://bogdanthegeek.github.io/blog/projects/vapeserver/#fnref:1
[33] https://bogdanthegeek.github.io/blog/projects/vapeserver/#fnref:2