API — Диагностика¶
Состояние устройства, лог, watchdog, self-test, EEPROM-диагностика.
Источник: src/API/System/, src/api_handlers.cpp.
GET /api/status¶
Минимальный публичный статус — не требует аутентификации. Использует веб-UI при первом коннекте, чтобы определить IP и режим.
Роль: USER (с allowPublicRead=true — реально без auth).
Запрос¶
Ответ — 200 OK¶
{
"wifi_mode": "STA",
"ip": "192.168.100.61",
"wifi_status": "CONNECTED",
"ssid": "MyHome",
"uptime": 38421,
"version": "0.12.0",
"dosators": [
{"ch": 0, "active": false},
{"ch": 1, "active": false},
{"ch": 2, "active": false}
]
}
| Поле | Тип | Описание |
|---|---|---|
wifi_mode |
string | STA, AP, или AP_STA |
ip |
string | IPv4 в текущей роли (AP IP или STA IP) |
wifi_status |
string | CONNECTED, CONNECTING, CONNECT_FAILED, AP_MODE |
ssid |
string | SSID текущей сети (STA) или AP |
uptime |
int | Секунды с boot |
version |
string | Версия прошивки (FW_VERSION) |
dosators[] |
array | По одному элементу на канал, active = DOSING |
GET /api/diag/system¶
Детальные метрики: heap, WiFi-RSSI, NTP, чип, OTA-раздел. Опрашивается дашбордом каждые 5 секунд.
Роль: ADMIN.
Запрос¶
Ответ — 200 OK¶
{
"heap": {
"free": 142000,
"min_free": 95000,
"largest_block": 90000,
"total": 327680
},
"task_count": 18,
"wifi": {
"connect_count": 1,
"rssi_cur": -64,
"rssi_min": -78,
"rssi_max": -52,
"rssi_avg": -67,
"session_ms": 1234567
},
"ntp": {
"synced": true,
"server": "pool.ntp.org",
"time": "2026-05-13T14:22:08"
},
"uptime_ms": 1234567,
"reset_reason": "SW_RESET",
"firmware": "0.12.0",
"ota_partition": "app0",
"chip": {
"model": "ESP32",
"cores": 2,
"revision": 1
}
}
| Поле | Описание |
|---|---|
heap.free |
Свободная heap, байт |
heap.min_free |
Минимум за всё время uptime (low-water mark) |
heap.largest_block |
Крупнейший непрерывный блок (для аллокаций > 4 КБ) |
heap.total |
Полный размер heap |
task_count |
Текущее число FreeRTOS-задач |
wifi.rssi_cur |
RSSI сейчас (dBm). null если не подключён |
wifi.rssi_* |
min/max/avg RSSI за текущую сессию |
wifi.session_ms |
Время непрерывного подключения, мс |
wifi.connect_count |
Число (пере)подключений за uptime |
ntp.synced |
NTP-синхронизация прошла |
ntp.time |
ISO-8601 локальное время (только если synced) |
reset_reason |
POWERON, SW_RESET, PANIC, BROWNOUT, TASK_WDT… |
ota_partition |
app0 или app1 — активный раздел |
GET /api/diag/alarms¶
Список активных аварий (alarms). Публичный — UI показывает их без авторизации.
Роль: USER (allowPublicRead=true).
Запрос¶
Ответ — 200 OK¶
{
"active": [
{
"id": 3,
"name": "LEV_EMPTY_CH0",
"severity": "WARNING",
"first_seen": 1715600000,
"last_seen": 1715603421,
"count": 5,
"first_seen_iso": "2026-05-13T12:00:00",
"last_seen_iso": "2026-05-13T12:57:01"
}
]
}
| Поле | Тип | Описание |
|---|---|---|
id |
int | Internal alarm ID (порядковый из enum) |
name |
string | Текстовое имя из alarm-таблицы |
severity |
string | ERROR, WARNING, INFO |
first_seen |
int | Unix epoch первого срабатывания |
last_seen |
int | Unix epoch последнего срабатывания |
count |
int | Сколько раз срабатывала |
first_seen_iso |
string | ISO-8601 (только если NTP synced, epoch > 2024) |
last_seen_iso |
string | Аналогично |
active пустой — нет активных аварий.
GET /api/diag/log¶
Ring-buffer системного лога (последние 64 записи).
Роль: ADMIN.
Параметры query¶
| Параметр | Тип | Default | Описание |
|---|---|---|---|
level |
int | 0 | Минимальный уровень: 1=ERROR, 2=WARN, 3=INFO, 4=DEBUG. 0 = все. |
limit |
int | 50 | Макс. записей (1…64) |
Запрос¶
Ответ — 200 OK¶
{
"count": 12,
"entries": [
{
"ts": 1715603421,
"level": "ERROR",
"tag": "MDNS",
"msg": "Cannot allocate memory (line: 3088, free heap: 1028 bytes)"
},
{
"ts": 1715603415,
"level": "WARN",
"tag": "telegram",
"msg": "TLS handshake timeout"
}
]
}
| Поле | Описание |
|---|---|
count |
Фактически возвращено записей |
ts |
Unix epoch секунд (0 если NTP не synced на момент записи) |
level |
ERROR, WARN, INFO, DEBUG |
tag |
Источник (например, MDNS, webserver, dosator) |
msg |
Текст лога |
GET /api/diag/watchdog¶
Состояние софт-watchdog'а: список задач под наблюдением, тайминги.
Роль: ADMIN.
Запрос¶
Ответ — 200 OK¶
{
"monitor_running": true,
"entries": [
{
"name": "dosator_0",
"timeout_ms": 5000,
"policy": "REBOOT",
"since_feed_ms": 12,
"missed": 0
},
{
"name": "telegram",
"timeout_ms": 300000,
"policy": "ALARM_ONLY",
"since_feed_ms": 28000,
"missed": 0
}
]
}
| Поле | Описание |
|---|---|
monitor_running |
Фоновая task проверки запущена |
name |
Имя watchee-задачи |
timeout_ms |
Таймаут до срабатывания |
policy |
REBOOT (перезагрузка) или ALARM_ONLY (только авария) |
since_feed_ms |
Время с последнего wdtFeed() |
missed |
Счётчик пропущенных дедлайнов |
GET /api/diag/eeprom¶
Неразрушающая I2C-диагностика чипа M24C16: ping, ID-страница (для оригинальных ST), размер чипа по числу ответивших I2C-устройств, скан всей I2C-шины. Безопасна для вызова в любой момент.
Деструктивный автодетект размера (Address Wraparound / Binary search / Page stamp) и RW-тест паттернами удалены — они писали тестовые байты по всему чипу и затирали кольцо счётчиков наработки. Полная запись доступна только явно —
POST /api/diag/eeprom/format(ниже).
Роль: ADMIN.
Запрос¶
Ответ — 200 OK¶
{
"ping": true,
"chip": {
"st_page_responded": true,
"st_mfr_code": "0x20",
"st_dev_type": "0x05",
"st_revision": "0x00",
"st_mfr_match": true,
"st_dev_match": true,
"i2c_eeprom_count": 8,
"detected_size_bytes": 2048,
"size_match": true,
"detected_page_size": 16,
"page_size_match": true,
"declared_page_size": 16,
"declared_total_size": 2048
},
"i2c_scan": ["0x50", "0x51", "0x52", "0x53", "0x54", "0x55", "0x56", "0x57"]
}
Поля:
ping— I2C ping0x50ответил.chip.st_*— ST ID-страница (mfr / dev / rev; клоны не отвечают).chip.i2c_eeprom_count— сколько адресов0x50…0x57откликнулись; M24C16 → 8.chip.detected_size_bytes— размер чипа, вычислен изi2c_eeprom_count(8 × 256 = 2048), неразрушающе.chip.detected_page_size— размер страницы (декларацияconfig.h, опытно не зондируется).chip.size_match/page_size_match— совпадение сEEPROM_TOTAL_BYTES/EEPROM_PAGE_SIZE.chip.declared_*— значения изconfig.hдля сравнения.i2c_scan— все ответившие I2C-адреса (0x08…0x77).
Подробности — ADR-007.
POST /api/diag/eeprom/format¶
ДЕСТРУКТИВНО. Полная очистка чипа EEPROM — запись 0xFF во все байты
и обнуление колец счётчиков наработки. Абсолютная RUN-наработка и время
насоса всех каналов после этого = 0. Это единственный способ обнулить
абсолютные счётчики.
Роль: ADMIN + повторный ввод пароля администратора в теле запроса.
Без верного пароля — 403.
Запрос¶
curl -u admin:admin123 -X POST \
-H 'Content-Type: application/json' -d '{"password": "admin123"}' \
http://awdc.local/api/diag/eeprom/format
password(string) — пароль администратора, обязателен.
Ответ — 200 OK¶
Неверный пароль → 403 {"ok": false, "message": "Wrong admin password"}.
В Web-UI — карточка «Опасная зона» (вкладка «Система»): раскрытие закрыто паролём администратора, внутри — кнопка форматирования.
GET /api/diag/selftest¶
Сводный результат всех probes (HARDWARE + STORAGE + NETWORK). Boot-probes выполняются один раз при старте, network-probes — по требованию (см. POST ниже).
Роль: ADMIN.
Запрос¶
Ответ — 200 OK¶
{
"overall": "OK",
"boot_duration_ms": 87,
"net_running": false,
"net_duration_ms": 2415,
"capabilities": {
"eeprom": {
"present": true,
"size_bytes": 2048,
"page_size": 16,
"slot_count": 128,
"vendor_st": true
},
"hmi": { "present": true },
"dispensers": {
"gpio_ok": [true, true, true],
"analog_ok": [true, true, true]
},
"storage": { "nvs_ok": true }
},
"probes": [
{
"name": "hmi.ping",
"category": "HARDWARE",
"passed": true,
"severity": "OK",
"detail": "TM1638 responded"
},
{
"name": "net.dns",
"category": "NETWORK",
"passed": true,
"severity": "OK",
"detail": "resolved 8.8.8.8 in 28 ms"
}
]
}
| Поле | Описание |
|---|---|
overall |
Worst-of-all: OK, WARN, FATAL |
boot_duration_ms |
Длительность boot-probes (HARDWARE+STORAGE) |
net_running |
Сейчас работает асинхронный network-probe? |
net_duration_ms |
Длительность последней network-сессии |
capabilities.* |
Зафиксированные при boot ёмкости железа |
probes[] |
Все probes с их результатами |
POST /api/diag/selftest/network/refresh¶
Перезапустить network-probes асинхронно. Возвращает 202 сразу,
результаты пишутся в кэш и доступны через GET /api/diag/selftest
по готовности.
Роль: ADMIN.
Запрос¶
Ответ — 202 Accepted¶
Ошибки¶
| Код | Тело |
|---|---|
| 409 | {"error":"network probe already running"} |
| 401 | {"error":"Unauthorized"} |
| 403 | {"error":"Forbidden"} |
Замечания¶
- В boot НЕ запускается (см. feedback_network_probes) — только по этому эндпоинту. Так делается чтобы не блокировать стартап при flaky-сети.
- Длится ~2…5 секунд. Прогресс не отдаётся — клиент polling'ит
GET /api/diag/selftestпокаnet_runningне станетfalse.
GET /api/diag/coredump¶
Статус сохранённого core dump + summary (задача, PC, backtrace). Core dump пишется в одноимённую flash-партицию при panic/TWDT-ребуте.
Роль: ADMIN.
Запрос¶
Ответ — 200 OK (дамп есть)¶
{
"available": true,
"size": 8192,
"task": "dosator_1",
"pc": "0x400e1234",
"elf_sha256": "a1b2c3...",
"backtrace": ["0x400e1234", "0x40098765", "0x400d4321"],
"bt_corrupted": false
}
Ответ — 200 OK (дампа нет)¶
| Поле | Описание |
|---|---|
available |
Есть ли валидный core dump в партиции |
size |
Размер дампа, байт |
task |
Имя задачи, в которой произошёл сбой |
pc |
Program counter на момент сбоя |
elf_sha256 |
SHA-256 ELF прошивки — для сопоставления с версией |
backtrace |
Цепочка адресов вызова (для разбора нужен ELF, см. raw) |
bt_corrupted |
Backtrace частично повреждён |
GET /api/diag/coredump/raw¶
Скачать бинарь core dump для оффлайн-разбора.
Роль: ADMIN.
Анализ (нужен .elf ровно той прошивки, что крешнулась):
404 {"error":"no core dump"} — дампа нет.
POST /api/diag/coredump/erase¶
Стереть сохранённый core dump.
Роль: ADMIN.
Ответ 200 OK: {"success": true}