I built esp32-streamdeck, a small DIY Stream Deck-like controller using an ESP32-S3 touch display. The firmware runs on an Elecrow CrowPanel 7" ESP32-S3 (costs < $40), draws a 3x3 grid in LVGL, reads touches from the GT911 controller, and HTTP requests to a companion server running on my Mac.
The server, esp32-streamdeck-server, is a small FastAPI server that handles/receives touch events made from the touch screen. Actions are mapped in a YAML file which are executed as shell commands.
Structure
On the firmware side there is a fixed tile table like below which is also rendered and shown in the photo I have added above.
static const Tile TILES[9] = {
{ "mute_mic", "Mic Mute", LV_SYMBOL_AUDIO, 0x3B82F6 },
{ "play_pause", "Play/Pause", LV_SYMBOL_PLAY, 0x22C55E },
{ "next_track", "Next", LV_SYMBOL_NEXT, 0x22C55E },
{ "vol_down", "Vol Down", LV_SYMBOL_LEFT, 0x22C55E },
{ "vol_up", "Vol Up", LV_SYMBOL_RIGHT, 0x22C55E },
{ "dnd_toggle", "DND", LV_SYMBOL_BELL, 0xF97316 },
{ "open_slack", "Slack", LV_SYMBOL_ENVELOPE,0x3B82F6 },
{ "open_terminal", "Alacritty", LV_SYMBOL_SETTINGS,0xF97316 },
{ "lock_screen", "Lock", LV_SYMBOL_CLOSE, 0xEF4444 },
};
On tap, the firmware POSTs the key to /tap (the server hosted on my machine).
{
"key": "mute_mic"
}
The server holds a YAML config like below:
mute_mic:
toggle: true
on: "osascript -e 'set volume input volume 0'"
off: "osascript -e 'set volume input volume 75'"
open_terminal:
run: "open -a Alacritty"
The server then runs appropriate commands / scripts on my machine.
Notes layer
I have added a second layer that displays my daily diary notes that usually has some markdown bullet points as tasks/notes etc. This layer keeps on refreshing by polling requests to the server.