sábado, 19 de diciembre de 2015

Reiniciar NodeMCU en Windows


Recientemente, al estar trasteando con el puerto serie de ESP8266 cargué el siguiente programa:
val = 65
while 1 do
 uart.write(0,string.char(val),"\r\n")
 val = val + 1
 if val > 90 then
    val = 65
 end
 tmr.delay(1000000)
end

Este programa lo que hace es, simplemente, ir mostrando por el puerto serie las letras del abecedario. Mi sorpresa fue que, a partir de ese momento, el módulo dejó de responder.

No podía realizar un reset por software, no respondía a ningún comando, después de un reset por hardware (botón de reset) se reiniciaba el programa y seguía mostrando las letras del abecedario.

Al mantener pulsado el botón de FLASH al alimentar el módulo, éste parecía entrar en modo espera y solo después de pulsar reset el módulo revivía, volviendo a ejecutar el dichoso programita.

La solución la encontré cargando nuevamente el firmware de NodeMCU. Para ello, nos descargamos de github el último firmware y lo descomprimimos en una carpeta de nuestro ordenador. A continuación, vamos a la carpeta win32 o win64 dependiendo del sistema operativo de tu PC, abrimos la carpera Release y ejecutamos el programa ESP8266Flasher.exe

Antes de conectar el módulo al puerto serie, debemos conectar el GPIO0 (Pin D3) a masa que equivale a pulsar el botón de FLASH.

nodemcudevkit_v1-0_io.jpg

Automáticamente, si tenemos instalado los driver del puerto, detectará nuestro módulo y cambiará el número del puerto COM

flasher01.png

A continuación hacemos click en el botón Flash(F) y esperamos a que termine la operación.

flasher02.png

Una vez que haya terminado, quitamos la conexión del pin GPIO0 a masa, pulsamos el botón de RESET de nuestro módulo NodeMCU y ya podemos seguir trabajando con nuestro ESP8266.

Communication with MCU...
Got answer! AutoDetect firmware...
NodeMCU firmware detected.
=node.heap()
21456

Al realizar un reset (node.restart()) veo que la información que me muestra es:
NodeMCU 0.9.5 build 20150318  powered by Lua 5.1.4
y antes de reiniciar el firmware la versión era la 0.9.6. Además no puedo utilizar el módulo dht
init.lua:12: attempt to index global 'dht' (a nil value)
Voy a la web de Github y veo que, efectivamente, la última versión es la 0.9.6. Descargo la versión float (no sé que diferencia hay con la otra versión).
Volvemos a ejecutar ESP8266Flasher.exe y seleccionamos la pestaña Config. En dicha pestaña, sustituir INTERNAL://NODEMCU por el firmware que hemos descargado:

Captura.PNG

Por último, vamos a la pestaña Operation y, como en el caso anterior, hacemos click en Flash(F)
Una vez que el proceso ha terminado, comprobamos la versión del firmware:
NodeMCU 0.9.6 build 20150704  powered by Lua 5.1.4
y ya podemos utilizar el módulo dht11
También podemos crear nuestro propio firmware. Para ello, vamos a la web NodeMCU custom build y, después de introducir nuestro correo electrónico ya que es allí donde nos enviarán el enlace para la descarga del firmware,  seleccionamos aquellos módulos que nos interesa incluir en el firmware. Yo he seleccionado todos los documentados:

compilación.PNG

A continuación hacemos click en Start Your build y nos aparecerá un mensaje indicándonos la compilación elegida.
compilación_01.PNG
compilación_02.PNG

Una vez que descarguemos la compilación, ya solo nos resta repetir el proceso anterior de carga del firmware.

NodeMCU custom build by frightanic.com
branch: master
commit: 93421f2702fb02ce169f82f96be7f2a8865511e1
SSL: false
modules: node,file,gpio,wifi,net,pwm,i2c,spi,tmr,adc,uart,ow,bit,mqtt,u8g,ws2812,cjson,crypto,rc,dht,ws2801
build  built on: 2015-12-19 15:57
powered by Lua 5.1.4 on SDK 1.4.0
 

Fuentes:
Cargar un nuevo firmware:
Construir nuestro propio firmware:

Visualizando la temperatura en Thingspeak


ThingSpeak es una plataforma Open Source para conectar productos y servicios al Internet de las Cosas (IoT). Permite a los desarrolladores interactuar con los dispositivos utilizando tecnologías Web estándar.

Un ejemplo muy sencillo de lo que puede conseguirse con ThingSpeak es el acceso a logs de parámetros como temperatura, humedad, estado de gpios, estado de carga de CPU... Aunque cualquier dato/información es perfectamente factible de ser accedida a través de esta plataforma.

Lo primero es darnos de alta. Entramos en la página y hacemos click en Sing Up:


ThingSpeak 01.PNG
Introducimos un ID, nuestro email y una clave de acceso. Aceptamos las condiciones del servicio y hacemos click en Create Acount

Una vez creada nuestra cuenta tenemos que crear nuestro primer canal. Para ello, hacemos click en New Channel:

ThinkSpeak02.PNG


A continuación rellenamos una serie de datos como el nombre del canal, su descripción, los campos que aparecerán en las gráficas etc. Un dato importante es si hacemos público el acceso para que cualquiera pueda acceder a los datos.

Otro dato importante son los campos (Field). Por cada campo que creamos nos aparecerá una gráfica. Yo he creado dos, uno para la temperatura y otro para la humedad.

Hacemos click en Save Channel y ya tenemos creado nuestro primer canal:
TyingSpeak03.PNG
Una vez creado nuestro primer canal, nos aparecerá algo así:

ThinkSpeak05.PNG


Un dato muy importante es la API Keys. Estas son claves que necesitaremos incluir en nuestra programación para enlazar con ThinkSpeak. En nuestro caso, utilizaremos la clave de escritura Write API Key

Empezamos a programar nuestro módulo. El esquema de conexión es el mismo que en el artículo Practicando con el sensor DHT11. El código completo es el siguiente:
--ThingSpeak - IoT
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","PASSWORD") -- SSID y PASSWORD de vuestra wifi
print(wifi.sta.getip())
tmr.delay(5000)
-- Declaración de variables
pin = 3
humi=0
temp=0
Write_Key = "N04IUK8Z1LUC0BF1" -- API Key del canal en ThinkSpeak
--Función leer sensor DH11
function LeerDHT11()
status,temp,humi = dht.read11(pin)
if( status == dht.OK ) then
 print("DHT Temperatura: "..temp.."ºC - Humedad: "..humi.."%")
 elseif( status == dht.ERROR_CHECKSUM ) then
 print( "DHT Checksum error." );
 elseif( status == dht.ERROR_TIMEOUT ) then
 print( "DHT Time out." );
 end
end
-- Enviamos datos a https://api.thingspeak.com
function sendTS(humi,temp)
 conn = nil
 conn = net.createConnection(net.TCP, 0)
 conn:on("receive",
    function(conn, payload)
     success = true
     print(payload)
    end)
 conn:on("connection",
    function(conn, payload)
     print("Connectado")
    -- Enviamos la Write_Key
     conn:send("GET /update?key="..Write_Key)
    -- Enviamos los datos de temperatura y humedad
    conn:send("&field1="..temp.."&field2="..humi)
    conn:send("HTTP/1.1\r\n\Host: api.thingspeak.com\r\nAccept: */*\r\nUser-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n\r\n")
    end)
 conn:on("disconnection",
    function(conn, payload)
     print("Desconectado")
    end)
 conn:connect(80,"184.106.153.149") -- IP pública de thingspeak
end
-- Ejecuta la lectura por primera vez y lo envía a thingspeak
LeerDHT11()
sendTS(humi,temp)
-- Timer que realiza la lectura cada 10 segundos
tmr.alarm(1,10000, 1, function() LeerDHT11() sendTS(humi,temp)end)


Toda la primera parte (conexión a la red wifi, lectura de la temperatura y humedad) es igual al artículo Creando un servidor web. La parte que nos interesa es la conexión con thingspeak. Para más información consultar su API.


Fuentes:

viernes, 18 de diciembre de 2015

Creando un servidor WEB


Aprovechando el circuito utilizado en el artículo “Practicando con el sensor DHT11” vamos a activar un servidor web para que muestre la información de temperatura y humedad a cualquier navegador que se conecte a nuestro NodeMCU a través de Wifi.

El código completo es el siguiente:

--Nos conectamos a la red Wifi

wifi.setmode(wifi.STATION)
wifi.sta.config("WLAN_E17C","ced0ccb58c84e525e35f")
print(wifi.sta.getip())

tmr.delay(5000)

-- Declaración de variables
pin = 5
humi=0
temp=0
--Función leer sensor DH11
function LeerDHT11()
status,temp,humi = dht.read11(pin)
if( status == dht.OK ) then
 print("DHT Temperatura: "..temp.."ºC - Humedad: "..humi.." %")
 elseif( status == dht.ERROR_CHECKSUM ) then
 print( "DHT Checksum error." );
 elseif( status == dht.ERROR_TIMEOUT ) then
 print( "DHT Time out." );
 end
end
-- Ejecuta la lectura por primera vez
LeerDHT11()
-- Timer que lee periódicamente la temperatura y la humedad
tmr.alarm(1,5000, 1, function() LeerDHT11() end)
-- Activamos el servidor WEB
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
 conn:on("receive",function(conn,payload)
 -- Enviamos la página web
 conn:send("HTTP/1.1 200 OK\n\n")
 conn:send("<!DOCTYPE HTML>")
 conn:send("<html><head><meta http-equiv=\"refresh\" content=\"5\"></head><body>")
 conn:send("<h2>Estación Meteorológica con sensor DHT11</h2>")
 conn:send("Temperatura: "..temp.." ºC<br>")
 conn:send("Humedad : "..humi.." %<br>")
 conn:send("</body></html>")
 conn:on("sent",function(conn) conn:close() end)
 end)
end)


Lo primero que hacemos es conectar el módulo a nuestra red wifi (para más información consultar la API módulo wifi):
--Nos conectamos a la red wifi
wifi.setmode(wifi.STATION) -- Modo Station
wifi.sta.config("SSID","password") -- Nos conectamos al AP
print(wifi.sta.getip()) -- Mostramos la IP por el puerto serie
tmr.delay(5000) -- pausa durante 5 segundos

A continuación declaramos las variables que vamos a utilizar:
pin = 5 -- Puerto de Datos de nuestro sensor DHT11
humi=0 -- Variable Humedad
temp=0 -- Variable Temperatura

Definimos la función que lee el sensor DHT11:
--Función leer sensor DH11
function LeerDHT11()
status,temp,humi = dht.read11(pin) -- Lee los datos del sensor
if( status == dht.OK ) then -- ¿Lectura correcta?
 -- Muestra por el puerto serie la temperatura y la humedad
 print("DHT Temperatura: "..temp.."ºC - Humedad: "..humi.." %")
 elseif( status == dht.ERROR_CHECKSUM ) then -- Lectura errónea
 -- Muestra por el puerto serie los códigos de error
 print( "DHT Checksum error." );
 elseif( status == dht.ERROR_TIMEOUT ) then
 print( "DHT Time out." );
 end
end
LeerDHT11() -- Primera lectura de la temperatura y humedad

A continuación configuramos el timer 1 la alarma para que cada 5 segundos haga una lectura de la temperatura y la humedad:
-- Timer que lee periódicamente la temperatura y la humedad
tmr.alarm(1,5000, 1, function() LeerDHT11() end)

Por último, creamos un servidor web que nos mostrará en la pantalla de nuestro navegador la temperatura y la humedad (para más información consultar la API Módulo net).
-- Activamos el servidor WEB
srv=net.createServer(net.TCP) --Creamos el servidor
srv:listen(80,function(conn) -- Escuchamos el puerto 80 (Http)
 conn:on("receive",function(conn,payload)
 -- Enviamos la página web
 conn:send("HTTP/1.1 200 OK\n\n")
 conn:send("<!DOCTYPE HTML>")
 conn:send("<html><head><meta http-equiv=\"refresh\" content=\"5\"></head><body>")
 conn:send("<h2>Estación Meteorológica con sensor DHT11</h2>")
 conn:send("Temperatura: "..temp.." ºC<br>")
 conn:send("Humedad : "..humi.." %<br>")
 conn:send("</body></html>")
 conn:on("sent",function(conn) conn:close() end)
 end)
end)

En nuestro navegador, si indicamos la url http://direcciónIP nos mostrará una pantalla como esta:

Captura.PNG


Explicación del código HTML:

HTTP/1.1 200 OK\n\n
Código de estado HTTP: Respuesta estándar para peticiones correctas
<meta http-equiv=\"refresh\" content=\"5\">
Hace que la página se refresque cada 5 segundos en nuestro navegador (o sea, cada 5 segundos nuestro navegador hará una petición http a la dirección IP de nuestro módulo) - Ver http://aprende-web.net/html/html8_2.php

\”
conn:send() envía todo lo que está entre los paréntesis. Si hay un string, tenemos que indicarlo entre comillas. Si en el código HTML hay unas comillas, para enviar correctamente el código de la página, hay que anteponer \

..
En código HTML eso es concatenar, es decir unir dos string.


Nota:
Existe otra forma de mostrar una página web y es separando el código init.lua del código correspondiente al servidor. De esta forma, podemos variar el código de la página web sin tener que modificar el fichero init.lua.

Para ello, creamos un fichero denominado server.lua con el siguiente código:
-- Activamos el servidor WEB
srv=net.createServer(net.TCP) --Creamos el servidor
srv:listen(80,function(conn) -- Escuchamos el puerto 80 (Http)
 conn:on("receive",function(conn,payload)
 -- Enviamos la página web
 conn:send("HTTP/1.1 200 OK\n\n")
 conn:send("<!DOCTYPE HTML>")
 conn:send("<html><head><meta http-equiv=\"refresh\" content=\"5\"></head><body>")
 conn:send("<h2>Estación Meteorológica con sensor DHT11</h2>")
 conn:send("Temperatura: "..temp.." ºC<br>")
 conn:send("Humedad : "..humi.." %<br>")
 conn:send("</body></html>")
 conn:on("sent",function(conn) conn:close() end)
 end)
end)

Este código es sustituido en el programa init.lua por:
dofile("server.lua")

dofile() abre el fichero con el nombre dado y ejecuta su contenido.


Fuentes: