49713 Designing for the Internet of Things
· 16 members
A hands-on introductory course exploring the Internet of Things and connected product experiences.
An IOT device that monitors the moisture level of one’s plant and depending on if the plant is adequately watered, the plant will light up and swivel around as a happy plant and cheer the owner up.
Keeping a plant’s water level satisfactory may be difficult to judge from just the appearance of the soil. With this IOT device, the soil-moisture level is easily visible by a red ( unsatisfactory ), green (satisfactory) or blue (very hydrated) LED and the exact measurement is logged in a spreadsheet where the plant owner can make sure the doing well. If the plant’s moisture level is below the set satisfactory level (may depend on the type of plant) and shines the red LED, an SMS notification will be sent to the plant owner’s mobile device reminding them to water the plant every ten minutes.
When the user is home, the plant owner can interact with the plant. By pressing a button, if the plant has an adequate water level, the plant will light up, play a tune, and swivel around to demonstrate it is a happy plant. If the water level is below satisfactory, a bad tone will be played and the plant will not dance around.
For this project, I wanted to go beyond checking the plant’s moisture level. Although it is important to keep your plant healthy and watered, I feel that adding something fun makes the experience more interactive and gives the user a sense of reward for taking care of their plant. The “dancing plant” idea was influenced by positive reinforcement from video games – for example, if you take care of a pet, their health or love for you will increase and they’ll smile or show you appreciation in some way. I wanted to create a subtle emotional attachment to the plant to potentially lengthen the lifespan of the plant.
The LED indicators were influenced by ambient design where a plant owner can just glance over at their plant and see if the plant needs water or not. This was similar to the umbrella that has a handle that indicates the weather outside and helps the user decide if the umbrella is needed at a quick glance at the glowing color [1].
After I determined an overall idea of what I wanted my IOT device to do, as explained in my Approach above, I created tasks and subtasks to complete for each component, integrating them together as I went along. This is the order of how I assembled my final product:
One of the ambitious ideas I rejected was because it was outside the scope of my project was a water pump to automatically water the plant if the moisture level was unsatisfactory. It apparently involved additional power sources and more advance techniques and took away from the user interaction. The other idea I did not pursue was constantly monitoring for a change in moisture level to detect when the user watered the plant and the moisture level went from unsatisfactory to satisfactory. The plant’s happy dance and lights would automatically start when that change was detected, but I chose a more simplistic path by adding a pushbutton to activate it.
The main challenge that I encountered was realizing that the code had to run sequentially and tasks could not be completed simultaneously. I wanted the LED’s, music and servo motor to work simultaneously, but instead, I solved this alternating LED illuminations, servo motor actions, and music.
Parts list:
//Created by Langley Vogt
//https://diotlabs.daraghbyrne.me/docs/controlling-outputs-motors/servo/#step-2-controlling-a-servo
//https://docs.particle.io/tutorials/hardware-projects/maker-kit/
//CONSIDERED WATER LEVELS
//Good Example= 1327
//Bad < 800
//Very watered > 1800
int lowMoistThres = 600;
int highMoistThres = 1800;
//Define servo motor and name
Servo spinnerServo;
int servoPin = A0;
int servoPos = 0; //intialize at zero
//LED declarations
int ledGreen = D7;
int ledRed = D6;
int ledBlue = D5;
int ledYellow = D4;
//soil
int moistPin = A1;
int moistVal = 0;
//Speaker piezo
int speakerPin = D2;
//push button
int buttonPin = D8;
int buttonState;
//Note Definitions
int NOTE_C = 1046;
int NOTE_D = 1174;
int NOTE_E = 1318;
int lowNOTE_C = 1046/2;
int lowNOTE_E = 1318/2;
int yayMelody[] = {NOTE_C, NOTE_C, NOTE_C, NOTE_C, NOTE_D, NOTE_C, NOTE_D, NOTE_E};
int noteDuration[] = {4, 8, 8, 4, 4, 4, 4, 2};
int badMelody[] = {lowNOTE_E, lowNOTE_C};
int noteDuration2[] = {2, 2};
// store the last time published
int last_published = -1;
int last_publishedSMS = -1;
//*********************************************************************//
void setup() {
//Define which pin the servo is connected to
spinnerServo.attach(A0);
//Call function so person can enter angle on app
Particle.function("servo", servoControl); //control via cloud
//Push variable to console
Particle.variable("MoistureReading", moistVal);
Particle.variable("buttonState", buttonState);
//Declare led as output
pinMode(ledGreen, OUTPUT);
pinMode(ledRed, OUTPUT);
pinMode(ledBlue, OUTPUT);
pinMode(ledYellow, OUTPUT);
//Initialize leds off
digitalWrite(ledRed, LOW);
digitalWrite(ledGreen, LOW);
digitalWrite(ledBlue, LOW);
digitalWrite(ledYellow, LOW);
//define pin mode input/ouputs for non-leds
pinMode(moistPin, INPUT);
pinMode(buttonPin, INPUT_PULLUP);
}
void loop() {
//send published data to spreadsheet every minute
writeToSpreadsheet();
//initialize led lights off
digitalWrite(ledRed, LOW);
digitalWrite(ledGreen, LOW);
digitalWrite(ledBlue, LOW);
digitalWrite(ledYellow, LOW);
//Variable definitions
moistVal = analogRead(moistPin);
buttonState = digitalRead(buttonPin);
//if the soil is not moist enough, shine red led
if (moistVal < lowMoistThres) {
digitalWrite(ledRed, HIGH);
delay(100);
//send SMS to remind user to water plant
sendSMS();
}
//Soil is adequately moist, green
else if (moistVal > lowMoistThres && moistVal < highMoistThres) {
//digitalWrite(ledPin, HIGH);
digitalWrite(ledGreen, HIGH);
delay(100);
}
//Very moist soil, blue light indicator
else if (moistVal > highMoistThres) {
//digitalWrite(ledPin_3, HIGH);
digitalWrite(ledBlue, HIGH);
delay(100);
}
//else just turn off all the lights
else {
digitalWrite(ledGreen, LOW);
digitalWrite(ledRed, LOW);
digitalWrite(ledBlue, LOW);
digitalWrite(ledYellow, LOW);
}
//If the user presses the button and the plant was watered adequately, flash lights, play music and spin
if ( (moistVal > lowMoistThres) && (buttonState == LOW)) {
//flash lights
digitalWrite(ledRed, HIGH);
delay(300);
digitalWrite(ledGreen, HIGH);
delay(300);
digitalWrite(ledBlue, HIGH);
delay(300);
digitalWrite(ledYellow, HIGH);
delay(300);
//flash lights
digitalWrite(ledRed, LOW);
delay(300);
digitalWrite(ledGreen, LOW);
delay(300);
digitalWrite(ledBlue, LOW);
delay(300);
digitalWrite(ledYellow, LOW);
delay(300);
//play melody
playNotes();
//flash lights
digitalWrite(ledRed, HIGH);
delay(300);
digitalWrite(ledGreen, HIGH);
delay(300);
digitalWrite(ledBlue, HIGH);
delay(300);
digitalWrite(ledYellow, HIGH);
delay(300);
//flash lights
digitalWrite(ledRed, LOW);
delay(300);
digitalWrite(ledGreen, LOW);
delay(300);
digitalWrite(ledBlue, LOW);
delay(300);
digitalWrite(ledYellow, LOW);
delay(300);
//turn all on
digitalWrite(ledRed, HIGH);
digitalWrite(ledGreen, HIGH);
digitalWrite(ledBlue, HIGH);
digitalWrite(ledYellow, HIGH);
//Swivel forward
for (int angle = 0; angle < 180; angle++) {
spinnerServo.write(angle);
delay(10);
}
//pause
delay(200);
//Swivel backward
for (int angle2 = 180; angle2 > 0; angle2--) {
spinnerServo.write(angle2);
delay(10);
}
delay(100);
//Spin faster, 2 rounds
spinnerServo.write(90);
delay(800);
spinnerServo.write(0);
delay(800);
spinnerServo.write(90);
delay(800);
spinnerServo.write(0);
delay(800);
//turn all on
digitalWrite(ledRed, LOW);
digitalWrite(ledGreen, LOW);
digitalWrite(ledBlue, LOW);
digitalWrite(ledYellow, LOW);
//flash lights
digitalWrite(ledRed, HIGH);
delay(300);
digitalWrite(ledGreen, HIGH);
delay(300);
digitalWrite(ledBlue, HIGH);
delay(300);
digitalWrite(ledYellow, HIGH);
delay(300);
//flash lights
digitalWrite(ledRed, LOW);
delay(300);
digitalWrite(ledGreen, LOW);
delay(300);
digitalWrite(ledBlue, LOW);
delay(300);
digitalWrite(ledYellow, LOW);
delay(300);
delay(100);
}
//Button pressed but not enough moisture
else if ((moistVal < lowMoistThres) && (buttonState == LOW)) {
//change light to yay
digitalWrite(ledRed, LOW);
delay(10);
digitalWrite(ledRed, LOW);
delay(10);
digitalWrite(ledRed, LOW);
playBadNotes();
delay(100);
}
else {
//do nothing
}
delay(100);
}
//Melody that plays when plant is watered adequately
void playNotes()
{
//for loop for melody, total 26 notes
for (int thisNote = 0; thisNote < 8; thisNote++) {
// to calculate the note duration, take one second
// divided by the note type.
//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
int newNoteDurations = 1000/noteDuration[thisNote];
tone(speakerPin, yayMelody[thisNote],newNoteDurations);
// to distinguish the notes, set a minimum time between them.
// the note's duration + 30% seems to work well:
int pauseBetweenNotes = newNoteDurations * 1.30;
delay(pauseBetweenNotes);
//no tone
noTone(speakerPin);
}
}
void playBadNotes()
{
//for loop for melody, total 26 notes
for (int thisNote2 = 0; thisNote2 < 2; thisNote2++) {
// to calculate the note duration, take one second
// divided by the note type.
//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
int newNoteDurations2 = 1000/noteDuration2[thisNote2];
tone(speakerPin, badMelody[thisNote2],newNoteDurations2);
// to distinguish the notes, set a minimum time between them.
// the note's duration + 30% seems to work well:
int pauseBetweenNotes2 = newNoteDurations2 * 1.30;
delay(pauseBetweenNotes2);
//no tone
noTone(speakerPin);
}
}
//Have user set angle for viewing position
int servoControl (String command) {
int newPos = command.toInt();
servoPos = constrain(newPos, 0, 180);
spinnerServo.write(servoPos);
return 1;
}
//Write data to spreadsheet
void writeToSpreadsheet() {
// check if 1 minute has elapsed
if( last_published + 60000 < millis() ){
Particle.publish( "writeToSpreadsheet", String(moistVal) );
last_published = millis();
}
}
//Notify plant owner via text message that their plant is thirsty
void sendSMS() {
// check if 1 minute has elapsed
//10 minutes is 6e8
if( last_publishedSMS + 60000 < millis() ){
Particle.publish( "sendSMS", String("Water your Plant!") );
last_publishedSMS = millis();
}
}
Click to Expand
Next steps could include implementing code that detects when the user has watered the plant adequately to trigger the happy plant dance. An upgraded servo motor, or even a torque motor, would make the swiveling quieter, less jerky and support heavier plants.
The form was prototyped, but I would like the final form of the apparatus to be made of wood, hide all of the wires so only the LED’s are visible on the wood’s surface for a minimalist, ambient aesthetic.
Lastly, a live dashboard accessible through a plant monitoring application that can be viewed on one’s mobile phone would enable the plant owner to monitor the plant’s moisture level easily throughout the day. It could also have fun functions that play different music melodies and activate various dances for the plant.
This was a very enjoyable project and I had a lot of fun making a more interactive plant that would lift the plant owner’s mood. I have not worked a servo motor before so I was able to quickly learn those skills, learn the limitations, adjust, and apply them to this project.
I learned simplistically “enchanting” an analog object can elevate the overall experience and interaction with it. As a student, this project was definitely a good stepping stone towards a larger, more involved IOT project where form is required. This project emphasized conceptual ideas and basics to prepare me to execute more complex projects.
A hands-on introductory course exploring the Internet of Things and connected product experiences.
An IOT device that monitors the moisture level of one’s plant and depending on if the plant is adequately watered, the plant will light up and swivel around as a happy plant and cheer the owner up.
November 6th, 2019