Category: Electronics

Photo resistors, in depth

Photo resistors, in depth

Some time ago I bought 50 photo resistors, 10 pieces of 5 different types each. Had no real need for that many but it’s very cheap if you buy them through AliExpress. It turned out to be quite difficult to find out which resistor does what. Tried looking for photoresistors, LDR, Light Dependent Resistor, photo cell but I could not find any explanation on the different types.

I did find the following information (the table is compiled from different sources):

Model Light resistance
(10 Lux) KOhm
Dark resistance
GL5506 2-6 0.15
GL5516 5-10 0.5
GL5528 10-20 1
GL5537 16-50 2
GL5539 30-90 5

But how does that help in connecting these things? Well, I decided to connect these things, all at the same time and look at the readings.

So connecting, how do we do that? Well, we need to understand (somewhat) Ohm’s law. To measure the amount of light, we want to measure the resistance of the light sensor. Unfortunately the Arduino or the ESP8266 do not measure resistance. They can however measure the voltage on an analog port and this is where we need Ohm’s law. First, let’s look at a schematic:


We connect the photo resistor to +5V (or +3.3V) on one end and the other end on a analog port (the ESP8266 has one, the Arduino has many). We also connect that same end to a resistor (in my diagram a 10K resistor) and the other end of the resistor to ground. What does this do? Well, current will flow from +5V over the photo resistor, continue over the 10K resistor and than to ground. What we are going to measure is the voltage at the point between the photo resistor and the normal resistor. Ohm’s law states V=IR, voltage is current times resistance. The current flowing from +5V to ground over both resistors will be the same (the amount of current does not change midway a circuit). That’s the key to figuring out the current at our analog port and here is how.

We can calculate the total current that flows from +5V to ground by adding the resistors and plugging it in Ohm’s law. Let’s say the photo resistor currently has a resistance of 10KΩ. The total resistance of the circuit is 10KΩ+10KΩ = 20KΩ. V = 5V which means I = 5V / 20KΩ = 0.25 mA. So we now know that there is 0.25 mA running through the circuit. Now we can calculate V at our analog input. The voltage drop over the photo resistor is V = IR and we now know I and R. V = 0.25 mA * 10KΩ = 2.5 V drop so at our analog input we will measure 5V – 2.5V = 2.5V. With 2 equal resistances, it makes sense that in the middle you will have 2.5V. Let’s take the next step.

The resistance of the photo resistor goes down when there is more light, so let’s make it a little darker, say 30KΩ. Let’s recalculate:

Total R = 30KΩ + 10KΩ = 40KΩ
I = 5V / 40KΩ = 0.125 mAVoltage drop over photo resistor = 0.125 mA * 30KΩ = 3.75 V
We measure 5V – 3.75V = 1.25V at our analog input

So less light means less voltage at our analog input. Well this math is all fine, but what does this mean? Well, if we know the voltage at our analog input we can also do the reverse and calculate the resistance of the photo resistor, just do the reverse.

Let me quickly share the setup and the code I used to test the resistors. You can my setup at the top. It is an Arduino Nano (which has 5 analog ports) and the 5 sensors, the bottom is the 5506 and moving up all the way to the 5539 at the top. It might be a bit hard to see. All black wires are +3.3V, all the white are ground and the different colors are for each of the analog inputs. You can see that I followed the schematic 5 times and importantly, used five 10KΩ resistors. You cannot combine all of them on one. Please do not forget to connect 3.3V to ARef which gives a better ready is my experience.

The code is simple, initialize five ports and continously display the results of the ports. I have added a header row every 10 rows so I can copy and paste that:

github-mark-32px Available on GitHub with more comment in the code. Direct download.

// The number of steps after which the header is repeated
#define STEPCOUNT 10
// The current step count
byte step = STEPCOUNT;

void setup() {
  // Setup serial
  // Set the reference for measuring the analog signals
  // For some reason I got incorrect readings without connecting
  // the 3.3v (on my Arduino Nano) to AREF and setting this
  // reference
  // Setup all analog inputs. We are using 5 which is the maximum
  // for the Nano on which I tested.

void loop() {
  // Increase our counter
  // If we are over our STEPCOUNT then we repeat the header
  if (step>STEPCOUNT) {
    // The reference to the different photo resistors
    // GL5506, GL5516, GL5528, GL5537 and GL 5539
    Serial.println("* 5506  5516  5528  5537  5539");
    step = 0;
  // Print all analog values to serial
  Serial.print("  ");
  Serial.print("   ");
  Serial.print("   ");
  Serial.print("   ");
  Serial.print("   ");

If we take the values it shows during daylight, inside the house:

* 5506  5516  5528  5537  5539
  975   969   774   619   218
  975   968   773   619   220
  975   969   773   618   216
  975   968   771   616   215
  975   968   771   617   221
  975   969   774   620   226
  975   969   776   622   225
  976   969   775   621   221
  975   969   774   619   220
  975   969   774   619   219
  975   968   772   617   216

As you can see, the values are relatively stable. So let’s take one of the values of the 5528, 775. How do we calculate the resistance from that? The analog port has a 10 bit resolution, which means it can go from 0 to 1023. 1023 will be at 3.3V (in my case. If you use a Uno for example it will be 5V). The reading is 496 which means it is reading a voltage of 775/1023*3.3V = 2.5V. So we know the voltage drop was 3.3V-2.5V = 0.8V. So we know V. Now we need to know I and from that we can calculate R. We can calculate R because we know V and R for the other part of the setup, the 10KΩ. I = V/R = 2.5V / 10KΩ = 0.25 mA. Now we take that 0.25 mA and use it for calculating the resistance of the photo resistor.
R = V/I = 0.8V / 0.25 mA = 3,200 Ω. Now you can go to the GL5528 datasheet and see


that the amount of light is between 30 and 85 lux. Well, that’s quite a broad range and not really useful for calculating the amount of lux but the deviation of these sensors might vary piece to piece but do not vary that much per piece, so if your sample decides to be more towards the dark side, it will be there with higher or lower light settings as well. What this means is that the sensors are great for detecting deltas in light even though getting a precise lux reading is not really feasible. Ok, well than we need more readings:

light from a normal bulb:
* 5506  5516  5528  5537  5539
  974   967   739   623   279
  982   981   844   717   437
  992   988   870   751   454
  994   991   874   758   440
  995   991   873   752   325
  992   986   836   700   137
  985   979   795   654   62
  978   972   754   610   34
  971   969   792   668   379
  988   985   854   727   440
  993   990   871   749   440

light from a mobile phone
* 5506  5516  5528  5537  5539
  1006   1007   956   925   795
  1006   1007   956   925   796
  1006   1007   956   925   797
  1006   1007   956   925   797
  1006   1007   956   926   798
  1006   1007   955   925   795
  1006   1007   956   925   796
  1005   1007   955   924   795
  1006   1007   955   924   793
  1005   1007   955   925   796
  1005   1007   955   925   795

light at dusk
* 5506  5516  5528  5537  5539
  834   834   385   208   20
  840   838   394   216   22
  840   840   402   222   23
  840   840   405   225   24
  839   839   404   225   24
  839   838   404   224   23
  840   838   405   225   24
  841   839   405   226   24
  841   839   405   226   24
  844   840   406   227   24
  845   840   406   227   24

light in the evening
* 5506  5516  5528  5537  5539
  582   583   118   52   1
  581   582   118   52   1
  580   583   118   53   1
  580   582   118   52   1
  580   581   118   52   1
  578   580   117   52   1
  577   577   116   52   1
  565   561   109   49   1
  584   583   118   51   1
  586   587   120   53   1
  589   590   121   53   1

There are a few things to notice. First, I thought it was interesting to see that the values fluctuated with a normal bulb. This sample was not an incident, it was doing that the whole time. I think it might have to do with the fact that light bulbs fluctuate because of the way the alternating current alternates but I’m not sure. The second thing to notice is that the 5506 does not really vary that much with light, especially compared to the others. It’s not because it’s broken, but it’s because it’s resistance is small compared to the 10kΩ resistor which means it does not leave a lot of ‘bandwidth’ to show it’s capabilities.

Maybe I will do some more readings where I take a resistor to match each photo resistor and see what the results are than.

I2C and MCP23017, part 2

I2C and MCP23017, part 2

After being able to set basic outputs and inputs in the previous post, the next step could be to be understand the inputs, internal pull ups and the triggers INTA and INTB.

Let’s start with internal pull ups. The MCP23017 has the ability to software define internal pull ups. This means that you do not have to build these outside the chip. The registers GPPUA (0x0C) and GPPUB (0x0D) are used to set or remove this internal pull up. As with the other registers, 8 bits can be set. 0 means do not use internal pull up while a 1 means use an internal pullup. For the remainder of this article, we will assume a range of buttons or other inputs on bank A.

Let’s tell the MCP23017 that all of bank A needs to have the internal pull up set:
(again pseudo code. The code will be different for the Raspberry Pi and the NodeMCU)


Now our inputs only need to connect to ground or float to send a signal. Next let’s tell the chip that we want any change on an input in bank A to send a signal to INTA. We do this by setting the individual bits on GPINTENA (0x04) high. (The corresponding GPINTENB can be found on 0x05).


All set, now if a change happens on any port on bank A, INTA is triggered. On your device (Raspberry PI or NodeMCU) you can define a trigger to react on this.

VERY IMPORTANT!! After each trigger on INTA (or INTB) you will NOT receive another trigger until you clear this one. You do this by either reading from GPIOA (or GPIOB) or by reading INTCAPA (0x10) (or INTCAPB at 0x11). Since you don’t know whether or trigger has happened during the initial boot up of your program, I suggest doing a read of GPIOA and/or GPIOB after you define your trigger to make sure the trigger is cleared.

The values you read from INTCAPA (or INTCAPB) show (in bit order) which ports set off the trigger.

If you are short on input ports on your device, it is possible to OR both INT ports such that you only have one trigger to alert you that there is a change on EITHER bank A or B. To do this, you set bit 6 (0x40) of either IOCONA (0x0A) or IOCONB (oxoB). To do this, you read the value first and than set or unset bit 6 and write it back.

current_value = read_from_i2c_bus(device_address,IOCONA)
new_value = current_value OR 0x40 (to set the 6th bit, chose one line \/)
new_value = current_value AND 0xBF (to unset the 6th bit, chose one line /\)

Depending on whether you want to set or unset the bit, you chose one of the 2 lines in the example above.

I2C and the MCP23017

I2C and the MCP23017

Sometimes in your Raspberry PI or NodeMCU projects you need more inputs and/or outputs than your PI/NodeMCU is giving you. One way to do that is to add more ports using the MCP23017 (or multiple even). It is a chip that works on the I2C bus and gives you 16 new in- and outputs.

How to connect them to your Raspberry Pi or your NodeMCU, I will discuss seperately. However the general workings of the chip is the same for both, so in this post I will explain how to connect it and what the different addresses are.

I2C is a bus which means you can have multiple devices on the same bus as long as each has a different address. The address for the MCP23017 is set by connecting pins to ground or Vdd. Let’s look at the pin layout:


As you can see there are 16 in- and outputs: GPA0-7 (bank A) and GPB0-7 (bank B). These are where you connect your digital in- and outputs to. Vdd is where you connect the + to (5v or 3.3v). Vss is connected to ground. SCL and SDA are the I2C bus channels and these you connect to your Raspberry Pi or NodeMCU bus. Note that you connect SCL to SCL and SDA to SDA (as opposed to Txd and Rxd which you switch). A0, A1 and A2 is where you set your address anywhere between 0x20 and 0x27 (hex 20 and hex 27). If you connect all three to ground, your device address is 0x20. If you connect A0 to Vdd and A1 and A2 to ground, your device address will be 0x21 etc. Also connect RESET to Vdd.

Once you have connected the power, connected the I2C bus and set the address, you are good to go.

To start, you have to tell the MCP23017 whether the ports are in- or outputs. You do this by writing to addresses IODIRA (0x00) for bank A and IODIRB (0x01) for bank B. There are 8 ports per bank which correspond to 8 bits. Setting a bit high means it is an input, setting a bit low means it is an output. I assume you can convert bits to a hex number, but a quick example to help you out:

Let’s say we want the first 4 ports of bank A to be inputs and the last 4 ports of bank A to be outputs. In bits this would mean that the first 4 bits are 1 and the last 4 bits are 0. If we write this out (starting with port 7 at the left and port 0 at the right), we get 00001111 which is the value in binary. If we convert this to hexadecimal we get 0x0F (add 1 for the bit at the right, than 2, than 4 and lastly 7. This gives 15 which is F in hexidecimal).

Of couse you can do the same for bank B. Now let’s tell the MCP23017 in pseudo code (the actual code of course differs per hardware platform) that the first 4 ports of bank A are inputs and the last 4 ports of bank A are outputs. We are assuming A0, A1 and A2 are connected to ground so the device address is 0x20.

device_address = 0x20
IODIRA = 0x00
bankAInOutput = 0x0F


Next we want to write to the outputs. We do a similar exercise, except we need a different address and we need to determine which ports to set high. Let’s say that we wish to turn all our outputs high. We need to convert this to a hexadecimal number as well. The first 4 bits (starting at the right) are for input, so we leave these at 0. The last 4 bits we want to set high. In binary we want to write the value 11110000 which corresponds to 0xF0. The address we want to write to is called OLATA and has address 0x14. Assuming we have executed our pseudo code above, we just need to do this to set the 4 outputs high:

OLATA = 0x14
All4OutputsHigh = 0xF0


Next, let’s see what our input is doing. These are the first 4 bits. To read our bank we need address GPIOA which has address 0x12. When we read this address we do not only get our inputs back but also the state of our current outputs, in other words, the status of every port in our bank. In pseudo code this will look something like this:

 GPIOA = 0x12

AllOfBankA = read_from_i2c_bus(device_address,GPIOA)

You will get a byte back which corresponds to 8 bits, the 8 ports. If you get back for example 0xF5 that means your 4 outputs are high (0xF0) which is what you would expect since we just set these and the 4 inputs have status 0x05 which means the first input (GPA0) is high (bit 0 which corresponds to a 1) and GPA2, bit 2 which corresponds to a 4. GPA1 and GPA3 are low in this example. If your are unfamiliar with hexadecimal and bits, there is plenty on the internet to learn this (example link).

Well, this covers the basics. We can determine which ports are input and which are output and we can set a value to each and read the value from them.

In the MCP23017 chip there are 2 banks, A and B and each have seperate addresses to use but remember the device address (in our example 0x20) is the same. To summarize the addresses:

Determine which ports are input and output:
IODIRA (0x00) and IODIRB (0x01)

Set the output ports:
OLATA (0x14) and OLATB (0x15)

To read both output and input ports:
GPIOA (0x12) and GPIOB (0x13)

 This covers the very basics of the MCP23017.

Read the next steps in I2C and MCP23017, part 2

I2C on a Raspberry Pi

I2C on a Raspberry Pi

For one of my projects I needed more in- and outputs than was standard available on the Raspberry so I decided to investigate the I2C interface. There is a lot of information on the internet but getting all the tweaks right caused some issues which I will describe here.

After some research I found you need a MCP23017 I2C Port Expander to use the I2C bus. Basically what the chip does is convert the I2C signal into 16! different input and outputs per chip (yes, you can have more than one of these attached to your Pi).

After ordering the chip through AliExpress I used the following website to get started:

How To Use A MCP23017 I2C Port Expander With The Raspberry Pi

And yes, I managed to copy the example to my own bread board:

Continue reading “I2C on a Raspberry Pi”

First impression

Ok, I couldn’t wait to play with my MetaWear so I installed the iOS app just to see what it can do. It is quite amazing what is in that little device. Some screen shots:



Orientation detection




Led and temperature

Led-Temperature Led Green

On top of that, it also has GPIO’s, I2C and more…..

Now all I need is a use case