SOLID C++ ESP32
Límites estrictos
- Preferir archivos de menos de 400 líneas; dividir cuando una unidad de traducción acumule varias responsabilidades
- Máximo ~30 métodos por clase
- Preferir cuerpos de función de menos de ~60 líneas (extraer ayudantes)
- Sin clases anidadas — mover los tipos internos a su propia cabecera / TU
- Exactamente uno tipo primario (
class/struct/enum/enum class) por cabecera pública cuando el proyecto lo requiera (etapa de inicialización / superficies estrictas)
Lenguaje y estilo
- El código, comentarios y documentación confirmados deben estar en inglés
- Usar siempre llaves en
if/for/while/switch - Sin cuerpos de función de una sola línea para lógica no trivial
- Nombres descriptivos (
telemetryManager, nomgr/tmpexcepto índices de bucle triviales) - Preferir un bloque
namespacepor unidad de traducción - Las cabeceras declaran la intención; los archivos
.cppse mantienen simples y directos - Los métodos públicos en interfaces
I*.h(y otras cabeceras públicas seleccionadas) necesitan un comentario de intención///de una línea
Arquitectura (SOLID + DI)
- Preferir interfaces (
I*) en los límites del dominio - Depender de abstracciones en el código de aplicación / dominio; construir adaptadores concretos únicamente en
main.cppo en una raíz de composición explícita - El hardware (BLE, Wi-Fi, pantalla, NVS, buses) se mantiene detrás de interfaces para que las pruebas unitarias del host usen simulaciones (fakes), no cirugía de
#ifdef - Preferir pequeños analizadores puros probados unitariamente en compilaciones
native/ host - Preferir composición sobre objetos dios en crecimiento
- Convertir
void*de FreeRTOS inmediatamente en un puntero tipado con nombre - Preferir ayudantes del proyecto (por ejemplo, copia segura de cadenas C) sobre variantes de
strncpy
Aislamiento de dominio / capa
- Mantener las pilas exclusivas del concentrador (web, autenticación, OTA, API HTTP) fuera de los objetivos puente / hoja
- No incluir (
#include) cabeceras de adaptadores concretos desde la capa incorrecta; si una guardarraya enumera nombres de adaptadores, respetarla
Sin instrumentación de pruebas en APIs de producción
El código de producción no debe ser modificado por banderas de compilación de pruebas.
- Prohibido en cabeceras y
.cpp:UNIT_TEST,#ifdef UNIT_TEST,#ifndef UNIT_TEST,#if defined(UNIT_TEST)o cualquier compuerta equivalente solo para pruebas (esp_cpp_guardrails falla por defecto) - Preferir DI: extraer una interfaz, inyectar una simulación (fake) en pruebas del host, o excluir las TU de adaptadores de dispositivo del
build_src_filterde compilaciónnative/ host — nunca usar ifdef para modificar fuentes de producción - La detección de plataforma (
__has_include(<esp_random.h>),ESP_PLATFORM) está bien; las banderas de compilación de pruebas que reestructuran la lógica de producción no lo están - Mantener tipos portables en cabeceras (
std::mutex, etc.) o inclusiones incondicionales de dispositivo en TU solo de dispositivo
Olor → solución:
| Olor | Solución |
|---|---|
#ifndef UNIT_TEST alrededor de llamadas Wi-Fi / BLE en un servicio | Inyectar IWifiConnector / IBleClient; simular en pruebas del host |
Cabeceras que definen tipos diferentes cuando UNIT_TEST | Siempre tipo portable, o dividir adaptador de dispositivo |
| Clase dios necesita ifdefs para compilar en host | Dividir responsabilidades; la raíz de composición conecta adaptadores |
Secretos
- Nunca codificar contraseñas Wi-Fi, contraseñas OTA / web, secretos de dispositivo, PMKs, tokens o credenciales LAN en el código fuente
- Inyectar secretos mediante variables de entorno / banderas de compilación consumidas por PlatformIO (o equivalente) — nunca confirmar
.envcon secretos reales
FreeRTOS de doble núcleo (ESP32)
El ESP32 clásico tiene dos núcleos:
| Núcleo | ID | Uso típico |
|---|---|---|
| PRO_CPU | 0 | Trabajo relacionado con Wi-Fi / BT / radio (bucles TX/RX de ESP-NOW, tareas de radio BLE/SPP) |
| APP_CPU | 1 | Trabajo pesado de aplicación: interfaz de usuario en pantalla, HTTP/web, ráfagas de análisis, SD, bucles no relacionados con radio |
Valores predeterminados (anular solo con un caso especial documentado):
- Asignar tareas sensibles a radio / pila con
xTaskCreatePinnedToCore(..., kRadioCoreId)dondekRadioCoreId = 0 - Asignar tareas de UI / pesadas no relacionadas con radio a
kAppCoreId = 1 - Evitar trabajo bloqueante prolongado en el núcleo 0; esto agota la pila de radio
- Preferir constantes con nombre (
kRadioCoreId/kAppCoreId) sobre valores mágicos0/1
Validación
- Ejecutar el script de guardarrayas del proyecto (o
tools/esp_cpp_guardrails) después de cambios estructurales - Agregar la verificación de unidad/host más pequeña que falle si la nueva lógica se rompe
- Compilar después de cambios en interfaces
- Actualizar el README cuando cambien el comportamiento, los scripts, las suposiciones de hardware o la arquitectura
- Preferir una revisión de calidad de código bloqueante antes de fusionar cuando el proyecto lo requiera
Lista de verificación
- Llaves en todas partes; identificadores y comentarios en inglés
- Menos de ~400 líneas / ~30 métodos; sin clases anidadas
- Adaptadores concretos solo en la raíz de composición; el dominio depende de
I* - Sin
UNIT_TESTni ifdefs de prueba en cabeceras; evitarlos en.cpp - Sin secretos codificados
-
///en nuevos métodos de interfaz pública - Tareas de radio en el núcleo 0; trabajo pesado de aplicación en el núcleo 1 (salvo excepción documentada)
- Guardarrayas / pruebas en verde