#include <avr/sleep.h>
#include <Wire.h>
#include <HIH61XX.h>
#include <RH_RF95.h>

HIH61XX hih(0x27);
RH_RF95 rf95;

#define receivingTime 3000

int rel1_on = A1;
int rel1_off = A0;
int rel2_on = A3;
int rel2_off = A2;
int pin2 = 3;
int led = 5;
int done = 4;

uint8_t offset = 30;
unsigned long actualTime;
unsigned long actualTime1;

bool rqst = true;  //after wake power cycle it reqests for registration
int id = 1234;
int queue = 0;
int comMode = false; //allows to wait for reply

float temp;
float hum;
int voltage;
int relay1 = 0;
int relay2 = 0;

int relay1_old = 0;
int relay2_old = 0;

  
void setup() 
{
  delay(2000);
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
  doneSignal();
  Serial.begin(115200);
  pinMode(pin2, INPUT);
  Serial.println("Initialisation complete.");
  
  //init relays
  pinMode(rel1_on, OUTPUT);
  pinMode(rel2_on, OUTPUT);
  pinMode(rel1_off, OUTPUT);
  pinMode(rel2_off, OUTPUT);

  digitalWrite(rel1_on, LOW);
  digitalWrite(rel2_on, LOW);
  digitalWrite(rel1_off, LOW);
  digitalWrite(rel2_off, LOW);
  
  //set relays to the default position
  ResetRelays();
  
  if (!rf95.init())
   Serial.println("init failed");

  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
  rf95.setFrequency(868.0);
  //PUT Radio to SLEEP
  rf95.sleep();
}

 void loop(){
   actualTime1 = millis(); 
   bool permit = BatteryDischargeCheck();
   
   if(permit){
     hih.start();
     hih.update();
     hum = hih.humidity()*100; 
     temp = hih.temperature();
     
     //handles LoRa sending and receiving function
     LoRaSending();  
     //Counts the actual wake time [ms]
     actualTime1 = millis() - actualTime1;
     //SLEEP
     rf95.sleep();
     Serial.print("\nWakeTime: ");
     Serial.print(actualTime1);
     Serial.println("ms\n");
   }
   
   delay(3);
   //Serial.println("\nSleepEnter");
   doneSignal();
   enterSleep();
}
//End of main

 bool BatteryDischargeCheck(){ 
     //BatteryDischargeCheck tests if the battery is charged enought to send data.
     voltage = readVcc();
     if(voltage < 2800){ 
       Serial.println("Battery discharge protection test FAILED.");
       Serial.println("Replace the battery.");
       return 0;
     } 
     
     else{
       Serial.println("Battery test = PASS");
       return 1;
     }
   }


 void LoRaSending(){
   
   uint8_t buffer[5]; //declaration of buffer with size of 5 char characters (bytes)
   byte sendLen;
   memset(buffer, '\0', sizeof(buffer));  //properly terminate the buffer (clear it)
   // Making right format for visualisation
   
   int x = hum*100;
   int y = temp*100;
   //voltage = readVcc();   
   
   Serial.print("\nId:   ");
   Serial.print(id);
   Serial.print("\ncomMod:   ");
   Serial.print(comMode);
   Serial.print("\nHum:  ");
   Serial.print(hum);
   Serial.print("%\n");
   Serial.print("Temp: ");
   Serial.print(temp);
   Serial.print("C\n");
   Serial.print("Voltage: ");
   Serial.print(voltage);
   Serial.print("\n");
   Serial.print("Rqst: ");
   Serial.print(rqst);
   Serial.print("\n");
 
   
   //deal with ID which represents 14bit number (0-16384) 
   //Which is whole 1 byte + 6 bites of 2nd byte
      
    // Split short into two char
      buffer[0] = id >> 6;
      buffer[1] = (id << 2) & 0xFC;

    //deal with RQST
      if(rqst)
      buffer[1] = buffer [1] | (rqst << 1);
      else
      buffer[1] = buffer [1] & B11111101;

   //deal with temperature
     uint8_t tmps = temp + offset;

     //get decimal part of number
     double integral;
     double fractional = modf(temp, &integral);
     uint8_t intTmpDec = fractional * 100;
   
     int var = RoundToAQuarter(intTmpDec);
     if(var == 4)
     tmps++;

     else{
      buffer[2] = buffer[2] | var;
     }
     
     tmps = tmps & B01111111; //cuts off highest bite
     buffer[1] = buffer[1] | (tmps >> 6);
     buffer[2] = buffer[2] | (tmps << 2); 
     
   //deal with hum
   //7 bits of whole number (0-127) + 1 bit od decimal (0 and 0.5)
     int wholeHum = hum; 

     integral = 0;
     fractional = modf(hum, &integral);
     uint8_t intHumDec = fractional * 100;
/*     //last bit
     Serial.print("HumDec: "); 
     Serial.println(intHumDec);
*/
     var = RoundToAHalf(intHumDec);
     if(var == 2)
     wholeHum++;
     else
     buffer[3] = buffer[3] | var;
     buffer[3] = buffer[3] | (wholeHum << 1);

   //deal with vltg
      buffer[4] = (voltage/1000) << 5;

      //split decimal in 2^5 levels
      //get rid of Whole number
      int decimalVolt = voltage - ((voltage/1000)*1000);
      
      Serial.print("DecimalVolt: ");
      Serial.println(decimalVolt);

      var = RoundToFourty(decimalVolt);
      Serial.print("VoltageLvl: ");
      Serial.println(var);

      buffer[4] = buffer[4] | (var & B00011111);
      

   Serial.print("MgsSent: [ ");
  
   //výpis MSG:send
   for(int i=0; i<sizeof(buffer); i++)
   {
    Serial.print(buffer[i]);
    Serial.print(" "); 
   }
    Serial.print("]\n");
   
   // SENDING
   //sprintf(buffer, "%d%d%d%d%d", id, x, y, voltage, rqst);  //x=hum y=temp
   //sendLen = strlen(buffer);  //get the length of buffer
   sendLen = sizeof(buffer);
   Serial.print("sendLen: ");
   Serial.println(sendLen);
  
   actualTime = millis(); //save actual time into variable
   rf95.send(buffer, sendLen);
   //rf95.send((uint8_t *) buffer, sendLen);
   rf95.waitPacketSent();
   
   //The whole message should be already sent 
   actualTime = millis() - actualTime;
   Serial.print("SendTm: ");
   Serial.print(actualTime);
   Serial.println("ms");
  
   
   
   //RECEIVING
   if(!comMode){ //alows senzor wait for reply only if the comMode is 0 (default)
     // Now wait for a reply
     uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
     uint8_t len = sizeof(buf);
     memset(buf, '\0', sizeof(buf));

     if (rf95.waitAvailableTimeout(receivingTime)){    
       // Should be a reply message for us now   
       if (rf95.recv(buf, &len)){
        // digitalWrite(led, HIGH);
         Serial.print("MsgRcvd: ");
         Serial.println((char*)buf);      
        // digitalWrite(led, LOW); 
         //Controling relays
         relay1 = buf[0]-48;
         relay2 = buf[1]-48;
         comMode = buf[2]-48;
         queue  = buf[3]-48;
       
         Serial.print("Relay1: ");
         Serial.println(relay1);
         Serial.print("Relay2: ");
         Serial.println(relay2);
         Serial.print("Index in GTW: ");
         Serial.println(queue);
       
         if(rqst){ //the moment of 1. received transmittion
           for(int i=0; i < queue; i++){ //blink as much time as the index is
             digitalWrite(led, HIGH);
             delay(1000);
             digitalWrite(led, LOW);
             delay(500);
           }
         }
       
         //after first reply REQUEST MODE IS DISABLED
         rqst = false;
      
         if(relay1!=relay1_old || relay2!=relay2_old)
           relayFunction(relay1, relay2);
         else
           Serial.print("Relay no change.");
      
         relay1_old=relay1;
         relay2_old=relay2;
         
         //Serial.print("\n---------------------\n");
       }
       else{
         Serial.println("recv failed");
       }
     }  
     else{
       Serial.println("GTW_noReply");
     }
   }
 }


 int readVcc() {
   // Read 1.1V reference against AVcc
   // set the reference to Vcc and the measurement to the internal 1.1V reference
   #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
     ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
   #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
     ADMUX = _BV(MUX5) | _BV(MUX0);
   #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
     ADMUX = _BV(MUX3) | _BV(MUX2);
   #else
     ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
   #endif  

   delay(2); // Wait for Vref to settle
   ADCSRA |= _BV(ADSC); // Start conversion
   while (bit_is_set(ADCSRA,ADSC)); // measuring

   uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
   uint8_t high = ADCH; // unlocks both

   int result = (high<<8) | low;

   result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
   return result; // Vcc in millivolts
 }

 void enterSleep(void){
   
   ADCSRA = 0; // disable ADC
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);
   EIFR = pin2-1; //reset using interrupt (clear flag)
   attachInterrupt(digitalPinToInterrupt(pin2), pin2Interrupt, CHANGE);
   delay(3);
   sleep_enable();
   // turn off brown-out enable in software
   // BODS must be set to one and BODSE must be set to zero within four clock cycles
   MCUCR = bit (BODS) | bit (BODSE);
   // The BODS bit is automatically cleared after three clock cycles
   MCUCR = bit (BODS);
   sleep_mode();
   sleep_disable(); 
 }

 void pin2Interrupt(void){ 
   sleep_disable(); 
   detachInterrupt(digitalPinToInterrupt(pin2));
 }



 void doneSignal(void){
   pinMode(done, OUTPUT);
   digitalWrite(done, HIGH);
   delay(2);
   digitalWrite(done, LOW);
   pinMode(done, INPUT);
 }
 
 void relayFunction(int relay1, int relay2){
   switch (relay1){
     case 0:
        // vypnuto
       Serial.print("RELAY 1: OFF");
       digitalWrite(rel1_off, HIGH);
       delay(5);
       digitalWrite(rel1_off, LOW);
       break;
     case 1:
       Serial.print("RELAY 1: ON");
       digitalWrite(rel1_on, HIGH);
       delay(5);
       digitalWrite(rel1_on, LOW);
       break;
   } 
   Serial.println();
   
   switch (relay2){
     case 0:
       Serial.print("RELAY 2: OFF");
       digitalWrite(rel2_off, HIGH);
       delay(5);
       digitalWrite(rel2_off, LOW);
       break;
     case 1:
       Serial.print("RELAY 2: ON");
       digitalWrite(rel2_on, HIGH);
       delay(5);
       digitalWrite(rel2_on, LOW);
       break;
   } 
 }

int  RoundToAQuarter(int intTmpDec)
     {
      if(intTmpDec < 12.5)
      return 0; 

      if(intTmpDec < 37.5)
      return 1;

      if(intTmpDec < 62.5)
      return 2; 

      if(intTmpDec < 87.5)
      return 3;

      else
      return 4;
     }

int RoundToAHalf(int intHumDec)
     {
      if(intHumDec < 25)
      return 0;

      if(intHumDec < 75)
      return 1;

      else
      return 2;
     }

int RoundToFourty(int decimalVolt)
{
      for(int i=0; i<25; i++)
      { 
        if(decimalVolt < (i*40)+40)
        return i;
      }
return 0;
}

void ResetRelays()
{
 Serial.println("RELAY 1: RESET");
 digitalWrite(rel1_off, HIGH);
 delay(5);
 digitalWrite(rel1_off, LOW);
 
 Serial.println("RELAY 2: RESET");
 digitalWrite(rel2_off, HIGH);
 delay(5);
 digitalWrite(rel2_off, LOW);  
}




