---
title: "WriteWatch v2"
url: "https://maker.wiznet.io/Lihan__/projects/writewatch-v2/"
markdown_url: "https://maker.wiznet.io/Lihan__/projects/writewatch-v2/md"
type: "UCC: User Created Content"
author: "Kunkles"
author_url: "https://github.com/Kunkles/WriteWatch"
editor: "WIZnet"
editor_url: "https://maker.wiznet.io/"
original_author: "Kunkles"
original_url: "https://github.com/Kunkles/WriteWatch"
published: "2026-05-31"
language: "en"
likes: 0
views: 279
comments: 0
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# WriteWatch v2

> Real-time video transfer monitor for macOS with integrated tally light controller — SwiftUI + ESP32-S3 firmware

Original author: Kunkles (source: https://github.com/Kunkles/WriteWatch)

## Article

## WriteWatch — When the "Record" Light Talks to the Network

`#W5500` `#ESP32S3` `#TCP_TOE` `#mDNS` `#Broadcast` `#TallyLight` `#SmallHD` `#SwiftUI` `#DIT` `#VideoProduction`

> 📚 **Context:** Professional video-production / on-set tooling — built and shipped by **32Thirteen Productions LLC** as a real working DIT (Digital Imaging Technician) utility. 🔧 **Verification status:** Firmware (`tally_light.ino`, v5.8) and macOS app are public, versioned (15 commits, 6 releases), and the README documents hardware-verified behavior on a Waveshare ESP32-S3-ETH unit driving SmallHD OLED 22 monitors running PageOS 6.3.1.

---

### 01 — What is this project?

On a film or broadcast set, a **tally light** is the small red lamp that tells everyone "this camera is live, this monitor is hot — don't walk through the shot." Traditionally, tally is wired through expensive, proprietary switcher hardware, and getting a SmallHD monitor's GPI (General Purpose Input) port to react to a recording event usually means buying into a closed ecosystem.

**WriteWatch** flips that around with two cooperating pieces:

1. **A macOS app (SwiftUI + AppKit + AVFoundation)** that watches capture folders in real time. It polls every 500 ms, detects video files that are *actively growing* (even over SMB/NFS network shares), tracks write rate and elapsed time, and validates the finished clip with AVFoundation (codec, duration, VFR detection).

2. **An ESP32-S3 + W5500 tally bridge** that turns those "recording started / stopped" events into a physical tally signal. The app discovers bridge units on the LAN, and its **"Follow WriteWatch"** automation fires the tally lights the moment an active write is detected — and clears them when recording stops.

The result is a fully open, self-hostable tally system: when the camera rolls and bytes start hitting the disk, the red light comes on across every monitor — no proprietary switcher required.

### 02 — Why network-driven tally over Ethernet?

#### 🔷 A set is a hostile RF environment

Film sets are crowded with wireless: video transmitters, comms, lighting control, monitoring. Wi-Fi alone is fragile here. A **wired Ethernet** tally bridge gives deterministic, interference-free signaling for the one thing that must never flicker — the "we are live" indicator. The firmware keeps Wi-Fi as a *fallback only*, with Ethernet as the primary path.

#### 🔷 HTTP is the universal integration language

The bridge exposes a tiny HTTP API (`/tally/on`, `/tally/off`, `/tally/test`, `/status`). That means it drops straight into **Bitfocus Companion** (the de-facto standard for broadcast button-box control) via a generic HTTP GET action — no custom driver, no SDK. Any HTTP client on the network can trigger tally.

#### 🔷 WIZnet ecosystem fit

For this class of device — a small embedded controller that must hold a rock-solid TCP server on a noisy LAN — the W5500's hardware TCP/IP stack is a far better fit than a software stack like LwIP on a bare MAC+PHY (e.g. an internal EMAC + LAN8720). It offloads the connection handling to silicon, freeing the ESP32-S3 to do GPIO timing and serve the config portal without contention.

### 03 — System architecture

![](https://maker.wiznet.io/upload/ckeditor5/632076252%5F1780317011%2Epng)

### 04 — Why W5500? ⭐

#### 🔷 Hardware TCP offload on a timing-sensitive device

The bridge's whole job is to be a reliable always-on HTTP endpoint while *also* toggling a GPIO with predictable timing. The W5500 carries its own hardware TCP/IP stack with 8 independent hardware sockets, so the ESP32-S3 never has to spend cycles reassembling segments or managing retransmits. The MCU handles the HTTP route logic and GPIO; the W5500 handles the wire.

#### 🔷 Usage mode: **TCP server (TOE) + mDNS over UDP**

This project uses the W5500 in **TCP (TOE) socket mode** — it runs an HTTP `WebServer` listening on port 80 (`/tally/on`, `/tally/off`, `/status`, etc.). Alongside it, **mDNS service advertisement** (`MDNS.addService("tally", "tcp", 80)`) uses the **UDP** path so the macOS app's Bonjour scanner (`_tally._tcp`) can auto-discover units on the LAN. So the chip is exercised across both of its core socket modes: TCP for control, UDP for multicast discovery.

> Note on integration: this firmware drives the W5500 through the **ESP-IDF / Arduino-ESP32 **`**ETH**`** driver** (`ETH.begin(ETH_PHY_W5500, ...)`), where the W5500 is registered as an SPI-attached Ethernet interface and the LwIP stack rides on top — rather than through WIZnet's own ioLibrary with native socket APIs. The *physical* networking is W5500; the socket abstraction is the ESP-IDF layer. This is a common and fully legitimate way W5500 ships in ESP32-S3 designs (the Waveshare ESP32-S3-ETH board is built around exactly this pattern).

#### 🔷 Why W5500 over the alternatives here

- **vs. Wi-Fi-only:** unacceptable on a set full of RF; W5500 gives a wired primary path.

- **vs. internal EMAC + external PHY (LAN8720):** the ESP32-S3 has *no* built-in EMAC (unlike the original ESP32), so an external MAC+PHY like the W5500 over SPI is the natural Ethernet solution for the S3 family.

- **vs. ENC28J60:** the W5500 brings a full hardware TCP/IP stack and 8 sockets versus the ENC28J60's raw-MAC-only design, which would force a software stack and more MCU load — exactly what you don't want on a real-time tally device.

#### 🔷 Verified evidence ✅

- Explicit W5500 pin map in the README (RST GPIO9, INT GPIO10, MOSI GPIO11, MISO GPIO12, CLK GPIO13, CS GPIO14).

- `ETH.begin(ETH_PHY_W5500, ETH_PHY_ADDR, ETH_CS_PIN, ETH_INT_PIN, ETH_RST_PIN, SPI2_HOST, ...)` in `setup()`.

- Network event handling for `ARDUINO_EVENT_ETH_GOT_IP` confirming live DHCP/static lease over Ethernet.

- README documents hardware-verified GPI behavior on SmallHD OLED 22 (PageOS 6.3.1), Active High, Pin 7.

### 05 — Key components

#### 🌐 WIZnet W5500 — TCP (TOE) server + UDP mDNS, SPI-attached

The networking heart of the bridge. Connected to the ESP32-S3 over SPI2 (HSPI host). Provides the wired Ethernet link that carries both the HTTP control API and the Bonjour discovery service.

#### 📟 Waveshare ESP32-S3-ETH

The carrier board pairing the ESP32-S3 with the W5500 — chosen because the ESP32-S3 lacks an internal Ethernet MAC, making an SPI Ethernet controller the standard route.

#### 💡 PC817 Optocoupler (NOYITO 1-channel)

Galvanically isolates the ESP32's GPIO18 tally output from the SmallHD monitor's GPI port — protecting both devices and matching the monitor's open-circuit-ON / closure-OFF logic.

#### 🖥️ macOS App (SwiftUI + AVFoundation)

Folder-watching, write-rate tracking, AVFoundation media validation, Bonjour discovery, and the "Follow WriteWatch" tally automation. Pure SwiftUI + AppKit + AVFoundation, no external dependencies.

#### 📺 SmallHD OLED 22 monitor (GPI)

The physical tally target. GPI configured as Tally Indicator, Active High, Pin 7, on PageOS 6.x.

### 06 — Application scenarios

**01. Multi-camera production sets** — Gang mode fires every tally bridge at once, or "Follow WriteWatch" automation lights monitors automatically the instant recording is detected, so talent and crew always know which cameras are hot.

**02. DIT / data-wrangling carts** — The original problem this solves: a DIT watching offload/capture folders gets a live, physical signal that ingest is actively writing, with disk-space warnings per volume.

**03. Broadcast control rooms with Bitfocus Companion** — Drop the HTTP endpoints onto Companion buttons for manual record-on/record-off tally control alongside the rest of a Stream Deck workflow.

**04. Generic GPI signaling** — Because the bridge is "just" an HTTP-to-GPI optocoupler with isolation, it generalizes to any device with a GPI trigger input, not only SmallHD monitors.

### Conclusion

> **WriteWatch turns an invisible software event — "bytes are landing on disk" — into a hardware-reliable, network-distributed red light, using the W5500 to hold a rock-solid wired control plane on the most RF-hostile environment there is: a film set.**

- ✅ Real, shipped DIT tool from a working production company (32Thirteen Productions LLC)

- ✅ W5500 used in both TCP (HTTP server) and UDP (mDNS discovery) modes

- ✅ Wired-Ethernet-primary design with Wi-Fi fallback — correct engineering for a set

- ✅ Standard HTTP API → instant Bitfocus Companion integration, no custom driver

- ✅ Galvanically isolated GPI output via PC817 optocoupler

- ✅ Browser-based config portal — flash once, configure per-unit via web, no per-unit recompiling

- ✅ Hardware-verified against real SmallHD OLED 22 monitors (PageOS 6.3.1)

- ✅ Clean, dependency-free codebase on both the macOS and firmware sides

### Q&A

**Q. Which W5500 socket mode does the tally bridge actually use?** A. Both core modes. The HTTP control server (port 80) runs over **TCP**, and mDNS service discovery (`_tally._tcp`) runs over **UDP** multicast. Control = TCP, discovery = UDP.

**Q. Why not just use the ESP32's built-in Ethernet?** A. The ESP32-S3 has no internal Ethernet MAC — that was a feature of the original ESP32, not the S3. So an external SPI Ethernet controller like the W5500 is the standard and necessary way to give the S3 wired networking.

**Q. Why Ethernet at all when there's Wi-Fi fallback?** A. Film sets are saturated with wireless traffic. Tally is safety-and-coordination-critical, so the wired W5500 path is primary and Wi-Fi only steps in if the cable is unplugged.

**Q. Is the W5500 driven through WIZnet's ioLibrary?** A. No — it's driven through the ESP-IDF / Arduino-ESP32 `ETH` driver (`ETH_PHY_W5500`), which registers the W5500 as an SPI Ethernet interface under the LwIP stack. The hardware is W5500; the socket API is the ESP-IDF layer.

---

## WriteWatch — '녹화' 표시등이 네트워크와 대화할 때

`#W5500` `#ESP32S3` `#TCP_TOE` `#mDNS` `#Broadcast` `#TallyLight` `#SmallHD` `#SwiftUI` `#DIT` `#VideoProduction`

> 📚 **컨텍스트:** 전문 영상 제작 / 현장 툴링 — **32Thirteen Productions LLC**가 실제 DIT(Digital Imaging Technician) 업무용으로 제작·배포한 작동하는 도구. 🔧 **검증 상태:** 펌웨어(`tally_light.ino`, v5.8)와 macOS 앱이 공개·버전 관리(15 커밋, 6 릴리스)되어 있으며, README는 PageOS 6.3.1을 구동하는 SmallHD OLED 22 모니터를 제어하는 Waveshare ESP32-S3-ETH 유닛에서의 하드웨어 검증 동작을 문서화하고 있다.

---

### 01 — 이 프로젝트는 무엇인가?

영화·방송 현장에서 **탤리 라이트(tally light)** 는 "이 카메라가 라이브다, 이 모니터가 작동 중이다 — 샷 안으로 들어오지 마라"를 모두에게 알리는 작은 빨간 등이다. 전통적으로 탤리는 값비싼 독점 스위처 하드웨어를 거쳐 배선되며, SmallHD 모니터의 GPI(General Purpose Input) 포트를 녹화 이벤트에 반응시키려면 보통 폐쇄적인 생태계에 종속되어야 한다.

**WriteWatch**는 두 개의 협력하는 구성 요소로 이를 뒤집는다.

1. **macOS 앱 (SwiftUI + AppKit + AVFoundation)** — 캡처 폴더를 실시간으로 감시한다. 500ms마다 폴링하여 *실제로 커지고 있는* 비디오 파일을 (SMB/NFS 네트워크 공유에서도) 감지하고, 쓰기 속도와 경과 시간을 추적하며, 완료된 클립을 AVFoundation으로 검증한다(코덱, 길이, VFR 감지).

2. **ESP32-S3 + W5500 탤리 브리지** — 이 "녹화 시작/정지" 이벤트를 물리적 탤리 신호로 변환한다. 앱은 LAN 상의 브리지 유닛을 자동 발견하고, **"Follow WriteWatch"** 자동화가 활성 쓰기가 감지되는 순간 탤리 라이트를 켜고 — 녹화가 멈추면 끈다.

결과적으로 완전히 개방적이고 자체 호스팅 가능한 탤리 시스템이 된다. 카메라가 돌고 바이트가 디스크에 떨어지기 시작하면 모든 모니터에서 빨간 불이 켜진다 — 독점 스위처 없이.

### 02 — 왜 이더넷 기반 네트워크 탤리인가?

#### 🔷 현장은 가혹한 RF 환경이다

영화 현장은 무선 트래픽으로 붐빈다 — 영상 송신기, 통신, 조명 제어, 모니터링. 여기서 Wi-Fi 단독은 취약하다. **유선 이더넷** 탤리 브리지는 절대 깜빡여서는 안 되는 단 하나의 신호인 "우리는 라이브다" 표시등에 결정론적이고 간섭 없는 시그널링을 제공한다. 펌웨어는 Wi-Fi를 *폴백 전용*으로 두고 이더넷을 주 경로로 삼는다.

#### 🔷 HTTP는 범용 통합 언어다

브리지는 아주 작은 HTTP API(`/tally/on`, `/tally/off`, `/tally/test`, `/status`)를 노출한다. 덕분에 방송용 버튼박스 제어의 사실상 표준인 **Bitfocus Companion**에 일반 HTTP GET 액션으로 그대로 연결된다 — 커스텀 드라이버도, SDK도 필요 없다. 네트워크상의 어떤 HTTP 클라이언트든 탤리를 트리거할 수 있다.

#### 🔷 WIZnet 생태계 적합성

시끄러운 LAN에서 견고한 TCP 서버를 유지해야 하는 소형 임베디드 컨트롤러라는 이 부류의 장치에는, 베어 MAC+PHY(예: 내장 EMAC + LAN8720) 위의 소프트웨어 스택(LwIP)보다 W5500의 하드웨어 TCP/IP 스택이 훨씬 잘 맞는다. 연결 처리를 실리콘에 오프로드하여 ESP32-S3가 경합 없이 GPIO 타이밍을 다루고 설정 포털을 서빙할 수 있게 한다.

### 03 — 시스템 아키텍처

![](https://maker.wiznet.io/upload/ckeditor5/632076252%5F1780317001%2Epng)

### 04 — 왜 W5500인가? ⭐

#### 🔷 타이밍 민감 장치에서의 하드웨어 TCP 오프로드

브리지의 핵심 임무는 GPIO를 예측 가능한 타이밍으로 토글하면서 *동시에* 항상 켜져 있는 안정적인 HTTP 엔드포인트가 되는 것이다. W5500은 8개의 독립 하드웨어 소켓을 갖춘 자체 하드웨어 TCP/IP 스택을 탑재하므로, ESP32-S3는 세그먼트 재조립이나 재전송 관리에 사이클을 쓸 필요가 없다. MCU는 HTTP 라우트 로직과 GPIO를, W5500은 물리 회선을 담당한다.

#### 🔷 사용 모드: **TCP 서버 (TOE) + UDP 기반 mDNS**

이 프로젝트는 W5500을 **TCP(TOE) 소켓 모드**로 사용한다 — 포트 80에서 HTTP `WebServer`를 리스닝한다(`/tally/on`, `/tally/off`, `/status` 등). 그와 함께 **mDNS 서비스 광고**(`MDNS.addService("tally", "tcp", 80)`)는 **UDP** 경로를 사용해 macOS 앱의 Bonjour 스캐너(`_tally._tcp`)가 LAN상의 유닛을 자동 발견하게 한다. 즉 칩이 두 가지 핵심 소켓 모드에 걸쳐 활용된다 — 제어는 TCP, 발견은 UDP.

> 통합 방식에 대한 참고: 이 펌웨어는 W5500을 WIZnet 자체 ioLibrary의 네이티브 소켓 API가 아니라 **ESP-IDF / Arduino-ESP32 **`**ETH**`** 드라이버**(`ETH.begin(ETH_PHY_W5500, ...)`)를 통해 구동한다. W5500이 SPI 연결 이더넷 인터페이스로 등록되고 그 위에 LwIP 스택이 올라가는 구조다. *물리적* 네트워킹은 W5500이고, 소켓 추상화는 ESP-IDF 레이어다. 이는 ESP32-S3 설계에서 W5500이 사용되는 흔하고 완전히 정당한 방식이다(Waveshare ESP32-S3-ETH 보드 자체가 정확히 이 패턴으로 설계됨).

#### 🔷 대안 대비 W5500의 우위

- **vs. Wi-Fi 단독:** RF로 가득 찬 현장에서는 부적합 — W5500이 유선 주 경로를 제공한다.

- **vs. 내장 EMAC + 외부 PHY(LAN8720):** ESP32-S3는 (오리지널 ESP32와 달리) 내장 EMAC이 *없다*. 따라서 SPI 연결 W5500 같은 외부 MAC+PHY가 S3 계열에 자연스러운 이더넷 해법이다.

- **vs. ENC28J60:** W5500은 완전한 하드웨어 TCP/IP 스택과 8개 소켓을 제공하는 반면, ENC28J60은 로우 MAC 전용이라 소프트웨어 스택과 더 많은 MCU 부하를 강요한다 — 실시간 탤리 장치에서 가장 피하고 싶은 상황이다.

#### 🔷 검증된 증거 ✅

- README의 명시적 W5500 핀 맵(RST GPIO9, INT GPIO10, MOSI GPIO11, MISO GPIO12, CLK GPIO13, CS GPIO14).

- `setup()` 내 `ETH.begin(ETH_PHY_W5500, ETH_PHY_ADDR, ETH_CS_PIN, ETH_INT_PIN, ETH_RST_PIN, SPI2_HOST, ...)`.

- 이더넷을 통한 실제 DHCP/정적 임대를 확인하는 `ARDUINO_EVENT_ETH_GOT_IP` 네트워크 이벤트 처리.

- SmallHD OLED 22(PageOS 6.3.1)에서의 하드웨어 검증된 GPI 동작(Active High, 7번 핀) 문서화.

### 05 — 핵심 구성 요소

#### 🌐 WIZnet W5500 — TCP(TOE) 서버 + UDP mDNS, SPI 연결

브리지의 네트워킹 심장. SPI2(HSPI 호스트)로 ESP32-S3에 연결된다. HTTP 제어 API와 Bonjour 발견 서비스 양쪽을 운반하는 유선 이더넷 링크를 제공한다.

#### 📟 Waveshare ESP32-S3-ETH

ESP32-S3와 W5500을 짝지은 캐리어 보드 — ESP32-S3에 내장 이더넷 MAC이 없어 SPI 이더넷 컨트롤러가 표준 경로가 되기 때문에 선택됨.

#### 💡 PC817 옵토커플러 (NOYITO 1채널)

ESP32의 GPIO18 탤리 출력을 SmallHD 모니터의 GPI 포트로부터 절연(galvanic isolation)한다 — 양 장치를 보호하고 모니터의 개방=ON / 단락=OFF 로직에 맞춘다.

#### 🖥️ macOS 앱 (SwiftUI + AVFoundation)

폴더 감시, 쓰기 속도 추적, AVFoundation 미디어 검증, Bonjour 발견, "Follow WriteWatch" 탤리 자동화. 외부 의존성 없는 순수 SwiftUI + AppKit + AVFoundation.

#### 📺 SmallHD OLED 22 모니터 (GPI)

물리적 탤리 대상. GPI를 Tally Indicator, Active High, 7번 핀으로 PageOS 6.x에서 구성.

### 06 — 응용 시나리오

**01. 멀티카메라 제작 현장** — Gang 모드로 모든 탤리 브리지를 한 번에 점화하거나, "Follow WriteWatch" 자동화로 녹화가 감지되는 즉시 모니터를 자동 점등 — 출연자와 스태프가 어느 카메라가 작동 중인지 항상 알 수 있다.

**02. DIT / 데이터 랭글링 카트** — 이 프로젝트가 원래 푸는 문제. 오프로드/캡처 폴더를 감시하는 DIT가 인제스트가 실제로 쓰기 중임을 알리는 물리적 신호를 받고, 볼륨별 디스크 공간 경고도 함께 받는다.

**03. Bitfocus Companion 기반 방송 부조정실** — HTTP 엔드포인트를 Companion 버튼에 매핑하여 Stream Deck 워크플로우와 함께 수동 녹화 on/off 탤리 제어를 한다.

**04. 범용 GPI 시그널링** — 브리지가 본질적으로 "절연된 HTTP-to-GPI 옵토커플러"이므로, SmallHD 모니터뿐 아니라 GPI 트리거 입력을 가진 어떤 장치에도 일반화된다.

### 결론

> **WriteWatch는 눈에 보이지 않는 소프트웨어 이벤트 — "바이트가 디스크에 떨어지고 있다" — 를 하드웨어처럼 신뢰할 수 있는, 네트워크로 분산된 빨간 불로 변환한다. 가장 RF가 가혹한 환경인 영화 현장에서 W5500을 사용해 견고한 유선 제어 평면을 유지하면서.**

- ✅ 실제로 운영되는 제작사(32Thirteen Productions LLC)가 배포한 실전 DIT 도구

- ✅ W5500을 TCP(HTTP 서버)와 UDP(mDNS 발견) 양쪽 모드로 사용

- ✅ Wi-Fi 폴백을 둔 유선 이더넷 우선 설계 — 현장에 맞는 올바른 엔지니어링

- ✅ 표준 HTTP API → 커스텀 드라이버 없이 즉각적인 Bitfocus Companion 통합

- ✅ PC817 옵토커플러를 통한 절연된 GPI 출력

- ✅ 브라우저 기반 설정 포털 — 한 번만 플래시하고 유닛별로 웹에서 설정, 유닛별 재컴파일 불필요

- ✅ 실제 SmallHD OLED 22 모니터(PageOS 6.3.1)에서 하드웨어 검증됨

- ✅ macOS와 펌웨어 양쪽 모두 깔끔하고 의존성 없는 코드베이스

### Q&A

**Q. 탤리 브리지는 W5500의 어떤 소켓 모드를 실제로 사용하나?** A. 두 핵심 모드 모두. HTTP 제어 서버(포트 80)는 **TCP**로, mDNS 서비스 발견(`_tally._tcp`)은 **UDP** 멀티캐스트로 동작한다. 제어 = TCP, 발견 = UDP.

**Q. 그냥 ESP32 내장 이더넷을 쓰면 안 되나?** A. ESP32-S3에는 내장 이더넷 MAC이 없다 — 그건 오리지널 ESP32의 기능이지 S3의 기능이 아니다. 그래서 W5500 같은 외부 SPI 이더넷 컨트롤러가 S3에 유선 네트워킹을 부여하는 표준적이고 필수적인 방법이다.

**Q. Wi-Fi 폴백이 있는데 왜 굳이 이더넷인가?** A. 영화 현장은 무선 트래픽으로 포화되어 있다. 탤리는 안전·협업에 치명적이므로 유선 W5500 경로가 주 경로이고, Wi-Fi는 케이블이 빠졌을 때만 개입한다.

**Q. W5500을 WIZnet ioLibrary로 구동하나?** A. 아니다 — ESP-IDF / Arduino-ESP32 `ETH` 드라이버(`ETH_PHY_W5500`)로 구동하며, W5500을 LwIP 스택 아래의 SPI 이더넷 인터페이스로 등록한다. 하드웨어는 W5500, 소켓 API는 ESP-IDF 레이어다.

---

Source: https://maker.wiznet.io/Lihan__/projects/writewatch-v2/
