Files
mangavalkandClaude Opus 5.5 ae87849e75 Add SignalRGB control via WLED emulation
The clock answers like a WLED device (mDNS _wled._tcp, /json/info,
/json/state, realtime UDP on 21324 and DDP on 4048), so SignalRGB can
drive the 4 backlight LEDs, one per tube, LED 0 = left tube. Frames are
shown at most 30 times a second (WLED_MAX_FPS); the normal effect
returns 2.5 s after they stop, and the alarm flash still wins.

Three new settings on the RGB page: enable (also a Home Assistant
switch), stay dark while the tubes are off, and scale by the RGB
brightness. They are stored in EEPROM after prm with their own magic
value, so the update does not factory-reset the clock.

page.js and site.css are now sent with no-cache: browsers kept them
for a year and ran the old script after a web UI update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 23:32:25 +01:00

7.0 KiB

Nixie clock: notes for Claude

An AliExpress WiFi nixie clock (4x IN-12 + 2x IN-3 neon dots, 4x WS2812, one under each tube) that is being moved from its closed stock firmware to ESP_UniClock (https://github.com/gpeter62/ESP_UniClock) with ArduinoOTA and Home Assistant (MQTT) support.

The device

  • On the network at 10.1.1.97 (WiFi dlink-iot). Since 2026-10-25 it runs ESP_UniClock (firmware/uniclock/, CLOCK_90): web UI on port 80 (JSON at /getConfiguration, /getCurrentInfos), ArduinoOTA on UDP 8266 (hostname nixie-in12, no password yet).
  • Board: RetroModding (Konstantin Repnikov) "NixieIN12v2 HV2.0", stock firmware SV1.3 (©2020). ESP-12 (ESP8266, MAC 30:83:98:99:BF:DE, Espressif).
  • Powered by USB-C 5 V only (no DC jack). Most likely the maker's "v2.0L lightweight 5 volt version". The user says the PCB looks different from the 12 V v2 board.
  • No buttons fitted, only the light sensor.
  • Stock settings (2026-10-25): time zone +01:00, EU DST on, NTP time.euro.apple.com, brightness auto, 24 h. Stock DST might not work: check in late March.

Stock firmware (from the flash dump)

  • firmware/stock/nixie_stock_SV1.3_full_4MB.bin (gitignored), SHA-256 b069f4eb93cf1e9063a30374db4d70508211f07b0a17049bcf9ebdeda65f2985, verified against the chip with verify-flash. Flash: 4 MB, DIO, 40 MHz.
  • Arduino ESP8266 core 2.x (NONOS SDK 2.2.2-dev), built for board ESP8266_NODEMCU. App at 0x1000 (~1 MB slot), SPIFFS at 0x200000. Settings live in SPIFFS as JSON (/config.json, /wifi.json, /light.json, /leds.json, /alerts.json, /hardware.json).
  • Hidden pages: /d = "Hardware setting" (hidden from the menu by default). It has checkboxes to hide/show menu items and a Debug mode (dev) checkbox. With Debug mode on, /u serves the standard ESP8266HTTPUpdateServer upload form, which is web OTA for .bin/.bin.gz. /r reboots on a plain GET; don't GET unknown paths blindly.
  • No pin numbers are visible as strings; confirming the pinout needs disassembly or the bit-stepping test sketch.

Pinout

From the 12 V v2 schematic (docs/schematic_v2_12V.jpg), checked against the stock firmware by disassembly (docs/stock-firmware-pins.md, tools in tools/). "Hardware" = also works on the clock with UniClock; "Binary" = only confirmed in the stock SV1.3 code.

Function GPIO Source
74HC595 DATA / CLOCK (OE tied to GND) 13 / 14 = HSPI MOSI / SCK (hardware SPI, MSB first) Hardware
74HC595 LATCH 15, plain digitalWrite (has 1k pull-down) Hardware
WS2812 data 12 (NeoPixel bit-bang, GRB 800 kHz; 4 fitted, stock assumes 12). SPI.begin() turns GPIO12 into MISO: set it back to OUTPUT afterwards Hardware
Photoresistor (LDR, 10k to GND) ADC (A0) Hardware
BTN1 / BTN2 (not fitted) 0 / 2, INPUT_PULLUP. Stock: GPIO0 is a button, GPIO2 a motion/presence input that wakes the display Binary
DFPlayer Mini (stock feature) UART0: GPIO1 TX / GPIO3 RX, 9600 Bd Binary
DS3231M RTC SDA / SCL 4 / 5: no I2C device answers (UniClock scan), so no RTC on this board; NTP only Hardware

Two chained 74HC595s make one 16-bit word, all bits active-high (confirmed from the binary; the high byte is sent first and goes to U2):

  • U1 QA..QH = cathodes 0..7 (bits 0..7), U2 QA = cathode 8 (bit 8), QB = cathode 9 (bit 9) (via 2x ULN2003, shared by all tubes)
  • U2 QC = P1 (bit 10) drives both IN-3 neon dots. QD..QG = anodes A1..A4 (bits 11..14, MMBTA42/92). QH = P2 (bit 15) drives the IN-12B decimal points on this board (JP2 closed), not a neon.
  • The display is multiplexed: one anode on at a time. A1 = leftmost tube (hour tens), A4 = rightmost (minute ones) (verified on the tubes). The dots are lit in the minute-tens slot only, as on stock.

Programming header

Header on the PCB: GND, GPIO0, RST, RXD, TXD, 3.3V. The USB-C port only supplies power. Flashing is done with a CH340C board on COM7 (COM3 is another device; leave it alone). Wiring: GND-GND, adapter TXD -> clock RXD, adapter RXD -> clock TXD, adapter 3.3V not connected (the clock is powered from its own USB-C). No auto-reset: to enter flash mode, hold GPIO0 to GND, tap RST to GND, then release GPIO0.

  • esptool is installed for the user (python -m esptool, v5.6.0). After --after no-reset the stub keeps running at the new baud rate and the next esptool call can't connect: do everything in one call, or re-enter flash mode.
  • Normal updates go over WiFi: pio run -e nixie_in12_ota -t upload in firmware/uniclock/ (web UI files: -t uploadfs). After the first switch from AP mode to WiFi the clock restarts once by itself, so ArduinoOTA/MQTT start on the real network.
  • /getCurrentInfos (no password) has uptime and heap; with LOOP_DIAG in CLOCK_90 also stalls (main-loop gaps of 250 ms or more, by step name).
  • firmware/stock/ holds the stock flash dump. Never overwrite or delete it; it is the only way back to SV1.3 (write-flash 0 <dump>). Dumps are gitignored because the flash holds the WiFi password; never commit them.

Constraints

  • The 170 V tube supply is live whenever USB-C is connected.
  • Hardware is only reachable via the user: anything that needs RST/GPIO0 or looking at the tubes must be asked of them. Builds can be checked here, but runtime behaviour can't.
  • Show the exact plan before making changes to the device (flashing, saving settings).

Plan

  1. Done: dump and inspect the stock firmware.
  2. Skipped (the disassembly was enough): bit-stepping test sketch.
  3. Done: 74HC595-multiplex driver (nixie595.ino) + CLOCK_90 in ESP_UniClock, ArduinoOTA, EU DST fix. Runs on the clock.
  4. Done: Home Assistant over MQTT (broker 10.1.1.175, discovery prefix homeassistant). mqtt.ino rewritten for ArduinoHA 2.1.0: switches Tubes/Auto dim, numbers Day/Night brightness, light Backlight (on/off, brightness, RGB -> UniClock colour wheel), select LED effect, button Cathode protect, sensors Light/WiFi signal. Broker settings are read at boot.
  5. Done (2026-10-25): tube flicker fixed (the bit-banged WS2812 update blocks interrupts, so it is now only sent when the pixels change, and only in the dark gap between tubes; this also fixed digits that seemed to hang). Saving the firmware URL no longer starts an update. Optional web password, cathode-protect random start, LED gamma.
  6. Done (2026-10-25): SignalRGB via WLED emulation (wled.ino, USE_WLED_REALTIME, WLED_MAX_FPS 30: no flicker, no loop stalls). SignalRGB's WLED plugin only sends as many LEDs as the channel's component has (needs 4). LED order checked: SignalRGB LED 0 = left tube, tubePixels {0,1,2,3} is right. Settings wledEnable / wledDarkWithTubes / wledUseBrightness live in prmExt, stored in EEPROM after prm with their own magic value: never add fields to prm (a MAGIC_VALUE bump factory-resets the clock, WiFi included). HA switch "SignalRGB". page.js/site.css were cached by browsers for a year; now sent with no-cache.
  7. Open: the user can create secrets.h (gitignored, never read it) for OTA and web passwords.