diff --git a/CLAUDE.md b/CLAUDE.md index 6640401..b32a866 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,7 +1,7 @@ # CLAUDE.md -ESPHome external component for a Samsung 2×20 serial VFD (5 V TTL, 9600 8N1, -write-only). It is consumed from git by an ESPHome server through +ESPHome external component for a Samsung 20L203 2×20 serial VFD (RS-232 +input, 9600 8N1, write-only). It is consumed from git by an ESPHome server through `external_components: source: github://...`, so the layout must stay `components/samsung_vfd/` at the repo root. @@ -13,6 +13,8 @@ write-only). It is consumed from git by an ESPHome server through and the templated action classes. - `components/samsung_vfd/samsung_vfd.cpp`: the shadow buffer, word wrap, UTF-8 → single-byte sanitising and `flush_()`. +- `docs/20L203DA12-datasheet.pdf`: the command set (section 5.4), jumpers, + wiring and the character tables. Check it before adding protocol features. - `examples/samsung-vfd.yaml`: the full reference device config. It pulls the component from GitHub; for local work, switch to the commented `type: local, path: ../components` source. @@ -27,8 +29,13 @@ write-only). It is consumed from git by an ESPHome server through `last_payload_` so the next flush retransmits. - Text sent to the display is one byte per character; always pass it through `sanitize_()`. -- Keep the protocol constants in `samsung_vfd.h`. Only `ESC @`, `0C` and `0B` - are known to work. +- Keep the protocol constants in `samsung_vfd.h`. The display boots in + overwrite mode: the cursor wraps from the end of line 1 to line 2 and from + the end of line 2 back home, and nothing scrolls. A full-screen redraw is + therefore `0B` plus `rows × cols` characters. +- The RXD input is RS-232. The reference build uses a MAX3232 (no inversion + in config); a directly wired ESP32 pin needs `inverted: true` on the UART TX + pin. ## Checking changes diff --git a/README.md b/README.md index 3345693..7ff029e 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,23 @@ # Samsung serial VFD — ESPHome component -An ESPHome external component for the Samsung 2-line × 20-character serial VFD -(vacuum fluorescent display) that takes 5 V TTL serial at 9600 baud, 8N1. +An ESPHome external component for the Samsung 20L203 2-line × 20-character VFD +(vacuum fluorescent display) pole display, driven over a single serial wire at +9600 baud. It keeps a copy of the screen in RAM and only sends data when something changes. Updates overwrite the screen in place instead of clearing it first, so the clock doesn't flicker. +

+ Front of the display + Back of the controller board +

+ ``` components/samsung_vfd/ the component (Python schema + C++) examples/samsung-vfd.yaml full example: clock + two Home Assistant text entities examples/secrets.yaml.example +docs/ photos and the 20L203DA12 datasheet ``` ## Using it from your ESPHome server @@ -43,24 +50,70 @@ into your ESPHome config folder. Add the keys from [examples/secrets.yaml.example](examples/secrets.yaml.example) to your `secrets.yaml`. -## Wiring +## Hardware -The component only sends to the display. The ESP32's 3.3 V output is enough for -most 5 V TTL inputs. ESP32 GPIOs are **not** 5 V tolerant, so never connect the -display's TX back to the ESP32 without a level shifter or divider. +The board in the photos is a 20L203DA5 (rev A). The included +[datasheet](docs/20L203DA12-datasheet.pdf) is for the 20L203DA12, a variant +with the same command set. -| Display | ESP32 | -|---------|-------| -| RX | GPIO17 (UART TX) | -| GND | GND | -| 5 V | external 5 V supply | +### Connector (3-pin Molex 5046-03A) -The display draws more current than a dev-board regulator can comfortably -supply. Power it from its own supply and connect the grounds together. +| Pin | Signal | Connect to | +|-----|--------|------------| +| 1 | VCC | 5–24 V DC supply | +| 2 | RXD | MAX3232 T1OUT (T1IN ← ESP32 GPIO17), see below | +| 3 | GND | supply GND, MAX3232 GND **and** ESP32 GND | -To use GPIO1 (TX0, shared with USB logging), set `tx_pin: GPIO1` and -`logger: baud_rate: 0` so log output doesn't end up on the display. If the -display stays blank at 3.3 V levels, add a 74HCT125 or similar level shifter. +### Power + +The display takes 5–24 V. At 5 V it draws about 0.8 A typical and 1.2 A +maximum, and the inrush current at power-on can be up to five times that. A +dev-board regulator can't supply this, so give the display its own supply, +for example 12 V at 1 A, and connect the grounds together. + +### Signal levels + +The RXD input goes through an RS-232 receiver: it expects ±3 to ±15 V and +idles at the negative level (mark). It tolerates ±30 V, so a 3.3 V ESP32 pin +can't damage it. There are two ways to connect it: + +- **Through a MAX3232 (recommended, and how the original build is wired).** + Power the MAX3232 from the ESP32's 3.3 V, then wire GPIO17 → T1IN and + T1OUT → display RXD. The MAX3232 produces real RS-232 levels and does the + inversion itself, so use the plain `tx_pin: GPIO17`. +- **Direct from the ESP32.** A plain TTL signal arrives inverted, so wire + GPIO17 straight to RXD and invert the pin in the UART config: + + ```yaml + uart: + id: uart_vfd + tx_pin: + number: GPIO17 + inverted: true + baud_rate: 9600 + ``` + + This relies on the receiver's switching threshold, which on most RS-232 + receivers is around 1.3 V. It isn't guaranteed by the spec, which asks for + at least 3 V. + +If the display shows garbage or nothing at all, the inversion setting is the +first thing to check: off with a MAX3232, on without one. + +### Jumpers + +The 2×8 pin header on the board selects the following. All jumpers open is the +factory default. + +| Jumpers | Setting | Default (open) | +|---------|---------|----------------| +| 1, 2 | baud rate: 9600 / 4800 / 2400 / 19200 | 9600 | +| 3, 4 | parity: none / odd / even | none | +| 5 | self-test at power-on | off | +| 6, 7, 8 | font for codes 0x80–0xFF | PC437 | + +This component expects 9600 baud and no parity, which ESPHome's UART uses by +default. ## Configuration @@ -70,7 +123,7 @@ samsung_vfd: uart_id: uart_vfd cols: 20 # default 20 (1–80) rows: 2 # default 2 (1–8) - send_newlines: false # default false; send CR LF between lines + send_newlines: false # default false; leave off for this display ``` The UART must be 9600 baud with a `tx_pin`. The component requires a `time:` @@ -121,26 +174,30 @@ characters become spaces. ## Protocol +These are the commands the component uses. The datasheet, section 5.4, lists +the rest, including cursor positioning, brightness, blinking and the +annunciator triangles. + | Bytes | Effect | |-------|--------| -| `1B 40` (`ESC @`) | init / reset | -| `0C` | clear screen | -| `0B` | cursor home | -| text | printed at the cursor; wraps to the next line by itself | +| `1B 40` (`ESC @`) | initialise: reset settings, clear, cursor home | +| `0C` | clear screen, cursor home | +| `0B` | cursor home (top-left) | +| `20`–`FF` | print a character and move the cursor right | -Each update sends `0B` (home) followed by both lines, each padded to exactly -`cols` characters. +At power-on the display is in overwrite mode. After the last column of the top +line the cursor continues on the bottom line, and after the last column of the +bottom line it goes back to the top-left. Nothing scrolls. Each update +therefore sends `0B` (home) followed by both lines, each padded to exactly 20 +characters. ## Troubleshooting -- **Line 2 lands on the wrong row, or text runs together:** try - `send_newlines: true`. -- **The screen scrolls up a line after each update:** the display scrolls as - soon as the last cell is written. Use `cols: 19` together with - `send_newlines: true`. -- **A stray character appears top-left:** the display doesn't treat `0B` as - home. Change `CMD_HOME` in - [samsung_vfd.h](components/samsung_vfd/samsung_vfd.h). -- **Garbled characters:** check that `uart:` is at 9600 baud and that the - display takes 5 V TTL levels, not RS-232 levels. A real RS-232 input needs a - MAX232 inverter. +- **Garbage or nothing on the display:** toggle `inverted:` on the TX pin (see + [Signal levels](#signal-levels)). Check that the baud jumpers 1 and 2 are + open (9600) and that parity jumpers 3 and 4 are open (none). +- **The display runs its self-test for about 20 s after power-on:** jumper 5 is + shorted. +- **Line 2 text lands on line 1:** make sure `send_newlines` is off. With + 20-column lines the cursor has already wrapped to line 2, and an extra line + feed sends it back to line 1. diff --git a/components/samsung_vfd/samsung_vfd.h b/components/samsung_vfd/samsung_vfd.h index 3107efc..ee72776 100644 --- a/components/samsung_vfd/samsung_vfd.h +++ b/components/samsung_vfd/samsung_vfd.h @@ -14,10 +14,7 @@ namespace samsung_vfd { static const char *const TAG = "samsung_vfd"; -// Control bytes, taken from older Arduino sketches that drove this display. -// The one known to work only ever used ESC @ and 0x0C; 0x0B ("home") came -// from an earlier test sketch. That sketch also sent 0xB0, which is left out: -// it is most likely a printable glyph, not a command. +// Control bytes, see section 5.4 of docs/20L203DA12-datasheet.pdf. static const uint8_t CMD_ESC = 0x1B; static const uint8_t CMD_INIT = 0x40; // ESC @ -> reset / init display static const uint8_t CMD_CLEAR = 0x0C; // FF -> clear screen (cursor to home) diff --git a/docs/20L203DA12-datasheet.pdf b/docs/20L203DA12-datasheet.pdf new file mode 100644 index 0000000..809ecc2 Binary files /dev/null and b/docs/20L203DA12-datasheet.pdf differ diff --git a/docs/back.jpg b/docs/back.jpg new file mode 100644 index 0000000..8b6fd22 Binary files /dev/null and b/docs/back.jpg differ diff --git a/docs/front.jpg b/docs/front.jpg new file mode 100644 index 0000000..989b829 Binary files /dev/null and b/docs/front.jpg differ diff --git a/examples/samsung-vfd.yaml b/examples/samsung-vfd.yaml index f137741..39d96d2 100644 --- a/examples/samsung-vfd.yaml +++ b/examples/samsung-vfd.yaml @@ -3,14 +3,13 @@ # Copy this file into your ESPHome config folder (and add the keys below to # your secrets.yaml), or use it as a reference for your own device config. # -# Wiring (display is 5V TTL serial, 9600 8N1): -# display RX <- ESP32 GPIO17 (TX) -# display GND <-> ESP32 GND -# display 5V <- 5V supply (the display needs more current than the ESP32 regulator can give) +# Wiring (3-pin connector on the display, 9600 8N1): +# pin 1 VCC <- 5-24V supply, up to 1.2A at 5V (not the ESP32 regulator) +# pin 2 RXD <- MAX3232 T1OUT <- T1IN <- ESP32 GPIO17 (TX) +# pin 3 GND <-> supply GND, MAX3232 GND and ESP32 GND # -# To drive the display from GPIO1 (TX0, the USB serial pin) instead, set -# `tx_pin: GPIO1` below and `baud_rate: 0` under `logger:` so log output does -# not end up on the display. +# RXD is an RS-232 input. Without a MAX3232, wire GPIO17 straight to RXD and +# set `inverted: true` under uart. esphome: name: samsung-vfd @@ -81,7 +80,9 @@ time: uart: id: uart_vfd - tx_pin: GPIO17 + tx_pin: + number: GPIO17 + inverted: false # true when wired directly, without a MAX3232 baud_rate: 9600 # The display is output-only for us; no rx_pin needed. @@ -90,8 +91,7 @@ samsung_vfd: uart_id: uart_vfd cols: 20 rows: 2 - # Lines are padded to 20 chars and the display wraps by itself. Set to true - # only if line 2 ends up on the wrong row (sends CR LF between the lines). + # Lines are padded to 20 chars and the display wraps to line 2 by itself. send_newlines: false # Bottom line shows the SNTP clock every second, except while a status banner is up.