Overview
Basic
The botanyBOT will keep a plant’s soil within an optimal level of moisture depending on the watering regime selected using two Arduinos and one Ethernet Shield. It does this by always reading the soil’s moisture level by default and watering the plant when it falls below the optimal range.
Watering Settings
The watering settings determine the optimal moisture range and the length of each watering session. “Sprinkle” is for plants that need dryer soil; this setting has the lowest moisture range and the shortest watering session. “Soak” has the highest moisture range and the longest watering session, keeping the soil noticeably wet. “Shower” falls between the previous settings.
Tank Level Indicators
The lights indicating the water level in the tank are triggered by a ladder of leads mounted to the inside of the tank. Once there is a minimum reading between lead at the same level, a corresponding LED is lit. The LED turns off once the reading is no longer detected.
Notification Settings
The notification settings determine the information that is passed on to the user via a set of notification methods. The notification types available are emails, twitter, and push notifications directly to a smartphone. *Setting up these notifications is as easy as replacing the filler email account/twitter handle/etc. on pushingbox.com with the user’s own information. They can be further customized by adding more services (i.e. adding a second email address) or modifying the actual content of the notifications themselves.* The notification levels build off one another. Selecting “Few” notifications means that the user will only be notified when the water tank is almost empty. The “Some” setting adds notifications before and after the plant is watered for them; by notifying them a few hours before the plant is watered, the user is given a chance to be involved and water the plant themselves. When set to “Many” notifications, the botanyBOT will send you messages about your parenting skills, interesting botanical facts, and other fun messages.
Error Cases
Procedure/Details
Notifications
We wanted the system communicate messages and reminders to the user from the beginning, and Twitter was always a primary vehicle for this. At first, we investigated accessing the Twitter API from the Arduino using Python. However, once our Arduino Ethernet Shield arrived, we had a discovered a notification service that could access Twitter as well as send emails and push notifications all in one step. The source Arduino code did not function as is, but with some tweaking, namely, pinging the site in order to use its IP address to reach it, triggering became notifications was simple. Through the website, we created an account and composed “scenarios” that are collections of customized services (emails, tweets, push notifications). Each scenario has its own ID number, which is used by the Arduino to trigger the specified notifications.
Testing Case Codes
One of the more time consuming aspects of our software component was constantly modifying and creating new code to test different test cases and subsets of the entire system. This process is useful for modular debugging, but it leaves a large opportunity for mistakes between steps.
Serial Communication
By far, the software component that gave us the most trouble is the communication between the two Arduinos we were using. We needed two Arduino because me required a multitude of pins for out lights, buttons, and sensors. The Arduino needed to communicate because only one had access to the internet through the Ethernet Shield. Even after downloading a facilitating library (EasyTransfer), use of the hardware serial pins proved more finicky than expected, so we moved on to using a software serial connection. The limitations associated with this method turned out to be inconsequential in the scope of our project.
Basic
The botanyBOT will keep a plant’s soil within an optimal level of moisture depending on the watering regime selected using two Arduinos and one Ethernet Shield. It does this by always reading the soil’s moisture level by default and watering the plant when it falls below the optimal range.
Watering Settings
The watering settings determine the optimal moisture range and the length of each watering session. “Sprinkle” is for plants that need dryer soil; this setting has the lowest moisture range and the shortest watering session. “Soak” has the highest moisture range and the longest watering session, keeping the soil noticeably wet. “Shower” falls between the previous settings.
Tank Level Indicators
The lights indicating the water level in the tank are triggered by a ladder of leads mounted to the inside of the tank. Once there is a minimum reading between lead at the same level, a corresponding LED is lit. The LED turns off once the reading is no longer detected.
Notification Settings
The notification settings determine the information that is passed on to the user via a set of notification methods. The notification types available are emails, twitter, and push notifications directly to a smartphone. *Setting up these notifications is as easy as replacing the filler email account/twitter handle/etc. on pushingbox.com with the user’s own information. They can be further customized by adding more services (i.e. adding a second email address) or modifying the actual content of the notifications themselves.* The notification levels build off one another. Selecting “Few” notifications means that the user will only be notified when the water tank is almost empty. The “Some” setting adds notifications before and after the plant is watered for them; by notifying them a few hours before the plant is watered, the user is given a chance to be involved and water the plant themselves. When set to “Many” notifications, the botanyBOT will send you messages about your parenting skills, interesting botanical facts, and other fun messages.
Error Cases
- If the moisture sensor detects no moisture, the botanyBOT assumes the sensor is not inserted in soil and does nothing except check the moisture level for a change.
- The botanyBOT will wait a few minutes between watering and rechecking the moisture to account for calibration and the time it takes the water to spread and settle in the soil.
- Plants will not be watered more than three times a day.
Procedure/Details
Notifications
We wanted the system communicate messages and reminders to the user from the beginning, and Twitter was always a primary vehicle for this. At first, we investigated accessing the Twitter API from the Arduino using Python. However, once our Arduino Ethernet Shield arrived, we had a discovered a notification service that could access Twitter as well as send emails and push notifications all in one step. The source Arduino code did not function as is, but with some tweaking, namely, pinging the site in order to use its IP address to reach it, triggering became notifications was simple. Through the website, we created an account and composed “scenarios” that are collections of customized services (emails, tweets, push notifications). Each scenario has its own ID number, which is used by the Arduino to trigger the specified notifications.
Testing Case Codes
One of the more time consuming aspects of our software component was constantly modifying and creating new code to test different test cases and subsets of the entire system. This process is useful for modular debugging, but it leaves a large opportunity for mistakes between steps.
Serial Communication
By far, the software component that gave us the most trouble is the communication between the two Arduinos we were using. We needed two Arduino because me required a multitude of pins for out lights, buttons, and sensors. The Arduino needed to communicate because only one had access to the internet through the Ethernet Shield. Even after downloading a facilitating library (EasyTransfer), use of the hardware serial pins proved more finicky than expected, so we moved on to using a software serial connection. The limitations associated with this method turned out to be inconsequential in the scope of our project.
/*
12/14/12
M. Lopardo, A. Sanford, S. Yang, M. Perry
Arduino Garden - PoE Section 3
Demo Code for Arduino 1 - with Ethernet Shield
This code runs the standard demo sequence.
-Controls Notification Settings
-Controls Tank Level Display and notifications
-Notifies when the tank is almost empty (on red)
and when it is full
-Notifies when plant is about to be watered and after watering
when signaled by Arduino 2
*/
#include <SPI.h>
#include <Ethernet.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(3,4); //(RX,TX)
//ETHERNET GUYS
#define TankWarningNote "v63DDA9A68B33C3C" //Tank warning scenario
#define thirstyNote "vD4D062AF0A0AF20" //Dry plant scenario
#define wateredNote "v2F69A6AFB35C073" //"Already watered" scenario
#define TankFullNote "v0BC2B01AF942C3B" //Full tank scenario
char serverName[] = "api.pushingbox.com"; //The site that handles the notifications
byte mac[] = {
0x90, 0xA2, 0xDA, 0x0D, 0x7B, 0x85 }; // physical mac address here
byte ip[] = {
10, 32, 27, 13 }; // Arduino's ip address
byte myserver[] = {
213, 186, 33, 19 }; // api.pushingbox.com IP address
EthernetClient client;
unsigned long LastTankWarning = 0;
//Button initializations
const int notifButton = 2; //pushbutton connected to pin 2 (on interrupt 0)
volatile int setting = 1; //Set mode initially to 1
volatile int NotifRestartFlag = 0; //See if loop needs to be restarted
volatile unsigned long OldNotifButtonTime = 0; //last time the mode was changed
unsigned long timeNow = 0;
//Notification Modes
int notifMode;
const int few = 1; //just tank level notifications
const int some = 2; //tank and watering notifications
const int many = 3; //tank, watering, and extra notifications
//LEDS
const int fewPin = A3; //notification setting Leds
const int somePin = A4;
const int manyPin = A5;
//Tank stuff
int sense1 = A0; //water level sensors
int sense2 = A1;
int sense3 = A2;
int sense1_val = 0; //values read by sensors
int sense2_val = 0;
int sense3_val = 0;
int full = 50; /*analog value to quantify resistance*/
const int greenLed1 = 7; //indicator Leds
const int greenLed2 = 6;
const int yellowLed = 5;
const int redLed = 8;
boolean tankSent = false; //Keep track of notifications sent
boolean thirstSent = false;
boolean wateredSent = false;
boolean TankFullSent = false;
char inChar; //incoming character from other Arduino
void setup() {
Ethernet.begin(mac, ip);
Serial.begin(9600);
mySerial.begin(9600);
//notifs
attachInterrupt(0, NotifButtonChange, FALLING); //jump to mode switch if button pressed
pinMode (fewPin, OUTPUT);
pinMode (somePin, OUTPUT);
pinMode (manyPin, OUTPUT);
digitalWrite (fewPin, HIGH); //initialize setting as "few"
notifMode = few;
//tank
pinMode (greenLed1, OUTPUT); //setting the LEDs (top) to output
pinMode (greenLed2, OUTPUT);
pinMode (yellowLed, OUTPUT);
pinMode (redLed, OUTPUT);
digitalWrite (greenLed1, HIGH);
digitalWrite (greenLed2, HIGH);
digitalWrite (yellowLed, HIGH);
digitalWrite (redLed, HIGH);
}
void loop() {
// NotifRestartFlag = 0;
CheckOtherArd();
sense1_val = analogRead(sense1); //read top tank sensor
CheckOtherArd(); //Check other Arduino for notification signals
if (sense1_val < full)
{
digitalWrite (greenLed1, LOW); //turn off led when its governing sensor is not detecting
TankFullSent = false; //reset notification
}
if (sense1_val > full)
{
digitalWrite (greenLed1, HIGH);
tankFull();
}
sense2_val = analogRead(sense2); //read 2nd tank sensor
CheckOtherArd();
if (sense2_val < full)
{
digitalWrite (greenLed2, LOW);
}
if (sense2_val > full)
{
digitalWrite (greenLed2, HIGH);
}
sense3_val = analogRead(sense3); //read yellow tank sensor
CheckOtherArd();
if (sense3_val < full)
{
digitalWrite (yellowLed, LOW);
digitalWrite (redLed, HIGH); //blink red LED when close to empty
delay(500);
digitalWrite (redLed, LOW);
delay(500);
digitalWrite (redLed, HIGH);
delay(500);
tankWarning();
}
if (sense3_val > full)
{
digitalWrite (yellowLed, HIGH);
tankSent = false;
}
}
void NotifButtonChange () {
if (OldNotifButtonTime + 10 <= millis()) {
switch (setting) {
case few:
digitalWrite (fewPin, LOW);
digitalWrite (somePin, HIGH);
setting = some;
break;
case some:
digitalWrite (somePin, LOW);
digitalWrite (manyPin, HIGH);
setting = many;
break;
case many:
digitalWrite (manyPin, LOW);
digitalWrite (fewPin, HIGH);
setting = few;
break;
}
OldNotifButtonTime = millis();
// NotifRestartFlag = 1;
}
}
void tankWarning () {
while (!tankSent) {
if (client.connect(myserver, 80)) { //starts client connection, checks for connection
client.print("GET /pushingbox?devid=");
client.print(TankWarningNote);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(serverName);
client.println("User-Agent: Arduino");
client.println();
tankSent = true;
LastTankWarning = millis();
client.stop();
}
}
}
void tankFull () {
while (!TankFullSent) {
if (client.connect(myserver, 80)) {
client.print("GET /pushingbox?devid=");
client.print(TankFullNote);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(serverName);
client.println("User-Agent: Arduino");
client.println();
TankFullSent = true;
LastTankWarning = millis();
client.stop();
}
}
}
void CheckOtherArd() {
if (mySerial.available() > 0) { //Check the Software Serial for notification signals
inChar = mySerial.read();
if (inChar == 't') {
while (!thirstSent) {
if (client.connect(myserver, 80)) {
client.print("GET /pushingbox?devid=");
client.print(thirstyNote);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(serverName);
client.println("User-Agent: Arduino");
client.println();
thirstSent = true;
wateredSent = false;
client.stop();
}
}
}
if (inChar == 'w') {
while (!wateredSent) {
if (client.connect(myserver, 80)) {
client.print("GET /pushingbox?devid=");
client.print(wateredNote);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(serverName);
client.println("User-Agent: Arduino");
client.println();
thirstSent = false;
wateredSent = true;
client.stop();
}
}
}
}
}
12/14/12
M. Lopardo, A. Sanford, S. Yang, M. Perry
Arduino Garden - PoE Section 3
Demo Code for Arduino 1 - with Ethernet Shield
This code runs the standard demo sequence.
-Controls Notification Settings
-Controls Tank Level Display and notifications
-Notifies when the tank is almost empty (on red)
and when it is full
-Notifies when plant is about to be watered and after watering
when signaled by Arduino 2
*/
#include <SPI.h>
#include <Ethernet.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(3,4); //(RX,TX)
//ETHERNET GUYS
#define TankWarningNote "v63DDA9A68B33C3C" //Tank warning scenario
#define thirstyNote "vD4D062AF0A0AF20" //Dry plant scenario
#define wateredNote "v2F69A6AFB35C073" //"Already watered" scenario
#define TankFullNote "v0BC2B01AF942C3B" //Full tank scenario
char serverName[] = "api.pushingbox.com"; //The site that handles the notifications
byte mac[] = {
0x90, 0xA2, 0xDA, 0x0D, 0x7B, 0x85 }; // physical mac address here
byte ip[] = {
10, 32, 27, 13 }; // Arduino's ip address
byte myserver[] = {
213, 186, 33, 19 }; // api.pushingbox.com IP address
EthernetClient client;
unsigned long LastTankWarning = 0;
//Button initializations
const int notifButton = 2; //pushbutton connected to pin 2 (on interrupt 0)
volatile int setting = 1; //Set mode initially to 1
volatile int NotifRestartFlag = 0; //See if loop needs to be restarted
volatile unsigned long OldNotifButtonTime = 0; //last time the mode was changed
unsigned long timeNow = 0;
//Notification Modes
int notifMode;
const int few = 1; //just tank level notifications
const int some = 2; //tank and watering notifications
const int many = 3; //tank, watering, and extra notifications
//LEDS
const int fewPin = A3; //notification setting Leds
const int somePin = A4;
const int manyPin = A5;
//Tank stuff
int sense1 = A0; //water level sensors
int sense2 = A1;
int sense3 = A2;
int sense1_val = 0; //values read by sensors
int sense2_val = 0;
int sense3_val = 0;
int full = 50; /*analog value to quantify resistance*/
const int greenLed1 = 7; //indicator Leds
const int greenLed2 = 6;
const int yellowLed = 5;
const int redLed = 8;
boolean tankSent = false; //Keep track of notifications sent
boolean thirstSent = false;
boolean wateredSent = false;
boolean TankFullSent = false;
char inChar; //incoming character from other Arduino
void setup() {
Ethernet.begin(mac, ip);
Serial.begin(9600);
mySerial.begin(9600);
//notifs
attachInterrupt(0, NotifButtonChange, FALLING); //jump to mode switch if button pressed
pinMode (fewPin, OUTPUT);
pinMode (somePin, OUTPUT);
pinMode (manyPin, OUTPUT);
digitalWrite (fewPin, HIGH); //initialize setting as "few"
notifMode = few;
//tank
pinMode (greenLed1, OUTPUT); //setting the LEDs (top) to output
pinMode (greenLed2, OUTPUT);
pinMode (yellowLed, OUTPUT);
pinMode (redLed, OUTPUT);
digitalWrite (greenLed1, HIGH);
digitalWrite (greenLed2, HIGH);
digitalWrite (yellowLed, HIGH);
digitalWrite (redLed, HIGH);
}
void loop() {
// NotifRestartFlag = 0;
CheckOtherArd();
sense1_val = analogRead(sense1); //read top tank sensor
CheckOtherArd(); //Check other Arduino for notification signals
if (sense1_val < full)
{
digitalWrite (greenLed1, LOW); //turn off led when its governing sensor is not detecting
TankFullSent = false; //reset notification
}
if (sense1_val > full)
{
digitalWrite (greenLed1, HIGH);
tankFull();
}
sense2_val = analogRead(sense2); //read 2nd tank sensor
CheckOtherArd();
if (sense2_val < full)
{
digitalWrite (greenLed2, LOW);
}
if (sense2_val > full)
{
digitalWrite (greenLed2, HIGH);
}
sense3_val = analogRead(sense3); //read yellow tank sensor
CheckOtherArd();
if (sense3_val < full)
{
digitalWrite (yellowLed, LOW);
digitalWrite (redLed, HIGH); //blink red LED when close to empty
delay(500);
digitalWrite (redLed, LOW);
delay(500);
digitalWrite (redLed, HIGH);
delay(500);
tankWarning();
}
if (sense3_val > full)
{
digitalWrite (yellowLed, HIGH);
tankSent = false;
}
}
void NotifButtonChange () {
if (OldNotifButtonTime + 10 <= millis()) {
switch (setting) {
case few:
digitalWrite (fewPin, LOW);
digitalWrite (somePin, HIGH);
setting = some;
break;
case some:
digitalWrite (somePin, LOW);
digitalWrite (manyPin, HIGH);
setting = many;
break;
case many:
digitalWrite (manyPin, LOW);
digitalWrite (fewPin, HIGH);
setting = few;
break;
}
OldNotifButtonTime = millis();
// NotifRestartFlag = 1;
}
}
void tankWarning () {
while (!tankSent) {
if (client.connect(myserver, 80)) { //starts client connection, checks for connection
client.print("GET /pushingbox?devid=");
client.print(TankWarningNote);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(serverName);
client.println("User-Agent: Arduino");
client.println();
tankSent = true;
LastTankWarning = millis();
client.stop();
}
}
}
void tankFull () {
while (!TankFullSent) {
if (client.connect(myserver, 80)) {
client.print("GET /pushingbox?devid=");
client.print(TankFullNote);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(serverName);
client.println("User-Agent: Arduino");
client.println();
TankFullSent = true;
LastTankWarning = millis();
client.stop();
}
}
}
void CheckOtherArd() {
if (mySerial.available() > 0) { //Check the Software Serial for notification signals
inChar = mySerial.read();
if (inChar == 't') {
while (!thirstSent) {
if (client.connect(myserver, 80)) {
client.print("GET /pushingbox?devid=");
client.print(thirstyNote);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(serverName);
client.println("User-Agent: Arduino");
client.println();
thirstSent = true;
wateredSent = false;
client.stop();
}
}
}
if (inChar == 'w') {
while (!wateredSent) {
if (client.connect(myserver, 80)) {
client.print("GET /pushingbox?devid=");
client.print(wateredNote);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(serverName);
client.println("User-Agent: Arduino");
client.println();
thirstSent = false;
wateredSent = true;
client.stop();
}
}
}
}
}