#include <Wire.h>
//#include <Wiring.h>
//#include <Wire.h>
#include <Arduino.h>
#include <JeeLib.h>
#include <LibHumidity.h>

// sets up the clock port and the clock module
PortI2C myport (1 /*, PortI2C::KHZ400 */);
DeviceI2C rtc (myport, 0x68);

//sets up the port of the relay
Port relays (2);

//sets up the port of the uart and the uart module
PortI2C i2cBusUart (3);
UartPlug uart (i2cBusUart, 0x4D);

//sets up the port of the memory and the memory module
PortI2C i2cBusMem (4);
MemoryPlug mem (i2cBusMem);
MemoryStream stream (mem);

//sets up the Sensirion sensor for temperature and humidity
LibHumidity humidity_Temp = LibHumidity(0);

MilliTimer sendTimer; //setup the time for the radio communications



// must be defined in case we're using the watchdog for low-power waiting
ISR(WDT_vect) { 
  Sleepy::watchdogEvent(); 
}


//global variables
char bufData[12];//buffer for the data is going to go into the memory

void setup()
{
  Serial.begin(9600);
  uart.begin(19200);


  // initialize the radio for this node
  rf12_initialize(2, RF12_915MHZ, 1); 

  //initialize the relays
  relays.digiWrite(0);
  relays.mode(OUTPUT);
  relays.digiWrite2(0);
  relays.mode2(OUTPUT);

  //uncomment the next two lines when you wat to use the memory for the first time
  //this will start the directory to the page 0 adress 0
  //after compiling and running it for 2 seconds comment them again and compile the code again
  //int buf[2]={0,0};
  //mem.save(0,0,buf,4);

  //uncomment he following line to set up the date and time for the clock for the first time
  //run the code for the 1 seconds comment the following line again and compile
  //if using loboviz set clock to GMT
  // (year,month,dsy,hour,minute,second)
  //setDate (15,7,30,20,37,0);
  
  /*while(1)
  {
    if(Serial.read()=='1')
    {
      char buf[12];
      load_Memory(buf);
    }
  }*/
  delay(10000);
  Serial.end();
}

void loop()
{  
  read_Save();
}

/******************************************************************
 * Function name: read_Save(void)
 * type: void
 * input parameters: void
 * output parameter: NULL
 * Description: function that records a Co2 value from the telaire sensor
 * and stores it into memory
 * Creators: MBARI
 ********************************************************************/
void read_Save()
{
  boolean cycle=true;
  int co2Value=0;
  int pumpingCounter=3; // number of minutes until the pump turn on
  int pumpRunTime=5000; //number of miliseconds for which the pump will run
  int timeForMeasurements=20;
  boolean idle=true; //number of minutes it will pass before the reading happens
  // the sensor will turn on 10 minutes before that in order to warm up
  int counter=0;
  byte time[6];
  int i=0;

  //send the Telair to sleep
  relays.digiWrite2(1);//turn on the sensor
  delay(10000);
  Telaire_IdleMode(idle);

  //clearing the memory data array
  for(int j=0;j<12;j++)
  {
    bufData[j]=NULL;
    j++;
  }
  
  while(cycle)
  { 
    //check for communication
    rf12_sleep(-1);
    rf12_recvDone();
    receive_Command();
    rf12_sleep(1);
    rf12_sleep(0);
    
  
    
    //sleep for n number of seconds if the pump does not turn on during the iteration
    if(pumpingCounter!=3)
    {
      Sleepy::loseSomeTime(pumpRunTime);
    }

    // turn the pump on for n number of seconds
    else if(pumpingCounter==3)
    {
      relays.digiWrite(1);
      Sleepy::loseSomeTime(pumpRunTime);
      relays.digiWrite(0);
      pumpingCounter=0;
    }
    Sleepy::loseSomeTime((word)(60000-pumpRunTime));
    counter++;
    pumpingCounter++;

    if(counter==timeForMeasurements-10)
    {
      Telaire_IdleMode(!idle);
    }

    if(counter==timeForMeasurements)
    {
      Serial.begin(9600);
      delay(20);
      co2Value=read_Co2();
      while((co2Value>2000||co2Value<=0)&&i<=10)
      {
        co2Value=read_Co2();
        if(i==10)
        {
          co2Value=1;
        }
        i++;
        delay(10000);
      }
      float temp=get_Temperature();
      delay(20);
      float humidity=get_Humidity();

      //getting the date and time in the buffer for the memory
      getDate(time);
      bufData[0]=time[1];
      bufData[1]=time[2];
      bufData[2]=time[0];
      bufData[3]=time[3];
      bufData[4]=time[4];
      bufData[5]=time[5];
      delay(20);

      Serial.print(time[1]);
      Serial.print("/");
      Serial.print(time[2]);
      Serial.print("/");
      Serial.print(time[0]);
      Serial.print(" ");
      Serial.print(time[3]);
      Serial.print(":");
      Serial.print(time[4]);
      Serial.print(":");
      Serial.print(time[5]);
      Serial.print(" ");
      Serial.print(co2Value);
      Serial.print(" ");
      Serial.print(temp);
      Serial.print(" ");
      Serial.print(humidity);
      Serial.println();
      saved(bufData);
      delay(10);
      cycle=false;
      counter=0;
      Serial.end();
    }
  }
}



/******************************************************************
 * Function name: load_Memory(char [])
 * type: void
 * input parameters: char a[]: array of n bytes which will be loaded
 * output parameter: NULL
 * Description: Loads the the information that is on memory and transmit 
 * it thorugh radio communications to a base station. 
 * Creators: MBARI
 ********************************************************************/

void load_Memory(char a[]) //function for loading characters in a fast way
{
  int finish[2];
  int remember[2]={
    1,0    };
  if(mem.isPresent())
  {
    mem.load(0,0,finish,4);
    delay(20);
    if(finish[0]!=0 && finish[1]!=0)
    {
      while((remember[0]!=finish[0])||(remember[1]!=finish[1]))
      {
        mem.load(remember[0],remember[1],a,12);
        delay(50);
        send_Radio(a,12);
        delay(1);
        transform(a);
        if(remember[1]<243)
        {
          remember[1]+=12;
        } 
        if(remember[1]>243)
        {
          remember[0]+=1;
          remember[1]=0;
        }
      }
    }
    else
    {
      Serial.println("memory empty");
      uint8_t checkDeletion[1]={50};
      rf12_sendStart(1,checkDeletion,1);
    }
  }
}

/******************************************************************
 * Function name: erase_Memory()
 * type: void
 * input parameters: void
 * output parameter: NULL
 * Description: Erases the memory.resets the directory making the memory
 * to start overriding previous values every time it saves 
 * new data
 * Creators: MBARI
 ********************************************************************/
void erase_Memory()
{
  int erase[2]={0,0};
  if(mem.isPresent())
  {
    mem.save(0,0,erase,4);
    Serial.println("memory erased");
  } 
}


/******************************************************************
 * Function name: receive_Command()
 * type: void
 * input parameters: NIULL
 * output parameter: NULL
 * Description: Checks if there is any radio communication command  
 * from the basen station and takes and action according
 * to the command
 * Creators: MBARI
 ********************************************************************/
void receive_Command()
{
  uint8_t buf[1];
  char buffer[12];
  byte InfoReceived=0;
  byte needToSend=0;
  int i=0;
  boolean out=true;
  
  Serial.begin(9600);
  delay(20);
  while(i<50 && out)//i<2000 && out
  {
    i++;

    if (rf12_recvDone() && rf12_crc == 0) // received good data if true
    {
      buf[0]=rf12_data[0];
      InfoReceived=1;
      needToSend=1;
    }

    if(needToSend && rf12_canSend() && InfoReceived)
    {
      rf12_sendStart(1, buf,1);
      InfoReceived=0;
      needToSend = 0;
      out=false;
      if(buf[0]==1)
      {
        load_Memory(buffer);
      }
      if(buf[0]==2)
      {
        erase_Memory();
      }
    }
  }
  Serial.end();
  delay(20);
}

/******************************************************************
 * Function name: read_Co2(void)
 * type: int
 * input parameters: void
 * output parameter: int
 * Description: tells tehe sensor to start doing measurments and store 
 * the two bytes in a varible called co2value
 * Creators: MBARI
 ********************************************************************/
int read_Co2()
{
  byte buf[56];
  byte i=0;
  int co2Value;

  uart.write(0xFF);
  uart.write(0xFE);
  uart.write(0x02);
  uart.write(0x02);
  uart.write(0x03);
  delay(20);

  // put he two bit answer from the sensor into a buffer
  while(uart.available())
  {
    buf[i]=uart.read();
    i++;
  }

  //save the two bits of Co2 into memory buffer
  bufData[6]=buf[3];
  bufData[7]=buf[4];

  // do a bitwise operation to acomodate the two bits
  // and make them an integer
  co2Value=0;
  co2Value|=buf[3] & 0xFF;
  co2Value=co2Value<<8;
  co2Value|=buf[4] & 0xFF;
  return co2Value;
}

/******************************************************************
 * Function name: get_Temp(void)
 * type: float
 * input parameters: void
 * output parameter: float
 * Description: tells the Sensirion sensor to start doing measurements 
 * of temperature, after that it divides the integer and 
 * float part of the number in two bytes so they can be 
 * stored into memory. The function uses the library 
 * LibHumidity from www.moderndevices.com in order to control
 * the sensor
 * Creators: MBARI
 ********************************************************************/
float get_Temperature()
{
  float temp;
  byte tempInt;
  byte tempFloat;

  /* get the measurement of temperature and divides them into an 
   integer part and a float part*/
  temp=humidity_Temp.GetTemperatureC();
  tempInt=floor(temp);
  tempFloat=(temp*100)-(tempInt*100);

  //get the date into memroy
  bufData[8]=tempInt;
  bufData[9]=tempFloat;

  return temp;
}

/******************************************************************
 * Function name: get_Humidity(void)
 * type: float
 * input parameters: void
 * output parameter: float
 * Description: tells the Sensirion sensor to start doing measurements 
 * of humidity, after that it divides the integer and 
 * float part of the number in two bytes so they can be 
 * stored into memory. The function uses the library 
 * LibHumidity from www.moderndevices.com in order to control
 * the sensor
 * Creators: MBARI
 ********************************************************************/
float get_Humidity()
{
  float humidity;
  byte humidityInt;
  byte humidityFloat;

  /* get the measurement of humidity and divides them into an 
   integer part and a float part*/
  humidity=humidity_Temp.GetHumidity(); 
  humidityInt=floor(humidity); 
  humidityFloat=(humidity*100)-(humidityInt*100);

  //get the date into memroy
  bufData[10]=humidityInt;
  bufData[11]=humidityFloat;

  return humidity;
}

/****************************************
 * Function name: bind2bcd(byte)
 * type: byte
 * input parameters: byte val
 * output parameter:  val + 6 * (val / 10);
 * Description:
 * Creators: Jeelabs.
 *****************************************
 */
static byte bin2bcd (byte val)
{
  return val + 6 * (val / 10);
  delay(1);
}

/****************************************
 * Function name: bcd2bcd(byte)
 * type: byte
 * input parameters: byte val
 * output parameter: val - 6 * (val >> 4);
 * Description:
 * Creators: Jeelabs
 *****************************************
 */
static byte bcd2bin (byte val)
{
  return val - 6 * (val >> 4);
  delay(1);
}

/***************************************************************************************************
 * // Function name: setDate(byte,  byte, byte, byte, byte)
 * type: static void
 * input parameters: byte yy: year
 * byte mm:month
 * byte dd: day
 * byte h: hour
 * byte m: minutes 
 * byte s: seconds
 * output parameter: NULL
 * Description:  This function is used in order to setup a specific day in the format yy/mm/dd h:m:s
 * Compile it once in order to set up the clock. Erase the call of this function from the code.
 * Finally compile again in order to make the clock to start running indepedently.
 * Creators: Jeelabs
 ********************************************************************************************************
 */
static void setDate (byte yy, byte mm, byte dd, byte h, byte m, byte s)
{
  rtc.send();
  rtc.write(0);
  rtc.write(bin2bcd(s));
  rtc.write(bin2bcd(m));
  rtc.write(bin2bcd(h));
  rtc.write(bin2bcd(0));
  rtc.write(bin2bcd(dd));
  rtc.write(bin2bcd(mm));
  rtc.write(bin2bcd(yy));
  rtc.write(0);
  rtc.stop();
}

/******************************************************************
 * Function name: getDate (void)
 * type: static void
 * input parameters: bye* buf: byte array in which the date and time would be introduced
 * output parameter: NULL
 * Description: get the date and time from the clock and puts it into
 * the byte array specified
 * Creators: MBARI
 ********************************************************************/

static void getDate (byte* buf)
{
  rtc.send();
  rtc.write(0);   
  rtc.stop();

  rtc.receive();
  buf[5] = bcd2bin(rtc.read(0));
  buf[4] = bcd2bin(rtc.read(0));
  buf[3] = bcd2bin(rtc.read(0));
  rtc.read(0);
  buf[2] = bcd2bin(rtc.read(0));
  buf[1] = bcd2bin(rtc.read(0));
  buf[0] = bcd2bin(rtc.read(1));
  rtc.stop();
}


/******************************************************************
 * Function name:saved(char[])
 * type: void
 * input parameters: char a[]= array whcih will be saved into memory
 * output parameter: NULL
 * Description: saves the input array into the memory. The way it works is
 * by checking the directory of the memory which is at location
 * [0,0] if there is nothing in memory start saving on adress [1,0]
 * if there is something on memory start saving where you left off
 * Creators: MBARI
 ********************************************************************/

void saved(char a[])
{
  int remember[2];
  if(mem.isPresent())
  {
    mem.load(0,0,remember,4);
    delay(10);
    if(remember[0]==0 && remember[1]==0)
    {
      mem.save(1,0, a,12);
      delay(10);
      remember[0]=1;
      remember[1]+=12;
      mem.save(0,0,remember,4);
      delay(10);
    }
    else 
    {
      if(remember[1]<243)
      {
        mem.save(remember[0],remember[1],a,12);
        delay(10);
        remember[1]+=12;
      }
      if (remember[1]>243)
      {
        remember[0]+=1;
        remember[1]=0;
        mem.save(remember[0],remember[1],a,12);
        remember[1]+=12; 
      }
      mem.save(0,0,remember,4);
      delay(10);
    }
  }
}



/******************************************************************
 * Function name: send_Radio( char a[], int length)
 * type: void
 * input parameters: char a[]: array which will be transmitted
 * int length: length of the array to be transmitted
 * output parameter: NULL
 * Description: sends an array of n length through radio communications
 * to a base station
 * Creators: MBARI
 ********************************************************************/
void send_Radio(char a[], int length)
{
  delay(10);
  while (!rf12_canSend()) // wait until sending is allowed
  {
    rf12_recvDone();    // processes any incoming data, it all happens "in the background"
  }
  rf12_sendStart(1,a, length);  // sends the data out
}

/******************************************************************
 * Function name: transform(char [])
 * type: void
 * input parameters: char a[]: array of n bytes which will be loaded
 * output parameter: NULL
 * Description: Transform an arry of characters into something that looks
 * like mm//dd//yy h:m:s CO2 ppm= xxx temperature= xx.xx 
 * humidity= xx.x
 * Creators: MBARI
 ********************************************************************/
void transform( char a[])
{
  int buffer[11];
  char string[80];
  for(int i=0;i<6;i++) //get the time form the clock
  {
    buffer[i]= (int)a[i];
  }
  buffer[6]=((a[6] &0xff)<<8) | (a[7] &0xff); //merge the two bytes of co2

  for(int i=8;i<12;i++) // get the values for temperature and humidity
  {
    buffer[i-1]=int(a[i]);
  }

  sprintf(string,"%02d/%02d/%02d %02d:%02d:%02d  CO2 ppm= %02d temperature= %02d.%02d humidity= %02d.%02d",
  buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5],buffer[6],buffer[7],buffer[8],buffer[9],buffer[10]);

  Serial.print(string);   
  Serial.println();
  delay(1);
}

/******************************************************************
 * Function name: Telaire-IdleMode(boolean mode)
 * type: void
 * input parameters: boolean mode, 
 * output parameter: NULL
 * Description: Indicates wheter the sensor is 
                going to go in Idle mode or not. True=Idle mode,     
                False=wake up
 * Creators: MBARI
 ********************************************************************/

void Telaire_IdleMode(boolean mode)
{
  boolean out=true;
  byte buf[3];

  //if true the Telaire will go into sleep mode
  if(mode==true)
  {
    while(out)
    {
      int i=0;
      uart.write(0xFF);
      uart.write(0xFE);
      uart.write(0x02);
      uart.write (0xB9);
      uart.write (0X01);
      while(uart.available())
      {
        buf[i]=uart.read();
        i++;
      }
      if((buf[0]==255)&&(buf[1]==250)&&(buf[2]==0))
      {
        out=false;
      }
    }
  }

  //if not true, the Telair will wake up
  if(mode==false)
  {
    while(out)
    {
      int i=0;
      uart.write(0xFF);
      uart.write(0xFE);
      uart.write(0x02);
      uart.write (0xB9);
      uart.write (0X02);
      while(uart.available())
      {
        buf[i]=uart.read();
        i++;
      }
      if((buf[0]==255)&&(buf[1]==250)&&(buf[2]==0))
      {
        out=false;
      }
    }
  }
}




