PCF8591 Analog To Digital

In this example we are going to connect to PCF8591 AD/DA Converter.

Raspberry PI has no Analog inputs or outputs, so to talk to analog devices we need to convert the signal.

Updated 6. May 2016 – Added R6 and R7 to get the voltage to safe levels on the I2C bus.

PCF8591

Disclaimer:
We do not take any responsibility for possible errors in the guide or errors that you might do wiring it up. Incorrect wiring can result in damaged sensor or damaged Raspberry PI.

Pieces we use in this example:

  • Raspberry PI 2 (or Raspberry PI 3)
  • Cobbler and Cobbler cable
  • Bread board
  • 2 kΩ resistor  (x4) – You don’t need 4 if you don’t plan to read from all the inputs.
  • 470 Ω resistor
  • 15 kΩ resistor (x2)
  • PCF8591 AD/DA Converter Module Analog To Digital Conversion. (Those are not expensive, I paid $6.20 for 3 piece lot, so thats close to $2.07 a piece)
  • WiringPI needs to be installed on the Raspberry PI -> See here.
  • I2CTools needs to be installed on the Raspberry PI and I2C needs to be enabled in the Raspberry PI settings.

If you are not familiar with breadboards, how to place a cobbler on the breadboard or need to know how to read the color codings on the resistors then click here bellow

   Using breadboard    Resistor color codings


To install the I2CTools you use the following command:

sudo apt-get install i2c-tools

You need to enable the i2c bus in the Raspberry PI settings, which you can do in the user interface if your running Jessie or if your on older system then you can do it in console.

Understanding the PCF8591 AD/DA Converter board:

This board operates at 5V, it has 4 analog inputs, and one analog output. Each analog channel is converted to 8 bit value (0 – 255).

The board comes with the three jumpers on as can be seen on the picture bellow:

PCF8591WithJumpers

On the board there is a light sensor, a temperature sensor and potentiometer. (Why would anyone want that ? I don’t know since those are not high quality sensors). But by default the light sensor is taking analog input 0, the temperature sensor is taking analog input 1 and the potentiometer is wired to analog input 3, leaving only analog input 2 unused. By taking off the jumpers then you can disable those sensors and get all the analog channels for your use.

P4 – Taking off P4 disconnects the temperature sensor and gives you analog channel 1 for your own use.

P5- Taking off P4 disconnects the light sensor and gives you analog channel 0 for your own use.

P6- Taking off P6 disconnects the 10KΩ potentiometer and gives you analog channel 3 for your own use.

We just take them all off for our test, I don’t see good purpose for those sensor on this board anyhow, then the board will look like this with all 4 inputs enabled:

PCF8591

The analog inputs go from 0 to 5 V
The analog output maxed for me at 3,64 V (It’s good to measure to know what your dealing with so you can account for it in your design as cheap circuits are not always as they claim to be).

This means for the inputs then the following applies:

  • 0 V will mean 0 for read value.
  • 5V will mean 255 for read value.

And for the output then the following applies:

  • Value of zero will mean you get 0 V out.
  • Value of 255 will mean you get about 3,64 V out.

Wiring up the board:

PCF8591SchemeView2

We use the 2 kΩ resistors to pull down the analog channels to prevent them from having fluctuating state. If you don’t plan to read all the analog channels then its enough to have pull down resistor on the channels that you want to use.

Note also the two 15 kΩ resistors which we use to pull down the voltage to safe levels for the I2C bus.

Then analog device can be connected to one of the red dots on the scheme above. We will just be connecting 3,3 V from PIN 1 on the Raspberry PI to one of the analog channels. (one of the red dots).

We have put voltage meter on the output so we can watch how to voltage changes depending on how much output we apply on the  analog output channel. We also put a LED there. The LED will not turn on on the lower voltages.

Here is one way to put it on the breadboard. (Try to go by scheme drawing rather than breadboard drawing to avoid mistakes and to gain more understanding)

PCF8591BBView2

GPIO Header

Blue wire we connected to 39 (GND)
Red to 4 (5V)
Green to pin 3 marked as GPIO 2 here above (Its a special GPIO pin that is also called SDA, you cannot choose any other pin for this)
Gray to the pin marked as GPIO 3 here above (Its a special pin that is also called SCL, you cannot choose any other pin for this).

Sanity tests in console:

After you have connected everything and made sure that wiringPI, i2ctools are installed that I2C is enabled in the Raspberry PI settings, then we are ready to do sanity tests in console.

First thing is getting the address of the I2C device, which is in this case the analog converter board.

To do that then we type:

sudo i2cdetect -y 1

This should show our device at 0x48 (if you got exactly same type of device – many other devices also use 0x48)

Test reading the channels (we always read twice since this device will in first read return last value set even if it was on different channel)

We select channel 0, and then read it twice (ignoring the first value).

i2cset -y 1 0x48 0x00
i2cget -y 1 0x48
i2cget -y 1 0x48

We select channel 1, and then read it twice (ignoring the first value).

i2cset -y 1 0x48 0x01
i2cget -y 1 0x48
i2cget -y 1 0x48

We select channel 2, and then read it twice (ignoring the first value).

i2cset -y 1 0x48 0x02
i2cget -y 1 0x48
i2cget -y 1 0x48

We select channel 3, and then read it twice (ignoring the first value).

i2cset -y 1 0x48 0x03
i2cget -y 1 0x48
i2cget -y 1 0x48

Note if your reading same channel over and over again like a stream feed then you don’t need to read twice as previous value will be from same channel making it a valid value.

Now we test writing to the analog output full voltage:

i2cset -y 1 0x48 0x41 0xff

Now we test writing to the analog output zero voltage:

i2cset -y 1 0x48 0x41 0x0

Now we test writing to the analog output half voltage:

i2cset -y 1 0x48 0x41 0x80

Putting it all in Xojo:

We create a module called I2C and add the following functions to it, making their scope as Protected

Protected Function Setup(devID As Int32) As Int32
 #If TargetARM And TargetLinux Then
 Soft Declare Function wpWiringPiI2CSetup Lib "libwiringPi.so" alias "wiringPiI2CSetup" (devID as Integer) as Integer
 Return wpWiringPiI2CSetup(devID)
 #Endif
End Function
 
Protected Function ReadReg8(fd as Integer, reg as Integer) As Integer
 #If TargetARM And TargetLinux Then
 Soft Declare Function wpWiringPiI2CReadReg8 Lib "libwiringPi.so" alias "wiringPiI2CReadReg8" (fd as Integer, reg as Integer) as Integer
 Return wpWiringPiI2CReadReg8(fd, reg)
 #Endif
 
End Function
 
Protected Function WriteReg8(fd as Integer, reg As Int32, data as Integer) As Integer
 #If TargetARM And TargetLinux Then
 Soft Declare Function wpWiringPiI2CWriteReg8 Lib "libwiringPi.so" alias "wiringPiI2CWriteReg8" (fd as Integer, reg as Integer, data as Integer) As Integer
 Return wpWiringPiI2CWriteReg8(fd, reg, data)
 #Endif
 
End Function

Then we put 4 buttons on a window to read channel 0 to 3:

// Button to read channel 0
Sub Action()
 Dim handle as Integer
 Dim value as Integer
 
 handle = I2C.Setup(&h48)
 
 if handle = -1 then
 MsgBox "Failed I2CSetup"
 return
 end if
 
 if handle <> -1 then
 // I am reading twice so I dont get previous value from different channel, if your always reading just same channel in tight loop then its ok to just read once
 // The value in the first reading might have no meaning since the way this board works then when you ask for value then your getting value from previous state.
 value = I2C.ReadReg8(handle,&h0)
 value = I2C.ReadReg8(handle,&h0) // At this point we know for sure that the previous state was &h0 so we get valid value
 
 MsgBox "Raw value: " + Str(value) + EndOfLine + "Voltage: " +Format(value / 255.0 * 5.0,"#.##") + " V"
 end if
End Sub
 
// Button to read channel 1
Sub Action()
 Dim handle as Integer
 Dim value as Integer
 
 handle = I2C.Setup(&h48)
 
 if handle = -1 then
 MsgBox "Failed I2CSetup"
 return
 end if
 
 if handle <> -1 then
 // I am reading twice so I dont get previous value from different channel, if your always reading just same channel in tight loop then its ok to just read once
 // The value in the first reading might have no meaning since the way this board works then when you ask for value then your getting value from previous state.
 value = I2C.ReadReg8(handle,&h1)
 value = I2C.ReadReg8(handle,&h1) // At this point we know for sure that the previous state was &h1 so we get valid value
 
 MsgBox "Raw value: " + Str(value) + EndOfLine + "Voltage: " +Format(value / 255.0 * 5.0,"#.##") + " V"
 end if
End Sub
 
// Button to read channel 2
Sub Action()
 Dim handle as Integer
 Dim value as Integer
 
 handle = I2C.Setup(&h48)
 
 if handle = -1 then
 MsgBox "Failed I2CSetup"
 return
 end if
 
 if handle <> -1 then
 // I am reading twice so I dont get previous value from different channel, if your always reading just same channel in tight loop then its ok to just read once
 // The value in the first reading might have no meaning since the way this board works then when you ask for value then your getting value from previous state.
 value = I2C.ReadReg8(handle,&h2)
 value = I2C.ReadReg8(handle,&h2) // At this point we know for sure that the previous state was &h2 so we get valid value
 
 MsgBox "Raw value: " + Str(value) + EndOfLine + "Voltage: " +Format(value / 255.0 * 5.0,"#.##") + " V"
 end if
End Sub
 
// Button to read channel 3
 Sub Action()
 Dim handle as Integer
 Dim value as Integer
 
 handle = I2C.Setup(&h48)
 
 if handle = -1 then
 MsgBox "Failed I2CSetup"
 return
 end if
 
 if handle <> -1 then
 // I am reading twice so I dont get previous value from different channel, if your always reading just same channel in tight loop then its ok to just read once
 // The value in the first reading might have no meaning since the way this board works then when you ask for value then your getting value from previous state.
 value = I2C.ReadReg8(handle,&h3)
 value = I2C.ReadReg8(handle,&h3) // At this point we know for sure that the previous state was &h3 so we get valid value
 
 MsgBox "Raw value: " + Str(value) + EndOfLine + "Voltage: " +Format(value / 255.0 * 5.0,"#.##") + " V"
 end if
End Sub

Then we put slider on the window, setting its initial value to 0, and min as 0 and max as 255. Here is Open event and ValueChanged events for the slider:

Sub Open()
 Dim handle as Integer
 
 handle = I2C.Setup(&h48)
 
 if handle = -1 then
 MsgBox "Failed I2CSetup"
 return
 end if
 
 if handle <> -1 then
 call I2C.WriteReg8(handle,&h41,0)
 end if
End Sub
 
Sub ValueChanged()
 Dim handle as Integer
 
 handle = I2C.Setup(&h48)
 
 if handle = -1 then
 MsgBox "Failed I2CSetup"
 return
 end if
 
 if handle <> -1 then
 call I2C.WriteReg8(handle,&h41,me.Value)
 end if
End Sub

Downloading the code:

   Download the code


When testing then you can test connecting 3,3 V to the analog inputs and see how it changes.

Having mastered this will enable usage of many analog sensors and devices.


Note that a better way to hook up the PCF8591 is to use I2C logic converter than to use the 15kΩ resistors, like for example:

WithLogicConverter

You can see more about that in our Building I2C logic converter guide.


 

AliExpress.com Product – HoldPeak HP-770D Multimeter

AliExpress.com Product – 1/4W 0.25W 122 kind * 20 PCs =2440 PCs (0.33ohm-4.7m) 5% carbon film resistors assorted kit

AliExpress.com Product – PCF8591 module AD / DA converter module analog to digital / digital to analog conversion

Leave a Reply

Einhugur technical blog that involves Xojo and Einhugur Xojo plugin related topics

%d bloggers like this: