ESP32 C3 C Example Program Reference

ESP32 C3 C Example Program Reference

ESP32 C3 C Example Program Reference

The ESP32-C3 is a low-power RISC-V microcontroller from Espressif with Wi-Fi and Bluetooth 5 (LE) support. It is programmed using the ESP-IDF framework (v5.x recommended) in C. All examples below use ESP-IDF APIs and assume a standard project structure created via idf.py create-project or the ESP-IDF examples template.

Compile and flash with:

idf.py set-target esp32c3
idf.py build flash monitor

This toggles GPIO2 (often connected to an onboard LED on many ESP32-C3 boards; active-low) every 1 ms.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"

#define GPIO_PIN    2

void app_main(void)
{
    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL << GPIO_PIN),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };
    gpio_config(&io_conf);

    while (1) {
        gpio_set_level(GPIO_PIN, 1);
        vTaskDelay(pdMS_TO_TICKS(1));
        gpio_set_level(GPIO_PIN, 0);
        vTaskDelay(pdMS_TO_TICKS(1));
    }
}

Note: vTaskDelay() provides millisecond resolution with FreeRTOS tick accuracy (default 1 ms).

2. Read ADC 20 times per second and append to a file

The ESP32-C3 has two ADC units (ADC1 with 6 channels on GPIO0–5). It supports 12-bit resolution in oneshot mode.

No built-in filesystem exists in basic ESP-IDF applications. File appending requires mounting an SD card (via SPI/SDMMC) or using SPIFFS/FAT on internal flash (requires partition table configuration). For simplicity, this example reads ADC1 channel 0 (GPIO0) at 20 Hz and prints values over USB CDC serial (visible in idf.py monitor).

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"

void app_main(void)
{
    adc_oneshot_unit_handle_t adc_handle;
    adc_oneshot_unit_init_cfg_t init_cfg = {
        .unit_id = ADC_UNIT_1,
        .ulp_mode = ADC_ULP_MODE_DISABLE,
    };
    ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_cfg, &adc_handle));

    adc_oneshot_chan_cfg_t chan_cfg = {
        .bitwidth = ADC_BITWIDTH_DEFAULT,
        .atten = ADC_ATTEN_DB_11,          // Full range ~0–3.3 V
    };
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL_0, &chan_cfg));

    while (1) {
        int raw;
        ESP_ERROR_CHECK(adc_oneshot_read(adc_handle, ADC_CHANNEL_0, &raw));
        printf("ADC raw: %d\n", raw);
        vTaskDelay(pdMS_TO_TICKS(50));     // 20 Hz
    }

    // Cleanup (not reached in this loop)
    adc_oneshot_del_unit(adc_handle);
}

CMakeLists.txt addition (for USB CDC console):

idf_component_register(... EMBED_FILES "tinyusb_config.c" ...)
# Or use menuconfig: Component config → ESP System Settings → Channel for console output → USB CDC

For actual file logging, enable SPIFFS in menuconfig and use fopen/fprintf after esp_vfs_spiffs_register().

3. PWM example with 5 different duty cycles

The ESP32-C3 uses the LEDC (LED Control) module for PWM (6 channels, up to 40 MHz resolution depending on clock).

This example configures LEDC channel 0 on GPIO2 at ~1 kHz and cycles through 0%, 25%, 50%, 75%, 100% duty.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"

#define PWM_GPIO    2
#define PWM_FREQ    1000
#define PWM_RES     LEDC_TIMER_10_BIT   // 1024 levels

void app_main(void)
{
    ledc_timer_config_t timer_conf = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .duty_resolution = PWM_RES,
        .timer_num = LEDC_TIMER_0,
        .freq_hz = PWM_FREQ,
        .clk_cfg = LEDC_AUTO_CLK
    };
    ledc_timer_config(&timer_conf);

    ledc_channel_config_t chan_conf = {
        .gpio_num = PWM_GPIO,
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .channel = LEDC_CHANNEL_0,
        .intr_type = LEDC_INTR_DISABLE,
        .timer_sel = LEDC_TIMER_0,
        .duty = 0,
        .hpoint = 0
    };
    ledc_channel_config(&chan_conf);

    const uint32_t duties[] = {0, 256, 512, 768, 1023};  // 0–1023 for 10-bit

    while (1) {
        for (int i = 0; i < 5; i++) {
            ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duties[i]);
            ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
            printf("Duty: %u / 1023 (%.0f%%)\n", duties[i], duties[i] * 100.0 / 1023);
            vTaskDelay(pdMS_TO_TICKS(2000));
        }
    }
}

4. Output to serial port (USB CDC or UART)

The ESP32-C3 supports USB CDC (native USB serial) and two UARTs (UART0 default for console, UART1 available).

This example uses USB CDC (most convenient) for printf output.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

static const char *TAG = "example";

void app_main(void)
{
    uint32_t counter = 0;

    while (1) {
        printf("USB CDC counter: %lu\n", counter++);
        ESP_LOGI(TAG, "Also visible via ESP_LOG at same baud");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

To enable USB CDC console:

  • In menuconfig: Component config → ESP System Settings → Channel for console output → USB CDC/JTAG
  • Or UART0 (GPIO20 TX / GPIO21 RX) if using external USB-to-serial adapter.

For explicit UART0 usage:

uart_config_t uart_config = {
    .baud_rate = 115200,
    .data_bits = UART_DATA_8_BITS,
    .parity    = UART_PARITY_DISABLE,
    .stop_bits = UART_STOP_BITS_1,
    .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
uart_param_config(UART_NUM_0, &uart_config);
uart_set_pin(UART_NUM_0, 20, 21, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
uart_driver_install(UART_NUM_0, 256, 0, 0, NULL, 0);

5. Clocks on the ESP32-C3 and how to read them

The ESP32-C3 clock tree includes several root sources and derived module clocks:

  • RC_FAST — Internal ~17.5 MHz RC oscillator
  • XTAL — External 40 MHz crystal (most common source)
  • RC_SLOW — Internal ~136 kHz RC oscillator
  • XTAL32K — External 32.768 kHz crystal (optional, for RTC)
  • PLL — Multiplies XTAL (typically to 80/160 MHz for CPU)
  • CPU_CLK — Usually PLL-derived (80 MHz default)
  • APB_CLK — Usually 80 MHz
  • REF_TICK — 1 MHz reference
  • PLL_48M / APLL — For USB/ peripherals when needed

Use the clock tree API (esp_clk_tree_xxx) introduced in newer ESP-IDF versions to read frequencies reliably.

Example program printing common clock frequencies:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_clk_tree.h"

void app_main(void)
{
    uint32_t freq_hz;

    while (1) {
        esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_CPU, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq_hz);
        printf("CPU_CLK:          %lu Hz\n", freq_hz);

        esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_APB, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq_hz);
        printf("APB_CLK:          %lu Hz\n", freq_hz);

        esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_XTAL, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq_hz);
        printf("XTAL:             %lu Hz\n", freq_hz);

        esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_RC_FAST, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq_hz);
        printf("RC_FAST:          ~%lu Hz\n", freq_hz);

        esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_PLL_F48M, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq_hz);
        printf("PLL_48M (USB):    %lu Hz\n", freq_hz);

        printf("------------------------\n");
        vTaskDelay(pdMS_TO_TICKS(3000));
    }
}

Typical default values (stock configuration with 40 MHz XTAL):

  • CPU_CLK ≈ 80 000 000 Hz
  • APB_CLK ≈ 80 000 000 Hz
  • XTAL = 40 000 000 Hz
  • RC_FAST ≈ 17 500 000 Hz
  • PLL-derived clocks (e.g., 48 MHz for USB when enabled)

For older ESP-IDF versions without esp_clk_tree_xxx, use esp_clk functions or read SYSTEM_SYSCLK_CONF_REG / SYSTEM_CPU_PER_CONF_REG manually (less recommended).

If you require continuous ADC mode (DMA), SD card logging, or specific board variants (e.g., ESP32-C3-DevKitM-1), please provide further details for refinement.

Here is a single, self-contained ESP-IDF example that demonstrates most of the requested ESP32-C3 peripherals in one program. Not every feature can be meaningfully used at the same time (hardware conflicts, pin multiplexing, interrupt load, etc.), so this example selectively shows the most representative and commonly used parts.

It includes:

  • USB CDC console output
  • ADC (oneshot mode + temperature sensor)
  • LEDC (PWM) – 3 channels
  • RMT – simple WS2812 / NeoPixel-like output on one channel
  • I²C master – scanning for devices
  • SPI2 master – loopback test (MOSI → MISO)
  • UART1 – secondary UART echo
  • TWAI (CAN) – basic transmit + receive statistics
  • Pulse counter – counting pulses on a GPIO
  • RTC watchdog – basic demonstration
  • General-purpose timer – periodic callback
  • GDMA – simple memory-to-memory transfer
// main.c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "driver/i2c_master.h"
#include "driver/spi_master.h"
#include "driver/uart.h"
#include "driver/twai.h"
#include "driver/pulse_cnt.h"
#include "driver/timer.h"
#include "driver/gdma.h"
#include "driver/wdt.h"
#include "soc/gdma_reg.h"
#include "soc/rmt_reg.h"

// ────────────────────────────────────────────────
//  Configuration – change pins according to your board
// ────────────────────────────────────────────────

#define EXAMPLE_LED_PWM_GPIO_1      2
#define EXAMPLE_LED_PWM_GPIO_2      3
#define EXAMPLE_LED_PWM_GPIO_3      4

#define EXAMPLE_RMT_TX_GPIO         5      // WS2812 data line
#define EXAMPLE_I2C_SDA             6
#define EXAMPLE_I2C_SCL             7
#define EXAMPLE_SPI_MOSI            10
#define EXAMPLE_SPI_MISO            11
#define EXAMPLE_SPI_SCLK            12
#define EXAMPLE_SPI_CS              13
#define EXAMPLE_UART1_TX            17
#define EXAMPLE_UART1_RX            18
#define EXAMPLE_PCNT_GPIO           19
#define EXAMPLE_TWAI_TX             20
#define EXAMPLE_TWAI_RX             21

static const char *TAG = "esp32c3-demo";

// ────────────────────────────────────────────────
//  1. USB CDC is used automatically when console is set to USB CDC in menuconfig
// ────────────────────────────────────────────────

// ────────────────────────────────────────────────
//  2. ADC + Temperature sensor
// ────────────────────────────────────────────────

static adc_oneshot_unit_handle_t adc_handle = NULL;
static adc_cali_handle_t cali_handle = NULL;

static void adc_init(void)
{
    adc_oneshot_unit_init_cfg_t init_cfg = {
        .unit_id = ADC_UNIT_1,
        .ulp_mode = ADC_ULP_MODE_DISABLE,
    };
    ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_cfg, &adc_handle));

    adc_oneshot_chan_cfg_t chan_cfg = {
        .bitwidth = ADC_BITWIDTH_DEFAULT,
        .atten = ADC_ATTEN_DB_11,
    };
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL_0, &chan_cfg));

    // Temperature sensor (only on ADC1)
    ESP_ERROR_CHECK(adc_oneshot_chan_cfg_t temp_cfg = {
        .bitwidth = ADC_BITWIDTH_DEFAULT,
        .atten = ADC_ATTEN_DB_0,
    });
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL_TEMP, &temp_cfg));

    // Optional: calibration (curve fitting)
    adc_cali_curve_fitting_config_t cali_cfg = {
        .unit_id = ADC_UNIT_1,
        .atten = ADC_ATTEN_DB_11,
        .bitwidth = ADC_BITWIDTH_DEFAULT,
    };
    adc_cali_create_scheme_curve_fitting(&cali_cfg, &cali_handle);
}

static void print_adc_and_temp(void)
{
    int raw, voltage = 0;
    adc_oneshot_read(adc_handle, ADC_CHANNEL_0, &raw);
    if (cali_handle) adc_cali_raw_to_voltage(cali_handle, raw, &voltage);

    int temp_raw;
    adc_oneshot_read(adc_handle, ADC_CHANNEL_TEMP, &temp_raw);
    float temp = (temp_raw - 800) / 3.9f; // very rough approximation

    ESP_LOGI(TAG, "ADC0 raw=%d ≈ %d mV   Temp sensor raw=%d ≈ %.1f °C",
             raw, voltage, temp_raw, temp);
}

// ────────────────────────────────────────────────
//  3. LEDC PWM – 3 channels
// ────────────────────────────────────────────────

static void ledc_init(void)
{
    ledc_timer_config_t timer = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .duty_resolution = LEDC_TIMER_10_BIT,
        .timer_num = LEDC_TIMER_0,
        .freq_hz = 5000,
        .clk_cfg = LEDC_AUTO_CLK
    };
    ledc_timer_config(&timer);

    ledc_channel_config_t channels[] = {
        {EXAMPLE_LED_PWM_GPIO_1, LEDC_CHANNEL_0},
        {EXAMPLE_LED_PWM_GPIO_2, LEDC_CHANNEL_1},
        {EXAMPLE_LED_PWM_GPIO_3, LEDC_CHANNEL_2},
    };

    for (int i = 0; i < 3; i++) {
        ledc_channel_config_t ch = {
            .gpio_num = channels[i].gpio_num,
            .speed_mode = LEDC_LOW_SPEED_MODE,
            .channel = channels[i].channel,
            .intr_type = LEDC_INTR_DISABLE,
            .timer_sel = LEDC_TIMER_0,
            .duty = 0,
            .hpoint = 0
        };
        ledc_channel_config(&ch);
    }
}

static void ledc_fade_example(void)
{
    static uint32_t phase = 0;
    for (int ch = 0; ch < 3; ch++) {
        uint32_t duty = (phase + ch * 341) % 1024;
        ledc_set_duty(LEDC_LOW_SPEED_MODE, ch, duty);
        ledc_update_duty(LEDC_LOW_SPEED_MODE, ch);
    }
    phase = (phase + 8) & 1023;
}

// ────────────────────────────────────────────────
//  4. RMT – simple WS2812 / NeoPixel-like output
// ────────────────────────────────────────────────

static rmt_channel_handle_t rmt_tx_chan = NULL;

static void rmt_init(void)
{
    rmt_tx_channel_config_t tx_chan_config = {
        .gpio_num = EXAMPLE_RMT_TX_GPIO,
        .clk_src = RMT_CLK_SRC_DEFAULT,
        .resolution_hz = 10 * 1000 * 1000, // 10 MHz
        .mem_block_symbols = 64,
        .trans_queue_depth = 4,
    };
    rmt_new_tx_channel(&tx_chan_config, &rmt_tx_chan);

    // Very simple 800 kHz WS2812 zero/one symbols
    rmt_symbol_word_t ws2812_zero = {
        .duration0 = 32, .level0 = 1,
        .duration1 = 18, .level1 = 0,
    };
    rmt_symbol_word_t ws2812_one = {
        .duration0 = 18, .level0 = 1,
        .duration1 = 32, .level1 = 0,
    };

    // You would normally use an encoder here – this is simplified
    ESP_LOGI(TAG, "RMT TX channel created on GPIO %d", EXAMPLE_RMT_TX_GPIO);
}

// ────────────────────────────────────────────────
//  5. I²C master – scan example
// ────────────────────────────────────────────────

static i2c_master_bus_handle_t i2c_bus = NULL;

static void i2c_init(void)
{
    i2c_master_bus_config_t bus_cfg = {
        .i2c_port = I2C_NUM_0,
        .sda_io_num = EXAMPLE_I2C_SDA,
        .scl_io_num = EXAMPLE_I2C_SCL,
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .glitch_ignore_cnt = 7,
        .flags.enable_internal_pullup = true,
    };
    i2c_new_master_bus(&bus_cfg, &i2c_bus);
}

static void i2c_scan(void)
{
    ESP_LOGI(TAG, "Scanning I2C bus...");
    for (uint8_t addr = 0x08; addr <= 0x77; addr++) {
        i2c_device_config_t dev_cfg = {
            .dev_addr_length = I2C_ADDR_BIT_LEN_7,
            .device_address = addr,
            .scl_speed_hz = 100000,
        };
        i2c_master_dev_handle_t dev_handle;
        if (i2c_master_probe(i2c_bus, addr, 100 / portTICK_PERIOD_MS) == ESP_OK) {
            ESP_LOGI(TAG, "Device found at 0x%02X", addr);
        }
    }
}

// ────────────────────────────────────────────────
//  6. SPI2 master – loopback test (connect MOSI ↔ MISO)
// ────────────────────────────────────────────────

static spi_device_handle_t spi = NULL;

static void spi_init(void)
{
    spi_bus_config_t buscfg = {
        .mosi_io_num = EXAMPLE_SPI_MOSI,
        .miso_io_num = EXAMPLE_SPI_MISO,
        .sclk_io_num = EXAMPLE_SPI_SCLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
    };
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 10 * 1000 * 1000,
        .mode = 0,
        .spics_io_num = EXAMPLE_SPI_CS,
        .queue_size = 7,
    };

    spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
    spi_bus_add_device(SPI2_HOST, &devcfg, &spi);
}

static void spi_loopback_test(void)
{
    uint8_t tx[8] = {0xAA, 0x55, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
    uint8_t rx[8] = {0};

    spi_transaction_t t = {
        .length = 8 * 8,
        .tx_buffer = tx,
        .rx_buffer = rx,
    };

    spi_device_transmit(spi, &t);

    bool ok = true;
    for (int i = 0; i < 8; i++) if (tx[i] != rx[i]) ok = false;

    ESP_LOGI(TAG, "SPI loopback test: %s", ok ? "PASS" : "FAIL");
}

// ────────────────────────────────────────────────
//  7. UART1 – secondary UART echo
// ────────────────────────────────────────────────

static void uart1_init(void)
{
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    uart_driver_install(UART_NUM_1, 256, 256, 0, NULL, 0);
    uart_param_config(UART_NUM_1, &uart_config);
    uart_set_pin(UART_NUM_1, EXAMPLE_UART1_TX, EXAMPLE_UART1_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}

static void uart1_echo_task(void *arg)
{
    uint8_t data[128];
    while (1) {
        int len = uart_read_bytes(UART_NUM_1, data, sizeof(data), pdMS_TO_TICKS(100));
        if (len > 0) {
            uart_write_bytes(UART_NUM_1, data, len);
            ESP_LOGI(TAG, "UART1 echoed %d bytes", len);
        }
    }
}

// ────────────────────────────────────────────────
//  8. TWAI (CAN)
// ────────────────────────────────────────────────

static void twai_init(void)
{
    twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(EXAMPLE_TWAI_TX, EXAMPLE_TWAI_RX, TWAI_MODE_NORMAL);
    twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS();
    twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();

    twai_driver_install(&g_config, &t_config, &f_config);
    twai_start();
}

static void twai_example(void)
{
    twai_message_t message = {
        .identifier = 0x123,
        .extd = 0,
        .data_length_code = 8,
        .data = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x11, 0x22, 0x33}
    };

    if (twai_transmit(&message, pdMS_TO_TICKS(100)) == ESP_OK) {
        ESP_LOGI(TAG, "TWAI message queued");
    }

    twai_status_info_t status;
    twai_get_status_info(&status);
    ESP_LOGI(TAG, "TWAI rx packets: %lu  tx packets: %lu  errors: %lu",
             status.rx_overrun, status.tx_ok, status.tx_error_counter);
}

// ────────────────────────────────────────────────
//  9. Pulse Counter (PCNT)
// ────────────────────────────────────────────────

static pcnt_unit_handle_t pcnt_unit = NULL;

static void pcnt_init(void)
{
    pcnt_unit_config_t unit_config = {
        .high_limit = 10000,
        .low_limit = -10000,
    };
    pcnt_new_unit(&unit_config, &pcnt_unit);

    pcnt_chan_config_t chan_config = {
        .edge_gpio_num = EXAMPLE_PCNT_GPIO,
        .level_gpio_num = -1,
    };
    pcnt_channel_handle_t pcnt_chan;
    pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan);

    pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD);
    pcnt_unit_enable(pcnt_unit);
    pcnt_unit_clear_count(pcnt_unit);
    pcnt_unit_start(pcnt_unit);
}

static void print_pcnt(void)
{
    int count;
    pcnt_unit_get_count(pcnt_unit, &count);
    ESP_LOGI(TAG, "Pulse counter: %d", count);
}

// ────────────────────────────────────────────────
//  10. Timer (general purpose)
// ────────────────────────────────────────────────

static timer_isr_handle_t timer_isr = NULL;

static bool IRAM_ATTR timer_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
    static uint32_t cnt = 0;
    cnt++;
    if (cnt % 100 == 0) {
        ESP_LOGI(TAG, "Timer callback @ 1 Hz");
    }
    return false;
}

static void timer_init(void)
{
    gptimer_config_t timer_config = {
        .clk_src = GPTIMER_CLK_SRC_DEFAULT,
        .direction = GPTIMER_COUNT_UP,
        .resolution_hz = 1000000, // 1 MHz
    };
    gptimer_handle_t gptimer = NULL;
    gptimer_new_timer(&timer_config, &gptimer);

    gptimer_event_callbacks_t cbs = {
        .on_alarm = timer_callback,
    };
    gptimer_register_event_callbacks(gptimer, &cbs, NULL);

    gptimer_alarm_config_t alarm_config = {
        .reload_count = 0,
        .alarm_count = 1000000, // 1 second
        .flags.auto_reload_on_alarm = true,
    };
    gptimer_set_alarm_action(gptimer, &alarm_config);

    gptimer_enable(gptimer);
    gptimer_start(gptimer);
}

// ────────────────────────────────────────────────
//  11. RTC Watchdog (simple enable)
// ────────────────────────────────────────────────

static void rtc_watchdog_demo(void)
{
    // Minimal configuration – will reset after ~3–5 s if not fed
    esp_task_wdt_config_t twdt_config = {
        .timeout_ms = 5000,
        .idle_core_mask = 0,
        .trigger_panic = true,
    };
    esp_task_wdt_deinit(); // in case already initialized
    esp_task_wdt_init(&twdt_config);
    esp_task_wdt_add(NULL); // subscribe current task
    // esp_task_wdt_reset() must be called periodically in production code
}

// ────────────────────────────────────────────────
//  12. GDMA – simple memory-to-memory copy
// ────────────────────────────────────────────────

static gdma_channel_handle_t dma_tx = NULL;

static void gdma_init(void)
{
    gdma_channel_config_t tx_config = {
        .direction = GDMA_CHANNEL_DIRECTION_TX,
        .priority = 5,
    };
    gdma_new_channel(&tx_config, &dma_tx);

    // Very basic demonstration – normally used with peripheral
    ESP_LOGI(TAG, "GDMA TX channel created");
}

// ────────────────────────────────────────────────
//  Main application
// ────────────────────────────────────────────────

void app_main(void)
{
    ESP_LOGI(TAG, "ESP32-C3 multi-peripheral demonstration");

    adc_init();
    ledc_init();
    rmt_init();
    i2c_init();
    spi_init();
    uart1_init();
    twai_init();
    pcnt_init();
    timer_init();
    gdma_init();
    rtc_watchdog_demo();

    xTaskCreate(uart1_echo_task, "uart1_echo", 2048, NULL, 5, NULL);

    uint32_t cycle = 0;

    while (1) {
        cycle++;

        print_adc_and_temp();
        ledc_fade_example();
        spi_loopback_test();
        i2c_scan();
        twai_example();
        print_pcnt();

        // Feed watchdog every loop
        esp_task_wdt_reset();

        ESP_LOGI(TAG, "──────────────────── Cycle %lu ────────────────────", cycle);
        vTaskDelay(pdMS_TO_TICKS(3000));
    }
}

Important Notes

  • Many pins are shared → you must choose non-conflicting GPIOs for your board.
  • Enable USB CDC in menuconfig (idf.py menuconfig → Component config → ESP System Settings → Channel for console output → USB CDC/JTAG).
  • TWAI needs proper transceiver (e.g. SN65HVD230) connected to TX/RX pins.
  • RMT example is incomplete (no real encoder) – use led_strip component for real WS2812.
  • GDMA example is minimal – real usage usually involves peripherals (SPI, I²S, UART, RMT…).
  • Pulse counter needs external pulses on the chosen GPIO.

This code serves mainly as a starting point and reference showing how to initialize and use most of the requested peripherals.

Let me know which peripheral you would like to see in more depth or with a complete realistic application example (e.g. NeoPixel driver, full CAN node, I²S audio output, etc.).

The ESP32-C3 is a highly integrated, ultra-low-power system-on-chip (SoC) developed by Espressif Systems. It combines wireless connectivity with a modern processor architecture and a rich set of peripherals, making it suitable for secure, battery-powered IoT applications such as smart home devices, sensors, wearables, industrial automation, and low-power wireless nodes.

Below is a comprehensive list of its key capabilities, drawn from the official Espressif ESP32-C3 Series Datasheet and Technical Reference Manual.

Processor and Memory

  • 32-bit single-core RISC-V processor (RV32IMC instruction set) operating at up to 160 MHz.
  • Performance: Approximately 407 CoreMark at 160 MHz (≈2.55 CoreMark/MHz).
  • On-chip memory:
  • 384 KB ROM
  • 400 KB SRAM (including 16 KB cache)
  • 8 KB SRAM in RTC domain (low-power retention)
  • Support for external SPI flash (via SPI, Dual SPI, Quad SPI, QPI interfaces), with cache acceleration for faster access.
  • In-circuit flash programming (ICP) support.

Wireless Connectivity

  • Wi-Fi:
  • IEEE 802.11b/g/n compliant (2.4 GHz band only)
  • 20 MHz and 40 MHz channel bandwidth
  • 1T1R (1 transmit, 1 receive) antenna configuration
  • Maximum data rate: 150 Mbps
  • Modes: Station, SoftAP, SoftAP + Station, promiscuous mode
  • Advanced features: WMM, A-MPDU/A-MSDU, TXOP, immediate Block ACK, fragmentation/defragmentation, hardware TSF (beacon monitoring), antenna diversity, 802.11mc FTM (Fine Timing Measurement)
  • Up to 4 virtual Wi-Fi interfaces
  • Bluetooth:
  • Bluetooth 5 (LE) and Bluetooth mesh support
  • Long Range (LR) mode
  • PHY rates: 125 kbps, 500 kbps, 1 Mbps, 2 Mbps
  • Advertising extensions, multiple advertisement sets, Channel Selection Algorithm #2
  • Internal Wi-Fi + Bluetooth coexistence mechanism (shared antenna)

Security Features

  • Hardware accelerators for:
  • AES-128/256 encryption/decryption
  • SHA (hashing)
  • RSA
  • HMAC
  • Digital signature
  • Random number generator (RNG)
  • Secure Boot
  • Flash encryption (including external memory)
  • Access permission control for memory and peripherals
  • Cryptographic hardware support for secure communication and firmware protection

Peripherals and Interfaces

  • GPIO: 22 programmable GPIOs (multiplexed with peripheral functions)
  • Digital interfaces:
  • 3 × SPI (SPI0/SPI1 for flash, SPI2 for general use)
  • 2 × UART
  • 1 × I²C
  • 1 × I²S
  • Remote Control peripheral (RMT): 2 TX + 2 RX channels (for IR, LED strips, etc.)
  • LED PWM controller: Up to 6 independent channels
  • TWAI® controller (compatible with ISO 11898-1, i.e., CAN 2.0)
  • Full-speed USB Serial/JTAG controller (for programming, debugging, and CDC serial console)
  • General DMA controller (GDMA): 3 TX + 3 RX channels
  • Analog interfaces:
  • SAR ADC: Two units, up to 6 channels total, 12-bit resolution
  • Temperature sensor
  • Timers and counters:
  • General-purpose timers
  • System timers
  • Watchdog timers (including RTC watchdog)
  • Other:
  • Pulse counter
  • Camera/LCD interface support (limited)
  • RTC peripherals (low-power domain)

Power Management

  • Multiple low-power modes: Active, Modem-sleep, Light-sleep, Deep-sleep
  • Ultra-low power consumption in sleep modes (e.g., deep sleep with RTC memory retention: ~5 µA typical)
  • Dynamic voltage/frequency scaling
  • Power management unit (PMU) with fine-grained control

Additional Capabilities

  • Operating voltage: 3.0–3.6 V
  • Ambient temperature range: –40 °C to +85 °C or –40 °C to +105 °C (depending on variant)
  • Package: QFN32 (5 × 5 mm)
  • Variants: ESP32-C3 (no embedded flash), ESP32-C3FN4/FH4 (4 MB embedded flash in package)
  • Development support: Full compatibility with ESP-IDF, Arduino core, MicroPython, and other ecosystems

The ESP32-C3 emphasizes cost-efficiency, security, and low power while retaining strong wireless performance. For the most authoritative and up-to-date details, consult the official Espressif documentation, particularly the ESP32-C3 Series Datasheet and Technical Reference Manual. If you require clarification on any specific peripheral or comparison with other ESP32 variants, please provide additional details.

Linux Rocks Every Day