Friday, April 19, 2013

Visual Studio 2010

I am not a fan of Visual Studio. Unfortunately I must use it for some projects. Recently I was forced to upgrade to Windows 7 and Visual Studio 2010. Not wanting to duplicate all my files, I decided to leave them on a network drive and just access them via the network.

Seems like a good idea, after all, why have a network if you store everything locally? Well, it seems that Visual Studio does not like that.

For some settings it will decide to find a suitable local directory for you. For some other settings it leaves you high and dry.

For example, when I try and build my program I get the error:

Error    1    error C1033: cannot open program database '\\server\share\working\project\debug\vc100.pdb'    \\server\share\working\project\stdafx.cpp    1    1    project

The internet was of little use which is why I thought I would put it in my blog.

This goes a long way to explain my criticism of Visual Studio. After installing several gig of software, is that the best error message it can come up with? Well I will try the help, Oh, that is online only. After jumping through some hoops, the help tells me that:

This error can be caused by disk error.

Well, no disks errors here. Perhaps it means that it does not like saving the .pdb on a network share. What is a .pdb anyway???

In the end, my solution (can I call it that or as Visual Studio hijacked that word?) was to save intermediate files locally:
  • Open Project -> Properties...
  • Select Configuration Properties\General
  • Select Intermediate Directory
  • Select <Edit...>
  • Expand Macros>>
  • Edit the value (by double clicking on the macros or just typing in):
$(TEMP)\$(ProjectName)\Debug\
  • Select OK
  • Select OK

And that seems to sort it out.

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);
}