---
title: "esp32-smallhd-tally-light"
url: "https://maker.wiznet.io/lawrence/projects/esp32-smallhd-tally-light/"
markdown_url: "https://maker.wiznet.io/lawrence/projects/esp32-smallhd-tally-light/md"
type: "UCC: User Created Content"
author: "Kunkles"
author_url: "https://github.com/Kunkles/esp32-smallhd-tally-light"
editor: "WIZnet"
editor_url: "https://maker.wiznet.io/"
original_author: "Kunkles"
original_url: "https://github.com/Kunkles/esp32-smallhd-tally-light"
published: "2026-06-01"
language: "en"
likes: 0
views: 317
comments: 0
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# esp32-smallhd-tally-light

> esp32-smallhd-tally-light

Original author: Kunkles (source: https://github.com/Kunkles/esp32-smallhd-tally-light)

## Article

## ESP32-S3 + W5500 Tally Light Bridge for SmallHD Monitors over Ethernet

**One-line summary (for the listing):** A field-ready HTTP-to-GPI tally bridge that turns an ESP32-S3 + WIZnet W5500 into a reliable wired link between broadcast control software (Bitfocus Companion) and SmallHD on-camera monitors.

---

### Overview

In live video production, a *tally light* tells the camera operator and on-air talent which camera is currently being broadcast. Missing or mistimed tally is a real on-air mistake, so the signal path has to be dependable.

This project, created by **Kunkles / 32Thirteen Productions LLC**, builds exactly that: a small bridge that receives a simple HTTP request from broadcast control software and converts it into an electrically isolated GPI (general-purpose input) contact closure that a SmallHD monitor understands.

The signal flow is straightforward:

```plaintext
Bitfocus Companion (Stream Deck, etc.)
   └─ HTTP GET  /tally/on  ·  /tally/off
        └─ ESP32-S3 (this firmware)
             └─ GPIO drives an optocoupler (PC817)
                  └─ SmallHD monitor GPI pin → tally indicator
```

It runs on the **Waveshare ESP32-S3-ETH**, which pairs an ESP32-S3 with a **WIZnet W5500** SPI Ethernet controller, with WiFi kept only as a fallback.

### Why W5500 Ethernet (and why wired comes first)

The decision to make Ethernet the primary interface is driven entirely by the operating environment:

- **Reliability** — Tally must never drop. A wired link removes the reconnection, roaming, and interference variables that WiFi carries.

- **Deterministic latency** — When several cameras switch tally at once, consistent, low-jitter response matters. Wired Ethernet via the W5500 gives that predictability.

- **Hostile RF environments** — Studios and live events are packed with wireless mics, IEM systems, camera links, and audience phones saturating 2.4/5 GHz. WiFi is the first link to suffer; wired survives.

- **Fits existing production infrastructure** — Camera control, NDI, and intercom already run over wired gigabit networks. A W5500-based bridge drops onto the same switch with managed static IPs and VLANs.

- **One cable, room to grow** — A wired backbone opens the door to PoE-style single-cable installs at ceiling or rig positions where mains power is awkward.

WiFi stays in the firmware purely for first-time setup convenience and as an emergency safety net if a cable is pulled.

### Hardware

| Component | Detail |
| --- | --- |
| Board | Waveshare ESP32-S3-ETH |
| Ethernet | **WIZnet W5500 (SPI)** + WiFi fallback |
| Tally output | GPIO18 (GPIO15 is a confirmed substitute) |
| Isolation | NOYITO PC817 single-channel optocoupler |
| Test button | GPIO3 |

**W5500 SPI pin mapping:** MOSI 11 · MISO 12 · SCK 13 · CS 14 · INT 10 · RST 9.

The optocoupler galvanically isolates the ESP32 from the monitor's GPI port, which protects both sides in a real production rig.

### How it works

The firmware is a single Arduino sketch built on the ESP32 Arduino core v3.x. Key design choices:

- **Flash-once, configure-in-browser.** All per-unit settings (hostname, IP mode, WiFi credentials, tally pin) are stored in NVS via the `Preferences` library. You flash identical firmware to every unit and give each one a unique hostname through the web portal — no per-unit code edits.

- **Ethernet-first, WiFi fallback.** Network events are handled asynchronously; the device uses whichever interface comes up, with Ethernet prioritized.

- **mDNS for discovery** (`tally-light.local`), with the practical advice to use static IPs in production since mDNS can be unreliable on managed/VLAN networks.

### HTTP API

| Endpoint | Method | Description |
| --- | --- | --- |
| `/` , `/config` | GET / POST | Web configuration portal |
| `/tally/on` | GET | Tally ON (GPIO HIGH) |
| `/tally/off` | GET | Tally OFF (GPIO LOW) |
| `/tally/test` | GET | One-second test pulse |
| `/status` | GET | Firmware version, IP, interface, mode, tally state |
| `/reset` | POST | Factory reset and reboot |

A built-in dark-themed web portal polls `/status` every few seconds to show a live tally indicator, making bench testing and field setup easy.

### Field notes worth knowing

The write-up captures hard-won deployment details, for example on the SmallHD OLED 22:

- Use **GPI Pin 7**, not Pin 1.

- Polarity is **Active High**; behavior is open-circuit = ON, contact closure = OFF (verified on PageOS 6.3.1).

- Other SmallHD models can differ — bench test each model before deployment.

---

Source: https://maker.wiznet.io/lawrence/projects/esp32-smallhd-tally-light/
