Automated Kitchen Garden Project

Hi everyone! I am a British Colombia Institute of Technology Student and is currently studying Mechanical Engineering. I am currently doing a project for my Engineering Project course. My project is a Automated Kitchen Garden controlled by Arduino and Rasp Pi that uses a growing method called hydroponics. I hope to seek help from your expertise on programming the Arduino and automation.

I have started with the main frame of the program and have ran into problems with time alarms. I’m basically trying to mix the PC time sync code with the Time alarm code from the Arduino Cookbook by Michael Margolis. The timers trigger just fine, but the alarms won’t trigger. I’m using a Arduino Mega. Below is my code.

#include <TimeAlarms.h>

#include<Time.h>

#define TIME_MSG_LEN  11   // time sync consists of a HEADER followed by ten
                           // ascii digits
#define TIME_HEADER  'T'   // Header tag for serial time sync message

//inputs (excluding pH and EC)
const int temp1=A0;
const int temp2=A1;
const int temp3=A2;
const int temp4=A3;
const int temp5=A4;
const int temp6=A5;
const int level1=2;
const int level2=3;
const int level3=4;
const int level4=5;

//outputs

 //Pumps:
const int pHup=22;
const int pHdown=24;
const int nutVeg=26;
const int nutFlw=28;
const int mixVeg=30;
const int mixFlw=31;
const int feedVeg=32;
const int feedFlw=33;
 //Drivers:
const int led1=34;
const int led2=35;
const int led3=36;
const int led4=37;
 //Fans:
const int ledFan1=38; 
const int ledFan2=39;
const int ledFan3=40;
const int cabFan=41;
const int boxFan=42;
const int coolFan1=43;
const int coolFan2=44;
 //Others:
const int coolPad1=45;
const int coolPad2=46;
const int alarm=47;
//Variables:
unsigned long vegStr;
unsigned long flwStr;
void setup(){
  //Serial devices baud rate set
  Serial.begin(38400);
  Serial1.begin(38400);
  Serial2.begin(38400);
  Serial3.begin(38400);
  //Define pin mode
   //Inputs
  pinMode(temp1,INPUT);
  pinMode(temp2,INPUT);
  pinMode(temp3,INPUT);
  pinMode(temp4,INPUT);
  pinMode(temp5,INPUT);
  pinMode(temp6,INPUT);
  pinMode(level1,INPUT);
  pinMode(level2,INPUT);
  pinMode(level3,INPUT);
  pinMode(level4,INPUT);
   //Outputs
  pinMode(pHup,OUTPUT);
  pinMode(pHdown,OUTPUT);
  pinMode(nutVeg,OUTPUT);
  pinMode(nutFlw,OUTPUT);
  pinMode(mixVeg,OUTPUT);
  pinMode(mixFlw,OUTPUT);
  pinMode(feedVeg,OUTPUT);
  pinMode(feedFlw,OUTPUT);
  pinMode(led1,OUTPUT);
  pinMode(led2,OUTPUT);
  pinMode(led3,OUTPUT);
  pinMode(led4,OUTPUT);
  pinMode(ledFan1,OUTPUT);
  pinMode(ledFan2,OUTPUT);
  pinMode(ledFan3,OUTPUT);
  pinMode(cabFan,OUTPUT);
  pinMode(boxFan,OUTPUT);
  pinMode(coolFan1,OUTPUT);
  pinMode(coolFan2,OUTPUT);
  pinMode(coolPad1,OUTPUT);
  pinMode(coolPad2,OUTPUT);
  pinMode(alarm,OUTPUT);
  //Turn on Dry Box Fan
  digitalWrite(boxFan,HIGH);
  //Time Request
  Serial.println("waiting for sync message");
  //Alarms and Timers
  Alarm.alarmRepeat(22,15,0,dayTime);
  Alarm.alarmRepeat(22,16,0,nightTime);
  Alarm.timerRepeat(120,wtrVeg);
  Alarm.timerRepeat(120,wtrFlw);
}

void loop(){
 Alarm.delay(0);
  if(Serial.available() )
  {
    processSyncMessage();
  }

  if(timeStatus()!= timeNotSet)
  {
    // here if the time has been set
    digitalClockDisplay();
  }
  

  if (millis()-vegStr>=15000){
    digitalWrite(feedVeg,LOW);
    Serial.println("Veg Pumpp Off");
  }

  if (millis()-flwStr>=15000){
    digitalWrite(feedFlw,LOW);
    Serial.println("Flw Pumpp Off");
  }
}

void dayTime(){
  digitalWrite(led1,HIGH);
  digitalWrite(led2,HIGH);
  digitalWrite(led3,HIGH);
  digitalWrite(led4,HIGH);
  Serial.println("Lights On");
}

void nightTime(){
  digitalWrite(led1,LOW);
  digitalWrite(led2,LOW);
  digitalWrite(led3,LOW);
  digitalWrite(led4,LOW);
  Serial.println("Lights Off");
}

void wtrVeg(){
  digitalWrite(feedVeg,HIGH);
  vegStr=millis();
  Serial.println("Veg Pump On");
}

void wtrFlw(){
  digitalWrite(feedFlw,HIGH);
  flwStr=millis();
  Serial.println("Flw Pump On");
}
  

void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year());
  Serial.println();
  Alarm.delay(1000);
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon 
  // and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void processSyncMessage() {
  // if time sync available from serial port, update time and return true
  // time message consists of a header and ten ascii digits
  while(Serial.available() >=  TIME_MSG_LEN ){  
    char c = Serial.read() ;
    Serial.print(c);
    if( c == TIME_HEADER ) {
      time_t pctime = 0;
      for(int i=0; i < TIME_MSG_LEN -1; i++){
        c = Serial.read();
        if( isDigit(c)) {
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number
        }
      }
      setTime(pctime);   // Sync clock to the time received on serial port
    }
  }
}

Please ignore the long list of pin designations as they are used for future coding. As you can see, the time library realated codes are the same as the ones found in the Cookbook. I’m using the Alarms to turn on and off some relays which are controlling LED panels for the plants. The timers trigger feeding pumps for feeding nutrient infused solution to the plant roots.

Maybe you need to set the time before setting the alarms?

If you want to deal with hours::minutes::seconds and days and months perhaps you should use an RTC.

Hmmm maybe it is a hardware trouble not a software one. Can you show us a little schematic of the connections? How are the relays atached to Arduino and the LEDs? What is the relay coil voltage?

Regards.

counting the hours:: minutes, days months etc without RTC ? I dont think this is a good way of doing things!

parcolai_bcit: The timers trigger just fine, but the alarms won't trigger.

Put that sketch to one side and write a new sketch that just triggers a single alarm, and get that working properly. Once you know how to do that, you can use that knowledge to fix your original sketch. Trying to learn how to use a new API in a complicated untested sketch is a recipe for trouble.

Right, I've tried the time alarm sketches from the Cookbook. They work fine. I hooked up a LED and a resistor to the pin that gets triggered by the alarm, it works fine. The time sync portion of the sketch also works fine. I'm using the processing example sketch with the sketch that I posted, and the time prints fine and in sync. I'm not using a RTD because I have 3 other serial devices with my Arduino mega plus a bunch of other I/O.

Could it possibly work if I put the processsyncmessage function before the alarm, in the void setup clause?

But did you try setting the time in your sketch before setting the alarms?

I'll try with the setTime function. But I'm asking my previous question because the last command of the processsyncmessage function is setTime(pctime). So theoretically, if I add the processsyncmessage function in before the alarms in the setup function, it should work, right?

parcolai_bcit: I'll try with the setTime function. But I'm asking my previous question because the last command of the processsyncmessage function is setTime(pctime). So theoretically, if I add the processsyncmessage function in before the alarms in the setup function, it should work, right?

I'm not sure if it will work or not but all example I had looked at had the correct time before setting up alarms but your sketch set the time after the alarms were set. If this is the root cause of your problem then you need to consider what happens if you update the time after the alarms are set. Will they still work at correct time or stop working/fire at wrong time. Maybe worth testing this if they do start working after initial time setup.

So I have tried putting the processSyncMessage function before the alarms, and it didn't work. So what now?

parcolai_bcit: So I have tried putting the processSyncMessage function before the alarms, and it didn't work. So what now?

Well if your saying the time is read over serial and it sets the internal time correctly and this all happens before the alarms are set then I cannot see why it's not working. Can you post your latest sketch for both arduino and processing and links to the Time & TimeAlarms libraries your using and I will try it myself to see if I can pinpoint the problem.

Hey Riva! Sorry for replying so late, I had some other stuff I have to take care of during the week. I actually have a different set of code that simulates the alarm function that I want in the actual main program.

#include <TimeAlarms.h>

#include <Time.h>


#define TIME_MSG_LEN  11   // time sync consists of a HEADER followed by ten
                           // ascii digits
#define TIME_HEADER  'T'   // Header tag for serial time sync message
const int Pin=22;

void setup()  {
  Serial.begin(38400);
  Serial.println("Waiting for time sync message");
  const int Pin=22;
  pinMode (Pin,OUTPUT);
  processSyncMessage();
  Alarm.alarmRepeat(8,10,0,OnAlarm);
  Alarm.alarmRepeat(8,15,0,OffAlarm);
}

void loop(){
  
  if(Serial.available() )
  {
    processSyncMessage();
  }
  if(timeStatus()!= timeNotSet)
  {
    // here if the time has been set
    digitalClockDisplay();
  }
  Alarm.delay(0);
 
}

void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year());
  Serial.println();
  delay(1000);
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon 
  // and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}
void OnAlarm(){
    digitalWrite (Pin ,HIGH);
}
void OffAlarm(){
  digitalWrite (Pin, LOW);
}
void processSyncMessage() {
  // if time sync available from serial port, update time and return true
  // time message consists of a header and ten ascii digits
  while(Serial.available() >=  TIME_MSG_LEN ){  
    char c = Serial.read() ;
    Serial.print(c);
    if( c == TIME_HEADER ) {
      time_t pctime = 0;
      for(int i=0; i < TIME_MSG_LEN -1; i++){
        c = Serial.read();
        if( isDigit(c)) {
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number
        }
      }
      setTime(pctime);   // Sync clock to the time received on serial port
    }
  }
}

And here’s the Processing code.

import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;

import processing.serial.*;

public static final short portIndex = 0;  // select the com port, 0 is the first port
public static final char TIME_HEADER = 'T'; //header byte for arduino serial time message 
public static final char TIME_REQUEST = 7;  // ASCII bell character 
public static final char LF = 10;     // ASCII linefeed
public static final char CR = 13;     // ASCII linefeed
Serial myPort;     // Create object from Serial class

void setup() {  
  size(200, 200);
  println(Serial.list());
  println(" Connecting to -> " + Serial.list()[portIndex]);
  myPort = new Serial(this,Serial.list()[portIndex], 38400);
}

void draw()
{
  if ( myPort.available() > 0) {  // If data is available,
    char val = char(myPort.read());         // read it and store it in val
    if(val == TIME_REQUEST){
       long t = getTimeNow();
       sendTimeMessage(TIME_HEADER, t);   
    }
    else
    { 
       if(val == LF)
           ; //igonore
       else if(val == CR)           
         println();
       else  
         print(val); // echo everying but time request
    }
  }  
}

void mousePressed() {  
  sendTimeMessage( TIME_HEADER, getTimeNow());   
}


void sendTimeMessage(char header, long time) {  
  String timeStr = String.valueOf(time);  
  myPort.write(header);  // send header and time to arduino
  myPort.write(timeStr);   
}

long getTimeNow(){
  // java time is in ms, we want secs    
  GregorianCalendar cal = new GregorianCalendar();
  cal.setTime(new Date());
  int  tzo = cal.get(Calendar.ZONE_OFFSET);
  int  dst = cal.get(Calendar.DST_OFFSET);
  long now = (cal.getTimeInMillis() / 1000) ; 
  now = now + (tzo/1000) + (dst/1000); 
  return now;
}

Please not that by the end of the project, I will not be using this code to send time info to the Arduino. Instead, there will be a GUI program that will do the exact same and do some other functions, which will be written in Java. But that will be a later discussion, as i know for a fact that the sync is working just fine. The time printed on the Processing IDE is in sync with the PC time.

As suspected the alarms fail to fire because the time was not set before they were defined.
You had put a processSyncMessage() in setup but unless you could time the mouseclick to send the time before the routine was called it would still not set the time and fall through to setting the alarms. I just put a while around the processSyncMessage() call so it would sit there until the time was set and then continue. This worked fine for me as you can see from processing output.

#include <TimeAlarms.h>

#include <Time.h>


#define TIME_MSG_LEN  11   // time sync consists of a HEADER followed by ten
// ascii digits
#define TIME_HEADER  'T'   // Header tag for serial time sync message
const int Pin=22;

void setup()  {
  Serial.begin(38400);
  Serial.println("Waiting for time sync message");
  pinMode (Pin,OUTPUT);
  while(timeStatus() != timeSet){
    processSyncMessage();
  }
  Serial.println("Synced");
  Alarm.alarmRepeat(9,55,0,OnAlarm);
  Alarm.alarmRepeat(9,56,0,OffAlarm);
}

void loop(){
  
  if(Serial.available() )
  {
    processSyncMessage();
  }
  if(timeStatus()!= timeNotSet)
  {
    // here if the time has been set
    digitalClockDisplay();
  }
  Alarm.delay(0);
  
}

void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year());
  Serial.println();
  delay(1000);
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon 
  // and leading 0
  Serial.print(":");
  if(digits < 10)
  Serial.print('0');
  Serial.print(digits);
}
void OnAlarm(){
  digitalWrite (Pin ,HIGH);
  Serial.println("XXXXXX ALARM ON XXXXXX");
}
void OffAlarm(){
  digitalWrite (Pin, LOW);
  Serial.println("XXXXXX ALARM OFF XXXXXX");
}
void processSyncMessage() {
  // if time sync available from serial port, update time and return true
  // time message consists of a header and ten ascii digits
  while(Serial.available() >=  TIME_MSG_LEN ){  
    char c = Serial.read() ;
    Serial.print(c);
    if( c == TIME_HEADER ) {
      time_t pctime = 0;
      for(int i=0; i < TIME_MSG_LEN -1; i++){
        c = Serial.read();
        if( isDigit(c)) {
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number
        }
      }
      setTime(pctime);   // Sync clock to the time received on serial port
    }
  }
}

9:54:55 5 4 2014
9:54:56 5 4 2014
9:54:57 5 4 2014
9:54:58 5 4 2014
9:54:59 5 4 2014
XXXXXX ALARM ON XXXXXX
9:55:00 5 4 2014
9:55:01 5 4 2014
9:55:02 5 4 2014
9:55:03 5 4 2014
9:55:04 5 4 2014
9:55:05 5 4 2014
9:55:06 5 4 2014
9:55:07 5 4 2014
9:55:08 5 4 2014
9:55:09 5 4 2014
9:55:10 5 4 2014
9:55:11 5 4 2014
9:55:12 5 4 2014
9:55:13 5 4 2014
9:55:14 5 4 2014
9:55:15 5 4 2014
9:55:16 5 4 2014
9:55:17 5 4 2014
9:55:18 5 4 2014
9:55:19 5 4 2014
9:55:20 5 4 2014
9:55:21 5 4 2014
9:55:22 5 4 2014
9:55:23 5 4 2014
9:55:24 5 4 2014
9:55:25 5 4 2014
9:55:26 5 4 2014
9:55:27 5 4 2014
9:55:28 5 4 2014
9:55:29 5 4 2014
9:55:30 5 4 2014
9:55:31 5 4 2014
9:55:32 5 4 2014
9:55:33 5 4 2014
9:55:34 5 4 2014
9:55:35 5 4 2014
9:55:36 5 4 2014
9:55:37 5 4 2014
9:55:38 5 4 2014
9:55:39 5 4 2014
9:55:40 5 4 2014
9:55:41 5 4 2014
9:55:42 5 4 2014
9:55:43 5 4 2014
9:55:44 5 4 2014
9:55:45 5 4 2014
9:55:46 5 4 2014
9:55:47 5 4 2014
9:55:48 5 4 2014
9:55:49 5 4 2014
9:55:50 5 4 2014
9:55:51 5 4 2014
9:55:52 5 4 2014
9:55:53 5 4 2014
9:55:54 5 4 2014
9:55:55 5 4 2014
9:55:56 5 4 2014
9:55:57 5 4 2014
9:55:58 5 4 2014
9:55:59 5 4 2014
XXXXXX ALARM OFF XXXXXX
9:56:00 5 4 2014
9:56:01 5 4 2014
9:56:02 5 4 2014
9:56:03 5 4 2014

Thanks a lot Riva! I think that will do. I'll keep you posted on my progress on the project.

Hello again! Over the past week, I had been making good progress on the project. Now I want to make it so that I can change the time the alarm triggers through serial monitor. Ultimately, this function will be integrated to the Java program and it will send messages to the Mega and change the alarm trigger time. Any idea of how I can do this? I have been reading up the Time Alarm library Sticky, but no luck so far.

The easy way may be to tweak your processSyncMessage() function so it can also detect alarm on/off times and adjust them in the same way it sets the time but obviously with a different message headers.