OVERVIEW
This is a smart 4-plant watering machine that caters to the different need of each plant.
I own a couple unfortunate plants, two small ones and two big ones. They suffer from dehydration whenever I’m away from home. I decided to design a plant watering machine because I want them to stay alive. Moreover, I want to design a watering machine that caters to the different need of every plant. The idea is that I am able to set a watering cycle such that the machine repetitively waters the plants with different amount of water every set amount of time. For example, I can configure the machine to water the 2 small plants with 1 portion of water and 2 big plants with 3 portions of water everyday.
The final product allows me to set the wait time between each watering cycle with 3 preset buttons (0~3 units of time). Also, for each individual plant, there are 3 preset buttons to set the watering amount (0~3 portions).
Labelled LED Interface:
plant 1 watering amount = 3 (15 seconds of watering)
plant 2 watering amount = 0
plant 3 watering amount = 2 (10 seconds of watering)
plant 4 watering amount = 1 (5 seconds of watering)
Time between cycle = 2 (20 seconds between each cycle)
PROCESS IMAGES AND REVIEW
One of the hardest problems of this project was designing the valves. It was a mechanical problem and I had zero experience in designing mechanics. I tried to design the valve three times and the last time worked.
In the first iteration, I built a valve with two crossing screws such that when the servo rotates, it “pinches” the tube to stop the water from flowing. However, this design was not good, because first of all, the tube was too hard to pinch, and second, the cross shape of the plastic base always trip on the tube and move the tube away.
For the second iteration, I ordered softer tubes that made it possible for the servo to stop water from flowing. In the above pictures, you can see the soft tube is much easier to bend.
In the third iteration, I remade the valves with round plastic base and wide apart screws. The crossing screws might cut the tube open. So I designed another version of the valve where the screws won’t pinch the tube, but rather stretch the tube. The soft tube is flexible enough to be stretched. The round plastic base also allows the tube to stay in place when the valve rotates.
This is how the valves look like when they are all open.
This is the prototype version of this watering machine. In the above image, you can see the first iteration of the valve design.
One problem I encountered was that the servos jitter really hard when the pump was on. I tested all servos in physical computing lab and found 4 servos that don’t jitter. I also added a capacitor to smooth out the power supply.
I also found it challenging to design the interface of the watering machine. I chose the Adafruit Trellis squishy 4×4 LED and button matrix simply because I love the feeling of pressing on them. However, with 16 buttons and LEDs it’s hard to tell the user how to use it and what the setting is. If I want to teach someone else to use the watering machine, I’d love it to be easy to explain and remember. The interface I came up with is the following. The top left button is a red button that execute the setting when it’s pressed. It blinks to confirm the setting. Column 2-4 allow the user to toggle the watering level from 0-3. The first column sets the wait time between each watering cycle from 0-3.
However, without the labels, it is extremely difficult to explain. Therefore, I labeled the buttons and asked someone who has never seen this project to try to interpret it. The only confusion with the labels is the time between cycle setting. The person wondered if it sets the watering frequency during a day or the amount of wait time between each cycle. Currently the time between cycle sets the amount of wait time between each cycle. One unit of time is 10 seconds. So in the above setting, there’s a 20-second wait between each cycle.
DISCUSSION
Response
“The interface might be confusing to some—no labels so it’s easy to get lost!”
Yeah I agree. It’d be great to use different colors to indicate the different functionality of each column. I could have also used a LED screen to show words and numbers. It was a personal decision to use the 4×4 LED and button matrix and I didn’t have enough variety of LED colors to color-code the functionality.
“I found the mechanism interesting, difficult to construct and very useful! Bravo Jeena!!”
Wahhoooo! It was pretty difficult to construct the valves. It took 3 iterations to finally get the valves to tighten properly. In the first iteration, I had two crossing screws close the valve by pinching the tube, but the tube was too hard to actually close up. The second iteration I bought softer tubes, which made it much easier. However, crossing screws were too weak to form a close seal. In the third iteration, I designed a slightly different valve mechanism, where the soft tubes are fixed on both end with zip ties and hot glue, and the valve simply stretch and bend the tube to form a seal.
Self critique
I mostly agree with the comments I received at the critique. I’m happy with how it turned out to be — a functioning watering machine that can water 4 different plants with a sufficient interface (for me). But I’d love it to be smaller, something that can be tucked away in my living room. Right now, it’s a giant open shelf of wires. It’s not pretty enough to occupy that much space. It’d be great to use a smaller clear acrylic box that encloses everything and allows me to see through.
What I learned
Mechanical problems are real.
I didn’t think the valves are hard to design at all. The idea is to water one plant at a time, so I close all valves but one to water only one plant. To switch between valves, I open another valve and close all other valves. I spent most time trying different ways to stop water from flowing while another valve is opened. In the prototype week, I didn’t figure out the mechanical problem. Next time, I should try to figure out the hardest part first.
Also, I gained a ton of soldering skills by soldering 32 LEDs in one go for the button interface, plus many wires later on. I learned how to be very careful and fast at the same time.
I drilled many holes to make the valves work. I drilled holes into the plastic pieces that come with the hobby servos first, then screw the screws in. It was not easy at all, given that the holes are all so tiny. Also, I drilled holes for the zip ties to secure the tubes onto the wooden shelf.
I learned how to pronounce “valve” correctly. It’s “vaaaalv” not “volve”. Ah.
Next steps
I will make a box with a clear acrylic door that can enclose the box.
TECHNICAL INFORMATION
Schematic
/* * Project 2 * Jeena Yin (qyin) * It took two weeks * * Collaboration: * Referenced code in * https://learn.adafruit.com/adafruit-trellis-diy-open-source-led-keypad/connecting * https://courses.ideate.cmu.edu/60-223/f2019/tutorials/code-bites#blink-without-blocking * * Summary: The code below waters 4 plant with different watering * amount according to the setting * * Inputs: * Adafruit Trellis LED buttons | PIN A2 * Valve 0 button | PIN 4 * Valve 1 button | PIN 5 * Valve 2 button | PIN 7 * Valve 3 button | PIN 8 * * Outputs: * Valve 0 servo | PIN 6 (PWM) * Valve 1 servo | PIN 9 (PWM) * Valve 2 servo | PIN 10 (PWM) * Valve 3 servo | PIN 11 (PWM) * Pump motor | PIN 3 */ #include <Wire.h> #include <Servo.h> #include "Adafruit_Trellis.h" #define NUMTRELLIS 1 #define numKeys (NUMTRELLIS * 16) #define INTPIN A2 // Valve 0 const int VALVE0_PIN = 6; // PWM const int VALVE0_SWITCH_PIN = 4; // for testing // Valve 1 const int VALVE1_PIN = 9; // PWM const int VALVE1_SWITCH_PIN = 5; // for testing // Valve 2 const int VALVE2_PIN = 10; // PWM const int VALVE2_SWITCH_PIN = 7; // for // Valve 3 const int VALVE3_PIN = 11; // PWM const int VALVE3_SWITCH_PIN = 8; // for testing // Pump const int PUMP_PIN = 3; const int VALVE_CLOSE_POS = 0; const int VALVE_OPEN_POS = 140; // 10 seconds as cycle length: if time set to 2 then water every 20 seconds const int CYCLELENGTH = 10; // 5 seconds as unit for watering amount const int WATERAMOUNTUNIT = 5; // Blink the red LED as feedback confirmation const int blinkLEDdelay = 70; // Array that stores the water amount setting int waterAmount[] = {0, 0, 0, 0}; int waterFrequency = 0; unsigned long microTimer = 0; unsigned long macroTimer = 0; unsigned long quarterMacroTimer = 0; // wait time between watering cycles in second unsigned long quarterMicroTimer = 0; // wait time between each plant in second int wateringPlantId = -1; // id of plant being watered // only water if frequency > 0 and wateramount is > 0 for any plant bool shouldWater = false; bool waitingForNextCycle = false; Servo valve0; Servo valve1; Servo valve2; Servo valve3; bool valveStates[] = {false, false, false, false}; Adafruit_Trellis matrix = Adafruit_Trellis(); Adafruit_TrellisSet trellis = Adafruit_TrellisSet(&matrix); void setup() { // put your setup code here, to run once: pinMode(PUMP_PIN, OUTPUT); pinMode(VALVE0_SWITCH_PIN, INPUT_PULLUP); pinMode(VALVE1_SWITCH_PIN, INPUT_PULLUP); pinMode(VALVE2_SWITCH_PIN, INPUT_PULLUP); pinMode(VALVE3_SWITCH_PIN, INPUT_PULLUP); pinMode(INTPIN, INPUT); Serial.begin(9600); valve0.attach(VALVE0_PIN); valve1.attach(VALVE1_PIN); valve2.attach(VALVE2_PIN); valve3.attach(VALVE3_PIN); for(int i = 0; i < 4; i++){ OpenValveForPlant(i); } OpenAllValves(); digitalWrite(INTPIN, HIGH); trellis.begin(0x70); // turn on all LEDs for (uint8_t i=0; i<numKeys; i++) { trellis.setLED(i); trellis.writeDisplay(); delay(50); } // then turn them off for (uint8_t i=0; i<numKeys; i++) { trellis.clrLED(i); trellis.writeDisplay(); delay(50); } trellis.setLED(0); // light up the red button only trellis.writeDisplay(); } void loop() { if (trellis.readSwitches()) { for (uint8_t i=0; i<numKeys; i++) { if (i == 0 && trellis.justReleased(i)) { BlinkLED(i); } if (trellis.justPressed(i)) { ButtonPressed(i); } } // tell the trellis to set the LEDs we requested trellis.writeDisplay(); } // Watering plants if(shouldWater) { if(millis()/1000 - microTimer >= quarterMicroTimer) { // switch to next plant if(wateringPlantId == 3) WaitForNextCycle(); // Cycle ends else{ GetReadyForPlant(wateringPlantId + 1); } } } // Waiting for next watering cycle else if(waitingForNextCycle) { if(millis()/1000 - macroTimer >= quarterMacroTimer) { Execute(); macroTimer = millis()/1000; } } // Only check test switches when it's not watering. else{ CheckSwitches(); } delay(30); } void ButtonPressed(int i) { // The red button is pressed if(i == 0) { Execute(); // if it was pressed, turn it on if (trellis.justPressed(i)) { trellis.setLED(i); } return; } int col = i % 4; int row = (int) (i / 4); // Set Time if(col == 0) { // Interface design silimar to a slider if (trellis.isLED(i)) { if(row == 3 || !trellis.isLED(i+4)) { // Frequency = 0 SetFrequency(0); for(int k = 1; k <= 3; k++) { trellis.clrLED(k*4); } } else { // Adjust Frequency SetFrequency(row); for(int k = 1; k <= row; k++) { trellis.setLED(k*4); } for(int k = row+1; k <= 3; k++) { trellis.clrLED(k*4); } } } else { // Adjust Frequency SetFrequency(row); for(int k = 1; k <= row; k++) { trellis.setLED(k*4); } for(int k = row+1; k <= 3; k++) { trellis.clrLED(k*4); } } } // Set water level else { // Interface design silimar to a slider if (trellis.isLED(i)) { if(col == 3 || !trellis.isLED(i+1)) { // No water SetWater(row, 0); for(int k = 1; k <= 3; k++) { trellis.clrLED(k+row*4); } } else { // Adjust water level SetWater(row, col); for(int k = 1; k <= col; k++) { trellis.setLED(k+row*4); } for(int k = col+1; k <= 3; k++) { trellis.clrLED(k+row*4); } } } else { // Adjust water level SetWater(row, col); for(int k = 1; k <= col; k++) { trellis.setLED(k+row*4); } for(int k = col+1; k <= 3; k++) { trellis.clrLED(k+row*4); } } } } // Helper function that starts watering the plant right now void Execute() { // Close all valves for(int i = 0; i < 4; i++) { CloseValve(i); } // only water if frequency > 0 and total wateramount is > 0 shouldWater = (waterFrequency > 0 && WaterAmountNonZero()); waitingForNextCycle = false; if(shouldWater) { Serial.println("Start watering"); TurnOnPump(); // Initialize global variables GetReadyForPlant(0); } else { TurnOffPump(); OpenAllValves(); Serial.println("Stop watering"); } } void GetReadyForPlant(int id) { Serial.print("Get ready for plant "); Serial.println(id); microTimer = millis()/1000; quarterMicroTimer = GetWaterTimeForPlant(id); Serial.print("Water amount(seconds): "); Serial.println(quarterMicroTimer); wateringPlantId = id; OpenValveForPlant(id); } void WaitForNextCycle() { Serial.println("Wait for next cycle... "); TurnOffPump(); OpenAllValves(); shouldWater = false; waitingForNextCycle = true; macroTimer = millis()/1000; quarterMacroTimer = waterFrequency * CYCLELENGTH; Serial.print("Wait time(seconds): "); Serial.println(quarterMacroTimer); } int GetWaterTimeForPlant(int id) { return waterAmount[id] * WATERAMOUNTUNIT; } // Change the global watering frequency per minute void SetFrequency(int frequency) { Serial.print("Set time: "); Serial.println(frequency); waterFrequency = frequency; } // Set the array of watering amount void SetWater(int plantId, int water) { Serial.print("Set water: "); Serial.print(plantId); Serial.print(" "); Serial.println(water); waterAmount[plantId] = water; } // Only close one valve void CloseValve(int valve) { // Don't close if is already closed if(!valveStates[valve]) return; else valveStates[valve] = false; Serial.print("Close "); Serial.println(valve); switch(valve) { case 0: valve0.write(VALVE_CLOSE_POS); break; case 1: valve1.write(VALVE_CLOSE_POS); break; case 2: valve2.write(VALVE_CLOSE_POS); break; case 3: valve3.write(VALVE_CLOSE_POS); break; default: break; } delay(50); } void OpenValve(int valve) { // Don't open if is already open if(valveStates[valve]) return; else valveStates[valve] = true; Serial.print("Open "); Serial.println(valve); switch(valve) { case 0: valve0.write(VALVE_OPEN_POS); break; case 1: valve1.write(VALVE_OPEN_POS); break; case 2: valve2.write(VALVE_OPEN_POS); break; case 3: valve3.write(VALVE_OPEN_POS); break; default: break; } delay(50); } // Helper function returns true if any plant water amount is > 0 bool WaterAmountNonZero() { for(int i = 0; i < 4; i++) { if(waterAmount[i] > 0) return true; } return false; } void CheckSwitches() { // return; if(!digitalRead(VALVE0_SWITCH_PIN)) { OpenValveForPlant(0); } if(!digitalRead(VALVE1_SWITCH_PIN)) { OpenValveForPlant(1); } if(!digitalRead(VALVE2_SWITCH_PIN)) { OpenValveForPlant(2); } if(!digitalRead(VALVE3_SWITCH_PIN)) { OpenValveForPlant(3); } } // Open valve for only plant id, close all other valves void OpenValveForPlant(int id) { for(int i = 0; i < 4; i++) { if(i == id) OpenValve(i); else CloseValve(i); } Serial.print("Opening valve for only plant "); Serial.println(id); } void TurnOnPump() { digitalWrite(PUMP_PIN, HIGH); Serial.println("Pump on"); } void TurnOffPump() { digitalWrite(PUMP_PIN, LOW); Serial.println("Pump off"); } // Blink the LED void BlinkLED(int i) { trellis.clrLED(i); trellis.writeDisplay(); delay(blinkLEDdelay); trellis.setLED(i); trellis.writeDisplay(); delay(blinkLEDdelay); trellis.clrLED(i); trellis.writeDisplay(); delay(blinkLEDdelay); trellis.setLED(i); trellis.writeDisplay(); delay(blinkLEDdelay); trellis.clrLED(i); trellis.writeDisplay(); delay(blinkLEDdelay); trellis.setLED(i); trellis.writeDisplay(); delay(blinkLEDdelay); } void OpenAllValves() { Serial.println("Open all"); for(int i = 0; i < 4; i++){ OpenValve(i); } }
Comments are closed.