Skip to the content
PCBKitNew board
2 layers60 × 50 mmEsp8266Interface, the printed circuit board in 3D

Esp8266 Interface

Size
60 × 50 mm
Copper layers
2
Thickness
1.6 mm
Parts
25
Nets
16
Pads
64
Tracks, vias
158, 2
Pours
4
Routed
Yes
KiCad
KiCad 7 (file 20221018)
Created
2 Jun 2023
Updated
19 Sep 2026

Overview

Esp8266Interface is an open-source printed circuit board by jimharrigan, from the jimharrigan/Esp8266Interface repository on GitHub, published under the GPL-3.0 licence. It is a 2-layer board of 60 × 50 mm with 25 parts on 16 nets, routed with 158 tracks and 2 vias.

Its parts include 7 connectors, 6 resistors, 5 capacitors, 3 diodes and 2 ICs. It belongs with the interfaces and adapters boards and the ESP8266 boards of this library.

README

README.md

Esp8266Interface

A WiFi bridge between a dry-contact (or opto-isolated) signal and MQTT / HTTP, built around an ESP-01 module. It reports the state of an external input to an MQTT topic and/or a pair of webhook URLs, and drives a relay output in response to MQTT messages or a polled URL.

Typical use is retrofitting network connectivity onto existing wired equipment — a doorbell transformer, an alarm panel output, a gate contact — without modifying it.

The repository contains both halves of the project:

PathContents
Esp8266Interface.inoThe Arduino firmware (single sketch)
Esp8266Interface/KiCad schematic and PCB for the carrier board
Esp8266Interface/production/Fabrication output: gerbers, BOM, positions, IPC netlist
Esp8266Interface.sln, *.vcxprojVisual Studio + Visual Micro project
Esp8266Interface/test/Scratch KiCad project, not part of the build

How it works

Input → network. An external signal pulls GPIO3 low, either through the on-board PC817 optocoupler (from the HV In header) or by a plain contact closure on the In3 header. A pin-change interrupt records the new level; the main loop notices the change and:

  • publishes Trigger (pin low) or Reset (pin high) to the configured MQTT topic, and
  • issues an HTTP GET to the configured trigger URL or reset URL.

Network → output. The firmware subscribes to a control topic. A payload of 1 or trigger drives GPIO1 high; anything else drives it low. GPIO1 gates a 2N7000 low-side switch on the Rly header, so this is intended to energise a relay coil. The new state is echoed back to the status topic as Output 1 / Output 0.

The same output can instead — or as well — be driven by polling. If a control URL is configured, the board fetches it every 2 s and looks at the reply: a body containing Trigger closes the relay, one containing Reset opens it. This suits a far end that has no MQTT broker and cannot reach in to the board, only be asked.

Hardware

Connectors

Silkscreen labels come from the BOM; the descriptions below are derived from the netlist.

RefLabelFunction
J1ESP012x4 socket for the ESP-01 module
J4PinPower in. Pin 1 GND, pin 2 raw DC into the LM1084-3.3 regulator
J5PoutPower out. Pin 1 raw input rail, pin 2 regulated +3.3 V
J7HV InIsolated sense input. Pin 1 is the common return; pin 2 feeds the optocoupler through R2 (1 k), pin 3 through R3 (68 k) + R2 for a higher-voltage source. D2 (1N4004) rectifies, so an AC source works; D1 (zener) and C5 clamp and smooth what reaches the PC817 LED via R1 (2.2 k)
J2In3Non-isolated input. Shorting the two pins pulls GPIO3 to GND, in parallel with the optocoupler
J3InConfig-portal button. Shorts GPIO0 to GPIO2, which the firmware holds low after boot
J6RlyRelay output. Pin 1 is the raw input rail, pin 2 the 2N7000 drain; D3 (1N4001) is the flyback diode across the coil
SW1—Momentary reset for the ESP-01 (RST to GND)

The zener D1 has no value in the BOM, so the usable voltage range of each HV In tap depends on the part actually fitted. Work out the range for your source before relying on it, and treat anything on HV In as hazardous unless you know otherwise — nothing on the board isolates HV In from its own source, only from the ESP side.

ESP-01 pin usage

PinDirectionUse
GPIO0input, 3.3 k pull-up (R6)Config-portal button, read in loop(); also the ESP-01 flash-mode strap
GPIO1outputRelay drive, through R4 (47 k) into the 2N7000 gate. Also the UART TX pin
GPIO2output, driven lowReturn path for the GPIO0 button, so the button cannot hold the board in flash mode at boot. 3.3 k pull-up (R5)
GPIO3input, internal pull-upSense input, from the optocoupler or In3. Also the UART RX pin

Two pins do double duty as the serial port, which shapes the firmware:

  • Serial.begin() is called with SERIAL_TX_ONLY so GPIO3 stays usable as an input.

  • GPIO1 cannot be the UART TX pin and the relay drive at the same time. The DEBUG_SERIAL switch at the top of the sketch picks one:

    DEBUG_SERIALBehaviour
    0 (default)Boot messages print, then setup() calls Serial.end() and claims GPIO1 as an output. Logging stops; the relay works
    1Serial logging stays on for the whole run; the relay output does nothing

    Use DBG_PRINT() / DBG_PRINTLN() for any log line added after boot — plain Serial calls past the end of setup() go to a closed port in the default build.

  • GPIO1 toggles as TX during boot. C1 (33 µF) on the MOSFET gate with R4 gives roughly a 1.5 s time constant, which stops the relay chattering while the module starts up — at the cost of the output switching slowly.

Building and flashing

The sketch targets the Arduino core for ESP8266 and needs these libraries:

Build with the Arduino IDE, arduino-cli, or the supplied Visual Studio / Visual Micro solution. Choose a flash layout that includes a LittleFS partition — the configuration is stored in /config.json and there is nowhere to save settings without it.

The board has no USB interface and no auto-reset circuit. To flash, use an external 3.3 V USB-to-serial adapter on the ESP-01's TX/RX/GND, hold GPIO0 low, and pulse reset. Doing this with the module out of the socket avoids back-feeding the Rly and HV In circuitry through GPIO1 and GPIO3.

Configuration

At boot the board tries the WiFi credentials WiFiManager has stored, giving up after 20 s. Failing to connect is not fatal — it keeps running while the ESP8266 keeps retrying WiFi in the background — and it deliberately does not open the config portal on its own, so a router outage cannot leave an access point running unattended in someone's wall. If WiFi stays down for 5 minutes the board reboots (see Failure handling).

The portal is opened on demand instead. Press the button on J3 and the board comes up as an access point named ESP with the password 1234567890, which is also how you configure it on first boot, when nothing is stored yet. The portal offers the standard WiFi and info pages plus a param page with these fields:

FieldStored asNotes
mqtt servermqtt_serverLeave empty to disable MQTT entirely
mqtt portmqtt_portDefaults to 8883
mqtt usernamemqtt_username
mqtt passwordmqtt_password
mqtt topicmqtt_topicStatus topic the board publishes to
mqtt control topicmqtt_control_topicSubscribed topic for output commands; leave empty to skip
Trigger Urltrigger_urlFetched when the input goes low, at boot if it is low, and every 10 minutes while it stays low
Reset Urlreset_urlFetched when the input goes high, at boot if it is high, and every 10 minutes while it stays high
Control Urlcontrol_urlPolled every 2 s for the output state; leave empty to skip

Settings are written to /config.json in LittleFS and reloaded at boot. The portal timeout is 300 s and runs non-blocking, so the rest of the loop keeps running while it is open. Saving MQTT settings takes effect immediately: the client is re-pointed at the new broker and any live session is dropped, so the next reconnect picks up the new server, port, credentials and control topic without a restart.

Messages

DirectionTopicPayload
Publishedmqtt_topicTrigger when the input goes low, Reset when it goes high, and the current state each time the broker connection comes up
Publishedmqtt_topicOutput 1 / Output 0 after a control message
Subscribedmqtt_control_topic1 or trigger turns the output on; any other payload turns it off

Payload matching is case-insensitive. An HTTP request counts as successful if the server answers with any 2xx status; the response body is only read for the control URL.

Control URL

A control URL is polled every 2 s and the reply decides where the output sits:

Reply bodyEffect
contains TriggerOutput on — relay energised
contains ResetOutput off
contains bothTreated as Trigger
contains neitherOutput left as it is
request failedOutput left as it is

The match is case-insensitive and anywhere in the body, so a page carrying other markup around the word still works. Only Output 1 / Output 0 transitions are published to the status topic — a poll that finds the same state as last time is silent, so a 2 s poll does not flood the broker.

Nothing about the poll reboots the board: a control URL that stops answering leaves the relay where it was rather than dropping it, and the next poll tries again. The request timeout is 1.5 s, under the 2 s interval, so an unreachable server cannot stall the loop for longer than one period, and the interval is measured from the end of each request rather than the start. The poll runs on the first pass through the loop, so a reboot picks the output back up immediately instead of waiting, and is held off entirely while the config portal is open, so a bad URL can still be corrected.

Note that the request is plain HTTP over WiFiClient — an https:// control URL will not work, and the reply is trusted as-is.

MQTT runs over WiFiClientSecure, but setInsecure() is called, so the server certificate is not validated. Credentials are stored in plaintext in the filesystem. Treat this as suitable for a trusted LAN, not the open internet.

Failure handling

The input is read at boot, so its state is sent even if it never changes: the matching URL on the first pass through the loop, and the MQTT status as soon as the broker connects.

  • URL request fails (no WiFi, no connection, or a non-2xx reply): the next attempt is due 30 s later instead of 10 minutes, and when it comes due the board reboots instead. After the reboot it reads the input again and makes a fresh attempt.
  • WiFi down for 5 minutes: the board reboots. The timer counts from power-up if WiFi never connects, so a board with no stored credentials reboots every 5 minutes until the portal is used.
  • MQTT disconnected: reconnects every 5 s, but only while WiFi is up. MQTT failures never cause a reboot.

Neither reboot happens while the config portal is open, and the 5-minute WiFi timer starts again when the portal closes. A reboot turns the output off until a control message arrives; a retained control message restores it as soon as MQTT reconnects, and a control URL restores it on the first poll.

On the ESP8266 the first software restart after a serial upload can hang in the bootloader, so power-cycle the board once after flashing.

Notes

The sketch compiles clean — no warnings — against ESP8266 core 3.1.2 with --warnings all, in both DEBUG_SERIAL configurations.

A few behaviours are deliberate rather than oversights:

  • The config portal opens on the button's falling edge, not while it is held, and not on top of a portal that is already running. It never opens by itself on a failed connection.
  • MQTT connection attempts are spaced MQTT_RECONNECT_INTERVAL (5 s) apart and skipped while WiFi is down, so they do not stall the loop. Saving settings in the portal resets that, so a new broker is tried on the next pass.
  • publishMessage() honours its retained argument; every call site passes true, so every status message is retained.
  • Both output paths go through SetOutput(), so they share one idea of where the output is. An MQTT command is acknowledged on the status topic whether or not it changed anything; a control URL poll only publishes when the level actually moves.
  • If both a control topic and a control URL are configured, they fight: whichever spoke last wins, and the URL speaks every 2 s. Configure one or the other.

License

GPL-3.0 — see LICENSE.

Parts

25
QtyValueFootprintReferences
2.01Capacitor_THT:C_Disc_D3.8mm_W2.6mm_P2.50mmC2, C4
23.3KResistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P2.54mm_VerticalR5, R6
110UFCapacitor_THT:CP_Radial_D5.0mm_P2.00mmC3
11N4001Diode_THT:D_DO-41_SOD81_P3.81mm_Vertical_KathodeUpD3
11N4004Diode_THT:D_DO-41_SOD81_P5.08mm_Vertical_KathodeUpD2
11kResistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P2.54mm_VerticalR2
12.2kResistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P2.54mm_VerticalR1
122UFCapacitor_THT:CP_Radial_D5.0mm_P2.00mmC5
12N7000Package_TO_SOT_THT:TO-92_InlineQ1
133UFCapacitor_THT:CP_Radial_D5.0mm_P2.00mmC1
147KResistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P2.54mm_VerticalR4
168KResistor_THT:R_Axial_DIN0309_L9.0mm_D3.2mm_P2.54mm_VerticalR3
1D_ZenerDiode_THT:D_A-405_P5.08mm_Vertical_AnodeUpD1
1ESP01Connector_PinHeader_2.54mm:PinHeader_2x04_P2.54mm_VerticalJ1
1HV InConnector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_VerticalJ7
1InConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ3
1In3Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ2
1LM1084-3.3Package_TO_SOT_THT:TO-220-3_Horizontal_TabDownU1
1PC817Package_DIP:DIP-4_W7.62mmU2
1PinConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ4
1PoutConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ5
1RlyConnector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_VerticalJ6
1SW_PushButton_Switch_THT:SW_Tactile_Straight_KSL0Axx1LFTRSW1

Questions

How big is the Esp8266Interface PCB?

It measures 60 × 50 mm and has 2 copper layers, 1.6 mm thick.

What parts does the Esp8266Interface use?

25 parts. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the Esp8266Interface KiCad files?

From the jimharrigan/Esp8266Interface repository on GitHub, at the commit this page was made from: the board is Esp8266Interface/Esp8266Interface.kicad_pcb and its schematic Esp8266Interface/Esp8266Interface.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the Esp8266Interface design in my own project?

Its repository publishes it under the GPL-3.0 licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).

Similar boards

  • 2 layers159.5 × 30 mmType f, the printed circuit board in 3D

    Type f

    google/mozc-devices

    Circuit diagrams and firmware source code for Gboard DIY keyboards

    25 parts3kApache-2.0
  • 2 layers113.5 × 95.1 mmSweep high, the printed circuit board in 3D

    Sweep high

    davidphilipbarr/Sweep

    Sweep - a small promicro based keyboard inspired by the Ferris.

    25 parts2.1k
  • 2 layers58.4 × 114.3 mmPicoPedal, the printed circuit board in 3D

    PicoPedal

    hackclub/OnBoard

    💡 Join 1,000 teenagers and make your first PCB with a $100 grant!

    25 parts915
  • 2 layers54 × 15 mmXyp gpib, the printed circuit board in 3D

    Xyp gpib

    xyphro/UsbGpib

    Versatile, cheap and portable USB to GPIB converter (USBTMC class based)

    25 parts690MIT
  • 2 layers50 × 76 mmVoron 0, the printed circuit board in 3D

    Voron 0

    dennisss/dacha

    Monorepo for awesome tools/projects/libraries

    25 parts397Apache-2.0
  • 2 layers237.5 × 102.9 mmStorage, the printed circuit board in 3D

    Storage

    jes/scamp-cpu

    A homebrew 16-bit CPU with a homebrew Unix-like-ish operating system.

    25 parts301Unlicense
  • 2 layers43.3 × 21 mmNanoS3 Carrier, the printed circuit board in 3D

    NanoS3 Carrier

    UnexpectedMaker/esp32s3

    Assorted files for my ESP32-S3 development boards

    25 parts296