SprinklerD/sprinkler.c

428 lines
14 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <libgen.h>
#include <time.h>
#include <stdbool.h>
// We want a success/failure return value from 'wiringPiSetup()'
#ifdef USE_WIRINGPI
#define WIRINGPI_CODES 1
#include <wiringPi.h>
//#else
// #include "sd_GPIO.h"
#endif
#include "GPIO_Pi.h"
#include "mongoose.h"
#include "utils.h"
#include "config.h"
#include "net_services.h"
#include "sd_cron.h"
#include "version.h"
//#define PIDFILE "/var/run/fha_daemon.pid"
#define PIDLOCATION "/var/run/"
//#define CFGFILE "./config.cfg"
//#define HTTPD_PORT 80
// Use threads to server http requests
//#define PTHREAD
/* Function prototypes */
void Daemon_Stop (int signum);
void main_loop (void);
void event_trigger (struct GPIOcfg *);
void intHandler(int signum);
//extern _sdconfig_;
#ifdef USE_WIRINGPI
/* BS functions due to limitations in wiringpi of not supporting a pointer in callback event */
// Let's hope no one wants mroe than 24 inputs
void event_trigger_0 (void) { event_trigger (&_sdconfig_.inputcfg[0]) ; }
void event_trigger_1 (void) { event_trigger (&_sdconfig_.inputcfg[1]) ; }
void event_trigger_2 (void) { event_trigger (&_sdconfig_.inputcfg[2]) ; }
void event_trigger_3 (void) { event_trigger (&_sdconfig_.inputcfg[3]) ; }
void event_trigger_4 (void) { event_trigger (&_sdconfig_.inputcfg[4]) ; }
void event_trigger_5 (void) { event_trigger (&_sdconfig_.inputcfg[5]) ; }
void event_trigger_6 (void) { event_trigger (&_sdconfig_.inputcfg[6]) ; }
void event_trigger_7 (void) { event_trigger (&_sdconfig_.inputcfg[7]) ; }
void event_trigger_8 (void) { event_trigger (&_sdconfig_.inputcfg[8]) ; }
void event_trigger_9 (void) { event_trigger (&_sdconfig_.inputcfg[9]) ; }
void event_trigger_10 (void) { event_trigger (&_sdconfig_.inputcfg[10]) ; }
void event_trigger_11 (void) { event_trigger (&_sdconfig_.inputcfg[11]) ; }
void event_trigger_12 (void) { event_trigger (&_sdconfig_.inputcfg[12]) ; }
void event_trigger_13 (void) { event_trigger (&_sdconfig_.inputcfg[13]) ; }
void event_trigger_14 (void) { event_trigger (&_sdconfig_.inputcfg[14]) ; }
void event_trigger_15 (void) { event_trigger (&_sdconfig_.inputcfg[15]) ; }
void event_trigger_16 (void) { event_trigger (&_sdconfig_.inputcfg[16]) ; }
void event_trigger_17 (void) { event_trigger (&_sdconfig_.inputcfg[17]) ; }
void event_trigger_18 (void) { event_trigger (&_sdconfig_.inputcfg[18]) ; }
void event_trigger_19 (void) { event_trigger (&_sdconfig_.inputcfg[19]) ; }
void event_trigger_20 (void) { event_trigger (&_sdconfig_.inputcfg[20]) ; }
void event_trigger_21 (void) { event_trigger (&_sdconfig_.inputcfg[21]) ; }
void event_trigger_22 (void) { event_trigger (&_sdconfig_.inputcfg[22]) ; }
void event_trigger_23 (void) { event_trigger (&_sdconfig_.inputcfg[23]) ; }
typedef void (*FunctionCallback)();
FunctionCallback callbackFunctions[] = {&event_trigger_0, &event_trigger_1, &event_trigger_2,
&event_trigger_3, &event_trigger_4, &event_trigger_5,
&event_trigger_6, &event_trigger_7, &event_trigger_8,
&event_trigger_9, &event_trigger_10, &event_trigger_11,
&event_trigger_12, &event_trigger_13, &event_trigger_14,
&event_trigger_15, &event_trigger_16, &event_trigger_17,
&event_trigger_18, &event_trigger_19, &event_trigger_20,
&event_trigger_21, &event_trigger_22, &event_trigger_23};
#endif
static int server_sock = -1;
struct mg_mgr _mgr;
#ifdef PTHREAD
void *connection_handler(void *socket_desc)
{
//logMessage (LOG_DEBUG, "connection_handler()");
//Get the socket descriptor
int sock = *(int*)socket_desc;
accept_request(sock);
close(sock);
pthread_exit(NULL);
//return 0;
}
#endif
/* ------------------ Start Here ----------------------- */
int main (int argc, char *argv[])
{
int i;
char *cfg = CFGFILE;
//int port = -1;
bool debuglog = false;
// Default daemon to true to logging works correctly before we even start deamon process.
_daemon_ = true;
//_sdconfig_.port = HTTPD_PORT;
for (i = 1; i < argc; i++)
{
if (strcmp (argv[i], "-h") == 0)
{
printf ("%s (options)\n -d do NOT run as daemon\n -v verbose\n -c [cfg file name]\n -f debug 2 file\n -h this", argv[0]);
exit (EXIT_SUCCESS);
}
else if (strcmp (argv[i], "-d") == 0)
_daemon_ = false;
else if (strcmp (argv[i], "-v") == 0)
debuglog = true;
else if (strcmp (argv[i], "-f") == 0)
_debug2file_ = true;
else if (strcmp (argv[i], "-c") == 0)
cfg = argv[++i];
//else if (strcmp (argv[i], "-p") == 0)
// port = atoi(argv[++i]);
}
/* Check we are root */
if (getuid() != 0)
{
logMessage(LOG_ERR,"%s Can only be run as root\n",argv[0]);
fprintf (stderr, "%s Can only be run as root\n",argv[0]);
exit(EXIT_FAILURE);
}
if (debuglog)
_sdconfig_.log_level = LOG_DEBUG;
else
_sdconfig_.log_level = LOG_INFO;
logMessage(LOG_NOTICE,"Starting %s version %s\n",argv[0],SD_VERSION);
readCfg(cfg);
_sdconfig_.calendar = true;
_sdconfig_.currentZone.type = zcNONE;
_sdconfig_.cron_update = 0;
//_sdconfig_.eventToUpdateHappened = false;
_sdconfig_.updateEventMask = 0;
read_cron();
read_cache();
/*
if (port != -1)
_sdconfig_.port = port;
*/
logMessage (LOG_DEBUG, "Running %s with options :- daemon=%d, verbose=%d, httpdport=%s, debug2file=%d configfile=%s\n",
argv[0], _daemon_, debuglog, _sdconfig_.socket_port, _debug2file_, cfg);
signal(SIGINT, intHandler);
signal(SIGTERM, intHandler);
signal(SIGSEGV, intHandler);
if (_daemon_ == false)
{
main_loop ();
}
else
{
char pidfile[256];
sprintf(pidfile, "%s/%s.pid",PIDLOCATION, basename(argv[0]));
daemonise (pidfile, main_loop);
}
exit (EXIT_SUCCESS);
}
void main_loop ()
{
int i;
#ifdef USE_WIRINGPI
/* Make sure the file '/usr/local/bin/gpio' exists */
struct stat filestat;
if (stat ("/usr/local/bin/gpio", &filestat) == -1)
{
logMessage(LOG_ERR,"The program '/usr/local/bin/gpio' is missing, exiting");
exit (EXIT_FAILURE);
}
/* Initialize 'wiringPi' library */
if (wiringPiSetup () == -1)
{
displayLastSystemError ("'wiringPi' library couldn't be initialized, exiting");
exit (EXIT_FAILURE);
}
logMessage(LOG_DEBUG, "Setting up GPIO with WiringPi\n");
//for (i=0; _sdconfig_.zonecfg[i].pin > -1 ; i++)
for (i=(_sdconfig_.master_valve?0:1); i <= _sdconfig_.zones ; i++)
{
logMessage (LOG_DEBUG, "Setting up Zone %d\n", i);
if (_sdconfig_.zonecfg[i].input_output == OUTPUT) {
digitalWrite(_sdconfig_.zonecfg[i].pin, digitalRead(_sdconfig_.zonecfg[i].pin));
logMessage (LOG_DEBUG, "Pre Set gpiopin %d set to current state to stop statup bounce when using output mode\n", _sdconfig_.zonecfg[i].pin);
}
//sleep(5);
pinMode (_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].input_output);
logMessage (LOG_DEBUG, "Set gpiopin %d set to %s\n", _sdconfig_.zonecfg[i].pin,(_sdconfig_.zonecfg[i].input_output==OUTPUT?"OUTPUT":"INPUT") );
/*
if (_sdconfig_.zonecfg[i].input_output == OUTPUT) {
digitalWrite(_sdconfig_.zonecfg[i].pin, 1);
logMessage (LOG_DEBUG, "Set pin %d set to high/off\n", _sdconfig_.zonecfg[i].pin);
}
*/
if ( _sdconfig_.zonecfg[i].startup_state == 0 || _sdconfig_.zonecfg[i].startup_state == 1 ) {
//sleep(5);
logMessage (LOG_DEBUG, "Setting gpiopin %d to state %d\n", _sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].startup_state);
digitalWrite(_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].startup_state);
}
if (_sdconfig_.zonecfg[i].set_pull_updown != NONE) {
//sleep(5);
logMessage (LOG_DEBUG, "Set gpiopin %d set pull up/down resistor to %d\n", _sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].set_pull_updown );
pullUpDnControl (_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].set_pull_updown);
}
if ( _sdconfig_.zonecfg[i].receive_mode != NONE) {
//sleep(5);
if (wiringPiISR (_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].receive_mode, callbackFunctions[i]) == -1)
{
displayLastSystemError ("Unable to set interrupt handler for specified pin, exiting");
exit (EXIT_FAILURE);
}
logMessage (LOG_DEBUG, "Set gpiopin %d for trigger with rising/falling mode %d\n", _sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].receive_mode );
// Reset output mode if we are triggering on an output pin, as last call re-sets state for some reason
if (_sdconfig_.zonecfg[i].input_output == OUTPUT) {
//sleep(5);
pinMode (_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].input_output);
logMessage (LOG_DEBUG, "ReSet gpiopin %d set to %s\n", _sdconfig_.zonecfg[i].pin,(_sdconfig_.zonecfg[i].input_output==OUTPUT?"OUTPUT":"INPUT") );
}
}
}
#else
logMessage(LOG_DEBUG, "Setting up GPIO\n");
gpioSetup();
if (! gpioSetup()) {
logMessage(LOG_ERR, "Failed to setup GPIO\n");
exit (EXIT_FAILURE);
}
for (i=(_sdconfig_.master_valve?0:1); i <= _sdconfig_.zones ; i++)
{
logMessage (LOG_DEBUG, "Setting up Zone %d\n", i);
#ifdef GPIO_SYSFS_MODE
// Only need to export and gain control if in sysfs mode.
pinUnexport(_sdconfig_.zonecfg[i].pin);
pinExport(_sdconfig_.zonecfg[i].pin);
#endif
pinMode (_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].input_output);
if ( _sdconfig_.zonecfg[i].startup_state != -1)
digitalWrite(_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].startup_state);
logMessage (LOG_DEBUG, "Set GPIO %d to %s\n", _sdconfig_.zonecfg[i].pin,(_sdconfig_.zonecfg[i].input_output==OUTPUT?"OUTPUT":"INPUT") );
}
for (i=0; i < _sdconfig_.inputs ; i++)
{
logMessage (LOG_DEBUG, "Setting up Input %d\n", i);
pinMode (_sdconfig_.inputcfg[i].pin, _sdconfig_.inputcfg[i].input_output);
if (registerGPIOinterrupt (_sdconfig_.inputcfg[i].pin, _sdconfig_.inputcfg[i].receive_mode, (void *)&event_trigger, (void *)&_sdconfig_.inputcfg[i]) != true)
{
displayLastSystemError ("Unable to set interrupt handler for specified pin, exiting");
exit (EXIT_FAILURE);
}
}
#endif
logMessage (LOG_DEBUG, "GPIO setup complete\n");
logMessage (LOG_DEBUG, "Starting HTTPD\n");
if (!start_net_services(&_mgr)) {
logMessage(LOG_ERR, "Can not start webserver on port %s.\n", _sdconfig_.socket_port);
exit(EXIT_FAILURE);
}
i=0;
while (true)
{
//logMessage (LOG_DEBUG, "mg_mgr_poll\n");
mg_mgr_poll(&_mgr, 500);
//logMessage (LOG_DEBUG, "updateEventMask=%d\n",_sdconfig_.updateEventMask);
check_cron();
if (zc_check() == true || check_delay24h() == true || _sdconfig_.updateEventMask != 0) {
//_sdconfig_.eventToUpdateHappened = false;
broadcast_sprinklerdstate(_mgr.active_connections);
broadcast_sprinklerdactivestate(_mgr.active_connections);
} else if (i > 10 && _sdconfig_.currentZone.type!=zcNONE) {
i=0;
broadcast_sprinklerdactivestate(_mgr.active_connections);
} else {
if (i >= 600) {
i=0;
broadcast_sprinklerdstate(_mgr.active_connections);
broadcast_sprinklerdactivestate(_mgr.active_connections);
}
}
//logMessage (LOG_DEBUG, "check_net_services\n");
if (check_net_services(&_mgr) == false) {
sleep(1);
}
i++;
//logMessage (LOG_DEBUG, "loop\n");
}
}
void Daemon_Stop (int signum)
{
int i;
/* 'SIGTERM' was issued, system is telling this daemon to stop */
//syslog (LOG_INFO, "Stopping daemon");
logMessage (LOG_INFO, "Stopping!\n");
//close(server_sock);
//(_sdconfig_.master_valve?0:1)
for (i=(_sdconfig_.master_valve?0:1); i <= _sdconfig_.zones ; i++)
{
if ( _sdconfig_.zonecfg[i].shutdown_state == 0 || _sdconfig_.zonecfg[i].shutdown_state == 1 ) {
logMessage (LOG_DEBUG, "Turning off Zone %d. Setting pin %d to state %d\n", i, _sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].shutdown_state);
digitalWrite(_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].shutdown_state);
}
}
#ifndef USE_WIRINGPI
gpioShutdown();
#endif
/*
#ifdef PTHREAD
logMessage (LOG_INFO, "Stopping httpd threads!\n");
pthread_exit(NULL);
#endif
*/
write_cache();
logMessage (LOG_INFO, "Exit!\n");
exit (EXIT_SUCCESS);
}
void intHandler(int signum) {
int i;
//syslog (LOG_INFO, "Stopping");
logMessage (LOG_INFO, "Stopping!\n");
for (i=(_sdconfig_.master_valve?0:1); i <= _sdconfig_.zones ; i++)
{
if ( _sdconfig_.zonecfg[i].shutdown_state == 0 || _sdconfig_.zonecfg[i].shutdown_state == 1 ) {
logMessage (LOG_DEBUG, "Turning off Zone %d. Setting pin %d to state %d\n", i, _sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].shutdown_state);
digitalWrite(_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].shutdown_state);
}
}
#ifndef USE_WIRINGPI
gpioShutdown();
#endif
close(server_sock);
write_cache();
/*
#ifdef PTHREAD
logMessage (LOG_INFO, "Stopping httpd threads!\n");
pthread_exit(NULL);
#endif
*/
exit (EXIT_SUCCESS);
}
void event_trigger (struct GPIOcfg *gpiopin)
{
//int out_state_toset;
int in_state_read;
time_t rawtime;
//struct tm * timeinfo;
//char timebuffer[20];
//bool changed=false;
time (&rawtime);
//timeinfo = localtime (&rawtime);
//strftime (timebuffer,20,"%T",timeinfo);
logMessage (LOG_INFO,"Received input change on pin %d\n", gpiopin->pin);
if ( (rawtime - gpiopin->last_event_time) < 1 && gpiopin->input_output == INPUT)
{
logMessage (LOG_DEBUG,"Ignoring input event on pin %d, time between triggers too short (%d-%d)=%d\n",gpiopin->pin,rawtime, gpiopin->last_event_time, (rawtime - gpiopin->last_event_time));
return;
}
gpiopin->last_event_time = rawtime;
in_state_read = digitalRead (gpiopin->pin);
gpiopin->last_event_state = in_state_read;
if ( in_state_read == gpiopin->on_state && strlen(gpiopin->command_on) > 0)
run_external(gpiopin->command_on);
else if ( in_state_read == !gpiopin->on_state && strlen(gpiopin->command_off) > 0)
run_external(gpiopin->command_off);
//if (_mgr.active_connections != NULL) {
// _sdconfig_.eventToUpdateHappened = true;
//}
}