User Tools

Site Tools


tamiwiki:users:6r1d:diymall_esp32_s3_fixture

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tamiwiki:users:6r1d:diymall_esp32_s3_fixture [2025/10/18 12:29] 6r1dtamiwiki:users:6r1d:diymall_esp32_s3_fixture [2025/10/19 01:32] (current) – [Tracing code] 6r1d
Line 1: Line 1:
-There's something very nice about ESP32 Wroom modules: you can use fixtures to quickly debug thingsremove a module, solder it and viola, you have something working. +Theres something great about [[https://www.espressif.com/en/products/socs/esp32-s3|ESP32-WROOM]] modules: you can prototype fast, use test fixtures to debug, then pop a module off, solder it onto a board, and voilà, you’ve got a working device
-Or a bad boardMostly that.+Or a non-working one. 
 +//Usually// it's one of those two.
  
-And I've been using a fixture by [[http://www.diymalls.com/|DIYMall]] for some time at this point.+====== Introduction ======
  
-It'easy to buy on AliExpress, for example, [[https://aliexpress.com/item/1005008525778779.html|here]], it works without hassle, and it will also require you trace all pins unless you have a reference.+I've been using a fixture by [[http://www.diymalls.com/|DIYMall]] for some time at this point. From my perspective, its main feature is the support for ESP32-S3-WROOM-1U and ESP32-S3-WROOM-1 ([[https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf|datasheet]]). 
 + 
 +{{:tamiwiki:users:6r1d:signal-2025-10-18-123252_002.jpeg?400|}} {{:tamiwiki:users:6r1d:signal-2025-10-18-123334_002.jpeg?400|}} 
 + 
 +It is very helpful as a fixture. It is not helpful whatsoever when it comes to proper documentation. If you open the [[http://diymalls.com/|DIYMalls site]], it shows a 403 and gives you nothing. 
 + 
 +So, the fixture is easy to buy on AliExpress, for example, [[https://aliexpress.com/item/1005008525778779.html|here]], it works without hassle, and it will also require you trace all pins unless you have a reference.
  
 Thus, I've traced everything I found so you don't have to. Thus, I've traced everything I found so you don't have to.
  
-^ ESP32-S3-Wroom contact pad ^ DS REF ^ Arduino pin ^ Board pin ^ +====== Pin assignments ====== 
-| 39                         | IO1    | 1           | D36       | + 
-| 38                         | IO2    | 2           | D35       | +^ ESP32-S3-Wroom contact pad ^ Datasheet reference ^ Arduino pin ^ Board pin ^ 
-| 4                          | IO4    | 4           | D1        | +| 39                         | IO1                 | 1           | D36       | 
-| 5                          | IO5    | 5           | D2        | +| 38                         | IO2                 | 2           | D35       | 
-| 6                          | IO6    | 6           | D3        | +| 4                          | IO4                 | 4           | D1        | 
-| 7                          | IO7    | 7           | D4        | +| 5                          | IO5                 | 5           | D2        | 
-| 12                         | IO8    | 8           | D9        | +| 6                          | IO6                 | 6           | D3        | 
-| 17                         | IO9    | 9           | D14       | +| 7                          | IO7                 | 7           | D4        | 
-| 18                         | IO10   | 10          | D15       | +| 12                         | IO8                 | 8           | D9        | 
-| 19                         | IO11   | 11          | D16       | +| 17                         | IO9                 | 9           | D14       | 
-| 20                         | IO12   | 12          | D17       | +| 18                         | IO10                | 10          | D15       | 
-| 21                         | IO13   | 13          | D18       | +| 19                         | IO11                | 11          | D16       | 
-| 22                         | IO14   | 14          | D19       | +| 20                         | IO12                | 12          | D17       | 
-| 8                          | IO15   | 15          | D5        | +| 21                         | IO13                | 13          | D18       | 
-| 9                          | IO16   | 16          | D6        | +| 22                         | IO14                | 14          | D19       | 
-| 10                         | IO17   | 17          | D7        | +| 8                          | IO15                | 15          | D5        | 
-| 11                         | IO18   | 18          | D8        | +| 9                          | IO16                | 16          | D6        | 
-| 23                         | IO21   | 21          | D20       | +| 10                         | IO17                | 17          | D7        | 
-| 31                         | IO38   | 38          | D28       | +| 11                         | IO18                | 18          | D8        | 
-| 32                         | IO39   | 39          | D29       | +| 23                         | IO21                | 21          | D20       | 
-| 33                         | IO40   | 40          | D30       | +| 31                         | IO38                | 38          | D28       | 
-| 34                         | IO41   | 41          | D31       | +| 32                         | IO39                | 39          | D29       | 
-| 35                         | IO42   | 42          | D32       | +| 33                         | IO40                | 40          | D30       | 
-| 24                         | IO47   | 47          | D21       | +| 34                         | IO41                | 41          | D31       | 
-| 25                         | IO48   | 48          | D22       | +| 35                         | IO42                | 42          | D32       | 
-| 36                         | RXD0   | 36          | D33       | +| 24                         | IO47                | 47          | D21       | 
-| 37                         | TXD0   | 37          | D34         +| 25                         | IO48                | 48          | D22       | 
 +| 36                         | RXD0                | 36          | D33       | 
 +| 37                         | TXD0                | 37          | D34       | 
 + 
 +===== Notes ===== 
 + 
 +  * GPIO pin 48 is the onboard LED, usually blue. 
 +  * Initially, I thought that onboard USB-UART is connected to something else than RXD0 / TXD0. It was not. It is the main UART. 
 +  
 +====== Tracing code ====== 
 + 
 +You might disagree with me and want to double-check. Excellent! More eyes (and feedback) mean better information. 
 + 
 +Grab your trusty 30-year-old LED soldered to a pair of DuPont sockets and start tracing. If you don’t have one handy (like me), grab your scope instead.((Marie Antoinette reference unintended.)) 
 + 
 +For convenience, here’s the code to do the job. I didn’t bother writing it from scratch, but I tested it, and it worked just fine for me. 
 +===== ESP-IDF (+ history and PWM debug) ===== 
 + 
 +This code gives you a predictable interactive workflow with history. 
 + 
 +<code> 
 +> help 
 +Commands: 
 +  list                          -> show test-safe GPIOs 
 +  status                        -> show current pin/mode 
 +  pin <gpio>                    -> start blinking GPIO 
 +  <gpio>                        -> same as 'pin <gpio>' 
 +  delay <ms>                    -> set blink delay 
 +  pwm <pin> [brightness]        -> start PWM (0-99, current: 50%) 
 +  brightness <level>            -> set PWM brightness (0-99) 
 +  freq <frequency>              -> set PWM frequency (Hz) 
 +  stop                          -> stop and release pin 
 + 
 +> 48 
 +Stopped. Pin released to INPUT. 
 +Blinking GPIO 48 at 100 ms. 
 + 
 +> 42 
 +Stopped. Pin released to INPUT. 
 +Blinking GPIO 42 at 100 ms. 
 + 
 +> 47 
 +Stopped. Pin released to INPUT. 
 +Blinking GPIO 47 at 100 ms. 
 + 
 +> 46 
 +Restricted: GPIO 46 is not in the test-safe set. 
 + 
 +> delay 40 
 +Set blink delay to 40 ms. 
 + 
 +> delay 10 
 +Set blink delay to 10 ms. 
 +</code> 
 + 
 +As for using it, you should know the gist at this point. 
 + 
 +<code bash> 
 +mkdir pin_tracer && cd pin_tracer 
 +idf.py create-project . && idf.py reconfigure && idf.py set-target esp32s3 
 +# fill the code 
 +# update the CMake configs 
 +idf.py build 
 +idf.py flash monitor 
 +</code> 
 + 
 +CMake config (code): 
 + 
 +<code> 
 +idf_component_register(SRCS "main.c" 
 +                      INCLUDE_DIRS "."
 +</code> 
 + 
 +CMake config (project): 
 + 
 +<code> 
 +# The following five lines of boilerplate have to be in your project'
 +# CMakeLists in this exact order for cmake to work correctly 
 +cmake_minimum_required(VERSION 3.16) 
 + 
 +include($ENV{IDF_PATH}/tools/cmake/project.cmake) 
 +project(.) 
 +</code> 
 + 
 +<file cpp esp32_s3_pin_tracer.c> 
 +#include <stdio.h> 
 +#include <string.h> 
 +#include <ctype.h> 
 +#include <inttypes.h> 
 + 
 +#include "freertos/FreeRTOS.h" 
 +#include "freertos/task.h" 
 + 
 +#include "driver/gpio.h" 
 +#include "driver/uart.h" 
 +#include "driver/uart_vfs.h" 
 +#include "esp_vfs_dev.h" 
 +#include "esp_system.h" 
 +#include "driver/ledc.h" 
 + 
 +/* 
 + * CONFIG 
 + */ 
 +// Defaults 
 +static uint32_t g_blink_delay_ms = 100; 
 +// Console config 
 +#define CLI_LINE_MAX 128 
 +#define HIST_MAX     10 
 + 
 +// PWM configuration 
 +#define LEDC_TIMER              LEDC_TIMER_0 
 +#define LEDC_MODE               LEDC_LOW_SPEED_MODE 
 +#define LEDC_CHANNEL            LEDC_CHANNEL_0 
 +#define LEDC_DUTY_RES           LEDC_TIMER_8_BIT  // 0-255 
 +#define LEDC_FREQUENCY          5000              // 5 kHz 
 + 
 +// Conservative "test-safe GPIO" set for ESP32-S3 modules. 
 +static const gpio_num_t TEST_SAFE_GPIO[] = { 
 +    1,  2, 
 +    4,  5,  6,  7, 
 +    8,  9,  10, 11, 12, 13, 14, 
 +    15, 16, 17, 18, 
 +    /* 19,20 excluded (USB D-/D+) */ 
 +    21, 
 +    /* 22..34 not bonded on typical modules */ 
 +    /* 35,36,37 excluded (PSRAM on some variants) */ 
 +    38, 39, 40, 41, 42,  /* JTAG-capable if configured */ 
 +    /* 43,44 excluded (UART0 console) */ 
 +    47, 48               /* may be 1.8 V on R16V */ 
 +}; 
 +static const size_t TEST_SAFE_GPIO_COUNT = sizeof(TEST_SAFE_GPIO) / sizeof(TEST_SAFE_GPIO[0]); 
 + 
 +// ====== RUNTIME STATE ====== 
 +static gpio_num_t g_current_pin = -1; 
 +static bool g_blinking = false; 
 +static bool g_pwm_mode = false; 
 +static uint8_t g_pwm_brightness = 50;  // 0-99 scale 
 +static uint32_t g_pwm_frequency = LEDC_FREQUENCY; 
 +static TaskHandle_t g_blink_task = NULL; 
 + 
 +/* ================== GPIO HELPERS ================== */ 
 + 
 +static bool is_test_safe(gpio_num_t gpio) 
 +
 +    for (size_t i = 0; i < TEST_SAFE_GPIO_COUNT; ++i) { 
 +        if (TEST_SAFE_GPIO[i] == gpio) return true; 
 +    } 
 +    return false; 
 +
 + 
 +static void print_test_safe_pins(void) 
 +
 +    printf("\nTest-safe GPIOs (ESP32-S3):\n  "); 
 +    for (size_t i = 0; i < TEST_SAFE_GPIO_COUNT; ++i) { 
 +        printf("%s%d", (i ? ", " : ""), TEST_SAFE_GPIO[i]); 
 +    } 
 +    printf("\nExcluded (reason): 0,3,45,46 (boot/strap/JTAG) | 19,20 (USB D-/D+) | 35-37 (PSRAM on some) | 43,44 (UART0 console)\n"); 
 +    printf("Note: On some R16V modules, GPIO47/48 are 1.8 V only.\n"); 
 +
 + 
 +static void release_pin(gpio_num_t pin) 
 +
 +    if (pin >= 0) { 
 +        gpio_set_level(pin, 0); 
 +        gpio_reset_pin(pin);               // back to default (input/hi-z) 
 +    } 
 +
 + 
 +static void stop_pwm(void) 
 +
 +    if (g_pwm_mode) { 
 +        ledc_stop(LEDC_MODE, LEDC_CHANNEL, 0); 
 +        g_pwm_mode = false; 
 +    } 
 +
 + 
 +static void stop_blink(void) 
 +
 +    if (g_blink_task) { 
 +        TaskHandle_t t = g_blink_task; 
 +        g_blink_task = NULL;               // signal task to exit 
 +        for (int i = 0; i < 20 && eTaskGetState(t) != eDeleted; ++i) { 
 +            vTaskDelay(pdMS_TO_TICKS(5)); 
 +        } 
 +    } 
 +    g_blinking = false; 
 +
 + 
 +static void stop_all(void) 
 +
 +    stop_blink(); 
 +    stop_pwm(); 
 +    release_pin(g_current_pin); 
 +    g_current_pin = -1; 
 +    printf("Stopped. Pin released to INPUT.\n"); 
 +
 + 
 +static void blink_task(void *arg) 
 +
 +    gpio_num_t pin = (gpio_num_t)(intptr_t)arg; 
 +    gpio_config_t io = { 
 +        .pin_bit_mask = 1ULL << 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); 
 +    gpio_set_level(pin, 0); 
 + 
 +    while (g_blink_task == xTaskGetCurrentTaskHandle()) { 
 +        gpio_set_level(pin, 1); 
 +        vTaskDelay(pdMS_TO_TICKS(g_blink_delay_ms)); 
 +        gpio_set_level(pin, 0); 
 +        vTaskDelay(pdMS_TO_TICKS(g_blink_delay_ms)); 
 +    } 
 + 
 +    release_pin(pin); 
 +    vTaskDelete(NULL); 
 +
 + 
 +static bool setup_pwm(gpio_num_t gpio) 
 +
 +    // Stop previous PWM if any 
 +    if (g_pwm_mode) { 
 +        ledc_stop(LEDC_MODE, LEDC_CHANNEL, 0); 
 +    } 
 +     
 +    // Configure LEDC timer 
 +    ledc_timer_config_t ledc_timer = { 
 +        .speed_mode       = LEDC_MODE, 
 +        .timer_num        = LEDC_TIMER, 
 +        .duty_resolution  = LEDC_DUTY_RES, 
 +        .freq_hz          = g_pwm_frequency,  // Use configurable frequency 
 +        .clk_cfg          = LEDC_AUTO_CLK 
 +    }; 
 +    esp_err_t ret = ledc_timer_config(&ledc_timer); 
 +    if (ret != ESP_OK) { 
 +        printf("PWM timer config failed: %s\n", esp_err_to_name(ret)); 
 +        return false; 
 +    } 
 + 
 +    // Configure LEDC channel 
 +    ledc_channel_config_t ledc_channel = { 
 +        .speed_mode     = LEDC_MODE, 
 +        .channel        = LEDC_CHANNEL, 
 +        .timer_sel      = LEDC_TIMER, 
 +        .intr_type      = LEDC_INTR_DISABLE, 
 +        .gpio_num       = gpio, 
 +        .duty           = 0,  // Start at 0 duty 
 +        .hpoint         = 0 
 +    }; 
 +    ret = ledc_channel_config(&ledc_channel); 
 +    if (ret != ESP_OK) { 
 +        printf("PWM channel config failed: %s\n", esp_err_to_name(ret)); 
 +        return false; 
 +    } 
 + 
 +    return true; 
 +
 + 
 +static void set_pwm_brightness(uint8_t level) 
 +
 +    if (level > 99) level = 99; 
 +    g_pwm_brightness = level; 
 +     
 +    // Convert 0-99 scale to 0-255 duty cycle 
 +    uint32_t duty = (level * 255) / 99; 
 +    ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, duty); 
 +    ledc_update_duty(LEDC_MODE, LEDC_CHANNEL); 
 +     
 +    printf("PWM brightness set to %d%% (duty: %"PRIu32"/255)\n", level, duty); 
 +
 + 
 +static void set_pwm_frequency(uint32_t freq) 
 +
 +    if (freq < 100) freq = 100; 
 +    if (freq > 50000) freq = 50000; // Conservative max for LEDC 
 +     
 +    g_pwm_frequency = freq; 
 +     
 +    if (g_pwm_mode) { 
 +        // Reconfigure PWM with new frequency 
 +        ledc_timer_config_t ledc_timer = { 
 +            .speed_mode       = LEDC_MODE, 
 +            .timer_num        = LEDC_TIMER, 
 +            .duty_resolution  = LEDC_DUTY_RES, 
 +            .freq_hz          = g_pwm_frequency, 
 +            .clk_cfg          = LEDC_AUTO_CLK 
 +        }; 
 +        esp_err_t ret = ledc_timer_config(&ledc_timer); 
 +        if (ret == ESP_OK) { 
 +            printf("PWM frequency set to %" PRIu32 " Hz\n", g_pwm_frequency); 
 +            // Restore brightness 
 +            set_pwm_brightness(g_pwm_brightness); 
 +        } else { 
 +            printf("Failed to set PWM frequency: %s\n", esp_err_to_name(ret)); 
 +        } 
 +    } else { 
 +        printf("PWM frequency set to %" PRIu32 " Hz (will apply when PWM starts)\n", g_pwm_frequency); 
 +    } 
 +
 + 
 +static void start_blink(gpio_num_t gpio) 
 +
 +    if (!is_test_safe(gpio)) { 
 +        printf("Restricted: GPIO %d is not in the test-safe set.\n", gpio); 
 +        return; 
 +    } 
 +     
 +    stop_all(); // Stop any previous mode 
 +     
 +    g_current_pin = gpio; 
 +    g_blinking = true; 
 + 
 +    if (xTaskCreatePinnedToCore(blink_task, "blink_task", 2048, (void*)(intptr_t)gpio, 
 +                            tskIDLE_PRIORITY + 1, &g_blink_task, tskNO_AFFINITY) != pdPASS) { 
 +        printf("Error: Failed to create blink task\n"); 
 +        g_blinking = false; 
 +        g_current_pin = -1; 
 +        return; 
 +    } 
 + 
 +    printf("Blinking GPIO %d at %" PRIu32 " ms.\n", g_current_pin, g_blink_delay_ms); 
 +
 + 
 +static void start_pwm(gpio_num_t gpio, uint8_t brightness) 
 +
 +    if (!is_test_safe(gpio)) { 
 +        printf("Restricted: GPIO %d is not in the test-safe set.\n", gpio); 
 +        return; 
 +    } 
 +     
 +    stop_all(); // Stop any previous mode 
 +     
 +    if (!setup_pwm(gpio)) { 
 +        printf("Failed to setup PWM on GPIO %d\n", gpio); 
 +        return; 
 +    } 
 +     
 +    g_current_pin = gpio; 
 +    g_pwm_mode = true; 
 +    set_pwm_brightness(brightness); 
 +     
 +    printf("PWM started on GPIO %d at %" PRIu32 " Hz\n", gpio, g_pwm_frequency); 
 +
 + 
 +/* ================== COMMAND PARSER ================== */ 
 + 
 +static void trim(char *s) 
 +
 +    size_t len = strlen(s); 
 +    while (len && (s[len-1] == '\r' || s[len-1] == '\n' || isspace((unsigned char)s[len-1]))) s[--len] = 0; 
 +    size_t i = 0; 
 +    while (s[i] && isspace((unsigned char)s[i])) i++; 
 +    if (i) memmove(s, s+i, strlen(s+i)+1); 
 +
 + 
 +static bool all_digits(const char *s) 
 +
 +    if (!*s) return false; 
 +    for (const char *p = s; *p; ++p) { 
 +        if (!isdigit((unsigned char)*p)) return false; 
 +    } 
 +    return true; 
 +
 + 
 +static void handle_command(char *line) 
 +
 +    trim(line); 
 +    if (!*line) return; 
 + 
 +    char cmd[64]; 
 +    strncpy(cmd, line, sizeof(cmd)-1); 
 +    cmd[sizeof(cmd)-1] = 0; 
 +    for (char *p = cmd; *p; ++p) *p = (char)tolower((unsigned char)*p); 
 + 
 +    if (strcmp(cmd, "list") == 0) { 
 +        print_test_safe_pins(); 
 +        return; 
 +    } 
 +    if (strcmp(cmd, "status") == 0) { 
 +        printf("Pin: %d, mode: %s, ", (int)g_current_pin,  
 +               g_blinking ? "blink" : (g_pwm_mode ? "pwm" : "idle")); 
 +        if (g_blinking) { 
 +            printf("delay: %" PRIu32 " ms\n", g_blink_delay_ms); 
 +        } else if (g_pwm_mode) { 
 +            printf("brightness: %d%%, frequency: %" PRIu32 " Hz\n",  
 +                   g_pwm_brightness, g_pwm_frequency); 
 +        } else { 
 +            printf("no active output\n"); 
 +        } 
 +        return; 
 +    } 
 +    if (strcmp(cmd, "stop") == 0) { 
 +        stop_all(); 
 +        return; 
 +    } 
 +    if (strncmp(cmd, "pin ", 4) == 0) { 
 +        int gpio = atoi(line + 4); 
 +        start_blink((gpio_num_t)gpio); 
 +        return; 
 +    } 
 +    if (strncmp(cmd, "delay ", 6) == 0) { 
 +        int v = atoi(line + 6); 
 +        if (v < 10) v = 10; 
 +        g_blink_delay_ms = (uint32_t)v; 
 +        printf("Set blink delay to %" PRIu32 " ms.\n", g_blink_delay_ms); 
 +        return; 
 +    } 
 +    if (strncmp(cmd, "pwm ", 4) == 0) { 
 +        // Parse "pwm <pin> <brightness>" or "pwm <pin>" 
 +        char *args = line + 4; 
 +        trim(args); 
 +         
 +        int gpio = -1, brightness = g_pwm_brightness; // Default to current brightness 
 +         
 +        if (sscanf(args, "%d %d", &gpio, &brightness) >= 1) { 
 +            if (brightness < 0) brightness = 0; 
 +            if (brightness > 99) brightness = 99; 
 +            start_pwm((gpio_num_t)gpio, (uint8_t)brightness); 
 +        } else { 
 +            printf("Usage: pwm <pin> [brightness 0-99]\n"); 
 +            printf("Current brightness: %d%%. Omit brightness to use current.\n", g_pwm_brightness); 
 +        } 
 +        return; 
 +    } 
 +    if (strncmp(cmd, "brightness ", 11) == 0 || strncmp(cmd, "bright ", 7) == 0) { 
 +        // Allow both "brightness XX" and "bright XX" 
 +        char *arg = (cmd[6] == ' ') ? line + 7 : line + 11; 
 +        int brightness = atoi(arg); 
 +        if (brightness >= 0 && brightness <= 99) { 
 +            if (g_pwm_mode) { 
 +                set_pwm_brightness((uint8_t)brightness); 
 +            } else { 
 +                printf("Error: PWM mode not active. Use 'pwm <pin> [brightness]' first.\n"); 
 +            } 
 +        } else { 
 +            printf("Brightness must be 0-99\n"); 
 +        } 
 +        return; 
 +    } 
 +    if (strncmp(cmd, "freq ", 5) == 0 || strncmp(cmd, "frequency ", 10) == 0) { 
 +        char *arg = (cmd[4] == ' ') ? line + 5 : line + 10; 
 +        int freq = atoi(arg); 
 +        if (freq > 0) { 
 +            set_pwm_frequency((uint32_t)freq); 
 +        } else { 
 +            printf("Frequency must be positive\n"); 
 +        } 
 +        return; 
 +    } 
 +    if (strcmp(cmd, "help") == 0) { 
 +        printf("Commands:\n" 
 +               "  list                          -> show test-safe GPIOs\n" 
 +               "  status                        -> show current pin/mode\n" 
 +               "  pin <gpio>                    -> start blinking GPIO\n" 
 +               "  <gpio>                        -> same as 'pin <gpio>'\n" 
 +               "  delay <ms>                    -> set blink delay\n" 
 +               "  pwm <pin> [brightness]        -> start PWM (0-99, current: %d%%)\n" 
 +               "  brightness <level>            -> set PWM brightness (0-99)\n" 
 +               "  freq <frequency>              -> set PWM frequency (Hz)\n" 
 +               "  stop                          -> stop and release pin\n", 
 +               g_pwm_brightness); 
 +        return; 
 +    } 
 +    if (all_digits(line)) { 
 +        start_blink((gpio_num_t)atoi(line)); 
 +        return; 
 +    } 
 + 
 +    printf("Unknown. Type 'help' for commands.\n"); 
 +
 + 
 +/* ================== CONSOLE ================== */ 
 +static void console_task(void *arg) 
 +
 +    const uart_port_t uart_num = UART_NUM_0; 
 + 
 +    char   line[CLI_LINE_MAX] = {0}; 
 +    size_t pos = 0;              // length of buffer 
 +    size_t cursor = 0;           // cursor index in [0..pos] 
 + 
 +    char hist[HIST_MAX][CLI_LINE_MAX] = {{0}}; 
 +    int  hist_count = 0;         // number of valid entries 
 +    int  hist_head  = 0;         // next insert index (ring buffer) 
 +    int  hist_view  = -1;        // -1 not browsing; else index into hist 
 + 
 +    // helpers 
 +    #define WRITE_STR(s) uart_write_bytes(uart_num, (s), strlen(s)) 
 + 
 +    auto void prompt(void) { 
 +        WRITE_STR("\r\n> "); 
 +        fflush(stdout); 
 +    } 
 + 
 +    auto void redraw_line(void) { 
 +        // Clear line, reprint prompt + buffer, then move cursor left if needed 
 +        WRITE_STR("\r\x1b[2K> "); 
 +        if (pos) uart_write_bytes(uart_num, line, pos); 
 +        if (pos > cursor) { 
 +            char seq[16]; int n = snprintf(seq, sizeof(seq), "\x1b[%zuD", (size_t)(pos - cursor)); 
 +            uart_write_bytes(uart_num, seq, n); 
 +        } 
 +    } 
 + 
 +    auto void push_history(const char *cmd) { 
 +        if (!cmd[0]) return; 
 +        int last = (hist_head - 1 + HIST_MAX) % HIST_MAX; 
 +        if (hist_count > 0 && strncmp(hist[last], cmd, CLI_LINE_MAX) == 0) return; 
 +        strncpy(hist[hist_head], cmd, CLI_LINE_MAX - 1); 
 +        hist[hist_head][CLI_LINE_MAX - 1] = 0; 
 +        hist_head = (hist_head + 1) % HIST_MAX; 
 +        if (hist_count < HIST_MAX) hist_count++; 
 +    } 
 + 
 +    auto void load_history(int idx) { 
 +        strncpy(line, hist[idx], CLI_LINE_MAX - 1); 
 +        line[CLI_LINE_MAX - 1] = 0; 
 +        pos = cursor = strlen(line); 
 +        redraw_line(); 
 +    } 
 + 
 +    auto bool in_history(void) { return hist_view != -1; } 
 + 
 +    prompt(); 
 + 
 +    enum { ESC_IDLE, ESC_ESC, ESC_CSI, ESC_TILDE } esc = ESC_IDLE; 
 +    char csi_param_buf[4] = {0}; 
 +    int  csi_param_len = 0; 
 + 
 +    while (1) { 
 +        uint8_t ch; 
 +        int got = uart_read_bytes(uart_num, &ch, 1, pdMS_TO_TICKS(30)); 
 +        if (got != 1) continue; 
 + 
 +        // Escape handling (arrows/Home/End/Del and Alt-B/F) 
 +        if (esc == ESC_ESC) { 
 +            if (ch == '[') { esc = ESC_CSI; csi_param_len = 0; continue; } 
 +            // Alt-b / Alt-f (word left/right) 
 +            if (ch == 'b' || ch == 'B') { 
 +                // word-left 
 +                if (cursor > 0) { 
 +                    while (cursor > 0 && isspace((unsigned char)line[cursor-1])) cursor--; 
 +                    while (cursor > 0 && !isspace((unsigned char)line[cursor-1])) cursor--; 
 +                    redraw_line(); 
 +                } 
 +                esc = ESC_IDLE; continue; 
 +            } 
 +            if (ch == 'f' || ch == 'F') { 
 +                // word-right 
 +                if (cursor < pos) { 
 +                    while (cursor < pos && !isspace((unsigned char)line[cursor])) cursor++; 
 +                    while (cursor < pos &&  isspace((unsigned char)line[cursor])) cursor++; 
 +                    redraw_line(); 
 +                } 
 +                esc = ESC_IDLE; continue; 
 +            } 
 +            // Unknown ESC seq -> ignore 
 +            esc = ESC_IDLE; 
 +            continue; 
 +        } else if (esc == ESC_CSI) { 
 +            if (ch >= '0' && ch <= '9') { 
 +                if (csi_param_len < (int)sizeof(csi_param_buf)-1) csi_param_buf[csi_param_len++] = (char)ch; 
 +                continue; 
 +            } 
 +            if (ch == '~') { 
 +                // Handle [3~ delete, [1~ home, [4~ end 
 +                int p = atoi(csi_param_buf); 
 +                if (p == 3) { // Delete (forward) 
 +                    if (cursor < pos) { 
 +                        memmove(&line[cursor], &line[cursor+1], pos - cursor - 1); 
 +                        pos--; 
 +                        line[pos] = 0; 
 +                        redraw_line(); 
 +                    } 
 +                } else if (p == 1) { // Home 
 +                    cursor = 0; redraw_line(); 
 +                } else if (p == 4) { // End 
 +                    cursor = pos; redraw_line(); 
 +                } 
 +                esc = ESC_IDLE; continue; 
 +            } 
 +            // Final byte for standard arrows/home/end 
 +            if (ch == 'A') { // Up 
 +                if (hist_count) { 
 +                    if (!in_history()) hist_view = (hist_head - 1 + HIST_MAX) % HIST_MAX; 
 +                    else { 
 +                        int oldest = (hist_head - hist_count + HIST_MAX) % HIST_MAX; 
 +                        if (hist_view != oldest) hist_view = (hist_view - 1 + HIST_MAX) % HIST_MAX; 
 +                    } 
 +                    load_history(hist_view); 
 +                } 
 +            } else if (ch == 'B') { // Down 
 +                if (in_history()) { 
 +                    int newest = (hist_head - 1 + HIST_MAX) % HIST_MAX; 
 +                    if (hist_view != newest) { hist_view = (hist_view + 1) % HIST_MAX; load_history(hist_view);
 +                    else { hist_view = -1; pos = cursor = 0; line[0] = 0; redraw_line();
 +                } 
 +            } else if (ch == 'C') { // Right 
 +                if (cursor < pos) { cursor++; redraw_line();
 +            } else if (ch == 'D') { // Left 
 +                if (cursor > 0)  { cursor--; redraw_line();
 +            } else if (ch == 'H') { // Home 
 +                cursor = 0; redraw_line(); 
 +            } else if (ch == 'F') { // End 
 +                cursor = pos; redraw_line(); 
 +            } 
 +            esc = ESC_IDLE; 
 +            continue; 
 +        } 
 + 
 +        // Start of escape? 
 +        if (ch == 0x1B) { esc = ESC_ESC; continue; } 
 + 
 +        // CR / LF -> execute 
 +        if (ch == '\r' || ch == '\n') { 
 +            WRITE_STR("\r\n"); 
 +            line[pos] = 0; 
 +            if (pos) { push_history(line); hist_view = -1; } 
 +            handle_command(line); 
 +            pos = cursor = 0; line[0] = 0; 
 +            prompt(); 
 +            continue; 
 +        } 
 + 
 +        // Ctrl-A / Ctrl-E: home/end 
 +        if (ch == 0x01) { cursor = 0; redraw_line(); continue; } 
 +        if (ch == 0x05) { cursor = pos; redraw_line(); continue; } 
 + 
 +        // Backspace / Delete-left 
 +        if (ch == 0x08 || ch == 0x7F) { 
 +            if (cursor > 0) { 
 +                memmove(&line[cursor - 1], &line[cursor], pos - cursor); 
 +                cursor--; pos--; 
 +                line[pos] = 0; 
 +                redraw_line(); 
 +            } 
 +            continue; 
 +        } 
 + 
 +        // Ctrl-U: clear line 
 +        if (ch == 0x15) { 
 +            pos = cursor = 0; line[0] = 0; redraw_line(); 
 +            continue; 
 +        } 
 + 
 +        // Ctrl-L: redraw 
 +        if (ch == 0x0C) { redraw_line(); continue; } 
 + 
 +        // Ctrl-W: delete previous word 
 +        if (ch == 0x17) { 
 +            size_t start = cursor; 
 +            while (start > 0 && isspace((unsigned char)line[start - 1])) start--; 
 +            while (start > 0 && !isspace((unsigned char)line[start - 1])) start--; 
 +            if (start < cursor) { 
 +                memmove(&line[start], &line[cursor], pos - cursor); 
 +                pos -= (cursor - start); 
 +                cursor = start; 
 +                line[pos] = 0; 
 +                redraw_line(); 
 +            } 
 +            continue; 
 +        } 
 + 
 +        // Printable ASCII: insert at cursor 
 +        if (isprint(ch)) { 
 +            if (pos + 1 < CLI_LINE_MAX) { 
 +                if (in_history()) { hist_view = -1; /* keep text */ } 
 +                memmove(&line[cursor + 1], &line[cursor], pos - cursor); 
 +                line[cursor++] = (char)ch; 
 +                pos++; 
 +                line[pos] = 0; 
 +                redraw_line(); 
 +            } 
 +            continue; 
 +        } 
 + 
 +        // ignore other control bytes 
 +    } 
 + 
 +    // Prevent task from returning (FreeRTOS requirement) 
 +    while (1) { 
 +        vTaskDelay(pdMS_TO_TICKS(1000)); 
 +    } 
 +
 + 
 +/* ================== APP ================== */ 
 + 
 +void app_main(void) 
 +
 +    const int baud = 115200; 
 +    const uart_port_t uart_num = UART_NUM_0; 
 +    uart_config_t cfg = { 
 +        .baud_rate = baud, 
 +        .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, 
 +    }; 
 +    ESP_ERROR_CHECK(uart_driver_install(uart_num, 512, 0, 0, NULL, 0)); 
 +    ESP_ERROR_CHECK(uart_param_config(uart_num, &cfg)); 
 +    uart_vfs_dev_use_driver(uart_num); 
 +    uart_vfs_dev_port_set_rx_line_endings(uart_num, ESP_LINE_ENDINGS_CRLF); 
 +    uart_vfs_dev_port_set_tx_line_endings(uart_num, ESP_LINE_ENDINGS_CRLF); 
 + 
 +    vTaskDelay(pdMS_TO_TICKS(200)); 
 + 
 +    printf("\nESP32-S3 GPIO Tester with PWM (ESP-IDF)\n"); 
 +    printf("Type 'help' for commands. Advanced editing supported.\n"); 
 +    print_test_safe_pins(); 
 + 
 +    xTaskCreatePinnedToCore(console_task, "console", 4096, NULL, 
 +                            tskIDLE_PRIORITY + 2, NULL, tskNO_AFFINITY); 
 + 
 +    vTaskDelete(NULL); 
 +
 +</file> 
 + 
 + 
 +===== Arduino (basic version) ===== 
 + 
 +Flash from Arduino IDE. Use the ESP32 board package by Espressif, pick ESP32S3 Dev Module, pick 115200 baud in Serial Monitor. 
 + 
 +<file cpp esp32_s3_pin_tracer.ino> 
 +#include <Arduino.h> 
 + 
 +// ====== CONFIG ====== 
 +// Blink timing (change at runtime with: delay <ms>
 +static uint32_t BLINK_DELAY_MS = 100; 
 + 
 +// Conservative "test-safe GPIO" set for ESP32-S3 modules. 
 +// Excludes pins that commonly serve boot strapping, USB D-/D+, PSRAM/flash, UART0 console, or JTAG, 
 +// which can interfere with programming, boot, or board-level functions during quick tests. 
 +// 
 +// Notes (summarized from the ESP32-S3 module/chip docs): 
 +// - 0, 3, 45, 46 are strapping/boot-related or JTAG-related; driving them can change boot modes or disable debug. 
 +// - 19, 20 are the on-chip USB D-/D+ differential pair; toggling them breaks USB comms. 
 +// - 35, 36, 37 are wired to Octal PSRAM on some variants; not available as GPIO there. 
 +// - 43 (U0TXD), 44 (U0RXD) are UART0 console pins used by Serial; poking them disrupts logs/flashing. 
 +// - 47, 48 form a differential SPI clock pair; safe as GPIO but mind 1.8 V I/O level on some R16V variants. 
 +const uint8_t TEST_SAFE_GPIO[] = { 
 +  1, 2,                      // ADC/Touch-capable, OK for digital use 
 +  4, 5, 6, 7,                // " 
 +  8, 9, 10, 11, 12, 13, 14,  // " 
 +  15, 16, 17, 18,            // " 
 +  // 19,20 excluded (USB D-/D+) 
 +  21,  // plain GPIO 
 +  // 22..34 don't exist on S3 modules 
 +  // 35,36,37 excluded (PSRAM on some variants) 
 +  38, 39, 40, 41, 42,  // JTAG-capable if configured, but OK as GPIO when JTAG is not in use 
 +  // 43,44 excluded (UART0 TX/RX used by Serial) 
 +  47, 48  // differential clock pair; OK as GPIO (watch 1.8 V I/O on some R16V variants) 
 +}; 
 +const size_t TEST_SAFE_GPIO_COUNT = sizeof(TEST_SAFE_GPIO) / sizeof(TEST_SAFE_GPIO[0]); 
 + 
 +// ====== RUNTIME STATE ====== 
 +static int currentPin = -1; 
 +static bool blinking = false; 
 + 
 +// ====== HELPERS ====== 
 +bool isTestSafe(int gpio) { 
 +  for (size_t i = 0; i < TEST_SAFE_GPIO_COUNT; ++i) { 
 +    if ((int)TEST_SAFE_GPIO[i] == gpio) return true; 
 +  } 
 +  return false; 
 +
 + 
 +void printTestSafePins() { 
 +  Serial.println(F("\nTest-safe GPIOs (ESP32-S3):")); 
 +  for (size_t i = 0; i < TEST_SAFE_GPIO_COUNT; ++i) { 
 +    Serial.print(F(i ? ", " : "  ")); 
 +    Serial.print(TEST_SAFE_GPIO[i]); 
 +  } 
 +  Serial.println(); 
 +  Serial.println(F("Excluded (reason): 0,3,45,46 (boot/strap/JTAG) | 19,20 (USB D-/D+) | 35-37 (PSRAM on some) | 43,44 (UART0 console)")); 
 +
 + 
 +void stopBlink() { 
 +  if (currentPin >= 0) { 
 +    digitalWrite(currentPin, LOW); 
 +    pinMode(currentPin, INPUT);  // leave safe 
 +  } 
 +  blinking = false; 
 +  currentPin = -1; 
 +  Serial.println(F("Stopped. Pin released to INPUT.")); 
 +
 + 
 +void startBlink(int gpio) { 
 +  if (!isTestSafe(gpio)) { 
 +    Serial.print(F("Restricted: GPIO ")); 
 +    Serial.print(gpio); 
 +    Serial.println(F(" is not in the test-safe set.")); 
 +    return; 
 +  } 
 +  if (blinking && currentPin == gpio) { 
 +    Serial.print(F("Already blinking GPIO ")); 
 +    Serial.println(gpio); 
 +    return; 
 +  } 
 +  // switch pin if needed 
 +  if (blinking && currentPin != gpio) stopBlink(); 
 + 
 +  currentPin = gpio; 
 +  pinMode(currentPin, OUTPUT); 
 +  digitalWrite(currentPin, LOW); 
 +  blinking = true; 
 + 
 +  Serial.print(F("Blinking GPIO ")); 
 +  Serial.print(currentPin); 
 +  Serial.print(F(" at ")); 
 +  Serial.print(BLINK_DELAY_MS); 
 +  Serial.println(F(" ms.")); 
 +
 + 
 +// Parse commands like: 
 +//   list 
 +//   pin 10 
 +//   delay 250 
 +//   stop 
 +void handleCommand(String line) { 
 +  line.trim(); 
 +  if (line.length() == 0) return; 
 + 
 +  line.toLowerCase(); 
 +  if (line == "list") { 
 +    printTestSafePins(); 
 +    return; 
 +  } 
 +  if (line == "stop") { 
 +    stopBlink(); 
 +    return; 
 +  } 
 +  if (line.startsWith("pin ")) { 
 +    int gpio = line.substring(4).toInt(); 
 +    startBlink(gpio); 
 +    return; 
 +  } 
 +  if (line.startsWith("delay ")) { 
 +    int v = line.substring(6).toInt(); 
 +    if (v < 10) v = 10;  // clamp a bit 
 +    BLINK_DELAY_MS = (uint32_t)v; 
 +    Serial.print(F("Set blink delay to ")); 
 +    Serial.print(BLINK_DELAY_MS); 
 +    Serial.println(F(" ms.")); 
 +    return; 
 +  } 
 + 
 +  // Single-number shortcut: just type the GPIO number 
 +  bool allDigits = true; 
 +  for (size_t i = 0; i < (size_t)line.length(); ++i) { 
 +    if (!isDigit(line[i])) { 
 +      allDigits = false; 
 +      break; 
 +    } 
 +  } 
 +  if (allDigits) { 
 +    startBlink(line.toInt()); 
 +    return; 
 +  } 
 + 
 +  Serial.println(F("Commands:")); 
 +  Serial.println(F("  list               -> show test-safe GPIOs")); 
 +  Serial.println(F("  pin <gpio>         -> start blinking that GPIO")); 
 +  Serial.println(F("  <gpio>             -> same as 'pin <gpio>'")); 
 +  Serial.println(F("  delay <ms>         -> set blink delay")); 
 +  Serial.println(F("  stop               -> stop and release pin")); 
 +
 + 
 +void setup() { 
 +  Serial.begin(115200); 
 +  // give USB-Serial/JTAG a moment 
 +  delay(400); 
 +  Serial.println(F("\nESP32-S3 GPIO Quick Tester")); 
 +  Serial.println(F("Type 'list' to see test-safe pins, 'pin <gpio>' to blink, 'delay <ms>' to change speed, 'stop' to release.\n")); 
 +  printTestSafePins(); 
 +
 + 
 +void loop() { 
 +  // Process serial line input 
 +  static String line; 
 +  while (Serial.available()) { 
 +    char c = (char)Serial.read(); 
 +    if (c == '\r') continue; 
 +    if (c == '\n') { 
 +      handleCommand(line); 
 +      line = ""; 
 +    } else { 
 +      line += c; 
 +    } 
 +  } 
 + 
 +  // Blink the selected pin 
 +  if (blinking && currentPin >= 0) { 
 +    digitalWrite(currentPin, HIGH); 
 +    delay(BLINK_DELAY_MS); 
 +    digitalWrite(currentPin, LOW); 
 +    delay(BLINK_DELAY_MS); 
 +  } else { 
 +    delay(5); 
 +  } 
 +
 +</file> 
 + 
 + 
 +====== Feedback ====== 
 + 
 + 
 +If you’d like to suggest corrections or aren’t satisfied with how I wrote this short article, feel free to ping me on [[https://t.me/I_am_6r1d|Telegram]]. 
 + 
 +This topic might deserve to be expanded into a document outside of my personal page. 
 +Then again, I’m not sure how many people around here actually use ESP32-S3.
tamiwiki/users/6r1d/diymall_esp32_s3_fixture.1760779748.txt.gz · Last modified: 2025/10/18 12:29 by 6r1d