Skip to content

Devices

ESP32

Connect an ESP32, ESP32-S2, S3, C3, C5, C6 or P4 to Thinger.io over Wi-Fi or Ethernet, with OTA updates and the connection in its own FreeRTOS task.

Reference6 min read

The ESP32 is a microcontroller from Espressif with Wi-Fi and Bluetooth. It is found on many development boards, some with Ethernet, LoRa, audio or a display. The Thinger.io Arduino library connects it over Wi-Fi with ThingerESP32 and over Ethernet with ThingerESP32Eth, always with TLS.

ESP32 development board with an ESP32-WROOM module, a micro USB connector and two rows of pins
ESP32 development board (ESP32-WROOM)

Supported chips ​

The same sketches work on every chip of the family. In the Arduino IDE, select the board of your chip in Tools > Board > esp32.

ChipNetworkClassOTA
ESP32Wi-Fi or EthernetThingerESP32, ThingerESP32EthYes, ThingerESP32OTA
ESP32-S2Wi-FiThingerESP32Yes, ThingerESP32OTA
ESP32-S3Wi-FiThingerESP32Yes, ThingerESP32OTA
ESP32-C3Wi-FiThingerESP32Yes, ThingerESP32OTA
ESP32-C5Wi-Fi, 2.4 and 5 GHzThingerESP32Yes, ThingerESP32OTA
ESP32-C6Wi-FiThingerESP32Yes, ThingerESP32OTA
ESP32-P4EthernetThingerESP32EthYes, ThingerESP32OTA

Boards built on these chips use the same classes, such as the Arduino Nano ESP32 (ESP32-S3) and the Seeed Studio XIAO ESP32 boards. The ESP32-P4 has no Wi-Fi of its own: connect it over Ethernet, as in ESP32 over Ethernet.

Set up the board ​

  1. Install the board package

    In the Arduino IDE, open Tools > Board > Boards Manager, search for esp32 and install esp32 by Espressif Systems. In PlatformIO, use platform = espressif32.

  2. Install the library

    Install thinger.io from the Library Manager. See Arduino for PlatformIO.

  3. Create the credentials file

    The sketches read the credentials from arduino_secrets.h, in the sketch folder:

    arduino_secrets.h
    cpp
    #define USERNAME "YOUR_USER"
    #define DEVICE_ID "YOUR_DEVICE"
    #define DEVICE_CREDENTIAL "YOUR_TOKEN"
    
    #define SSID "wifi-network"
    #define SSID_PASSWORD "wifi-password"

    DEVICE_CREDENTIAL holds the device credentials. The Ethernet sketch does not use SSID and SSID_PASSWORD.

Each example is in File > Examples > thinger.io > ESP32.

ESP32 over Wi-Fi ​

Example ESP32. The board joins the Wi-Fi network set with add_wifi() and exposes an input resource for GPIO 16 and an output resource with the milliseconds since it started.

ESP32.ino
cpp
#define THINGER_SERIAL_DEBUG

#include <ThingerESP32.h>
#include "arduino_secrets.h"

ThingerESP32 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);

void setup() {
  // open serial for debugging
  Serial.begin(115200);

  pinMode(16, OUTPUT);

  thing.add_wifi(SSID, SSID_PASSWORD);
  // add more networks calling add_wifi again: it connects to the strongest one
  // thing.add_wifi("other-network", "other-password");
  // WPA2-Enterprise networks (PEAP), such as eduroam. Pass the CA certificate of the
  // authentication server (provided by your organization) as the last parameter to validate it
  // thing.add_wifi_enterprise("eduroam", "user@example.com", "password", "anonymous@example.com", EDUROAM_CA);

  // digital pin control example (i.e. turning on/off a light, a relay, configuring a parameter, etc)
  thing["GPIO_16"] << digitalPin(16);

  // resource output example (i.e. reading a sensor value)
  thing["millis"] >> outputValue(millis());
}

void loop() {
  thing.handle();
}

Without add_wifi(), the board joins the last network stored in its flash. With several calls to add_wifi(), it joins the strongest of those networks, and add_wifi_enterprise() adds a WPA2-Enterprise network such as eduroam (version 3 of the esp32 board package). See Connect to several Wi-Fi networks and Connect to a WPA2-Enterprise network.

ESP32 with OTA updates ​

Example ESP32OTA. The same sketch with the OTA object, so you can send new firmware from the Visual Studio Code extension. Upload it once over USB; the next updates go over the Internet.

ESP32OTA.ino
cpp
#define THINGER_SERIAL_DEBUG

// compatibility fields of this firmware: the VS Code extension asks for confirmation before updating
// a device with different values (i.e., a firmware for another PCB version)
// #define THINGER_OTA_CHECK(FIELD) FIELD("PCB_MODEL", "MY_BOARD") FIELD("PCB_VERSION", "V1")

// roll back to the previous firmware if a new one cannot connect to Thinger.io in 5 minutes, or reboots before
// #define THINGER_OTA_ROLLBACK

#include <ThingerESP32.h>
#include <ThingerESP32OTA.h>
#include "arduino_secrets.h"

ThingerESP32 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);

// receives firmware updates from the Thinger.io extension for Visual Studio Code
ThingerESP32OTA ota(thing);

void setup() {
  // open serial for debugging
  Serial.begin(115200);

  pinMode(16, OUTPUT);

  thing.add_wifi(SSID, SSID_PASSWORD);

  // digital pin control example (i.e. turning on/off a light, a relay, configuring a parameter, etc)
  thing["GPIO_16"] << digitalPin(16);

  // resource output example (i.e. reading a sensor value)
  thing["millis"] >> outputValue(millis());
}

void loop() {
  thing.handle();
}

Uncomment THINGER_OTA_CHECK to declare the hardware the firmware is built for, as in Check the hardware compatibility, and THINGER_OTA_ROLLBACK to go back to the previous firmware when a new one does not connect, as in Roll back a failed update.

ESP32 with the connection in its own task ​

Example ESP32RTOS. #define THINGER_FREE_RTOS before the include adds thing.start(), which runs the connection in its own FreeRTOS task. loop() stays free for your code, with no thing.handle().

ESP32RTOS.ino
cpp
#define THINGER_SERIAL_DEBUG
#define THINGER_FREE_RTOS

#include <ThingerESP32.h>
#include "arduino_secrets.h"

ThingerESP32 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);

void setup() {
  // open serial for debugging
  Serial.begin(115200);

  pinMode(16, OUTPUT);

  thing.add_wifi(SSID, SSID_PASSWORD);

  // digital pin control example (i.e. turning on/off a light, a relay, configuring a parameter, etc)
  thing["GPIO_16"] << digitalPin(16);

  // resource output example (i.e. reading a sensor value)
  thing["millis"] >> outputValue(millis());

  // start the connection in its own task
  thing.start();
}

void loop() {
  // your code here: no thing.handle() needed
  // wrap variables shared with device resources in thing.lock() and thing.unlock()
}

thing.start(core, stack_size) takes the CPU core and the stack size of the task (by default, the core that runs loop() and 8192 bytes). thing.stop() ends the task and thing.is_running() tells whether it runs. Wrap any variable that loop() and a device resource both use in thing.lock() and thing.unlock(), and also the calls to the platform from loop(), such as thing.write_bucket(). thing.get_state() and thing.get_error() can be read from any task or core without them.

ESP32 over Ethernet ​

Example ESP32Eth. For ESP32 and ESP32-P4 boards with an Ethernet port. The example is set up for a LAN8720 PHY; for other boards, change the PHY settings below. The board gets its address over DHCP and exposes an output resource with its network data.

ESP32Eth.ino
cpp
// Ethernet PHY of your board: these values are for a LAN8720 (RMII)
#ifndef ETH_PHY_TYPE
#define ETH_PHY_TYPE  ETH_PHY_LAN8720
#define ETH_PHY_ADDR  0
#define ETH_PHY_MDC   23
#define ETH_PHY_MDIO  18
#define ETH_PHY_POWER -1
#define ETH_CLK_MODE  ETH_CLOCK_GPIO0_IN
#endif

// enable debug output over serial
#define THINGER_SERIAL_DEBUG

// skip the certificate check (private instance with a self-signed certificate)
// #define THINGER_INSECURE_SSL

// #define THINGER_SERVER "YOUR_SERVER"  // only on a private instance

// root certificate (PEM) of a server with its own certificate authority
// #define THINGER_ROOT_CA R"(-----BEGIN CERTIFICATE-----
// ...
// -----END CERTIFICATE-----)"

#include <ThingerESP32Eth.h>
#include <ThingerESP32OTA.h>
#include "arduino_secrets.h"

ThingerESP32Eth thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);

// receives firmware updates from the Thinger.io extension for Visual Studio Code
ThingerESP32OTA ota(thing);

void setup() {
  // enable serial for debugging
  Serial.begin(115200);

  // fixed address instead of DHCP: ip, gateway, subnet and, optionally, two DNS servers
  // thing.set_address("192.168.1.55", "192.168.1.1", "255.255.255.0", "8.8.8.8", "8.8.4.4");

  // host name of the board on the network
  thing.set_hostname("ESP32Eth");

  // network data of the board
  thing["eth"] >> [](pson& out){
    out["hostname"] = ETH.getHostname();
    out["mac"] = ETH.macAddress();
    out["ip"] = ETH.localIP().toString();
    out["link"] = ETH.linkSpeed();
  };
}

void loop() {
  thing.handle();
}

The eth resource returns:

json
{
  "hostname": "ESP32Eth",
  "mac": "AA:BB:CC:DD:EE:FF",
  "ip": "192.168.1.55",
  "link": 100
}

Ethernet PHY settings ​

ETH.begin() only starts the Ethernet port when these values match the board. The example sets them for a LAN8720; change them for other boards, before the include.

DefineLAN8720 valueMeaning
ETH_PHY_TYPEETH_PHY_LAN8720PHY chip. Others: ETH_PHY_TLK110, ETH_PHY_RTL8201, ETH_PHY_DP83848, ETH_PHY_KSZ8041, ETH_PHY_KSZ8081…
ETH_PHY_ADDR0Address of the PHY on the management bus.
ETH_PHY_MDC23GPIO of the MDC clock line.
ETH_PHY_MDIO18GPIO of the MDIO data line.
ETH_PHY_POWER-1GPIO that powers the PHY, or -1 if the board has none.
ETH_CLK_MODEETH_CLOCK_GPIO0_INSource of the 50 MHz RMII clock: here, an external clock on GPIO 0.

The board maker documents the values of each board. #ifndef ETH_PHY_TYPE keeps the values of board packages that already define them: the ESP32P4 Dev Module board of the esp32 package defines its own PHY settings, so on an ESP32-P4 the example compiles without changes. Check them against your board.

  • set_address(ip, gateway, subnet, dns1, dns2) sets a fixed address. The DNS servers default to 8.8.8.8 and 8.8.4.4. Call it before the first thing.handle().
  • set_hostname() sets the host name the board announces on the network.
  • ThingerESP32Eth also supports #define THINGER_FREE_RTOS and thing.start().

ESP32 set up from a phone ​

Example ESP32WebConfig. ThingerESP32WebConfig opens its own Wi-Fi network, where you enter the Wi-Fi network and the device credentials from a phone. See WebConfig.

Notes ​

  • On the ESP32, analogPin() is not available: read the pin with analogRead() inside an output resource.
  • The connection uses TLS and validates the server certificate. For a private instance whose certificate comes from its own authority, define its root certificate with THINGER_ROOT_CA before the include, as in Server and TLS. #define THINGER_INSECURE_SSL skips the check, for a private instance with a self-signed certificate.
  • ESP32 boards join 2.4 GHz Wi-Fi networks; the ESP32-C5 also joins 5 GHz networks.
  • ThingerESP32OTA receives the firmware compressed. #define THINGER_OTA_COMPRESSION 0 before the includes makes it receive the firmware uncompressed.

See also ​