<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Michiel's Blog - #pinetime</title><link href="https://tildeweb.nl/~michiel/tags/pinetime.html" rel="alternate"/><link href="https://tildeweb.nl/~michiel/tags/pinetime/atom.xml" rel="self"/><id>https://tildeweb.nl/~michiel/tags/pinetime.html</id><updated>2026-10-26T23:00:00+01:00</updated><subtitle>Blog - #pinetime</subtitle><entry><title>Python on my wrist</title><link href="https://tildeweb.nl/~michiel/python-on-my-wrist.html" rel="alternate"/><published>2026-10-26T23:00:00+01:00</published><updated>2026-10-26T23:00:00+01:00</updated><author><name>Michiel W. Beijen</name></author><id>tag:tildeweb.nl,2026-10-26:/~michiel/python-on-my-wrist.html</id><summary type="html">Reviving PineTime smartwatch running up-to-date MicroPython and wasp-os</summary><content type="html">&lt;p&gt;I have a &lt;a href="https://pine64.org/devices/pinetime/"&gt;PineTime&lt;/a&gt;. It is a cheap, open,
hackable smartwatch from Pine64.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Dutch watch face on my PineTime" class="small" src="images/pinetime-dutchclock.jpg"&gt;&lt;/p&gt;
&lt;h2&gt;It&amp;rsquo;s not an Apple Watch&lt;/h2&gt;
&lt;p&gt;When people see it, they think: that looks like a smartwatch, so it must be a
smartwatch. Like an Apple Watch. It&amp;rsquo;s not! An Apple Watch costs around 700 USD,
and this one 25. Think of it as a programmable Casio with a color display,
Bluetooth, a step counter and a heart rate monitor.&lt;/p&gt;
&lt;p&gt;It really does not have much CPU or memory: a 64 MHz ARM Cortex-M4, 64 KB of
RAM (yes, kilobytes), 512 KB of flash for the firmware and 4 MB of external
flash for files. After the Bluetooth stack and MicroPython itself have had
their share, about 12 KB of RAM is left for the apps. Everything on this watch
has to be small.&lt;/p&gt;
&lt;p&gt;The screen is &lt;em&gt;fine&lt;/em&gt;, though: 240×240 pixels on 1.3 inches, about 260 pixels
per inch. So if anything on it looks blocky in this post, that&amp;rsquo;s on purpose.
However, you can&amp;rsquo;t have an always-on display: the battery would run out too
soon. Apple Watches once had the same limitation, too!&lt;/p&gt;
&lt;p&gt;The watch features an app for aforementioned watch monitor and step counter,
and you can send notifications to it via Bluetooth.&lt;/p&gt;
&lt;h2&gt;How I got here&lt;/h2&gt;
&lt;p&gt;I bought my PineTime in March 2023, for 26.99 USD. It ships with
&lt;a href="https://infinitime.io"&gt;InfiniTime&lt;/a&gt;. That&amp;rsquo;s fine. But InfiniTime is written in
C++, and I run a &lt;a href="https://pythonleiden.nl"&gt;Python group&lt;/a&gt;. So I preferred
&lt;a href="https://github.com/wasp-os/wasp-os"&gt;wasp-os&lt;/a&gt;, a MicroPython-based watch OS.
However, even in 2023 that project was already quite dormant. It was running a
2019 version of MicroPython and was quite hard to build. Recently its
maintainer added a note that wasp-os is
&lt;a href="https://github.com/wasp-os/wasp-os/issues/536"&gt;looking for new maintainers&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Reviving the OS&lt;/h2&gt;
&lt;p&gt;Then I found my watch in a drawer again, and thought it would be nice to work
on it a bit. So I took it upon myself to get wasp-os into shape again. Besides
moving to mainline MicroPython (more on that below), this is what changed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It builds again.&lt;/strong&gt; The Python tooling is
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/3789669ce7e66a986a701ae5fb80c5489f6ea5a2"&gt;managed with &lt;code&gt;uv&lt;/code&gt;&lt;/a&gt;,
  so setting up is one command, and the
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/37c30e605cda4e62e2933d3bbd8a12980f1362c4"&gt;full build works again&lt;/a&gt;,
  including the bootloader bits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faster flashing.&lt;/strong&gt; Updating the firmware over Bluetooth
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/6da72b7c8441330b5b815d2a02a00ddeb9e81bb5"&gt;now uses&lt;/a&gt;
  &lt;a href="https://github.com/hbldh/bleak"&gt;bleak&lt;/a&gt; instead of the deprecated &lt;code&gt;gatttool&lt;/code&gt;,
  and
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/55dcc8c125c8232126266f43fa29d28bfbb40b56"&gt;takes about three and a half minutes&lt;/a&gt;
  instead of over eight. When it fails, it
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/3e19e808ac5e435b71cc5867f9671c04bc187c2c"&gt;tells you why&lt;/a&gt;,
  instead of hiding the errors, as it did before.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;More memory.&lt;/strong&gt; The filesystem on the external flash
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/bcb5c033a4b97d333513c984b648bfec31c48dd2"&gt;now uses the chip&amp;rsquo;s real 4 KB blocks&lt;/a&gt;,
  which got rid of a permanent 4 KB buffer. Free RAM at boot went from 7.1 KB
  to 12.4 KB. On this watch, that&amp;rsquo;s a lot!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It recovers by itself.&lt;/strong&gt; A watchdog
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/ec666c93fb0f75e2f5c2be5a1d6690dab03e2b32"&gt;now resets the watch&lt;/a&gt;
  when it hangs, instead of leaving it dead with Bluetooth still up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No more buzzing.&lt;/strong&gt; A swipe could leave the vibration motor buzzing until
  you reset the watch. That&amp;rsquo;s
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/17a5300b5a8ae1ae82cff62a662e34337ffc7b5b"&gt;fixed&lt;/a&gt;,
  and the
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/22c9e7c3d1c497a1618232233ea9aa898550e744"&gt;motor is driven right&lt;/a&gt;
  now.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Swipes work reliably.&lt;/strong&gt; The touch controller
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/0f597cdc7bf6a7c87ff55b85e77ceca0dc914c34"&gt;now reports when you lift your finger&lt;/a&gt;,
  and in Settings you can
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/96430ce354435e7f4f30462e9e30485e2bc284f7"&gt;swipe along a slider&lt;/a&gt;
  to change it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It survives a flat battery.&lt;/strong&gt; After my holiday, where I did not bring the
  watch, I put it on the charger, it booted and the watch said &lt;strong&gt;INIT HARDWARE
  FAILED&lt;/strong&gt;. A chip had kept the I2C bus stuck. Now it
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/16dadba68771281318be3d16771f6ab37e0c8721"&gt;unsticks the bus at boot&lt;/a&gt;,
  shows the actual error if something still fails, and the clock
  &lt;a href="https://github.com/mbeijen/wasp-os/commit/e9fe31542c25f4c9d837bbade89811a78269f77b"&gt;starts at the build date&lt;/a&gt;
  instead of somewhere in 2020.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Mainline MicroPython&lt;/h2&gt;
&lt;p&gt;wasp-os ran on a fork of MicroPython from 2019, with patches for the watch.
Porting it to mainline MicroPython also took a few changes in MicroPython
itself, so I submitted three pull requests upstream:
&lt;a href="https://github.com/micropython/micropython/pull/19729"&gt;frozen native code&lt;/a&gt;, a
fix for &lt;a href="https://github.com/micropython/micropython/pull/19731"&gt;PWM allocation&lt;/a&gt;
and an
&lt;a href="https://github.com/micropython/micropython/pull/19733"&gt;ADC sample time option&lt;/a&gt;.
All three got merged, so for the first time wasp-os
&lt;a href="https://github.com/mbeijen/wasp-os/commit/a36f2863119b82f072c331be4e2d602a6e2e6cea"&gt;runs on mainline MicroPython&lt;/a&gt;,
and an up-to-date version too. No fork needed! The changes are targeted for
MicroPython 1.30, which is planned for December.&lt;/p&gt;
&lt;h2&gt;Two new watch faces&lt;/h2&gt;
&lt;p&gt;Of course I wanted my own watch faces. The first one is simple: the existing
week clock, but in Dutch, because I&amp;rsquo;m Dutch. Dutch abbreviates weekdays with
two letters, so it says &lt;em&gt;ma 26 okt 2026&lt;/em&gt;. Remember that the memory on this
watch is &lt;em&gt;tiny&lt;/em&gt;, so there&amp;rsquo;s no room for a full multilingual OS. But you can
just translate a watch face, build it, and put your localized version on the
watch. You&amp;rsquo;ll see a picture of it on top of this post.&lt;/p&gt;
&lt;p&gt;The second one is more fun: an 8-bit style clock. The digits come from a tiny
6×7 pixel font, and every font pixel is drawn as a block of 7×7 screen pixels
for the time and 3×3 for the rest. That gives the retro look, and it needs no
font data at all. Below the time and the ISO 8601 date (YYYY-MM-DD, obviously),
it shows the &lt;a href="https://en.wikipedia.org/wiki/Unix_time"&gt;Unix epoch&lt;/a&gt;, counting
every second.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The pixel watch face on my PineTime" class="small" src="images/pinetime-pixelclock.jpg"&gt;&lt;/p&gt;
&lt;h2&gt;The Guido app&lt;/h2&gt;
&lt;p&gt;And then, as a finishing touch and to emphasize the Python angle, I created the
Guido app. It shows a blocky portrait of Guido van Rossum, the creator of
Python. Swipe, and you get the Python logo, then the MicroPython logo, and then
the logo of our own &lt;a href="https://pythonleiden.nl"&gt;Python Leiden&lt;/a&gt; meetup. One more
swipe and you&amp;rsquo;re back at the clock.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The four screens of the Guido app" src="images/wasp-guido-app.png"&gt;&lt;/p&gt;
&lt;p&gt;The portrait is 80×80 pixels in 16 colors, drawn three times larger. That fills
the 240×240 screen and gives it a nice retro look. The logos are drawn sharp,
at full resolution.&lt;/p&gt;
&lt;p&gt;Flash is tight on this watch. The firmware has about 330 KB to work with, and a
single full-screen picture as raw pixels takes 112 KB, a third of that. So I
made a small image format of up to 16 colors per picture plus run-length
encoding, with a
&lt;a href="https://github.com/mbeijen/wasp-os/blob/af007ef849437301b64b8067334bdabe373bd664/apps/guido.py#L141-L181"&gt;decoder of about 40 lines&lt;/a&gt;
that draws them straight to the display. All four pictures together now take
under 12 KB. Still, that&amp;rsquo;s too much for something this silly to include by
default, so the app is optional: you have to add it to your build yourself.&lt;/p&gt;
&lt;p&gt;The original photo is by
&lt;a href="https://commons.wikimedia.org/wiki/File:Guido_van_Rossum_in_PyConUS24_(cropped).jpg"&gt;Kushal Das&lt;/a&gt;,
taken at PyCon US 2024 and licensed
&lt;a href="https://creativecommons.org/licenses/by-sa/4.0/"&gt;CC BY-SA 4.0&lt;/a&gt;, so the blocky
version is shared under the same license.&lt;/p&gt;
&lt;h2&gt;Coming up: the PineTime Pro&lt;/h2&gt;
&lt;p&gt;Pine64 is working on a successor: the
&lt;a href="https://pine64.org/2026/03/28/pinetime_march_2026/"&gt;PineTime Pro&lt;/a&gt;. It gets a
410×502 AMOLED screen, GPS, a dual-core Cortex-M33 and a digital crown. It&amp;rsquo;s
still early, there&amp;rsquo;s no release date yet, and the original PineTime keeps being
made. But that would be a much bigger playground for MicroPython. Guido in
AMOLED!&lt;/p&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;p&gt;Everything is in the
&lt;a href="https://github.com/mbeijen/wasp-os/tree/pinetime-upstream-micropython"&gt;&lt;code&gt;pinetime-upstream-micropython&lt;/code&gt;&lt;/a&gt;
branch of my wasp-os fork.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://news.ycombinator.com/item?id=50255573"&gt;Discussion on Hacker News&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content></entry></feed>