<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="/atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://duggan.ie/tag/rust/</id>
  <title type="text">@duggan — rust</title>
  <updated>2026-03-03T11:17:43.000Z</updated>
  <author>
    <name>Ross Duggan</name>
    <email>ross@duggan.ie</email>
    <uri>https://duggan.ie/</uri>
  </author>
  <icon>https://duggan.ie/favicon.ico</icon>
  <link href="https://duggan.ie/tag/rust/atom.xml" rel="first"/>
  <link href="https://duggan.ie/tag/rust/atom.xml?page=1" rel="last"/>
  <link href="https://duggan.ie/tag/rust/atom.xml" rel="self"/>
  <logo>https://duggan.ie/og-image.png</logo>
  <rights type="text">All rights reserved 2026, Ross Duggan</rights>
  <subtitle type="text">Posts tagged rust</subtitle>
  <entry>
    <id>https://duggan.ie/posts/building-a-stripe-dashboard-with-an-esp32-desktop-clock-and-rust</id>
    <title type="text">Building a Stripe dashboard with an ESP32 desktop clock and Rust</title>
    <updated>2026-02-13T11:27:12.000Z</updated>
    <author>
      <name>Ross Duggan</name>
      <email>ross@duggan.ie</email>
      <uri>https://duggan.ie/</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;A few weeks ago I built some custom firmware to turn the &lt;/span&gt;&lt;a href="https://www.ulanzi.com/products/ulanzi-pixel-smart-clock-2882" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Ulanzi TC001 desktop clock&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; into a Stripe subscription statistics and notification doodad.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Here's a fun little simulator that demonstrates the various screens and animations it has now:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="web-embed-block web-embed-inline my-4" style="height: 230px; border: 1px solid rgb(229, 231, 235); border-radius: 0.5rem; overflow: hidden;"&gt;&lt;iframe src="https://duggan.ie/post-assets/stripe-dashboard-sim" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy" style="width: 100%; height: 100%;" title="Stripe Dashboard Simulator"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;span style="white-space: pre-wrap;"&gt;This is a WASM compiled variant of the project's integrated simulator&amp;nbsp;that I added so I could iterate on the design without having to flash the device repeatedly. The buttons along the bottom trigger various screens and are just for debugging; they don't appear on the device.&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;On boot it creates a wireless access point with a captive portal for configuration, like the automatic login page you get with airport WiFi.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;To me, this is a perfect demonstration of the power of frontier coding assistants. The activation energy of this project was high, and even then, it allowed me to quickly become more and more ambitious in a very short period of time.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I've had the Ulanzi TC001 sitting doing nothing other than tell the time in the year since I purchased it – another project component doomed to never get any of my time...&lt;/span&gt;&lt;/p&gt;&lt;h3 id="first-pass-awtrix-and-a-raspberry-pi" class="group relative"&gt;&lt;a href="#first-pass-awtrix-and-a-raspberry-pi" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;div class="web-embed-block web-embed-inline my-4" style="max-width: 550px; margin-left: auto; margin-right: auto; height: 300px;"&gt;&lt;iframe src="/files/f0ec5af5765bdfab.html" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy" style="width: 100%; height: 100%;" title="raspbi"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;span style="white-space: pre-wrap;"&gt;First pass: AWTRIX and a Raspberry Pi&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Initially I started out flashing it with the &lt;/span&gt;&lt;a href="https://github.com/Blueforcer/awtrix3" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;AWTRIX3&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; firmware, and running a Node.js service to update it over the network. It was maybe 30 minutes effort to put together, I showed it off to my cofounders and they loved it! We had just turned on the subscription payments system after iterating on the product with early customers over several months, and it was exciting to see people finding it useful enough to actually pay for it.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I thought it would be nice if they could also have one of these, but the fact that you need basically a whole other server to do the custom logic made it a non-starter. AWTRIX is really meant for smart home hackers.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;So I had a bright idea: maybe I can customise the firmware? Set it up so it just communicates with Stripe directly?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Then I had a more fun idea: instead of modifying AWTRIX3, a feature-rich C++ project, how about I get Claude to help me write some firmware from scratch in Rust? Perhaps paring it back to the essentials would be simpler than trying to shoehorn my idea into an already complex system.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I had a vague impression that Rust was good for embedded systems development, but hadn't written any before, so this would really test just how far I could get using a coding assistant to build in a language and runtime environment (ESP32) I was new to.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;The convenient thing about this project is that it is fairly unambiguous whether the system works or not –&amp;nbsp;plus it helps that the ESP32 boards are difficult to brick&lt;/span&gt;&lt;span&gt;&lt;sup&gt;[1]&lt;/sup&gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt;, and &lt;/span&gt;&lt;span&gt;&lt;span style="white-space: pre-wrap;"&gt;not too expensive&lt;/span&gt;&lt;sup&gt;[2]&lt;/sup&gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; to replace.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I've been using AI coding assistants a lot over the last few months, and I was still genuinely shocked at how easy it was to get to a basic working state using Claude. However, I can guess at some ideas why it might have been more successful than I initially expected.&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li value="1"&gt;&lt;b&gt;&lt;strong class="font-semibold" style="white-space: pre-wrap;"&gt;Rust&lt;/strong&gt;&lt;/b&gt;&lt;span style="white-space: pre-wrap;"&gt; –&amp;nbsp;A compiled language that has a reputation for being expressive and thoughtfully documented, with safety features that perhaps makes it naturally amenable to the self-directed feedback loop of agents.&lt;/span&gt;&lt;/li&gt;&lt;li value="2"&gt;&lt;b&gt;&lt;strong class="font-semibold" style="white-space: pre-wrap;"&gt;The simplicity of the hardware&lt;/strong&gt;&lt;/b&gt;&lt;span style="white-space: pre-wrap;"&gt; –&amp;nbsp;the low resolution display and limited features of the system limits the design space. There's a lot you can do with an ESP32, a 32x8 LED matrix and a piezo buzzer –&amp;nbsp;but not &lt;/span&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;that&lt;/em&gt;&lt;/i&gt;&lt;span style="white-space: pre-wrap;"&gt; much.&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;It wasn't without bugs, of course. For example, initially, the screen glitched out continuously:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure class="video-wrapper"&gt;&lt;video src="/files/66c0b9c20613f255.mp4" autoplay="" muted="" loop="" playsinline="" data-playback="silentLoop" title="glitch11.mp4" style="max-width: 100%; height: auto"&gt;&lt;/video&gt;&lt;/figure&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;This turned out to be due to "&lt;/span&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;interrupt latency from WiFi interfering with the WS2812B signal generation in the RMT ISR&lt;/em&gt;&lt;/i&gt;&lt;span style="white-space: pre-wrap;"&gt;" – 😵‍💫&lt;/span&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;It actually looks pretty cool, and I'll have to keep it in mind if I want to generate an effect like that for some other project, but it's just distracting in this case.&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Once the features and basic setup were working, I was able to step back and start to think what would make it fun and easy.&lt;/span&gt;&lt;/p&gt;&lt;h2 id="artwork" class="group relative"&gt;&lt;a href="#artwork" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Artwork&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I did not have any success creating actual pixel art with OpenAI. ChatGPT can create things &lt;/span&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;in the style&lt;/em&gt;&lt;/i&gt;&lt;span style="white-space: pre-wrap;"&gt; of pixel art, but which are actually thousands of pixels in width and height, but I could not get it to build something to the constraints of the TC001 display. Maybe it's possible, but I did not have the patience to find out.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;On the other hand, Claude Code Opus 4.6 seems to be quite capable of creating that minuscule artwork, and some of it looks quite good. However, I didn't realize Opus 4.6 could actually create images before I'd already hand drawn most of what I wanted using &lt;/span&gt;&lt;a href="http://pixilart.com" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;PixilArt&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;/p&gt;&lt;h2 id="captive-portal" class="group relative"&gt;&lt;a href="#captive-portal" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;Captive portal&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I wasn't really sure what was involved in creating a captive portal. I didn't even Google it before getting Claude to write it, and it turned out to be conceptually relatively straightforward: DNS interception. Your device connects to the access point, and whatever URL it requests, the AP responds with the configuration page.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;There are also some magic URLs that the OS checks for, which seem to trigger the familiar "popup" you get after connecting to a network that supports one:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure class="image-wrapper" style="width: 357px; margin-left: auto; margin-right: auto; text-align: center"&gt;&lt;img src="/files/8a0691c904941073.png" alt="Screenshot 2026-02-10 at 11.11.32.png" width="357" height="776.138671875" style="max-width: 100%; height: auto;"&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;All it really does is save the person setting it up the hassle of knowing what IP to visit in their browser, but it's a detail I really enjoy, since it's one of those technical curiosities that I've briefly wondered about – for however long it took to connect to the WiFi – then forgotten.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;The native simulator includes a reasonably accurate replication of the TC001's integrated piezo buzzer, as well as a live Stripe API integration. This was an addition that I just took a punt on once the basic tent poles of the system were working.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;From the native simulator, it was a fairly straightforward path to compiling to Wasm, hence the little simulator at the top of the post.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;It was fun having a device running in the corner of my office, beeping when a new subscriber arrived, but my original goal was to have a version I could hand to my cofounders. I had ordered another couple of the TC001s on AliExpress, and conveniently they arrived just a day or two before I was due to meet up with David and Clodagh.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;This is the point at which I discovered that at some point since I got my own TC001 a year or so back, the manufacturer had apparently decided that 8MB of storage was overkill for a desktop clock, and the new models were shipping with only 4MB of flash memory. This required some slight rejigging of the code – which Claude was more than capable of – ditching my beloved OTA firmware rollback feature.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Another key component is, of course, setup instructions, which I included as a single printed page, with example screens:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure class="image-wrapper"&gt;&lt;img src="/files/97506d6321126465.jpg" alt="IMG_6549.jpg" width="inherit" height="inherit" style="max-width: 100%; height: auto;"&gt;&lt;/figure&gt;&lt;div class="web-embed-block web-embed-inline my-4" style="height: 1100px; border: 1px solid rgb(229, 231, 235); border-radius: 0.5rem; overflow: hidden;"&gt;&lt;iframe src="/files/e3949aa697e9cec3.html" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy" style="width: 100%; height: 100%;" title="setup-guide"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;David and Clodagh were delighted, and were fortunately both able to get the devices set up easily when they got home, and now we all have little chirping devices to tell us when new customers arrive!&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;One thing I did overlook was that Clodagh tends to bounce between her home office, and a small office we rent, which means she needs multiple wifi connections configured. Will have to fix that in a future firmware update. For better or worse, I was not able to finish my planned auto-update functionality before handing the devices over. Given how new this is to me I am as likely to brick their devices remotely as improve them, so manual updates from the firmware page will do for now!&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;There is currently no comments system. If you'd like to share an opinion either with me or about this post, please feel free to do so with me either via email (&lt;/em&gt;&lt;/i&gt;&lt;a href="mailto:ross@duggan.ie" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;ross@duggan.ie&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;) on Mastodon (&lt;/em&gt;&lt;/i&gt;&lt;a href="http://mastodon.ie/@duggan" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;@duggan@mastodon.ie&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;) or even on &lt;/em&gt;&lt;/i&gt;&lt;a href="https://news.ycombinator.com" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;Hacker News&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;a href="https://news.ycombinator.com" rel="noreferrer"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;.&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;PS: I created a tool to create self-contained HTML embeds from Twitter archives while writing this post. &lt;/em&gt;&lt;/i&gt;&lt;a href="https://github.com/duggan/tweetembed" rel="noreferrer"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;You can find it here&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;.&lt;/em&gt;&lt;/i&gt;&lt;/p&gt;&lt;section class="changelog"&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Changelog:&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;time datetime="2026-03-12T22:54:25.000Z"&gt;Mar 12, 2026, 10:54 PM&lt;/time&gt; — Fix layout issue.&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;div class="footnotes"&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;span style="white-space: pre-wrap;"&gt;Make inaccessible, basically.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="white-space: pre-wrap;"&gt;A TC001 on AliExpress is something like €35 delivered.&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</content>
    <link href="https://duggan.ie/posts/building-a-stripe-dashboard-with-an-esp32-desktop-clock-and-rust" rel="alternate"/>
    <published>2026-03-08T21:39:37.677Z</published>
    <summary type="html">&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Putting together a little desktop notifier for new Stripe subscribers using Rust! 🦀&lt;/span&gt;&lt;/p&gt;</summary>
  </entry>
  <entry>
    <id>https://duggan.ie/posts/microgpt-on-the-esp32-but-why</id>
    <title type="text">microgpt on the ESP32 – but... why?</title>
    <updated>2026-03-03T11:17:43.000Z</updated>
    <author>
      <name>Ross Duggan</name>
      <email>ross@duggan.ie</email>
      <uri>https://duggan.ie/</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;/p&gt;&lt;figure class="video-wrapper"&gt;&lt;video src="/files/41a1e2505d620418.mp4" autoplay="" muted="" loop="" playsinline="" data-playback="silentLoop" title="esp32gpt_demo3.mp4" style="max-width: 100%; height: auto"&gt;&lt;/video&gt;&lt;/figure&gt;&lt;span style="white-space: pre-wrap;"&gt;Yesterday I was looking at the &lt;/span&gt;&lt;a href="https://zclaw.dev" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;zclaw project&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, and I thought it would be cool if the ESP32 could run an actual language model instead of phoning out to OpenAI, Anthropic, or some other provider.&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Then I remembered that &lt;/span&gt;&lt;a href="https://karpathy.github.io/2026/02/12/microgpt/" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Andrej Karpathy had dropped microgpt&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; a couple of weeks ago, and thought it might be fun to try and get a GPT running on the ESP32. I've got a couple of them lying around from a few other tinkering projects, as well as a functioning ESP32 Rust project to base it on.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I am very much the &lt;/span&gt;&lt;span&gt;&lt;span style="white-space: pre-wrap;"&gt;dog-on-computer&lt;/span&gt;&lt;sup&gt;[1]&lt;/sup&gt;&lt;/span&gt;&lt;span style="white-space: pre-wrap;"&gt; meme regarding this stuff. I have trained and run LLMs for years, but I have not tried writing them. I have the general idea, tokenizers, transformers, gradient descent, yada yada, but they're mostly just words.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;With the blog post and my other project as a starting point, it was, however, pretty easy to get to a functional port of microgpt training and then generating a continuous stream of names on the ESP32!&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="https://github.com/duggan/esp32gpt" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;The code is here&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, and the Claude session that produced the first functioning variant of the project is below:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="link-preview-card border border-gray-200 rounded-lg overflow-hidden my-4"&gt;&lt;a href="https://rockstar.ninja/s/7yM0cgfXOq8Z" target="_blank" rel="noopener noreferrer" class="no-underline text-inherit flex flex-row"&gt;&lt;div class="flex-1 p-3 min-w-0"&gt;&lt;div class="link-preview-site flex items-center gap-1.5 mb-2 text-xs text-gray-500"&gt;&lt;img src="/files/7c24b933e4c9df9e.svg" class="w-4 h-4 m-0"&gt;&lt;span&gt;rockstar.ninja&lt;/span&gt;&lt;/div&gt;&lt;div class="link-preview-title font-semibold text-base leading-tight mb-1"&gt;For a fun technical challenge, and based on our work in ~/Projects/stripe-dashboard, and the informa&lt;/div&gt;&lt;div class="link-preview-description text-sm text-gray-500 leading-snug line-clamp-2"&gt;opus 4.6 · haiku 4.5 · 92 turns · For a fun technical challenge, and based on our work in ~/Projects/stripe-dashboard, and the information in https://k...&lt;/div&gt;&lt;div class="text-xs text-gray-400 mt-2"&gt;rockstar.ninja&lt;/div&gt;&lt;/div&gt;&lt;div class="w-[200px] shrink-0 overflow-hidden bg-gray-100"&gt;&lt;img src="/files/23581c424049bc81.png" alt="For a fun technical challenge, and based on our work in ~/Projects/stripe-dashboard, and the informa" class="w-full h-full object-cover m-0"&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;span style="white-space: pre-wrap;"&gt;I also had a fresh session of Claude conduct a review, which spotted that the implementation was missing &lt;/span&gt;&lt;code spellcheck="false" style="white-space: pre-wrap;"&gt;&lt;span class="bg-gray-50 dark:bg-gray-800/30 font-mono px-2 py-0.5 rounded border border-gray-200/60 dark:border-gray-700/50 text-gray-800 dark:text-gray-200 relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-blue-50/30 before:to-purple-50/30 dark:before:from-blue-900/20 dark:before:to-purple-900/20 before:-z-10"&gt;RMSNorm&lt;/span&gt;&lt;/code&gt;&lt;span style="white-space: pre-wrap;"&gt;, and filled in the gaps:&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="link-preview-card border border-gray-200 rounded-lg overflow-hidden my-4"&gt;&lt;a href="https://rockstar.ninja/s/WGkvb2mtPva1" target="_blank" rel="noopener noreferrer" class="no-underline text-inherit flex flex-row"&gt;&lt;div class="flex-1 p-3 min-w-0"&gt;&lt;div class="link-preview-site flex items-center gap-1.5 mb-2 text-xs text-gray-500"&gt;&lt;img src="/files/7c24b933e4c9df9e.svg" class="w-4 h-4 m-0"&gt;&lt;span&gt;rockstar.ninja&lt;/span&gt;&lt;/div&gt;&lt;div class="link-preview-title font-semibold text-base leading-tight mb-1"&gt;Let's do a thorough review of this repository, and evaluate whether it meets the goal of reproducing&lt;/div&gt;&lt;div class="link-preview-description text-sm text-gray-500 leading-snug line-clamp-2"&gt;opus 4.6 · haiku 4.5 · 48 turns · Let's do a thorough review of this repository, and evaluate whether it meets the goal of reproducing microgpt on the ...&lt;/div&gt;&lt;div class="text-xs text-gray-400 mt-2"&gt;rockstar.ninja&lt;/div&gt;&lt;/div&gt;&lt;div class="w-[200px] shrink-0 overflow-hidden bg-gray-100"&gt;&lt;img src="/files/9fce82853b325987.png" alt="Let's do a thorough review of this repository, and evaluate whether it meets the goal of reproducing" class="w-full h-full object-cover m-0"&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 id="what-s-the-point" class="group relative"&gt;&lt;a href="#what-s-the-point" class="absolute -left-6 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-gray-400 hover:text-gray-600 no-underline"&gt;#&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;What's the point?&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Well, I learned that training a language model on an ESP32 is something that can be done. I did not know that yesterday. I also found this is not by any means the first or even most interesting attempt. &lt;/span&gt;&lt;a href="https://github.com/DaveBben/esp32-llm" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;David Bennett used a slightly larger ESP32 variant (1MB of RAM, still minuscule) to spit out 20 tokens a second of much more recognizable "LLM" output&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;. Two years ago!&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I think I've learned a little more than I'd have learned reading a blog post from &lt;/span&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;someone else&lt;/em&gt;&lt;/i&gt;&lt;span style="white-space: pre-wrap;"&gt; describing how they'd done it. I did a little post-Claude tinkering with stuff I'd remembered from the last ESP32 project (core pinning, RNG).&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Does this magically make me able to understand the math? No, I'd have to use my brain more for that, but I've read some of the code, and have given Karpathy's post a closer read than I might have done otherwise.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;This was a shower thought that was turned into a working demo. A year ago, I'd have forgotten about it and moved on, or jotted something down in a list of rainy day projects that I would realistically never get around to.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Some people will find this unsatisfying, maybe even a quintessential example of the missed learning opportunity that happens when someone uses LLMs. I'm not sure I even disagree in principle. I just know that I was very unlikely to ever even &lt;/span&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;attempt&lt;/em&gt;&lt;/i&gt;&lt;span style="white-space: pre-wrap;"&gt; to do this. There are lots of random ideas that I'll never get around to.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;The space of possible ideas is large, and time is finite. Maybe noodling on this for an hour or two will open me up to other possibilities. &lt;/span&gt;&lt;a href="https://en.wikipedia.org/wiki/The_old_man_lost_his_horse" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Too early to tell&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;I played video games a lot as a teenager, and watched television, and both of these were going to rot brains. Brains are more resilient than they seem to get credit for.&lt;/span&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;There is currently no comments system. If you'd like to share an opinion either with me or about this post, please feel free to do so with me either via email (&lt;/em&gt;&lt;/i&gt;&lt;a href="mailto:ross@duggan.ie" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;ross@duggan.ie&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;) on Mastodon (&lt;/em&gt;&lt;/i&gt;&lt;a href="http://mastodon.ie/@duggan" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;@duggan@mastodon.ie&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;) or even on &lt;/em&gt;&lt;/i&gt;&lt;a href="https://news.ycombinator.com" rel="noreferrer" dir="ltr"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;Hacker News&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;a href="https://news.ycombinator.com" rel="noreferrer"&gt;&lt;i&gt;&lt;em class="italic" style="white-space: pre-wrap;"&gt;.&lt;/em&gt;&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;&lt;section class="changelog"&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Changelog:&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;time datetime="2026-03-03T17:02:22.000Z"&gt;Mar 3, 2026, 5:02 PM&lt;/time&gt; — Fixed a typo.&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;div class="footnotes"&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="https://duggan.ie/files/7d49752be1352668.jpg"&gt;[image: https://duggan.ie/files/7d49752be1352668.jpg]&lt;/a&gt;&lt;br&gt;&lt;span style="white-space: pre-wrap;"&gt;(I have no idea what I'm doing)&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</content>
    <link href="https://duggan.ie/posts/microgpt-on-the-esp32-but-why" rel="alternate"/>
    <published>2026-03-03T16:57:32.445Z</published>
    <summary type="html">&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Yesterday I was looking at the &lt;/span&gt;&lt;a href="https://zclaw.dev" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;zclaw project&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt;, and I thought it would be cool if the ESP32 could run an actual language model.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="white-space: pre-wrap;"&gt;Then I remembered that &lt;/span&gt;&lt;a href="https://karpathy.github.io/2026/02/12/microgpt/" rel="noreferrer"&gt;&lt;span style="white-space: pre-wrap;"&gt;Andrej Karpathy had dropped microgpt&lt;/span&gt;&lt;/a&gt;&lt;span style="white-space: pre-wrap;"&gt; a couple of weeks ago, and thought it might be fun to try and get a GPT running on the ESP32. I've got a couple of them lying around from a few other tinkering projects, as well as a functioning ESP32 Rust project to base it on.&lt;/span&gt;&lt;/p&gt;</summary>
  </entry>
</feed>