ESP32 Detailed Overview
1. Introduction to ESP32

The ESP32 is a powerful, low-cost, low-power system on a chip (SoC) designed by Espressif Systems. It features both Wi-Fi and Bluetooth capabilities, making it an ideal choice for IoT (Internet of Things) applications, wearables, and smart home solutions. Its versatility, energy efficiency, and high processing power have made it one of the most widely used microcontrollers for connected devices.

2. Key Features of ESP32
ESP32 Doit dev-kit-1 pinout
3. Wi-Fi Connectivity in Detail

The ESP32’s Wi-Fi capability supports the 2.4 GHz frequency band and offers 802.11 b/g/n protocol support. It can act as a:

4. Bluetooth Capabilities

The ESP32’s dual-mode Bluetooth capabilities allow it to support both Bluetooth Classic and BLE (Bluetooth Low Energy). This makes it suitable for a range of applications that require short-range wireless communication, such as:

5. Peripheral Interfaces

The ESP32 is equipped with a range of peripheral interfaces that allow for communication with external devices:

6. Security Features
7. Low Power Modes

The ESP32 is optimized for low-power consumption. It offers several power-saving modes, including:

8. Applications of ESP32
List of ESP32 Variants
Board CPU Speed RAM Flash Memory Peripherals Operating Voltage Other Specifications
ESP32-WROOM-32240 MHz (Dual-core Xtensa LX6)520 KB SRAM4 MB34 GPIO, UART, SPI, I2C, ADC, DAC, PWM, Wi-Fi, Bluetooth3.3VStandard ESP32 module, widely used in IoT applications
ESP32-WROVER240 MHz (Dual-core Xtensa LX6)520 KB SRAM, 4 MB PSRAM4 MB34 GPIO, UART, SPI, I2C, ADC, DAC, PWM, Wi-Fi, Bluetooth3.3VIncludes extra PSRAM, ideal for applications needing more RAM
ESP32-WROVER-B240 MHz (Dual-core Xtensa LX6)520 KB SRAM, 8 MB PSRAM4 MB34 GPIO, UART, SPI, I2C, ADC, DAC, PWM, Wi-Fi, Bluetooth3.3VMore PSRAM compared to WROVER, great for AI/ML applications
ESP32-S2240 MHz (Single-core Xtensa LX7)320 KB SRAM4 MB43 GPIO, USB, UART, SPI, I2C, ADC, DAC, PWM, Wi-Fi3.3VLower power, secure IoT-focused, lacks Bluetooth
ESP32-S3240 MHz (Dual-core Xtensa LX7)512 KB SRAM4-8 MB43 GPIO, USB, UART, SPI, I2C, ADC, DAC, PWM, Wi-Fi, Bluetooth3.3VEnhanced AI acceleration, better for vision/ML applications
ESP32-C3160 MHz (Single-core RISC-V)400 KB SRAM4 MB22 GPIO, UART, SPI, I2C, ADC, PWM, Wi-Fi, Bluetooth 5.03.3VRISC-V architecture, lower power consumption, compact size
ESP32-C6160 MHz (Single-core RISC-V)400 KB SRAM4 MB22 GPIO, UART, SPI, I2C, ADC, PWM, Wi-Fi 6, Bluetooth 5.03.3VWi-Fi 6 support, optimized for low-latency applications
ESP32-PICO-D4240 MHz (Dual-core Xtensa LX6)520 KB SRAM4 MB34 GPIO, UART, SPI, I2C, ADC, DAC, PWM, Wi-Fi, Bluetooth3.3VCompact, integrated flash and crystal, ideal for space-constrained designs
ESP32-MINI-1240 MHz (Dual-core Xtensa LX6)520 KB SRAM4 MB34 GPIO, UART, SPI, I2C, ADC, DAC, PWM, Wi-Fi, Bluetooth3.3VSmall form factor, great for embedded projects
ESP32-DevKitC240 MHz (Dual-core Xtensa LX6)520 KB SRAM4 MB34 GPIO, UART, SPI, I2C, ADC, DAC, PWM, Wi-Fi, Bluetooth3.3VGeneral-purpose development board, USB interface

ESP32 ADC (Analog to Digital Converter) Detailed Overview

1. Introduction to the ADC on ESP32

The ESP32 features two 12-bit ADC modules, which allow for analog-to-digital conversions, enabling voltage measurements from sensors like temperature sensors, potentiometers, light sensors, and more. Each module has a 12-bit resolution, converting the voltage into 4096 discrete levels.

2. Key Features of ESP32 ADC

3. ADC Input Pins

The ESP32 ADC channels are divided into ADC1 (GPIO 32-39) and ADC2 (GPIO 0, 2, 4, 12-15, 25-27).

4. Example 1: Reading Voltage with the ADC

Here is a code example showing how to use ADC1 to read a voltage signal:

 
// ADC Example to Read Voltage
const int analogPin = 34;  // ADC1 Channel 6 (GPIO 34)
int adcValue = 0;          // Variable to store ADC value
float voltage = 0.0;       // Variable to store calculated voltage

void setup() {
    Serial.begin(115200);  // Initialize serial communication
    analogReadResolution(12);  // Set ADC resolution to 12 bits
}

void loop() {
    adcValue = analogRead(analogPin);   // Read the analog value from the pin
    voltage = (adcValue / 4095.0) * 3.3;  // Calculate the voltage
    Serial.print("ADC Value: ");
    Serial.print(adcValue);
    Serial.print(" | Voltage: ");
    Serial.println(voltage);
    delay(1000);  // Wait for 1 second before reading again
}

5. Example 2: Attenuation for Higher Voltage Reading

To read voltages higher than 1.1V, you can configure attenuation, as shown below:


// ADC with Attenuation to read higher voltages
const int analogPin = 33;  // ADC1 Channel 5 (GPIO 33)
int adcValue = 0;
float voltage = 0.0;

void setup() {
    Serial.begin(115200);
    analogReadResolution(12);   // Set ADC to 12-bit resolution
    analogSetPinAttenuation(analogPin, ADC_11db);  // Set attenuation to 11 dB
}

void loop() {
    adcValue = analogRead(analogPin);   // Read the analog value
    voltage = (adcValue / 4095.0) * 3.9;  // Calculate voltage
    Serial.print("ADC Value: ");
    Serial.print(adcValue);
    Serial.print(" | Voltage: ");
    Serial.println(voltage);
    delay(1000);  // Wait 1 second before reading again
}

ESP32 ADC with DMA

Overview

The ADC (Analog-to-Digital Converter) on the ESP32 can be used to read analog signals and convert them into digital values. When dealing with high-speed or high-resolution data acquisition, using DMA (Direct Memory Access) with the ADC can significantly improve performance by offloading data transfer tasks from the CPU to the DMA controller.

Key Features

Example Code

Configuring ADC with DMA

This example demonstrates how to set up the ESP32 ADC with DMA to continuously read analog data and store it in a buffer.


// Include necessary libraries
#include "driver/adc.h"
#include "driver/dma.h"

// Define ADC properties
#define ADC_WIDTH         ADC_WIDTH_BIT_12
#define ADC_ATTEN         ADC_ATTEN_DB_0
#define ADC_CHANNEL       ADC1_CHANNEL_0
#define DMA_BUFFER_SIZE   1024

// DMA buffer for ADC data
uint16_t adc_buffer[DMA_BUFFER_SIZE];

// DMA configuration
void setup_adc_dma() {
    // Configure ADC width and attenuation
    adc1_config_width(ADC_WIDTH);
    adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN);

    // Initialize DMA
    dma_config_t dma_config = {
        .src_addr = (void*) ADC1_BASE,
        .dest_addr = adc_buffer,
        .size = DMA_BUFFER_SIZE * sizeof(uint16_t),
        .channel = 0
    };
    dma_init(&dma_config);

    // Start ADC conversion
    adc1_start();
}

void setup() {
    // Initialize serial communication
    Serial.begin(115200);

    // Set up ADC with DMA
    setup_adc_dma();
}

void loop() {
    // Process ADC data
    for (int i = 0; i < DMA_BUFFER_SIZE; i++) {
        Serial.println(adc_buffer[i]);
    }

    // Wait before the next read
    delay(1000);
}

ESP32 I2C Interface - Master and Slave Mode Examples

1. Introduction to the I2C Interface

The ESP32 supports the I2C communication protocol, which allows it to communicate with multiple devices using just two lines: SDA (data line) and SCL (clock line). I2C is commonly used in sensors, memory devices, and communication between microcontrollers.

2. Key Features of ESP32 I2C

3. Example 1: ESP32 as I2C Master

In this example, the ESP32 acts as an I2C master device, sending and receiving data to/from a slave device at address 0x27.

Basic I2C Example (Master Mode):

 
#include <Wire.h>

void setup() {
    Wire.begin();  // Join I2C bus as master
    Serial.begin(115200);
}

void loop() {
    Wire.beginTransmission(0x3C);  // Communicate with device at address 0x3C
    Wire.write(0x00);              // Send data
    Wire.endTransmission();
    delay(1000);
}

4. Example 2: ESP32 as I2C Slave

In this example, the ESP32 is configured as an I2C slave device with an address of 0x27, ready to respond to communication from a master device.

 
#include <Wire.h>

void receiveEvent(int bytes);  // Function to handle data reception
void requestEvent();           // Function to send data when requested

void setup() {
    Wire.begin(0x27);  // Join I2C bus with address 0x27 as a slave
    Wire.onReceive(receiveEvent);  // Register the receive event handler
    Wire.onRequest(requestEvent);  // Register the request event handler
    Serial.begin(115200);
}

void loop() {
    delay(100);  // Main loop does nothing, just waits for events
}

// Function to handle data reception from the master
void receiveEvent(int bytes) {
    while (Wire.available()) {
        char c = Wire.read();  // Read the received byte
        Serial.print(c);       // Print received data
    }
}

// Function to handle data request from the master
void requestEvent() {
    Wire.write("ACK");  // Send back an acknowledgment
}

ESP32 I2S Interface

Overview

The I2S (Inter-IC Sound) interface is a serial bus interface standard used to connect digital audio devices. It is widely used for audio applications due to its ability to transmit data in a synchronous manner. I2S can be used to interface with various audio components such as ADCs, DACs, and audio processors.

ESP32 I2S Implementation

The ESP32 microcontroller supports the I2S interface, which is used for handling digital audio signals. The ESP32 I2S peripheral can be configured as either an I2S master or an I2S slave. This flexibility allows the ESP32 to communicate with various audio components, either generating or receiving audio data.

Key Features

Configuration Parameters

The ESP32 I2S interface is configured using several parameters. Here’s an overview of the main configuration options:

I2S Master vs. I2S Slave

The ESP32 can operate as both an I2S master and an I2S slave. Here’s a brief overview of the differences:

Example Code

I2S Master Code

 
// Include the I2S driver library
#include <driver/i2s>

// Define I2S configuration parameters
#define I2S_NUM              I2S_NUM_0   // I2S peripheral number
#define I2S_BCK_IO           26          // I2S Bit Clock (BCK) pin
#define I2S_WS_IO            25          // I2S Word Select (WS) pin
#define I2S_DATA_IO          22          // I2S Data pin
#define SAMPLE_RATE          44100       // Audio sample rate
#define I2S_BUFFER_SIZE      1024        // Buffer size for I2S data

void setup() {
    // Configure the I2S driver
    i2s_config_t i2s_config = {
        mode: I2S_MODE_MASTER | I2S_MODE_TX,   // I2S master and transmitter mode
        sample_rate: SAMPLE_RATE,              // Sample rate in Hz
        bits_per_sample: I2S_BITS_PER_SAMPLE_16BIT, // 16-bit audio samples
        channel_format: I2S_CHANNEL_FMT_RIGHT_LEFT, // Stereo audio format
        communication_format: I2S_COMM_FORMAT_I2S_MSB, // I2S format
        dma_buf_count: 2,                     // Number of DMA buffers
        dma_buf_len: I2S_BUFFER_SIZE,         // Length of each DMA buffer
        intr_alloc_flags: ESP_INTR_FLAG_LEVEL1 // Interrupt priority
    };

    // Set up the I2S pins
    i2s_pin_config_t pin_config = {
        bck_io_num: I2S_BCK_IO,    // Bit Clock pin
        ws_io_num: I2S_WS_IO,      // Word Select pin
        data_out_num: I2S_DATA_IO, // Data output pin
        data_in_num: I2S_PIN_NO_CHANGE // No data input pin used
    };

    // Install the I2S driver
    i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);

    // Set the I2S pin configuration
    i2s_set_pin(I2S_NUM, &pin_config);

    // Prepare data to send
    uint8_t data_to_send[I2S_BUFFER_SIZE] = {0}; // Initialize buffer with zeroes

    // Start sending data
    while (true) {
        i2s_write_bytes(I2S_NUM, (const char *)data_to_send, I2S_BUFFER_SIZE, portMAX_DELAY);
    }
}

void loop() {
    // Nothing to do in the loop
}
    

I2S Slave Code

 
// Include the I2S driver library
#include <driver/i2s>

// Define I2S configuration parameters
#define I2S_NUM              I2S_NUM_0   // I2S peripheral number
#define I2S_BCK_IO           26          // I2S Bit Clock (BCK) pin
#define I2S_WS_IO            25          // I2S Word Select (WS) pin
#define I2S_DATA_IO          22          // I2S Data pin
#define SAMPLE_RATE          44100       // Audio sample rate
#define I2S_BUFFER_SIZE      1024        // Buffer size for I2S data

void setup() {
    // Configure the I2S driver
    i2s_config_t i2s_config = {
        mode: I2S_MODE_SLAVE | I2S_MODE_RX,   // I2S slave and receiver mode
        sample_rate: SAMPLE_RATE,              // Sample rate in Hz
        bits_per_sample: I2S_BITS_PER_SAMPLE_16BIT, // 16-bit audio samples
        channel_format: I2S_CHANNEL_FMT_RIGHT_LEFT, // Stereo audio format
        communication_format: I2S_COMM_FORMAT_I2S_MSB, // I2S format
        dma_buf_count: 2,                     // Number of DMA buffers
        dma_buf_len: I2S_BUFFER_SIZE,         // Length of each DMA buffer
        intr_alloc_flags: ESP_INTR_FLAG_LEVEL1 // Interrupt priority
    };

    // Set up the I2S pins
    i2s_pin_config_t pin_config = {
        bck_io_num: I2S_BCK_IO,    // Bit Clock pin
        ws_io_num: I2S_WS_IO,      // Word Select pin
        data_out_num: I2S_PIN_NO_CHANGE, // No data output pin used
        data_in_num: I2S_DATA_IO  // Data input pin
    };

    // Install the I2S driver
    i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);

    // Set the I2S pin configuration
    i2s_set_pin(I2S_NUM, &pin_config);

    // Buffer to hold received data
    uint8_t received_data[I2S_BUFFER_SIZE];

    // Start receiving data
    while (true) {
        i2s_read_bytes(I2S_NUM, (char *)received_data, I2S_BUFFER_SIZE, portMAX_DELAY);
        // Process received data here
    }
}

void loop() {
    // Nothing to do in the loop
}
    

ESP32 SPI Interface

Overview

The Serial Peripheral Interface (SPI) is a synchronous serial communication protocol used for high-speed data transfer between devices. It operates in full-duplex mode, meaning that data can be transmitted and received simultaneously.

ESP32 SPI Implementation

The ESP32 microcontroller supports two SPI hardware peripherals: HSPI and VSPI. These can be configured to work as either SPI master or SPI slave. The ESP32 SPI interface is highly flexible and can be used for a variety of applications including communication with sensors, displays, and other peripherals.

Key Features

Configuration Parameters

The ESP32 SPI interface is configured using several parameters. Here’s an overview of the main configuration options:

SPI Master vs. SPI Slave

The ESP32 can operate as both an SPI master and an SPI slave. Here’s a brief overview of the differences:

Example Code

SPI Master Code

 
// Include the SPI master driver library
#include <driver/spi_master>

// Define GPIO pins for SPI interface
#define SPI_HOST          HSPI_HOST   // Use the HSPI host for communication
#define PIN_MISO           19          // GPIO pin for Master In Slave Out
#define PIN_MOSI           23          // GPIO pin for Master Out Slave In
#define PIN_SCK            18          // GPIO pin for Clock
#define PIN_CS             5           // GPIO pin for Chip Select

void setup() {
    // Initialize the SPI bus
    spi_bus_config_t bus_config = {
        mosi_io_num: PIN_MOSI,  // Set MOSI pin
        miso_io_num: PIN_MISO,  // Set MISO pin
        sclk_io_num: PIN_SCK,   // Set Clock pin
        quadwp_io_num: -1,      // Not used
        quadhd_io_num: -1       // Not used
    };
    // Initialize the SPI bus with the given configuration
    spi_bus_initialize(SPI_HOST, &bus_config, 1);

    // Configure the SPI device
    spi_device_interface_config_t dev_config = {
        command_bits: 0,                // No command bits used
        address_bits: 0,                // No address bits used
        dummy_bits: 0,                  // No dummy bits used
        mode: 0,                        // SPI mode 0
        clock_speed_hz: 1 * 1000 * 1000, // Set SPI clock speed to 1 MHz
        spics_io_num: PIN_CS,           // Set Chip Select pin
        queue_size: 7                   // Number of transactions that can be queued
    };
    spi_device_handle_t spi;
    // Add the SPI device to the bus
    spi_bus_add_device(SPI_HOST, &dev_config, &spi);

    // Prepare data to send
    uint8_t data_to_send[1] = {0xAA};  // Data to be transmitted (e.g., 0xAA)

    // Create and configure the SPI transaction
    spi_transaction_t transaction = {
        length: 8,                // Data length in bits
        tx_buffer: data_to_send,  // Pointer to data to send
        rx_buffer: NULL           // No data expected to be received
    };

    // Perform SPI transaction
    spi_device_transmit(spi, &transaction); // Transmit the data
}

void loop() {
    // Nothing to do in the loop
}
    

SPI Slave Code

 
// Include the SPI slave driver library
#include <driver/spi_slave>

// Define GPIO pins for SPI interface
#define SPI_HOST          HSPI_HOST   // Use the HSPI host for communication
#define PIN_MISO           19          // GPIO pin for Master In Slave Out
#define PIN_MOSI           23          // GPIO pin for Master Out Slave In
#define PIN_SCK            18          // GPIO pin for Clock
#define PIN_CS             5           // GPIO pin for Chip Select

void setup() {
    // Initialize the SPI bus
    spi_bus_config_t bus_config = {
        mosi_io_num: PIN_MOSI,  // Set MOSI pin
        miso_io_num: PIN_MISO,  // Set MISO pin
        sclk_io_num: PIN_SCK,   // Set Clock pin
        quadwp_io_num: -1,      // Not used
        quadhd_io_num: -1       // Not used
    };
    // Initialize the SPI bus with the given configuration
    spi_bus_initialize(SPI_HOST, &bus_config, 1);

    // Configure the SPI slave
    spi_slave_interface_config_t dev_config = {
        mode: 0,                  // SPI mode 0
        spics_io_num: PIN_CS,     // Set Chip Select pin
        queue_size: 3,            // Number of transactions that can be queued
        flags: SPI_SLAVE_KEEP_CS_LOW // Keep CS line low during transactions
    };
    // Initialize the SPI slave with the given configuration
    spi_slave_initialize(SPI_HOST, &bus_config, &dev_config, 1);

    // Buffer to hold received data
    uint8_t received_data[1];

    // Create and configure the SPI transaction
    spi_transaction_t transaction = {
        length: 8,               // Data length in bits
        tx_buffer: NULL,         // No data to send
        rx_buffer: received_data // Pointer to buffer for received data
    };

    // Wait for SPI transaction
    spi_slave_transmit(SPI_HOST, &transaction, portMAX_DELAY); // Receive data
}

void loop() {
    // Process received data here
}
    

ESP32 Timers

Overview

The ESP32 microcontroller includes multiple hardware timers that can be used for a variety of tasks such as generating precise time delays, creating periodic events, and measuring time intervals. The ESP32 has two types of timers: hardware timers and watchdog timers.

Hardware Timers

The ESP32 has four hardware timers, each of which can be configured independently. These timers can be used for tasks like generating PWM signals, periodic interrupts, and delay functions. They are highly versatile and can be used in both one-shot and periodic modes.

Key Features

Configuration Parameters

The ESP32 timers can be configured using several parameters. Here’s an overview of the main configuration options:

Watchdog Timers

In addition to the general-purpose timers, the ESP32 also includes watchdog timers that are used to reset the system if it becomes unresponsive. The watchdog timers are critical for ensuring the reliability of the system in various scenarios.

Key Features

Example Code

Hardware Timer Example

 
// Include the timer library
#include "driver/timer.h"

// Define timer configurations
#define TIMER_DIVIDER         16      // Timer clock divider
#define TIMER_SCALE           (TIMER_BASE_CLK / TIMER_DIVIDER) // Timer scale for microseconds
#define TIMER_INTERVAL_SEC    (1)     // Timer interval in seconds

// Timer interrupt handler
void IRAM_ATTR timer_group0_isr(void* arg) {
    // Clear the interrupt
    timer_spinlock_take(TIMER_GROUP_0);
    TIMERG0.int_clr_timers.t0 = 1;
    timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
    timer_spinlock_give(TIMER_GROUP_0);

    // Your interrupt handling code here
    printf("Timer Interrupt Triggered!\n");
}

void setup() {
    // Configure the timer
    timer_config_t config;
    config.alarm_en = TIMER_ALARM_EN;
    config.auto_reload = true;
    config.counter_dir = TIMER_COUNT_UP;
    config.intr_type = TIMER_INTR_LEVEL;
    config.timer_idx = TIMER_0;
    config.counter_en = TIMER_PAUSE;
    config.divider = TIMER_DIVIDER;

    // Initialize timer
    timer_init(TIMER_GROUP_0, TIMER_0, &config);

    // Set the timer alarm value
    timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, TIMER_SCALE * TIMER_INTERVAL_SEC);

    // Enable timer interrupt
    timer_enable_intr(TIMER_GROUP_0, TIMER_0);
    timer_isr_register(TIMER_GROUP_0, TIMER_0, timer_group0_isr, NULL, 0, NULL);

    // Start the timer
    timer_start(TIMER_GROUP_0, TIMER_0);
}

void loop() {
    // Main loop does nothing, timer ISR handles tasks
}
    

Watchdog Timer Example

 
// Include the watchdog library
#include "esp_system.h"
#include "esp_task_wdt.h"

// Define watchdog timer timeout
#define WDT_TIMEOUT_S    10  // Watchdog timeout in seconds

void setup() {
    // Initialize the watchdog timer
    esp_task_wdt_init(WDT_TIMEOUT_S, true); // Enable panic on watchdog timeout

    // Add the current task to the watchdog
    esp_task_wdt_add(NULL); // NULL refers to the current task

    // Start the watchdog timer
    esp_task_wdt_reset();
}

void loop() {
    // Main loop does nothing, watchdog timer needs periodic resets
    while (true) {
        delay(5000); // Delay for 5 seconds
        esp_task_wdt_reset(); // Reset watchdog timer
    }
}
    

Timers as Triggers

Timer Channel Triggered Peripheral / DMA Channel
Timer 0 Channel 0 DMA Channel 0
Timer 0 Channel 1 DMA Channel 1
Timer 1 Channel 0 DMA Channel 2
Timer 1 Channel 1 DMA Channel 3
Timer 2 Channel 0 DMA Channel 4
Timer 2 Channel 1 DMA Channel 5
Timer 3 Channel 0 DMA Channel 6
Timer 3 Channel 1 DMA Channel 7

Timer Triggers

Trigger Source Timers Triggered
External Signal (GPIO) Timer 0, Timer 1, Timer 2, Timer 3
Timer 0 Timer 1, Timer 2
Timer 1 Timer 2, Timer 3
Timer 2 Timer 3
PWM All Timers
ADC All Timers

ESP32 Interrupts Examples

Overview

Interrupts are crucial for real-time applications as they allow the microcontroller to respond to events immediately. The ESP32 supports various types of interrupts including external interrupts, serial communication interrupts, I2C interrupts, SPI interrupts, I2S interrupts, ADC interrupts, and timer interrupts. This document provides detailed examples for each type of interrupt.

Types of Interrupts

Examples

External Interrupt Example

 
// Include necessary libraries
#include "driver/gpio.h"

// Define GPIO pin for external interrupt
#define INTERRUPT_PIN  4

// ISR for handling the interrupt
void IRAM_ATTR gpio_isr_handler(void* arg) {
    // Handle the interrupt (e.g., toggle an LED)
    gpio_set_level(GPIO_NUM_2, !gpio_get_level(GPIO_NUM_2));
}

void setup() {
    // Initialize the GPIO for the interrupt pin
    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_INTR_NEGEDGE; // Trigger interrupt on falling edge
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pin_bit_mask = (1ULL << INTERRUPT_PIN);
    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
    io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
    gpio_config(&io_conf);

    // Initialize the GPIO for the LED (output)
    gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);

    // Install ISR service
    gpio_install_isr_service(0);

    // Attach the ISR to the interrupt pin
    gpio_isr_handler_add(INTERRUPT_PIN, gpio_isr_handler, (void*) INTERRUPT_PIN);
}

void loop() {
    // Main loop does nothing, ISR handles the interrupt
}
    

Serial Interrupt Example

 
// Include necessary libraries
#include "driver/uart.h"

// Define UART properties
#define UART_NUM          UART_NUM_1
#define BUF_SIZE           1024
#define TXD_PIN            (UART_PIN_NO_CHANGE)
#define RXD_PIN            9

// ISR for handling UART interrupts
void IRAM_ATTR uart_isr_handler(void* arg) {
    uint16_t rx_fifo_len;
    uart_get_buffered_data_len(UART_NUM, &rx_fifo_len);

    // Handle UART data reception
    uint8_t data[BUF_SIZE];
    int length = uart_read_bytes(UART_NUM, data, rx_fifo_len, 100 / portTICK_RATE_MS);
    // Process received data
}

void setup() {
    // Initialize UART
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_BITS_8,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
    };
    uart_param_config(UART_NUM, &uart_config);
    uart_set_pin(UART_NUM, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    uart_driver_install(UART_NUM, BUF_SIZE * 2, 0, 0, NULL, 0);

    // Install UART ISR
    uart_isr_register(UART_NUM, uart_isr_handler, NULL, 0, NULL);
}

void loop() {
    // Main loop does nothing, UART ISR handles interrupts
}
    

I2C Interrupt Example

 
// Include necessary libraries
#include "driver/i2c.h"

// Define I2C properties
#define I2C_MASTER_NUM     I2C_NUM_0
#define I2C_SDA_IO         21
#define I2C_SCL_IO         22
#define I2C_FREQ_HZ        100000

// ISR for handling I2C interrupts
void IRAM_ATTR i2c_isr_handler(void* arg) {
    // Handle I2C events (not directly supported by I2C driver)
}

void setup() {
    // Initialize I2C
    i2c_config_t conf;
    conf.mode = I2C_MODE_MASTER;
    conf.sda_io_num = I2C_SDA_IO;
    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
    conf.scl_io_num = I2C_SCL_IO;
    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed = I2C_FREQ_HZ;
    i2c_param_config(I2C_MASTER_NUM, &conf);
    i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0);
}

void loop() {
    // Main loop does nothing, I2C ISR would handle events if supported
}
    

SPI Interrupt Example

 
// Include necessary libraries
#include "driver/spi_master.h"

// Define SPI properties
#define SPI_HOST          HSPI_HOST
#define DMA_CHANNEL       1

// ISR for handling SPI interrupts
void IRAM_ATTR spi_isr_handler(void* arg) {
    // Handle SPI events (not directly supported by SPI driver)
}

void setup() {
    // Initialize SPI
    spi_bus_config_t buscfg = {
        .miso_io_num = -1,
        .mosi_io_num = 23,
        .sclk_io_num = 18,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1
    };
    spi_bus_initialize(SPI_HOST, &buscfg, DMA_CHANNEL);

    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 1000000,
        .mode = 0,
        .spics_io_num = 5,
        .queue_size = 1,
    };
    spi_device_handle_t spi;
    spi_bus_add_device(SPI_HOST, &devcfg, &spi);
}

void loop() {
    // Main loop does nothing, SPI ISR would handle events if supported
}
    

I2S Interrupt Example

 
// Include necessary libraries
#include "driver/i2s.h"

// Define I2S properties
#define I2S_NUM           I2S_NUM_0
#define I2S_BCK_IO        26
#define I2S_WS_IO         25
#define I2S_DO_IO         22

// ISR for handling I2S interrupts
void IRAM_ATTR i2s_isr_handler(void* arg) {
    // Handle I2S events (not directly supported by I2S driver)
}

void setup() {
    // Initialize I2S
    i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_TX,
        .sample_rate = 44100,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
        .communication_format = I2S_COMM_FORMAT_I2S_MSB,
        .intr_alloc_flags = ESP_INTR_FLAG_IRAM,
        .dma_buf_count = 8,
        .dma_buf_len = 64
    };
    i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);

    i2s_pin_config_t pin_config = {
        .bck_io_num = I2S_BCK_IO,
        .ws_io_num = I2S_WS_IO,
        .data_out_num = I2S_DO_IO,
        .data_in_num = I2S_PIN_NO_CHANGE
    };
    i2s_set_pin(I2S_NUM, &pin_config);
}

void loop() {
    // Main loop does nothing, I2S ISR would handle events if supported
}
    

ADC Interrupt Example

 
// Include necessary libraries
#include "driver/adc.h"

// ISR for handling ADC interrupts
void IRAM_ATTR adc_isr_handler(void* arg) {
    // Handle ADC conversion complete event
    int adc_value = adc1_get_raw(ADC1_CHANNEL_0);
    printf("ADC Value: %d\n", adc_value);
}

void setup() {
    // Initialize ADC
    adc1_config_width(ADC_WIDTH_BIT_12);
    adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_0);

    // ADC interrupt setup
    adc1_add_sample(ADC1_CHANNEL_0);
}

void loop() {
    // Main loop does nothing, ADC ISR handles interrupts
}
    

Timer Interrupt Example

 
// Include necessary libraries
#include "driver/timer.h"

// Define timer properties
#define TIMER_DIVIDER         16      // Timer clock divider
#define TIMER_SCALE           (TIMER_BASE_CLK / TIMER_DIVIDER) // Timer scale for microseconds
#define TIMER_INTERVAL_SEC    (1)     // Timer interval in seconds

// ISR for handling the timer interrupt
void IRAM_ATTR timer_group0_isr(void* arg) {
    // Clear the interrupt
    timer_spinlock_take(TIMER_GROUP_0);
    TIMERG0.int_clr_timers.t0 = 1;
    timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
    timer_spinlock_give(TIMER_GROUP_0);

    // Your interrupt handling code here
    printf("Timer Interrupt Triggered!\n");
}

void setup() {
    // Configure the timer
    timer_config_t config;
    config.alarm_en = TIMER_ALARM_EN;
    config.auto_reload = true;
    config.counter_dir = TIMER_COUNT_UP;
    config.intr_type = TIMER_INTR_LEVEL;
    config.timer_idx = TIMER_0;
    config.counter_en = TIMER_PAUSE;
    config.divider = TIMER_DIVIDER;

    // Initialize timer
    timer_init(TIMER_GROUP_0, TIMER_0, &config);

    // Set the timer alarm value
    timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, TIMER_SCALE * TIMER_INTERVAL_SEC);

    // Enable timer interrupt
    timer_enable_intr(TIMER_GROUP_0, TIMER_0);
    timer_isr_register(TIMER_GROUP_0, TIMER_0, timer_group0_isr, NULL, 0, NULL);

    // Start the timer
    timer_start(TIMER_GROUP_0, TIMER_0);
}

void loop() {
    // Main loop does nothing, timer ISR handles tasks
}
    

RTC Interrupt Example

The **RTC interrupt** is triggered by the Real-Time Clock to perform tasks at specific time intervals. This example configures an RTC alarm interrupt.

 

#include "driver/rtc_io.h"
#include "esp_sleep.h"

void setup() {
    Serial.begin(115200);
    esp_sleep_enable_timer_wakeup(10 * 1000000); // Set wakeup timer for 10 seconds
    Serial.println("Going to sleep now...");
    esp_deep_sleep_start();  // Enter deep sleep mode
}

void loop() {
    // Code will only run after waking up from deep sleep
    if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
        Serial.println("Woke up from RTC Timer interrupt!");
    }
    delay(1000);
}

    

Explanation

In this example, we use the ESP32’s **deep sleep** mode and set up an RTC wake-up timer to wake the ESP32 every 10 seconds. The function `esp_sleep_enable_timer_wakeup()` configures the interrupt to trigger after the specified time.

Bluetooth Interrupt Example

The ESP32 can trigger interrupts based on Bluetooth events, such as device connections. Here’s an example to configure a Bluetooth interrupt using the **BluetoothSerial** library.

 

#include "BluetoothSerial.h"

BluetoothSerial SerialBT;

void btCallback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
    if (event == ESP_SPP_SRV_OPEN_EVT) {
        Serial.println("Bluetooth Connected!");
    }
}

void setup() {
    Serial.begin(115200);
    SerialBT.register_callback(btCallback);
    SerialBT.begin("ESP32_Test"); // Device name
    Serial.println("Waiting for Bluetooth connection...");
}

void loop() {
    if (SerialBT.available()) {
        Serial.println("Received data via Bluetooth.");
    }
}

    

Explanation

This example uses the **BluetoothSerial** library to handle Bluetooth connections. The callback function `btCallback()` is invoked when a Bluetooth device connects to the ESP32. The `ESP_SPP_SRV_OPEN_EVT` event indicates that the connection has been established.

WiFi Interrupt Example

The ESP32 can also trigger interrupts based on WiFi events, such as connection or disconnection. The following example demonstrates how to set up a WiFi interrupt.

 

#include <WiFi.h>

void WiFiEvent(WiFiEvent_t event) {
    switch (event) {
        case SYSTEM_EVENT_STA_GOT_IP:
            Serial.println("WiFi Connected, IP Address Obtained");
            break;
        case SYSTEM_EVENT_STA_DISCONNECTED:
            Serial.println("WiFi Disconnected");
            break;
    }
}

void setup() {
    Serial.begin(115200);
    WiFi.onEvent(WiFiEvent);  // Register WiFi event handler
    WiFi.begin("YourSSID", "YourPassword");

    Serial.println("Connecting to WiFi...");
}

void loop() {
    // Regular loop code
}

    

Explanation

In this example, we register a WiFi event handler using `WiFi.onEvent()`. The `WiFiEvent()` function is called when specific WiFi events occur, such as when the device connects to or disconnects from a WiFi network. The **SYSTEM_EVENT_STA_GOT_IP** event indicates that the ESP32 has successfully connected and obtained an IP address, while **SYSTEM_EVENT_STA_DISCONNECTED** is triggered when the WiFi disconnects.

ESP32 RTC (Real-Time Clock) Detailed Description

The ESP32 microcontroller features a Real-Time Clock (RTC), a built-in component that allows the ESP32 to track time and manage power efficiently, even when the main CPU is powered down or in deep sleep mode. The RTC is especially useful for time-sensitive applications or when power efficiency is crucial.

Key Features of ESP32 RTC

How the ESP32 RTC Works

The ESP32 RTC has its own 32.768 kHz crystal oscillator, which helps in maintaining accurate timekeeping even in low-power modes. It can run autonomously when the main cores of the ESP32 are asleep, which enables significant power savings.

The RTC manages the following tasks:

ESP32 RTC Memory

The ESP32 provides special memory regions that are powered independently of the main CPU. This memory allows you to store small amounts of data even when the CPU is in deep sleep mode.

Example Code for Using ESP32 RTC

 
    
// Example code to configure ESP32 RTC

#include "esp_sleep.h"
#include "time.h"

void setup() {
  Serial.begin(115200);

  // Set time struct
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }

  // Configure RTC to wake up from deep sleep
  esp_sleep_enable_timer_wakeup(10 * 1000000); // Wake up after 10 seconds
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
}

void loop() {
  // The loop won't be executed due to deep sleep
}

ESP32 ULP Coprocessor Example

This example shows how to read an analog sensor using the ULP and wake up the ESP32 if a threshold is crossed.

 
#include "esp32/ulp.h"
#include "esp_sleep.h"
#include "driver/adc.h"
#include "esp_log.h"

// Threshold for sensor value to wake up ESP32
#define THRESHOLD 1000

// ULP program declaration
extern const uint8_t ulp_bin_start[] asm("_binary_ulp_program_bin_start");
extern const uint8_t ulp_bin_end[] asm("_binary_ulp_program_bin_end");

void setup() {
    // Initialize serial monitor
    Serial.begin(115200);
    delay(1000);

    // Initialize ULP and load ULP program
    initULP();

    // Go to deep sleep with ULP wakeup enabled
    esp_sleep_enable_ulp_wakeup();
    esp_deep_sleep_start();
}

void loop() {
    // The loop should never run, as the ESP32 is in deep sleep.
}

void initULP() {
    // Load ULP binary program into ULP memory
    size_t ulp_size = ulp_bin_end - ulp_bin_start;
    ulp_load_binary(0, ulp_bin_start, ulp_size);

    // Initialize ADC channel (example: ADC1, Channel 6 / GPIO34)
    adc1_config_width(ADC_WIDTH_BIT_12);
    adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);

    // Set ULP wakeup period to 1 second (1000 ms)
    ulp_set_wakeup_period(0, 1000 * 1000);  // 1 second

    // Start the ULP program
    ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
}

Understanding DMA (Direct Memory Access)

Direct Memory Access (DMA) is a feature used in microcontrollers and other computing systems to transfer data directly between peripherals and memory, bypassing the CPU. By offloading these data transfers to the DMA controller, the CPU can perform other tasks, significantly improving system performance, especially in real-time applications.

How DMA Works

In a typical embedded system without DMA, the CPU is responsible for reading data from a peripheral (e.g., an ADC, UART, or SPI) and writing it to memory, or vice versa. This process consumes significant CPU cycles, especially for high-frequency or large-volume data transfers. DMA automates these transfers by allowing peripherals to communicate directly with memory, reducing the CPU's involvement.

Basic DMA Operation

The DMA controller operates as a separate entity within the microcontroller, managing the data transfer between memory and peripherals. The following sequence outlines the basic operation of a DMA transfer:

  1. Initialization: The CPU configures the DMA controller, specifying the source and destination addresses, the size of the data to be transferred, and the transfer mode.
  2. Trigger: The DMA transfer is triggered by an event, such as a peripheral request (e.g., a timer, UART, or ADC) or a software trigger initiated by the CPU.
  3. Transfer: Once triggered, the DMA controller reads the data from the source (peripheral or memory) and writes it to the destination (memory or peripheral), without CPU intervention.
  4. Completion: After the transfer is complete, the DMA controller can generate an interrupt to notify the CPU, allowing the CPU to handle post-transfer processing if necessary.

DMA Transfer Modes

DMA supports several modes of operation, depending on the needs of the application:

DMA Trigger Sources

DMA transfers can be triggered by a variety of sources, including:

Benefits of Using DMA

DMA provides several advantages over CPU-driven data transfers:

DMA Limitations

Despite its benefits, DMA has some limitations:

Conclusion

DMA is an essential feature in embedded systems, offering improved data transfer efficiency, reduced CPU load, and enhanced real-time performance. By offloading data transfer tasks from the CPU to the DMA controller, system resources are optimized, allowing for more complex and responsive applications. Understanding how to configure and utilize DMA effectively is a key skill in embedded systems development.

DMA Channels for Peripherals

DMA Channel Triggered Peripheral
DMA Channel 0 UART0, SPI0
DMA Channel 1 UART1, SPI1
DMA Channel 2 UART2, SPI2
DMA Channel 3 I2C0, I2S0
DMA Channel 4 I2C1, I2S1
DMA Channel 5 Timer 0, Timer 1
DMA Channel 6 Timer 2, Timer 3
DMA Channel 7 Ethernet, Wi-Fi

Peripherals Triggering DMA Channels

Peripheral Valid DMA Channels
UART0 DMA Channel 0
UART1 DMA Channel 1
UART2 DMA Channel 2
SPI0 DMA Channel 0
SPI1 DMA Channel 1
SPI2 DMA Channel 2
I2C0 DMA Channel 3
I2C1 DMA Channel 4
Timer 0 DMA Channel 5
Timer 1 DMA Channel 5
Timer 2 DMA Channel 6
Timer 3 DMA Channel 6
Ethernet DMA Channel 7
Wi-Fi DMA Channel 7

ESP32 WiFi Modes & ESPconfig Library

The **ESP32** microcontroller comes with a highly integrated **WiFi** module capable of working in **Client mode**, **Station mode**, and **Relay/Range Extender mode**. These modes enable the ESP32 to connect to networks, act as a standalone WiFi access point, or extend WiFi range.

ESPConfig Example in Arduino C

The ESPConfig library by Peter Dunne simplifies the configuration management of ESP8266 and ESP32-based projects. It allows for easy reading and writing of configuration parameters, making your projects more modular and maintainable.

Features of ESPConfig

Source Code

You can find the source code for the ESPConfig library on GitHub: ESPConfig GitHub Repository

Basic Setup

In this example, we will create a simple configuration for an ESP32 device, including settings for Wi-Fi SSID and password.

 
 /*  author: Peter Dunne, peterjazenga@gmail.com
 *  Web interface to Configure WiFi interface
 *  Bluetooth (ESP32 only) control and Configuration interface to phones/tablets/etc.
 
 *  sysConfig.init(); is called after Serial.begin(); and it handles all WiFi, OTA, NAT, Time setting and Bluetooth configuration
 *  WPS is also supported via sysConfig
 *  sysConfig.run(); keeps things updated, provides for servicing of the OTA & Web server issues among others, it also handles the blinking of the status LED
 */

#include "sysconfig32.h";

void setup() {
  Serial.begin(115200);
  sysConfig.init();
  Serial.println("Ready.");  
 }

void loop() {
  sysConfig.run();
}

WiFi Client Mode Example

In **Client mode**, the ESP32 connects to a WiFi network to access the internet or communicate with other network devices. This mode is commonly used to allow the ESP32 to act as an IoT device.

 

#include <Wifi.h>

const char* ssid = "your_SSID";
const char* password = "your_PASSWORD";

void setup() {
    Serial.begin(115200);
    WiFi.begin(ssid, password);
    
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void loop() {
    // Code for internet communication here
}

    

Explanation

This code demonstrates how to connect the ESP32 to a WiFi network in **Client mode**. The `WiFi.begin()` function takes the SSID and password of the WiFi network and attempts to connect. The `WiFi.status()` function checks the connection status, and once connected, the ESP32 is ready for internet communication.

Station Mode Example

In **Station mode**, the ESP32 creates its own WiFi network, allowing other devices to connect to it. This is useful when the ESP32 needs to act as a server for IoT applications or act as a hotspot.

 

#include <Wifi.h>

const char* ssid = "ESP32_AP";
const char* password = "12345678";

void setup() {
    Serial.begin(115200);
    WiFi.softAP(ssid, password);
    Serial.println("ESP32 Access Point is ready");
}

void loop() {
    // Code for server-side or data handling here
}

    

Explanation

In **Station mode**, the ESP32 acts as an **Access Point** (AP), allowing other WiFi-enabled devices to connect to it. The `WiFi.softAP()` function sets up the ESP32's WiFi as an AP, and the device becomes discoverable with the SSID "ESP32_AP" and password "12345678".

Conclusion

The ESP32 is a versatile microcontroller with powerful WiFi capabilities. Whether you need to connect it to an existing network (Client mode), create an access point (Station mode), or extend your network (Relay mode), the ESP32 offers flexible solutions. The **ESPconfig** library by Peter Dunne provides additional tools to make WiFi management even easier.

ESP32 Dual Core Example in Arduino C

This example demonstrates how to use the dual-core capabilities of the ESP32 microcontroller in Arduino. The ESP32 has two processing cores:

Code Description

In this example, we are running two different tasks on the two cores:

We use the FreeRTOS operating system (which is built into the ESP32) to create and manage the tasks. By specifying which core each task should run on, we can take advantage of the ESP32’s dual-core architecture.

Key Functions Used:

Example Code


#include <Arduino.h>

// Pin for LED
#define LED_PIN 2

// Handles for tasks
TaskHandle_t Task1;
TaskHandle_t Task2;

// Function for Task1 (running on Core 0)
void Task1code( void * pvParameters ) {
    Serial.print("Task1 running on core ");
    Serial.println(xPortGetCoreID());

    for(;;) {
        digitalWrite(LED_PIN, HIGH);
        delay(500);
        digitalWrite(LED_PIN, LOW);
        delay(500);
    }
}

// Function for Task2 (running on Core 1)
void Task2code( void * pvParameters ) {
    Serial.print("Task2 running on core ");
    Serial.println(xPortGetCoreID());

    for(;;) {
        Serial.println("Task2 is running");
        delay(1000);
    }
}

void setup() {
    // Initialize Serial Monitor
    Serial.begin(115200);
    delay(1000);

    // Configure LED pin as output
    pinMode(LED_PIN, OUTPUT);

    // Create Task1 on Core 0
    xTaskCreatePinnedToCore(
        Task1code,   // Task function
        "Task1",     // Task name
        10000,       // Stack size
        NULL,        // Task parameters
        1,           // Task priority
        &Task1,      // Task handle
        0            // Core to run on (Core 0)
    );

    // Create Task2 on Core 1
    xTaskCreatePinnedToCore(
        Task2code,   // Task function
        "Task2",     // Task name
        10000,       // Stack size
        NULL,        // Task parameters
        1,           // Task priority
        &Task2,      // Task handle
        1            // Core to run on (Core 1)
    );
}

void loop() {
    // Nothing to do in loop, tasks are handled by FreeRTOS
}
    

Steps to Run the Code:

  1. Copy the code into the Arduino IDE.
  2. Select your ESP32 board from the board manager.
  3. Upload the code to your ESP32.
  4. Open the Serial Monitor (set the baud rate to 115200) to see the output from Task1 and Task2.

Expected Output

In the Serial Monitor, you should see the following messages:

Task1 running on core 0
Task2 running on core 1
Task2 is running
Task2 is running
...

Additionally, the LED connected to GPIO pin 2 will blink every 500 milliseconds.

ESP32 Bluetooth Detailed Description

The ESP32 comes equipped with a powerful Bluetooth module that supports both Bluetooth Classic (BR/EDR) and Bluetooth Low Energy (BLE). This feature makes the ESP32 an ideal choice for wireless communication in a variety of IoT applications.

Key Features of ESP32 Bluetooth

How ESP32 Bluetooth Works

The Bluetooth module in the ESP32 allows it to communicate wirelessly with other Bluetooth-enabled devices, including smartphones, computers, or other IoT devices. The dual-mode capability means that you can use Bluetooth Classic for continuous high-speed communication or BLE for energy-efficient, infrequent data transfers.

Common Use Cases

Example Code for Using ESP32 Bluetooth (BLE)

 
// Example code for ESP32 BLE

#include <BLEDevice>
#include <BLEServer>
#include <BLEUtils>
#include <BLE2902>
            
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
            
// UUIDs for BLE Service and Characteristic
#define SERVICE_UUID        "0000180A-0000-1000-8000-00805F9B34FB"
#define CHARACTERISTIC_UUID "00002A29-0000-1000-8000-00805F9B34FB"
            
void setup() {
  Serial.begin(115200);
  BLEDevice::init("ESP32_Bluetooth");
            
  // Create BLE Server
  pServer = BLEDevice::createServer();
            
  // Create BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);
            
  // Create BLE Characteristic
  pCharacteristic = pService->createCharacteristic(
          CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_READ |
                      BLECharacteristic::PROPERTY_WRITE
                    );
            
  // Start the service
 pService->start();
            
  // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("Waiting for a client to connect...");
}
            
void loop() {
  // If a device connects
  if (pServer->getConnectedCount() > 0 && !deviceConnected) {
    deviceConnected = true;
    Serial.println("Device connected");
  }
            
  // If a device disconnects
  if (pServer->getConnectedCount() == 0 && deviceConnected) {
    deviceConnected = false;
    Serial.println("Device disconnected");
  }
            
  delay(1000); // Wait for 1 second
}