ESP32 S3 - PC817 Optocoupler: Monitor 12V/24V DC Input Signal
Industrial and automotive equipment speaks in 12V and 24V, but the ESP32 S3 is a strictly 3.3V board with no 5V tolerance at all — touch a GPIO pin with 24V and that pin is gone in an instant. A PC817 optocoupler isolation module removes the danger entirely by carrying the signal across an insulating gap as light, so your ESP32 S3 can watch a 24V PLC output or a 12V vehicle circuit as nothing more exotic than an ordinary digital input, and because the module already carries the series resistor its internal LED needs, the whole job comes down to buying the variant that matches your signal and landing two field wires on a screw terminal. In practice that answers the question most people actually arrive with — is the machine, the pump, the conveyor running right now? — because the module taps straight across the equipment's own supply and reports its real state without altering a single wire of it.
What you'll build in this tutorial:
- An isolated input stage that turns a 12V or 24V DC signal into a safe 3.3V logic level for the ESP32 S3
- The one purchase decision that decides whether it works at all: choosing the 24V, 12V or 3-5V module variant
- A one-minute bench test that tells you whether your module reads active-LOW or active-HIGH, and a sketch with a single constant to match either
- A parallel tap across a 24V beacon or guard interlock that tells you whether the equipment is genuinely energized
- The same input built from the bare PC817 chip and a resistor you size yourself, for when you would rather solder than buy

Hardware Preparation
Or you can buy the following kits:
| 1 | × | DIYables ESP32 S3 Starter Kit (ESP32 S3 included) | |
| 1 | × | DIYables Sensor Kit (18 sensors/displays) |
Additionally, some of these links are for products from our own brand, DIYables .
You need exactly one module - the one whose input voltage matches the signal you are going to watch, which the next section works out for you.
Overview of PC817 Optocoupler
The PC817 — also sold as a photocoupler or opto-isolator — is a 4-pin DIP part that holds two components facing each other across an insulating gap: an infrared LED on one side and a phototransistor on the other. When current flows through the LED, its light switches the transistor on, so the signal crosses the gap as light rather than as electricity, and the two sides never share a copper path. That single property is what lets a 3.3V ESP32 S3 sit safely next to a 24V machine circuit, and it is the same PC817 doing the work whether you solder the raw chip yourself or buy the ready-made isolation module this tutorial is built around.
Key Specifications
| Parameter | Value |
|---|---|
| Package | 4-pin DIP (pin 1 marked by a dot or a notch) |
| LED forward voltage (Vf) | about 1.2 V typical, 1.4 V max |
| LED forward current (If) | 20 mA continuous max, 5-20 mA normal, 10 mA a good target |
| Collector-emitter voltage (VCEO) | 35 V max |
| Collector current (IC) | 50 mA max |
| Collector power dissipation | 150 mW |
| Isolation voltage | 5000 Vrms for 1 minute |
| Current Transfer Ratio (CTR) | 50% to 600% depending on rank |
| Switching speed | rise about 4 us, fall about 3 us |
| Operating temperature | -30 C to +100 C |
Pinout
The PC817 carries its four pins as two facing pairs, with pin 1 marked by a dot or a notch and the numbering running counter-clockwise from there. On a module these four pins are already wired to the screw terminals and the pin header, but knowing them still explains everything the module does.
- Pin 1 - Anode (A) — LED anode, the positive of the input side; the 12V or 24V signal arrives here through a series resistor, which on a module is already fitted for you.
- Pin 2 - Cathode (K) — LED cathode, the negative of the input side; connect this pin to the ground of the 12V/24V circuit, never to the ESP32 S3 GND.
- Pin 3 - Emitter (E) — phototransistor emitter on the output side; connect this pin to the ESP32 S3 GND.
- Pin 4 - Collector (C) — phototransistor collector on the output side; connect this pin to the ESP32 S3 digital input pin.

Which Optocoupler Module Do I Need — 24V, 12V or 3-5V?
A PC817 isolation module arrives with its current-limiting resistor already soldered on, and that resistor can only be sized for one input voltage. That is why these boards are not sold as a single universal part but as three separate variants, and why picking the right one is the module equivalent of doing the resistor math yourself. Read the voltage of the signal you intend to watch, then buy accordingly:
- 24V module — for 24V DC signals: PLC transistor outputs, machine-guard interlocks, contactor and solenoid coils, conveyor status beacons, most factory-floor field wiring.
- 12V module — for 12V DC signals: automotive and marine circuits, 12V alarm and freezer outputs, LED strip supplies, small door and gate controllers.
- 3-5V module — for 3.3V and 5V logic signals coming from another microcontroller, a sensor board, or a control board you want to keep electrically separate from the ESP32 S3.
Fitting the wrong variant is the one mistake that costs you the module rather than an afternoon. Run a 12V module from a 24V line and roughly double the intended current goes through its internal LED, which will damage it. Run a 24V module from a 12V line and the opposite happens: the LED is underdriven, so the output may switch late, switch unreliably, or never switch at all — and an intermittent reading like that is far harder to diagnose than a dead board. Check the printing on the module against the voltage on your meter before you connect anything; with a module you never calculate, you simply choose correctly.
Isolating a 3.3V or 5V Logic Signal
Low-voltage signals are worth isolating too, and that is what the 3-5V variant is for. Whenever the two circuits run from separate power supplies, sit at opposite ends of a long cable, or one of them is electrically noisy — a motor driver, a stepper stage, the 5V control board buried inside a mains-powered appliance — the job the PC817 is doing is breaking the shared ground path, not surviving a high voltage. It is also worth remembering that the PC817's input cares about current, not voltage: a 5V rail able to deliver ten amps is no harder on the optocoupler than one that delivers ten milliamps, because the on-board resistor sets the LED current either way — what the beefier supply really changes is how badly a wiring mistake elsewhere ends, which is one more argument for isolating it. The 3-5V board covers 3.3V and 5V signals with the same variant, and 3.3V is the practical floor for the PC817 in any form, since the internal LED alone drops about 1.2 V of it.
Wiring Diagram
The 12V/24V side and the ESP32 S3 side are wired as two completely separate circuits that only meet inside the PC817's plastic body. With a module that split is physical and obvious: the field wiring lands on the screw terminals at one end of the board, the ESP32 S3 connects to the pin header at the other, and nothing crosses between them.
Safety Notes
Do not connect the ground of the 12V or 24V circuit to the ESP32 S3 GND. Those two grounds staying apart is the entire reason this circuit protects your board — tie them together and the module will still switch, but the isolation you built it for is gone and a fault on the industrial side reaches the ESP32 S3 directly. Equally important, fit the module variant that matches the voltage you are actually measuring rather than whichever one was already in the drawer, because a 12V module on a 24V line drives its internal LED at roughly twice the current it was built for and will damage it, while a 24V module on a 12V line is underdriven and may switch late or not at all; if you are building the bare-chip version instead, that same rule becomes sizing the series resistor for the input voltage. Mind the polarity of that input while you are at it, because it is an LED and conducts one way only — swap IN+ and IN- and the circuit simply stays dark rather than reporting anything, so establish which side of your tap is positive before wiring it. If the thing you are monitoring is inductive — a relay coil, contactor, solenoid valve, motor starter or the coil that drives a beacon — fit an ordinary diode in reverse-parallel directly across the input terminals, cathode to IN+ and anode to IN-: the collapsing field throws a large reverse spike at an input LED rated for only 6 V reverse, and while that diode does nothing at all in normal operation, on a spike it conducts and clamps the reverse voltage to well under a volt. A 1N4148 suits small taps and a 1N4007 heavier ones. On the output side, keep everything referenced to 3.3V: the ESP32 S3 is not 5V tolerant, so power the module from the board's 3.3V rail and never from 5V. Finally, pick the GPIO with some care. This tutorial uses GPIO19, which on the ESP32 S3 doubles as the native USB D- line, GPIO20 being D+ — but that second role only claims the pins when your sketch actually uses the native USB peripheral, and on a board that reaches the Arduino IDE through a USB-to-UART bridge, as most ESP32 S3 development boards do, GPIO19 and GPIO20 are ordinary GPIOs with a working internal pull-up. If you are using native USB instead — USB CDC serial over the board's native port, USB HID, or anything built on TinyUSB — then those two pins belong to the USB peripheral and the module's output has to go somewhere else; GPIO16 and GPIO17 are free of any special role and either one will do. Separately, on N16R8 modules stay away from GPIO26 to GPIO37, which are reserved for the octal SPI flash and PSRAM. The ESP32 S3 pinout guide lists every pin's constraints if you need a different one.
Wiring with a PC817 optocoupler isolation module:

This image is created using Fritzing. Click to enlarge image
| Module Terminal/Pin | Connects to |
|---|---|
| IN+ (screw terminal) | positive of the 12V/24V signal |
| IN- (screw terminal) | ground of the 12V/24V circuit ONLY |
| VCC (header) | ESP32 S3 3.3V |
| GND (header) | ESP32 S3 GND |
| OUT / DO (header) | ESP32 S3 GPIO19 |
Modules come in 1, 2, 4 and 8 channel versions, and every channel repeats this same pattern: its own pair of input screw terminals, its own output pin, and usually its own status LED that lights while that channel's input side is energised. Only VCC and GND are shared between channels.
Connect the PC817 in Parallel With the Device to See Whether It Is Running
The module's input must go across the device you are watching — in parallel with it — and never in series with it. Wiring it in series would route the device's whole load current through an infrared LED rated for 20 mA, and the PC817 would be destroyed the instant the circuit closed. Take the conveyor's 24V "running" beacon as the worked example: the beacon keeps both of its own wires untouched, and you simply run a second pair alongside them to the module's screw terminals — with no resistor of your own to add, because the 24V module already carries one.
| Tap point on the device | Connects to |
|---|---|
| Switched / positive terminal of the beacon | module IN+ screw terminal |
| Negative / return terminal of the beacon | module IN- screw terminal |
Nothing in the existing circuit is cut or rerouted, and the tap draws only about 10 mA — negligible next to what a beacon, a contactor coil or a conveyor motor starter pulls — so the machine carries on working exactly as it did before. It is a read-only measurement.
Because the tap watches the device's own supply rather than a command your sketch issued, the ESP32 S3 follows the equipment's real state: it sees the conveyor an operator started from the panel, and it equally sees the guard interlock that was commanded on and never actually came on. That is what turns the reading into something worth logging — run-hour totals, idle-time records and "did it really start?" alerts all fall out of it. One honest limitation is worth stating plainly: this tells you the device is energised, not that it is functioning, so a seized motor or a burnt-out heater element still draws power and still reports as ENERGIZED.
Two field quirks are worth knowing before you tap real machinery. Some PLC transistor outputs, triac outputs and LED-beacon drivers leak a small current even while switched off, and enough leakage half-lights the PC817's LED so that the ESP32 S3 reports ENERGIZED permanently; the giveaway is a reading that never falls back to OPEN, and the cure is a bleeder resistor of roughly 4.7 kohm to 10 kohm fitted in parallel with the module's input terminals, giving that leakage a path that bypasses the LED. The second quirk is that the input is a single LED and therefore conducts in one direction only, which makes the PC817 a DC-only part — sensing an AC line calls for an optocoupler whose input stage carries two back-to-back LEDs instead.
Is Your Optocoupler Module Active HIGH or Active LOW?
On the bare chip the answer is settled physics. The ESP32 S3 pin is held up by its internal pull-up while the phototransistor pulls it down, so the reading is inverted:
- External 12V/24V signal present → LED on → transistor conducts → the pin reads LOW
- External 12V/24V signal absent → LED off → transistor off → the pin reads HIGH
On a module you have to check which way round yours is, because both kinds are sold: many manufacturers bring the phototransistor straight out and behave exactly like the bare chip, while others add an inverting stage on the board so the output follows the input instead. The printing on the board is rarely explicit enough to trust, and the test takes under a minute:
- Wire the module as in the table above and upload the sketch from this tutorial, which prints the state on every poll.
- Leave the input screw terminals unconnected and read what the Serial Monitor prints — that is your idle state.
- Apply the real signal to IN+ and IN- and read the Serial Monitor again.
- Whatever level appears while the signal is present is your module's active level. If the sketch already reports ENERGIZED at that moment, your module is active-LOW and there is nothing to change.
- Cross-check against the module's own status LED, which lights whenever the input side is energised. If that LED is lit while the sketch still says OPEN, yours is the inverting kind.
Correcting it is a single edit rather than a rewrite: the sketch below carries one constant, MODULE_ACTIVE_LOW, set to true by default. Change it to false and every reading flips to suit an active-HIGH module.
Optional: Build It From the Bare PC817 Chip
If you would rather solder the raw 4-pin chip than buy a module, everything above still applies — you simply supply the resistor the module would have had. Wire it using the pinout listed earlier: the signal wire through the resistor to pin 1, the external circuit's ground to pin 2, ESP32 S3 GND to pin 3, and GPIO19 to pin 4. Size the resistor with R = (Vin - Vf) / If, taking Vf = 1.2 V and a target current of 10 mA, which gives 220 ohm at 3.3V, 390 ohm at 5V, 1 kohm at 12V and 2.2 kohm at 24V. Use a 1/2 W part on a 24V line; 1/4 W is fine everywhere below that. Reusing a 1 kohm resistor on 24V is the classic mistake here — it pushes about 22.8 mA through an LED rated for 20 mA continuous, and shortens the PC817's life just as surely as a mismatched module does.
How To Program
No library is needed for this project — the module hands the ESP32 S3 a plain digital level, so pinMode() and digitalRead() are the whole toolkit. Configure GPIO19 as INPUT_PULLUP so the pin idles HIGH when nothing is driving it, then let the sketch translate that raw level into a real-world state instead of scattering LOW and HIGH comparisons through the rest of your code. That translation happens in exactly one place, against the MODULE_ACTIVE_LOW constant at the top of the file: leave it true for the common active-LOW module and for the bare chip, and set it false if the polarity test above showed that your module inverts on-board. The sketch polls the pin twice a second, prints the current state of the monitored line, and adds an extra transition message the moment the external circuit switches on or off.
ESP32 S3 Code - PC817 Optocoupler
Detailed Instructions
- New to ESP32 S3? Complete our Getting Started with ESP32 S3 guide first.
- Measure the signal you want to watch and pick the matching module — the 24V module for a 24V line, the 12V module for a 12V line, the 3-5V module for a logic-level signal.
- Land the field wiring on the screw terminals: the switched positive to IN+, the external circuit's return to IN-.
- Wire the output side: VCC to the ESP32 S3 3.3V pin, GND to GND, and OUT to GPIO19.
- Double-check that the external circuit's ground is NOT connected anywhere on the ESP32 S3.
- Plug the ESP32 S3 into your computer with the USB Type-C cable.
- Open the Arduino IDE and select your ESP32 S3 board and its COM port.
- Copy the code above into the editor and press the Upload button.

- Open the Serial Monitor and set it to 115200 baud.

- Switch the external 12V or 24V signal on and off and watch the reported state follow it. If it reads backwards, set MODULE_ACTIVE_LOW to false and upload again.
- Pro Tip: Before wiring anything industrial, rehearse the circuit with a bench supply or a spare 12V adapter — if the printed state stays ENERGIZED with the input disconnected, your two grounds are accidentally joined somewhere.
Serial Monitor Output
※ NOTE THAT:
If the Serial Monitor reports ENERGIZED all the time and never changes, check four things in this order: that the module variant matches your input voltage, that MODULE_ACTIVE_LOW is set the right way round for your module, the polarity of IN+ and IN- (the internal LED only conducts one way), and whether the external ground has crept onto the ESP32 S3 GND rail. If it reports OPEN all the time, the input LED is almost certainly not lighting — the module's own status LED tells you that at a glance, and a 24V module fed from a 12V line is a common cause.
Application Ideas
Once your ESP32 S3 can read an isolated digital input, almost any piece of 12V or 24V equipment becomes something you can monitor without modifying it.
- Machine-guard interlock logger: Watch the 24 V interlock line of a guarded machine and record every time the guard circuit opens, so you can see how often production is actually being interrupted.
- Conveyor run-hour monitor: Tap the 24 V lamp line that drives a conveyor's "running" and "fault" beacons, mirror the plant-floor status onto a Wi-Fi dashboard served by the ESP32 S3, and total the hours the conveyor is genuinely running so a "did it start?" alert fires when it should be moving and is not.
- Freezer alarm watcher: Sense when a commercial freezer's 12 V alarm output goes active — a job for the 12V module — and push a notification long before anyone happens to walk past the unit.
- Solenoid coil actuation counter: Monitor a 24 V solenoid or contactor coil and count actuations, so maintenance can be scheduled on real duty cycles rather than the calendar.
- Long-run gate switch input: Read a 24 V switch at the far end of a long cable run where a bare 3.3V wire would pick up too much noise to be trusted — a sturdier cousin of the ESP32 S3 limit switch project.
- Isolated trigger for a switched load: Let a validated 24 V field signal decide when the ESP32 S3 closes a relay, keeping the control logic separate from the field wiring at both ends.
Video Tutorial
Watch the step-by-step video walkthrough for this ESP32 S3 project below.
Challenge Yourself
The circuit is small, but there is plenty of room to grow it into something genuinely useful on a workbench.
- Beginner: Light the ESP32 S3's on-board RGB LED on GPIO48 whenever the isolated input is energised, so you get a glanceable indicator without opening the Serial Monitor.
- Intermediate: Time how long the external signal stays energised on each activation, print the duration in seconds, and keep a running total of on-time since boot.
- Advanced: Add software debouncing for contact-driven signals and expand to a 4-channel 24V optocoupler module, reading four independent field lines and publishing their combined state over Wi-Fi.