Updated for 2019 China-US Young Maker Competition
IntroductionGardening 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 ComponentsWe 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
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 HubWe 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 DatabaseNext 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.
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.
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.
Comments