A DEC VT520 as a Linux Writing Terminal (with german umlauts)

Using a VT520 on a Utilite Pro over a Zimodem Wi-Fi modem — with working German umlauts, UTF-8 files, and nano.

This guide describes a working setup and explains how to rebuild it. It covers the two problems that cost the most time:

  1. German umlauts are garbled or missing because the VT520 speaks ISO-8859-1 / DEC character sets, while modern Linux and Hugo expect UTF-8.
  2. Typed characters are invisible because netkit-telnetd and the Zimodem negotiate the Telnet ECHO option incorrectly.

The solution keeps the host fully UTF-8, transcodes only inside GNU screen, and explicitly re-enables terminal echo for VT520 logins.


1. Result

After following this guide:

  • You log in as vt at the VT520 and land in a screen session automatically.
  • nano displays ÄÖÜäöüß correctly.
  • Files written from the VT520 are stored as UTF-8 on the host, so Hugo and every other modern tool work normally.
  • SSH sessions from a PC (kitty, xterm, etc.) are completely unaffected.
  • If the Zimodem link drops, logging in again reattaches the existing screen session, including the open nano buffer.

Tested environment:

Component Version / value
Host CompuLab Utilite Pro, armv7l
OS Ubuntu 22.04.5 LTS
Kernel 4.1.15-cm-fx6-8.0
Host locale de_DE.UTF-8
Terminal DEC VT520, 80x24
Modem Zimodem (Wi-Fi serial modem), 9600 8N1
Telnet server netkit-telnetd 0.17-44build1 via inetd
screen GNU screen 4.09.00
Editor GNU nano 6.2
terminfo ncurses-term (infocmp vt520 works)

2. Topology

 DEC VT520                 Zimodem                    Utilite Pro (Linux)
 ISO-8859-1                Wi-Fi bridge               de_DE.UTF-8
 80x24, 9600 8N1   --->    telnet client   ---TCP/23-->  inetd -> telnetd
 XON/XOFF                  ATDT"host:port"               -> login -> bash
                                                          -> screen
                                                             -> UTF-8 apps
                                                                (nano, shell)

The Zimodem is a serial-to-Telnet bridge. The VT520 thinks it is talking to a modem; the Linux host sees an ordinary Telnet login on port 23. No special terminal server or serial cable to the host is involved.

In the reference setup:

  • Utilite Pro: 192.168.178.169
  • Zimodem: 192.168.178.25
  • Login user: vt

Substitute your own addresses and user name where they appear.


3. Why umlauts break

3.1 Two incompatible encodings

Modern Linux uses UTF-8. In UTF-8, ä is two bytes:

ä  ->  C3 A4
ö  ->  C3 B6
ü  ->  C3 BC
ß  ->  C3 9F

The VT520 does not support UTF-8. It uses ISO-8859-1 (plus DEC supplemental character sets), where each character is one byte:

ä  ->  E4
ö  ->  F6
ü  ->  FC
ß  ->  DF

If you send UTF-8 bytes straight to the VT520, it prints two wrong characters (ä) or nothing useful. If you switch the whole host to ISO-8859-1, the VT520 looks fine but Hugo, Git, and every modern tool receive Latin-1 files and break.

3.2 The design decision

Keep the host in UTF-8. Transcode only on the VT520 connection.

GNU screen can translate between an inner and an outer encoding. The host applications keep talking UTF-8; screen converts to ISO-8859-1 for the VT520 and back again for keyboard input.

This has three benefits:

  • Hugo and all files stay UTF-8.
  • The VT520 gets the single-byte characters it understands.
  • SSH sessions are untouched because the translation only exists inside the VT520’s screen.

4. Prerequisites on the Linux host

Install the terminal definitions, screen, nano, and a Telnet server:

sudo apt update
sudo apt install screen nano ncurses-term telnetd

Make sure the German UTF-8 locale exists:

locale -a | grep -i de_DE.utf8
# if missing:
sudo locale-gen de_DE.UTF-8
sudo update-locale LANG=de_DE.UTF-8

Check that the VT520 terminal description is available:

infocmp vt520 >/dev/null && echo "vt520 terminfo OK"

Check that the Telnet server is listening:

ss -ltnp | grep ':23'

The reference host runs telnetd from inetd. Any working Telnet daemon on port 23 is fine; the ECHO bug described below is a netkit-telnetd behaviour and may differ on other implementations.

Security note: Telnet is unencrypted. Use this only on a trusted LAN, or put the VT520 connection behind a VPN. Do not expose port 23 to the Internet.


5. Step-by-step setup

All files live in the home directory of the login user, here /home/vt.

5.1 Create ~/.screenrc.vt520

This is the heart of the encoding fix:

# ~/.screenrc.vt520
# Outside (VT520): ISO-8859-1. Inside (applications): UTF-8.
# screen translates in both directions.

startup_message off
vbell off
defscrollback 1000

shell /bin/bash

defencoding utf8
screen -t shell
encoding utf8 iso8859-1

What the lines do:

Line Purpose
startup_message off No copyright banner on a slow 80x24 screen.
vbell off The VT520 has no useful visual bell.
defscrollback 1000 Keep scrollback for reconnecting.
shell /bin/bash Always run bash inside the window, independent of the account’s login shell.
defencoding utf8 Default encoding for new windows is UTF-8.
screen -t shell Create the first window and name it shell.
encoding utf8 iso8859-1 Window (applications) = UTF-8, connected terminal (VT520) = ISO-8859-1.

The argument order is defined by man screen: the first encoding is the window, the optional second one is the connected terminal.

5.2 Add the VT520 block to ~/.bashrc

Insert this block after the standard “if not running interactively, return” check and before the color/prompt setup. It must run for the outer Telnet login, before screen starts.

# ---------------------------------------------------------------------------
# VT520 via Zimodem (Telnet). The Zimodem reports TERM=zimodem, for which no
# terminfo exists. This applies only to that access path; SSH is untouched.
# ---------------------------------------------------------------------------
case "$TERM" in
    [Zz]imodem|vt520|vt525)
        if [ -z "${SSH_CONNECTION:-}" ] && [ -t 0 ] && [ -t 1 ]; then
            export TERM=vt520
            export VT520=1
            export LANG=de_DE.UTF-8
            export MANOPT="-E ascii"
            unset LC_ALL

            # The Zimodem does not negotiate NAWS, so the pty would be 0x0.
            # The VT520 cannot do UTF-8 -> keep the outer side ISO-8859-1.
            #
            # netkit-telnetd offers DO ECHO; the Zimodem answers WONT ECHO.
            # telnetd then offers WILL ECHO; the Zimodem answers DONT ECHO.
            # Result: the pty starts with -echo, so input is invisible
            # (but still works). Re-enable echo explicitly. VT520 only.
            stty rows 24 cols 80 cs8 -parenb -istrip -ixon -iutf8 echo 2>/dev/null

            # Reset the VT520 state on every login: SGR, margins, origin,
            # character set, cursor. Otherwise a state left behind by nano or
            # screen can make the prompt invisible while the keyboard works.
            printf '\033[!p'        # DECSTR soft reset
            printf '\033[?4l'       # jump scroll instead of smooth scroll
            printf '\033[?6l'       # origin mode off
            printf '\033[?7h'       # autowrap on
            printf '\033[?25h'      # cursor visible
            printf '\033[0m'        # attributes off
            printf '\033[1;24r'     # full scroll region
            printf '\033[H\033[2J'  # home and clear
        fi
        ;;
esac

# Inside screen windows the pty is UTF-8 (screen translates to the outside).
if [ -n "${STY:-}" ]; then
    stty iutf8 2>/dev/null
    export MANOPT="-E ascii"

    # The VT520 has no ANSI color support. Drop color aliases that may come
    # from /etc/bash.bashrc; the color aliases below are not defined at all
    # when VT520=1.
    if [ -n "${VT520:-}" ]; then
        unset LS_COLORS
        unalias ls grep fgrep egrep 2>/dev/null
    fi
fi

Then make the existing color block conditional, so the VT520 never receives --color=auto:

if [ -x /usr/bin/dircolors ] && [ -z "${VT520:-}" ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    alias grep='grep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias egrep='egrep --color=auto'
fi

Two details matter:

  • [Zz]imodem handles both Zimodem (the Zimodem default) and zimodem.
  • -iutf8 on the outer pty is correct: the VT520 sends ISO-8859-1 bytes. Inside screen, the STY branch sets iutf8 again for UTF-8.

5.3 Add auto-start to ~/.profile

Append this after the normal PATH setup:

# ---------------------------------------------------------------------------
# VT520 via Zimodem: start screen automatically.
# screen translates UTF-8 <-> ISO-8859-1 so nano writes UTF-8 files (Hugo)
# while the VT520 still shows umlauts. SSH sessions (kitty) are untouched.
# ---------------------------------------------------------------------------
if [ -z "${SSH_CONNECTION:-}" ] && [ -z "${STY:-}" ] && [ -t 0 ] && [ -t 1 ] \
   && [ ! -e "$HOME/.no-screen" ]; then
    case "$TERM" in
        vt520|vt525|[Zz]imodem)
            if command -v screen >/dev/null 2>&1; then
                exec screen -dRR -c "$HOME/.screenrc.vt520"
            fi
            ;;
    esac
fi

Why the guards matter:

  • SSH_CONNECTION empty: never touch SSH logins.
  • STY empty: do not nest screen inside an existing session.
  • -t 0 and -t 1: only run for interactive terminals.
  • .no-screen: escape hatch for a plain shell.
  • screen -dRR: detach a stale attachment, resume the newest session, or create one. This is what makes reconnect after a Zimodem drop work.

Order note: .profile sources .bashrc first. That is why the VT520 block sets TERM=vt520; by the time the auto-start check runs, the original Zimodem value may already be gone. The case therefore accepts vt520, vt525, and both spellings of Zimodem.

5.4 Optional helper scripts

These are not required, but they make life much easier.

~/vt520-check.sh — self-test

Run it at the VT520 after login. It checks the display, the keyboard byte encoding, and the encoding of a written file.

#!/bin/bash
# Check the VT520 session (run as user vt at the VT520).
printf 'TERM=%s  LANG=%s  Screen=%s  Size=%s\n' \
    "$TERM" "$LANG" "${STY:+yes}" "$(stty size 2>/dev/null)"

printf 'Display: ÄÖÜäöüß   <-- must look exactly like this\n'
printf '\nType an ä and press Enter: '
IFS= read -r x
hex=$(printf '%s' "$x" | od -An -tx1 | tr -d ' \n')
printf '\nKeyboard sends: %s\n' "$hex"
case "$hex" in
  c3a4) echo 'OK: keyboard sends UTF-8.' ;;
  e4)   echo 'ERROR: keyboard sends Latin-1 -> screen transcoding is missing.' ;;
  *)    echo "Unexpected: $hex" ;;
esac

printf '\nWrite a test file and check its encoding:\n'
printf 'äöüß ÄÖÜ\n' > /tmp/vt520-test.md
file -i /tmp/vt520-test.md
printf 'Bytes: '
od -An -tx1 /tmp/vt520-test.md
printf '\nExpected: charset=utf-8 and bytes c3a4 c3b6 c3bc c39f (not e4 f6 fc df).\n'

~/vt-reset.sh — reset from SSH

When the VT520 display is wedged but the host is reachable, run this from an SSH session. It finds the Telnet login’s pty and writes a clean DEC reset to it.

#!/bin/bash
# Send a clean reset to the VT520 console.
# Usage (from kitty/SSH when the VT520 display is stuck): ~/vt-reset.sh

set -u

pts="${1:-}"
if [ -z "$pts" ]; then
    pts=$(ps -eo tty=,args= | awk '/login -h 192\.168\.178\.25/ {print $1; exit}')
fi

if [ -z "$pts" ]; then
    echo "No VT520 session (login -h 192.168.178.25) found."
    echo "Are you logged in at the VT520?"
    exit 1
fi

dev="/dev/$pts"
if [ ! -w "$dev" ]; then
    echo "Cannot write to $dev (permission denied?)."
    exit 1
fi

{
    printf '\033[!p'        # DECSTR soft reset
    printf '\033[?4l'       # jump scroll
    printf '\033[?6l'       # origin off
    printf '\033[?7h'       # autowrap on
    printf '\033[?25h'      # cursor visible
    printf '\033[0m'        # attributes off
    printf '\033[1;24r'     # full scroll region
    printf '\033[H\033[2J'  # home and clear
} > "$dev"

echo "Reset sent to $dev."
echo "If the display is still stuck: at the VT520 press '+++' (2 s pause), then"
echo 'ATF4 / AT&W / ATDT"192.168.178.169:23".'

The IP in the awk pattern must match your Zimodem address.

~/vt520-da.sh — terminal identification

A Python script that sends DEC DA, DECRQM, and DECRQSS queries and stores the raw answers. Useful when a VT520 behaves unexpectedly. It must run outside screen, directly on /dev/tty. See the deployed file for the full query list; it is a diagnostic tool, not part of normal operation.

5.5 Make the scripts executable

chmod +x ~/vt520-check.sh ~/vt-reset.sh ~/vt520-da.sh

5.6 Test from a PC first

Before reconnecting the VT520, verify that SSH is still normal:

ssh vt@192.168.178.169
echo "$TERM"        # should be xterm-256color or similar
echo "$STY"         # should be empty
locale              # UTF-8

If screen starts over SSH, one of the guards is wrong. Check that SSH_CONNECTION is set and that TERM is not Zimodem.


6. Logging in and writing

  1. Wake the VT520 and press Enter, or dial from the Zimodem:

    +++            (three plus signs, then wait 2 seconds, no Enter)
    ATDT"192.168.178.169:23"
    
  2. At login:, type vt and press Enter. The user name is not echoed — this is the Telnet ECHO bug, not a fault. Type it blind.

  3. At Password:, type the password. Passwords are never echoed.

  4. After login, .bashrc fixes the pty, resets the VT520, and .profile starts screen. The prompt appears with visible input.

  5. Edit a post:

    nano content/posts/my-post.md
    
  6. Save with Ctrl-O, Enter, exit with Ctrl-X.

Because screen translates, nano sees a UTF-8 terminal and writes UTF-8. The VT520 sees ISO-8859-1 and displays umlauts correctly.

If the Zimodem connection drops, the screen session keeps running on the host. Dial in again and log in; screen -dRR reattaches the same session, including the open nano buffer.

Plain shell without screen

touch ~/.no-screen     # next VT520 login: no screen
rm ~/.no-screen        # back to automatic screen

Use this only for testing. Without screen, umlauts will break again.


7. The invisible-input bug (Telnet ECHO)

7.1 Symptom

The prompt is visible and commands execute, but typed characters never appear. This is especially confusing at the login prompt, where the user name is also invisible.

7.2 Root cause

netkit-telnetd and the Zimodem disagree about who echoes:

telnetd -> IAC DO ECHO
Zimodem -> IAC WONT ECHO

telnetd -> IAC WILL ECHO
Zimodem -> IAC DONT ECHO

The pty is left in -echo. Input reaches the shell, but the terminal driver does not echo it back.

This was confirmed by reading the Zimodem source (pet2asc.ino, handleAsciiIAC) and by reproducing the negotiation locally. It is not an XOFF problem and not a VT520 fault.

7.3 Immediate fix

At the VT520, type blind and press Enter:

stty echo

Input becomes visible immediately.

7.4 Permanent fix

The stty ... echo line in the ~/.bashrc VT520 block re-enables echo on every VT520 login. It is gated on TERM=Zimodem/vt520 and on SSH_CONNECTION being empty, so SSH sessions are unaffected.

The login prompt itself remains blind because ~/.bashrc runs only after authentication. That is expected. Just type vt and Enter.


8. The frozen-display problem (flow control)

This is a different failure from the ECHO bug.

8.1 Symptom

No new text appears at all, not even a prompt. The keyboard may still send bytes, but the screen does not update.

8.2 Cause

The VT520 uses XON/XOFF flow control. It sends XOFF when its buffer is full or when it enters Hold. The Zimodem pauses host-to-terminal output until it sees XON again. If that XON is lost, the display stays frozen forever.

8.3 Recovery

  1. Press a bare Ctrl-Q. Do not press Ctrl-A q — inside screen that would quit screen.

  2. Alternatively press the Hold key: F1 on a VT keyboard, Scroll Lock on a PC keyboard.

  3. If that does not help, reset the modem blindly at the VT520:

    +++                     (three plus, 2 second pause, no Enter)
    Enter
    ATZ                     Enter    reset the Zimodem
    ATF4                    Enter    disable flow control
    AT&W                    Enter    save the setting
    ATDT"192.168.178.169:23" Enter    dial again
    
  4. Log in again.

ATF4 makes the Zimodem ignore XOFF and keep sending. At 9600 baud with jump scroll this is safe and is the most robust setting for this terminal.

8.4 Missing prompt but working keyboard

If commands execute but the prompt is invisible, the VT520 state was left inconsistent by a full-screen program. Every login resets it (DECSTR, jump scroll, full scroll region, clear). From SSH you can trigger the same reset without logging in at the VT520:

~/vt-reset.sh

9. Verification

Run at the VT520:

~/vt520-check.sh

Expected output:

TERM=screen  LANG=de_DE.UTF-8  Screen=yes  Size=24 80
Display: ÄÖÜäöüß   <-- must look exactly like this
...
Keyboard sends: c3a4
OK: keyboard sends UTF-8.
...
/tmp/vt520-test.md: text/plain; charset=utf-8
Bytes: c3a4 c3b6 c3bc c39f ...

Also check:

stty -a | grep -o '\becho\b'      # must print echo
echo "$STY"                        # non-empty inside screen
locale | head -1                   # LANG=de_DE.UTF-8

From SSH, confirm that nothing changed:

echo "$TERM"        # xterm-256color or similar, not vt520
echo "$STY"         # empty
alias ls            # may contain --color=auto

10. Troubleshooting

Symptom Cause Fix
Typed characters invisible, commands run Telnet ECHO negotiation leaves pty at -echo Blindly type stty echo; permanent fix in ~/.bashrc
Login name invisible .bashrc has not run yet; echo is off before authentication Expected; type vt and Enter
ä instead of ä UTF-8 bytes reaching the VT520; no screen transcoding Check echo $STY; start screen with ~/.screenrc.vt520
Umlauts appear as ? or boxes Outer encoding or iutf8 wrong Check locale, stty -a, and the screenrc encoding line
Colors, inverse text, escape garbage ANSI color aliases active on a monochrome terminal Ensure VT520=1; disable LS_COLORS and color aliases
Screen freezes completely XOFF / Hold / Zimodem flow control Bare Ctrl-Q, Hold key, then ATZ + ATF4 + AT&W
Prompt missing but input works VT520 state corrupted by a full-screen app Log out and in, or run ~/vt-reset.sh from SSH
PTY is 0x0 or 1x1 Zimodem does not negotiate NAWS stty rows 24 cols 80 (already in .bashrc)
screen starts over SSH Missing SSH_CONNECTION / STY guard Check ~/.profile conditions
screen does not start at VT520 ~/.no-screen exists, or TERM does not match rm ~/.no-screen; check echo $TERM
Screen session lost after reconnect screen was not used, or host rebooted Use screen -dRR; screen survives link drops, not reboots

Manual escape from a wedged session

If screen itself is unresponsive but the shell works, detach with Ctrl-A d. If even that fails, disconnect at the modem and dial in again; screen -dRR reattaches.


11. Character-set limitations

The VT520 in ISO-8859-1 mode handles the German basics:

Ä Ö Ü ä ö ü ß § ° µ ± ² ³

It cannot display characters that do not exist in ISO-8859-1, notably:

„ “ ” ‚ ‘ ’ – — … € Œ œ Š š Ž ž

For Hugo posts this matters. The host file is still UTF-8, so such characters are preserved if they are inserted from another machine. But they cannot be typed or displayed at the VT520. Practical rules:

  • Write typographic quotes, en/em dashes, ellipses, and the euro sign from a PC, or
  • use ASCII substitutes at the VT520 (", -, ..., EUR), or
  • keep them out of the body text and add them during a later editing pass.

MANOPT="-E ascii" reduces the chance that man pages send characters the VT520 cannot show.


12. Why SSH stays unaffected

All VT520 logic is guarded by two independent conditions:

  1. $TERM must be Zimodem, zimodem, vt520, or vt525.
  2. SSH_CONNECTION must be empty.

An SSH login from kitty reports TERM=xterm-256color and has SSH_CONNECTION set, so neither the reset block nor the screen auto-start runs. Color aliases remain active, the locale stays UTF-8, and the pty is a normal UTF-8 pty.

This was verified explicitly:

TERM=xterm-256color
STY=            (empty)
LANG=de_DE.UTF-8
alias ls        ls --color=auto

13. File overview and backups

Path Purpose
~/.screenrc.vt520 UTF-8 ↔ ISO-8859-1 translation, bash inside windows
~/.bashrc VT520 reset, stty echo, VT520=1, color guard
~/.profile guarded screen -dRR auto-start
~/.no-screen if present: no screen (test/escape hatch)
~/vt520-check.sh display, keyboard, and encoding self-test
~/vt-reset.sh reset the VT520 pty from SSH
~/vt520-da.sh DEC identification and keyboard diagnostic
~/VT520-HILFE.txt short German recovery guide

Before changing these files, back them up:

for f in ~/.bashrc ~/.profile ~/.screenrc.vt520 ~/VT520-HILFE.txt; do
    [ -e "$f" ] && cp -a "$f" "$f.bak-$(date +%Y%m%d-%H%M%S)"
done

To undo everything, restore the backups and remove ~/.screenrc.vt520 and ~/.no-screen.


14. Adapting to your own network

Change these values:

  1. Zimodem address in ~/vt-reset.sh and in any ATDT command.
  2. Host address in the Zimodem dial string (ATDT"192.168.178.169:23").
  3. Login user if not vt.
  4. Locale if not German: adjust LANG and the outer encoding. For a US setup, en_US.UTF-8 with ISO-8859-1 still works for Western European characters; for other scripts you need a terminal that supports UTF-8.
  5. Baud rate and flow control to match your VT520 and Zimodem configuration. If flow control causes trouble, ATF4 + AT&W is the reliable choice at 9600 baud.

If your Telnet daemon negotiates ECHO correctly, the stty echo line is harmless. If it does not, the line is essential.


15. Short version

If you only remember five things:

  1. Keep the host UTF-8.
  2. Run screen with encoding utf8 iso8859-1.
  3. Auto-start screen -dRR only for TERM=Zimodem/vt520 and only outside SSH.
  4. Put stty ... echo in the VT520 login block; the Telnet ECHO negotiation is broken.
  5. Frozen display is flow control: bare Ctrl-Q, then ATZ / ATF4 / AT&W if needed.

That is the whole setup.

Webmentions

Reaktionen aus dem IndieWeb (Replies, Likes, Reposts).

  1. Lade Webmentions …