Skip to content

Software

Ricardo H. Ramirez-Gonzalez edited this page May 21, 2018 · 10 revisions

Arduino micro-controller

The Arduino micro-controller is used to monitor the status of the chamber via several sensors. The status of the chamber and the target parameters are stored in the CurrentStatus data structure defined in datastructures.h.

The controller is always in a loop comparing the target parameters and listening for commands from the serial port.

Control flow

Read environment

On each iteration of the loop() function the state of the chamber and any changes from the expeced statos, according to the preferences setup for the chamber.

void loop() {
  delay(100);
  status_read_environment(&cs);
  status_control_temperature(&cs);
  status_control_light(&cs);
  cs.started_up = true;
  if (Serial.available() > 0) {
    recvWithEndMarker(&cs);
  }
  if(cs.new_data){
    parse_new_data(&cs);
   }
}

Temperature and humidity

The Grove Temperature and Humidity Sensor Pro v1.3 comes with a library that converts the readings from the sensor to float values that are stored.

dht_control.ino

#include "DHT.h"
#define DHTTYPE DHT22
DHT dht(PIN_TMP_SENSOR, DHTTYPE);

void dht_setup(){
    dht.begin();
}

float dht_humidity(){
  return dht.readHumidity();
}


float dht_temperature(){
  return dht.readTemperature();
}

satus_structure.ino

void status_read_environment(struct CurrentStatus * cs){
  cs->humidity = dht_humidity();
  cs->temperature = dht_temperature();
}

Control temperature

When the chamber is warmer than the expected temperature, the peltier element is used to extract heat from the chamber. To improve the efficiency, we use two standard computer fans. To avoid the unnecessary execution of code, we use the next_peltier_cool_status and peltier_cool_status variables to know if the current status of the system is the same as the expected.

void status_control_temperature(struct CurrentStatus * cs){
  if(cs->peltier_cool_status && cs->temperature > cs->min_tmp){
    cs->next_peltier_cool_status = true;
  }else{
    cs->next_peltier_cool_status = cs->temperature > cs->max_tmp;
  }

  if( !cs->started_up || cs->peltier_cool_status != cs->next_peltier_cool_status){
    if(cs->next_peltier_cool_status){
      status_start_cool(cs);
    }else{
      status_stop_cool(cs);
    }
  }
}

The speed of the fans are controlled by the PWM pin.

void fan_set_speed(int pwm, int f_speed){
  analogWrite(pwm,f_speed);
}

The relays are controlled with a digital signal that turns them on or off.

void relay_on(int port){
  digitalWrite(port, HIGH);
}

void relay_off(int port){
  digitalWrite(port, LOW);
}

When starting or stopping the peltier element the fans attached to them are speed up to improve the heat transfer.

void status_start_cool(struct CurrentStatus * cs){
  relay_on(PIN_PELTIER_1);
  relay_on(PIN_PELTIER_2);
  relay_on(PIN_PELTIER_3);
  fan_set_speed(PIN_FAN_INSIDE, 0);
  fan_set_speed(PIN_FAN_OUTSIDE, 0);
  cs->peltier_cool_status = true;
}

void status_stop_cool(struct CurrentStatus * cs){
  relay_off(PIN_PELTIER_1);
  relay_off(PIN_PELTIER_2);
  relay_off(PIN_PELTIER_3);
  fan_set_speed(PIN_FAN_INSIDE, 255);
  fan_set_speed(PIN_FAN_OUTSIDE, 255);
  cs->peltier_cool_status = false;
}

Control humidity

The humidity control consists on a fan to circulate air out of the chamber. The logic is similar to the temperature control, but it only turns on and off the fan that circulates air in the chamber.

void status_control_humidity(struct CurrentStatus * cs){
  cs->next_humidity_fan_status = cs->humidity > cs->max_humidity;
  if(cs->humidity_fan_status != cs->next_humidity_fan_status){
    if(cs->next_humidity_fan_status){
      relay_on(PIN_HUMIDITY_FAN);
      cs->humidity_fan_status = true;
    }else{
      relay_off(PIN_HUMIDITY_FAN);
      cs->humidity_fan_status = false;
    }
  }
}

Control Light

The light is controlled with a relay that turns on and off the lights according to the expected status.

void status_control_light(struct CurrentStatus * cs){
  if(!cs->started_up || cs->next_light != cs->light){
    if(cs->next_light){
      status_start_light(cs);
    }else{
      status_stop_light(cs);
    }
}

void status_start_light(struct CurrentStatus * cs){
  relay_on(PIN_LIGHT);
  cs->light = true;
}

void status_stop_light(struct CurrentStatus * cs){
  relay_off(PIN_LIGHT);
  cs->light = false;
}

External communication

The Arduino microcontroller is used only to ensure that the conditions in the chamber are mantained. However, the settings of the growing condition and logs are controled from an external computer, in our prototype a Raspberry Pi. For that reason the controller is always listening to the Serial interface for commands.

void recvWithEndMarker(struct CurrentStatus * cs) {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

  while (Serial.available() > 0 && cs->new_data == false) {
     rc = Serial.read();
     if (rc != endMarker) {
      cs->receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
     }else {
      cs->receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      cs->new_data = true;
    }
  }
}

Each line is considered a command. The format is: COMAND=VALUE. The = character is used as token marker for commands with arguments.

void parse_new_data(struct CurrentStatus * cs) {
  if (cs->new_data == true) {
    String messageFromPC;
    char * strtokIndx; // this is used by strtok() as an index
    strtokIndx = strtok(cs->receivedChars,"=");
    messageFromPC = String(strtokIndx); // copy it to messageFromPC
    Serial.print("{\"CMD\":\"");
    Serial.print(messageFromPC);
    Serial.print("\"}");
    Serial.println();
    Serial.flush();
    int tmp_val;
    if(messageFromPC == "PRINT"){
      print_sensor_error(cs);
      status_print(cs);
    }else if(messageFromPC == "max_tmp"){
      strtokIndx = strtok(NULL, ",");
      cs->max_tmp = atof(strtokIndx);
      cs->min_tmp = cs->max_tmp -1;
      cs->started_up = false;
      status_print(cs);
    }else if(messageFromPC == "min_tmp"){
      strtokIndx = strtok(NULL, ",");
      //TODO: Add the code to the python module to control this
      cs->min_tmp = atof(strtokIndx);
      cs->started_up = false;
      status_print(cs);
    }else if (messageFromPC == "max_humidity") {
      strtokIndx = strtok(NULL, ",");
      cs->max_humidity = atof(strtokIndx);
      cs->started_up = false;
      status_print(cs);
    }else if (messageFromPC == "light") {
      strtokIndx = strtok(NULL, ",");
      tmp_val = atoi(strtokIndx);
      cs->next_light = tmp_val > 0;
      cs->started_up = false;
      status_print(cs);
    }
    else{
      Serial.print("{\"ERROR\": \"Unknown command '");
      Serial.print(messageFromPC);
      Serial.print("'\"}");
      Serial.println();
      Serial.flush();
    }
    status_clear_in_buffer();
    cs->new_data = false;
  }
}

The commands that are currently implemented are:

  • PRINT= Sends to the serial interface the status of the controller. The values are returned formatted as json
  • max_tmp=XX.XX Sets the maximum temperature (in Centigrades).
  • min_tmp=XX.XX Sets the minimum temperature (in Centigrades).
  • max_humidity=XX.XX Sets the maximum humidity (in percentage).
  • light=1|0 Sets the lights as on or off.

Clone this wiki locally