Arduino Nano ESP32 - PC817 Optocoupler: Monitor 12V/24V DC Input Signal

A lot of the equipment that ends up in a small home-automation or off-grid install already speaks in 12V or 24V: a solar charge controller with a "load on" status line, a 12V doorbell transformer, a gate or garage controller, an alarm panel siren output. Wiring any of those straight to an Arduino Nano ESP32 pin would end the project immediately. A PC817 optocoupler module sits in the middle and passes the information across as light instead of electricity, so the board can watch the line without ever touching it. Most of the time the real question is not "is there a voltage on this pair?" but "did that thing actually turn on?" - and the same part answers it, because the line you are watching is the line that feeds the device.

In this guide, we will learn how to use the PC817 optocoupler with the Arduino Nano ESP32. In detail, we will learn:

Arduino Nano ESP32 and PC817 optocoupler for isolated 12V and 24V digital input

Because the Nano ESP32 is small and has WiFi on board, this is a natural fit for a little always-on monitor: tuck it inside a 12V solar cabinet or next to a gate controller and have it tell your phone whether the gate motor really started.

Hardware Preparation

1×Arduino Nano ESP32
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×PC817 Optocoupler Module, 3-5V input
1×PC817 Optocoupler Module, 12V input
1×Breadboard
1×Jumper Wires
1×Recommended: Screw Terminal Expansion Board for Arduino Nano
1×Recommended: Breakout Expansion Board for Arduino Nano
1×Recommended: Power Splitter for Arduino Nano ESP32

Or you can buy the following kits:

1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

You buy one module - the one whose input voltage matches the signal you are going to watch. The next section works out which.

Overview of PC817 Optocoupler

The PC817, also sold as a photocoupler or an opto-isolator, is a 4-pin DIP part that holds two things inside one plastic body: an infrared LED and a phototransistor, facing each other across an insulating gap. Feed a current through the LED and its light switches the phototransistor on. The signal crosses as light, never as current, which is exactly why the 12V or 24V side and the Arduino Nano ESP32 side stay electrically separate.

A PC817 optocoupler module is that chip already mounted on a small board, with its series resistor fitted at the factory, screw terminals on the isolated side, a status LED, and header pins on the board side. That fitted resistor is why the modules are sold in variants, and it is also why there is nothing left for you to calculate.

PC817 Optocoupler
Package 4-pin DIP, pin 1 marked by a dot or a notch
Input LED forward voltage about 1.2V typical, 1.4V max
Input LED forward current 5-20mA normal range, 20mA max
Output collector-emitter voltage 35V max
Output collector current 50mA max
Collector power dissipation 150mW
Isolation voltage 5000Vrms for 1 minute
Current Transfer Ratio 50% to 600% depending on rank
Switching speed about 4us rise, 3us fall
Operating temperature -30C to +100C

How It Works

The two halves of the module never touch. IN+ and IN- belong to the external 12V or 24V circuit. VCC, GND and OUT belong to the Arduino Nano ESP32.

With the internal pull-up of the Arduino Nano ESP32 enabled on the pin wired to OUT, the readings come out like this:

  • The external 12V or 24V signal is PRESENT: the internal LED lights, the phototransistor conducts, it pulls the pin down to GND, and the pin reads LOW.
  • The external 12V or 24V signal is ABSENT: the LED is dark, the phototransistor blocks, the pull-up holds the pin up, and the pin reads HIGH.

So the reading is inverted: LOW means the line is live. That is settled physics for the optocoupler itself, and it is how most modules behave. Some manufacturers, though, put an inverting stage on the module's output side so that OUT follows the input instead of opposing it, and nothing on the packaging tells you which one you are holding. There is a one-minute test for it further down, and the sketch swaps over with a single constant.

Keeping the Grounds Apart

The isolation only exists as long as the two grounds stay separate. Do NOT run a wire from the negative of the 12V or 24V supply - the wire that lands on IN- - to the GND of the Arduino Nano ESP32. If you tie them together the module still switches and the sketch still prints the right thing, so nothing looks wrong - but the gap that was protecting the board is gone, and a fault on the 24V side now has a path straight into the Nano ESP32.

Output Side Runs at 3.3V

The Arduino Nano ESP32 is a 3.3V board, and its pins are not 5V tolerant the way a classic ATmega Arduino's are. That matters less than it sounds like here, as long as you keep two independent decisions apart in your head:

  • The variant you buy is decided by the signal you are watching. A 24V solar controller status line calls for the 24V module - and that stays true whether the board reading it runs at 3.3V, at 5V, or at anything else. The input side never learns what board is on the far side of the gap.
  • The module's output side is always powered from this board. Its VCC comes from the Nano ESP32 3.3V pin and its GND from the board GND, whichever input variant you bought.

The second rule matters because the phototransistor is a plain switch to GND: whatever the input side is carrying - 12V, 24V - the highest voltage OUT can ever present is whatever its output side is powered from. Take that from 3.3V and the pin is safe by construction. Feed the module's VCC from a 5V rail instead and OUT will present 5V and stress the Nano ESP32, even though the input side is perfectly happy - buying the right variant does nothing to protect you from that, because they are separate choices. Enabling the internal pull-up on D2 works for the same reason: it references the board's own 3.3V rail.

Which PC817 Optocoupler Module Do I Need?

The module arrives with its series resistor already fitted, and any one resistor is correct for one input voltage only. That is why these boards are sold as three separate variants. Nothing here gets calculated - you buy the one that matches the signal you intend to watch.

Signal you want to watch Module variant Typical sources
3.3V or 5V logic 3-5V module a sensor output, another microcontroller, the low-voltage control board inside an appliance
12V DC 12V module a gate or garage-door controller output, a doorbell transformer, camper and automotive circuits
24V DC 24V module a solar charge controller status line, an alarm panel output, a PLC output

Both ways of getting this wrong are worth knowing before you order:

  • A 12V module on a 24V line. Its resistor was sized for 12V, so roughly double the intended current goes through the internal LED. The module runs hot, fades, and eventually fails. This is the mistake that costs you the part.
  • A 24V module on a 12V line. Now the LED is underdriven. The output may switch late, switch only sometimes, or never switch at all - and you will spend the evening blaming the sketch for it.

Choosing the variant is the module's version of sizing a resistor: one purchase decision, made before anything is wired, and the only one that really matters here.

Do not skip past the 3-5V variant either, just because 3.3V and 5V are voltages the Nano ESP32 could take directly. A logic-level signal earns its isolation whenever the two circuits run from separate supplies, sit at opposite ends of a long cable, or one of them is electrically noisy - a motor driver, or the low-voltage control board buried inside a mains-powered appliance. Isolation is about breaking the ground path between two circuits, not only about surviving a high voltage.

Wiring Diagram

Wiring the PC817 Optocoupler Module

The wiring diagram between Arduino Nano ESP32 and PC817 optocoupler isolation module

This image is created using Fritzing. Click to enlarge image

Screw the external 12V or 24V signal into the IN+ terminal and the ground of that same external circuit into the IN- terminal. On the board side, wire the module's VCC to 3.3V, GND to GND, and OUT to pin D2 of the Arduino Nano ESP32. There is no resistor to add and nothing to recalculate - the variant you chose already carries the right one. Modules with 2, 4 or 8 channels work the same way: one pair of input terminals and one OUT pin per channel, each channel isolated on its own.

Wiring the Module in Parallel With the Device, Never in Series

The diagram above assumes the 12V or 24V signal is already sitting on a spare pair of wires, waiting to be read. In a real install it rarely is - the signal is the wire that already drives something, and that wire is busy. Take a 12V gate or garage-door controller: the controller closes its output, 12V appears on the pair going to the motor, the gate runs. The job is to let the Arduino Nano ESP32 see that moment without changing anything about it. The line is 12V, so this is the 12V module.

The module's input goes ACROSS the device, in parallel with it. Nothing is cut, nothing is rerouted, the existing wiring stays exactly where it is - you only hang two extra wires off terminals that are already there:

  • The positive, switched terminal of the device → IN+ on the module.
  • The negative, return terminal of the device → IN- on the module.

No series resistor belongs anywhere in that tap. The module's own resistor is already sitting in the path, which is exactly why the variant has to match the voltage the device runs on.

What you must not do is put the module in series with the device. In series, the entire load current of that gate motor would have to squeeze through the internal LED, and a part rated for 20mA would be destroyed the instant the controller fired. Parallel is the only correct arrangement.

Across the terminals, the tap draws about 10mA. That is nothing beside what a relay coil, a solenoid, a lamp or a motor pulls, so the gate controller carries on working exactly as before - the measurement is read-only and non-invasive.

Two details decide whether the tap survives its first switching cycle:

  • Polarity matters. The input side is an LED and conducts in one direction only. Swap IN+ and IN- and nothing breaks, but nothing works either - the module simply never turns on, and its status LED stays dark. Work out which side of the tap is the positive one before you wire it.
  • Inductive loads need a clamp diode. A relay coil, contactor, solenoid, valve or motor throws a large reverse-voltage spike the moment it switches off, and the PC817 input LED is rated for only 6V in reverse. Fit an ordinary diode in reverse-parallel directly across the input terminals - diode cathode to IN+, diode anode to IN-. In normal operation that diode is reverse-biased and does nothing at all; on a spike it conducts and clamps the reverse voltage to well under a volt. A 1N4148 covers a small tap, a 1N4007 a heavier one.

One boundary to keep in mind: the PC817 input is a single LED, so it answers to one polarity only and is the wrong part for sensing an AC line - that needs an optocoupler whose input stage carries two back-to-back LEDs. And if the terminals you are tapping belong to a solid-state output rather than a dry contact or a relay, read the leakage entry in the Troubleshooting section below before you trust the reading.

Monitoring Whether the Gate Controller Is Actually On or Off

Because the tap sits across the device instead of in the path that commands it, what the Arduino Nano ESP32 reads is the device's real state rather than your intention for it. Somebody opens the gate with a handset, and the reading follows. The alarm panel drives its siren on its own without asking anyone, and the reading follows that too. And when the controller is told to fire but nothing appears on its output, the reading stays idle - which is how you find out the gate never started. That gap between "a command was sent" and "the thing actually ran" is what makes this worth building: run-hour counters, machine idle-time logs, and a "did it actually start?" alert pushed out over WiFi.

Be clear about what the reading really means, though. It tells you the device is energised, not that it is functioning - a seized gate motor or a burnt-out heating element still draws power and still reads as on.

If You Would Rather Build It From the Bare Chip

PC817 optocoupler pinout for Arduino Nano ESP32 isolated input
image source: diyables.io

The raw 4-pin chip and one resistor give you the same circuit without the screw terminals - you just take the sizing on yourself. Hold the chip with the dot or notch at the top left and count anti-clockwise: pin 1 anode and pin 2 cathode are the input LED, pin 3 emitter and pin 4 collector are the output side. Signal through the resistor into pin 1, pin 2 back to the external circuit's ground only, pin 3 to the Arduino Nano ESP32 GND, pin 4 to pin D2. Size the resistor from R = (Vin - 1.2) / 0.01, about 10mA through the LED: 220 ohm at 3.3V, 390 ohm at 5V, 1 kohm at 12V, 2.2 kohm at 24V - the last in 1/2 W, the rest in 1/4 W. Leave a 1 kohm in place moving from 12V to 24V and the LED sees about 23mA, past its 20mA maximum, and it will fade and fail: the bare-chip version of fitting the wrong variant. Everything else on this page applies unchanged.

Is Your Optocoupler Module Active Low or Active High?

Both kinds are on sale, they look identical on the bench, and the listing you bought from almost certainly did not say. Settle it with the board itself before you write any logic on top of the reading:

  • Wire the module as above and upload the sketch from the section below, leaving the input terminals dead for now.
  • Watch the Serial Monitor for a few lines and note what it prints while there is no signal on the input.
  • Energise the input terminals. The module's on-board status LED lights whenever its input side is drawing current, so use that LED as the cross-check - it tells you the input really is energised, whatever the pin thinks.
  • Note what the Serial Monitor prints now. Whatever appears while the status LED is lit is your module's active state.

If the printout follows the LED - idle while it is dark, present while it is lit - your module is active low and the sketch is already right. If the two run exactly opposite, your module inverts on board. Open the sketch, set MODULE_ACTIVE_LOW to false, and upload again. That one constant is the only thing that changes; the wiring is fine and the rest of the code does not care.

How To Program For PC817 Optocoupler

  • Use the pin label printed on the board. The Arduino Nano ESP32 silkscreen reads D0, D1, D2 and so on, and those numbers are not the raw GPIO numbers of the ESP32-S3 underneath. Write D2 in the sketch, not 2, and the Arduino core maps it to the right GPIO for you.
  • Set that pin to input with the internal pull-up using pinMode(). For example, pin D2:
pinMode(D2, INPUT_PULLUP);
int raw = digitalRead(D2);
  • Turn that raw level into a plain true or false through one constant, so a module that reads the other way round costs you a single edit instead of a rewrite.
const bool MODULE_ACTIVE_LOW = true; bool signal_active = MODULE_ACTIVE_LOW ? (raw == LOW) : (raw == HIGH);
  • Skip the library search. No library exists for the PC817 and none is needed - this is ordinary digital input reading.

Arduino Nano ESP32 Code - PC817 Optocoupler

/* * This Arduino Nano ESP32 code was developed by newbiely.com * * This Arduino Nano ESP32 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-nano-esp32/arduino-nano-esp32-pc817-optocoupler-monitor-12v-24v-dc-input-signal */ #define PC817_PIN D2 // The Arduino Nano ESP32 pin D2 connected to the PC817 module's OUT pin // (or the bare chip's collector, pin 4) // Use the printed Dx label, not the raw ESP32-S3 GPIO number // Most PC817 modules pull the output LOW when the input signal is present. // If your module works the other way round, change this to false. const bool MODULE_ACTIVE_LOW = true; bool signal_active; // true when the external 12V/24V signal is present void setup() { // Initialize the Serial to communicate with the Serial Monitor. Serial.begin(9600); // Enable the internal pull-up so the pin sits at 3.3V while the optocoupler is off pinMode(PC817_PIN, INPUT_PULLUP); } void loop() { int raw = digitalRead(PC817_PIN); // The optocoupler pulls the pin to GND while its internal LED is lit, // so the reading is normally inverted: LOW means the external signal is present signal_active = MODULE_ACTIVE_LOW ? (raw == LOW) : (raw == HIGH); if (signal_active) Serial.println("[isolated input D2] 12V PRESENT"); else Serial.println("[isolated input D2] idle"); delay(500); }

Detailed Instructions

Follow these instructions step by step:

  • If this is the first time you use Arduino Nano ESP32, see how to setup environment for Arduino Nano ESP32 on Arduino IDE.
  • Wire the module to the Arduino Nano ESP32 as shown in the module wiring diagram above, using the variant that matches your signal: the 3-5V module for a logic-level signal, the 12V module for a 12V line, the 24V module for a 24V line.
  • Double-check that the ground of the 12V or 24V circuit goes nowhere near the Arduino Nano ESP32 GND.
  • Connect the Arduino Nano ESP32 to your computer with the USB cable.
  • Launch the Arduino IDE on your computer.
  • Select the Arduino Nano ESP32 board and its corresponding COM port.
  • Copy the code above and paste it into the Arduino IDE, then click the Upload button.
  • Open the Serial Monitor and switch the external 12V or 24V signal on and off.
Newbiely | Arduino IDE 2.3.8
──
File
Edit
Sketch
Tools
Help
Arduino Nano ESP32
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'Arduino Nano ESP32' on 'COM15')
New Line
9600 baud
[isolated input D2] idle [isolated input D2] idle [isolated input D2] idle [isolated input D2] 12V PRESENT [isolated input D2] 12V PRESENT [isolated input D2] 12V PRESENT [isolated input D2] 12V PRESENT [isolated input D2] idle [isolated input D2] idle
Ln 11, Col 1
Arduino Nano ESP32 on COM15
2
  • If those lines run the opposite way to the module's status LED, yours is the active-high kind: set MODULE_ACTIVE_LOW to false and upload again.

From here the sketch is easy to grow. Have it toggle a relay when the isolated line goes live, count how many times a gate controller fires overnight, or push the state out over WiFi from inside a solar cabinet.

Troubleshooting

If the reading does not follow the external signal, work through these checks:

  • Nothing ever changes and the status LED never lights: the input side is not being driven at all. IN+ and IN- are probably swapped - the input is an LED and conducts one way only - or the variant is wrong for the line, since a 24V module on a 12V circuit is underdriven and may never switch. On the bare chip, the same symptom means the chip is fitted backwards; pin 1 is the one next to the dot or notch.
  • The reading is exactly backwards - idle while the device is running, present while it is off: nothing is miswired. You have one of the modules that inverts on its output side, so OUT follows the input instead of opposing it. Confirm it against the status LED with the check in the "Is Your Optocoupler Module Active Low or Active High?" section above, then set MODULE_ACTIVE_LOW to false in the sketch and upload again.
  • The reading is stuck at present even with the input terminals disconnected: the board side is miswired. Confirm OUT really is on D2 and VCC on 3.3V. On the bare chip, pin 3 and pin 4 are swapped - the emitter (pin 3) belongs on GND and the collector (pin 4) on D2.
  • The reading flickers: The internal pull-up is probably not enabled. Confirm the sketch uses INPUT_PULLUP and not plain INPUT; a floating input drifts on its own, exactly as it does with a button.
  • The reading never goes back to idle: The thing you tapped is probably a solid-state output - a PLC transistor output, a triac output, an LED-lamp driver - and those leak a small current even when they are switched off. Enough leakage partly lights the internal LED, and the board goes on reporting the device as on long after it stopped. Fit a bleeder resistor of about 4.7 kohm to 10 kohm in parallel with the module's input, across IN+ and IN-, so the leakage drains through it instead of through the optocoupler.
  • The module runs warm, or the reading grows unreliable over weeks: the input voltage is above what that variant was built for - typically a 12V module left on a 24V line. Fit the 24V variant instead. On the bare chip, the series resistor is too small; recheck it against the values in the bare-chip section.
  • You need the actual voltage, not just present or absent: An optocoupler only reports on or off. To read a level, see Arduino Nano ESP32 - Measure Voltage.

Video Tutorial

Function References

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!