What problem are you going to solve?

We run in a busy world where we find it difficult to water plants on time. It was found that gardeners used so much water in their gardens or on their balconies that they lost track of things. The result is that the plants are often either too dry or have to deal with floods. As far as the environment is concerned, it is necessary to take precautions against possible floods beforehand and to be careful not to waste too much water unnecessarily. Otherwise, we will have to deal with serious consequences in the future, such as water shortages, droughts, and so on. With Automatic Plant Watering Systems, you don't need to worry about your plants being thirsty.

What are you going to build to solve this problem? How is it different from existing solutions? Why is it useful?

The idea is to measure temperature, humidity automatically, and moisture levels, transmit them wirelessly via BT and can control a hose if necessary, which can also be controlled remotely via an app from anywhere in the world. The moisture and temperature data are constantly fed to a TinyML model to water the plant based on the inference. An automatic plant watering system ensures that plants get just the right amount of water at just the right time. You can use the Automatic Plant Watering System to care for all your plants effortlessly 24*7.

Block Diagram

Hardware Setup & Connections

I'll be using the Nordic Thingy:53 which has numerous in-built sensors (Temperature, Humidity, Pressure, Gas, Inertial measurement unit (IMU), Magnetometer, Low-power accelerometer, Color, and light sensor), and a Gas sensor to measure temperature, humidity levels using edge impulse.

NodeMCU is programmed to control the functioning of the water pump and to measure the soil moisture level.


We will be setting up the Nordic Thingy 53 for edge impulse classification.

IDE setup

  • Connect the development board to your computer
  • Use a USB cable to connect the development board to your computer. Then, set the power switch to 'on'.
  • Download the latest Edge Impulse firmware: Edge Impulse firmware: nordic-thingy53-full.zip
  • Open nRF Connect for Desktop and launch the Programmer app. Move the power switch SW1 to the OFF position. Take off the top cover to access the SW2 button. Press SW2 while switching SW1 to the ON position.
  • Update the firmware

Generating TFLite Model

We will use edge impulse to build & train the TensorFlow Lite model. We will be collecting the gas sensor data for the following cases

  • Temperature
  • Humidity
  • Pressure

We will be creating an edge impulse project named Thingy 53: Env sensing. Running the following command will require your edge impulse email & password.

edge-impulse-daemon

We have a 65% & 35% split for training and testing data. Next, we create Impulse and add the processing block with Flattern that Flatten an axis into a single value, useful for slow-moving averages like temperature data, in combination with other blocks. Next, we add a Keras Classification learning block, then click on Save Impulse. Next, we click the Generate features to train the model. After a few minutes, we can see the output in the Feature Explorer.

You can visualize the features using the feature explorer.

Hardware & Software

Connect the soil moisture sensor and water pump to the nodemcu as shown below. The main goal of this project is to implement a cost-effective and efficient solution for watering our plants automatically using IoT that uses sensors, Wi-Fi-enabled microcontrollers, and cloud systems. This can be done by sending the soil moisture data to the cloud to be accessed anywhere and anytime. A smartphone or tablet application is also provided to automate the process of watering plants. This device can measure soil humidity/moisture in percentage (%). When the soil is wet, it will have a higher resistance between AO and GND pins, if it is dry it will have lower resistance between AO and GND pins. A TinyML model is generated to water the plants automatically.

Connections:

NodeMCU ------------------------------------- Soil Moisture Sensor

VCC -> Vcc

GND -> Ground

IO32 -> Signal

NodeMCU ------------------------------------- Water pump

VCC -> Vcc

IO22 -> Signal

#include <BLEDevice.h>#include <BLEUtils.h>#include <BLEServer.h>#include "Wire.h"#define DS3231_I2C_ADDRESS 0x68// See the following for generating UUIDs:// https://www.uuidgenerator.net/#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"#define SENSOR_PIN 32#define PUMP 22int sensorValue = 0, set_time[3] = {7, 14, 23}; // variable to store the value coming from the sensorvoid setup() {  Serial.begin(115200);  Serial.println("Starting BLE work!");  Wire.begin();  setDS3231time(30, 42, 16, 5, 13, 10, 16);  pinMode(32, INPUT); //MOISTURE SENSOR  pinMode(22, OUTPUT); //WATER PUMP  BLEDevice::init("PLANT NODE 1");  BLEServer *pServer = BLEDevice::createServer();  BLEService *pService = pServer->createService(SERVICE_UUID);  BLECharacteristic *pCharacteristic = pService->createCharacteristic(                                         CHARACTERISTIC_UUID,                                         BLECharacteristic::PROPERTY_READ |                                         BLECharacteristic::PROPERTY_WRITE                                       );  pCharacteristic->setValue("PLANT NODE 1 CONNECTED");  pService->start();  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();  pAdvertising->addServiceUUID(SERVICE_UUID);  pAdvertising->setScanResponse(true);  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue  pAdvertising->setMinPreferred(0x12);  BLEDevice::startAdvertising();  Serial.println("Characteristic defined! Now you can read it in your phone!");}void loop() {  /*  BLE COM */  std::string REC =   pumpCharacteristic->readValue();  if (REC == "water") {    Serial.println("Watering the plant");    digitalWrite(PUMP, LOW);    delay(3000);    digitalWrite(PUMP, HIGH);  }  else {    digitalWrite(PUMP, HIGH);  }  int cur_hour = getHour();  int watered = 0;     for (int x = 0, x < 3; x++) {    if (cur_hour == set_time[i])    {      /* Soil Moisture sensor */      sensorValue = analogRead(sensorPin);      if (sensorValue < 520) && (watered == 0) {        Serial.println("Watering the plant");        digitalWrite(PUMP, LOW);        delay(3000);        digitalWrite(PUMP, HIGH);        watered = 1;      }      else {        digitalWrite(PUMP, HIGH);      }    }  }  delay(1000);}// Convert normal decimal numbers to binary coded decimalbyte decToBcd(byte val) {  return ( (val / 10 * 16) + (val % 10) );}// Convert binary coded decimal to normal decimal numbersbyte bcdToDec(byte val) {  return ( (val / 16 * 10) + (val % 16) );}void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte                   dayOfMonth, byte month, byte year) {  // sets time and date data to DS3231  Wire.beginTransmission(DS3231_I2C_ADDRESS);  Wire.write(0x0E); // select register  Wire.write(0b00011100); // write register bitmap, bit 7 is /EOS  Wire.write(decToBcd(second)); // set seconds  Wire.write(decToBcd(minute)); // set minutes  Wire.write(decToBcd(hour)); // set hours  Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)  Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)  Wire.write(decToBcd(month)); // set month  Wire.write(decToBcd(year)); // set year (0 to 99)  Wire.endTransmission();}void readDS3231time(byte *second,                    byte *minute,                    byte *hour,                    byte *dayOfWeek,                    byte *dayOfMonth,                    byte *month,                    byte *year) {  Wire.beginTransmission(DS3231_I2C_ADDRESS);  Wire.write(0); // set DS3231 register pointer to 00h  Wire.endTransmission();  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);  // request seven bytes of data from DS3231 starting from register 00h  *second = bcdToDec(Wire.read() & 0x7f);  *minute = bcdToDec(Wire.read());  *hour = bcdToDec(Wire.read() & 0x3f);  *dayOfWeek = bcdToDec(Wire.read());  *dayOfMonth = bcdToDec(Wire.read());  *month = bcdToDec(Wire.read());  *year = bcdToDec(Wire.read());}void getHour() {  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;  // retrieve data from DS3231  readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,                 &year);  // send it to the serial monitor  return hour;}

We made a mobile app to control the pump via BLE.

This project can be implemented to reduce water wastage by reducing the usage of water. This might be a simple/tiny project but can make a huge impact in saving water (Indicator 6.4.1 - Change in water use efficiency over time)

Resources:

https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/ug_thingy53_gs.html#updating-through-usb