Growthings User Guide

Welcome to the User Guide of Growthings, an affordable, easily programmable table-top smart greenhouse for everyone!

If you are here for the first time and looking to get started on building your smart greenhouse, please go to the Installation page for detailed guide on how to set up your hardware and software.

If you are looking for information on how start programming the electronics, please go to the Quickstart Guide page.

If you are trying to find out how to program a certain device, please click on the names of the sensors below the corresponding image in the table below.

Devices Supported:
Sensors Actuators Displays
temp_sensor servo oled
Temperature/Humidity Sensor Servo OLED Screen
temp_sensor_pro relay led
Temperature/Humidity Sensor Pro Relay LED Lights
light_sensor button grow_light
Light Sensor Button Grow Light Strip
moisture_sensor buzzer  
Moisture Sensor Buzzer  

Introduction

Installation

Setting up the Raspberry Pi Zero W

PiCamera Installation

Oled Screen Installation

Downloading the Preconfigured Image

Writing the Image to an MicroSD card

Setting up Wifi

Setting up Node-RED App

Assembling the Smart Greenhouse

Assembling the smart greenhouse is very simple. Use the following two steps:

Step 2. Assemble parts controlled by the relay (fan/pump)

You will need the following parts controlled by the relay

  • A 12V ventilation fan (40mm x 40mm) or a 12V water pump
  • A 9V battery with clip
  • A Wire stripper
  • Tape

How to connect the fan/pump to the relay

First, cut off the two pin connector on the fan. Remove the protective material from the wires and expose the copper wires with the wire stripper. Now, if you connect the red and black wires to the corresponding wires on the 9V battery clip, the fan will spin:

_images/no_relay.png

Now, think of the relay as a regular switch. Keep the black wires connected (you might want to tape it. Insert the red wire from the fan to one terminal of the relay (Either one will work. You might want to loosen the retention screws first) and the red wire from the battery clip to the other. This way, when the relay is activated, the circuit will be closed, and the fan will start to spin.

_images/relay.png

Step 3. Software installation

Use the link below to download main.py (right click on the link -> save target as main.py). Use EsPy’s file manager to upload the file to the WioLink board. Alternatively, you can copy the code below and create a new file called main.py in EsPy. Save the file and use the upload button to upload it to the WioLink board. Either way, when you are finished, simply press the reset button. The board should start to work after 5 seconds.

Download Links:

Quickstart Guide

This guide assumes that you have the software environment already configured. If not, please follow the Setting up WioLink boards before proceeding.

1. Hardware for the Smart Greenhouse

The red development board that we are using for building the GrowThings smart greenhouse is called the Wio Link board developed by SeeedStudio, a Chinese company that develops many electronic devices. The board is based on ESP8266, a $4 microcontroller chip with built-in WiFi for Internet of Things development. A microcontroller features a simpler structure than a microcomputer like Raspberry Pi. Therefore, they are typically smaller and less expensive than a microcomputer, but consumes far less energy. They do not require an operating system, which allows developers to communicate with hardware directly.

_images/ports.png

MicroUSB and Battery Ports: The image above shows the Wio Link board. At the bottom of the board is a microUSB port used to connect the board to the computer. To the left of the microUSB port is the battery port. The Wio Link board can be powered through the microUSB port and a 3.7v rechargeable Li-Po battery. Like how most smartphones work, connecting the board to USB charges the battery.

Config and Reset Buttons: Two buttons lay on both sides of the board. These are the config button on the left, and the reset button on the right. The reset button on the right is more often used, whose purpose is to restart the board.

LED Indicators: With the microUSB port facing down, this is the upright position for the board, as depicted in the picture above. There is a blue and a red LED on the top-left of the board. The former is the WiFi indicator and the latter the power indicator. Please ensure that the power LED is on when programming the board. Otherwise the board is not powered properly and some devices, such as the light sensor, might not function properly. There is also a green battery indicator showing the status of the rechargeable battery. It will blink if battery is not found, and remain on if battery is being charged.

Grove Ports: 6 grove ports can be found on both sides of the board. With the board in its upright position, these ports are numbered, from top to bottom and from left to right, ports 1 through 6. It is through these ports that the board is connected to other grove compatible devices. Please remember the numbers for these ports as we will need these numbers to tell the microcontroller which device is connected to which port.

2. The Software Development Environment

We are going to use the EsPy Integrated Development Environment (IDE) to program the Wio Link board. An IDE not only allows us to program the board, it also flashes the firmware and manages the files on the board. Please go to the EsPy folder (might be named Release), and run EsPy.exe (it is a good idea to create a desktop shortcut to it). The program looks like the following:

_images/screenshot.png

Hint

If an error pops up and says .Net Framework is required or needs update, please follow the link below and install the latest version of the .Net Framework. EsPy is written in C# which requires .Net Framework runtime.

Microsoft .net Framework (Required for Windows 7, 8, 8.1, Optional for Windows 10)

Like most Windows applications, this program has a menu bar, a toolbar, a main scripting window, and a terminal window. at the bottom of the screen. These four rightmost buttons on the toolbar are very important (see below). They are Connect, Disconnect, Soft Reset, and File Manager. The terminal serves two functions. First, it communicates directly with the Wio Link board and monitors the status of the board. Second, it passes code to the board for it to execute. In other words, we can directly write code in the terminal, after the >>> prompt, and when we hit enter, the code will be directly executed and we can see the results immediately.

_images/toolbar.png

To get started with programming with MicroPython on the Wio Link board, first connect to the board to one of the USB ports on the computer. Then, go to Device->Ports, and make sure a COM port (which could be “COM” followed by any number) is selected:

_images/menu_ports.png

Next, click the Connect button on the toolbar. You will see this message in the terminal:

_images/terminal_connect.png

Now, reset the board either by pressing the “reset” button on the board, hitting Ctrl+D, or clicking the Soft Reset button on the toolbar. You will then see a >>> prompt in the terminal window at the bottom of the screen:

_images/terminal_ready.png

Congratulations! Your Wio Link board is ready to be programmed.

4. Device Programming with MicroPython

Programming the electronic devices follows three simple steps:

  1. Import the corresponding class for the device
  2. Create a virtual shortcut to the device
  3. Control the devices using the built in functions for that class

1. Import the corresponding class

Let us use the Temperature/Humidity Sensor connected to Port 3 as an example. Type the following in the Terminal.

>>> from sensors import TemperatureSensor

Now you should be pretty familiar with from module import Class syntax. Here are a few rules to help you find the classes easily and prevent errors:

sensors is the name of the module, which is always in all lowercase ending with an “s.” There are three modules available: sensors, actuators, and displays. Within each module you will find the classes available for your device. Refer to the table below to locate your class, which is always in CamelCase. Python is a case sensitive language and it is important to make sure that you write the code with the correct case.

Optional: What is a class?

In Object-Oriented Programming. A class is blueprint for creating objects. The following diagram explains the relationship between classes and objects.

https://ds055uzetaobb.cloudfront.net/image_optimizer/722c82aff075a14313be7fa7463f7fedad151a0a.png

In this case, the TemperatureSensor is a class, and we know that all TemperatureSensors built this way behave in a certain way, but we need to create an object specifically for the specific one that is connected to Port 3. Therefore, we need the second step:

2. Create an object referring to the sensor

Create an object of the class TemperatureSensor referring to the sensor at Port 3 like this:

>>> temp_sensor = TemperatureSensor(port=3)

or

>>> temp_sensor = TemperatureSensor(3)

Now Python knows there is a temperature sensor connected at port 3. We have even given it a name temp_sensor. This is just an alias, or a nickname, for us to remember. Think of it as a shortcut. Next time we want to refer to the temperature sensor at port 3, we can use the name, and Python will know we want to communicate with the temperature sensor at port 3.

3. Interact with the devices using the objects

Each class offers a set of functionalities for interacting with the devices. In this case, the TemperatureSensor class offers TemperatureSensor.get_temperature() and TemperatureSensor.get_humidity() methods. To use these methods, simply use the dot notation to access the methods:

>>> temp_sensor.get_temperature()
>>> temp_sensor.get_humidity()

Please refer to the API Reference for more information on how to use these classes.

4. Control one device with another

Now connect a button to Port 1 and an LED strip to Port 2. Since now we need to write a more complicated program, we switch to scripting. Click File->New->Python to create a new Python script, or hit Ctrl+N. Name the new file as test.py:

_images/new_file.png

Initialize them with the following code. Note that the LED strip is a display and the button is an actuator.

# import classes

from displays import GrowLight
from actuators import Button

# create shortcuts

button = Button(port=1)
gl = GrowLight(port=2)

while True:          # an infinite loop
    if b.is_pressed: # determine if the button is pressed
        gl.on()      # turns the led strip on if button is pressed
    else:
        gl.off()     # turns the led strip off if button not pressed

This simple program loops forever. It will constantly determine if the button is pressed, and turns the LED strip on if it is, and off if it is not.

Hint

The pound or hashtag sign # indicates that the subsequent text on the same line is comments. Comments are used for programmers to communicate with themselves or other programmers what they are doing with the code. They are greyed out and Python will not consider them as part of the program.

API Reference

Please use the list or table below to find out more about how to program your devices

sensors - Working with Sensors

The sensors module provides a collection of classes to interact with sensors, such as temperature, light, and soil moisture sensor.

Temperature/Humidity Sensor

https://media.digikey.com/Photos/Seeed%20Technology%20Ltd/101020011_sml.JPG
class sensors.TemperatureSensor(port[=3])

Allows reading temperature and humidity information from the Grove Temperature Sensor based on DHT11. We recommend that this sensor be connected to Port 3, which is the default for this class. The port parameter defines which port the sensor is connected to.

TemperatureSensor.get_temperature(celsius[=False])

Returns the temperature reading in Fahrenheit. If celsius is set to True then the celsius temperature will be returned.

Note

This temperature sensor offers a resolution of 1 degree Celsius. If you want more accurate readings, please try the pro version below.

TemperatureSensor.get_humidity()

Returns the relative humidity in percentage.

TemperatureSensor.show_data(screen, line)

Shows the temperature (in Fahrenheit) and relative humidity on the specified screen object on the specified line. Also returns a tuple of the same temperature in Fahrenheit and relative humidity to avoid repeated readings on the sensor.

Example
from sensors import TemperatureSensor()
t = TemperatureSensor() # defines a temperature sensor at default port (Port 3)

t.get_temperature() # returns the Fahrenheit value
t.get_temperature(True) # returns the Celsius value
t.get_humidity() # returns the humidity value

Temperature/Humidity Sensor Pro

https://static.generation-robots.com/3637-large_default/grove-temperature-and-humidity-sensor-pro.jpg
class sensors.TemperatureSensorPro(port[=3])

Allows reading temperature and humidity information from the Grove Temperature Sensor Pro based on DHT22 (AM2302). We recommend that this sensor be connected to Port 3, which is the default for this class. The port parameter defines which port the sensor is connected to.

TemperatureSensorPro.get_temperature(celsius[=False])

Returns the temperature reading in Fahrenheit. If celsius is set to True then the celsius temperature will be returned.

Note

This temperature sensor offers a resolution of .1 degree Celsius.

TemperatureSensorPro.get_humidity()

Returns the relative humidity in percentage.

TemperatureSensorPro.show_data(screen, line)

Shows the temperature (in Fahrenheit) and relative humidity on the specified screen object on the specified line. Also returns a tuple of the same temperature in Fahrenheit and relative humidity to avoid repeated readings on the sensor.

from sensors import TemperatureSensorPro
t = TemperatureSensor(3) # defines a temperature sensor pro at default port (Port 3)

t.get_temperature() # returns the Fahrenheit value
t.get_temperature(True) # returns the Celsius value
t.get_humidity() # returns the humidity value

# shows temperature/humidity data on the oled screen
from displays import OledScreen

screen = OledScreen(6)
t.show_data(screen, 1)

Light Sensor

https://raw.githubusercontent.com/SeeedDocument/Grove-Digital_Light_Sensor/master/img/Digital_Light_Sensor.jpg
class sensors.LightSensor(port[=6], address[=0x29])

Allows reading lux values from the Grove Digital Light Sensor based on the TSL2561 I2C light sensor. The port parameter cannot be any other number than 6, and the sensor can be connected to the board through an I2C hub. The address parameter assigns the I2C address of the light sensor. 0x29 (41) is the default for the Grove sensor. The Adafruit version has a default of 0x39 (57).

LightSensor.get_lux()

Returns the light intensity reading as lux.

Hint

Useful lux values:

  • Sunlight: 107,527
  • Full Daylight: 10,752
  • Overcast Day: 1,075
  • Very Dark Day: 107
  • Twilight: 10.8
  • Full Moon: .108
LightSensor.show_data(screen, line)

Shows the light intensity reading in lux on the specified screen object on the specified line. Also returns the same lux reading to avoid repeated reading on the sensor.

Example
# The following code reads light value every 5 seconds,
# and if it's too dark (lux < 100), prints a warning message

from sensors import LightSensor
import time

l = LightSensor()

while True:
    lux = l.get_lux()

    if lux < 100:
        print("Too Dark!")

    time.sleep(20) # wait for 20 seconds

Moisture Sensor

https://github.com/SeeedDocument/Grove_Moisture_Sensor/raw/master/images/Moisture_sensor_.jpg
class sensors.MoistureSensor(port[=4])

Allows reading moisture values from the Grove Moisture Sensor. The port parameter cannot be any other number than 4, because the sensor is analog.

MoistureSensor.get_moisture(port[=4])

Returns the raw moisture reading.

Warning

Because the moisture sensor is analog, the values of the sensor readings might vary from case to case. It is a good idea to calibrate the sensor by experimenting on the soil.

MoistureSensor.show_data(screen, line)

Shows the raw moisture reading on the specified screen object on the specified line. Also returns the same raw moisture reading to avoid repeated reading on the sensor.

actuators - Working with Actuators

The actuators module provides a collection of classes to interact with actuators, such as servos, relays, and buttons.

Servo

https://github.com/SeeedDocument/Grove-Servo/raw/master/img/Grove%E2%80%94Servo.jpg
class actuators.Servo(port[=2], position[=0])

Allows control of a Grove Servo. Default port for the servo is 2. The position parameter sets the initial position of the servo.

Servo.set_position(degree)

Sets the degree position of the servo (between 0 and 180, which is half a circle). If degree is greater than 180, the servo will be set at the 180 degree position. Likewise, if degree is less than 0, the servo will rotate to the 0 degree position.

Servo.get_position()

Returns the current position of the servo in degrees.

Example:
from actuators import Servo

s = Servo(1, init_degree = 180) # defines a servo connected to port 1 with initial position at 180 degrees.
s.set_position(90) # rotate the servo by 90 degrees.

Relay

https://raw.githubusercontent.com/SeeedDocument/Grove-Relay/master/img/Twig-Relay.jpg
class actuators.Relay(port[=1])

Allows control of a Grove Relay. The relay by default is normally open (NO) triggered by a high signal.

Relay.on()

Activate the relay, close the circuit, and turn on whatever appliance that’s connected to the relay.

Relay.off()

Deactivate the relay, open the circuit, and turn off whatever appliance that’s connected to the relay.

Relay.is_on()

Returns True if the relay is on, or False if the relay is off.

Button

https://github.com/SeeedDocument/Grove_Button/raw/master/img/Button.jpg
class actuators.Button(port[=2], pullup[=True])

Allows control of a Grove Button. There are two ways to use a button. First, you can access Button.is_pressed property or Button.is_pressed() method to determine if the button is pressed. Alternatively, you can also set a callback function with Button.on_release() method use the interrupt mechanism. Please see example of how to use the callback.

Button.is_pressed()

Returns True if the button is pressed, or False if it is not.

Button.on_press(callback)

Executes the callback function provided to the method when the button is pressed.

Button.on_release(callback)

Executes the callback function provided to the method when the button is released.

Example

Controlling the LED with the Button

from actuators import Led, Button
led = Led(1) # Specifies an LED at Port 1
button = Button(2) # Specifies a button at Port 2

## Turns on the LED when the button is pressed

while True:

    if button.is_pressed():
        led.on()
    else:
        led.off()

Turns the LED on/off with a callback function

from actuators import Button
from displays import Led
led = Led(1, on=False) # Specifies an LED at Port 1
button = Button(2) # Specifies a button at Port 2

## Define a callback function

def turn_on_led(pin):
    global led # Need this line to refer to the led object outside the function.

    if led.is_on():
        led.off()
    else:
        led.on()

## Set the callback function to Button.on_release method.

button.on_release(callback=turn_on_led) # Note that no () are needed.

Buzzer

https://github.com/SeeedDocument/Grove_Buzzer/raw/master/img/buzzer_s.jpg
class actuators.Buzzer(port[=2])

Allows control of a Grove Buzzer. You can control the buzzer to play a note or a piece of music. The notes are written in strings. Available notes are ["c", "d", "e", "f", "g", "a", "b", "C", " "]. White space means skip.

Buzzer.play_note(note, duration[=0.5])

Plays a note. duration controls how long the note gets played.

Buzzer.play_music(notes, rhythms[=None], tempo[=1])

Plays a string of notes. You may supply a list of numbers the same length as notes as rhythms, how long each note gets played. You may also specify an overall tempo. The larger tempo is, the slower the music.

Example

Using the buzzer

from actuators import Buzzer
buzzer = Buzzer(port=2)

# Plays one note

buzzer.play_note("c", duration=1)

# Plays a piece of music

buzzer.play_music("cdefgabC", rhythms=[32, 16, 8, 4, 2, 1, 0.5, 1], tempo = 0.8)

displays - Working with Screens and LEDs

The displays module provides a collection of classes to interact with screens, LED lights, and LED strips.

OLED Screen

https://statics3.seeedstudio.com/seeed/img/2016-09/RfiiaySPfAWrtPqmFhC1Co4u.jpg
class displays.OledScreen(port[=6], width[=128], height[=64], address[=0x3c])

Allows control of a Grove 0.96’ OLED screen. It is an I2C device so it will only work on Port 6 or an I2C hub that is connected to Port 6. You can specify the width and height of the screen (default 128x64). You can also specify the I2C address of the screen. If you are not sure, just use default values and everything will be taken care of.

The screen has an internal representation of the content it displays called framebuffer. Usually you will need to change framebuffer first, and call the show() method to change what is actually displayed on the screen. Some of the following methods only changes the buffer, while some directly modifies what is displayed on the screen. Please choose these methods accordingly.

OledScreen.clear()

Clears the framebuffer. Does NOT change what is displayed on the screen. Please call the show() method subsequently to see the result.

OledScreen.clear_line(line)

Clears the specified line in the framebuffer. Does NOT change what is displayed on the screen. Please call the show() method subsequently to see the result.

OledScreen.write_line(line, message)

Writes the message to the specified line. Does NOT change what is displayed on the screen. Please call the show() method subsequently to see the result.

OledScreen.show_line(line, message)

Writes and shows the message to the specified line. This method directly changes what is displayed on the screen.

OledScreen.show_sensor_data(sensor, line)

Writes the data on the specified sensor to the specified line and returns the data readings from the sensor to avoid reading data repetitively.

Example:
from displays import OledScreen

screen = OledScreen(6)
screen.show_line(1, "Hello World!")

LED Lights

https://github.com/SeeedDocument/Grove-Red_LED/raw/master/img/Grove-LED_Photo.jpg
class displays.Led(port[=1], on[=True])

Allows control of a Grove LED socket. It is possible to switch the LEDs on the socket. The LEDs have polarities. The longer leg is positive.

Led.on(fade[=False], duration[=1])

Turns on the LED. If the fade parameter is set to True, then the led will turn on gradually in the number of seconds set to the duration parameter.

Led.off(fade[=False], duration[=1])

Turns off the LED. If the fade parameter is set to True, then the led will turn off gradually in the number of seconds set to the duration parameter.

Led.is_on()

Returns True if the LED is on, or False if it is off.

Grow Light Strip

https://statics3.seeedstudio.com/product/30led%20Strip_02.jpg
class displays.GrowLight(port[=1], n[=60], on[=True])

Allows control of a 5V LED strip based on the WS2812b (NeoPixel). n specifies the number of LEDs on the strip. Default is 60. If on is set to True then the GrowLight will automatically turn on.

GrowLight.on()

Turns on the LED strip as a plant growth light that emits red and blue light.

GrowLight.off()

Turns off the LED strip.

GrowLight.is_on()

Returns True if the grow light is on, or False if it is off.

Makes the LED strip blink. You can specify the color and times it blinks. color is a list or a tuple of R, G, B values, with intensity that goes from 0 to 255. For example, [255, 0, 0] sets the LED strip to red at its maximum intensity. Use interval to control how long each blink lasts.

GrowLight.demo(program[="cycle"])

Demonstrates 3 different animations on the LED strip. The default program is "cycle". Also available are "bounce" and "fade". Try them out!

Hint

This class is a subclass of MicroPython’s neopixel.NeoPixel class, so it can be programmed the same way as the Neo Pixel. See this page for more details and examples.

iot Connecting with Other Devices

The iot module provides a collection of classes to connect to WiFi and exchange data with other devices, such as Raspberry Pis.

WiFi

This class can be used to connect the Wio Link board to WiFi.

class iot.WiFi(ssid, password)

Stores the WiFi ssid and password, so when the connect() method is called, the Wio Link board will connect to the specified WiFi ssid with the password.

Note

After WiFi is successfully connected, the Wio Link board will attempt to access the same WiFi at each subsequent boot. You can click the “Soft Reset” button to get the terminal to work again.

WiFi.connect()

When the connect() method is called, the Wio Link board will connect to the specified WiFi ssid with the password specified when object is created.

Example:
# Connects to a WiFi access point
# with SSID starbucks
# and Password 12345678

from iot import WiFi
wifi = WiFi(ssid="starbucks", password="12345678")
wifi.connect()

Node-RED

This class can be used to send data to any device that runs Node-RED, such as Raspberry Pis, or even a Node-RED instance running on the cloud, provided the IP address of the hosting machine is known.

class iot.NodeRed(ip[, port=1880])

Stores the ip address (or domain) of the machine running Node-RED. If Node-RED runs on a different port than 1880, the port parameter can be used to specify the port.

NodeRed.send_http(url, data, debug[=True])

Send the specified data to the http-in node in the Node-RED flow. the url parameter needs to be exactly the same as the URL field of the http-in node (shown below). data can either be a number, a string, or a dictionary. By default, if the data is sent successfully, the program will prompt "Data sent!. If that is undesirable, you can turn it off by setting debug to False.

_images/node_red_http.png
Example:
# Connects to a WiFi access point
# with SSID starbucks
# and Password 12345678

from iot import WiFi, NodeRed
from sensors import TemperatureSensor
import time

wifi = WiFi(ssid="starbucks", password="12345678")
wifi.connect()

# Temperature Sensor is connected to Port 3
temp_sensor = TemperatureSensor(3)

# Node-RED is hosted on a machine with IP 192.168.1.123
# It has a http in node configured with URL /test

node = NodeRed(ip="192.168.1.123")

# Sends temperature and humidity data to Node-RED every 10 seconds

while True:
    t = temp_sensor.get_temperature()
    h = temp_sensor.get_humidity()

    data_dict = {
        "temperature": t,
        "humidity": h
    }

    node.send_http(url="/test", data=data_dict)
    time.sleep(10)

Connecting to BC Server

class iot.BcServer

There is a dedicated server set up at Boston College with a Node-RED instance running for data collection purposes. To use the server, you can directly use this class. No parameters is needed to set it up.

BcServer.send_http(data, debug[=True])

Specify the data, either a JSON or a String, and this method will send the data directly to the BC server. No need to specify IP or URL. By default, if the data is sent successfully, the program will prompt "Data sent!. If that is undesirable, you can turn it off by setting debug to False.

Devices Supported:
Sensors Actuators Displays
temp_sensor servo oled
Temperature/Humidity Sensor Servo OLED Screen
temp_sensor_pro relay led
Temperature/Humidity Sensor Pro Relay LED Lights
light_sensor button grow_light
Light Sensor Button Grow Light Strip
moisture_sensor buzzer  
Moisture Sensor Buzzer