ESP32 Stream Deck

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.

Stream Deck grid

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.

Notes view