kid-arcade · part 1

Build Your Kid a $25 Learning Toy That Isn't a Tablet

My six-year-old wanted a screen for long car rides. Every option on the market wanted something back from him: an account, a subscription, an ad impression, a dopamine loop tuned by a growth team. I did not want to negotiate with any of that. I wanted a thing that does math and reading practice, runs off a phone charger, and does nothing else.

So I built it. Hardware cost was about $25. Electronics experience required, none at all: no soldering, no breadboard, no resistor color codes. One board, one USB cable, one afternoon of setup. This post is the full recipe, including the parts that went wrong, so you can build one for your own kid.

Everything is on GitHub: gsandi/esp32-kid-learning-arcade, including the firmware, the question banks and STL files for the case. There is a 30-second demo if you want to see it running first.

(This is part 1 of 2. Part 2 covers the bigger 7-inch version I built later, which is a different class of machine. Start here, this is the one to build first.)

The product rules came before the code

Before any hardware, I wrote down what the toy would refuse to do. These constraints did more for the final result than any line of firmware:

  • Five questions per round, then stop. It never feels like homework, and it has a natural “okay, done” moment.
  • Stars are the only reward. Correct answers earn a star. Stars accumulate quietly in a corner. No confetti explosions, no jackpot sounds, no streaks to protect.
  • Wrong answers cost nothing. No timers, no penalties, no lives. Getting it wrong just means trying again.
  • Offline, forever. No accounts, no ads, no internet, no telemetry. It cannot phone home because there is nothing to phone.
  • No cartoon aesthetic. Dark background, one accent color, big touch targets. Kids’ software does not have to look like a cereal box. He notices the difference. It feels like his device rather than a toy pretending to be one.

All five come down to the same thing, which is treating the kid as capable instead of tricking him into learning.

The hardware: one board, that’s it

The entire bill of materials is a single item: a Hosyond 4.0” ESP32 development board, the ESP32-32E variant. It is a 320×480 ST7796S display with an ESP32 soldered to the back of it, resistive touch, USB-C. Around $25 on Amazon.

What you get for that:

Component What it is
MCU ESP32-WROOM-32E, dual-core 240 MHz, 4 MB flash, 520 KB SRAM
Display 4.0” ST7796S, 320×480, RGB565 color, SPI
Touch XPT2046 resistive (shares the SPI bus with the display on GPIO 12/13/14)
USB Type-C with a CH340C serial chip, auto-reset, so you flash without touching any buttons
Power Any USB phone charger or power bank

There is no wiring because there is nothing to wire. The display is the computer. The repo also includes STL files for a 3D-printed case if you want it to survive a backpack.

Two boring gotchas account for most “it doesn’t work” moments:

  • Your USB-C cable must be a data cable. Many are charge-only and will never enumerate as a serial device. If your laptop does not see the board, swap cables before you debug anything else.
  • On macOS, the CH340 serial chip needs the WCH driver installed once. If /dev/cu.wchusbserial* does not show up when you plug in, that is the fix.

The firmware: one file, on purpose

The whole game is a single main.cpp, roughly two thousand lines of C++ built with PlatformIO and the TFT_eSPI display library. No LVGL, no UI framework, no abstraction layers.

I decided that deliberately and I would defend it for a project this size. Every embedded UI demo on the internet looks the same, with the default widget theme, blue gradients and rounded rainbow buttons. Owning every pixel was the cheapest way to get a look I actually wanted. On a 4 MB microcontroller an abstraction layer also costs you flash, RAM and debugging depth in exchange for flexibility you will never use. The project has a standing rule written into its docs: no new abstraction layers unless splitting becomes unavoidable.

What the one file contains:

  • Nine question generators. Math: counting objects, missing number, ten-frame, addition under 10, make-10. Reading: which-letter-does-it-start-with, missing letter in a CVC word, rhyming, uppercase/lowercase matching. All question banks are plain C arrays in the source, so you can read them, edit them and add your own in minutes.
  • A star counter persisted to flash (ESP32’s NVS storage), so progress survives power-off.
  • A PIN-gated parent panel for brightness and star resets, reached by long-pressing and entering a 4-digit PIN. The default is 0000, so change it in the source.
  • Round logic: 5 questions, gentle color-wash feedback per answer, a round-complete screen, done.

The gotcha that will bite you: touch calibration

Resistive touch panels are analog. Every XPT2046 panel reads slightly differently, and the raw corner constants in my source are tuned to my panel. Flash my firmware to your board unmodified and taps will land slightly off, and a six-year-old will decide the toy is broken within about ninety seconds.

There is no software fix for this, it is physics. The repo makes calibration a required setup step rather than a footnote. You open the serial monitor with pio device monitor while the normal firmware is running, tap each of the four corners, and every tap prints a line like TAP raw=(218,3712) mapped=(...). Write down the four raw numbers, put them into the RAW_X_LEFT / RAW_X_RIGHT / RAW_Y_TOP / RAW_Y_BOTTOM constants near the top of src/main.cpp, and reflash. If taps land on the wrong side afterwards, the axis is mirrored and you swap the two values in that pair. Ten minutes. Do not skip it.

That is the honest part of $25 hardware. The real world needs a tuning knob that a clean software model cannot see.

How AI tools actually fit into this

I wrote very little of this firmware by hand. Most evenings the workflow was me describing behavior, an AI coding agent writing and flashing it, and me holding the device and reporting what a kid would see.

The concrete setup, for anyone who wants to reproduce the workflow and not just the toy:

  • Claude Code in the terminal was the primary builder. It wrote the question generators, the screen flow, the NVS persistence and the settings panel, all driven by prompts that described kid-visible behavior rather than code. The project keeps a log of every prompt, and reading it back is the real story of the build. An actual entry, verbatim: “for the math its not complex enough for my kid .. can we do multiples or s…”. That one sentence became skip counting within days, and then the multiplication tables. The unit of work was a parent’s observation rather than a ticket.
  • A project constitution file did the steering. The repo has a CLAUDE.md that states the stack, the anti-abstraction rule, and the git discipline: main must always be stable and device-tested, new work goes on feat/ branches, and you flash to the physical device before merging. Written rules work better than repeated prompting, because the agent reads them every session and the tenth session behaves like the first.
  • I stayed the hardware in the loop. An agent can compile for the ESP32. It cannot feel that a touch target is 20 pixels too small for a small thumb, or that the feedback screen lingers a beat too long. Every loop ended with me holding the physical device. The calibration constants above are the clearest example, because those values live in my specific panel and no model could produce them.
  • The product rules were mine. Five questions, stars only, no penalties. No tool proposed any of that. The AI made the build fast. Whether it was any good came down to the constraints.

What surprised me is how well embedded work suits this workflow. Firmware has a brutally honest feedback loop, in that it either compiles and runs on the device or it does not, and that keeps an AI pair grounded in a way web work sometimes does not.

Build one

Everything is public and written for a non-technical parent:

  1. Buy the board. Search Amazon for “Hosyond ESP32 4 inch display”; the same hardware also sells as “LCDWiki ESP32-32E”. The listing has to say 320×480 ST7796S with USB-C and CH340C.
  2. Clone the repo and run ./scripts/setup-macos.sh, which installs Homebrew and PlatformIO. It does not install the CH340 serial driver, so if your Mac still does not see the board after plugging it in, that is the driver (or a charge-only cable).
  3. pio run -t upload flashes it over USB-C.
  4. Run touch calibration. Really. The README walks it through tap by tap, and also covers Windows and Linux setup.
  5. Edit the question arrays to match what your kid is working on. This is the best part. When the questions track what is happening at school, the toy stays relevant for months.

About 30 minutes end to end, no soldering. There is a 30-second demo if you want to see it running first.

The metric I care about has nothing to do with frame rate. On the last long drive, he picked this up instead of asking for the tablet.

Part 2 is the 7-inch version: a dual-chip ESP32-P4 build with a laptop-class display pipeline, real audio and over-the-air updates. Same game, very different machine.