Greenhouse Monitoring

I have done a number of monitoring projects over the previous years and thought it was time to revisit greenhouse monitoring.

Parts

I choose to start by monitoring my little Ikea greenhouse located on my windowsill. To start I put together a basic monitoring board using an Arduino Nano clone and a DHT22 sensor.

arduino sensor

This is a breadboard with some older components I had from previous projects (hence the look).

Parts List

  • DHT 22 Humidity and temperature sensor
  • TSL2561 light sensor
  • Arduino Nano clone
  • Breadboard
  • 4.7k “pull up” resistor

The Arduino is connected to a Raspberry Pi using a serial USB cable. JSON is sent over the connection and read via cron every minute using a Python script.

Arduino Code

#include <DHT.h>
#define DHTPIN 2
#define DHTTYPE DHT22
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_TSL2561_U.h>


DHT dht(DHTPIN, DHTTYPE);
float humidity;
float temperature;
float light;

Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT, 12345);

void setup() {
  Serial.print("Starting");
  Serial.begin(9600);
  dht.begin();

  if (!tsl.begin())
  {
    /* There was a problem detecting the TSL2561 ... check your connections */
    Serial.print("Ooops, no TSL2561 detected ... Check your wiring or I2C ADDR!");
    while (1);
  }

  /* Display some basic information on this sensor */
  displaySensorDetails();

  /* Setup the sensor gain and integration time */
  configureSensor();

}

void loop() {


  humidity = dht.readHumidity();
  temperature = dht.readTemperature();

  sensors_event_t event;
  tsl.getEvent(&event);
  if (event.light)
  {
    light = event.light;
  }
  else
  {
    /* If event.light = 0 lux the sensor is probably saturated
       and no reliable data could be generated! */
    light = -1;
  }
  //light = 0;
  Serial.print("{");

  Serial.print("\"temperature\":");
  Serial.print(temperature);
  Serial.print(",\"humidity\":");
  Serial.print(humidity);
  Serial.print(",\"light\":");
  Serial.print(light);

  Serial.println("}");
  delay(1000); //just here to slow down the output for easier reading

}

void displaySensorDetails(void)
{
  sensor_t sensor;
  tsl.getSensor(&sensor);
  Serial.println("------------------------------------");
  Serial.print  ("Sensor:       "); Serial.println(sensor.name);
  Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
  Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
  Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" lux");
  Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" lux");
  Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" lux");
  Serial.println("------------------------------------");
  Serial.println("");
  delay(500);
}


void configureSensor(void)
{
  /* You can also manually set the gain or enable auto-gain support */
  tsl.setGain(TSL2561_GAIN_1X);      /* No gain ... use in bright light to avoid sensor saturation */
  // tsl.setGain(TSL2561_GAIN_16X);     /* 16x gain ... use in low light to boost sensitivity */
  //tsl.enableAutoRange(true);          /* Auto-gain ... switches automatically between 1x and 16x */

  /* Changing the integration time gives you better sensor resolution (402ms = 16-bit data) */
  tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS);      /* fast but low resolution */
  // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS);  /* medium resolution and speed   */
  // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS);  /* 16-bit data but slowest conversions */

  /* Update these values depending on what you've set above! */
  Serial.println("------------------------------------");
  Serial.print  ("Gain:         "); Serial.println("Auto");
  Serial.print  ("Timing:       "); Serial.println("13 ms");
  Serial.println("------------------------------------");
}

Python Code

Code below is a rough bit of Python using the Click and Influx libraries to save the output to an InfluxDB database on my remote server.

import logging
import serial
import json
import click
from influxdb import InfluxDBClient
from datetime import datetime
from time import sleep

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s.%(msecs)03d %(levelname)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

logger = logging.getLogger(__name__)


def update_influx(measurements, location):
    client = InfluxDBClient("192.168.0.100", 8086, "root", "root", "greenhouse_outside")
    json_body = []

    for name, value in measurements.items():
        json_body.append(
            {
                "measurement": name,
                "tags": {
                    "location": location
                },
                "time": datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"),
                "fields": {"value": round(value, 2)},
            }
        )
    logger.info(f"Sending \n {json.dumps(json_body, indent=2)}")
    client.write_points(json_body)


@click.command()
@click.argument("port")
def take_reading(port):
    logger.info(f"Using the port {port}")
    ser = serial.Serial(port)
    for i in range(0, 20):
        line = ser.readline().decode("utf-8")
        line = line.replace('nan', '0')
        logger.info(f"Raw fetch: {line}")
    errors = 0
    measurements = {}

    while True:
        try:
            if errors == 5:
                break
            measurements = json.loads(line)
            if measurements['humidity'] == 0:
                measurements['humidity'] = 0.0
            break
        except json.decoder.JSONDecodeError:
            sleep(2)

            line = ser.readline().decode("utf-8")
            line = line.replace('nan', '0')
            logger.info(f"Raw fetch: {line}")
            errors += 1

    update_influx(measurements, 'greenhouse_internal')
    logger.info(f"Processed Internal: {measurements}")
    logger.info("Complete")
    return measurements


if __name__ == "__main__":
    take_reading()

Grafana and InfluxDB

To store the data I’m using an InfluxDB server running on an old Ubuntu powered laptop.

https://docs.influxdata.com/influxdb/v1.8/introduction/install/ has the official guide to installing and setup.

To display the data I use Grafana https://grafana.com again installed on the same server as InfluxDB.