esp8266

Hace algunas semanas compré por Ebay 3 módulos ESP8266 con la esperanza de poder utilizarlos en aplicaciones de IoT. Oí hablar de este módulo en Hackaday y solo leer de lo que era capaz no me lo pensé dos veces:

  • Procesador con puertos GPIO (General Purpose Input Output)
  • Wifi integrado
  • ¡Solo vale 4€! (Actualización: actualmente es posible encontrarlo por ~1.5€)

Por tanto, este chip es capaz de conectarse con la red Wifi de tu casa, guardar o escribir información en una base de datos, hacer las veces de servidor, leer información de sensores, activar otros actuadores (como relés)… ¡Las posibilidades son enormes! Es un pequeñísimo Arduino con Wifi integrado.

Los primeros meses desde la salida del ESP8266 fueron bastante oscuros, ya que no se tenía apenas información, no había un SDK oficial y lo que había estaba en chino.

Pero recientemente la comunidad ESP8266 ha empezado a florecer ofreciendo diferentes vertientes por las que poder configurarlo. Yo me voy a centrar únicamente en la que utiliza el firmware NodeMCU.

Este firmware hace correr un compilador de LUA en el procesador, de manera que podremos escribir los programas que queramos correr en este mismo lenguaje.

Para poder instalar el firmware solo hay que conectar un FTDI (USB-UART converter) a nuestro ESP8266 de la siguiente manera:

esp826

Advertir que es obligatorio alimentar el ESP8266 a 3.3V, ya que por lo contrario quemaréis el chip.

Para entrar en modo de escritura de firmware hay que conectar el pin GPIO0 a masa. Yo he utilizado un jumper con que puedo conectar y desconectar fácilmente el pin.

Cuando tengamos este montaje hecho, necesitaremos descargar el grabador del firmware, que lo podemos encontrar también en el GitHub de NodeMCU.

Una vez descargado, abrimos el flasher:

ESP8266-flasher-folder

Con lo que nos aperecerá la siguiente aplicación:

ESP8266-flasher

Automáticamente cuando conectemos nuestro FTDI cambiará el puerto COM. Si no te sucede asegúrate de tener los drivers para tu FTDI instalados.

Flashear el ESP8266 con NodeMCU es tan sencillo como darle al botón Flash(F), y se pondrá a escribir el firmware en la memoria del procesador. Como se puede ver en la pestaña Config hace cuatro escrituras distintas, siendo la tercera la que más tiempo lleva. Cuando termine el FTDI se desconectará del equipo y aparecerá un símbolo verde abajo en la esquina izquierda. En mi caso dicho símbolo no ha aparecido pero el flasheo ha sido correcto igualmente.

Ahora que ya tenemos el firmware subido, tenemos que cargar nuestro programa. Para ello utilizaremos el programa LUAUploader de Hari Wiguna. Con esta aplicación especialmente desarrollada para el ESP8266 podremos subir nuestros programas de una manera muy sencilla.

Cabe destacar que la corriente que entrega un puerto USB del ordenador no es suficiente para iniciar el módulo de Wi-Fi, así que a partir de este momento es necesario alimentar el ESP8266 con una fuente externa. En mi caso he utilizado dos pilas LR03 (las pequeñas de los mandos) para obtener 3V. Recuerda que no sirven los 5V o el chip se quemará. Cuando conectes la fuente externa desconecta el pin de 3.3V del FTDI para evitar problemas, pero deja el pin de GND para unificar las masas del FTDI y de la fuente externa.

Una vez lo tengamos todo listo podemos pasar a lo divertido. Abrimos el LUAUploader y nos aparecerá la siguiente pantalla:

LuaUploaderPara comprobar que el firmware ha sido grabado correctamente iremos a la pestaña “Immediate” y escribiremos un pequeño chunk de prueba (un chunk es cada trozo de código que Lua puede ejecutar)

dummy = 5
print(dummy)

Si el compilador nos devuelve 5, quiere decir que el firmware ha sido escrito correctamente:

LUAtest

 

Para poder conectarnos a nuestra red Wi-Fi es necesario introducir el siguiente chunk:

ip = wifi.sta.getip()
print(ip)
--nos devolverá nil
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","password")
ip = wifi.sta.getip()
print(ip)
--nos devolverá la IP asignada

Hay veces que después de intentar conectarse a la red Wi-Fi continúa devolviendo nil. Probad a imprimir de nuevo la dirección IP con:

ip = wifi.sta.getip()
print(ip)

Ahora, para hacer una pequeña prueba, podemos montar un servidor en el propio ESP8266 con el chunk:

srv=net.createServer(net.TCP) 
srv:listen(80,function(conn) 
   conn:on("receive",function(conn,payload) 
      print(payload) 
      conn:send("<h1>Hola Ruben!</h1>")
   end) 
   conn:on("sent",function(conn) conn:close() end)
end)

Una vez subido, si accedemos a la dirección del ESP8266 nos devolverá la página con un heading y el texto “Hola Ruben!” (para poder escribir acentos hay que utilizar los códigos ASCII en HTML y utilizar la columna HTML Número: é = &#233;)

web

Podéis encontrar muchos más ejemplos en el GitHub de NodeMCU.

Por fin podemos hablar de el primer proyecto más o menos serio que he hecho con ESP8266 y esta vez se trata de un medidor de nivel agua con los datos guardados en la nube, de manera que sean accesibles desde cualquier sitio.

Principios de funcionamiento

Para empezar, imaginemos que tenemos una cisterna donde tenemos agua. Primero debemos asumir que las medidas de la cisterna se mantienen constantes en todo momento, esto es, ancho, largo y alto. Para conocer el volumen de agua que reside en el interior tan solo necesitaremos saber cuál es la altura del agua, ya que es la única variable del sistema.

Con esto claro, necesitamos un mecanismo que sea capaz de medir a qué altura de encuentra el agua. Mi idea ha sido utilizar un sensor ultrasónico, cuyo funcionamiento es el siguiente:

  1. El altavoz emite un sonido de frecuencia ultrasónica (imperceptible al oido humano)
  2. Se pone un cronómetro en marcha.
  3. El micrófono recibe la señal que había sido emitida por el altavoz.
  4. Se para el cronómetro que se había puesto en marcha al emitir el sonido
  5. Se calcula sabiendo la velocidad del sonido, la distancia a la que está el objeto sobre el cual ha rebotado la pulsación ultrasónica.

hc

Materiales

Para empezar los materiales que he utilizado son:

  • ESP-01 (ESP8266)
  • Fuente de alimentación de 3.3V
  • Fuente de alimentación de 5V
  • Programador FTDI
  • Sensor ultrasónico HC-SR04

Montaje hardware

El módulo ultrasónico HC-SR04 tiene 4 pines:

  1. Vdd: debemos conectar 5V. No podemos conectar directamente los 3.3V con los que alimentamos el ESP-01. Una lástima.
  2. Trig: es el encargado de dar la orden al altavoz de que emita el pulso.
  3. Echo: es el micrófono que espera el rebote del pulso emitido.
  4. GND: es importante unificar la masa del ESP-01 con la propia de la fuente de 5V para evitar problemas de grounding.

En mi caso he utilizado los 5V que tiene disponible el Arduino, aunque es algo bastante engorroso. Para la versión final, utilizaré un convertidor lineal (para conseguir los 3.3V) y una sola alimentación.

La conexión entre el ESP-01 y el HC-SR04 es extremadamente sencilla:

  1. GPIO0 – Echo
  2. GPIO2 – Trig

Software

Para hacer funcionar el HC-SR04 con NodeMCU, he utilizado el código que ha subido sza2 en GitHub modificándolo un poco.

Se trata de dos archivos: init.lua y hcsr04.lua. Cabe destacar que el ESP8266 ejecuta init.lua en cuanto se enchufa. La base de su funcionamiento reside en el uso de la función tmr.alarm(), que ejecuta una función cada intervalo de tiempo que nosotros definamos. En este caso, la función de medir distancia.

tmr.alarm(0, 3600000, 1, function() send(device.measure()) end)

El código de sza2 envía vía serie el resultado cada 0,5s, pero podemos configurarlo como nosotros queramos. Solo recordar que hay que introducir el tiempo en milisegundos.

En el código original se imprime por pantalla el resultado, pero como hemos dicho anteriormente nuestro objetivo es que la lectura sea subida a un servidor para poder ver el nivel de agua desde cualquier sitio.

Para ello he utilizado una plataforma llamada ThingSpeak, que sirve precisamente para esto. Su funcionamiento es muy sencillo. La información se guarda en los que ThingSpeak llama canales. En cada canal podemos añadir hasta 8 variables que queramos monitorizar, y la manera en que podemos guardar nuevos datos es haciendo una petición HTTP con el método GET. Las variables que tenemos que incluir en la petición HTTP es el código del canal (para saber dónde hay que guardar la información), el número del campo a guardar seguido del valor.

conn:send("GET /update?key=CodigoAPI&field1="..value1.." &amp;field2="..value2.."HTTP/1.1\r\n")

De esta manera quedará registrado automáticamente en ThingSpeak, como podemos ver en este applet:

También decir que el HC-SR04 devuelve valores contiguos que difieren unos de otros por motivos obvios, por lo que he modificado el código para que devuelva el valor medio de 500 medidas.

El código completo del archivo init.lua es:

dofile("hcsr04.lua")
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","pass")
print(wifi.sta.getip())
device = hcsr04.init()
print("Loading...")
 
function send(value)
  if value ~= -1 then
    print(value)
    conn=net.createConnection(net.TCP, 0)
    conn:on("receive", function(conn, payload) print(payload) end)
    conn:connect(80,'184.106.153.149')
    conn:send("GET /update?key=CodigoAPI&field1="..value.." HTTP/1.1\r\n")
    conn:send("Host: api.thingspeak.com\r\n")
    conn:send("Accept: */*\r\n")
    conn:send("User-Agent: Mozilla/4.0 (compatible;ESP8266; Windows NT 5.1)\r\n")
    conn:send("\r\n")
  end
end
 
tmr.alarm(0, 60000, 1, function() send(device.measure()) end)
hcsr04 = {};
 
function hcsr04.init(pin_trig, pin_echo)
	local self = {}
	self.time_start = 0
	self.time_end = 0
	self.trig = pin_trig or 4
	self.echo = pin_echo or 3
	gpio.mode(self.trig, gpio.OUTPUT)
	gpio.mode(self.echo, gpio.INT)
 
	function self.echo_cb(level)
		if level == 1 then
			self.time_start = tmr.now()
			gpio.trig(self.echo, "down")
		else
			self.time_end = tmr.now()
		end
	end
 
	function self.measure()
          medidas = 500
          mean = 0
		for i=1,medidas,1 do
			gpio.trig(self.echo, "up", self.echo_cb)
			gpio.write(self.trig, gpio.HIGH)
			tmr.delay(100)
			gpio.write(self.trig, gpio.LOW)
			tmr.delay(100000)
			if (self.time_end - self.time_start) < 0 then
				return -1
			end
			mean = mean + (self.time_end - self.time_start) / 58
		end
		print(mean/medidas)
		return mean/medidas
	end
	return self
end