Planta Inteligente IoT: Construye un Invernadero Conectado con Arduino y Azure
Componentes y materiales
| Componente | Cantidad |
|---|---|
| Seeed Base Shield V2 | 1 |
| Seeed Grove - Relé | 1 |
| Seeed Grove - Sensor de Temperatura | 1 |
| Arduino UNO | 1 |
| Helium Starter Kit (LEGACY) | 1 |
Aplicaciones y servicios online
- Microsoft Azure
- Microsoft Visual Studio 2015
Sobre el proyecto
Actualizado para China-US Young Maker Competition 2019
Introducción
La jardinería puede ser un hobby divertido para algunos, pero para muchos es una molestia. En este artículo escribo una guía simple para construir una planta inteligente IoT que envíe datos de sensores a Azure IoT Hub y los almacene en Azure SQL vía Azure Functions, controlando el riego automática y remotamente.
Helium IoT Hub se conecta a Azure IoT Hub sin problemas. Explicaremos todo el proceso. Al ser serverless, el único código necesario es Azure Function y código Arduino.
Paso 1: Reunir componentes
- Arduino UNO
- SEEED Grove Base Shield
- Helium Atom + Helium Element con Helium Arduino Breakout board
- Una bomba
- Sensores: Temperatura/Humedad, Humedad suelo, Luz UV
- Pantalla OLED Grove
Paso 2: Configurar Arduino con Helium y sensores
Usaremos Arduino como nuestra app, enviando datos de Helium Atom a Helium Hub. Primero registra tu Atom en Helium Network Dashboard.
Luego registra Element como punto de acceso (versión cellular solo encender).
Tras activar Element debería verse en Access Point.
Conecta todos los sensores y Helium Atom. Debería verse así (un poco desordenado pero se puede limpiar luego).
Ejecuta el siguiente código para verificar que el programa funciona.
#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() {
Serial.begin(9600);
pinMode(relay, OUTPUT);
delay(150);
TH02.begin();
delay(100);
Serial.println("TH02_dev is available.\n");
DBG_PRINTLN(F("Starting"));
helium.begin(HELIUM_BAUD_RATE);
helium_connect(&helium);
channel_create(&channel, CHANNEL_NAME);
Wire.begin();
}
void loop() {
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();
SeeedGrayOled.setNormalDisplay();
SeeedGrayOled.setVerticalMode();
SeeedGrayOled.setTextXY(0, 0);
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);
}
La bomba requiere 12V, Arduino solo da 5V máx. Para que funcione, suelda dos cables en la fuente de alimentación (rojo=12V, negro=GND). El relé controla cuándo se bombea agua.
Paso 3: Configurar Helium Hub y Azure IoT Hub
Crea IoT Hub en Azure (nivel estándar o gratis). Ve a Shared Access Policies > RegistryReadWrite > Connection String Primary Key. Crea primer dispositivo para probar.
En Helium Dashboard crea Helium Connection, pega connection string, lo demás se autollenará.
Los topics MQTT se generan automáticamente en Helium Hub, accesibles por canal.
Para probar envío a Azure:
git clone https://github.com/helium/helium-cli.git
cd helium-cli
make
./helium -p /dev/
./helium -p /dev/serial0 channel create "Azure IoT App"
./helium -p /dev/serial0 channel send 1 "Hello Azure"
Verifica en Helium Dashboard y Azure IoT Hub Overview.
Dispositivo autenticado vía X509, Helium lo gestiona todo.
Paso 4: Crear Azure SQL Database
Crea base de datos SQL (nivel Basic para prototipo). Usa Query Editor para crear tabla:
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
)
Obtén connection string en Connection Strings.
Paso 5: Crear Azure Function App
Crea Function App en Compute (plan de consumo).
En Function > Platform features > Application settings, añade connection string SQL.
Crea Function con trigger Event Hub (IoT Hub). Usa C#. Cambia código para insertar directamente en Azure SQL:
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 Humidity = map["Humidity"];
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, Humidity, Moisture, UVLight) VALUES (" + Temperature + ", " + Humidity + ", " + Moisture + ", " + UVLight + ");";
using (SqlCommand cmd = new SqlCommand(text, conn)) {
var rows = await cmd.ExecuteNonQueryAsync();
log.Info($"{rows} rows were updated");
}
}
log.Info($"C# IoT Hub trigger function processed a message: {myIoTHubMessage}");
}
Cuando funcione verás logs de filas insertadas.
Crea HTTP Trigger para recuperar datos (API). Ruta /data, Authorization anonymous, GET only.
Código para devolver último registro:
#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, Humidity from dbo.SmartPlant Order by DateCreated DESC";
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 { 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; }
}
Resultado del último registro.
Paso 6: UI para salida
App Android simple para monitorizar 4 sensores y activar bomba peristáltica. Datos cada 60 segundos.
Caja Arduino cerrada para mejor vista junto a la planta.
Simulación de bombeo.
Extra: Integración Alexa
Código
Azure Function GET Request
#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 { 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 Hub
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)) {
var rows = await cmd.ExecuteNonQueryAsync();
log.Info($"{rows} rows were updated");
}
}
log.Info($"C# IoT Hub trigger function processed a message: {myIoTHubMessage}");
}
Azure function shortcut
#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) {
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)) {
var rows = await cmd.ExecuteNonQueryAsync();
log.Info($"{rows} rows were updated");
}
}
return req.CreateResponse(HttpStatusCode.OK, "Success");
}
Código Arduino
#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() {
Serial.begin(9600);
pinMode(relay, OUTPUT);
delay(150);
TH02.begin();
delay(100);
Serial.println("TH02_dev is available.\n");
DBG_PRINTLN(F("Starting"));
helium.begin(HELIUM_BAUD_RATE);
helium_connect(&helium);
channel_create(&channel, CHANNEL_NAME);
Wire.begin();
}
void loop() {
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();
SeeedGrayOled.setNormalDisplay();
SeeedGrayOled.setVerticalMode();
SeeedGrayOled.setTextXY(0, 0);
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);
}
Repositorio del proyecto
https://github.com/Nyceane/smart-plant-iot
Esquemático
Arquitectura Helium conecta dispositivo IoT directamente a Azure IoT Hub
Proceso de manufactura
- Papelera Inteligente: Solución IoT Celular para Gestión Automatizada de Residuos
- Barman Inteligente: Construye tu Propia Estación de Cócteles con Raspberry Pi
- Revolucionando la agricultura: Cómo IoT impulsa la eficiencia y la sostenibilidad
- Datos inteligentes: Navegando la próxima frontera de IoT y Big Data
- GE Appliances Inaugura Planta de Refrigeración Inteligente e Industria 4.0 en Decatur, Alabama
- Hospitales Inteligentes del Mañana: El Papel Crítico del Software Inteligente
- Cómo las smart cities aprovechan IoT, microservicios y dashboards para gestión eficiente de activos
- Maximice el ROI de sus inversiones en IoT: Una guía práctica
- Construyendo un Almacén Inteligente Optimizado con un Ecosistema IoT Robusto
- Asegura tus Dispositivos Inteligentes: Mejores Prácticas para la Seguridad de IoT
- Explorando Innovaciones IoT de Vanguardia y Aplicaciones Reales