Automatic Plant Rotator With Sunlight Sensor

by yoheio in Circuits > Raspberry Pi

5241 Views, 22 Favorites, 0 Comments

Automatic Plant Rotator With Sunlight Sensor

IMG_8831.jpeg
IMG_8832.jpeg
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

Connect BYJ48 Motor With Raspberry Pi

Screen Shot 2021-04-22 at 12.38.42 PM.png

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

IMG_8833.jpeg
IMG_8834.jpeg
IMG_8835.jpeg

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.

  1. Superglued 3D printed plate with a wheel from the smart car kit
  2. Screwed in BYJ48 motor to the smart car body (now the foundation of this device)
  3. 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

Screen Shot 2021-04-22 at 12.53.19 PM.png
IMG_8836.jpg

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

Screen Shot 2021-04-22 at 1.00.01 PM.png
Screen Shot 2021-04-20 at 2.20.25 PM.png

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.py

from influxdb import InfluxDBClient


def read_metric():
import seeed_si114x
SI1145 = seeed_si114x.grove_si114x()
return SI1145.ReadVisible


def 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

Screen Shot 2021-04-22 at 1.20.55 PM.png

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.py

import RPi.GPIO as GPIO
import time
from influxdb import InfluxDBClient


THRESH = 265
ROTATE_THRESH = 50000


def 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) % 4
with open('/home/pi/plant-rotator/current_section.txt', 'w') as f:
f.write('%s\n' % section)
section_value = 0
rotate()
return section, section_value


def read_metric():
# the rotation has to happen before this import
import seeed_si114x
SI1145 = seeed_si114x.grove_si114x()
return SI1145.ReadVisible


def 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!