Вы находитесь на странице: 1из 13

instructables

Self-sustaining Plant Wall

by IntroToMaking

Many busy professionals want to have plants in their home, but don’t have the time to maintain them. This self-
watering planter maintains the plant by itself, requiring only a tank refill every other week. It waters the plants when
the soil is dry, turns on the lights when the room is dark but the owner is awake and away, and signals when it
runs low on water.

Supplies:

Self-sustaining Plant Wall: Page 1


1. Case:
Wood finish Hinge (2 pack)
Latch (1x)
Screws (1 ½ and 1 ¾ inch)
1x6 wood (3x, cut into 24 inch lengths)
1x3 wood (1x, cut into 24 inch lengths)
Plywood (1x, 24”x24”)

2. Tank:
Freezer ice cube tub

3. Planters & Plants:


Foamcore
Plastic
Tape
Soil
Pothos plants (variety)

4. Electronics:
1 Arduino Uno
1 breadboard (permanent is an option)
2x LED Grow Light Strips (2/6 that come with that product)
2x peristaltic liquid pump with silicone tubing
longer tubing

From Elegoo 37 Sensor Kit V2:


1 Arduino water-level sensor
1 Arduino photo-resistor
1 I2C real-time clock (code assumes DS3231)
1 5V relay

An additional 5V relay
2 I2C soil-moisture sensors (Code assumes “Chirp!” brand.)
1 controllable 4-outlet power relay
PC Power Connector, 6ft
Arduino power supply
Stacking Headers
Jumper Wires
Servo Extension Cables may be helpful
1A Diodes
Wire (of sufficient gauge for your pump amperage draw)

5. Tools: 3D printer; Soldering kit; Sander; Drill; Bandsaw; Hot glue gun; wire cutter

Self-sustaining Plant Wall: Page 2


Step 1: Initial Case Construction

Used HomeDepots complimentary service to have shelves. Two 1x3 lengths formed the containing lip of
three 1x6 planks and one 1x3 cut into 24 inch pieces the shelves. A seventh 1x6 lengths was used along
(which were all a little different in actual length). with one of the 1x3 lengths to form a door for the
NOTE: This was too short for the lights used, water and electrical compartment.Two thin strips of
recommend at least 27 inches. wood were screwed onto the pieces to attach them.
Once the frame and shelves and door are assembled,
Four 1x6 lengths formed the frame, with the sides of gently sand everything and apply two coats of stain.
the frame going outside the bottom and top boards for Attach the door with hinges and latch.
strength. Two additional pieces were used for

Self-sustaining Plant Wall: Page 3


Self-sustaining Plant Wall: Page 4
Step 2: Electronics - Hardware

1. Do the easy soldering:


1. stacking headers to 1 of the chirp sensors.
2. some jumper wires to the other chirp sensor

2. Some soldering where orientation matters


1. Diodes across pumps
2. Green and Blue wires to positive pump terminals
3. Ground wire (shared) to negative pump terminals

3. Wire it up!
1. See Tinkercad picture above, and EAGLE .brd file here.
1. moisture sensors are represented as gas sensors, with pin locations
matching orientation of picture in Chirp! Documentation (link below).
2. power relay is represented as a single relay off the board.
3. RTC is represented as 8-pin header
4. water level sensor is represented as potentiometer

2. Chirp! documentation for which pin is which, reset is brown in the diagram.
3. Elegoo Wiring Diagrams available on their download page, look for "37-in-1 V2".

Step 3: Integration and Final Case Assembly

Drill all holes for tubing and insert lights (since the be in the planter.
lights themselves are 24 inches long, this requires
digging holes into the side shelves to accommodate Attach electronics in electronics compartment. Screw
the power cables on the lights. 24”x24” sheet onto back of box. Attach final two 1x6
lengths to create a gap between the wall and back of
Perforate the tubing. To make a hole in the tube, box for tubing and wires as well as for added stability
make two 45-degree cuts with the wire-cutters going and ease of mounting to a wall.
opposite directions. Put small holes in the tubes
approximately every 1.5" along the lengths that will

Self-sustaining Plant Wall: Page 5


Step 4: Plant Plants

We selected Pothos plants because of their Line with plastic to waterproof and tape plastic down
heartiness, visual diversity and tendency to continue
growing in an aesthetically optimal, vine-like manner. Gently split apart plants and rearrange into planter
boxes
Create four planter boxes that will fit into the shelf and
can be taken out as and if needed. Add soil to planter boxes as needed

Cut pattern out of foam core Insert planter boxes into shelves and arrange
watering tubes across them
Hot glue boxes together

Self-sustaining Plant Wall: Page 6


Step 5: Code

The code uses the DS3231 library, which you can obtain through the Arduino IDE Tools -> Manage Libraries,
search for DS3231.

/* Functionality:
* -- Activate pumps on moisture low
* -- If photoresistor low, and valid hours of day, then turn lights on for 10 minutes
* -- If water level low, then blink lights
*/

#include
#include

// Assumption: Wire assumes that the two communication pins, which are here being used to multiplex
// the two moisture sensors and the real-time clock, are plugged into SDA (A4) and SCL (A5)

/*
* Future wishlist:
* *** Use the moisture sensor's chirp mode as the alarm for low-tank level.
* Make it reset after the water is refilled.
*/

#define NUM_PLANTERS 2

//#define DEBUG // Uncomment this to enable debug printing

//#define SHORTEN_DAY_NIGHT_CYCLE

Self-sustaining Plant Wall: Page 7


#ifdef DEBUG
#define DEBUG_PRINT(s) Serial.println(s)
#else
#define DEBUG_PRINT(s)
#endif

/******************** Time ***************************/

#define TICK_TIME 5000 // how often to check moisture level. All times in ms
unsigned int tickCount = 0;

/******************** Clock ***************************/

DS3231 clock;

/************************* Water level **********************/

#define LOW_WATER_THRESHOLD 300


int waterLevelPin = A0;

/*********************** Moisture sensing ************************/

typedef struct {int value;} MoistureLevel;

const int moistResetPin = 2;


const byte moistureSensorAddresses[] = {0x20, 0x21}; // multiplexed; for both pins; AKA 32,33
#define MOISTURE_THRESHOLD 300;

/********************************* Pump control **************************/

int pumpPins[NUM_PLANTERS] = {6, 7};

#define PUMP_TIME 2000 // Time to run each pump for after detecting low moisture. All times in ms

/******************************** Light control ***************************/

#define DARK_THRESHOLD 600


int photoResistorPin = A1;

int growLightPin = 3;

typedef struct {
int value;
} OptionalNonnegInt;

OptionalNonnegInt lastDarkTime;

#ifdef SHORTEN_DAY_NIGHT_CYCLE
#define TICKS_PER_LIGHT_CHECK 1
#else
#define TICKS_PER_LIGHT_CHECK ((1000 * 10 / TICK_TIME) * 60)
#endif

/***************************************************************************************************
*************************************** END HEADER ************************************************
***************************************************************************************************
*/

/************************* Wire library ******************************************/

void writeI2CRegister8bit(int addr, int value) {


// Serial.println("about to use wire");
Wire.beginTransmission(addr);
DEBUG_PRINT("begun transmission"); // for an unknown reason, these print statements seem to help the moisture sensors initialize
Wire.write(value);
DEBUG_PRINT("wrote value to :");
DEBUG_PRINT(addr);
Wire.endTransmission(); //for some reason this needs the reset pin connected?
DEBUG_PRINT("ended transmission, returning to setup");
}

unsigned int readI2CRegister16bit(int addr, int reg) {


Wire.beginTransmission(addr);
Wire.write(reg);
Self-sustaining Plant Wall: Page 8
Wire.endTransmission();
delay(1000);
Wire.requestFrom(addr, 2);
unsigned int t = Wire.read() << 8;
t = t | Wire.read();
return t;
}

/***************************** Nonneg-ints ****************************/

OptionalNonnegInt makeEmptyInt() {
OptionalNonnegInt ret;
ret.value = -1;
return ret;
}

OptionalNonnegInt makeNonnegInt(int x) {
OptionalNonnegInt ret;
ret.value = x;
return ret;
}

int isEmpty(OptionalNonnegInt n) {
return n.value == -1;
}

int getValue(OptionalNonnegInt n) {
return n.value;
}

/***************************** Time ****************************/

void setupClock() {
DEBUG_PRINT("setting up clock");
clock.begin();
DEBUG_PRINT("set up clock");

// This line resets the clock to the sketch compiling time


// Uncomment this when configuring a device, then recomment it and upload again
//clock.setDateTime(__DATE__, __TIME__);
}

void printCurTime() {
RTCDateTime dt = clock.getDateTime();
Serial.println("Printing time: Year, month, day, hour, minute");

Serial.println(dt.year);
Serial.println(dt.month);
Serial.println(dt.day);
Serial.println(dt.hour);
Serial.println(dt.minute);
}

int getHourOfDay() {
/*
#ifdef SHORTEN_DAY_NIGHT_CYCLE
RTCDateTime dt = clock.getDateTime();
return ((dt.minute % 2) * 60 + dt.second) / 5;
#else
return clock.getDateTime().hour;
#endif
*/
return 12;
}

OptionalNonnegInt timeSince(OptionalNonnegInt x) {
if (isEmpty(x)) {
return makeEmptyInt();
} else {
int t1 = getValue(x);
int t2 = millis();
return makeNonnegInt(t2 - t1);
}
}

/**************************** Light control ***********************/


Self-sustaining Plant Wall: Page 9
/**************************** Light control ***********************/

void setupLights() {
pinMode(growLightPin, OUTPUT);
}

int isDark() {
int darkLevel = analogRead(photoResistorPin);
DEBUG_PRINT("light level: ");
DEBUG_PRINT(darkLevel);
return darkLevel > DARK_THRESHOLD;
}

int forceDarkenLights() {
int h = getHourOfDay();
DEBUG_PRINT("hour is");
DEBUG_PRINT(h);
return !(h >= 10 && h <= 22);
}

void trySetLights(int status) {


if (forceDarkenLights()) {
DEBUG_PRINT("forcing light to off b/c time of day");
digitalWrite(growLightPin, 0);
} else {
digitalWrite(growLightPin, status);
}
}

void updateDarkTime() {
if ((tickCount % TICKS_PER_LIGHT_CHECK) == 0) {

/* This code solve the problem that it can't test if the room is dark
* when the lights are on.
*
* But....when testing, this flicker is really annoying.
*/
#ifndef SHORTEN_DAY_NIGHT_CYCLE
trySetLights(0);
delay(100);
#endif

if (isDark()) {
lastDarkTime = makeNonnegInt(millis());
} else {
lastDarkTime = makeEmptyInt();
}
}
}

/************************* Moisture sensing *********************************/

void moistureSetup() {

pinMode(moistResetPin, OUTPUT);

// reset the sensors to make them listen for I2C comms.


digitalWrite(moistResetPin, LOW);
delay(40);
digitalWrite(moistResetPin, HIGH);
delay(40);

// This is used to get moisture sensors into sensor mode instead of chirp mode
for (int i = 0; i < NUM_PLANTERS; i++) {
// This simple reset should work, but doesn't:
// writeI2CRegister8bit(moistures[i], 6);
// delay(500);
writeI2CRegister8bit(moistureSensorAddresses[i], 3);
// delay(200);
// readI2CRegister16bit(moistures[i], 0);
// readMoisture(i);
}
}

MoistureLevel readMoisture(int i) {
MoistureLevel ret;
Self-sustaining Plant Wall: Page 10
MoistureLevel ret;
ret.value = readI2CRegister16bit(moistureSensorAddresses[i], 0);
DEBUG_PRINT("Moisture level: ");
DEBUG_PRINT(ret.value);
return ret;
}

int moistureLow(MoistureLevel l) {
return l.value <= MOISTURE_THRESHOLD;
}

/************************ Pump control *************************************/

void pumpSetup(){
for (int i = 0; i < NUM_PLANTERS; i++) {
pinMode(pumpPins[i], OUTPUT);
}
}

void runPump(int i) {
DEBUG_PRINT("watering plant");
digitalWrite(pumpPins[i], HIGH);
delay(PUMP_TIME);
digitalWrite(pumpPins[i], LOW);
}

/************************ Water level *************************************/

int waterLow() {
int waterLevel = analogRead(waterLevelPin);
DEBUG_PRINT("Water level:");
DEBUG_PRINT(waterLevel);
return waterLevel < LOW_WATER_THRESHOLD;
}

/*********************** Main Arduino control *******************************/

void setup() {
Wire.begin();
Serial.begin(9600);
pumpSetup();
moistureSetup();
setupClock();
setupLights();
}

void loop() {
DEBUG_PRINT("tick");

#ifdef DEBUG
printCurTime();
#endif

pumpControl: {
for (int i = 0; i < NUM_PLANTERS; i++) {
DEBUG_PRINT("read sensor");
DEBUG_PRINT(readMoisture(i).value);
if (moistureLow(readMoisture(i))) {
runPump(i);
}
}
}

lightControl: {
updateDarkTime();

int lightShouldBeOn;

if (waterLow()) {
// Hijack the light mechanism to flash the lights
// to indicate low water
lightShouldBeOn = tickCount % 2 == 0;
} else {
lightShouldBeOn = !isEmpty(lastDarkTime);
}

Self-sustaining Plant Wall: Page 11


DEBUG_PRINT("Dark time");
DEBUG_PRINT(lastDarkTime.value);
DEBUG_PRINT("Light should be on?");
DEBUG_PRINT(lightShouldBeOn);
trySetLights(lightShouldBeOn);
}

timeControl: {
tickCount++;
delay(TICK_TIME);
}
}

The code is divided into sections for each of the sensors it integrates with, and to encapsulate the various policies,
such as timing, thresholds for the light and moisture sensors, and the time of the dark hours.

The main function gives the main functionality. It does these functions repeatedly:

Pump control:

Checks if each moisture sensor is reporting low moisture. If so, it will run the corresponding pump for 2 seconds.

Light control:

First, if the time is between 10 PM and 10 AM, then the planter is in dark hours. Ignore the rest of this part; the
grow lights will not turn on under any circumstances during dark hours. Otherwise, it: Checks if the room is dark,
and records the time if so. If the grow lights are on, it will briefly flicker them off to do the check If the water level is
low, it will turn the lights on and off at roughly 5 second intervals. If the water level is not low, and the room is dark,
activate the lights.

After performing light control, it waits 5 seconds, before beginning the next “tick” of the main loop.

A few other things about the code:

You will need to reset the RTC before using. Uncomment the relevant line in setupClock() to do so.
You can toggle the DEBUG flag to turn on debug printouts
You can toggle the SHORTEN_DAY_NIGHT_CYCLE flag to change the behavior so that it
pretends to run through a full day-night cycle every 120 seconds. This is useful for testing purposes.
When this flag is on, it will also no longer flicker the lights to check for darkness. Because it records
the last time the room was dark, the code can easily be modified to wait several minutes before
turning on the grow lights, as did a previous version.

Moisture sensor Address change:

Because the moisture sensors communicate via I2C on the same pins, they need unique addresses, and they
can't be the same as the RTC. We couldn't find what the RTC uses in a cursory search, but the sensors start at
0x20. You will need to change one of them to something else (we used 0x21). The reason you don't solder a
jumper directly between the two, and only a header on one, is so that you can change the address of only one of
them and now have them on separate addresses. We modified the code available from the maker of Chirp!,
because of problems getting it into sensor mode with the original code. Ours is available here.

Self-sustaining Plant Wall: Page 12


Step 6: Future Work

The biggest improvement would be the ability to set lights are off. This gives it an annoying flicker, and
the dark-hours. Right now, the planter is hardcoded to also means that, if you turn on the room lights, it will
only turn the lights on between the hours of 10 AM be a while before the planter turns its lights off. An
and 10 PM. alternative could be a more sophisticated photo-
sensing setup. For example, it could determine that, if
Another improvement would be the low-water signal. the area under the grow lights is significantly brighter
It begins flashing its lights when it’s low water, which than a more distant sensor, then there is no
is an unintuitive signal. One alternative would be to significant lighting in the room other than the grow
use the "chirp" functionality in the Chirp! brand lights. Another possibility is to use the different
moisture sensors we are using. Another alternative spectra of the grow-lights vs. normal home lighting.
would be to attach a separate speaker and play a
sound. On the physical side, it would be nice to make the
tank more removable for easier cleaning.
The current code will periodically turn off the lights in
order to check if the room is dark when the planter’s

I love the plants you chose for your setup :)

Self-sustaining Plant Wall: Page 13

Вам также может понравиться