/*
 * This program measures the temperatures of my pool, one solar panel, and the
 * hot water leaving the panels.  It generates ASCII records that are input
 * to gnuplot, when given the "-g" option.  When not given "-g", this generates
 * HTML for a small web page that shows the current values.
 *
 * This program also turns on and off a valve that sends pool water up through the 
 * panels and back.  It turns the value on when the panel temperature goes above 150 F
 * and turns it off when the panel falls below 90 F.
 *
 * (c) Robert Bedichek, 2003, 2005, 2006
 */
                                                                                                            
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include<sys/mman.h>
#include<sys/types.h>
#include<unistd.h>

#define RELAY_FNAME "/tmp/relay_value"
#define VALVE_FNAME "/tmp/pool_valve"
#define POOL_TEMP_FNAME "/tmp/pool_temp"
#define MESSAGE_FNAME "/tmp/pool_message"
#define RECIRC_PUMP_FNAME "/tmp/recirc_pump_on"
#define SOLAR_PUMP_FNAME "/tmp/solar_pump_on"

#define BIG_ROOM_HEATING_FNAME "/tmp/big_room_heat_on"
#define HALL_HEATING_FNAME "/tmp/hall_heat_on"
#define LILLIAN_BEDROOM_HEATING_FNAME "/tmp/lillian_bedroom_heat_on"
#define MASTER_BEDROOM_HEATING_FNAME "/tmp/master_bedroom_heat_on"
#define STUDY_HEATING_FNAME "/tmp/study_heat_on"
#define TOILET_ROOM_HEATING_FNAME "/tmp/toilet_room_heat_on"

typedef int bool;
enum {
  false = 0, true = 1};

enum relay {
  no_relay = -1,
  recirc_pump_relay = 0,
  solar_diverter_valve = 1,
  solar_pump_relay = 2,
  master_bedroom_heat = 3,
  toilet_room_heat = 4, // (used to be 2)
  hall_heat = 5,  // + Lilly's room + hall + study
  telephone_relay = 6,
  dead_relay = 7};

  //  study_heat = 3, repurposed this relay for MBR as MBR relay failed
  //  lillian_bedroom_heat = 4,


enum relay force_relay_on = no_relay;
enum relay force_relay_off = no_relay;

struct channel {
  double multiplier;
  double offset;
  const char *name;
  const char *units;
  double value;
};

#define CHAN_AIRTEMP (2)

struct channel channels[4] = {
  { 180.0, -461.0, "Inlet", "F"},
  { 180.0, -462.0, "Solar Panel Outlet", "F"},
  { 180.0, -466.0, "Solar Panel Air", "F"},
  { 180.0, -466.0, "Attic Air", "F"}};

int verbose = 0;
int graph = 0;
int use_lcd = 0;

#define GPIOBASE	0x80840000
#define PADR	0
#define PADDR	(0x10 / sizeof(unsigned int))
#define PHDR	(0x40 / sizeof(unsigned int))
#define PHDDR	(0x44 / sizeof(unsigned int))

#define DIO_DATA (4/sizeof(unsigned int))
#define DIO_DIRECTION  (0x14/sizeof(unsigned int))
volatile unsigned int *dio_data;
volatile unsigned int *dio_direction;

int pulse_fd;

// These delay values are calibrated for the EP9301 
// CPU running at 166 Mhz, but should work also at 200 Mhz
#define SETUP	15
#define PULSE	36
#define HOLD	22

#define COUNTDOWN(x)	asm volatile ( \
  "1:\n"\
  "subs %1, %1, #1;\n"\
  "bne 1b;\n"\
  : "=r" ((x)) : "r" ((x)) \
);

volatile unsigned int *gpio;
volatile unsigned int *phdr;
volatile unsigned int *phddr;
volatile unsigned int *padr;
volatile unsigned int *paddr;

void lcdcommand(unsigned int);
void write_lcd(const unsigned char *);
unsigned int lcdwait(void);
void lcdinit(void);

volatile unsigned short *complete, *data;
volatile unsigned char *control;

volatile unsigned char *watchdog_feed;
volatile unsigned char *watchdog_control;

// R104-88 registers (a PC104 relay/digital-input card from Tri-M)
volatile unsigned char *relay_control_reg;
volatile unsigned char *digital_input_reg;
unsigned char relay_value;  // Relay register is read-only, so need to keep track

double big_room_temperature = 0.0;
double hall_temperature = 0.0;
double lillian_bedroom_temperature = 0.0;
double master_bedroom_temperature = 0.0;
double study_temperature = 0.0;
double toilet_room_temperature = 0.0;

double big_room_thermostat = 0.0;
double hall_thermostat = 0.0;
double lillian_bedroom_thermostat = 0.0;
double master_bedroom_thermostat = 0.0;
double study_thermostat = 0.0;
double toilet_room_thermostat = 0.0;

double solar_hot_water_enable = 0.0;
double solar_tank_temperature = 0.0;
double domestic_hot_water_temperature = 0.0;
double right_solar_panel_temperature = 0.0;
double left_solar_panel_temperature = 0.0;
double heat_exchanger_input_temperature = 0.0;

/*
 * Reset the watchdog timer so that it won't reset the system in the next 8 seconds.
 */
void reset_watchdog_timer(void)
{
  if (watchdog_feed != (unsigned char *)0)
    *watchdog_feed = 5;
}

/*
 * Enable the watchdog timer so that if it is not fed (reset) for 8 seconds, it 
 * will reset the system.
 */
int watchdoginit(void)
{
  int fd = open("/dev/mem", O_RDWR);
  if (fd <= 0)
    {
      fprintf(stderr, "Unable to open /dev/mem in watchdoginit\n");
      exit(1);
    }
  watchdog_control = (unsigned char *)mmap(0, 
					   getpagesize(), 
					   PROT_READ|PROT_WRITE, 
					   MAP_SHARED, 
					   fd, 
					   0x23800000);
  if (watchdog_control == (unsigned char *)-1)
    {
      fprintf(stderr, "Unable to mmap the watchdog control register\n");
      exit(1);
    }

  watchdog_feed = (unsigned char *)mmap(0, 
					getpagesize(), 
					PROT_WRITE, 
					MAP_SHARED, 
					fd, 
					0x23c00000);
  if (watchdog_feed == (unsigned char *)-1)
    {
      fprintf(stderr, "Unable to mmap the watchdog feed register\n");
      exit(1);
    }

  int retry = 100;
  do {
    reset_watchdog_timer();
    *watchdog_control = 7;
  } while (*watchdog_control != 7 && --retry > 0);
  if (retry == 0)
    {
      fprintf(stderr, "Unable to set watchdog timer, continuing ..\n");
    }
  close(fd);
}

/*
 * Map the Tri-M relay board.
 */
int relayinit(void)
{
  int fd = open("/dev/mem", O_RDWR);
  if (fd <= 0)
    {
      fprintf(stderr, "Unable to open /dev/mem in relayinit\n");
      exit(1);
    }
  relay_control_reg = mmap(0, 
			   getpagesize(), 
			   PROT_READ|PROT_WRITE, 
			   MAP_SHARED, 
			   fd, 
			   0x11e00000);
  if (relay_control_reg == (unsigned char *)-1)
    {
      fprintf(stderr, "Unable to mmap the relay control register\n");
      exit(1);
    }
  relay_control_reg += 0x240;
  digital_input_reg = relay_control_reg + 2;
  relay_value = 0;
  close(fd);
}

/*
 * Disable the watchdog timer so that it no longer has to be fed (reset) to
 * avoid a system reset.
 */
void disable_watchdog_timer(void)
{
  if (watchdog_control != (unsigned char *)0)
    {
      reset_watchdog_timer();
      int retry = 100;
      do {
	*watchdog_control = 0;
      } while (*watchdog_control != 0 && --retry > 0);
      if (retry == 0)
	{
	  disable_watchdog_timer();
	  fprintf(stderr, "Unable to turn off watchdog timer\n");
	  exit(1);
	}
      printf("Watchdog timer disabled, exiting\n");
    }
}



int adinit(void)
{
  int fd = open("/dev/mem", O_RDWR);
  if (fd <= 0)
    {
      disable_watchdog_timer();
      fprintf(stderr, "Unable to open /dev/mem in adinit\n");
      exit(1);
    }
  control = (unsigned char *)mmap(0, 
				  getpagesize(), 
				  PROT_READ|PROT_WRITE, 
				  MAP_SHARED, 
				  fd, 
				  0x10c00000);
  if (control == (unsigned char *)-1)
    {
      disable_watchdog_timer();
      fprintf(stderr, "Unable to mmap the A/D control register\n");
      exit(1);
    }

  data = (unsigned short *)control;

  complete = (unsigned short *)mmap(0, 
				    getpagesize(), 
				    PROT_READ|PROT_WRITE, 
				    MAP_SHARED, 
				    fd, 
				    0x10800000);
  if (complete == (unsigned short *)-1)
    {
      disable_watchdog_timer();
      fprintf(stderr, "Unable to mmap the A/D 'complete' register\n");
      exit(1);
    }
  close(fd);
}


double measure_voltage(int channel)
{
  int cnt; 
  int res;

  *control = 0x40 | channel;// channel 0
  cnt = 0;
  while ((*complete & 0x80) != 0 && cnt < 100000) cnt++;
  res = *data;
  return ((((double)res * 50000.0)/4096.0) + 5.0) / 10000.0;
}

void lcdinit(void) 
{
  static int fd = 0;

  if (fd == 0)
    {
      fd = open("/dev/mem", O_RDWR);
      gpio = (unsigned int *)mmap(0, getpagesize(), 
				  PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIOBASE);
    }
  if (fd <= 0)
    {
      disable_watchdog_timer();
      fprintf(stderr, "Unable to open /dev/mem\n");
      exit(1);
    }

  phdr = &gpio[PHDR];
  padr = &gpio[PADR];
  paddr = &gpio[PADDR];
  phddr = &gpio[PHDDR];
  
  *paddr = 0x0;  // All of port A to inputs
  *phddr |= 0x38; // bits 3:5 of port H to outputs
  *phdr &= ~0x18; // de-assert EN, de-assert RS
  usleep(15000);
  lcdcommand(0x38); // two rows, 5x7, 8 bit
  usleep(4100);
  lcdcommand(0x38); // two rows, 5x7, 8 bit
  usleep(100);
  lcdcommand(0x38); // two rows, 5x7, 8 bit
  lcdcommand(0x6); // cursor increment mode
  lcdwait();
  lcdcommand(0x1); // clear display
  lcdwait();
  lcdcommand(0xc); // display on, blink off, cursor off
  lcdwait();
  lcdcommand(0x2); // return home
}

void sigalarm_handler(int ignored)
{
  int v;
  reset_watchdog_timer();
}

void timerinit(void)
{
  int retval;
  struct itimerval v;

  signal(SIGALRM, &sigalarm_handler);
  v.it_value.tv_sec = 0;
  v.it_value.tv_usec = 100000;
  v.it_interval = v.it_value;

  retval = setitimer(ITIMER_REAL, &v, (struct itimerval *)0);
  if (retval != 0)
    {
      fprintf(stderr, "Setitimer failed\n");
      disable_watchdog_timer();
      exit(1);
    }
}

void sigint_handler(int ignored)
{
  printf("sigint hander\n");
  disable_watchdog_timer();
  exit(0);
}


/* This program takes lines from stdin and prints them to the
 * 2 line LCD connected to the TS-7200 LCD header.  e.g
 *
 *    echo "hello world" | lcdmesg
 * 
 * It may need to be tweaked for different size displays
 */


unsigned int lcdwait(void) {
  int i, dat, tries = 0;
  unsigned int ctrl = *phdr;

  *paddr = 0x0; // set port A to inputs
  ctrl = *phdr;
	
  do {
    // step 1, apply RS & WR
    ctrl |= 0x30; // de-assert WR
    ctrl &= ~0x10; // de-assert RS
    *phdr = ctrl;

    // step 2, wait
    i = SETUP;
    COUNTDOWN(i);

    // step 3, assert EN
    ctrl |= 0x8;
    *phdr = ctrl;

    // step 4, wait
    i = PULSE;
    COUNTDOWN(i);

    // step 5, de-assert EN, read result
    dat = *padr;
    ctrl &= ~0x8; // de-assert EN
    *phdr = ctrl;

    // step 6, wait
    i = HOLD;
    COUNTDOWN(i);
  } while (dat & 0x80 && tries++ < 1000);
  return dat;
}

void lcdcommand(unsigned int cmd) {
  int i;
  unsigned int ctrl = *phdr;

  *paddr = 0xff; // set port A to outputs
	
  // step 1, apply RS & WR, send data
  *padr = cmd;
  ctrl &= ~0x30; // de-assert RS, assert WR
  *phdr = ctrl;

  // step 2, wait
  i = SETUP;
  COUNTDOWN(i);

  // step 3, assert EN
  ctrl |= 0x8;
  *phdr = ctrl;

  // step 4, wait
  i = PULSE;
  COUNTDOWN(i);

  // step 5, de-assert EN	
  ctrl &= ~0x8; // de-assert EN
  *phdr = ctrl;

  // step 6, wait 
  i = HOLD;
  COUNTDOWN(i);
}

void write_lcd(const unsigned char *dat) {
  int i;
  unsigned int ctrl = *phdr;

  do {
    lcdwait();
    *paddr = 0xff; // set port A to outputs

    // step 1, apply RS & WR, send data
    *padr = *dat++;
    ctrl |= 0x10; // assert RS
    ctrl &= ~0x20; // assert WR
    *phdr = ctrl;

    // step 2
    i = SETUP;
    COUNTDOWN(i);

    // step 3, assert EN
    ctrl |= 0x8;
    *phdr = ctrl;

    // step 4, wait 800 nS
    i = PULSE;
    COUNTDOWN(i);

    // step 5, de-assert EN	
    ctrl &= ~0x8; // de-assert EN
    *phdr = ctrl;
    
    // step 6, wait
    i = HOLD;
    COUNTDOWN(i);
  } while(*dat);
}



static void process_arguments(int argc, char *argv[])
{
  int c;
  use_lcd = 1;
  do
    {
      c = getopt(argc, argv, "vg0:1:b:h:l:m:s:t:B:H:L:M:S:T:D:A:C:E:a:");
      switch (c) {

      case 'v':
	verbose = 1;
	break;

      case 'g':
	graph = 1;
	break;

      case '0':
	force_relay_off = (enum relay)atoi(optarg);
	if (force_relay_off < 0 || force_relay_off > 7)
	  {
	    fprintf(stderr, "Relay num out of range (0..7)\n");
	    exit(1);
	  }
	break;

      case '1':
	force_relay_on = (enum relay)atoi(optarg);
	if (force_relay_on < 0 || force_relay_on > 7)
	  {
	    fprintf(stderr, "Relay num out of range (0..7)\n");
	    exit(1);
	  }
	break;

      case 'B':
	big_room_temperature = atof(optarg);
	if (big_room_temperature < 0.0 || big_room_temperature > 130.0)
	  {
	    fprintf(stderr, "Big room temperature out of range (0..130)\n");
	    exit(1);
	  }
	break;

      case 'a':
	solar_hot_water_enable = atof(optarg);
	break;

      case 'D':
	domestic_hot_water_temperature = atof(optarg);
	if (domestic_hot_water_temperature < 0.0 || domestic_hot_water_temperature > 230.0)
	  {
	    fprintf(stderr, "Domestic hot water temperature out of range (0..230)\n");
	    exit(1);
	  }
	break;

      case 'C':
	right_solar_panel_temperature = atof(optarg);
	if (right_solar_panel_temperature < -40.0 || right_solar_panel_temperature > 330.0)
	  {
	    fprintf(stderr, "solar panel temperature out of range (-40..330)\n");
	    exit(1);
	  }
	break;

      case 'E':
	heat_exchanger_input_temperature = atof(optarg);
	if (heat_exchanger_input_temperature < 0.0 || heat_exchanger_input_temperature > 220.0)
	  {
	    fprintf(stderr, "heat exchanger input temperature out of range (0..220)\n");
	    exit(1);
	  }
	break;

      case 'A':
	solar_tank_temperature = atof(optarg);
	if (solar_tank_temperature < 0.0 || big_room_temperature > 230.0)
	  {
	    fprintf(stderr, "Solar tank temperature out of range (0..230)\n");
	    exit(1);
	  }
	break;

      case 'H':
	hall_temperature = atof(optarg);
	if (hall_temperature < 0.0 || hall_temperature > 130.0)
	  {
	    fprintf(stderr, "Hall temperature out of range (0..130)\n");
	    exit(1);
	  }
	break;

      case 'L':
	lillian_bedroom_temperature = atof(optarg);
	if (lillian_bedroom_temperature < 0.0 || lillian_bedroom_temperature > 130.0)
	  {
	    fprintf(stderr, "Lillian bedroom temperature out of range (0..130)\n");
	    exit(1);
	  }
	break;

      case 'M':
	master_bedroom_temperature = atof(optarg);
	if (master_bedroom_temperature < 0.0 || master_bedroom_temperature > 130.0)
	  {
	    fprintf(stderr, "Master bedroom temperature out of range (0..130)\n");
	    exit(1);
	  }
	break;

      case 'S':
	study_temperature = atof(optarg);
	if (study_temperature < 0.0 || study_temperature > 130.0)
	  {
	    fprintf(stderr, "Study temperature out of range (0..130)\n");
	    exit(1);
	  }
	break;

      case 'T':
	toilet_room_temperature = atof(optarg);
	if (toilet_room_temperature < 0.0 || toilet_room_temperature > 130.0)
	  {
	    fprintf(stderr, "Toilet room temperature out of range (0..130)\n");
	    exit(1);
	  }
	break;

      case 'b':
	big_room_thermostat = atof(optarg);
	if (big_room_thermostat < 32.0 || big_room_thermostat > 90.0)
	  {
	    fprintf(stderr, "Big room thermostat value out of range (32..90)\n");
	    exit(1);
	  }
	break;

      case 'h':
	hall_thermostat = atof(optarg);
	if (hall_thermostat < 32.0 || hall_thermostat > 90.0)
	  {
	    fprintf(stderr, "Hall thermostat value out of range (32..90)\n");
	    exit(1);
	  }
	break;

      case 'l':
	lillian_bedroom_thermostat = atof(optarg);
	if (lillian_bedroom_thermostat < 32.0 || lillian_bedroom_thermostat > 90.0)
	  {
	    fprintf(stderr, "Lillian's bedroom thermostat value out of range (32..90)\n");
	    exit(1);
	  }
	break;

      case 'm':
	master_bedroom_thermostat = atof(optarg);
	if (master_bedroom_thermostat < 32.0 || master_bedroom_thermostat > 90.0)
	  {
	    fprintf(stderr, "Master bedroom thermostat value out of range (32..90)\n");
	    exit(1);
	  }
	break;

      case 's':
	study_thermostat = atof(optarg);
	if (study_thermostat < 32.0 || study_thermostat > 90.0)
	  {
	    fprintf(stderr, "Study thermostat value out of range (32..90)\n");
	    exit(1);
	  }
	break;

      case 't':
	toilet_room_thermostat = atof(optarg);
	if (toilet_room_thermostat < 32.0 || toilet_room_thermostat > 90.0)
	  {
	    fprintf(stderr, "Toilet room thermostat value out of range (32..90)\n");
	    exit(1);
	  }
	break;

      case -1:
	break;

      default:
	fprintf(stderr, "usage: %s [-l][-v][-f][-g] ...\n", argv[0]);
	fprintf(stderr, "          [-0 <relay num>] (force relay off)\n");
        fprintf(stderr, "          [-1 <relay_num>] (force relay on)\n");

        fprintf(stderr, "          [-b <big room thermostat value>]\n");
	fprintf(stderr, "          [-B <big room temperature>]\n");
	fprintf(stderr, "          [-D <domestic hot water temperature>]\n");
	fprintf(stderr, "          [-S <solar tank temperature>]\n");

	fprintf(stderr, "          [-h <hallway thermostat value>]\n");
	fprintf(stderr, "          [-H <hallway temperature>]\n");

	fprintf(stderr, "          [-l <Lillian's bedroom thermostat value>]\n");
	fprintf(stderr, "          [-L <Lillian's bedroom temperature>]\n");

	fprintf(stderr, "          [-m <master bedroom thermostat value>]\n");
	fprintf(stderr, "          [-M <master bedroom temperature>]\n");

	fprintf(stderr, "          [-s <study thermostat value>]\n");
	fprintf(stderr, "          [-S <study temperature>]\n");

	fprintf(stderr, "          [-t <toilet room thermostat value>]\n");
	fprintf(stderr, "          [-T <toilet room temperature>]\n");
	exit(1);
      }
    } while (c != -1);
  if (force_relay_on != no_relay && force_relay_off != no_relay)
    {
      fprintf(stderr, "%s: error: cannot use the -0 and the -1 options together\n", argv[0]);
      exit(1);
    }
}

/*
 * Return true if the passed temperature is a reasonable value.
 */
static int inrange(double x)
{
  return (-10.0 < x && x < 300.0);
}

/*
 * Return true if there was an error.
 */
static int measure()
{
  int i, j, err = 0;
  const int  SAMPLES = 50;
  const int  CHANNELS = 4;
  double values[CHANNELS];
  int samples[CHANNELS];

  bzero((void *)values, sizeof(values));
  bzero((void *)samples, sizeof(samples));
  for (j = 0 ; j < SAMPLES ; j++)
    for (i = 0 ; i < CHANNELS ; i++) 
      {
	double voltage = measure_voltage(i);
	double value = voltage * channels[i].multiplier + channels[i].offset;
	if (inrange(value))
	  {
	    samples[i]++;
	    values[i] += value;
	  }
      }

  for (i = 0 ; i < CHANNELS ; i++)
      if (samples[i] > 0)
	channels[i].value = values[i] / samples[i];
      else
	err = 1;

  return err;
}

/*
 * Return the current time in minutes.
 */
static double current_time_in_minutes()
{
  time_t t = time(0);
  return (double)t / 60.0;
}

/*
 * Generate a line suitable for input to gnuplot, with the
 * time, temperature values, PV current, and current pump status.
 */
static void output_graph_line(char *timestring)
{
  int i;
  FILE *fp;

  timestring[24] = '\0';
  printf("%s ", &timestring[4]);
  for (i = 0 ; i < 4 ; i++)
    {
      double value = channels[i].value;
      if (inrange(value))
	printf("%5.1lf ", value);
      else
	printf("--- ");
    }
  fp = fopen(VALVE_FNAME, "r");
  if (fp)
    {
      char buf[10], *p;
      fgets(buf, sizeof(buf), fp);
      p = index(buf, '\n');
      if (p)
	*p = '\0';
      printf("%s", buf);
      fclose(fp);
    }
  printf("\n");
}

/*
 * Record the current pump status in a file so that subsequent invocations
 * will know whether the pump is on or off.
 */
static void pump(char *valve_status)
{
  FILE *fp;
  fp = fopen(VALVE_FNAME, "w");
  if (fp)
    {
      fprintf(fp, "%s", valve_status);
      fclose(fp);
    }
}

/*
 * Return true if the pump is on.
 */
static int valve_status()
{
  FILE *fp;
  int valve_val = 0;
  fp = fopen(VALVE_FNAME, "r");
  if (fp != 0) {
      fscanf(fp, "%d", &valve_val);
  } else {
    // The valve status file doesn't exist, so 
    // create one with a "zero", assume it is off,
    // and return that as our function value.
    fp = fopen(VALVE_FNAME, "w");
    assert(fp != 0);
    fprintf(fp, "%d", valve_val);
  }
  fclose(fp);
  return valve_val > 0;
}

/*
 * Return the last pool temperature and the time it was valid
 */
static double read_pool_temp(double *time)
{
  FILE *fp;
  *time = -1.0;
  double pool_temp = -1.0;
  fp = fopen(POOL_TEMP_FNAME, "r");
  if (fp != 0) {
    int n = fscanf(fp, "%lf,%lf", &pool_temp, time);
    if (n != 2)
      pool_temp = -1.0;
  }
  if (pool_temp == -1.0) {
    pool_temp = 0.0;
    fp = fopen(POOL_TEMP_FNAME, "w");
    assert(fp != 0);
    fprintf(fp, "0.0,0.0");
  }
  fclose(fp);
  return pool_temp;
}

/*
 * Write the passed temperature to the high point file
 */
static void write_pool_temp(double temp, double time)
{
  FILE *fp;
  fp = fopen(POOL_TEMP_FNAME, "w");
  assert(fp != 0);
  fprintf(fp, "%5.1lf,%15.0lf", temp, time);
  fclose(fp);
}
/*
 * If there is a message in /tmp, we echo it.
 */
static void insert_optional_message()
{
  FILE *fp;
  fp = fopen(MESSAGE_FNAME, "r");
  if (fp)
    {
      int c;
      while ((c = fgetc(fp)) != EOF)
	printf("%c", c);
      fclose(fp);
    }
}


void
relay_control(enum relay relay_to_operate, int val)
{
  assert(relay_control_reg != 0);
  assert((int)relay_to_operate < 8);
  assert(val == 0 || val == 1);
  unsigned mask = 1 << (int)relay_to_operate;

  int x;
  FILE *fp = fopen(RELAY_FNAME, "r+");
  int old_relay_value;
  if (fp == (FILE *)0) {
    // If the file doesn't exist, create it
    fp = fopen(RELAY_FNAME, "w");
    fprintf(fp, "0");
    fclose(fp);
    fp = fopen(RELAY_FNAME, "r+");
    assert(fp != 0);
  }
  x = fscanf(fp, "%d", &old_relay_value);
  assert(x == 1);
  rewind(fp);
  relay_value = (old_relay_value & ~mask) | val << relay_to_operate;

  // The first four relays are controled by the first four bits in the "relay_control_reg" register
  // The second set of four relays are controlled by the first four bits of the following registers
  if (mask & 0xf)
    *relay_control_reg = relay_value;
  else
    {
      mask >>= 4;
      *(relay_control_reg + 1) = relay_value >> 4;
    }
  fprintf(fp, "%d", relay_value);
  fputc(EOF, fp);
  x = fclose(fp);
  assert(x == 0);
}

int
digital_input_value(void)
{
  assert(digital_input_reg != 0);
  return *digital_input_reg & 0xf;
}

/*
 * Control the relay which control the 24VAC going to the
 * motorized diverter valve.  A non zero value causes DIO bit 2
 * to be an output and to be driven to 0, which drives the reed relay,
 * which energizes the 24VDC relay.  Note that we depend on 'lcdinit()'
 * to set 'gpio' correctly.
 */
void
motorized_valve_control(int val)
{
  //  assert(gpio != 0);

  relay_control(solar_diverter_valve, !val);
  // We used to control the pool valve with GPIO outputs on the TS7200 board.
  // Now we use a relay on the Tri-M relay board, which also controls the heating system
  //  gpio[DIO_DIRECTION] = val ? 0xff : 0;  // Bit 2 controls the solar valve relay
  //  gpio[DIO_DATA] = 0;                 // Energize the relays if this bit is an output
}

void
recirc_pump_control(int val)
{
  relay_control(recirc_pump_relay, val);
}

void
solar_pump_control(int val)
{
  relay_control(solar_pump_relay, val);
}

void
telephone_control(int val)
{
  relay_control(telephone_relay, val);
}

static void output_solar_lcd(int valve_status)
{
  struct timeval tv;
  struct timezone tz;
  int v;

  v = gettimeofday(&tv, &tz);
  assert(v == 0);
  tv.tv_sec -= 60 * tz.tz_minuteswest;

  char buf[100];
  snprintf(buf, sizeof(buf), "%02d:%02d     Attic: %5.1lf %s",
	   (int)fmod(tv.tv_sec/3600., 24.),
	   (int)fmod(tv.tv_sec/60., 60.),
	   channels[3].value, channels[0].units);

  write_lcd(buf);
  lcdwait();
  lcdcommand(0xa8); // set DDRAM addr to second row
  snprintf(buf, sizeof(buf), "%s Panel:%5.1lf %s",
	   valve_status ? "Heating  " : "Valve Off",
	   channels[2].value, channels[2].units);
  write_lcd(buf);

}

static void output_heating_lcd(int call_for_heat, double hall_temperature, double big_room_temperature)
{
  struct timeval tv;
  struct timezone tz;
  int v;

  v = gettimeofday(&tv, &tz);
  assert(v == 0);
  tv.tv_sec -= 60 * tz.tz_minuteswest;

  char buf[100];
  snprintf(buf, sizeof(buf), "%02d:%02d     Hall: %5.1lf F",
	   (int)fmod(tv.tv_sec/3600., 24.),
	   (int)fmod(tv.tv_sec/60., 60.),
	   hall_temperature);

  write_lcd(buf);
  lcdwait();
  lcdcommand(0xa8); // set DDRAM addr to second row
  snprintf(buf, sizeof(buf), "%s Living:%5.1lf F",
	   call_for_heat ? "Heating" : "-------",
	   big_room_temperature);
  write_lcd(buf);

}

/*
 * Generate HTML for a small web page showing the current temperatures,
 * PV current, and whether we are command the pump on or off.
 */
static void output_html(char *timestring)
{
  int i;
  bool turning_valve_on = false;
  bool turning_valve_off = false;
  double pool_temp = 0.0;
  bool estimated = false;
  FILE *fp;

  printf("<!DOCTYPE HTML PUBLIC>\n");
  printf("<HTML>\n");
  printf("<! Contructed with the 'solar' program by Robert Bedichek>\n");
  printf("<TITLE>Bedichek Solar Heated Pool Monitor</TITLE>\n");
  printf("<meta http-equiv=\"refresh\" content=\"60\">\n");
  printf("<H3>Bedichek Pool Solar Heating System</H3>\n");

  /*
    if (valve_status())
    {
    double current_inlet_temp = channels[0].value;
    double time_of_last_known = 0.0;
    double last_known_pool_temp = read_pool_temp(&time_of_last_known);
    double minutes_since_last_known_temp = current_time_in_minutes() - time_of_last_known;

    if ((minutes_since_last_known_temp < (60.0 * 24.0)) && last_known_pool_temp > current_inlet_temp)
    pool_temp = last_known_pool_temp;
    else
    {
    pool_temp = current_inlet_temp;
    write_pool_temp(pool_temp, current_time_in_minutes());
    }
    }
    else
    {
    double time_of_last_known;
    double last_known_pool_temp = read_pool_temp(&time_of_last_known);
    if (last_known_pool_temp > 0.0 && time_of_last_known > 0.0)
    {
    double minutes_since_last_known_temp = current_time_in_minutes() - time_of_last_known;
    if (minutes_since_last_known_temp > 0 && minutes_since_last_known_temp < (24*60))
    {
    pool_temp = last_known_pool_temp - minutes_since_last_known_temp * .00371;
    estimated = true;
    }
    }
    }
  */

  insert_optional_message();

  printf("<table>\n");
  printf("<tr>\n");
  printf("<th>");
  printf("  <IMG SRC=\"poolcam.jpeg\" HEIGHT=""180"" WIDTH=""180"" ALT=\"Pool Camera Snapshot\"\n");
  printf("</th>\n");
  printf("\n");
  printf("<td>\n");
  printf("<table border=""1"" align=""left"">\n");

  pool_temp = channels[0].value;
  if (pool_temp > 0.0)
    printf("<tr> <th> <font size=+2 color=""0000ff""> Pool Temperature:  %5.1lf %s %s</font> <th> </tr>\n", 
	   pool_temp, channels[0].units, estimated ? "(estimated)" : "");

  for (i = 1 ; i < 4 ; i++) 
    {
      double value = channels[i].value;

      printf("<tr> <th> %s ", channels[i].name);
      if (value > -30 && value < 300)
	printf(" %5.1lf %s  </th> </tr> \n", value, channels[i].units);
      else
	printf("Invalid Value (probe/wire problem?)</th> </tr>\n");
    }


  left_solar_panel_temperature = channels[CHAN_AIRTEMP].value;
  if (solar_tank_temperature > 120.0 && pool_temp < 85.0)
    {
      printf("<tr> <th> Diverter Valve On (Sending Water to Panels)</th> </tr>");
      turning_valve_on = true;
    }
  else if (solar_tank_temperature < 110.0 || pool_temp > 88.0)
    {
      printf("<tr> <th> Diverter Valve Off (Water Bypassing Panels) </th> </tr>\n");
      turning_valve_off = true;
    }
  printf("<tr> <th> %s </th> </tr>", timestring);

  printf("<tr> <th> Domestic Hot Water: %4.1lf   </th> </tr>\n", domestic_hot_water_temperature);
  printf("<tr> <th> Solar Tank Temperature: %4.1lf </th> </tr>\n", solar_tank_temperature);
  printf("<tr> <th> Solar Panel Temperature: %4.1lf </th> </tr>\n", right_solar_panel_temperature);
  printf("<tr> <th> Heat Exchanger Input Temperature: %4.1lf </th> </tr>\n", heat_exchanger_input_temperature);


  time_t t;
  t = time((time_t *)0);
  struct tm *tm;

  tm = localtime(&t);

  // Control the telephone relay, enable at 7:00 AM, disable at 8:00 PM.
  telephone_control(tm->tm_hour >= 6 && tm->tm_hour < 20);

  fp = fopen(RECIRC_PUMP_FNAME, "w");
  int recirc_pump = 0;

  // Between 6AM and 10:30 PM, run the recirc pump on the hour and half hour.
  
  if (tm->tm_hour >= 6 && tm->tm_hour < 23 && (tm->tm_min % 30) <= 5) {
    recirc_pump = 1;
    if (fp != 0)
      fprintf(fp, "50.0\n");

    printf("<tr> <th> Recirc Pump On </th> </tr>\n");
    recirc_pump_control(1);
  } 
  else
    {
      if (fp != 0) 
	fprintf(fp, "00.0\n");
      
      printf("<tr> <th> Recirc Pump Off </th> </tr>\n");
      recirc_pump_control(0);
    }

  fp = fopen(SOLAR_PUMP_FNAME, "r");
  int pump_status = 0;
  if (fp) {
    double pump_val = 0.0;
    int n = fscanf(fp, "%lf", &pump_val);
    pump_status = n == 1 && pump_val > 0;
  }
      
  fp = fopen(SOLAR_PUMP_FNAME, "w");
  double hysteresis = pump_status ? -17.0 : 30.0;

  struct timeval tv;

  // Allow the pump to operate between 11 AM and 5 PM
  // Note that the ARM computer is often off by an hour
  // in the spring due to daylight savings time.

  int hour_ok = tm->tm_hour > 10.0 && tm->tm_hour < 17.0;

  if (hour_ok &&
      ((solar_tank_temperature + hysteresis) < right_solar_panel_temperature ||
       (solar_tank_temperature + hysteresis) < left_solar_panel_temperature))
    {
      if (fp)
	fprintf(fp, "45.0\n");
      printf("<tr> <th> %s Solar Pump On </th> </tr>\n", pump_status ? "" : "Turning");
      solar_pump_control(1);
    } 
  else
    {
      if (fp != 0)
	fprintf(fp, "000.0\n");
      printf("<tr> <th> %s Solar Pump Off  (%s) </th> </tr>\n", 
	     pump_status ? "Turning" : "",
	     hour_ok ? "Temperature" : "Time");
      solar_pump_control(0);
    }


  printf("</table>\n");
  printf("</td>\n");
  printf("</tr>\n");
  printf("</table>\n");

  printf("<p><IMG SRC=""pooltemps.png"" ALT=""Today's Pool temperature graph"">\n");
  printf("<p><IMG SRC=""pooltemps4pm.png"" ALT=""Seaonal pool temperature graph"">\n");

  printf("<p><a href=""history"">Daily Histories</a> &nbsp &nbsp");
  printf("   <a href=""design.html"">Background information on this web page</a>\n");
  printf("<hr> <font SIZE=-1><i> For more information, contact robert-pool@bedichek.org</i></font>\n");


    
  if (turning_valve_on)
    {
      int childpid;
      pump("195");
      fflush(stdout);
      motorized_valve_control(1);
    }
  else if (turning_valve_off)
    {
      pump("0");
      fflush(stdout);
      motorized_valve_control(0);
    }

  if (recirc_pump) {
    sleep(2*60);
    recirc_pump_control(0);
  }
}

/*
 * Start a timer going that will cause this program to die if we take longer than
 * 30 seconds to run.  I'm doing this because when my system is first booted, this
 * program hangs.
 */
void set_deadman_timer()
{
  int v;
  struct itimerval itimer_val;
 
  itimer_val.it_interval.tv_sec = 40;
  itimer_val.it_interval.tv_usec = 0;
  itimer_val.it_value = itimer_val.it_interval;
  v = setitimer(ITIMER_REAL, &itimer_val, (struct itimerval *)0);
}

/*
 * Top level function.
 */
int main (int argc, char *argv[])
{
  int c;
  long v, i;
  char *timestring;

  signal(SIGINT, sigint_handler);
  signal(SIGHUP, sigint_handler);
  process_arguments(argc, argv);

  //	watchdoginit();
  timerinit();
  adinit();
  if (use_lcd)
    lcdinit();
  relayinit();

  set_deadman_timer();
  time_t t;
  t = time((time_t *)0);
  assert(t != -1);
  timestring = ctime(&t);


  if (verbose) {
    double v[8];
    for (i = 0 ; i < 8 ; i++)
      printf("Voltage on channel %d is %5.2lf\n", i, measure_voltage(i));
    printf("Digital input value: 0x%x\n", digital_input_value());
  }
  if (force_relay_on != no_relay)
    {
      relay_control(force_relay_on, 1);
      printf("Left relay %d on\n", (int)force_relay_on);
      exit(0);
    }
  if (force_relay_off != no_relay)
    {
      relay_control(force_relay_off, 0);
      printf("Left relay %d off\n", (int)force_relay_off);
      exit(0);
    }


  v = measure();
  if (big_room_thermostat != 0.0 && big_room_temperature != 0.0)
    {
      int new_heating, x;
      FILE *fp = fopen(BIG_ROOM_HEATING_FNAME, "r+");
      double old_heating_value;
      if (fp == (FILE *)0) {
	fp = fopen(BIG_ROOM_HEATING_FNAME, "w");
	fprintf(fp, "000.0");
	fclose(fp);
	fp = fopen(BIG_ROOM_HEATING_FNAME, "r+");
	assert(fp != 0);
      }
      x = fscanf(fp, "%lf", &old_heating_value);
      assert(x == 1);
      rewind(fp);
      new_heating = (big_room_thermostat + ((old_heating_value == 0.0) ? 0.0 : .3)) > big_room_temperature;


      if (new_heating)
	x = fprintf(fp, "105.0");
      else
	x = fprintf(fp, "000.0");

      fputc(EOF, fp);
      assert(x > 2);
      x = fclose(fp);
      assert(x == 0);
      //      relay_control(big_room_heat, new_heating);
      if (use_lcd)
	output_heating_lcd(new_heating, hall_temperature, big_room_temperature);
      if (verbose)
	{
	  printf("%s hall: %5.1lf  big room:%5.1lf  thermostat:%5.1lf\n",
		 new_heating ? "Heating" : "-------", hall_temperature, big_room_temperature, big_room_thermostat);
	}
    }

  if (hall_thermostat != 0.0 && hall_temperature != 0.0)
    {
      int new_heating, x;
      FILE *fp = fopen(HALL_HEATING_FNAME, "r+");
      double old_heating_value;
      if (fp == (FILE *)0)
	{
	  fprintf(stderr, "Unable to open %s for reading/writing\n", HALL_HEATING_FNAME);
	  exit(1);
	}
      x = fscanf(fp, "%lf", &old_heating_value);
      assert(x == 1);
      rewind(fp);
      new_heating = (hall_thermostat + ((old_heating_value == 0.0) ? 0.0 : .3)) > hall_temperature;

      if (new_heating)
	x = fprintf(fp, "105.0");
      else
	x = fprintf(fp, "000.0");

      fputc(EOF, fp);
      assert(x > 2);
      x = fclose(fp);
      assert(x == 0);
      relay_control(hall_heat, new_heating);
    }

  if (lillian_bedroom_thermostat != 0.0 && lillian_bedroom_temperature != 0.0)
    {
      int new_heating, x;
      FILE *fp = fopen(LILLIAN_BEDROOM_HEATING_FNAME, "r+");
      double old_heating_value;
      if (fp == (FILE *)0)
	{
	  fprintf(stderr, "Unable to open %s for reading/writing\n", LILLIAN_BEDROOM_HEATING_FNAME);
	  exit(1);
	}
      x = fscanf(fp, "%lf", &old_heating_value);
      assert(x == 1);
      rewind(fp);
      new_heating = (lillian_bedroom_thermostat + ((old_heating_value == 0.0) ? 0.0 : .3)) > lillian_bedroom_temperature;
      
      if (new_heating)
	x = fprintf(fp, "105.0");
      else
	x = fprintf(fp, "000.0");

      fputc(EOF, fp);
      assert(x > 2);
      x = fclose(fp);
      assert(x == 0);
      //      relay_control(lillian_bedroom_heat, new_heating);
    }

  if (master_bedroom_thermostat != 0.0 && master_bedroom_temperature != 0.0)
    {
      int new_heating, x;
      FILE *fp = fopen(MASTER_BEDROOM_HEATING_FNAME, "r+");
      double old_heating_value;
      if (fp == (FILE *)0)
	{
	  fprintf(stderr, "Unable to open %s for reading/writing\n", MASTER_BEDROOM_HEATING_FNAME);
	  exit(1);
	}
      x = fscanf(fp, "%lf", &old_heating_value);
      assert(x == 1);
      rewind(fp);
      new_heating = (master_bedroom_thermostat + ((old_heating_value == 0.0) ? 0.0 : .3)) > master_bedroom_temperature;

      if (new_heating)
	x = fprintf(fp, "105.0");
      else
	x = fprintf(fp, "000.0");

      fputc(EOF, fp);
      assert(x > 2);
      x = fclose(fp);
      assert(x == 0);
      relay_control(master_bedroom_heat, new_heating);
    }

  if (study_thermostat != 0.0 && study_temperature != 0.0)
    {
      int new_heating, x;
      FILE *fp = fopen(STUDY_HEATING_FNAME, "r+");
      double old_heating_value;
      if (fp == (FILE *)0)
	{
	  fprintf(stderr, "Unable to open %s for reading/writing\n", STUDY_HEATING_FNAME);
	  exit(1);
	}
      x = fscanf(fp, "%lf", &old_heating_value);
      assert(x == 1);
      rewind(fp);
      new_heating = (study_thermostat + ((old_heating_value == 0.0) ? 0.0 : .3)) > study_temperature;

      if (new_heating)
	x = fprintf(fp, "105.0");
      else
	x = fprintf(fp, "000.0");

      fputc(EOF, fp);
      assert(x > 2);
      x = fclose(fp);
      assert(x == 0);
      //      relay_control(study_heat, new_heating);
    }

  if (toilet_room_thermostat != 0.0 && toilet_room_temperature != 0.0)
    {
      int new_heating, x;
      FILE *fp = fopen(TOILET_ROOM_HEATING_FNAME, "r+");
      double old_heating_value;
      if (fp == (FILE *)0)
	{
	  fprintf(stderr, "Unable to open %s for reading/writing\n", TOILET_ROOM_HEATING_FNAME);
	  exit(1);
	}
      x = fscanf(fp, "%lf", &old_heating_value);
      assert(x == 1);
      rewind(fp);
      new_heating = (toilet_room_thermostat + ((old_heating_value == 0.0) ? 0.0 : .3)) > toilet_room_temperature;

      if (new_heating)
	x = fprintf(fp, "105.0");
      else
	x = fprintf(fp, "000.0");

      fputc(EOF, fp);
      assert(x > 2);
      x = fclose(fp);
      assert(x == 0);
      //      relay_control(toilet_room_heat, new_heating); repurposed the relay
    }

  else
    {
      if (use_lcd)
	output_solar_lcd(valve_status());
    }
  if (v == 0)
    if (graph)
      output_graph_line(timestring);
    else
      output_html(timestring);
  else
    if (graph == 0)
      {
	printf("<!DOCTYPE HTML PUBLIC>\n");
	printf("<HTML>\n");
	printf("<! Contructed with the 'solar' program by Robert Bedichek>\n");
	printf("<TITLE>Bedichek Solar Heated Pool Monitor</TITLE>\n");
	printf("<meta http-equiv=\"refresh\" content=\"60\">\n");
	printf("<H3>Bedichek Pool Solar Heating System</H3>\n");
	printf("<H1>Current Pool Temperature is unknown, some sensor problem with %s</H1>\n", channels[v].name);
	printf("</HTML>\n");
      }
    else
      fprintf(stderr, "Channel %s out of range\n", channels[v].name);
}
