Sunspot Home

more notes for backup and a bad memory - - -

Chicken detector using thermal infrared PIR

We have some chickens that have learned to eat eggs - but which ones?
We have a webcam display of the nest boxes in the house.
The egg eaters do not enter the nest boxes fully so the RFID ankle fobs may not be detected.

Objective
Use cheap PIR detectors to give a warning in the house and also (later) record a picture of the culprit

First test - first record - Is that cockerel hiding from a rival - or is he the thief?
cock thief

The HC-SR501 PIR detectors are these from eBay (about £1 from China) :-

PIR

An NSLU2 (Slug) runs the RFID system and an HD webcam that views the nest boxes.

The Slug now also runs the (Lesson 1) Python code below to listen to the PIR detectors.

At minimum time delay setting a PIR detector's output goes high for a few seconds when it senses a moving hot body.
The PIR signals connect to a hex inverter chip CD4069 .
The PIR gives 3.3 volts high - plenty to trigger 5 volt CMOS logic.

A PCF8574 eBay module sits on the Slug's i2c bus (this bus is lifted to 5 volts for other devices by a level converter board).
PCF8574
Note that this version of a PCF8574 eBay module has 1Kohm resistors on the I/O pins. A similar board does not seem to include them.
(It is easy to have the PCF8574 output trying to drive a low CMOS output high if the code is in error)

The 3 PIR detectors drive the PCF8574 board pins 3 4 5 as inputs (via the CD4069).
LEDs are connected to pins 0 1 2 and the PCF8574 drives these pins as outputs,
The software uses these to confirm the detections and the leds can be seen on the webcam - proving that the system is running correctly.
(sorry - you must add the Python tabs).

'i2c_PCF8574_addr_read is a C program to get input like "BYTE = 239"

#!/usr/bin/python
# /home/pi/chickens/PCF8574test4.py 63
# addresses allowed for PCF8574A
# Decimal 55 56 57 58 59 60 61 63
# HEX 0x38 39 3a 3b 3c 3d 3e 3f

import os
import sys
import time

print "*** Hit CTRL+C to stop ***"

address = sys.argv[1] # get the address argument

print "address = ", address

while True:

print "Current date & time " + time.strftime("%c")
data_received = os.popen('i2c_PCF8574_addr_read '+ address).read()
print "data_received = ", data_received

# get the byte from the received text string (like BYTE = 239)
received_byte = data_received[7:10]
print "received_byte = ", received_byte

# set the confirmation led s to the same pattern as received

# nothing detected
if received_byte == "255":
os.popen('i2c_PCF8574_addr_byteout ' + address + ' 248' )
os.popen('echo -n 0 >/www/ramdisk/PIR.txt' )
#C
if received_byte == "247":
os.popen('i2c_PCF8574_addr_byteout ' + address + ' 249' )
os.popen('echo -n 1 >/www/ramdisk/PIR.txt' )
#B
if received_byte == "239":
os.popen('i2c_PCF8574_addr_byteout ' + address + ' 250' )
os.popen('echo -n 2 >/www/ramdisk/PIR.txt' )
#BC
if received_byte == "231":
os.popen('i2c_PCF8574_addr_byteout ' + address + ' 251' )
os.popen('echo -n 3 >/www/ramdisk/PIR.txt' )
#A
if received_byte == "223":
os.popen('i2c_PCF8574_addr_byteout ' + address + ' 252' )
os.popen('echo -n 4 >/www/ramdisk/PIR.txt' )
#AC
if received_byte == "215":
os.popen('i2c_PCF8574_addr_byteout ' + address + ' 253' )
os.popen('echo -n 5 >/www/ramdisk/PIR.txt' )
#AB
if received_byte == "207":
os.popen('i2c_PCF8574_addr_byteout ' + address + ' 254' )
os.popen('echo -n 6 >/www/ramdisk/PIR.txt' )
#ABC
if received_byte == "199":
os.popen('i2c_PCF8574_addr_byteout ' + address + ' 255' )
os.popen('echo -n 7 >/www/ramdisk/PIR.txt' )

time.sleep(2)

# lowest 3 bits are the confirmation leds
# the 3 bits to the left connect to the PIRs and
# must be set to 1to allow the next receive to work
# highest 2 - don't care
# ABC PIR confirmation response

# 255 1111 1111 248 1111 1000 none on
# AB C ABC
# 247 1111 0111 C 249 1111 1001
# 239 1110 1111 B 250 1111 1010
# 231 1110 0111 BC 251 1111 1011
# 223 1101 1111 A 252 1111 1100
# 215 1101 0111 AC 253 1111 1101
# 207 1100 1111 AB 254 1111 1110
# 199 1100 0111 ABC 255 1111 1111

The Joggler in the kitchen runs the Blassic bassic program Read_RFID.bas in a loop.
It uses wget to pull data from the Slug ramdisk and put it into the Joggler ramdisk.

This code is added to the existing RFID program to grab the PIR data :-

'5b===================================================START PIR

'Grab the PIR data from the Slug and put it in the local ramdisk

SHELL "wget -O /var/www/ramdisk/PIR.txt 192.168.0.53:8053/ramdisk/PIR.txt"
OPEN "/var/www/ramdisk/PIR.txt" FOR INPUT AS #1
IF EOF (1) THEN GOTO StartAgain
INPUT #1,PIR$
CLOSE #1

' print the PIR detection for testing
PIRdecimal = VAL (PIR$)
PRINT "PIRdecimal is ", PIRdecimal

IF PIRdecimal > 0 THEN GOSUB TalkPIR
PAUSE 1000
'========================================================END PIR

etc etc then :-

' ======================================================announce the PIR detection
LABEL TalkPIR
SHELL "amixer -c 0 -- sset Master playback 80% > /dev/null 2>&1" ' lower the volume
PAUSE 800

SHELL "aplay /home/graham/sounds/online.wav" 'beep sound precedes the announcement
PAUSE 2000

IF PIRdecimal = 1 THEN Box$ = " 1" : talk$ = "flite -t " + "' " + Box$ + "'":SHELL talk$
IF PIRdecimal = 2 THEN Box$ = " 2" :talk$ = "flite -t " + "' " + Box$ + "'":SHELL talk$
IF PIRdecimal = 3 THEN Box$ = " 1 and 2" :talk$ = "flite -t " + "' " + Box$ + "'":SHELL talk$
IF PIRdecimal = 4 THEN Box$ = " 3" :talk$ = "flite -t " + "' " + Box$ + "'":SHELL talk$
IF PIRdecimal = 5 THEN Box$ = " 1 and 3" :talk$ = "flite -t " + "' " + Box$ + "'":SHELL talk$
IF PIRdecimal = 6 THEN Box$ = " 1 and 2" :talk$ = "flite -t " + "' " + Box$ + "'":SHELL talk$
IF PIRdecimal = 7 THEN Box$ = " 1 and 2 and 3" :talk$ = "flite -t " + "' " + Box$ + "'":SHELL talk$

SHELL "amixer -c 0 -- sset Master playback 99% > /dev/null 2>&1" ' restore the volume
PAUSE 1000

RETURN



The C code is similar to these :-

/*
PCF8574_addr_read.c
-------------------
read the data on the 8 line port of an i2c bus PCF8574
------------------------------------------------------

1) set the i2c address of the PCF8574
2) send data 255 to turn off all 8 lines - make them inputs
3) read the data on the 8 port lines as a byte
usage :- type (without the < >)
<PCF8574_address_read_4> <space> <i2c_bus_address_of_PCF8574_as_decimal>
*/

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

int i2c;
int buf[1];
int address; /* i2c bus address of the PCF8574 */
int byte; /* the 8 bit byte that represents the data present on the 8 wire port */

int main(int argc, char** argv)
{
if (argc != 2) /* report error if we are not getting just 1 input after the program name */

{

printf("Error. usage: %s i2c_chip_address\n", argv[0]);

}

address = atoi(argv[1]); /* address is the first number after the program name */

i2c = open("/dev/i2c/0",O_RDWR); /* open the i2c-bus number 0 */

ioctl(i2c,I2C_SLAVE, address); /* set the address of the chip we will talk to */

buf[0] = 255;
write(i2c,buf,1); /* we send 255 to make all lines go off, ready for input data */

read(i2c,buf,1); /* now buf(0) contains the byte of data on the 8 port lines*/

byte = buf[0];

printf("BYTE = %d \n", byte);
/* BYTE value is 0-256)*/

i2c = close(i2c);
}

 

/*
PCF8574_addr_byteout.c
----------------------
Send address and byte to switch lines on 8 line port of i2c bus PCF8574
-----------------------------------------------------------------------

1) send the i2c address
2) send byte to set the 8 port lines on or off

usage :- type with spaces but without the < >
<PCF8574_addr_byteout.c> <address as decimal> <byte out value as decimal 0-255>

*/

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

int i2c;
int buf[1];
int address;

int main(int argc, char** argv)
{
if (argc != 3) /* error if we are not getting just 2 inputs after the program name */

{

printf("Error. usage: %s i2c_chip_address byte_to_send_out\n", argv[0]);

}

/* address is the first number after the program name */

address = atoi(argv[1]);

/* the byte to send out to the PC8574 is the second number */

/* place it into the first element of the buf array */

buf[0] = atoi(argv[2]);

i2c = open("/dev/i2c/0",O_RDWR); /* open the device on i2c-bus number 0*/

ioctl(i2c,I2C_SLAVE, address); /* set the i2c chip address */

write(i2c,buf,1); /* send the byte */

i2c = close(i2c);

}

 

- more to come . . . .


Please email me if you want to swap notes

Sunspot Home