Skip navigation

Arduino Projects

1 Post authored by: rubroe

In order to control our plants via Dropbox we built the following circuit / cyber physical system / IoT application. You might notice that the Arduino, the wired components and the irrigation run "autonomous". This is another aspect of our project, to increase sustainable gardening. The water comes from a natural pond, the power from an old automotive battery/solar panel. This project was part of a thesis and the advanced development / recursive modelling has been performed with SysML to verify applicability of Model Based Systems Engineering (MBSE) in the context of IoT.

 

{gallery} Layout

Screen Shot 2015-05-16 at 12.55.46.png

Layout:

SmarDen_schem.png

Schema:

{gallery} Dropbox GUI and Hardware

Screen Shot 2015-05-16 at 11.50.58.png

Measured data: Battery level; Humidity; Temperature; XBee connection; Water level

Screen Shot 2015-05-16 at 11.50.33.png

Camera: Growth progress

Screen Shot 2015-05-16 at 11.49.40.png

Arduino: Components

Screen Shot 2015-05-16 at 11.49.18.png

Battery:

Screen Shot 2015-05-16 at 11.49.59.png

Solar panel

Arduino code:

#include <MemoryFree.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/power.h>


#include "pb_encode.h"
#include "pb_decode.h"
#include "messages_pb.h"


#include <SoilHumidity2.h>
#include <LM35.h>


//#include <AltSoftSerial.h>
#include <SoftwareSerial.h>


#include <JPEGCamera.h>


// sensors


SoilHumidity2 hsensor(3, 1); // soil resistance sensor: digital out pin, analog in pin
LM35 tsensor(0); // temperature sensor: analog in pin


const int batVPin = 2;   // bat level analog pin
const int waterVPin = 3; // water level in pin
const int waterOPin = 5; // water level sensor digital out pin
const int pumpOPin = 6;  // pump digital out pin


long pumpEndT = -1; // time to turn pump off, -1 if pump is off


long lastCommand = millis(); // time of last command. if we do not receive a command within 2 min. we go to sleep for 56 secs -> save power
int pumpBattLevel = 0;  // level of bat while pumping


const int WATER_LEVEL = 400;




// communication
SoftwareSerial s(8,9);      // cam serial
SoftwareSerial s2(10,11);  // xbee serial




JPEGCamera cam(s);




// xbee comm protocol
const unsigned int MAGIC = 7557; // frame prefix


messages_SensorData sensorData;
messages_Command commandData;
messages_picData picData;
messages_Ok okData;


char rBuffer[16];      // read buffer
uint8_t wBuffer[32];   // write buffer
pb_ostream_t stream;




// xbee comm protocol parsing
int readState = 0; // 0: magic_h, 1: magic_l, 2: len_h, 3: len_l, 4: buffer
int readExpectedLen = 0;
int m;
uint8_t h;
uint8_t l;


uint8_t command = 255;
int command_arg;


// control led
int led = 13;
int value = HIGH;


// power control
volatile long sleepOn = 0;
int powerPin = 12;


void softReset() // Restarts program from beginning but does not reset the peripherals and registers
{
  asm volatile ("  jmp 0");
}




ISR(WDT_vect)
{
  //Serial.println("ISR");
  if (sleepOn > -1)
  {
  }
  else
  {
    softReset();
  }
}




void setupWDT()
{
  /*** Setup the WDT ***/


  /* Clear the reset flag. */
  MCUSR &= ~(1<<WDRF);


  /* In order to change WDE or the prescaler, we need to
   * set WDCE (This will allow updates for 4 clock cycles).
   */
  WDTCSR |= (1<<WDCE) | (1<<WDE);


  /* set new watchdog timeout prescaler value */
  WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */


  /* Enable the WD interrupt (note no reset). */
  WDTCSR |= _BV(WDIE);
}


void enterSleep()
{
//   set_sleep_mode(SLEEP_MODE_PWR_SAVE);   /* EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption. */
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   /* EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption. */


  /* Now enter sleep mode. */
  sleep_enable();
  sleep_mode();






}


void camSave()
{
  /*
   s.begin(38400);
    s.listen();
  wdt_reset();
  cam.reset();
  delay(6000);
  wdt_reset();
  cam.powerSaving(true);
  */
}




void sleepIt(long duration)
{
  LEDLow();
  sleepOn = duration/8000;
  if (sleepOn == 0)
    sleepOn = 1;
  digitalWrite(powerPin, LOW);
  while (sleepOn)
  {
    enterSleep();
  /* The program will continue from here after the WDT timeout*/
    sleepOn--;
  }
  sleep_disable(); /* First thing to do is disable sleep. */

  /* Re-enable the peripherals. */
  power_all_enable();
  delay(100);
  digitalWrite(powerPin, HIGH);


// camSave();
}




// parse xbee comm
void doSerial()
{
  s2.listen();
  //Serial.println("event");
  while (s2.available())
  {
    invertLED();
    switch (readState)
    {
    case 0:
      h = s2.read();
      if (h == highByte(MAGIC))
        readState = 1;
      break;
    case 1:
      l = s2.read();
      m = word(h,l);
      if (m != MAGIC)
        readState = 0;
      else
      {
        readState = 2;
      }
      m = 0;
      break;
    case 2:
      h = s2.read();
      readState = 3;
      break;
    case 3:
      l = s2.read();
      readExpectedLen = word(h, l);
      // Serial.print("len ");
      //  Serial.println(readExpectedLen, DEC);
      if (readExpectedLen > 32)
      {
        readState = 0;
      }
      else
      {
        readState = 4;
      }
      break;
    case 4:
      if (s2.readBytes(rBuffer, readExpectedLen) == readExpectedLen)
      {
        pb_istream_t stream = pb_istream_from_buffer((uint8_t *)rBuffer, sizeof(rBuffer));
        pb_decode(&stream, messages_Command_fields, &commandData);
        command = commandData.command;
        //  Serial.print(7,DEC);
        //  Serial.println(command,DEC);
      }
      readState = 0;
      break;
    default:
      readState = 0;
      break;
    }
  }
}




void setup()
{
  stream = pb_ostream_from_buffer(wBuffer, sizeof(wBuffer));
  s2.begin(9600);    // set low xbee baudrate to reduce error rate.
  s.begin(38400);    // default cam baudrate is 38400
//  Serial.begin(9600);  // use serial for debug, but may block if used together with software serial
//  Serial.println("setup");
  pinMode(led, OUTPUT);
  pinMode(batVPin, INPUT);
  pinMode(waterVPin, INPUT);
  pinMode(waterOPin, OUTPUT);
  pinMode(pumpOPin, OUTPUT);
  pinMode(powerPin, OUTPUT);
  // turn xbee on
  digitalWrite(powerPin, HIGH);


setupWDT();
lastCommand = millis();


// camSave();

// set LED to high
LEDHigh();
   // wdt_enable(WDTO_8S);   // set watchdog to 8 seconds. may be set lower in future


}


void loop()
{
  wdt_reset();   // trigger watchdog
  if (pumpEndT > 0 && pumpEndT < millis())  // check if pump is on
    offPump();
  doSerial();  // parse input
  if (command < 255)  // if we received a command: execute it
  {
    lastCommand = millis();
    invertLED();
    wdt_reset();
    doCommand();
    command = 255;
    // Serial.print(0,DEC);
    // Serial.print(command, DEC);
  }
  else
  {
    if (pumpEndT <= 0 && (millis() - lastCommand) > 60000)
    {
      sleepIt(56000);
      lastCommand = millis()-55000;
    }
  }



}


void LEDHigh()
{
   value = LOW;
invertLED();
}


void LEDLow()
{
   value = HIGH;
invertLED();
}


void invertLED()
{
  if (value == HIGH)
    value = LOW;
  else
    value = HIGH;
  digitalWrite(led, value);
}


void doPump()
{


  pb_ostream_t stream = pb_ostream_from_buffer(wBuffer, sizeof(wBuffer));
  if (pb_encode(&stream, messages_Ok_fields, &okData))
  {
    //Serial.print("b ");
    //Serial.println(stream.bytes_written, DEC);
     sendMessage(stream.bytes_written);
  }
//     else
//  Serial.println("error encoding ok in doPump");


  long duration = commandData.arg;
// Serial.print(millis(), DEC);Serial.print(" doPump ");Serial.println(duration, DEC);
  if (duration > 0)  // duration 0 == heart beat
  {
    pumpEndT = millis()+duration;
    digitalWrite(pumpOPin, HIGH);
    delay(500);
    pumpBattLevel = getBatLevel(); // get bat level only when bat is under load
  }


}


void doSleep()
{
  long duration = commandData.arg;

  pb_encode(&stream, messages_Ok_fields, &okData);


  sendMessage(stream.bytes_written);


  if (duration > 0)  // duration 0 == heart beat
  {
    sleepIt(duration);
  }


}


int getBatLevel()
{
   return analogRead(batVPin);
}


void getWaterLevel()
{
  digitalWrite(waterOPin, HIGH);
  delay(500);
  int x = analogRead(waterVPin);
  digitalWrite(waterOPin, LOW);
  bool hasWater = x > WATER_LEVEL;
  sensorData.hasWater = hasWater;
}


void offPump()
{
  //  Serial.print(millis(), DEC);Serial.println(" offPump");
  pumpEndT = -1;
  digitalWrite(pumpOPin, LOW);
}


void sendPic()
{
  // Serial.println();
  // Serial.println(9, DEC);
  // Serial port connected to the cam
  s.listen();
  delay(1000);
  cam.reset();
  delay(4000);
  wdt_reset();
// cam.powerSaving(false);


  /*
  delay(50);
   cam.chBaudRate(1);
   delay(50);
   s.end();
   s.begin(19200);
   delay(50);
   */
  invertLED();
  cam.takePicture();
  wdt_reset();
  delay(25);
  picData.size = cam.getSize();
  pb_ostream_t stream = pb_ostream_from_buffer(wBuffer, sizeof(wBuffer));
  pb_encode(&stream, messages_picData_fields, &picData);


  sendMessage(stream.bytes_written);
  delay(10);
  invertLED();
  wdt_reset();
  cam.readData(s2);
  s2.flush();
  cam.stopPictures();
  invertLED();
cam.powerSaving(true);


}


void sendSensorData()
{
  wdt_reset();
  sensorData.temperature = tsensor.temperature();
  wdt_reset();
  sensorData.humidity = hsensor.humidity();
  wdt_reset();
  if (pumpBattLevel > 0)
  {
    sensorData.batLevel = pumpBattLevel;
    pumpBattLevel = 0;
  }
  else
    sensorData.batLevel = getBatLevel();

  getWaterLevel();


  pb_ostream_t stream = pb_ostream_from_buffer(wBuffer, sizeof(wBuffer));
  pb_encode(&stream, messages_SensorData_fields, &sensorData);


  sendMessage(stream.bytes_written);
}


void sendMessage(size_t len)
{
// Serial.print("send ");Serial.println(len, DEC);
  sendStartMessage(len);
  s2.write(wBuffer, len);
  s2.flush();
  delay(100);
  //Serial.println("endsend");
}


void sendStartMessage(size_t len)
{
  s2.write(highByte(MAGIC));
  s2.write(lowByte(MAGIC));
  s2.write(highByte(len));
  s2.write(lowByte(len));
}


void doCommand()
{
  // Serial.print(8,DEC);
  // Serial.println(command,DEC);
  switch (command)
  {
  case 0:
    sendSensorData();
    break;
  case 1:
   doPump();
    break;
  case 2:
    sendPic();
    break;
  case 3:
    doSleep();
    break;
  default:
    break;
  }


}











      

 

BoM:

Raspberry Pi B+

Arduino Pro Mini

XBee

Charge Controller

Solar Panel

12V Battery

Temp Sensor

Humidity Sensor

Water Pump

Water Level Sensor

 

LinkSprite JPEG Color - Infrared

 

 

Open Source Software Packages:

Yet Another Java Service Wrapper http://sourceforge.net/projects/yajsw/

Yet Another Cron4J http://sourceforge.net/projects/yacron4j/

vfs dropbox plugin http://sourceforge.net/projects/vfs-dbx/

 

If you have any questions, please dont hesitate to contact us.

 

Best regards,

RR