A Little Internet Portal on My Wall
A few months ago, I was browsing Amazon when an ad for a 64 × 64 iDotMatrix display caught my attention. I had no idea what I would do with it, but it felt like I could eventually build something fun around it, so I bought it.
It arrived just before I left for a trip. I tried the phone app once and left the display at home.
My friend Bharat had been playing with ESP32 boards, and we kept sharing project ideas and reels. That made me wonder whether I could use one to control the display without its phone app and connect it to the internet. I ordered this ESP32 WROOM-32 board to try it.
The board was waiting when I returned from the trip. Bharat and I got Wi-Fi configuration working and reached the point where the display showed its Bluetooth icon, but no image appeared. After a couple of hours, we paused the project.
I picked it up again a few weeks later.
Starting again from another room
I had recently had a small cyst removed from my knee, and the stitches made it difficult to sit at my desk. I was mostly resting on a bed in another room.
Around the same time, I had started using Amp for my coding work and was trying its Orbs, which let agents work in a remote sandbox. A few days earlier, I had used an Orb to make and deploy changes to my site entirely from my phone while travelling, so I was curious to see how far I could take that way of working.
Orbs worked well when everything Amp needed was online. This project was different because the ESP32 had to be plugged into my MacBook, so I ran Amp locally instead.
Giving Amp access to the real setup
Running Amp locally gave it access to the code and the USB port, but it still could not see the display. I left the ESP32 connected to my MacBook and pointed the Dell webcam towards the display on the pegboard. Amp could now edit the ESP32 code, flash it onto the board, and use the camera to see what appeared on the wall.
I then described what I wanted the finished setup to do:
- Make setup easy: the ESP32 should create its own Wi-Fi hotspot. After connecting to it from a phone or laptop, I should be able to open a web page and enter my home Wi-Fi credentials and the server URL. The board should remember the details and connect to the strongest saved network.
- Connect to the display: the ESP32 should find the nearby iDotMatrix display over Bluetooth, keep the connection open, and reconnect if either device restarts or moves out of range.
- Receive new pictures: the website should send a changed 64 × 64 frame to the ESP32 over a WebSocket. If that connection failed, the ESP32 should request the current frame over HTTP instead.
- Prove it on the wall: Amp should use the webcam and keep trying until the expected picture was visible, rather than stopping when the code built or Bluetooth connected.
For the next couple of hours, I checked the thread from the Amp mobile app while Amp kept running on my MacBook. It changed the code and loaded it onto the board. After each attempt, it checked the logs and looked through the webcam to see whether the wall had changed.
The first thing we got onto the display was HELLO WORLD, sent directly from the ESP32. Amp then published another frame through the website and used the camera to check that the wall changed with it. I did not need to enter the room.
Getting a complete image through Bluetooth
The HELLO WORLD test looked simple on the wall, but it fixed the part that had stopped Bharat and me earlier: moving a complete image from the ESP32 to the display over Bluetooth. The ESP32 could connect successfully while the display ignored every image it sent.
To understand why, I had to learn just enough of the iDotMatrix protocol. One frame is 12,288 bytes of raw RGB data. The display expects it in three protocol chunks, and Bluetooth breaks each of those into even smaller writes.
My early firmware mixed up these layers. One version declared the wrong payload size, another followed a PNG-based reference, and another tried to send Bluetooth packets that were much too large. The connection could look healthy while the display quietly rejected the image.
What finally worked was sending the raw frame slowly, including a checksum for the complete image, and waiting for the display to confirm it through FA03.
I learned that this wait matters because a successful GATT write only means the ESP32 handed bytes to its Bluetooth stack, not that the wall accepted a complete frame. Keeping that confirmed connection open also removed the delay and failures caused by reconnecting for every image. The ESP32 sends the last frame again every ten seconds because the display can sometimes lose it without disconnecting.
Keeping the backend small
While debugging the hardware, I was also deciding how the website should store and rotate submissions. My first plans were much more complicated than the small amount of data required. I considered Postgres, Redis, object storage, and a rotation worker, even though each post was only some metadata and a small RGB grid.
I eventually put the API routes and WebSocket endpoint inside naman.md. Each artwork is one JSON file in the GitHub repository I already use for site engagement, and a runtime cache keeps the site from reading GitHub every time.
I also removed the rotation worker. The latest twelve posts rotate in ten-second slots, and the current time decides which one belongs to each slot.
The WebSocket sends the selected frame to the ESP32 as soon as the slot changes. If the socket stays disconnected for fifteen seconds, the ESP32 starts checking the Frame API every five seconds. It skips the Bluetooth upload when the pixels are already the same.
With both halves working, the complete path looked like this:
Opening the wall to other people
With the technical path working, I started wondering what I would actually put on the display. A private remote control was useful for testing, but it was not very interesting on its own. I liked the idea of letting other people send something into my room instead.
I had also seen The Thoughtful List, where people leave small thoughts for others to find. That was part of the inspiration. I wanted a similar invitation, except the result would appear as pixels on something physical beside me.
Anyone can post something to my wall by drawing, adding text, or turning an image into a 64 × 64 frame. The original image never leaves the browser. Only the pixel grid is saved. A new post joins the rotation, while older ones remain in the gallery.
What the setup taught me
Amp could move between the C++ firmware, the TypeScript backend, and the React interface in one thread, which made this a lot of fun to build. I spent that whole day sitting on the bed, opening the Amp mobile app every so often to see how things were going. In the other room, Amp was changing the code, flashing the ESP32, and checking the display. If I wanted to know what was on the wall, I could ask for the latest webcam photo without getting up.
It also made me understand why the right feedback loops matter so much for the quality of an AI agent's work. An agent can only react to what it can inspect. If Amp only had the build output, it could have stopped with code that compiled while the display remained blank. The serial logs, server responses, and webcam view let it check each part of the path, find where an attempt had failed, and try again.
These were the checks Amp had along the way:
| Signal | What it actually proved |
|---|---|
| Passing build | The code compiled |
| HTTP success | The API responded |
| Serial output | The ESP32 reached that step |
| BLE client connected | The firmware established a connection |
| Webcam frame | The intended pixels reached the wall |
The MacBook and webcam were only part of the debugging loop. Now the ESP32 reaches naman.md over Wi-Fi and the display over Bluetooth, without my laptop in between. When I am working at my desk, the latest posts move through it every ten seconds.
If someone leaves a drawing or a note on the Pixel Wall, it eventually appears a few feet away from me. That is the fun part I was hoping for when I clicked the Amazon ad, even though I could not describe it then.







+ COMMENTS +
LOADING COMMENTS...