1. Тема дня
Когда ошибка обнаружена, не всегда нужно перезагружать весь MCU. Строим recovery ladder:
fault
-> local FSM recovery
-> service restart
-> peripheral reset
-> hardware power cycle
-> MCU reset
-> SAFE MODEГлавная мысль: watchdog — последняя линия защиты, а не основная recovery-архитектура.
2. Зачем это нужно
EC25 может зависнуть, но input/ADC/config остаются здоровыми. Перезагружать весь ESP32 при каждом LTE fault слишком грубо. MQTT можно перезапустить без modem reset. Storage error может перевести систему в DEGRADED, не ломая phase detection.
3. Теория
Fault domain
Граница восстановимости:
INPUT DOMAIN: GPIO/ADC/phase FSM
MODEM DOMAIN: UART/AT/EC25/PDP
MQTT DOMAIN: socket/MQTT client/commands
STORAGE DOMAIN: NVS/config
DIAGNOSTIC DOMAIN: logger/metricsHealth lease
Подсистема продлевает lease, когда делает полезный progress.
typedef struct {
uint32_t service_id;
health_state_t state;
uint32_t generation;
uint64_t last_progress_us;
uint32_t progress_counter;
uint32_t lease_timeout_ms;
uint32_t restart_count;
} health_record_t;Heartbeat в loop недостаточен. Нужно progress: AT transaction finished, ADC block processed, FSM meaningful transition.
Supervisor
Supervisor не знает внутренности AT/CEREG. Подсистема сообщает health, reason, suggested recovery. Supervisor решает budget/escalation.
Recovery ladder EC25
L0 retry AT transaction
L1 reset AT parser + transactions
L2 restart modem software service
L3 hardware EC25 reset
L4 power-cycle EC25
L5 restart ESP32
L6 SAFE MODERestart budget
Ограничить количество restart в окне времени:
modem software restart: max 3 / 5 min
hardware modem reset: max 2 / 15 min
MCU reboot: max 3 / 30 minService generation
После restart generation++ и старые события игнорируются. Restart transaction
RUNNING -> QUIESCING -> STOPPING -> RESETTING -> STARTING -> VERIFYING -> RUNNINGHEALTH_OK выставляется только после proof of health, не сразу после init.
4. Типичные ошибки
- Supervisor просто кормит watchdog.
- Heartbeat обновляется в каждом loop без useful progress.
- Каждая подсистема сама вызывает esp_restart().
- Нет restart budget.
- Task restart считается service restart.
- Forced task deletion как стандартная recovery.
- Restart без generation increment.
- Events приходят до полной initialization новой service genera- tion.
- HEALTH_OK сразу после init().
- Optional modem fault вызывает reboot critical input logic.
- Dependent MQTT fault считается независимым от modem fail- ure.
- Supervisor знает внутренние AT/CEREG детали.
- Hardware watchdog feed вызывается из timer независимо от health.
- Safe mode использует те же зависимости, что normal mode.
5. Практическое задание
Создай SERVICE_INPUT и SERVICE_MODEM health records:
typedef enum { SERVICE_INPUT = 0, SERVICE_MODEM, SERVICE_COUNT } service_id_t;
typedef struct {
health_state_t state;
uint32_t generation;
uint32_t progress;
uint64_t last_progress_us;
uint32_t reason;
} service_health_t;Добавь progress API:
void supervisor_report_progress(service_id_t service)
{
service_health_t *h = &s_health[service];
h->progress++;
h->last_progress_us = platform_monotonic_us();
}Политики:
INPUT: lease=1000ms, critical=true, max_local_restarts=1
MODEM: lease=10000ms, critical=false, max_local_restarts=3Сломай modem progress fault injection’ом. Ожидание: supervisor detects expired lease -> modem restart -> generation++ -> ESP32 не reboot.
6. Что попробовать дальше
- Связать critical input_progress с ESP-IDF TWDT user API.
- Сделать CLI health.
- HIL: Raspberry Pi перестаёт отвечать на AT, проверяет уровень escalation и budget.
Задание
Modem lease истёк, но critical input processing исправен. Опишите ограниченное recovery, сохраняющее здоровую domain и исключающее старые modem events после restart.
Критерии самопроверки: Сохраните input processing, задайте budget, увеличьте generation, отклоните stale events и проверьте health.
Показать ответ автора
Supervisor применяет modem policy в пределах restart budget, quiesce/restart modem service вместо немедленного reboot всего MCU, увеличивает generation и отклоняет старые events. HEALTH_OK возвращается после проверенного progress. Escalation выполняется при неудаче нижнего уровня или исчерпании budget.