Automatic Plant Rotator With Sunlight Sensor
by yoheio in Circuits > Raspberry Pi
5563 Views, 22 Favorites, 0 Comments
Automatic Plant Rotator With Sunlight Sensor
This is a writeup of a project I did for my Introduction to Making class. I was inspired by my wife who wanted a device that automatically rotates her plant so that each section gets an even distribution of sunlight.
Supplies
ULN2003 Stepper Motor Driver Board
Grove Base Hat for Raspberry Pi
Female to Female Breadboard Jumper Wires
Some kind of plate (I used a plate that I 3D printed)
Some kind of foundation (I used parts from this Smart Car Chassis kit)
Connect BYJ48 Motor With Raspberry Pi
I mainly followed this tutorial to get the motor working with my Raspberry Pi.
You can modify the 512 (360 degrees) number in the test script to change how much you want the motor to rotate by.
for i in range(512):
Modify the following line to change the speed of rotation.
time.sleep(0.001)
Attach Plate, Motor, and Foundation
I miraculously found a smart cart chassis kit that fit well with the BYJ48 stepper motor and also provided stability to the plate so I decided to combine everything.
- Superglued 3D printed plate with a wheel from the smart car kit
- Screwed in BYJ48 motor to the smart car body (now the foundation of this device)
- Had to apply a thin layer of scotch tape around the motor shaft so that it fit perfectly with the wheel
Before attaching everything, I would recommend placing your plant/pot on the plate and making sure that it's stable even when it rotates. My plant weighs roughly 1kg.
Attaching Grove Sunlight Sensor
I mainly followed this tutorial to get the sunlight sensor working with my Raspberry Pi.
If you can get the test script to run, you should be set!
python3 examples/BasicRead.py
I then taped the sensor to the foundation so that the sensor is always in the same position.
Collecting Sunlight Data
I followed this tutorial to set up InfluxDB and Grafana on my raspberry Pi.
InfluxDB is a time series database designed to handle high write and query loads
Grafana is a multi-platform open source analytics and interactive visualization web application
After getting influx running, I created a database called plant and set a retention policy so that we don't retain the data forever.
influx > create database plant > create retention policy "two_weeks" on "plant" duration 2w replication 1
I wrote up a quick script to save the sunlight level to InfluxDB
# collect.pyfrom influxdb import InfluxDBClientdef read_metric():import seeed_si114xSI1145 = seeed_si114x.grove_si114x()return SI1145.ReadVisibledef save_metric(metric, value):client = InfluxDBClient('localhost', 8086, 'pi', 'password', 'plant')point = {'measurement': metric,'fields': {'value': value}}client.write_points([point])if __name__ == '__main__':sun_value = read_metric()save_metric('sunlight', sun_value)
I added this script to a cron job so that it is executed every minute
* * * * * python3 /home/pi/plant-rotator/collect.py
I graphed the collected data in Graphana to establish a baseline.
Determining Baseline and Rotation Threshold
After a couple days of data collection, I noticed that the sunlight level typically ranges from about 258~1200. I decided to ignore all values below 265 as those numbers were observed during the night and set a threshold of 50,000 accumulative delta.
In other words, I keep track of the sunlight level for one section every minute, and keep a running count of how much sunlight exposure exceeding 265 that section gets. When that aggregated number exceeds 50,000, I rotate the plant 90 degrees, reset the counter, and start tracking again for the new section.
Logic (pseudo code):
If ∑max(sunlight_level - 265) > 50000:
rotate section = (section + 1) % 4
If the plant is getting the max sunlight level around 1200, the device should rotate every 50 minutes, but in practice, it rotates about once a day.
Combining Everything
Based on the threshold from the previous section, I combined everything and wrote a program that automates the data collection and rotation of the plant. The program consists of a script and two text files that keep track of the the current section and the running total of sunlight exposure.
# run.pyimport RPi.GPIO as GPIOimport timefrom influxdb import InfluxDBClientTHRESH = 265ROTATE_THRESH = 50000def rotate():GPIO.setmode(GPIO.BOARD)control_pins = [7,11,13,15]for pin in control_pins:GPIO.setup(pin, GPIO.OUT)GPIO.output(pin, 0)halfstep_seq = [[1,0,0,0],[1,1,0,0],[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,1,1],[0,0,0,1],[1,0,0,1]]for i in range(128):for halfstep in range(8):for pin in range(4):GPIO.output(control_pins[pin], halfstep_seq[halfstep][pin])time.sleep(0.002)GPIO.cleanup()def get_section():with open('/home/pi/plant-rotator/section_sunlight.txt', 'r') as f:section_value = int(f.readline())with open('/home/pi/plant-rotator/current_section.txt', 'r') as f:section = int(f.readline())if section_value > ROTATE_THRESH:section = (section + 1) % 4with open('/home/pi/plant-rotator/current_section.txt', 'w') as f:f.write('%s\n' % section)section_value = 0rotate()return section, section_valuedef read_metric():# the rotation has to happen before this importimport seeed_si114xSI1145 = seeed_si114x.grove_si114x()return SI1145.ReadVisibledef save_metric(metric, value):client = InfluxDBClient('localhost', 8086, 'pi', 'password', 'plant')point = {'measurement': metric,'fields': {'value': value}}client.write_points([point])def update_section_value(new_value):with open('/home/pi/plant-rotator/section_sunlight.txt', 'w') as f:f.write('%s\n' % new_value)if __name__ == '__main__':section, section_value = get_section()sun_value = read_metric()delta = max(sun_value - THRESH, 0)save_metric('sunlight-%s' % section, sun_value)save_metric('sunlight-delta-%s' % section, delta)update_section_value(section_value + delta)
We replace the data collection cron job with this new script so that it runs every minute.
* * * * * python3 /home/pi/plant-rotator/run.py
That's it!