simple programming of the esp32-s3-devkitc-1 using esp-idf

ESP32-S3-DevKitC-1 with externally wired LED — drawn with KiCad 6

For some number of weeks I’ve been experimenting with the Espressif ESP32-S3-DevKitC-1-N8R8 and ESP32-C3-DevKitC-02. I purchased them both from Adafruit. In this post I’m going to write about the ESP32-S3, or S3.

These boards with newish processors are marked as experimental with no mainline support either in the Arduino IDE nor Micro Python/Circuit Python. I initially tried to build a version of Micro Python from directions I found on the web that would work in the S3 board using the ESP-IDF tools, but while Micro Python would compile and load, it wouldn’t work. When I decided I’d wasted enough time trying to get Micro Python to work, I switched to the pure C/C++ development tools in the ESP-IDF with both platforms and found much success and satisfaction.

I turned to learning a bit more about using freeRTOS with the S3 board, and to that end I created a variant of the standard blink application I call dualblink. The code will blink the on-board NeoPixel as well as an off-board LED, as shown in the simple schematic diagram at the start of this post.

Since the board is powered by the micro USB port labeled UART in the image below, there is no other power attachment needed. All you need to hook up the external LED is the LED and a 1KΩ resister wired as shown above. Then compile the code using the ESP-IDF tool,, flash it to the device, and sit back and watch the blinkin’ lights.

This code started as an ESP-IDF example called blink. It’s evolved such that the code starts two tasks, one to blink the on-board NeoPixel LED and one to blink the off-board regular LED. The NeoPixel cycles through five colors and has a 1 second period with a 50% duty cycle (500ms on, 500ms off). The second LED task has a 1.1 second period with a 9% duty cycle (100 mS on, 1 second off). Both tasks are started in app_main(). The code should be pretty self-explanatory.


  This example code is in the Public Domain (or CC0 licensed, at your option.)

  Unless required by applicable law or agreed to in writing, this
  software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  CONDITIONS OF ANY KIND, either express or implied.
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "led_strip.h"
#include "sdkconfig.h"

static const char *TAG = "DUAL_BLINK";

static void vTaskBlinkNeoPixel(void * pvParameters) {
    static led_strip_t *pStrip_a;
    pStrip_a = led_strip_init(CONFIG_BLINK_LED_RMT_CHANNEL, CONFIG_BLINK_GPIO, 1);
    pStrip_a->clear(pStrip_a, 50);

    static uint color_select = 0;
    #define NUM_COLORS 5
    static int led_colors[NUM_COLORS][4] = {
        {0, 32, 0, 0},  // red
        {0, 0, 32, 0},  // green
        {0, 0, 0, 32},  // blue
        {0, 32, 0, 16}, // violet
        {0, 0, 32, 32}  // cyan

    for( ;; ) {
        // Set NeoPixel LED to a color using RGB from 0 (0%) to 255 (100%)
        // for each color.
        pStrip_a->set_pixel(pStrip_a, led_colors[color_select][0],
        if (color_select >= NUM_COLORS) color_select = 0;

        // Refresh the strip to send data
        pStrip_a->refresh(pStrip_a, 100);
        vTaskDelay(500 / portTICK_PERIOD_MS);

        // Set NeoPixel LED dark by clearing all its pixels.
        pStrip_a->clear(pStrip_a, 50);
        vTaskDelay(500 / portTICK_PERIOD_MS);

static void vTaskBlinkLED(void * pvParameters) {
    #define BLINK_GPIO46_LED 46
    // Set the GPIO as a push/pull output
    gpio_set_direction(BLINK_GPIO46_LED, GPIO_MODE_OUTPUT);

    for ( ;; ) {
        gpio_set_level(BLINK_GPIO46_LED, true);   // LED on
        vTaskDelay(100 / portTICK_PERIOD_MS);
        gpio_set_level(BLINK_GPIO46_LED, false);  // LED off
        vTaskDelay(2000 / portTICK_PERIOD_MS);

void app_main(void) {
    int core = xPortGetCoreID();
    ESP_LOGI(TAG, "app_main running on core %i", core);
    // BaseType_t xReturned;
    TaskHandle_t xHandle = NULL;
    static uint8_t ucParameterToPass = 1;


    TaskHandle_t xHandle2 = NULL;
    static uint8_t ucParameterToPass2 = 1;


    while (true) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);

Here’s what a typical run looks like when you run -p /dev/ttyUSB0 flash monitor.

Build:Mar 27 2021
rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
mode:DIO, clock div:1
entry 0x403b6248
I (24) boot: ESP-IDF v5.0-dev-1554-g20847eeb96-dirty 2nd stage bootloader
I (25) boot: compile time 21:55:49
I (25) boot: chip revision: 0
I (28) boot.esp32s3: Boot SPI Speed : 80MHz
I (33) boot.esp32s3: SPI Mode       : DIO
I (38) boot.esp32s3: SPI Flash Size : 2MB
I (43) boot: Enabling RNG early entropy source...
I (48) boot: Partition Table:
I (52) boot: ## Label            Usage          Type ST Offset   Length
I (59) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (66) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (74) boot:  2 factory          factory app      00 00 00010000 00100000
I (81) boot: End of partition table
I (86) esp_image: segment 0: paddr=00010020 vaddr=3c020020 size=09148h ( 37192) map
I (101) esp_image: segment 1: paddr=00019170 vaddr=3fc919c0 size=027a4h ( 10148) load
I (105) esp_image: segment 2: paddr=0001b91c vaddr=40374000 size=046fch ( 18172) load
I (115) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=19414h (103444) map
I (138) esp_image: segment 4: paddr=0003943c vaddr=403786fc size=092bch ( 37564) load
I (147) esp_image: segment 5: paddr=00042700 vaddr=50000000 size=00010h (    16) load
I (152) boot: Loaded app from partition at offset 0x10000
I (152) boot: Disabling RNG early entropy source...
I (167) cpu_start: Pro cpu up.
I (167) cpu_start: Starting app cpu, entry point is 0x403751c4
0x403751c4: call_start_cpu1 at /home/popos/Develop/esp/esp-idf/components/esp_system/port/cpu_start.c:152

I (0) cpu_start: App cpu up.
I (181) cpu_start: Pro cpu start user code
I (181) cpu_start: cpu freq: 240000000 Hz
I (181) cpu_start: Application information:
I (184) cpu_start: Project name:     dualblink
I (189) cpu_start: App version:      1
I (193) cpu_start: Compile time:     Mar 21 2022 21:55:46
I (199) cpu_start: ELF file SHA256:  47cfacc829e236ab...
I (205) cpu_start: ESP-IDF:          v5.0-dev-1554-g20847eeb96-dirty
I (213) heap_init: Initializing. RAM available for dynamic allocation:
I (220) heap_init: At 3FC94B98 len 0004B468 (301 KiB): D/IRAM
I (226) heap_init: At 3FCE0000 len 0000EE34 (59 KiB): STACK/DRAM
I (233) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (239) heap_init: At 600FE000 len 00002000 (8 KiB): RTCRAM
I (246) spi_flash: detected chip: generic
I (250) spi_flash: flash io: dio
W (254) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (267) sleep: Configure to isolate all GPIO pins in sleep state
I (274) sleep: Enable automatic switching of GPIO sleep configuration
I (281) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (301) DUAL_BLINK: app_main running on core 0
I (321) gpio: GPIO[46]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 

Note that the cpu frequency is 240 MHz (line 37). This is selected through menuconfig and then Component config -> ESP32S3-Specific -> CPU frequency. The default is currently 160 MHz even though the board is sold with an advertised 240 MHz operational frequency.

Device pinout



via The Guardian

The photo above seems to sum up my impression of the Ukrainian people against the Russians. Attempting to hold out against overwhelming forces in the middle of the destruction against many of their major cities, especially Kyiv. Putin is now committed to a course of action he won’t abandon until, and unless, he faces overwhelming defeat. And I strongly believe he’s barely gotten started in how much cruel and destructive force he can muster against Ukraine, and now anyone else daring to support Ukraine. Putin does not care. He is this era’s Stalin, who sent tens of millions into the golags, and who was responsible for the Holodomor, or Terror-Famine, from 1932 to 1933 that killed between 3.5 and 4 million Ukrainians. Some say that it was Stalin’s attempt to crush a Ukrainian independence movement at that time. Putin wants to repeat history, this time by force of arms.

If Putin can’t do it alone, it would appear that Putin’s running dog lackey and Chechen warlord Ramzan Kadyrov is in Ukraine, near Kyiv, leading his fighters against Ukraines and other Chechen fighters fighting for Ukraine. Apocalyptic times ahead for sure.