In my previous post I wrote about how my washing machine and dryer pick their own schedule based on energy prices. That post was about the concept — a Homey app that finds the cheapest window to run your appliances. What I didn't mention was the thing on the kitchen wall that makes it actually usable.
Because here's the truth about smart home automation: if the only way to interact with it is through an app on your phone, it won't survive contact with your household.
The Problem With Apps
I call it the spouse test. If your partner needs to unlock their phone, find the right app, navigate to the right screen, and tap three buttons just to start the dryer at a cheap time — they're going to press the button on the dryer instead. And they'd be right to.
A physical device on the wall changes that dynamic entirely. It's always on, always showing the current state, and requires exactly one tap to do the thing. No login, no loading spinner, no "update available" popup. It's the difference between a light switch and a lighting app.
So I decided to build one. A small touch screen near the laundry area that shows energy prices, appliance status, and lets you schedule a run with a single tap.
$15 of Hardware, Infinite Ambition
The ESP-2432S028R — affectionately known as the "Cheap Yellow Display" or CYD — is one of those products that shouldn't exist at its price point. For about $15, you get an ESP32 microcontroller, a 2.8-inch color TFT display with touch input, WiFi, and enough GPIO pins to feel dangerous.
The screen is 320 by 240 pixels. That's not a lot. For context, the icon for your weather app is probably bigger than this entire display. But for a single-purpose device that shows two appliance cards and a price indicator, it's plenty.
The ESP32 handles WiFi, MQTT communication with my Homey hub, NTP time sync, and over-the-air firmware updates. All on a chip that draws about half a watt. The whole thing runs off a USB-C cable.
The First Pivot
I didn't start with custom firmware. Like any reasonable person, I started with ESPHome — the YAML-based framework that lets you configure ESP32 devices without writing C++. Define your sensors, your display layout, your automations, and ESPHome generates the firmware for you.
It worked. For about two hours.
The problem was MQTT topic structure. My Homey app publishes appliance data on specific topics with JSON payloads — state, pricing, scheduling, savings data. ESPHome's MQTT integration is designed for Home Assistant's auto-discovery format, and bending it to work with custom topic structures felt like writing C++ with extra steps. Worse steps, actually, because you're debugging generated code you didn't write.
So I pivoted to PlatformIO with the Arduino framework. Full C++ control, direct access to proven libraries like TFT_eSPI for the display and espMqttClient for MQTT, and — crucially — the ability to structure my code the way the problem demanded rather than the way a YAML schema allowed.
Was it more work? Absolutely. Was it the right call? Without question. Some projects fit neatly into a configuration-driven framework. This one needed a real codebase.
Building a UI on 320 by 240 Pixels
Designing a touch interface for a 2.8-inch screen is an exercise in brutal prioritization. There's no room for nice-to-haves. Every pixel has a job.
The layout I landed on has three elements. A top bar showing the current time, date, energy price, and WiFi status. Two appliance cards — one for the washer, one for the dryer — each showing their current state with a color-coded badge, key stats, and an action button. That's it.
The action button is context-sensitive. If the appliance is idle, it says PLAN and opens a scheduling modal. If it's already scheduled, it says CANCEL. The modal lets you pick a deadline: 4, 8, 12, or 24 hours from now. Tap your choice and the system finds the cheapest slot within that window.
Color does a lot of heavy lifting on a small screen. Green badge means idle and ready. Blue means scheduled. Yellow means the system is still learning this appliance's power profile. Red means something needs attention. You can read the state of both appliances from across the room without your glasses.
One design choice I'm particularly happy with: when an appliance is scheduled, the card shows the start time, the expected price per cycle, and how much you're saving compared to running it right now. That last number — "Saving €0.38" — turns out to be incredibly motivating. It makes the abstract concept of energy optimization tangible.
The Bugs That Taught Me Things
Embedded development has a way of humbling you. Here are three problems that took longer to solve than I'd like to admit.
Dual SPI buses. The CYD board has two SPI peripherals: one for the display (ILI9341) and one for the touch controller (XPT2046). Most example code assumes they share a bus. They don't — and they can't, because the display's SPI runs at 40 MHz while the touch controller maxes out at 2.5 MHz. Sharing a bus means reconfiguring speed on every swap, which causes timing glitches. The fix was putting them on separate hardware SPI buses (HSPI and VSPI), each with their own pins. Obvious in hindsight. Took a full afternoon to figure out.
SPI reentrancy crashes. This one was subtle. MQTT messages arrive asynchronously via callbacks. My first implementation updated the display directly from the MQTT callback — parse the JSON, update the card, done. It worked great until a message arrived while the display was mid-draw. Two SPI transactions on the same bus at once: instant crash, no useful stack trace.
The solution is almost embarrassingly simple: the MQTT callback sets a boolean flag. The main loop checks the flag, and if it's set, redraws the screen. No concurrent SPI access, no crashes. It's the embedded equivalent of "don't update the DOM from a web worker" — except the consequence isn't a console warning, it's a hard reset.
Touch calibration. Every CYD unit has slightly different touch calibration values. The raw coordinates from the XPT2046 don't map 1:1 to screen pixels — they need to be scaled and offset. My first unit worked perfectly with the default calibration. My second unit registered taps about 30 pixels to the left. The fix was a calibration routine and storing per-unit values, but the debugging process involved a lot of tapping one spot and watching a dot appear somewhere else. It felt like playing Operation with an invisible board.
The Result
The finished device sits on the wall near our washing machine. It shows the current energy price, the status of both appliances, and lets you schedule a run with two taps: hit PLAN, pick your deadline. The system finds the cheapest slot within your window and confirms the schedule.
It updates over-the-air, reconnects automatically if WiFi drops, and uses about as much power as a phone charger. The total hardware cost was under €20.
But the real measure of success isn't technical. It's that my partner uses it without thinking about it. There's no app to open, no concept to explain. Tap the card, pick when you need it done. The rest happens automatically.
Sometimes the best smart home upgrade isn't smarter software — it's a simple screen on the wall that does exactly one thing well.
