Thursday, April 4, 2013

Graphing the inputs of PCF8591

As discussed in previous posts, the PCF8591 provides up to four analog inputs. Displaying these inputs as numbers makes it hard to visualise what is really going on so here I will present some code which can graph the values in real time.

I am still using my demo board from I2C Analog to Digital Converter. (Thanks to Martin X for finding the YL-40 the schematic on the The BrainFyre Blog)
DX pcf8591-8-bit-a-d-d-a-converter-module-150190 YL-40 schematic

It is not clear from the schematic (or looking at the board) but the four inputs are:
  • AIN0 - Jumper P5 - Light Dependent Resistor (LDR)
  • AIN1 - Jumper P4 - Thermistor
  • AIN2 - Not connected
  • AIN3 - Jumper P6 - Potentiometer
It also seems that the board has I2C pull-up resistors which are not required because the Raspberry Pi already has them. This does not appear to cause any problems.

So my plan is to graph the four inputs so I can visualise them responding to changes.

Once again, I don't want to set out to teach C programming but I will be introducing a library called curses (actually ncurses) which makes it easy to display a text interface in a terminal window (virtual terminal).

It will also be able to adjust the analog output value using the + and - keys.

I will only add notes where I am doing something new from the example shown in Programming I2C.

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>

We need a new header file
#include <ncurses.h>

int main( int argc, char **argv )
{
        int i;
        int r;
        int fd;
        unsigned char command[2];
        unsigned char value[4];
        useconds_t delay = 2000;

        char *dev = "/dev/i2c-1";
        int addr = 0x48;

        int j;
        int key;

Here we do some ncurses setup. This will allow us to check for a keyboard key press without having to wait for one to be pressed.
        initscr();
        noecho();
        cbreak();
        nodelay(stdscr, true);
        curs_set(0);


This will print out a message on the screen (at the current cursor location which is the top left)
        printw("PCF8591");


This will print out some labels for our graph bars. The text is printed at the specified location (row, column)
        mvaddstr(10, 0, "Brightness");
        mvaddstr(12, 0, "Temperature");
        mvaddstr(14, 0, "?");
        mvaddstr(16, 0, "Resistor");


We must now call refresh which will cause ncurses to update the screen with our changes
        refresh();
        fd = open(dev, O_RDWR );
        if(fd < 0)
        {
                perror("Opening i2c device node\n");
                return 1;
        }

        r = ioctl(fd, I2C_SLAVE, addr);
        if(r < 0)
        {
                perror("Selecting i2c device\n");
        }

        command[1] = 0;
        while(1)
        {
                for(i = 0; i < 4; i++)
                {
                        command[0] = 0x40 | ((i + 1) & 0x03); // output enable | read input i
                        r = write(fd, &command, 2);
                        usleep(delay);
                        // the read is always one step behind the selected input
                        r = read(fd, &value[i], 1);
                        if(r != 1)
                        {
                                perror("reading i2c device\n");
                        }
                        usleep(delay);

 The full range of the analog value 0 - 255 would not fit on most screens so we scale down by a factor of 4. This should fit on a 80x25 terminal nicely.
                        value[i] = value[i] / 4;


Position the cursor at the start of the bar
                        move(10 + i + i, 12);
For each position in the graph, either draw a * to show the value or a space to remove any * that might be there from a previous value
                        for(j = 0; j < 64; j++)
                        {
                                if(j < value[i])
                                {
                                        addch('*');
                                }
                                else
                                {
                                        addch(' ');
                                }
                        }
                }

                refresh();

 Check the keyboard and process the keypress
                key = getch();
                if(key == 43)
                {
                        command[1]++;
                }
                else if(key == 45)
                {
                        command[1]--;
                }
                else if(key > -1)
                {
                        break;
                }
        }


Shutdown ncurses
        endwin();
        close(fd);
        printf("%d\n", key);
        return(0);
}



To compile this program you need to use a new flag -l which says to link with the spcecified library (ncurses)

gcc -Wall -o pcf8591d-graph pcf8591d-graph.c -lncurses

When you run the program you should see something like this:
PCF8591


Brightness  *****************************************************

Temperature *******************************************************

?           *********************

Resistor    *****************************************


While it is running the graphs should move as you change the inputs. Try for example shining a torch on the LDR or adjusting the Pot.

You can adjust the green LED with + and - (hint, use - to go from 0 to 255 for maximum effect). Any other key will cause the program to quit.

The graph shows nicely how the inputs can change but it also shows how the value can fluctuate without any input changes. I can't explain exactly why but I would expect much of the fluctuation is because of the lack of a stable external clock/oscillator and/or instability in the reference voltage. Needless to say this is a low cost demo board and may not be exploiting the full potential of the PCF8591.

Here is the complete source code (pcf8591d-graph.c):
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>

#include <ncurses.h>

int main( int argc, char **argv )
{
        int i;
        int r;
        int fd;
        unsigned char command[2];
        unsigned char value[4];
        useconds_t delay = 2000;

        char *dev = "/dev/i2c-1";
        int addr = 0x48;

        int j;
        int key;


        initscr();
        noecho();
        cbreak();
        nodelay(stdscr, true);
        curs_set(0);

        printw("PCF8591");

        mvaddstr(10, 0, "Brightness");
        mvaddstr(12, 0, "Temperature");
        mvaddstr(14, 0, "?");
        mvaddstr(16, 0, "Resistor");

        refresh();
        fd = open(dev, O_RDWR );
        if(fd < 0)
        {
                perror("Opening i2c device node\n");
                return 1;
        }

        r = ioctl(fd, I2C_SLAVE, addr);
        if(r < 0)
        {
                perror("Selecting i2c device\n");
        }

        command[1] = 0;
        while(1)
        {
                for(i = 0; i < 4; i++)
                {
                        command[0] = 0x40 | ((i + 1) & 0x03); // output enable | read input i
                        r = write(fd, &command, 2);
                        usleep(delay);
                        // the read is always one step behind the selected input
                        r = read(fd, &value[i], 1);
                        if(r != 1)
                        {
                                perror("reading i2c device\n");
                        }
                        usleep(delay);


                        value[i] = value[i] / 4;
                        move(10 + i + i, 12);

                        for(j = 0; j < 64; j++)
                        {
                                if(j < value[i])
                                {
                                        addch('*');
                                }
                                else
                                {
                                        addch(' ');
                                }
                        }
                }

                refresh();

                key = getch();
                if(key == 43)
                {
                        command[1]++;
                }
                else if(key == 45)
                {
                        command[1]--;
                }
                else if(key > -1)
                {
                        break;
                }
        }


        endwin();
        close(fd);
        printf("%d\n", key);
        return(0);
}


36 comments:

  1. Hi. I've been trying to get the PCF working with my Raspberry Pi. Finally managed, thanks to help from this blog, so thank you!
    Any ideas on how to calibrate the sensors? For instance, I want the temperature in degrees C. I'm not sure on the scale of the onboard sensor.

    Hope you can help!
    --
    Mike

    ReplyDelete
    Replies
    1. Very usefull post, but I am facing the same problem trying to figure out how to calibrate the temperature sensor and getting a conversion degrees C.

      Any tip would be helpful!

      Delete
  2. Your program was very helpful.

    thanks

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I've altered this source to use a PCF8591 I2C board I got from WaveShare connected to an analog light sensor. I've added a "ruler" of sorts at top and increased the screen length to 128 and halved not quartered the value. I also have a working version using wiringPi's pcf8591Setup which I can post if anyone cares as well.

    source:
    https://github.com/linuxgnuru/i2c-analog

    ReplyDelete
  5. Hi, do you have this code for arduino? Thanks.

    ReplyDelete
    Replies
    1. In Arduino it's even easier, in the IDE go to File -> Examples -> 07. Display -> bargraph. The key function is map.

      Delete
  6. Hi,

    I am trying to read AC Mains current with ACS712 Module + PCF8591 Module on Raspberry Pi.

    I measured 0,45A on Arduino but in Raspberry I measured 0,36A. Is it beacuse of PCF8591 or am I doing something wrong?

    Arduino Code: http://henrysbench.capnfatz.com/henrys-bench/acs712-arduino-ac-current-tutorial/

    My circuit : http://tinypic.com/r/1604w7k/9

    ReplyDelete
    Replies
    1. for one thing, the Arduino has a 10 bit analog where as the PCF8591 is only 8 bits; what that means is Arduino = 0 - 1023, PCF8591 = 0 - 255; so the Arduino will give a more precise measurement then the other can.

      Delete
    2. This comment has been removed by the author.

      Delete
    3. Data loss is supposed to be maximum 0.5. But my loss is 0.9.

      Delete
    4. hmm, i'm at a lost. My next step is I'd try the module in the arduino (http://tronixstuff.com/2013/06/17/tutorial-arduino-and-pcf8591-adc-dac-ic/) that should tell you if it's the pcf8591 or the pi

      Delete
    5. Thank you for being interested

      Delete
  7. This comment has been removed by the author.

    ReplyDelete
  8. I analyze the project and need to use Graphing Calculator for the inputs as well. Anyway you don't make reference to the tools that can be utilized in quick raiser reactors, and is right now principally not utilized simply because of the expense of uranium is so low. Additionally, as I do my essay fast to consider Physics aspects, it is that there won't be a solitary vitality stage that will be practical over the whole world.

    ReplyDelete
  9. Your detailed instructions work for my working project https://casinority.com/ae/ really good, I am very grateful to you for your help and blog, I can safely recommend it to my friends.

    ReplyDelete
  10. I personally liked your post because mostfungames you shared a good post. It will help me a lot.

    ReplyDelete
  11. Deadline is approaching and you still don't have your case brief written? There is no need to worry because there is a website which can help. https://mid-terms.com/buy-case-brief/

    ReplyDelete
  12. if you need a great writing assistance with writing your essay or any other paper look here

    ReplyDelete
  13. I know that making writing tas is a challenge. Only tests are more difficult for me. I use a https://millionessays.com/online-test-for-me.html. It's the best service for me.

    ReplyDelete
  14. But how would we go about handling a true analog input? The Raspberry Pi only understands digital inputs, therefore, Open here for more the signal needs to be converted.

    ReplyDelete
  15. But how would we go about handling a true analog input? The Raspberry Pi only understands digital inputs, therefore, the signal needs to be converted.

    jogos do friv online

    ReplyDelete
  16. This comment has been removed by the author.

    ReplyDelete
  17. Thanks for sharing this information with us.
    We have a big family of write my essay today who have a vast understanding of the English language and rules related to it. All of them are holding higher degrees with distinctions in their respective fields. Our highly professional writers are experts in writing styles such as MLA, Harward, APA, etc. They know how to structure an essay or Write my Essay for Me
    research paper writer by following the rules of the English Language.

    ReplyDelete
  18. Thanks for sharing such informative blog. Doctor of Nursing Practice (DNP) project essentially examines how well one understands the field of nursing practice and one’s ability to solve a real world public health problem. Owing to the fact that working on this type of project can be somehow difficult, some scholars opt to order DNP capstone project writing help.

    ReplyDelete
  19. It’s hard to find knowledgeable people on this topic, but you sound like you know what you’re talking about! Thanks

    Player.me
    Information
    Click Here
    Visit Web

    ReplyDelete
  20. Thanks for sharing this information. I enjoy reading this one! Success Leads Digital Marketing

    ReplyDelete