guia-de-programacion-del-esp8266-en-entorno-arduino

Guía de programación del ESP8266 en entorno Arduino

En esta entrada entramos en detalle en la programación del ESP8266. Y vamos a comenzar con una guía de programación del ESP8266 en el entorno Arduino viendo las funciones principales y comparándolas con sus equivalentes en un Arduino convencional.

En la entrada anterior de la serie sobre el ESP8266 vimos cómo configurar el entorno de Arduino para programar ESP8266. Ya adelantamos que, pese a ser muy parecidos, existen diferencias entre programar un Arduino y un ESP8266.

Por otro lado, también hemos visto los detalles del hardware del ESP8266, comparándolos con los de Arduino. En esta entrada asumiremos que tenemos claras las diferencias en cuanto a hardware. En caso de duda, referiros a la entrada anterior.

Por tanto, comenzamos con la guía de programación del ESP8266, las distintas funciones del ESP8266 y las similitudes y diferencias con la programación de un Arduino.

Funciones de tiempo

Millis y Delay

Las funciones de tiempo millis() y micros() funcionan igual que en Arduino. También funcionan delay() y delayMicroseconds(), con las consideraciones anteriores que veremos a continuación sobre Yielding.

Yielding

Esta es una de las diferencias más importantes respecto a un Arduino. El ESP8266 ejecuta muchas funciones relacionadas con mantener y gestionar la conexión WiFI y la pila TC/IP. Si no ejecuta estas acciones tendremos problemas de comunicación, bloqueos y reinicios.

Por este motivo se recomienda evitar bloqueos superiores a 100-200ms en nuestro código. De hecho, si pasan 3 segundos sin llamar a estas funciones el WatchDog reiniciará el ESP8266. Y aún con el WatchDog desactivado, con una espera mayor de 8 segundos el ESP8266 se reiniciará él solito.

Las funciones del ESP8266 se ejecutan al final de cada loop, al llamar a la función delay(), o con la función desarrollada para el ESP8266 en Arduino yield(), que podríamos considerar equivalente a delay(0).

En resumen, si tenéis un proceso que requiere más de 100-200ms, deberéis modificarlo para que incluya un delay() o un yield().

Por otro lado la función delaymicroseconds() no hace una llamada a yield(), por lo que deberemos evitar usarla para esperas mayores de unos 20ms.

Conexiones y hardware

Designación de los pines

La designación de los pines está (en teoría correctamente) asociada en la implementación del ESP8266 en el entorno de Arduino, según la siguiente tabla:

Alias - GPIOAlias - GPIOAlias - GPIO
D0 - GPIO3D6 - GPIO12D12 - GPIO12
D1 - GPIO1D7 - GPIO13D13 - GPIO14
D2 - GPIO16D8 - GPIO0D14 - GPIO4
D3 - GPIO5D9 - GPIO2D15 - GPIO5
D4 - GPIO4D10 - GPIO15
D5 - GPIO14D11 - GPIO13

Por tanto, para hacer referencia a un pin podemos usar ambas designaciones indistintamente. Así que, en teoría, las dos sentencias siguientes son equivalentes,

digitalWrite(D5, LOW);  // D5, que es igual a GPIO14

digitalWrite(14, LOW);  // GPIO14 directamente

Sin embargo, en la práctica, sabemos que no todos los fabricantes siguen el mismo criterio a la hora de identificar los pines en las placas (sobre todo los chinos).

Por tanto, al realizar la programación del ESP8266 deberemos prestar especial atención al pinout de nuestra placa y tener muy presente posibles equivocaciones en la designación para evitarnos quebraderos de cabeza.

Salidas y entradas digitales (GPIO)

Las entradas y salidas digitales (GPIO) se programan prácticamente igual que en cualquier Arduino. Así, podemos cambiar el modo de un GPIO entre entrada o salida con la función:

pinMode(pin, mode)

Donde el modo puede ser OUTPUT, INPUT o INPUT_PULLUP. El pin 16 tiene un modo adicional INPUT_PULLDOWN_16, pero no es frecuente que vayamos a usarlo. Como en Arduino, por defecto todos los pines se inicializan como INPUT.

Por otro lado, cuando el GPIO están en modo OUTPUT podemos emplearlo como salida asignando un valor (LOW o HIGH) igual que en Arduino con:

digitalWrite(pin, output)

Por último, cuando el GPIO está en modo entrada podemos leer su valor igual que en Arduino con:

digitalRead(pin)

Salidas analógicas (PWM)

La programación de salidas PWM (salidas “analógicas”) es muy parecida a Arduino convencional. Sin embargo el ES8266 no dispone de PWM por hardware por lo que, en su lugar, lo realiza por software.

Esto supone un coste computacional adicional que Arduino no tiene. Sin embargo, también permite que podamos usar PWM en cualquier GPIO.

Para ello, igual que en Arduino, para usar una salida PWM empleamosla función:

analogWrite(pin, value)

Por defecto el rango es de 10bits, por lo que value toma valores de 0-1023 (la mayoría de modelos de Arduino toman valores de 0-255). No obstante, podemos cambiar el rango hasta 14bits de salida con:

analogWriteRange(range)

La frecuencia por defecto es de 1kHz, pero puede ser cambiado con la función (el mínimo es 100Hz y el máximo es 40kHz)

analogWriteFreq(frequency)

Otra diferencia con un Arduino es que si en el mismo programa queremos usar una salida PWM como digital posteriormente, deberemos desactivar el PWM de forma explícita. Para ello simplemente hacemos:

analogWrite(pin, 0)

Entradas analógicas (ADC)

El ESP8266 tiene una única entrada analógica (ADC), que en el entorno Arduino se designa con A0. Para leerlo se emplea la misma función que Arduino, es decir, simplemente usamos la función:

analogRead(A0)

La respuesta es un valor de 10bits en el rango de 0-1023 (igual que en la mayoría de Arduinos), cuyo voltaje es proporcional a la tensión en la entrada. Recordar que el ESP8266 tiene una entrada analógica máxima de 1V, aunque muchas placas tienen un conversor para ampliarlo hasta 5V.

Verificar la tensión máxima admisible del ADC de vuestra placa. Aplicar una tensión superior a la permitida dañará el pin ADC.

Por otro lado, el ESP8266 tiene un modo adicional que permite medir su voltaje de alimentación. Esto es útil, por ejemplo, al funcionar con baterías. Para activarlo, añadimos esta línea en nuestro Sketch.

ADC_MODE(ADC_VCC)

Mientras estemos usando este modo el pin ADC real deberá estar desconectado y, lógicamente, no podremos usarlo como entrada analógica.

Interrupciones

El ESP8266 tiene interrupciones en todos los pines GPIO, excepto en el GPIO16 (D2). El uso es similar a las interrupciones por hardware en Arduino.

attachInterrupt(digitalPinToInterrupt(interruptPin), handler, FALLING);

Las opciones son RISING, FALLING, CHANGE, ONLOW, ONHIGH, ONLOW_WE, ONHIGH_WE.

PROGMEM

La macro PROGMEM, que guarda variables en la memoria flash en lugar de en la memoria dinámica, también funciona en el ESP8266 con una pequeña pero importante diferencia.

En lugar de Arduino, que realiza un análisis previo para evitar tener la misma variable múltiples veces, el equivalente en ESP8266 no realiza este preprocesamiento.

Así, por ejemplo:

string text1 = F("hello");
string text2 = F("hello");

Guardará dos veces el literal “hello” en la memoria. Podemos evitar esto con la función FPSTR así

const char hello[] PROGMEM = "hello";
string texto1 = FSPTR(hello);
string texto2 = FSPTR(hello);

Comunicación

Puerto serie

El uso del puerto serie en el ESP8266 es muy similar a su uso en Arduino y emplea todas las funciones a las que estamos acostumbrado para enviar y recibir datos (read, write, print, println…)

Las únicas peculiaridades son relativas a funciones adicionales disponibles en el ESP8266, ya que el ESP8266 tiene 2 puertos serie.

El primer puerto (UART0) está conectado a 2 parejas de pins. Por defecto se emplea TX0 GPIO1 y RX0 GPIO3, pero puede cambiarse para que sea TX0 GPIO15 y RX0 GPIO13. Si queremos cambiar entre ambas posibilidades usamos la función.

Serial.swap()

El segundo puerto (UART1) está asociado a los pines TX1 GPIO2 y RX2 GPIO8. Pero el GPIO8 es usado para la conexión con la memoria externa. Por tanto, el UART1 solo puede usarse para enviar datos. Las funciones son mismas que las habituales, pero usando Serial1 en lugar de Serial. Por ejemplo, el UART1 se inicializaría así:

Serial1.begin(baud)

Bus I2C

El uso del I2C en el ESP8266 es similar a Arduino y emplea las mismas funciones. La diferencia es que ESP8266 no dispone de hardware para comunicación I2C por lo que lo simula por software.

Esto supone una carga de proceso adicional para el ESP8266 que Arduino no tiene. Pero, como ventaja, podemos usar dos pines cualesquiera para la comunicación.

Por defecto, los pines empleados son SDA GPIO4 (D2) y SCL GPIO5 (D14). Pero podemos cambiarlo con la función:

Wire.begin(SDA, SCL)

La velocidad máxima es de unos 450kHZ. Por lo demás, la programación es idéntica a Arduino.

Bus SPI

El ESP8266 tiene un SPI por hardware accesible para el usuario (designado a veces HSPI). Nuevamente, su uso es similar y emplea las mismas funciones que en un Arduino convencional. Los pines por defecto son MISO GPIO12 (D12), MOSI GPIO13 (D7), CLK GPIO14 (D13) y SS GPIO15 (D10).

Adicionalmente, en el ESP8266 podemos cambiar la frecuencia del SPI de forma sencilla hasta 1Mhz con la función:

SPI.setFrequency(frequency)

Comunicación WiFI

Para acceder a las funcionalidades WiFi del ESP8266 usaremos la librería ESP8266WiFI, cuyo funcionamiento es parecido a la librería de Arduino WiFi.

ESP8266WiFi Class

Para incluirlo en nuestro proyecto empleamos:

#include <ESP8266WiFi.h>

La funcionalidad WiFi es uno de los principales puntos de interés del ESP8266. Veremos su uso de forma intensiva en los próximos tutoriales de la serie del ESP8266, por lo que no vamos a profundizar de momento en ella.

Librerías de Arduino

Una de las preguntas que con más frecuencia se hacen al hablar de programación del ESP8266 en Arduino ¿funcionarán las librerías de Arduino en el ESP8266? Y la respuesta es, siento deciros, que en general no van a ser compatibles.

Pero no tampoco es, no siempre. Si la librería únicamente usa código de C++ (ejemplo, una librería de cálculo), o usa funciones del entorno para realizar el manejo del hardware, esta librería sí que va a funcionar.

Sin embargo, si accede a elementos internos de la arquitectura como timers, registros, o si usa códigos de ensamblador para acelerar ciertas tareas, por ejemplo, la librería no va a funcionar a no ser que haya sido adaptada específicamente para el ESP8266.

Afortunadamente, la popularidad de del ESP8266 ha hecho que la comunidad desarrolle o adapte la mayoría de librerías disponibles en Arduino. Aunque, de entre las muchas disponibles, tendremos que buscar específicamente las compatibles con ESP8266.

Como vemos, pese a algunas diferencias, en general es muy similar a programar un Arduino. Esto es debido al genial trabajo de la comunidad al desarrollar la compatibilidad entre dispositivos, y hace que sea muy sencillo usar el ESP8266 con el entorno de Arduino.

En las próximas entradas veremos cómo hacer algo parecido con el ESP32, y empezaremos a ver ejemplos y proyectos con el ESP8266.