# Building a plant monitoring system Author: Daniel Johansson (dj222mx) ###### tags: IoT esp32 MQTT Node-Red InfluxDB Grafana Raspberry pi i2cOLED BME280 Capacitive Soil Moisture Sensor Analog temperature sensor ## Introduction This tutorial presents the steps required to make a plant monitoring system using a esp32 microcontroller. It will also show how to present the measurements on a OLED-display and making a visual presentation using Grafana running on a Raspberry Pi. Time estimate, 1 day if lucky and with some prior knowledge. For me, with little to no experience this probably took closer to 10 days. (A valuable lesson learned: Low quality equipment = Lots of troubleshooting). ### Objective I like plants! However I'm absolutely terrible at taking care of them. This will be an attempt to help my beloved plants from dying. This will also be a first step to integrate some smart systems to my manual watering through my irrigation hoses in the garden. For me, every step will be a learning process all the way from installing the first software to placing a final product in a plant. I am well aware that the final product probably will come at a later date, but that date will be much closer after this project. ## Material | Hardware | Price | Link | Comments | | -------- | -------- | -------- | -------- | |<img src="https://i.imgur.com/ppvtpAE.jpg" alt="sensor" width="100"/><br>Capacitive Soil Moisture Sensor| 6-pack for 220 sek<br>(approximately 40 sek each)| [Amazon.se](https://www.amazon.se/gp/product/B08GCRZVSR/ref=ppx_yo_dt_b_asin_title_o08_s03?ie=UTF8&psc=1)|Watch [this youtube tutorial](https://www.youtube.com/watch?v=IGP38bz-K48&ab_channel=Flaura-SmartPlantPot) before you buy this type of sensors, (linked sensor is of type NE555 20M)| |<img src="https://i.imgur.com/L4e9HZ6.jpg" alt="sensor" width="100"/><br>Breadboard Kit with wires and AC-Adapter| 3-pack for 170sek<br>(approximately 60 sek each)| [Amazon.se](https://www.amazon.se/gp/product/B07VC9ZRW1/ref=ppx_yo_dt_b_asin_title_o08_s03?ie=UTF8&psc=1)|These breadboards from AZ-Delivery are not the highest quality, and the pins on the AC adaptors are bent, (but they did still work). Bought a starterkit from [Elegoo](https://www.amazon.se/gp/product/B01M7N4WB6/ref=ppx_yo_dt_b_asin_title_o08_s02?ie=UTF8&psc=1) that were of much higher quality.| |<img src="https://i.imgur.com/6XpnRsz.jpg" alt="sensor" width="100"/><br>Digital Temperature Sensor, Waterproof| 5-pack for 140sek<br>(approximately 30 sek each)| [Amazon.se](https://www.amazon.se/gp/product/B075FYYLLV/ref=ppx_yo_dt_b_asin_title_o08_s03?ie=UTF8&psc=1)|Needs soldering to connect properly to breadboard| |<img src="https://i.imgur.com/KiaPdaS.jpg" alt="sensor" width="100"/><br>9V AC/DC Power Adapter| 100sek | [Amazon.se](https://www.amazon.se/gp/product/B07NSMYZXS/ref=ppx_yo_dt_b_asin_title_o03_s00?ie=UTF8&psc=1)|I also tried a battery option with a [battery-clip](https://www.amazon.se/gp/product/B082ZM798K/ref=ppx_yo_dt_b_asin_title_o03_s01?ie=UTF8&psc=1) and a standard [9V Battery](https://www.biltema.se/kontor---teknik/batterier/alkaliska-batterier/9v6lr61-alkaliskt-batteri-2000041781?gclid=CjwKCAjw_ISWBhBkEiwAdqxb9iK7y6y8q4RQpeYLBvgKZ-I7tIvjQQEm4EpRaNbdB1PS2S-8q4vixRoCbO4QAvD_BwE), that however did not last very long.| |<img src="https://i.imgur.com/AVV7Y10.jpg" alt="sensor" width="100"/><br>Resistance Kit (that contains a 4.7k Ohm resistor)| 113sek for 525 resistors<br>(lets say 1pc 4,7k resistor costs 1 sek) | [Amazon.se](https://www.amazon.se/-/en/gp/product/B07Q87JZ9G/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1)|| |<img src="https://i.imgur.com/VtoaCsm.jpg" alt="sensor" width="100"/><br>BME280 Humidity, Temperature & Pressure Sensor| 2-pack for 150sek<br>(approximately 75 sek each) | [Amazon.se](https://www.amazon.se/gp/product/B088R89TSX/ref=ppx_yo_dt_b_asin_title_o08_s01?ie=UTF8&psc=1)|Needs soldering| |<img src="https://i.imgur.com/ofZSDGd.jpg" alt="sensor" width="100"/><br>OLED Display| 3-pack for 170sek<br>(approximately 60 sek each) | [Amazon.se](https://www.amazon.se/-/en/gp/product/B074N9VLZX/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1)|| |<img src="https://i.imgur.com/VCzN5L5.png" alt="sensor" width="100"/><br>Raspberry Pi| 600 sek | [Kjell & Company](https://www.kjell.com/se/produkter/dator/raspberry-pi/raspberry-pi-3-model-b-enkortsdator-p88100)|I had a 3b laying around, other versions should work fine as well| |<img src="https://i.imgur.com/WYC76CZ.png" alt="sensor" width="100"/><br>Raspberry Pi power adaptor| 105 sek | [Dustin Home](https://www.dustinhome.se/product/5010909900/stromadapter)|| |<img src="https://i.imgur.com/kx6OOA9.jpg" alt="sensor" width="100"/><br>ESP32 Development Board| 5-pack for 450sek<br>(approximately 90 sek each) | [Amazon.se](https://www.amazon.se/-/en/gp/product/B074RG86SR/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1)|The first unit that i tested, i was unable to connect to, second one worked. Again AZ-Delivery product with questionable quality, some bent pins| <img src="https://i.imgur.com/ovOxGEt.png" alt="sensor" width="100"/><br>Lödstation| 800 sek | [Kjell & Company](https://www.kjell.com/se/produkter/el-verktyg/verktyg/lodning/lodkolvar/dibotech-lodstation-pro-60-w-p40570)|Two parts needed soldering, cheaper versions exist| <img src="https://i.imgur.com/6uRHMIx.png" alt="sensor" width="100"/><br>Solder wire| 120 sek | [Kjell & Company](https://www.kjell.com/se/produkter/el-verktyg/verktyg/lodning/lodtenn-lod/almit-blyfritt-lodtenn-05-mm-15-g-p40662)|| <img src="https://i.imgur.com/lastV3x.jpg" alt="sensor" width="100"/><br>Flux Paste| 170 sek | [Amazon.se](https://www.amazon.se/-/en/gp/product/B07Y54XKX2/ref=ppx_od_dt_b_asin_title_s03?ie=UTF8&psc=1)|Can probably manage without for this small project, but good to have| | Device | Total Price | | -------- | -------- | |Raspberry pi for MQTT/Node-Red/InfluxDB/Grafana|~700 sek| |ESP32 with sensors for both soil and air, with OLED|~550 sek| |ESP for only soil moisture|~290 sek| | Tools | Total Price | | -------- | -------- | |Soldering equipment|~1100 sek| ## ESP32 Setup The entire code can be found here: [Github](https://github.com/gos84/Plant1) ### Flashing Firmware Its always a good idea to install the latest firmware on a new microcontroller to make sure it has the latest bug-fixes and possible upgrades to security. * Connect your board to the computer with a USB cable that is capable of data transferring (not only charging). * The ESP32-board is quite wide, you can use two breadboards as shown in the image below to get more space, (but not needed). ![](https://i.imgur.com/ojGgi9v.jpg) * Install hardware drivers for your COM ports from [this site](https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers). * Download the latest firmware from [this site](https://micropython.org/download/esp32/). * Flash the ESP32-board using [this site.](https://nabucasa.github.io/esp-web-flasher/) * Choose *460800* Baud rate (i used *115200* which, atleast for me, also worked). * Connect using the COM-port for the USB to UART Bridge Controller. * (Keep the Boot button on your board down both during Erase and Programming, press reset button when told to). * Press Erase to delete the files from your board (**Do not shut down your browser or disconnect the USB-cable during this process**) * Enter 1000 as offset and choose your file with the firmware you downloaded previously (**Do not shut down your browser or disconnect the USB-cable during this process**) ## Computer Setup We will need to install some software on the computer to be able to program the microcontroller. There are many different development IDEs to choose from. For this tutorial we will go with *Atom IDE*. ### Installing software * Install [NodeJS](https://nodejs.org/en/), (download current version) * Install [Atom](https://atom.io/) * Open Atom and install the Pymakr package (*File-Settings-Packages* and search for '*pymakr*') * Open settings on Pymakr and in *Global settings*. Then enter the device com port into the Device address (list) field and remove the check box from *Safe-boot before upload* We should now be good to start connecting our sensors and program the board. If you haven't restarted your computer, now is a good opportunity to do so. #### Using Atom First of we need to create a folder that will be our Project folder that we upload the ESP32-board. We do this in Atom by navigating to File-Add Project folder. Give it a suitable name and create a subfolder which we name 'lib' where we will store all library files. We connect and upload our code to the ESP32 using Atom IDE. In the pymakr plugin we can connect to the device using: 'Connect Device' followed by the same COM-port we used during the firmware update. We also create 2 files which we give the names: `boot.py` and `main.py`. Code placed in `boot.py` will run at boot up before the main code that we place in `main.py`. In this tutorial our `boot.py` will be empty and everything we write is in `main.py`. ## Connecting sensors Lets connect 1 sensor at a time to make sure it works before continuing with the next one. Remember to unplug the microcontroller from the computer and the power outlet before making connections. Also remember to triple check the connections you made before powering the device to prevent damage to the board. The time between reading are controlled by the `sleep()` function at the end of the code in the main loop in `main.py`. I've chosen to set this to 1min. There are no measurement reading that change drastically that need a lot of readings. But since i don't run the device on battery I can still have a decently high resolution on the readings. This value can of course be changed to what you want it to be. ### OLED We will start off by connecting and writing some code for the OLED-device. We start with this first so we can print some information on the display as we go along. This sensor communicates using I2C communication protocol, so the wiring is very simple. | BME280 | ESP32 | | ------ | ----- | |Voc|3.3V| |GND|GND| |SCL|GPIO22| |SDA|GPIO21| An image for the wiring for the OLED is shown in the next chapter about the BME280 since it also uses I2C, the connections are the same. The following libraries are needed for the OLED scree. ``` import machine import ssd1306 ``` And the only code we need to start diplay information on the screen is: ``` #OLED i2cOLED = machine.I2C(-1, machine.Pin(22), machine.Pin(21), freq=500000) # Note the frequency, different than the BME280 sensor. display = ssd1306.SSD1306_I2C(128, 64, i2cOLED) # change 128 and 64 to match your display if different screenresolution display.fill(0) # Clear the display display.show() ``` To present our measurement reading on the display we add this at the end of the loop: ``` #OLED display.fill(0) display.text("Soilmoist: " + ehum + " %", 0, 0, 1) display.text("Soiltemp: " + etemp + " C", 0, 10, 1) display.text("Airtemp: " + temp + " C", 0, 30, 1) display.text("Airhum: " + hum + " %", 0, 40, 1) display.text("Airpres: " + pres + " hPa", 0, 50, 1) display.show() ``` ### BME280 For this sensor we first need to solder the pins. This sensor also communicates using I2C communication protocol, so the wiring is the same as for the OLED device. | BME280 | ESP32 | | ------ | ----- | |Vin|3.3V| |GND|GND| |SCL|GPIO22| |SDA|GPIO21| Image of wiring for the BME280 and the OLED-display: | Schematic | Wiring | | ------ | ----- | |<img src="https://i.imgur.com/dFibLFQ.png" alt="sensor" width="500"/>|<img src="https://i.imgur.com/jDgQ78h.jpg" alt="sensor" width="500"/><br>Notice that the wires for SCL and SDA connect on the same pin for the BME280 sensor and the OLED display| For the BME280 i used [this library](https://github.com/RuiSantosdotme/ESP-MicroPython/raw/master/code/WiFi/HTTP_Client_IFTTT_BME280/BME280.py). Download and save to the library folder in the project folder. The code used for the BME280 sensor are as follows. libraries needed: ``` import machine from time import sleep_ms import BME280 ``` Set the I2C Pins and frequency: ``` i2cBME = machine.I2C(scl=machine.Pin(22), sda=machine.Pin(21), freq=10000) ``` During main while loop: ``` bme = BME280.BME280(i2c=i2cBME) #create a BME280 object temp = ("{0:.1f}").format(bme.temperature) # read temperature and format to 1 decimal hum = ("{0:.1f}").format(bme.humidity) # read humidity and format to 1 decimal pres = ("{0:.1f}").format(bme.pressure) # read pressure and format to 1 decimal print('Air Temperature: ', temp) # print the readings on the shell print('Air Humidity: ', hum) # print the readings on the shell print('Air Pressure: ', pres) # print the readings on the shell ``` ### Soil moisture sensor I did not watch the Youtube-video shown in the material list, but lucky for me, I bought one that works with 3.3V and not missing any components. We need to change the attenuation to full voltage 3.3V, else we only get 4095 readings from the sensor. Default value would only return value *4095*. The sensor returns a voltage in mV depending on the resistance in the soil. More water = lower resistance and a higher returned voltage. And vice versa. To calibrate leave the sensor in dry air and look for the highest returned voltage. Then place the sensor in a glass of water and look for the lowest returned voltage. Not sure that the soil moisture level is linear, but for this it works well enough. My sensors lowest and highest values was 1080mV and 2400mV Sometime it exceeds those values after a reboot, in that case we set the value to min or high in that case to prevent negative or value above 100%. Since the reading differ a bit we take the average over 10 reading 100mS apart and use that value. We also display the readings on the OLED. The code looks like this: Needed libraries: ``` import machine ``` Set attenuation, wet and dry values and calculate moisture level from voltage reading: ``` #Soil moisture sensor adc = machine.ADC(machine.Pin(36)) adc.atten(machine.ADC.ATTN_11DB) # change attenuation to full range 3.3V wet = 1080 # calibrate (lowest value with sensor in a glass of water) dry = 2400 # kalibrera (highest value with sensor in air) def scale_value(value, in_min, out_max, out_min): scaled_value = ((value - in_min) * 100) / (out_max - out_min) # set value the 0-100 % return scaled_value ``` and in the loop: ``` #jordfuktighet tmp = 0 for y in range(0, 10): # average value of 10 readings during 1s tmp = tmp + adc.read() sleep_ms(100) moisture=tmp/10 if moisture > dry: # sometimes the value would go outside the calibrated range moisture = dry if moisture < wet: # sometimes the value would go outside the calibrated range moisture = wet print('Soil Moisture: ', str(round(scale_value(moisture, dry, wet, dry)))) ehum = str(round(scale_value(moisture, dry, wet, dry))) ``` ### Soil temperature sensor, ds18x20 First we need to do some soldering on the wires to be able to connect to breadboard. I just solder on 3 jumper cables with the corresponding color (red, black, yellow) at the end of the cable from the sensor. The connections are as follows: ![](https://i.imgur.com/8OHLoBs.jpg) A 4.7K Resistor is used as a pull-up resistor & is connected between digital output pin & VCC Pin I connected the digital output pin to GPIO36. Note: Keeping the air temperature sensor and the soil temperature sensor next to each other they differ by 1 degree C, with the soil sensor showing the higher value. The code is as follows: The following libraries are used: ``` import machine import onewire import ds18x20 from time import sleep_ms, sleep, time ``` ``` #Soil temperature, ds18x20 ds_pin = machine.Pin(4) ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin)) roms = ds_sensor.scan() #if more than 1 sensor is connected ``` In the loop, (here i needed a delay of 750ms before reading the temperature to avoid a CRC-error): ``` #soiltemperature ds_sensor.convert_temp() sleep_ms(750) for rom in roms: #print(rom) print('Soil Temperature: ', ("{0:.1f}").format(ds_sensor.read_temp(rom))) etemp = ("{0:.1f}").format(ds_sensor.read_temp(rom)) #only one sensor ``` ## Wi-Fi and MQTT I've chosen to use Wi-Fi for communication for three reasons: * I have no need for the devices to communicate outside a local network. * I already have good Wi-Fi-coverage both outdoor and indoor where i would place this device. * And i don't need to pay a monthly fee if I manage to make lots of plant monitoring devices. ### Wi-Fi We should really not store our SSID and Password unencrypted in the code. I would highly recommend some kind of encryption. (Specially if placed outside your house). But this device will be placed indoors and not connected to the internet, so we will use the easy route and just connect to our Wi-Fi using the *network* library and putting our network credentials in the code. https://hackmd.io/@lnu-iot/SJ8TGsUd5 We will also place out code in the main.py file and not in the boot.py file to be able to reconnect if connection is lost. We will define a function that connects to the Wi-Fi and also display some information on the OLED screen with the following code: (change the '*SSID-Name*' and *SSID-Password* to yours) ``` #WIFI def do_connect(): import network sta_if = network.WLAN(network.STA_IF) # Put modem on Station mode if not sta_if.isconnected(): # Check if already connected print('connecting to network...') display.fill(0) display.text("Connecting", 0, 0, 1) display.text("to WiFi...", 0, 10, 1) display.show() sta_if.active(True) # Activate network interface sta_if.connect('SSID-Name', 'SSID-Password') # Your WiFi Credential # Check if it is connected otherwise wait while not sta_if.isconnected(): pass # Print the IP assigned by router print('network config:', sta_if.ifconfig()) display.text("Connected!", 0, 20, 1) #IPaddr = sta_if.ifconfig()[0]) #display.text("IP: " +IPaddr, 0, 30, 1) display.show() ``` And then we simply use this code to connect to the Wi-Fi:<br> `do_connect() # WiFi Connection` In our main loop we will also test our connection so it hasn't been lost, if so we simply reconnect to the WiFi and also the MQTT-Broker before we continue with the loop. ``` while True: #check wifi connection sta_if = network.WLAN(network.STA_IF) if not sta_if.isconnected(): do_connect() sleep(1) try: display.fill(0) display.text("Connecting to", 0, 0, 1) display.text("MQTT Broker...", 0, 10, 1) display.show() client = connect_mqtt() except OSError as e: display.fill(0) display.text("Failed to connect", 0, 0, 1) display.text("Restarting...", 0, 10, 1) display.show() restart_and_reconnect() ``` ### MQTT Let’s start by preparing the ESP32-board to connect to our MQTT-broker and publish some measurement readings. * To use MQTT with the ESP32/ESP8266 and MicroPython, you need to install the [umqttsimple library](https://raw.githubusercontent.com/RuiSantosdotme/ESP-MicroPython/master/code/MQTT/umqttsimple.py). * The libraries we need are: ``` import machine import ubinascii from umqttsimple import MQTTClient ``` We need to set some definitions. * The Ip-address to the MQTT-server. * The topics for the measurement readings. (we have 5 measurements for this device: *topic_pub_tempX*, which we publish under sensors/plant1) * We are not using the message interval for this application since we publish the topics in a loop with a long sleep duration between publishing’s ``` #MQTT declarations mqtt_server = '192.168.1.91' # change to your MQTT-broker IP-adress client_id = ubinascii.hexlify(machine.unique_id()) topic_pub_temp1 = b'sensors/plant1/soilhum' topic_pub_temp2 = b'sensors/plant1/soiltemp' topic_pub_temp3 = b'sensors/plant1/airtemp' topic_pub_temp4 = b'sensors/plant1/airhum' topic_pub_temp5 = b'sensors/plant1/airpres' last_message = 0 message_interval = 1 ``` We define two functions used to connect and reconnect to the MQTT broker. Change the '*Username*' and '*Password*' to what you chose during the mosquitto installation on the Pi. ``` def connect_mqtt(): global client_id, mqtt_server #client = MQTTClient(client_id, mqtt_server) client = MQTTClient(client_id, mqtt_server, user='Username', password='Password') client.connect() print('Connected to %s MQTT broker' % (mqtt_server)) return client def restart_and_reconnect(): print('Failed to connect to MQTT broker. Reconnecting...') time.sleep(10) machine.reset() ``` Use the following code to connect to the MQTT-broker: (the display functions are for splaying information on the OLED, if connection is successful, this will pass by fast). ``` # Connect to MQTT Broker try: display.fill(0) display.text("Connecting to", 0, 0, 1) display.text("MQTT Broker...", 0, 10, 1) display.show() client = connect_mqtt() except OSError as e: display.fill(0) display.text("Failed to connect", 0, 0, 1) display.text("Restarting...", 0, 10, 1) display.show() restart_and_reconnect() display.fill(0) display.text("Connected", 0, 0, 1) display.show() ``` In the main loop we will (if our set minimum time has elapsed), publish our sensor readings to the MQTT-broker: ``` #Start loop while True: #MQTT publish if (time() - last_message) > message_interval: client.publish(topic_pub_temp1, ehum) client.publish(topic_pub_temp2, etemp) client.publish(topic_pub_temp3, temp) client.publish(topic_pub_temp4, hum) client.publish(topic_pub_temp5, pres) last_message = time() ``` ## Raspberry Pi Setup ### Install Rasbian * Download [Rasbian image](https://www.raspberrypi.com/software/operating-systems/) (i went with Raspberry Pi OS with desktop (bullseye) since i will use a display later for showing graphs from Grafana). * Flash the image on the SD-card, i prefer [Balena etcher](https://www.balena.io/etcher/) * Enable SSH on Raspberry Pi in Headless Mode * Navigate to the boot folder. This is the root folder of your SD card. Boot is the default volume name when you install a Raspbian system on an SD card. * In the boot volume, create a file without an extension and name it SSH. * Insert the SD-card into the Raspberry Pi and boot it up. * SSH into the Raspberry Pi, (For windows use [Putty](https://www.putty.org/)). In Putty enter the Ip-address for the Raspberry Pi, make sure SSH is selected and use the port *22* * When connected, log in with the default username: *pi* and password: *raspberry*. * Since all Raspbian installations come with a default account and password, the first thing you need to do is to change the password. Do this by typing <code>sudo raspi-config</code> and change the password under *Change User Password*. * Change DHCP to static IP, i did this in my router settings. You can also type: <code>sudo nano /etc/dhcpcd.conf</code> configure a static IP followed by a reboot: <code>sudo reboot</code> ## Install MQTT broker/mosquitto The next part is to set up the MQTT broker. Ive chose to run Mosquitto on a Raspberry Pi 3b since i felt it was unnecessary to make each plant monitor system open to the internet, and I've got no use for connecting to the plants remotely at the time being. It will also be better/more secure to only have one device open to the internet (the Pi). So if the need should arise, we can then open the pi the internet. For this tutorial, we will use [Mosquitto](https://mosquitto.org/). * Before installing the MQTT broker we need to update the operating system. Run the following command:<br>`sudo apt update && sudo apt upgrade` * To install Mosquitto type the following command: `sudo apt install mosquitto mosquitto-clients` * To make Mosquitto auto start when the Raspberry Pi boots, you need to run the following command:<br>`sudo systemctl enable mosquitto.service` * You can test that it is installed and running by typing: `mosquitto -v` * To enable remote access with authentication we will run the following command, but replace YOUR_USERNAME with the username you want to use and type in a password:<br>`sudo mosquitto_passwd -c /etc/mosquitto/passwd YOUR_USERNAME` * To enable remote access so that we can communicate with other IoT devices, we need to edit/create a configuration file * Run the following command:<br>`sudo nano /etc/mosquitto/mosquitto.conf` * Paste the following line at the top of the file:<br>`per_listener_settings true` * Paste the following three lines at the end of the file:<br>`allow_anonymous false`<br>`listener 1883`<br>`password_file /etc/mosquitto/passwd` * Press *ctrl-x*, then *y*, and press *Enter* to exit and save the changes * Restart Mosquitto:<br>`sudo systemctl restart mosquitto` * Check if mosquitto is running by typing:<br>`sudo systemctl status mosquitto` ## influxDB Next we will install influxDB to store our measurement data in. * First we add inluf reposistories to apt: ``` wget -qO- https://repos.influxdata.com/influxdb.key | sudo apt-key add - source /etc/os-release echo "deb https://repos.influxdata.com/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/influxdb.list ``` * Update apt with the new repos, & install influxDB:<br>`sudo apt update && sudo apt install -y influxdb` * Then start the influxdb service and set it to run at boot: ``` sudo systemctl unmask influxdb.service sudo systemctl start influxdb sudo systemctl enable influxdb.service ``` * Last thing is to create a database (with the name '*home*') and a user with privileges to write to the database. Write following commands in influxDB, with the *'USERNAME'* and *'PASSWORD'* that you want to use: ``` create database home use home create user 'USERNAME' with password 'PASSWORD' with all privileges grant all privileges on home to 'USERNAME' ``` ## Node-RED Next up is something that will read the MQTT topics and store them in a database. For this I've chosen Node-red to read and store the data to the influxDB database, could also have gotten with Telegraf but Node-red seamed intuitive and powerful. To install Node-RED do the following: * Type the following to install Node-RED: ``` bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered) ``` * Press *Y* and press *Enter* to accept * When asked: “Would you like to install Pi-specific nodes?” Press *Y* and *Enter* * After installation is complete configure Node-RED by typing:<br>`node-red admin init` * Press *Enter* to create a Node-RED Settings file * Choose to set up user security and enter a username and password. * define user permissions to *full access* * Type *No* to enable Project features and select the *default* flows file. * Provide a passphrase, select a theme and press Yes to allow Function nodes to load external modules. * Start Node-Red by typing:<br>`node-red-start` * And add Node-Red to start at boot:<br>`sudo systemctl enable nodered.service` Lets access Node-RED in a browser by typing:<br>`http://<ipaddress>:1880` Now lets create a flow that reads from our MQTT-Broker and stores the measurement reading to our influxDB database. We goal will look like this: ![](https://i.imgur.com/cxPEfVc.png) * The first thing you need to do is install the InfluxDB Node-Red package * Click the *menu-button* in the top right corner and navigate to *manage palette* and then the *Install tab* * Type *InfluxDB* into the search box and select the package called *node-red-contrib-influxdb* Now we are ready to drag the 3 different nodes to our workflow. We need: | mqtt-in | function | influxdb out | | -------- | -------- | -------- | | <img src="https://i.imgur.com/IMYlqWV.png" alt="sensor"> | <img src="https://i.imgur.com/CgpZDBV.png" alt="sensor"> | <img src="https://i.imgur.com/gnryvSo.png" alt="sensor"> | Double-click the MQTT in-node and type in the Server, Topic and Name. You will need to make 5 of these, 1 for each measurement. But after you make one you can just copy it and change the values ![](https://i.imgur.com/Qsb5cHE.png) Since our MQTT-client (the ESP32-board) are publishing the measurement data as variables of type *String*, we need to convert them to a number to be able to make a graph in Grafana. We do this with a function. Double-click the function node and type in the following: ![](https://i.imgur.com/nFa3EMz.png) Finally we write our measurement data to the InfluxDB database with the last node. Double-click the Influxdb out node and type the following: ![](https://i.imgur.com/2h8YrtZ.png) Now we are ready to make some graphs in Grafana! ## Grafana Finally we want to show a graphical representation of out measurement data. Grafana. I've also tested [MQTT explorer](http://mqtt-explorer.com/) to just make some graphs directly from the MQTT broker, but it lacks a lot of the functionality that Grafana has with all the customization. * First we add the Grafana packages to apt: ``` wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add - echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list ``` * Then we update and install Grafana:<br>`sudo apt update && sudo apt install -y grafana` * Then start the Grafana service and set it to run at boot: ``` sudo systemctl unmask grafana-server.service sudo systemctl start grafana-server sudo systemctl enable grafana-server.service ``` * We can now log in to Grafana using a browser and create our graphs, use the following url with correct ip-address and log in:<br>`http://<ipaddress>:3000` Now we hopefully have some data-reading in our InfluxDB database and Grafana up and running. Let's create some Graphs. * First we add a database in Grafana. * Log in to your Grafana instance and go to “*Data Sources*”. Select “*Add new Data Source*” and find *InfluxDB* under “*Timeseries Databases*”. * Since both services on the same Pi, set the URL to *localhost* and use the default port *8086* * Type in the name of the database, user and password that we set earlier. * Hit “Save & Test” to connect * Now we can make a dashboard. From the main page, click + | Create | Dashboard and Add a New Panel. * Choose a topic and select the last value as shown in the image below:![](https://i.imgur.com/1X9kkri.png) * Play around and customize as you want and for each topic you want to see. ![](https://i.imgur.com/LvKqxQ9.png) # Testing the Plant monitor I found a plant that needed some attention, the soil was completely dry. When i inserted the Soil moisture sensor it showed ~10%, luckily this plant is hard to kill: ![](https://i.imgur.com/K8FxHbs.jpg) The plant was so dry it was even difficult to water. The soil did not get moist since the water went straight through it, without the soil soaking up any water. I ended up watering a lot and even added some new soil. Maybe I overdid the watering some, it ended up at 85%. Luckily it probably is one of the sturdiest plants there is. Looking at the graphs, i watered the plant between 15:10 and 15:25, the dips represents the capacitor briefly being removed from the soil. The graphs are missing data between 07:00 and 10:00 due to the Raspberry Pi becoming unresponsive, some troubleshooting might still be required. There are some short breaks at 11:00 due to some updates I made to the code. But overall, it looks promising! ![](https://i.imgur.com/N22uykz.png) I will hopefully add more devices with only soil moisture sensors for my indoor plants, if made for outdoor plants, I will add the temperature sensor for the soil. # Future This project needs some nice 3D-printed (waterproof) cases. It would also be fun to combine this project with my outdoor irrigation hoses. I bought a small touch screen to the Pi to show graphs for plants. For some reason it suddenly stopped working, so the Pi is back to headless mode for now. Some optimizations to the code, for example encrypt Wi-Fi credentials and make some optimizations for Power consumption. # Conclusion This feels like a really good start, I learned a lot, especially since i had zero experience with microdevices or even with Python before starting this project. Both the ESP32 and the Raspberry Pi seams to run smoothly now after some previous problems. But I still expect some crashes and troubleshooting ahead of me. If the device will be running on battery, I need to look over the power managment. I tried a standard 9V battery but it didn't last long with this setup. Connecting through a power supply is recommended, for now.