Open Hydroponics evandavey

open-hydroponics / firmware / arduino_aeroponics_firmware / arduino_aeroponics_firmware.pde

11.9 KB · raw

/* Hydroponics System 

-- Aeroponics controller
-- Author: Evan Davey, evan.j.davey@gmail.com

--V1.1
--- adds string array for state labels
--- converts data string to json from csv
--- implements a newState variable to allow state change events
--- sends data string on state changes
--- adds a name variable to identify devices

Cycles a pump for use in a hyrdoponics system
Implemented as a state machine 
X-valid serial command 
      *menu -> send main menu
      *data -> send data
      **rst -> state 3 
0-idle 
  Outputs: pumpCtrl=Low,statusLed=Blink,reset=Low
  off timer ends -> state 1
  waterSensor=High -> state 2
  
1-pumping
  Outputs: pumpCtrl=High,statusLed=Low,reset=Low
  on timer ends -> state 1
  waterSensor=High -> state 2
  
2-error,water low
  Outputs: pumpCtrl=Low,statusLed=fast blink,reset=Low
  requires reset to leave this state

3-reset
  Outputs: pumpCtrl=Low,statusLed=Low,reset=High
  Waits delay_time before resetting


*/


// hardware
const int pumpCtrl = 2; //connect via a transistor with back voltage protection
const int statusLed = 13; //connect using a current limiting resistor
const int reset = 12; //connect to reset via a transistor tied to ground
const int waterSensor = 8; //connect to a normally closed float switch

// firmware defaults
const float vers=1.1;
char* name="Aeroponics 1";
const unsigned long default_pump_on_time = 30000; //on 30 seconds 
const unsigned long default_pump_off_time = 300000; //off 5 minutes
const long baud_rate=115200; //baud rate for serial 
volatile int debug_mode=0; //when high, outputs status data 
int pumpCycles = 1; //counter of pump cycles
int reset_delay=4; //time to wait after reset command issued
int start_state=0; //starting state
char* stateStrings[]={"0 - Idle","1 - Pumping","2 - Error, Water too low","3 - Waiting to reset"};
 

// 0 - idle
// 1 - pumping
// 2 - error, water level
// 3 - waiting for reset
volatile int currentState = -1;
volatile int newState = -1;




// Variables will change:
int ledState = LOW;             // ledState used to set the LED
int pumpState = LOW;             // ledState used to set the LED
long pumpTimer = 0; // will store last time LED was updated
int waterSensorState = LOW;


volatile char cmd='-1';
long pump_on_time; //on 30 seconds 
long pump_off_time; //off 5 minutes
unsigned long previousMillis = 0;
boolean in_menu=false;


void setup() {


  pinMode(pumpCtrl, OUTPUT);
  pinMode(statusLed, OUTPUT);
  pinMode(reset, OUTPUT);
  pinMode(waterSensor, INPUT);

  pump_off_time=default_pump_off_time;
  pump_on_time=default_pump_on_time;

  Serial.begin(baud_rate);

  //delay(1000);


  newState=start_state;


}

void keepTime() {
   //keep the time
  unsigned long currentMillis = millis();
  pumpTimer += currentMillis-previousMillis;
  previousMillis = currentMillis;
  
 
  
}

void mainControlLoop() {
  
  keepTime();
  
  //check for errors
  errorCheck();
  
  //handle state
  handleState();

  
}


void loop()
{

  mainControlLoop();
  
  //in the main menu
  if (in_menu) {
    menu_main();
  }
 
  //process received serial data
  //available command *menu
  handleSerialIn();

  if (debug_mode && !in_menu) {


    if (pumpTimer % 5000 == 0) {
      Serial.println(":: DBEUG ::");
      status_toString();
    }
  } 

}


void handleSerialIn() {


  if ( Serial.available() > 0) { // if there are bytes waiting on the serial port

    char inByte = Serial.read(); // read a byte
    if (inByte == '*') { // if that byte is the desired character

      int len = 5; // expected string is 6 bytes long
      char inString[len]; // declare string variable
      for (int i = 0; i < len; i++) {
        inString[i] = Serial.read();
        delay(100);
      }

      //Serial.println(inString);

      if ( strstr(inString, "menu") != NULL ) { // check to see if the respose is "reset"
          menu_main(); 
      } else if (strstr(inString, "data") != NULL ) {

        //send data
        data_toString();

      } else if (strstr(inString, "*rst") != NULL ) {

        Serial.println("Resetting..");
        //send data
        newState=3;

      } else if (strstr(inString, "sone") != NULL ) {

        //Serial.println("Changing state to 1");
        //send data
        pumpTimer=0;
        newState=1;
        Serial.flush();

      } else if (strstr(inString, "stwo") != NULL ) {

        //send data
        newState=2;
        Serial.flush();

      } else if (strstr(inString,"idle") != NULL ) {

        //send data
        newState=0;
        pumpTimer=0;
        Serial.flush();


     }
    }
  }
}


void state_change() {
 
   data_toString(); 
  
}

void handleState() {
  
  if (newState!=currentState) {
    currentState=newState;
    state_change();
  }

  switch(currentState) {
    //IDLE
  case 0:

    flashLed(statusLed,1,1000);
    digitalWrite(pumpCtrl,LOW);
    //pump off timer exceeded, switch to pumping state
    if (pumpTimer >= pump_off_time) {
      pumpTimer=0;
      pumpCycles++;
      newState=1;
    }
    break;

    //PUMPING
  case 1:


    digitalWrite(pumpCtrl,HIGH);
    //pump on timer exceeded, switch to idle state
    if (pumpTimer >= pump_on_time) {
      pumpTimer=0;
      newState=0;
    }
    break;

    //ERROR
  case 2:
    digitalWrite(pumpCtrl,LOW);
    flashLed(statusLed,3,100);
    break;

    //RESET
  case 3:
  
   
    
    for (int i=reset_delay;i>0;i--) {
      Serial.print("Will reset in ");
      Serial.print(i);
      Serial.println("s");
      delay(1000);
    }
    
    Serial.println("** Resetting now **");
    digitalWrite(reset,HIGH);
    newState=0;
    break;



  default:
    break;

  }      


}

void data_toString() {


  
  
  //delay(1000);
  Serial.print("{");
  
  Serial.print("\"State\"");
  Serial.print(":");
  Serial.print(currentState);
  Serial.print(",");
  
   Serial.print("\"State Description\"");
  Serial.print(":");
  Serial.print(stateStrings[currentState]);
  Serial.print(",");
  
  Serial.print("\"Pump Timer\"");
  Serial.print(":");
  Serial.print(pumpTimer/1000);
  Serial.print(",");
  
  Serial.print("\"Pump Off Time\"");
  Serial.print(":");
  Serial.print(pump_off_time);
  Serial.print(",");
  
  Serial.print("\"Pump On Time\"");
  Serial.print(":");
  Serial.print(pump_on_time);
  Serial.print(",");
  
  Serial.print("\"Pump Cycles\"");
  Serial.print(":");
  Serial.print(pumpCycles);
  Serial.print(",");
  
  Serial.print("\"Water Sensor\"");
  Serial.print(":");
  Serial.print(waterSensorState);
  Serial.print(",");
  
  Serial.print("\"Name\"");
  Serial.print(":");
  Serial.print("\"");
  Serial.print(name);
  Serial.print("\"");
  Serial.print(",");

  Serial.print("}");
  
  Serial.println("");


}


void errorCheck() {

  // water level too low, enter error state
  if (!checkWater()) {
    newState=2; 
    return;
  } 





}


//checks the water sensor and returns true if water is present
boolean checkWater() {

  waterSensorState=digitalRead(waterSensor);

  if (waterSensorState == HIGH) {
    return false;
  } 
  else {
    return true;
  } 

}

void state_toString(int state) {

  
  Serial.println(stateStrings[state]);
 

}

void status_toString() {

  Serial.print("Version: ");
  Serial.println(vers);
  Serial.print("Debug Mode: ");
  Serial.println(debug_mode);
  Serial.print("State: ");
  state_toString(currentState);
  Serial.print("Pump Timer: ");
  Serial.println(pumpTimer/1000);
  Serial.print("Pump Cycles: ");
  Serial.println(pumpCycles);
  Serial.print("Pump Off Time: ");
  Serial.println(pump_off_time/1000);
  Serial.print("Pump On Time: ");
  Serial.println(pump_on_time/1000);
  Serial.print("Water Sensor: ");
  Serial.println(waterSensorState); 

}

void menu_show() {

  Serial.println("::::: HYDRO SYSTEM :::::");
  status_toString();
  delay(500);
  Serial.println("--------------------------------");
  Serial.println("(1) - exit menu");
  Serial.println("(2) - reset");
  Serial.println("(3) - toggle debug");
  Serial.println("(4) - force state");
  Serial.println("(5) - set pump on time");
  Serial.println("(6) - set pump off time");

}

void menu_main() {


  //first time in, show the menu
  if (!in_menu) {
    delay(700);
    Serial.flush();
    menu_show();
    in_menu=true;
  }


  //wait for command
  if (in_menu) {

    if ( Serial.available() > 0) { // if there are bytes waiting on the serial port

      cmd = Serial.read();

      switch (cmd) {

      case '1':
        Serial.println("exiting menu");
        in_menu=false;
        break;

      case '2':

        in_menu=false;
        newState=3;
        break;

      case '3':
        in_menu=false;
        debug_mode=!debug_mode;
        Serial.print("Debug mode=");
        Serial.println(debug_mode);
        break;

      case '4':


        Serial.println("States-");
        for (int i=0;i<=3;i++) {
          state_toString(i);
        }
        Serial.print("State Number (0-3): ");

        Serial.flush();
        while (true) {
          //ensure operation continues while waiting
          mainControlLoop();
          if ( Serial.available() > 0) { // if there are bytes waiting on the serial port
            char * cmd_c="0";
            cmd_c[0]=Serial.read();

            int cmd_i=atoi(cmd_c);

            if (cmd_i<=3) {
              Serial.print("Setting state to: ");
              Serial.println(cmd_i);
              pumpTimer=0;
              newState=cmd_i;
              in_menu=false;
              break;
            } 
            else {

              Serial.print("State Number (0-3): ");
              Serial.flush();
            }
          } 

        }
        break;

      case '5':
        
        Serial.println("(1) 10 seconds");
        Serial.println("(2) 20 seconds");
        Serial.println("(3) 30 seconds");
        Serial.println("(4) 60 seconds");
        Serial.println("(5) 120 seconds");

        Serial.flush();
        while (true) {
          //ensure operation continues while waiting
          mainControlLoop();
          int t;
          if ( Serial.available() > 0) { 
            cmd=Serial.read();

           
            switch(cmd) {

            case '1':
              t=10;
              break;
            case '2':
              t=20;
              break;
            case '3':
              t=30;
              break;
            case '4':
              t=60;
              break;
            case '5':
              t=120;
              break;
            default:
              t=30;
            }
            
          pump_on_time=t*1000;
          Serial.print("new pump time:") ;
          Serial.println(t); 
          in_menu=false;
          break; 

          } 
         
        }  
        break;
      
      case '6':
        
        Serial.println("(1) 60 seconds");
        Serial.println("(2) 2 minutes");
        Serial.println("(3) 5 minutes");
        Serial.println("(4) 10 minutes");
        Serial.println("(5) 15 minutes");

        Serial.flush();
        while (true) {
          //ensure operation continues while waiting
          mainControlLoop();
          long t;
          if ( Serial.available() > 0) { 
            cmd=Serial.read();

           
            switch(cmd) {

            case '1':
              t=60;
              break;
            case '2':
              t=120;
              break;
            case '3':
              t=300;
              break;
            case '4':
              t=600;
              break;
            case '5':
              t=900;
              break;
            default:
              t=300;
            }
            
          pump_off_time=t*1000;
         
        
          Serial.print("new pump time:") ;
          Serial.println(t); 
          in_menu=false;
          break; 

          } 
         
        }  
        break;   
      default:
        Serial.println("illegal command");
        Serial.flush();
        menu_show(); 

      }

    }
  }



}


//blinks a Led pin numBlinks at the blinkRate 
void flashLed(int ledPin,int numBlinks,int blinkRate) {


  digitalWrite(ledPin,LOW);
  for (int i=1;i<=numBlinks;i++) {

    digitalWrite(ledPin,HIGH);
    delay(blinkRate);
    digitalWrite(ledPin,LOW);
    delay(blinkRate);

  }

}