Skip to content

Repository files navigation

ESP32 Tailscale Print Bridge

Print to your home printer from anywhere — no port forwarding, no cloud print service, no PC left running at home. A $10 ESP32-S3 board joins your Tailscale network as a real node and forwards print jobs to your printer over the LAN:

 laptop, anywhere            Tailscale tunnel              your home LAN
┌──────────────┐   WireGuard (direct or DERP relay)  ┌─────────┐        ┌─────────┐
│  Windows PC  │ ═══════════════════════════════════▶│ ESP32-S3│──────▶ │ printer │
│  LPR driver  │        100.100.20.30:515            │  proxy  │  :515  │  (LPD)  │
└──────────────┘                                     └─────────┘        └─────────┘

The ESP32 makes only outbound connections (WiFi → Tailscale control plane + WireGuard), so nothing on your home network is exposed to the internet. Verified end-to-end on real hardware: printing from the home LAN (fast, direct WireGuard path) and from a cellular hotspot on the other side of a carrier NAT (~25 s per page via Tailscale's DERP relay).

Credits — and a note about bugs

This project stands on two great open-source components, vendored in components/:

  • CamM2325/microlink (MIT) — a Tailscale-compatible client for ESP32. Thank you — this project would not exist without it.
  • smartalock/wireguard-lwip (BSD-3) — the WireGuard implementation for lwIP that microlink builds on.

⚠️ Heads-up if you use upstream microlink directly (≤ v2.1.0 at the time of writing): building this bridge uncovered three real bugs, all reported upstream:

Bug Effect Report Fix
lwIP entered from the wrong task on decrypted RX random crash + reboot mid-transfer under TCP load #17 PR #20
unvalidated peer endpoints installed at peer-add one-way tunnel — peers behind carrier NAT (phone hotspots) can never reach you #18 PR #21
DERP home region hardcoded to Dallas very slow relayed traffic outside North America #19 config note below

The copy vendored in this repository already includes all three fixes — you don't need to do anything.

Hardware

  • ESP32-S3 with PSRAM. Built and tested on an ESP32-S3 N16R8 devkit (16 MB flash, 8 MB octal PSRAM). PSRAM is mandatory — microlink holds 3×64 KB buffers at once while fetching the network map; without PSRAM it exhausts internal RAM and never gets a tailnet address (endless Empty MapResponse).
  • Any printer that speaks LPR/LPD (TCP port 515) on your LAN. Tested with a Brother MFC-J2340DW (LPR queue BINARY_P1). Most network printers from Brother/HP/Epson/Canon support LPD — check yours before starting (see the printer section below).

Config warning: never enable CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP. Forcing WiFi buffers into octal PSRAM breaks WiFi on the S3 (esp_wifi_scan_start fails, disconnect reason=201). The provided sdkconfig.defaults is already set up correctly: PSRAM on, WiFi buffers internal.

Setup

1. Prerequisites

  • A (free) Tailscale account with Tailscale installed on the computer you'll print from.
  • ESP-IDF v5.3 installed and exported (. ~/esp/esp-idf/export.sh).

2. Configure

git clone <this-repo>
cd esp32-tailscale-print-bridge

# your WiFi + Tailscale auth key (file is gitignored, secrets stay local)
cp sdkconfig.credentials.example sdkconfig.credentials
# edit sdkconfig.credentials with your values

Then two code-level settings:

  1. Your printer's LAN IP — edit main/main.c:

    #define PRINTER_IP  "192.168.1.222"   /* <-- set to YOUR printer's LAN IP */

    Find it on the printer's own network status page/printout. Give the printer a DHCP reservation in your router so it doesn't move.

  2. Your nearest DERP region — edit components/microlink/include/microlink_internal.h:

    #define ML_DERP_REGION  3   /* Singapore in this copy — pick YOURS */

    This is the relay region used whenever a direct connection can't be established (typical for phones/hotspots behind carrier NAT). A wrong region costs real speed: with the relay on the wrong continent, a one-page job took ~4 minutes; with the right one, ~25 seconds. Pick the region closest to the printer's location from Tailscale's DERP region list (common ids: 1 NYC, 2 SFO, 3 Singapore, 4 Frankfurt, 9 Dallas, 10 Seattle).

3. Build & flash

idf.py set-target esp32s3
idf.py build
idf.py flash monitor

(flash.sh in this repo is an alternative flasher for WSL2+usbip setups, where esptool's RTS-based reset silently fails; ignore it on a normal Linux/macOS/Windows host.)

4. Verify

On the serial console you should see the board join WiFi, connect to Tailscale, and get a tailnet address:

m5-proxy: WiFi got LAN IP: 192.168.x.x
...
m5-proxy: *** TAILNET CONNECTED — VPN IP: 100.x.x.x ***
m5-proxy: print-proxy listening on 0.0.0.0:515 -> 192.168.1.222:515

The board also appears in your Tailscale admin console as print-bridge. Note its 100.x.x.x address — that's your printer's address from anywhere in the world.

Add the printer on Windows (manual setup)

Know your printer first. Before configuring the port, check your printer's documentation or its web admin page for LPD/LPR support and its queue name (Brother uses BINARY_P1; HP is often raw or TEXT; Epson/Canon have their own). Having the queue name ready makes the port configuration one-shot.

  1. Settings → Bluetooth & devices → Printers & scanners → Add device — wait, then choose "Add manually" ("The printer that I want isn't listed").

    Step 1

  2. Select "Add a printer using an IP address or hostname" → Device type: TCP/IP Device → Hostname/IP: the board's tailnet IP (the 100.x.x.x from step 4 above — not the printer's LAN IP). Uncheck "Query the printer and automatically select the driver".

    Step 2

  3. When asked for the port type, choose Custom → Settings and set:

    • Protocol: LPR
    • Queue Name: your printer's LPR queue (e.g. BINARY_P1 for Brother)
    • LPR Byte Counting: unchecked works (the verified setup below); if your driver's jobs come out truncated, try enabling it
    • SNMP Status Enabled: UNCHECKED ← critical, see below

    Step 3

  4. Pick your printer's driver (install the manufacturer's driver first if Windows doesn't list it), finish the wizard, and print a test page.

    Step 4

Why SNMP must be off: the bridge forwards TCP port 515 only. SNMP is UDP and never reaches the printer, so with "SNMP Status Enabled" checked, Windows decides the printer is offline and holds your job in the queue forever. This is the #1 setup mistake.

Troubleshooting

Symptom Cause / fix
Job sits in the Windows queue, printer shows "offline" SNMP still enabled on the port — uncheck it (step 3 above).
Prints work on home WiFi but are slow from cellular (~minutes) Wrong ML_DERP_REGION — pick the region nearest the printer. Relayed prints of ~25 s/page are normal; carrier NAT usually prevents a direct path.
Board never gets a 100.x address, log shows Empty MapResponse repeatedly PSRAM not working — board must actually have PSRAM, and CONFIG_SPIRAM=y (+ octal mode for N16R8) must be set.
WiFi won't connect, disconnect reason=201 CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP got enabled — remove it, WiFi buffers must stay in internal RAM.
Diagnosing "is it the printer or the tunnel?" python3 tools/lpr_probe.py <host> — run once against the printer's LAN IP and once against the board's tailnet IP, compare ACK latencies (it aborts the job; nothing prints).

Limitations (honest list)

  • One print job at a time (listen backlog of 1). A second job waits; that's the right behavior for a printer anyway.
  • TCP/LPR only. No UDP forwarding — so no SNMP status, no printer discovery, no scanning. Print works; the driver's status readback doesn't.
  • Peers behind carrier NAT ride the DERP relay (~25 s/page). This is a Tailscale-architecture reality, not a bug; home/office networks usually get a fast direct path.
  • Control plane is Tailscale SaaS — the coordination server hostname is compiled into microlink. Self-hosted Headscale is untested.
  • The Tailscale node key expires (default ~180 days). When it does, the board needs a fresh auth key in sdkconfig.credentials and a reflash.
  • IPv4 only.

License

MIT — see LICENSE. Vendored components keep their own licenses: microlink (MIT, Cameron Malone), wireguard-lwip (BSD-3, Daniel Hope).

About

Print to your home printer from anywhere — ESP32-S3 as a Tailscale node proxying LPR, outbound-only, no port forwarding

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages