Saturday, December 22, 2012

Analog Interface

The capability to read analog inputs is a feature that is greatly missed on the Raspberry Pi, but I agree with the decision to omit this capability in order to keep the price down.  Besides, if they did include an analog interface, many would complain that it isn't adequate for their purpose.  How many input channels do you need?  What resolution? 8 bit, 10 bit, 12 bit 16 bit?  What throughput rate?

Fortunately, there are many analog input chips that use the SPI or I2C bus, making it almost trivial to add analog inputs to the Pi.  I chose the MCP3008, an 8 channel 10 bit ADC available from Adafruit.com.  Add a bi-directional logic level converter and some connectors and you're ready to go.

The level converter is on my main interface board which provides two SPI bus connectors.  The analog interface is a simple board which includes a connector for SPI, the MCP3008 chip, a jumper to choose the analog reference, and screw terminals for the inputs.  I ended up adding several more screw terminals connected to 5V in order to power thermistors. 

The SPI serial bus is full duplex, but the way it works may seems odd to a programmers point of view.  (It makes perfect sense if you understand how the hardware works.)  You may be familiar with how full duplex works on an RS-232 line: data can be sent and received at the same time, but independently.  That independence is due to the fact that RS-232 is an asynchronous protocol.  SPI is a synchronous protocol; meaning everything is driven by a clock pulse.
Data bits will be sent out the MISO line on each cycle of the CLOCK line.  At the same time data bits are being read in on the MOSI line.  The number of bits out is the number of bits you will read back in.  This means that you may have to write more bits than expected for a given command and you may read bits that are unused.

Fortunately, we don't have to worry much about the ugly details at the lower level.  There is a device driver for SPI that is included with the recent versions of Raspbian and the WiringPi library provides support for SPI I/O.  The functions wiringPiSPISetup and  wiringPiSPIDataRW are all that is needed.  Here is the source code for a program that I used when testing and calibrating sensors.


#include <stdio.h> #include <stdint.h> #include <wiringPi.h> #include <gertboard.h> // read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7) int readadc(adcnum) { uint8_t buff[3]; int adc; if ((adcnum > 7) || (adcnum < 0)) return -1; buff[0] = 1; buff[1] = (8+adcnum)<<4; buff[2] = 0; wiringPiSPIDataRW(0, buff, 3); adc = ((buff[1]&3) << 8) + buff[2]; return adc; } int main(int argc, char *argv[]) { int i, chan; uint32_t x1, tot ; printf ("SPI test program\n") ; // initialize the WiringPi API if (wiringPiSPISetup (0, 1000000) < 0) return -1 ; // get the channel to read, default to 0 if (argc>1) chan = atoi(argv[1]); else chan = 0; // run until killed with Ctrl-C while (1) { tot = 0; for (i=0; i<100; i++) { // read data and add to total x1 = readadc(chan); tot += x1; delay(10); } // display the average value printf("chan %d: %d \n", chan, (tot/100)) ; } return 0 ; }

Note: The SPI device is not loaded by default.  The easy way to get it loaded is to used the "gpio" utility that comes with the WiringPi library.  Just enter
      gpio load spi
and the drivers will be loaded and ready to use.

Tuesday, December 4, 2012

Dual power supplies is the way to go

You may notice in the picture of my setup (in the previous post) that the Raspberry Pi is being powered normally via the USB power connector.  This was done because the Pi was failing to boot when powered through the GPIO port as I had planned.  While I did not do any tests, the obvious cause is a lack of sufficient current to power the Pi.  It really does want to have a solid 700mA of 5 volt juice.  My planned configuration worked fine on the workbench but failed once I mounted it on the wall and connected the alarm sensors.  The sensors require 12V power and I was using the same power supply to drive the sensors and the power adapter for the interface board.  That 12V power supply was only 2A and that was apparently too little for all of this.

I could have gotten a larger 12V power supply, but I already have several of these 2A supplies. Realization finally struck me that using a single power supply would be a mistake.  If any of the sensor lines was compromised, shorting the power to ground, it would cause the Pi to shut down suddenly.  So now I have one supply just for the alarm sensors and another that drives the interface board, and through that, the Pi.

The system is up and has been running for over a week with no major problems.  I spent several hours improving my code for driving the X10 interface to make it as reliable as possible.  It seems to work as well as it ever has when using heyu or ActiveHome to drive it.  X10 is inherently unreliable, but is usually good enough for casual use.  If you have a fairly new house that is wired properly, then it can work fairly well.  My house, unfortunately, is old and poorly wired.  I have made some improvements over the years, but there are still areas of my house that the X10 signals simply will not reach.  I will cover X10 in more detail in an future post.