How to make ESP32 work with optocouplers inputs

Hi,

I need to read some states from and external existing device (the main unit of a wired alarm) which brings out those states as 12V levels or using some LEDS of which I need to detect if they are lit or not.

I’m using a Wemos D1 Mini ESP32 board, and I’m encountering a lot of problems in reading the remote states. I searched a lot on the internet but I cannot realize what I’m doing wrong.

On both figures below the D1 mini board is powered using a 3.3V regulator which shares the GND pin with the remote system.

In figure 1 on the left you can see the first circuit I tried, using the following code:

esphome:
  name: esp-32-try
  
esp32:
  board: wemos_d1_mini32

binary_sensor:
  - platform: gpio
    id: try_in
    pin:  
        number: GPIO16
        mode:
            input: true
            pullup: true

and it didn’t work: simply nothing happens when connecting +12V to pin 1 of the PC817 or removing it the gpio16 pin value remains stable, so I was not able to detect state change.

After a lot of searching I was able to find a guy who realized something very similar to my needs, so I changed my circuit as in Figure 2 above on the right, using the following code:

esphome:
  name: esp-32-try
  
esp32:
  board: wemos_d1_mini32

switch:
  - platform: gpio
    id: buildin_led
    pin: 
        number: GPIO2
    
binary_sensor:
  - platform: gpio
    id: try_in
    pin:  
        number: GPIO16
        mode:
            input: true
    on_press:
        then:
            - switch.turn_on: buildin_led
    on_release:
        then:
            - switch.turn_off: buildin_led

here I added the switch/led in order to have a local state for the input without the need to look at HomeAssistant screen.

At first I tried the figure 2 circuit without the 1MOhm resistor, and the behaviour got crazy: if I don’t connect to 12V the pin 1 of the PC817 the onboard led blinks randomly, so the state showed in HomeAssistant.

After adding the 1MOhm resistor it got stable, but again I was not able to detect any state change.

What am I doing wrong?

Many thanks in advance

First one will work if your wiring match the circuit (and your optocoupler is not damaged).
You don’t have logger on your yaml, so how you expect to see if it’s triggering?

Connect your optopcoupler to your microcontroller using a pullup resistor like the example below I pulled from my I/O boards :
pullup

You can also use the internal pullup but I prefer physical pullups since it means I don’t have to remember to configure something in software (then troubleshoot why it doesn’t work when I forgot).

Ground the transistor side of your optocoupler to the microcontroller ground. Ground the LED side of the optocoupler to the alarm circuit ground.

First one will work if your wiring match the circuit (and your optocoupler is not damaged).

I hope my optocoupler is not damaged, I’ll check or change it.
I’m sure my wiring match the cicuit.

You don’t have logger on your yaml, so how you expect to see if it’s triggering?

I have logging (I removed it from yaml for simplicity) anyway I expect to see the GPIO going up or down from HomeAssistant , or to see the led going lit or dim as in the second yaml

Thanks for your answer.

If I understood correctly, apart from the pullup resistor you’re suggesting me to return to this wiring:

which is the one in my Figure 1 above, on the left, the first I tried.
Is it correct?

I’m a bit puzzled, since it’s the first thing I tried, and it did’t work :thinking:

If you tried it without resistor in input, it is damaged.

If you tried it without resistor in input, it is damaged.

Furtunately I paid attention and never tried without the 560 Ohm resistor :slightly_smiling_face:

First one, that will be correct circuit…
But I don’t see pullup in your code. It should look like:

binary_sensor:
  - platform: gpio
    id: try_in
    pin:  
        number: GPIO16
        mode:
            input: true
            pullup: true
    on_press:
        then:
            - switch.turn_on: buildin_led
    on_release:
        then:
            - switch.turn_off: buildin_led

You should also check in documentation is that pin have pullup capatibility, or just put external one, lets say 1k, between io pin and 3V3.
Best regards,

At hardware level, all usable gpios up to 33 suppose to have pullup capability.
The first yaml in OP has pullups defined.

ESP internal pullups are sometimes not enough. Typically around 20K. Try a 1K pullup to 3v3.

Without the pullup resistor, either internal or external, the pin is probably always pulled low. Or perhaps floating if the optocoupler is turned off. Depends on impedance.

Add the pullup resistor and it should start working. Out of habit I always use 4.7K pullups. But any value greater than 1K to about 50K will work.

Ok, just a message to thank you all and to resume my issue.

First, it happened that trying and trying again and wiring and rewiring my circuit I ended up breaking my PC817 optocoupler :man_facepalming:… and obviously I didn’t notice immediately.

Now I use a socket in order to be able to check it and to replace it easily.

Then, about at the same time I ran into this bug: Mini D1 ESP32 doesn't boot with power on VCC pin after upgrade to ESPHOME 2023.8.1 · Issue #4864 · esphome/issues · GitHub

So I rewired my circuit to be powered from micro-USB and I replaced the optocoupler, and now… it works!

Schematic identical to figure 1 in my first post, except for the power supply which now comes from micro-USB, and using internal pullups.

I’m planning to insert a pulldown resistor between GND and pin 1 of the optocoupler, not to have a potentially floating input… but in any case I’m already happy like this! :slightly_smiling_face:

Many thanks again to all of you.