Passive infrared sensor is connected to VCC/GND and GPIO4 (pin number 7). I used eLinux-RPi Low-level peripherals for GPIO controls.
Solid State Relay is connected to GPIO17 (pin number 11). The mechanical relay is cheaper but that is not easy to use for AC100V control. I tried once but Pi became unstable when the relay turned off at a bigger speaker volume setting.
Sensor detection is ignored for one minite after the previous announcement.
Use daemon() to run this program for the announcement system. The input directory is defined directly by chdir(). Use syslog() for logging. But, the daemon should be added at the end after all debugs.
Use Chinese Character because Yahoo! JAPAN weather forecast RSS is in Japanese.
The announcement voices were recorded with my sons.
tenki.wav ... "Weather forecast!"
ame.wav ... "It will rain..."
kasa.wav ... "Don't forget the umbrella!"
yuki.wav ... "It will be snow!"
kumori.wav ... "It will be cloudy!"
hare.wav ... "It will be sunny!"
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <syslog.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/time.h>
#define BCM2708_PERI_BASE 0x3F000000
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */
#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)
//#define HOST "open.live.bbc.co.uk"
#define HOST "Weather Information Server Address"
#define RSSSIZE 8096
#define PAGE "RSS file name"
#define PORT 80
#define USERAGENT "HTMLGET 1.0"
// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
#define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
#define GET_GPIO(g) (*(gpio+13)&(1<<g)) // 0 if LOW, (1<<g) if HIGH
#define GPIO_PULL *(gpio+37) // Pull up/pull down
#define GPIO_PULLCLK0 *(gpio+38) // Pull up/pull down clock
int mem_fd;
void *gpio_map;
// I/O access
volatile unsigned *gpio;
void setup_io();
int create_tcp_socket();
int speakweather();
char *get_ip(char *host);
char *build_get_query(char *host, char *page);
int get_weather(char *rssbuf, int *index, char *title);
int main(int argc, char **argv)
{
if (daemon(1,0) == -1) {
syslog(LOG_USER|LOG_INFO,"failed to launch daemon.\n");
return -1;
}
chdir("/home/pi/cpp/speakweather");
struct tm *s_time;
time_t the_time;
static int timecount=0;
setup_io();
INP_GPIO(4);
INP_GPIO(17);
OUT_GPIO(17);
while(1){
if(GET_GPIO(4) > 0 && timecount==0){
(void) time(&the_time);
s_time=localtime(&the_time);
if(s_time->tm_hour>=6 && s_time->tm_hour<12){
GPIO_SET = 1<<17;
if(speakweather()!=0){
syslog(LOG_USER|LOG_INFO,"speakweather error.");
break;
}
GPIO_CLR = 1<<17;
timecount=60;
}
}else if(timecount>0){
timecount--;
}
sleep(1);
}
GPIO_CLR = 1<<17;
return 0;
}
int speakweather()
{
struct sockaddr_in *remote;
int sock;
int tmpres;
char *ip;
char *get;
char totalbuf[RSSSIZE+1];
char buf[BUFSIZ+1];
char titlebuf[BUFSIZ+1];
char todayweekday[100];
char todayweekdayjp[100];
char host[]=HOST;
char page[]=PAGE;
char *tempchar;
int titlenum;
int index;
time_t timer;
struct tm *date;
timer = time(NULL);
date = localtime(&timer);
strftime(todayweekday, sizeof(todayweekday), "%A", date);
if (strcmp(todayweekday,"Sunday")==0){
strcpy(todayweekdayjp,"日");
}else
if (strcmp(todayweekday,"Monday")==0){
strcpy(todayweekdayjp,"月");
}else
if (strcmp(todayweekday,"Tuesday")==0){
strcpy(todayweekdayjp,"火");
}else
if (strcmp(todayweekday,"Wednesday")==0){
strcpy(todayweekdayjp,"水");
}else
if (strcmp(todayweekday,"Thursday")==0){
strcpy(todayweekdayjp,"木");
}else
if (strcmp(todayweekday,"Friday")==0){
strcpy(todayweekdayjp,"金");
}else{
strcpy(todayweekdayjp,"土");
}
sock = create_tcp_socket();
ip = get_ip(host);
remote = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in *));
remote->sin_family = AF_INET;
tmpres = inet_pton(AF_INET, ip, (void *)(&(remote->sin_addr.s_addr)));
if( tmpres < 0)
{
syslog(LOG_USER|LOG_INFO,"Can't set remote->sin_addr.s_addr");
return -1;
}else if(tmpres == 0){
syslog(LOG_USER|LOG_INFO,"%s is not a valid IP addressn", ip);
return -1;
}
remote->sin_port = htons(PORT);
if(connect(sock, (struct sockaddr *)remote, sizeof(struct sockaddr)) < 0){
syslog(LOG_USER|LOG_INFO,"Could not connect");
return -1;
}
get = build_get_query(host, page);
//Send the query to the server
int sent = 0;
while(sent < strlen(get))
{
tmpres = send(sock, get+sent, strlen(get)-sent, 0);
if(tmpres == -1){
syslog(LOG_USER|LOG_INFO,"Can't send query");
}
sent += tmpres;
}
//now it is time to receive the page
memset(buf, 0, sizeof(buf));
memset(totalbuf, 0, sizeof(totalbuf));
char * htmlcontent;
while((tmpres = recv(sock, buf, BUFSIZ, 0)) > 0){
if(strlen(totalbuf)+strlen(buf) < RSSSIZE){
strcat(totalbuf, buf);
}
memset(buf, 0, tmpres);
}
if(tmpres < 0)
{
syslog(LOG_USER|LOG_INFO,"Error receiving data");
}
htmlcontent = totalbuf;
while(get_weather(htmlcontent, &index, titlebuf)==0){
if(strstr(titlebuf, todayweekdayjp)!=NULL){
printf("%s\n",titlebuf);
system("aplay tenki.wav > /dev/null 2>&1");
if(strstr(titlebuf,"雨")!=NULL){
system("aplay ame.wav > /dev/null 2>&1");
system("aplay kasa.wav > /dev/null 2>&1");
}else
if(strstr(titlebuf,"雪")!=NULL){
system("aplay yuki.wav > /dev/null 2>&1");
}else
if(strstr(titlebuf,"曇")!=NULL){
system("aplay kumori.wav > /dev/null 2>&1");
}else
if(strstr(titlebuf,"晴")!=NULL){
system("aplay hare.wav > /dev/null 2>&1");
}
break;
}
htmlcontent += index + 15;
}
free(get);
free(remote);
free(ip);
close(sock);
return 0;
}
int create_tcp_socket()
{
int sock;
if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
syslog(LOG_USER|LOG_INFO,"Can't create TCP socket");
exit(1);
}
return sock;
}
char *get_ip(char *host)
{
struct hostent *hent;
int iplen = 15; //XXX.XXX.XXX.XXX
char *ip = (char *)malloc(iplen+1);
memset(ip, 0, iplen+1);
if((hent = gethostbyname(host)) == NULL)
{
syslog(LOG_USER|LOG_INFO,"Can't get IP");
exit(1);
}
if(inet_ntop(AF_INET, (void *)hent->h_addr_list[0], ip, iplen) == NULL)
{
syslog(LOG_USER|LOG_INFO,"Can't resolve host");
exit(1);
}
return ip;
}
char *build_get_query(char *host, char *page)
{
char *query;
char *getpage = page;
char tpl[] = "GET /%s HTTP/1.0\r\nHost: %s\r\nUser-Agent: %s\r\n\r\n";
if(getpage[0] == '/'){
getpage = getpage + 1;
syslog(LOG_USER|LOG_INFO,"Removing leading \"/\", converting %s to %s\n", page, getpage);
}
// -5 is to consider the %s %s %s in tpl and the ending \0
query = (char *)malloc(strlen(host)+strlen(getpage)+strlen(USERAGENT)+strlen(tpl)-5);
sprintf(query, tpl, getpage, host, USERAGENT);
return query;
}
int get_weather(char *rssbuf, int *index, char *title)
{
char* ret;
char* retend;
ret = strstr(rssbuf, "<title>");
if(ret!=NULL){
ret+=7;
retend = strstr(ret, "</title>");
if(retend!=NULL && retend-ret<BUFSIZ){
strncpy(title, ret, retend-ret);
*index=retend-rssbuf;
return 0;
}
}
return -1;
}
//
// Set up a memory regions to access GPIO
//
void setup_io()
{
/* open /dev/mem */
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
syslog(LOG_USER|LOG_INFO,"can't open /dev/mem ");
exit(-1);
}
/* mmap GPIO */
gpio_map = mmap(
NULL, //Any adddress in our space will do
BLOCK_SIZE, //Map length
PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
MAP_SHARED, //Shared with other processes
mem_fd, //File to map
GPIO_BASE //Offset to GPIO peripheral
);
close(mem_fd); //No need to keep mem_fd open after mmap
if (gpio_map == MAP_FAILED) {
syslog(LOG_USER|LOG_INFO,"mmap error %d", (int)gpio_map);//errno also set!
exit(-1);
}
// Always use volatile pointer!
gpio = (volatile unsigned *)gpio_map;
} // setup_io
[Daemonize]
Create speakweather under /etc/init.d/. Comment sentences in INIT INFO are necessary for update-rc.d command later. /lib/lsb/init-functions is also necessary for status_of_proc command.
#! /bin/sh
### BEGIN INIT INFO
# Provides: speakweather
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
### END INIT INFO
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="speakweather"
NAME=speakweather
DAEMON=/home/pi/cpp/speakweather/$NAME
SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --exec $DAEMON \
|| return 2
return 0
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --name $NAME
RETVAL="$?"
return "$RETVAL"
}
case "$1" in
start)
do_start
;;
stop)
do_stop
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status}" >&2
exit 3
;;
esac
:
Make link files by update-rc.d command. The link files will be created in /etc/rc0.d/~/etc/rc6.d/. After that, speakweather will run automatically after the boot.
pi@raspberrypi ~ $ sudo update-rc.d speakweather defaults
update-rc.d: using dependency based boot sequencing
pi@raspberrypi ~ $ ls /etc/rc[0-6].d/*speakweather*
/etc/rc0.d/K01speakweather /etc/rc2.d/S02speakweather /etc/rc4.d/S02speakweather /etc/rc6.d/K01speakweather
/etc/rc1.d/K01speakweather /etc/rc3.d/S02speakweather /etc/rc5.d/S02speakweather
service command can controll speakweather daemon.
pi@raspberrypi ~ $ sudo service speakweather start
pi@raspberrypi ~ $ sudo service speakweather status
[ ok ] speakweather is running.
pi@raspberrypi ~ $ sudo service speakweather stop
pi@raspberrypi ~ $ sudo service speakweather status
[FAIL] speakweather is not running ... failed!