Proyecto IoT DIY: Construye una jardinera inteligente de bajo coste
Hace unas semanas decidí comprar una suculenta para mi escritorio de la oficina, simplemente por los beneficios que aportan al medio ambiente. Por si no lo sabías, cultivar suculentas o cactus puede beneficiarte de múltiples maneras: ayudan a purificar el aire, mejoran la humedad de los espacios y aportan oxígeno fresco al ambiente
Tras unos días con Lana (sí, la llamé Lana 💕) en mi escritorio, empecé a sentir que faltaba algo. Como creador apasionado y IoT en Ubidots , decidí crear una " Jardinera Inteligente " para Lana con nuestras herramientas. Tras varios días de arduo trabajo y muchos problemas de impresión, finalmente obtuve este genial resultado, y tenía muchas ganas de compartir mi proceso con ustedes 🙆♀️💚:
Este proyecto se inspiró en uno anterior creado por Paul, un creador 👨💻 (impresión 3D, electrónica, software), al que sigo desde hace meses, y puedo decir que he aprendido muchos trucos y consejos de él. Si buscas inspiración para tus proyectos de electrónica, deberías seguirlo , créeme.
Personalmente, me gusta mucho encontrar mi propia inspiración siguiendo proyectos de otras personas, de todos modos, siempre encontraré una manera de agregar mi propio estilo, dar créditos y, por supuesto, compartir el resultado final para que otras personas como yo también se inspiren. 🤓 🤓
Este post contiene un paso a paso de mi proyecto, así como todos los recursos necesarios para replicar el “ Smart Planter ”.
Requisitos
- Una suculenta o un cactus
- NodeMCU ESP8266
- Sensor de temperatura/humedad DHT11
- Sensor de humedad del suelo
- Anillo RGB
- (5) LED blancos
- Resistencia de 220 Ω
- Cables
- Placa de pruebas PCB
- Cabezales macho en ángulo recto
- Impresora 3D
- Soldadores
- Alambre de soldadura
Paso a paso
- Impresión 3D
- Alambrado
- Caja
- Programación del NodeMCU ESP8266
- Dashboard - Control y monitorización
- Resumen
1. Impresión 3D
Mis habilidades para modelar piezas 3D son algo limitadas (estoy trabajando en ello para crear más proyectos con la impresora 3D), pero existe una comunidad abierta llamada Thingiverse que nos facilita la vida. Es una comunidad de diseño activa para descubrir, crear y compartir objetos imprimibles en 3D. Después de ver muchos diseños de macetas, decidí imprimir la siguiente:
Haga clic aquí para descargar el diseño - cosa:2857938
Me gustó mucho este diseño porque ya era una maceta IoT , y la lámpara era un accesorio precioso y genial. En resumen, este modelo encajaba a la perfección con lo que tenía en mente desde el principio:
- Lámpara para control remoto.
- Espacio para colocar componentes electrónicos para agregar algunos sensores.
Sin embargo, sabía que el espacio para la electrónica no era suficiente para todas las funcionalidades que quería incluir. Para solucionarlo, imprimí dos veces la pieza inferior:
- Pieza blanca : Para colocar el NodeMCU ESP8266, sensores y cables.
- Pieza transparente : Para colocar el anillo NeoPixel.
Teniendo como resultado la obra maestra a continuación:
NOTA: El anillo NeoPixel no es obligatorio para este proyecto; es solo un accesorio que lo embellece. También puedes usar solo una pieza.
2. Cableado
Según el firmware proporcionado en esta guía, consulte el diagrama y la tabla a continuación para establecer una conexión adecuada entre los componentes utilizados.
3. Carcasa
Con las conexiones de cables adecuadas ya establecidas, coloque todos los componentes dentro de la maceta como se muestra a continuación:
Como pueden observar, coloqué el NodeMCU en una placa de pruebas PCB con 10 conectores en ángulo recto. La idea era establecer la conexión con los sensores y ahorrar espacio para la electrónica. Siendo sincero, podría haber organizado mejor los cables. Sin embargo, la idea era crear una PCB personalizada para la maceta; en algún momento, todo quedaría integrado. Además, perforé la pieza blanca para cruzar los cables del anillo NeoPixel. Para terminar, pegué el anillo NeoPixel apuntando hacia abajo en la parte inferior y luego pegué la pieza transparente a la blanca para difuminar la luz. Al final, obtuve este fantástico resultado:
4. Programación del NodeMCU ESP8266
1. Para trabajar con la ESP8266 en el IDE de Arduino, deberá instalarla mediante el Administrador de Placas Arduino . Si no está familiarizado con la adición de una placa con el IDE de Arduino, consulte este artículo para obtener más información .
2. Con la plataforma ESP8266 instalada, seleccione el dispositivo ESP8266 con el que trabaja. En este caso, trabajamos con un NodeMCU 1.0 . Para seleccionar su placa desde el IDE de Arduino, seleccione Herramientas > Placa "Módulo ESP8266 genérico" .
3. Descargue e instale la Ubidots MQTTESP8266 . Para obtener una explicación detallada sobre cómo instalar bibliotecas con el IDE de Arduino, consulte esta guía .
4. Pegue el código a continuación en el IDE de Arduino. A continuación, asigne su token Ubidots Ubidots SSID (nombre de WiFi ) y contraseña de la red disponible.
NOTA: Recuerde asignar los pines correctos para los componentes utilizados en caso de utilizar una conexión diferente a la proporcionada anteriormente.
/* Jardinera inteligente RGB integrada con Ubidots para monitorización y control. Este código funciona para: 1) Leer dos sensores: DHT11 y humedad del suelo. 2) Publicar las lecturas de los sensores en Ubidots. 3) Suscribirse a múltiples variables para control remoto. Librerías requeridas: - Ubidots ESP8266 MQTT - (https://github.com/ubidots/ubidotsubidots-esp) - Adafruit NeoPixel - (https://github.com/adafruit/Adafruit_NeoPixel) - DHT - (https://github.com/adafruit/DHT-sensor-library) Hecho por: Maria Hernández - IoT Developer Advocate @ Ubidots Revisión: José GarcíaubidotsDevelopment & Support Manager @ Ubidots /****************************************** * Incluir librerías ****************************************/ #include<Adafruit_NeoPixel.h> #incluir<stdio.h> #incluir<map> #include "DHT.h" #include "UbidotsESPMQTT.h" /**************************************** * Define Pines ****************************************/ #define LIGHTPIN D1 // Pin digital para Lámpara Led. #define DHTPIN D5 // Pin digital para sensor DHT. #define NEOPIXELSPIN D6 // Pin digital para Anillo NeoPixel. #define MOISTUREPIN A0 // Pin analógico para Sensor de Humedad. /****************************************** * Define Constantes *******************************************/ #define TOKEN "BBFF-xxxxxxxxxx" // Asigna tu TOKEN Ubidots . #define WIFINAME "xxxxxxxxxx" // Asigna tu SSID. #define WIFIPASS "xxxxxxxxxx" // Asigna tu Contraseña WiFi. #define DEVICE "planter" // Etiqueta del Dispositivo Ubidots . #define VAR_PUB_1 "temperatura" // Etiqueta de las variables de Ubidots para publicar datos. #define VAR_PUB_2 "humedad" #define VAR_PUB_3 "humedad del suelo" #define VAR_PUB_4 "índice de calor" #define VAR_SUB_1 "luz-1" // Etiqueta de las variables de Ubidots para suscribirse a los datos; \ // Estas variables deben crearse en Ubidots. #define VAR_SUB_2 "light-2" #define NUMPIXELS 12 // Anillo NeoPixel de 12 bits // Descomente el tipo que esté usando #define DHTTYPE DHT11 // DHT 11 //#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 //#define DHTTYPE DHT21 // DHT 21 (AM2301) typedef enum { red, green, blue, yellow, white, black } NeoPixelColor; // R, G, B uint8_t myColors[][6] = {{250, 0, 0}, // Rojo. {0, 255, 0}, // Verde. {0, 0, 255}, // Azul. {255, 255, 0}, // Amarillo. {255, 255, 255}, // Blanco. {0, 0, 0}}; // Negro. const uint8_t numberOfVariables = 2; // Número de variables para la suscripción. char *variableLabels[numberOfVariables] = {VAR_SUB_1, VAR_SUB_2}; // Etiqueta de las variables para la suscripción. float value; // Almacena el valor entrante. int lastValue; bool bottomLight; // Indicador para controlar las condiciones de la luz inferior. unsigned long initTime; // Almacena el tiempo de inicialización. const long SECONDS_TO_RECONNECT = 180000; // Periodo para reconectar la conexión MQTT. // Función de comparación con las funciones de mapa. struct cmp_str { bool operator()(char const *a, char const *b) const { return strcmp(a, b) < 0; } }; // Declaración de la función de mapa. typedef std::function<void()> Tipo de función; typedef std::map<const char *, FunctionType, cmp_str> mapTopicSubscription; /**************************************** * Definir instancias ****************************************/ Ubidots client(TOKEN); Adafruit_NeoPixel pixels(NUMPIXELS, NEOPIXELSPIN, NEO_GRB + NEO_KHZ800); DHT dht(DHTPIN, DHTTYPE); mapTopicSubscription ubiSubTopic; /**************************************** * Funciones principales *******************************************/ void setup() { initTime = millis(); // Guardar el tiempo de inicialización Serial.begin(115200); pinMode(LIGHTPIN, OUTPUT); // Declarar el modo pin // Define las funciones asignadas para manejar el evento de suscripción. ubiSubTopic[VAR_SUB_1] = &subscriptionHandler1; ubiSubTopic[VAR_SUB_2] = &subscriptionHandler2;ubidotsSetBroker("industrial.api.ubidotsubidots"); // Establece el broker correctamente para la cuenta comercial. client.setDebug(true); // Pasa un valor booleano verdadero o falso para activar los mensajes de depuración. client.wifiConnection(WIFINAME, WIFIPASS); // Establece la conexión wifi. client.begin(callback); dht.begin(); // Inicializa el sensor DHT. pixels.begin(); // Inicializa el anillo NeoPixel. pixels.clear(); // Establece todos los colores de los píxeles en 'desactivados'. // Establece la suscripción con las variables definidas.ubidotsSubscribe(DEVICE, VAR_SUB_1);ubidotsSubscribe(DEVICE, VAR_SUB_2); } void loop() { // Restablece la suscripción con las variables definidas cuando se pierde la conexión o cada 3 minutos. if (!client.connected() || abs(millis() - initTime) > SECONDS_TO_RECONNECT) { initTime = millis(); client.reconnect();ubidotsSubscribe(DEVICE, VAR_SUB_1);ubidotsSubscribe(DEVICE, VAR_SUB_2); } client.reconnect(); // Leyendo los valores de temperatura, humedad y humedad del suelo. float humidity = dht.readHumidity(); float temperature = dht.readTemperature(); int soilMoisture = analogRead(MOISTUREPIN); // Calcular el índice de calor en Celsius (isFahreheit = false). float heatIndexC = dht.computeHeatIndex(temperature, humidity, false); // Verificar si alguna lectura falló y salir antes (para intentarlo de nuevo). if (isnan(humedad) || isnan(temperatura)) { Serial.println(F("¡Error al leer desde el sensor DHT!")); } // Controla los colores de NeoPixel según los valores de temperatura. if (bottomLight) { if (inRange(temperatura, 0, 16)) colorWipe(azul, 50); if (inRange(temperatura, 16, 21)) colorWipe(verde, 50); if (inRange(temperatura, 21, 26)) colorWipe(amarillo, 50); if (inRange(temperatura, 26, 40)) colorWipe(rojo, 50); } // Agrega variables que se publicarán en Ubidots. client.add(VAR_PUB_1, temperatura); client.add(VAR_PUB_2, humedad); client.add(VAR_PUB_3, humedad del suelo); client.add(VAR_PUB_4, heatIndexC); // Publica todas las variables agregadas al dispositivo definido.ubidotsPublish(DEVICE); client.loop(); delay(1000); } /**************************************** * Funciones de suscripción ****************************************/ // Función que se ejecutará cuando var_sub_1 cambie su estado. void subscriptionHandler1() { if (value == 1) { Serial.println("Lámpara de la jardinera encendida."); digitalWrite(LIGHTPIN, HIGH); } else { Serial.println("Lámpara de la jardinera apagada."); digitalWrite(LIGHTPIN, LOW); } }; // Función que se ejecutará cuando var_sub_2 cambie su estado. void subscriptionHandler2() { if (value != lastValue) { if (value == 1) { Serial.println("Luz inferior de la jardinera encendida."); for (int i = 0; i < 3; i++) { colorWipe(rojo, 50); colorWipe(verde, 50); colorWipe(azul, 50); }; colorWipe(blanco, 200); bottomLight = true; } else { Serial.println("Luz inferior de la jardinera apagada."); colorWipe(blanco, 50); colorWipe(negro, 200); bottomLight = false; } } lastValue = value; }; /**************************************** * Funciones auxiliares *******************************************/ // Devuelve un int con la longitud de un char int strLen(char *s) { int l = 0; while (*s != '\0') { s++; l++; } return (l); } // Retrollamada para manejar la suscripción void callback(char *topic, byte *payload, unsigned int length) { char *variableLabel = (char *)malloc(sizeof(char) * 30); getVariableLabelTopic(topic, variableLabel); // Guarda la etiqueta de la variable. value = btof(payload, length); // Guarda el valor de la variable suscrita. executeCases(variableLabel); // Ejecuta el manejador de la función para la // variable suscrita. free(variableLabel); // Libera memoria. } // Analiza el tema recibido para extraer la etiqueta de la variable. void getVariableLabelTopic(char *topic, char *variableLabel) { sprintf(variableLabel, ""); for (int i = 0; i < numberOfVariables; i++) { char *resultLv = strstr(topic, variableLabels[i]); if (resultLv != NULL) { uint8_t len = strlen(resultLv); char result[100]; uint8_t i = 0; for (i = 0; i < len - 3; i++) { result[i] = resultLv[i]; } result[i] = '\0'; snprintf(variableLabel, strLen(result) + 1, "%s", result); break; } } } // Convierte desde una matriz de caracteres a un valor flotante. float btof(byte *payload, unsigned int length) { char *demo_ = (char *)malloc(sizeof(char) * 10); for (int i = 0; i < length; i++) { demo_[i] = payload[i]; } return atof(demo_); } // Ejecuta la "Función de suscripción" correspondiente en función del valor recibido. void executeCases(char *variableLabel) { if (ubiSubTopic.find(variableLabel) != ubiSubTopic.end()) { mapTopicSubscription::iterator i = ubiSubTopic.find(variableLabel); (i->second)(); } } // Rellena los píxeles del anillo de NeoPixel uno tras otro con color. void colorWipe(NeoPixelColor color, int wait) { int r, g, b; r = myColors[color][0]; g = myColors[color][1]; b = myColors[color][2]; for (int i = 0; i < pixels.numPixels(); i++) { pixels.setPixelColor(i, r, g, b); pixels.show(); delay(wait); } } // Verifica si el valor recibido está en el rango esperado bool inRange(float x, int low, int high) { return ((x - low) > 0 && (high - x) >= 0); }
5. Verifique su código en el IDE de Arduino. Para ello, en la esquina superior izquierda del IDE de Arduino verá el icono de la " Marca de verificación "; selecciónelo para verificar su código.
6. Sube el código a tu "NodeMCU 1.0" . Para ello, selecciona el de la flecha derecha junto al icono de la marca de verificación .
7. Para verificar la conectividad del dispositivo y los datos enviados, abra el monitor serial seleccionando el ícono de " lupa " en la esquina superior derecha del IDE de Arduino para ver los logs , así como las respuestas del Ubidots .
8. Tras unos segundos de inicialización, el código proporcionado creará automáticamente un nuevo dispositivo en su Ubidots . Vaya a la sección de dispositivos de su cuenta Ubidots ; debería ver los nuevos dispositivos creados automáticamente:
Ingresa al dispositivo “ Planter ” y observa las variables configuradas transmitiendo los datos:
9. A continuación, cree dos nuevas variables sin procesar para establecer la suscripción y controlar las luces de forma remota. Para ello, haga clic en "Añadir variable > Variable sin procesar " y asígneles la etiqueta definida en el código. En el código de ejemplo proporcionado, las etiquetas de variable que se deben crear son "light-1" y "light-2" .
Con las variables creadas exitosamente, deberías tener el siguiente resultado:
5. Dashboard - Control y monitorización
Con todo integrado, es hora de presentar los datos de los dispositivos en un Dashboard . Este dashboard también servirá para controlar el estado de ambas lámparas.
1. Para crear su primer dashboard , vaya al Dashboard Pestaña (Datos → Dashboards ) . Luego, seleccione el icono más (+) en la esquina superior derecha y seleccione el tipo de widget deseado. Debería poder crear dashboards como el siguiente:
Más información sobre Dashboards Ubidots : Marca de la aplicación: estilos personalizados para sus dashboards y widgets
Ubidots es muy intuitivo para que cualquier usuario administre sus datos, permitiendo a cada uno personalizarlo tanto como quiera; si quieres saber más, te recomiendo consultar las siguientes guías:
- Conceptos básicos Ubidots : dispositivos, variables, Dashboardsy alertas
- Conceptos básicos Ubidots : aplicaciones, organizaciones y usuarios explicados
2. Ubidots admite eventos ya integrados para permitirle enviar eventos, alertas y notificaciones basadas en los datos de sus sensores y actuadores.
Conoce los tipos de eventos admitidos y aprende a configurar cada uno de ellos:
- Notificaciones por correo electrónico
- Notificaciones SMS
- Eventos de webhook: más información
- Notificaciones de Telegram
- Notificaciones de Slack: más información
- Notificaciones de llamadas de voz: más información
- Notificación de regreso a la normalidad: obtenga más información
- Notificaciones de geocercas: más información
6. Resumen
Después de un par de horas de creación y codificación (y muchos cafés), le di vida a esta Smart Planter:
Como mencioné al principio, hice esto no solo porque quería una nueva decoración para el escritorio, sino también porque sentía que le faltaba algo a Lana. Quería inventar mi propia forma de comunicarme con ella a través IoT y los datos. Si buscas otras formas de comunicarte con tus plantas, puedes escucharlas usando el MIDI Sprout . ¡Genial, verdad? 🤩
Esta es solo la versión V0 del Smart Planter y hay algunas cosas que quiero mejorar, como el diseño, la organización de la electrónica con una PCB personalizada y la integración con otros servicios como Google Assistance, Alexa y Twitter.
Espero que lo hayan disfrutado tanto como yo. Si tienen algún comentario o pregunta sobre el proyecto, ¡no duden en contactarme! Estoy totalmente abierto a recibir comentarios constructivos para enriquecer este tipo de proyectos y compartirlos con ustedes. 😁💚
Más proyectos IoT útiles: