#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "fanIOCtls.h"

static void port_init(int envIOBase);

static int fp = 0;

//
// SHT1x
//
static int HUMPort;     // '= &H0 '1110
static int HUMDDrBit;   // '= &H1 '0001
static int HUMCLKBit;   // '= &H4 '0100
static int HUMDinBit;   // '= &H8 '1000
static int HUMDoutBit;  // '= &H2 '0010
static long HUMShadow=0;// '= &H2 '0010
static void s_connectionreset();
static void s_transstart();
static int s_measure(float *, int checksum,int mode);
static int s_write_byte(int);
static void s_read_byte(float * , int, int ack);

// Initalization

int envOpen() {
	fp = open("/dev/fan0", O_NONBLOCK);
	if(fp == 0) return -1;
	else {
		port_init(ioctl(fp, GET_PORTS_CTL));
		return 0;
	}
}

int envClose() {
	return close(fp);
}

int envDiagLED(int state) {
	return ioctl(fp, DIAG_LED_CTL, state);
}


// I/O

int envReadCfg(void) {
	return ioctl(fp, READ_CFG_CTL);
}

int envReadDig(void) {
	return ioctl(fp, READ_DIG_CTL);
}

int envSetDig(int value) {
	return ioctl(fp, SET_DIG_CTL, value);
}

int envReadAna(int channel) {
	return ioctl(fp, READ_ANA_CTL, channel);
}

int envFan(int value) {
	return ioctl(fp, FAN_CTL, value);
}

int envSetEnables(int value) {
	return ioctl(fp, SET_ENABLES_CTL, value);
}


// Temperature

void envInitTemp(int ch) {
	ioctl(fp, INIT_TEMP_CTL, ch);
}

void envReadTemp(int *TempNow, int *TempHi, int *TempLo, int *TempCfg, int ch) {
	int temps[5];
	temps[0] = *TempNow;
	temps[1] = *TempHi;
	temps[2] = *TempLo;
	temps[3] = *TempCfg;
	temps[4] = ch;
	
	int retval = ioctl(fp, READ_TEMP_CTL, temps);
	
	*TempNow = temps[0];
	*TempHi  = temps[1];
	*TempLo = temps[2];
	*TempCfg = temps[3];
}

void envWriteTemp (int TempHi, int TempLo, int ch) {
	int temps[5] = { 0,
			 TempHi,
			 TempLo,
			 0,
			 ch };
	int retval = ioctl(fp, WRITE_TEMP_CTL, temps);
}


// Humidity and Temperature from the SHT1x

double envHumidity(void) {
	float raw_hum=0;
	int er=0;
	int checksum=0;
	const float C1=-4.0;		// for 12 Bit
	const float C2=+0.0405;		// for 12 Bit
	const float C3=-0.0000028;	// for 12 Bit
	s_connectionreset();	//' reset the sensor

	//'measure humidity. 0 for humidity 1 for temperature
	er = er + s_measure(&raw_hum,checksum, 0); 
	if (er != 0) {
		s_connectionreset();      //in case of an error: connection reset
		return -1;
	}
	else {
		//calc. humidity from ticks to [%RH]
		raw_hum=C3*raw_hum*raw_hum + C2*raw_hum + C1;     
		if (raw_hum<0.1) return 0;
		if (raw_hum>100) return 100;
		return raw_hum;
		//return humidity here.
        
		//' Calculate humidity % value after getting the correct MSB and LSB
	}
	//----------wait approx. 0.8s to avoid heating up SHTxx------------------------------ timer should take care of this
}

double envTemperature(int tmp) {
	float raw_tmp=0;
	int er=0;
	int checksum=0;
	s_connectionreset();       //' reset the sensor
	er = er + s_measure(&raw_tmp,checksum, 1); //'measure humidity. 0 for humidity 1 for temperature
	if (er != 0) {
		s_connectionreset();                 //in case of an error: connection reset
		return -1;
	}
	else {
		raw_tmp=raw_tmp * 0.01 - 40;//calc temperature
		if(tmp==0) return raw_tmp;
		//convert to fahrenheit
		if(tmp==1) {
			raw_tmp=((9./5.)*raw_tmp + 32.);
			return raw_tmp;
		}
		return -1;
	}
//----------wait approx. 0.8s to avoid heating up SHTxx------------------------------ timer should take care of this
}



// --------------------- Non exported utility functions ------------------------- 

static void port_init(int envIOBase)
{
   HUMPort = 0x0;   //'1110   'Indicates register 0
   //
   // The BusIOWrite and Read functions take the address, not an offset.  So,
   // add the base address to the offset below to convert to an absoulute
   // address.
   //
   HUMPort += envIOBase;  
   
   HUMDDrBit = 0x1; //'0001   'Indicates least significant bit in register 0

   //'Indicates the 2nd bit in register 0 (from lsb if lsb=0)
   HUMCLKBit = 0x4; //'0100   

   //'Indicates the 3rd bit in register 0 (from lsb if lsb=0)
   HUMDinBit = 0x8; //'1000   

   //'Indicates the 1st bit in register 0 (from lsb if lsb=0)
   HUMDoutBit = 0x2; //'0010  
}

// Local wrapper
static inline int BusIOWrite8(int addr, int value) {
	int busWrite[2];
	busWrite[0] = addr;
	busWrite[1] = value;
	return ioctl(fp, BUS_WRITE8_CTL, busWrite);
}

static inline int BusIORead8(int addr) {
	return ioctl(fp, BUS_READ8_CTL, addr);
}

static int s_measure(float *raw_hum, int checksum, int mode)
{
   // Humidity code changed 08/11/05 ht & dtc may not work anymore

   int er=0;
   int I;
   //clock_t ticks1, ticks2;
   long portvalue=0;
   //double myhum;
   s_transstart();                //' transmission start
   switch(mode)
   {					//' send command to sensor
      case 0: //'Humidity
	 er = er + s_write_byte(0x05);
	 break;
      case 1: //'Temperature
	 er = er + s_write_byte(0x03);
	 break;
      default:
				//should never get here
	 er=1;
	 break;       
   }
        
   HUMShadow = HUMShadow & (~ HUMDDrBit);  //'Set DDR for input
   BusIOWrite8(HUMPort, HUMShadow);

   I = 0;
   //ticks1=clock();
   //ticks2=ticks1;
   usleep(3000000);  //pwait for 3 secs
   portvalue = BusIORead8(HUMPort);
   
        
   if ((portvalue & HUMDinBit) != 0) 
   {
      er = er + 1;
   }
    
   s_read_byte(raw_hum,15,1);                  //'read the first byte (MSB)
   //MSB_txt.Text = BitDisp$(p_value)
   s_read_byte(raw_hum,7,1);                  //'read the second byte (LSB)
    
   //calc_sth11 (p_value);
   return er;                              //'return error status
}



static void s_transstart()
{    
    
   HUMShadow= HUMShadow | (HUMDDrBit);  //Set DDR for output
   BusIOWrite8(HUMPort,HUMShadow);
    
   HUMShadow = HUMShadow | (HUMDoutBit);     //' Raise  Data
   BusIOWrite8(HUMPort, HUMShadow);
     
   HUMShadow = HUMShadow & (~HUMCLKBit);     //' Lower Clock
   BusIOWrite8(HUMPort,HUMShadow);
    
   if (1); //nop

   HUMShadow= HUMShadow | (HUMCLKBit);      //' Raise Clock
   BusIOWrite8(HUMPort, HUMShadow);

   //'Wait 1 clock cycle
   if(1);

   HUMShadow = HUMShadow & (~HUMDoutBit);     //' Lower  Data
   BusIOWrite8(HUMPort,HUMShadow);
    
   //'Wait 1 clock cycle
   if (1);
        
   HUMShadow = HUMShadow & (~HUMCLKBit);     //' Lower Clock
   BusIOWrite8(HUMPort, HUMShadow);
    
   //'Wait 1 clock cycle
   if(1);
   //'Wait 1 clock cycle
   if(1);
   //'Wait 1 clock cycle
   if(1);
    
   HUMShadow = HUMShadow | (HUMCLKBit);      //' Raise Clock
   BusIOWrite8(HUMPort, HUMShadow);
   //'Wait 1 clock cycle
   if(1);

   HUMShadow = HUMShadow | (HUMDoutBit);     //' Raise  Data
   BusIOWrite8(HUMPort, HUMShadow);
    
   //'Wait 1 clock cycle
   if(1);

   HUMShadow = HUMShadow & (~HUMCLKBit);     //' Lower Clock
   BusIOWrite8(HUMPort, HUMShadow);
}

static int s_write_byte(int value)
{
   int er=0;
   int i;
   long portvalue=0;
   HUMShadow = HUMShadow | (HUMDDrBit);  //'Set DDR for output
   BusIOWrite8(HUMPort, HUMShadow);
    
   i=0x80;
   while (i > 0)
   {
    
      if (i & value) 
      {
	 HUMShadow = HUMShadow | (HUMDoutBit);     //' Raise  Data
	 BusIOWrite8(HUMPort, HUMShadow);
      }
      else
      {
	 HUMShadow = HUMShadow & (~HUMDoutBit);     //' Lower  Data
	 BusIOWrite8(HUMPort, HUMShadow);   
      }
      HUMShadow = HUMShadow | (HUMCLKBit);      //' Raise Clock
      BusIOWrite8(HUMPort, HUMShadow);
      
      //'Wait 1 clock cycle
      if(1);
      //'Wait 1 clock cycle
      if(1);
      //'Wait 1 clock cycle
      if(1);
        
      if (i == 1) 
      {
	 HUMShadow = HUMShadow & (~HUMDDrBit);  //'Set DDR for input to release the data line for the last command bit
	 BusIOWrite8(HUMPort, HUMShadow);
      }
        
      HUMShadow = HUMShadow & (~HUMCLKBit);     //' Lower Clock
      BusIOWrite8(HUMPort, HUMShadow);
      i = i / 2;                                   //'shift bit for masking
   }
    
   HUMShadow = HUMShadow & (~HUMDDrBit);  //'Set DDR for input to release the data line
   BusIOWrite8(HUMPort,HUMShadow);
    
   HUMShadow = HUMShadow | (HUMCLKBit);      //' Raise Clock ---- clk #9 for ack
   BusIOWrite8(HUMPort, HUMShadow);
    
    
   HUMShadow = HUMShadow & (~HUMDDrBit);  //'Set DDR for input
   BusIOWrite8(HUMPort, HUMShadow);
    
   portvalue = BusIORead8(HUMPort); //' Read Port
   if ((portvalue & HUMDinBit) > 0)               //'check ack (DATA will be pulled down by SHT11)
   {
      er = er + 1; //'error 
   }
    
   HUMShadow = HUMShadow & (~HUMCLKBit);     //' Lower Clock
   BusIOWrite8(HUMPort, HUMShadow);  
   return er;

}
//----------------------------------------------------------------------------------
// communication reset: DATA-line=1 and at least 9 SCK cycles followed by transstart
//       _____________________________________________________         ________
// DATA:                                                      |_______|
//          _    _    _    _    _    _    _    _    _        ___     ___
// SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______|   |___|   |______

static void s_connectionreset()
{
   int i;
   
   HUMShadow = HUMShadow | (HUMDDrBit);  //'Set DDR for output
   BusIOWrite8(HUMPort, HUMShadow);
    
     
   HUMShadow = HUMShadow | (HUMDoutBit);     //' high  Data
   BusIOWrite8(HUMPort,HUMShadow);
     
     
   HUMShadow = HUMShadow & (~HUMCLKBit);     //' Lower Clock
   BusIOWrite8(HUMPort, HUMShadow);
     
   for(i= 0;i<=8;i++)  //' 9 Cycles
   {
      HUMShadow = HUMShadow | HUMCLKBit;
      BusIOWrite8(HUMPort,HUMShadow);                   //    ' Raise Clock
        
      HUMShadow = HUMShadow & (~HUMCLKBit);
      BusIOWrite8(HUMPort,HUMShadow);                    //   ' Lower Clock
        
   }
}

static void s_read_byte(float *raw_hum,int bit_pos,int ack) 
{
   unsigned char I;
	
   long portvalue;
        
   HUMShadow = HUMShadow & (~HUMDDrBit);  //'Set DDR for input to release data line
   BusIOWrite8(HUMPort, HUMShadow);
     
   I = 0x80;
    
   while ((I) > 0)
   {
      HUMShadow = HUMShadow | (HUMCLKBit);      //' Raise Clock
      BusIOWrite8(HUMPort, HUMShadow);
              
      portvalue = BusIORead8(HUMPort);//    ' Read Port
      if ((portvalue & HUMDinBit) > 0)//                       'check bit
      {
                
	 *raw_hum+=(float)(2<<(bit_pos-1));
      }
		
      HUMShadow = HUMShadow & (~HUMCLKBit);     //' Lower Clock
      BusIOWrite8(HUMPort, HUMShadow);
      //' MsgBox "lower clock"
      I = I / 2;                       //                'shift bit for masking
      bit_pos--;
   }
    
   HUMShadow = HUMShadow | (HUMDDrBit) ;// 'Set DDR for output
   BusIOWrite8(HUMPort, HUMShadow);
    
   
   if (ack)
   {
      HUMShadow = HUMShadow & (~HUMDoutBit);  //   ' Lower  Data ---- in case of "ack==1" pull down DATA-Line since checksum is not used
      BusIOWrite8(HUMPort, HUMShadow);
     
   }
   else
   {
      HUMShadow = HUMShadow | (HUMDoutBit); //    ' Raise  Data
      BusIOWrite8(HUMPort, HUMShadow);
        
   }
   HUMShadow = HUMShadow | (HUMCLKBit);      //' Raise Clock clk #9 for ack
   BusIOWrite8(HUMPort, HUMShadow);
    
    
   if(1);
   if(1);
   if(1);
    
   HUMShadow = HUMShadow & (~HUMCLKBit);     //' Lower Clock
   BusIOWrite8(HUMPort, HUMShadow);
    
   HUMShadow = HUMShadow & (~HUMDDrBit);//  'Set DDR for input to release data line
   BusIOWrite8(HUMPort, HUMShadow);
    
}
