Skip to content

Matrix Gateway

Alex Van de Putte edited this page Jul 17, 2026 · 28 revisions

Matrix Gateway

A split-flap display with no split flaps.

This is the SplitFlap Gateway firmware ported to an Adafruit MatrixPortal ESP32-S3 driving a HUB75 RGB LED matrix. The RS-485 transceiver is gone; in its place is a software bus and a wall of virtual split-flap modules, each one emulating a real module's wire protocol byte-for-byte and rendering itself as a flapping character cell on the panel.

Repo: github.com/avandeputte/MatrixPortalGateway

Everything above the bus is the unmodified gateway: the same web UI, REST API, MQTT, Home Assistant discovery, OTA, and module registry. The Companion drives it without any changes and cannot tell the difference from real hardware.

Why use it

  • Try the whole stack with no mechanical build — web UI, companion, Home Assistant, the lot, on one ~$25 board plus a panel. No modules, no RS-485, no steppers, no calibration.
  • An LED-matrix display in its own right — full colour, real accented characters, lowercase, the colour flaps, and an animated flip effect with a physical-looking seam.
  • A faithful test target — because it emulates at the protocol level, it exercises the same code paths a real gateway does.

What you need

Part Notes
Adafruit MatrixPortal ESP32-S3 The controller (Adafruit #5778).
A HUB75 LED panel Default layout is a 15 × 3 wall on a 128 × 32 chain (two 64×32 panels in series, or one native 128×32). Bigger panels give roomier cells.
A 5 V power supply Sized for your panel — check the panel/Adafruit specs.

64-row panels (128×64) use the HUB75 E address line — which the MatrixPortal wires out of the box to connector pin 8, the common panel layout, so a 64-row panel normally just works with no board changes. Only a panel that expects E on pin 16 needs the board's solder jumper moved. See the repo's README for the panel/size table.

Panel wiring varies one other way: some panels are BGR rather than RGB. Nothing can detect that — it shows up as every colour being wrong in a telltale pattern (red draws blue, yellow draws cyan, while green and white look fine). That's a setting, not a hardware fix: Settings → LED Panel → BGR.

Flash & set up

pio run -t upload        # build + flash over USB (double-tap RESET if the port is busy)
pio device monitor       # 115200 baud, native USB CDC

WiFi works like the real SplitFlap Gateway — with one difference: its hostname is configurable, and the fallback AP is named after it.

  1. On first boot it raises an access point called splitflap-gw-<6 hex digits> (derived from the board's MAC — e.g. splitflap-gw-a3f19c), password 12345678.
  2. Join it, open http://192.168.4.1, and save your SSID + password in Settings → WiFi.
  3. It joins your LAN and the AP shuts off. Reach it at http://.local — and you can change the hostname to something friendlier in Settings.

The same mDNS caveats apply: if .local doesn't resolve on your network, find the board's IP in your router's DHCP client list (look for the hostname above) and use that instead — see Finding it on your network. From there it's the same web UI as the gateway — same tabs, same settings.

What's emulated (and what isn't)

  • Emulated: everything the gateway actually sends — display and homing commands, flap-set config, version/identity queries, broadcasts, and batch queries. Each virtual module even reports a (fabricated but stable) serial number, so the gateway discovers them exactly as it would real modules.

  • The reel is bigger: nothing is drawn from a physical reel, so nothing is rationed. A virtual module carries 237 flaps where a real one carries 64:

    flaps what they are
    0–155 every Windows-1252 glyph — letters, digits, punctuation, accents
    156–162 the seven colour flaps — red, orange, yellow, green, blue, violet, white
    163–222 the 60 lowercase letters, accents included
    223–236 14 pictographs — ♥ ♦ ♣ ♠ ☀ ☺ ♪ ● ■ ⌂ and the four arrows
  • Not emulated: the mechanism. There's no stepper, Hall sensor or EEPROM, so calibration, diagnostics and provisioning are no-ops (the modules are "born" provisioned). You won't use the Calibration or Provision tabs here.

  • The flip is a rendering effect, not a physics simulation — you can cap or disable it.

Reaching the extra flaps

The last two rows of that table are the interesting ones, and they need a word of explanation, because for a long time they were on the reel but unreachable.

The gateway's original display protocol carries one byte per character, and it has a collision it can never fix: the byte for lowercase r already means RED. That is why m38-r sets the red flap, and it is why, on that path, lowercase has to fold to uppercase and a heart — which has no Windows-1252 byte at all — cannot be addressed by character in any way. This is not a bug to be tidied up; it is the price of a protocol that spends seven of its letters on colours.

Firmware 1.6 answers it by adding a second, index-addressed endpoint:

POST /api/display/cells
{ "start": 0, "step_ms": 15,
  "cells": [ {"ch": "H"}, {"ch": "i"}, {"color": "red"}, {"skip": true} ] }

A cell names a character, a colour by name, a blank, or skip (leave that module alone). Because a colour is now a name rather than a borrowed letter, i can simply be the letter i — and flap 223 can be a heart.

The old character protocol still works exactly as it did, byte for byte. Nothing about a physical wall changes.

You do not have to do any of this yourself. The Companion asks the gateway what it is (GET /api/config), and if it finds a Matrix Gateway on firmware 1.6 or newer it uses the index API automatically — so apps render in mixed case, colours stay colours, and pictographs appear. Point it at a physical wall and it falls back to the character protocol and folds everything to capitals. Same app, same companion, whichever wall you own.

The panel as a raw canvas

Everything above treats the panel as a wall of flaps. But it is also, physically, a grid of RGB LEDs — and the Matrix Gateway can hand that grid over as a plain framebuffer, bypassing the split-flap emulation entirely, so you can draw anything on it. A real split-flap gateway has no framebuffer at all, so this is the one place a Matrix wall does something the hardware it emulates simply cannot.

There are three ways in, and a client discovers all of them from GET /api/capabilities — the Matrix gateway advertises a canvas object (its pixel formats and panel width/height) and an effects list; a physical gateway carries neither:

  • Draw ops (POST /api/canvas/ops) — a JSON batch of clear / pixel / hline / vline / rect / text commands, applied in order and presented. Shapes and labels without composing a whole frame.
  • A raw frame (PUT /api/canvas/frame) — a full width × height buffer of pixels, rgb888 or rgb565 (the format is inferred from the body length). The way to mirror a picture; the panel's colour depth loses nothing to it.
  • On-device effects (POST /api/canvas/effect) — plasma, fire or matrix rain, animated by the panel itself at its native ~70 fps with nothing on the network. Pushing frames over HTTP tops out near 8 fps; an on-device effect is limited only by the panel.

While a canvas is active the reel renderer stands down — the same way it does during an OTA — and the canvas owns every pixel; releasing it repaints the wall from the modules' current state. Nothing canvas is persisted, so a reboot always returns to the flaps.

You don't drive any of this by hand. The Companion exposes it through canvas apps — apps that draw on the panel instead of returning flap pages: the built-in Effects, Lumina Clock, Weather Sky, Date Card, World Time, Countdown Bars and Image apps, plus any you write yourself. The endpoints are specified in full under the Canvas tag of the firmware's openapi.yaml.

How it fits the rest of the wiki

The Matrix Gateway is a drop-in replacement for the modules and the gateway. Wherever the rest of this wiki says "the gateway," it applies here too — including registering the Companion and everything in Home Assistant. Skip the Hardware and Module Firmware pages entirely.


Next: Companion →

Clone this wiki locally