ESP32 Smart Matrix

February 3, 2023 4 min read

I was scrolling through Instagram one night when I saw an ad for a 'lo-fi connected display' called the Tidbyt.

Tidbyt - See What Matters
Tidbyt keeps you updated on transit, weather and more. Leave your phone in your pocket.

I thought the price tag was a bit steep at $179 for what was essentially an ESP32 controlling a matrix display, so I did a bit of shopping on AliExpress. Turns out, all I needed to purchase was the display itself for under thirty dollars.

Looking through the available libraries to integrate with the display, I settled on ESP32-MatrixPanel-DMA as it required relatively little CPU time to push display data. Loading the example code showed a great animation!

0:00
/

The firmware was originally designed to process GIF animations received over MQTT but I quickly ran into issues with larger files. I wanted to decode the files in-memory, instead of saving them to flash, to save time. Turns out, a post on Tidbyt's community forum detailed that they used WebP animations to cut the file sizes.

Using WebP was a great exercise in trying to get native C to compile to the memory-constrained ESP32 platform. I had to dig around in the libwebp documentation for a bit, but eventually, I was displaying static images on the display!

this should be animated!

I thought animations would be just adding a line or two, but I had issues when trying to decode subframes in the animation. I realized that these subframes had a completely different size and position as the others, so I just made a hacky for-loop solution to copy the new pixels to the last frame.

uint8_t * fragmentTmp = (uint8_t *) malloc(iter.width*iter.height*4);
if(WebPDecodeRGBAInto(iter.fragment.bytes, iter.fragment.size, fragmentTmp, iter.width * iter.height * 4, iter.width * 4) != NULL) {
     int px = 0;
     for(int y = iter.y_offset; y < (iter.y_offset + iter.height); y++) {
         for(int x = iter.x_offset; x < (iter.x_offset + iter.width); x++) {
             //go pixel by pixel.
             int pixelOffsetCF = ((y*MATRIX_WIDTH)+x)*3;
             int pixelOffsetFT = px*4;

             int alphaValue = fragmentTmp[pixelOffsetFT+3];
                                    
             if(alphaValue == 255) {
                 memcpy(currentFrame+pixelOffsetCF, fragmentTmp+pixelOffsetFT, 3);
             }
                                    
             px++;
         }
    }
}

With this new addition, I was able to get the animation to play on the screen.

0:00
/

I started to work on the frame and settled on making it out of some spare 1x3s I had laying around. The sides are joined with 45-degree miter cuts and when  the glue dried, I stained it a deep walnut color. At this point, I decided to attach a TSL2561 ambient light sensor to add some basic auto-brightness capabilities to the project.

After I got the device firmware ironed out, I wrote a simple scheduling server in JavaScript to send applets to the screen over MQTT. I tried to make it scalable for multiple devices and it supports caching data with Redis. Here's a photo of it working with the day-night-clock applet.

really cool!

With most of the code out of the way, I needed to create some of my own apps to run on the display. I originally wanted to use the same Pixlet platform as the Tidbyt to enable cross-compatibility, so I had to teach myself Starlark.

The first app that I wrote simply displayed the three scores from my Oura ring, along with a graph displaying the historical data over the past week. It ended up being a great starter app that integrated with an external web service.

App in development mode
I just opened a PR to add the app to the official community apps repository, so hopefully it will be in the Tidbyt app soon!

I've uploaded both the firmware and server code to my GitHub and I'll link them below.. Here's a few pictures and videos of the final product though!

0:00
/
GitHub - acvigue/SmartMatrix-ESP32: Internet connected smart matrix with support for decoding animated WebP files sent over MQTT
Internet connected smart matrix with support for decoding animated WebP files sent over MQTT - GitHub - acvigue/SmartMatrix-ESP32: Internet connected smart matrix with support for decoding animated...
GitHub - acvigue/SmartMatrixServer: Server to schedule and run applets to be sent to SmartMatrix
Server to schedule and run applets to be sent to SmartMatrix - GitHub - acvigue/SmartMatrixServer: Server to schedule and run applets to be sent to SmartMatrix