- The bit-banged WS2812 update disables interrupts (about 30 us per pixel), which made the lit tube stay on longer and flash. neoShow() now only sends when the pixels changed (fixed colour was resent every second) and waits for the dark gap between tubes; the bit-bang strip has no headroom pixels. This also fixed digits that seemed to hang. - 4 WS2812 on this board, one per tube (not 8) - Saving the firmware URL in the web UI no longer starts an HTTP update; never update from an empty URL - Restart once after the first switch from AP mode to WiFi, so ArduinoOTA and MQTT start on the real network - Optional web UI password (WEB_USER/WEB_PASSWORD in secrets.h); /getConfiguration returns the stored WiFi and MQTT passwords - Cathode protect: random mode now starts at a random tube (the +-1 start digits were swapped and then overwritten) - Apply the unused gamma table to wheel colours - LOOP_DIAG (off by default): main-loop stalls in /getCurrentInfos, which now also reports uptime and free heap Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
104 lines
6.4 KiB
Markdown
104 lines
6.4 KiB
Markdown
# 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. Open: the user can create `secrets.h` (gitignored, never read it) for OTA and web passwords.
|