Presentación

Presentación

Mi trabajo y mis hobbies están muy relacionados con el uso de dispositivos electrónicos de todo tipo, así que suelo apuntarme en ficheros Le...

sábado, 14 de septiembre de 2019

ESP8266

Existe numerosa bibliografía sobre este magnífico chip que es un micro con WiFi. Lo podemos encontrar formando parte de otras tarjetas como nodemcu o casi sin nada, como ESP-12E. Pongo aquí algunas referencias y resúmenes de cosas que son interesantes al iniciar un proyecto y que no están del todo bien descritas en otros documentos.

Lo normal es usar Arduino. Los detalles de implementación de la librería de ESP8266 para Arduino están en https://github.com/esp8266/Arduino y la documentación en https://arduino-esp8266.readthedocs.io/en/2.5.2/

Pines

Lamentablemente nos encontramos con un número limitado de entradas y salidas de propósito general y varias de ellas tienen además funciones compartidas. Esto es un resumen de lo imprescindible sobre pines (Dx es lo que aparece en la serigrafía en GPIOx es el nombre real del pin en el micro):

D0 GPIO16 normal, pero saca un 1 al arrancar, también puede que esté conectado a RST (según tarjeta) para volver de deep_sleep
D1 GPIO5 SCL (I2C)
D2 GPIO4 SDC (I2C)
D3 GPIO0 normal, pero esta pull-up a VCC para arranque normal (si se pone a 0 en inicio, entra en modo programación flash)
D4 GPIO2 normal, pero está pull-up a VCC para arranque normal (si está a 0 inicialmente, no arranca) Está conectado al LED (activo nivel bajo)
D5 GPIO14 normal
D6 GPIO12 normal
D7 GPIO13 normal
D8 GPIO15 normal, pero está pull-down a GND para arranque normal (si se pone a 1 en inicio arranca desde SD)
GPIO6-11 FLASH, no usar
TX GPIO1 Serie, no usar si se va a programar on board. Se conecta al RX del USB-Serie externo, si se usa uno externo.
RX GPIO3 Serie, no usar si se va a programar on board. Se conecta al TX del USB-Serie externo, si se usa uno externo.
RST reset a nivel bajo
CH_PC/ENABLE Habilita el micro.
A0/ADC0 Analog Input rango 0.0-1.0 voltios, salvo que se use una tarjeta con divisor, que son casi todas (en ese caso 0-3.3V).

Aparte del GPIO16 parece que el 1,3,9 y 10 también están en alto, pero estos no se usan normalmente así que da igual.
Parece que el resto pueden estar a nivel bajo, salvo GPIO4 y 5 que serían los más seguros para conectar Relés (aunque ya están ocupados con el I2C).
GPIO12-14 Pueden utilizarse como interfaz SPI junto con GPIO15 que hace de CS.
Todos admiten interrupciones salvo el GPIO16
Todos admiten PWM.
En Arduino los pines se referencian con un entero igual al número de GPIO (Las etiquetas Dx de algunas tarjetas no sirven).

Información adicional sobre tarjetas y pines:
https://randomnerdtutorials.com/esp8266-pinout-reference-gpios/
https://www.forward.com.au/pfod/ESP8266/GPIOpins/index.html

Modos de bajo consumo

El chip consume unos 10-20 mA en modo normal y unos 70 mA mientras usa la WiFi. Por defecto, el chip ahorra energía si no estamos usando la WiFi, entrando en modo ahorro automáticamente (MODEM_SLEEP) por lo que si no hay requisitos fuertes de consumo, el chip consumirá poco. Los modos de bajo consumo están disponibles sólo en modo estación; en modo Access Point, debe estar siempre dando servicio y no puede consumir poco. A continuación los diferentes modos, cómo activarlos y su consumo.

El modo por defecto en el que se pone el chip se fijan con  WiFi.setSleepMode() por defecto es WIFI_MODEM_SLEEP.

Nota: Los consumos que se citan suponen el chip sin apenas componentes externos (Ej. ESP-12). En placas como nodemcu, entre otras, hay circuitos como el USB a serie y el regulador de 3.3 V que consumen entre 2 y 10 mA dependiendo de la versión.

modem_sleep

Es el modo por defecto y entra en él de forma automática. No obstante, entra y sale de este modo continuamente para mantener el contacto con la estación y se producen picos esporádicos de consumo que se pueden evitar si forzamos la parada total de WiFi y apagamos el modem completamente. Esto se realiza con una llamada a WiFi.forceSleepBegin() que nos desconectará completamente y apagará el modem (será necesario reconectar de nuevo al volver de sleep).
En este modo el chip trabaja normalmente y el consumo es de unos 20 mA.
Para volver a usar la WiFi debemos hacer WiFi.forceSleepWake() y volver a conectar al AP.
Nota: forceSleepBegin() puede tardar un tiempo en ejecutarse y en algunos casos produce un reset por soft watchdog timeout. Esto ocurre especialmente en aquellos casos en los que no se ha podido establecer conexión con un cliente TCPIP, por ejemplo. En estos casos, resulta conveniente añadir un delay de al menos unos 200 ms antes de llamar a forceSleepBegin (si todas las conexiones que se hicieran durante la sesión han ido bien, no es necesario añadir este tiempo). También se podría deshabilitar el soft watchdog antes de llamar a esta función y habilitarlo cuando acaba.
Este modo es interesante forzarlo cuando se realizan conexiones esporádicas a un servidor, pero la CPU debe estar haciendo cosas entre conexión y conexión. Por ejemplo, una estación de sensores que recoge datos cada pocos segundos y envía el resumen cada minuto.

light_sleep

En este modo, el chip entra en suspensión y no hace nada. El consumo cae a menos de 1 mA. Este modo resulta interesante pues aunque todo está completamente parado, no es necesario desconectarse del AP y volverse a conectar, pues sigue conectado (salvo que pase mucho tiempo y el router nos desconecte). Lamentablemente tiene dos características que impiden que se pueda explotar todo el potencial de este modo:
  • No podemos forzar entrar en este modo. En nuestro programa habremos puesto  WiFi.setSleepMode(WIFI_LIGHT_SLEEP,1) para que light_sleep sea el modo por defecto, pero no podemos controlar cuando el chip decidirá entrar en ese modo. Normalmente, si ponemos un loop con un delay de al menos 100 ms entrará en light_sleep, pero con menos es posible que no entre.
  • No podemos decidir internamente cuándo volver. El modo light_sleep es para siempre y la única forma de seguir con la ejecución es habilitar algún pin externo y producir externamente un pulso. Se puede hacer un sencillo circuito RC conectado a un pin para que se active a intervalos más o menos regulares (se necesita otro pin para resetear el circuito RC). La instrucción para configurar este pin especial es gpio_pin_wakeup_enable(GPIO_ID_PIN(gpiopin), GPIO_PIN_INTR_LOLEVEL). Otra forma de salir es mediante un reset, pero entonces mejor usamos el siguiente modo de bajo consumo.

deep_sleep

En cualquier instante podemos entrar en este modo con ESP.deepSleep(microseconds, mode). En este modo el consumo es de unos pocos uA, es decir, prácticamente nada pues todo está parado y ningún dispositivo interno recibe alimentación salvo un contador que es el que sirve para volver.
Lo único que sucede cuando han pasado los microsegundos especificados, es que se activa a 0 la salida GPIO16, nada más. Si esta salida la conectamos al reset (RST) entonces el chip se reinicia. Es decir, este modo sirve para apagar el chip y poderlo encender más adelante, comenzando el programa desde el principio.
Este modo es especialmente interesante en aquellas aplicaciones donde el chip se despierta a intervalos regulares, realiza cualquier cosa (siempre la misma) y vuelve a dormir, ahorrando mucha energía entre acción y acción. Ejemplo, un sensor de temperatura que envía sus datos cada minuto.

Conexión WiFi óptima

Velocidad

Si se usan los modos modem_sleep y deep_sleep interesa conectar con el AP lo más rápido posible. Se puede obtener tiempos de conexión-envío-desconexión del orden de 250 ms. Para ello hay que configurar todos los parámetros del router, con la instrucción WiFi.config(...) donde se especifica una IP estática, puerta de enlace, máscara, servidor de nombres, etc.
El retraso en la conexión se puede reducir incluso más si incluimos la información del canal del AP y su MAC en los argumentos de WiFi.begin(). Como estos dos parámetros no se conocen, se puede hacer una primera conexión normal, averiguar el canal con WiFi.channel() y la MAC con WiFi.BSSID(). Estos valores se pueden guardar en la memoria RTC, pues se conserva incluso en deep_sleep. Si con estos valores no conecta, se puede intentar conectar sin ellos y obtener los nuevos valores de canal, si hubiera cambiado.

Persistencia

Por defecto, las librerías guardan varios de los parámetros de conexión WiFi en Flash para poder reconectar rápidamente. Esto significa que cada vez que el chip se conecta a internet escribimos en la Flash, con lo que, dependiendo de la aplicación, la FLASH podría degradarse en relativamente poco tiempo (Ejemplo, un chip que se conecta cada minuto para enviar algo). Afortunadamente podemos deshabilitar esta escritura haciendo:

  WiFi.persistent(false);

Si guardamos nosotros los datos tal como se pone en el apartado de velocidad, no hace falta tener habilitado esto para obtener el máximo rendimiento y además conservaremos la FLASH por mucho tiempo.

Conexión con un cliente y lectura de datos

Dependiendo de la versión de librería de ESP8266 para Arduino, las funciones WiFiClient.connected() y WiFiClient.available() han tenido diferentes significados y funciones. En la última, que además ofrece un rendimiento óptimo, las cosas disponibles pueden aparecer incluso después de cerrada la conexión. Con todo esto, la forma adecuada de conectar y leer algo sería la siguiente:

WiFiClient TCPcli;
if (TCPcli.connect("xxx.yy.com", 80))
{
  TCPcli.print("GET / HTTP/1.1\r\n");
  TCPcli.print("Host: xxx.yy.com\r\n");
  TCPcli.print("Connection: close\r\n\r\n");
  while(TCPcli.connected() || TCPcli.available()) // ambos
  {
    if (TCPcli.available()>0) {
      char c = TCPcli.read();
      ...
    }
  }
}
TCPcli.stop();