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


Tuesday, March 26, 2013

scp files with spaces in the filename

scp is a great tool. Built to run over ssh it maintains a good unix design. It does however cause a few problems with it's nonchalant handling of file names.

For example, transferring a file which has a space in the name causes problems because of the amount of escaping required to get the space to persist on the remote side of the connection.

Copying files from local to remote is easy enough, just quote or escape the file name using your shell.
scp Test\ File remote:
scp 'Test File' remote:
scp "Test File" remote:

Copying files from remote to local can be more tricky
scp remtote:Test\ File .
scp: Test: No such file or directory
scp: File: No such file or directory


scp stimulus:"Test File" .
scp: Test: No such file or directory
scp: File: No such file or directory


scp "stimulus:Test File" .
scp: Test: No such file or directory
scp: File: No such file or directory


The escaping is working on your local machine but the remote is still splitting the name at the space.

To solve that, we need an extra level of escape, one for the remote server. Remember that your shell will eat the \ so you need a double \\ to send it to the remote
scp remote:Test\\\ File .
Test File              100%    0     0.0KB/s   00:00

You can also combine the remote escape with local quoting
scp remote:"Test\ File" .
or
scp "remote:Test\ File" .

Best solution of all, avoid spaces in file names. If you can't avoid it, I find the easiest solution is to replace ' ' with '\\\ '.
 

Saturday, March 2, 2013

Programming I2C

Although you can perform simple i2c reads and writes using the command line tools i2cget and i2cset, for a more integrated approach you can use a programming language to talk to the bus.

The are dozens of languages which make claims about ease of use and learning etc. and I am sure you can program i2c from them.

What I will demonstrate here is the simple way to do it from c. Although I don't aim to teach how to program in c, I will try and explain what the code is doing so you can follow along even if you are new to c.

This will use some basic i2c read and writes as described at  http://www.kernel.org/doc/Documentation/i2c/dev-interface
We will also need to perform some IO Control (ioctl) which are i2c specific.

First we need some code to get us started. The #include basicly make certain function calls and constants available to the rest of our program.

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


All of our code will live in the main function for now. main() is where all c programs start.

We need some variables which must be declared at the start of the function.
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;


The [ ] syntax means an array so char command[2] is actually a variable which can hold 2 char values.

Some of our variables have been initialised to specific values so they are ready to use. The ones which have not been initialised will contain random values so we must assign a value to them before they can be used.

0x48 means hexadecimal 48 (which is decimal 72).

Next we print out a banner to show that the program is running
printf("PCF8591 Test\n");

The we get down to business and open the i2c device
        fd = open(dev, O_RDWR );
        if(fd < 0)
        {
                perror("Opening i2c device node\n");
                return 1;
        }


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


Now we have an infinite loop
         while(1)
        {

There will be no way to end the program except by pressing Control C.

Next we have another loop which will run four times
                for(i = 0; i < 4; i++)
                {

Then we build a command for the pcf8591. The value of this is specified in the data sheet http://doc.chipfind.ru/pdf/philips/pca8591.pdf

In the first 8 bits of the command we will enable the analog output bit (0x40) and select which of the 4 inputs to read ((i + 1) & 0x03). We do a bitwise or to combine these values together with the | symbol.
command[0] = 0x40 | ((i + 1) & 0x03); // output enable | read input i

The // is the start of a comment so you can explain you code to the reader.

In the next 8 bits we increment the value for the analog output
command[1]++;

Now we are ready to send the command to the i2c bus
r = write(fd, &command, 2);

It is not clear why, but we need to wait for the command to be processed
usleep(delay);

Now we are ready to read a value. Remembering that the read is always one value behind the selected input (hence the +1 we used above).
r = read(fd, &value[i], 1);
if(r != 1)
{
        perror("reading i2c device\n");
}
usleep(delay);


Then we end the loop
        }
and now we can print out our results
        printf("0x%02x 0x%02x 0x%02x 0x%02x\n", value[0], value[1], value[2], value[3]);

end our infinite loop
    }

and although we may never reach here, we will clean up and quit.
  close(fd);
  return(0);
}


Now, if you enter all the code into a file called pcf8591d.c (you can copy the complete code as show below) then you are ready to compile it with this command
gcc -Wall -o pcf8591d pcf8591d.c
This says to compile the .c file and write the output (-o) to pcf8591d (if you don't specify an output file the default of a.out will be used which can be a but confusing). -Wall will make sure all warnings are printed out by the compiler.

Assuming the compile (and link) was successful you are ready to run
./pcf8591d
and you should see output like this:
PCF8591 Test
0x5f 0xd3 0xac 0x80
0xc1 0xd3 0xae 0x80
0xc1 0xd3 0xb0 0x80
0xc1 0xd3 0xb2 0x80
0xc1 0xd3 0xb7 0x80
0xc1 0xd3 0xba 0x80
0xc1 0xd3 0xde 0x80
0xc1 0xd3 0xdc 0x80
0xc1 0xd3 0xe0 0x80

To stop the program press ^c (Control + C).

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



// Written by John Newbigin jnewbigin@chrysocome.net

// Based on info from http://www.kernel.org/doc/Documentation/i2c/dev-interface
// And http://doc.chipfind.ru/pdf/philips/pca8591.pdf

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;

        printf("PCF8591 Test\n");

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

        while(1)
        {
                for(i = 0; i < 4; i++)
                {
                        command[0] = 0x40 | ((i + 1) & 0x03); // output enable | read input i
                        command[1]++;
                        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);
                }
                printf("0x%02x 0x%02x 0x%02x 0x%02x\n", value[0], value[1], value[2], value[3]);
        }

        close(fd);
        return(0);
}

In the next blog I will improve the output to print a graph of the values so you can see them move up and down.


Wednesday, February 27, 2013

PXE boot WINPE

As part of our new Windows 7/AD deployment, SCCM is being used to control the imaging process of desktop computers.

We already have a comprehensive set of PXE enabled boot options so we needed a way to integrate the SCCM tools into our existing PXE setup.

The existing setup is syslinux(pxelinux) 3.11 on CentOS 5, ISC dhcpd and tftp.

We have several Linux live CDs, memtest, novell tools, DOS and chain booting to another (novell) server.

Our Microsoft team provide a bootable CD which we can use to image desktop machines and on some subnets they now provide the ability to PXE boot. Initially I hoped we could just chain boot to the SCCM server but that does not work.

After much research and testing I worked out a process using wimboot. Based on instructions found on http://ipxe.org/howto/sccm and http://forum.ipxe.org/showthread.php?tid=5745 I managed to write a script which converts a CD (ISO image).

The pxelinux entry is:
LABEL ad
com32 ad/linux.c32
append ad/wimboot initrd=ad/winpe.cpio


The files are in a tftp subdirectory called ad

linux.c32 is part of syslinux. I am using version 4.02 which was copied from a CentOS 6 server.

wimboot is available for download from ipxe.

winpe.cpio is a file we are going to create using my script below.

I also required a copy of bootmgr.exe which should be available on your microsoft tftp server but I was unable to find it and in the end I got a copy from our Mircosoft team.

Finally, I needed a copy of wimlib which has a linux version of the imagex tool. I was unable to find a RPM of this package and I just built it from source using the --without-ntfs-3g. (The source comes with .spec files so an RPM should be easy to build).

So here is my script:
convert_cd_into_pxe.sh:
#!/bin/bash
# Written by John Newbigin jnewbigin@chrysocome.net
ISO=x86_PROD_MS.iso

MNTPNT=wim
SCCMFILES=iso

IMAGEX=./wimlib-1.2.5/imagex

unalias cp

if [ "$(whoami)" = "root" ] ; then

   if [ ! -f bootmgr.exe ] ; then
      echo "You need a copy of bootmgr.exe"
      exit 1
   fi
   umount $SCCMFILES
   mkdir $SCCMFILES
   mkdir $MNTPNT
   mount -o loop $ISO $SCCMFILES

   cp $SCCMFILES/boot/bcd BCD
   cp $SCCMFILES/boot/boot.sdi .
   cp $SCCMFILES/sources/boot.wim .


   $IMAGEX mountrw boot.wim 1 $MNTPNT

   cp -drv $SCCMFILES/sms/* $MNTPNT/sms/
   umount $SCCMFILES
   rmdir $SCCMFILES

   ARCH=$(grep TsBootShell.exe $MNTPNT/Windows/System32/winpeshl.ini | cut -d \\ -f 4)
   # edit winpeshl.ini
   perl -pi -e "s|.*$ARCH.*|\"wscript.exe\",\"%SYSTEMDRIVE%\\\\sms\\\\bin\\\\$ARCH\\\\bootstrap.vbs\"|" $MNTPNT/Windows/System32/winpeshl.ini

   # install bootstrap.vbs
   cat > $MNTPNT/sms/bin/$ARCH/bootstrap.vbs << END
Set os = WScript.CreateObject ( "WScript.Shell" )
os.Run "%COMSPEC%", 7, false
os.Run "%COMSPEC% /c title Initialising... && wpeinit " & _
"&& net start dnscache", 1, true
os.Run WScript.ScriptFullName & "\..\TsmBootStrap.exe /env:WinPE " & _
"/configpath:%SYSTEMDRIVE%\sms\data", 1, true
END

   $IMAGEX unmount $MNTPNT --commit
   rmdir $MNTPNT

   ls BCD bootmgr.exe boot.sdi boot.wim | cpio --create -H newc > winpe.cpio
   rm BCD boot.sdi boot.wim
  
else
   echo "You must be root to run this script"
   exit 1
fi


I hope this is of use to someone.

Saturday, February 23, 2013

Puppet tip

I have a custom puppet type called iptables. Not surprisingly it is used to build iptables rules.

Recently I was setting up a router which required MASQUERADING a few virtual network interfaces. I found that the module was lacking the ability to negate a match. For example I wanted a rule:
-A FORWARD ! -s 192.168.1.0/24 -i vnet -j DROP

It seemed like a simple task to add in the ability to insert the !. After all it is mostly just string manipulation.

My puppet setup already has pluginsync enabled and working so I though deploying my changes would be easy too.

I managed to make my changes (even though ruby is quite a foreign language to me). When I ran puppet I could see the changes being pulled down to the client but alas, it did not work correctly.

I checked with several web pages which try to explain how easy it is to add new properties and everything I was doing was correct.

After wasting hours tinkering in the ruby code I had the idea to restart the puppet master and bang, it started working.

Seems so obvious in hindsight.

Monday, February 4, 2013

udev tip

From time to time I have to write some udev rules but I always struggle to work out which magic strings are required.

It does not help that every version of udev seems to change everything.

I have found a command for modern versions which helps work out what is required:

udevadm info -e

From there you can find out everything there is available to identify your devices.

The udev 'rule' is then a series of commands separated by a comma (,). You can test for equality with == and set properties with either = or :=. When you use := your value can not be overridden by another rule. For example:

SUBSYSTEM=="video4linux", MODE:="0666"

Will match all video devices and set the permissions to be world read write.

If you save that into a file called /etc/udev/rules.d/99-v4l2.rules
you can then apply it with the command:
udevadm trigger


There is much more which can be done but that is enough to get started. The names of the keys which can be set is actually listed in the man page man 7 udev but it be quite confusing to follow without examples.

Next time I have to work on flashcache I will try and blog about the new GOTO style rules :-(