Project tutorial

Smart Plant IoT © LGPL

Auto watering the plant and tracking plant's health through Helium and Microsoft Azure IoT.

  • 9,008 views
  • 3 comments
  • 34 respects

Components and supplies

Apps and online services

About this project

Smart Plant IoT Demo

Introduction

Gardening might be a fun hobby for some, but for many others it's much of a hassle to deal with. In this article I am writing a simple guide on how to build a smart IoT plant that would send sensor data to Azure IoT Hub and storing over Azure SQL via Azure functions, at the same time both auto and remotely control water for the plant.

Helium IoT Hub connects to Azure IoT Hub in a seamless fashion, in this article we will explain how the entire process works. Since the entire project is serverless, the only code needed for the entire process to work is just Azure Function and Arduino code.

Step 1: Gather Components

We are building a simple product using

  • Arduino UNO
  • SEEED Grove Base shield
  • Helium Atom + Helium Element with Helium Arduino Breakout board
  • A pump
  • Temperature/Humidity sensor, Moisture Sensor, UV Light Sensor
  • Grove OLED Display

Step 2: Set Up Arduino with Helium and Sensors

In this article we will focus on using Arduino as our app, the first thing is that we will be pushing data from Helium Atom to Helium Hub. We first have to register our Atom on Helium Network Dashboard.

After setting up the Atom we'd also have to register Element as they are the access point, (for those who has cellular version powering it up would do).

After activating element we should see it on Access Point.

Next, we need to attach all the sensor as well as the Helium Atom, when it's all done it should look something like this, a bit messy but we can clean that up later.

We can run the following code to know that the program is running.

#include "Arduino.h"
#include "Board.h"
#include "Helium.h"
#include "HeliumUtil.h"
#include <TH02_dev.h>
#include "Arduino.h"
#include "Wire.h"
#include <SeeedGrayOLED.h>
#include <avr/pgmspace.h>
#define CHANNEL_NAME "Azure IoT App"
Helium  helium(&atom_serial);
Channel channel(&helium);
int relay = 5;
void setDisplayToOriginalState()
{
 SeeedGrayOled.init(SSD1327);
}
void setup() {
 // put your setup code here, to run once:
 Serial.begin(9600);
 pinMode(relay, OUTPUT);
 delay(150);
 /* Reset HP20x_dev */
 TH02.begin();
 delay(100);
 Serial.println("TH02_dev is available.\n");
 DBG_PRINTLN(F("Starting"));
 // Begin communication with the Helium Atom
 // The baud rate differs per supported board
 // and is configured in Board.h
 helium.begin(HELIUM_BAUD_RATE);
 // Connect the Atom to the Helium Network
 helium_connect(&helium);
 // Begin communicating with the channel. This should only need to
 // be done once. The HeliumUtil functions add simple retry logic
 // to re-create a channel if it disconnects.
 channel_create(&channel, CHANNEL_NAME);
 Wire.begin();
}
void loop() {
 //Sound Pollution
 int moisture = 0;
 for (int i = 0; i < 32; i++)
 {
   moisture += analogRead(A0);
 }
 int uvlight = 0;
 for (int i = 0; i < 32; i++)
 {
   uvlight += analogRead(A1);
 }
 float temper = TH02.ReadTemperature();
 float humidity = TH02.ReadHumidity();
 String dataString = "Moisture=" + String(moisture) + "&UVLight=" + String(uvlight) + "&Temperature=" + String(temper) + "&Humidity=" + String(humidity);
 char data[dataString.length()];
 dataString.toCharArray(data, dataString.length());
 channel_send(&channel, CHANNEL_NAME, data, strlen(data));
 Serial.println(data);
 setDisplayToOriginalState();
 SeeedGrayOled.clearDisplay();     //Clear Display.
 SeeedGrayOled.setNormalDisplay(); //Set Normal Display Mode
 SeeedGrayOled.setVerticalMode();  // Set to vertical mode for displaying text
 SeeedGrayOled.setTextXY(0, 0);          //Set the cursor to 0th line, 0th Column
 String moisturestring = "Moisture: " + String(moisture);
 char moibuffer[moisturestring.length()];
 moisturestring.toCharArray(moibuffer, moisturestring.length());
 SeeedGrayOled.putString(moibuffer);
 SeeedGrayOled.setTextXY(2, 0);
 String uvstring = "UVLight: " + String(uvlight);
 char uvbuffer[uvstring.length()];
 uvstring.toCharArray(uvbuffer, uvstring.length());
 SeeedGrayOled.putString(uvbuffer);
 SeeedGrayOled.setTextXY(4, 0);
 String temperaturestring = String(temper) + " C";
 char tempbuffer[temperaturestring.length()];
 temperaturestring.toCharArray(tempbuffer, temperaturestring.length());
 SeeedGrayOled.putString(tempbuffer);
 SeeedGrayOled.setTextXY(6, 0);
 String humidstring = "Humid: " + String(humidity);
 char humidbuffer[temperaturestring.length()];
 humidstring.toCharArray(humidbuffer, humidstring.length());
 SeeedGrayOled.putString(humidbuffer);
 if(moisture < 100)
 {
   digitalWrite(relay, HIGH);
   delay(5000);
   digitalWrite(relay, LOW);
 }
 delay(60000);
} 

The water pump requires 12V, while normal Arduino would only output 5V max, so in order to get the lock to work we can tap into the power source by soldering two wires at the power source like image below. We will use red wire as 12V and black wire as ground.

The relay will act as a control to whether when the water will be pumped in.

Step 3: Set Up Helium Hub and Azure IoT Hub

We first create IoT Hub under all services, it would be wise to move IoT Hub into favorite so it would be accessed much more easier. We can use standard tier since the Free Trial $200 trial credit can cover it. You can also choose to use the Free Tier as well.

After selection the name you can move to Size and Scale.

After it's created we need to go to Shared Access Policies->RegistryReadWrite entry-> Connection String -- Primary Key, also make sure Registry Read and Registry Write is checked, although they should be default

We can create our first device for prototype to test out the connection

After getting that primary connection string, go to Helium Dashboard and create a Helium Connection, after pasting the connection string into the connection field, everything else should be automatically filled.

After setting this up, we would be able to get all the MQTT strings being automatically generated in Helium Hub. This can be easily accessed through the channel.

Since Azure requires device to publish and subscribe to a fixed MQTT topic, this will allow Helium Atom to do that as well as letting IoT Hub to push messages to Helium Atom. We can do following to test out the sending to Azure.

git clone https://github.com/helium/helium-cli.git
cd helium-cli
make
./helium -p /dev/

That will check whether Helium is installed correctly

./helium -p /dev/serial0 channel create "Azure IoT App"
./helium -p /dev/serial0 channel send 1 "Hello Azure" 

This will send information from the Atom to Azure directly, we should check that on both Helium Dashboard as well as Azure IoT Hub Overview

And on Azure IoT Hub below we should see the same result

Device is authenticated through X509, and Helium platform handles all of it. Making it simple and clean.

Step 5: Set Up Azure SQL Database

Next we need to be able to store the data coming from IoT device. There is a great guide about this written in detail on https://blogs.msdn.microsoft.com/sqlserverstorageengine/2018/01/23/working-with-azure-iot-data-in-azure-sql-database/ In this article we will focus on quick integration of how that happens. We first go to SQL databases to create a database as image below, we can select Basic Tier as we are only starting the app, the free trial credit should be able to cover it. This is the cheapest option for prototyping, as you scale, you might want to move to Azure Cosmos in the future since the minimum on Cosmos is $25.

Afterwards we can use Query editor to create following table, for starter we are just gona use Smart Plant IoT's simple data structure to get started

CREATE TABLE SmartPlant (
id bigint IDENTITY (1,1) NOT NULL,
Temperature int NOT NULL,
Humidity int NOT NULL,
Moisture int NOT NULL,
UVLight int NOT NULL,
DateCreated datetime default CURRENT_TIMESTAMP
)

Now we have a table to store the data to, we need to connect this to an eventhub so that data can be stored in. Go to Connection Strings and grab the connection string for the next step.

Step 4: Create Azure Function App

In order to connect to function we will use Event Hub. We first need to create an Azure Function App, which allows serverless structure, which is great for IoT applications since we no longer have to maintain. To start we first need to create a function App under compute.

We can create Function under these settings

Just take about a couple of minutes and we will have it under our notifications.

Function App Deployed

Now that we have functions, Next we will create a function under IoT Hub (Event Hub) trigger so we can get the event hub running. Go to function->platform features->Application settings

In here we are going to add the connection string we've created in the previous step. Save it after created

Next step is create a Event Hub function, for this example we will use C#. After clicking new connection things should be auto populated.

Change the Function to following, this is to to insert data directly into Azure SQL Database.

using System.Configuration;
using System.Data.SqlClient;
using System.Threading.Tasks;
public static async Task Run(string myIoTHubMessage, TraceWriter log)
{
var map = myIoTHubMessage.Split('&').Select(x => x.Split('=')).ToDictionary(x => x[0], x => x[1]);
   String Temperature = map["Temperature"];
   String H
   String Moisture = map["Moisture"];
   String UVLight = map["UVLight"];
var str = ConfigurationManager.ConnectionStrings["sqldb_connection"].ConnectionString;
using (SqlConnection conn = new SqlConnection(str))
   {
       conn.Open();
var text = "INSERT INTO dbo.SmartPlant (Temperature, 
using (SqlCommand cmd = new SqlCommand(text, conn))
       {
// Execute the command and log the # rows affected.
var rows = await cmd.ExecuteNonQueryAsync();
           log.Info($"{rows} rows were updated");
       }
   }
   log.Info($"C# IoT Hub trigger function processed a message: {myIoTHubMessage}");
}

When successful, you should be able to see

At this point we have the entire end to end data sending from Helium to Azure SQL via Azure IoT Hub. Next we need to retrieve the data, which we need to create an HTTP Trigger via Azure Function API.

We will change couple of values, routing to be /data so we can access /api/smartplant, and Authorization level to be anonymous, and HTTP method for GET only

As for the code, we can test it out by accessing address

http://<yourapp>.azurewebsites.net/api/smartplant?name=foobar&code=<functionkey>

This would test out the result and return "hello foobar". When this is finished, we can use following code to return the actual data. Next we can use following code to test out the entire app. This is the simplest query, which additional information can be gathered by writing more complex queries, but for prototype we will just focus on getting one record.

#r "System.Configuration"
#r "System.Data"
#r "Newtonsoft.Json"
using System;
using System.Net;
using System.Configuration;
using System.Data.SqlClient;
using System.Threading.Tasks;
using System.Text;
using Newtonsoft.Json;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
   log.Info("C# HTTP trigger function processed a request.");
var str = ConfigurationManager.ConnectionStrings["sqldb_connection"].ConnectionString;
using (SqlConnection conn = new SqlConnection(str))
   {
       conn.Open();
var text = "SELECT Top 100 Temperature, Moisture, UVLight
       SmartPlant ret = new SmartPlant();
using (SqlCommand cmd = new SqlCommand(text, conn))
       {
           SqlDataReader reader = await cmd.ExecuteReaderAsync();
try
           {
while (reader.Read())
               {
                   ret.Temperature = (int)reader[0];
                   ret.Moisture = (int)reader[1];
                   ret.UVLight = (int)reader[2];
                   ret.Humidity = (int)reader[3];
               }
           }
finally
           {
// Always call Close when done reading.
               reader.Close();
           }
var json = JsonConvert.SerializeObject(ret, Formatting.Indented);
return new HttpResponseMessage(HttpStatusCode.OK) 
           {
               Content = new StringContent(json, Encoding.UTF8, "application/json")
           };        
       }
   }
}
public class SmartPlant
{
    public float Temperature { get; set; }
    public float Moisture { get; set; }
    public float UVLight { get; set; }
    public float Humidity { get; set; }
}

When all done, it should yield result for the latest record.

Step 5: UI for Output

Now that everything is connected from end to end, we can build an simple Android application that can check up the plant's overall health. In this case we are using a very simple Android app to monitor the 4 sensors that's around the plant, as well as trigger the peristaltic pump to water the plant if necessary. It should display and update information like below. The data should be passing every 60 seconds (or however you want to set it)

On the other side the Arduino enclosure can be closed so it would have a much better view next to the plant.

We can easily simulate it's own pumping.

Extra: Alexa Integration

Code

Azure Function GET RequestC#
Restful call from calling /api/smartplant
#r "System.Configuration"
#r "System.Data"
#r "Newtonsoft.Json"
using System;
using System.Net;
using System.Configuration;
using System.Data.SqlClient;
using System.Threading.Tasks;
using System.Text;
using Newtonsoft.Json;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
   log.Info("C# HTTP trigger function processed a request.");
var str = ConfigurationManager.ConnectionStrings["sqldb_connection"].ConnectionString;
using (SqlConnection conn = new SqlConnection(str))
   {
       conn.Open();
var text = "SELECT Top 100 Temperature, Moisture, UVLight from dbo.IoTData Order by DateCreated DESC";
       EventData ret = new EventData();
using (SqlCommand cmd = new SqlCommand(text, conn))
       {
           SqlDataReader reader = await cmd.ExecuteReaderAsync();
try
           {
while (reader.Read())
               {
                   ret.Temperature = (int)reader[0];
                   ret.Moisture = (int)reader[1];
                   ret.UVLight = (int)reader[1];
               }
           }
finally
           {
// Always call Close when done reading.
               reader.Close();
           }
var json = JsonConvert.SerializeObject(ret, Formatting.Indented);
return new HttpResponseMessage(HttpStatusCode.OK) 
           {
               Content = new StringContent(json, Encoding.UTF8, "application/json")
           };        
       }
   }
}
public class SmartPlant
{
    public float Temperature { get; set; }
    public float Moisture { get; set; }
    public float UVLight { get; set; }
}
Azure function data insert from IoT HubC#
Inserting data via Azure function
using System.Configuration;
using System.Data.SqlClient;
using System.Threading.Tasks;
public static async Task Run(string myIoTHubMessage, TraceWriter log)
{
 var map = myIoTHubMessage.Split('&').Select(x => x.Split('=')).ToDictionary(x => x[0], x => x[1]);
    String Type = map["Type"];
    String Confidence = map["Confidence"];
    log.Info(Type);
    log.Info(Confidence);
 var str = ConfigurationManager.ConnectionStrings["sqldb_connection"].ConnectionString;
 using (SqlConnection conn = new SqlConnection(str))
    {
        conn.Open();
 var text = "INSERT INTO dbo.IoTData (Type, Confidence) VALUES ('" + Type + "', " + Confidence + ");";
 using (SqlCommand cmd = new SqlCommand(text, conn))
        {
 // Execute the command and log the # rows affected.
 var rows = await cmd.ExecuteNonQueryAsync();
            log.Info($"{rows} rows were updated");
        }
    }
    log.Info($"C# IoT Hub trigger function processed a message: {myIoTHubMessage}");
}
Azure function shortcutC#
directly inserting into AzureSQL
#r "System.Configuration"
#r "System.Data"

using System;
using System.Configuration;
using System.Data.SqlClient;
using System.Threading.Tasks;
using System.Net;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{


    string Temperature = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "Temperature", true) == 0)
        .Value;

    string Moisture = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "Moisture", true) == 0)
        .Value;

    string UVLight = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "UVLight", true) == 0)
        .Value;

    if (Temperature == null || Moisture == null || UVLight == null)
    {
        // Get request body
        return req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body");
    }

    var str = ConfigurationManager.ConnectionStrings["sqldb_connection"].ConnectionString;
    
    using (SqlConnection conn = new SqlConnection(str))
    {
        conn.Open();
        var text = "INSERT INTO dbo.SmartPlant (Temperature, Moisture, UVLight) VALUES (" + Temperature + ", " + Moisture  + ", " + UVLight + ");";

        using (SqlCommand cmd = new SqlCommand(text, conn))
        {
            // Execute the command and log the # rows affected.
            var rows = await cmd.ExecuteNonQueryAsync();
            log.Info($"{rows} rows were updated");
        }
    }

    return req.CreateResponse(HttpStatusCode.OK, "Success");
}
Arduino codeArduino
Arduino code for uploading the data as well as automatically watering the plant
#include "Arduino.h"
#include "Board.h"
#include "Helium.h"
#include "HeliumUtil.h"
#include <TH02_dev.h>
#include "Arduino.h"
#include "Wire.h"
#include <SeeedGrayOLED.h>
#include <avr/pgmspace.h>

#define CHANNEL_NAME "Azure IoT App"

Helium  helium(&atom_serial);
Channel channel(&helium);
int relay = 5;

void setDisplayToOriginalState()
{
  SeeedGrayOled.init(SSD1327);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(relay, OUTPUT);
  delay(150);
  /* Reset HP20x_dev */
  TH02.begin();
  delay(100);
  Serial.println("TH02_dev is available.\n");
  DBG_PRINTLN(F("Starting"));

  // Begin communication with the Helium Atom
  // The baud rate differs per supported board
  // and is configured in Board.h
  helium.begin(HELIUM_BAUD_RATE);

  // Connect the Atom to the Helium Network
  helium_connect(&helium);

  // Begin communicating with the channel. This should only need to
  // be done once. The HeliumUtil functions add simple retry logic
  // to re-create a channel if it disconnects.
  channel_create(&channel, CHANNEL_NAME);
  Wire.begin();
}

void loop() {

  //Sound Pollution
  int moisture = 0;
  for (int i = 0; i < 32; i++)
  {
    moisture += analogRead(A0);
  }

  int uvlight = 0;
  for (int i = 0; i < 32; i++)
  {
    uvlight += analogRead(A1);
  }

  float temper = TH02.ReadTemperature();
  float humidity = TH02.ReadHumidity();


  String dataString = "Moisture=" + String(moisture) + "&UVLight=" + String(uvlight) + "&Temperature=" + String(temper) + "&Humidity=" + String(humidity);
  char data[dataString.length()];
  dataString.toCharArray(data, dataString.length());
  channel_send(&channel, CHANNEL_NAME, data, strlen(data));
  Serial.println(data);

  setDisplayToOriginalState();
  SeeedGrayOled.clearDisplay();     //Clear Display.
  SeeedGrayOled.setNormalDisplay(); //Set Normal Display Mode
  SeeedGrayOled.setVerticalMode();  // Set to vertical mode for displaying text
  SeeedGrayOled.setTextXY(0, 0);          //Set the cursor to 0th line, 0th Column
  String moisturestring = "Moisture: " + String(moisture);
  char moibuffer[moisturestring.length()];
  moisturestring.toCharArray(moibuffer, moisturestring.length());
  SeeedGrayOled.putString(moibuffer);

  SeeedGrayOled.setTextXY(2, 0);
  String uvstring = "UVLight: " + String(uvlight);
  char uvbuffer[uvstring.length()];
  uvstring.toCharArray(uvbuffer, uvstring.length());
  SeeedGrayOled.putString(uvbuffer);

  SeeedGrayOled.setTextXY(4, 0);
  String temperaturestring = String(temper) + " C";
  char tempbuffer[temperaturestring.length()];
  temperaturestring.toCharArray(tempbuffer, temperaturestring.length());
  SeeedGrayOled.putString(tempbuffer);

  SeeedGrayOled.setTextXY(6, 0);
  String humidstring = "Humid: " + String(humidity);
  char humidbuffer[temperaturestring.length()];
  humidstring.toCharArray(humidbuffer, humidstring.length());
  SeeedGrayOled.putString(humidbuffer);

  if(moisture < 100)
  {
    digitalWrite(relay, HIGH);
    delay(5000);
    digitalWrite(relay, LOW);
  }
  delay(60000);
}
Project repo
Project repo for netduino smart plant

Schematics

Helium Architecture
Helium architecture connects IoT Device directly to Azure IoT Hub
Helium arch agqcbqmlep

Comments

Similar projects you might like

Octopod: Smart IoT Home/Industry Automation Project

Project tutorial by Saksham Bhutani

  • 7,019 views
  • 8 comments
  • 30 respects

Plant Monitoring System using AWS IoT

Project tutorial by CJA3D

  • 30,463 views
  • 6 comments
  • 77 respects

Windows 10 IoT Plant Monitoring System

Project tutorial by BME-AUT

  • 12,872 views
  • 4 comments
  • 59 respects

Home Smart Home

Project tutorial by Syed Sanoor

  • 68,548 views
  • 10 comments
  • 193 respects

IoT Blink - Getting started with IoT

Project showcase by AppShed Support

  • 2,026 views
  • 0 comments
  • 11 respects
Add projectSign up / Login