441 lines
15 KiB
C
441 lines
15 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 "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 zones
|
|
void event_trigger_0 (void) { event_trigger (&_sdconfig_.zonecfg[0]) ; }
|
|
void event_trigger_1 (void) { event_trigger (&_sdconfig_.zonecfg[1]) ; }
|
|
void event_trigger_2 (void) { event_trigger (&_sdconfig_.zonecfg[2]) ; }
|
|
void event_trigger_3 (void) { event_trigger (&_sdconfig_.zonecfg[3]) ; }
|
|
void event_trigger_4 (void) { event_trigger (&_sdconfig_.zonecfg[4]) ; }
|
|
void event_trigger_5 (void) { event_trigger (&_sdconfig_.zonecfg[5]) ; }
|
|
void event_trigger_6 (void) { event_trigger (&_sdconfig_.zonecfg[6]) ; }
|
|
void event_trigger_7 (void) { event_trigger (&_sdconfig_.zonecfg[7]) ; }
|
|
void event_trigger_8 (void) { event_trigger (&_sdconfig_.zonecfg[8]) ; }
|
|
void event_trigger_9 (void) { event_trigger (&_sdconfig_.zonecfg[9]) ; }
|
|
void event_trigger_10 (void) { event_trigger (&_sdconfig_.zonecfg[10]) ; }
|
|
void event_trigger_11 (void) { event_trigger (&_sdconfig_.zonecfg[11]) ; }
|
|
void event_trigger_12 (void) { event_trigger (&_sdconfig_.zonecfg[12]) ; }
|
|
void event_trigger_13 (void) { event_trigger (&_sdconfig_.zonecfg[13]) ; }
|
|
void event_trigger_14 (void) { event_trigger (&_sdconfig_.zonecfg[14]) ; }
|
|
void event_trigger_15 (void) { event_trigger (&_sdconfig_.zonecfg[15]) ; }
|
|
void event_trigger_16 (void) { event_trigger (&_sdconfig_.zonecfg[16]) ; }
|
|
void event_trigger_17 (void) { event_trigger (&_sdconfig_.zonecfg[17]) ; }
|
|
void event_trigger_18 (void) { event_trigger (&_sdconfig_.zonecfg[18]) ; }
|
|
void event_trigger_19 (void) { event_trigger (&_sdconfig_.zonecfg[19]) ; }
|
|
void event_trigger_20 (void) { event_trigger (&_sdconfig_.zonecfg[20]) ; }
|
|
void event_trigger_21 (void) { event_trigger (&_sdconfig_.zonecfg[21]) ; }
|
|
void event_trigger_22 (void) { event_trigger (&_sdconfig_.zonecfg[22]) ; }
|
|
void event_trigger_23 (void) { event_trigger (&_sdconfig_.zonecfg[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;
|
|
|
|
readCfg(cfg);
|
|
|
|
logMessage(LOG_NOTICE,"Starting %s version %s\n",argv[0],SD_VERSION);
|
|
|
|
_sdconfig_.calendar = true;
|
|
_sdconfig_.currentZone.type = zcNONE;
|
|
_sdconfig_.cron_update = 0;
|
|
_sdconfig_.eventToUpdateHappened = false;
|
|
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();
|
|
|
|
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);
|
|
/*
|
|
// We actually don't need to register a interupt handeler for outputs. But we will for inputs, so leave this here for future use.
|
|
if (registerGPIOinterrupt (_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].receive_mode, (void *)&event_trigger, (void *)&_sdconfig_.zonecfg[i]) != true)
|
|
{
|
|
displayLastSystemError ("Unable to set interrupt handler for specified pin, exiting");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
*/
|
|
logMessage (LOG_DEBUG, "Set GPIO %d to %s\n", _sdconfig_.zonecfg[i].pin,(_sdconfig_.zonecfg[i].input_output==OUTPUT?"OUTPUT":"INPUT") );
|
|
|
|
}
|
|
#endif
|
|
/*
|
|
for (i=0; i < _sdconfig_.pinscfgs ; i++)
|
|
{
|
|
if ( _sdconfig_.zonecfg[i].startup_state == 0 || _sdconfig_.zonecfg[i].startup_state == 1 ) {
|
|
logMessage (LOG_DEBUG, "Setting pin %d to state %d\n", _sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].startup_state);
|
|
digitalWrite(_sdconfig_.zonecfg[i].pin, _sdconfig_.zonecfg[i].startup_state);
|
|
}
|
|
}
|
|
*/
|
|
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);
|
|
|
|
check_cron();
|
|
if (zc_check() == true || check_delay24h() == true || _sdconfig_.eventToUpdateHappened) {
|
|
_sdconfig_.eventToUpdateHappened = false;
|
|
broadcast_sprinklerdstate(_mgr.active_connections);
|
|
}
|
|
|
|
if (i >= 20) {
|
|
i=0;
|
|
if (_sdconfig_.currentZone.type != zcNONE)
|
|
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);
|
|
|
|
|
|
//printf("Received trigger %d - at %s - last trigger %d\n",digitalRead (gpioconfig->pin), timebuffer, gpioconfig->last_event_state);
|
|
|
|
logMessage (LOG_DEBUG,"%s Received input change on pin %d - START\n",timebuffer, gpiopin->pin);
|
|
|
|
if ( (rawtime - gpiopin->last_event_time) < 1 && gpiopin->input_output == INPUT)
|
|
{
|
|
logMessage (LOG_DEBUG," ignoring, time between triggers too short (%d-%d)=%d\n",rawtime, gpiopin->last_event_time, (rawtime - gpiopin->last_event_time));
|
|
return;
|
|
}
|
|
gpiopin->last_event_time = rawtime;
|
|
//logMessage (LOG_DEBUG,"Diff between last event %d (%d, %d)\n",rawtime - gpioconfig->last_event_time, rawtime, gpioconfig->last_event_time);
|
|
|
|
/* Handle button pressed interrupts */
|
|
|
|
in_state_read = digitalRead (gpiopin->pin);
|
|
//logMessage (LOG_DEBUG, " Init pin state %d, previous state %d\n", in_state_read, gpiopin->last_event_state);
|
|
|
|
//if ( gpioconfig->receive_state == BOTH || in_state_read == gpioconfig->receive_state)
|
|
//{
|
|
gpiopin->last_event_state = in_state_read;
|
|
|
|
/*
|
|
if (gpiopin->ext_cmd != NULL && strlen(gpiopin->ext_cmd) > 0) {
|
|
//logMessage (LOG_DEBUG, "command '%s'\n", gpioconfig->ext_cmd);
|
|
run_external(gpiopin->ext_cmd, in_state_read);
|
|
}
|
|
*/
|
|
//sleep(1);
|
|
//} else {
|
|
// logMessage (LOG_DEBUG," ignoring, reseived state does not match cfg\n");
|
|
// return;
|
|
//}
|
|
|
|
// Sleep for 1 second just to limit duplicte events
|
|
//sleep(1);
|
|
|
|
logMessage (LOG_DEBUG,"%s Receive input change on pin %d - END\n",timebuffer, gpiopin->pin);
|
|
//if (changed ==true )
|
|
if (_mgr.active_connections != NULL) {
|
|
_sdconfig_.eventToUpdateHappened = true;
|
|
//broadcast_zonestate(_mgr.active_connections, gpiopin);
|
|
}
|
|
}
|
|
|
|
|