Quantcast
Channel: The Raspberry Pi Hobbyist
Viewing all 60 articles
Browse latest View live

My Pi Has Arrived

$
0
0

It's Chrismas in July!  My Raspberry Pi has finally arrived.  Setup was pretty easy:

  • Standard cell phone charger (micro USB) at least 700mA
  • either an HDMI or composite video connection
  • USB keyboard and mouse
  • Cat5 cable run to my network switch
  • Compact Flash card, 4GB, loaded with Raspbian “wheezy” OS
The system booted up just as expected.  I logged on and entered "startx" to begin the graphical interface.  Gave it a test drive.  The processing power is on the low side but still pretty good.  A powerful graphics chip makes 1080p HD video possible.  I am impressed.

Ran the configuration program
sudo Raspi-config 
set timezone and enable SSH

Set the IP address to static (default is automatic)
sudo vi /etc/network/interfaces
and change the eth0 section to
iface eth0 inet static
address 192.168.0.6
netmask 255.255.255.0
gateway 192.168.0.1
Then restart eth0 to take the change
sudo ifdown eth0
sudo ifup eth0

Create a user for myself and set the password
adduser ted
passwd ted

Change the default "pi" user's password while I'm at it.
passwd

Update the package manager and install Samba to create windows shares.
apt-get update
apt-get install samba
apt-get install samba-common
Configured and created the /pihome network share (google "Samba Howto")
vi /etc/sambe/smb.conf 

I am using my preferred development environment on my Windows system and access the files via the network share.  The command line via SSH is used to build it.

I installed the WiringPi library and got the GPIO part working.

My next post will describe the hardware interface I an building - serial port, 8 inputs, 6 relay outputs.


Raspberry Pi Serial Port

$
0
0
Prototyping the serial port interface.
Many of the GPIO pins on the Pi have other special uses.  The most useful of these are the serial port pins #8 and #10, which are transmit and receive for an RS-232 serial port.  By default this port will output diagnostic messages during boot and then provide a user login.  The configuration is 8 bits, no parity, 1 stop bit, no hardware handshaking, at 115200 baud.  The device name is /dev/ttyAMA0.

I need to use this serial port to interface to my X10 system via a CM11A computer interface module.  That can be a topic for several future posts.

First lets cover some important facts about RS-232 and voltage levels.  The Pi uses levels that are 0V to represent a zero and 3.3V to represent a one.  RS-232 uses -3V to -15V to represent a zero and 3V to 15V to represent a one.  Thus, a level converter is required to create this interface.  The MAX232 series of chips was designed for this exact purpose.

External capacitors are needed to drive the charge pumps inside the chip.  Note: There are several variations of the MAX232 chip which have different requirements.  The one shown in the circuit here uses 0.1uF capacitors.  The ones I have use 1.0uF and some versions even have the capacitors built in.  When in doubt, check the datasheet for the chart that shows the requirements for each variation.
Data Sheet for MAX232 family

If, like me, you want to take complete control of the serial port for your own uses, there are two configuration changes to make:

First, disable the boot up and diagnostic output to the serial port.
sudo vi /boot/cmdline.txt
and remove the two options referring to the serial port.
So, this
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
becomes this
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

Second, disable the login prompt
sudo vi /etc/inittab
find the line near the end
T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
and delete it or comment it out by putting a # at the start of the line.

Reboot and the serial port will now be free for your exclusive use.

Always Sage Advice - Use Protection

$
0
0
If you read any about interfacing to the Pi GPIO pins you are bound to come across the dire warnings - BeCareful. These pins connect directly to the microprocessor without any protection.  You can destroy the Universe if you wire something wrong.

OK.  Maybe not that dire, but you get the idea.  You may get away with interfacing directly with the GPIO pins (I know I have) but even if your circuit is designed perfectly, accidents still happen.  Something can fall across the circuit board and short things out.  So it is wise to protect your Pi.

I am building an interface that will connect to hard-wired alarm circuitry.  That means lots of lines running lots of places and just that many more opportunities for bad things to happen.  This protection is also a good idea since a lot of people using the Pi will likely be from the younger and less experienced crowd.  This is what the Pi was developed to encourage, so having a safe way to interface to the most flexible I/O on the device is critical.

Here are some of the options for protecting a logic circuit.

Zener Diode - A Zener diode is one that allows no reverse current to flow until a threshold is reached.  Above that threshold, current will flow.  A 3.3V Zener diode between a GPIO pin and ground can protect it from any over voltage that is applied.  Any voltage above 3.3V will just be shunted to ground.

Mike Cooke has provided a design for a screw terminal break-out board for the Raspberry Pi GPIO that uses Zener diodes for protection.  The design may be found here: Raspberry Pi Breakout Box

Transistor - A transistor can be used to switch a known safe logic level.

Line Driver or Buffer - Chips that contain multiple transistor switches internally.  These are easier to install and provide a cleaner design when you have many lines to protect.

Opto-Isolators - Chips similar to Buffers but these use pairs of internal LEDs and optical sensors instead of transistors.  This provides total circuit isolation and can be useful in a noisy electrical environment.


A Bi-directional Logic Level Converter seemed like the perfect thing to use. However, I tried the TXB0108 provided by Adafruit and had problems.  It seemed to work fine in my prototyping board, but when I put things together, I found that it had problems driving some TTL chips.  I checked it with a multimeter and found it only put out 2.5 volts when asserted on the 5V side.  This wasn't enough for an input into the MAX232N that is used for the RS-232 interface.

I am interested in hearing from anyone else who has tried this chip.

The GPIO pins that are used for output on my interface are protected by transistors which control relays.  That is more than adequate protection.  I am still pondering what route to take now for protecting the inputs. Opto-isolators are something I am already familiar with (and probably already have some) so I am leaning toward that option.

Eventually, I will build a new version of my interface so that I can apply all that I have learned along the way. I already regret not leaving pins open for I2C.  That will have go in the next revision.

GPIO Input Circuit

$
0
0
After holidays and many other distractions, I am finally able to get back to work on my Raspberry Pi interface.

Here is the GPIO input circuit that I came up with using an opto-coupler for protection. The opto-coupler that I chose is the LTV-847 (Jameco part number 878286) which provides 4 opto-couplers in a 16-pin DIP format.

Power applied to the anode and cathode will cause the internal LED to emit light.  This is detected by the internal photocell which controls the output.  Because there is no electrical connection between the input and output sides, opto-couplers are handy for connecting between very different voltage levels.  They are also excellent at preventing the introduction of electrical noise into a system.  For this application, the isolation will provide electrical protection to the Raspberry Pi.

The complete circuit for using this is shown below.  The 1KΩ resistor on the input is for limiting the current that can flow through the LED.  The 10KΩ pull-up resistor is internal to the Raspberry  Pi.  Be sure to set the pull-up option when you set the pin to input mode.  Using a separate 5V power supply for the interface provides greater protection than powering this all from the 5V line on the GPIO header.  If that line gets shorted to ground, or even if it just draws too much current, it can cause the Pi to suddenly reboot.


When the input is open, no current will flow through the detector and the Raspberry Pi will see the pin hi due to the pull-up resistor.  When the the input is connected to ground, current will flow and the Pi will see the the pin  as low, since it is effectively connected to ground now.

GPIO Output Relay Interface

$
0
0
Note:  See my revised relay circuit here.

To allow the greatest flexibility, my outputs are all relays.  This allows me to switch a variety of voltages and provides protection to the GPIO pins of the Raspberry Pi.  A relay is just a switch that is controlled by an electromagnetic coil.  Powering the coil will make the switch turn on.  The relays I used are made to mount on a circuit board and can be driven by 5V.  They can easily switch 12V or more at a moderate current level.  They are NOT meant to control house current!  That can be the topic of another post.

The Pi GPIO pins will only output 3.3V at a few milliamps.  This is not enough to drive the relay directly, but is is enough to switch a transistor on and off.  A common NPN switching transistor handles that job nicely.  I used the 2N2222 which is highly available (i.e. even Radio Shack carries it.)


Note:  NO SPST is a switch type designation and means Normally Open, Single Pole Single Throw.  This is the simplest type of switch.

A 1K ohm current limiting resistor is attached to the base of the transistor.  Power is connected to the relay coil which is then connected to the collector of the transistor.  The emitter is connected to ground.  When the GPIO pin is low, no current will flow from the collector to the emitter and the relay will be off.  Setting the GPIO pin to high will "turn on" the transistor, power will flow and the relay will turn on.

Notice that there is a diode attached across the coil of the relay in a reversed orientation.  There is magnetic energy stored in the coil while it is energized and holding the relay closed.  When power is removed, the collapsing magnetic field in the coil causes a brief but powerful surge of reverse voltage which can damage the switching transistor and cause premature failure.  The diode is there to stop this reverse voltage and protect the transistor.  I prefer to use relays that have internal surge suppression diodes and they are becoming more common now.  If your relay does not have the diode internally, then it is highly recommended that one be added.

So Many Inputs, So Few GPIO Pins

$
0
0
I keep coming up with so many improvements to my interface design (I'm up to version 6 now) that I'm not getting much built.  I decided that I had to make the pins for the SPI and I2C buses available.  But that means using five GPIO pins that I had other plans for.  I have to have at least eight inputs to handle the motion detectors and door sensors for the alarm system.  At least four relay outputs would be nice too, so the Raspberry Pi is quickly running out of GPIO pins.
Time to get a little help from another useful IC - the multiplexer.   There are several possible choices, but the obvious one is the 74151, of which I conveniently happen to have a few.

This diagram shows how this needs to be connected.  GPIO pins 1, 2, and 3 are set to output mode and used as address lines to select which input to read.  GPIO pin 0 is set to input mode and connected to the output of the multiplexer.

(Note: I always refer to WiringPi pin numbers, not the standard  BCM numbering.)

The result is that the interface can still have 8 inputs, but only use 4 GPIO pins to do it.

I will probably do the same thing (in reverse) to allow two GPIO pins to provide four select lines for the SPI bus.  It may be a while until I do that since the SPI bus is for future expansion.  I don't have any devices for it yet, just ideas.

Great Article on Designing the RasPi

Revision to Relay Circuit

$
0
0
I have used the relay circuit from my earlier post many times and I know that it works.  However, I have seen that circuit drawn with a 1KΩ resistor and also with a 10KΩ resistor and started to wonder why.   Since this blog is intended to help new hobbyist learn, I thought it would be good to share what I found out.  (Warning:  There is math involved )

After some thought and further research I made the following changes to the GPIO relay control circuit.  The current limiting resistor is now 10K and I will explain why in a moment.  Also, a 100KΩ resistor has been added.  This is recommended to ensure that the transistor turns off if the input is left open.  In my design, the input is always either high or low and is never open, so I am leaving this out of my interface.
The current limiting resistor must be sized to allow enough current to saturate the base of the transistor to guarantee that it switches.  Current too far above this level could damage the transistor.  Every transistor has an inherent property known as the common emitter current gain, commonly referred to as HFE.  The proper size for the resistor may be calculated using the following formula.
R = Supply Voltage / ( max A / HFE * 1.3 )
The supply voltage (from the GPIO pin) is 3.3V.  The HFE for a 2N2222 transistor is assumed to be at least 100.  The current draw on the relays that I use ranges between 30 and 50mA.  Plugging in the numbers for 30mA gives a result of R = 8461.  If 50mA is used, then R = 5076.  However, the HFE is very likely higher.  If we assume that it is 150, then the resistor value at 30mA becomes 12692.  This shows that a 10KΩ resistor is probably more appropriate for this configuration.

The bottom line is that any resistor between 1K and 10K should work OK.  I just felt that this issue should be addressed before any arguments start.


Folder Paper Case

$
0
0
I saw a post on the RasPi forum today about a new folder paper case and I thought I would try it out since my RasPi number 4 needs a home.

http://www.iammer.com/raspi/case.html

I printed it on heavy card stock and on regular paper.  The regular paper was used for practice.  Good idea since I found that I folded it upside down.  Fold it with the printed side down.  I used scissors, an exacto knife and an old piece of wood as a cutting board.  Here is how it went.

All it takes - printed card stock, scissors, and a knife.

After being cut out.

Make good creases on all the folds.

The result - Very functional and surprisingly study little box.

The connectors and SD card hold the RasPi tightly in place.
I like the result.  Especially for a case that is basically free (assuming you can get a sheet of heavy card stock.)  The plain white looks really boring, but it should be pretty easy to add whatever design you like.


Flip the case over and there is lots of space to express your inner Pi.  OK, so I'm not Picasso.

Source Code - Installment One

$
0
0
Edit 15-Oct-2012:  Posted corrected code - missed a few typos. GpioPoller.c is now the multiplexed version. Edit to make HTML behave nicely.

I have been looking at this source code issue from the wrong perspective.  I knew that I would be posting my source code here eventually, but I didn't think that it would be useful to that many people.  That was when tunnel vision had me thinking of just this alarm system project.

The example code that I present here is really much more widely applicable.  This is my main function, which implements a daemon process in C.  Also included are my data structures and my method for using worker threads.  Copious comments have been added to help clarify things.

RPiHouse.h   data structures, function prototypes, and global variables

/*--------------------------------------------------------------------------- RPiHouse.h - include file for the Raspberry Pi re-write of controld 08-Aug-2012 Ted Hale add enums and new dev struct 08-Oct-2012 Cleanup and comments for release of source ---------------------------------------------------------------------------*/ #define PIDFILE "/var/run/RPiHouse.pid" #define CONFIGFILE "/pihome/RPiHouse.conf" #define DEVICEFILE "/pihome/devices.conf" #define MAXDEVICES 100 #define MYPORT 17100 #define MAXCONNECTIONS 10 #define BYTE unsigned char //#include "mysql.h" //#include "mysqld_error.h" // causes Global variables to be defined in the main // and referenced as extern in all the other source files #ifndef EXTERN #define EXTERN extern #endif // device types typedef enum { X10, Gout, Gin } DevType; // device categories typedef enum { Light, OutdoorLight, MotionSensor, DoorSensor, Other } DevCategory; // the device structure typedef struct { char *name; DevType type; BYTE addr; BYTE house; DevCategory category; char *oncmd; char *offcmd; int stat; time_t tOn; time_t tOff; } Device; // "at" commands structure. this is used in a linked list typedef struct { char *cmd; // command to perform time_t time; // when to perform it int period; // -1: one time, else seconds to add for next time void *next; // next in queue } At_qEntry; // prototype definitions for the worker threads void *ListenerThread(void *param); void *GpioPoller(void *param); void *X10Thread(void *param); void *LogicThread(void *param); // some other prototype definitions int DoCommands(char *Cmd); int LogToClients(char *format, ... ); int AtCmd(char *cmd); void X10TurnOn(char *dev); void X10TurnOff(char *dev); void DoTurnOnOff(char *name, int onoff); char *CatName(int n); char *TypeName(int n); // GLOBAL variables. A lock needs to be used to prevent any // simultaneous access from multiple threads EXTERN int kicked; // flag for shutdown or restart EXTERN int nDevices; // number of devices defined EXTERN Device dev[MAXDEVICES]; // the array of devices ///EXTERN MYSQL *conn; // the DB connection EXTERN int logsock[MAXCONNECTIONS]; // log listeners EXTERN time_t Sunrise; // time of sunrise for today EXTERN time_t Sunset; // time of sunset for today

main.c   The entry point for the program

/*--------------------------------------------------------------------------- main.c By Ted B. Hale part of the home alarm and automation system previously known as "control" renamed to RPiHouse for this rebuild on the Raspberry Pi This file implements the main for a daemon process 01-Nov-2009 Starting over from scratch (mostly) on Linux 23-Jul-2010 on VersaLogic Jaguar embedded system now disabled weather thread 03-Aug-2012 re-write for Raspberry Pi 08-Oct-2012 Cleanup and comments for release of source ---------------------------------------------------------------------------*/ #include <errno.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <sys/timeb.h> #include <pthread.h> // database not used yet //#define dbhost "localhost" //#define dbuser "control" //#define dbpass "secret" //#define dbdatabase "control" // this defines the pre-processor variable EXTERN to be nothing // it results in the variables in RPiHouse.h being defined only here #define EXTERN #include "RPiHouse.h" //************************************************************************ // reads the device file defined in RpiHouse.h andsets up the dev table int ReadDevices() { FILE *f; char line[200]; char *p; int i, n; // open the device config file f = fopen(DEVICEFILE,"r"); if (!f) { Log("Failed to open device file [%s]\n",CONFIGFILE); return 0; } // read lines from the file. It is structured like a windows ini file // where the device names are the section names ( enclosed in [] ) nDevices = 0; n = -1; while (read_line(f,line)>=0) { //Log("READCONFIG: %s",line); // these are all lines to ignore - comments and lines that are blank if ((line[0]==';')||(line[0]=='#')||(line[0]==' ')||(line[0]==0)) continue; // is this a device name if (line[0]=='[') { n++; nDevices = n+1; // is the table full if (nDevices>=MAXDEVICES) { Log(" ***** Out of devices *****"); break; } // add a new device to the table dev[n].name = strdup(line+1); p = strchr(dev[n].name,']'); if (p) *p = 0; dev[n].stat = 0; dev[n].tOn = 0; dev[n].tOff = 0; continue; } // ignore everything until a device is defined if (nDevices==0) continue; // parse out "variable=value" // get pointer to = p = strchr(line,'='); // if no =, then skip if (!p) continue; // this will put a null terminator after the variable name *p=0; // bump p by 1 to have it point at the value p++; // the line may have a newline or other character on the end // this will fix that if (p[strlen(p)-1]<' ') p[strlen(p)-1] = 0; // set specified variable // device type if (!strcmp(line,"type")) { if (!strcasecmp (p,"X10")) { dev[n].type = X10; } else if (!strcasecmp (p,"Gout")) { dev[n].type = Gout; } else if (!strcasecmp (p,"Gin")) { dev[n].type = Gin; } } // device address if (!strcmp(line,"addr")) { dev[n].addr = atoi(p); } // house code part of address for X10 devices if (!strcmp(line,"house")) { dev[n].house = *p-'A'; } // device category if (!strcmp(line,"category")) { if (!strcasecmp (p,"Light")) { dev[n].category = Light; } else if (!strcasecmp (p,"MotionSensor")) { dev[n].category = MotionSensor; } else if (!strcasecmp (p,"DoorSensor")) { dev[n].category = DoorSensor; } else { dev[n].category = Other; } } // execute this command when the device turns on // mostly useful for GPIO inputs but can be used for X10 too if (!strcmp(line,"on")) { dev[n].oncmd = strdup(p); } // same for when it turns off if (!strcmp(line,"off")) { dev[n].offcmd = strdup(p); } } ///Log("Done reading devices"); // close the config file fclose(f); // output a table of the devices to the log file for (i=0; i<nDevices; i++) { Log(" %-20s %-15s %-5s address: %2d %2d", dev[i].name, CatName(dev[i].category), TypeName(dev[i].type), dev[i].addr, dev[i].house); } return 0; } //************************************************************************ // handles signals to restart or shutdown void sig_handler(int signo) { switch (signo) { case SIGPWR: break; case SIGHUP: // do a restart Log("SIG restart\n"); LogToClients("SIG restart"); kicked = 1; break; case SIGINT: case SIGTERM: // do a clean exit Log("SIG exit\n"); LogToClients("SIG exit"); kicked = 2; break; } } //************************************************************************ // and finally, the main program // a cmd line parameter of "f" will cause it to run in the foreground // instead of as a daemon int main(int argc, char *argv[]) { pid_t pid; FILE *f; pthread_t tid1,tid2,tid3,tid4,tid5; // thread IDs struct tm *today; // check cmd line param if ((argc==1) || strncmp(argv[1],"f",1)) { //printf("going to daemon mode\n"); // Spawn off a child, then kill the parent. // child will then have no controlling terminals, // and will become adopted by the init proccess. if ((pid = fork()) < 0) { perror("Error forking process "); exit (-1); } else if (pid != 0) { exit (0); // parent process goes bye bye } // The child process continues from here setsid(); // Become session leader; } // trap some signals signal(SIGTERM, sig_handler); signal(SIGINT, sig_handler); signal(SIGPWR, sig_handler); signal(SIGHUP, sig_handler); // save the pid in a file pid = getpid(); f = fopen(PIDFILE,"w"); if (f) { fprintf(f,"%d",pid); fclose(f); } // open the debug log LogOpen("/pihome/logs/RPiHouse"); // database not added back yet // init MySQL interface /* conn = mysql_init(NULL); if (conn == NULL) { Log("mysql_init Error %u: %s\n", mysql_errno(conn), mysql_error(conn)); } else { if (mysql_real_connect(conn, dbhost, dbuser, dbpass, dbdatabase, 0, NULL, 0) == NULL) { Log("Error %u: %s\n", mysql_errno(conn), mysql_error(conn)); mysql_close(conn); conn = NULL; } }*/ // start the main loop do { LogToClients("STARTING"); // read config info ReadDevices(); // start the various threads tid1 = tid2 = tid3 = tid4 = tid5 = 0; pthread_create(&tid1, NULL, GpioPoller, NULL); pthread_create(&tid2, NULL, X10Thread, NULL); pthread_create(&tid3, NULL, LogicThread, NULL); pthread_create(&tid4, NULL, ListenerThread, NULL); ///pthread_create(&tid5, NULL, MiscThread, NULL); // wait for signal to restart or exit do { sleep(1); } while (!kicked); // wait for running threads to stop if (tid1!=0) pthread_join(tid1, NULL); if (tid2!=0) pthread_join(tid2, NULL); if (tid3!=0) pthread_join(tid3, NULL); if (tid4!=0) pthread_join(tid4, NULL); if (tid5!=0) pthread_join(tid5, NULL); // exit? if (kicked==2) break; // else restart, set flag back to 0 kicked = 0; } while (1); // forever // delete the PID file unlink(PIDFILE); return 0; }


GpioPoller.c   The GPIO polling thread

/*--------------------------------------------------------------------------- GpioPoller.c Poll the GPIO devices via the wiringPi interface Ted Hale 08-Aug-2012 initial version for Raspberry Pi re-write of controld 08-Oct-2012 Cleanup and comments for release of source 14-Oct-2012 modify for muxed input ---------------------------------------------------------------------------*/ #include <errno.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <sys/timeb.h> #include <pthread.h> #include <wiringPi.h> #include "RPiHouse.h" // Thread entry point, param is not used void *GpioPoller(void *param) { int pin, i, x, a0, a1, a2; // initialize the WireingPi interface Log("GpioPoller: init wiringPi"); if (wiringPiSetup () == -1) { Log("Error on wiringPiSetup. GpioPoller thread quitting."); return; } Log("GpioPoller: init devices"); // initialize input circuit // muxed input - 0 is input 1-3 are address bits pinMode (0, INPUT); pullUpDnControl(0,PUD_UP); for (i=1; i<4; i++) { pinMode (i, OUTPUT); digitalWrite(i, 0); } // relay outputs are 4-7 for (i=4; i<8; i++) { pinMode (i, OUTPUT); digitalWrite(i, 0); } // start polling loop do { for (i = 0 ; i < nDevices ; i++) { switch (dev[i].type) { case Gin: // set mux address a0 = ((dev[i].addr & 1)==1)?1:0; a1 = ((dev[i].addr & 2)==2)?1:0; a2 = ((dev[i].addr & 4)==4)?1:0; digitalWrite(1, a0); digitalWrite(2, a1); digitalWrite(3, a2); // mux needs a tiny amount of time for the value to settle Sleep(0); // input is pulled high, so 1 is off and 0 (shorted to ground) is on x = digitalRead(0); // if on if (x==0) { // and it wasn't already on if (!dev[i].stat) { Log("GpioPoller> %s ON\n",dev[i].name); LogToClients("%s ON",dev[i].name); /////DoCommands(dev[i].oncmd); } time(&dev[i].tOn); dev[i].stat=1; } else { // if Off // and it wasn't already off if (dev[i].stat) { Log("micropoller> %s OFF\n",dev[i].name); LogToClients("%s OFF",dev[i].name); /////DoCommands(dev[i].offcmd); } time(&dev[i].tOff); dev[i].stat=0; } // Save to DB // ??? this should be only when the value changes !!! /*if (conn!=NULL) { sprintf(sql,"insert into data (var,val) VALUES ('%s',%d)",dev[i].name,dev[i].stat); if (mysql_query(conn, sql)) { Log("mysql_query Error sql: %s\n errno = %u: %s", sql, mysql_errno(conn), mysql_error(conn)); } }*/ break; case Gout: // do nothing break; default: // other modes not yet supported break; } } // let other thread run, sleep 10ms Sleep(10); } while (kicked==0); // exit loop if flag set }

Interface Nearly Complete

$
0
0
After far too many revisions, the GPIO interface for the Raspberry Pi alarm system is nearly done.  It has:

• 8   Digital Inputs
• 4   Relays
• 1   RS-232 serial port
• 2   SPI bus connectors
• 1   I2C bus connector (eventually)

I still need to to finish wiring the SPI connectors, but the serial port, the digital inputs, and the relays are all working.  I will probably go ahead and add connectors for the I2C bus pins for future use.  I already have an eight port A/D converter for the SPI bus and more GPIO would also be easy to add via SPI.

I added a jumper block near the GPIO connector that allows me to connect (or not) 5V on the interface to 5V on the RasPi.  This would allow me to power the board from the RasPi or to backfeed power to the RasPi from the board.  I plan on making a jumper with a polyfuse inline.  Using that as the jumper will prevent the  RasPi from drawing too much current.

Interface Completed

$
0
0
After yet another design change, I finally have the interface complete and ready to test.
Version 7 Finally Comes to Life



          ✓ Serial Port
          ✓ 8 Digital Inputs
          ✓ 4 Relay Outputs
          ✓ 2 SPI Ports
          ✓ 1 I2C Port
          ✓ Fused Power to Pi




Making the fuse involved the tiniest soldering I have ever done.  That is a surface mount poly-fuse soldered to two wires.  Once connectors are added, it is used for the jumper that connects the interface board 5V supply with the Raspberry Pi GPIO 5V pin.

The picture above shows one SPI port connected to an ADC chip on the bread board.  Once everything checks out, it will be time to mount it all.

Mounted and Testing Begun

$
0
0
The interface is complete now with all (well, most anyway) of the kinks out of it.  Here it is mounted on the wall in a utility area of the house.


I still need to install a few more sensors for the alarm system and I will soon add analog input using the SPI bus.  That worked just as expected on the breadboard.  You can see it connected in the blog post below.

There was some discussion on the forum about the serial connection for the X10 interface.  My CM11a works fine with just Tx, Rx, and GND connected.

The power supply was salvaged from a micro ATX PC case.  The 12V power supply that powers it is only 2A and I had trouble running the RasPi powered from my interface.  It worked fine on the bench with these same power supplies.  I assume that the motion detectors, which are wired to the same 12V supply are drawing too much current. This causes the ATX PS to fail to output adequate 5V current.  For the time being I have removed the power jumper (+1 for configurability) and just power it the normal way.  Once I get a beefier 12V PS, I will try powering the RasPi from the interface again.

Dual power supplies is the way to go

$
0
0
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.

Analog Interface

$
0
0
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.


Reading Temperature With Thermistors

$
0
0
My first interest for an analog input is a probe to read the temperature in my beer fridge. (I am a home brewer and occasionally need closely controlled temperature for fermenting lagers.) There were a couple of 10K ohm thermistors in my box of misc components, so I decided to try those. Something like the TMP36 sensor may be a better choice, and I will try some of those soon.

A thermistor is a special type of resistor that changes resistance with temperature.  A 10KΩ thermistor is 10KΩ at 25°C.  The first problem to solve is how to use the thermistor to produce a voltage that the MPC3008 analog to digital converter can read.  A simple voltage divider, as show in the diagram here, will do the trick.

The thermistor is R1, R2 is 10KΩ, and Vin is 5V.  This will make Vout = 2.5V at 25°C.  The chart for this thermistor shows that it should vary from 1.3V at 32°F to 2.9V at 95°F.  Setting the reference voltage on the MPC3008 to 3.3V will provide the best resolution for this range. I took test measurements by placing the thermistor in an insulated cup of water ranging from ice water to very warm.  Well, that was the plan anyway.  Reminder to self: when using a thermistor or other sensor in water, shrink tubing won't cut it.  I got a rude reminder of this when I first tried to do a calibration.


My solution to the waterproofing was to simply encase the thermistor and connecting wire in epoxy.  I formed a small mold using wax paper, filled it with freshly mixed epoxy, and pushed the sensor into the epoxy.  Once it dried, I trimmed it and it came out looking like this.














I took test measurements successfully and put the data into Excel. The plot of this data shows, as expected,  a very linear curve.  Putting this data into a Least Squares Fit function produced a slope of 0.13239 and an intercept of -14.402.  This gives me parameters to convert from counts to degrees with reasonable accuracy.  Once in use I will probably need to tweak the intercept value for individual sensors due to variations in the components.

The MPC3008 is a 10 bit ADC, meaning that it will output a value between 0 and 1023.  This corresponds to zero and reference volts.  For example, if the reference is 5V, an input of 2.5V would give an output of 512 counts.  To convert from counts to volts:

            V = Vref *counts/1024

Now that I have the data from my earlier calibration, I can simply use that to convert directly from counts to degrees F.

           Temp = -14.402 + (Counts * 0.13239)

My automation/alarm software has now been enhanced to read the analog data and convert it to real values using the provided conversion parameters.  I am now monitoring the temperature in my beer fridge and once I set up a large relay to control the power, I will be able to finely control the temperature and maybe brew a decent lager.

All cleaned up and re-mounted

$
0
0
A nice clean board (actually, i just flipped the old one over) and all components re-mounted.  System is up and running 24/7 now.
The Cleanly Re-Mounted Alarm/Automation System

Jumbo Digital Picture Frame

$
0
0
What do you do with a spare flat panel monitor?  If you are a Raspberry Pi hobbyist, you turn it into a jumbo size digital picture frame.  Photography is another of my hobbies and this is a great way to display my work.

Hardware Part 1 - Modify the Monitor Power Supply

This section requires some knowledge of power supplies and delicate soldering skills.  This step is totally optional and can be skipped.  I didn't want a second wire hanging from the picture frame for the Raspberry Pi power supply, so I tapped 5V off of the monitor's power supply.  You can power the Pi normally.  If you are really lucky you will have a monitor with a built in USB hub and you can power it from there.

I completely disassembled the monitor and located 5V and ground on the back side of the power supply where one of the connectors attached.  The wires in the lower right of this image show the power lines that I attached.

I shouldn't need to say this, but: DANGER.  Make sure the power supply is unplugged when you work on it.  Mains power can kill you!


Hardware Part 2 - The Control Panel

The monitor outputs 5V even if it is turned off, because it isn't really off - just in standby.  I added a power switch and LED as well as a push button to signal the Pi to shut down.  A simple script will monitor the pushpbutton and execute a halt when it is pressed.




If you only want to install the shutdown button, just connect it to a GPIO pin and to ground.  The pinouts that I used are shown here.  Don't be concerned about the inconsistency in the GPIO pin numbers.  This is explained below and is due to differences in the pin numbering used by the Raspberry Pi developers, the native pin numbers on the BCM chip, and the WiringPi library.



Hardware Part 3 -Build a case and put it all together

I used the plastic from the case to an old backup tape.  These are more flexible and a lot less brittle than most plastic CD cases.  I was able to use scissors to cut the plastic to fit the curved shape on the rear of the monitor.  Machine screws hold the case together tightly enough to keep the Raspberry Pi in place.  It is mounted so that the SD card extends a little bit below the monitor.  I would have liked to have it all hidden, but there wasn't room and this does make it easier to remove and insert the SD card.

Both the case and the control panel are held in place with Velcro   This allows me to peel the case loose so that I can plug a network cable in if needed.  Tie wraps are used to hold the HDMI cable and power cable neatly in place.  It is a very tight fit.

A added a short length of rope for hanging which is attached to the top two VESA mounting screws.  My frame is 21 inches and is quite a bit heavier than a typical painting, so I wanted something stronger than a typical picture hanger on the wall.   My work of art wouldn't be happy crashing to the floor.  I was very lucky and found a stud in the wall exactly where I wanted to mount the frame.  If you have to use drywall anchors, then I recommend using two about four inches apart and make the rope long enough to go over both.  If your walls are stucco, then I wish you luck.

System Preparation

Windows only recognizes the first partition on removable media and I wanted a large FAT partition to put the pictures on when the SD card is plugged in to my Windows laptop.  That means I had to use the /boot partition for this purpose.

  1. Load Raspbian as usual. Shut down the Pi and remove the sdcard.
  2. Install "Minitool Partition Wizard Home Edition" on a windows system with an sdcard slot. http://download.cnet.com/MiniTool-Partition-Wizard-Home-Edition/3000-2094_4-10962200.html
  3. Run the partition wizard and move the second partition (the system partition) all the way to the end of the disk space.  You could shrink the partition first but I didn't bother.
  4. Expand the first partition to fill the remaining space.
  5. Put the SD card back into the Pi, boot up, and log in.
  6. Update the apt database using:  sudo apt-get update
  7. Install the Linux Frame Buffer Interface using:  sudo apt-get install fbi
  8. Create a directory to hold the pictures that the frame will display:  sudo mkdir /boot/PICTURES 
  9. Create a new user named "slideshow" which is a member of the "video" group:  sudo adduser slideshow --ingroup video
  10. Change the inittab file to automatically log on the slideshow user:  sudo nano /etc/inittab  Find the line that begins 1:2345:respawn:/sbin/getty --noclear 38400 tty1  Comment out that line by placing a # in front of it and then add this line:  1:2345:respawn:/bin/login -f slideshow tty1 </dev/tty1 >/dev/tty1 2>&1
  11. Create a script to run the slideshow:  sudo nano /boot/slideshow.sh which includes this one line: fbi -noverbose -m 1920x1080 -t 10 /boot/PICTURES/*.jpg   
  12. Some notes about the fbi command line:  The -m option specifies the resolution to use on the monitor.  This may need to be changed to display better for the monitor that you use.  The mode specified must be in the /etc/fb.modes  file.  I had to add the mode I needed, which is shown below.  The -t 10 option causes the program to run a slideshow with a 10 second period for each picture.  The -a option causes the program to automatically resize the images to fit the screen.  When I first tried this I was using 10 mega pixel images (about 3800 pixels across) and the Pi was taking 8-10 seconds to perform the resize.  To prevent this I removed the -a option and used FastStone Image Resizer to resize all the pictures for my slideshow to fit into a 1920x1080 screen.  You can find that utility here: http://www.faststone.org/FSResizerDownload.htm
  13. Make the script executable:  sudo chmod 775 /boot/slideshow.sh
  14. Edit the bash login script for the slideshow user:  sudo nano ~slideshow/.bashrc and add this line to the end:   /boot/slideshow.sh
  15. Once all of the above has been done, you can reboot and the system will automatically run the slideshow.  You can stop it by pressing Control-C on the keyboard.
  16. Of course, you have to put some pictures in the pictures directory.  You can do this many ways, but I re-partitioned the sdcard so that I could plug it into my laptop and copy pictures directly to the sdcard.  The fbi command will support formats other than JPG, so don't feel that you are limited to that format.  You do need to realize that Linux is case sensitive with file names, so be sure to rename them all consistently. 

Here is the block that I added to the /etc/fb.modes file:


mode "1920x1080"
    geometry 1920 1080 1920 1080 32
    timings 0 0 0 0 0 0 0
    accel true
    rgba 8/16,8/8,8/0,0/0
endmode


The default fb.modes file did not include a 1920x1080 resolution so I had to find this example.  Fortunately it  works fine with my monitor.  More information about the fb.modes file can be found here:

Trigger Shutdown Script 

The drawing above shows the shutdown switch connected to GPIO-1.  I normally refer to the GPIO pins using the WiringPi numbering scheme.  However, I am not using WiringPi for this project.  We will access the GPIO pin via the /proc filesystem.  WiringPi GPIO-1 is GPIO-18 to the system.

Create a script to monitor the pin and perform a shutdown: sudo nano /etc/ini.d/wait-for-trigger.sh  
The contents of the script is this:


#!/bin/bash

# monitor GPIO pin 18 (wiringPi pin 1) for shutdown signal

# export GPIO pin 18 and set to input with pull-up
echo "18" > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio18/direction
echo "high" > /sys/class/gpio/gpio18/direction

# wait for pin to go low
while [ true ]
do
if [ "$(cat /sys/class/gpio/gpio18/value)" == '0' ]
then
echo "SHUT DOWN NOW *****"
        halt &
        exit 1
fi
sleep 1
done


First, the GPIO pin is configured for input with a pull-up resistor enabled.  Then the scripts loops once a second and checks if the pin is pulled low from the push button being pressed.  If it is, then it executes the halt command and exits.

This script must be run at startup.  The easiest way to accomplish this is to add it to the end of the local startup script:  sudo nano /etc/rc.local
Add this line at the end of the file:
/etc/ini.d/wait-for-trigger.sh &
The & at the end of the line makes the command run in the background.  Without it, the local.rc script would not exit and the system would hang on startup.

All Completed:




Low Memory Automatic Reboot

$
0
0
Below is my solution to the memory leak problem that I discovered in the frame buffer interface used by my digital picture frame.  My first thought was to simply schedule a reboot at midnight, but the Raspberry Pi has no internal clock.  My picture frame is not connected to the network, so it never really knows what time it is. Also, the low memory system hang may occur in less than 24 hours.

The better solution is to monitor the amount of free system memory and reboot when it gets too low.  I use the /proc file system to access the system memory status, the awk command to pull out the value that I need, and a bash script to tie it all together.

lowmemreboot.sh

#!/bin/bash
declare -i fmem
while [ true ]
do
        fmem=` awk '/MemFree/ { print $2 }' /proc/meminfo`
        if [ "$fmem" -lt "10000" ]
        then
                echo FREE MEMORY IS LOW----FORCING REBOOT
                reboot
                exit
        fi
        sleep 60
done


Note that the command that awk runs is surrounded with single quotes.  The entire command is surrounded with back quotes.  This causes the output of the command to be saved in the fmem variable.

This script is run from /etc/rc.local the same way that wait-for-gpio-trigger.sh is run.  When the free system memory drops below 10K, the system will reboot and all is well again for a while.  Now I can just let the picture frame run forever and not worry about it.

I would like to see someone write a real slideshow program that does multiple transition types and supports controls such as pause and reverse.  I have too many more interesting projects on my mind, but maybe someone will find the time to do this.


USB Hub Modified to Back Feed Power

$
0
0
Most powered USB hubs will not feed power back into the host system.  This is normally a good thing.  However, it is possible to power the Raspberry Pi by back feeding power from a powered USB hub.
Note:  Doing this bypasses the fuse on the Pi, so it is possible to damage it if too much power is drawn into it.  Since all my peripherals will be connected to the hub and GPIO will not be used, then there is little risk of this occurring.  Another note: The rev 1 Raspberry Pi boards have fuses on the USB ports.  These will provide some protection but will also reduce the power.  I expect that a typical powered USB hub will have adequate power such that this will not be a problem.

Below is a simple modification to make a USB hub backfeed.  The power connector on the hub I used has +5V on the outside of the barrel, and ground on the inside.  There are three connectors on the power socket - one ground and two power connectors.  When the power plug is inserted then power is drawn from the power supply instead of from the host.  This can be overcome by simply connecting the two power connections on the socket.




Now the 2A power supply for the USB hub will power all of the periperals as well as the Raspberry Pi itself.

I need this much power in order to connect an external hard drive.  The purpose for this system will be described in my next blog post.
Viewing all 60 articles
Browse latest View live