Raspberry Pi Pico - PC817 Optocoupler: Monitor 12V/24V DC Input Signal

In this guide, we will learn to set up and use the PC817 optocoupler with the Raspberry Pi Pico. We will cover the following details:

Raspberry Pi Pico PC817 optocoupler isolated digital input

The Raspberry Pi Pico is small, inexpensive and needs very little power, so it is a great board to leave permanently wired to one machine as a tiny standalone watcher. The question such a watcher really answers is not whether a voltage is present, but whether the device on the other end is actually running right now. Two projects this guide is aimed at: counting the cycles of a machine that reports each stroke on a 24V PLC output, and watching the 12V position signal of a gate or barrier controller so you always know whether it is open or shut. Both of those signals are far too high for a Raspberry Pi Pico to touch directly, and the PC817 is what makes them readable.

Hardware Preparation

1×Raspberry Pi Pico W
1×Raspberry Pi Pico Alternatively,
1×Micro USB Cable
1×PC817 Optocoupler Module 3-5V (for 3.3V and 5V logic signals)
1×PC817 Optocoupler Module 12V (for 12V DC signals)
1×Breadboard
1×Jumper Wires
1×Recommended: Screw Terminal Expansion Board for Raspberry Pi Pico

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 .

Overview of PC817 Optocoupler

The PC817, also sold as a photocoupler or an opto-isolator, is a 4-pin chip that carries a signal from one circuit to another without any wire between them. Inside the plastic body there is an infrared LED pointing at a phototransistor across an insulating gap. When current flows through the LED, its light switches the transistor on, so the signal crosses as light instead of electricity. That is the whole reason the two sides stay electrically separate, and it is why a Raspberry Pi Pico can watch a 24V industrial line without ever being wired to it.

Most people never solder the chip itself. A PC817 optocoupler isolation module puts that chip on a small board together with its series resistor and a status LED, brings the input side out to screw terminals marked IN+ and IN-, and brings the output side out to a header marked VCC, GND and OUT. That is the version this guide wires up, and modules come in 1, 2, 4 and 8 channel versions, which is handy when one Raspberry Pi Pico has to watch several isolated lines at once.

PC817
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 to 20 mA normal
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 µs, fall about 3 µs
Operating Temperature -30°C to +100°C

Which Optocoupler Module Do I Need?

Because the resistor that feeds the internal LED is already fitted to the board, one module cannot cover every input voltage. The modules are sold as three separate variants, and the resistor on each one is chosen for that variant's voltage. Buy the variant that matches the signal you intend to watch:

Signal you want to watchModule to buy
A 24 volt DC signal, such as a PLC output24V module
A 12 volt DC signal, such as a gate or barrier lamp12V module
A 3.3 volt or 5 volt logic signal3-5V module

Getting this right is the one purchase decision that matters here. With a module you never calculate anything; you choose the correct variant and wire it up. Get it wrong in either direction and you will notice:

  • Run a 12V module from a 24 volt line and roughly double the intended current goes through its internal LED, which will damage it.
  • Run a 24V module from a 12 volt line and the LED is underdriven, so the output may switch late, switch unreliably, or never switch at all.

Low voltage is not a reason to skip the isolation and reach for a plain jumper wire. A 5 volt or 3.3 volt logic signal is worth isolating whenever the two circuits run from separate supplies, sit at opposite ends of a long cable, or one of them is electrically noisy, such as a motor driver or the low-voltage control board buried inside a mains-powered appliance. Isolation is about breaking the ground path, not only about surviving a high voltage. That is what the 3-5V module is for.

Pinout

The PC817 has four pins. Pins 1 and 2 belong to the input side, which faces the 12V or 24V circuit and which a module brings out to the IN+ and IN- terminals. Pins 3 and 4 belong to the output side, which faces the Raspberry Pi Pico and which a module brings out to its VCC, GND and OUT header.

  • Pin 1 - Anode (A): the positive leg of the internal LED. The 12V or 24V signal reaches this pin through the series resistor, which is already on the board of a module.
  • Pin 2 - Cathode (K): the negative leg of the internal LED. Connect to the ground of the 12V or 24V circuit, not to the ground of the Raspberry Pi Pico.
  • Pin 3 - Emitter (E): the phototransistor emitter on the output side. Connect to a GND pin (0 volts) of the Raspberry Pi Pico.
  • Pin 4 - Collector (C): the phototransistor collector on the output side. Connect to a GPIO input pin of the Raspberry Pi Pico with the internal pull-up to 3.3 volts turned on.
PC817 optocoupler pinout
image source: diyables.io

How It Works

On the output side the collector is held up by the internal pull-up of the Raspberry Pi Pico, so the pin idles at 3.3 volts. As soon as the external signal appears, the LED lights, the phototransistor conducts, and it drags the pin down to 0 volts.

  • External 12V or 24V signal PRESENT: the LED is on, the transistor conducts, and the GPIO pin reads LOW.
  • External 12V or 24V signal ABSENT: the LED is off, the transistor is off, and the GPIO pin reads HIGH.

The reading is therefore inverted, which surprises most people the first time. A LOW reading means the signal is there, not missing. Our MicroPython code flips it back with a single comparison so that the rest of the program can stay readable. If you have already worked through the Raspberry Pi Pico - Button tutorial, this is exactly the same active-LOW pattern you used there, only now the switching is done by light instead of by a metal contact.

Optocoupler Module Active High or Active Low

Everything above is settled physics for the bare chip wired as shown. A module is a different matter: some makers add a transistor stage that inverts the signal again on the board, so the OUT pin follows the input instead of opposing it. Both kinds are on sale, they look identical, and nothing on the silkscreen tells you which one you are holding. Find out before you trust the reading.

This matters more on this page than on most, because the plan is to leave the Raspberry Pi Pico running as a cycle counter in a cabinet. A module of the opposite polarity does not fail loudly - it counts the gaps instead of the strokes, so it happily records exactly the wrong half of every cycle and the number still looks plausible on the screen. Two minutes in the Thonny Shell now saves you from screwing the lid down on a counter that has been lying since the day it was installed.

With the module wired and the Raspberry Pi Pico connected, click the red Stop button in Thonny so no script is running, then type these three lines into the Shell at the bottom of the window:

from machine import Pin pin = Pin(0, Pin.IN, Pin.PULL_UP) pin.value()

Leave the input terminals unpowered and press Enter on the last line: note the number the Shell prints back. Now apply the signal to IN+ and IN-, press the Up arrow to bring that same line back, and press Enter again. The value that appears while the signal is present is the active level of your module. The status LED on the module is your cross-check, because it lights whenever the input side is energised - if the LED is on and the pin still reads 1, your module is active HIGH.

There is one line to change in the code below. MODULE_ACTIVE_LOW is set to True, which suits the common modules and the bare chip. If your test says the Shell prints 1 while the signal is present, set it to False and nothing else in the script has to move.

The Two Grounds Stay Apart

This is the part that protects your board, so read it twice. Do NOT connect the ground of the 12V or 24V circuit to the GND of the Raspberry Pi Pico. The 12V/24V ground belongs to the IN- terminal of the module, or to pin 2 of a bare chip, and stops there. The Raspberry Pi Pico ground belongs to the GND pin of the output header, or to pin 3 of a bare chip, and stops there. Keeping those two grounds apart is the whole point of the chip. If you tie them together the PC817 will still switch and your code will still print the right thing, but the isolation is gone and a fault on the machine side now has a path straight into your Raspberry Pi Pico. When you only want to scale a voltage down rather than isolate it, a divider such as the one in the Raspberry Pi Pico - Measure Voltage tutorial is the right tool instead.

Keeping the Output Side at 3.3 Volts

The Raspberry Pi Pico is a 3.3V board and its GPIO pins are not 5V tolerant. Anything you feed into a GPIO pin has to stay at or below 3.3 volts. That is easy to respect here, because the phototransistor only ever pulls the pin down towards 0 volts and never pushes anything into it. Take the VCC of the module from the 3V3(OUT) pin of the Raspberry Pi Pico, which is physical pin 36. Do not take it from VBUS or VSYS, because those sit near 5 volts and the module would then drive its OUT pin to 5 volts. This applies to modules that invert on-board as well, and those are the ones that actively drive the OUT pin high, so the rail you feed them is exactly the voltage that lands on GP0.

Wiring Diagram

Wire the components as shown below, with the module variant that matches the line you are watching.

Raspberry Pi Pico - PC817 Optocoupler Module Wiring

The output header goes to the Raspberry Pi Pico: VCC to 3V3(OUT) on physical pin 36, GND to any GND pin, and OUT to GP0, which is physical pin 1. The screw terminals go to the isolated circuit: IN+ to the positive side of the signal and IN- to its return. There is no resistor to fit, because the module already carries the one that suits its variant.

The wiring diagram between Raspberry Pi and Pico PC817 optocoupler isolation module 12V input

This image is created using Fritzing. Click to enlarge image

Wiring the Module in Parallel With a Device to Know If It Is Running

So far this guide has talked about a signal, but what you usually have in front of you is a device, and the real question is whether that device is running. Take the 12V gate or barrier controller from the start of the page. Its position output feeds a 12V indicator lamp that is lit whenever the barrier is up. You leave that lamp wired exactly as the installer left it, and you hang a 12V module across it:

  • The positive, switched terminal of the lamp goes to the IN+ terminal of the module.
  • The negative, return terminal of the lamp goes to the IN- terminal of the module.

Nothing in the controller is cut and nothing is rerouted, and nothing goes between the lamp and its supply. The module sits in parallel with the device, never in series with it. Wiring it in series would send the whole load current of the lamp, coil or motor through the internal LED, which is rated for 20 mA, and the chip would be gone the instant the device switched on. The parallel tap draws only about 10 mA, which is nothing beside what a lamp, relay coil, valve or motor already pulls, so the barrier controller carries on behaving exactly as it did before. This is a read-only measurement.

Because the tap follows the device itself and not the button or the controller output that commanded it, the Raspberry Pi Pico sees the real state: the barrier rising because a car triggered the loop, someone raising it by hand at night, or the barrier failing to move at all when it should have. That is what turns the reading into something worth logging - run hours, cycle counts, idle time, and did-it-actually-start alerts all fall out of the same single bit. One honest limitation: this tells you the device is energised, not that it is working, so a seized gate motor or a burnt-out lamp still draws power and still reads as running.

A few things to sort out before you power the tap up:

  • Polarity matters: the input side of the module is an LED and conducts in one direction only. Swap IN+ and IN- and it will simply never turn on, so work out which side of the tap is positive first.
  • Inductive loads need a clamp diode: a relay coil, solenoid, contactor, valve or motor throws a large reverse-voltage spike when it switches off, and the PC817 input LED will only stand 6 volts in reverse. Fit an ordinary diode in reverse-parallel directly across the two input terminals, cathode to IN+ and anode to IN-. In normal operation it is reverse-biased and does nothing; on a spike it conducts and clamps the reverse voltage to well under a volt. A 1N4148 suits a small tap, a 1N4007 a heavier one.
  • Leaky solid-state outputs cause false readings: PLC transistor outputs, triac outputs and LED-lamp drivers often leak a little current even when they are switched off, and enough leakage part-lights the PC817 LED, so the Raspberry Pi Pico reports the device as running when it is not. The symptom is a reading that never goes back to idle. Fit a bleeder resistor of about 4.7 kohm to 10 kohm across the input terminals and the leakage flows through that instead of through the LED.
  • DC only: the input is a single LED, so it responds to one polarity, and it is the wrong part for sensing an AC line, which needs an optocoupler whose input stage has two back-to-back LEDs.

If You Would Rather Build It From the Bare Chip

A bare PC817 costs a few cents and does the same job on a breadboard, but then the series resistor on pin 1 is yours to size, using the pinout above. The rule is R = (Vin - 1.2) / 0.01, taking 1.2 volts as the LED drop and 10 mA as the target current, which gives:

  • 3.3 volt input: 220 ohm
  • 5 volt input: 390 ohm
  • 12 volt input: 1 kohm
  • 24 volt input: 2.2 kohm, and use a 1/2 W part here because it dissipates about 0.24 W

Pin 3 goes to a GND pin of the Raspberry Pi Pico and pin 4 to GP0, exactly as the module's GND and OUT do. Change the resistor whenever you change the voltage you are watching: leaving a 1 kohm resistor in place when you move from a 12V circuit to a 24V one pushes about 22.8 mA through an LED rated for 20 mA. Wired this way the chip is always active LOW, so the code below needs no change.

Raspberry Pi Pico Code - PC817 Optocoupler

No library is needed. Reading a PC817 is plain digital input reading, so the whole program is a pull-up, a comparison and a print.

""" This Raspberry Pi Pico MicroPython code was developed by newbiely.com This Raspberry Pi Pico code is made available for public use without any restriction For comprehensive instructions and wiring diagrams, please visit: https://newbiely.com/tutorials/raspberry-pico/raspberry-pi-pico-pc817-optocoupler-monitor-12v-24v-dc-input-signal """ from machine import Pin import utime # The Raspberry Pi Pico pin connected to the PC817 collector (GP0, physical pin 1) PC817_PIN = 0 # Most PC817 modules pull the output down to 0V when the input signal is present. # If your module works the other way round, change this to False. MODULE_ACTIVE_LOW = True # The internal pull-up holds the pin at 3.3V while the PC817 is off isolated_input = Pin(PC817_PIN, Pin.IN, Pin.PULL_UP) while True: # The PC817 pulls the pin down to 0V while the 12V/24V signal is present, # so the reading is inverted: LOW means the external signal is ON raw = isolated_input.value() signal_active = (raw == 0) if MODULE_ACTIVE_LOW else (raw == 1) if signal_active: print("Isolated input: SIGNAL PRESENT") else: print("Isolated input: IDLE") # Wait half a second before reading the isolated input again utime.sleep(0.5)

Detailed Instructions

Please follow these instructions step by step:

  • Ensure that Thonny IDE is installed on your computer.
  • Ensure that MicroPython firmware is installed on your Raspberry Pi Pico.
  • If this is your first time using a Raspberry Pico, refer to the Raspberry Pi Pico - Getting Started tutorial for detailed instructions.
  • Use the module variant that matches your signal: the 3-5V module for a 3.3V or 5V logic signal, the 12V module for a 12V signal, the 24V module for a 24V signal.
  • Wire the components according to the provided diagram, keeping the 12V/24V ground away from the Raspberry Pi Pico GND.
  • Connect the Raspberry Pi Pico to your computer using a USB cable.
  • Launch the Thonny IDE on your computer.
  • On Thonny IDE, select MicroPython (Raspberry Pi Pico) Interpreter by navigating to Tools Options.
  • In the Interpreter tab, select MicroPython (Raspberry Pi Pico) from the drop-down menu.
  • Ensure the correct port is selected. Thonny IDE should automatically detect the port, but you may need to select it manually (e.g., COM3 on Windows or /dev/ttyACM0 on Linux).
  • Copy the above code and paste it to the Thonny IDE's editor.
  • If your polarity test said the Shell prints 1 while the signal is present, set MODULE_ACTIVE_LOW to False before you save.
  • Save the script to your Raspberry Pi Pico by:
    • Click the Save button, or use Ctrl+S keys.
    • In the save dialog, you will see two sections: This computer and Raspberry Pi Pico. Select Raspberry Pi Pico
    • Save the file as main.py
  • Click the green Run button (or press F5) to run the script. The script will execute.
  • Switch the 12V or 24V signal on, leave it on for a few seconds, then switch it off again.
  • Check out the message in the Shell at the bottom of Thonny.
Shell x
>>> %Run -c $EDITOR_CONTENT
MPY: soft reboot Isolated input: IDLE Isolated input: IDLE Isolated input: IDLE Isolated input: SIGNAL PRESENT Isolated input: SIGNAL PRESENT Isolated input: SIGNAL PRESENT Isolated input: SIGNAL PRESENT Isolated input: SIGNAL PRESENT Isolated input: IDLE Isolated input: IDLE Isolated input: IDLE
MicroPython (Raspberry Pi Pico) • Board CDC @ COM29 ≡

If the SIGNAL PRESENT lines appear while the signal is off and the IDLE lines while it is on, you have an active-HIGH module and the fix is the one line above, not a rewrite.

If you name your script main.py and save it to the root directory of the Raspberry Pi Pico, it will automatically run each time the Pico is powered on or reset. That matters for this project more than for most, because a device watcher is meant to be left in a cabinet next to the machine with nothing but a USB charger for power. Left like that, with a counter added to the code, the Raspberry Pi Pico becomes a permanent cycle counter that can say whether the machine ran today and how many times it ran, without anyone plugging a laptop into it. Confirm the polarity before the cabinet is closed, because a counter that has been counting the wrong half of every cycle looks exactly like a working one from the outside. If you name your script anything other than main.py, you will need to start it by hand from Thonny's Shell every time.

From here the same input can drive an action instead of a print. Feeding the isolated reading into the Raspberry Pi Pico - Relay tutorial turns the watcher into a small controller that reacts to the 24V line on its own.

Video Tutorial

Learn More

※ 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!