Proyecto DIY IoT : construya una maceta inteligente de bajo costo
Hace unas semanas decidí comprar una suculenta para el escritorio de mi oficina únicamente por los beneficios que pueden aportar al medio ambiente. Por si no lo sabes, 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.
Después de unos días de tener a Lana (Sí, la llamé Lana 💕) en mi escritorio, comencé a sentir que faltaba algo. Como creador apasionado y IoT en Ubidots , decidí crear una " maceta inteligente " para Lana utilizando nuestras herramientas. Después de unos días de arduo trabajo y muchos problemas de impresión, finalmente obtuve este fantástico resultado y estaba ansioso por compartir con ustedes mi proceso 🙆♀️💚:
Este proyecto se inspiró en uno anterior creado por Paul a Maker 👨💻 (Impresión 3D, Electrónica, Software) que vengo siguiendo hace meses, y puedo decir que he aprendido muchos consejos y trucos 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 otra persona, de todos modos, siempre encontraré la manera de agregar mi propio estilo, dar créditos y por supuesto, compartir el resultado final para que otras personas como yo lo obtengan. inspirado también. 🤓 🤓
Este post contiene el paso a paso de mi proyecto, así como todos los recursos necesarios para replicar el " Smart Planter ".
Requisitos
- Una suculenta o cactus
- NodoMCU ESP8266
- Sensor de temperatura/humedad DHT11
- Sensor de humedad del suelo
- Anillo RGB
- (5) LED blancos
- Resistencia de 220 Ω
- alambres
- PCB de placa de pruebas
- Conectores macho en ángulo recto
- Impresora 3D
- soldadores
- Alambre de soldadura
Paso a paso
- Impresión 3D
- Alambrado
- Caja
- Programación NodeMCU ESP8266
- Dashboard : control y monitorización
- Resumen
1. Impresión 3D
Mis habilidades para modelar piezas 3D son algo limitadas (estoy trabajando en esto para crear más proyectos usando la impresora 3D), pero hay una comunidad abierta llamada Thingiverse que nos hace la vida más fácil. Es una próspera comunidad de diseño para descubrir, crear y compartir cosas imprimibles en 3D. Después de ver muchos diseños de maceteros, decidí imprimir el siguiente:
Haga clic aquí para descargar el diseño - thing:2857938
Me gustó mucho este diseño porque ya era una maceta IoT y la lámpara era un accesorio hermoso y genial. Básicamente, este modelo encajaba perfectamente con lo que tenía en mente desde el principio:
- Lámpara para control remoto.
- Espacio para colocar componentes electrónicos para añadir 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 solucionar esto, imprimí 2 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, solo fue un accesorio que lo hacía más bonito. También puedes utilizar una sola pieza.
2. Cableado
De acuerdo con 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 puede notar, coloqué el NodeMCU en una placa de circuito impreso con 10 cabezales en ángulo recto. La idea era establecer la conexión con los sensores y ahorrar algo de espacio para la electrónica. Para ser honesto, podría haber hecho un mejor trabajo organizando los cables. Sin embargo, la idea era crear una PCB personalizada para la jardinera, en un momento todo estaría integrado. Además, perforé la pieza blanca para pasar los cables del anillo NeoPixel a través de ella. Para terminar, pegué el anillo NeoPixel apuntando hacia abajo en la parte inferior, luego pegué la pieza transparente a la blanca para difuminar la luz. Al final obtuve este increíble resultado:
4. Programación NodeMCU ESP8266
1. Para poder trabajar con la ESP8266 en el IDE de Arduino, deberá instalar la plataforma ESP8266 utilizando el Arduino Board Manager . Si no está familiarizado con cómo agregar una placa con Arduino IDE, consulte este artículo para obtener orientación adicional .
2. Con la plataforma ESP8266 instalada, seleccione el dispositivo ESP8266 con el que está trabajando. En el caso, estamos trabajando 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 de cómo instalar bibliotecas utilizando el IDE de Arduino, consulte esta guía .
4. Pegue el siguiente código en el IDE de Arduino. Luego, asigne su TOKEN , SSID (nombre de WiFi ) y contraseña únicos Ubidots de la red disponible.
NOTA: Recuerde asignar los pines correctos para los componentes utilizados en caso de que utilice una conexión diferente a la proporcionada anteriormente.
/* RGB Smart Planter integrado con Ubidots para monitoreo y control. Este código funciona para: 1) Leer dos sensores: DHT11 y Humedad del suelo. 2) Publicar lecturas de sensores en Ubidots . 3) Suscríbase a múltiples variables para control remoto. Bibliotecas requeridas: - Ubidots ESP8266 MQTT - ( ubidots -mqtt-esp) - Adafruit NeoPixel - (https://github.com/adafruit/Adafruit_NeoPixel) - DHT ubidots (https:// github.com/adafruit/DHT-sensor-library) Elaborado por: María Hernández - Defensora de desarrolladores IoT @ Ubidots Revisión: José García - Gerente de desarrollo y soporte @ Ubidots /*************** ************************* * Incluir bibliotecas ********************** ******************/ #incluir<Adafruit_NeoPixel.h> #incluir<stdio.h> #incluir<map> #include "DHT.h" #include " Ubidots ESPMQTT.h" /********************************** ****** * Definir pines ****************************************/ #define LIGHTPIN D1 // Pin digital para Lámpara Led. #define DHTPIN D5 // Pin digital para sensor DHT. #define NEOPIXELSPIN D6 // Pin digital para NeoPixel Ring. #define MOISTUREPIN A0 // Pin analógico para sensor de humedad. /****************************************** * Definir constantes ****** ********************************/ #define TOKEN "BBFF-xxxxxxxxxx" // Asigna tu TOKEN Ubidots . #define WIFINAME "xxxxxxxxxx" // Asigne su SSID. #define WIFIPASS "xxxxxxxxxx" // Asigna tu contraseña WiFi. #define DISPOSITIVO "plantador" // Etiqueta del dispositivo Ubidots . #define VAR_PUB_1 "temperatura" // Etiqueta de variables 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 variables Ubidots para suscribirse a datos; \ // Estas variables deben crearse en Ubidots . #define VAR_SUB_2 "light-2" #define NUMPIXELS 12 // Anillo NeoPixel de 12 bits // Descomenta cualquier tipo que estés usando #define DHTTYPE DHT11 // DHT 11 //#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 //#define DHTTYPE DHT21 // DHT 21 (AM2301) typedef enum { rojo, verde, azul, amarillo, blanco, negro } 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 númeroDeVariables = 2; // Número de variables para suscripción. char *variableLabels[númeroDeVariables] = {VAR_SUB_1, VAR_SUB_2}; // Etiqueta de variables para suscripción. valor flotante; // Almacena el valor entrante. int último valor; bool luz inferior; // bandera para controlar las condiciones de la luz inferior. tiempo de inicio largo sin firmar; // Almacena el tiempo de inicio. constante larga SECONDS_TO_RECONNECT = 180000; // Período para volver a conectar la conexión MQTT. // Funtor de comparación con funciones de mapa. struct cmp_str { bool operator()(char const *a, char const *b) const { return strcmp(a, b) < 0; } }; // Declaración de función de mapa. typedef std::función<void()> Tipo de función; typedef estándar::mapa<const char *, FunctionType, cmp_str> mapTopicSubscripción; /****************************************** * Definir instancias ****** **********************************/ Cliente Ubidots (TOKEN); Píxeles de Adafruit_NeoPixel (NUMPIXELS, NEOPIXELSPIN, NEO_GRB + NEO_KHZ800); DHT dht(DHTPIN, DHTTYPE); mapTopicSubscripción ubiSubTopic; /**************************************** * Funciones principales ****** ********************************/ void setup() { initTime = millis(); // Guarda el tiempo de inicio Serial.begin(115200); pinMode(LUZPIN, SALIDA); // Declarar modo pin // Define las funciones asignadas para manejar el evento de suscripción. ubiSubTopic[VAR_SUB_1] = &subscriptionHandler1; ubiSubTopic[VAR_SUB_2] = &subscriptionHandler2; cliente. ubidots SetBroker(" ubidots "); // Configura el corredor correctamente para la // cuenta comercial. client.setDebug(verdadero); // Pase un valor bool verdadero o falso para activar mensajes de depuración. client.wifiConnection(WIFINAME, WIFIPASS); // Establecer conexión WiFi. cliente.begin(devolución de llamada); dht.begin(); // Inicializa el sensor DHT. píxeles.begin(); // Inicializa el anillo NeoPixel. píxeles.clear(); // Establece todos los colores de píxeles en "desactivados". // Establece suscripción con variables definidas. cliente. ubidots Suscribirse (DISPOSITIVO, VAR_SUB_1); cliente. ubidots Suscribirse (DISPOSITIVO, VAR_SUB_2); } void loop() { // Restablece la suscripción con variables definidas cuando se pierde la conexión o cada 3 minutos. if (!client.connected() || abs(millis() - initTime) > SECONDS_TO_RECONNECT) { initTime = millis(); cliente.reconectar(); cliente. ubidots Suscribirse (DISPOSITIVO, VAR_SUB_1); cliente. ubidots Suscribirse (DISPOSITIVO, VAR_SUB_2); } cliente.reconectar(); // Lectura de valores de temperatura, humedad y humedad del suelo.a float humedad = dht.readHumidity(); temperatura flotante = dht.readTemperature(); int sueloMoisture = analogRead(MOISTUREPIN); // Calcular el índice de calor en Celsius (isFahreheit = false). float heatIndexC = dht.computeHeatIndex (temperatura, humedad, falso); // Verifique si alguna lectura falló y salga temprano (para volver a intentarlo). if (isnan(humedad) || isnan(temperatura)) { Serial.println(F("¡Error al leer desde el sensor DHT!")); } // Controla los colores de NeoPixel en función de 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 para publicar en Ubidots . client.add(VAR_PUB_1, temperatura); client.add(VAR_PUB_2, humedad); client.add(VAR_PUB_3, humedad del suelo); cliente.add(VAR_PUB_4, heatIndexC); // Publica todas las variables agregadas al dispositivo definido. cliente. Publicación ubidots (DISPOSITIVO); cliente.loop(); retraso(1000); } /****************************************** * Funciones de suscripción ***** ***********************************/ // Función que se ejecutará cuando var_sub_1 cambie su estado. void subscribeHandler1() { if (valor == 1) { Serial.println("Lámpara de macetero encendida."); escritura digital (LIGHTPIN, ALTA); } else { Serial.println("Lámpara de la maceta apagada."); escritura digital (LIGHTPIN, BAJO); } }; // Función que se ejecutará cuando var_sub_2 cambie su estado. void subscribeHandler2() { if (valor!= lastValue) { if (value == 1) { Serial.println("La luz inferior de la maceta está encendida."); for (int i = 0; i < 3; i++) { colorWipe(rojo, 50); colorWipe(verde, 50); colorWipe(azul, 50); }; colorWipe(blanco, 200); luz inferior = verdadero; } else { Serial.println("La luz inferior de la maceta está apagada."); colorWipe(blanco, 50); colorWipe(negro, 200); luz inferior = falso; } } últimoValor = valor; }; /**************************************** * Funciones auxiliares ****** **********************************/ // Devuelve un int con la longitud de un char int strLen(char *s) {int l = 0; mientras (*s != '\0') { s++; l++; } retorno (l); } // Devolución de llamada para manejar la devolución de llamada nula de suscripción (char *tema, byte *carga útil, longitud int sin signo) { char *variableLabel = (char *)malloc(sizeof(char) * 30); getVariableLabelTopic(tema,variableLabel); // Guarda la etiqueta de la variable. valor = btof(carga útil, longitud); // Guarda el valor de la variable suscrita. ejecutarCases(etiquetavariable); // Ejecuta el controlador de funciones para la // variable suscrita. gratis(etiquetavariable); // Memoria libre. } // Analiza el tema recibido para extraer la etiqueta de la variable. void getVariableLabelTopic(char *tema, char *variableLabel) { sprintf(variableLabel, ""); for (int i = 0; i < numberOfVariables; i++) { char *resultLv = strstr(tema, variablesLabels[i]); if (resultLv != NULL) { uint8_t len = strlen(resultLv); resultado de carácter carbónico[100]; uint8_t yo = 0; for (i = 0; i < len - 3; i++) { resultado[i] = resultadoLv[i]; } resultado[i] = '\0'; snprintf(variableLabel, strLen(resultado) + 1, "%s", resultado); romper; } } } // Transmitir desde una matriz de caracteres a un valor flotante. float btof(byte *carga útil, longitud int sin signo) { char *demo_ = (char *)malloc(sizeof(char) * 10); for (int i = 0; i < longitud; i++) { demo_[i] = carga útil[i]; } devolver atof(demo_); } // Ejecuta la respectiva "Función de Suscripción" en base al valor recibido. void ejecutarCases(char *variableLabel) { if (ubiSubTopic.find(variableLabel) != ubiSubTopic.end()) { mapTopicSubscription::iterator i = ubiSubTopic.find(variableLabel); (i->segundo)(); } } // Rellena los píxeles del anillo de NeoPixel uno tras otro con color. void colorWipe(Color NeoPixelColor, int espera) { int r, g, b; r = misColores[color][0]; g = misColores[color][1]; b = misColores[color][2]; for (int i = 0; i < píxeles.numPixels(); i++) { píxeles.setPixelColor(i, r, g, b); píxeles.show(); retrasar(esperar); } } // 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 dentro del IDE de Arduino. Para hacer esto, en la esquina superior izquierda de nuestro IDE de Arduino verás el ícono de " Marca de verificación "; selecciónelo para verificar su código.
6. Cargue el código en su “NodeMCU 1.0” . Para hacer esto, elija el ícono de " flecha hacia la derecha" al lado del ícono de " marca de verificación ".
7. Para verificar la conectividad del dispositivo y los datos enviados, abra el monitor serie 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. Después de unos segundos de inicialización, el código proporcionado creará un nuevo dispositivo automáticamente en su Ubidots . Vaya a la sección de dispositivos de su cuenta Ubidots ; Deberías ver nuevos dispositivos creados automáticamente:
Ingrese al dispositivo “ Planter ” y vea las variables configuradas transmitiendo los datos:
9. Luego, cree dos nuevas variables sin procesar para establecer la suscripción y controlar las luces de forma remota. Para hacer esto, haga clic en "Agregar variable > Variable sin formato " y asigne la etiqueta de variable definida en el código. Para el código de muestra proporcionado, las etiquetas de variables que se crearán son "light-1" y "light-2" .
Con las variables creadas exitosamente, debería tener el siguiente resultado:
5. Dashboard : control y supervisión
Con todo ya integrado, llega el momento de presentar los datos de los dispositivos en un Dashboard . Asimismo, el dashboard también nos servirá para controlar el estado de ambas luces.
1. Para crear su primer dashboard , vaya al Dashboard pestaña (Datos → Dashboards ) . Luego, seleccione el ícono más (+) en la esquina superior derecha, luego seleccione el tipo de widget deseado. Debería poder crear dashboards como el siguiente:
Más sobre Dashboards Ubidots : Marca de aplicación: estilos personalizados para sus dashboards y widgets
Ubidots es muy intuitivo para que cualquier usuario administre sus datos, permitiendo a cualquiera personalizarlos tanto como quiera; En caso de que quieras saber más, te recomiendo que consultes las siguientes guías:
- Conceptos básicos Ubidots : dispositivos, variables, Dashboards y alertas
- Conceptos básicos Ubidots : aplicaciones, organizaciones y usuarios explicados
2. Ubidots admite eventos ya integrados para permitirle enviar eventos, alertas y notificaciones en función de los datos de sus sensores y actuadores.
Consulta el tipo de eventos admitidos y aprende cómo configurar cada uno de ellos.:
- Notificaciones por correo electrónico
- Notificaciones por SMS
- Eventos de webhook: obtenga más información
- Notificaciones de telegramas
- Notificaciones de Slack: más información
- Notificaciones de llamadas de voz: obtenga más información
- Volver a la notificación normal: obtener más información
- Notificaciones de geovalla: obtenga más información
6. Resumen
Después de un par de horas de hacer y codificar (y de muchos cafés), le di vida a esta Smart Planter:
Como mencioné al principio, hice esto porque no solo quería una nueva decoración de escritorio, sino que también sentía que a Lana le faltaba algo, quería inventar mi propia forma de comunicarme con ella a través de IoT y datos. En caso de que quieras otras formas de comunicarte con tus plantas, puedes escucharlas usando MIDI Sprout . Esto es genial, ¿verdad? 🤩
Este es solo el V0 de Smart Planter y hay algunas cosas que quiero mejorar, como el diseño, la organización electrónica con una PCB personalizada y la integración con otros servicios como Google Assistance, Alexa y Twitter.
Espero que hayas disfrutado esto como yo. En caso de que tenga algún comentario o pregunta sobre el proyecto, ¡no dude en comunicarse! Estoy totalmente abierto a comentarios constructivos para potenciar este tipo de proyectos y compartirlos contigo. 😁💚
Proyectos IoT más útiles: