Skip to content

electronicayciencia/esp32-remote-serial-console

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Remote Serial Console over Telnet (ESP32-C3)

An out-of-band remote serial console using the ESP32-C3 to provide emergency access via WiFi to a headless server or embedded system when primary network services fail.

The ESP32-C3 acts as a network-to-serial bridge: it connects to your target device’s serial console via its internal USB CDC interface and exposes it securely over a password-protected Telnet server on a static WiFi IP.

Overview and features

Suitable for ESP32-C3 boards with native USB-Serial bridge (Super Mini or Waveshare ESP32-C3-Zero).

  • Local Management: Use UART0 for device configuration & debugging. Requires physical access to the device and external USB to Serial adapter (maybe a second ESP32C3: ESP32-C3 UART Bridge).
  • Static IP: configurable IP and port.
  • Telnet negotiation: suppress local echo and parse IAC/DO/WILL commands to keep terminal clean. Multiple telnet newline format are supported (LF, CR-LF, CR-NUL).
  • Session authentication: Optional password protection with delay after 3 failed login attempts.
  • Low level USB CDC I/O to avoid usb_serial_jtag driver. See USB Serial JTAG Read Bug
  • Background USB reader prevents host console buffer stalls and retains the last 64 KB of output for immediate viewing on connect.
  Server            ESP32-C3            Client
┌────────┐     ┌───────┬────────┐     ┌────────┐
│        │ USB │       │        │ TCP │        │
│Console │◄───►│  CDC  │  WiFi  │◄───►│ Telnet │
│        │     │       │        │     │        │
└────────┘     └───────┴────────┘     └────────┘
                    ┌─────┐
                    │UART0│ ◄──────►  Initial provisioning & debug
                    └─────┘

Build and Flash

Tested on ESP-IDF version: v5.5.2

idf.py set-target esp32c3
idf.py menuconfig
idf.py build
idf.py flash

Options for menuconfig:

  • Component config → ESP System Settings → Channel for console output: Default: UART0
  • Component config → ESP System Settings → Channel for console secondary output: No secondary console

Operation

Monitor and configure

Right after flashing, connect to UART0: 115200 bauds.

W (302) main: Device is not configured yet.

=== ESP32 Console Ready ===
=== Configuration Status ===
Device is INCOMPLETELY CONFIGURED.

=== Configuration Values ===
ssid:    (not set)
pass:    (not set)
ip:      (not set)
mask:    255.255.255.0
gw:      192.168.1.1
port:    23
sespass: *** (set)

Please configure the device. Reboot when ready.

Type 'help' to see available commands.

esp>

Configure options:

esp> ssid "My wifi AP"
OK
esp> pass mypass
OK
esp> ip 192.168.1.4
OK
esp> sespass secret
OK
esp> reboot
Rebooting...

Default password is secret. Set sespass to "" to disable it.

After reboot, check WiFi connection logs, etc.

Look for IP confirmation:

I (1000) wifi_manager: Got IP: 192.168.1.4

You can reconfigure options at any moment. To show current configuration:

esp> conf
=== Configuration Status ===
Device is FULLY CONFIGURED and ready to connect.

=== Configuration Values ===
ssid:    "My wifi AP"
pass:    *** (set)
ip:      192.168.1.4
mask:    255.255.255.0
gw:      192.168.1.1
port:    23
sespass: *** (set)

Access console remotely

Telnet to configured IP and port. Using a telnet client: Putty, Telnet, etc.

telnet 192.168.1.4 23

Example session:

Remote Console Telnet Server
Password: ******

--- Remote console open ---
(Telnet EOF or disconnect to close session)


Ubuntu 18.04.2 LTS cuadrado ttyACM0

cuadrado login: reinoso
Password:
Last login: Fri Jan  2 12:47:07 CET 2026 on ttyACM0
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-55-generic x86_64)

reinoso@cuadrado:~$

Troubleshooting

Error when you try to send bytes to Linux when ESP32 is not connected, or agetty is not running at the other end:

--- Remote console open ---
(Telnet EOF or disconnect to close session)


--- Remote end not listening ---

Linux setup tips

Spawn a terminal in the Linux box (for testing)

Plug the board to the Linux box.

Identify the tty assigned to the ESP32 (could be different in each reboot).

# readlink  /dev/serial/by-id/usb-Espressif_USB_JTAG_serial_debug_unit_*
../../ttyACM0

Get the device name:

# basename ../../ttyACM0
ttyACM0

Spawn a serial console:

# setsid \
  agetty -L \
  $(basename $(readlink /dev/serial/by-id/usb-Espressif_USB_JTAG_serial_debug_unit_*))\
  linux

Decide a name for your new device

Connect your device and check its USB properties in syslog:

kernel: usb 1-4: new full-speed USB device number 4 using xhci_hcd
kernel: usb 1-4: New USB device found, idVendor=303a, idProduct=1001, bcdDevice= 1.01
kernel: usb 1-4: Product: USB JTAG/serial debug unit
kernel: usb 1-4: Manufacturer: Espressif
kernel: usb 1-4: SerialNumber: AC:A7:04:BA:3A:38

Or list the full info with this command:

# udevadm info -a -n /dev/ttyACM0
...
ATTRS{idProduct}=="1001"
ATTRS{idVendor}=="303a"
ATTRS{manufacturer}=="Espressif"
ATTRS{product}=="USB JTAG/serial debug unit"
ATTRS{serial}=="AC:A7:04:BA:3A:38"
...

Choose the most relevant for your case and create a new udev rule.

Since this is the only "Espressif"'s "USB JTAG/serial debug unit" I will have in my box, I need only idProduct and idVendor values. Feel free to add ATTRS{serial} if you require it.

# cat /etc/udev/rules.d/99-esp32-console.rules

KERNEL=="ttyACM*", \
SUBSYSTEMS=="usb", \
ATTRS{idVendor}=="303a", \
ATTRS{idProduct}=="1001", \
SYMLINK+="ttyESP32"

This rule will create a symlink called /dev/ttyESP32 to /dev/ttyACMx each time the kernel detects your ESP32:

lrwxrwxrwx 1 root root          7 Jan  4 13:55 /dev/ttyESP32 -> ttyACM0
crw-rw---- 1 root dialout 166,  0 Jan  4 13:55 /dev/ttyACM0

Make it available at early boot

Best path: custom compiled kernel with cdc_acm driver built-in.

Second best path: add cdc_acm module to the Init Ram Disk:

# cat /etc/initramfs-tools/modules

cdc_acm

Now run update-initramfs.

Enable TTY at bootup (systemd)

Activate a new terminal using systemd tty generator:

systemctl enable --now getty@ttyESP32.service

Redirect kernel messages via rsyslog

The kernel console= parameter will not work if the driver is compiled as a module. So a true console is not possible unless you compile a custom kernel.

You cannot add an additional console once the system is running and ttyESP32 is available.

But you can setup syslog to forward kernel messages to ttyESP32 (in Ubuntu):

# usermod -aG dialout syslog
# echo 'kern.*  /dev/ttyESP32' | tee /etc/rsyslog.d/99-ttyESP32.conf
# systemctl restart rsyslog

The incoming kernel messages will be kept until new login in a 64kb ring buffer.

Security Notes

  • Telnet traffic is unencrypted.
  • Session requires password, but credentials are sent in plaintext.

Author

Reinoso Guzman (https://www.electronicayciencia.com).

License

The MIT License (MIT).

About

Out-of-band remote serial console based on ESP32-C3 to provide emergency access to a headless server or embedded system when primary network services fail.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors