Blog del Ing. Alejandro Alomar (webalealomar@gmail.com). En este blog subo algunos de los trabajos que hago como hobby (no profesional), hay temas variados como, impresoras 3d, IOT (internet de las cosas), programación de ESP8266, electrónica, etc.
En el tutorial de hoy explico como hacer un dispositivo de IOT (internet de las cosas) que nos permite obtener la inclinación en X, Y y Z (roll, pitch y yaw). Para esto voy a explicar cómo conectar un MPU6050, que es un IMU (Unidad de Medición Inercial) a un ESP8266 y a partir de un filtro complementario obtener dichos ángulos.
MPU6050:
#include<Wire.h>//Direccion I2C de la IMU#defineMPU0x68//Ratios de conversion#defineA_R16384.0// 32768/2#defineG_R131.0// 32768/250//Conversion de radianes a grados 180/PI#defineRAD_A_DEG=57.295779//MPU-6050 da los valores en enteros de 16 bits//Valores RAWint16_tAcX,AcY,AcZ,GyX,GyY,GyZ;//AngulosfloatAcc[2];floatGy[3];floatAngle[3];Stringvalores;longtiempo_prev;floatdt;voidsetup(){Wire.begin(4,5);// D2(GPIO4)=SDA / D1(GPIO5)=SCLWire.beginTransmission(MPU);Wire.write(0x6B);Wire.write(0);Wire.endTransmission(true);Serial.begin(115200);}voidloop(){//Leer los valores del Acelerometro de la IMUWire.beginTransmission(MPU);Wire.write(0x3B);//Pedir el registro 0x3B - corresponde al AcXWire.endTransmission(false);Wire.requestFrom(MPU,6,true);//A partir del 0x3B, se piden 6 registrosAcX=Wire.read()<<8|Wire.read();//Cada valor ocupa 2 registrosAcY=Wire.read()<<8|Wire.read();AcZ=Wire.read()<<8|Wire.read();//A partir de los valores del acelerometro, se calculan los angulos Y, X//respectivamente, con la formula de la tangente.Acc[1]=atan(-1*(AcX/A_R)/sqrt(pow((AcY/A_R),2)+pow((AcZ/A_R),2)))*RAD_TO_DEG;Acc[0]=atan((AcY/A_R)/sqrt(pow((AcX/A_R),2)+pow((AcZ/A_R),2)))*RAD_TO_DEG;//Leer los valores del GiroscopioWire.beginTransmission(MPU);Wire.write(0x43);Wire.endTransmission(false);Wire.requestFrom(MPU,6,true);//A partir del 0x43, se piden 6 registrosGyX=Wire.read()<<8|Wire.read();//Cada valor ocupa 2 registrosGyY=Wire.read()<<8|Wire.read();GyZ=Wire.read()<<8|Wire.read();//Calculo del angulo del GiroscopioGy[0]=GyX/G_R;Gy[1]=GyY/G_R;Gy[2]=GyZ/G_R;dt=(millis()-tiempo_prev)/1000.0;tiempo_prev=millis();//Aplicar el Filtro ComplementarioAngle[0]=0.98*(Angle[0]+Gy[0]*dt)+0.02*Acc[0];Angle[1]=0.98*(Angle[1]+Gy[1]*dt)+0.02*Acc[1];//Integración respecto del tiempo paras calcular el YAWAngle[2]=Angle[2]+Gy[2]*dt;//Mostrar los valores por consolavalores="90, "+String(Angle[0])+","+String(Angle[1])+","+String(Angle[2])+", -90";Serial.println(valores);delay(10);}
En este tutorial explico cómo montar un servidor web que soporta WebSockets en un ESP8266 y muestro como utilizar dicho servidor web para controlar un led RGB. La aplicación de WebSockets en IOT (internet de las cosas) nos permite crear dispositivos con una excelente experiencia de usuario, ya que el uso de WebSockets es una de las tecnologías web con menor latencia.
Lista de Materiales:
1) NodeMCU (ESP8266) 1) Led RGB (ánodo común) 3) Resistencia de 330 ohms.
WebSocketServer_STA:
#include<ESP8266WiFi.h>#include<WebSocketsServer.h>#include<ESP8266WebServer.h>#include<ESP8266mDNS.h>#defineLED_RED05// D1#defineLED_GREEN12// D6#defineLED_BLUE13// D7constchar*ssid="-----";constchar*password="-----";intcontconexion=0;Stringpagina="<html>""<head>""<script>""var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);""connection.onopen = function () { connection.send('Connect ' + new Date()); };""connection.onerror = function (error) { console.log('WebSocket Error ', error);};""connection.onmessage = function (e) { console.log('Server: ', e.data);};""function sendRGB() {"" var r = parseInt(document.getElementById('r').value).toString(16);"" var g = parseInt(document.getElementById('g').value).toString(16);"" var b = parseInt(document.getElementById('b').value).toString(16);"" if(r.length < 2) { r = '0' + r; }"" if(g.length < 2) { g = '0' + g; }"" if(b.length < 2) { b = '0' + b; }"" var rgb = '#'+r+g+b;"" console.log('RGB: ' + rgb);"" connection.send(rgb);""}""</script>""</head>""<body>""LED Control:<br/><br/>""R: <input id='r' type='range' min='0' max='255' step='1' value='0' oninput='sendRGB();'/><br/>""G: <input id='g' type='range' min='0' max='255' step='1' value='0' oninput='sendRGB();'/><br/>""B: <input id='b' type='range' min='0' max='255' step='1' value='0'oninput='sendRGB();'/><br/>""</body>""</html>";ESP8266WebServerserver=ESP8266WebServer(80);WebSocketsServerwebSocket=WebSocketsServer(81);voidwebSocketEvent(uint8_tnum,WStype_ttype,uint8_t*payload,size_tlength){switch(type){caseWStype_DISCONNECTED:Serial.printf("[%u] Disconnected!\n",num);break;caseWStype_CONNECTED:{IPAddressip=webSocket.remoteIP(num);Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n",num,ip[0],ip[1],ip[2],ip[3],payload);// send message to clientwebSocket.sendTXT(num,"Connected");}break;caseWStype_TEXT:Serial.printf("[%u] get Text: %s\n",num,payload);if(payload[0]=='#'){// we get RGB data// decode rgb datauint32_trgb=(uint32_t)strtol((constchar*)&payload[1],NULL,16);analogWrite(LED_RED,abs(255-(rgb>>16)&0xFF));analogWrite(LED_GREEN,abs(255-(rgb>>8)&0xFF));analogWrite(LED_BLUE,abs(255-(rgb>>0)&0xFF));}break;}}voidsetup(){Serial.begin(115200);Serial.println();WiFi.mode(WIFI_STA);//para que no inicie el SoftAP en el modo normalWiFi.begin(ssid,password);while(WiFi.status()!=WL_CONNECTEDandcontconexion<50){//Cuenta hasta 50 si no se puede conectar lo cancela++contconexion;delay(500);Serial.print(".");}if(contconexion<50){//para usar con ip fijaIPAddressIp(192,168,1,180);IPAddressGateway(192,168,1,1);IPAddressSubnet(255,255,255,0);WiFi.config(Ip,Gateway,Subnet);Serial.println("");Serial.println("WiFi conectado");Serial.println(WiFi.localIP());}else{Serial.println("");Serial.println("Error de conexion");}pinMode(LED_RED,OUTPUT);pinMode(LED_GREEN,OUTPUT);pinMode(LED_BLUE,OUTPUT);// start webSocket serverwebSocket.begin();webSocket.onEvent(webSocketEvent);if(MDNS.begin("esp8266")){Serial.println("MDNS responder started");}// handle indexserver.on("/",[](){server.send(200,"text/html",pagina);});server.begin();// Add service to MDNSMDNS.addService("http","tcp",80);MDNS.addService("ws","tcp",81);digitalWrite(LED_RED,1);// 1 = apagadodigitalWrite(LED_GREEN,1);digitalWrite(LED_BLUE,1);analogWriteRange(255);}voidloop(){webSocket.loop();server.handleClient();}
WebSocketServer_softAP:
#include<ESP8266WiFi.h>#include<WebSocketsServer.h>#include<ESP8266WebServer.h>#defineLED_RED05// D1#defineLED_GREEN12// D6#defineLED_BLUE13// D7constchar*ssid="RGB";constchar*password="asdfghjk";Stringpagina="<html>""<head>""<script>""var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);""connection.onopen = function () { connection.send('Connect ' + new Date()); };""connection.onerror = function (error) { console.log('WebSocket Error ', error);};""connection.onmessage = function (e) { console.log('Server: ', e.data);};""function sendRGB() {"" var r = parseInt(document.getElementById('r').value).toString(16);"" var g = parseInt(document.getElementById('g').value).toString(16);"" var b = parseInt(document.getElementById('b').value).toString(16);"" if(r.length < 2) { r = '0' + r; }"" if(g.length < 2) { g = '0' + g; }"" if(b.length < 2) { b = '0' + b; }"" var rgb = '#'+r+g+b;"" console.log('RGB: ' + rgb);"" connection.send(rgb);""}""</script>""</head>""<body>""LED Control:<br/><br/>""R: <input id='r' type='range' min='0' max='255' step='1' value='0' oninput='sendRGB();'/><br/>""G: <input id='g' type='range' min='0' max='255' step='1' value='0' oninput='sendRGB();'/><br/>""B: <input id='b' type='range' min='0' max='255' step='1' value='0'oninput='sendRGB();'/><br/>""</body>""</html>";ESP8266WebServerserver=ESP8266WebServer(80);WebSocketsServerwebSocket=WebSocketsServer(81);voidwebSocketEvent(uint8_tnum,WStype_ttype,uint8_t*payload,size_tlength){switch(type){caseWStype_DISCONNECTED:Serial.printf("[%u] Disconnected!\n",num);break;caseWStype_CONNECTED:{IPAddressip=webSocket.remoteIP(num);Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n",num,ip[0],ip[1],ip[2],ip[3],payload);// send message to clientwebSocket.sendTXT(num,"Connected");}break;caseWStype_TEXT:Serial.printf("[%u] get Text: %s\n",num,payload);if(payload[0]=='#'){// we get RGB data// decode rgb datauint32_trgb=(uint32_t)strtol((constchar*)&payload[1],NULL,16);analogWrite(LED_RED,abs(255-(rgb>>16)&0xFF));analogWrite(LED_GREEN,abs(255-(rgb>>8)&0xFF));analogWrite(LED_BLUE,abs(255-(rgb>>0)&0xFF));}break;}}voidsetup(){Serial.begin(115200);Serial.println();WiFi.softAP(ssid,password);IPAddressmyIP=WiFi.softAPIP();Serial.print("IP del access point: ");Serial.println(myIP);Serial.println("WebServer iniciado...");pinMode(LED_RED,OUTPUT);pinMode(LED_GREEN,OUTPUT);pinMode(LED_BLUE,OUTPUT);// start webSocket serverwebSocket.begin();webSocket.onEvent(webSocketEvent);// handle indexserver.on("/",[](){server.send(200,"text/html",pagina);});server.begin();digitalWrite(LED_RED,1);// 1 = apagadodigitalWrite(LED_GREEN,1);digitalWrite(LED_BLUE,1);analogWriteRange(255);}voidloop(){webSocket.loop();server.handleClient();}
En este tutorial de internet de las cosas (IOT) explico cómo carga de manera inalámbrica, nuestro sketch de Arduino en un ESP8266. Para realizar esto no valemos de un método llamado OTA (Over The Air / Por el Aire).
Tutorial21_OTA:
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
const char* ssid = "..........";
const char* password = "..........";
int contconexion = 0;
unsigned long previousMillis = 0;
int ledState = LOW;
void setup() {
Serial.begin(115200);
Serial.println("");
Serial.println("");
pinMode(2, OUTPUT);
digitalWrite(2, LOW);
WiFi.mode(WIFI_STA); //para que no inicie solamente en modo estación
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED and contconexion <50) { //Cuenta hasta 50 si no se puede conectar lo cancela
++contconexion;
Serial.print(".");
delay(500);
}
if (contconexion <50) {
//para usar con ip fija
IPAddress Ip(192,168,1,180);
IPAddress Gateway(192,168,1,1);
IPAddress Subnet(255,255,255,0);
WiFi.config(Ip, Gateway, Subnet);
Serial.println("");
Serial.println("WiFi conectado");
Serial.println(WiFi.localIP());
ArduinoOTA.onStart([]() {
Serial.println("Start");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
else {
Serial.println("");
Serial.println("Error de conexion");
}
}
void loop() {
ArduinoOTA.handle();
unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= 1000) {
previousMillis = currentMillis;
if (ledState == LOW)
ledState = HIGH; // Note that this switches the LED *off*
else
ledState = LOW; // Note that this switches the LED *on*
digitalWrite(2, ledState);
}
}
Este es un tutorial sobre internet de las cosas (IOT), en el que muestro un ejemplo de cómo usar multicast DNS (mDNS) en un ESP8266, esto nos permite asignarle un nombre de dominio en forma local a la IP que le asignó el DHCP a nuestro ESP8622.
Este es otro tutorial sobre internet de las cosas (IOT) usando MQTT en un ESP8266, en el que muestro como usar una script de Python que monitorea MQTT para enviar un email cuando la temperatura que lee el sensor LM35 supera un determinado valor.
tutorial19.py:
#!/usr/bin/env python 1
# -*- coding: utf-8 -*-
import paho.mqtt.client as mqtt
import sys
import smtplib
import mimetypes
global supero
supero = False
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Conectado - Codigo de resultado: "+str(rc))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("/#")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
lista = msg.topic.split("/")
global supero
if str(lista[2]) == "temperatura" and float(msg.payload) < 26:
supero = False
if supero == False and str(lista[2]) == "temperatura" and float(msg.payload) > 26:
supero = True
from email.MIMEText import MIMEText
emisor = "____@gmail.com"
receptor = "____@gmail.com"
print("Enviando Email...")
# Configuracion del mensaje
mensaje = MIMEText("La temperatura de la " + lista[1] + " es de: " + str(msg.payload))
mensaje['From']=emisor
mensaje['To']=receptor
mensaje['Subject']= "La temperatura de la " + lista[1] + " superó los 26ºC"
try:
# Nos conectamos al servidor SMTP de Gmail
serverSMTP = smtplib.SMTP('smtp.gmail.com',587)
serverSMTP.ehlo()
serverSMTP.starttls()
serverSMTP.ehlo()
serverSMTP.login(emisor,"__password__")
print("Conectado con exito al servidor SMTP...")
except:
print("Error al intentar conectarse al servidor SMTP...")
try:
# Enviamos el mensaje
serverSMTP.sendmail(emisor,receptor,mensaje.as_string())
# Cerramos la conexion
serverSMTP.close()
print("Email enviado con exito...")
except:
print("Error al intentar enviar el email...")
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
try:
client.connect("m13.cloudmqtt.com", 11948, 60)
except:
print("No se pudo conectar con el MQTT Broker...")
print("Cerrando...")
sys.exit()
client.username_pw_set("lqeamtbn", "vh0cU_Vcszxp")
try:
client.loop_forever()
except KeyboardInterrupt: #precionar Crtl + C para salir
print("Cerrando...")