Digital Read with 4N35 Optocoupler

Hi all,

I am trying to daisy chain several of the same circuit (NodeMCU based) such that when I power an input on the first device, it'll trigger the second, and so on.

I'm trying to achieve this using optocouplers. I send a digitalWrite from a pin on Device 1 into another pin on Device 2 which is set up for digitalRead, and there is a 4N35 optocoupler in between. When digitalRead is high, fire Device 2. The pin on Device 2 is getting power from a power line on the NodeMCU with a 4.7k resistor going into the optocoupler (pin 5 on the receiver side).

This seems like it ought to work and indeed the switching seems to be happening when I test with a multimeter. But my digitalRead printouts are always printing 1/HIGH.

I should add that I am using a MCP23017 for all my GPIO purposes, so all the IO pins I refer to above are on that bus.

Anyone have an idea? Thanks!

Seeing your code and a circuit diagram would make the problem easier to understand

Sure, here's a breadboard mockup of what it looks like. The port labeled WRITE on Device 1 connects to the port labeled READ on Device 2 via 3.5mm stereo cable. That's a 200 ohm resistor on the emitter side (works well on other parts of the circuit) of the 4N35, and a 4.7k ohm on the receiver side.

Here's some code that I (quickly) stripped from the broader project:

#include <Wire.h>
#include <Adafruit_MCP23017.h>
Adafruit_MCP23017 mcp;

void setup() {
  Serial.begin(115200);
  mcp.begin();
  mcp.pinMode(1, INPUT);
  mcp.pinMode(2, OUTPUT);
}

void loop() {
  // Testing on Device 1
  mcp.digitalWrite(2, HIGH);
  delay(2000);
  mcp.digitalWrite(2, LOW);
  delay(2000);

  // Testing on Device 2 - always prints 1
  Serial.println(mcp.digitalRead(1));
}

That's no replacement for a simple circuit diagram, on the contrary. It's worse.

I don't know the pinout of that optocoupler and the MCP23017, so don't know how those are connected.

A hand drawn schematic is better than this.

I do miss a filtering cap on the power input of the MCP chip.

And pullups on the SDA and SCL pins seem to be missing.

Ok thanks for the advice. I'm not an expert in schematics but I went through the circuit in Fritzing and this should be it. Link to larger image here: https://i.imgur.com/clbBNkb.png

Are you sure I need pullups on SDA/SCL on the MCP23017?

You need pullups on the I2C bus, somewhere, does not matter what device(s).

I2C pullups.

Ok, so if I add 4.7k ohm resistors to both of those lines and pull them high, will that do the trick?

And would this be a cause of the problem I'm having with my digitalRead issue on the line with the optocoupler?

Ok, so if I add 4.7k ohm resistors to both of those lines and pull them high, will that do the trick?

I don't know if adding the required pullups will fix everything but it won't communicate if you don't.

And would this be a cause of the problem I'm having with my digitalRead issue on the line with the optocoupler?

If you are not able to communicate with the 23017 would not that be a problem?

Another thing is that the address pins must be externally biased, that is pulled high or low with external resistors or connection to Vcc or gnd. See A0-A2 in table 1-1 of data sheet.

try this I2C scanner to confirm communication over the bus. Get the chip talking before trying to use it.

// I2C scanner by Nick Gammon.  Thanks Nick.

#include <Wire.h>

void setup() {
  Serial.begin (115200); //*****  make sure serial monitor baud matches *****

  // Leonardo: wait for serial port to connect
  while (!Serial) 
    {
    }

  Serial.println ();
  Serial.println ("I2C scanner. Scanning ...");
  byte count = 0;
  
  Wire.begin();
  for (byte i = 1; i < 120; i++)
  {
    Wire.beginTransmission (i);
    if (Wire.endTransmission () == 0)
      {
      Serial.print ("Found address: ");
      Serial.print (i, DEC);
      Serial.print (" (0x");
      Serial.print (i, HEX);
      Serial.println (")");
      count++;
      delay (1);  // maybe unneeded?
      } // end of good response
  } // end of for loop
  Serial.println ("Done.");
  Serial.print ("Found ");
  Serial.print (count, DEC);
  Serial.println (" device(s).");
}  // end of setup

void loop() {}

That schematic is better readable, but if you want to move on with this hobby I really suggest you take the plunge and learn KiCAD or EagleCAD. Both produce far better schematics (e.g. for that MCP23017 all pins are arranged in a sensible manner, it doesn't try to follow the semi-random pin assignment of the IC housing).

GPA0 needs a pull-up resistor (enable the MCP's built-in pull-up, that should do).

For the project itself: indeed make sure you can see the MCP in an I2C scanner so you know that at least the bus is working, and the chip is reacting to communication. Next step is reading the inputs.

I am able to communicate with the 23017 though. I have a circuit that I've been using for about a year that uses 10 of the IO pins on the 23017 and it works fine. I just don't have pullups on SDA/SCL, just a plain line connecting those pins to GPIO0 and GPIO2 on the NodeMCU.

wvmarle:
That schematic is better readable, but if you want to move on with this hobby I really suggest you take the plunge and learn KiCAD or EagleCAD. Both produce far better schematics (e.g. for that MCP23017 all pins are arranged in a sensible manner, it doesn't try to follow the semi-random pin assignment of the IC housing).

GPA0 needs a pull-up resistor (enable the MCP's built-in pull-up, that should do).

For the project itself: indeed make sure you can see the MCP in an I2C scanner so you know that at least the bus is working, and the chip is reacting to communication. Next step is reading the inputs.

Thanks, I am learning EagleCAD actually, but I haven't moved all my work over there from Fritzing just yet. But I definitely recognize the need to do that as soon as possible, so thanks for the push :wink:

In general the circuit works. I have an additional 10 GPIO pins on the MCP23017 (all digital writes) that I've been using in this circuit for over a year and it's been working fine.

So it sounds like I need to do the following

  • 4.7k ohm resistors on the SDA/SCL lines
  • enable pullup resistor on the GPA0 (the line I'm trying to read)
  • check the MCP23017 in an I2C scanner

Correct? Thank you, this is a huge help.

joshbg2k:

  • 4.7k ohm resistors on the SDA/SCL lines

Yes.
Maybe your NodeMCU has the built-in pull-up resistors enabled. These are about 30k so out of spec for I2C but if you're lucky, and communicate at just 100 kHz, they may do the job. That's probably why it used to work - and likely only just.

  • enable pullup resistor on the GPA0 (the line I'm trying to read)

Yes.
I recall the pull-ups of the MCP are about 100k (see datasheet) so quite weak. If that doesn't work, or if you get false triggers, try a stronger pull-up (10k is common).

  • check the MCP23017 in an I2C scanner

Indeed.

wvmarle:
.
I recall the pull-ups of the MCP are about 100k (see datasheet) so quite weak. If that doesn't work, or if you get false triggers, try a stronger pull-up (10k is common).

Ok I've been working on this for the past couple hours and haven't quite gotten it yet.

I've got a 3.3v power line going into pin 5 (collector) of the optocoupler, and a line from the MCP23017 pin going into pin 4 (emitter) of the optocoupler with resistor (tried 10k, 4.7k) to power.

Multimeter is still indicating the power is switching as expected, but digital read still printing 1.

Couple questions -

  • Does my above approach seem correct? It's basically the same circuit as the breadboard image above with the recommended items added.
  • Do I still need a resistor on the pin 5 collector line? Originally I had a 4.7k there (didn't work), now I've tried it with and without the 4.7k (doesn't work)
  • Should I not expect this to work until I can get the SDA/SCL pullups installed? I'm hacking a circuit board so it's not really feasible to add those resistors at this very moment.

It seems really close, hope I can get there. Thanks again.

Update - I've tried a few scanners and it's not finding an i2c. So confused why the device worked for so long.

The optopcoupler part seems OK - you see the expected signal on your multimeter. That's a good start.

For the MCP: it won't work without pull-up resistors. it's interesting that it even worked before, it normally can't work without them. So yes, you have to add this.

As long as you don't see the chip on an I2C scanner, there's no point in continuing trying to read its pins. You don't have communication with the chip in the first place.

Ok, I've been working on this for a couple days and wanted to update the thread here.

First I want to clarify that the I2C scanners actually did work earlier (I hadn't specified the pins in the Wire.pins() method when I wrote the reply. After I set them, it found the MCP23017). But I hacked the 4.7k ohm resistors into the circuit on the SDA/SCL lines and that's working fine now.

Now back to reading the line with the optocoupler -

Pin 4 (optocoupler) is connected to the line I'm reading on the MPC23017. That line is pulled down with a 100k resistor. Lower resistor values result in higher voltage readings when sending LOW to the optocoupler, so I kept adding higher value resistors until I was seeing voltages < 100 mV on my multimeter.

Pin 5 (optocoupler) is connected to 3.3v (with or without a 4.7k resistor seems to make no difference.

Your earlier reply, based on my breadboard image, said to pull the read pin on the MCP up to power with a 10k resistor. Pin 4 (optocoupler) was connected to 3.3v (with 4.7k resistor) here. This resulted in the circuit always reading ~0v regardless of what values were being sent into the optocoupler.

It was driving me bananas trying to figure out why the optocoupler switch worked but the code didn't. I was using the Adafruit MCP23017 library and there must have been some kind of error somewhere, because when I switched over to the Wire library it started working right away. So that's great!

So any final thoughts here for a sanity check? Should I keep the 4.7k resistor to power on Pin 5 (optocoupler)?

Thanks for your help!

An up-to-date circuit diagram would be very useful. I can't follow what you're saying here.

By the way, for your I2C without pull-up resistors (I really have to look into the Wire library): yesterday I was playing with a MCP7940N RTC, and it worked fine connected to my Pro Micro without pull-up resistors (it's not a breakout board, just the chip and a resonator, really no pull-up resistors there). Must be through the internal pull-ups. That it's out of spec doesn't mean it doesn't work, it's just not a stable solution :slight_smile:

Thanks, here's the schematic:

Link to full res: https://i.imgur.com/eSwo8aK.png

Several issues I see here (it's really impressive how unreadable those Fritzing circuit diagrams are! What a mess, almost as bad as the colourful wiring diagram, which at least has colours).

A0-A2 pins should be connected to GND. This is missing.

SCL and SDA are not connected to the NodeMCU.

One side of the opto is connected to SDA1, the other to SDA2. The whole idea behind optocouplers is that it separates two circuits. The schematic is such a terrible mess that I can't figure out what is connected to what and how signals could possibly flow - a complete redraw of this circuit is in order to untangle all those connections. I think it's time for you to ditch Fritzing and get a proper circuit drawing program, such as KiCAD or EagleCAD. Hand drawn is usually better than this.

Power to the MCP23017 is missing, and I don't see a decoupling cap for it.

Haha, yeah sorry about that. I mostly use Fritzing to create PCBs. In practice SDA1 and SDA2 are not both connected to the same optocoupler, I just did it that way to save everyone from having to look at two of those diagrams, but the idea is to make the device chainable, so SDA2 writes to the next device which is reading SDA1, and so on. Sorry for the poor diagram.

Those things (except the cap) are in place in the actual circuit. The next diagram will not be created in Fritzing, I promise. In the meantime, just add the decoupling cap across power and ground on the mcp23017?