// This arduino code runs the pump and stirrer module
// Communication is based on https://forum.arduino.cc/t/serial-input-basics-updated/382007

// Pin definitions
#define DIRA_fill A3
#define DIRB_fill A2
#define ENABLE_fill 3
#define DIRA_drain 2
#define DIRB_drain 4
#define ENABLE_drain 5
#define DIRA_reactor A1
#define DIRB_reactor A0
#define ENABLE_reactor 6
#define DIRA_bath 7
#define DIRB_bath 8
#define ENABLE_bath 9

// Global variables
int last_time = 0; //last time measurement data was sent by the arduino in s
int time = 0; //actual time
int timeinterval = 1; //time interval in which measurements should be made and their data sent
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



// Setup of pin's and communication
void setup() {
  pinMode(DIRA_fill,OUTPUT);
  pinMode(DIRB_fill,OUTPUT);
  pinMode(ENABLE_fill,OUTPUT);
  pinMode(DIRA_drain,OUTPUT);
  pinMode(DIRB_drain,OUTPUT);
  pinMode(ENABLE_drain,OUTPUT);
  pinMode(DIRA_reactor,OUTPUT);
  pinMode(DIRB_reactor,OUTPUT);
  pinMode(ENABLE_reactor,OUTPUT);
  pinMode(DIRA_bath,OUTPUT);
  pinMode(DIRB_bath,OUTPUT);
  pinMode(ENABLE_bath,OUTPUT);
  Serial.begin(9600);
}



// 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
        ExecuteInstructions(); // execute the instructions by setting the values of the actors. The ExecuteInstructions function is defined below
        NewInstructions = false; //set the variable to show, that the instructions have been executed
    }

    //time = millis()/1000; // calculate actual time in s
    //if((time-last_time)>=timeinterval){ // check if it is time to send new measurement data
    //  SendData(); // send the data using the SendData function defined below
    //  last_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 ExecuteInstructions() {  //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
  
  // Execute the received instructions
  if (strcmp(Arduino,"Ard_Pump_Stirr")==0){
    if (strcmp(Actor,"fill")==0){
      if (Actorvalue == 0){
        digitalWrite(DIRA_fill,LOW);
        digitalWrite(DIRB_fill,LOW);
        analogWrite(ENABLE_fill,0);
      } else if (Actorvalue > 0){
        digitalWrite(DIRA_fill,HIGH);
        digitalWrite(DIRB_fill,LOW);
        Actorvalue = map(Actorvalue,0,100,230,255); // map the entry in % power to the value used to run the analog output
        Actorvalue = constrain(Actorvalue,0,255); // constrain the value to possible outputs
        analogWrite(ENABLE_fill,Actorvalue);
      } else if (Actorvalue < 0){
        digitalWrite(DIRA_fill,LOW);
        digitalWrite(DIRB_fill,HIGH);
        Actorvalue = map(Actorvalue,0,-100,230,255); // map the entry in % power to the value used to run the analog output
        Actorvalue = constrain(Actorvalue,0,255); // constrain the value to possible outputs
        analogWrite(ENABLE_fill,Actorvalue);
      }
    }
    if (strcmp(Actor,"drain")==0){
      if (Actorvalue == 0){
        digitalWrite(DIRA_drain,LOW);
        digitalWrite(DIRB_drain,LOW);
        analogWrite(ENABLE_drain,0);
      } else if (Actorvalue > 0){
        digitalWrite(DIRA_drain,LOW);
        digitalWrite(DIRB_drain,HIGH);
        Actorvalue = map(Actorvalue,0,100,217,255); // map the entry in % power to the value used to run the analog output
        Actorvalue = constrain(Actorvalue,0,255); // constrain the value to possible outputs
        analogWrite(ENABLE_drain,Actorvalue);
      } else if (Actorvalue < 0){
        digitalWrite(DIRA_drain,HIGH);
        digitalWrite(DIRB_drain,LOW);
        Actorvalue = map(Actorvalue,0,-100,217,255); // map the entry in % power to the value used to run the analog output
        Actorvalue = constrain(Actorvalue,0,255); // constrain the value to possible outputs
        analogWrite(ENABLE_drain,Actorvalue);
      }
    }
    if (strcmp(Actor,"reactor")==0){
      if (Actorvalue == 0){
        digitalWrite(DIRA_reactor,LOW);
        digitalWrite(DIRB_reactor,LOW);
        analogWrite(ENABLE_reactor,0);
      }
      if (Actorvalue == 1){
        digitalWrite(DIRA_reactor,HIGH);
        digitalWrite(DIRB_reactor,LOW);
        analogWrite(ENABLE_reactor,255);
      }
    }
    if (strcmp(Actor,"bath")==0){
      if (Actorvalue == 0){
        digitalWrite(DIRA_bath,LOW);
        digitalWrite(DIRB_bath,LOW);
        analogWrite(ENABLE_bath,0);
      }
      if (Actorvalue == 1){
        digitalWrite(DIRA_bath,HIGH);
        digitalWrite(DIRB_bath,LOW);
        analogWrite(ENABLE_bath,255);
      }
    }
  }
}


void SendData() { //sent the acutal timestamp and the measurement values from the arduino to the pc
  Serial.print(time);
  Serial.print(',');
  Serial.println(analogRead(0)/1023.0);
}