Communication Protocols
ESP32 excels at connectivity. This guide covers WiFi, Bluetooth, and serial protocols (I2C, SPI, UART, ESP-NOW).
WiFi
Station Mode (Connect to Network)
#include <WiFi.h>
const char* ssid = "YourNetworkName";
const char* password = "YourPassword";
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA); // Station mode
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("Signal strength (RSSI): ");
Serial.print(WiFi.RSSI());
Serial.println(" dBm");
}
void loop() {
// Check connection status
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi disconnected! Reconnecting...");
WiFi.reconnect();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
}
delay(10000);
}
WiFi Connection with Timeout and Retry
#include <WiFi.h>
const char* ssid = "YourNetworkName";
const char* password = "YourPassword";
const int WIFI_TIMEOUT_MS = 20000;
const int WIFI_RETRY_DELAY_MS = 5000;
bool connectToWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
unsigned long startAttemptTime = millis();
while (WiFi.status() != WL_CONNECTED &&
millis() - startAttemptTime < WIFI_TIMEOUT_MS) {
delay(100);
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi connection failed!");
return false;
}
Serial.printf("Connected! IP: %s\n", WiFi.localIP().toString().c_str());
return true;
}
void setup() {
Serial.begin(115200);
while (!connectToWiFi()) {
Serial.println("Retrying in 5 seconds...");
delay(WIFI_RETRY_DELAY_MS);
}
}
Access Point Mode (Create Network)
#include <WiFi.h>
const char* ap_ssid = "ESP32-AP";
const char* ap_password = "12345678"; // Min 8 characters
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAP(ap_ssid, ap_password);
Serial.println("Access Point Started");
Serial.print("AP IP address: ");
Serial.println(WiFi.softAPIP());
}
void loop() {
Serial.printf("Connected stations: %d\n", WiFi.softAPgetStationNum());
delay(5000);
}
Dual Mode (Station + Access Point)
#include <WiFi.h>
const char* sta_ssid = "HomeNetwork";
const char* sta_password = "HomePassword";
const char* ap_ssid = "ESP32-Config";
const char* ap_password = "12345678";
void setup() {
Serial.begin(115200);
// Configure both modes
WiFi.mode(WIFI_AP_STA);
// Start AP
WiFi.softAP(ap_ssid, ap_password);
Serial.printf("AP started: %s\n", WiFi.softAPIP().toString().c_str());
// Connect to station
WiFi.begin(sta_ssid, sta_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.printf("\nStation connected: %s\n", WiFi.localIP().toString().c_str());
}
HTTP Client
#include <WiFi.h>
#include <HTTPClient.h>
void makeHTTPRequest() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi not connected!");
return;
}
HTTPClient http;
http.begin("http://httpbin.org/get");
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("HTTP Response code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
}
} else {
Serial.printf("HTTP request failed: %s\n",
http.errorToString(httpCode).c_str());
}
http.end();
}
// POST request
void makeHTTPPost() {
HTTPClient http;
http.begin("http://httpbin.org/post");
http.addHeader("Content-Type", "application/json");
String jsonPayload = "{\"sensor\":\"temperature\",\"value\":25.5}";
int httpCode = http.POST(jsonPayload);
if (httpCode > 0) {
String response = http.getString();
Serial.println(response);
}
http.end();
}
HTTPS Client
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
// Root CA certificate (example for httpbin.org)
const char* rootCACertificate = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADAn\n" \
"..." \
"-----END CERTIFICATE-----\n";
void makeHTTPSRequest() {
WiFiClientSecure *client = new WiFiClientSecure;
if (client) {
client->setCACert(rootCACertificate);
HTTPClient https;
if (https.begin(*client, "https://httpbin.org/get")) {
int httpCode = https.GET();
if (httpCode > 0) {
Serial.printf("HTTPS Response: %d\n", httpCode);
String payload = https.getString();
Serial.println(payload);
}
https.end();
}
delete client;
}
}
// Skip certificate verification (not recommended for production)
void makeInsecureHTTPS() {
WiFiClientSecure *client = new WiFiClientSecure;
client->setInsecure(); // Don't verify certificate
HTTPClient https;
https.begin(*client, "https://example.com");
int httpCode = https.GET();
// ...
https.end();
delete client;
}
Simple Web Server
#include <WiFi.h>
#include <WebServer.h>
WebServer server(80);
void handleRoot() {
String html = "<html><body>";
html += "<h1>ESP32 Web Server</h1>";
html += "<p>Temperature: 25.5°C</p>";
html += "<p><a href='/led/on'>Turn LED ON</a></p>";
html += "<p><a href='/led/off'>Turn LED OFF</a></p>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void handleLedOn() {
digitalWrite(2, HIGH);
server.send(200, "text/plain", "LED is ON");
}
void handleLedOff() {
digitalWrite(2, LOW);
server.send(200, "text/plain", "LED is OFF");
}
void handleNotFound() {
server.send(404, "text/plain", "Not found");
}
void setup() {
Serial.begin(115200);
pinMode(2, OUTPUT);
// Connect to WiFi (see previous examples)
WiFi.begin("ssid", "password");
while (WiFi.status() != WL_CONNECTED) delay(500);
// Configure routes
server.on("/", handleRoot);
server.on("/led/on", handleLedOn);
server.on("/led/off", handleLedOff);
server.onNotFound(handleNotFound);
server.begin();
Serial.printf("Server started at http://%s\n",
WiFi.localIP().toString().c_str());
}
void loop() {
server.handleClient();
}
WebSocket Server
#include <WiFi.h>
#include <WebSocketsServer.h>
WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) {
switch (type) {
case WStype_DISCONNECTED:
Serial.printf("[%u] Disconnected\n", num);
break;
case WStype_CONNECTED:
{
IPAddress ip = webSocket.remoteIP(num);
Serial.printf("[%u] Connected from %s\n", num, ip.toString().c_str());
}
break;
case WStype_TEXT:
Serial.printf("[%u] Received: %s\n", num, payload);
// Echo back
webSocket.sendTXT(num, "Received: " + String((char*)payload));
// Broadcast to all clients
webSocket.broadcastTXT("Broadcast: " + String((char*)payload));
break;
}
}
void setup() {
Serial.begin(115200);
// Connect to WiFi...
webSocket.begin();
webSocket.onEvent(webSocketEvent);
Serial.println("WebSocket server started on port 81");
}
void loop() {
webSocket.loop();
}
mDNS (Local DNS)
#include <WiFi.h>
#include <ESPmDNS.h>
void setup() {
// Connect to WiFi...
// Start mDNS
if (MDNS.begin("esp32")) {
Serial.println("mDNS responder started");
Serial.println("Access at: http://esp32.local");
// Advertise services
MDNS.addService("http", "tcp", 80);
}
}
Bluetooth
ESP32 supports both Classic Bluetooth and BLE (Bluetooth Low Energy).
Bluetooth Serial (Classic)
#include "BluetoothSerial.h"
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Enable it in menuconfig
#endif
BluetoothSerial SerialBT;
void setup() {
Serial.begin(115200);
SerialBT.begin("ESP32-BT"); // Bluetooth device name
Serial.println("Bluetooth started! Pair with 'ESP32-BT'");
}
void loop() {
// Forward Serial to Bluetooth
if (Serial.available()) {
SerialBT.write(Serial.read());
}
// Forward Bluetooth to Serial
if (SerialBT.available()) {
Serial.write(SerialBT.read());
}
delay(20);
}
BLE Server (Peripheral)
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
Serial.println("Device connected");
}
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("Device disconnected");
pServer->startAdvertising(); // Restart advertising
}
};
class MyCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic* pCharacteristic) {
String value = pCharacteristic->getValue();
if (value.length() > 0) {
Serial.print("Received: ");
Serial.println(value.c_str());
}
}
};
void setup() {
Serial.begin(115200);
// Initialize BLE
BLEDevice::init("ESP32-BLE");
// Create server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create service
BLEService* pService = pServer->createService(SERVICE_UUID);
// Create characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY
);
pCharacteristic->setCallbacks(new MyCallbacks());
pCharacteristic->addDescriptor(new BLE2902());
// Start service
pService->start();
// Start advertising
BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->start();
Serial.println("BLE server started");
}
void loop() {
if (deviceConnected) {
// Send notification
static int counter = 0;
String value = String(counter++);
pCharacteristic->setValue(value.c_str());
pCharacteristic->notify();
delay(1000);
}
}
BLE Client (Central)
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEClient.h>
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
static BLEAddress* pServerAddress;
static boolean doConnect = false;
static boolean connected = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.printf("Found: %s\n", advertisedDevice.toString().c_str());
if (advertisedDevice.haveServiceUUID() &&
advertisedDevice.isAdvertisingService(BLEUUID(SERVICE_UUID))) {
Serial.println("Found our service!");
BLEDevice::getScan()->stop();
pServerAddress = new BLEAddress(advertisedDevice.getAddress());
doConnect = true;
}
}
};
bool connectToServer() {
BLEClient* pClient = BLEDevice::createClient();
Serial.println("Connecting to server...");
pClient->connect(*pServerAddress);
BLERemoteService* pRemoteService = pClient->getService(BLEUUID(SERVICE_UUID));
if (pRemoteService == nullptr) {
Serial.println("Failed to find service");
pClient->disconnect();
return false;
}
pRemoteCharacteristic = pRemoteService->getCharacteristic(BLEUUID(CHARACTERISTIC_UUID));
if (pRemoteCharacteristic == nullptr) {
Serial.println("Failed to find characteristic");
pClient->disconnect();
return false;
}
Serial.println("Connected!");
return true;
}
void setup() {
Serial.begin(115200);
BLEDevice::init("");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->start(30); // Scan for 30 seconds
}
void loop() {
if (doConnect) {
if (connectToServer()) {
connected = true;
}
doConnect = false;
}
if (connected) {
// Read value
String value = pRemoteCharacteristic->readValue();
Serial.printf("Read value: %s\n", value.c_str());
delay(1000);
}
}
I2C (Inter-Integrated Circuit)
Two-wire communication for sensors, displays, and more.
I2C Scanner
#include <Wire.h>
void setup() {
Serial.begin(115200);
Wire.begin(); // Default: SDA=21, SCL=22
Serial.println("I2C Scanner");
scanI2C();
}
void scanI2C() {
byte error, address;
int deviceCount = 0;
Serial.println("Scanning...");
for (address = 1; address < 127; address++) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.printf("Device found at 0x%02X\n", address);
deviceCount++;
}
}
Serial.printf("Found %d device(s)\n", deviceCount);
}
void loop() {
delay(5000);
scanI2C();
}
I2C with Custom Pins
#include <Wire.h>
#define SDA_PIN 16
#define SCL_PIN 17
void setup() {
Wire.begin(SDA_PIN, SCL_PIN);
// Or with frequency
Wire.begin(SDA_PIN, SCL_PIN, 400000); // 400kHz
}
I2C Reading Example (Generic)
#include <Wire.h>
#define DEVICE_ADDRESS 0x68
uint8_t readRegister(uint8_t reg) {
Wire.beginTransmission(DEVICE_ADDRESS);
Wire.write(reg);
Wire.endTransmission(false); // Repeated start
Wire.requestFrom(DEVICE_ADDRESS, (uint8_t)1);
return Wire.read();
}
void writeRegister(uint8_t reg, uint8_t value) {
Wire.beginTransmission(DEVICE_ADDRESS);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission();
}
void readMultipleBytes(uint8_t reg, uint8_t* buffer, size_t length) {
Wire.beginTransmission(DEVICE_ADDRESS);
Wire.write(reg);
Wire.endTransmission(false);
Wire.requestFrom(DEVICE_ADDRESS, length);
for (size_t i = 0; i < length; i++) {
buffer[i] = Wire.read();
}
}
Multiple I2C Buses
ESP32 has two I2C controllers:
#include <Wire.h>
TwoWire I2C_1 = TwoWire(0); // First I2C bus
TwoWire I2C_2 = TwoWire(1); // Second I2C bus
void setup() {
I2C_1.begin(21, 22); // SDA, SCL
I2C_2.begin(16, 17); // Different pins
// Use like normal
I2C_1.beginTransmission(0x68);
I2C_2.beginTransmission(0x3C);
}
SPI (Serial Peripheral Interface)
High-speed communication for displays, SD cards, etc.
SPI Basics
#include <SPI.h>
// Default VSPI pins:
// MOSI: 23
// MISO: 19
// CLK: 18
// CS: 5 (user defined)
#define CS_PIN 5
void setup() {
Serial.begin(115200);
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH);
SPI.begin();
}
void spiTransfer(uint8_t* data, size_t length) {
digitalWrite(CS_PIN, LOW);
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
for (size_t i = 0; i < length; i++) {
data[i] = SPI.transfer(data[i]);
}
SPI.endTransaction();
digitalWrite(CS_PIN, HIGH);
}
uint8_t readRegister(uint8_t reg) {
uint8_t value;
digitalWrite(CS_PIN, LOW);
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
SPI.transfer(reg | 0x80); // Read flag
value = SPI.transfer(0x00);
SPI.endTransaction();
digitalWrite(CS_PIN, HIGH);
return value;
}
void writeRegister(uint8_t reg, uint8_t value) {
digitalWrite(CS_PIN, LOW);
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
SPI.transfer(reg & 0x7F); // Write flag (clear MSB)
SPI.transfer(value);
SPI.endTransaction();
digitalWrite(CS_PIN, HIGH);
}
SPI Modes
Mode | CPOL | CPHA | Clock Idle | Data Sample
-----|------|------|------------|-------------
0 | 0 | 0 | LOW | Rising edge
1 | 0 | 1 | LOW | Falling edge
2 | 1 | 0 | HIGH | Falling edge
3 | 1 | 1 | HIGH | Rising edge
Custom SPI Pins
#include <SPI.h>
// Custom pins
#define MOSI_PIN 13
#define MISO_PIN 12
#define CLK_PIN 14
#define CS_PIN 15
SPIClass hspi(HSPI);
void setup() {
hspi.begin(CLK_PIN, MISO_PIN, MOSI_PIN, CS_PIN);
pinMode(CS_PIN, OUTPUT);
}
void loop() {
digitalWrite(CS_PIN, LOW);
hspi.transfer(0x00);
digitalWrite(CS_PIN, HIGH);
}
Multiple SPI Devices
#include <SPI.h>
#define CS_DEVICE_A 5
#define CS_DEVICE_B 15
void setup() {
SPI.begin();
pinMode(CS_DEVICE_A, OUTPUT);
pinMode(CS_DEVICE_B, OUTPUT);
digitalWrite(CS_DEVICE_A, HIGH);
digitalWrite(CS_DEVICE_B, HIGH);
}
void readDeviceA() {
digitalWrite(CS_DEVICE_A, LOW);
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
// ... transfer
SPI.endTransaction();
digitalWrite(CS_DEVICE_A, HIGH);
}
void readDeviceB() {
digitalWrite(CS_DEVICE_B, LOW);
SPI.beginTransaction(SPISettings(4000000, LSBFIRST, SPI_MODE1)); // Different settings
// ... transfer
SPI.endTransaction();
digitalWrite(CS_DEVICE_B, HIGH);
}
UART (Serial Communication)
ESP32 has three UART controllers.
Hardware Serial
// UART0: GPIO 1 (TX), GPIO 3 (RX) - Used for programming/debug
// UART1: GPIO 10 (TX), GPIO 9 (RX) - Often conflicting with flash
// UART2: GPIO 17 (TX), GPIO 16 (RX) - User available
#include <HardwareSerial.h>
HardwareSerial Serial2(2); // UART2
void setup() {
Serial.begin(115200); // USB/Debug
Serial2.begin(9600, SERIAL_8N1, 16, 17); // RX, TX
}
void loop() {
// Forward between USB and UART2
if (Serial.available()) {
Serial2.write(Serial.read());
}
if (Serial2.available()) {
Serial.write(Serial2.read());
}
}
Custom UART Pins
#include <HardwareSerial.h>
HardwareSerial mySerial(1); // UART1
void setup() {
// Custom pins for UART1
mySerial.begin(9600, SERIAL_8N1, 25, 26); // RX=25, TX=26
}
Reading GPS Data (Example)
#include <HardwareSerial.h>
HardwareSerial GPS(2);
void setup() {
Serial.begin(115200);
GPS.begin(9600, SERIAL_8N1, 16, 17);
}
void loop() {
if (GPS.available()) {
String line = GPS.readStringUntil('\n');
if (line.startsWith("$GPRMC")) {
Serial.println(line);
// Parse GPS data...
}
}
}
ESP-NOW
Peer-to-peer communication without WiFi network. Low latency, up to 250 bytes per message.
ESP-NOW Sender
#include <esp_now.h>
#include <WiFi.h>
// Receiver MAC address
uint8_t receiverMAC[] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX};
typedef struct {
int id;
float temperature;
float humidity;
} SensorData;
SensorData dataToSend;
void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
Serial.printf("Send status: %s\n",
status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail");
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
Serial.printf("Sender MAC: %s\n", WiFi.macAddress().c_str());
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW init failed");
return;
}
esp_now_register_send_cb(onDataSent);
// Register peer
esp_now_peer_info_t peerInfo;
memset(&peerInfo, 0, sizeof(peerInfo));
memcpy(peerInfo.peer_addr, receiverMAC, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
Serial.println("ESP-NOW initialized");
}
void loop() {
dataToSend.id = 1;
dataToSend.temperature = 25.5;
dataToSend.humidity = 60.2;
esp_err_t result = esp_now_send(receiverMAC, (uint8_t*)&dataToSend, sizeof(dataToSend));
if (result == ESP_OK) {
Serial.println("Sent successfully");
} else {
Serial.println("Send error");
}
delay(2000);
}
ESP-NOW Receiver
#include <esp_now.h>
#include <WiFi.h>
typedef struct {
int id;
float temperature;
float humidity;
} SensorData;
SensorData receivedData;
void onDataReceived(const uint8_t *mac_addr, const uint8_t *data, int len) {
char macStr[18];
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0], mac_addr[1], mac_addr[2],
mac_addr[3], mac_addr[4], mac_addr[5]);
Serial.printf("Received from: %s\n", macStr);
memcpy(&receivedData, data, sizeof(receivedData));
Serial.printf("ID: %d, Temp: %.1f, Humidity: %.1f\n",
receivedData.id, receivedData.temperature, receivedData.humidity);
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
Serial.printf("Receiver MAC: %s\n", WiFi.macAddress().c_str());
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW init failed");
return;
}
esp_now_register_recv_cb(onDataReceived);
Serial.println("ESP-NOW receiver ready");
}
void loop() {
// Data received via callback
delay(10);
}
Get MAC Address
Run this to find your ESP32's MAC address:
#include <WiFi.h>
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
Serial.printf("MAC Address: %s\n", WiFi.macAddress().c_str());
}
void loop() {}
Protocol Comparison
| Protocol | Speed | Distance | Wiring | Power | Use Case |
|---|---|---|---|---|---|
| WiFi | High | Long (30m+) | None | High | Internet, cloud |
| BLE | Medium | ~10m | None | Low | Sensors, wearables |
| BT Classic | Medium | ~10m | None | Medium | Audio, serial |
| ESP-NOW | Medium | ~200m | None | Low | P2P, sensors |
| I2C | 400kHz | 1m | 2 wires | Low | Sensors, EEPROM |
| SPI | 80MHz | 0.3m | 4+ wires | Low | Displays, SD cards |
| UART | 5Mbps | 15m | 2-3 wires | Low | GPS, serial devices |
Next: Power Management →