Add photos and datasheet; correct wiring and protocol docs
The 20L203 input is RS-232 (reference build uses a MAX3232), it runs on 5-24 V, and it boots in non-scrolling overwrite mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bd9389e04a
commit
3fcd15ad86
7 files changed
+113
-52
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
# CLAUDE.md
|
# CLAUDE.md
|
||||||
|
|
||||||
ESPHome external component for a Samsung 2×20 serial VFD (5 V TTL, 9600 8N1,
|
ESPHome external component for a Samsung 20L203 2×20 serial VFD (RS-232
|
||||||
write-only). It is consumed from git by an ESPHome server through
|
input, 9600 8N1, write-only). It is consumed from git by an ESPHome server through
|
||||||
`external_components: source: github://...`, so the layout must stay
|
`external_components: source: github://...`, so the layout must stay
|
||||||
`components/samsung_vfd/` at the repo root.
|
`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.
|
and the templated action classes.
|
||||||
- `components/samsung_vfd/samsung_vfd.cpp`: the shadow buffer, word wrap,
|
- `components/samsung_vfd/samsung_vfd.cpp`: the shadow buffer, word wrap,
|
||||||
UTF-8 → single-byte sanitising and `flush_()`.
|
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
|
- `examples/samsung-vfd.yaml`: the full reference device config. It pulls the
|
||||||
component from GitHub; for local work, switch to the commented
|
component from GitHub; for local work, switch to the commented
|
||||||
`type: local, path: ../components` source.
|
`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.
|
`last_payload_` so the next flush retransmits.
|
||||||
- Text sent to the display is one byte per character; always pass it through
|
- Text sent to the display is one byte per character; always pass it through
|
||||||
`sanitize_()`.
|
`sanitize_()`.
|
||||||
- Keep the protocol constants in `samsung_vfd.h`. Only `ESC @`, `0C` and `0B`
|
- Keep the protocol constants in `samsung_vfd.h`. The display boots in
|
||||||
are known to work.
|
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
|
## Checking changes
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,23 @@
|
|||||||
# Samsung serial VFD — ESPHome component
|
# Samsung serial VFD — ESPHome component
|
||||||
|
|
||||||
An ESPHome external component for the Samsung 2-line × 20-character serial VFD
|
An ESPHome external component for the Samsung 20L203 2-line × 20-character VFD
|
||||||
(vacuum fluorescent display) that takes 5 V TTL serial at 9600 baud, 8N1.
|
(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
|
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,
|
changes. Updates overwrite the screen in place instead of clearing it first,
|
||||||
so the clock doesn't flicker.
|
so the clock doesn't flicker.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<img src="docs/front.jpg" alt="Front of the display" height="300">
|
||||||
|
<img src="docs/back.jpg" alt="Back of the controller board" height="300">
|
||||||
|
</p>
|
||||||
|
|
||||||
```
|
```
|
||||||
components/samsung_vfd/ the component (Python schema + C++)
|
components/samsung_vfd/ the component (Python schema + C++)
|
||||||
examples/samsung-vfd.yaml full example: clock + two Home Assistant text entities
|
examples/samsung-vfd.yaml full example: clock + two Home Assistant text entities
|
||||||
examples/secrets.yaml.example
|
examples/secrets.yaml.example
|
||||||
|
docs/ photos and the 20L203DA12 datasheet
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using it from your ESPHome server
|
## 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
|
[examples/secrets.yaml.example](examples/secrets.yaml.example) to your
|
||||||
`secrets.yaml`.
|
`secrets.yaml`.
|
||||||
|
|
||||||
## Wiring
|
## Hardware
|
||||||
|
|
||||||
The component only sends to the display. The ESP32's 3.3 V output is enough for
|
The board in the photos is a 20L203DA5 (rev A). The included
|
||||||
most 5 V TTL inputs. ESP32 GPIOs are **not** 5 V tolerant, so never connect the
|
[datasheet](docs/20L203DA12-datasheet.pdf) is for the 20L203DA12, a variant
|
||||||
display's TX back to the ESP32 without a level shifter or divider.
|
with the same command set.
|
||||||
|
|
||||||
| Display | ESP32 |
|
### Connector (3-pin Molex 5046-03A)
|
||||||
|---------|-------|
|
|
||||||
| RX | GPIO17 (UART TX) |
|
|
||||||
| GND | GND |
|
|
||||||
| 5 V | external 5 V supply |
|
|
||||||
|
|
||||||
The display draws more current than a dev-board regulator can comfortably
|
| Pin | Signal | Connect to |
|
||||||
supply. Power it from its own supply and connect the grounds together.
|
|-----|--------|------------|
|
||||||
|
| 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
|
### Power
|
||||||
`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.
|
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
|
## Configuration
|
||||||
|
|
||||||
@@ -70,7 +123,7 @@ samsung_vfd:
|
|||||||
uart_id: uart_vfd
|
uart_id: uart_vfd
|
||||||
cols: 20 # default 20 (1–80)
|
cols: 20 # default 20 (1–80)
|
||||||
rows: 2 # default 2 (1–8)
|
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:`
|
The UART must be 9600 baud with a `tx_pin`. The component requires a `time:`
|
||||||
@@ -121,26 +174,30 @@ characters become spaces.
|
|||||||
|
|
||||||
## Protocol
|
## 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 |
|
| Bytes | Effect |
|
||||||
|-------|--------|
|
|-------|--------|
|
||||||
| `1B 40` (`ESC @`) | init / reset |
|
| `1B 40` (`ESC @`) | initialise: reset settings, clear, cursor home |
|
||||||
| `0C` | clear screen |
|
| `0C` | clear screen, cursor home |
|
||||||
| `0B` | cursor home |
|
| `0B` | cursor home (top-left) |
|
||||||
| text | printed at the cursor; wraps to the next line by itself |
|
| `20`–`FF` | print a character and move the cursor right |
|
||||||
|
|
||||||
Each update sends `0B` (home) followed by both lines, each padded to exactly
|
At power-on the display is in overwrite mode. After the last column of the top
|
||||||
`cols` characters.
|
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
|
## Troubleshooting
|
||||||
|
|
||||||
- **Line 2 lands on the wrong row, or text runs together:** try
|
- **Garbage or nothing on the display:** toggle `inverted:` on the TX pin (see
|
||||||
`send_newlines: true`.
|
[Signal levels](#signal-levels)). Check that the baud jumpers 1 and 2 are
|
||||||
- **The screen scrolls up a line after each update:** the display scrolls as
|
open (9600) and that parity jumpers 3 and 4 are open (none).
|
||||||
soon as the last cell is written. Use `cols: 19` together with
|
- **The display runs its self-test for about 20 s after power-on:** jumper 5 is
|
||||||
`send_newlines: true`.
|
shorted.
|
||||||
- **A stray character appears top-left:** the display doesn't treat `0B` as
|
- **Line 2 text lands on line 1:** make sure `send_newlines` is off. With
|
||||||
home. Change `CMD_HOME` in
|
20-column lines the cursor has already wrapped to line 2, and an extra line
|
||||||
[samsung_vfd.h](components/samsung_vfd/samsung_vfd.h).
|
feed sends it back to line 1.
|
||||||
- **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.
|
|
||||||
@@ -14,10 +14,7 @@ namespace samsung_vfd {
|
|||||||
|
|
||||||
static const char *const TAG = "samsung_vfd";
|
static const char *const TAG = "samsung_vfd";
|
||||||
|
|
||||||
// Control bytes, taken from older Arduino sketches that drove this display.
|
// Control bytes, see section 5.4 of docs/20L203DA12-datasheet.pdf.
|
||||||
// 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.
|
|
||||||
static const uint8_t CMD_ESC = 0x1B;
|
static const uint8_t CMD_ESC = 0x1B;
|
||||||
static const uint8_t CMD_INIT = 0x40; // ESC @ -> reset / init display
|
static const uint8_t CMD_INIT = 0x40; // ESC @ -> reset / init display
|
||||||
static const uint8_t CMD_CLEAR = 0x0C; // FF -> clear screen (cursor to home)
|
static const uint8_t CMD_CLEAR = 0x0C; // FF -> clear screen (cursor to home)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.1 MiB |
+10
-10
@@ -3,14 +3,13 @@
|
|||||||
# Copy this file into your ESPHome config folder (and add the keys below to
|
# 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.
|
# your secrets.yaml), or use it as a reference for your own device config.
|
||||||
#
|
#
|
||||||
# Wiring (display is 5V TTL serial, 9600 8N1):
|
# Wiring (3-pin connector on the display, 9600 8N1):
|
||||||
# display RX <- ESP32 GPIO17 (TX)
|
# pin 1 VCC <- 5-24V supply, up to 1.2A at 5V (not the ESP32 regulator)
|
||||||
# display GND <-> ESP32 GND
|
# pin 2 RXD <- MAX3232 T1OUT <- T1IN <- ESP32 GPIO17 (TX)
|
||||||
# display 5V <- 5V supply (the display needs more current than the ESP32 regulator can give)
|
# pin 3 GND <-> supply GND, MAX3232 GND and ESP32 GND
|
||||||
#
|
#
|
||||||
# To drive the display from GPIO1 (TX0, the USB serial pin) instead, set
|
# RXD is an RS-232 input. Without a MAX3232, wire GPIO17 straight to RXD and
|
||||||
# `tx_pin: GPIO1` below and `baud_rate: 0` under `logger:` so log output does
|
# set `inverted: true` under uart.
|
||||||
# not end up on the display.
|
|
||||||
|
|
||||||
esphome:
|
esphome:
|
||||||
name: samsung-vfd
|
name: samsung-vfd
|
||||||
@@ -81,7 +80,9 @@ time:
|
|||||||
|
|
||||||
uart:
|
uart:
|
||||||
id: uart_vfd
|
id: uart_vfd
|
||||||
tx_pin: GPIO17
|
tx_pin:
|
||||||
|
number: GPIO17
|
||||||
|
inverted: false # true when wired directly, without a MAX3232
|
||||||
baud_rate: 9600
|
baud_rate: 9600
|
||||||
# The display is output-only for us; no rx_pin needed.
|
# The display is output-only for us; no rx_pin needed.
|
||||||
|
|
||||||
@@ -90,8 +91,7 @@ samsung_vfd:
|
|||||||
uart_id: uart_vfd
|
uart_id: uart_vfd
|
||||||
cols: 20
|
cols: 20
|
||||||
rows: 2
|
rows: 2
|
||||||
# Lines are padded to 20 chars and the display wraps by itself. Set to true
|
# Lines are padded to 20 chars and the display wraps to line 2 by itself.
|
||||||
# only if line 2 ends up on the wrong row (sends CR LF between the lines).
|
|
||||||
send_newlines: false
|
send_newlines: false
|
||||||
|
|
||||||
# Bottom line shows the SNTP clock every second, except while a status banner is up.
|
# Bottom line shows the SNTP clock every second, except while a status banner is up.
|
||||||
|
|||||||
Reference in new issue
Block a user