/****************************************************************************************
 * Notice:  When compiling, be sure to use the flags -lX11, -lpthread and of course,    *
 *          be sure the DLP-2232PB-G is plugged into a USB port. I use:                 *
 *                                                                                      *
 *          cc -Wall -lX11 -lpthread -lm -o sensor_monitor sensor_monitor.c             *
 *                                                                                      *
 *          Pin 5 (AN1) of the DLP-2232PB-G is being used for the input.  Remember, its *
 *          input impedance is quite low and a preamp may be necessary.                 *
 *                                                                                      *
 *          Edit main() to try the various capabilities.                                *
 *          The thermometer routines need a pull-up resistor.  See ds18s20 spec sheet.  *
 *                                                                                      *
 *          Study the 16F877A port specs carefully.  Different pins/ports have varied   *
 *          capabilities - Schmidtt trigger, open-collector, TTL                        *
 *                                                                                      *
 *          Written by Craig Van Degrift <craig@yosemitefoothills.com>                  *
 *                     http://yosemitefoothills.com                                     *
 *                                                                                      *
 *          This demo code is only a best effort.  Use at your own risk.                *
 *          Uncomment the function calls in main() to try the different capabilities.   *
 *                                                                                      *
 *          Consider this code a framework which will need considerable modification    *
 *          to meet the needs of a particular measurement task.                         *
 *                                                                                      *
 ****************************************************************************************/

#include <fcntl.h>         // fcntl(), open() and their flags
#include <termios.h>       // termios() and its flags, cfmakeraw(), cfsetispeed(), cfsetospeed(), tcflush(), tcsetattr() 
#include <stdlib.h>        // exit()
#include <unistd.h>        // write(), read(), close(), getpid()
#include <signal.h>        // struct sigaction, sigemptyset(), sigaddset() and related flags
#include <string.h>        // strncmp(), bzero()
#include <X11/Xutil.h>     // X windows functions
#include <stdio.h>         // printf(), sprintf(), fprintf(), fopen(), fclose(), FILE
#include <sys/time.h>      // struct itimerval, struct timeval, struct timespec, setitimer(), getitimer() and related flags
#include <time.h>          // nanosleep()
#include <error.h>         // error()
#include <pthread.h>       // POSIX threading: create_thread()
#include <math.h>          // pow(), sqrt()

enum dlp_command{
        LineIn=0xA5,
        LineOut=0xA6, 
        GetBoardId=0xA7,
        ConfigureAtoDs=0xA8,
        StartAtoD=0xA9,
        EEPROM_Read=0xAA,
        EEPROM_Write=0xAB,
        StartDS18S20=0xAC,
        ReadDS18S20=0xAD,
        ReadDS18S20Id=0xBA,
        SearchDS18S20s=0xBB,
        StartMatchDS18S20=0xBC,
        ReadMatchDS18S20=0xBD,
        Reserved=0xAE,
        Loopback=0xAF,
        ReadPortA=0x55,
        WritePortA=0x56,
        ReadPortC=0x59,
        WritePortC=0x5A,
        ReadPortD=0x5B,
        WritePortD=0x5C
};

enum dlp_port{
        PortA=0x28,
        PortB=0x30,
        PortC=0x38,
        PortD=0x40,
        PortE=0x48
};

enum port_pin{
        Pin0=0x00,
        Pin1=0x01,
        Pin2=0x02,
        Pin3=0x03,
        Pin4=0x04,
        Pin5=0x05,
        Pin6=0x06,
        Pin7=0x07
};

enum analog_port{
        AN0=0,
        AN1=1,
        AN2=2,
        AN3=3,
        AN4=4
};

enum digital_pin_state{
        Low=0,
        High=1
};

enum ds18s20_power{
        NotChecked = 0,
        Parasitic = 1,
        External = 2,
        CheckAmbiguous = 3,
        CheckFailed = 4
};

enum temperature_scale{
        Celsius = 0,
        Fahrenheit = 1
};

int dlp2232pbg;  // Set when identification string returns 2232PB

// PIC16F877A clock frequency
// Valid combinations of clock frequency and divider settings
//   are checked in setup_d_to_a () function.
// DLP-2232PB-G and DLP-2232M-G have 20000000 clock crystals
// The maximum allowed clock frequency for the PIC16F877A is 20000000
const unsigned int PIC16F877A_clock_frequency = 20000000;  // 20000000 maximum

// PIC16F877A divider settings
// Valid values are 64, 32, 16, 8, 4, 2 and 0
//   depending on clock frequency.  0 means internal RC clock
const unsigned int PIC16F877A_divider = 32;  // Fastest allowed for 20 MHz clock frequency

// PIC16F877A analog port settings
// Note: DLP-2232PB-G uses AN6 and AN7 for SI/WUB and TXE# so they must be digital in that unit
//         Therefore values of 0x00, 0x01, and 0x08 are not valid for the DLP-2232PB-G
//
// Valid values of 0-15 given here are determined from the function parameters
//    enum analog_port_ref and int analog_ports (number of analog ports) as follows.
//
// analog_port_ref = anything, analog_ports = 0
// 0x06 - AN0, AN1, AN2, AN3, AN4, AN5, AN6, AN7 are digital;
// 0x07 - Same as 6
//
// analog_port_ref = Vdd_Vss, analog_ports = 1, 3, 5, 6, 8
// 0x0E - AN0 is analog;  AN1, AN2, AN3, AN4, AN5, AN6, AN7 are digital; Vref+ = Vdd, Vref- = Vss
// 0x04 - AN0, AN1, AN3 are analog; AN2, AN4, AN5, AN6, AN7 are digital; Vref+ = Vdd, Vref- = Vss
// 0x02 - AN0, AN1, AN2, AN3, AN4 are analog; AN5, AN6, AN7 are digital; Vref+ = Vdd, Vref- = Vss
// 0x09 - AN0, AN1, AN2, AN3, AN4, AN5 are analog; AN6, AN7 are digital; Vref+ = Vdd, Vref- = Vss
// 0x00 - AN0, AN1, AN2, AN3, AN4, AN5, AN6, AN7 are analog;             Vref+ = Vdd, Vref- = Vss
//
// analog_port_ref = AN3_Vss, analog_ports = 2, 4, 5, 7
// 0x05 - AN0, AN1 are analog; AN2, AN4, AN5, AN6, AN7 are digial;       Vref+ = AN3, Vref- = Vss
// 0x03 - AN0, AN1, AN2, AN4 are analog; AN5, AN6, AN7 are digital;      Vref+ = AN3, Vref- = Vss
// 0x0A - AN0, AN1, AN2, AN4, AN5 are analog; AN6, AN7 are digital;      Vref+ = AN3, Vref- = Vss
// 0x01 - AN0, AN1, AN2, AN4, AN5, AN6, AN7, are analog;                 Vref+ = AN3, Vref- = Vss
//
// analog_port_ref = AN3_AN2, analog_ports = 1, 2, 3, 4, 6
// 0x0F - AN0 is analog;  AN1, AN4, AN5, AN6, AN7 are digital;           Vref+ = AN3, Vref- = AN2
// 0x0D - AN0, AN1 are analog; AN4, AN5, AN6, AN7 are digital;           Vref+ = AN3, Vref- = AN2
// 0x0C - AN0, AN1, AN4 are analog; AN5, AN6, AN7 are digial;            Vref+ = AN3, Vref- = AN2
// 0x0B - AN0, AN1, AN4, AN5 are analog; AN6, AN7 are digital;           Vref+ = AN3, Vref- = AN2
// 0x08 - AN0, AN1, AN4, AN5, AN6, AN7 are analog;                       Vref+ = AN3, Vref- = AN2

enum analog_port_ref {
        Vdd_Vss = 0x10,
        AN3_Vss = 0x20,
        AN3_AN2 = 0x30
};

enum polarity{
        Pos=0,
        Neg=1
};

#define TRUE  1
#define FALSE 0
const int true = TRUE;
const int false = FALSE;

int DAs_configured = FALSE;
int port_A_is_all_digital = FALSE;
int analog_port[8];
int thermometer_port[40];
int verbose = FALSE;



#define BAUDRATE B230400

// The number of points needed depends on the block_count and block_size.
// For best speed use small block_count and large block_size
// Block size loop is .5 uS faster.
#define MAX_POINTS_PER_SCAN 65536
unsigned char inbuf[MAX_POINTS_PER_SCAN], outbuf[16];

void signal_handler_IO(int status);  // I can only count on getting at least one signal when a read is ready.
struct sigaction saio;
volatile int signals=0;  // Minimum signal count.  Subsequent signals might be hidden when the handler is running


enum data_type{
        Digital,
        Analog
};

struct scope_arg{
  enum data_type type;
  int            block_count;
  int            block_size;
  int            delay;
  int            t_scale_expansion;
  int            display_width;
};

// Threading
pthread_t scope_thread1 = 0;
pthread_t scope_thread2 = 0;
pthread_mutex_t dlp_command_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t verbose_mutex = PTHREAD_MUTEX_INITIALIZER;

const char* device="/dev/ttyUSB1";
int fd = 0;

// Temperature Measurement
struct sigaction conversion_done;
double latest_T;
enum dlp_port latest_T_port;
enum port_pin latest_T_pin;
int T_i_started  = 0;
int T_i_finished = 0;
#define MAX_T_POINTS 10000
#define MAX_THERMOMETERS 20

enum temperature_scale preferred_T_scale = Celsius;

struct T_reading {
  double         T_Celsius;
  struct timeval t;
  enum dlp_port  port;
  enum port_pin  pin;
  int            matching;
  unsigned char  id[8];
} T[MAX_T_POINTS];

int thermometer_count = 0;

struct T_specs {
  unsigned char	id[8];
  double adjustment;
  char location[20];
} thermometerSpecs[36] = {
  {{'\x10','\x48','\xb6','\x35','\x01','\x08','\x00','\xe8'},-0.01,{"Ceiling center"}},
  {{'\x10','\x18','\xbf','\x35','\x01','\x08','\x00','\x35'}, 0.05,{"Ceiling north "}},
  {{'\x10','\xec','\xbe','\x35','\x01','\x08','\x00','\x56'}, 0.16,{"Ceiling south "}},
  {{'\x10','\xe2','\xae','\x35','\x01','\x08','\x00','\x39'}, 0.10,{"Ceiling east  "}},
  {{'\x10','\x96','\xb4','\x35','\x01','\x08','\x00','\xbc'}, 0.07,{"Ceiling west  "}},
  {{'\x10','\xf5','\xad','\x35','\x01','\x08','\x00','\xa9'},-0.13,{"Wall north top"}},
  {{'\x10','\xc3','\x9c','\x35','\x01','\x08','\x00','\xbf'}, 0.04,{"Wall south top"}},
  {{'\x10','\x4b','\xb5','\x35','\x01','\x08','\x00','\xff'}, 0.09,{"Window middle "}},
  {{'\x10','\xc7','\xab','\x35','\x01','\x08','\x00','\xb6'}, 0.06,{"Wall east top "}},
  {{'\x10','\x7f','\xb1','\x35','\x01','\x08','\x00','\xd1'},-0.04,{"Wall west top "}},
  {{'\x28','\xe0','\xe4','\x14','\x01','\x00','\x00','\x6f'}, 0.04,{"Wall north mid"}},
  {{'\x28','\x18','\xe0','\x14','\x01','\x00','\x00','\xa3'}, 0.00,{"Wall south mid"}},
  {{'\x28','\xb4','\xf9','\x14','\x01','\x00','\x00','\x0d'}, 0.06,{"Wall east mid "}},
  {{'\x28','\x9c','\xfd','\x14','\x01','\x00','\x00','\x05'}, 0.01,{"Wall west mid "}},
  {{'\x28','\xc2','\xf8','\x14','\x01','\x00','\x00','\xea'},-0.02,{"Wall north bot"}},
  {{'\x28','\xd2','\xde','\x14','\x01','\x00','\x00','\xd5'},-0.14,{"Wall south bot"}},
  {{'\x28','\xde','\xec','\x14','\x01','\x00','\x00','\xaf'},-0.03,{"Wall east bot "}},
  {{'\x28','\xa1','\xe2','\x14','\x01','\x00','\x00','\xb1'}, 0.10,{"Wall west bot "}},
  {{'\x28','\x85','\xf7','\x14','\x01','\x00','\x00','\x75'}, 0.06,{"Window top    "}},
  {{'\x28','\xd7','\xe4','\x14','\x01','\x00','\x00','\x07'},-0.10,{"Window bottom "}},
  {{'\x28','\xef','\x03','\x15','\x01','\x00','\x00','\x4f'}, 0.12,{"Floor center  "}},
  {{'\x28','\x18','\xee','\x14','\x01','\x00','\x00','\x01'}, 0.01,{"              "}},
  {{'\x28','\xd8','\xf4','\x14','\x01','\x00','\x00','\x5f'}, 0.01,{"              "}},
  {{'\x28','\xd8','\xfd','\x14','\x01','\x00','\x00','\xac'}, 0.06,{"              "}},
  {{'\x28','\x94','\xfc','\x14','\x01','\x00','\x00','\x69'}, 0.01,{"              "}},
  {{'\x28','\xf4','\x00','\x15','\x01','\x00','\x00','\xa2'},-0.11,{"              "}},
  {{'\x28','\xcc','\x00','\x15','\x01','\x00','\x00','\xee'},-0.10,{"              "}},
  {{'\x28','\x96','\xfb','\x14','\x01','\x00','\x00','\x56'},-0.13,{"              "}},
  {{'\x28','\x11','\xde','\x14','\x01','\x00','\x00','\x13'},-0.08,{"              "}},
  {{'\x28','\x89','\xf4','\x14','\x01','\x00','\x00','\x46'},-0.07,{"              "}},
  {{'\x28','\xb9','\xe7','\x14','\x01','\x00','\x00','\x99'}, 0.02,{"              "}},
  {{'\x28','\xc5','\xe2','\x14','\x01','\x00','\x00','\xae'},-0.07,{"              "}},
  {{'\x28','\xfd','\xef','\x14','\x01','\x00','\x00','\x0e'},-0.06,{"              "}},
  {{'\x28','\xcb','\xea','\x14','\x01','\x00','\x00','\x83'},-0.02,{"              "}},
  {{'\x28','\x97','\xef','\x14','\x01','\x00','\x00','\x02'}, 0.09,{"              "}},
  {{'\x00'}}
};

struct ds18s20 {
  enum dlp_port      port;
  enum port_pin      pin;
  enum ds18s20_power power;
  unsigned char      id[8];
} thermometer[MAX_THERMOMETERS];

struct itimerval timer, start_time, cur_time;
struct timespec remaining_time;
struct timespec delay = {(time_t)0, (long int)1000*1000000};

void nap(int microseconds);
void send_dlp_command();
int echo_byte(unsigned char c);
void identify();
void show_file_descriptor_owner_pid(int fd);
void show_file_descriptor_flags(int fd);
void check_binary_transparency();
int get_burst_size(unsigned char block_count, unsigned char block_size); 
int sio_init();
void sio_deinit();
void signal_handler_IO(int status);
int getResults(int count, int burst_size);
void setup_a_to_d(enum analog_port_ref refs, unsigned int analog_ports, unsigned int PIC16F877A_divider);
void get_voltage(enum analog_port port, unsigned char block_count, unsigned char block_size, unsigned char delay, double* V);
char portToLetter(enum dlp_port port);
char* stateToWord(enum digital_pin_state state);
double time_test();
void save_burst_data();
void show_burst_data();

int check_CRC(unsigned char chars[],int count);
void showThermometerLocation(unsigned char* id);
double thermometerAdjustment(unsigned char* id);
void search_temp_sensors(enum dlp_port port, enum port_pin pin);
int already_started(enum dlp_port port, enum port_pin pin);
void read_ds18s20(enum dlp_port port, enum port_pin pin, char* sensor_id);
void printPortPin(enum dlp_port port, enum port_pin pin);
void get_ds18s20_result();
enum digital_pin_state read_pin_state(enum dlp_port port, enum port_pin pin);
void set_pin_state(enum dlp_port port, enum port_pin pin, enum digital_pin_state state);
void* show_PIC16F877A_eeprom();
int read_PIC16F877A_eeprom(unsigned char address);
void write_PIC16F877A_eeprom(unsigned char address, unsigned char value);
void read_port(enum dlp_port port, unsigned char block_count, unsigned char block_size, unsigned char delay, int* state[], int channels);
void set_port(enum dlp_port port, unsigned char bits);

int get_data();
void x_init();
void x_deinit();
void redraw();
void* scope(void* ptr);

void nap(int microseconds){
  struct timespec nap_interval;
  nap_interval.tv_sec=0;
  nap_interval.tv_nsec=microseconds*1000;
  nanosleep(&nap_interval, NULL);
}

void send_dlp_command(){
  int i,j,k;
  int res;
  static int test = 0;
  int burst_size;
  test++;
  if (test != 1){
    printf("Entry and exit in send_dlp_command are not matched!!!\n");
    exit(-1);
  }
  burst_size = 0;
  switch (outbuf[1]){
    case LineOut:
    case ConfigureAtoDs:
    case EEPROM_Write:
      outbuf[0] = 3;
      j = 0;
      break;
    case WritePortA:
    case WritePortC:
    case WritePortD:
      outbuf[0] = 2;
      j = 0;
      break;
    case LineIn:
    case EEPROM_Read:
    case StartDS18S20:
    case Loopback:
      outbuf[0] = 2;
      j = 1;
      break;
    case ReadPortA:
    case ReadPortC:
    case ReadPortD:
      outbuf[0] = 4;
      burst_size = get_burst_size(outbuf[2], outbuf[3]);
      j = 1;
      break;
    case StartAtoD:
      outbuf[0] = 5;
      burst_size = get_burst_size(outbuf[3], outbuf[4]);
      j = 2;
      break;
    case GetBoardId:
      outbuf[0] = 1;
      j = 6;
      break;
    case ReadDS18S20Id:
      outbuf[0] = 2;
      j = 8;
      break;
    case ReadDS18S20:
      outbuf[0] = 2;
      j = 9;
      break;
    case SearchDS18S20s:
      outbuf[0] = 2;
      j = -9;  // -9 signifies unknown number of 9-byte groups coming back
      break;
    case ReadMatchDS18S20:
      outbuf[0] = 10;
      j = 9;
      break;
    case StartMatchDS18S20:
      outbuf[0] = 10;
      j = 1;
      break;
    default:
      printf("Illegal DLP instruction code: %02X\n", outbuf[1]);
      exit(-1);
  }
  k = outbuf[0]+1;
  outbuf[k] = 0;
  for (i=0; i < k; i++)
    outbuf[k] ^= outbuf[i];
  res = write(fd, outbuf, k+1);
  if (res != k+1){
    printf("Unable to write command %02X to DLP-2232PB-G\n", outbuf[1]);
    exit(-1);
  }
  getResults(j, burst_size);
  test--;
}

int echo_byte(unsigned char c){
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = 0xAF;
  outbuf[2] = c;
  send_dlp_command();
  if (inbuf[0] != c){
    printf("Bad echo: Sent %02X, but received %02X\n", c, inbuf[0]);
    exit(-1);
  }
  pthread_mutex_unlock(&dlp_command_mutex);

  pthread_mutex_lock(&verbose_mutex);
  if (verbose)
    printf("0x%02X echoed correctly\n", c);
  pthread_mutex_unlock(&verbose_mutex);
  return c;
}

void identify(){
  char *dlp2232pbgId = "2232PB\0";
  char id[7];
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = 0xA7;
  send_dlp_command();
  strncpy(id, (char*)inbuf, 6);
  id[6] = 0;
  if (strncmp(dlp2232pbgId, id, 6))
    dlp2232pbg = true;
  pthread_mutex_unlock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose)
    printf("Unit returned identification of \"%s\"\n", id);
  pthread_mutex_unlock(&verbose_mutex);
}

// A function to prove that the process id (pid) was set
void show_file_descriptor_owner_pid(int fd){
  int i;
  i = fcntl(fd, F_GETOWN);
  printf("File descriptor owner is %d\n",i);
}

void show_file_descriptor_flags(int fd){
  int i;
  i = fcntl(fd, F_GETFL);
  printf("fcntl result is %04X\n", i);
  if (i & O_ASYNC)
    printf("O_ASYNC is set\n");
  if (i & O_NONBLOCK)
    printf("O_NONBLOCK is set\n");
  if (i & O_RDWR)
    printf("O_RDWR is set\n");
  if (i & O_RDONLY)
    printf("O_RDONLY is set\n");
  if (i & O_WRONLY)
    printf("O_WRONLY is set\n");
  if (i & O_NOCTTY)
    printf("O_NOCTTY is set\n");
  if (i & O_CREAT)
    printf("O_CREAT is set\n");
  if (i & O_EXCL)
    printf("O_EXCL is set\n");
  if (i & O_APPEND)
    printf("O_APPEND is set\n");
  if (i & O_TRUNC)
    printf("O_TRUNC is set\n");
//  if (i & O_DIRECT)                // These are not available to
//    printf("O_DIRECT is set\n");  // application programs, only
//  if (i & O_NOATIME)              // to kernel drivers.
//    printf("O_NOATIME is set\n");
}

// This function checks that all 256 possible bytes echo properly from the DLP-2232PB-G
// Incorrect termios settings can cause some bytes to be altered.
void check_binary_transparency(){
  int i;
  for (i=0; i<256; i++){
    echo_byte(i);
    if (inbuf[0] == i)
      printf(".");
    else
      printf("<%02X>",i);
  }
  printf("\n");
}

int get_burst_size(unsigned char block_count, unsigned char block_size){ 
  int burst_size;
  if (block_size == 0)
    burst_size = 256;
  else 
    burst_size = block_size;

  if (block_count == 0)
    burst_size *= 256;
  else
    burst_size *= block_count;
  return burst_size;
}

// DLP-2232PB-G Routines based on details provided in the DLP-2232PB-G Data Sheet
int sio_init(){
  int i;
  sigset_t mask;
  struct termios newtio;

  // Seting up signal handler for thermometer reading delays
  conversion_done.sa_handler = get_ds18s20_result;
  sigemptyset(&mask);
  sigaddset(&mask, SIGALRM);
  conversion_done.sa_mask = mask;
  conversion_done.sa_flags = 0;
  conversion_done.sa_restorer = NULL;
  sigaction(SIGALRM, &conversion_done, NULL);

  // Setting up signal handler used by read() when doing asynchronous I/O
  saio.sa_handler = signal_handler_IO;
  sigemptyset(&mask);
  sigaddset(&mask, SIGIO);
  saio.sa_mask = mask;
  saio.sa_flags = SA_NODEFER;
  saio.sa_restorer = NULL;
  sigaction(SIGIO, &saio, NULL);

  if ((fd = open(device, O_RDWR )) < 0 )   // If O_ASYNC is set here, hangs read for lack of ownership
    error(-1, 0, "Could not open %s - Check DLP-2232PB-G USB connection and power jumpers.",device);

//  show_file_descriptor_flags(fd);     // At this point only O_RDWR is set
//  show_file_descriptor_owner_pid(fd); // and the owner process pid = 0

  fcntl(fd, F_SETOWN, getpid());  // Making this process own the file descriptor allows O_ASYNC to be set!!!
  fcntl(fd, F_SETFL, O_ASYNC );   // Use signals - See man 2 open for explanation

//  show_file_descriptor_flags(fd);     // Now O_ASYNC is set
//  show_file_descriptor_owner_pid(fd); // and the owner is this process

  bzero(&newtio, sizeof(newtio)); // start with a clean slate
  newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
  newtio.c_iflag = IGNPAR;
  newtio.c_oflag = 0;
  newtio.c_cc[VTIME]  = 0;  // inter-character timer (unused in Linux)
  newtio.c_cc[VMIN]  = 1;  // blocking read until 1 character arrives
  cfmakeraw(&newtio);
  cfsetospeed(&newtio, BAUDRATE); // The speed settings don't seem to really matter.
  cfsetispeed(&newtio, BAUDRATE);
  tcflush(fd, TCIFLUSH);          // Make sure channel is cleared
  tcsetattr(fd, TCSANOW, &newtio);

// Just in case I have left something out that is set by default, here are all settings:
// # stty -a -F /dev/ttyUSB1
// speed 230400 baud; rows 0; columns 0; line = 0;
// intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>; eol = <undef>; eol2 = <undef>; swtch = <undef>;
// start = <undef>; stop = <undef>; susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>; flush = <undef>;
// min = 1; time = 0;
// -parenb -parodd cs8 -hupcl -cstopb cread clocal crtscts
// -ignbrk -brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8
// -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
// -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke
//  check_binary_transparency();

// Other initializations
  for (i=0; i<40; i++)
    thermometer_port[i] = false;

  T[0].T_Celsius = 0.0;
  T[0].t.tv_sec = 0;
  T[0].t.tv_usec = 0;
  T[0].port = 0;
  T[0].pin  = 0;

  return fd;
}

void sio_deinit(){
  while (T_i_started > T_i_finished) sleep(1);
  if (scope_thread1)
    pthread_join(scope_thread1, NULL);
  if (scope_thread2)
    pthread_join(scope_thread2, NULL);
//    printf("%d signals received\n",signals);
  close(fd);
}

void signal_handler_IO(int status){
  signals++;
}

char portToLetter(enum dlp_port port){
  return ((port - PortA) >> 3) + 'A';
}

char* stateToWord(enum digital_pin_state state){
  if (state == Low)
    return "low";
  else
    return "high";
}

double time_test(){
  double duration;
  start_time.it_interval.tv_sec = 1000;
  start_time.it_interval.tv_usec = 0;
  start_time.it_value.tv_sec = 1000;
  start_time.it_value.tv_usec = 0;
  setitimer(ITIMER_REAL, &start_time, NULL);
 
  // function to be timed goes here
  
  getitimer(ITIMER_REAL, &cur_time);
  if (cur_time.it_value.tv_usec < start_time.it_value.tv_usec){
    cur_time.it_value.tv_usec += 1000;
    cur_time.it_value.tv_sec  -= 1;
  }
  duration = (double)(start_time.it_value.tv_sec  - cur_time.it_value.tv_sec )*1000000
           + (double)(start_time.it_value.tv_usec - cur_time.it_value.tv_usec);
  printf("Time test took ");
  if (duration < 1000)
    printf("%0.0f microseconds\n", duration);
  else if (duration < 1000000)
    printf("%0.3f milliseconds\n", duration/1000);
  else
    printf("%0.6f seconds\n", duration/1000000);
  return duration;
}

//void save_burst_data(){
//  FILE *f;
//  int i;
//  f = fopen("data.out","w");
//  for (i=0; i< burst_size; i++)
//    fprintf(f, "%05d: %5.2f\n",t[i],V[i]);
//  fclose(f);
//}

//void show_burst_data(){
//  int i,j;
//  for (i = 0; i < burst_size; i++){
//    if (type == Analog)
//      printf("%5d: %6.1f  ", i, V[i]);
//    else{
//      printf("%5d: ", i);
//      for (j = 7; j >= 0; j--){
//        printf("%c", '0' +((inbuf[i] & (1 << j)) !=0));
//        if (j == 4)
//          printf(" ");
//      }
//      printf("  ");
//    }
//    if (i%10 == 9)
//      printf("\n");
//  }
//  printf("\n");
//}

  
// X-Window Routines are based on "A Brief Intro to X11 Programming" found on
// http://www.math.msu.su/~vvb/2course/Borisenko/CppProjects/GWindow/xintro.html
void* scope(void* arg){
  XEvent event;
  KeySym key;
  char text[255];
  XColor tmp;
  char title[100];
  int screen;
  Display *display;
  Window win;
  GC gc;
  struct scope_arg* settings = (struct scope_arg*)arg;
  unsigned long black, white, darkgreen, red;
  int i;
  enum data_type type  = settings->type;
  enum analog_port analog_input_port = AN1;
  enum dlp_port digital_input_port = PortC;
  enum port_pin pin;
  int t_scale_expansion = settings->t_scale_expansion;
  double* V;
  XPoint* points;
  int point_count;
  int burst_size;
  int block_count = settings->block_count;
  int block_size = settings->block_size;
  long analog_height = 600;
  long digital_height = 400;
  int** state;
  int channels = 8;
  int reading_delay = settings->delay;  // units are microseconds
  long width = settings->display_width;


  burst_size = get_burst_size(block_count, block_size);

  if (type == Analog){
    setup_a_to_d(Vdd_Vss, 3, 32);
    V = (double*)malloc(burst_size*sizeof(double));
    get_voltage(analog_input_port, block_count, block_size, reading_delay, V);
    point_count = burst_size;
    
    // Convert the voltages and times to XPoints
    points = (XPoint*)malloc(point_count*sizeof(XPoint));
    for (i = 0; i < point_count; i++) {
      points[i].x = i * t_scale_expansion;
      points[i].y = analog_height - 1 - 10 - V[i]/10;
    }
  }
  else{
    state = (int**)malloc(burst_size*sizeof(int));
    for (i = 0; i < burst_size; i++)
      state[i]=(int*)malloc(channels*sizeof(int));
    read_port(digital_input_port, block_count, block_size, reading_delay, state, channels);

    // Convert the pin states and times to XPoints
    point_count = burst_size;
    points = (XPoint*)malloc(channels*point_count*sizeof(XPoint));
    for (i = 0; i < point_count; i++){
      for (pin = 0; pin < channels; pin++){
        points[i*channels+pin].x = i * t_scale_expansion;
        points[i*channels+pin].y = digital_height - 1 - 10 - 50*pin - 20* state[i][pin];
      }
    }
  }

  // Create the X-window and the required colors
  display = XOpenDisplay((char*)0);
  screen = DefaultScreen(display);
  black = BlackPixel(display, screen);
  white = WhitePixel(display, screen);
  XParseColor(display, DefaultColormap(display, screen), "darkgreen", &tmp);
  XAllocColor(display, DefaultColormap(display, screen), &tmp);
  darkgreen = tmp.pixel;
  XParseColor(display, DefaultColormap(display, screen), "red", &tmp);
  XAllocColor(display, DefaultColormap(display, screen), &tmp);
  red = tmp.pixel;

  if (type == Analog){
    win = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, width, analog_height, 5, white, black);
    XSetStandardProperties(display, win, "Oscilloscope", "Scope", None, NULL, 0 , NULL);
  }
  else{
    win = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, width, digital_height, 5, white, black);
    sprintf(title, "8-Channel Logic Probe (%.1f uS/point)", 4.0+reading_delay);
    XSetStandardProperties(display, win, title, "LogicProbe", None, NULL, 0 , NULL);
  }
  XSelectInput(display, win, ExposureMask | ButtonPressMask | KeyPressMask);
  gc = XCreateGC(display, win, 0, 0);
  XSetBackground(display, gc, white);
  XSetForeground(display, gc, black);
  XClearWindow(display, win);
  XMapRaised(display, win);
  while(1) {
    XNextEvent(display, &event);
    if (event.type == Expose && event.xexpose.count == 0) {

      // Draw dark green grid at 100x100 pixels
      XSetForeground(display, gc, darkgreen);
      if (type == Analog){
        XDrawLine(display, win, gc, 0, 0, 0, analog_height-1);
        for (i = 0; i < analog_height; i+=100) {
          XDrawLine(display, win, gc, 0, analog_height-1-i-10, width-1, analog_height-1-i-10);
        }
        for (i = 0; i < width; i+=100) {
          XDrawLine(display, win, gc, width-1-i, 0, width-1-i, analog_height-1-10);
        }

        // Plot red lines between the points
        XSetForeground(display, gc, red);
        for (i = 1; i< point_count; i++) {
          XDrawLine(display, win, gc, points[i-1].x, points[i-1].y, points[i].x, points[i].y);
        }

        // Plot the actual data points as white dots
        XSetForeground(display, gc, white);
        XDrawPoints(display, win, gc, points, point_count, NotifyNormal);
      }
      else {
        // Draw dark green grid at 100x100 pixels
        XDrawLine(display, win, gc, 0, 0, 0, digital_height-1);
        for (i = 0; i < digital_height; i+=50) {
          XDrawLine(display, win, gc, 0, digital_height-1-i-10, width-1, digital_height-1-i-10);
        }
        for (i = 0; i < width; i+=50) {
          XDrawLine(display, win, gc, width-1-i, 0, width-1-i, digital_height-1-10);
        }

        // Plot red lines between the points
        XSetForeground(display, gc, red);
        for (i = 1; i < point_count; i++) {
          for (pin = 0; pin < 8; pin++){
            XDrawLine(display, win, gc, points[i*8+pin-8].x, points[i*8+pin-8].y, points[i*8+pin].x, points[i*8+pin].y);
          }
        }

        // Plot the actual data points as white dots
        XSetForeground(display, gc, white);
        XDrawPoints(display, win, gc, points, point_count*8, NotifyNormal);
      }
    }

    if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1) {
      if (text[0] == 'q') {
        break;
      }
      printf("You pressed the %c key!\n", text[0]);
    }

    if (event.type == ButtonPress) {
      printf("You pressed a button at (%i, %i)\n", event.xbutton.x, event.xbutton.y);
      break;
    }
  }

  // Free allocations for X-window system
  free(points);
  if (type == Analog)
    free(V);
  XFreeGC(display, gc);
  XDestroyWindow(display, win);
  XCloseDisplay(display);
  return NULL;
}

int getResults(int count, int burst_size){
  int res;
  int i, j, done, group_size;
  unsigned char status_byte;

  if ((res = read(fd, &status_byte, 1)) < 0)
      error(-1, 0, "Error reading status byte");
  switch (status_byte) {
    case 0x55:
      break;
    case 0xAA:
      printf("Parity error in command\n");
      return(status_byte);
    case 0xAB:
      printf("Aborting command...\n");
      return(status_byte);
    case 0xFF:
      printf("Communication error...\n");
      return(status_byte);
    default:
      printf("Mystery status byte: %02X\n", status_byte);
    return(status_byte);
      ;
  }
  if (!count)
    return(0);

  i = 0;
  if (count < 0){
    done = false;
    count = -count;
    group_size = count;
    while (! done){
      while (i < count){
        i += read(fd, &inbuf[i], count - i); 
      }
      count += group_size;
      if (inbuf[i-1] == 0x00 || inbuf[i-1] == 0x02 || inbuf[i-1] == 0x08 || inbuf[i-1] == 0xC0){
        done = true;
        for (j = 1; j < group_size; j++){
          if (inbuf[i - 1 - j] != inbuf[i-1]){
            done = false;
            break;
          }
        }
      }
    }
  } else if (burst_size == 0 || burst_size == 1){
    while (i < count){
      i += read(fd, &inbuf[i], count - i);
    }
  }
  else{
    while (i < count*burst_size){
      i += read(fd, &inbuf[i], count*burst_size - i);
    }
  }
//  if (verbose && burst_size !=0)
//    show_burst_data();
  return(0);
}

//  Based on Chapter 11 of the PIC16F87XA Data Sheet
void setup_a_to_d(enum analog_port_ref refs, unsigned int analog_ports, unsigned int PIC16F877A_divider){
  int i;
  char *ref_string;
  char *port_string;
  int max_analog_ports;

  if (dlp2232pbg)
    max_analog_ports = 6;
  else
    max_analog_ports = 8;

  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = ConfigureAtoDs; 
  if (analog_ports > max_analog_ports ){
    printf("%d analog ports specified, but maximum is %d.\n", analog_ports, max_analog_ports);
    exit(-1);
  }
  switch (refs){
    case Vdd_Vss:
      ref_string = "Vdd and Vss";
      break;
    case AN3_Vss:
      ref_string = "pin 3 and Vss";
      break;
    case AN3_AN2:
      ref_string = "pin 3 and pin 2";
      break;
    default:
      printf("Bad analog port reference value: %d\n",refs);
      exit(-1);
  }

  port_A_is_all_digital = false;
  for (i=0; i<8; i++)
    analog_port[i] = false;
  switch (refs + analog_ports){
    case 0x10:
    case 0x20:
    case 0x30:
      outbuf[2] = 0x06;
      port_string = "All pins on Port A are digital.";
      port_A_is_all_digital = true;
      break;
    case 0x11:
      outbuf[2] = 0x0E;
      port_string = "pin 0 is analog, pins 1-7 are digital";
      analog_port[0] = true;
      break;
    case 0x13:
      outbuf[2] = 0x04;
      port_string = "pins 0-2 are analog, pins 3-7 are digital";
      for (i=0; i<3; i++)
        analog_port[i] = true;
      break;
    case 0x15:
      outbuf[2] = 0x02;
      port_string = "pins 0-4 are analog, pins 5-7 are digital";
      for (i=0; i<5; i++)
        analog_port[i] = true;
      break;
    case 0x16:
      outbuf[2] = 0x09;
      port_string = "pins 0-5 are analog, pins 6 and 7 are digital";
      for (i=0; i<6; i++)
        analog_port[i] = true;
      break;
    case 0x18:      
      if (!dlp2232pbg) {
        outbuf[2] = 0x00;
        port_string = "All 8 pins are analog";
        for (i=0; i<8; i++)
          analog_port[i] = true;
      }
      break;
    case 0x22:
      outbuf[2] = 0x05;
      port_string = "pins 0 and 1 are analog, pins 2 and 4-7 are digital";
      analog_port[0] = true;
      analog_port[1] = true;
      analog_port[3] = true;
      break;
    case 0x24:
      outbuf[2] = 0x03;
      port_string = "pins 0-2 and 4 are analog, pins 5-7 are digital";
      for (i=0; i<5; i++)
        analog_port[i] = true;
      break;
    case 0x25:
      outbuf[2] = 0x0A;
      port_string = "pins 0-2 and 4-5 are analog, pins 6-7 are digital"; 
      for (i=0; i<6; i++)
        analog_port[i] = true;
      break;
    case 0x27:
      if (!dlp2232pbg) {
        outbuf[2] = 0x01;
        port_string = "pins 0-2 and 4-7 are analog";
        for (i=0; i<8; i++)
          analog_port[i] = true;
      }
      break;
    case 0x31:
      outbuf[2] = 0x0F;
      port_string = "pin 0 is analog, pins 1 and 4-7 are digital";
      analog_port[0] = true;
      analog_port[2] = true;
      analog_port[3] = true;
      break;
    case 0x32:
      outbuf[2] = 0x0D;
      port_string = "pins 0 and 1 are analog, pins 4-7 are digital";
      for (i=0; i<4; i++)
        analog_port[i] = true;
      break;
    case 0x33:
      outbuf[2] = 0x0C;
      port_string = "pins 0, 1, and 4 are analog, pins 5-7 are digital";
      for (i=0; i<5; i++)
        analog_port[i] = true;
      break;
    case 0x34:
      outbuf[2] = 0x0B;
      port_string = "pins 0, 1, 4, and 5 are analog, pins 6 and 7 are digital";
      for (i=0; i<6; i++)
        analog_port[i] = true;
      break;
    case 0x36:
      if (!dlp2232pbg) {
        outbuf[2] = 0x08;
        port_string = "pins 0, 1, and 4-7 are analog";
        for (i=0; i<8; i++)
          analog_port[i] = true;
      }
      break;
    default:
      printf("Unavailable combination of analog references (%s) and number of ports (%d)\n", ref_string, analog_ports);
      exit(-1);
  }
  if (verbose){
    if (outbuf[2] == 0x06)
      printf("%s\n", port_string);
    else
      printf("Port A %s, analog references from %s.\n",port_string, ref_string);
  }

  switch (PIC16F877A_divider){
    case 64:
      outbuf[2] |= 0xC0;
      outbuf[3] = 0x81;
      if (PIC16F877A_clock_frequency <= 20000000)
        break;
    case 32:
      outbuf[2] |= 0x80;
      outbuf[3] = 0x81;
      if (PIC16F877A_clock_frequency <= 20000000)
        break;
    case 16:
      outbuf[2] |= 0xC0;
      outbuf[3] = 0x41;
      if (PIC16F877A_clock_frequency <= 10000000)
        break;
    case 8:
      outbuf[2] |= 0x80;
      outbuf[3] = 0x41;
      if (PIC16F877A_clock_frequency <= 5000000)
        break;
    case 4:
      outbuf[2] |= 0xC0;
      outbuf[3] = 0x01;
      if (PIC16F877A_clock_frequency <= 2500000)
        break;
    case 2:
      outbuf[2] |= 0x80;
      outbuf[3] = 0x01;
      if (PIC16F877A_clock_frequency <= 1250000)
        break;
    case 0:  // Internal RC Clock - only recommended for sleep operation
            //                     if clock frequency > 1000000
      outbuf[2] |= 0x80;
      outbuf[3] = 0xC1;
    default:
      printf("PIC16F877A_divider setting of %d is too high for PIC16F877A_clock_frequency of %d\n",
      PIC16F877A_divider, PIC16F877A_clock_frequency);
      exit(-1);  
  }
  send_dlp_command();
  DAs_configured = true;
  pthread_mutex_unlock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose){
    printf("D/A Converters set up with %d analog ports using %s as references and ", analog_ports, ref_string);
    if (PIC16F877A_divider)
      printf("a clock divider of %d.\n",PIC16F877A_divider);
    else
      printf("an RC clock.\n");
  }
  pthread_mutex_unlock(&verbose_mutex);
}

void get_voltage(enum analog_port port, unsigned char block_count, unsigned char block_size, unsigned char delay, double* V){
  int i;
  if (analog_port[port] != true){
    printf("Analog port %d is currently configured as a digital port.\n", port);
    exit(-1);
  }
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = StartAtoD;
  if (DAs_configured == false){
    printf("Must configure D/As before reading voltages\n");
    exit(-4);
  }
  outbuf[2] = port;
  outbuf[3] = block_count; // nbr of blocks
  outbuf[4] = block_size;  // block size 0 for 256
  outbuf[5] = delay;
  send_dlp_command();
  for (i=0; i < 2*get_burst_size(block_count, block_size); i+=2){
    V[i/2]= ((double)inbuf[i+1]*256+inbuf[i])/256*5020/3.996;
  }
  pthread_mutex_unlock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose)
//    printf("Analog voltages from Port A, pin %d:\n", port);
  pthread_mutex_unlock(&verbose_mutex);
}

void printPortPin(enum dlp_port port, enum port_pin pin){
  unsigned char outputString[15]="Port x, Pin x ";
  outputString[5] = 'A' + ((port - PortA) >> 3);
  outputString[12] = '0'+pin;
  outputString[14] = 0x00;
  printf("%s", outputString);
}

int check_CRC(unsigned char chars[],int count){
  int i;
  int crc;
  unsigned int lookup_table[256] = {
    0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
    157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
    35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
    190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
    70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
    219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
    101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
    248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
    140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
    17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
    175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
    50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
    202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
    87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
    233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
    116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
  };

  crc = 0;
  for (i=0; i<count; i++){
    crc = lookup_table[crc ^ chars[i]];
  }
  return crc;
}

void showThermometerLocation(unsigned char* id){
  int i, j;
  i = 0;
  while (thermometerSpecs[i].id[0] != 0x00){
    j = 0;
    while (j < 8 && thermometerSpecs[i].id[j] == id[j]) j++;
    if (j == 8){
      printf("%20s ", (char*)thermometerSpecs[i].location);
      return;
    }
    i++;
  }
  printf("*** Unlisted thermometer ***\n");
}

double thermometerAdjustment(unsigned char* id){
  int i, j;
  i = 0;
  while (thermometerSpecs[i].id[0] != 0x00){
    j = 0;
    while (j < 8 && thermometerSpecs[i].id[j] == id[j]) j++;
    if (j == 8){
      return thermometerSpecs[i].adjustment;
    }
    i++;
  }
  printf("*** Unlisted thermometer ***\n");
  return 0.0;
}

void search_temp_sensors(enum dlp_port port, enum port_pin pin){
  int i,j,k, found, last_group;

  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = SearchDS18S20s;
  outbuf[2] = (unsigned char)(port+pin);
  send_dlp_command();
  last_group = false;
  i = 0;
  while (!last_group){
    k = 0;           
    found = false;
    if (inbuf[i] == 0x02 || inbuf[i] == 0x08 || inbuf[i] == 0x00 || inbuf[i] == 0xc0){
      last_group = true;
      for (j = 1; j < 8; j++){
        if (inbuf[i+j] != inbuf[i]){
          last_group = false;
          break;
        }
      }
      if (last_group){
        switch (inbuf[i]){
          case 0x02:
            printf("Sensor search error: Temperature sensor not detected on pin %d of port %c.\n", pin, portToLetter(port));
            break;
          case 0x08:
            printf("Sensor search error: Short-circuit detected or no pull-up resistor at pin %d of port %c.\n", pin, portToLetter(port));
            break;
	  case 0xC0:
	    printf("Search algorithm reported 0x11 result for test bit pair on pin %d of port %c.\n", pin, portToLetter(port));
//	    break;
          case 0x00:
            break;
          default:
            last_group = false;
        }
      }
    }
    if (!last_group){
      if (check_CRC(&inbuf[i],8)){
        printf("CRC check failed during thermometer search: ");
        for (j=0; j<8; j++)
          printf("%02X", inbuf[i+j]);
        printf("\n");
        exit(-1);
      }
      while (k < thermometer_count){
	if (inbuf[i] == 0x10 || inbuf[i] == 0x28){  // Make sure only work with thermometers
          found = true;                                         // Be sure to test this fully.
          for (j = 0; j < 8; j++){
            if (thermometer[k].id[j] != inbuf[i+j])
              found = false;
          }
	}
        k++;
      }
      if (!found)
        k = thermometer_count;
      else
        k--;
      thermometer[k].port = port;
      thermometer[k].pin  = pin;
      switch (inbuf[i+8]){
        case 0:
          thermometer[k].power = Parasitic;
          break;
        case 0x80:
          thermometer[k].power = External;
          break;
        default:
          thermometer[k].power = CheckFailed;
          printf("Check failed: inbuf=%d\n",inbuf[i+8]);
      }
      if (!found){
        for (j=0; j<8; j++)
          thermometer[k].id[j] = inbuf[i+j];
        thermometer_count++;
      }
      i += 9;
    }
  }
  pthread_mutex_unlock(&dlp_command_mutex);

  pthread_mutex_lock(&verbose_mutex);
  if (verbose){
    for (i = 0; i < thermometer_count; i++){
      if (thermometer[i].port == port && thermometer[i].pin == pin) {
        showThermometerLocation(thermometer[i].id);	
        printf("Thermometer %d is on port %c, pin %d, has id=", i, portToLetter(port), pin);
        for (j = 0; j < 8; j++)
          printf("%02x", thermometer[i].id[j]);
        switch (thermometer[i].power){
          case Parasitic:
            printf(", and uses parasitic power.");
            break;
          case External:
            printf(", and uses external power.");
            break;
          case CheckFailed:
            printf(" - power check failed.");
            break;
          case CheckAmbiguous:
            printf(" - power check was ambiguous.");
            break;
          case NotChecked:
            printf(", and power not been checked.");
          default:
            printf(", and is in impossible state.");
        } 
        printf("\n");
      }
    }
  }
  pthread_mutex_unlock(&verbose_mutex);
}


int already_started(enum dlp_port port, enum port_pin pin){
  int i;
  for (i = T_i_finished; i < T_i_started; i++)
    if (T[i].port == port && T[i].pin == pin)
      return true;
  return false;
}


void read_ds18s20(enum dlp_port port, enum port_pin pin, char* sensor_id){
  struct itimerval conversion_wait;
  struct timeval t;
  unsigned char   response;
  int i,j, first_T_i_started;
  switch (port){
    case PortA:
      if (port == PortA && analog_port[pin]){
        printf("Port A, pin %d must be set to digital for DS18S20 measurement.\n", pin);
        exit(-1);
      }
      if (pin > 5){
        printf("Port A, pins 5-7 are not available for temperature measurement.\n");
        exit(-1);
      }
      break;
    case PortB:
      if (pin != 0 && pin !=5){
        printf("Only pins 0 and 5 of port B can be used for temperature measurement.\n");
        exit(-1);
      }
      break;
    case PortC:
      break;
    case PortD:
      printf("Note: Port D is shared with Channel B of the FTDI USB interface chip.\n");
      break;
    case PortE:
      if (pin < 3)
        printf("Note: Port E pins 0-2 are shared with the LED and control lines of Channel B of the FTDI USB interface chip.\n");
      else {
        printf("Pins 3-7 of Port E are not available.\n");
        exit(-1);
      }
      break;
    }
  while (already_started(port, pin)) sleep(1);  
  first_T_i_started = T_i_started + 1;
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[2] = (unsigned char)(port+pin);
  if (!sensor_id){   // Sensor id is NULL - measure all sensors on this port and pin
     for (j = 0; j < thermometer_count; j++){
       if (thermometer[j].port == port && thermometer[j].pin == pin){
         T_i_started++;
         T[T_i_started].matching = true;
          outbuf[1] = StartMatchDS18S20;
         for (i=3; i< 11; i++){
           outbuf[i] = thermometer[j].id[i-3];
           T[T_i_started].id[i-3] = outbuf[i];
         }
         send_dlp_command();
         response = inbuf[0];
       }
     }
  }
  else if (strlen(sensor_id) != 0){  // 8-byte sensor id is given - measure specific thermometer
    T_i_started++;
    T[T_i_started].matching = true;
    outbuf[1] = StartMatchDS18S20;
    for (i=3; i< 11; i++){
      outbuf[i] = *(sensor_id+i-3);
      T[T_i_started].id[i-3] = outbuf[i];
    }
    send_dlp_command();
    response = inbuf[0];
  }
  else{    // Sensor id is not NULL and not 8-bytes - assume one thermometer on this port and pin
    T_i_started++;
    T[T_i_started].matching = false;
    outbuf[1] = StartDS18S20;
    send_dlp_command();
    response = inbuf[0];
    outbuf[1] = ReadDS18S20Id;
    send_dlp_command();
    for (i=0; i < 8; i++){
      T[T_i_started].id[i] = inbuf[i];
    }
  } 
  pthread_mutex_unlock(&dlp_command_mutex);
  thermometer_port[port+pin-0x28] = false;
  switch (response){
    case 99:
      thermometer_port[port+pin-0x28] = true;
      break;
    case 8:
      printf("\n **** Short-circuit or no pull-up resistor at pin %d of port %c ****\n", pin, portToLetter(port));
      exit(-1);
    case 2:
      printf("\n **** Temperature sensor ");
      if (T[T_i_started].matching){
        printf("(");
        for (i=0; i<8; i++)
          printf("%02x",T[T_i_started].id[i]);
        printf(") ");
      }
      printf("not detected at pin %d of port %c ****\n", pin, portToLetter(port));
      exit(-1);
    default:
      printf("\n **** Unknown Temperature sensor status byte of %02X ****\n", response);
      exit(-1);
  }
  pthread_mutex_lock(&verbose_mutex);
  if (verbose)
//    printf("Temperature sensor ready at pin %d of port %c:\n", pin, portToLetter(port));
  pthread_mutex_unlock(&verbose_mutex);

  // A valid temperature will only be available 750 milliseconds after the above initiation.
  // We now prepare a timer that will send a SIGALRM signal after 750 mS and have that signal
  // cause get_ds18s20() to harvest the temperature result.
  gettimeofday(&t,NULL);
  for (i = first_T_i_started; i < T_i_started + 1; i++){
    T[i].t.tv_sec = t.tv_sec;
    T[i].t.tv_usec = t.tv_usec;
    T[i].port = port;
    T[i].pin  = pin;
  }

  conversion_wait.it_interval.tv_sec  = 0;
  conversion_wait.it_interval.tv_usec = 0;
  conversion_wait.it_value.tv_sec     = 0;
  conversion_wait.it_value.tv_usec    = 750000;
  setitimer(ITIMER_REAL, &conversion_wait, NULL);
  // When this timer counts down to zero, get_ds18s20_result() will be run.
  // Until then, we can do other things as long as they don't use the port/pin
  // being used for the temperature measurement.
}


// This is only to be called by the setitimer() call in read_ds18s20().
// Do not call it separately.  read_ds18s20 initiates a new reading and 
// get_ds18s20_result() gets the answer 750 milliseconds later.
void get_ds18s20_result(int signal){
  int res;
  struct timeval t;
  struct itimerval conversion_wait;
  int additional_time;
  char time_string[26];
  int i;
  T_i_finished++;
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[2] = (unsigned char)(T[T_i_finished].port + T[T_i_finished].pin);
  if(T[T_i_finished].matching){
    outbuf[1] = ReadMatchDS18S20;
    for (i=3; i < 11; i++)
      outbuf[i] = T[T_i_finished].id[i-3];
  }
  else{
    outbuf[1] = ReadDS18S20;
  }
  send_dlp_command();

  if (check_CRC(inbuf,9)){
    printf("CRC check failed during thermometer reading - received bytes: ");
    for (i=0; i<9; i++)
      printf("%02X", inbuf[i]);
    printf("\n");
    exit(-1);
  }
  if (T[T_i_finished].id[0] == 0x10){
    switch ((int)inbuf[1]){
      case 0:
        res = (inbuf[0] & 0xFE);
        break;
      case 255:
        res = -1 - 255 + inbuf[0];
        break;
      default:
        printf("Temperature high byte is %02X, but should be 0 or 0xFF.\n", inbuf[1]);
        exit(-1);
    }
    T[T_i_finished].T_Celsius = 0.5*(double)res - 0.25 + (double)(inbuf[7]-inbuf[6])/inbuf[7]
	  +thermometerAdjustment(T[T_i_finished].id);
  } else if(T[T_i_finished].id[0] == 0x28){
    if (inbuf[1] > 0x0F)
      res = (0xFFFF0000 | (inbuf[1] << 8)) | inbuf[0];
    else
      res = (inbuf[1] << 8) | inbuf[0];
    T[T_i_finished].T_Celsius = ((double)res/16 + thermometerAdjustment(T[T_i_finished].id));
  }
  
  pthread_mutex_unlock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose){
    asctime_r(localtime(&T[T_i_finished].t.tv_sec), time_string);
    time_string[19] = 0;
    showThermometerLocation(T[T_i_finished].id);
    printf("%3d: %s: Thermometer (id=", T_i_finished, &time_string[4]);
    for (i = 0; i< 8; i++)
      printf("%02x", T[T_i_finished].id[i]);
    printf(") at port %c pin %d reports T = ", portToLetter(T[T_i_finished].port),  T[T_i_finished].pin);
    if (preferred_T_scale == Celsius)
      printf("%5.2f degrees C\n", T[T_i_finished].T_Celsius);
    else
      printf("%5.1f degrees F\n", 9*T[T_i_finished].T_Celsius/5 + 32);
  }
  pthread_mutex_unlock(&verbose_mutex);

  // Set up timer for any additional thermometer readings that have been started.
  gettimeofday(&t,NULL);
  if (T_i_finished < T_i_started){
    additional_time = 750 - ((int)(t.tv_sec  - T[T_i_finished].t.tv_sec )*1000
                          +  (int)(t.tv_usec - T[T_i_finished].t.tv_usec)/1000);
    if (additional_time <= 0)
      additional_time = 1;

    conversion_wait.it_interval.tv_sec  = 0;
    conversion_wait.it_interval.tv_usec = 0;
    conversion_wait.it_value.tv_sec     = 0;
    conversion_wait.it_value.tv_usec    = additional_time*1000;
    setitimer(ITIMER_REAL, &conversion_wait, NULL);
  }
}

enum digital_pin_state read_pin_state(enum dlp_port port, enum port_pin pin){
  unsigned char response;
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = LineIn;
  outbuf[2] = (unsigned char)(port + pin);
  send_dlp_command();
  response = inbuf[0];
  pthread_mutex_unlock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose){
    printf("Pin %d of port %c is ", pin, portToLetter(port));
    if (response == 0)
      printf("low\n");
    else
      printf("high\n");
  }
  pthread_mutex_unlock(&verbose_mutex);
  switch ((int)response){
    case 0:
      return Low;
    case 1:
      return High;
    default:
      printf("Pin %d of port %c returned an improper value: %02X\n", pin, portToLetter(port), response);
      exit(-1);
  }
}

void set_pin_state(enum dlp_port port, enum port_pin pin, enum digital_pin_state state){
  if (port == PortA && pin > Pin5){
    printf("Pins 6 and 7 of Port A are not available on the DLP2232PB-G.\n");
    exit(-1);
  }
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = LineOut;
  outbuf[2] = (unsigned char)(port + pin);
  outbuf[3] = state;
  send_dlp_command();
  pthread_mutex_unlock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose){
    printf("Pin %d of port %c has been set %s\n", pin, portToLetter(port), stateToWord(state));
    if (port == PortA && pin == Pin4)
      printf("Remember: This pin is an open-collector output, not TTL.\n");
  }
  pthread_mutex_unlock(&verbose_mutex);
}

void* show_PIC16F877A_eeprom(){
  int i;
  unsigned char response[256];
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = EEPROM_Read;
  for (i=0; i < 256; i++){
    outbuf[2] = i;
    send_dlp_command();
    response[i] = inbuf[0];
  }
  pthread_mutex_unlock(&dlp_command_mutex);

  pthread_mutex_lock(&verbose_mutex);
  printf("Current PIC16F877A EEPROM data displayed with\n  addresses relative to EEPROM start at 0x2100:");
  for (i = 0; i < 256; i++) {
    if (i % 16 == 0)
      printf("\n%02X: ", i);
    printf("%02X ",response[i]);
  }
  printf("\n");
  pthread_mutex_unlock(&verbose_mutex);
  return NULL;
}

int read_PIC16F877A_eeprom(unsigned char address){
  unsigned char response;
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = EEPROM_Read;
  outbuf[2] = address;
  send_dlp_command();
  response = inbuf[0];
  pthread_mutex_unlock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose)
    printf("PIC16F877A EEPROM address %02X holds %02X\n", address, response);
  pthread_mutex_unlock(&verbose_mutex);
  return response;
}

void write_PIC16F877A_eeprom(unsigned char address, unsigned char value){
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = EEPROM_Write;
  outbuf[2] = address;
  outbuf[3] = value;
  send_dlp_command();
  pthread_mutex_unlock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose)
    printf("PIC16F877A EEPROM address %02X has been set to %02X\n", address, value);
  pthread_mutex_unlock(&verbose_mutex);
}

void read_port(enum dlp_port port, unsigned char block_count, unsigned char block_size, unsigned char delay, int* state[], int channels){
  int pin;
  int i;
  pthread_mutex_lock(&dlp_command_mutex);
  switch (port){
    case PortA:
      if (port_A_is_all_digital == false)
        setup_a_to_d(Vdd_Vss, 0, 32);
      outbuf[1] = ReadPortA;
      break;
    case PortC:
      outbuf[1] = ReadPortC;
      break;
    case PortD:
      outbuf[1] = ReadPortD;
      break;
    default:
      printf("read_port is only for Ports A, C, and D of the PIC16F877A\n");
      exit(-1);
  }
  outbuf[2] = block_count;
  outbuf[3] = block_size;
  outbuf[4] = delay;
  send_dlp_command();
  for (i = 0; i < get_burst_size(block_count, block_size); i++){
    for (pin = 0; pin < channels; pin++){
      state[i][pin] = inbuf[i] >> pin & 1;
    }
  }
  pthread_mutex_unlock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose)
//    printf("Digital pin readings from Port %c:\n", portToLetter(port));
  pthread_mutex_unlock(&verbose_mutex);
}

void set_port(enum dlp_port port, unsigned char bits){
  pthread_mutex_lock(&dlp_command_mutex);
  switch (port){
    case PortA:
      if (port_A_is_all_digital == false)
        setup_a_to_d(Vdd_Vss, 0, 32);
      outbuf[1] = WritePortA;
      break;
    case PortC:
      outbuf[1] = WritePortC;
      break;
    case PortD:
      outbuf[1] = WritePortD;
      break;
    default:
      printf("write_port is only for Ports A, C, and D of the PIC16F877A\n");
      exit(-1);
  }
  outbuf[2] = bits;
  send_dlp_command();
  pthread_mutex_lock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose)
    printf("Port %c pins have been set to %02X\n", portToLetter(port), bits);
  pthread_mutex_unlock(&verbose_mutex);
}

int main(int argc, char** argv) {
  double temperature_average = 0;
  double variance = 0.0;
  double standard_deviation;
  int i;
  verbose = true;
  sio_init();
//  echo_byte('C');
//  identify();
  verbose = false;
  search_temp_sensors(PortB, Pin0);
  search_temp_sensors(PortB, Pin5);
  verbose = true;
//exit(-1);
/*
  read_ds18s20(PortB, Pin0, "\x10\x48\xb6\x35\x01\x08\x00\xe8");
  read_ds18s20(PortB, Pin0, "\x10\x18\xbf\x35\x01\x08\x00\x35");
  read_ds18s20(PortB, Pin0, "\x10\xec\xbe\x35\x01\x08\x00\x56");
  read_ds18s20(PortB, Pin0, "\x10\xe2\xae\x35\x01\x08\x00\x39");
  read_ds18s20(PortB, Pin0, "\x10\x96\xb4\x35\x01\x08\x00\xbc");
  read_ds18s20(PortB, Pin0, "\x10\xf5\xad\x35\x01\x08\x00\xa9");
  read_ds18s20(PortB, Pin0, "\x10\xc3\x9c\x35\x01\x08\x00\xbf");
  read_ds18s20(PortB, Pin0, "\x10\x4b\xb5\x35\x01\x08\x00\xff");
  read_ds18s20(PortB, Pin0, "\x10\xc7\xab\x35\x01\x08\x00\xb6");
  read_ds18s20(PortB, Pin0, "\x10\x7f\xb1\x35\x01\x08\x00\xd1");
  read_ds18s20(PortB, Pin0, "\x28\xe0\xe4\x14\x01\x00\x00\x6f");
  read_ds18s20(PortB, Pin0, "\x28\x18\xe0\x14\x01\x00\x00\xa3");
  read_ds18s20(PortB, Pin0, "\x28\xb4\xf9\x14\x01\x00\x00\x0d");
  read_ds18s20(PortB, Pin0, "\x28\x9c\xfd\x14\x01\x00\x00\x05");
  read_ds18s20(PortB, Pin0, "\x28\xc2\xf8\x14\x01\x00\x00\xea");
  read_ds18s20(PortB, Pin0, "\x28\xd2\xde\x14\x01\x00\x00\xd5");
  read_ds18s20(PortB, Pin0, "\x28\xde\xec\x14\x01\x00\x00\xaf");
  read_ds18s20(PortB, Pin0, "\x28\xa1\xe2\x14\x01\x00\x00\xb1");
  read_ds18s20(PortB, Pin0, "\x28\x85\xf7\x14\x01\x00\x00\x75");
  read_ds18s20(PortB, Pin0, "\x28\xd7\xe4\x14\x01\x00\x00\x07");
  read_ds18s20(PortB, Pin0, "\x28\xef\x03\x15\x01\x00\x00\x4f");
  read_ds18s20(PortB, Pin5, "\x28\x18\xee\x14\x01\x00\x00\x01");
  read_ds18s20(PortB, Pin5, "\x28\xd8\xf4\x14\x01\x00\x00\x5f");
  read_ds18s20(PortB, Pin5, "\x28\xd8\xfd\x14\x01\x00\x00\xac");
  read_ds18s20(PortB, Pin5, "\x28\x94\xfc\x14\x01\x00\x00\x69");
  read_ds18s20(PortB, Pin5, "\x28\xf4\x00\x15\x01\x00\x00\xa2");
  read_ds18s20(PortB, Pin5, "\x28\xcc\x00\x15\x01\x00\x00\xee");
  read_ds18s20(PortB, Pin5, "\x28\x96\xfb\x14\x01\x00\x00\x56");
  read_ds18s20(PortB, Pin5, "\x28\x11\xde\x14\x01\x00\x00\x13");
  read_ds18s20(PortB, Pin5, "\x28\x89\xf4\x14\x01\x00\x00\x46");
  read_ds18s20(PortB, Pin5, "\x28\xb9\xe7\x14\x01\x00\x00\x99");
  read_ds18s20(PortB, Pin5, "\x28\xc5\xe2\x14\x01\x00\x00\xae");
  read_ds18s20(PortB, Pin5, "\x28\xfd\xef\x14\x01\x00\x00\x0e");
  read_ds18s20(PortB, Pin5, "\x28\xcb\xea\x14\x01\x00\x00\x83");
  read_ds18s20(PortB, Pin5, "\x28\x97\xef\x14\x01\x00\x00\x02");
*/
  read_ds18s20(PortB, Pin0, NULL);
  read_ds18s20(PortB, Pin5, NULL);

  temperature_average = 0;
  while (T_i_finished != thermometer_count) sleep(1);
  for (i=1; i <= thermometer_count; i++)
    temperature_average += T[i].T_Celsius;
  temperature_average = temperature_average/thermometer_count;
  for (i=1; i <= thermometer_count; i++)
    variance += pow((T[i].T_Celsius - temperature_average), 2.0);
  standard_deviation = sqrt(variance/(thermometer_count-1));
  printf("Average temperature = %6.2f +- %3.2f degrees C ",temperature_average, standard_deviation);
  printf(" = %6.2f +- %3.2f degrees F\n",9*temperature_average/5+32, 9*standard_deviation/5);


  sio_deinit();
  return 0;
}
