Skip navigation

Arduino Projects

1 Post authored by: rubroe

In order to control plants via Dropbox we built the following control circuit / Cyber Physical System (CPS) / Internet of Things application. To support sustainability, the water used is taken out of a natural pond. The required energy is supplied by a solar panel that charges an old automotive accumulator (battery). Images of the plant growth progress and data is being sent to dropbox constantly. 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 and CPS.

 

{gallery} Layout

Screen Shot 2015-05-16 at 12.55.46.png

Layout:

SmarDen_schem.png

Schema:

CPS_1.png

SysML CPS Profile & abstract reference architecture (1/3):

{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

Images sent to Dropbox:

Screen Shot 2015-05-16 at 11.49.40.png

Arduino:

Screen Shot 2015-05-16 at 11.49.18.png

Battery:

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,

Ruben Rösner