// This aruino code is used to run the heater module
// Communication is based on https://forum.arduino.cc/t/serial-input-basics-updated/382007


//Libraries
#include <RCSwitch.h> // this library is used to send commands to the remote controled power socket in which the heating element is plugged


// Pin definitions
#define NTC_pin_bath 7 // GND-reference resistor-NTC_pin_bath-NTC in bath-5V
#define NTC_pin_plate 6 // GND-reference resistor-NTC_pin_plate-NTC on hotplate-5V

// Objects
RCSwitch mySwitch = RCSwitch(); // Object used to sending commands to a remotely controllable switch

// Global variables
int last_save_time = 0; //last time measurement data was sent by the arduino in s
int last_heat_time = 0; //last time the temperature was controled in s
int time = 0; //actual time
int save_timeinterval = 1; //time interval in which measurements should be made and their data sent
int heat_timeinterval = 1; //time interval in which the temperature should be controlled
const byte numChars = 32;     // number of characters in the message received from the PC. Can be much bigger, because the buffer is cleared before it is full
char receivedChars[numChars]; // Array for received characters
char tempChars[numChars];     // temporary array for use when parsing to not change the original data receivedChars

// variables to hold the parsed data
// The received data has the format "<Arduino,Actor,Actorvalue>" with the name of the arduino and the actor, whichs value should be changed to the actorvalue
char Arduino[numChars] = {0};   // Variable for the name of the arduino, whichs actors should act
char Actor[numChars] = {0};   // Variable for the name of the actor, whichs value should be changed
int Actorvalue = 0;           // Variable for the value the actor should be changed to
boolean NewInstructions = false; // Indicates if message is completely received
float U_NTC_10K_bath; // voltage of the NTC for bath temperature in V
float U_NTC_10K_plate; // voltage of the NTC for heating plate temperature in V
float Temperature_bath; // temperature of the bath NTC in °C
float Temperature_plate; // temperature of the heating plate NTC in °C
float start_Temperature; // temperature at the beginning of the temperature ramp
float target_Temperature = 25.0;  // target temperature in °C
float set_Temperature; // actual set temperature of the temperature ramp for the temperature controler in °C
float Temperature_rise_rate = 2.0/60.0*1.1;  // rise rate of the temperature ramp in °C/ 1.1 s
unsigned long start_time;  //start time of the temperature ramp in ms
unsigned long current_time; //current time of the temperature ramp in ms
int heater_status; // 1 if heater is on 0 if it is off


// Setup of pin's and communication
void setup() {
    Serial.begin(9600); // serial communication with the computer
      
    // Setup the Transmitter for sending instructions to the remote controlled switch
    mySwitch.enableTransmit(10); // Transmitter is connected to Arduino Pin #10 
    mySwitch.setProtocol(1); // Optional set protocol (default is 1, will work for most outlets)
    mySwitch.setPulseLength(424); // Optional set pulse length.
    // mySwitch.setRepeatTransmit(15); // Optional set number of transmission repetitions.
  
    // Setup the pins
    pinMode(NTC_pin_bath,INPUT);
    pinMode(NTC_pin_plate,INPUT);

    // Calculations of initial values
    U_NTC_10K_bath=analogRead(NTC_pin_bath); 
    Temperature_bath=log(10000.0 * ((1024.0 / U_NTC_10K_bath - 1))); //correlation between temperature an measured voltage
    U_NTC_10K_plate=analogRead(NTC_pin_plate);
    Temperature_plate=log(10000.0 * ((1024.0 / U_NTC_10K_plate - 1))); //correlation between temperature an measured voltage
    start_Temperature= 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temperature_plate * Temperature_plate ))* Temperature_plate )-273.15; //correlation between temperature an measured voltage
    set_Temperature = start_Temperature;
    start_time = millis();
    heater_status = 0;
}



// Main loop running continuously
void loop() {

    ReceiveInstructions(); // the ReceiveInstructions function is defined below and receives the instructions from the PC to the arduino using serial communication

    if (NewInstructions == true) { //if new instructions have been received
        DecodeInstructions(); // decode the instructions
        NewInstructions = false; //set the variable to show, that the instructions have been executed
    }

    time = millis()/1000; // calculate actual time in s

    if ((time-last_heat_time)>=heat_timeinterval){ // this loop is for temperature control of the heater
        TemperatureControl(); // the TemperatureControl function is defined below
        last_heat_time = time; // remember the last time the temperature has been controlled / the heater status has been changed
    }

    if((time-last_save_time)>=save_timeinterval){ // check if it is time to send new measurement data
      SendData(); // send the data using the SendData function defined below
      last_save_time = time; // remember the last time data has been sent
    }
}



void ReceiveInstructions() { //receives the instructions from the PC to the arduino using serial communication
    static boolean recvInProgress = false; //indicates if startMarker has been received and message is collected
    static byte ndx = 0; //index for the char array
    char startMarker = '<'; //startMarker for the array
    char endMarker = '>'; //endMarker for the array "New Line" in arduino IDE serial monitor puts a '\n' at the end of each message as endMarker
    char rc; // single received character which is put into the receivedChars array

    while (Serial.available() > 0 && NewInstructions == false) { //if new data is available and the message is not completed 
        rc = Serial.read(); //read single character from buffer

        if (recvInProgress == true) { //if the start marker has been received and the message is now collected
            if (rc != endMarker) { //if the received character is not the endMarker
                receivedChars[ndx] = rc; //attach the character to the array
                ndx++; //increase index of the received character array
                if (ndx >= numChars) { //check that not too many characters have been received
                    ndx = numChars - 1;
                }
            }
            else { //if the received character is the endMarker
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false; //set the indicator that the message is completely received
                ndx = 0; //reset the array index
                NewInstructions = true; //set the indicator that the data can be displayed
            }
        }

        else if (rc == startMarker) { //if the received character is the startMarker
            recvInProgress = true; //set the indicator that the characters should be collected to the message
        }
    }
}



void DecodeInstructions() {  //process the received data and execute the instructions
    // Parse the received Data into it's components
    char * strtokIndx; // char * is a pointer (the memory adress for char data) this is used by strtok() as an index
    strcpy(tempChars, receivedChars); //copies the string in receivedChars into tempChars
    // this temporary copy is necessary to protect the original data
    // because strtok() used in parseData() replaces the commas with \0
    strtokIndx = strtok(tempChars,",");      // strtok splits a string (tempChars) into pieces (tokens) using a delimiter (,). it replaces the delimiter with \0 (NULL) and returns the pointer to the next token each time it is called
    strcpy(Arduino, strtokIndx); // copy it to messageFromPC
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    strcpy(Actor, strtokIndx); // copy it to messageFromPC
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    Actorvalue = atoi(strtokIndx);     // convert this part to an integer
}


void TemperatureControl() {
  // Determine current variables
  current_time=millis()-start_time;
  U_NTC_10K_bath=analogRead(NTC_pin_bath);
  Temperature_bath=log(10000.0 * ((1024.0 / U_NTC_10K_bath - 1)));
  Temperature_bath= 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temperature_bath * Temperature_bath ))* Temperature_bath )-273.15;
  U_NTC_10K_plate=analogRead(NTC_pin_plate);
  Temperature_plate=log(10000.0 * ((1024.0 / U_NTC_10K_plate - 1)));
  Temperature_plate= 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temperature_plate * Temperature_plate ))* Temperature_plate )-273.15;

  if (strcmp(Arduino,"Ard_Heat")==0){ // if the received instructions are actually for the arduino called Ard_Heat
    if (strcmp(Actor,"Heater")==0){ // if the instructions are vor the Heater actor
      target_Temperature = Actorvalue; // set the received actor value to the new target temperature
      if(target_Temperature > 100){ // prevent unrealistic target temperatures
        target_Temperature = 100;
      } else if (target_Temperature < 0){
        target_Temperature = 0;
      }
      if((Temperature_plate<=(set_Temperature-2.5)) && (Temperature_plate > (-100))){ // if the actual plate temperature is valid and below the set temperature - 2.5
        mySwitch.send("000101010001010101010101"); // send a command to the rc switch to switch it on. This command was determined by listening to the command of the original remote of the switch using a receiver
        delay(100);  // wait 100 ms
        mySwitch.send("000101010001010101010101"); // send the command again in case it was not received at the first time
        heater_status = 1; // set the indicator for the heater status to 1
      }
    }
  }
  if((Temperature_plate > (set_Temperature-2)) || (Temperature_plate < (-100))){ // if the actual plate temperature is not valid or higher than the set temperature - 2
    mySwitch.send("000101010001010101010100"); // send a command to the rc switch to switch it off. This command was determined by listening to the command of the original remote of the switch using a receiver
    delay(100);  // wait 100 ms
    mySwitch.send("000101010001010101010100"); // send the command again in case it was not received at the first time
    heater_status = 0; // set the indicator for the heater status to 0
  }

 if(set_Temperature < -100){ // if the set temperature is not valid
    set_Temperature = Temperature_plate; // set it to the actual plate temperature
  }
  set_Temperature = set_Temperature + Temperature_rise_rate; // increase the set temperature until
  if(set_Temperature > target_Temperature){ // it is higher than the target_Temperature
    set_Temperature = target_Temperature;
  }

}


void SendData() { //sent the acutal timestamp and the measurement values from the arduino to the pc
  Serial.print("time_Ard_Heat,");
  Serial.print(current_time/1000);
  Serial.print(",T1,");
  Serial.print(Temperature_plate);
  Serial.print(",T2,");
  Serial.print(Temperature_bath);
  Serial.print(",T1_set,");
  Serial.print(set_Temperature);
  Serial.print(",heater_status,");
  Serial.println(heater_status);
}
