Skip to main content

embedded-best-practices

Embedded systems development best practices for ESP32, FreeRTOS, and ESP-IDF. Use when writing firmware code, reviewing implementations, or learning about embedded patterns.

Zur Installation springen

Quellinformationen

Repository
laurigates/mcu-tinkering-lab
Letzte Quellaktivität
7. April 2026 um 07:36
Erkannte Sprache von SKILL.md
Englisch
Sterne
7
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
embedded-best-practices
description
Embedded systems development best practices for ESP32, FreeRTOS, and ESP-IDF. Use when writing firmware code, reviewing implementations, or learning about embedded patterns.
user-invocable
false
allowed-tools
Read, Grep, Glob
# Embedded Best Practices Skill This skill provides comprehensive guidance for embedded systems development with a focus on ESP32 and ESP-IDF. ## When to Use - Writing new firmware code - Reviewing implementation approaches - Learning embedded patterns - Debugging issues - Optimizing code ## ESP-IDF Project Structure ### Recommended Layout ``` project/ ├── main/ │ ├── CMakeLists.txt │ ├── main.c │ ├── Kconfig.projbuild │ └── include/ │ └── project.h ├── components/ │ └── custom_component/ │ ├── CMakeLists.txt │ ├── component.c │ └── include/ │ └── component.h ├── CMakeLists.txt ├── sdkconfig.defaults ├── partitions.csv └── README.md ``` ### Component Organization - One responsibility per component - Clear public interface in include/ - Private implementation in src/ - Document dependencies ## FreeRTOS Best Practices ### Task Design ```c // Good: Proper task function void sensor_task(void *pvParameters) { sensor_config_t *config = (sensor_config_t *)pvParameters; while (1) { // Do work read_sensor(config); // Must yield to prevent watchdog vTaskDelay(pdMS_TO_TICKS(100)); } // Tasks should never return, but if they do: vTaskDelete(NULL); } // Create with appropriate stack xTaskCreate(sensor_task, "sensor", 4096, &config, 5, &task_handle); ``` ### Stack Sizing - Start with 4096 bytes for typical tasks - Use `uxTaskGetStackHighWaterMark()` to measure actual usage - Add 25% safety margin - Camera/network tasks may need 8192+ ### Synchronization ```c // Mutex for shared resource protection SemaphoreHandle_t mutex = xSemaphoreCreateMutex(); // Use with timeout, never infinite wait in production if (xSemaphoreTake(mutex, pdMS_TO_TICKS(1000)) == pdTRUE) { // Access shared resource xSemaphoreGive(mutex); } else { ESP_LOGE(TAG, "Failed to acquire mutex"); } ``` ### Queue Usage ```c // Prefer queues for inter-task communication QueueHandle_t data_queue = xQueueCreate(10, sizeof(sensor_data_t)); // Send with timeout sensor_data_t data = {.value = 42}; if (xQueueSend(data_queue, &data, pdMS_TO_TICKS(100)) != pdTRUE) { ESP_LOGW(TAG, "Queue full, dropping data"); } // Receive sensor_data_t received; if (xQueueReceive(data_queue, &received, portMAX_DELAY) == pdTRUE) { process_data(&received); } ``` ## Memory Management ### Static vs Dynamic Allocation ```c // Prefer static for fixed resources static StaticTask_t task_buffer; static StackType_t task_stack[4096]; TaskHandle_t task = xTaskCreateStatic( task_func, "task", 4096, NULL, 5, task_stack, &task_buffer ); // Dynamic for variable-size resources char *buffer = heap_caps_malloc(size, MALLOC_CAP_DEFAULT); if (buffer == NULL) { ESP_LOGE(TAG, "Allocation failed"); return ESP_ERR_NO_MEM; } // ... use buffer ... free(buffer); ``` ### String Handling ```c // Bad char buf[64]; sprintf(buf, "Value: %d", value); // Good - prevents buffer overflow char buf[64]; snprintf(buf, sizeof(buf), "Value: %d", value); // For const strings, keep in flash static const char *TAG = "mymodule"; ESP_LOGI(TAG, "Starting"); ``` ## Error Handling ### ESP-IDF Error Pattern ```c esp_err_t initialize_peripheral(void) { esp_err_t ret; ret = gpio_config(&io_conf); if (ret != ESP_OK) { ESP_LOGE(TAG, "GPIO config failed: %s", esp_err_to_name(ret)); return ret; } ret = spi_bus_initialize(SPI2_HOST, &bus_cfg, DMA_CHAN); if (ret != ESP_OK) { ESP_LOGE(TAG, "SPI init failed: %s", esp_err_to_name(ret)); // Clean up GPIO if needed return ret; } return ESP_OK; } // Use ESP_ERROR_CHECK for fatal errors only ESP_ERROR_CHECK(nvs_flash_init()); ``` ### Graceful Degradation ```c // Don't crash on non-fatal errors if (wifi_connect() != ESP_OK) { ESP_LOGW(TAG, "WiFi failed, running in offline mode"); run_offline_mode(); } ``` ## Peripheral Initialization ### GPIO Configuration ```c gpio_config_t io_conf = { .pin_bit_mask = (1ULL << GPIO_NUM_2), .mode = GPIO_MODE_OUTPUT, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; ESP_ERROR_CHECK(gpio_config(&io_conf)); ``` ### I2C Setup ```c i2c_config_t conf = { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_21, .scl_io_num = GPIO_NUM_22, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = 400000, }; ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &conf)); ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0)); ``` ## Interrupt Handlers ### Keep ISRs Minimal ```c // ISR - keep it SHORT static void IRAM_ATTR gpio_isr_handler(void *arg) { uint32_t gpio_num = (uint32_t)arg; // Just signal, don't process xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); } // Process in task void gpio_task(void *arg) { uint32_t io_num; while (1) { if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) { // Heavy processing here, not in ISR process_gpio_event(io_num); } } } ``` ### IRAM Considerations - Mark ISR handlers with `IRAM_ATTR` - Functions called from ISR also need `IRAM_ATTR` - Minimize IRAM usage (limited to ~128KB) ## WiFi Best Practices ### Connection Handling ```c // Use event loop for WiFi events static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) { ESP_LOGI(TAG, "Disconnected, retrying..."); esp_wifi_connect(); } } // Register handler ESP_ERROR_CHECK(esp_event_handler_instance_register( WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL)); ``` ### PSRAM and WiFi - WiFi uses significant memory - Enable PSRAM for memory-intensive applications - Use `CONFIG_SPIRAM_USE_MALLOC` to extend heap ## Logging ### Log Levels ```c ESP_LOGE(TAG, "Error: critical failure"); // Always shown ESP_LOGW(TAG, "Warning: unusual condition"); // Important ESP_LOGI(TAG, "Info: normal operation"); // Default ESP_LOGD(TAG, "Debug: detailed info"); // Development ESP_LOGV(TAG, "Verbose: very detailed"); // Tracing ``` ### Production Logging - Set log level via menuconfig - Reduce logging in production (ESP_LOGW minimum) - Log strings consume flash space ## Power Management ### Light Sleep ```c // Enable automatic light sleep esp_pm_config_esp32_t pm_config = { .max_freq_mhz = 240, .min_freq_mhz = 80, .light_sleep_enable = true, }; ESP_ERROR_CHECK(esp_pm_configure(&pm_config)); ``` ### Deep Sleep ```c // Configure wakeup source esp_sleep_enable_timer_wakeup(60 * 1000000); // 60 seconds // Enter deep sleep esp_deep_sleep_start(); ``` ## Additional Resources For more detailed information on specific topics, consult: - ESP-IDF Programming Guide - FreeRTOS documentation - ESP32 Technical Reference Manual
Auf GitHub ansehen