From 0d21c5c205329cd03aee66952796fcddbd894665 Mon Sep 17 00:00:00 2001 From: Krzysztof Kallas Date: Tue, 29 Sep 2026 17:30:25 +0200 Subject: [PATCH 1/3] Store all text files with LF line endings Some files had been saved with CRLF on Windows, so a one-line change showed as a whole-file diff. .gitattributes now stores text as LF (.bat stays CRLF for cmd, .command/.sh LF for macOS). Co-Authored-By: Claude Opus 5.5 --- .gitattributes | 2 + README.md | 154 +- kota_tech/core/logline.py | 404 +- kota_tech/diag/engine.py | 1998 ++++----- kota_tech/diag/firmware.py | 294 +- kota_tech/diag/guides.py | 802 ++-- kota_tech/diag/guides.toml | 1480 +++---- kota_tech/diag/rules.toml | 1464 +++---- kota_tech/flash/image.py | 520 +-- kota_tech/matter/payload.py | 226 +- kota_tech/sd/provision.py | 522 +-- kota_tech/service/report.py | 310 +- kota_tech/ui/cheatsheet.py | 174 +- kota_tech/ui/diag_panel.py | 504 +-- kota_tech/ui/flash_tab.py | 728 ++-- kota_tech/ui/main_window.py | 848 ++-- kota_tech/ui/matter_qr.py | 152 +- ...t_A4CB8FC946F8_fw1.0.16_uncommissioned.txt | 2126 ++++----- .../live_brownout_loop_A4CB8FC946F8_fw1.0.txt | 3870 ++++++++--------- 19 files changed, 8290 insertions(+), 8288 deletions(-) diff --git a/.gitattributes b/.gitattributes index adfdc3d..8df85fe 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,6 @@ +* text=auto eol=lf *.command text eol=lf *.sh text eol=lf *.bat text eol=crlf *.bin binary +*.png binary diff --git a/README.md b/README.md index 471f15b..64b29fd 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,77 @@ -# KOTA Sensorbox Technician Tool - -Diagnoza sensorboxa przez USB, wgrywanie firmware z folderu `firmware/` i przygotowanie karty SD. -Działa na Windows 10/11 i macOS 13 (Ventura) lub nowszym, na Macach z Intelem i Apple Silicon. - -Repozytorium: https://gitea.kota-app.com/kkallas/kota-technician-tool (dostęp nadaje właściciel repozytorium). - -## Dla technika: dwa pliki - -| System | Aktualizacja | Uruchomienie | -|---|---|---| -| Windows | `update_windows.bat` | `run_windows.bat` | -| macOS | `update_macos.command` | `run_macos.command` | - -- **Aktualizacja** sprawdza na Gitea najnowszą wydaną wersję i pobiera tylko to, co się zmieniło - (pliki programu, firmware i potrzebne biblioteki). Uruchom ją, gdy firmware team ogłosi nową wersję, - albo po prostu przed wyjazdem do klienta. Pokazuje, co się zmieniło. -- **Uruchomienie** startuje program. Nie łączy się z Gitea, działa też bez internetu (po pierwszej aktualizacji). - -Nie trzymaj folderu narzędzia w Google Drive ani OneDrive. - -## Pierwsza instalacja (raz na komputer) - -1. Na Gitea otwórz **Releases** i przy najnowszej wersji pobierz **Source Code (ZIP)**. -2. Rozpakuj do zwykłego folderu, np. `Dokumenty\KOTA\technician-tool` (macOS: `~/Documents/KOTA/technician-tool`). -3. Uruchom plik aktualizacji dla swojego systemu. Za pierwszym razem: - - połączy folder z Gitea i zapyta o login do Gitea (hasło lub token); system je zapamięta, - - zainstaluje brakujące narzędzia za Twoją zgodą: Git (Windows: przez winget; macOS: Apple Command Line Tools - - po ich instalacji uruchom aktualizację jeszcze raz) oraz `uv` (menedżer Pythona, tylko dla bieżącego - użytkownika), - - pobierze biblioteki Pythona (1–2 minuty, potrzebny internet). -4. Uruchom program plikiem `run_...`. - -**macOS:** jeśli Finder odpowie, że pliku nie można uruchomić (zdarza się po rozpakowaniu zip), otwórz Terminal, -wpisz `bash ` (ze spacją), przeciągnij `update_macos.command` do okna i naciśnij Enter. Po pierwszej aktualizacji -oba pliki `.command` dają się już otwierać dwuklikiem. - -Zamiast zip można też zrobić `git clone https://gitea.kota-app.com/kkallas/kota-technician-tool.git` i dalej -używać tych samych dwóch plików. - -Konkretna starsza wersja (np. do serwisu nietypowego boxa): w folderze narzędzia `git switch --detach v0.8`; -kolejna aktualizacja wraca do najnowszej. - -Wszystkie wersje używają jednego środowiska Pythona poza folderem narzędzia: -`%LOCALAPPDATA%\KotaTechnicianTool\venv` (Windows), `~/Library/Caches/KotaTechnicianTool/venv` (macOS). -Stare środowiska `venv-0.x` z poprzednich wersji można usunąć. - -Sesje, kopie zapasowe flash i ustawienia są w `~/KotaTechnicianTool` (Windows: `C:\Users\\KotaTechnicianTool`). - -## Port sensorboxa - -- Windows: `COMx - Sensorbox (native USB) [MAC]` -- macOS: `/dev/cu.usbmodemXXXX - Sensorbox (native USB) [MAC]` - -Kabel USB musi przenosić dane (nie tylko ładowanie). Przy restartach z komunikatem o brownout użyj innego portu -lub zasilacza. - -## Firmware - -Wgrać można tylko pliki `.bin` z folderu `firmware/`. Lista pokazuje datę kompilacji zapisaną w pliku, -najnowszy build jest na górze. Nowy build: wrzuć plik do `firmware/` i kliknij **Refresh list**. - -## Dla zespołu - -- Praca nad zmianą: gałąź (`git switch -c feature/`) → commity z przechodzącymi testami → - `git push -u origin feature/` → pull request na Gitea → merge do `main`. -- Wydanie wersji: numer w `pyproject.toml`, `kota_tech/__init__.py` i `uv.lock`, tag `vX.Y` - (`git tag -a vX.Y -m "Technician tool vX.Y"`, `git push origin vX.Y`), opis w **Releases** na Gitea. - Aktualizacja techników bierze **tylko tagi `v*`** – zmiany w `main` bez tagu do nich nie trafiają. -- Nie commitujemy `ENV.TXT` z prawdziwym tokenem ani haseł Wi-Fi. -- Testy: - -``` -uv run --frozen pytest -q -``` - -Zmiany w wersjach: `docs/CHANGES-v0.*.md`; historia v0.1–v0.8 jest w tagach gita. +# KOTA Sensorbox Technician Tool + +Diagnoza sensorboxa przez USB, wgrywanie firmware z folderu `firmware/` i przygotowanie karty SD. +Działa na Windows 10/11 i macOS 13 (Ventura) lub nowszym, na Macach z Intelem i Apple Silicon. + +Repozytorium: https://gitea.kota-app.com/kkallas/kota-technician-tool (dostęp nadaje właściciel repozytorium). + +## Dla technika: dwa pliki + +| System | Aktualizacja | Uruchomienie | +|---|---|---| +| Windows | `update_windows.bat` | `run_windows.bat` | +| macOS | `update_macos.command` | `run_macos.command` | + +- **Aktualizacja** sprawdza na Gitea najnowszą wydaną wersję i pobiera tylko to, co się zmieniło + (pliki programu, firmware i potrzebne biblioteki). Uruchom ją, gdy firmware team ogłosi nową wersję, + albo po prostu przed wyjazdem do klienta. Pokazuje, co się zmieniło. +- **Uruchomienie** startuje program. Nie łączy się z Gitea, działa też bez internetu (po pierwszej aktualizacji). + +Nie trzymaj folderu narzędzia w Google Drive ani OneDrive. + +## Pierwsza instalacja (raz na komputer) + +1. Na Gitea otwórz **Releases** i przy najnowszej wersji pobierz **Source Code (ZIP)**. +2. Rozpakuj do zwykłego folderu, np. `Dokumenty\KOTA\technician-tool` (macOS: `~/Documents/KOTA/technician-tool`). +3. Uruchom plik aktualizacji dla swojego systemu. Za pierwszym razem: + - połączy folder z Gitea i zapyta o login do Gitea (hasło lub token); system je zapamięta, + - zainstaluje brakujące narzędzia za Twoją zgodą: Git (Windows: przez winget; macOS: Apple Command Line Tools + - po ich instalacji uruchom aktualizację jeszcze raz) oraz `uv` (menedżer Pythona, tylko dla bieżącego + użytkownika), + - pobierze biblioteki Pythona (1–2 minuty, potrzebny internet). +4. Uruchom program plikiem `run_...`. + +**macOS:** jeśli Finder odpowie, że pliku nie można uruchomić (zdarza się po rozpakowaniu zip), otwórz Terminal, +wpisz `bash ` (ze spacją), przeciągnij `update_macos.command` do okna i naciśnij Enter. Po pierwszej aktualizacji +oba pliki `.command` dają się już otwierać dwuklikiem. + +Zamiast zip można też zrobić `git clone https://gitea.kota-app.com/kkallas/kota-technician-tool.git` i dalej +używać tych samych dwóch plików. + +Konkretna starsza wersja (np. do serwisu nietypowego boxa): w folderze narzędzia `git switch --detach v0.8`; +kolejna aktualizacja wraca do najnowszej. + +Wszystkie wersje używają jednego środowiska Pythona poza folderem narzędzia: +`%LOCALAPPDATA%\KotaTechnicianTool\venv` (Windows), `~/Library/Caches/KotaTechnicianTool/venv` (macOS). +Stare środowiska `venv-0.x` z poprzednich wersji można usunąć. + +Sesje, kopie zapasowe flash i ustawienia są w `~/KotaTechnicianTool` (Windows: `C:\Users\\KotaTechnicianTool`). + +## Port sensorboxa + +- Windows: `COMx - Sensorbox (native USB) [MAC]` +- macOS: `/dev/cu.usbmodemXXXX - Sensorbox (native USB) [MAC]` + +Kabel USB musi przenosić dane (nie tylko ładowanie). Przy restartach z komunikatem o brownout użyj innego portu +lub zasilacza. + +## Firmware + +Wgrać można tylko pliki `.bin` z folderu `firmware/`. Lista pokazuje datę kompilacji zapisaną w pliku, +najnowszy build jest na górze. Nowy build: wrzuć plik do `firmware/` i kliknij **Refresh list**. + +## Dla zespołu + +- Praca nad zmianą: gałąź (`git switch -c feature/`) → commity z przechodzącymi testami → + `git push -u origin feature/` → pull request na Gitea → merge do `main`. +- Wydanie wersji: numer w `pyproject.toml`, `kota_tech/__init__.py` i `uv.lock`, tag `vX.Y` + (`git tag -a vX.Y -m "Technician tool vX.Y"`, `git push origin vX.Y`), opis w **Releases** na Gitea. + Aktualizacja techników bierze **tylko tagi `v*`** – zmiany w `main` bez tagu do nich nie trafiają. +- Nie commitujemy `ENV.TXT` z prawdziwym tokenem ani haseł Wi-Fi. +- Testy: + +``` +uv run --frozen pytest -q +``` + +Zmiany w wersjach: `docs/CHANGES-v0.*.md`; historia v0.1–v0.8 jest w tagach gita. diff --git a/kota_tech/core/logline.py b/kota_tech/core/logline.py index 6967f3a..7b061de 100644 --- a/kota_tech/core/logline.py +++ b/kota_tech/core/logline.py @@ -1,202 +1,202 @@ -"""Serial byte stream -> framed, cleaned, parsed log lines. - -ESP-IDF log format: " () : ", optionally -wrapped in ANSI colour codes. ROM/bootloader output (boot banner, reset -reason) has no level prefix and is classified separately, since a boot -banner is the only reliable reboot marker. - -Firmware 1.0.16+ adds structured diagnostic lines logged under the "KOTA" -tag ("I (812) KOTA: boot fw=1.0.16 build=4f10989e ... last_restart=none") -and prefixes every ESP_LOGE message with a stable error ID ("[E-SD-03] "). -Both are parsed here so the diagnostics engine reads fields directly -instead of matching message text. -""" - -from __future__ import annotations - -import re -import time -from dataclasses import dataclass, field -from enum import Enum - -from .redact import redact - -ANSI_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]") -ESP_LOG_RE = re.compile(r"^([EWIDV]) \((\d+)\) ([^:]{1,40}?): ?(.*)$") -# ESP32-S3 ROM prints e.g. "ESP-ROM:esp32s3-20210327" then -# "rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)". -BOOT_BANNER_RE = re.compile(r"^ESP-ROM:") -RESET_REASON_RE = re.compile(r"^rst:0x([0-9a-fA-F]+) \(([A-Z0-9_]+)\)") -# "[E-SD-03] Failed to initialize the card ..." (firmware error_ids.csv). -ERROR_ID_RE = re.compile(r"^\[(E-[A-Z0-9]+-\d{2,})\] ?") -KOTA_TAG = "KOTA" -PANIC_RE = re.compile( - r"Guru Meditation Error|abort\(\) was called|ESP_ERROR_CHECK failed|" - r"^Backtrace:|Rebooting\.\.\.$" -) - - -class LineKind(str, Enum): - LOG = "log" # ESP_LOGx line - BOOT = "boot" # ROM boot banner (start of a new boot) - RESET = "reset" # ROM reset-reason line - PANIC = "panic" # crash / abort output - KOTA = "kota" # structured "KOTA: key=value ..." line - RAW = "raw" # anything else (task list rows, line protocol, ...) - HOST = "host" # event generated by this tool (connect, reset, ...) - - -def host_event(text: str) -> LogLine: - return LogLine(f"--- {text} ---", LineKind.HOST) - - -@dataclass(frozen=True) -class LogLine: - text: str # cleaned + redacted - kind: LineKind - host_time: float = field(default_factory=time.time) - level: str | None = None # E/W/I/D/V for LOG lines - uptime_ms: int | None = None - tag: str | None = None - message: str | None = None - reset_reason: str | None = None - error_id: str | None = None # "E-SD-03" when the message carries one - record: str | None = None # KOTA lines: "boot", "i2c", "health", ... - fields: dict[str, str] = field(default_factory=dict) # KOTA key=value pairs - words: tuple[str, ...] = () # KOTA bare words ("verify", "OK", "->", ...) - - @property - def match_text(self) -> str: - """Text for message rules: the error-ID prefix removed, so rules - written for older firmware keep matching.""" - if self.error_id and self.message is not None and self.tag: - return self.text.replace(f"[{self.error_id}] ", "", 1) - return self.text - - -def parse_kota(body: str) -> tuple[str, dict[str, str], tuple[str, ...]]: - """'rtc verify running=1 delta=2s ok=1' -> ('rtc', {...}, ('verify',)). - - Values never contain spaces. The mic line has a parenthesised aside - ("(GPIO3=1, expected 1)"): brackets and commas are stripped from keys - and values, and the aside's words are kept as bare words. - """ - tokens = body.split() - if not tokens: - return "", {}, () - fields: dict[str, str] = {} - words: list[str] = [] - for tok in tokens[1:]: - key, eq, value = tok.partition("=") - if eq and key.strip("(,"): - key = key.lstrip("(") - # "0x68=DS1307(missing)" keeps its bracket; "(GPIO3=1," loses it. - value = value.rstrip(",") - if tok.startswith("(") or value.endswith(")") and "(" not in value: - value = value.rstrip(")") - fields[key] = value - else: - words.append(tok.strip("(),")) - return tokens[0], fields, tuple(w for w in words if w) - - -def clean(raw: str) -> str: - return ANSI_RE.sub("", raw).replace("\r", "").rstrip() - - -def parse_line(raw: str, host_time: float | None = None) -> LogLine: - text = redact(clean(raw)) - t = time.time() if host_time is None else host_time - if m := ESP_LOG_RE.match(text): - level, ms, tag, msg = m.groups() - tag = tag.strip() - if tag == KOTA_TAG: - record, fields, words = parse_kota(msg) - return LogLine(text, LineKind.KOTA, t, level, int(ms), tag, msg, - record=record, fields=fields, words=words) - eid = None - if em := ERROR_ID_RE.match(msg): - eid, msg = em.group(1), msg[em.end():] - return LogLine(text, LineKind.LOG, t, level, int(ms), tag, msg, error_id=eid) - if BOOT_BANNER_RE.match(text): - return LogLine(text, LineKind.BOOT, t) - if m := RESET_REASON_RE.match(text): - return LogLine(text, LineKind.RESET, t, reset_reason=m.group(2)) - if PANIC_RE.search(text): - return LogLine(text, LineKind.PANIC, t) - return LogLine(text, LineKind.RAW, t) - - -class LineFramer: - """Accumulates bytes and yields complete lines. - - A partial line is released after `idle_flush_s` without new bytes so - prompts / unterminated output are not held back forever. - - Lines are also split where a new log line starts in the middle - ("I (2302) chip[DL]: I (2302) chip[DL]: Posting..."): the two S3 cores - interleave output, and the USB-Serial-JTAG FIFO keeps the tail of an old - line while no host reads, releasing it glued in front of the next live - output. After a (re)open, the fragment in front of the first such split - is stale and dropped. Timing cannot identify it, because the FIFO only - flushes when the board next prints, possibly seconds later. - """ - - def __init__(self, idle_flush_s: float = 0.3, max_line: int = 4096, - drop_first_partial: bool = False): - self._buf = bytearray() - self._last_rx = 0.0 - self.idle_flush_s = idle_flush_s - self.max_line = max_line - self._first_line_pending = drop_first_partial - - def cancel_drop(self) -> None: - self._first_line_pending = False - - def feed(self, data: bytes, now: float | None = None) -> list[str]: - now = time.monotonic() if now is None else now - if data: - self._buf.extend(data) - self._last_rx = now - out: list[str] = [] - while (i := self._buf.find(b"\n")) >= 0: - out += self._emit(self._buf[:i]) - del self._buf[: i + 1] - if len(self._buf) >= self.max_line: - out += self._emit(self._buf) - self._buf.clear() - return out - - def poll_idle(self, now: float | None = None) -> list[str]: - now = time.monotonic() if now is None else now - if self._buf and now - self._last_rx >= self.idle_flush_s: - return self.flush() - return [] - - def flush(self) -> list[str]: - if not self._buf: - return [] - out = self._emit(self._buf) - self._buf.clear() - return out - - def _emit(self, raw: bytes | bytearray) -> list[str]: - parts = split_glued(bytes(raw).decode("utf-8", errors="replace")) - if self._first_line_pending: - self._first_line_pending = False - if len(parts) > 1: - parts = parts[1:] # stale FIFO fragment from before the open - return parts - - -# Start of a log line (optionally ANSI-coloured) or ROM banner. -_LINE_START = re.compile(r"(?:\x1b\[[0-9;]*m)?(?:[EWIDV] \(\d+\) \S|ESP-ROM:)") - - -def split_glued(text: str) -> list[str]: - # Left-to-right scan so an ANSI colour prefix stays with its line. - cuts = [m.start() for m in _LINE_START.finditer(text) if m.start() > 0] - if not cuts: - return [text] - bounds = [0, *cuts, len(text)] - return [text[a:b] for a, b in zip(bounds, bounds[1:]) if a < b] +"""Serial byte stream -> framed, cleaned, parsed log lines. + +ESP-IDF log format: " () : ", optionally +wrapped in ANSI colour codes. ROM/bootloader output (boot banner, reset +reason) has no level prefix and is classified separately, since a boot +banner is the only reliable reboot marker. + +Firmware 1.0.16+ adds structured diagnostic lines logged under the "KOTA" +tag ("I (812) KOTA: boot fw=1.0.16 build=4f10989e ... last_restart=none") +and prefixes every ESP_LOGE message with a stable error ID ("[E-SD-03] "). +Both are parsed here so the diagnostics engine reads fields directly +instead of matching message text. +""" + +from __future__ import annotations + +import re +import time +from dataclasses import dataclass, field +from enum import Enum + +from .redact import redact + +ANSI_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]") +ESP_LOG_RE = re.compile(r"^([EWIDV]) \((\d+)\) ([^:]{1,40}?): ?(.*)$") +# ESP32-S3 ROM prints e.g. "ESP-ROM:esp32s3-20210327" then +# "rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)". +BOOT_BANNER_RE = re.compile(r"^ESP-ROM:") +RESET_REASON_RE = re.compile(r"^rst:0x([0-9a-fA-F]+) \(([A-Z0-9_]+)\)") +# "[E-SD-03] Failed to initialize the card ..." (firmware error_ids.csv). +ERROR_ID_RE = re.compile(r"^\[(E-[A-Z0-9]+-\d{2,})\] ?") +KOTA_TAG = "KOTA" +PANIC_RE = re.compile( + r"Guru Meditation Error|abort\(\) was called|ESP_ERROR_CHECK failed|" + r"^Backtrace:|Rebooting\.\.\.$" +) + + +class LineKind(str, Enum): + LOG = "log" # ESP_LOGx line + BOOT = "boot" # ROM boot banner (start of a new boot) + RESET = "reset" # ROM reset-reason line + PANIC = "panic" # crash / abort output + KOTA = "kota" # structured "KOTA: key=value ..." line + RAW = "raw" # anything else (task list rows, line protocol, ...) + HOST = "host" # event generated by this tool (connect, reset, ...) + + +def host_event(text: str) -> LogLine: + return LogLine(f"--- {text} ---", LineKind.HOST) + + +@dataclass(frozen=True) +class LogLine: + text: str # cleaned + redacted + kind: LineKind + host_time: float = field(default_factory=time.time) + level: str | None = None # E/W/I/D/V for LOG lines + uptime_ms: int | None = None + tag: str | None = None + message: str | None = None + reset_reason: str | None = None + error_id: str | None = None # "E-SD-03" when the message carries one + record: str | None = None # KOTA lines: "boot", "i2c", "health", ... + fields: dict[str, str] = field(default_factory=dict) # KOTA key=value pairs + words: tuple[str, ...] = () # KOTA bare words ("verify", "OK", "->", ...) + + @property + def match_text(self) -> str: + """Text for message rules: the error-ID prefix removed, so rules + written for older firmware keep matching.""" + if self.error_id and self.message is not None and self.tag: + return self.text.replace(f"[{self.error_id}] ", "", 1) + return self.text + + +def parse_kota(body: str) -> tuple[str, dict[str, str], tuple[str, ...]]: + """'rtc verify running=1 delta=2s ok=1' -> ('rtc', {...}, ('verify',)). + + Values never contain spaces. The mic line has a parenthesised aside + ("(GPIO3=1, expected 1)"): brackets and commas are stripped from keys + and values, and the aside's words are kept as bare words. + """ + tokens = body.split() + if not tokens: + return "", {}, () + fields: dict[str, str] = {} + words: list[str] = [] + for tok in tokens[1:]: + key, eq, value = tok.partition("=") + if eq and key.strip("(,"): + key = key.lstrip("(") + # "0x68=DS1307(missing)" keeps its bracket; "(GPIO3=1," loses it. + value = value.rstrip(",") + if tok.startswith("(") or value.endswith(")") and "(" not in value: + value = value.rstrip(")") + fields[key] = value + else: + words.append(tok.strip("(),")) + return tokens[0], fields, tuple(w for w in words if w) + + +def clean(raw: str) -> str: + return ANSI_RE.sub("", raw).replace("\r", "").rstrip() + + +def parse_line(raw: str, host_time: float | None = None) -> LogLine: + text = redact(clean(raw)) + t = time.time() if host_time is None else host_time + if m := ESP_LOG_RE.match(text): + level, ms, tag, msg = m.groups() + tag = tag.strip() + if tag == KOTA_TAG: + record, fields, words = parse_kota(msg) + return LogLine(text, LineKind.KOTA, t, level, int(ms), tag, msg, + record=record, fields=fields, words=words) + eid = None + if em := ERROR_ID_RE.match(msg): + eid, msg = em.group(1), msg[em.end():] + return LogLine(text, LineKind.LOG, t, level, int(ms), tag, msg, error_id=eid) + if BOOT_BANNER_RE.match(text): + return LogLine(text, LineKind.BOOT, t) + if m := RESET_REASON_RE.match(text): + return LogLine(text, LineKind.RESET, t, reset_reason=m.group(2)) + if PANIC_RE.search(text): + return LogLine(text, LineKind.PANIC, t) + return LogLine(text, LineKind.RAW, t) + + +class LineFramer: + """Accumulates bytes and yields complete lines. + + A partial line is released after `idle_flush_s` without new bytes so + prompts / unterminated output are not held back forever. + + Lines are also split where a new log line starts in the middle + ("I (2302) chip[DL]: I (2302) chip[DL]: Posting..."): the two S3 cores + interleave output, and the USB-Serial-JTAG FIFO keeps the tail of an old + line while no host reads, releasing it glued in front of the next live + output. After a (re)open, the fragment in front of the first such split + is stale and dropped. Timing cannot identify it, because the FIFO only + flushes when the board next prints, possibly seconds later. + """ + + def __init__(self, idle_flush_s: float = 0.3, max_line: int = 4096, + drop_first_partial: bool = False): + self._buf = bytearray() + self._last_rx = 0.0 + self.idle_flush_s = idle_flush_s + self.max_line = max_line + self._first_line_pending = drop_first_partial + + def cancel_drop(self) -> None: + self._first_line_pending = False + + def feed(self, data: bytes, now: float | None = None) -> list[str]: + now = time.monotonic() if now is None else now + if data: + self._buf.extend(data) + self._last_rx = now + out: list[str] = [] + while (i := self._buf.find(b"\n")) >= 0: + out += self._emit(self._buf[:i]) + del self._buf[: i + 1] + if len(self._buf) >= self.max_line: + out += self._emit(self._buf) + self._buf.clear() + return out + + def poll_idle(self, now: float | None = None) -> list[str]: + now = time.monotonic() if now is None else now + if self._buf and now - self._last_rx >= self.idle_flush_s: + return self.flush() + return [] + + def flush(self) -> list[str]: + if not self._buf: + return [] + out = self._emit(self._buf) + self._buf.clear() + return out + + def _emit(self, raw: bytes | bytearray) -> list[str]: + parts = split_glued(bytes(raw).decode("utf-8", errors="replace")) + if self._first_line_pending: + self._first_line_pending = False + if len(parts) > 1: + parts = parts[1:] # stale FIFO fragment from before the open + return parts + + +# Start of a log line (optionally ANSI-coloured) or ROM banner. +_LINE_START = re.compile(r"(?:\x1b\[[0-9;]*m)?(?:[EWIDV] \(\d+\) \S|ESP-ROM:)") + + +def split_glued(text: str) -> list[str]: + # Left-to-right scan so an ANSI colour prefix stays with its line. + cuts = [m.start() for m in _LINE_START.finditer(text) if m.start() > 0] + if not cuts: + return [text] + bounds = [0, *cuts, len(text)] + return [text[a:b] for a, b in zip(bounds, bounds[1:]) if a < b] diff --git a/kota_tech/diag/engine.py b/kota_tech/diag/engine.py index 8c77f31..3805f34 100644 --- a/kota_tech/diag/engine.py +++ b/kota_tech/diag/engine.py @@ -1,999 +1,999 @@ -"""Diagnostics engine: parsed log lines -> findings, milestones, LED, verdict. - -Line-level knowledge lives in rules.toml. This module adds what a single -line cannot express: - * boot tracking (ROM banner, reset reason, uptime drops when the banner - was missed) and classification of why the previous boot ended; - * the status-LED model (Status::set_status / set_status_conditional); - * crash capture (panic / ESP_ERROR_CHECK output); - * root-cause grouping via `explained_by` chains; - * the structured "KOTA:" lines of firmware 1.0.16+ (diag/kota.py): exact - status/LED, restart reasons, I2C/SD/RTC/Matter/mic self-report and the - 60 s health line, plus error IDs ("[E-SD-03]") looked up in - error_ids.csv. Older firmware is still diagnosed from message text. - -Findings are session-wide (a reboot loop must keep its cause visible); -milestones and the LED are per boot. -""" - -from __future__ import annotations - -import re -import tomllib -from collections import Counter, deque -from dataclasses import dataclass, field -from enum import IntEnum -from pathlib import Path - -from ..core.logline import LineKind, LogLine -from . import kota -from .firmware import FirmwareInfo - -RULES_PATH = Path(__file__).with_name("rules.toml") -_MAC_LINE = re.compile(r"Device MAC address: ([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})") - - -class Severity(IntEnum): - IGNORE = -1 - INFO = 1 - WARNING = 2 - ERROR = 3 - CRITICAL = 4 - - @property - def label(self) -> str: - return self.name.capitalize() - - -# name -> (label, swatch colour) -LED = { - "UNKNOWN": ("Unknown", "#888888"), - "OFF": ("Off", "#444444"), - "GREEN": ("Green", "#2e9e44"), - "PURPLE": ("Purple", "#8e44ad"), - "RED": ("Red", "#d64541"), - "BLUE": ("Blue", "#2f6fd6"), - "CYAN": ("Cyan", "#1fc8d8"), - "YELLOW": ("Yellow", "#e6c229"), - "ORANGE": ("Orange", "#f08c1a"), - "WHITE": ("White", "#f4f4f4"), -} - -# (id, label, optional) - optional milestones are listed only once reached. -MILESTONES = [ - ("rom_boot", "Board booted", False), - ("app_started", "Firmware started", False), - ("sd_mounted", "SD card mounted", False), - ("env_loaded", "ENV.TXT read (InfluxDB settings)", False), - ("matter_started", "Matter started", True), - ("matter_commissioned", "Paired in Matter", True), - ("local_wifi_mode", "Local Wi-Fi mode (SSID from SD card)", True), - ("i2c_ok", "All sensors answer on the I2C bus", True), - ("sensors_ready", "All sensors started", False), - ("wifi_connected", "Connected to Wi-Fi", False), - ("time_synced", "Clock set from the Internet", False), - ("clock_verified", "Clock keeps time (RTC verified)", True), - ("data_uploaded", "Data uploaded to InfluxDB", False), - ("ota_registered", "Registered for firmware updates", False), - ("ota_checked", "Checked for firmware updates", False), - ("ota_update_started", "Firmware update started", True), - ("ota_update_done", "Firmware update installed", True), - ("ota_update_accepted", "Updated firmware accepted", True), -] - -# Findings that stop start-up; a crash/reboot right after them is their effect. -_INIT_FAILURES = [ - "rtc.init_failed", "sensor.init_failed", "sd.card_fail", "sd.mount_fail", - "sd.env_open_fail", "influx.env_missing", "influx.init_fail", - "kota.i2c_stuck", "kota.sensor_missing", "kota.rtc_read_failed", "kota.sd_unmounted", -] -_NETWORK_ROOTS = [ - "influx.buffer_fail", "ota.check_failed", "net.dns_fail", - "matter.not_commissioned", "wifi.disconnected", -] -# Printed by the brown-out handler without a timestamp ("E BOD: ..."); on the -# ESP32-S3 the ROM then reports RTC_SW_SYS_RST, not a brown-out reset. -_BROWNOUT = "Brownout detector was triggered" -_RESET_BY_TOOL_WINDOW_S = 5.0 -_SHORT_BOOT_MS = 60_000 - - -class _Fmt(dict): - def __missing__(self, key: str) -> str: - return "?" - - -def _fmt(s: str, groups: dict[str, str]) -> str: - return s.format_map(_Fmt(groups)) if s else s - - -@dataclass(frozen=True) -class Rule: - id: str - pattern: re.Pattern[str] | None # None: raised from KOTA: fields - severity: Severity - title: str = "" - meaning: str = "" - actions: tuple[str, ...] = () - explained_by: tuple[str, ...] = () - milestone: str | None = None - clears: tuple[str, ...] = () - led: str | None = None - led_from: str | None = None - split_by: str | None = None - min_count: int = 1 - min_uptime_ms: int = 0 - - -def load_rules(path: Path = RULES_PATH) -> list[Rule]: - data = tomllib.loads(path.read_text(encoding="utf-8")) - rules = [] - for r in data["rule"]: - rules.append(Rule( - id=r["id"], - pattern=re.compile(r["pattern"]) if "pattern" in r else None, - severity=Severity[r["severity"].upper()], - title=r.get("title", ""), - meaning=r.get("meaning", ""), - actions=tuple(r.get("actions", ())), - explained_by=tuple(r.get("explained_by", ())), - milestone=r.get("milestone"), - clears=tuple(r.get("clears", ())), - led=r.get("led"), - led_from=r.get("led_from"), - split_by=r.get("split_by"), - min_count=int(r.get("min_count", 1)), - min_uptime_ms=int(r.get("min_uptime_ms", 0)), - )) - _validate(rules) - return rules - - -def _validate(rules: list[Rule]) -> None: - ids = [r.id for r in rules] - dupes = [i for i, n in Counter(ids).items() if n > 1] - if dupes: - raise ValueError(f"duplicate rule ids: {dupes}") - known = set(ids) - for r in rules: - for ref in (*r.explained_by, *r.clears): - # status.* / reboot.* findings are created by the engine itself. - if ref not in known and not ref.startswith(("status.", "reboot.")): - raise ValueError(f"{r.id}: unknown rule id {ref!r}") - for colour in (r.led, r.led_from): - if colour and colour not in LED: - raise ValueError(f"{r.id}: unknown LED colour {colour!r}") - if r.severity != Severity.IGNORE and not r.title: - raise ValueError(f"{r.id}: visible rule needs a title") - - -@dataclass -class Finding: - key: str # rule id, or "rule id:split value" - rule_id: str - severity: Severity - title: str - meaning: str - actions: tuple[str, ...] - explained_by: tuple[str, ...] = () - min_count: int = 1 - count: int = 0 - first_uptime_ms: int | None = None - last_uptime_ms: int | None = None - last_seen: float = 0.0 - boot: int = 0 - resolved: bool = False - resolved_by: str | None = None - evidence: deque[str] = field(default_factory=lambda: deque(maxlen=3)) - error_ids: dict[str, str] = field(default_factory=dict) # "E-SD-03" -> technician action - - @property - def active(self) -> bool: - return self.count >= self.min_count and not self.resolved - - def seen_text(self) -> str: - """'Seen 3x, first at 2.9 s after start, last at 61.2 s' (panel and service note).""" - s = f"Seen {self.count}x" - if self.first_uptime_ms is not None: - first = f"{self.first_uptime_ms / 1000:.1f}" - s += f", first at {first} s after start" - last = f"{self.last_uptime_ms / 1000:.1f}" if self.last_uptime_ms is not None else first - if self.count > 1 and last != first: - s += f", last at {last} s" - if self.resolved and self.resolved_by: - s += f" - resolved ({self.resolved_by})" - return s - - -@dataclass -class BootRecord: - index: int - host_time: float - explicit: bool # ROM banner seen (vs. inferred) - reset_reason: str | None = None - tool_reset: bool = False - last_uptime_ms: int = 0 - saw_log: bool = False - led_at_end: str | None = None - panic_lines: list[str] = field(default_factory=list) - brownout: bool = False # "E BOD: Brownout detector was triggered" before the restart - recent_errors: deque[str] = field(default_factory=lambda: deque(maxlen=5)) - intentional_restart: bool = False # factory reset / setup-mode toggle - # KOTA: lines (firmware 1.0.16+) - kota: dict[str, LogLine] = field(default_factory=dict) # last line per record - last_restart: str | None = None # "boot ... last_restart=" (why the previous boot ended) - restart_reason: str | None = None # "restart reason=" printed just before this boot ended - previous_status: tuple[str, int] | None = None # "led previous_boot status= code=" - mode: str | None = None # matter | wifi | provisioning - - -@dataclass -class RootCause: - finding: Finding - consequences: list[Finding] - - @property - def severity(self) -> Severity: - return max([self.finding.severity, *(c.severity for c in self.consequences)]) - - -@dataclass -class Report: - verdict: str - verdict_severity: Severity | None # None = healthy / nothing found - led: str - led_reason: str - firmware: FirmwareInfo - milestones: list[tuple[str, str, int | None]] # (id, label, uptime_ms) - roots: list[RootCause] - earlier: list[Finding] # seen only in previous boots - resolved: list[Finding] - boots: list[BootRecord] - lines: int - mid_run: bool # no boot seen yet - technician: list[str] = field(default_factory=list) # guided-check answers - led_pattern: str = "" # "2 pulses", "solid", ... (exact when from a KOTA led line) - led_meaning: str = "" - led_exact: bool = False # reported by the box itself - box: list[tuple[str, str, str]] = field(default_factory=list) # (topic, text, ok|warn|bad|info) - matter_qr: str | None = None - matter_manual: str | None = None - - def led_text(self) -> str: - t = LED[self.led][0] + (f", {self.led_pattern}" if self.led_pattern else "") - return t + (f" = {self.led_meaning}" if self.led_meaning else "") - - def to_text(self) -> str: - out = [f"VERDICT: {self.verdict}", - f"Expected status LED: {self.led_text()}" - + (f" (last change: {self.led_reason})" if self.led_reason else ""), - f"Firmware: {self.firmware.describe()}", - f"Boots seen: {max(len(self.boots) - (0 if self.boots and self.boots[0].explicit else 1), 0)}"] - if self.box: - out += ["", "Box self-report (KOTA lines):"] - out += [f" {topic}: {text}" for topic, text, _ in self.box] - out += ["", "Milestones (current boot):"] - for _, label, t in self.milestones: - out.append(f" [{'x' if t is not None else ' '}] {label}" - + (f" @ {t / 1000:.1f} s" if t is not None else "")) - out.append("") - out.append("Findings:" if self.roots else "Findings: none") - for rc in self.roots: - out += _finding_text(rc.finding, " ") - for c in rc.consequences: - out.append(" consequence:") - out += _finding_text(c, " ") - if self.earlier: - out.append("") - out.append("Seen in earlier boots only:") - for f in self.earlier: - out.append(f" - [{f.severity.label}] {f.title} (x{f.count})") - if self.resolved: - out.append("") - out.append("Resolved during this session:") - for f in self.resolved: - out.append(f" - {f.title}" + (f" (by: {f.resolved_by})" if f.resolved_by else "")) - if self.technician: - out.append("") - out.append("Technician checks:") - out += [f" - {t}" for t in self.technician] - return "\n".join(out) - - -def _finding_text(f: Finding, ind: str) -> list[str]: - lines = [f"{ind}- [{f.severity.label}] {f.title} (x{f.count})", f"{ind} {f.meaning}"] - lines += [f"{ind} {i}. {a}" for i, a in enumerate(f.actions, 1)] - for eid, action in f.error_ids.items(): - lines.append(f"{ind} error {eid}" + (f": {action}" if action else "")) - if f.evidence: - lines.append(f"{ind} log: {f.evidence[-1]}") - return lines - - -class DiagnosticsEngine: - def __init__(self, rules: list[Rule] | None = None): - self.rules = rules if rules is not None else load_rules() - self._rule_by_id = {r.id: r for r in self.rules} - self._pattern_rules = [r for r in self.rules if r.pattern is not None] - self.error_ids = kota.load_error_ids() - self.reset() - - # ---------------------------------------------------------------- API - def reset(self) -> None: - self.findings: dict[str, Finding] = {} - self.boots: list[BootRecord] = [BootRecord(0, 0.0, explicit=False)] - self.milestones: dict[str, int] = {} - self.led = "UNKNOWN" - self.led_reason = "" - self.firmware = FirmwareInfo() - self.lines = 0 - self._tool_reset_at: float | None = None - self._panic_tail = 0 # RAW lines after a panic line to keep - self.technician: list[str] = [] - self.mac: str | None = None # from "ID: Device MAC address: ..." at boot - self.matter_advert: str | None = None # "Advertise commission parameter vendorID=..." line - self.matter_qr: str | None = None # "KOTA: matter ... qr=MT:..." - self.matter_manual: str | None = None - self.kota_seen = False # firmware prints KOTA: lines - self._reset_led_state() - - def _reset_led_state(self) -> None: - self.led_pattern = "" - self.led_exact = False # a KOTA led line was seen this boot - self.status: tuple[str, int] | None = None - - @property - def boot(self) -> BootRecord: - return self.boots[-1] - - def feed(self, line: LogLine) -> None: - self.lines += 1 - text = line.text - if line.kind == LineKind.HOST: - if "reset issued by tool" in text: - self._tool_reset_at = line.host_time - return - if _BROWNOUT in text: - self.boot.brownout = True - if line.kind == LineKind.BOOT: - self._new_boot(line.host_time, explicit=True) - self.milestones["rom_boot"] = 0 - return - if line.kind == LineKind.RESET: - self.boot.reset_reason = line.reset_reason - if line.reset_reason and "POWERON" in line.reset_reason: - self.led, self.led_reason = "OFF", "power-on - firmware not started yet" - return - if line.kind in (LineKind.LOG, LineKind.KOTA) and line.uptime_ms is not None: - b = self.boot - # Uptime jumped back to a fresh-boot value: the ROM banner was - # missed (port reconnect, capture started late). - if b.saw_log and line.uptime_ms + 3000 < b.last_uptime_ms and line.uptime_ms < 10_000: - self._new_boot(line.host_time, explicit=False) - b = self.boot - b.saw_log = True - b.last_uptime_ms = max(b.last_uptime_ms, line.uptime_ms) - - if line.kind == LineKind.PANIC: - self._on_panic(line) - elif self._panic_tail and line.kind == LineKind.RAW: - self.boot.panic_lines.append(text) - self._panic_tail -= 1 - self._crash_finding().evidence.append(text) - - if line.kind == LineKind.KOTA: - self._on_kota(line) - return - - self.firmware.observe(text) - if m := _MAC_LINE.search(text): - self.mac = m.group(1).upper() - if "Advertise commission parameter" in text: - self.matter_advert = text - matched = False - hit: list[Finding] = [] - rule_text = line.match_text - for rule in self._pattern_rules: - m = rule.pattern.search(rule_text) - if not m: - continue - matched = True - if rule.min_uptime_ms and (line.uptime_ms or 0) < rule.min_uptime_ms: - continue - groups = {k: v for k, v in m.groupdict().items() if v is not None} - if rule.milestone and rule.milestone not in self.milestones: - self.milestones[rule.milestone] = line.uptime_ms or 0 - # The box's own "KOTA: led" lines are exact; message rules only guess. - if rule.led and not self.led_exact: - self._set_led(rule.led, rule.led_from, text) - for cid in rule.clears: - for f in self.findings.values(): - if f.rule_id == cid and not f.resolved: - f.resolved = True - f.resolved_by = rule.milestone or rule.id - if rule.id in ("sys.factory_reset",) or "Short press:" in text: - self.boot.intentional_restart = True - if rule.severity != Severity.IGNORE: - hit.append(self._hit(rule, groups, line)) - - self._stateful(line) - if line.error_id: - info = self.error_ids.get(line.error_id) - for f in hit: - f.error_ids[line.error_id] = info.action if info else "" - if not matched: - self._hit_error_id(line, info) - if line.level == "E": - self.boot.recent_errors.append(text) - if not matched and line.tag and not line.error_id: - self._hit_other(line) - - def report(self) -> Report: - all_f = list(self.findings.values()) + self._reboot_findings() - cur = self.boot.index - active = [f for f in all_f if f.active] - current = [f for f in active if f.boot == cur] - earlier = [f for f in active if f not in current] - resolved = [f for f in all_f if f.resolved and f.count >= f.min_count] - roots = _group(current) - # Worst consequence first, then the root's own severity, then order seen. - roots.sort(key=lambda rc: (-rc.severity, -rc.finding.severity, rc.finding.first_uptime_ms or 0)) - - mid_run = (not any(b.explicit or "i2c" in b.kota or "boot" in b.kota for b in self.boots) - and self.boot.index == 0) - if self.lines == 0: - verdict, vsev = "Waiting for data from the sensorbox...", None - elif roots and roots[0].severity >= Severity.WARNING: - vsev = roots[0].severity - prefix = "Problem" if vsev >= Severity.ERROR else "Attention" - verdict = f"{prefix}: {roots[0].finding.title}" - elif "data_uploaded" in self.milestones: - verdict, vsev = "Healthy: data is reaching InfluxDB", None - else: - verdict, vsev = "No problems found so far", None - if mid_run and self.lines: - verdict += " (connected mid-run - press Reset & capture for a full check)" - - ms = [(mid, label, self.milestones.get(mid)) for mid, label, optional in MILESTONES - if not optional or mid in self.milestones] - # Reached milestones in the order they happened, then the pending ones. - ms.sort(key=lambda m: (m[2] is None, m[2] or 0)) - pattern = self.led_pattern - return Report(verdict, vsev, self.led, self.led_reason, self.firmware, ms, roots, - earlier, resolved, list(self.boots), self.lines, mid_run, list(self.technician), - led_pattern=pattern, led_meaning=kota.led_meaning(self.led, pattern) if pattern else "", - led_exact=self.led_exact, box=self._box_summary(), - matter_qr=self.matter_qr, matter_manual=self.matter_manual) - - def add_confirmed(self, key: str, severity: Severity, title: str, meaning: str, - actions: tuple[str, ...]) -> Finding: - """A finding established by a guided check (technician answer + log).""" - f = self.findings.get(key) - if f is None: - f = self.findings[key] = Finding(key, key, severity, title, meaning, tuple(actions)) - f.count += 1 - f.boot = self.boot.index - f.resolved = False - f.evidence.append("confirmed by the technician during a guided check") - return f - - def note_technician(self, text: str) -> None: - self.technician.append(text) - - # ------------------------------------------------------------ internals - def _new_boot(self, host_time: float, explicit: bool) -> None: - prev = self.boot - # An inferred boot right after a banner-started boot is the same boot. - if not explicit and prev.explicit and not prev.saw_log: - return - prev.led_at_end = self.led - b = BootRecord(prev.index + 1, host_time, explicit) - if self._tool_reset_at is not None and host_time - self._tool_reset_at <= _RESET_BY_TOOL_WINDOW_S: - b.tool_reset = True - self._tool_reset_at = None - self.boots.append(b) - self.milestones = {} - # The WS2812 latches its colour through a software reset until - # Status::init() rewrites it; only a power-on turns it dark. - self.led_reason = "restarting - LED keeps its last colour until the firmware starts" - self.firmware = FirmwareInfo() - self._panic_tail = 0 - self._reset_led_state() - - def _set_led(self, colour: str, led_from: str | None, text: str) -> None: - if led_from and self.led != led_from: - return - self.led = colour - self.led_reason = text - - def _hit(self, rule: Rule, groups: dict[str, str], line: LogLine) -> None: - key = rule.id - if rule.split_by and rule.split_by in groups: - key = f"{rule.id}:{groups[rule.split_by]}" - f = self.findings.get(key) - if f is None: - f = self.findings[key] = Finding( - key, rule.id, rule.severity, _fmt(rule.title, groups), _fmt(rule.meaning, groups), - tuple(_fmt(a, groups) for a in rule.actions), rule.explained_by, rule.min_count) - self._touch(f, line) - return f - - def _touch(self, f: Finding, line: LogLine) -> None: - f.count += 1 - # Uptime restarts every boot, so "first seen" is per current boot. - if f.first_uptime_ms is None or f.boot != self.boot.index: - f.first_uptime_ms = line.uptime_ms - f.last_uptime_ms = line.uptime_ms - f.last_seen = line.host_time - f.boot = self.boot.index - f.evidence.append(line.text) - if f.resolved: - f.resolved, f.resolved_by = False, None # problem came back - - def _custom(self, key: str, severity: Severity, title: str, meaning: str, - actions: tuple[str, ...], explained_by: tuple[str, ...] = ()) -> Finding: - f = self.findings.get(key) - if f is None: - f = self.findings[key] = Finding(key, key, severity, title, meaning, actions, explained_by) - return f - - def _hit_other(self, line: LogLine) -> None: - f = self._custom( - f"other.{line.tag}", Severity.INFO, f"Unrecognised error messages from {line.tag}", - "These errors are not in the rule set yet; they may be harmless.", - ("If the box misbehaves, save the session log and send it to the firmware team.",)) - self._touch(f, line) - - def _crash_finding(self) -> Finding: - return self._custom( - "sys.crash", Severity.CRITICAL, "Firmware stopped with an error and restarts", - "The firmware hit a fatal error. The error printed just before it is the real cause.", - ("See the grouped cause; otherwise save the session log for the firmware team.",), - tuple(_INIT_FAILURES) + ("reboot.brownout",)) - - def _on_panic(self, line: LogLine) -> None: - b = self.boot - if len(b.panic_lines) < 8: - b.panic_lines.append(line.text) - self._panic_tail = 4 # file/func/expression lines follow ESP_ERROR_CHECK - f = self._crash_finding() - if f.count == 0 and b.recent_errors: - f.meaning += f" Last error before it: {b.recent_errors[-1]}" - self._touch(f, line) - - def _stateful(self, line: LogLine) -> None: - # RTC set by SNTP but reads "uninitialized" again later: the chip - # does not keep time (crystal/module fault), not just a flat cell. - synced = self.milestones.get("time_synced") - if (synced is not None and line.uptime_ms is not None - and line.uptime_ms > synced + 10_000 - and "RTC is uninitialized" in line.text): - f = self._custom( - "rtc.lost_after_sync", Severity.ERROR, - "Clock stops again after being set from the Internet", - "The clock was set from the Internet but stopped running shortly after. The RTC module or its crystal is faulty.", - ("Replace the RTC module (inside the case).",)) - self._touch(f, line) - - # ------------------------------------------------------- KOTA: lines - def _on_kota(self, line: LogLine) -> None: - self.kota_seen = True - rec, f = line.record or "", line.fields - # A second boot line in one boot: the ROM banner and the uptime drop - # were both missed (e.g. capture started with a gap). - if rec == "boot" and "boot" in self.boot.kota: - self._new_boot(line.host_time, explicit=False) - key = f"{rec}.verify" if rec == "rtc" and "verify" in line.words else rec - self.boot.kota[key] = line - handler = getattr(self, f"_kota_{rec}", None) - if handler is not None: - handler(line, f) - - def _raise(self, rule_id: str, line: LogLine, groups: dict[str, str] | None = None) -> Finding: - """Raise a rules.toml finding from parsed KOTA fields (no pattern matching).""" - return self._hit(self._rule_by_id[rule_id], groups or {}, line) - - def _resolve(self, rule_id: str, by: str, key: str | None = None) -> None: - for f in self.findings.values(): - if f.rule_id == rule_id and (key is None or f.key == key) and not f.resolved: - f.resolved, f.resolved_by = True, by - - def _milestone(self, mid: str, line: LogLine) -> None: - if mid not in self.milestones: - self.milestones[mid] = line.uptime_ms or 0 - - def _kota_boot(self, line: LogLine, f: dict[str, str]) -> None: - b = self.boot - self.firmware.observe_kota(f) - mac = f.get("mac", "") - if re.fullmatch(r"[0-9A-Fa-f]{12}", mac): - self.mac = ":".join(mac[i:i + 2] for i in range(0, 12, 2)).upper() - b.reset_reason = b.reset_reason or f.get("reset") - b.last_restart = f.get("last_restart") - b.mode = f.get("mode") - self._milestone("app_started", line) - - def _kota_restart(self, line: LogLine, f: dict[str, str]) -> None: - reason = f.get("reason", "") - self.boot.restart_reason = reason - if reason in kota.INTENTIONAL_RESTARTS: - self.boot.intentional_restart = True - - def _kota_i2c(self, line: LogLine, f: dict[str, str]) -> None: - if f.get("bus") == "stuck": - self._raise("kota.i2c_stuck", line, {"sda": f.get("SDA", "?"), "scl": f.get("SCL", "?")}) - return - self._resolve("kota.i2c_stuck", "I2C bus free again") - missing = False - for addr, value in f.items(): - if not addr.startswith("0x") or value == "?": - continue - name = value.removesuffix("(missing)") - if value.endswith("(missing)"): - missing = True - self._raise("kota.sensor_missing", line, {"sensor": name, "addr": addr}) - else: - self._resolve("kota.sensor_missing", f"{name} found on I2C", f"kota.sensor_missing:{name}") - if not missing: - self._milestone("i2c_ok", line) - - def _kota_rtc(self, line: LogLine, f: dict[str, str]) -> None: - if "verify" in line.words: - if f.get("ok") == "1": - self._milestone("clock_verified", line) - self._resolve("kota.rtc_not_ticking", "clock verified") - else: - self._raise("kota.rtc_not_ticking", line, - {"running": f.get("running", "?"), "delta": f.get("delta", "no reading")}) - elif "read_failed" in line.words: - self._raise("kota.rtc_read_failed", line, {"err": f.get("err", "?")}) - elif f.get("running") == "0": - self._raise("kota.rtc_stopped", line, {"time": f.get("time", "?")}) - elif f.get("running") == "1": - self._resolve("kota.rtc_read_failed", "clock answers") - self._resolve("kota.rtc_stopped", "clock running") - - def _kota_sd(self, line: LogLine, f: dict[str, str]) -> None: - if f.get("mounted") == "0": - self._raise("kota.sd_unmounted", line, {"err": f.get("err", "card missing or unreadable")}) - return - self._milestone("sd_mounted", line) - self._resolve("kota.sd_unmounted", "SD card mounted") - keys = {k for k in f.get("env_keys", "").split(",") if k and k != "-"} - missing = [k for k in ("INFLUX_URL", "INFLUX_TOKEN") if k not in keys] - for k in missing: - self._raise("influx.env_missing", line, {"key": k}) - if not missing: - self._milestone("env_loaded", line) - if f.get("fs") and f["fs"] != "FAT32": - self._raise("kota.sd_not_fat32", line, {"fs": f["fs"]}) - free = kota.parse_size_gb(f.get("free", "")) - if free is not None and free < 0.1: - self._raise("kota.sd_full", line, {"free": f["free"]}) - lines = f.get("buffer_lines", "0") - if lines.isdigit() and int(lines) > 500: - self._raise("kota.buffer_backlog", line, {"lines": lines}) - - def _kota_matter(self, line: LogLine, f: dict[str, str]) -> None: - if f.get("qr", "-").startswith("MT:"): - self.matter_qr = f["qr"] - if f.get("manual", "-") != "-": - self.matter_manual = f["manual"] - fabrics = f.get("fabrics", "") - if fabrics == "0": - self._raise("matter.not_commissioned", line) - elif fabrics.isdigit(): - self._milestone("matter_commissioned", line) - self._resolve("matter.not_commissioned", "paired in Matter") - - def _kota_mic(self, line: LogLine, f: dict[str, str]) -> None: - if f.get("enabled") == "0": - self._raise("mic.disabled", line) - elif f.get("enabled") == "1": - self._resolve("mic.disabled", "microphone switched on") - - def _kota_status(self, line: LogLine, f: dict[str, str]) -> None: - w = line.words - new = w[w.index("->") + 1] if "->" in w and w.index("->") + 1 < len(w) else (w[-1] if w else "") - code = int(f["code"]) if f.get("code", "").isdigit() else 0 - self._apply_status(new, code, line, f.get("reason", "")) - - def _apply_status(self, status: str, code: int, line: LogLine, reason: str) -> None: - self.status = (status, code) - if not self.led_exact: - self.led, self.led_pattern = kota.led_for_status(status, code) - self.led_reason = f"status {status}" + (f" code={code}" if code else "") + ( - f", reason: {reason}" if reason else "") - sf = kota.status_finding(status, code) - key = sf[0] if sf else None - for fd in self.findings.values(): - if fd.rule_id.startswith("status.") and fd.key != key and not fd.resolved: - fd.resolved, fd.resolved_by = True, f"status -> {status}" - if sf: - key, sev, title, meaning, actions, expl = sf - fd = self._custom(key, Severity[sev.upper()], title, meaning, actions, expl) - self._touch(fd, line) - - def _kota_led(self, line: LogLine, f: dict[str, str]) -> None: - if "previous_boot" in line.words: - code = f.get("code", "0") - self.boot.previous_status = (f.get("status", "?"), int(code) if code.isdigit() else 0) - return - if "restart_flash" in line.words: - return - colour = kota.LED_COLOURS.get(f.get("color", "")) - if colour is None: - return - pattern = kota.pattern_text(f) - self.led, self.led_pattern, self.led_exact = ("OFF", "", True) if pattern == "off" else (colour, pattern, True) - if not self.led_reason or self.status is None: - self.led_reason = "reported by the box" - - def _kota_health(self, line: LogLine, f: dict[str, str]) -> None: - up = kota.parse_seconds(f.get("up", "")) or 0 - status = f.get("status", "") - # Connected mid-run: the health line is the first exact status we get. - if status and (self.status is None or self.status[0] != status): - self._apply_status(status, 0, line, "from the health report") - mode = self.boot.mode - wifi = f.get("wifi") - rssi = int(f["rssi"]) if f.get("rssi", "").lstrip("-").isdigit() else None - if wifi == "connected": - self._milestone("wifi_connected", line) - self._resolve("kota.wifi_down", "Wi-Fi connected") - if rssi is not None and rssi < -75: - self._raise("kota.weak_wifi", line, {"rssi": str(rssi)}) - elif rssi is not None: - self._resolve("kota.weak_wifi", "signal OK") - elif wifi == "disconnected" and mode != "provisioning": - self._raise("kota.wifi_down", line) - if f.get("time_valid") == "1": - self._milestone("time_synced", line) - self._resolve("kota.time_invalid", "time valid") - elif f.get("time_valid") == "0" and up >= 120: - self._raise("kota.time_invalid", line) - if f.get("sd") == "missing": - self._raise("kota.sd_unmounted", line, {"err": "reported missing by the health check"}) - last = f.get("last_upload", "never") - if last != "never": - self._milestone("data_uploaded", line) - self._resolve("kota.no_upload", "data uploaded") - elif wifi == "connected" and up >= 300: - self._raise("kota.no_upload", line) - heap = f.get("heap", "") - if heap.isdigit() and int(heap) < 20_000: - self._raise("kota.low_heap", line, {"heap": heap}) - ota = f.get("ota") - if ota == "registered": - self._milestone("ota_registered", line) - self._resolve("kota.ota_unregistered", "registered") - elif ota == "unregistered" and wifi == "connected" and mode != "provisioning": - self._raise("kota.ota_unregistered", line) - - def _hit_error_id(self, line: LogLine, info: kota.ErrorId | None) -> None: - eid = line.error_id or "?" - msg = (line.message or "").strip() - msg = msg if len(msg) <= 90 else msg[:87] + "..." - if info: - sev = Severity.WARNING if info.severity == "warning" else Severity.ERROR - f = self._custom(f"eid.{eid}", sev, f"{info.module_name}: {msg}", - f"The firmware reported error {eid} ({info.module_name}).", - (info.action, "If it repeats, save the session log for the firmware team.")) - f.rule_id = f"eid.{info.module}" - f.error_ids[eid] = info.action - else: - f = self._custom(f"eid.{eid}", Severity.WARNING, f"Error {eid}: {msg}", - "This error ID is not in the tool's error table (newer firmware?).", - ("Save the session log and send it to the firmware team.",)) - self._touch(f, line) - - def _box_summary(self) -> list[tuple[str, str, str]]: - """What the box reported about itself in this boot (KOTA lines), for the panel.""" - k = self.boot.kota - out: list[tuple[str, str, str]] = [] - if ln := k.get("boot"): - f = ln.fields - lr = f.get("last_restart", "none") - bad = kota.restart_finding(lr) is not None - out.append(("Boot", f"v{f.get('fw', '?')} build {f.get('build', '?')}, mode {f.get('mode', '?')}, " - f"reset {f.get('reset', '?')}, last restart: {lr}", "warn" if bad else "ok")) - if ps := self.boot.previous_status: - if ps[0] != "OK": - out.append(("Before restart", f"status {ps[0]}" + (f" code={ps[1]}" if ps[1] else ""), "warn")) - if ln := k.get("i2c"): - f = ln.fields - if f.get("bus") == "stuck": - out.append(("I2C", f"bus stuck (SDA={f.get('SDA')}, SCL={f.get('SCL')})", "bad")) - else: - devs = [(v.removesuffix("(missing)"), v.endswith("(missing)")) - for a, v in f.items() if a.startswith("0x") and v != "?"] - miss = [n for n, m in devs if m] - text = ("missing: " + ", ".join(miss) + "; " if miss else "") + "found: " + ( - ", ".join(n for n, m in devs if not m) or "none") - out.append(("I2C", text, "bad" if miss else "ok")) - if ln := k.get("sd"): - f = ln.fields - if f.get("mounted") == "0": - out.append(("SD card", "not mounted" + (f" ({f['err']})" if f.get("err") else ""), "bad")) - else: - keys = f.get("env_keys", "-") - ok = "INFLUX_URL" in keys and "INFLUX_TOKEN" in keys - out.append(("SD card", f"{f.get('type', '?')} {f.get('size', '?')} {f.get('fs', '?')}, " - f"{f.get('free', '?')} free, ENV keys: {keys}, buffered: {f.get('buffer_lines', '?')}", - "ok" if ok else "bad")) - rtc, verify = k.get("rtc"), k.get("rtc.verify") - if rtc or verify: - parts, state = [], "ok" - if rtc: - if "read_failed" in rtc.words: - parts.append(f"read failed ({rtc.fields.get('err', '?')})") - state = "bad" - else: - running = rtc.fields.get("running") == "1" - parts.append(("running" if running else "STOPPED") + f", time {rtc.fields.get('time', '?')}") - state = "ok" if running else "warn" - if verify: - ok = verify.fields.get("ok") == "1" - parts.append("verified ticking" if ok else "NOT keeping time") - state = state if ok else "bad" - out.append(("Clock (RTC)", "; ".join(parts), state)) - if ln := k.get("matter"): - f = ln.fields - fabrics = f.get("fabrics", "?") - out.append(("Matter", f"{'paired' if fabrics not in ('0', '?') else 'NOT paired'} (fabrics {fabrics}), " - f"pairing window {f.get('window', '?')}", "ok" if fabrics not in ("0", "?") else "warn")) - if ln := k.get("mic"): - on = ln.fields.get("enabled") == "1" - out.append(("Microphone", "on" if on else "switched OFF (switch on the back)", "ok" if on else "info")) - if ln := k.get("health"): - f = ln.fields - wifi = f.get("wifi", "?") - text = (f"up {f.get('up', '?')}, status {f.get('status', '?')}, Wi-Fi {wifi}" - + (f" ({f.get('rssi')} dBm)" if wifi == "connected" else "") - + f", time {'OK' if f.get('time_valid') == '1' else 'NOT set'}, SD {f.get('sd', '?')}, " - f"buffer {f.get('buffer', '?')}, heap {f.get('heap', '?')}, last upload {f.get('last_upload', '?')}" - + ("" if f.get("last_upload") == "never" else " ago") + f", OTA {f.get('ota', '?')}") - good = f.get("status") == "OK" and wifi == "connected" and f.get("last_upload") != "never" - out.append(("Health", text, "ok" if good else "warn")) - return out - - # ------------------------------------------------------------ reboots - def _reboot_findings(self) -> list[Finding]: - """Classify why each earlier boot ended (needs the next boot's reset reason).""" - out: list[Finding] = [] - short_unexpected: list[BootRecord] = [] - cur = self.boot - # The box keeps its status across a restart ("led previous_boot"): if - # it was in an error state right before the latest restart (often the - # technician's Reset & capture), that state is worth showing. - if cur.previous_status and cur.previous_status[0] not in ("OK", "UPDATING", "SELF_CHECK"): - sf = kota.status_finding(*cur.previous_status) - st, code = cur.previous_status - # Only informative once the box reports OK again in this boot. - sev = Severity.INFO if self.status and self.status[0] == "OK" else Severity.WARNING - f = Finding("boot.previous_status", "boot.previous_status", sev, - f"Before the last restart: {sf[2] if sf else st}", - "The box remembers its status across a restart. Right before the latest restart it was in " - f"state {st}" + (f" code={code}" if code else "") + ". " - + (sf[3] if sf else "") + " Check whether the problem comes back in this boot.", - sf[4] if sf else ()) - f.count, f.boot, f.last_seen = 1, cur.index, cur.host_time - f.evidence.append(f"led previous_boot status={st} code={code}") - out.append(f) - for prev, nxt in zip(self.boots, self.boots[1:]): - if not prev.saw_log and prev.index == 0: - continue # nothing captured before the first boot - reason = nxt.reset_reason or "unknown" - if nxt.tool_reset or prev.intentional_restart: - continue - # Firmware 1.0.16+ records why it restarted itself. - last = nxt.last_restart or prev.restart_reason - if last in kota.INTENTIONAL_RESTARTS: - continue - key, sev, title, meaning, actions, expl = None, Severity.INFO, "", "", (), () - rf = kota.restart_finding(last) if last else None - if prev.brownout or "BROWN" in reason or last == "brownout": - # A crash right after a brown-out is its effect, not a firmware bug. - key, sev, title = "reboot.brownout", Severity.CRITICAL, "Power supply voltage drops (brown-out)" - meaning = ("The supply voltage fell too low and the chip reset itself - typically when the " - "Wi-Fi/Bluetooth radio starts. The box cannot run like this.") - actions = ("Switch the box's power switch ON - USB from a laptop alone may not be enough.", - "Check the power supply, cable and connectors; try another supply.", - "If it persists on a good supply: the board's power circuit is faulty.") - elif rf and not (last == "panic" and prev.panic_lines): - sev_name, title, meaning, actions = rf - sev = Severity[sev_name.upper()] - key = {"status_watchdog": "reboot.status_watchdog", "panic": "reboot.crash", - "brownout": "reboot.brownout", "task_wdt": "reboot.watchdog", "int_wdt": "reboot.watchdog", - "wdt": "reboot.watchdog"}.get(last, f"reboot.{last}") - if nxt.previous_status and nxt.previous_status[0] != "OK": - st, code = nxt.previous_status - meaning += f" Before the restart the box was in state {st}" + (f" code={code}" if code else "") + "." - if last == "status_watchdog": - expl = tuple(_INIT_FAILURES) + kota._SENSOR_ROOTS + ("status.error", "sys.out_of_resources") - elif last == "status_oom_watchdog": - expl = ("sys.out_of_resources",) - elif rf: - pass # panic with captured crash output: covered by sys.crash - elif "USB_UART_CHIP_RESET" in reason: - key, title = "reboot.usb", "Box was reset over USB" - meaning = "The board was reset through its USB port (a flashing tool, a serial program, or this tool in an earlier session)." - elif "POWERON" in reason: - key, title = "reboot.power", "Box was switched off and on" - meaning = "Power was interrupted (power switch, cable or supply)." - actions = ("If nobody switched it off: check the power supply and cable.",) - elif "WDT" in reason: - key, sev, title = "reboot.watchdog", Severity.ERROR, "Firmware froze and was restarted by the watchdog" - meaning = "The firmware stopped responding; the hardware watchdog restarted it." - actions = ("Save the session log for the firmware team.",) - elif prev.panic_lines: - pass # covered by sys.crash - elif (not nxt.last_restart and prev.led_at_end in ("PURPLE", "RED") - and prev.last_uptime_ms >= 140_000): # older firmware: no restart record - key, title = "reboot.status_watchdog", "Box restarted itself after 2.5 minutes of errors" - meaning = "By design the firmware restarts when it stays in an error state (purple/red) for 150 s." - actions = ("Fix the grouped cause; the restarts stop by themselves.",) - expl = tuple(_NETWORK_ROOTS) + ("sys.out_of_resources",) - else: - key, sev, title = "reboot.unexpected", Severity.WARNING, "Unexpected restart" - meaning = f"The firmware restarted (reset reason {reason})." - actions = ("Watch whether it repeats; save the session log for the firmware team.",) - if key: - out.append(self._reboot_finding(key, sev, title, meaning, actions, expl, nxt)) - if prev.last_uptime_ms < _SHORT_BOOT_MS and "POWERON" not in reason: - short_unexpected.append(prev) - if len(short_unexpected) >= 2: - avg = sum(b.last_uptime_ms for b in short_unexpected) / len(short_unexpected) / 1000 - f = self._reboot_finding( - "reboot.loop", Severity.CRITICAL, "Reboot loop", - f"The box restarted {len(short_unexpected)} times, about every {avg:.0f} s. " - "The error printed just before each restart is the cause.", - ("Fix the grouped cause below.",), tuple(_INIT_FAILURES) + ("reboot.brownout",), self.boot) - f.count = len(short_unexpected) - out.append(f) - # One finding per cause, counting how often it happened. - merged: dict[str, Finding] = {} - for f in out: - if f.key in merged: - merged[f.key].count += f.count - merged[f.key].evidence.extend(f.evidence) - else: - merged[f.key] = f - return list(merged.values()) - - def _reboot_finding(self, key, sev, title, meaning, actions, expl, boot: BootRecord) -> Finding: - f = Finding(key, key, sev, title, meaning, actions, expl) - f.count, f.boot, f.last_seen = 1, self.boot.index, boot.host_time - if boot.reset_reason: - f.evidence.append(f"reset reason: {boot.reset_reason}") - return f - - -def _group(findings: list[Finding]) -> list[RootCause]: - """Attach each finding to the root of its explained_by chain.""" - by_rule: dict[str, list[Finding]] = {} - for f in findings: - by_rule.setdefault(f.rule_id, []).append(f) - - def parent(f: Finding) -> Finding | None: - for rid in f.explained_by: - if by_rule.get(rid): - return by_rule[rid][0] - return None - - def root(f: Finding) -> Finding: - seen = {f.key} - while (p := parent(f)) is not None and p.key not in seen: - seen.add(p.key) - f = p - return f - - roots: dict[str, RootCause] = {} - for f in findings: - r = root(f) - rc = roots.setdefault(r.key, RootCause(r, [])) - if r is not f: - rc.consequences.append(f) - for rc in roots.values(): - rc.consequences.sort(key=lambda c: (-c.severity, c.first_uptime_ms or 0)) - return list(roots.values()) +"""Diagnostics engine: parsed log lines -> findings, milestones, LED, verdict. + +Line-level knowledge lives in rules.toml. This module adds what a single +line cannot express: + * boot tracking (ROM banner, reset reason, uptime drops when the banner + was missed) and classification of why the previous boot ended; + * the status-LED model (Status::set_status / set_status_conditional); + * crash capture (panic / ESP_ERROR_CHECK output); + * root-cause grouping via `explained_by` chains; + * the structured "KOTA:" lines of firmware 1.0.16+ (diag/kota.py): exact + status/LED, restart reasons, I2C/SD/RTC/Matter/mic self-report and the + 60 s health line, plus error IDs ("[E-SD-03]") looked up in + error_ids.csv. Older firmware is still diagnosed from message text. + +Findings are session-wide (a reboot loop must keep its cause visible); +milestones and the LED are per boot. +""" + +from __future__ import annotations + +import re +import tomllib +from collections import Counter, deque +from dataclasses import dataclass, field +from enum import IntEnum +from pathlib import Path + +from ..core.logline import LineKind, LogLine +from . import kota +from .firmware import FirmwareInfo + +RULES_PATH = Path(__file__).with_name("rules.toml") +_MAC_LINE = re.compile(r"Device MAC address: ([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})") + + +class Severity(IntEnum): + IGNORE = -1 + INFO = 1 + WARNING = 2 + ERROR = 3 + CRITICAL = 4 + + @property + def label(self) -> str: + return self.name.capitalize() + + +# name -> (label, swatch colour) +LED = { + "UNKNOWN": ("Unknown", "#888888"), + "OFF": ("Off", "#444444"), + "GREEN": ("Green", "#2e9e44"), + "PURPLE": ("Purple", "#8e44ad"), + "RED": ("Red", "#d64541"), + "BLUE": ("Blue", "#2f6fd6"), + "CYAN": ("Cyan", "#1fc8d8"), + "YELLOW": ("Yellow", "#e6c229"), + "ORANGE": ("Orange", "#f08c1a"), + "WHITE": ("White", "#f4f4f4"), +} + +# (id, label, optional) - optional milestones are listed only once reached. +MILESTONES = [ + ("rom_boot", "Board booted", False), + ("app_started", "Firmware started", False), + ("sd_mounted", "SD card mounted", False), + ("env_loaded", "ENV.TXT read (InfluxDB settings)", False), + ("matter_started", "Matter started", True), + ("matter_commissioned", "Paired in Matter", True), + ("local_wifi_mode", "Local Wi-Fi mode (SSID from SD card)", True), + ("i2c_ok", "All sensors answer on the I2C bus", True), + ("sensors_ready", "All sensors started", False), + ("wifi_connected", "Connected to Wi-Fi", False), + ("time_synced", "Clock set from the Internet", False), + ("clock_verified", "Clock keeps time (RTC verified)", True), + ("data_uploaded", "Data uploaded to InfluxDB", False), + ("ota_registered", "Registered for firmware updates", False), + ("ota_checked", "Checked for firmware updates", False), + ("ota_update_started", "Firmware update started", True), + ("ota_update_done", "Firmware update installed", True), + ("ota_update_accepted", "Updated firmware accepted", True), +] + +# Findings that stop start-up; a crash/reboot right after them is their effect. +_INIT_FAILURES = [ + "rtc.init_failed", "sensor.init_failed", "sd.card_fail", "sd.mount_fail", + "sd.env_open_fail", "influx.env_missing", "influx.init_fail", + "kota.i2c_stuck", "kota.sensor_missing", "kota.rtc_read_failed", "kota.sd_unmounted", +] +_NETWORK_ROOTS = [ + "influx.buffer_fail", "ota.check_failed", "net.dns_fail", + "matter.not_commissioned", "wifi.disconnected", +] +# Printed by the brown-out handler without a timestamp ("E BOD: ..."); on the +# ESP32-S3 the ROM then reports RTC_SW_SYS_RST, not a brown-out reset. +_BROWNOUT = "Brownout detector was triggered" +_RESET_BY_TOOL_WINDOW_S = 5.0 +_SHORT_BOOT_MS = 60_000 + + +class _Fmt(dict): + def __missing__(self, key: str) -> str: + return "?" + + +def _fmt(s: str, groups: dict[str, str]) -> str: + return s.format_map(_Fmt(groups)) if s else s + + +@dataclass(frozen=True) +class Rule: + id: str + pattern: re.Pattern[str] | None # None: raised from KOTA: fields + severity: Severity + title: str = "" + meaning: str = "" + actions: tuple[str, ...] = () + explained_by: tuple[str, ...] = () + milestone: str | None = None + clears: tuple[str, ...] = () + led: str | None = None + led_from: str | None = None + split_by: str | None = None + min_count: int = 1 + min_uptime_ms: int = 0 + + +def load_rules(path: Path = RULES_PATH) -> list[Rule]: + data = tomllib.loads(path.read_text(encoding="utf-8")) + rules = [] + for r in data["rule"]: + rules.append(Rule( + id=r["id"], + pattern=re.compile(r["pattern"]) if "pattern" in r else None, + severity=Severity[r["severity"].upper()], + title=r.get("title", ""), + meaning=r.get("meaning", ""), + actions=tuple(r.get("actions", ())), + explained_by=tuple(r.get("explained_by", ())), + milestone=r.get("milestone"), + clears=tuple(r.get("clears", ())), + led=r.get("led"), + led_from=r.get("led_from"), + split_by=r.get("split_by"), + min_count=int(r.get("min_count", 1)), + min_uptime_ms=int(r.get("min_uptime_ms", 0)), + )) + _validate(rules) + return rules + + +def _validate(rules: list[Rule]) -> None: + ids = [r.id for r in rules] + dupes = [i for i, n in Counter(ids).items() if n > 1] + if dupes: + raise ValueError(f"duplicate rule ids: {dupes}") + known = set(ids) + for r in rules: + for ref in (*r.explained_by, *r.clears): + # status.* / reboot.* findings are created by the engine itself. + if ref not in known and not ref.startswith(("status.", "reboot.")): + raise ValueError(f"{r.id}: unknown rule id {ref!r}") + for colour in (r.led, r.led_from): + if colour and colour not in LED: + raise ValueError(f"{r.id}: unknown LED colour {colour!r}") + if r.severity != Severity.IGNORE and not r.title: + raise ValueError(f"{r.id}: visible rule needs a title") + + +@dataclass +class Finding: + key: str # rule id, or "rule id:split value" + rule_id: str + severity: Severity + title: str + meaning: str + actions: tuple[str, ...] + explained_by: tuple[str, ...] = () + min_count: int = 1 + count: int = 0 + first_uptime_ms: int | None = None + last_uptime_ms: int | None = None + last_seen: float = 0.0 + boot: int = 0 + resolved: bool = False + resolved_by: str | None = None + evidence: deque[str] = field(default_factory=lambda: deque(maxlen=3)) + error_ids: dict[str, str] = field(default_factory=dict) # "E-SD-03" -> technician action + + @property + def active(self) -> bool: + return self.count >= self.min_count and not self.resolved + + def seen_text(self) -> str: + """'Seen 3x, first at 2.9 s after start, last at 61.2 s' (panel and service note).""" + s = f"Seen {self.count}x" + if self.first_uptime_ms is not None: + first = f"{self.first_uptime_ms / 1000:.1f}" + s += f", first at {first} s after start" + last = f"{self.last_uptime_ms / 1000:.1f}" if self.last_uptime_ms is not None else first + if self.count > 1 and last != first: + s += f", last at {last} s" + if self.resolved and self.resolved_by: + s += f" - resolved ({self.resolved_by})" + return s + + +@dataclass +class BootRecord: + index: int + host_time: float + explicit: bool # ROM banner seen (vs. inferred) + reset_reason: str | None = None + tool_reset: bool = False + last_uptime_ms: int = 0 + saw_log: bool = False + led_at_end: str | None = None + panic_lines: list[str] = field(default_factory=list) + brownout: bool = False # "E BOD: Brownout detector was triggered" before the restart + recent_errors: deque[str] = field(default_factory=lambda: deque(maxlen=5)) + intentional_restart: bool = False # factory reset / setup-mode toggle + # KOTA: lines (firmware 1.0.16+) + kota: dict[str, LogLine] = field(default_factory=dict) # last line per record + last_restart: str | None = None # "boot ... last_restart=" (why the previous boot ended) + restart_reason: str | None = None # "restart reason=" printed just before this boot ended + previous_status: tuple[str, int] | None = None # "led previous_boot status= code=" + mode: str | None = None # matter | wifi | provisioning + + +@dataclass +class RootCause: + finding: Finding + consequences: list[Finding] + + @property + def severity(self) -> Severity: + return max([self.finding.severity, *(c.severity for c in self.consequences)]) + + +@dataclass +class Report: + verdict: str + verdict_severity: Severity | None # None = healthy / nothing found + led: str + led_reason: str + firmware: FirmwareInfo + milestones: list[tuple[str, str, int | None]] # (id, label, uptime_ms) + roots: list[RootCause] + earlier: list[Finding] # seen only in previous boots + resolved: list[Finding] + boots: list[BootRecord] + lines: int + mid_run: bool # no boot seen yet + technician: list[str] = field(default_factory=list) # guided-check answers + led_pattern: str = "" # "2 pulses", "solid", ... (exact when from a KOTA led line) + led_meaning: str = "" + led_exact: bool = False # reported by the box itself + box: list[tuple[str, str, str]] = field(default_factory=list) # (topic, text, ok|warn|bad|info) + matter_qr: str | None = None + matter_manual: str | None = None + + def led_text(self) -> str: + t = LED[self.led][0] + (f", {self.led_pattern}" if self.led_pattern else "") + return t + (f" = {self.led_meaning}" if self.led_meaning else "") + + def to_text(self) -> str: + out = [f"VERDICT: {self.verdict}", + f"Expected status LED: {self.led_text()}" + + (f" (last change: {self.led_reason})" if self.led_reason else ""), + f"Firmware: {self.firmware.describe()}", + f"Boots seen: {max(len(self.boots) - (0 if self.boots and self.boots[0].explicit else 1), 0)}"] + if self.box: + out += ["", "Box self-report (KOTA lines):"] + out += [f" {topic}: {text}" for topic, text, _ in self.box] + out += ["", "Milestones (current boot):"] + for _, label, t in self.milestones: + out.append(f" [{'x' if t is not None else ' '}] {label}" + + (f" @ {t / 1000:.1f} s" if t is not None else "")) + out.append("") + out.append("Findings:" if self.roots else "Findings: none") + for rc in self.roots: + out += _finding_text(rc.finding, " ") + for c in rc.consequences: + out.append(" consequence:") + out += _finding_text(c, " ") + if self.earlier: + out.append("") + out.append("Seen in earlier boots only:") + for f in self.earlier: + out.append(f" - [{f.severity.label}] {f.title} (x{f.count})") + if self.resolved: + out.append("") + out.append("Resolved during this session:") + for f in self.resolved: + out.append(f" - {f.title}" + (f" (by: {f.resolved_by})" if f.resolved_by else "")) + if self.technician: + out.append("") + out.append("Technician checks:") + out += [f" - {t}" for t in self.technician] + return "\n".join(out) + + +def _finding_text(f: Finding, ind: str) -> list[str]: + lines = [f"{ind}- [{f.severity.label}] {f.title} (x{f.count})", f"{ind} {f.meaning}"] + lines += [f"{ind} {i}. {a}" for i, a in enumerate(f.actions, 1)] + for eid, action in f.error_ids.items(): + lines.append(f"{ind} error {eid}" + (f": {action}" if action else "")) + if f.evidence: + lines.append(f"{ind} log: {f.evidence[-1]}") + return lines + + +class DiagnosticsEngine: + def __init__(self, rules: list[Rule] | None = None): + self.rules = rules if rules is not None else load_rules() + self._rule_by_id = {r.id: r for r in self.rules} + self._pattern_rules = [r for r in self.rules if r.pattern is not None] + self.error_ids = kota.load_error_ids() + self.reset() + + # ---------------------------------------------------------------- API + def reset(self) -> None: + self.findings: dict[str, Finding] = {} + self.boots: list[BootRecord] = [BootRecord(0, 0.0, explicit=False)] + self.milestones: dict[str, int] = {} + self.led = "UNKNOWN" + self.led_reason = "" + self.firmware = FirmwareInfo() + self.lines = 0 + self._tool_reset_at: float | None = None + self._panic_tail = 0 # RAW lines after a panic line to keep + self.technician: list[str] = [] + self.mac: str | None = None # from "ID: Device MAC address: ..." at boot + self.matter_advert: str | None = None # "Advertise commission parameter vendorID=..." line + self.matter_qr: str | None = None # "KOTA: matter ... qr=MT:..." + self.matter_manual: str | None = None + self.kota_seen = False # firmware prints KOTA: lines + self._reset_led_state() + + def _reset_led_state(self) -> None: + self.led_pattern = "" + self.led_exact = False # a KOTA led line was seen this boot + self.status: tuple[str, int] | None = None + + @property + def boot(self) -> BootRecord: + return self.boots[-1] + + def feed(self, line: LogLine) -> None: + self.lines += 1 + text = line.text + if line.kind == LineKind.HOST: + if "reset issued by tool" in text: + self._tool_reset_at = line.host_time + return + if _BROWNOUT in text: + self.boot.brownout = True + if line.kind == LineKind.BOOT: + self._new_boot(line.host_time, explicit=True) + self.milestones["rom_boot"] = 0 + return + if line.kind == LineKind.RESET: + self.boot.reset_reason = line.reset_reason + if line.reset_reason and "POWERON" in line.reset_reason: + self.led, self.led_reason = "OFF", "power-on - firmware not started yet" + return + if line.kind in (LineKind.LOG, LineKind.KOTA) and line.uptime_ms is not None: + b = self.boot + # Uptime jumped back to a fresh-boot value: the ROM banner was + # missed (port reconnect, capture started late). + if b.saw_log and line.uptime_ms + 3000 < b.last_uptime_ms and line.uptime_ms < 10_000: + self._new_boot(line.host_time, explicit=False) + b = self.boot + b.saw_log = True + b.last_uptime_ms = max(b.last_uptime_ms, line.uptime_ms) + + if line.kind == LineKind.PANIC: + self._on_panic(line) + elif self._panic_tail and line.kind == LineKind.RAW: + self.boot.panic_lines.append(text) + self._panic_tail -= 1 + self._crash_finding().evidence.append(text) + + if line.kind == LineKind.KOTA: + self._on_kota(line) + return + + self.firmware.observe(text) + if m := _MAC_LINE.search(text): + self.mac = m.group(1).upper() + if "Advertise commission parameter" in text: + self.matter_advert = text + matched = False + hit: list[Finding] = [] + rule_text = line.match_text + for rule in self._pattern_rules: + m = rule.pattern.search(rule_text) + if not m: + continue + matched = True + if rule.min_uptime_ms and (line.uptime_ms or 0) < rule.min_uptime_ms: + continue + groups = {k: v for k, v in m.groupdict().items() if v is not None} + if rule.milestone and rule.milestone not in self.milestones: + self.milestones[rule.milestone] = line.uptime_ms or 0 + # The box's own "KOTA: led" lines are exact; message rules only guess. + if rule.led and not self.led_exact: + self._set_led(rule.led, rule.led_from, text) + for cid in rule.clears: + for f in self.findings.values(): + if f.rule_id == cid and not f.resolved: + f.resolved = True + f.resolved_by = rule.milestone or rule.id + if rule.id in ("sys.factory_reset",) or "Short press:" in text: + self.boot.intentional_restart = True + if rule.severity != Severity.IGNORE: + hit.append(self._hit(rule, groups, line)) + + self._stateful(line) + if line.error_id: + info = self.error_ids.get(line.error_id) + for f in hit: + f.error_ids[line.error_id] = info.action if info else "" + if not matched: + self._hit_error_id(line, info) + if line.level == "E": + self.boot.recent_errors.append(text) + if not matched and line.tag and not line.error_id: + self._hit_other(line) + + def report(self) -> Report: + all_f = list(self.findings.values()) + self._reboot_findings() + cur = self.boot.index + active = [f for f in all_f if f.active] + current = [f for f in active if f.boot == cur] + earlier = [f for f in active if f not in current] + resolved = [f for f in all_f if f.resolved and f.count >= f.min_count] + roots = _group(current) + # Worst consequence first, then the root's own severity, then order seen. + roots.sort(key=lambda rc: (-rc.severity, -rc.finding.severity, rc.finding.first_uptime_ms or 0)) + + mid_run = (not any(b.explicit or "i2c" in b.kota or "boot" in b.kota for b in self.boots) + and self.boot.index == 0) + if self.lines == 0: + verdict, vsev = "Waiting for data from the sensorbox...", None + elif roots and roots[0].severity >= Severity.WARNING: + vsev = roots[0].severity + prefix = "Problem" if vsev >= Severity.ERROR else "Attention" + verdict = f"{prefix}: {roots[0].finding.title}" + elif "data_uploaded" in self.milestones: + verdict, vsev = "Healthy: data is reaching InfluxDB", None + else: + verdict, vsev = "No problems found so far", None + if mid_run and self.lines: + verdict += " (connected mid-run - press Reset & capture for a full check)" + + ms = [(mid, label, self.milestones.get(mid)) for mid, label, optional in MILESTONES + if not optional or mid in self.milestones] + # Reached milestones in the order they happened, then the pending ones. + ms.sort(key=lambda m: (m[2] is None, m[2] or 0)) + pattern = self.led_pattern + return Report(verdict, vsev, self.led, self.led_reason, self.firmware, ms, roots, + earlier, resolved, list(self.boots), self.lines, mid_run, list(self.technician), + led_pattern=pattern, led_meaning=kota.led_meaning(self.led, pattern) if pattern else "", + led_exact=self.led_exact, box=self._box_summary(), + matter_qr=self.matter_qr, matter_manual=self.matter_manual) + + def add_confirmed(self, key: str, severity: Severity, title: str, meaning: str, + actions: tuple[str, ...]) -> Finding: + """A finding established by a guided check (technician answer + log).""" + f = self.findings.get(key) + if f is None: + f = self.findings[key] = Finding(key, key, severity, title, meaning, tuple(actions)) + f.count += 1 + f.boot = self.boot.index + f.resolved = False + f.evidence.append("confirmed by the technician during a guided check") + return f + + def note_technician(self, text: str) -> None: + self.technician.append(text) + + # ------------------------------------------------------------ internals + def _new_boot(self, host_time: float, explicit: bool) -> None: + prev = self.boot + # An inferred boot right after a banner-started boot is the same boot. + if not explicit and prev.explicit and not prev.saw_log: + return + prev.led_at_end = self.led + b = BootRecord(prev.index + 1, host_time, explicit) + if self._tool_reset_at is not None and host_time - self._tool_reset_at <= _RESET_BY_TOOL_WINDOW_S: + b.tool_reset = True + self._tool_reset_at = None + self.boots.append(b) + self.milestones = {} + # The WS2812 latches its colour through a software reset until + # Status::init() rewrites it; only a power-on turns it dark. + self.led_reason = "restarting - LED keeps its last colour until the firmware starts" + self.firmware = FirmwareInfo() + self._panic_tail = 0 + self._reset_led_state() + + def _set_led(self, colour: str, led_from: str | None, text: str) -> None: + if led_from and self.led != led_from: + return + self.led = colour + self.led_reason = text + + def _hit(self, rule: Rule, groups: dict[str, str], line: LogLine) -> None: + key = rule.id + if rule.split_by and rule.split_by in groups: + key = f"{rule.id}:{groups[rule.split_by]}" + f = self.findings.get(key) + if f is None: + f = self.findings[key] = Finding( + key, rule.id, rule.severity, _fmt(rule.title, groups), _fmt(rule.meaning, groups), + tuple(_fmt(a, groups) for a in rule.actions), rule.explained_by, rule.min_count) + self._touch(f, line) + return f + + def _touch(self, f: Finding, line: LogLine) -> None: + f.count += 1 + # Uptime restarts every boot, so "first seen" is per current boot. + if f.first_uptime_ms is None or f.boot != self.boot.index: + f.first_uptime_ms = line.uptime_ms + f.last_uptime_ms = line.uptime_ms + f.last_seen = line.host_time + f.boot = self.boot.index + f.evidence.append(line.text) + if f.resolved: + f.resolved, f.resolved_by = False, None # problem came back + + def _custom(self, key: str, severity: Severity, title: str, meaning: str, + actions: tuple[str, ...], explained_by: tuple[str, ...] = ()) -> Finding: + f = self.findings.get(key) + if f is None: + f = self.findings[key] = Finding(key, key, severity, title, meaning, actions, explained_by) + return f + + def _hit_other(self, line: LogLine) -> None: + f = self._custom( + f"other.{line.tag}", Severity.INFO, f"Unrecognised error messages from {line.tag}", + "These errors are not in the rule set yet; they may be harmless.", + ("If the box misbehaves, save the session log and send it to the firmware team.",)) + self._touch(f, line) + + def _crash_finding(self) -> Finding: + return self._custom( + "sys.crash", Severity.CRITICAL, "Firmware stopped with an error and restarts", + "The firmware hit a fatal error. The error printed just before it is the real cause.", + ("See the grouped cause; otherwise save the session log for the firmware team.",), + tuple(_INIT_FAILURES) + ("reboot.brownout",)) + + def _on_panic(self, line: LogLine) -> None: + b = self.boot + if len(b.panic_lines) < 8: + b.panic_lines.append(line.text) + self._panic_tail = 4 # file/func/expression lines follow ESP_ERROR_CHECK + f = self._crash_finding() + if f.count == 0 and b.recent_errors: + f.meaning += f" Last error before it: {b.recent_errors[-1]}" + self._touch(f, line) + + def _stateful(self, line: LogLine) -> None: + # RTC set by SNTP but reads "uninitialized" again later: the chip + # does not keep time (crystal/module fault), not just a flat cell. + synced = self.milestones.get("time_synced") + if (synced is not None and line.uptime_ms is not None + and line.uptime_ms > synced + 10_000 + and "RTC is uninitialized" in line.text): + f = self._custom( + "rtc.lost_after_sync", Severity.ERROR, + "Clock stops again after being set from the Internet", + "The clock was set from the Internet but stopped running shortly after. The RTC module or its crystal is faulty.", + ("Replace the RTC module (inside the case).",)) + self._touch(f, line) + + # ------------------------------------------------------- KOTA: lines + def _on_kota(self, line: LogLine) -> None: + self.kota_seen = True + rec, f = line.record or "", line.fields + # A second boot line in one boot: the ROM banner and the uptime drop + # were both missed (e.g. capture started with a gap). + if rec == "boot" and "boot" in self.boot.kota: + self._new_boot(line.host_time, explicit=False) + key = f"{rec}.verify" if rec == "rtc" and "verify" in line.words else rec + self.boot.kota[key] = line + handler = getattr(self, f"_kota_{rec}", None) + if handler is not None: + handler(line, f) + + def _raise(self, rule_id: str, line: LogLine, groups: dict[str, str] | None = None) -> Finding: + """Raise a rules.toml finding from parsed KOTA fields (no pattern matching).""" + return self._hit(self._rule_by_id[rule_id], groups or {}, line) + + def _resolve(self, rule_id: str, by: str, key: str | None = None) -> None: + for f in self.findings.values(): + if f.rule_id == rule_id and (key is None or f.key == key) and not f.resolved: + f.resolved, f.resolved_by = True, by + + def _milestone(self, mid: str, line: LogLine) -> None: + if mid not in self.milestones: + self.milestones[mid] = line.uptime_ms or 0 + + def _kota_boot(self, line: LogLine, f: dict[str, str]) -> None: + b = self.boot + self.firmware.observe_kota(f) + mac = f.get("mac", "") + if re.fullmatch(r"[0-9A-Fa-f]{12}", mac): + self.mac = ":".join(mac[i:i + 2] for i in range(0, 12, 2)).upper() + b.reset_reason = b.reset_reason or f.get("reset") + b.last_restart = f.get("last_restart") + b.mode = f.get("mode") + self._milestone("app_started", line) + + def _kota_restart(self, line: LogLine, f: dict[str, str]) -> None: + reason = f.get("reason", "") + self.boot.restart_reason = reason + if reason in kota.INTENTIONAL_RESTARTS: + self.boot.intentional_restart = True + + def _kota_i2c(self, line: LogLine, f: dict[str, str]) -> None: + if f.get("bus") == "stuck": + self._raise("kota.i2c_stuck", line, {"sda": f.get("SDA", "?"), "scl": f.get("SCL", "?")}) + return + self._resolve("kota.i2c_stuck", "I2C bus free again") + missing = False + for addr, value in f.items(): + if not addr.startswith("0x") or value == "?": + continue + name = value.removesuffix("(missing)") + if value.endswith("(missing)"): + missing = True + self._raise("kota.sensor_missing", line, {"sensor": name, "addr": addr}) + else: + self._resolve("kota.sensor_missing", f"{name} found on I2C", f"kota.sensor_missing:{name}") + if not missing: + self._milestone("i2c_ok", line) + + def _kota_rtc(self, line: LogLine, f: dict[str, str]) -> None: + if "verify" in line.words: + if f.get("ok") == "1": + self._milestone("clock_verified", line) + self._resolve("kota.rtc_not_ticking", "clock verified") + else: + self._raise("kota.rtc_not_ticking", line, + {"running": f.get("running", "?"), "delta": f.get("delta", "no reading")}) + elif "read_failed" in line.words: + self._raise("kota.rtc_read_failed", line, {"err": f.get("err", "?")}) + elif f.get("running") == "0": + self._raise("kota.rtc_stopped", line, {"time": f.get("time", "?")}) + elif f.get("running") == "1": + self._resolve("kota.rtc_read_failed", "clock answers") + self._resolve("kota.rtc_stopped", "clock running") + + def _kota_sd(self, line: LogLine, f: dict[str, str]) -> None: + if f.get("mounted") == "0": + self._raise("kota.sd_unmounted", line, {"err": f.get("err", "card missing or unreadable")}) + return + self._milestone("sd_mounted", line) + self._resolve("kota.sd_unmounted", "SD card mounted") + keys = {k for k in f.get("env_keys", "").split(",") if k and k != "-"} + missing = [k for k in ("INFLUX_URL", "INFLUX_TOKEN") if k not in keys] + for k in missing: + self._raise("influx.env_missing", line, {"key": k}) + if not missing: + self._milestone("env_loaded", line) + if f.get("fs") and f["fs"] != "FAT32": + self._raise("kota.sd_not_fat32", line, {"fs": f["fs"]}) + free = kota.parse_size_gb(f.get("free", "")) + if free is not None and free < 0.1: + self._raise("kota.sd_full", line, {"free": f["free"]}) + lines = f.get("buffer_lines", "0") + if lines.isdigit() and int(lines) > 500: + self._raise("kota.buffer_backlog", line, {"lines": lines}) + + def _kota_matter(self, line: LogLine, f: dict[str, str]) -> None: + if f.get("qr", "-").startswith("MT:"): + self.matter_qr = f["qr"] + if f.get("manual", "-") != "-": + self.matter_manual = f["manual"] + fabrics = f.get("fabrics", "") + if fabrics == "0": + self._raise("matter.not_commissioned", line) + elif fabrics.isdigit(): + self._milestone("matter_commissioned", line) + self._resolve("matter.not_commissioned", "paired in Matter") + + def _kota_mic(self, line: LogLine, f: dict[str, str]) -> None: + if f.get("enabled") == "0": + self._raise("mic.disabled", line) + elif f.get("enabled") == "1": + self._resolve("mic.disabled", "microphone switched on") + + def _kota_status(self, line: LogLine, f: dict[str, str]) -> None: + w = line.words + new = w[w.index("->") + 1] if "->" in w and w.index("->") + 1 < len(w) else (w[-1] if w else "") + code = int(f["code"]) if f.get("code", "").isdigit() else 0 + self._apply_status(new, code, line, f.get("reason", "")) + + def _apply_status(self, status: str, code: int, line: LogLine, reason: str) -> None: + self.status = (status, code) + if not self.led_exact: + self.led, self.led_pattern = kota.led_for_status(status, code) + self.led_reason = f"status {status}" + (f" code={code}" if code else "") + ( + f", reason: {reason}" if reason else "") + sf = kota.status_finding(status, code) + key = sf[0] if sf else None + for fd in self.findings.values(): + if fd.rule_id.startswith("status.") and fd.key != key and not fd.resolved: + fd.resolved, fd.resolved_by = True, f"status -> {status}" + if sf: + key, sev, title, meaning, actions, expl = sf + fd = self._custom(key, Severity[sev.upper()], title, meaning, actions, expl) + self._touch(fd, line) + + def _kota_led(self, line: LogLine, f: dict[str, str]) -> None: + if "previous_boot" in line.words: + code = f.get("code", "0") + self.boot.previous_status = (f.get("status", "?"), int(code) if code.isdigit() else 0) + return + if "restart_flash" in line.words: + return + colour = kota.LED_COLOURS.get(f.get("color", "")) + if colour is None: + return + pattern = kota.pattern_text(f) + self.led, self.led_pattern, self.led_exact = ("OFF", "", True) if pattern == "off" else (colour, pattern, True) + if not self.led_reason or self.status is None: + self.led_reason = "reported by the box" + + def _kota_health(self, line: LogLine, f: dict[str, str]) -> None: + up = kota.parse_seconds(f.get("up", "")) or 0 + status = f.get("status", "") + # Connected mid-run: the health line is the first exact status we get. + if status and (self.status is None or self.status[0] != status): + self._apply_status(status, 0, line, "from the health report") + mode = self.boot.mode + wifi = f.get("wifi") + rssi = int(f["rssi"]) if f.get("rssi", "").lstrip("-").isdigit() else None + if wifi == "connected": + self._milestone("wifi_connected", line) + self._resolve("kota.wifi_down", "Wi-Fi connected") + if rssi is not None and rssi < -75: + self._raise("kota.weak_wifi", line, {"rssi": str(rssi)}) + elif rssi is not None: + self._resolve("kota.weak_wifi", "signal OK") + elif wifi == "disconnected" and mode != "provisioning": + self._raise("kota.wifi_down", line) + if f.get("time_valid") == "1": + self._milestone("time_synced", line) + self._resolve("kota.time_invalid", "time valid") + elif f.get("time_valid") == "0" and up >= 120: + self._raise("kota.time_invalid", line) + if f.get("sd") == "missing": + self._raise("kota.sd_unmounted", line, {"err": "reported missing by the health check"}) + last = f.get("last_upload", "never") + if last != "never": + self._milestone("data_uploaded", line) + self._resolve("kota.no_upload", "data uploaded") + elif wifi == "connected" and up >= 300: + self._raise("kota.no_upload", line) + heap = f.get("heap", "") + if heap.isdigit() and int(heap) < 20_000: + self._raise("kota.low_heap", line, {"heap": heap}) + ota = f.get("ota") + if ota == "registered": + self._milestone("ota_registered", line) + self._resolve("kota.ota_unregistered", "registered") + elif ota == "unregistered" and wifi == "connected" and mode != "provisioning": + self._raise("kota.ota_unregistered", line) + + def _hit_error_id(self, line: LogLine, info: kota.ErrorId | None) -> None: + eid = line.error_id or "?" + msg = (line.message or "").strip() + msg = msg if len(msg) <= 90 else msg[:87] + "..." + if info: + sev = Severity.WARNING if info.severity == "warning" else Severity.ERROR + f = self._custom(f"eid.{eid}", sev, f"{info.module_name}: {msg}", + f"The firmware reported error {eid} ({info.module_name}).", + (info.action, "If it repeats, save the session log for the firmware team.")) + f.rule_id = f"eid.{info.module}" + f.error_ids[eid] = info.action + else: + f = self._custom(f"eid.{eid}", Severity.WARNING, f"Error {eid}: {msg}", + "This error ID is not in the tool's error table (newer firmware?).", + ("Save the session log and send it to the firmware team.",)) + self._touch(f, line) + + def _box_summary(self) -> list[tuple[str, str, str]]: + """What the box reported about itself in this boot (KOTA lines), for the panel.""" + k = self.boot.kota + out: list[tuple[str, str, str]] = [] + if ln := k.get("boot"): + f = ln.fields + lr = f.get("last_restart", "none") + bad = kota.restart_finding(lr) is not None + out.append(("Boot", f"v{f.get('fw', '?')} build {f.get('build', '?')}, mode {f.get('mode', '?')}, " + f"reset {f.get('reset', '?')}, last restart: {lr}", "warn" if bad else "ok")) + if ps := self.boot.previous_status: + if ps[0] != "OK": + out.append(("Before restart", f"status {ps[0]}" + (f" code={ps[1]}" if ps[1] else ""), "warn")) + if ln := k.get("i2c"): + f = ln.fields + if f.get("bus") == "stuck": + out.append(("I2C", f"bus stuck (SDA={f.get('SDA')}, SCL={f.get('SCL')})", "bad")) + else: + devs = [(v.removesuffix("(missing)"), v.endswith("(missing)")) + for a, v in f.items() if a.startswith("0x") and v != "?"] + miss = [n for n, m in devs if m] + text = ("missing: " + ", ".join(miss) + "; " if miss else "") + "found: " + ( + ", ".join(n for n, m in devs if not m) or "none") + out.append(("I2C", text, "bad" if miss else "ok")) + if ln := k.get("sd"): + f = ln.fields + if f.get("mounted") == "0": + out.append(("SD card", "not mounted" + (f" ({f['err']})" if f.get("err") else ""), "bad")) + else: + keys = f.get("env_keys", "-") + ok = "INFLUX_URL" in keys and "INFLUX_TOKEN" in keys + out.append(("SD card", f"{f.get('type', '?')} {f.get('size', '?')} {f.get('fs', '?')}, " + f"{f.get('free', '?')} free, ENV keys: {keys}, buffered: {f.get('buffer_lines', '?')}", + "ok" if ok else "bad")) + rtc, verify = k.get("rtc"), k.get("rtc.verify") + if rtc or verify: + parts, state = [], "ok" + if rtc: + if "read_failed" in rtc.words: + parts.append(f"read failed ({rtc.fields.get('err', '?')})") + state = "bad" + else: + running = rtc.fields.get("running") == "1" + parts.append(("running" if running else "STOPPED") + f", time {rtc.fields.get('time', '?')}") + state = "ok" if running else "warn" + if verify: + ok = verify.fields.get("ok") == "1" + parts.append("verified ticking" if ok else "NOT keeping time") + state = state if ok else "bad" + out.append(("Clock (RTC)", "; ".join(parts), state)) + if ln := k.get("matter"): + f = ln.fields + fabrics = f.get("fabrics", "?") + out.append(("Matter", f"{'paired' if fabrics not in ('0', '?') else 'NOT paired'} (fabrics {fabrics}), " + f"pairing window {f.get('window', '?')}", "ok" if fabrics not in ("0", "?") else "warn")) + if ln := k.get("mic"): + on = ln.fields.get("enabled") == "1" + out.append(("Microphone", "on" if on else "switched OFF (switch on the back)", "ok" if on else "info")) + if ln := k.get("health"): + f = ln.fields + wifi = f.get("wifi", "?") + text = (f"up {f.get('up', '?')}, status {f.get('status', '?')}, Wi-Fi {wifi}" + + (f" ({f.get('rssi')} dBm)" if wifi == "connected" else "") + + f", time {'OK' if f.get('time_valid') == '1' else 'NOT set'}, SD {f.get('sd', '?')}, " + f"buffer {f.get('buffer', '?')}, heap {f.get('heap', '?')}, last upload {f.get('last_upload', '?')}" + + ("" if f.get("last_upload") == "never" else " ago") + f", OTA {f.get('ota', '?')}") + good = f.get("status") == "OK" and wifi == "connected" and f.get("last_upload") != "never" + out.append(("Health", text, "ok" if good else "warn")) + return out + + # ------------------------------------------------------------ reboots + def _reboot_findings(self) -> list[Finding]: + """Classify why each earlier boot ended (needs the next boot's reset reason).""" + out: list[Finding] = [] + short_unexpected: list[BootRecord] = [] + cur = self.boot + # The box keeps its status across a restart ("led previous_boot"): if + # it was in an error state right before the latest restart (often the + # technician's Reset & capture), that state is worth showing. + if cur.previous_status and cur.previous_status[0] not in ("OK", "UPDATING", "SELF_CHECK"): + sf = kota.status_finding(*cur.previous_status) + st, code = cur.previous_status + # Only informative once the box reports OK again in this boot. + sev = Severity.INFO if self.status and self.status[0] == "OK" else Severity.WARNING + f = Finding("boot.previous_status", "boot.previous_status", sev, + f"Before the last restart: {sf[2] if sf else st}", + "The box remembers its status across a restart. Right before the latest restart it was in " + f"state {st}" + (f" code={code}" if code else "") + ". " + + (sf[3] if sf else "") + " Check whether the problem comes back in this boot.", + sf[4] if sf else ()) + f.count, f.boot, f.last_seen = 1, cur.index, cur.host_time + f.evidence.append(f"led previous_boot status={st} code={code}") + out.append(f) + for prev, nxt in zip(self.boots, self.boots[1:]): + if not prev.saw_log and prev.index == 0: + continue # nothing captured before the first boot + reason = nxt.reset_reason or "unknown" + if nxt.tool_reset or prev.intentional_restart: + continue + # Firmware 1.0.16+ records why it restarted itself. + last = nxt.last_restart or prev.restart_reason + if last in kota.INTENTIONAL_RESTARTS: + continue + key, sev, title, meaning, actions, expl = None, Severity.INFO, "", "", (), () + rf = kota.restart_finding(last) if last else None + if prev.brownout or "BROWN" in reason or last == "brownout": + # A crash right after a brown-out is its effect, not a firmware bug. + key, sev, title = "reboot.brownout", Severity.CRITICAL, "Power supply voltage drops (brown-out)" + meaning = ("The supply voltage fell too low and the chip reset itself - typically when the " + "Wi-Fi/Bluetooth radio starts. The box cannot run like this.") + actions = ("Switch the box's power switch ON - USB from a laptop alone may not be enough.", + "Check the power supply, cable and connectors; try another supply.", + "If it persists on a good supply: the board's power circuit is faulty.") + elif rf and not (last == "panic" and prev.panic_lines): + sev_name, title, meaning, actions = rf + sev = Severity[sev_name.upper()] + key = {"status_watchdog": "reboot.status_watchdog", "panic": "reboot.crash", + "brownout": "reboot.brownout", "task_wdt": "reboot.watchdog", "int_wdt": "reboot.watchdog", + "wdt": "reboot.watchdog"}.get(last, f"reboot.{last}") + if nxt.previous_status and nxt.previous_status[0] != "OK": + st, code = nxt.previous_status + meaning += f" Before the restart the box was in state {st}" + (f" code={code}" if code else "") + "." + if last == "status_watchdog": + expl = tuple(_INIT_FAILURES) + kota._SENSOR_ROOTS + ("status.error", "sys.out_of_resources") + elif last == "status_oom_watchdog": + expl = ("sys.out_of_resources",) + elif rf: + pass # panic with captured crash output: covered by sys.crash + elif "USB_UART_CHIP_RESET" in reason: + key, title = "reboot.usb", "Box was reset over USB" + meaning = "The board was reset through its USB port (a flashing tool, a serial program, or this tool in an earlier session)." + elif "POWERON" in reason: + key, title = "reboot.power", "Box was switched off and on" + meaning = "Power was interrupted (power switch, cable or supply)." + actions = ("If nobody switched it off: check the power supply and cable.",) + elif "WDT" in reason: + key, sev, title = "reboot.watchdog", Severity.ERROR, "Firmware froze and was restarted by the watchdog" + meaning = "The firmware stopped responding; the hardware watchdog restarted it." + actions = ("Save the session log for the firmware team.",) + elif prev.panic_lines: + pass # covered by sys.crash + elif (not nxt.last_restart and prev.led_at_end in ("PURPLE", "RED") + and prev.last_uptime_ms >= 140_000): # older firmware: no restart record + key, title = "reboot.status_watchdog", "Box restarted itself after 2.5 minutes of errors" + meaning = "By design the firmware restarts when it stays in an error state (purple/red) for 150 s." + actions = ("Fix the grouped cause; the restarts stop by themselves.",) + expl = tuple(_NETWORK_ROOTS) + ("sys.out_of_resources",) + else: + key, sev, title = "reboot.unexpected", Severity.WARNING, "Unexpected restart" + meaning = f"The firmware restarted (reset reason {reason})." + actions = ("Watch whether it repeats; save the session log for the firmware team.",) + if key: + out.append(self._reboot_finding(key, sev, title, meaning, actions, expl, nxt)) + if prev.last_uptime_ms < _SHORT_BOOT_MS and "POWERON" not in reason: + short_unexpected.append(prev) + if len(short_unexpected) >= 2: + avg = sum(b.last_uptime_ms for b in short_unexpected) / len(short_unexpected) / 1000 + f = self._reboot_finding( + "reboot.loop", Severity.CRITICAL, "Reboot loop", + f"The box restarted {len(short_unexpected)} times, about every {avg:.0f} s. " + "The error printed just before each restart is the cause.", + ("Fix the grouped cause below.",), tuple(_INIT_FAILURES) + ("reboot.brownout",), self.boot) + f.count = len(short_unexpected) + out.append(f) + # One finding per cause, counting how often it happened. + merged: dict[str, Finding] = {} + for f in out: + if f.key in merged: + merged[f.key].count += f.count + merged[f.key].evidence.extend(f.evidence) + else: + merged[f.key] = f + return list(merged.values()) + + def _reboot_finding(self, key, sev, title, meaning, actions, expl, boot: BootRecord) -> Finding: + f = Finding(key, key, sev, title, meaning, actions, expl) + f.count, f.boot, f.last_seen = 1, self.boot.index, boot.host_time + if boot.reset_reason: + f.evidence.append(f"reset reason: {boot.reset_reason}") + return f + + +def _group(findings: list[Finding]) -> list[RootCause]: + """Attach each finding to the root of its explained_by chain.""" + by_rule: dict[str, list[Finding]] = {} + for f in findings: + by_rule.setdefault(f.rule_id, []).append(f) + + def parent(f: Finding) -> Finding | None: + for rid in f.explained_by: + if by_rule.get(rid): + return by_rule[rid][0] + return None + + def root(f: Finding) -> Finding: + seen = {f.key} + while (p := parent(f)) is not None and p.key not in seen: + seen.add(p.key) + f = p + return f + + roots: dict[str, RootCause] = {} + for f in findings: + r = root(f) + rc = roots.setdefault(r.key, RootCause(r, [])) + if r is not f: + rc.consequences.append(f) + for rc in roots.values(): + rc.consequences.sort(key=lambda c: (-c.severity, c.first_uptime_ms or 0)) + return list(roots.values()) diff --git a/kota_tech/diag/firmware.py b/kota_tech/diag/firmware.py index 88aad52..420cd6c 100644 --- a/kota_tech/diag/firmware.py +++ b/kota_tech/diag/firmware.py @@ -1,147 +1,147 @@ -"""Firmware identification from the boot log. - -Up to firmware 1.0 the app version string was "1.0" in every build, so -builds are told apart by the ELF SHA256 prefix printed by app_init. Boxes -receive new firmware over OTA; an unknown SHA usually means a box has already -updated. - -Firmware 1.0.16+ prints "KOTA: boot fw=1.0.16 build=4f10989e ..." (version -unique per build, first 8 hex digits of the ELF SHA256). Builds found in the -tool's firmware/ folder are registered with `register_local_build` so the -box's build can be shown with its compile date. -""" - -from __future__ import annotations - -import re -from dataclasses import dataclass, field - - -@dataclass(frozen=True) -class KnownFirmware: - name: str - source: str - sd_wifi: bool # reads SSID/PASSWORD from ENV.TXT - setup_portal: bool # short press -> Wi-Fi setup portal - - -KNOWN: dict[str, KnownFirmware] = { - "45fa9d5b9": KnownFirmware( - name="v1.0 (built Jun 29 2026)", - source="factory image new_system.bin", - sd_wifi=False, - setup_portal=False, - ), - "c94ba0e9b": KnownFirmware( - name="v1.0 source build (Sep 26 2026)", - source="developer build, built by Krzysztof Kallas", - sd_wifi=True, - setup_portal=True, - ), -} - -# elf sha (full or prefix) -> (version, compile time, file name) of builds in firmware/ -LOCAL_BUILDS: dict[str, tuple[str, str, str]] = {} - - -def register_local_build(elf_sha: str, version: str, built: str, file_name: str) -> None: - LOCAL_BUILDS[elf_sha.lower()] = (version, built, file_name) - - -def same_build(a: str | None, b: str | None) -> bool: - """ELF SHA prefixes of different lengths (8 from KOTA: boot, 9+ from - app_init, 64 from an image) name the same build if they agree.""" - if not a or not b: - return False - n = min(len(a), len(b)) - return n >= 6 and a[:n].lower() == b[:n].lower() - - -def local_build(elf_sha: str | None) -> tuple[str, str, str] | None: - return next((v for sha, v in LOCAL_BUILDS.items() if same_build(sha, elf_sha)), None) - - -_ELF = re.compile(r"app_init: ELF file SHA256:\s+(?P[0-9a-f]{6,})") -_VER = re.compile(r"app_init: App version:\s+(?P\S+)") -_BUILT = re.compile(r"app_init: Compile time:\s+(?P.+)$") -_IDF = re.compile(r"app_init: ESP-IDF:\s+(?P\S+)") -# Evidence of features, for builds not in KNOWN. -_FEATURE_SD_WIFI = re.compile(r"WIFI_CREDENTIALS: ") -_FEATURE_PORTAL = re.compile(r"WIFI_PROVISIONING: |Short press: ") -# Deployed v1.0 logs the SD mount under the INFLUX_BUFFER tag. -_LEGACY_SD_TAG = re.compile(r"INFLUX_BUFFER: SD card mounted") - - -@dataclass -class FirmwareInfo: - elf_sha: str | None = None - version: str | None = None - built: str | None = None - idf: str | None = None - hw: str | None = None - evidence: set[str] = field(default_factory=set) - - def observe_kota(self, fields: dict[str, str]) -> None: - """'KOTA: boot fw=1.0.16 build=4f10989e hw=2 ...'""" - if fields.get("fw"): - self.version = fields["fw"] - build = fields.get("build", "") - # app_init's longer prefix is kept if it agrees. - if build and not same_build(self.elf_sha, build): - self.elf_sha = build.lower() - if fields.get("hw"): - self.hw = fields["hw"] - self.evidence.add("kota") # 1.0.16+: SD Wi-Fi and setup portal exist - - def observe(self, text: str) -> None: - if m := _ELF.search(text): - self.elf_sha = m.group("sha") - elif m := _VER.search(text): - self.version = m.group("v") - elif m := _BUILT.search(text): - self.built = m.group("t").strip() - elif m := _IDF.search(text): - self.idf = m.group("v") - if _FEATURE_SD_WIFI.search(text): - self.evidence.add("sd_wifi") - if _FEATURE_PORTAL.search(text): - self.evidence.add("setup_portal") - if _LEGACY_SD_TAG.search(text): - self.evidence.add("legacy_v1") - - @property - def known(self) -> KnownFirmware | None: - if not self.elf_sha: - return None - return next((k for sha, k in KNOWN.items() if same_build(self.elf_sha, sha)), None) - - @property - def supports_sd_wifi(self) -> bool | None: - """True/False when determinable, None when unknown (no boot seen).""" - if k := self.known: - return k.sd_wifi - if "sd_wifi" in self.evidence or "kota" in self.evidence: - return True - if "legacy_v1" in self.evidence: - return False - return None - - def describe(self) -> str: - if k := self.known: - return f"{k.name} - {k.source}" - if self.elf_sha and (self.version not in (None, "1.0") or local_build(self.elf_sha)): - local = local_build(self.elf_sha) - built = self.built or (local[1] if local else None) - text = f"v{self.version or (local[0] if local else '?')} (build {self.elf_sha[:8]}" - text += f", compiled {built})" if built else ")" - if self.hw: - text += f", hardware {self.hw}" - if local: - text += f" - same as firmware/{local[2]}" - return text - if self.elf_sha: - built = f", built {self.built}" if self.built else "" - return f"Unknown build {self.elf_sha[:9]}{built} (probably updated over OTA)" - if "legacy_v1" in self.evidence: - return "Probably v1.0 factory image (press Reset & capture to confirm)" - return "Not identified yet - press Reset & capture" +"""Firmware identification from the boot log. + +Up to firmware 1.0 the app version string was "1.0" in every build, so +builds are told apart by the ELF SHA256 prefix printed by app_init. Boxes +receive new firmware over OTA; an unknown SHA usually means a box has already +updated. + +Firmware 1.0.16+ prints "KOTA: boot fw=1.0.16 build=4f10989e ..." (version +unique per build, first 8 hex digits of the ELF SHA256). Builds found in the +tool's firmware/ folder are registered with `register_local_build` so the +box's build can be shown with its compile date. +""" + +from __future__ import annotations + +import re +from dataclasses import dataclass, field + + +@dataclass(frozen=True) +class KnownFirmware: + name: str + source: str + sd_wifi: bool # reads SSID/PASSWORD from ENV.TXT + setup_portal: bool # short press -> Wi-Fi setup portal + + +KNOWN: dict[str, KnownFirmware] = { + "45fa9d5b9": KnownFirmware( + name="v1.0 (built Jun 29 2026)", + source="factory image new_system.bin", + sd_wifi=False, + setup_portal=False, + ), + "c94ba0e9b": KnownFirmware( + name="v1.0 source build (Sep 26 2026)", + source="developer build, built by Krzysztof Kallas", + sd_wifi=True, + setup_portal=True, + ), +} + +# elf sha (full or prefix) -> (version, compile time, file name) of builds in firmware/ +LOCAL_BUILDS: dict[str, tuple[str, str, str]] = {} + + +def register_local_build(elf_sha: str, version: str, built: str, file_name: str) -> None: + LOCAL_BUILDS[elf_sha.lower()] = (version, built, file_name) + + +def same_build(a: str | None, b: str | None) -> bool: + """ELF SHA prefixes of different lengths (8 from KOTA: boot, 9+ from + app_init, 64 from an image) name the same build if they agree.""" + if not a or not b: + return False + n = min(len(a), len(b)) + return n >= 6 and a[:n].lower() == b[:n].lower() + + +def local_build(elf_sha: str | None) -> tuple[str, str, str] | None: + return next((v for sha, v in LOCAL_BUILDS.items() if same_build(sha, elf_sha)), None) + + +_ELF = re.compile(r"app_init: ELF file SHA256:\s+(?P[0-9a-f]{6,})") +_VER = re.compile(r"app_init: App version:\s+(?P\S+)") +_BUILT = re.compile(r"app_init: Compile time:\s+(?P.+)$") +_IDF = re.compile(r"app_init: ESP-IDF:\s+(?P\S+)") +# Evidence of features, for builds not in KNOWN. +_FEATURE_SD_WIFI = re.compile(r"WIFI_CREDENTIALS: ") +_FEATURE_PORTAL = re.compile(r"WIFI_PROVISIONING: |Short press: ") +# Deployed v1.0 logs the SD mount under the INFLUX_BUFFER tag. +_LEGACY_SD_TAG = re.compile(r"INFLUX_BUFFER: SD card mounted") + + +@dataclass +class FirmwareInfo: + elf_sha: str | None = None + version: str | None = None + built: str | None = None + idf: str | None = None + hw: str | None = None + evidence: set[str] = field(default_factory=set) + + def observe_kota(self, fields: dict[str, str]) -> None: + """'KOTA: boot fw=1.0.16 build=4f10989e hw=2 ...'""" + if fields.get("fw"): + self.version = fields["fw"] + build = fields.get("build", "") + # app_init's longer prefix is kept if it agrees. + if build and not same_build(self.elf_sha, build): + self.elf_sha = build.lower() + if fields.get("hw"): + self.hw = fields["hw"] + self.evidence.add("kota") # 1.0.16+: SD Wi-Fi and setup portal exist + + def observe(self, text: str) -> None: + if m := _ELF.search(text): + self.elf_sha = m.group("sha") + elif m := _VER.search(text): + self.version = m.group("v") + elif m := _BUILT.search(text): + self.built = m.group("t").strip() + elif m := _IDF.search(text): + self.idf = m.group("v") + if _FEATURE_SD_WIFI.search(text): + self.evidence.add("sd_wifi") + if _FEATURE_PORTAL.search(text): + self.evidence.add("setup_portal") + if _LEGACY_SD_TAG.search(text): + self.evidence.add("legacy_v1") + + @property + def known(self) -> KnownFirmware | None: + if not self.elf_sha: + return None + return next((k for sha, k in KNOWN.items() if same_build(self.elf_sha, sha)), None) + + @property + def supports_sd_wifi(self) -> bool | None: + """True/False when determinable, None when unknown (no boot seen).""" + if k := self.known: + return k.sd_wifi + if "sd_wifi" in self.evidence or "kota" in self.evidence: + return True + if "legacy_v1" in self.evidence: + return False + return None + + def describe(self) -> str: + if k := self.known: + return f"{k.name} - {k.source}" + if self.elf_sha and (self.version not in (None, "1.0") or local_build(self.elf_sha)): + local = local_build(self.elf_sha) + built = self.built or (local[1] if local else None) + text = f"v{self.version or (local[0] if local else '?')} (build {self.elf_sha[:8]}" + text += f", compiled {built})" if built else ")" + if self.hw: + text += f", hardware {self.hw}" + if local: + text += f" - same as firmware/{local[2]}" + return text + if self.elf_sha: + built = f", built {self.built}" if self.built else "" + return f"Unknown build {self.elf_sha[:9]}{built} (probably updated over OTA)" + if "legacy_v1" in self.evidence: + return "Probably v1.0 factory image (press Reset & capture to confirm)" + return "Not identified yet - press Reset & capture" diff --git a/kota_tech/diag/guides.py b/kota_tech/diag/guides.py index cad6b4b..6ae4839 100644 --- a/kota_tech/diag/guides.py +++ b/kota_tech/diag/guides.py @@ -1,401 +1,401 @@ -"""Guided troubleshooting: executes the decision trees in guides.toml. - -GUI-independent. The runner is fed the same LogLines as the diagnostics -engine, reads the engine's report to route and to skip questions the log -already answers, asks the host to perform tool actions (board reset), and -records every answer and conclusion in the engine so they appear in the -report and the session log. -""" - -from __future__ import annotations - -import re -import time -import tomllib -from collections.abc import Callable -from dataclasses import dataclass, field -from pathlib import Path - -from ..core.logline import LogLine -from .engine import LED, DiagnosticsEngine, Severity - -GUIDES_PATH = Path(__file__).with_name("guides.toml") -# LED answers a technician can give (engine colours + a reboot-loop pattern). -LED_ANSWERS = ["GREEN", "PURPLE", "RED", "BLUE", "WHITE", "YELLOW", "ORANGE", "CYAN", "OFF", "CYCLING"] -LED_ANSWER_LABELS = {**{k: v[0] for k, v in LED.items()}, - "CYCLING": "Keeps changing / goes dark every few seconds"} -STEP_TYPES = {"led", "question", "info", "wait", "route"} -END_SEVERITY = {"ok": None, "info": Severity.INFO, "warning": Severity.WARNING, - "error": Severity.ERROR, "critical": Severity.CRITICAL} - - -@dataclass(frozen=True) -class Option: - label: str - next: str - - -@dataclass(frozen=True) -class Step: - id: str - type: str - text: str = "" - options: tuple[Option, ...] = () - route: dict[str, str] = field(default_factory=dict) # led colour -> target - routes: dict[str, str] = field(default_factory=dict) # rule id -> target - default: str | None = None - next: str | None = None - tool_action: str | None = None - until: re.Pattern[str] | None = None - until_milestone: str | None = None - fail_on: re.Pattern[str] | None = None - timeout_s: float = 60.0 - pass_on_timeout: bool = False - on_ok: str | None = None - on_fail: str | None = None - on_timeout: str | None = None - skip_if_finding: str | None = None - skip_unless_feature: str | None = None - skip_to: str | None = None - show_qr: bool = False # show the Matter pairing QR next to the step text - - def targets(self) -> list[str]: - t = [o.next for o in self.options] + list(self.route.values()) + list(self.routes.values()) - t += [x for x in (self.default, self.next, self.on_ok, self.on_fail, self.on_timeout, self.skip_to) if x] - return t - - -@dataclass(frozen=True) -class End: - id: str - severity: str - title: str - text: str - actions: tuple[str, ...] = () - - -@dataclass(frozen=True) -class Guide: - id: str - title: str - summary: str - recommend: tuple[str, ...] - start: str - steps: dict[str, Step] - ends: dict[str, End] - - -def load_guides(path: Path = GUIDES_PATH) -> dict[str, Guide]: - data = tomllib.loads(path.read_text(encoding="utf-8")) - guides: dict[str, Guide] = {} - for g in data["guide"]: - steps = {} - for s in g.get("step", []): - steps[s["id"]] = Step( - id=s["id"], type=s["type"], text=s.get("text", ""), - options=tuple(Option(o["label"], o["next"]) for o in s.get("options", ())), - route=dict(s.get("route", {})), routes=dict(s.get("routes", {})), - default=s.get("default"), next=s.get("next"), tool_action=s.get("tool_action"), - until=re.compile(s["until"]) if "until" in s else None, - until_milestone=s.get("until_milestone"), - fail_on=re.compile(s["fail_on"]) if "fail_on" in s else None, - timeout_s=float(s.get("timeout_s", 60)), pass_on_timeout=bool(s.get("pass_on_timeout", False)), - on_ok=s.get("on_ok"), on_fail=s.get("on_fail"), on_timeout=s.get("on_timeout"), - skip_if_finding=s.get("skip_if_finding"), skip_unless_feature=s.get("skip_unless_feature"), - skip_to=s.get("skip_to"), show_qr=bool(s.get("show_qr", False)), - ) - ends = {e["id"]: End(e["id"], e["severity"], e["title"], e["text"], tuple(e.get("actions", ()))) - for e in g.get("end", [])} - guides[g["id"]] = Guide(g["id"], g["title"], g["summary"], tuple(g.get("recommend", ())), - g["start"], steps, ends) - _validate(guides) - return guides - - -def _validate(guides: dict[str, Guide]) -> None: - for g in guides.values(): - if g.start not in g.steps: - raise ValueError(f"guide {g.id}: start step {g.start!r} missing") - for s in g.steps.values(): - if s.type not in STEP_TYPES: - raise ValueError(f"{g.id}.{s.id}: unknown step type {s.type!r}") - if s.type == "led" and set(s.route) - set(LED_ANSWERS): - raise ValueError(f"{g.id}.{s.id}: unknown LED colours {set(s.route) - set(LED_ANSWERS)}") - if s.type == "wait" and not (s.until or s.until_milestone or s.pass_on_timeout): - raise ValueError(f"{g.id}.{s.id}: wait step needs until/until_milestone/pass_on_timeout") - if s.tool_action not in (None, "reset"): - raise ValueError(f"{g.id}.{s.id}: unknown tool_action {s.tool_action!r}") - for t in s.targets(): - kind, _, ref = t.partition(":") - if kind == "end" and ref not in g.ends: - raise ValueError(f"{g.id}.{s.id}: unknown end {ref!r}") - elif kind == "guide" and ref not in guides: - raise ValueError(f"{g.id}.{s.id}: unknown guide {ref!r}") - elif not ref and t not in g.steps: - raise ValueError(f"{g.id}.{s.id}: unknown step {t!r}") - for e in g.ends.values(): - if e.severity not in END_SEVERITY: - raise ValueError(f"{g.id}.end.{e.id}: unknown severity {e.severity!r}") - - -def recommended(guides: dict[str, Guide], engine: DiagnosticsEngine) -> list[Guide]: - """Guides matching the current root findings (in root order), then the LED.""" - report = engine.report() - keys: list[str] = [] - for rc in report.roots: - keys.append(rc.finding.rule_id) - keys += [c.rule_id for c in rc.consequences] - if report.led not in ("UNKNOWN",): - keys.append(f"led:{report.led}") - out: list[Guide] = [g for g in guides.values() if "always" in g.recommend] - for k in keys: - for g in guides.values(): - if k in g.recommend and g not in out: - out.append(g) - return out - - -@dataclass -class View: - """What the UI should show right now.""" - guide: Guide | None = None - step: Step | None = None - text: str = "" - options: list[tuple[str, str]] = field(default_factory=list) # (key, label) - waiting: bool = False - remaining_s: float = 0.0 - note: str = "" - end: End | None = None - can_back: bool = False - - -class GuideRunner: - def __init__(self, engine: DiagnosticsEngine, guides: dict[str, Guide] | None = None, - on_action: Callable[[str], bool] | None = None, - on_log: Callable[[str], None] | None = None, - clock: Callable[[], float] = time.monotonic): - self.engine = engine - self.guides = guides if guides is not None else load_guides() - self._on_action = on_action or (lambda _a: False) - self._on_log = on_log or (lambda _t: None) - self._clock = clock - self.cancel() - - # ------------------------------------------------------------ control - def cancel(self) -> None: - self.guide: Guide | None = None - self.step: Step | None = None - self.end: End | None = None - self.note = "" - self._stack: list[tuple[str, str]] = [] # (guide id, step id) for Back - self._wait_started = 0.0 - self._wait_boot = 0 - self._wait_milestones: set[str] = set() - - def start(self, guide_id: str) -> None: - self.cancel() - self._enter_guide(guide_id) - - def answer(self, key: str) -> None: - """Pick an option: LED colour name for led steps, option index otherwise.""" - s = self.step - if s is None: - return - if s.type == "led": - label = LED_ANSWER_LABELS.get(key, key) - self._record(s, label) - self._check_led(key) - target = s.route.get(key) - elif s.type == "question": - opt = s.options[int(key)] - self._record(s, opt.label) - target = opt.next - else: - return - if target: - self._push() - self._go(target) - - def proceed(self) -> None: - """Continue on info steps; 'skip waiting' on wait steps.""" - s = self.step - if s is None: - return - if s.type == "info" and s.next: - self._push() - self._go(s.next) - elif s.type == "wait": - self._record(s, "technician skipped the wait") - self._finish_wait("timeout") - - def back(self) -> None: - # Never step back into a wait: re-entering it could restart the box. - while self._stack: - gid, sid = self._stack.pop() - if self.guides[gid].steps[sid].type != "wait": - self.guide, self.end, self.note = self.guides[gid], None, "" - self._enter_step(sid) - return - - # --------------------------------------------------------------- feed - def feed(self, line: LogLine) -> None: - s = self.step - if s is None or s.type != "wait": - return - if s.fail_on and s.fail_on.search(line.text): - self._record(s, f"log: {line.text}") - self._finish_wait("fail") - elif s.until and s.until.search(line.text): - self._record(s, f"log: {line.text}") - self._finish_wait("ok") - elif s.until_milestone and self._milestone_new(s.until_milestone): - self._record(s, f"milestone reached: {s.until_milestone}") - self._finish_wait("ok") - - def tick(self) -> None: - s = self.step - if s is not None and s.type == "wait" and self._clock() - self._wait_started >= s.timeout_s: - self._finish_wait("ok" if s.pass_on_timeout else "timeout") - - # --------------------------------------------------------------- view - def view(self) -> View: - v = View(guide=self.guide, step=self.step, end=self.end, note=self.note, can_back=bool(self._stack)) - s = self.step - if s is None: - return v - v.text = self._render(s.text) - if s.type == "led": - v.options = [(k, LED_ANSWER_LABELS[k]) for k in LED_ANSWERS if k in s.route] - elif s.type == "question": - v.options = [(str(i), o.label) for i, o in enumerate(s.options)] - elif s.type == "wait": - v.waiting = True - v.remaining_s = max(0.0, s.timeout_s - (self._clock() - self._wait_started)) - return v - - # ---------------------------------------------------------- internals - def _push(self) -> None: - if self.guide and self.step: - self._stack.append((self.guide.id, self.step.id)) - - def _enter_guide(self, guide_id: str) -> None: - self.guide = self.guides[guide_id] - self._on_log(f"technician: started guide '{self.guide.title}'") - self._enter_step(self.guide.start) - - def _go(self, target: str) -> None: - kind, _, ref = target.partition(":") - if kind == "end" and ref: - self._conclude(self.guide.ends[ref]) - elif kind == "guide" and ref: - self._enter_guide(ref) - else: - self._enter_step(target) - - def _enter_step(self, step_id: str) -> None: - s = self.guide.steps[step_id] - self.step, self.note = s, "" - if s.skip_if_finding and self._finding_active(s.skip_if_finding): - self._auto(s, f"skipped - log already shows {s.skip_if_finding}", s.skip_to) - return - if s.skip_unless_feature and not self._feature(s.skip_unless_feature): - self._auto(s, f"skipped - firmware has no {s.skip_unless_feature}", s.skip_to) - return - if s.type == "route": - target = self._route(s) - self._auto(s, f"routed by log findings -> {target}", target) - return - if s.type == "wait": - self._wait_started = self._clock() - self._wait_boot = self.engine.boot.index - self._wait_milestones = set(self.engine.milestones) - if s.tool_action and not self._on_action(s.tool_action): - self.note = "Could not restart the box from the tool - connect to it first, or switch it off and on." - # A milestone that is already reached satisfies the wait at once - # unless the step first restarts the box. - if s.until_milestone and not s.tool_action and s.until_milestone in self.engine.milestones: - self._record(s, f"already reached: {s.until_milestone}") - self._finish_wait("ok") - - def _auto(self, s: Step, why: str, target: str | None) -> None: - self._on_log(f"technician guide: {s.id}: {why}") - if target: - self._go(target) - - def _finish_wait(self, outcome: str) -> None: - s = self.step - target = {"ok": s.on_ok, "fail": s.on_fail or s.on_timeout, "timeout": s.on_timeout or s.on_fail}[outcome] - if target is None: - return - self._push() - self._go(target) - - def _milestone_new(self, mid: str) -> bool: - if mid not in self.engine.milestones: - return False - return self.engine.boot.index != self._wait_boot or mid not in self._wait_milestones - - def _route(self, s: Step) -> str: - report = self.engine.report() - for rc in report.roots: - for f in (rc.finding, *rc.consequences): - if f.rule_id in s.routes: - return s.routes[f.rule_id] - return s.default or "end:" + next(iter(self.guide.ends)) - - def _check_led(self, seen: str) -> None: - expected = self.engine.led - if expected in ("UNKNOWN", "OFF") or seen == "CYCLING" or seen == expected: - return - exp_label, see_label = LED[expected][0], LED_ANSWER_LABELS[seen] - self.engine.add_confirmed( - "tech.led_mismatch", Severity.WARNING, - f"LED shows {see_label}, but the log implies {exp_label}", - f"The firmware state in the log corresponds to {exp_label}, while the technician sees {see_label}. " - "Either the LED just changed (check again in a few seconds), the LED/its wiring is faulty, " - "or the box runs different firmware. Trust the log findings.", - ("Look at the LED again for 10 seconds.", "If it stays different: note it for repair (LED board).")) - - def _conclude(self, end: End) -> None: - self.step, self.end = None, end - self._on_log(f"technician guide result: {end.title}") - sev = END_SEVERITY[end.severity] - self.engine.note_technician(f"{self.guide.title}: {end.title}") - if sev is not None: - self.engine.add_confirmed(f"tech.{self.guide.id}.{end.id}", sev, end.title, end.text, end.actions) - - def _record(self, s: Step, answer: str) -> None: - q = self._render(s.text) - self.engine.note_technician(f"{q} -> {answer}") - self._on_log(f"technician: {q[:80]} -> {answer}") - - def _finding_active(self, rule_id: str) -> bool: - return any(f.rule_id == rule_id and f.active for f in self.engine.findings.values()) - - def _feature(self, name: str) -> bool: - fw = self.engine.firmware - if name == "sd_wifi": - return fw.supports_sd_wifi is not False - if name == "setup_portal": - if fw.known is not None: - return fw.known.setup_portal - if "setup_portal" in fw.evidence: - return True - return "legacy_v1" not in fw.evidence # unknown firmware: ask - return False - - def _render(self, text: str) -> str: - report = self.engine.report() - sensors = sorted({f.title.removesuffix(" failed to start") for f in self.engine.findings.values() - if f.rule_id == "sensor.init_failed" and f.active}) - ctx = { - "expected_led": LED[report.led][0], - "failing_sensors": ", ".join(sensors) or "see the Diagnosis tab", - "top_finding": report.roots[0].finding.title if report.roots else "none", - } - return text.format_map(_Fmt(ctx)) - - -class _Fmt(dict): - def __missing__(self, key: str) -> str: - return "{" + key + "}" +"""Guided troubleshooting: executes the decision trees in guides.toml. + +GUI-independent. The runner is fed the same LogLines as the diagnostics +engine, reads the engine's report to route and to skip questions the log +already answers, asks the host to perform tool actions (board reset), and +records every answer and conclusion in the engine so they appear in the +report and the session log. +""" + +from __future__ import annotations + +import re +import time +import tomllib +from collections.abc import Callable +from dataclasses import dataclass, field +from pathlib import Path + +from ..core.logline import LogLine +from .engine import LED, DiagnosticsEngine, Severity + +GUIDES_PATH = Path(__file__).with_name("guides.toml") +# LED answers a technician can give (engine colours + a reboot-loop pattern). +LED_ANSWERS = ["GREEN", "PURPLE", "RED", "BLUE", "WHITE", "YELLOW", "ORANGE", "CYAN", "OFF", "CYCLING"] +LED_ANSWER_LABELS = {**{k: v[0] for k, v in LED.items()}, + "CYCLING": "Keeps changing / goes dark every few seconds"} +STEP_TYPES = {"led", "question", "info", "wait", "route"} +END_SEVERITY = {"ok": None, "info": Severity.INFO, "warning": Severity.WARNING, + "error": Severity.ERROR, "critical": Severity.CRITICAL} + + +@dataclass(frozen=True) +class Option: + label: str + next: str + + +@dataclass(frozen=True) +class Step: + id: str + type: str + text: str = "" + options: tuple[Option, ...] = () + route: dict[str, str] = field(default_factory=dict) # led colour -> target + routes: dict[str, str] = field(default_factory=dict) # rule id -> target + default: str | None = None + next: str | None = None + tool_action: str | None = None + until: re.Pattern[str] | None = None + until_milestone: str | None = None + fail_on: re.Pattern[str] | None = None + timeout_s: float = 60.0 + pass_on_timeout: bool = False + on_ok: str | None = None + on_fail: str | None = None + on_timeout: str | None = None + skip_if_finding: str | None = None + skip_unless_feature: str | None = None + skip_to: str | None = None + show_qr: bool = False # show the Matter pairing QR next to the step text + + def targets(self) -> list[str]: + t = [o.next for o in self.options] + list(self.route.values()) + list(self.routes.values()) + t += [x for x in (self.default, self.next, self.on_ok, self.on_fail, self.on_timeout, self.skip_to) if x] + return t + + +@dataclass(frozen=True) +class End: + id: str + severity: str + title: str + text: str + actions: tuple[str, ...] = () + + +@dataclass(frozen=True) +class Guide: + id: str + title: str + summary: str + recommend: tuple[str, ...] + start: str + steps: dict[str, Step] + ends: dict[str, End] + + +def load_guides(path: Path = GUIDES_PATH) -> dict[str, Guide]: + data = tomllib.loads(path.read_text(encoding="utf-8")) + guides: dict[str, Guide] = {} + for g in data["guide"]: + steps = {} + for s in g.get("step", []): + steps[s["id"]] = Step( + id=s["id"], type=s["type"], text=s.get("text", ""), + options=tuple(Option(o["label"], o["next"]) for o in s.get("options", ())), + route=dict(s.get("route", {})), routes=dict(s.get("routes", {})), + default=s.get("default"), next=s.get("next"), tool_action=s.get("tool_action"), + until=re.compile(s["until"]) if "until" in s else None, + until_milestone=s.get("until_milestone"), + fail_on=re.compile(s["fail_on"]) if "fail_on" in s else None, + timeout_s=float(s.get("timeout_s", 60)), pass_on_timeout=bool(s.get("pass_on_timeout", False)), + on_ok=s.get("on_ok"), on_fail=s.get("on_fail"), on_timeout=s.get("on_timeout"), + skip_if_finding=s.get("skip_if_finding"), skip_unless_feature=s.get("skip_unless_feature"), + skip_to=s.get("skip_to"), show_qr=bool(s.get("show_qr", False)), + ) + ends = {e["id"]: End(e["id"], e["severity"], e["title"], e["text"], tuple(e.get("actions", ()))) + for e in g.get("end", [])} + guides[g["id"]] = Guide(g["id"], g["title"], g["summary"], tuple(g.get("recommend", ())), + g["start"], steps, ends) + _validate(guides) + return guides + + +def _validate(guides: dict[str, Guide]) -> None: + for g in guides.values(): + if g.start not in g.steps: + raise ValueError(f"guide {g.id}: start step {g.start!r} missing") + for s in g.steps.values(): + if s.type not in STEP_TYPES: + raise ValueError(f"{g.id}.{s.id}: unknown step type {s.type!r}") + if s.type == "led" and set(s.route) - set(LED_ANSWERS): + raise ValueError(f"{g.id}.{s.id}: unknown LED colours {set(s.route) - set(LED_ANSWERS)}") + if s.type == "wait" and not (s.until or s.until_milestone or s.pass_on_timeout): + raise ValueError(f"{g.id}.{s.id}: wait step needs until/until_milestone/pass_on_timeout") + if s.tool_action not in (None, "reset"): + raise ValueError(f"{g.id}.{s.id}: unknown tool_action {s.tool_action!r}") + for t in s.targets(): + kind, _, ref = t.partition(":") + if kind == "end" and ref not in g.ends: + raise ValueError(f"{g.id}.{s.id}: unknown end {ref!r}") + elif kind == "guide" and ref not in guides: + raise ValueError(f"{g.id}.{s.id}: unknown guide {ref!r}") + elif not ref and t not in g.steps: + raise ValueError(f"{g.id}.{s.id}: unknown step {t!r}") + for e in g.ends.values(): + if e.severity not in END_SEVERITY: + raise ValueError(f"{g.id}.end.{e.id}: unknown severity {e.severity!r}") + + +def recommended(guides: dict[str, Guide], engine: DiagnosticsEngine) -> list[Guide]: + """Guides matching the current root findings (in root order), then the LED.""" + report = engine.report() + keys: list[str] = [] + for rc in report.roots: + keys.append(rc.finding.rule_id) + keys += [c.rule_id for c in rc.consequences] + if report.led not in ("UNKNOWN",): + keys.append(f"led:{report.led}") + out: list[Guide] = [g for g in guides.values() if "always" in g.recommend] + for k in keys: + for g in guides.values(): + if k in g.recommend and g not in out: + out.append(g) + return out + + +@dataclass +class View: + """What the UI should show right now.""" + guide: Guide | None = None + step: Step | None = None + text: str = "" + options: list[tuple[str, str]] = field(default_factory=list) # (key, label) + waiting: bool = False + remaining_s: float = 0.0 + note: str = "" + end: End | None = None + can_back: bool = False + + +class GuideRunner: + def __init__(self, engine: DiagnosticsEngine, guides: dict[str, Guide] | None = None, + on_action: Callable[[str], bool] | None = None, + on_log: Callable[[str], None] | None = None, + clock: Callable[[], float] = time.monotonic): + self.engine = engine + self.guides = guides if guides is not None else load_guides() + self._on_action = on_action or (lambda _a: False) + self._on_log = on_log or (lambda _t: None) + self._clock = clock + self.cancel() + + # ------------------------------------------------------------ control + def cancel(self) -> None: + self.guide: Guide | None = None + self.step: Step | None = None + self.end: End | None = None + self.note = "" + self._stack: list[tuple[str, str]] = [] # (guide id, step id) for Back + self._wait_started = 0.0 + self._wait_boot = 0 + self._wait_milestones: set[str] = set() + + def start(self, guide_id: str) -> None: + self.cancel() + self._enter_guide(guide_id) + + def answer(self, key: str) -> None: + """Pick an option: LED colour name for led steps, option index otherwise.""" + s = self.step + if s is None: + return + if s.type == "led": + label = LED_ANSWER_LABELS.get(key, key) + self._record(s, label) + self._check_led(key) + target = s.route.get(key) + elif s.type == "question": + opt = s.options[int(key)] + self._record(s, opt.label) + target = opt.next + else: + return + if target: + self._push() + self._go(target) + + def proceed(self) -> None: + """Continue on info steps; 'skip waiting' on wait steps.""" + s = self.step + if s is None: + return + if s.type == "info" and s.next: + self._push() + self._go(s.next) + elif s.type == "wait": + self._record(s, "technician skipped the wait") + self._finish_wait("timeout") + + def back(self) -> None: + # Never step back into a wait: re-entering it could restart the box. + while self._stack: + gid, sid = self._stack.pop() + if self.guides[gid].steps[sid].type != "wait": + self.guide, self.end, self.note = self.guides[gid], None, "" + self._enter_step(sid) + return + + # --------------------------------------------------------------- feed + def feed(self, line: LogLine) -> None: + s = self.step + if s is None or s.type != "wait": + return + if s.fail_on and s.fail_on.search(line.text): + self._record(s, f"log: {line.text}") + self._finish_wait("fail") + elif s.until and s.until.search(line.text): + self._record(s, f"log: {line.text}") + self._finish_wait("ok") + elif s.until_milestone and self._milestone_new(s.until_milestone): + self._record(s, f"milestone reached: {s.until_milestone}") + self._finish_wait("ok") + + def tick(self) -> None: + s = self.step + if s is not None and s.type == "wait" and self._clock() - self._wait_started >= s.timeout_s: + self._finish_wait("ok" if s.pass_on_timeout else "timeout") + + # --------------------------------------------------------------- view + def view(self) -> View: + v = View(guide=self.guide, step=self.step, end=self.end, note=self.note, can_back=bool(self._stack)) + s = self.step + if s is None: + return v + v.text = self._render(s.text) + if s.type == "led": + v.options = [(k, LED_ANSWER_LABELS[k]) for k in LED_ANSWERS if k in s.route] + elif s.type == "question": + v.options = [(str(i), o.label) for i, o in enumerate(s.options)] + elif s.type == "wait": + v.waiting = True + v.remaining_s = max(0.0, s.timeout_s - (self._clock() - self._wait_started)) + return v + + # ---------------------------------------------------------- internals + def _push(self) -> None: + if self.guide and self.step: + self._stack.append((self.guide.id, self.step.id)) + + def _enter_guide(self, guide_id: str) -> None: + self.guide = self.guides[guide_id] + self._on_log(f"technician: started guide '{self.guide.title}'") + self._enter_step(self.guide.start) + + def _go(self, target: str) -> None: + kind, _, ref = target.partition(":") + if kind == "end" and ref: + self._conclude(self.guide.ends[ref]) + elif kind == "guide" and ref: + self._enter_guide(ref) + else: + self._enter_step(target) + + def _enter_step(self, step_id: str) -> None: + s = self.guide.steps[step_id] + self.step, self.note = s, "" + if s.skip_if_finding and self._finding_active(s.skip_if_finding): + self._auto(s, f"skipped - log already shows {s.skip_if_finding}", s.skip_to) + return + if s.skip_unless_feature and not self._feature(s.skip_unless_feature): + self._auto(s, f"skipped - firmware has no {s.skip_unless_feature}", s.skip_to) + return + if s.type == "route": + target = self._route(s) + self._auto(s, f"routed by log findings -> {target}", target) + return + if s.type == "wait": + self._wait_started = self._clock() + self._wait_boot = self.engine.boot.index + self._wait_milestones = set(self.engine.milestones) + if s.tool_action and not self._on_action(s.tool_action): + self.note = "Could not restart the box from the tool - connect to it first, or switch it off and on." + # A milestone that is already reached satisfies the wait at once + # unless the step first restarts the box. + if s.until_milestone and not s.tool_action and s.until_milestone in self.engine.milestones: + self._record(s, f"already reached: {s.until_milestone}") + self._finish_wait("ok") + + def _auto(self, s: Step, why: str, target: str | None) -> None: + self._on_log(f"technician guide: {s.id}: {why}") + if target: + self._go(target) + + def _finish_wait(self, outcome: str) -> None: + s = self.step + target = {"ok": s.on_ok, "fail": s.on_fail or s.on_timeout, "timeout": s.on_timeout or s.on_fail}[outcome] + if target is None: + return + self._push() + self._go(target) + + def _milestone_new(self, mid: str) -> bool: + if mid not in self.engine.milestones: + return False + return self.engine.boot.index != self._wait_boot or mid not in self._wait_milestones + + def _route(self, s: Step) -> str: + report = self.engine.report() + for rc in report.roots: + for f in (rc.finding, *rc.consequences): + if f.rule_id in s.routes: + return s.routes[f.rule_id] + return s.default or "end:" + next(iter(self.guide.ends)) + + def _check_led(self, seen: str) -> None: + expected = self.engine.led + if expected in ("UNKNOWN", "OFF") or seen == "CYCLING" or seen == expected: + return + exp_label, see_label = LED[expected][0], LED_ANSWER_LABELS[seen] + self.engine.add_confirmed( + "tech.led_mismatch", Severity.WARNING, + f"LED shows {see_label}, but the log implies {exp_label}", + f"The firmware state in the log corresponds to {exp_label}, while the technician sees {see_label}. " + "Either the LED just changed (check again in a few seconds), the LED/its wiring is faulty, " + "or the box runs different firmware. Trust the log findings.", + ("Look at the LED again for 10 seconds.", "If it stays different: note it for repair (LED board).")) + + def _conclude(self, end: End) -> None: + self.step, self.end = None, end + self._on_log(f"technician guide result: {end.title}") + sev = END_SEVERITY[end.severity] + self.engine.note_technician(f"{self.guide.title}: {end.title}") + if sev is not None: + self.engine.add_confirmed(f"tech.{self.guide.id}.{end.id}", sev, end.title, end.text, end.actions) + + def _record(self, s: Step, answer: str) -> None: + q = self._render(s.text) + self.engine.note_technician(f"{q} -> {answer}") + self._on_log(f"technician: {q[:80]} -> {answer}") + + def _finding_active(self, rule_id: str) -> bool: + return any(f.rule_id == rule_id and f.active for f in self.engine.findings.values()) + + def _feature(self, name: str) -> bool: + fw = self.engine.firmware + if name == "sd_wifi": + return fw.supports_sd_wifi is not False + if name == "setup_portal": + if fw.known is not None: + return fw.known.setup_portal + if "setup_portal" in fw.evidence: + return True + return "legacy_v1" not in fw.evidence # unknown firmware: ask + return False + + def _render(self, text: str) -> str: + report = self.engine.report() + sensors = sorted({f.title.removesuffix(" failed to start") for f in self.engine.findings.values() + if f.rule_id == "sensor.init_failed" and f.active}) + ctx = { + "expected_led": LED[report.led][0], + "failing_sensors": ", ".join(sensors) or "see the Diagnosis tab", + "top_finding": report.roots[0].finding.title if report.roots else "none", + } + return text.format_map(_Fmt(ctx)) + + +class _Fmt(dict): + def __missing__(self, key: str) -> str: + return "{" + key + "}" diff --git a/kota_tech/diag/guides.toml b/kota_tech/diag/guides.toml index 4237587..0189ce6 100644 --- a/kota_tech/diag/guides.toml +++ b/kota_tech/diag/guides.toml @@ -1,740 +1,740 @@ -# Guided troubleshooting: decision trees that combine technician answers -# with live log evidence (diag.guides.GuideRunner executes them). -# -# Guide: id, title, summary, recommend (rule ids / "led:" / "always"), -# start (first step id), [[guide.step]], [[guide.end]] -# Step types: -# led technician picks the LED colour; `route` maps colour -> target. -# Colours: GREEN PURPLE RED BLUE YELLOW ORANGE WHITE OFF CYCLING -# The answer is compared with the colour the firmware log implies. -# question `options` = [{label, next}] -# info instruction + Continue (`next`) -# wait optional `tool_action` ("reset"), then watch the log: -# `until` (regex) / `until_milestone`, `fail_on` (regex), -# `timeout_s`; go to on_ok / on_fail / on_timeout. -# `pass_on_timeout = true`: reaching the timeout without fail_on -# counts as ok (used for "watch for N seconds"). -# route automatic: first current root finding whose rule id is in -# `routes` decides; otherwise `default`. -# Targets: "", "end:", "guide:". -# Optional on any step: skip_if_finding / skip_unless_feature (+ skip_to). -# Text placeholders: {expected_led} {failing_sensors} {top_finding} -# End severity: ok | info | warning | error | critical - -# ================================================================= quick -[[guide]] -id = "quick" -title = "Quick check - start here" -summary = "Tell the tool what the LED shows; it picks the right checks." -recommend = ["always"] -start = "led" - -[[guide.step]] -id = "led" -type = "led" -text = "Look at the RGB status LED on the box (no need to open it). Which colour is it?" -route = { GREEN = "heartbeat", PURPLE = "guide:network", RED = "guide:sensors", BLUE = "guide:rtc_i2c", WHITE = "guide:white", YELLOW = "guide:update", ORANGE = "guide:update", CYAN = "guide:network", OFF = "guide:power", CYCLING = "guide:reboot_loop" } - -[[guide.step]] -id = "heartbeat" -type = "question" -text = "Is the small LED next to the RGB LED blinking about once per second?" -options = [ - { label = "Yes, blinking", next = "green_route" }, - { label = "No - steady on or off", next = "end:hung" }, -] - -[[guide.step]] -id = "green_route" -type = "route" -routes = { "rtc.clock_not_running" = "guide:clock", "mic.silent" = "guide:mic", "mic.disabled" = "guide:mic", "matter.not_commissioned" = "guide:network", "net.dns_fail" = "guide:network" } -default = "end:healthy" - -[[guide.end]] -id = "healthy" -severity = "ok" -title = "Box looks healthy" -text = "Green LED, firmware running and no problems in the log." -actions = ["If the customer still reports missing data, keep the tool connected for 5 minutes and check that 'Data uploaded to InfluxDB' gets ticked."] - -[[guide.end]] -id = "hung" -severity = "error" -title = "Firmware is not running normally" -text = "The heartbeat LED should blink once per second whenever the firmware runs. A steady heartbeat LED means the firmware has frozen." -actions = [ - "Switch the box off and on with the power switch.", - "If it happens again: save the session log and re-flash the official firmware (Flash firmware tab, App only).", -] - -# =============================================================== network -[[guide]] -id = "network" -title = "Purple LED / no Internet" -summary = "Pairing in Matter, site Wi-Fi and Internet checks, with live confirmation." -recommend = ["led:PURPLE", "led:CYAN", "matter.not_commissioned", "net.dns_fail", "influx.buffer_fail", "ota.registration_failed", - "status.conn", "status.conn.1", "kota.wifi_down", "kota.no_upload", "kota.weak_wifi", "kota.ota_unregistered"] -start = "paired" - -[[guide.step]] -id = "paired" -type = "question" -skip_if_finding = "matter.not_commissioned" -skip_to = "pair_intro" -text = "Was this box already added to the customer's Matter app (Apple Home, Google Home, ...) at this site?" -options = [ - { label = "Yes, it was paired before", next = "internet" }, - { label = "No / not sure", next = "pair_intro" }, -] - -[[guide.step]] -id = "internet" -type = "question" -text = "Does the site Wi-Fi have working Internet? Check with a phone connected to the same Wi-Fi (open any website)." -options = [ - { label = "Yes, Internet works", next = "changed" }, - { label = "No Internet on the site Wi-Fi", next = "end:site_offline" }, -] - -[[guide.step]] -id = "changed" -type = "question" -text = "Was the Wi-Fi name or password changed, or the router replaced, since the box was installed?" -options = [ - { label = "Yes / possibly", next = "end:repair_needed" }, - { label = "No", next = "end:offline_unknown" }, -] - -[[guide.step]] -id = "pair_intro" -type = "info" -text = "The box is not on any Wi-Fi yet: it must be paired in Matter. You need: a phone with Bluetooth on, the customer's Matter app, the 2.4 GHz Wi-Fi password, and the QR sticker of this box. Keep the tool connected - it follows the pairing live." -next = "pair_start" - -[[guide.step]] -id = "pair_start" -type = "wait" -text = "In the Matter app choose 'Add device' and scan the QR code shown here (the same as the sticker), or type the 11-digit manual code. Keep the phone within 1 m of the box." -show_qr = true -until = 'MAIN: Commissioning session started' -timeout_s = 300 -on_ok = "pair_wifi" -on_timeout = "not_found" - -[[guide.step]] -id = "not_found" -type = "question" -text = "The box has not seen the phone connect yet. What does the app show?" -options = [ - { label = "Still searching / let me try again", next = "pair_start" }, - { label = "'Device not found' or similar", next = "end:ble_not_found" }, -] - -[[guide.step]] -id = "pair_wifi" -type = "wait" -text = "The phone is connected to the box. Follow the app: choose the 2.4 GHz Wi-Fi network and enter its password." -until = 'MAIN: Commissioning complete|MAIN: Fabric is committed' -fail_on = 'Commissioning failed, fail safe timer expired' -timeout_s = 300 -on_ok = "pair_time" -on_fail = "end:pair_failed" -on_timeout = "end:pair_failed" - -[[guide.step]] -id = "pair_time" -type = "wait" -text = "Paired! The box is joining the Wi-Fi and getting the time from the Internet..." -until_milestone = "time_synced" -timeout_s = 120 -on_ok = "pair_upload" -on_timeout = "end:joined_no_internet" - -[[guide.step]] -id = "pair_upload" -type = "wait" -text = "Online. Waiting for the first measurements to reach the database (up to 4 minutes)..." -until = 'INFLUX: HTTP request performed, status code: 2\d\d' -fail_on = 'INFLUX: Failed to perform HTTP request: (401|403|404)' -timeout_s = 240 -on_ok = "end:online" -on_fail = "end:influx_rejected" -on_timeout = "end:no_upload" - -[[guide.end]] -id = "online" -severity = "ok" -title = "Box is paired and uploading data" -text = "Pairing, Internet time and the first upload all worked." -actions = [ - "The box may now download a firmware update (yellow, then orange LED, a few minutes). Do not switch it off.", - "It should end green.", -] - -[[guide.end]] -id = "site_offline" -severity = "warning" -title = "Site network has no Internet" -text = "The box cannot work until the site Wi-Fi reaches the Internet. This is not a box fault." -actions = ["Ask the customer to fix the Internet connection, then power-cycle the box."] - -[[guide.end]] -id = "repair_needed" -severity = "warning" -title = "Box must be paired again" -text = "The box still remembers the old Wi-Fi. It needs a factory reset and new pairing." -actions = [ - "Hold the Matter button for 5 seconds (LED turns white) - this erases the old pairing.", - "Remove the box from the customer's Matter app, then run this guide again and choose 'No / not sure'.", -] - -[[guide.end]] -id = "offline_unknown" -severity = "error" -title = "Box is paired but cannot reach the Internet - cause unclear" -text = "The network works for other devices, but not for this box." -actions = [ - "Check the router does not block new devices (guest isolation, MAC filter). Box MAC is shown in the port list.", - "Move the box closer to the router to rule out weak signal.", - "If nothing helps: factory reset (hold the Matter button 5 s) and pair again.", -] - -[[guide.end]] -id = "ble_not_found" -severity = "error" -title = "Phone cannot find the box over Bluetooth" -text = "The box only accepts pairing for a limited time after start-up." -actions = [ - "Switch the box off and on to reopen the pairing window, then try again within 10 minutes.", - "Check Bluetooth and location permission are on for the Matter app.", - "If the box was paired to another app before: hold the Matter button 5 s (factory reset) first.", -] - -[[guide.end]] -id = "pair_failed" -severity = "error" -title = "Pairing did not finish" -text = "The phone connected but the box did not join the Wi-Fi." -actions = [ - "Use a 2.4 GHz network (many routers have a separate 2.4 GHz name).", - "Re-enter the Wi-Fi password carefully.", - "Switch the box off and on, then pair again.", -] - -[[guide.end]] -id = "joined_no_internet" -severity = "error" -title = "Box joined the Wi-Fi but has no Internet" -text = "Pairing worked, but the box cannot reach time or data servers." -actions = [ - "Check the network allows outgoing connections (HTTPS, NTP) for new devices.", - "Try a phone hotspot to rule out the site network.", -] - -[[guide.end]] -id = "influx_rejected" -severity = "critical" -title = "Database rejected the box (token / bucket)" -text = "The box is online but InfluxDB refused the upload. The SD card settings are wrong." -actions = ["Re-provision the SD card with the current ENV.TXT (SD card is inside the case)."] - -[[guide.end]] -id = "no_upload" -severity = "warning" -title = "Online, but no upload seen yet" -text = "Uploads happen every minute; the first one can be delayed while the air sensor warms up." -actions = ["Keep the tool connected a few more minutes and watch the Diagnosis tab."] - -# ================================================================= RTC / I2C -[[guide]] -id = "rtc_i2c" -title = "Blue LED - clock / I2C cable" -summary = "Re-seat the I2C cable and verify the start-up live." -recommend = ["led:BLUE", "rtc.init_failed", "i2c.errors", "kota.i2c_stuck", "kota.rtc_read_failed", "status.rtc.1", "status.rtc.2"] -start = "explain" - -[[guide.step]] -id = "explain" -type = "info" -text = "Blue means the clock chip did not answer. It is the first chip on the shared I2C cable between the boards, so a loose cable is the most common cause. This check needs the case opened." -next = "reseat" - -[[guide.step]] -id = "reseat" -type = "info" -text = "Switch the power OFF. Open the case. Unplug and firmly re-plug the I2C cable between the boards at both ends. Close the case and switch the power ON. Then press Continue." -next = "verify" - -[[guide.step]] -id = "verify" -type = "wait" -text = "Watching the start-up (up to 60 s)..." -until_milestone = "sensors_ready" -fail_on = 'MAIN: Failed to initialize RTC' -timeout_s = 60 -on_ok = "end:i2c_fixed" -on_fail = "damaged" -on_timeout = "damaged" - -[[guide.step]] -id = "damaged" -type = "question" -text = "The clock still does not answer. Is the I2C cable or its connector visibly damaged (pinched, broken wire, bent pin)?" -options = [ - { label = "Yes, damaged", next = "end:replace_cable" }, - { label = "No, looks fine", next = "end:rtc_module" }, -] - -[[guide.end]] -id = "i2c_fixed" -severity = "ok" -title = "Fixed - all sensors start again" -text = "After re-seating the cable the clock and all sensors started." -actions = ["Make sure the cable is routed without strain before closing the case."] - -[[guide.end]] -id = "replace_cable" -severity = "error" -title = "Replace the I2C cable" -text = "A damaged I2C cable stops the clock and all sensors." -actions = ["Replace the cable, then run this check again."] - -[[guide.end]] -id = "rtc_module" -severity = "error" -title = "Clock (RTC) module is probably faulty" -text = "The cable is fine but the clock chip does not answer." -actions = ["Replace the RTC module, or send the box for repair with the session log."] - -# ==================================================================== white -[[guide]] -id = "white" -title = "White LED - SD card / reset / setup mode" -summary = "Tell apart the three white states and check the SD card." -recommend = ["led:WHITE", "sd.card_fail", "sd.mount_fail", "sd.env_open_fail", "influx.env_missing", "influx.init_fail", - "kota.sd_unmounted", "status.sd", "status.portal", "status.reset"] -start = "button" - -[[guide.step]] -id = "button" -type = "question" -text = "Did anyone hold the Matter button (outside the case) for about 5 seconds just before the LED turned white?" -options = [ - { label = "Yes", next = "end:factory_reset" }, - { label = "No / don't know", next = "portal" }, -] - -[[guide.step]] -id = "portal" -type = "question" -skip_unless_feature = "setup_portal" -skip_to = "sd_reseat" -text = "On a phone, look at the Wi-Fi list: is there a network called 'KotaSensorBox-....'?" -options = [ - { label = "Yes", next = "end:setup_mode" }, - { label = "No", next = "sd_reseat" }, -] - -[[guide.step]] -id = "sd_reseat" -type = "info" -text = "The SD card is probably not readable. This needs the case opened: switch the power OFF, open the case, push the SD card in until it clicks (or remove and re-insert it). Close the case, switch the power ON, then press Continue." -next = "sd_verify" - -[[guide.step]] -id = "sd_verify" -type = "wait" -text = "Watching the SD card start-up (up to 60 s)..." -until = 'Read INFLUX_URL from \.env file' -fail_on = 'Failed to initialize the card|Failed to mount filesystem|Failed to read INFLUX_(URL|TOKEN)|Failed to open \.env file' -timeout_s = 60 -on_ok = "end:sd_fixed" -on_fail = "end:sd_reprovision" -on_timeout = "end:sd_reprovision" - -[[guide.end]] -id = "factory_reset" -severity = "info" -title = "White = factory reset in progress" -text = "The box erased its Matter pairing and restarts." -actions = ["Pair it again in the Matter app (Purple LED guide)."] - -[[guide.end]] -id = "setup_mode" -severity = "info" -title = "White = Wi-Fi setup mode" -text = "The box is waiting for Wi-Fi settings on its own network." -actions = [ - "Connect a phone to KotaSensorBox-.... and open http://sensorbox.local/ to enter the Wi-Fi,", - "or short-press the Matter button (< 2 s) to leave setup mode.", -] - -[[guide.end]] -id = "sd_fixed" -severity = "ok" -title = "Fixed - SD card is read again" -text = "The box found the SD card and read ENV.TXT." -actions = ["Close the case and watch the Diagnosis tab for a minute."] - -[[guide.end]] -id = "sd_reprovision" -severity = "critical" -title = "SD card is unreadable or has wrong settings" -text = "Even after re-seating, the card or its ENV.TXT cannot be used." -actions = [ - "Re-provision the card with the tool (SD card tab) or use a new card.", - "The Diagnosis tab shows which step failed (card, file system or ENV.TXT keys).", -] - -# ================================================================== sensors -[[guide]] -id = "sensors" -title = "Red LED - sensor or hardware fault" -summary = "Find the failing sensor and verify after re-seating its cable." -recommend = ["led:RED", "sensor.init_failed", "sensor.selfcheck_failed", "sys.out_of_resources", "ota.selfcheck_failed", - "kota.sensor_missing", "status.error", "status.error.1", "status.error.2", "status.error.3", "status.error.4", - "status.error.5", "status.error.6", "status.oom"] -start = "which" - -[[guide.step]] -id = "which" -type = "route" -routes = { "sensor.init_failed" = "reseat", "rtc.init_failed" = "guide:rtc_i2c", "ota.selfcheck_failed" = "end:selfcheck", "sensor.selfcheck_failed" = "end:selfcheck", "sys.out_of_resources" = "end:oom" } -default = "capture" - -[[guide.step]] -id = "capture" -type = "wait" -text = "The log does not show the cause yet. The tool restarts the box and records its start-up (45 s)..." -tool_action = "reset" -timeout_s = 45 -pass_on_timeout = true -on_ok = "which_again" - -[[guide.step]] -id = "which_again" -type = "route" -routes = { "sensor.init_failed" = "reseat", "rtc.init_failed" = "guide:rtc_i2c", "sys.out_of_resources" = "end:oom" } -default = "end:red_unclear" - -[[guide.step]] -id = "reseat" -type = "info" -text = "Failing: {failing_sensors}. This needs the case opened: switch the power OFF, open the case and re-seat the cable of that sensor. Close the case, switch the power ON, then press Continue." -next = "verify" - -[[guide.step]] -id = "verify" -type = "wait" -text = "Watching the sensor start-up (up to 60 s)..." -until_milestone = "sensors_ready" -fail_on = 'MAIN: Failed to initialize' -timeout_s = 60 -on_ok = "end:sensor_fixed" -on_fail = "end:sensor_replace" -on_timeout = "end:sensor_replace" - -[[guide.end]] -id = "sensor_fixed" -severity = "ok" -title = "Fixed - all sensors start again" -text = "After re-seating, every sensor started." -actions = ["Close the case and watch the Diagnosis tab for a minute."] - -[[guide.end]] -id = "sensor_replace" -severity = "error" -title = "Sensor still fails - replace it" -text = "The sensor does not start even with a re-seated cable." -actions = ["Replace the named sensor (or its cable) and run this check again."] - -[[guide.end]] -id = "selfcheck" -severity = "error" -title = "A firmware update was rejected by its self-check" -text = "The box rolled back to its previous firmware because a sensor failed the test." -actions = ["Check the sensor named in the Diagnosis tab (inside the case), then power-cycle."] - -[[guide.end]] -id = "oom" -severity = "error" -title = "Firmware ran out of memory" -text = "Not a hardware fault; the firmware restarts itself." -actions = ["Power-cycle. If it repeats, send the session log to the firmware team."] - -[[guide.end]] -id = "red_unclear" -severity = "warning" -title = "Red LED without a clear cause in the log" -text = "The start-up did not show a failing sensor." -actions = ["Save the session log and send it to the firmware team."] - -# ============================================================= reboot loop -[[guide]] -id = "reboot_loop" -title = "LED keeps changing / box restarts" -summary = "Record several start-ups and find the error just before each restart." -recommend = ["led:CYCLING", "reboot.loop", "sys.crash", "reboot.unexpected", "reboot.brownout", "reboot.watchdog", - "reboot.crash", "reboot.status_watchdog", "reboot.status_oom_watchdog"] -start = "record" - -[[guide.step]] -id = "record" -type = "wait" -text = "The tool restarts the box and records 45 seconds of start-up..." -tool_action = "reset" -timeout_s = 45 -pass_on_timeout = true -on_ok = "cause" - -[[guide.step]] -id = "cause" -type = "route" -routes = { "rtc.init_failed" = "guide:rtc_i2c", "sd.card_fail" = "guide:white", "sd.mount_fail" = "guide:white", "sd.env_open_fail" = "guide:white", "influx.env_missing" = "guide:white", "influx.init_fail" = "guide:white", "sensor.init_failed" = "guide:sensors", "reboot.brownout" = "end:power_supply", "matter.not_commissioned" = "guide:network", "net.dns_fail" = "guide:network", "influx.buffer_fail" = "guide:network" } -default = "end:loop_unclear" - -[[guide.end]] -id = "power_supply" -severity = "error" -title = "Power supply is too weak or unstable" -text = "The chip reset itself because the voltage dropped." -actions = ["Check the power supply, cable and connectors; try another supply."] - -[[guide.end]] -id = "loop_unclear" -severity = "warning" -title = "No clear restart cause in 45 s" -text = "The box did not show a known start-up error during the recording." -actions = ["Keep the tool connected for 5 minutes, then save the session log for the firmware team."] - -# ================================================================== update -[[guide]] -id = "update" -title = "Yellow / orange LED - firmware update" -summary = "Wait for the update and its self-check to finish." -recommend = ["led:YELLOW", "led:ORANGE", "ota.updating", "ota.selfcheck"] -start = "wait" - -[[guide.step]] -id = "wait" -type = "wait" -text = "A firmware update or its self-check is running. Do NOT switch the box off. Waiting up to 10 minutes..." -until = 'Passing selfcheck|OTA update successful' -fail_on = 'OTA update failed|Failing selfcheck' -timeout_s = 600 -on_ok = "end:update_ok" -on_fail = "end:update_failed" -on_timeout = "end:update_slow" - -[[guide.end]] -id = "update_ok" -severity = "ok" -title = "Update finished" -text = "The new firmware was installed or accepted." -actions = ["Wait for the green LED."] - -[[guide.end]] -id = "update_failed" -severity = "error" -title = "Update failed" -text = "The box keeps its current firmware and retries later." -actions = ["Check the Wi-Fi is stable. The Diagnosis tab shows why it failed."] - -[[guide.end]] -id = "update_slow" -severity = "warning" -title = "Update is taking very long" -text = "No end of the update was seen in 10 minutes." -actions = ["Check the Wi-Fi signal; leave the box powered and check again later."] - -# =================================================================== power -[[guide]] -id = "power" -title = "LED off" -summary = "Power switch, heartbeat LED and USB connection." -recommend = ["led:OFF"] -start = "switch" - -[[guide.step]] -id = "switch" -type = "question" -text = "Is the power switch on the outside of the case set to ON?" -options = [ - { label = "It was OFF - I switched it on", next = "boot" }, - { label = "It is ON", next = "heartbeat" }, -] - -[[guide.step]] -id = "boot" -type = "wait" -text = "Waiting for the box to start (up to 30 s)..." -until_milestone = "app_started" -timeout_s = 30 -on_ok = "end:was_off" -on_timeout = "heartbeat" - -[[guide.step]] -id = "heartbeat" -type = "question" -text = "Is the small LED next to the RGB LED blinking?" -options = [ - { label = "Yes, blinking", next = "end:rgb_led_fault" }, - { label = "No", next = "end:no_power" }, -] - -[[guide.end]] -id = "was_off" -severity = "ok" -title = "Box was switched off" -text = "It starts normally after switching on." -actions = ["Run the Quick check again once the LED shows a colour."] - -[[guide.end]] -id = "rgb_led_fault" -severity = "warning" -title = "Firmware runs, but the RGB LED stays dark" -text = "The heartbeat LED shows the firmware is running; the RGB LED or its wiring is faulty." -actions = ["Use the Diagnosis tab instead of the LED; replace the LED board when convenient."] - -[[guide.end]] -id = "no_power" -severity = "error" -title = "Box does not start" -text = "Neither LED is lit: no power or the firmware does not start." -actions = [ - "Check the power supply and cable.", - "If the USB port shows up in this tool, re-flash the official firmware (Flash firmware tab, App only).", -] - -# =================================================================== clock -[[guide]] -id = "clock" -title = "Clock not set" -summary = "Why readings are on hold and when the coin cell matters." -recommend = ["rtc.clock_not_running", "rtc.lost_after_sync", "kota.rtc_stopped", "kota.rtc_not_ticking", "kota.time_invalid"] -start = "internet" - -[[guide.step]] -id = "internet" -type = "question" -text = "The box clock is not set, so readings are on hold. Will the box have Internet at its installation site?" -options = [ - { label = "Yes", next = "end:clock_auto" }, - { label = "No / not yet", next = "end:clock_offline" }, -] - -[[guide.end]] -id = "clock_auto" -severity = "info" -title = "Clock will be set automatically" -text = "On its first Internet connection the box sets its clock and starts recording." -actions = ["If the clock is lost again after every power-off, replace the RTC coin cell (inside the case)."] - -[[guide.end]] -id = "clock_offline" -severity = "error" -title = "Box will not record data without Internet" -text = "Without the Internet the clock cannot be set, and the firmware holds all readings until it is." -actions = [ - "Give the box Internet access (pair it in Matter).", - "Replace the RTC coin cell (inside the case) so the time survives power-offs.", -] - -# ===================================================================== mic -[[guide]] -id = "mic" -title = "Microphone check" -summary = "Switched-off or silent microphone." -recommend = ["mic.disabled", "mic.silent", "mic.read_fail"] -start = "which" - -[[guide.step]] -id = "which" -type = "route" -routes = { "mic.disabled" = "switch", "mic.silent" = "clap", "mic.read_fail" = "end:mic_faulty" } -default = "clap" - -[[guide.step]] -id = "switch" -type = "question" -text = "The box reads its microphone switch as OFF. The switch is on the back of the case, just above the power switch (no need to open the box). Should this box measure noise?" -options = [ - { label = "Yes - I flipped the microphone switch", next = "switch_verify" }, - { label = "No - it is meant to be off", next = "end:mic_off_ok" }, -] - -[[guide.step]] -id = "switch_verify" -type = "wait" -# The firmware re-reads the switch before every reading (~10 s): no restart needed. -text = "Waiting for the next microphone reading (up to 30 s)..." -until = 'MICROPHONE: Read \d+ bytes' -fail_on = 'Microphone is in shutdown state' -timeout_s = 30 -on_ok = "end:mic_on" -on_fail = "flip_back" -on_timeout = "flip_back" - -[[guide.step]] -id = "flip_back" -type = "question" -text = "The box still reads the switch as OFF. Flip the microphone switch to the other position (it may have been ON already)." -options = [ - { label = "Done - I flipped it to the other position", next = "switch_verify2" }, - { label = "Leave it off", next = "end:mic_off_ok" }, -] - -[[guide.step]] -id = "switch_verify2" -type = "wait" -text = "Waiting for the next microphone reading (up to 30 s)..." -until = 'MICROPHONE: Read \d+ bytes' -fail_on = 'Microphone is in shutdown state' -timeout_s = 30 -on_ok = "end:mic_on" -on_fail = "end:mic_switch_faulty" -on_timeout = "end:mic_switch_faulty" - -[[guide.step]] -id = "clap" -type = "wait" -text = "Check the microphone switch (back of the case, above the power switch) is ON. Then clap your hands or speak loudly right next to the box for about 20 seconds..." -until = 'dB peak: (?!-inf)' -timeout_s = 45 -on_ok = "end:mic_ok" -on_timeout = "end:mic_faulty" - -[[guide.end]] -id = "mic_on" -severity = "ok" -title = "Microphone is on and recording" -text = "The firmware reads audio samples again." -actions = ["Leave the microphone switch in this position."] - -[[guide.end]] -id = "mic_off_ok" -severity = "info" -title = "Microphone intentionally off" -text = "No noise data is recorded for this box; everything else works." -actions = [] - -[[guide.end]] -id = "mic_switch_faulty" -severity = "warning" -title = "Microphone switch has no effect" -text = "The box reads the microphone as OFF in both switch positions (enable line GPIO3)." -actions = ["Check the switch and its cable to the main board (inside the case); replace the switch if needed."] - -[[guide.end]] -id = "mic_ok" -severity = "ok" -title = "Microphone works" -text = "It picked up sound - the earlier silence was just a very quiet room." -actions = [] - -[[guide.end]] -id = "mic_faulty" -severity = "error" -title = "Microphone records nothing" -text = "Even loud noise next to the box gives no signal, although the switch is on." -actions = ["Check the microphone board/cable (inside the case); replace the microphone if needed."] +# Guided troubleshooting: decision trees that combine technician answers +# with live log evidence (diag.guides.GuideRunner executes them). +# +# Guide: id, title, summary, recommend (rule ids / "led:" / "always"), +# start (first step id), [[guide.step]], [[guide.end]] +# Step types: +# led technician picks the LED colour; `route` maps colour -> target. +# Colours: GREEN PURPLE RED BLUE YELLOW ORANGE WHITE OFF CYCLING +# The answer is compared with the colour the firmware log implies. +# question `options` = [{label, next}] +# info instruction + Continue (`next`) +# wait optional `tool_action` ("reset"), then watch the log: +# `until` (regex) / `until_milestone`, `fail_on` (regex), +# `timeout_s`; go to on_ok / on_fail / on_timeout. +# `pass_on_timeout = true`: reaching the timeout without fail_on +# counts as ok (used for "watch for N seconds"). +# route automatic: first current root finding whose rule id is in +# `routes` decides; otherwise `default`. +# Targets: "", "end:", "guide:". +# Optional on any step: skip_if_finding / skip_unless_feature (+ skip_to). +# Text placeholders: {expected_led} {failing_sensors} {top_finding} +# End severity: ok | info | warning | error | critical + +# ================================================================= quick +[[guide]] +id = "quick" +title = "Quick check - start here" +summary = "Tell the tool what the LED shows; it picks the right checks." +recommend = ["always"] +start = "led" + +[[guide.step]] +id = "led" +type = "led" +text = "Look at the RGB status LED on the box (no need to open it). Which colour is it?" +route = { GREEN = "heartbeat", PURPLE = "guide:network", RED = "guide:sensors", BLUE = "guide:rtc_i2c", WHITE = "guide:white", YELLOW = "guide:update", ORANGE = "guide:update", CYAN = "guide:network", OFF = "guide:power", CYCLING = "guide:reboot_loop" } + +[[guide.step]] +id = "heartbeat" +type = "question" +text = "Is the small LED next to the RGB LED blinking about once per second?" +options = [ + { label = "Yes, blinking", next = "green_route" }, + { label = "No - steady on or off", next = "end:hung" }, +] + +[[guide.step]] +id = "green_route" +type = "route" +routes = { "rtc.clock_not_running" = "guide:clock", "mic.silent" = "guide:mic", "mic.disabled" = "guide:mic", "matter.not_commissioned" = "guide:network", "net.dns_fail" = "guide:network" } +default = "end:healthy" + +[[guide.end]] +id = "healthy" +severity = "ok" +title = "Box looks healthy" +text = "Green LED, firmware running and no problems in the log." +actions = ["If the customer still reports missing data, keep the tool connected for 5 minutes and check that 'Data uploaded to InfluxDB' gets ticked."] + +[[guide.end]] +id = "hung" +severity = "error" +title = "Firmware is not running normally" +text = "The heartbeat LED should blink once per second whenever the firmware runs. A steady heartbeat LED means the firmware has frozen." +actions = [ + "Switch the box off and on with the power switch.", + "If it happens again: save the session log and re-flash the official firmware (Flash firmware tab, App only).", +] + +# =============================================================== network +[[guide]] +id = "network" +title = "Purple LED / no Internet" +summary = "Pairing in Matter, site Wi-Fi and Internet checks, with live confirmation." +recommend = ["led:PURPLE", "led:CYAN", "matter.not_commissioned", "net.dns_fail", "influx.buffer_fail", "ota.registration_failed", + "status.conn", "status.conn.1", "kota.wifi_down", "kota.no_upload", "kota.weak_wifi", "kota.ota_unregistered"] +start = "paired" + +[[guide.step]] +id = "paired" +type = "question" +skip_if_finding = "matter.not_commissioned" +skip_to = "pair_intro" +text = "Was this box already added to the customer's Matter app (Apple Home, Google Home, ...) at this site?" +options = [ + { label = "Yes, it was paired before", next = "internet" }, + { label = "No / not sure", next = "pair_intro" }, +] + +[[guide.step]] +id = "internet" +type = "question" +text = "Does the site Wi-Fi have working Internet? Check with a phone connected to the same Wi-Fi (open any website)." +options = [ + { label = "Yes, Internet works", next = "changed" }, + { label = "No Internet on the site Wi-Fi", next = "end:site_offline" }, +] + +[[guide.step]] +id = "changed" +type = "question" +text = "Was the Wi-Fi name or password changed, or the router replaced, since the box was installed?" +options = [ + { label = "Yes / possibly", next = "end:repair_needed" }, + { label = "No", next = "end:offline_unknown" }, +] + +[[guide.step]] +id = "pair_intro" +type = "info" +text = "The box is not on any Wi-Fi yet: it must be paired in Matter. You need: a phone with Bluetooth on, the customer's Matter app, the 2.4 GHz Wi-Fi password, and the QR sticker of this box. Keep the tool connected - it follows the pairing live." +next = "pair_start" + +[[guide.step]] +id = "pair_start" +type = "wait" +text = "In the Matter app choose 'Add device' and scan the QR code shown here (the same as the sticker), or type the 11-digit manual code. Keep the phone within 1 m of the box." +show_qr = true +until = 'MAIN: Commissioning session started' +timeout_s = 300 +on_ok = "pair_wifi" +on_timeout = "not_found" + +[[guide.step]] +id = "not_found" +type = "question" +text = "The box has not seen the phone connect yet. What does the app show?" +options = [ + { label = "Still searching / let me try again", next = "pair_start" }, + { label = "'Device not found' or similar", next = "end:ble_not_found" }, +] + +[[guide.step]] +id = "pair_wifi" +type = "wait" +text = "The phone is connected to the box. Follow the app: choose the 2.4 GHz Wi-Fi network and enter its password." +until = 'MAIN: Commissioning complete|MAIN: Fabric is committed' +fail_on = 'Commissioning failed, fail safe timer expired' +timeout_s = 300 +on_ok = "pair_time" +on_fail = "end:pair_failed" +on_timeout = "end:pair_failed" + +[[guide.step]] +id = "pair_time" +type = "wait" +text = "Paired! The box is joining the Wi-Fi and getting the time from the Internet..." +until_milestone = "time_synced" +timeout_s = 120 +on_ok = "pair_upload" +on_timeout = "end:joined_no_internet" + +[[guide.step]] +id = "pair_upload" +type = "wait" +text = "Online. Waiting for the first measurements to reach the database (up to 4 minutes)..." +until = 'INFLUX: HTTP request performed, status code: 2\d\d' +fail_on = 'INFLUX: Failed to perform HTTP request: (401|403|404)' +timeout_s = 240 +on_ok = "end:online" +on_fail = "end:influx_rejected" +on_timeout = "end:no_upload" + +[[guide.end]] +id = "online" +severity = "ok" +title = "Box is paired and uploading data" +text = "Pairing, Internet time and the first upload all worked." +actions = [ + "The box may now download a firmware update (yellow, then orange LED, a few minutes). Do not switch it off.", + "It should end green.", +] + +[[guide.end]] +id = "site_offline" +severity = "warning" +title = "Site network has no Internet" +text = "The box cannot work until the site Wi-Fi reaches the Internet. This is not a box fault." +actions = ["Ask the customer to fix the Internet connection, then power-cycle the box."] + +[[guide.end]] +id = "repair_needed" +severity = "warning" +title = "Box must be paired again" +text = "The box still remembers the old Wi-Fi. It needs a factory reset and new pairing." +actions = [ + "Hold the Matter button for 5 seconds (LED turns white) - this erases the old pairing.", + "Remove the box from the customer's Matter app, then run this guide again and choose 'No / not sure'.", +] + +[[guide.end]] +id = "offline_unknown" +severity = "error" +title = "Box is paired but cannot reach the Internet - cause unclear" +text = "The network works for other devices, but not for this box." +actions = [ + "Check the router does not block new devices (guest isolation, MAC filter). Box MAC is shown in the port list.", + "Move the box closer to the router to rule out weak signal.", + "If nothing helps: factory reset (hold the Matter button 5 s) and pair again.", +] + +[[guide.end]] +id = "ble_not_found" +severity = "error" +title = "Phone cannot find the box over Bluetooth" +text = "The box only accepts pairing for a limited time after start-up." +actions = [ + "Switch the box off and on to reopen the pairing window, then try again within 10 minutes.", + "Check Bluetooth and location permission are on for the Matter app.", + "If the box was paired to another app before: hold the Matter button 5 s (factory reset) first.", +] + +[[guide.end]] +id = "pair_failed" +severity = "error" +title = "Pairing did not finish" +text = "The phone connected but the box did not join the Wi-Fi." +actions = [ + "Use a 2.4 GHz network (many routers have a separate 2.4 GHz name).", + "Re-enter the Wi-Fi password carefully.", + "Switch the box off and on, then pair again.", +] + +[[guide.end]] +id = "joined_no_internet" +severity = "error" +title = "Box joined the Wi-Fi but has no Internet" +text = "Pairing worked, but the box cannot reach time or data servers." +actions = [ + "Check the network allows outgoing connections (HTTPS, NTP) for new devices.", + "Try a phone hotspot to rule out the site network.", +] + +[[guide.end]] +id = "influx_rejected" +severity = "critical" +title = "Database rejected the box (token / bucket)" +text = "The box is online but InfluxDB refused the upload. The SD card settings are wrong." +actions = ["Re-provision the SD card with the current ENV.TXT (SD card is inside the case)."] + +[[guide.end]] +id = "no_upload" +severity = "warning" +title = "Online, but no upload seen yet" +text = "Uploads happen every minute; the first one can be delayed while the air sensor warms up." +actions = ["Keep the tool connected a few more minutes and watch the Diagnosis tab."] + +# ================================================================= RTC / I2C +[[guide]] +id = "rtc_i2c" +title = "Blue LED - clock / I2C cable" +summary = "Re-seat the I2C cable and verify the start-up live." +recommend = ["led:BLUE", "rtc.init_failed", "i2c.errors", "kota.i2c_stuck", "kota.rtc_read_failed", "status.rtc.1", "status.rtc.2"] +start = "explain" + +[[guide.step]] +id = "explain" +type = "info" +text = "Blue means the clock chip did not answer. It is the first chip on the shared I2C cable between the boards, so a loose cable is the most common cause. This check needs the case opened." +next = "reseat" + +[[guide.step]] +id = "reseat" +type = "info" +text = "Switch the power OFF. Open the case. Unplug and firmly re-plug the I2C cable between the boards at both ends. Close the case and switch the power ON. Then press Continue." +next = "verify" + +[[guide.step]] +id = "verify" +type = "wait" +text = "Watching the start-up (up to 60 s)..." +until_milestone = "sensors_ready" +fail_on = 'MAIN: Failed to initialize RTC' +timeout_s = 60 +on_ok = "end:i2c_fixed" +on_fail = "damaged" +on_timeout = "damaged" + +[[guide.step]] +id = "damaged" +type = "question" +text = "The clock still does not answer. Is the I2C cable or its connector visibly damaged (pinched, broken wire, bent pin)?" +options = [ + { label = "Yes, damaged", next = "end:replace_cable" }, + { label = "No, looks fine", next = "end:rtc_module" }, +] + +[[guide.end]] +id = "i2c_fixed" +severity = "ok" +title = "Fixed - all sensors start again" +text = "After re-seating the cable the clock and all sensors started." +actions = ["Make sure the cable is routed without strain before closing the case."] + +[[guide.end]] +id = "replace_cable" +severity = "error" +title = "Replace the I2C cable" +text = "A damaged I2C cable stops the clock and all sensors." +actions = ["Replace the cable, then run this check again."] + +[[guide.end]] +id = "rtc_module" +severity = "error" +title = "Clock (RTC) module is probably faulty" +text = "The cable is fine but the clock chip does not answer." +actions = ["Replace the RTC module, or send the box for repair with the session log."] + +# ==================================================================== white +[[guide]] +id = "white" +title = "White LED - SD card / reset / setup mode" +summary = "Tell apart the three white states and check the SD card." +recommend = ["led:WHITE", "sd.card_fail", "sd.mount_fail", "sd.env_open_fail", "influx.env_missing", "influx.init_fail", + "kota.sd_unmounted", "status.sd", "status.portal", "status.reset"] +start = "button" + +[[guide.step]] +id = "button" +type = "question" +text = "Did anyone hold the Matter button (outside the case) for about 5 seconds just before the LED turned white?" +options = [ + { label = "Yes", next = "end:factory_reset" }, + { label = "No / don't know", next = "portal" }, +] + +[[guide.step]] +id = "portal" +type = "question" +skip_unless_feature = "setup_portal" +skip_to = "sd_reseat" +text = "On a phone, look at the Wi-Fi list: is there a network called 'KotaSensorBox-....'?" +options = [ + { label = "Yes", next = "end:setup_mode" }, + { label = "No", next = "sd_reseat" }, +] + +[[guide.step]] +id = "sd_reseat" +type = "info" +text = "The SD card is probably not readable. This needs the case opened: switch the power OFF, open the case, push the SD card in until it clicks (or remove and re-insert it). Close the case, switch the power ON, then press Continue." +next = "sd_verify" + +[[guide.step]] +id = "sd_verify" +type = "wait" +text = "Watching the SD card start-up (up to 60 s)..." +until = 'Read INFLUX_URL from \.env file' +fail_on = 'Failed to initialize the card|Failed to mount filesystem|Failed to read INFLUX_(URL|TOKEN)|Failed to open \.env file' +timeout_s = 60 +on_ok = "end:sd_fixed" +on_fail = "end:sd_reprovision" +on_timeout = "end:sd_reprovision" + +[[guide.end]] +id = "factory_reset" +severity = "info" +title = "White = factory reset in progress" +text = "The box erased its Matter pairing and restarts." +actions = ["Pair it again in the Matter app (Purple LED guide)."] + +[[guide.end]] +id = "setup_mode" +severity = "info" +title = "White = Wi-Fi setup mode" +text = "The box is waiting for Wi-Fi settings on its own network." +actions = [ + "Connect a phone to KotaSensorBox-.... and open http://sensorbox.local/ to enter the Wi-Fi,", + "or short-press the Matter button (< 2 s) to leave setup mode.", +] + +[[guide.end]] +id = "sd_fixed" +severity = "ok" +title = "Fixed - SD card is read again" +text = "The box found the SD card and read ENV.TXT." +actions = ["Close the case and watch the Diagnosis tab for a minute."] + +[[guide.end]] +id = "sd_reprovision" +severity = "critical" +title = "SD card is unreadable or has wrong settings" +text = "Even after re-seating, the card or its ENV.TXT cannot be used." +actions = [ + "Re-provision the card with the tool (SD card tab) or use a new card.", + "The Diagnosis tab shows which step failed (card, file system or ENV.TXT keys).", +] + +# ================================================================== sensors +[[guide]] +id = "sensors" +title = "Red LED - sensor or hardware fault" +summary = "Find the failing sensor and verify after re-seating its cable." +recommend = ["led:RED", "sensor.init_failed", "sensor.selfcheck_failed", "sys.out_of_resources", "ota.selfcheck_failed", + "kota.sensor_missing", "status.error", "status.error.1", "status.error.2", "status.error.3", "status.error.4", + "status.error.5", "status.error.6", "status.oom"] +start = "which" + +[[guide.step]] +id = "which" +type = "route" +routes = { "sensor.init_failed" = "reseat", "rtc.init_failed" = "guide:rtc_i2c", "ota.selfcheck_failed" = "end:selfcheck", "sensor.selfcheck_failed" = "end:selfcheck", "sys.out_of_resources" = "end:oom" } +default = "capture" + +[[guide.step]] +id = "capture" +type = "wait" +text = "The log does not show the cause yet. The tool restarts the box and records its start-up (45 s)..." +tool_action = "reset" +timeout_s = 45 +pass_on_timeout = true +on_ok = "which_again" + +[[guide.step]] +id = "which_again" +type = "route" +routes = { "sensor.init_failed" = "reseat", "rtc.init_failed" = "guide:rtc_i2c", "sys.out_of_resources" = "end:oom" } +default = "end:red_unclear" + +[[guide.step]] +id = "reseat" +type = "info" +text = "Failing: {failing_sensors}. This needs the case opened: switch the power OFF, open the case and re-seat the cable of that sensor. Close the case, switch the power ON, then press Continue." +next = "verify" + +[[guide.step]] +id = "verify" +type = "wait" +text = "Watching the sensor start-up (up to 60 s)..." +until_milestone = "sensors_ready" +fail_on = 'MAIN: Failed to initialize' +timeout_s = 60 +on_ok = "end:sensor_fixed" +on_fail = "end:sensor_replace" +on_timeout = "end:sensor_replace" + +[[guide.end]] +id = "sensor_fixed" +severity = "ok" +title = "Fixed - all sensors start again" +text = "After re-seating, every sensor started." +actions = ["Close the case and watch the Diagnosis tab for a minute."] + +[[guide.end]] +id = "sensor_replace" +severity = "error" +title = "Sensor still fails - replace it" +text = "The sensor does not start even with a re-seated cable." +actions = ["Replace the named sensor (or its cable) and run this check again."] + +[[guide.end]] +id = "selfcheck" +severity = "error" +title = "A firmware update was rejected by its self-check" +text = "The box rolled back to its previous firmware because a sensor failed the test." +actions = ["Check the sensor named in the Diagnosis tab (inside the case), then power-cycle."] + +[[guide.end]] +id = "oom" +severity = "error" +title = "Firmware ran out of memory" +text = "Not a hardware fault; the firmware restarts itself." +actions = ["Power-cycle. If it repeats, send the session log to the firmware team."] + +[[guide.end]] +id = "red_unclear" +severity = "warning" +title = "Red LED without a clear cause in the log" +text = "The start-up did not show a failing sensor." +actions = ["Save the session log and send it to the firmware team."] + +# ============================================================= reboot loop +[[guide]] +id = "reboot_loop" +title = "LED keeps changing / box restarts" +summary = "Record several start-ups and find the error just before each restart." +recommend = ["led:CYCLING", "reboot.loop", "sys.crash", "reboot.unexpected", "reboot.brownout", "reboot.watchdog", + "reboot.crash", "reboot.status_watchdog", "reboot.status_oom_watchdog"] +start = "record" + +[[guide.step]] +id = "record" +type = "wait" +text = "The tool restarts the box and records 45 seconds of start-up..." +tool_action = "reset" +timeout_s = 45 +pass_on_timeout = true +on_ok = "cause" + +[[guide.step]] +id = "cause" +type = "route" +routes = { "rtc.init_failed" = "guide:rtc_i2c", "sd.card_fail" = "guide:white", "sd.mount_fail" = "guide:white", "sd.env_open_fail" = "guide:white", "influx.env_missing" = "guide:white", "influx.init_fail" = "guide:white", "sensor.init_failed" = "guide:sensors", "reboot.brownout" = "end:power_supply", "matter.not_commissioned" = "guide:network", "net.dns_fail" = "guide:network", "influx.buffer_fail" = "guide:network" } +default = "end:loop_unclear" + +[[guide.end]] +id = "power_supply" +severity = "error" +title = "Power supply is too weak or unstable" +text = "The chip reset itself because the voltage dropped." +actions = ["Check the power supply, cable and connectors; try another supply."] + +[[guide.end]] +id = "loop_unclear" +severity = "warning" +title = "No clear restart cause in 45 s" +text = "The box did not show a known start-up error during the recording." +actions = ["Keep the tool connected for 5 minutes, then save the session log for the firmware team."] + +# ================================================================== update +[[guide]] +id = "update" +title = "Yellow / orange LED - firmware update" +summary = "Wait for the update and its self-check to finish." +recommend = ["led:YELLOW", "led:ORANGE", "ota.updating", "ota.selfcheck"] +start = "wait" + +[[guide.step]] +id = "wait" +type = "wait" +text = "A firmware update or its self-check is running. Do NOT switch the box off. Waiting up to 10 minutes..." +until = 'Passing selfcheck|OTA update successful' +fail_on = 'OTA update failed|Failing selfcheck' +timeout_s = 600 +on_ok = "end:update_ok" +on_fail = "end:update_failed" +on_timeout = "end:update_slow" + +[[guide.end]] +id = "update_ok" +severity = "ok" +title = "Update finished" +text = "The new firmware was installed or accepted." +actions = ["Wait for the green LED."] + +[[guide.end]] +id = "update_failed" +severity = "error" +title = "Update failed" +text = "The box keeps its current firmware and retries later." +actions = ["Check the Wi-Fi is stable. The Diagnosis tab shows why it failed."] + +[[guide.end]] +id = "update_slow" +severity = "warning" +title = "Update is taking very long" +text = "No end of the update was seen in 10 minutes." +actions = ["Check the Wi-Fi signal; leave the box powered and check again later."] + +# =================================================================== power +[[guide]] +id = "power" +title = "LED off" +summary = "Power switch, heartbeat LED and USB connection." +recommend = ["led:OFF"] +start = "switch" + +[[guide.step]] +id = "switch" +type = "question" +text = "Is the power switch on the outside of the case set to ON?" +options = [ + { label = "It was OFF - I switched it on", next = "boot" }, + { label = "It is ON", next = "heartbeat" }, +] + +[[guide.step]] +id = "boot" +type = "wait" +text = "Waiting for the box to start (up to 30 s)..." +until_milestone = "app_started" +timeout_s = 30 +on_ok = "end:was_off" +on_timeout = "heartbeat" + +[[guide.step]] +id = "heartbeat" +type = "question" +text = "Is the small LED next to the RGB LED blinking?" +options = [ + { label = "Yes, blinking", next = "end:rgb_led_fault" }, + { label = "No", next = "end:no_power" }, +] + +[[guide.end]] +id = "was_off" +severity = "ok" +title = "Box was switched off" +text = "It starts normally after switching on." +actions = ["Run the Quick check again once the LED shows a colour."] + +[[guide.end]] +id = "rgb_led_fault" +severity = "warning" +title = "Firmware runs, but the RGB LED stays dark" +text = "The heartbeat LED shows the firmware is running; the RGB LED or its wiring is faulty." +actions = ["Use the Diagnosis tab instead of the LED; replace the LED board when convenient."] + +[[guide.end]] +id = "no_power" +severity = "error" +title = "Box does not start" +text = "Neither LED is lit: no power or the firmware does not start." +actions = [ + "Check the power supply and cable.", + "If the USB port shows up in this tool, re-flash the official firmware (Flash firmware tab, App only).", +] + +# =================================================================== clock +[[guide]] +id = "clock" +title = "Clock not set" +summary = "Why readings are on hold and when the coin cell matters." +recommend = ["rtc.clock_not_running", "rtc.lost_after_sync", "kota.rtc_stopped", "kota.rtc_not_ticking", "kota.time_invalid"] +start = "internet" + +[[guide.step]] +id = "internet" +type = "question" +text = "The box clock is not set, so readings are on hold. Will the box have Internet at its installation site?" +options = [ + { label = "Yes", next = "end:clock_auto" }, + { label = "No / not yet", next = "end:clock_offline" }, +] + +[[guide.end]] +id = "clock_auto" +severity = "info" +title = "Clock will be set automatically" +text = "On its first Internet connection the box sets its clock and starts recording." +actions = ["If the clock is lost again after every power-off, replace the RTC coin cell (inside the case)."] + +[[guide.end]] +id = "clock_offline" +severity = "error" +title = "Box will not record data without Internet" +text = "Without the Internet the clock cannot be set, and the firmware holds all readings until it is." +actions = [ + "Give the box Internet access (pair it in Matter).", + "Replace the RTC coin cell (inside the case) so the time survives power-offs.", +] + +# ===================================================================== mic +[[guide]] +id = "mic" +title = "Microphone check" +summary = "Switched-off or silent microphone." +recommend = ["mic.disabled", "mic.silent", "mic.read_fail"] +start = "which" + +[[guide.step]] +id = "which" +type = "route" +routes = { "mic.disabled" = "switch", "mic.silent" = "clap", "mic.read_fail" = "end:mic_faulty" } +default = "clap" + +[[guide.step]] +id = "switch" +type = "question" +text = "The box reads its microphone switch as OFF. The switch is on the back of the case, just above the power switch (no need to open the box). Should this box measure noise?" +options = [ + { label = "Yes - I flipped the microphone switch", next = "switch_verify" }, + { label = "No - it is meant to be off", next = "end:mic_off_ok" }, +] + +[[guide.step]] +id = "switch_verify" +type = "wait" +# The firmware re-reads the switch before every reading (~10 s): no restart needed. +text = "Waiting for the next microphone reading (up to 30 s)..." +until = 'MICROPHONE: Read \d+ bytes' +fail_on = 'Microphone is in shutdown state' +timeout_s = 30 +on_ok = "end:mic_on" +on_fail = "flip_back" +on_timeout = "flip_back" + +[[guide.step]] +id = "flip_back" +type = "question" +text = "The box still reads the switch as OFF. Flip the microphone switch to the other position (it may have been ON already)." +options = [ + { label = "Done - I flipped it to the other position", next = "switch_verify2" }, + { label = "Leave it off", next = "end:mic_off_ok" }, +] + +[[guide.step]] +id = "switch_verify2" +type = "wait" +text = "Waiting for the next microphone reading (up to 30 s)..." +until = 'MICROPHONE: Read \d+ bytes' +fail_on = 'Microphone is in shutdown state' +timeout_s = 30 +on_ok = "end:mic_on" +on_fail = "end:mic_switch_faulty" +on_timeout = "end:mic_switch_faulty" + +[[guide.step]] +id = "clap" +type = "wait" +text = "Check the microphone switch (back of the case, above the power switch) is ON. Then clap your hands or speak loudly right next to the box for about 20 seconds..." +until = 'dB peak: (?!-inf)' +timeout_s = 45 +on_ok = "end:mic_ok" +on_timeout = "end:mic_faulty" + +[[guide.end]] +id = "mic_on" +severity = "ok" +title = "Microphone is on and recording" +text = "The firmware reads audio samples again." +actions = ["Leave the microphone switch in this position."] + +[[guide.end]] +id = "mic_off_ok" +severity = "info" +title = "Microphone intentionally off" +text = "No noise data is recorded for this box; everything else works." +actions = [] + +[[guide.end]] +id = "mic_switch_faulty" +severity = "warning" +title = "Microphone switch has no effect" +text = "The box reads the microphone as OFF in both switch positions (enable line GPIO3)." +actions = ["Check the switch and its cable to the main board (inside the case); replace the switch if needed."] + +[[guide.end]] +id = "mic_ok" +severity = "ok" +title = "Microphone works" +text = "It picked up sound - the earlier silence was just a very quiet room." +actions = [] + +[[guide.end]] +id = "mic_faulty" +severity = "error" +title = "Microphone records nothing" +text = "Even loud noise next to the box gives no signal, although the switch is on." +actions = ["Check the microphone board/cable (inside the case); replace the microphone if needed."] diff --git a/kota_tech/diag/rules.toml b/kota_tech/diag/rules.toml index 0e70f1a..8bfb504 100644 --- a/kota_tech/diag/rules.toml +++ b/kota_tech/diag/rules.toml @@ -1,732 +1,732 @@ -# Log signatures for the KOTA sensorbox firmware. -# -# Derived from the firmware source (log strings, Status::set_status calls) and -# checked against real captures (tests/fixtures). Patterns match the cleaned -# line text, not the tag: the deployed v1.0 build logs some messages under -# different tags than the source (e.g. SD mount under INFLUX_BUFFER). -# -# Fields: -# id unique, "." -# pattern Python regex (search). Named groups can be used in `title`. -# Matched against the line with its error ID ("[E-SD-03] ") -# removed. Rules without a pattern ("kota.*") are raised by -# the engine from the structured KOTA: lines (diag/kota.py). -# severity critical | error | warning | info | ignore -# ignore = never shown (benign ESP_LOGE lines, LED-only rules) -# title short technician-facing headline -# meaning what it means, in plain words -# actions what to do, in order (prefer no-unscrewing actions first) -# explained_by list of rule ids; while one of them is active this finding -# is shown as a consequence of it instead of a root cause -# milestone boot milestone reached when the line is seen -# clears finding ids resolved when this line is seen -# led status LED colour this line sets (firmware Status::set_status) -# led_from only change the LED if it currently has this colour -# (Status::set_status_conditional) -# split_by named group; one finding per distinct value (e.g. per sensor) -# min_count finding becomes active only after this many occurrences -# min_uptime_ms ignore occurrences earlier than this uptime - -# ---------------------------------------------------------------- milestones - -[[rule]] -id = "boot.app_started" -pattern = 'app_init: Project name:\s+kota-sensorboard' -severity = "ignore" -milestone = "app_started" -led = "GREEN" # Status::init() -> STATUS_OK - -[[rule]] -id = "boot.sd_mounted" -pattern = 'SD card mounted successfully' -severity = "ignore" -milestone = "sd_mounted" - -[[rule]] -id = "boot.env_loaded" -pattern = 'Read INFLUX_URL from \.env file' -severity = "ignore" -milestone = "env_loaded" - -[[rule]] -id = "boot.matter_started" -pattern = 'ESP-Matter started successfully' -severity = "ignore" -milestone = "matter_started" - -[[rule]] -id = "boot.local_wifi_mode" -pattern = 'SSID/PASSWORD found in \.env' -severity = "ignore" -milestone = "local_wifi_mode" - -[[rule]] -id = "boot.sensors_ready" -# DeviceStatus::init installs the temperature sensor; it is the last init -# step, and any earlier init failure aborts before reaching it. -pattern = 'temperature_sensor: Range \[' -severity = "ignore" -milestone = "sensors_ready" - -[[rule]] -id = "net.wifi_connected" -pattern = 'sta ip: \d|got ip:|WIFI_SERVICE: Got IP address|IPv4 address changed|Interface IP Address changed' -severity = "ignore" -milestone = "wifi_connected" -clears = ["matter.not_commissioned", "wifi.disconnected"] - -[[rule]] -id = "matter.commissioned" -pattern = 'MAIN: Commissioning complete|MAIN: Fabric is committed' -severity = "ignore" -milestone = "matter_commissioned" -clears = ["matter.not_commissioned", "matter.commissioning_failed"] - -[[rule]] -id = "net.time_synced" -pattern = 'SNTP: Time synchronized' -severity = "ignore" -milestone = "time_synced" -clears = ["rtc.clock_not_running", "net.dns_fail"] - -[[rule]] -id = "influx.upload_ok" -pattern = 'INFLUX: HTTP request performed, status code: 2\d\d' -severity = "ignore" -milestone = "data_uploaded" -clears = ["influx.buffer_fail", "net.dns_fail", "net.tls_fail"] - -[[rule]] -id = "influx.write_ok" -pattern = 'Successfully wrote to Influx' -severity = "ignore" -milestone = "data_uploaded" -led = "GREEN" -led_from = "PURPLE" - -[[rule]] -id = "ota.registered" -pattern = 'Device registered successfully|OTA token loaded from NVS' -severity = "ignore" -milestone = "ota_registered" -clears = ["ota.registration_failed"] - -[[rule]] -id = "ota.checked" -pattern = 'OTA_CLIENT: No update available' -severity = "ignore" -milestone = "ota_checked" -clears = ["ota.check_failed"] -led = "GREEN" -led_from = "PURPLE" - -# ----------------------------------------------------------- benign E lines - -[[rule]] -id = "benign.mac" -pattern = 'Device MAC address' -severity = "ignore" - -[[rule]] -id = "benign.attr_cb" -pattern = 'MAIN: Attribute update callback' -severity = "ignore" - -[[rule]] -id = "benign.zcl" -pattern = 'chip\[ZCL\]: WRITE ERR|data_model: Cluster cannot be NULL' -severity = "ignore" - -[[rule]] -id = "benign.wifi_start" -# Harmless Wi-Fi noise seen on every 1.0.16 boot: Matter asks for a connect -# while the station is already connecting, and an unpaired box polls a -# network it does not have yet (matter.not_commissioned covers that case). -pattern = 'wifi:sta is connecting, return error|esp_wifi_connect\(\) failed: ESP_ERR_WIFI_CONN|Failed to get configured network when updating network status' -severity = "ignore" - -[[rule]] -id = "benign.connect_chain" -# Transport-level echo of a failure that a more specific rule reports. -pattern = 'esp-tls: Failed to open new connection|transport_base: Failed to open a new connection|HTTP_CLIENT: Connection failed, sock < 0|INFLUX: Failed to perform HTTP request: 0,|INFLUX_BUFFER: Failed to write chunk to Influx|OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at' -severity = "ignore" - -# ------------------------------------------------------------------ network - -[[rule]] -id = "matter.not_commissioned" -pattern = 'CHIPoBLE advertising started|MAIN: Commissioning window opened' -severity = "warning" -title = "Box is not connected to Wi-Fi (not commissioned in Matter)" -meaning = "The box is waiting to be added to a Matter network over Bluetooth. Until then it has no Wi-Fi, so it cannot upload data or receive firmware updates." -actions = [ - "Add the box in the customer's Matter app (Apple Home, Google Home, ...) by scanning the QR sticker, or enter the manual code printed under it.", - "Use a 2.4 GHz Wi-Fi network; keep the phone close to the box during pairing.", - "If pairing keeps failing: hold the Matter button 5 s (factory reset) and try again.", -] -milestone = "ble_advertising" - -[[rule]] -id = "matter.commissioning_failed" -pattern = 'Commissioning failed, fail safe timer expired' -severity = "error" -title = "Matter pairing attempt failed" -meaning = "A phone started adding the box to Matter but did not finish in time." -actions = [ - "Retry pairing from the Matter app with the phone next to the box.", - "Check the Wi-Fi is 2.4 GHz and the password entered in the app is correct.", -] - -[[rule]] -id = "net.dns_fail" -pattern = '''couldn't get hostname for :(?P[\w.-]+)''' -severity = "error" -title = "No Internet: server names cannot be resolved" -meaning = "The box could not look up a server name. Either it has no Wi-Fi connection, or the network has no working Internet/DNS." -actions = [ - "Check the site Wi-Fi has Internet (open a website on a phone connected to the same network).", - "If the box was never paired: commission it in Matter first.", - "Check the router does not block new devices (guest isolation, MAC filtering).", -] -explained_by = ["matter.not_commissioned", "wifi.disconnected", "wifi.setup_portal"] - -[[rule]] -id = "net.tls_fail" -pattern = 'mbedtls_ssl_handshake returned|Failed to verify peer certificate|esp-tls-mbedtls: .*[Hh]andshake' -severity = "error" -title = "Secure connection to the server failed" -meaning = "The box reached the server but the encrypted (HTTPS) connection failed. Common causes: wrong clock on the box, or a network that intercepts HTTPS (corporate proxy/firewall)." -actions = [ - "Try another network (e.g. phone hotspot) to rule out a firewall.", - "Check the clock finding below: HTTPS needs the correct date.", -] -explained_by = ["rtc.clock_not_running"] - -[[rule]] -id = "wifi.disconnected" -pattern = 'WiFi disconnected, retrying in' -severity = "error" -min_count = 3 -title = "Box cannot connect to the configured Wi-Fi" -meaning = "The Wi-Fi name/password from the SD card does not work: wrong password, network out of range, or not 2.4 GHz." -actions = [ - "Check SSID and PASSWORD in ENV.TXT (re-provision the SD card with the tool).", - "Make sure the network is 2.4 GHz and in range.", -] - -[[rule]] -id = "wifi.setup_portal" -pattern = 'WiFi provisioning portal ready: connect to "(?P[^"]+)"' -severity = "warning" -title = "Box is in Wi-Fi setup mode (network {ssid})" -meaning = "The box is hosting its own Wi-Fi network for configuration instead of connecting to the site Wi-Fi." -actions = [ - "Connect a phone to {ssid} and open http://sensorbox.local/ to enter the Wi-Fi details,", - "or short-press the Matter button (< 2 s) to leave setup mode.", -] -led = "WHITE" - -# ------------------------------------------------------------------- InfluxDB - -[[rule]] -id = "influx.buffer_fail" -pattern = 'Failed to write chunk with \d+ retries to Influx|MAIN: Failed to flush Influx buffer' -severity = "error" -title = "Measurements cannot be uploaded to InfluxDB" -meaning = "Uploads keep failing, so readings are stored on the SD card and sent later. The LED turns purple (firmware v1.0 also restarts itself after about 2.5 minutes in this state)." -actions = [ - "Fix the network problem first (see the finding this is grouped under).", - "Buffered data is uploaded automatically once the connection works.", -] -explained_by = ["net.dns_fail", "net.tls_fail", "matter.not_commissioned", "wifi.disconnected", "wifi.setup_portal"] - -[[rule]] -id = "influx.purple" -pattern = 'Failed to write chunk with \d+ retries to Influx|Failed to flush buffer before writing|Failed to append new entry to buffer' -severity = "ignore" -led = "PURPLE" - -[[rule]] -id = "influx.auth" -pattern = 'INFLUX: Failed to perform HTTP request: (?P401|403)' -severity = "critical" -title = "InfluxDB rejected the token (HTTP {code})" -meaning = "The box reaches the database but the INFLUX_TOKEN on the SD card is wrong or revoked." -actions = [ - "Re-provision the SD card with the current ENV.TXT (SD card is inside the case).", - "If all boxes show this: the shared token was rotated - update ENV.TXT for everyone.", -] - -[[rule]] -id = "influx.not_found" -pattern = 'INFLUX: Failed to perform HTTP request: (?P404)' -severity = "critical" -title = "InfluxDB bucket or organisation not found (HTTP 404)" -meaning = "INFLUX_URL on the SD card points to a bucket/org that does not exist." -actions = ["Re-provision the SD card with the correct ENV.TXT."] - -[[rule]] -id = "influx.server_error" -pattern = 'INFLUX: Failed to perform HTTP request: (?P5\d\d)' -severity = "warning" -title = "InfluxDB server error (HTTP {code})" -meaning = "The database server has a problem. The box is fine; data is buffered on the SD card." -actions = ["Nothing to fix on the box. Report to the backend team if it persists."] - -[[rule]] -id = "influx.bad_request" -pattern = 'INFLUX: Bad request, check line protocol format' -severity = "warning" -title = "InfluxDB refused some measurements (HTTP 400)" -meaning = "A measurement was malformed and dropped (firmware issue, not a hardware fault)." -actions = ["Save the session log and send it to the firmware team."] - -[[rule]] -id = "influx.env_missing" -split_by = "key" -pattern = 'Failed to read (?PINFLUX_URL|INFLUX_TOKEN) from \.env file' -severity = "critical" -title = "ENV.TXT on the SD card is missing {key}" -meaning = "The box cannot start without the InfluxDB settings; it shows white and restarts." -actions = ["Re-provision the SD card with the tool (SD card is inside the case)."] - -[[rule]] -id = "influx.init_fail" -pattern = 'MAIN: Failed to initialize Influx buffer' -severity = "critical" -title = "Data upload could not be initialised" -meaning = "Start-up stopped because the InfluxDB settings could not be read from the SD card. The box restarts." -actions = ["Re-provision the SD card with the tool (SD card is inside the case)."] -explained_by = ["influx.env_missing", "sd.mount_fail", "sd.card_fail", "sd.env_open_fail"] -led = "WHITE" - -# ----------------------------------------------------------------- SD card - -[[rule]] -id = "sd.card_fail" -pattern = 'Failed to initialize the card \((?P[^)]+)\)' -severity = "critical" -title = "SD card not detected ({err})" -meaning = "The SD card does not respond: missing, not seated, or damaged. The box shows white and restarts." -actions = [ - "Open the case and re-seat the SD card.", - "Try a freshly provisioned card.", -] -led = "WHITE" - -[[rule]] -id = "sd.mount_fail" -pattern = 'Failed to mount filesystem' -severity = "critical" -title = "SD card is not formatted correctly" -meaning = "The card responds but has no readable FAT32 file system." -actions = ["Re-provision the card with the tool (FAT32, MBR) - SD card is inside the case."] -led = "WHITE" - -[[rule]] -id = "sd.env_open_fail" -pattern = 'Failed to open \.env file for reading' -severity = "critical" -title = "ENV.TXT not found on the SD card" -meaning = "The card is readable but the configuration file is missing." -actions = ["Re-provision the card with the tool (SD card is inside the case)."] - -[[rule]] -id = "sd.write_fail" -pattern = 'Failed to (create buffer file|open buffer file for appending)' -severity = "error" -title = "Cannot write to the SD card" -meaning = "Offline data cannot be stored: the card is full, write-protected or failing." -actions = ["Replace / re-provision the SD card (inside the case)."] - -# --------------------------------------------------------------- RTC / I2C - -[[rule]] -id = "rtc.init_failed" -pattern = 'MAIN: Failed to initialize RTC: (?P\S+)' -severity = "critical" -title = "Clock chip (RTC) or I2C bus not responding ({err})" -meaning = "The RTC is the first chip the firmware talks to over I2C, so this also appears when the I2C cable between the boards is loose or broken. LED blue, the box restarts." -actions = [ - "Open the case and check the I2C cable/connector between the boards.", - "If the cable is fine: the RTC module is faulty.", -] -explained_by = ["kota.i2c_stuck"] -led = "BLUE" - -[[rule]] -id = "rtc.clock_not_running" -pattern = 'RTC is uninitialized, using current time as timestamp' -severity = "warning" -min_count = 5 -title = "Clock is not set - sensor readings are on hold" -meaning = "The clock chip answers but its clock is not running (it reads 1 Jan 2000; typical after its coin cell was removed or is empty), and there is no Internet time yet. Readings are held until the time is set, so no data is recorded." -actions = [ - "Give the box Internet access: the clock is set automatically on the first connection.", - "If this comes back after every power-off: replace the RTC coin cell (inside the case).", -] -explained_by = ["kota.rtc_stopped"] - -[[rule]] -id = "i2c.errors" -pattern = 'I2C (read|write) failed: (?P\S+)|Failed to add I2C device' -severity = "error" -min_count = 2 -title = "I2C bus communication errors" -meaning = "Sensors are not answering reliably on the I2C bus: loose connector, damaged cable, or a failing sensor." -actions = ["Open the case and check the I2C cable/connectors between the boards."] -explained_by = ["kota.i2c_stuck", "kota.sensor_missing"] - -# ------------------------------------------------------------------ sensors - -[[rule]] -id = "sensor.init_failed" -split_by = "sensor" -pattern = 'MAIN: Failed to initialize (?PBME688|SEN66|AS7341|Microphone|Flicker sensor|DeviceStatus): (?P.+)' -severity = "critical" -title = "{sensor} failed to start" -meaning = "Start-up stopped at the {sensor} sensor ({err}). LED red, the box restarts." -actions = [ - "Open the case and check the {sensor} connector/cable.", - "If several sensors fail in turn: check the shared I2C cable.", -] -explained_by = ["kota.i2c_stuck", "kota.sensor_missing"] -led = "RED" - -[[rule]] -id = "sensor.selfcheck_failed" -split_by = "sensor" -pattern = '(?PBME688|SEN66|AS7341|Microphone|Flicker sensor) self check failed' -severity = "error" -title = "{sensor} failed the post-update self-check" -meaning = "After a firmware update the {sensor} did not work; the box rolls back to the previous firmware." -actions = ["Check the {sensor} connector (inside the case), then power-cycle."] - -[[rule]] -id = "sen66.not_ready" -pattern = 'SEN66: (Failed while awaiting sensor ready|Self-check failed: Sensor not ready|Failed to read measurement)' -severity = "error" -title = "Air-quality sensor (SEN66) not delivering data" -meaning = "The SEN66 answers but never reports a measurement." -actions = ["Check the SEN66 cable (inside the case); the sensor may be faulty."] - -[[rule]] -id = "bme688.read_fail" -pattern = 'Failed to get (BME688 )?data from BME688|No new data available from BME688' -severity = "warning" -min_count = 3 -title = "Temperature/humidity sensor (BME688) read errors" -meaning = "The BME688 repeatedly fails to return a reading." -actions = ["Check the BME688 connection (inside the case)."] - -[[rule]] -id = "as7341.read_fail" -pattern = 'Failed to read (spectral|basic counts) data' -severity = "warning" -min_count = 2 -title = "Light sensor (AS7341) read errors" -meaning = "The AS7341 repeatedly fails to return a reading." -actions = ["Check the AS7341 connection (inside the case)."] - -[[rule]] -id = "mic.silent" -pattern = 'MICROPHONE: .*dB peak: -inf' -severity = "warning" -min_count = 2 -title = "Microphone records pure silence" -meaning = "Every audio sample is zero. A real room is never perfectly silent, so the microphone or its wiring is likely faulty." -actions = [ - "Make some noise near the box and watch whether this finding keeps growing.", - "If it does: check the microphone board/cable (inside the case).", -] - -[[rule]] -id = "mic.read_fail" -pattern = 'Failed to read samples' -severity = "error" -title = "Microphone read error" -meaning = "The audio interface returned an error." -actions = ["Check the microphone board/cable (inside the case)."] - -[[rule]] -id = "gather.stalled" -pattern = 'tasks still running, waiting' -severity = "warning" -min_uptime_ms = 180000 -min_count = 5 -title = "Sensor readings are stuck" -meaning = "A sensor has not finished its reading for minutes (the first 2 minutes of warm-up are normal)." -actions = ["See the grouped cause, or note which sensor stopped logging and check its cable."] -explained_by = ["rtc.clock_not_running", "sen66.not_ready"] - -# ---------------------------------------------------------- OTA / firmware - -[[rule]] -id = "ota.registration_failed" -pattern = 'Device registration failed' -severity = "warning" -title = "Box is not registered for firmware updates" -meaning = "The box could not reach the update server (bin.kota-app.com) to register, so it will not receive new firmware." -actions = ["Fix the network problem; registration is retried automatically every few seconds."] -explained_by = ["net.dns_fail", "net.tls_fail", "matter.not_commissioned", "wifi.disconnected", "wifi.setup_portal"] - -[[rule]] -id = "ota.check_failed" -pattern = 'Failed to check for updates|OTA Update check failed with status code: (?P\d+)' -severity = "error" -title = "Firmware update check failed" -meaning = "The update server could not be reached or refused the request. LED purple." -actions = ["Fix the network problem; the check is retried every 15 minutes."] -explained_by = ["net.dns_fail", "net.tls_fail", "matter.not_commissioned", "wifi.disconnected"] -led = "PURPLE" - -[[rule]] -id = "ota.unauthorized" -pattern = 'Unauthorized, clearing saved token' -severity = "warning" -title = "Update server did not accept the box - it will re-register" -meaning = "The saved update token was rejected; the box registers again automatically." -actions = ["Nothing to do unless it repeats. Then report the box MAC to the backend team."] - -[[rule]] -id = "ota.updating" -pattern = 'New update available, starting OTA update' -severity = "info" -title = "Firmware update is downloading - do not switch off" -meaning = "The box found new firmware and is installing it (LED yellow). It restarts when done." -actions = ["Wait. Do not switch the power off."] -milestone = "ota_update_started" -led = "YELLOW" - -[[rule]] -id = "ota.update_failed" -pattern = 'OTA update failed: (?P\S+)' -severity = "error" -title = "Firmware update failed ({err})" -meaning = "The download or installation was interrupted; the box keeps its current firmware and retries later." -actions = ["Check the network is stable; the update is retried automatically."] - -[[rule]] -id = "ota.update_ok" -pattern = 'OTA update successful, rebooting' -severity = "info" -title = "Firmware update installed - restarting" -meaning = "New firmware was installed. After the restart it runs a self-check (LED orange, ~2.5 min)." -actions = ["Wait for green."] -milestone = "ota_update_done" - -[[rule]] -id = "ota.selfcheck" -pattern = 'Device is in selfcheck mode' -severity = "info" -title = "Post-update self-check running (~2.5 min)" -meaning = "The new firmware is testing all sensors before it is accepted (LED orange)." -actions = ["Wait. Do not switch off."] -led = "ORANGE" - -[[rule]] -id = "ota.selfcheck_failed" -pattern = 'Failing selfcheck: (?P.+)' -severity = "error" -title = "Update self-check failed: {reason}" -meaning = "The new firmware rejected itself; the box rolls back to the previous version." -actions = ["Check the named sensor (inside the case), then power-cycle."] -led = "RED" - -[[rule]] -id = "ota.selfcheck_passed" -pattern = 'Passing selfcheck' -severity = "ignore" -milestone = "ota_update_accepted" -led = "GREEN" - -[[rule]] -id = "ota.reset_during_update" -pattern = 'Device was reset during update' -severity = "warning" -title = "Box was switched off during a firmware update" -meaning = "An update was interrupted; the box reports it and retries." -actions = ["Leave the box powered while the LED is yellow or orange."] - -# --------------------------------------------------------------- system - -[[rule]] -id = "sys.out_of_resources" -pattern = 'Failed to create (task for sensor|gather task|main gather task)' -severity = "error" -title = "Firmware ran out of memory" -meaning = "The firmware could not start an internal task (LED red); it restarts itself after 30 s." -actions = ["Power-cycle. If it repeats, save the session log for the firmware team."] -led = "RED" - -[[rule]] -id = "sys.factory_reset" -pattern = 'Factory reset button pressed|Factory resetting device' -severity = "warning" -title = "Factory reset triggered with the Matter button" -meaning = "The box forgets its Matter network and must be paired again with the QR sticker." -actions = ["Re-commission the box in the Matter app."] -led = "WHITE" - -[[rule]] -id = "mic.disabled" -pattern = 'MICROPHONE: Microphone is in shutdown state' -severity = "info" -title = "Microphone is switched off - no noise measurements" -meaning = "The microphone switch on the back of the case (just above the power switch) is OFF, so noise levels are not recorded." -actions = ["If this box should measure noise: flip the microphone switch (back of the case, above the power switch). No restart needed - it is picked up within about 10 s."] - -[[rule]] -id = "gather.empty_reading" -pattern = 'SENSOR_GATHERER: Failed to get line protocol from sensor' -severity = "warning" -min_count = 2 -title = "A sensor returned no data" -meaning = "One of the sensors skipped its reading; the preceding log lines name the sensor." -actions = ["Check the log lines just before this message for the sensor name."] -explained_by = ["mic.disabled", "mic.read_fail", "bme688.read_fail", "as7341.read_fail", "rtc.clock_not_running"] - -# ------------------------------------------------ structured KOTA: lines -# Firmware 1.0.16+ reports its state as "KOTA: key=value ..." lines. -# These rules have no pattern: the engine raises them from the parsed fields -# (see diag/kota.py for the line formats). Named values in {braces} come -# from the line's fields. - -[[rule]] -id = "kota.i2c_stuck" -severity = "critical" -title = "I2C bus stuck (SDA={sda}, SCL={scl})" -meaning = "A data line of the sensor bus is held low before any sensor is used: a shorted or broken sensor cable, or a faulty sensor. LED blue, 2 pulses." -actions = [ - "Open the case and check the I2C cable between the boards.", - "Unplug the sensors one by one to find the one holding the bus.", -] - -[[rule]] -id = "kota.sensor_missing" -split_by = "sensor" -severity = "error" -title = "{sensor} does not answer on the I2C bus" -meaning = "The start-up scan did not find the {sensor} at address {addr}. The other sensors answer, so the bus itself works: the {sensor} cable, connector or the sensor is faulty." -actions = [ - "Open the case and re-seat the {sensor} cable/connector.", - "If it is still missing after a restart: replace the {sensor}.", -] -explained_by = ["kota.i2c_stuck"] - -[[rule]] -id = "kota.rtc_read_failed" -severity = "critical" -title = "Clock chip (RTC) cannot be read ({err})" -meaning = "The DS1307 clock does not answer. It is on the shared I2C bus, so a loose cable also causes this." -actions = [ - "Open the case and check the I2C cable/connector between the boards.", - "If the cable is fine: replace the RTC module.", -] -explained_by = ["kota.i2c_stuck", "kota.sensor_missing"] - -[[rule]] -id = "kota.rtc_stopped" -severity = "warning" -title = "Clock chip was stopped at start-up (time {time})" -meaning = "The RTC answers but its clock was not running - typical after its coin cell was removed or is empty. It is set again from the Internet once the box is online; until then readings are held." -actions = [ - "Give the box Internet access: the clock is set automatically.", - "If this comes back after every power-off: replace the RTC coin cell (inside the case).", -] - -[[rule]] -id = "kota.rtc_not_ticking" -severity = "error" -title = "Clock chip does not keep time" -meaning = "The clock was set from the Internet, but 2 s later it had not advanced correctly (running={running}, delta={delta}). The RTC module or its crystal is faulty." -actions = ["Replace the RTC module (inside the case)."] - -[[rule]] -id = "kota.sd_unmounted" -severity = "critical" -title = "SD card not mounted ({err})" -meaning = "The firmware cannot use the SD card: missing, not seated, not FAT32 or damaged. Without it the box cannot read ENV.TXT. LED white, fast blinking." -actions = [ - "Open the case and re-seat the SD card.", - "Re-provision the card with the tool (FAT32, MBR) or try a freshly provisioned card.", -] - -[[rule]] -id = "kota.sd_not_fat32" -severity = "warning" -title = "SD card file system is {fs}, not FAT32" -meaning = "The card works, but the provisioning standard is FAT32." -actions = ["Re-provision the card with the tool when convenient."] - -[[rule]] -id = "kota.sd_full" -severity = "warning" -title = "SD card is almost full ({free} free)" -meaning = "Offline readings may soon not be stored." -actions = ["Check the buffered readings upload (Internet access), or replace the card."] - -[[rule]] -id = "kota.buffer_backlog" -severity = "warning" -title = "{lines} readings are waiting on the SD card to be uploaded" -meaning = "The box stored readings while it could not upload them. They are sent automatically once the connection works." -actions = ["Fix the connection problem if there is one; watch the health line: the buffer should shrink."] -explained_by = ["kota.wifi_down", "matter.not_commissioned", "status.conn", "status.conn.1", "net.dns_fail", "influx.auth"] - -[[rule]] -id = "kota.wifi_down" -severity = "error" -min_count = 2 -title = "Box has no Wi-Fi connection" -meaning = "The periodic health report says Wi-Fi is disconnected, so the box cannot upload data or receive updates." -actions = [ - "Check the site Wi-Fi is on, 2.4 GHz and in range.", - "Matter boxes: check the box is still paired in the Matter app. Wi-Fi-from-SD boxes: check SSID/PASSWORD in ENV.TXT.", -] -explained_by = ["matter.not_commissioned", "wifi.setup_portal", "wifi.disconnected"] - -[[rule]] -id = "kota.weak_wifi" -severity = "warning" -title = "Weak Wi-Fi signal ({rssi} dBm)" -meaning = "The connection works but the signal is weak; uploads may fail intermittently. Below about -75 dBm is unreliable." -actions = ["Move the box or the access point closer, or add a repeater."] - -[[rule]] -id = "kota.time_invalid" -severity = "warning" -title = "Box has no valid time - readings are on hold" -meaning = "Readings are only stored with a valid timestamp. The time comes from the RTC or from the Internet." -actions = [ - "Give the box Internet access so it can set its clock.", - "If it is online and this persists: check the RTC findings.", -] -explained_by = ["kota.wifi_down", "matter.not_commissioned", "kota.rtc_not_ticking", "kota.rtc_read_failed", "kota.rtc_stopped", "rtc.clock_not_running", "net.dns_fail"] - -[[rule]] -id = "kota.no_upload" -severity = "error" -title = "Connected to Wi-Fi, but no data uploaded yet (5+ min)" -meaning = "The box has Wi-Fi but no upload to InfluxDB has succeeded yet." -actions = [ - "Check the network has Internet (open a website on a phone on the same Wi-Fi).", - "Check INFLUX_URL / INFLUX_TOKEN in ENV.TXT (re-provision the SD card).", -] -explained_by = ["kota.time_invalid", "influx.auth", "influx.not_found", "status.conn.1", "net.dns_fail", "net.tls_fail", "influx.server_error"] - -[[rule]] -id = "kota.low_heap" -severity = "warning" -title = "Firmware memory is low ({heap} bytes free)" -meaning = "Internal memory is running low; the firmware may fail to start tasks and restart." -actions = ["Save the session log for the firmware team."] - -[[rule]] -id = "kota.ota_unregistered" -severity = "warning" -min_count = 3 -title = "Online, but not registered for firmware updates" -meaning = "The box has Wi-Fi but is still not registered with the update server, so it will not receive new firmware." -actions = ["Check the network allows HTTPS to bin.kota-app.com; registration is retried automatically."] -explained_by = ["ota.registration_failed", "net.dns_fail", "net.tls_fail"] +# Log signatures for the KOTA sensorbox firmware. +# +# Derived from the firmware source (log strings, Status::set_status calls) and +# checked against real captures (tests/fixtures). Patterns match the cleaned +# line text, not the tag: the deployed v1.0 build logs some messages under +# different tags than the source (e.g. SD mount under INFLUX_BUFFER). +# +# Fields: +# id unique, "." +# pattern Python regex (search). Named groups can be used in `title`. +# Matched against the line with its error ID ("[E-SD-03] ") +# removed. Rules without a pattern ("kota.*") are raised by +# the engine from the structured KOTA: lines (diag/kota.py). +# severity critical | error | warning | info | ignore +# ignore = never shown (benign ESP_LOGE lines, LED-only rules) +# title short technician-facing headline +# meaning what it means, in plain words +# actions what to do, in order (prefer no-unscrewing actions first) +# explained_by list of rule ids; while one of them is active this finding +# is shown as a consequence of it instead of a root cause +# milestone boot milestone reached when the line is seen +# clears finding ids resolved when this line is seen +# led status LED colour this line sets (firmware Status::set_status) +# led_from only change the LED if it currently has this colour +# (Status::set_status_conditional) +# split_by named group; one finding per distinct value (e.g. per sensor) +# min_count finding becomes active only after this many occurrences +# min_uptime_ms ignore occurrences earlier than this uptime + +# ---------------------------------------------------------------- milestones + +[[rule]] +id = "boot.app_started" +pattern = 'app_init: Project name:\s+kota-sensorboard' +severity = "ignore" +milestone = "app_started" +led = "GREEN" # Status::init() -> STATUS_OK + +[[rule]] +id = "boot.sd_mounted" +pattern = 'SD card mounted successfully' +severity = "ignore" +milestone = "sd_mounted" + +[[rule]] +id = "boot.env_loaded" +pattern = 'Read INFLUX_URL from \.env file' +severity = "ignore" +milestone = "env_loaded" + +[[rule]] +id = "boot.matter_started" +pattern = 'ESP-Matter started successfully' +severity = "ignore" +milestone = "matter_started" + +[[rule]] +id = "boot.local_wifi_mode" +pattern = 'SSID/PASSWORD found in \.env' +severity = "ignore" +milestone = "local_wifi_mode" + +[[rule]] +id = "boot.sensors_ready" +# DeviceStatus::init installs the temperature sensor; it is the last init +# step, and any earlier init failure aborts before reaching it. +pattern = 'temperature_sensor: Range \[' +severity = "ignore" +milestone = "sensors_ready" + +[[rule]] +id = "net.wifi_connected" +pattern = 'sta ip: \d|got ip:|WIFI_SERVICE: Got IP address|IPv4 address changed|Interface IP Address changed' +severity = "ignore" +milestone = "wifi_connected" +clears = ["matter.not_commissioned", "wifi.disconnected"] + +[[rule]] +id = "matter.commissioned" +pattern = 'MAIN: Commissioning complete|MAIN: Fabric is committed' +severity = "ignore" +milestone = "matter_commissioned" +clears = ["matter.not_commissioned", "matter.commissioning_failed"] + +[[rule]] +id = "net.time_synced" +pattern = 'SNTP: Time synchronized' +severity = "ignore" +milestone = "time_synced" +clears = ["rtc.clock_not_running", "net.dns_fail"] + +[[rule]] +id = "influx.upload_ok" +pattern = 'INFLUX: HTTP request performed, status code: 2\d\d' +severity = "ignore" +milestone = "data_uploaded" +clears = ["influx.buffer_fail", "net.dns_fail", "net.tls_fail"] + +[[rule]] +id = "influx.write_ok" +pattern = 'Successfully wrote to Influx' +severity = "ignore" +milestone = "data_uploaded" +led = "GREEN" +led_from = "PURPLE" + +[[rule]] +id = "ota.registered" +pattern = 'Device registered successfully|OTA token loaded from NVS' +severity = "ignore" +milestone = "ota_registered" +clears = ["ota.registration_failed"] + +[[rule]] +id = "ota.checked" +pattern = 'OTA_CLIENT: No update available' +severity = "ignore" +milestone = "ota_checked" +clears = ["ota.check_failed"] +led = "GREEN" +led_from = "PURPLE" + +# ----------------------------------------------------------- benign E lines + +[[rule]] +id = "benign.mac" +pattern = 'Device MAC address' +severity = "ignore" + +[[rule]] +id = "benign.attr_cb" +pattern = 'MAIN: Attribute update callback' +severity = "ignore" + +[[rule]] +id = "benign.zcl" +pattern = 'chip\[ZCL\]: WRITE ERR|data_model: Cluster cannot be NULL' +severity = "ignore" + +[[rule]] +id = "benign.wifi_start" +# Harmless Wi-Fi noise seen on every 1.0.16 boot: Matter asks for a connect +# while the station is already connecting, and an unpaired box polls a +# network it does not have yet (matter.not_commissioned covers that case). +pattern = 'wifi:sta is connecting, return error|esp_wifi_connect\(\) failed: ESP_ERR_WIFI_CONN|Failed to get configured network when updating network status' +severity = "ignore" + +[[rule]] +id = "benign.connect_chain" +# Transport-level echo of a failure that a more specific rule reports. +pattern = 'esp-tls: Failed to open new connection|transport_base: Failed to open a new connection|HTTP_CLIENT: Connection failed, sock < 0|INFLUX: Failed to perform HTTP request: 0,|INFLUX_BUFFER: Failed to write chunk to Influx|OTA_CLIENT: Error ESP_ERR_HTTP_CONNECT at' +severity = "ignore" + +# ------------------------------------------------------------------ network + +[[rule]] +id = "matter.not_commissioned" +pattern = 'CHIPoBLE advertising started|MAIN: Commissioning window opened' +severity = "warning" +title = "Box is not connected to Wi-Fi (not commissioned in Matter)" +meaning = "The box is waiting to be added to a Matter network over Bluetooth. Until then it has no Wi-Fi, so it cannot upload data or receive firmware updates." +actions = [ + "Add the box in the customer's Matter app (Apple Home, Google Home, ...) by scanning the QR sticker, or enter the manual code printed under it.", + "Use a 2.4 GHz Wi-Fi network; keep the phone close to the box during pairing.", + "If pairing keeps failing: hold the Matter button 5 s (factory reset) and try again.", +] +milestone = "ble_advertising" + +[[rule]] +id = "matter.commissioning_failed" +pattern = 'Commissioning failed, fail safe timer expired' +severity = "error" +title = "Matter pairing attempt failed" +meaning = "A phone started adding the box to Matter but did not finish in time." +actions = [ + "Retry pairing from the Matter app with the phone next to the box.", + "Check the Wi-Fi is 2.4 GHz and the password entered in the app is correct.", +] + +[[rule]] +id = "net.dns_fail" +pattern = '''couldn't get hostname for :(?P[\w.-]+)''' +severity = "error" +title = "No Internet: server names cannot be resolved" +meaning = "The box could not look up a server name. Either it has no Wi-Fi connection, or the network has no working Internet/DNS." +actions = [ + "Check the site Wi-Fi has Internet (open a website on a phone connected to the same network).", + "If the box was never paired: commission it in Matter first.", + "Check the router does not block new devices (guest isolation, MAC filtering).", +] +explained_by = ["matter.not_commissioned", "wifi.disconnected", "wifi.setup_portal"] + +[[rule]] +id = "net.tls_fail" +pattern = 'mbedtls_ssl_handshake returned|Failed to verify peer certificate|esp-tls-mbedtls: .*[Hh]andshake' +severity = "error" +title = "Secure connection to the server failed" +meaning = "The box reached the server but the encrypted (HTTPS) connection failed. Common causes: wrong clock on the box, or a network that intercepts HTTPS (corporate proxy/firewall)." +actions = [ + "Try another network (e.g. phone hotspot) to rule out a firewall.", + "Check the clock finding below: HTTPS needs the correct date.", +] +explained_by = ["rtc.clock_not_running"] + +[[rule]] +id = "wifi.disconnected" +pattern = 'WiFi disconnected, retrying in' +severity = "error" +min_count = 3 +title = "Box cannot connect to the configured Wi-Fi" +meaning = "The Wi-Fi name/password from the SD card does not work: wrong password, network out of range, or not 2.4 GHz." +actions = [ + "Check SSID and PASSWORD in ENV.TXT (re-provision the SD card with the tool).", + "Make sure the network is 2.4 GHz and in range.", +] + +[[rule]] +id = "wifi.setup_portal" +pattern = 'WiFi provisioning portal ready: connect to "(?P[^"]+)"' +severity = "warning" +title = "Box is in Wi-Fi setup mode (network {ssid})" +meaning = "The box is hosting its own Wi-Fi network for configuration instead of connecting to the site Wi-Fi." +actions = [ + "Connect a phone to {ssid} and open http://sensorbox.local/ to enter the Wi-Fi details,", + "or short-press the Matter button (< 2 s) to leave setup mode.", +] +led = "WHITE" + +# ------------------------------------------------------------------- InfluxDB + +[[rule]] +id = "influx.buffer_fail" +pattern = 'Failed to write chunk with \d+ retries to Influx|MAIN: Failed to flush Influx buffer' +severity = "error" +title = "Measurements cannot be uploaded to InfluxDB" +meaning = "Uploads keep failing, so readings are stored on the SD card and sent later. The LED turns purple (firmware v1.0 also restarts itself after about 2.5 minutes in this state)." +actions = [ + "Fix the network problem first (see the finding this is grouped under).", + "Buffered data is uploaded automatically once the connection works.", +] +explained_by = ["net.dns_fail", "net.tls_fail", "matter.not_commissioned", "wifi.disconnected", "wifi.setup_portal"] + +[[rule]] +id = "influx.purple" +pattern = 'Failed to write chunk with \d+ retries to Influx|Failed to flush buffer before writing|Failed to append new entry to buffer' +severity = "ignore" +led = "PURPLE" + +[[rule]] +id = "influx.auth" +pattern = 'INFLUX: Failed to perform HTTP request: (?P401|403)' +severity = "critical" +title = "InfluxDB rejected the token (HTTP {code})" +meaning = "The box reaches the database but the INFLUX_TOKEN on the SD card is wrong or revoked." +actions = [ + "Re-provision the SD card with the current ENV.TXT (SD card is inside the case).", + "If all boxes show this: the shared token was rotated - update ENV.TXT for everyone.", +] + +[[rule]] +id = "influx.not_found" +pattern = 'INFLUX: Failed to perform HTTP request: (?P404)' +severity = "critical" +title = "InfluxDB bucket or organisation not found (HTTP 404)" +meaning = "INFLUX_URL on the SD card points to a bucket/org that does not exist." +actions = ["Re-provision the SD card with the correct ENV.TXT."] + +[[rule]] +id = "influx.server_error" +pattern = 'INFLUX: Failed to perform HTTP request: (?P5\d\d)' +severity = "warning" +title = "InfluxDB server error (HTTP {code})" +meaning = "The database server has a problem. The box is fine; data is buffered on the SD card." +actions = ["Nothing to fix on the box. Report to the backend team if it persists."] + +[[rule]] +id = "influx.bad_request" +pattern = 'INFLUX: Bad request, check line protocol format' +severity = "warning" +title = "InfluxDB refused some measurements (HTTP 400)" +meaning = "A measurement was malformed and dropped (firmware issue, not a hardware fault)." +actions = ["Save the session log and send it to the firmware team."] + +[[rule]] +id = "influx.env_missing" +split_by = "key" +pattern = 'Failed to read (?PINFLUX_URL|INFLUX_TOKEN) from \.env file' +severity = "critical" +title = "ENV.TXT on the SD card is missing {key}" +meaning = "The box cannot start without the InfluxDB settings; it shows white and restarts." +actions = ["Re-provision the SD card with the tool (SD card is inside the case)."] + +[[rule]] +id = "influx.init_fail" +pattern = 'MAIN: Failed to initialize Influx buffer' +severity = "critical" +title = "Data upload could not be initialised" +meaning = "Start-up stopped because the InfluxDB settings could not be read from the SD card. The box restarts." +actions = ["Re-provision the SD card with the tool (SD card is inside the case)."] +explained_by = ["influx.env_missing", "sd.mount_fail", "sd.card_fail", "sd.env_open_fail"] +led = "WHITE" + +# ----------------------------------------------------------------- SD card + +[[rule]] +id = "sd.card_fail" +pattern = 'Failed to initialize the card \((?P[^)]+)\)' +severity = "critical" +title = "SD card not detected ({err})" +meaning = "The SD card does not respond: missing, not seated, or damaged. The box shows white and restarts." +actions = [ + "Open the case and re-seat the SD card.", + "Try a freshly provisioned card.", +] +led = "WHITE" + +[[rule]] +id = "sd.mount_fail" +pattern = 'Failed to mount filesystem' +severity = "critical" +title = "SD card is not formatted correctly" +meaning = "The card responds but has no readable FAT32 file system." +actions = ["Re-provision the card with the tool (FAT32, MBR) - SD card is inside the case."] +led = "WHITE" + +[[rule]] +id = "sd.env_open_fail" +pattern = 'Failed to open \.env file for reading' +severity = "critical" +title = "ENV.TXT not found on the SD card" +meaning = "The card is readable but the configuration file is missing." +actions = ["Re-provision the card with the tool (SD card is inside the case)."] + +[[rule]] +id = "sd.write_fail" +pattern = 'Failed to (create buffer file|open buffer file for appending)' +severity = "error" +title = "Cannot write to the SD card" +meaning = "Offline data cannot be stored: the card is full, write-protected or failing." +actions = ["Replace / re-provision the SD card (inside the case)."] + +# --------------------------------------------------------------- RTC / I2C + +[[rule]] +id = "rtc.init_failed" +pattern = 'MAIN: Failed to initialize RTC: (?P\S+)' +severity = "critical" +title = "Clock chip (RTC) or I2C bus not responding ({err})" +meaning = "The RTC is the first chip the firmware talks to over I2C, so this also appears when the I2C cable between the boards is loose or broken. LED blue, the box restarts." +actions = [ + "Open the case and check the I2C cable/connector between the boards.", + "If the cable is fine: the RTC module is faulty.", +] +explained_by = ["kota.i2c_stuck"] +led = "BLUE" + +[[rule]] +id = "rtc.clock_not_running" +pattern = 'RTC is uninitialized, using current time as timestamp' +severity = "warning" +min_count = 5 +title = "Clock is not set - sensor readings are on hold" +meaning = "The clock chip answers but its clock is not running (it reads 1 Jan 2000; typical after its coin cell was removed or is empty), and there is no Internet time yet. Readings are held until the time is set, so no data is recorded." +actions = [ + "Give the box Internet access: the clock is set automatically on the first connection.", + "If this comes back after every power-off: replace the RTC coin cell (inside the case).", +] +explained_by = ["kota.rtc_stopped"] + +[[rule]] +id = "i2c.errors" +pattern = 'I2C (read|write) failed: (?P\S+)|Failed to add I2C device' +severity = "error" +min_count = 2 +title = "I2C bus communication errors" +meaning = "Sensors are not answering reliably on the I2C bus: loose connector, damaged cable, or a failing sensor." +actions = ["Open the case and check the I2C cable/connectors between the boards."] +explained_by = ["kota.i2c_stuck", "kota.sensor_missing"] + +# ------------------------------------------------------------------ sensors + +[[rule]] +id = "sensor.init_failed" +split_by = "sensor" +pattern = 'MAIN: Failed to initialize (?PBME688|SEN66|AS7341|Microphone|Flicker sensor|DeviceStatus): (?P.+)' +severity = "critical" +title = "{sensor} failed to start" +meaning = "Start-up stopped at the {sensor} sensor ({err}). LED red, the box restarts." +actions = [ + "Open the case and check the {sensor} connector/cable.", + "If several sensors fail in turn: check the shared I2C cable.", +] +explained_by = ["kota.i2c_stuck", "kota.sensor_missing"] +led = "RED" + +[[rule]] +id = "sensor.selfcheck_failed" +split_by = "sensor" +pattern = '(?PBME688|SEN66|AS7341|Microphone|Flicker sensor) self check failed' +severity = "error" +title = "{sensor} failed the post-update self-check" +meaning = "After a firmware update the {sensor} did not work; the box rolls back to the previous firmware." +actions = ["Check the {sensor} connector (inside the case), then power-cycle."] + +[[rule]] +id = "sen66.not_ready" +pattern = 'SEN66: (Failed while awaiting sensor ready|Self-check failed: Sensor not ready|Failed to read measurement)' +severity = "error" +title = "Air-quality sensor (SEN66) not delivering data" +meaning = "The SEN66 answers but never reports a measurement." +actions = ["Check the SEN66 cable (inside the case); the sensor may be faulty."] + +[[rule]] +id = "bme688.read_fail" +pattern = 'Failed to get (BME688 )?data from BME688|No new data available from BME688' +severity = "warning" +min_count = 3 +title = "Temperature/humidity sensor (BME688) read errors" +meaning = "The BME688 repeatedly fails to return a reading." +actions = ["Check the BME688 connection (inside the case)."] + +[[rule]] +id = "as7341.read_fail" +pattern = 'Failed to read (spectral|basic counts) data' +severity = "warning" +min_count = 2 +title = "Light sensor (AS7341) read errors" +meaning = "The AS7341 repeatedly fails to return a reading." +actions = ["Check the AS7341 connection (inside the case)."] + +[[rule]] +id = "mic.silent" +pattern = 'MICROPHONE: .*dB peak: -inf' +severity = "warning" +min_count = 2 +title = "Microphone records pure silence" +meaning = "Every audio sample is zero. A real room is never perfectly silent, so the microphone or its wiring is likely faulty." +actions = [ + "Make some noise near the box and watch whether this finding keeps growing.", + "If it does: check the microphone board/cable (inside the case).", +] + +[[rule]] +id = "mic.read_fail" +pattern = 'Failed to read samples' +severity = "error" +title = "Microphone read error" +meaning = "The audio interface returned an error." +actions = ["Check the microphone board/cable (inside the case)."] + +[[rule]] +id = "gather.stalled" +pattern = 'tasks still running, waiting' +severity = "warning" +min_uptime_ms = 180000 +min_count = 5 +title = "Sensor readings are stuck" +meaning = "A sensor has not finished its reading for minutes (the first 2 minutes of warm-up are normal)." +actions = ["See the grouped cause, or note which sensor stopped logging and check its cable."] +explained_by = ["rtc.clock_not_running", "sen66.not_ready"] + +# ---------------------------------------------------------- OTA / firmware + +[[rule]] +id = "ota.registration_failed" +pattern = 'Device registration failed' +severity = "warning" +title = "Box is not registered for firmware updates" +meaning = "The box could not reach the update server (bin.kota-app.com) to register, so it will not receive new firmware." +actions = ["Fix the network problem; registration is retried automatically every few seconds."] +explained_by = ["net.dns_fail", "net.tls_fail", "matter.not_commissioned", "wifi.disconnected", "wifi.setup_portal"] + +[[rule]] +id = "ota.check_failed" +pattern = 'Failed to check for updates|OTA Update check failed with status code: (?P\d+)' +severity = "error" +title = "Firmware update check failed" +meaning = "The update server could not be reached or refused the request. LED purple." +actions = ["Fix the network problem; the check is retried every 15 minutes."] +explained_by = ["net.dns_fail", "net.tls_fail", "matter.not_commissioned", "wifi.disconnected"] +led = "PURPLE" + +[[rule]] +id = "ota.unauthorized" +pattern = 'Unauthorized, clearing saved token' +severity = "warning" +title = "Update server did not accept the box - it will re-register" +meaning = "The saved update token was rejected; the box registers again automatically." +actions = ["Nothing to do unless it repeats. Then report the box MAC to the backend team."] + +[[rule]] +id = "ota.updating" +pattern = 'New update available, starting OTA update' +severity = "info" +title = "Firmware update is downloading - do not switch off" +meaning = "The box found new firmware and is installing it (LED yellow). It restarts when done." +actions = ["Wait. Do not switch the power off."] +milestone = "ota_update_started" +led = "YELLOW" + +[[rule]] +id = "ota.update_failed" +pattern = 'OTA update failed: (?P\S+)' +severity = "error" +title = "Firmware update failed ({err})" +meaning = "The download or installation was interrupted; the box keeps its current firmware and retries later." +actions = ["Check the network is stable; the update is retried automatically."] + +[[rule]] +id = "ota.update_ok" +pattern = 'OTA update successful, rebooting' +severity = "info" +title = "Firmware update installed - restarting" +meaning = "New firmware was installed. After the restart it runs a self-check (LED orange, ~2.5 min)." +actions = ["Wait for green."] +milestone = "ota_update_done" + +[[rule]] +id = "ota.selfcheck" +pattern = 'Device is in selfcheck mode' +severity = "info" +title = "Post-update self-check running (~2.5 min)" +meaning = "The new firmware is testing all sensors before it is accepted (LED orange)." +actions = ["Wait. Do not switch off."] +led = "ORANGE" + +[[rule]] +id = "ota.selfcheck_failed" +pattern = 'Failing selfcheck: (?P.+)' +severity = "error" +title = "Update self-check failed: {reason}" +meaning = "The new firmware rejected itself; the box rolls back to the previous version." +actions = ["Check the named sensor (inside the case), then power-cycle."] +led = "RED" + +[[rule]] +id = "ota.selfcheck_passed" +pattern = 'Passing selfcheck' +severity = "ignore" +milestone = "ota_update_accepted" +led = "GREEN" + +[[rule]] +id = "ota.reset_during_update" +pattern = 'Device was reset during update' +severity = "warning" +title = "Box was switched off during a firmware update" +meaning = "An update was interrupted; the box reports it and retries." +actions = ["Leave the box powered while the LED is yellow or orange."] + +# --------------------------------------------------------------- system + +[[rule]] +id = "sys.out_of_resources" +pattern = 'Failed to create (task for sensor|gather task|main gather task)' +severity = "error" +title = "Firmware ran out of memory" +meaning = "The firmware could not start an internal task (LED red); it restarts itself after 30 s." +actions = ["Power-cycle. If it repeats, save the session log for the firmware team."] +led = "RED" + +[[rule]] +id = "sys.factory_reset" +pattern = 'Factory reset button pressed|Factory resetting device' +severity = "warning" +title = "Factory reset triggered with the Matter button" +meaning = "The box forgets its Matter network and must be paired again with the QR sticker." +actions = ["Re-commission the box in the Matter app."] +led = "WHITE" + +[[rule]] +id = "mic.disabled" +pattern = 'MICROPHONE: Microphone is in shutdown state' +severity = "info" +title = "Microphone is switched off - no noise measurements" +meaning = "The microphone switch on the back of the case (just above the power switch) is OFF, so noise levels are not recorded." +actions = ["If this box should measure noise: flip the microphone switch (back of the case, above the power switch). No restart needed - it is picked up within about 10 s."] + +[[rule]] +id = "gather.empty_reading" +pattern = 'SENSOR_GATHERER: Failed to get line protocol from sensor' +severity = "warning" +min_count = 2 +title = "A sensor returned no data" +meaning = "One of the sensors skipped its reading; the preceding log lines name the sensor." +actions = ["Check the log lines just before this message for the sensor name."] +explained_by = ["mic.disabled", "mic.read_fail", "bme688.read_fail", "as7341.read_fail", "rtc.clock_not_running"] + +# ------------------------------------------------ structured KOTA: lines +# Firmware 1.0.16+ reports its state as "KOTA: key=value ..." lines. +# These rules have no pattern: the engine raises them from the parsed fields +# (see diag/kota.py for the line formats). Named values in {braces} come +# from the line's fields. + +[[rule]] +id = "kota.i2c_stuck" +severity = "critical" +title = "I2C bus stuck (SDA={sda}, SCL={scl})" +meaning = "A data line of the sensor bus is held low before any sensor is used: a shorted or broken sensor cable, or a faulty sensor. LED blue, 2 pulses." +actions = [ + "Open the case and check the I2C cable between the boards.", + "Unplug the sensors one by one to find the one holding the bus.", +] + +[[rule]] +id = "kota.sensor_missing" +split_by = "sensor" +severity = "error" +title = "{sensor} does not answer on the I2C bus" +meaning = "The start-up scan did not find the {sensor} at address {addr}. The other sensors answer, so the bus itself works: the {sensor} cable, connector or the sensor is faulty." +actions = [ + "Open the case and re-seat the {sensor} cable/connector.", + "If it is still missing after a restart: replace the {sensor}.", +] +explained_by = ["kota.i2c_stuck"] + +[[rule]] +id = "kota.rtc_read_failed" +severity = "critical" +title = "Clock chip (RTC) cannot be read ({err})" +meaning = "The DS1307 clock does not answer. It is on the shared I2C bus, so a loose cable also causes this." +actions = [ + "Open the case and check the I2C cable/connector between the boards.", + "If the cable is fine: replace the RTC module.", +] +explained_by = ["kota.i2c_stuck", "kota.sensor_missing"] + +[[rule]] +id = "kota.rtc_stopped" +severity = "warning" +title = "Clock chip was stopped at start-up (time {time})" +meaning = "The RTC answers but its clock was not running - typical after its coin cell was removed or is empty. It is set again from the Internet once the box is online; until then readings are held." +actions = [ + "Give the box Internet access: the clock is set automatically.", + "If this comes back after every power-off: replace the RTC coin cell (inside the case).", +] + +[[rule]] +id = "kota.rtc_not_ticking" +severity = "error" +title = "Clock chip does not keep time" +meaning = "The clock was set from the Internet, but 2 s later it had not advanced correctly (running={running}, delta={delta}). The RTC module or its crystal is faulty." +actions = ["Replace the RTC module (inside the case)."] + +[[rule]] +id = "kota.sd_unmounted" +severity = "critical" +title = "SD card not mounted ({err})" +meaning = "The firmware cannot use the SD card: missing, not seated, not FAT32 or damaged. Without it the box cannot read ENV.TXT. LED white, fast blinking." +actions = [ + "Open the case and re-seat the SD card.", + "Re-provision the card with the tool (FAT32, MBR) or try a freshly provisioned card.", +] + +[[rule]] +id = "kota.sd_not_fat32" +severity = "warning" +title = "SD card file system is {fs}, not FAT32" +meaning = "The card works, but the provisioning standard is FAT32." +actions = ["Re-provision the card with the tool when convenient."] + +[[rule]] +id = "kota.sd_full" +severity = "warning" +title = "SD card is almost full ({free} free)" +meaning = "Offline readings may soon not be stored." +actions = ["Check the buffered readings upload (Internet access), or replace the card."] + +[[rule]] +id = "kota.buffer_backlog" +severity = "warning" +title = "{lines} readings are waiting on the SD card to be uploaded" +meaning = "The box stored readings while it could not upload them. They are sent automatically once the connection works." +actions = ["Fix the connection problem if there is one; watch the health line: the buffer should shrink."] +explained_by = ["kota.wifi_down", "matter.not_commissioned", "status.conn", "status.conn.1", "net.dns_fail", "influx.auth"] + +[[rule]] +id = "kota.wifi_down" +severity = "error" +min_count = 2 +title = "Box has no Wi-Fi connection" +meaning = "The periodic health report says Wi-Fi is disconnected, so the box cannot upload data or receive updates." +actions = [ + "Check the site Wi-Fi is on, 2.4 GHz and in range.", + "Matter boxes: check the box is still paired in the Matter app. Wi-Fi-from-SD boxes: check SSID/PASSWORD in ENV.TXT.", +] +explained_by = ["matter.not_commissioned", "wifi.setup_portal", "wifi.disconnected"] + +[[rule]] +id = "kota.weak_wifi" +severity = "warning" +title = "Weak Wi-Fi signal ({rssi} dBm)" +meaning = "The connection works but the signal is weak; uploads may fail intermittently. Below about -75 dBm is unreliable." +actions = ["Move the box or the access point closer, or add a repeater."] + +[[rule]] +id = "kota.time_invalid" +severity = "warning" +title = "Box has no valid time - readings are on hold" +meaning = "Readings are only stored with a valid timestamp. The time comes from the RTC or from the Internet." +actions = [ + "Give the box Internet access so it can set its clock.", + "If it is online and this persists: check the RTC findings.", +] +explained_by = ["kota.wifi_down", "matter.not_commissioned", "kota.rtc_not_ticking", "kota.rtc_read_failed", "kota.rtc_stopped", "rtc.clock_not_running", "net.dns_fail"] + +[[rule]] +id = "kota.no_upload" +severity = "error" +title = "Connected to Wi-Fi, but no data uploaded yet (5+ min)" +meaning = "The box has Wi-Fi but no upload to InfluxDB has succeeded yet." +actions = [ + "Check the network has Internet (open a website on a phone on the same Wi-Fi).", + "Check INFLUX_URL / INFLUX_TOKEN in ENV.TXT (re-provision the SD card).", +] +explained_by = ["kota.time_invalid", "influx.auth", "influx.not_found", "status.conn.1", "net.dns_fail", "net.tls_fail", "influx.server_error"] + +[[rule]] +id = "kota.low_heap" +severity = "warning" +title = "Firmware memory is low ({heap} bytes free)" +meaning = "Internal memory is running low; the firmware may fail to start tasks and restart." +actions = ["Save the session log for the firmware team."] + +[[rule]] +id = "kota.ota_unregistered" +severity = "warning" +min_count = 3 +title = "Online, but not registered for firmware updates" +meaning = "The box has Wi-Fi but is still not registered with the update server, so it will not receive new firmware." +actions = ["Check the network allows HTTPS to bin.kota-app.com; registration is retried automatically."] +explained_by = ["ota.registration_failed", "net.dns_fail", "net.tls_fail"] diff --git a/kota_tech/flash/image.py b/kota_tech/flash/image.py index aa3ab5e..befc2f9 100644 --- a/kota_tech/flash/image.py +++ b/kota_tech/flash/image.py @@ -1,260 +1,260 @@ -"""Firmware images: validation, integrity, layout and the firmware folder. - -The tool flashes ONLY the .bin files in its own firmware/ folder (next to -kota_tech/). There is no import and no per-user library: to offer a build, -the firmware team puts the file into that folder. Images are listed by the -compile date stored in the firmware itself (ESP-IDF app descriptor), newest -first, so the file name does not matter. - -Two kinds: - * merged - bootloader at 0x0 + partition table + app (merged-binary.bin); - App-only or Full flashing; - * app - the application binary only (build/kota-sensorboard.bin); - App-only flashing, laid out like a merged image in the folder. - -Every image is checked structurally (ESP32-S3, kota-sensorboard app -descriptor, segment checksum and the appended SHA-256) so a truncated or -corrupted file is refused before anything is written. -""" - -from __future__ import annotations - -import datetime as dt -import hashlib -import struct -from dataclasses import dataclass -from pathlib import Path - -FIRMWARE_DIR = Path(__file__).resolve().parents[2] / "firmware" - -PT_OFFSET = 0x8000 -PT_SIZE = 0xC00 -ESP32S3_CHIP_ID = 9 -IMAGE_MAGIC = 0xE9 -APP_DESC_MAGIC = 0xABCD5432 -FLASH_SIZES = {0: "1MB", 1: "2MB", 2: "4MB", 3: "8MB", 4: "16MB", 5: "32MB"} -_MONTHS = {m: i for i, m in enumerate( - ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], 1)} - - -class ImageError(ValueError): - pass - - -@dataclass(frozen=True) -class Partition: - label: str - type: int - subtype: int - offset: int - size: int - - -@dataclass(frozen=True) -class AppInfo: - project: str - version: str - built: str # "Sep 28 2026 15:07:57" as in the app descriptor - idf: str - elf_sha: str # hex - - @property - def built_at(self) -> dt.datetime | None: - try: - mon, day, year, hms = self.built.split() - h, m, s = (int(x) for x in hms.split(":")) - return dt.datetime(int(year), _MONTHS[mon], int(day), h, m, s) - except (ValueError, KeyError): - return None - - @property - def compiled(self) -> str: - """'2026-09-28 15:07' (ISO-like, sortable) or the raw descriptor text.""" - t = self.built_at - return t.strftime("%Y-%m-%d %H:%M") if t else self.built - - -@dataclass(frozen=True) -class FirmwareImage: - path: Path - kind: str # "merged" | "app" - sha256: str - flash_size: str - partitions: tuple[Partition, ...] - partition_table: bytes # expected table on the box - app_offset: int # where the app starts inside the file - app_length: int # exact app image length (from its structure) - app: AppInfo - - @property - def name(self) -> str: - return self.path.name - - @property - def data(self) -> bytes: - return self.path.read_bytes() - - def partition(self, label: str) -> Partition: - for p in self.partitions: - if p.label == label: - return p - raise ImageError(f"partition {label!r} not in image") - - def app_bytes(self) -> bytes: - return self.data[self.app_offset:self.app_offset + self.app_length] - - def blank_otadata(self) -> bytes: - """Erased otadata: the bootloader then boots ota_0.""" - return b"\xff" * self.partition("otadata").size - - def label(self) -> str: - """Choice text: compile date first (that is what the technician picks by).""" - kind = "" if self.kind == "merged" else ", app only" - return f"Compiled {self.app.compiled} - v{self.app.version} [{self.app.elf_sha[:8]}]{kind} ({self.name})" - - -# ---------------------------------------------------------------- parsing - -def parse_partition_table(raw: bytes) -> tuple[Partition, ...]: - parts = [] - for i in range(0, len(raw) - 31, 32): - entry = raw[i:i + 32] - magic = entry[:2] - if magic == b"\xaa\x50": - _, ptype, sub, off, size = struct.unpack_from(" int: - """Validate an ESP app image at `offset`; return its exact length. - - Checks the segment checksum and, when the header says so, the appended - SHA-256 - the same checks the bootloader performs before booting it. - """ - if len(data) < offset + 24 or data[offset] != IMAGE_MAGIC: - raise ImageError(f"no firmware image at 0x{offset:x}") - segments = data[offset + 1] - chip_id = struct.unpack_from(" len(data): - raise ImageError("image is truncated (segment header missing)") - _, seg_len = struct.unpack_from(" len(data): - raise ImageError("image is truncated (segment data missing)") - for b in data[pos:pos + seg_len]: - checksum ^= b - pos += seg_len - pos += 15 - ((pos - offset) % 16) # pad so the checksum ends a 16-byte block - if pos >= len(data) or data[pos] != checksum: - raise ImageError("image checksum is wrong - the file is corrupted") - pos += 1 - if hash_appended: - if pos + 32 > len(data): - raise ImageError("image is truncated (SHA-256 missing)") - if hashlib.sha256(data[offset:pos]).digest() != data[pos:pos + 32]: - raise ImageError("image SHA-256 does not match its content - the file is corrupted") - pos += 32 - return pos - offset - - -def _app_info(data: bytes, offset: int) -> AppInfo: - d = data[offset + 0x20:offset + 0x20 + 256] - if len(d) < 256 or struct.unpack_from(" FirmwareImage: - """Validate a merged or app-only image. App-only images need a `reference` - (a merged image) that provides the flash layout.""" - data = path.read_bytes() - if len(data) >= PT_OFFSET + PT_SIZE and data[0] == IMAGE_MAGIC and data[PT_OFFSET:PT_OFFSET + 2] == b"\xaa\x50": - app_image_length(data, 0) # bootloader must be intact - table = data[PT_OFFSET:PT_OFFSET + PT_SIZE] - parts = parse_partition_table(table) - labels = {p.label for p in parts} - for need in ("nvs", "otadata", "ota_0"): - if need not in labels: - raise ImageError(f"partition table has no {need!r} partition") - ota0 = next(p for p in parts if p.label == "ota_0") - kind, app_off, flash_size = "merged", ota0.offset, FLASH_SIZES.get(data[3] >> 4, "?") - elif data[:1] == bytes([IMAGE_MAGIC]): - if reference is None: - raise ImageError("app-only file, but there is no complete (merged) image in the folder " - "to take the flash layout from") - kind, app_off = "app", 0 - parts, table, flash_size = reference.partitions, reference.partition_table, reference.flash_size - ota0 = next(p for p in parts if p.label == "ota_0") - else: - raise ImageError("not an ESP32 firmware file") - length = app_image_length(data, app_off) - if length > ota0.size: - raise ImageError(f"firmware ({length} bytes) does not fit the ota_0 partition ({ota0.size} bytes)") - app = _app_info(data, app_off) - return FirmwareImage(path, kind, hashlib.sha256(data).hexdigest(), - flash_size, parts, table, app_off, length, app) - - -# ------------------------------------------------------------- catalogue - -def folder_images(folder: Path = FIRMWARE_DIR) -> tuple[list[FirmwareImage], list[str]]: - """(valid images newest-compiled first, problems) for the .bin files in `folder`. - - A broken or foreign file is reported, never offered. The same build - under two file names is offered once. - """ - if not folder.is_dir(): - return [], [f"firmware folder not found: {folder}"] - files = sorted(p for p in folder.iterdir() if p.is_file() and p.suffix.lower() == ".bin") - merged: list[FirmwareImage] = [] - app_only: list[Path] = [] - problems: list[str] = [] - for f in files: - try: - merged.append(inspect_image(f)) - except ImageError as exc: - if "app-only" in str(exc): - app_only.append(f) - else: - problems.append(f"{f.name}: {exc}") - except OSError as exc: - problems.append(f"{f.name}: cannot read ({exc})") - merged.sort(key=_newest_first) - reference = merged[0] if merged else None - images = list(merged) - for f in app_only: - try: - images.append(inspect_image(f, reference)) - except (ImageError, OSError) as exc: - problems.append(f"{f.name}: {exc}") - images.sort(key=_newest_first) - unique: dict[str, FirmwareImage] = {} - for img in images: - if img.sha256 in unique: - problems.append(f"{img.name}: same file as {unique[img.sha256].name} (listed once)") - else: - unique[img.sha256] = img - return list(unique.values()), problems - - -def _newest_first(img: FirmwareImage) -> tuple: - # Unknown compile dates last; merged before app-only for the same date. - t = img.app.built_at - return (t is None, -t.timestamp() if t else 0.0, img.kind != "merged", img.name) +"""Firmware images: validation, integrity, layout and the firmware folder. + +The tool flashes ONLY the .bin files in its own firmware/ folder (next to +kota_tech/). There is no import and no per-user library: to offer a build, +the firmware team puts the file into that folder. Images are listed by the +compile date stored in the firmware itself (ESP-IDF app descriptor), newest +first, so the file name does not matter. + +Two kinds: + * merged - bootloader at 0x0 + partition table + app (merged-binary.bin); + App-only or Full flashing; + * app - the application binary only (build/kota-sensorboard.bin); + App-only flashing, laid out like a merged image in the folder. + +Every image is checked structurally (ESP32-S3, kota-sensorboard app +descriptor, segment checksum and the appended SHA-256) so a truncated or +corrupted file is refused before anything is written. +""" + +from __future__ import annotations + +import datetime as dt +import hashlib +import struct +from dataclasses import dataclass +from pathlib import Path + +FIRMWARE_DIR = Path(__file__).resolve().parents[2] / "firmware" + +PT_OFFSET = 0x8000 +PT_SIZE = 0xC00 +ESP32S3_CHIP_ID = 9 +IMAGE_MAGIC = 0xE9 +APP_DESC_MAGIC = 0xABCD5432 +FLASH_SIZES = {0: "1MB", 1: "2MB", 2: "4MB", 3: "8MB", 4: "16MB", 5: "32MB"} +_MONTHS = {m: i for i, m in enumerate( + ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], 1)} + + +class ImageError(ValueError): + pass + + +@dataclass(frozen=True) +class Partition: + label: str + type: int + subtype: int + offset: int + size: int + + +@dataclass(frozen=True) +class AppInfo: + project: str + version: str + built: str # "Sep 28 2026 15:07:57" as in the app descriptor + idf: str + elf_sha: str # hex + + @property + def built_at(self) -> dt.datetime | None: + try: + mon, day, year, hms = self.built.split() + h, m, s = (int(x) for x in hms.split(":")) + return dt.datetime(int(year), _MONTHS[mon], int(day), h, m, s) + except (ValueError, KeyError): + return None + + @property + def compiled(self) -> str: + """'2026-09-28 15:07' (ISO-like, sortable) or the raw descriptor text.""" + t = self.built_at + return t.strftime("%Y-%m-%d %H:%M") if t else self.built + + +@dataclass(frozen=True) +class FirmwareImage: + path: Path + kind: str # "merged" | "app" + sha256: str + flash_size: str + partitions: tuple[Partition, ...] + partition_table: bytes # expected table on the box + app_offset: int # where the app starts inside the file + app_length: int # exact app image length (from its structure) + app: AppInfo + + @property + def name(self) -> str: + return self.path.name + + @property + def data(self) -> bytes: + return self.path.read_bytes() + + def partition(self, label: str) -> Partition: + for p in self.partitions: + if p.label == label: + return p + raise ImageError(f"partition {label!r} not in image") + + def app_bytes(self) -> bytes: + return self.data[self.app_offset:self.app_offset + self.app_length] + + def blank_otadata(self) -> bytes: + """Erased otadata: the bootloader then boots ota_0.""" + return b"\xff" * self.partition("otadata").size + + def label(self) -> str: + """Choice text: compile date first (that is what the technician picks by).""" + kind = "" if self.kind == "merged" else ", app only" + return f"Compiled {self.app.compiled} - v{self.app.version} [{self.app.elf_sha[:8]}]{kind} ({self.name})" + + +# ---------------------------------------------------------------- parsing + +def parse_partition_table(raw: bytes) -> tuple[Partition, ...]: + parts = [] + for i in range(0, len(raw) - 31, 32): + entry = raw[i:i + 32] + magic = entry[:2] + if magic == b"\xaa\x50": + _, ptype, sub, off, size = struct.unpack_from(" int: + """Validate an ESP app image at `offset`; return its exact length. + + Checks the segment checksum and, when the header says so, the appended + SHA-256 - the same checks the bootloader performs before booting it. + """ + if len(data) < offset + 24 or data[offset] != IMAGE_MAGIC: + raise ImageError(f"no firmware image at 0x{offset:x}") + segments = data[offset + 1] + chip_id = struct.unpack_from(" len(data): + raise ImageError("image is truncated (segment header missing)") + _, seg_len = struct.unpack_from(" len(data): + raise ImageError("image is truncated (segment data missing)") + for b in data[pos:pos + seg_len]: + checksum ^= b + pos += seg_len + pos += 15 - ((pos - offset) % 16) # pad so the checksum ends a 16-byte block + if pos >= len(data) or data[pos] != checksum: + raise ImageError("image checksum is wrong - the file is corrupted") + pos += 1 + if hash_appended: + if pos + 32 > len(data): + raise ImageError("image is truncated (SHA-256 missing)") + if hashlib.sha256(data[offset:pos]).digest() != data[pos:pos + 32]: + raise ImageError("image SHA-256 does not match its content - the file is corrupted") + pos += 32 + return pos - offset + + +def _app_info(data: bytes, offset: int) -> AppInfo: + d = data[offset + 0x20:offset + 0x20 + 256] + if len(d) < 256 or struct.unpack_from(" FirmwareImage: + """Validate a merged or app-only image. App-only images need a `reference` + (a merged image) that provides the flash layout.""" + data = path.read_bytes() + if len(data) >= PT_OFFSET + PT_SIZE and data[0] == IMAGE_MAGIC and data[PT_OFFSET:PT_OFFSET + 2] == b"\xaa\x50": + app_image_length(data, 0) # bootloader must be intact + table = data[PT_OFFSET:PT_OFFSET + PT_SIZE] + parts = parse_partition_table(table) + labels = {p.label for p in parts} + for need in ("nvs", "otadata", "ota_0"): + if need not in labels: + raise ImageError(f"partition table has no {need!r} partition") + ota0 = next(p for p in parts if p.label == "ota_0") + kind, app_off, flash_size = "merged", ota0.offset, FLASH_SIZES.get(data[3] >> 4, "?") + elif data[:1] == bytes([IMAGE_MAGIC]): + if reference is None: + raise ImageError("app-only file, but there is no complete (merged) image in the folder " + "to take the flash layout from") + kind, app_off = "app", 0 + parts, table, flash_size = reference.partitions, reference.partition_table, reference.flash_size + ota0 = next(p for p in parts if p.label == "ota_0") + else: + raise ImageError("not an ESP32 firmware file") + length = app_image_length(data, app_off) + if length > ota0.size: + raise ImageError(f"firmware ({length} bytes) does not fit the ota_0 partition ({ota0.size} bytes)") + app = _app_info(data, app_off) + return FirmwareImage(path, kind, hashlib.sha256(data).hexdigest(), + flash_size, parts, table, app_off, length, app) + + +# ------------------------------------------------------------- catalogue + +def folder_images(folder: Path = FIRMWARE_DIR) -> tuple[list[FirmwareImage], list[str]]: + """(valid images newest-compiled first, problems) for the .bin files in `folder`. + + A broken or foreign file is reported, never offered. The same build + under two file names is offered once. + """ + if not folder.is_dir(): + return [], [f"firmware folder not found: {folder}"] + files = sorted(p for p in folder.iterdir() if p.is_file() and p.suffix.lower() == ".bin") + merged: list[FirmwareImage] = [] + app_only: list[Path] = [] + problems: list[str] = [] + for f in files: + try: + merged.append(inspect_image(f)) + except ImageError as exc: + if "app-only" in str(exc): + app_only.append(f) + else: + problems.append(f"{f.name}: {exc}") + except OSError as exc: + problems.append(f"{f.name}: cannot read ({exc})") + merged.sort(key=_newest_first) + reference = merged[0] if merged else None + images = list(merged) + for f in app_only: + try: + images.append(inspect_image(f, reference)) + except (ImageError, OSError) as exc: + problems.append(f"{f.name}: {exc}") + images.sort(key=_newest_first) + unique: dict[str, FirmwareImage] = {} + for img in images: + if img.sha256 in unique: + problems.append(f"{img.name}: same file as {unique[img.sha256].name} (listed once)") + else: + unique[img.sha256] = img + return list(unique.values()), problems + + +def _newest_first(img: FirmwareImage) -> tuple: + # Unknown compile dates last; merged before app-only for the same date. + t = img.app.built_at + return (t is None, -t.timestamp() if t else 0.0, img.kind != "merged", img.name) diff --git a/kota_tech/matter/payload.py b/kota_tech/matter/payload.py index e95c3ed..2326165 100644 --- a/kota_tech/matter/payload.py +++ b/kota_tech/matter/payload.py @@ -1,113 +1,113 @@ -"""Matter onboarding payloads: the "MT:..." QR string and the 11-digit manual code. - -Implements the Matter spec (5.1.3 QR payload, 5.1.4 manual pairing code): -bit fields packed LSB-first, Base38 in 3-byte chunks, Verhoeff check digit. - -The sensorbox firmware uses the Matter SDK example commissionable data -(CONFIG_EXAMPLE_COMMISSIONABLE_DATA_PROVIDER): every v1.0 box advertises -VID 0xFFF1 / PID 0x8000 / discriminator 3840 with passcode 20202021, which is -exactly the sticker (QR "MT:Y.K9042C00KA0648G00", manual 3497-011-2332). -VID/PID/discriminator are taken from the box's own log when available; the -passcode is never logged, so the known default is used. -""" - -from __future__ import annotations - -import re -from dataclasses import dataclass - -BASE38 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-." -RENDEZVOUS_BLE = 1 << 1 -DEFAULT_PASSCODE = 20202021 # Matter SDK test passcode used by the v1.0 firmware -DEFAULT = dict(vendor_id=0xFFF1, product_id=0x8000, discriminator=3840) - -# chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 -_ADVERTISE = re.compile(r"vendorID=(\d+) productID=(\d+) discriminator=(\d+)") - - -@dataclass(frozen=True) -class SetupPayload: - vendor_id: int = DEFAULT["vendor_id"] - product_id: int = DEFAULT["product_id"] - discriminator: int = DEFAULT["discriminator"] - passcode: int = DEFAULT_PASSCODE - rendezvous: int = RENDEZVOUS_BLE - flow: int = 0 # standard commissioning flow - version: int = 0 - from_log: bool = False # VID/PID/discriminator read from this box's log - qr: str | None = None # exact payload reported by the box ("KOTA: matter qr=MT:...") - manual: str | None = None # exact 11-digit manual code reported by the box - - @property - def from_box(self) -> bool: - return self.qr is not None - - def qr_text(self) -> str: - if self.qr: - return self.qr - fields = [(self.version, 3), (self.vendor_id, 16), (self.product_id, 16), (self.flow, 2), - (self.rendezvous, 8), (self.discriminator, 12), (self.passcode, 27), (0, 4)] - value, shift = 0, 0 - for v, bits in fields: - if not 0 <= v < (1 << bits): - raise ValueError(f"field value {v} does not fit in {bits} bits") - value |= v << shift - shift += bits - return "MT:" + _base38(value.to_bytes(shift // 8, "little")) - - def manual_code(self) -> str: - if self.manual: - return self.manual - short = self.discriminator >> 8 # upper 4 bits - chunk1 = short >> 2 # vid_pid_present = 0 - chunk2 = ((short & 0x3) << 14) | (self.passcode & 0x3FFF) - chunk3 = self.passcode >> 14 - digits = f"{chunk1}{chunk2:05d}{chunk3:04d}" - return digits + str(verhoeff_check(digits)) - - def manual_code_pretty(self) -> str: - c = self.manual_code() - return f"{c[:4]}-{c[4:7]}-{c[7:]}" - - -def _base38(data: bytes) -> str: - out = [] - for i in range(0, len(data), 3): - chunk = data[i:i + 3] - value = int.from_bytes(chunk, "little") - for _ in range({3: 5, 2: 4, 1: 2}[len(chunk)]): - value, rem = divmod(value, 38) - out.append(BASE38[rem]) - return "".join(out) - - -_D = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 0, 6, 7, 8, 9, 5], [2, 3, 4, 0, 1, 7, 8, 9, 5, 6], - [3, 4, 0, 1, 2, 8, 9, 5, 6, 7], [4, 0, 1, 2, 3, 9, 5, 6, 7, 8], [5, 9, 8, 7, 6, 0, 4, 3, 2, 1], - [6, 5, 9, 8, 7, 1, 0, 4, 3, 2], [7, 6, 5, 9, 8, 2, 1, 0, 4, 3], [8, 7, 6, 5, 9, 3, 2, 1, 0, 4], - [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]] -_P = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 5, 7, 6, 2, 8, 3, 0, 9, 4], [5, 8, 0, 3, 7, 9, 6, 1, 4, 2], - [8, 9, 1, 6, 0, 4, 3, 5, 2, 7], [9, 4, 5, 3, 1, 2, 6, 8, 7, 0], [4, 2, 8, 6, 5, 7, 3, 9, 0, 1], - [2, 7, 9, 3, 8, 0, 6, 4, 1, 5], [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]] -_INV = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9] - - -def verhoeff_check(digits: str) -> int: - c = 0 - for i, d in enumerate(reversed(digits)): - c = _D[c][_P[(i + 1) % 8][int(d)]] - return _INV[c] - - -def payload_from_log(lines: list[str], qr: str | None = None, manual: str | None = None) -> SetupPayload: - """Firmware 1.0.16+ reports its exact QR payload and manual code - ("KOTA: matter ... qr=MT:... manual=..."); older firmware: use the - advertised VID/PID/discriminator if seen; defaults otherwise.""" - base = SetupPayload() - for text in reversed(lines): - if m := _ADVERTISE.search(text): - base = SetupPayload(int(m.group(1)), int(m.group(2)), int(m.group(3)), from_log=True) - break - if qr: - return SetupPayload(base.vendor_id, base.product_id, base.discriminator, from_log=True, - qr=qr, manual=manual if manual and manual.isdigit() else None) - return base +"""Matter onboarding payloads: the "MT:..." QR string and the 11-digit manual code. + +Implements the Matter spec (5.1.3 QR payload, 5.1.4 manual pairing code): +bit fields packed LSB-first, Base38 in 3-byte chunks, Verhoeff check digit. + +The sensorbox firmware uses the Matter SDK example commissionable data +(CONFIG_EXAMPLE_COMMISSIONABLE_DATA_PROVIDER): every v1.0 box advertises +VID 0xFFF1 / PID 0x8000 / discriminator 3840 with passcode 20202021, which is +exactly the sticker (QR "MT:Y.K9042C00KA0648G00", manual 3497-011-2332). +VID/PID/discriminator are taken from the box's own log when available; the +passcode is never logged, so the known default is used. +""" + +from __future__ import annotations + +import re +from dataclasses import dataclass + +BASE38 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-." +RENDEZVOUS_BLE = 1 << 1 +DEFAULT_PASSCODE = 20202021 # Matter SDK test passcode used by the v1.0 firmware +DEFAULT = dict(vendor_id=0xFFF1, product_id=0x8000, discriminator=3840) + +# chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 +_ADVERTISE = re.compile(r"vendorID=(\d+) productID=(\d+) discriminator=(\d+)") + + +@dataclass(frozen=True) +class SetupPayload: + vendor_id: int = DEFAULT["vendor_id"] + product_id: int = DEFAULT["product_id"] + discriminator: int = DEFAULT["discriminator"] + passcode: int = DEFAULT_PASSCODE + rendezvous: int = RENDEZVOUS_BLE + flow: int = 0 # standard commissioning flow + version: int = 0 + from_log: bool = False # VID/PID/discriminator read from this box's log + qr: str | None = None # exact payload reported by the box ("KOTA: matter qr=MT:...") + manual: str | None = None # exact 11-digit manual code reported by the box + + @property + def from_box(self) -> bool: + return self.qr is not None + + def qr_text(self) -> str: + if self.qr: + return self.qr + fields = [(self.version, 3), (self.vendor_id, 16), (self.product_id, 16), (self.flow, 2), + (self.rendezvous, 8), (self.discriminator, 12), (self.passcode, 27), (0, 4)] + value, shift = 0, 0 + for v, bits in fields: + if not 0 <= v < (1 << bits): + raise ValueError(f"field value {v} does not fit in {bits} bits") + value |= v << shift + shift += bits + return "MT:" + _base38(value.to_bytes(shift // 8, "little")) + + def manual_code(self) -> str: + if self.manual: + return self.manual + short = self.discriminator >> 8 # upper 4 bits + chunk1 = short >> 2 # vid_pid_present = 0 + chunk2 = ((short & 0x3) << 14) | (self.passcode & 0x3FFF) + chunk3 = self.passcode >> 14 + digits = f"{chunk1}{chunk2:05d}{chunk3:04d}" + return digits + str(verhoeff_check(digits)) + + def manual_code_pretty(self) -> str: + c = self.manual_code() + return f"{c[:4]}-{c[4:7]}-{c[7:]}" + + +def _base38(data: bytes) -> str: + out = [] + for i in range(0, len(data), 3): + chunk = data[i:i + 3] + value = int.from_bytes(chunk, "little") + for _ in range({3: 5, 2: 4, 1: 2}[len(chunk)]): + value, rem = divmod(value, 38) + out.append(BASE38[rem]) + return "".join(out) + + +_D = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 0, 6, 7, 8, 9, 5], [2, 3, 4, 0, 1, 7, 8, 9, 5, 6], + [3, 4, 0, 1, 2, 8, 9, 5, 6, 7], [4, 0, 1, 2, 3, 9, 5, 6, 7, 8], [5, 9, 8, 7, 6, 0, 4, 3, 2, 1], + [6, 5, 9, 8, 7, 1, 0, 4, 3, 2], [7, 6, 5, 9, 8, 2, 1, 0, 4, 3], [8, 7, 6, 5, 9, 3, 2, 1, 0, 4], + [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]] +_P = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 5, 7, 6, 2, 8, 3, 0, 9, 4], [5, 8, 0, 3, 7, 9, 6, 1, 4, 2], + [8, 9, 1, 6, 0, 4, 3, 5, 2, 7], [9, 4, 5, 3, 1, 2, 6, 8, 7, 0], [4, 2, 8, 6, 5, 7, 3, 9, 0, 1], + [2, 7, 9, 3, 8, 0, 6, 4, 1, 5], [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]] +_INV = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9] + + +def verhoeff_check(digits: str) -> int: + c = 0 + for i, d in enumerate(reversed(digits)): + c = _D[c][_P[(i + 1) % 8][int(d)]] + return _INV[c] + + +def payload_from_log(lines: list[str], qr: str | None = None, manual: str | None = None) -> SetupPayload: + """Firmware 1.0.16+ reports its exact QR payload and manual code + ("KOTA: matter ... qr=MT:... manual=..."); older firmware: use the + advertised VID/PID/discriminator if seen; defaults otherwise.""" + base = SetupPayload() + for text in reversed(lines): + if m := _ADVERTISE.search(text): + base = SetupPayload(int(m.group(1)), int(m.group(2)), int(m.group(3)), from_log=True) + break + if qr: + return SetupPayload(base.vendor_id, base.product_id, base.discriminator, from_log=True, + qr=qr, manual=manual if manual and manual.isdigit() else None) + return base diff --git a/kota_tech/sd/provision.py b/kota_tech/sd/provision.py index 422a3a2..01a6043 100644 --- a/kota_tech/sd/provision.py +++ b/kota_tech/sd/provision.py @@ -1,261 +1,261 @@ -"""Format an SD card for the sensorbox and write / check ENV.TXT. - -Card format required by the firmware: MBR partition table, FAT32 (its FAT -driver has no exFAT and no long file names), ENV.TXT in the root. - -Windows: formatting needs administrator rights, so kota_tech.sd.win_format -runs elevated (UAC prompt). It re-checks the disk (number, size, bus, not the -Windows drive) right before erasing it, so a card swapped after the -technician confirmed is not touched, and it writes MBR + FAT32 itself -(no Storage cmdlets, which stalled on empty card-reader slots). Larger cards -get a 32 GB FAT32 partition (Windows convention; plenty for the box). -macOS: `diskutil eraseDisk FAT32 KOTA MBRFormat` (same as format_sd.sh). -""" - -from __future__ import annotations - -import json -import os -import plistlib -import subprocess -import sys -import time -import uuid -from dataclasses import dataclass, field -from pathlib import Path - -from .disks import Disk -from .envfile import BUFFER_NAME, ENV_NAME, EnvCheck, EnvConfig, find_case_insensitive, parse_env - -VOLUME_LABEL = "KOTA" - - -@dataclass -class Outcome: - ok: bool - message: str - mount: str = "" - log: list[str] = field(default_factory=list) - - -@dataclass -class CardReport: - mount: str - filesystem: str - env: EnvCheck | None - buffered_lines: int | None # measurements waiting on the card for upload - problems: list[str] - - -# ----------------------------------------------------------------- Windows - -WORK_ROOT = Path.home() / "KotaTechnicianTool" / "tmp" - - -def _workdir() -> Path: - # Not tempfile.mkdtemp(): on Windows it creates an owner-only folder, and a - # file written there by the ELEVATED script is then unreadable for this - # (non-elevated) process. A normally inherited ACL works for both. - work = WORK_ROOT / f"sd_{uuid.uuid4().hex[:10]}" - work.mkdir(parents=True, exist_ok=False) - return work - - -def win_format_command(disk: Disk, result: Path) -> tuple[str, str, str]: - """(program, parameters, working dir) for the elevated helper.""" - root = Path(__file__).resolve().parents[2] # the version folder holding kota_tech/ - params = f'-m kota_tech.sd.win_format --disk {int(disk.id)} --size {int(disk.size)} ' \ - f'--label {VOLUME_LABEL} --result "{result}"' - return sys.executable, params, str(root) - - -def _run_elevated(program: str, params: str, cwd: str, timeout_s: int = 300) -> int | None: - """ShellExecuteEx 'runas' + wait. None if the technician refused the UAC prompt.""" - import ctypes - from ctypes import wintypes - - class SHELLEXECUTEINFOW(ctypes.Structure): - _fields_ = [("cbSize", wintypes.DWORD), ("fMask", ctypes.c_ulong), ("hwnd", wintypes.HWND), - ("lpVerb", wintypes.LPCWSTR), ("lpFile", wintypes.LPCWSTR), ("lpParameters", wintypes.LPCWSTR), - ("lpDirectory", wintypes.LPCWSTR), ("nShow", ctypes.c_int), ("hInstApp", wintypes.HINSTANCE), - ("lpIDList", ctypes.c_void_p), ("lpClass", wintypes.LPCWSTR), ("hkeyClass", wintypes.HKEY), - ("dwHotKey", wintypes.DWORD), ("hIconOrMonitor", wintypes.HANDLE), ("hProcess", wintypes.HANDLE)] - - SEE_MASK_NOCLOSEPROCESS, SW_HIDE = 0x40, 0 - info = SHELLEXECUTEINFOW(cbSize=ctypes.sizeof(SHELLEXECUTEINFOW), fMask=SEE_MASK_NOCLOSEPROCESS, - lpVerb="runas", lpFile=program, lpParameters=params, lpDirectory=cwd, nShow=SW_HIDE) - shell32 = ctypes.WinDLL("shell32", use_last_error=True) - k32 = ctypes.WinDLL("kernel32", use_last_error=True) - if not shell32.ShellExecuteExW(ctypes.byref(info)): - return None # ERROR_CANCELLED (1223) or failure - k32.WaitForSingleObject(info.hProcess, timeout_s * 1000) - code = wintypes.DWORD() - k32.GetExitCodeProcess(info.hProcess, ctypes.byref(code)) - k32.CloseHandle(info.hProcess) - return code.value - - -def _format_windows(disk: Disk, log: list[str]) -> Outcome: - result = _workdir() / "result.json" - log.append("Asking Windows for administrator rights to format the card (UAC prompt)...") - t = time.perf_counter() - code = _run_elevated(*win_format_command(disk, result)) - if code is None: - return Outcome(False, "Administrator permission was refused - the card was not formatted.", log=log) - try: - res = json.loads(result.read_text(encoding="utf-8")) - except (OSError, ValueError) as exc: - return Outcome(False, f"The formatter did not report back ({exc.__class__.__name__}, exit {code}). " - "Check the card with 'Check card'.", log=log) - steps = ", ".join(f"{k} {v:.1f} s" for k, v in res.get("timings", {}).items()) - log.append(f"{res.get('message', '')} ({time.perf_counter() - t:.1f} s incl. permission prompt; {steps})") - return Outcome(bool(res.get("ok")), res.get("message", ""), res.get("drive", ""), log) - - -def _eject_windows(mount: str) -> bool: - """Flush, lock, dismount and eject the card - what 'Safely Remove' does. - - Not Shell.Application InvokeVerb('Eject'): it fails silently because the - verb is named 'E&ject' (and is localised on non-English Windows). - """ - import ctypes - from ctypes import wintypes - - k32 = ctypes.WinDLL("kernel32", use_last_error=True) - k32.CreateFileW.restype = wintypes.HANDLE - k32.CreateFileW.argtypes = [wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD, wintypes.LPVOID, - wintypes.DWORD, wintypes.DWORD, wintypes.HANDLE] - k32.DeviceIoControl.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.LPVOID, wintypes.DWORD, - wintypes.LPVOID, wintypes.DWORD, ctypes.POINTER(wintypes.DWORD), wintypes.LPVOID] - GENERIC_RW, SHARE_RW, OPEN_EXISTING = 0xC0000000, 0x3, 3 - FSCTL_LOCK_VOLUME, FSCTL_DISMOUNT_VOLUME = 0x00090018, 0x00090020 - IOCTL_STORAGE_MEDIA_REMOVAL, IOCTL_STORAGE_EJECT_MEDIA = 0x002D4804, 0x002D4808 - handle = k32.CreateFileW(f"\\\\.\\{mount[0]}:", GENERIC_RW, SHARE_RW, None, OPEN_EXISTING, 0, None) - if handle in (None, wintypes.HANDLE(-1).value): - return False - ret = wintypes.DWORD() - - def ioctl(code: int, buf=None, size: int = 0) -> bool: - return bool(k32.DeviceIoControl(handle, code, buf, size, None, 0, ctypes.byref(ret), None)) - - try: - k32.FlushFileBuffers(handle) - for _ in range(10): # Explorer/indexer may hold it briefly - if ioctl(FSCTL_LOCK_VOLUME): - break - time.sleep(0.5) - else: - return False - if not ioctl(FSCTL_DISMOUNT_VOLUME): - return False - allow = ctypes.c_bool(False) # PREVENT_MEDIA_REMOVAL{FALSE} - ioctl(IOCTL_STORAGE_MEDIA_REMOVAL, ctypes.byref(allow), 1) - ioctl(IOCTL_STORAGE_EJECT_MEDIA) # card readers may not support it; dismount suffices - return True - finally: - k32.CloseHandle(handle) - - -# ------------------------------------------------------------------- macOS - -def _format_macos(disk: Disk, log: list[str]) -> Outcome: - log.append(f"diskutil eraseDisk FAT32 {VOLUME_LABEL} MBRFormat {disk.id}") - proc = subprocess.run(["diskutil", "eraseDisk", "FAT32", VOLUME_LABEL, "MBRFormat", disk.id], - capture_output=True, text=True, timeout=600) - log += [l for l in proc.stdout.splitlines() if l.strip()] - if proc.returncode != 0: - return Outcome(False, "Formatting failed: " + (proc.stderr.strip() or proc.stdout.strip()[-200:]) - + " (is the card's write-protect switch on?)", log=log) - part = f"{disk.id}s1" - for _ in range(20): # wait for the volume to mount - info = plistlib.loads(subprocess.run(["diskutil", "info", "-plist", part], capture_output=True).stdout or b"") - if info.get("MountPoint"): - return Outcome(True, "Formatted FAT32 (MBR).", info["MountPoint"], log) - time.sleep(0.5) - return Outcome(False, "Formatted, but the card did not mount.", log=log) - - -def _eject_macos(disk_id: str) -> bool: - return subprocess.run(["diskutil", "eject", disk_id], capture_output=True, timeout=60).returncode == 0 - - -# ------------------------------------------------------------------ common - -def write_env(mount: str, env: EnvConfig) -> Outcome: - """Write ENV.TXT (8.3 name, LF, no BOM), keep other files (buffered data), read back.""" - problems = [i.text for i in env.problems() if i.level == "error"] - if problems: - return Outcome(False, "Settings are not valid: " + " ".join(problems)) - root = Path(mount) - if not root.is_dir(): - return Outcome(False, f"Card not found at {mount}.") - old = find_case_insensitive(root, ENV_NAME) - if old is not None and old.name != ENV_NAME: - old.unlink() # replace env.txt / Env.txt variants - target = root / ENV_NAME - data = env.render() - with open(target, "wb") as fh: - fh.write(data) - fh.flush() - os.fsync(fh.fileno()) - if target.read_bytes() != data: - return Outcome(False, "ENV.TXT read-back does not match - the card may be faulty.") - check = parse_env(target.read_bytes()) - if not check.ok: - return Outcome(False, "ENV.TXT was written but does not validate: " - + " ".join(i.text for i in check.issues if i.level == "error")) - return Outcome(True, f"ENV.TXT written and verified on {mount}.", mount) - - -def format_and_write(disk: Disk, env: EnvConfig, eject_card: bool = True) -> Outcome: - problems = [i.text for i in env.problems() if i.level == "error"] - if problems: - return Outcome(False, "Settings are not valid: " + " ".join(problems)) - log: list[str] = [] - if sys.platform == "win32": - fmt = _format_windows(disk, log) - elif sys.platform == "darwin": - fmt = _format_macos(disk, log) - else: - return Outcome(False, "SD provisioning is supported on Windows and macOS only.") - if not fmt.ok: - return fmt - wr = write_env(fmt.mount, env) - log.append(wr.message) - if not wr.ok: - return Outcome(False, wr.message, fmt.mount, log) - if eject_card: - log.append("Card ejected - you can remove it." if eject(fmt.mount, disk) else - "Could not eject the card automatically - use 'Safely remove' before pulling it out.") - return Outcome(True, "Card ready: formatted FAT32 and ENV.TXT verified.", fmt.mount, log) - - -def eject(mount: str, disk: Disk | None = None) -> bool: - """True if the card was dismounted and can be pulled out safely.""" - if sys.platform == "win32": - return _eject_windows(mount) - if sys.platform == "darwin" and disk is not None: - return _eject_macos(disk.id) - return False - - -def check_card(mount: str, filesystem: str = "") -> CardReport: - """What the box would find on this card (values masked in the UI).""" - root = Path(mount) - problems: list[str] = [] - # Windows says "FAT32"; macOS diskutil says "DOS_FAT_32" / "Windows_FAT_32" / "MS-DOS FAT32". - fs = filesystem.upper().replace("_", "").replace(" ", "") - if fs and "FAT32" not in fs and fs not in ("FAT", "MSDOS"): - problems.append(f"File system is {filesystem}; the box needs FAT32 - re-format the card.") - env_file = find_case_insensitive(root, ENV_NAME) - env = parse_env(env_file.read_bytes()) if env_file else None - if env_file is None: - problems.append("No ENV.TXT on the card - the box stops at start-up (white LED).") - elif len(env_file.name.split(".")[0]) > 8: - problems.append("ENV.TXT has a long name variant; use exactly ENV.TXT.") - buf = find_case_insensitive(root, BUFFER_NAME) - lines = None - if buf is not None: - with open(buf, "rb") as fh: - lines = sum(1 for ln in fh if ln.strip()) - return CardReport(mount, filesystem, env, lines, problems) +"""Format an SD card for the sensorbox and write / check ENV.TXT. + +Card format required by the firmware: MBR partition table, FAT32 (its FAT +driver has no exFAT and no long file names), ENV.TXT in the root. + +Windows: formatting needs administrator rights, so kota_tech.sd.win_format +runs elevated (UAC prompt). It re-checks the disk (number, size, bus, not the +Windows drive) right before erasing it, so a card swapped after the +technician confirmed is not touched, and it writes MBR + FAT32 itself +(no Storage cmdlets, which stalled on empty card-reader slots). Larger cards +get a 32 GB FAT32 partition (Windows convention; plenty for the box). +macOS: `diskutil eraseDisk FAT32 KOTA MBRFormat` (same as format_sd.sh). +""" + +from __future__ import annotations + +import json +import os +import plistlib +import subprocess +import sys +import time +import uuid +from dataclasses import dataclass, field +from pathlib import Path + +from .disks import Disk +from .envfile import BUFFER_NAME, ENV_NAME, EnvCheck, EnvConfig, find_case_insensitive, parse_env + +VOLUME_LABEL = "KOTA" + + +@dataclass +class Outcome: + ok: bool + message: str + mount: str = "" + log: list[str] = field(default_factory=list) + + +@dataclass +class CardReport: + mount: str + filesystem: str + env: EnvCheck | None + buffered_lines: int | None # measurements waiting on the card for upload + problems: list[str] + + +# ----------------------------------------------------------------- Windows + +WORK_ROOT = Path.home() / "KotaTechnicianTool" / "tmp" + + +def _workdir() -> Path: + # Not tempfile.mkdtemp(): on Windows it creates an owner-only folder, and a + # file written there by the ELEVATED script is then unreadable for this + # (non-elevated) process. A normally inherited ACL works for both. + work = WORK_ROOT / f"sd_{uuid.uuid4().hex[:10]}" + work.mkdir(parents=True, exist_ok=False) + return work + + +def win_format_command(disk: Disk, result: Path) -> tuple[str, str, str]: + """(program, parameters, working dir) for the elevated helper.""" + root = Path(__file__).resolve().parents[2] # the version folder holding kota_tech/ + params = f'-m kota_tech.sd.win_format --disk {int(disk.id)} --size {int(disk.size)} ' \ + f'--label {VOLUME_LABEL} --result "{result}"' + return sys.executable, params, str(root) + + +def _run_elevated(program: str, params: str, cwd: str, timeout_s: int = 300) -> int | None: + """ShellExecuteEx 'runas' + wait. None if the technician refused the UAC prompt.""" + import ctypes + from ctypes import wintypes + + class SHELLEXECUTEINFOW(ctypes.Structure): + _fields_ = [("cbSize", wintypes.DWORD), ("fMask", ctypes.c_ulong), ("hwnd", wintypes.HWND), + ("lpVerb", wintypes.LPCWSTR), ("lpFile", wintypes.LPCWSTR), ("lpParameters", wintypes.LPCWSTR), + ("lpDirectory", wintypes.LPCWSTR), ("nShow", ctypes.c_int), ("hInstApp", wintypes.HINSTANCE), + ("lpIDList", ctypes.c_void_p), ("lpClass", wintypes.LPCWSTR), ("hkeyClass", wintypes.HKEY), + ("dwHotKey", wintypes.DWORD), ("hIconOrMonitor", wintypes.HANDLE), ("hProcess", wintypes.HANDLE)] + + SEE_MASK_NOCLOSEPROCESS, SW_HIDE = 0x40, 0 + info = SHELLEXECUTEINFOW(cbSize=ctypes.sizeof(SHELLEXECUTEINFOW), fMask=SEE_MASK_NOCLOSEPROCESS, + lpVerb="runas", lpFile=program, lpParameters=params, lpDirectory=cwd, nShow=SW_HIDE) + shell32 = ctypes.WinDLL("shell32", use_last_error=True) + k32 = ctypes.WinDLL("kernel32", use_last_error=True) + if not shell32.ShellExecuteExW(ctypes.byref(info)): + return None # ERROR_CANCELLED (1223) or failure + k32.WaitForSingleObject(info.hProcess, timeout_s * 1000) + code = wintypes.DWORD() + k32.GetExitCodeProcess(info.hProcess, ctypes.byref(code)) + k32.CloseHandle(info.hProcess) + return code.value + + +def _format_windows(disk: Disk, log: list[str]) -> Outcome: + result = _workdir() / "result.json" + log.append("Asking Windows for administrator rights to format the card (UAC prompt)...") + t = time.perf_counter() + code = _run_elevated(*win_format_command(disk, result)) + if code is None: + return Outcome(False, "Administrator permission was refused - the card was not formatted.", log=log) + try: + res = json.loads(result.read_text(encoding="utf-8")) + except (OSError, ValueError) as exc: + return Outcome(False, f"The formatter did not report back ({exc.__class__.__name__}, exit {code}). " + "Check the card with 'Check card'.", log=log) + steps = ", ".join(f"{k} {v:.1f} s" for k, v in res.get("timings", {}).items()) + log.append(f"{res.get('message', '')} ({time.perf_counter() - t:.1f} s incl. permission prompt; {steps})") + return Outcome(bool(res.get("ok")), res.get("message", ""), res.get("drive", ""), log) + + +def _eject_windows(mount: str) -> bool: + """Flush, lock, dismount and eject the card - what 'Safely Remove' does. + + Not Shell.Application InvokeVerb('Eject'): it fails silently because the + verb is named 'E&ject' (and is localised on non-English Windows). + """ + import ctypes + from ctypes import wintypes + + k32 = ctypes.WinDLL("kernel32", use_last_error=True) + k32.CreateFileW.restype = wintypes.HANDLE + k32.CreateFileW.argtypes = [wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD, wintypes.LPVOID, + wintypes.DWORD, wintypes.DWORD, wintypes.HANDLE] + k32.DeviceIoControl.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.LPVOID, wintypes.DWORD, + wintypes.LPVOID, wintypes.DWORD, ctypes.POINTER(wintypes.DWORD), wintypes.LPVOID] + GENERIC_RW, SHARE_RW, OPEN_EXISTING = 0xC0000000, 0x3, 3 + FSCTL_LOCK_VOLUME, FSCTL_DISMOUNT_VOLUME = 0x00090018, 0x00090020 + IOCTL_STORAGE_MEDIA_REMOVAL, IOCTL_STORAGE_EJECT_MEDIA = 0x002D4804, 0x002D4808 + handle = k32.CreateFileW(f"\\\\.\\{mount[0]}:", GENERIC_RW, SHARE_RW, None, OPEN_EXISTING, 0, None) + if handle in (None, wintypes.HANDLE(-1).value): + return False + ret = wintypes.DWORD() + + def ioctl(code: int, buf=None, size: int = 0) -> bool: + return bool(k32.DeviceIoControl(handle, code, buf, size, None, 0, ctypes.byref(ret), None)) + + try: + k32.FlushFileBuffers(handle) + for _ in range(10): # Explorer/indexer may hold it briefly + if ioctl(FSCTL_LOCK_VOLUME): + break + time.sleep(0.5) + else: + return False + if not ioctl(FSCTL_DISMOUNT_VOLUME): + return False + allow = ctypes.c_bool(False) # PREVENT_MEDIA_REMOVAL{FALSE} + ioctl(IOCTL_STORAGE_MEDIA_REMOVAL, ctypes.byref(allow), 1) + ioctl(IOCTL_STORAGE_EJECT_MEDIA) # card readers may not support it; dismount suffices + return True + finally: + k32.CloseHandle(handle) + + +# ------------------------------------------------------------------- macOS + +def _format_macos(disk: Disk, log: list[str]) -> Outcome: + log.append(f"diskutil eraseDisk FAT32 {VOLUME_LABEL} MBRFormat {disk.id}") + proc = subprocess.run(["diskutil", "eraseDisk", "FAT32", VOLUME_LABEL, "MBRFormat", disk.id], + capture_output=True, text=True, timeout=600) + log += [l for l in proc.stdout.splitlines() if l.strip()] + if proc.returncode != 0: + return Outcome(False, "Formatting failed: " + (proc.stderr.strip() or proc.stdout.strip()[-200:]) + + " (is the card's write-protect switch on?)", log=log) + part = f"{disk.id}s1" + for _ in range(20): # wait for the volume to mount + info = plistlib.loads(subprocess.run(["diskutil", "info", "-plist", part], capture_output=True).stdout or b"") + if info.get("MountPoint"): + return Outcome(True, "Formatted FAT32 (MBR).", info["MountPoint"], log) + time.sleep(0.5) + return Outcome(False, "Formatted, but the card did not mount.", log=log) + + +def _eject_macos(disk_id: str) -> bool: + return subprocess.run(["diskutil", "eject", disk_id], capture_output=True, timeout=60).returncode == 0 + + +# ------------------------------------------------------------------ common + +def write_env(mount: str, env: EnvConfig) -> Outcome: + """Write ENV.TXT (8.3 name, LF, no BOM), keep other files (buffered data), read back.""" + problems = [i.text for i in env.problems() if i.level == "error"] + if problems: + return Outcome(False, "Settings are not valid: " + " ".join(problems)) + root = Path(mount) + if not root.is_dir(): + return Outcome(False, f"Card not found at {mount}.") + old = find_case_insensitive(root, ENV_NAME) + if old is not None and old.name != ENV_NAME: + old.unlink() # replace env.txt / Env.txt variants + target = root / ENV_NAME + data = env.render() + with open(target, "wb") as fh: + fh.write(data) + fh.flush() + os.fsync(fh.fileno()) + if target.read_bytes() != data: + return Outcome(False, "ENV.TXT read-back does not match - the card may be faulty.") + check = parse_env(target.read_bytes()) + if not check.ok: + return Outcome(False, "ENV.TXT was written but does not validate: " + + " ".join(i.text for i in check.issues if i.level == "error")) + return Outcome(True, f"ENV.TXT written and verified on {mount}.", mount) + + +def format_and_write(disk: Disk, env: EnvConfig, eject_card: bool = True) -> Outcome: + problems = [i.text for i in env.problems() if i.level == "error"] + if problems: + return Outcome(False, "Settings are not valid: " + " ".join(problems)) + log: list[str] = [] + if sys.platform == "win32": + fmt = _format_windows(disk, log) + elif sys.platform == "darwin": + fmt = _format_macos(disk, log) + else: + return Outcome(False, "SD provisioning is supported on Windows and macOS only.") + if not fmt.ok: + return fmt + wr = write_env(fmt.mount, env) + log.append(wr.message) + if not wr.ok: + return Outcome(False, wr.message, fmt.mount, log) + if eject_card: + log.append("Card ejected - you can remove it." if eject(fmt.mount, disk) else + "Could not eject the card automatically - use 'Safely remove' before pulling it out.") + return Outcome(True, "Card ready: formatted FAT32 and ENV.TXT verified.", fmt.mount, log) + + +def eject(mount: str, disk: Disk | None = None) -> bool: + """True if the card was dismounted and can be pulled out safely.""" + if sys.platform == "win32": + return _eject_windows(mount) + if sys.platform == "darwin" and disk is not None: + return _eject_macos(disk.id) + return False + + +def check_card(mount: str, filesystem: str = "") -> CardReport: + """What the box would find on this card (values masked in the UI).""" + root = Path(mount) + problems: list[str] = [] + # Windows says "FAT32"; macOS diskutil says "DOS_FAT_32" / "Windows_FAT_32" / "MS-DOS FAT32". + fs = filesystem.upper().replace("_", "").replace(" ", "") + if fs and "FAT32" not in fs and fs not in ("FAT", "MSDOS"): + problems.append(f"File system is {filesystem}; the box needs FAT32 - re-format the card.") + env_file = find_case_insensitive(root, ENV_NAME) + env = parse_env(env_file.read_bytes()) if env_file else None + if env_file is None: + problems.append("No ENV.TXT on the card - the box stops at start-up (white LED).") + elif len(env_file.name.split(".")[0]) > 8: + problems.append("ENV.TXT has a long name variant; use exactly ENV.TXT.") + buf = find_case_insensitive(root, BUFFER_NAME) + lines = None + if buf is not None: + with open(buf, "rb") as fh: + lines = sum(1 for ln in fh if ln.strip()) + return CardReport(mount, filesystem, env, lines, problems) diff --git a/kota_tech/service/report.py b/kota_tech/service/report.py index 5fb9d80..2702ac7 100644 --- a/kota_tech/service/report.py +++ b/kota_tech/service/report.py @@ -1,155 +1,155 @@ -"""Hand-off of a diagnosis to the KOTA service system (Google Apps Script). - -The tool opens the box's service page in the technician's browser: - #kota-report= -The service page (v1.1.0+) reads the fragment and pre-fills status and note; -the technician checks it, adds the location and saves - so the report is -authored by their own Google login and the tool holds no credentials. -The fragment is never sent to any server (redirect host or Apps Script). -""" - -from __future__ import annotations - -import base64 -import json -import re - -from .. import __version__ -from ..diag.engine import Report, Severity - -DEFAULT_BASE = "https://service.kota-app.com/" -NOTE_LIMIT = 4800 # the service form and Code.gs accept 5000; leave room for the technician - -# Status names exactly as in the service system (Code.gs STATUSES). -OK = "Działa poprawnie" -CHECK = "Do sprawdzenia" -IN_SERVICE = "W trakcie serwisu" -HARDWARE = "Błąd hardware" -SOFTWARE = "Błąd software" -POWER = "Problem z zasilaniem" -CONNECTIVITY = "Problem z łącznością" - -# Root-finding rule id prefix -> service status (first match wins). -_STATUS_BY_RULE = [ - ("reboot.brownout", POWER), ("reboot.power", POWER), ("tech.power.", POWER), - ("sys.crash", SOFTWARE), ("reboot.watchdog", SOFTWARE), ("sys.out_of_resources", SOFTWARE), - ("influx.bad_request", SOFTWARE), ("ota.selfcheck_failed", SOFTWARE), ("tech.quick.hung", SOFTWARE), - ("rtc.init_failed", HARDWARE), ("rtc.lost_after_sync", HARDWARE), ("i2c.", HARDWARE), ("sensor.", HARDWARE), - ("sd.", HARDWARE), ("sen66.", HARDWARE), ("bme688.", HARDWARE), ("as7341.", HARDWARE), - ("mic.read_fail", HARDWARE), ("mic.silent", HARDWARE), ("tech.rtc_i2c.", HARDWARE), ("tech.sensors.", HARDWARE), - ("tech.mic.mic_faulty", HARDWARE), ("tech.mic.mic_switch_faulty", HARDWARE), ("tech.white.sd_", HARDWARE), - ("matter.", CONNECTIVITY), ("net.", CONNECTIVITY), ("wifi.", CONNECTIVITY), ("influx.", CONNECTIVITY), - ("ota.", CONNECTIVITY), ("tech.network.", CONNECTIVITY), - # firmware 1.0.16+ (KOTA: lines, status, error IDs) - ("reboot.crash", SOFTWARE), ("reboot.status_oom_watchdog", SOFTWARE), ("status.oom", SOFTWARE), - ("kota.low_heap", SOFTWARE), - ("kota.i2c_stuck", HARDWARE), ("kota.sensor_missing", HARDWARE), ("kota.rtc_", HARDWARE), - ("kota.sd_", HARDWARE), ("status.error", HARDWARE), ("status.rtc", HARDWARE), ("status.sd", HARDWARE), - ("kota.wifi_down", CONNECTIVITY), ("kota.weak_wifi", CONNECTIVITY), ("kota.no_upload", CONNECTIVITY), - ("kota.ota_unregistered", CONNECTIVITY), ("kota.buffer_backlog", CONNECTIVITY), ("status.conn", CONNECTIVITY), - ("kota.time_invalid", CHECK), - *((f"eid.{m}", HARDWARE) for m in ("SD", "ENV", "RTC", "BME", "SEN", "AS", "MIC", "FLK", "SPI")), - *((f"eid.{m}", CONNECTIVITY) for m in ("INF", "WIFI", "OTA", "PROV")), - ("eid.", SOFTWARE), - ("rtc.clock_not_running", CHECK), ("reboot.", CHECK), -] -_MAC = re.compile(r"^[0-9A-F]{12}$") - - -def normalize_mac(mac: str) -> str: - return re.sub(r"[^0-9A-Fa-f]", "", mac or "").upper() - - -def service_status(report: Report) -> str: - """Suggested status; the technician can change it in the form.""" - problems = [rc for rc in report.roots if rc.severity >= Severity.WARNING] - if not problems: - return OK if any(m == "data_uploaded" and t is not None for m, _, t in report.milestones) else CHECK - rid = problems[0].finding.rule_id - for prefix, status in _STATUS_BY_RULE: - if rid.startswith(prefix): - return status - return CHECK - - -BOX_MARK = {"ok": "✔", "warn": "!", "bad": "✖", "info": "i"} # same marks as the Diagnosis panel -SD_WIFI = {True: "reads Wi-Fi settings from the SD card", - False: "does not read Wi-Fi settings from the SD card", - None: "Wi-Fi-from-SD support unknown"} - - -def service_note(report: Report, limit: int = NOTE_LIMIT) -> str: - """The Diagnosis panel as text: verdict, LED, firmware, box self-report, - start-up progress with times, findings with details and when they were seen. - - Falls back to shorter finding details when the full text does not fit the - service form (consequences' explanations first, then all), then truncates. - """ - for detail in ("all", "roots", "none"): - text = _note(report, detail) - if len(text) <= limit: - return text - return text[:limit - 20].rstrip() + "\n...(shortened)" - - -def _note(report: Report, detail: str) -> str: - fw = report.firmware - source = " (reported by the box)" if report.led_exact else "" - lines = [f"[KOTA Technician Tool {__version__}] {report.verdict}", "", - f"Status LED should be: {report.led_text()}{source}"] - if report.led_reason: - lines.append(f" from: {report.led_reason}") - lines += ["", f"Firmware: {fw.describe()}", f" {SD_WIFI[fw.supports_sd_wifi]}"] - if report.box: - lines += ["", "Box self-report (KOTA lines):"] - lines += [f" {BOX_MARK.get(st, '-')} {topic}: {text}" for topic, text, st in report.box] - lines += ["", "Start-up progress (current boot):"] - lines += [f" ✔ {label} ({t / 1000:.1f} s)" if t is not None else f" ○ {label}" - for _, label, t in report.milestones] - lines += ["", "Findings - root causes first:" if report.roots else "Findings: no problems detected in the log"] - for rc in report.roots: - lines += _finding(rc.finding, "", detail in ("all", "roots")) - for c in rc.consequences: - lines += _finding(c, " ↳ ", detail == "all") - for title, items in (("Seen in earlier boots only", report.earlier), - ("Resolved during this session", report.resolved)): - if items: - lines += ["", f"{title} ({len(items)}):"] - lines += [f" - [{f.severity.label}] {f.title} - {f.seen_text()}" for f in items] - if report.technician: - lines += ["", "Technician checks:"] - lines += [f" * {t}" for t in report.technician[-12:]] - return "\n".join(lines) - - -def _finding(f, prefix: str, detailed: bool) -> list[str]: - ind = " " * len(prefix) + " " - out = [f"{prefix}[{f.severity.label}] {f.title}"] - if detailed: - out.append(f"{ind}What it means: {f.meaning}") - if f.actions: - out.append(f"{ind}What to do:") - out += [f"{ind} {i}. {a}" for i, a in enumerate(f.actions, 1)] - out += [f"{ind}Error {eid}" + (f": {a}" if a else "") for eid, a in f.error_ids.items()] - out.append(f"{ind}{f.seen_text()}") - if f.evidence: - out.append(f"{ind}Log: {f.evidence[-1]}") - return out - - -def report_url(mac: str, report: Report, base: str = DEFAULT_BASE) -> str: - mac = normalize_mac(mac) - if not _MAC.match(mac): - raise ValueError(f"not a box MAC address: {mac!r}") - payload = {"v": 1, "mac": mac, "status": service_status(report), "note": service_note(report), - "tool": f"v{__version__}"} - raw = json.dumps(payload, ensure_ascii=False, separators=(",", ":")).encode("utf-8") - frag = base64.urlsafe_b64encode(raw).decode("ascii").rstrip("=") - base = base if base.endswith("/") else base + "/" - return f"{base}{mac}#kota-report={frag}" - - -def decode_fragment(url: str) -> dict: - """Inverse of report_url (used by tests and for troubleshooting).""" - frag = url.split("#kota-report=", 1)[1] - return json.loads(base64.urlsafe_b64decode(frag + "=" * (-len(frag) % 4)).decode("utf-8")) +"""Hand-off of a diagnosis to the KOTA service system (Google Apps Script). + +The tool opens the box's service page in the technician's browser: + #kota-report= +The service page (v1.1.0+) reads the fragment and pre-fills status and note; +the technician checks it, adds the location and saves - so the report is +authored by their own Google login and the tool holds no credentials. +The fragment is never sent to any server (redirect host or Apps Script). +""" + +from __future__ import annotations + +import base64 +import json +import re + +from .. import __version__ +from ..diag.engine import Report, Severity + +DEFAULT_BASE = "https://service.kota-app.com/" +NOTE_LIMIT = 4800 # the service form and Code.gs accept 5000; leave room for the technician + +# Status names exactly as in the service system (Code.gs STATUSES). +OK = "Działa poprawnie" +CHECK = "Do sprawdzenia" +IN_SERVICE = "W trakcie serwisu" +HARDWARE = "Błąd hardware" +SOFTWARE = "Błąd software" +POWER = "Problem z zasilaniem" +CONNECTIVITY = "Problem z łącznością" + +# Root-finding rule id prefix -> service status (first match wins). +_STATUS_BY_RULE = [ + ("reboot.brownout", POWER), ("reboot.power", POWER), ("tech.power.", POWER), + ("sys.crash", SOFTWARE), ("reboot.watchdog", SOFTWARE), ("sys.out_of_resources", SOFTWARE), + ("influx.bad_request", SOFTWARE), ("ota.selfcheck_failed", SOFTWARE), ("tech.quick.hung", SOFTWARE), + ("rtc.init_failed", HARDWARE), ("rtc.lost_after_sync", HARDWARE), ("i2c.", HARDWARE), ("sensor.", HARDWARE), + ("sd.", HARDWARE), ("sen66.", HARDWARE), ("bme688.", HARDWARE), ("as7341.", HARDWARE), + ("mic.read_fail", HARDWARE), ("mic.silent", HARDWARE), ("tech.rtc_i2c.", HARDWARE), ("tech.sensors.", HARDWARE), + ("tech.mic.mic_faulty", HARDWARE), ("tech.mic.mic_switch_faulty", HARDWARE), ("tech.white.sd_", HARDWARE), + ("matter.", CONNECTIVITY), ("net.", CONNECTIVITY), ("wifi.", CONNECTIVITY), ("influx.", CONNECTIVITY), + ("ota.", CONNECTIVITY), ("tech.network.", CONNECTIVITY), + # firmware 1.0.16+ (KOTA: lines, status, error IDs) + ("reboot.crash", SOFTWARE), ("reboot.status_oom_watchdog", SOFTWARE), ("status.oom", SOFTWARE), + ("kota.low_heap", SOFTWARE), + ("kota.i2c_stuck", HARDWARE), ("kota.sensor_missing", HARDWARE), ("kota.rtc_", HARDWARE), + ("kota.sd_", HARDWARE), ("status.error", HARDWARE), ("status.rtc", HARDWARE), ("status.sd", HARDWARE), + ("kota.wifi_down", CONNECTIVITY), ("kota.weak_wifi", CONNECTIVITY), ("kota.no_upload", CONNECTIVITY), + ("kota.ota_unregistered", CONNECTIVITY), ("kota.buffer_backlog", CONNECTIVITY), ("status.conn", CONNECTIVITY), + ("kota.time_invalid", CHECK), + *((f"eid.{m}", HARDWARE) for m in ("SD", "ENV", "RTC", "BME", "SEN", "AS", "MIC", "FLK", "SPI")), + *((f"eid.{m}", CONNECTIVITY) for m in ("INF", "WIFI", "OTA", "PROV")), + ("eid.", SOFTWARE), + ("rtc.clock_not_running", CHECK), ("reboot.", CHECK), +] +_MAC = re.compile(r"^[0-9A-F]{12}$") + + +def normalize_mac(mac: str) -> str: + return re.sub(r"[^0-9A-Fa-f]", "", mac or "").upper() + + +def service_status(report: Report) -> str: + """Suggested status; the technician can change it in the form.""" + problems = [rc for rc in report.roots if rc.severity >= Severity.WARNING] + if not problems: + return OK if any(m == "data_uploaded" and t is not None for m, _, t in report.milestones) else CHECK + rid = problems[0].finding.rule_id + for prefix, status in _STATUS_BY_RULE: + if rid.startswith(prefix): + return status + return CHECK + + +BOX_MARK = {"ok": "✔", "warn": "!", "bad": "✖", "info": "i"} # same marks as the Diagnosis panel +SD_WIFI = {True: "reads Wi-Fi settings from the SD card", + False: "does not read Wi-Fi settings from the SD card", + None: "Wi-Fi-from-SD support unknown"} + + +def service_note(report: Report, limit: int = NOTE_LIMIT) -> str: + """The Diagnosis panel as text: verdict, LED, firmware, box self-report, + start-up progress with times, findings with details and when they were seen. + + Falls back to shorter finding details when the full text does not fit the + service form (consequences' explanations first, then all), then truncates. + """ + for detail in ("all", "roots", "none"): + text = _note(report, detail) + if len(text) <= limit: + return text + return text[:limit - 20].rstrip() + "\n...(shortened)" + + +def _note(report: Report, detail: str) -> str: + fw = report.firmware + source = " (reported by the box)" if report.led_exact else "" + lines = [f"[KOTA Technician Tool {__version__}] {report.verdict}", "", + f"Status LED should be: {report.led_text()}{source}"] + if report.led_reason: + lines.append(f" from: {report.led_reason}") + lines += ["", f"Firmware: {fw.describe()}", f" {SD_WIFI[fw.supports_sd_wifi]}"] + if report.box: + lines += ["", "Box self-report (KOTA lines):"] + lines += [f" {BOX_MARK.get(st, '-')} {topic}: {text}" for topic, text, st in report.box] + lines += ["", "Start-up progress (current boot):"] + lines += [f" ✔ {label} ({t / 1000:.1f} s)" if t is not None else f" ○ {label}" + for _, label, t in report.milestones] + lines += ["", "Findings - root causes first:" if report.roots else "Findings: no problems detected in the log"] + for rc in report.roots: + lines += _finding(rc.finding, "", detail in ("all", "roots")) + for c in rc.consequences: + lines += _finding(c, " ↳ ", detail == "all") + for title, items in (("Seen in earlier boots only", report.earlier), + ("Resolved during this session", report.resolved)): + if items: + lines += ["", f"{title} ({len(items)}):"] + lines += [f" - [{f.severity.label}] {f.title} - {f.seen_text()}" for f in items] + if report.technician: + lines += ["", "Technician checks:"] + lines += [f" * {t}" for t in report.technician[-12:]] + return "\n".join(lines) + + +def _finding(f, prefix: str, detailed: bool) -> list[str]: + ind = " " * len(prefix) + " " + out = [f"{prefix}[{f.severity.label}] {f.title}"] + if detailed: + out.append(f"{ind}What it means: {f.meaning}") + if f.actions: + out.append(f"{ind}What to do:") + out += [f"{ind} {i}. {a}" for i, a in enumerate(f.actions, 1)] + out += [f"{ind}Error {eid}" + (f": {a}" if a else "") for eid, a in f.error_ids.items()] + out.append(f"{ind}{f.seen_text()}") + if f.evidence: + out.append(f"{ind}Log: {f.evidence[-1]}") + return out + + +def report_url(mac: str, report: Report, base: str = DEFAULT_BASE) -> str: + mac = normalize_mac(mac) + if not _MAC.match(mac): + raise ValueError(f"not a box MAC address: {mac!r}") + payload = {"v": 1, "mac": mac, "status": service_status(report), "note": service_note(report), + "tool": f"v{__version__}"} + raw = json.dumps(payload, ensure_ascii=False, separators=(",", ":")).encode("utf-8") + frag = base64.urlsafe_b64encode(raw).decode("ascii").rstrip("=") + base = base if base.endswith("/") else base + "/" + return f"{base}{mac}#kota-report={frag}" + + +def decode_fragment(url: str) -> dict: + """Inverse of report_url (used by tests and for troubleshooting).""" + frag = url.split("#kota-report=", 1)[1] + return json.loads(base64.urlsafe_b64decode(frag + "=" * (-len(frag) % 4)).decode("utf-8")) diff --git a/kota_tech/ui/cheatsheet.py b/kota_tech/ui/cheatsheet.py index 83eede6..b42d389 100644 --- a/kota_tech/ui/cheatsheet.py +++ b/kota_tech/ui/cheatsheet.py @@ -1,87 +1,87 @@ -"""Quick-start cheat sheet shown on launch. - -LED table for firmware 1.0.16+ (firmware-diagnostics-what-changed.md, -status.cpp Status::pattern_for): colour AND pattern name the fault. Older -boxes (v1.0, built Jun 29 2026) show the same colours, always solid. -""" - -# (swatch colour, LED name, pattern -> meaning, first action) -LED_TABLE = [ - ("#2e9e44", "Green", "solid: everything OK.", "Nothing to do."), - ("#1fc8d8", "Cyan", - "breathing: not paired in Matter yet
fast blink: a phone is connected for pairing.", - "Pair the box in the Matter app (QR sticker or the QR code in the tool)."), - ("#f4f4f4", "White", - "fast blink: SD card or ENV.TXT error
solid: factory reset (Matter button held 5 s)
" - "breathing: Wi-Fi setup portal.", - "SD card is inside the case: re-seat or re-provision it. Setup portal: short-press the button to leave."), - ("#d64541", "Red", - "1-6 pulses: 1 BME688, 2 SEN66, 3 AS7341, 4 microphone, 5 flicker, 6 self-check failed
" - "solid: other internal error / out of memory.", - "Count the pulses, then check that sensor's cable. The box restarts itself after 150 s."), - ("#2f6fd6", "Blue", - "1 pulse: clock (RTC) not responding
2 pulses: I2C bus stuck (shorted / broken sensor cable).", - "Check the I2C cable between the boards first, then the RTC module."), - ("#8e44ad", "Purple", - "solid: no network
blinking: server rejected the box (wrong ENV.TXT credentials).", - "Solid: check Wi-Fi / Internet. Blinking: re-provision the SD card with the current ENV.TXT."), - ("#e6c229", "Yellow", "blinking, faster over time: downloading an update.", "Do not switch off. Wait."), - ("#f08c1a", "Orange", "solid: self-check after an update (~2.5 min).", - "Wait; should turn green (or red on failure)."), - ("#444444", "Off", "Firmware did not start (or crashed very early).", - "Check power switch & USB cable. If the small LED is also dark: re-flash."), -] - -HINTS = [ - "Before a self-restart the LED flashes the error colour 3 times; after the restart it " - "shows the previous error for 3 s. So a reboot loop shows its cause. Connect serial and press " - "Reset & capture: the tool reads the restart reason from the box.", - "Small LED next to the RGB LED (heartbeat): blinks once per second while online, " - "double-blinks while offline. Dark or steady = firmware hung or not running.", - "Serial monitor: lines starting with KOTA: are the box's own diagnosis (boot, I2C, SD, " - "clock, Matter, microphone, status, LED, and a health line every 60 s). The Diagnosis panel reads them " - "directly; filter the view with KOTA diagnostics + errors. Error lines carry a code like " - "[E-SD-03] - the tool shows the recommended action for it.", - "Outside the case (back): power switch, the microphone switch just above it " - "(off = no noise measurements), and the Matter button. Holding the " - "Matter button 5 s factory-resets the box (it forgets its Matter " - "network and must be commissioned again with the sticker QR code).", - "Inside the case (unscrew): RESET and BOOT buttons and the SD card. " - "Normally not needed - the tool resets the board over USB.", - "Flashing uses only the firmware files in the tool's firmware folder; choose the build " - "by its compile date.", -] - -STEPS = [ - "Switch the sensorbox power switch ON.", - "Connect the laptop to the box's native USB port with a data-capable cable.", - "Open the Serial monitor tab, choose the sensorbox port, press " - "Connect.", - "Press Reset & capture to record a full boot log from the first line.", - "Read the Diagnosis panel on the right, then open Guided troubleshooting " - "and start Quick check: the tool asks what you see and checks the log live.", -] - - -def cheatsheet_html() -> str: - rows = "".join( - f"      {name}" - f"{meaning}{action}" - for c, name, meaning, action in LED_TABLE - ) - steps = "".join(f"
  • {s}
  • " for s in STEPS) - hints = "".join(f"
  • {h}
  • " for h in HINTS) - return f""" -

    Sensorbox quick start

    -
      {steps}
    -

    RGB status LED - colour and pattern

    - - - - - {rows} -
    LEDPattern: meaningFirst action
    -

    Good to know

    -
      {hints}
    - """ +"""Quick-start cheat sheet shown on launch. + +LED table for firmware 1.0.16+ (firmware-diagnostics-what-changed.md, +status.cpp Status::pattern_for): colour AND pattern name the fault. Older +boxes (v1.0, built Jun 29 2026) show the same colours, always solid. +""" + +# (swatch colour, LED name, pattern -> meaning, first action) +LED_TABLE = [ + ("#2e9e44", "Green", "solid: everything OK.", "Nothing to do."), + ("#1fc8d8", "Cyan", + "breathing: not paired in Matter yet
    fast blink: a phone is connected for pairing.", + "Pair the box in the Matter app (QR sticker or the QR code in the tool)."), + ("#f4f4f4", "White", + "fast blink: SD card or ENV.TXT error
    solid: factory reset (Matter button held 5 s)
    " + "breathing: Wi-Fi setup portal.", + "SD card is inside the case: re-seat or re-provision it. Setup portal: short-press the button to leave."), + ("#d64541", "Red", + "1-6 pulses: 1 BME688, 2 SEN66, 3 AS7341, 4 microphone, 5 flicker, 6 self-check failed
    " + "solid: other internal error / out of memory.", + "Count the pulses, then check that sensor's cable. The box restarts itself after 150 s."), + ("#2f6fd6", "Blue", + "1 pulse: clock (RTC) not responding
    2 pulses: I2C bus stuck (shorted / broken sensor cable).", + "Check the I2C cable between the boards first, then the RTC module."), + ("#8e44ad", "Purple", + "solid: no network
    blinking: server rejected the box (wrong ENV.TXT credentials).", + "Solid: check Wi-Fi / Internet. Blinking: re-provision the SD card with the current ENV.TXT."), + ("#e6c229", "Yellow", "blinking, faster over time: downloading an update.", "Do not switch off. Wait."), + ("#f08c1a", "Orange", "solid: self-check after an update (~2.5 min).", + "Wait; should turn green (or red on failure)."), + ("#444444", "Off", "Firmware did not start (or crashed very early).", + "Check power switch & USB cable. If the small LED is also dark: re-flash."), +] + +HINTS = [ + "Before a self-restart the LED flashes the error colour 3 times; after the restart it " + "shows the previous error for 3 s. So a reboot loop shows its cause. Connect serial and press " + "Reset & capture: the tool reads the restart reason from the box.", + "Small LED next to the RGB LED (heartbeat): blinks once per second while online, " + "double-blinks while offline. Dark or steady = firmware hung or not running.", + "Serial monitor: lines starting with KOTA: are the box's own diagnosis (boot, I2C, SD, " + "clock, Matter, microphone, status, LED, and a health line every 60 s). The Diagnosis panel reads them " + "directly; filter the view with KOTA diagnostics + errors. Error lines carry a code like " + "[E-SD-03] - the tool shows the recommended action for it.", + "Outside the case (back): power switch, the microphone switch just above it " + "(off = no noise measurements), and the Matter button. Holding the " + "Matter button 5 s factory-resets the box (it forgets its Matter " + "network and must be commissioned again with the sticker QR code).", + "Inside the case (unscrew): RESET and BOOT buttons and the SD card. " + "Normally not needed - the tool resets the board over USB.", + "Flashing uses only the firmware files in the tool's firmware folder; choose the build " + "by its compile date.", +] + +STEPS = [ + "Switch the sensorbox power switch ON.", + "Connect the laptop to the box's native USB port with a data-capable cable.", + "Open the Serial monitor tab, choose the sensorbox port, press " + "Connect.", + "Press Reset & capture to record a full boot log from the first line.", + "Read the Diagnosis panel on the right, then open Guided troubleshooting " + "and start Quick check: the tool asks what you see and checks the log live.", +] + + +def cheatsheet_html() -> str: + rows = "".join( + f"      {name}" + f"{meaning}{action}" + for c, name, meaning, action in LED_TABLE + ) + steps = "".join(f"
  • {s}
  • " for s in STEPS) + hints = "".join(f"
  • {h}
  • " for h in HINTS) + return f""" +

    Sensorbox quick start

    +
      {steps}
    +

    RGB status LED - colour and pattern

    + + + + + {rows} +
    LEDPattern: meaningFirst action
    +

    Good to know

    +
      {hints}
    + """ diff --git a/kota_tech/ui/diag_panel.py b/kota_tech/ui/diag_panel.py index 184eac5..39116e7 100644 --- a/kota_tech/ui/diag_panel.py +++ b/kota_tech/ui/diag_panel.py @@ -1,252 +1,252 @@ -"""Diagnostics side panel: renders a diag.engine.Report. - -Layout: verdict banner, expected LED (colour + pattern + meaning when the -box reports it), firmware, the box's own self-report (KOTA lines), start-up -milestones, a findings tree (root causes; their consequences as children; -titles only) and a wrapped detail pane for the selected finding. -""" - -from __future__ import annotations - -from PySide6.QtCore import Qt -from PySide6.QtGui import QBrush, QColor -from PySide6.QtWidgets import ( - QFrame, - QHBoxLayout, - QLabel, - QListWidget, - QListWidgetItem, - QPushButton, - QSizePolicy, - QSplitter, - QTextBrowser, - QTreeWidget, - QTreeWidgetItem, - QVBoxLayout, - QWidget, -) - -from ..diag.engine import LED, Finding, Report, Severity - -SEV_COLOR = { - Severity.CRITICAL: "#b03a2e", - Severity.ERROR: "#d9534f", - Severity.WARNING: "#c77c02", - Severity.INFO: "#2e6da4", -} -# verdict banner (background, text) by verdict severity; None = healthy -BANNER = { - Severity.CRITICAL: ("#f6d5d1", "#7b1e14"), - Severity.ERROR: ("#f9dedc", "#8a2019"), - Severity.WARNING: ("#fdebc8", "#6b4400"), - None: ("#d8f0dd", "#1b5e2a"), -} -_KEY = Qt.ItemDataRole.UserRole -BOX_STATE = {"ok": ("#1b7a33", "✔"), "warn": ("#c77c02", "!"), "bad": ("#b03a2e", "✖"), "info": ("#2e6da4", "i")} - - -def _heading(text: str) -> QLabel: - lbl = QLabel(text) - f = lbl.font() - f.setBold(True) - lbl.setFont(f) - return lbl - - -def _esc(s: str) -> str: - return s.replace("&", "&").replace("<", "<").replace(">", ">") - - -class DiagnosticsPanel(QWidget): - def __init__(self, parent: QWidget | None = None): - super().__init__(parent) - self.setMinimumWidth(380) - - self.verdict = QLabel(wordWrap=True) - self.verdict.setMargin(10) - vf = self.verdict.font() - vf.setPointSize(vf.pointSize() + 2) - vf.setBold(True) - self.verdict.setFont(vf) - - self.led_swatch = QLabel() - self.led_swatch.setFixedSize(22, 22) - self.led_text = QLabel(wordWrap=True) - led_row = QHBoxLayout() - led_row.addWidget(self.led_swatch, 0, Qt.AlignmentFlag.AlignTop) - led_row.addWidget(self.led_text, 1) - - self.firmware = QLabel(wordWrap=True) - self.firmware.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) - - self.box_title = _heading("Box self-report (KOTA lines)") - self.box = QLabel(wordWrap=True) - self.box.setTextFormat(Qt.TextFormat.RichText) - self.box.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) - - self.milestones = QListWidget() - self.milestones.setFrameShape(QFrame.Shape.NoFrame) - self.milestones.setSelectionMode(QListWidget.SelectionMode.NoSelection) - self.milestones.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Maximum) - - self.tree = QTreeWidget() - self.tree.setHeaderHidden(True) - self.tree.setIndentation(16) - self.tree.setTextElideMode(Qt.TextElideMode.ElideRight) - self.detail = QTextBrowser() - self.detail.setOpenExternalLinks(False) - findings = QSplitter(Qt.Orientation.Vertical) - findings.addWidget(self.tree) - findings.addWidget(self.detail) - findings.setSizes([220, 260]) - - lay = QVBoxLayout(self) - lay.setContentsMargins(4, 0, 0, 0) - lay.addWidget(self.verdict) - lay.addWidget(_heading("Status LED should be")) - lay.addLayout(led_row) - lay.addWidget(_heading("Firmware")) - lay.addWidget(self.firmware) - self.qr_btn = QPushButton("Matter pairing QR code...") - self.qr_btn.setToolTip("Show the box's Matter QR code on screen to scan with the phone") - lay.addWidget(self.qr_btn, 0, Qt.AlignmentFlag.AlignLeft) - lay.addWidget(self.box_title) - lay.addWidget(self.box) - lay.addWidget(_heading("Start-up progress (current boot)")) - lay.addWidget(self.milestones) - lay.addWidget(_heading("Findings - root causes first; click one for details")) - lay.addWidget(findings, 1) - - self._findings: dict[str, Finding] = {} - self._selected: str | None = None - # key -> expanded, chosen by the user; survives the rebuild on refresh. - self._expanded: dict[str, bool] = {} - self.tree.currentItemChanged.connect(self._on_select) - self.tree.itemCollapsed.connect(lambda it: self._remember(it, False)) - self.tree.itemExpanded.connect(lambda it: self._remember(it, True)) - - # ------------------------------------------------------------------ - def show_report(self, r: Report) -> None: - bg, fg = BANNER.get(r.verdict_severity, BANNER[Severity.WARNING]) - if r.lines == 0: - bg, fg = "#e6e6e6", "#333333" - self.verdict.setText(r.verdict) - self.verdict.setStyleSheet(f"QLabel {{ background:{bg}; color:{fg}; border-radius:6px; }}") - - name, colour = LED[r.led] - self.led_swatch.setStyleSheet( - f"QLabel {{ background:{colour}; border:1px solid #777; border-radius:11px; }}") - ask = "" if r.led == "UNKNOWN" else " - check it matches the box" - pattern = f", {_esc(r.led_pattern)}" if r.led_pattern else "" - meaning = f" = {_esc(r.led_meaning)}" if r.led_meaning else "" - source = " (reported by the box)" if r.led_exact else "" - reason = f"
    from: {_esc(r.led_reason)}" if r.led_reason else "" - self.led_text.setText(f"{name}{pattern}{meaning}{source}{ask}{reason}") - - fw = r.firmware - sd = {True: "reads Wi-Fi settings from the SD card", - False: "does not read Wi-Fi settings from the SD card", - None: "Wi-Fi-from-SD support unknown"} - self.firmware.setText(f"{_esc(fw.describe())}
    {sd[fw.supports_sd_wifi]}") - - self.box_title.setVisible(bool(r.box)) - self.box.setVisible(bool(r.box)) - self.box.setText("" + "".join( - f"" - f"" - for topic, text, st in r.box) + "
    {BOX_STATE[st][1]} {_esc(topic)} {_esc(text)}
    ") - - self.milestones.clear() - for _, label, t in r.milestones: - done = t is not None - it = QListWidgetItem(("✔ " if done else "○ ") + label - + (f" ({t / 1000:.1f} s)" if done else "")) - it.setForeground(QBrush(QColor("#1b7a33" if done else "#8a8a8a"))) - self.milestones.addItem(it) - rows = min(max(self.milestones.count(), 1), 15) - self.milestones.setFixedHeight(rows * max(self.milestones.sizeHintForRow(0), 16) + 6) - - self._fill_tree(r) - - def _fill_tree(self, r: Report) -> None: - self.tree.blockSignals(True) - self.tree.clear() - self._findings = {} - if not r.roots: - it = QTreeWidgetItem(["No problems detected in the log so far."]) - it.setForeground(0, QBrush(QColor("#1b7a33"))) - self.tree.addTopLevelItem(it) - for rc in r.roots: - top = self._item(rc.finding, bold=True) - self.tree.addTopLevelItem(top) - for c in rc.consequences: - top.addChild(self._item(c, prefix="↳ ")) - top.setExpanded(self._expanded.get(rc.finding.key, True)) - for title, key, items in (("Seen in earlier boots only", "grp:earlier", r.earlier), - ("Resolved during this session", "grp:resolved", r.resolved)): - if not items: - continue - grp = QTreeWidgetItem([f"{title} ({len(items)})"]) - grp.setData(0, _KEY, key) - grp.setForeground(0, QBrush(QColor("#666666"))) - self.tree.addTopLevelItem(grp) - for f in items: - grp.addChild(self._item(f, prefix="✔ " if key == "grp:resolved" else "")) - grp.setExpanded(self._expanded.get(key, False)) - # Keep the selection across refreshes; default to the top root cause. - target = self._selected if self._selected in self._findings else ( - r.roots[0].finding.key if r.roots else None) - self.tree.blockSignals(False) - self._select(target) - - def _remember(self, item: QTreeWidgetItem, expanded: bool) -> None: - if key := item.data(0, _KEY): - self._expanded[key] = expanded - - def _item(self, f: Finding, prefix: str = "", bold: bool = False) -> QTreeWidgetItem: - it = QTreeWidgetItem([f"{prefix}[{f.severity.label}] {f.title}"]) - it.setData(0, _KEY, f.key) - it.setForeground(0, QBrush(QColor(SEV_COLOR.get(f.severity, "#333333")))) - it.setToolTip(0, f.title) - if bold: - font = it.font(0) - font.setBold(True) - it.setFont(0, font) - self._findings[f.key] = f - return it - - def _select(self, key: str | None) -> None: - if key is None: - self.detail.setHtml("

    Nothing to show yet.

    ") - return - match = self.tree.findItems("*", Qt.MatchFlag.MatchWildcard | Qt.MatchFlag.MatchRecursive) - for it in match: - if it.data(0, _KEY) == key: - self.tree.setCurrentItem(it) - return - - def _on_select(self, cur: QTreeWidgetItem | None, _prev) -> None: - key = cur.data(0, _KEY) if cur else None - f = self._findings.get(key or "") - if f is None: - return - self._selected = key - self.detail.setHtml(_detail_html(f)) - - -def _detail_html(f: Finding) -> str: - colour = SEV_COLOR.get(f.severity, "#333") - actions = "".join(f"
  • {_esc(a)}
  • " for a in f.actions) - seen = _esc(f.seen_text()) - evidence = "
    ".join(_esc(e) for e in f.evidence) - codes = "".join(f"
  • {_esc(eid)}" + (f" - {_esc(a)}" if a else "") + "
  • " - for eid, a in f.error_ids.items()) - return ( - f"

    {_esc(f.title)}

    " - f"

    What it means: {_esc(f.meaning)}

    " - + (f"

    What to do:

      {actions}
    " if actions else "") - + (f"

    Firmware error codes (error_ids.csv):

      {codes}
    " if codes else "") - + f"

    {seen}

    " - + (f"

    {evidence}

    " - if evidence else "") - ) +"""Diagnostics side panel: renders a diag.engine.Report. + +Layout: verdict banner, expected LED (colour + pattern + meaning when the +box reports it), firmware, the box's own self-report (KOTA lines), start-up +milestones, a findings tree (root causes; their consequences as children; +titles only) and a wrapped detail pane for the selected finding. +""" + +from __future__ import annotations + +from PySide6.QtCore import Qt +from PySide6.QtGui import QBrush, QColor +from PySide6.QtWidgets import ( + QFrame, + QHBoxLayout, + QLabel, + QListWidget, + QListWidgetItem, + QPushButton, + QSizePolicy, + QSplitter, + QTextBrowser, + QTreeWidget, + QTreeWidgetItem, + QVBoxLayout, + QWidget, +) + +from ..diag.engine import LED, Finding, Report, Severity + +SEV_COLOR = { + Severity.CRITICAL: "#b03a2e", + Severity.ERROR: "#d9534f", + Severity.WARNING: "#c77c02", + Severity.INFO: "#2e6da4", +} +# verdict banner (background, text) by verdict severity; None = healthy +BANNER = { + Severity.CRITICAL: ("#f6d5d1", "#7b1e14"), + Severity.ERROR: ("#f9dedc", "#8a2019"), + Severity.WARNING: ("#fdebc8", "#6b4400"), + None: ("#d8f0dd", "#1b5e2a"), +} +_KEY = Qt.ItemDataRole.UserRole +BOX_STATE = {"ok": ("#1b7a33", "✔"), "warn": ("#c77c02", "!"), "bad": ("#b03a2e", "✖"), "info": ("#2e6da4", "i")} + + +def _heading(text: str) -> QLabel: + lbl = QLabel(text) + f = lbl.font() + f.setBold(True) + lbl.setFont(f) + return lbl + + +def _esc(s: str) -> str: + return s.replace("&", "&").replace("<", "<").replace(">", ">") + + +class DiagnosticsPanel(QWidget): + def __init__(self, parent: QWidget | None = None): + super().__init__(parent) + self.setMinimumWidth(380) + + self.verdict = QLabel(wordWrap=True) + self.verdict.setMargin(10) + vf = self.verdict.font() + vf.setPointSize(vf.pointSize() + 2) + vf.setBold(True) + self.verdict.setFont(vf) + + self.led_swatch = QLabel() + self.led_swatch.setFixedSize(22, 22) + self.led_text = QLabel(wordWrap=True) + led_row = QHBoxLayout() + led_row.addWidget(self.led_swatch, 0, Qt.AlignmentFlag.AlignTop) + led_row.addWidget(self.led_text, 1) + + self.firmware = QLabel(wordWrap=True) + self.firmware.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) + + self.box_title = _heading("Box self-report (KOTA lines)") + self.box = QLabel(wordWrap=True) + self.box.setTextFormat(Qt.TextFormat.RichText) + self.box.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) + + self.milestones = QListWidget() + self.milestones.setFrameShape(QFrame.Shape.NoFrame) + self.milestones.setSelectionMode(QListWidget.SelectionMode.NoSelection) + self.milestones.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Maximum) + + self.tree = QTreeWidget() + self.tree.setHeaderHidden(True) + self.tree.setIndentation(16) + self.tree.setTextElideMode(Qt.TextElideMode.ElideRight) + self.detail = QTextBrowser() + self.detail.setOpenExternalLinks(False) + findings = QSplitter(Qt.Orientation.Vertical) + findings.addWidget(self.tree) + findings.addWidget(self.detail) + findings.setSizes([220, 260]) + + lay = QVBoxLayout(self) + lay.setContentsMargins(4, 0, 0, 0) + lay.addWidget(self.verdict) + lay.addWidget(_heading("Status LED should be")) + lay.addLayout(led_row) + lay.addWidget(_heading("Firmware")) + lay.addWidget(self.firmware) + self.qr_btn = QPushButton("Matter pairing QR code...") + self.qr_btn.setToolTip("Show the box's Matter QR code on screen to scan with the phone") + lay.addWidget(self.qr_btn, 0, Qt.AlignmentFlag.AlignLeft) + lay.addWidget(self.box_title) + lay.addWidget(self.box) + lay.addWidget(_heading("Start-up progress (current boot)")) + lay.addWidget(self.milestones) + lay.addWidget(_heading("Findings - root causes first; click one for details")) + lay.addWidget(findings, 1) + + self._findings: dict[str, Finding] = {} + self._selected: str | None = None + # key -> expanded, chosen by the user; survives the rebuild on refresh. + self._expanded: dict[str, bool] = {} + self.tree.currentItemChanged.connect(self._on_select) + self.tree.itemCollapsed.connect(lambda it: self._remember(it, False)) + self.tree.itemExpanded.connect(lambda it: self._remember(it, True)) + + # ------------------------------------------------------------------ + def show_report(self, r: Report) -> None: + bg, fg = BANNER.get(r.verdict_severity, BANNER[Severity.WARNING]) + if r.lines == 0: + bg, fg = "#e6e6e6", "#333333" + self.verdict.setText(r.verdict) + self.verdict.setStyleSheet(f"QLabel {{ background:{bg}; color:{fg}; border-radius:6px; }}") + + name, colour = LED[r.led] + self.led_swatch.setStyleSheet( + f"QLabel {{ background:{colour}; border:1px solid #777; border-radius:11px; }}") + ask = "" if r.led == "UNKNOWN" else " - check it matches the box" + pattern = f", {_esc(r.led_pattern)}" if r.led_pattern else "" + meaning = f" = {_esc(r.led_meaning)}" if r.led_meaning else "" + source = " (reported by the box)" if r.led_exact else "" + reason = f"
    from: {_esc(r.led_reason)}" if r.led_reason else "" + self.led_text.setText(f"{name}{pattern}{meaning}{source}{ask}{reason}") + + fw = r.firmware + sd = {True: "reads Wi-Fi settings from the SD card", + False: "does not read Wi-Fi settings from the SD card", + None: "Wi-Fi-from-SD support unknown"} + self.firmware.setText(f"{_esc(fw.describe())}
    {sd[fw.supports_sd_wifi]}") + + self.box_title.setVisible(bool(r.box)) + self.box.setVisible(bool(r.box)) + self.box.setText("" + "".join( + f"" + f"" + for topic, text, st in r.box) + "
    {BOX_STATE[st][1]} {_esc(topic)} {_esc(text)}
    ") + + self.milestones.clear() + for _, label, t in r.milestones: + done = t is not None + it = QListWidgetItem(("✔ " if done else "○ ") + label + + (f" ({t / 1000:.1f} s)" if done else "")) + it.setForeground(QBrush(QColor("#1b7a33" if done else "#8a8a8a"))) + self.milestones.addItem(it) + rows = min(max(self.milestones.count(), 1), 15) + self.milestones.setFixedHeight(rows * max(self.milestones.sizeHintForRow(0), 16) + 6) + + self._fill_tree(r) + + def _fill_tree(self, r: Report) -> None: + self.tree.blockSignals(True) + self.tree.clear() + self._findings = {} + if not r.roots: + it = QTreeWidgetItem(["No problems detected in the log so far."]) + it.setForeground(0, QBrush(QColor("#1b7a33"))) + self.tree.addTopLevelItem(it) + for rc in r.roots: + top = self._item(rc.finding, bold=True) + self.tree.addTopLevelItem(top) + for c in rc.consequences: + top.addChild(self._item(c, prefix="↳ ")) + top.setExpanded(self._expanded.get(rc.finding.key, True)) + for title, key, items in (("Seen in earlier boots only", "grp:earlier", r.earlier), + ("Resolved during this session", "grp:resolved", r.resolved)): + if not items: + continue + grp = QTreeWidgetItem([f"{title} ({len(items)})"]) + grp.setData(0, _KEY, key) + grp.setForeground(0, QBrush(QColor("#666666"))) + self.tree.addTopLevelItem(grp) + for f in items: + grp.addChild(self._item(f, prefix="✔ " if key == "grp:resolved" else "")) + grp.setExpanded(self._expanded.get(key, False)) + # Keep the selection across refreshes; default to the top root cause. + target = self._selected if self._selected in self._findings else ( + r.roots[0].finding.key if r.roots else None) + self.tree.blockSignals(False) + self._select(target) + + def _remember(self, item: QTreeWidgetItem, expanded: bool) -> None: + if key := item.data(0, _KEY): + self._expanded[key] = expanded + + def _item(self, f: Finding, prefix: str = "", bold: bool = False) -> QTreeWidgetItem: + it = QTreeWidgetItem([f"{prefix}[{f.severity.label}] {f.title}"]) + it.setData(0, _KEY, f.key) + it.setForeground(0, QBrush(QColor(SEV_COLOR.get(f.severity, "#333333")))) + it.setToolTip(0, f.title) + if bold: + font = it.font(0) + font.setBold(True) + it.setFont(0, font) + self._findings[f.key] = f + return it + + def _select(self, key: str | None) -> None: + if key is None: + self.detail.setHtml("

    Nothing to show yet.

    ") + return + match = self.tree.findItems("*", Qt.MatchFlag.MatchWildcard | Qt.MatchFlag.MatchRecursive) + for it in match: + if it.data(0, _KEY) == key: + self.tree.setCurrentItem(it) + return + + def _on_select(self, cur: QTreeWidgetItem | None, _prev) -> None: + key = cur.data(0, _KEY) if cur else None + f = self._findings.get(key or "") + if f is None: + return + self._selected = key + self.detail.setHtml(_detail_html(f)) + + +def _detail_html(f: Finding) -> str: + colour = SEV_COLOR.get(f.severity, "#333") + actions = "".join(f"
  • {_esc(a)}
  • " for a in f.actions) + seen = _esc(f.seen_text()) + evidence = "
    ".join(_esc(e) for e in f.evidence) + codes = "".join(f"
  • {_esc(eid)}" + (f" - {_esc(a)}" if a else "") + "
  • " + for eid, a in f.error_ids.items()) + return ( + f"

    {_esc(f.title)}

    " + f"

    What it means: {_esc(f.meaning)}

    " + + (f"

    What to do:

      {actions}
    " if actions else "") + + (f"

    Firmware error codes (error_ids.csv):

      {codes}
    " if codes else "") + + f"

    {seen}

    " + + (f"

    {evidence}

    " + if evidence else "") + ) diff --git a/kota_tech/ui/flash_tab.py b/kota_tech/ui/flash_tab.py index b963f33..1accf88 100644 --- a/kota_tech/ui/flash_tab.py +++ b/kota_tech/ui/flash_tab.py @@ -1,364 +1,364 @@ -"""Flash firmware tab: only the .bin files in the tool's firmware/ folder, -chosen by their compile date; App-only by default.""" - -from __future__ import annotations - -import datetime as dt -import tempfile -from pathlib import Path - -from PySide6.QtCore import QObject, Qt, QTimer, QUrl, Signal -from PySide6.QtGui import QDesktopServices -from PySide6.QtWidgets import ( - QButtonGroup, - QCheckBox, - QComboBox, - QGroupBox, - QHBoxLayout, - QLabel, - QMessageBox, - QPlainTextEdit, - QProgressBar, - QPushButton, - QRadioButton, - QVBoxLayout, - QWidget, -) - -from ..core.session import SESSIONS_DIR -from ..diag.firmware import local_build, register_local_build, same_build -from ..flash.image import FIRMWARE_DIR, FirmwareImage, folder_images -from ..flash.plan import Mode, build_plan -from ..flash.runner import FlashRunner, Result -from ..serialio.ports import PortInfo, list_serial_ports - -BACKUP_DIR = SESSIONS_DIR.parent / "backups" -VERIFY_TIMEOUT_MS = 45_000 - -HELP = ( - "If flashing cannot connect: use a data-capable USB cable directly on the laptop, close " - "other programs using the port, then retry. Last resort (case must be opened): hold BOOT, " - "press and release RESET, release BOOT, and press Start again." -) - - -class _Bridge(QObject): - step = Signal(int, str) - progress = Signal(int) - output = Signal(str) - done = Signal(object) - - -class FlashTab(QWidget): - def __init__(self, serial_tab, parent: QWidget | None = None): - super().__init__(parent) - self.serial_tab = serial_tab - self._images: list[FirmwareImage] = [] - self._ports: list[PortInfo] = [] # before any signal can call _update() - self._runner: FlashRunner | None = None - self._port: PortInfo | None = None - self._image: FirmwareImage | None = None - - # 1. box - box = QGroupBox("1. Sensorbox") - self.port_combo = QComboBox(minimumWidth=360) - refresh = QPushButton("Refresh") - refresh.clicked.connect(self.refresh_ports) - self.box_fw = QLabel(wordWrap=True) - bl = QVBoxLayout(box) - row = QHBoxLayout() - row.addWidget(self.port_combo) - row.addWidget(refresh) - row.addStretch(1) - bl.addLayout(row) - bl.addWidget(self.box_fw) - - # 2. firmware - only files in the tool's firmware/ folder - fwbox = QGroupBox("2. Firmware (by compile date)") - self.image_combo = QComboBox(minimumWidth=360) - self.image_combo.setToolTip(f"Firmware files in {FIRMWARE_DIR}") - self.reload_btn = QPushButton("Refresh list") - self.reload_btn.setToolTip("Read the firmware folder again (after the firmware team added a file)") - self.reload_btn.clicked.connect(lambda: self._load_images(self._selected_sha())) - self.folder_btn = QPushButton("Open firmware folder") - self.folder_btn.clicked.connect( - lambda: QDesktopServices.openUrl(QUrl.fromLocalFile(str(FIRMWARE_DIR)))) - self.image_info = QLabel(wordWrap=True) - self.image_info.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) - fl = QVBoxLayout(fwbox) - fl.addWidget(self.image_combo) - brow = QHBoxLayout() - brow.addWidget(self.reload_btn) - brow.addWidget(self.folder_btn) - brow.addStretch(1) - fl.addLayout(brow) - fl.addWidget(self.image_info) - self._problems: list[str] = [] - - # 3. mode - modebox = QGroupBox("3. What to write") - self.rb_app = QRadioButton("App only (recommended for repairs)") - self.rb_full = QRadioButton("Full image (new or completely wiped boards)") - self.mode_group = QButtonGroup(self) - self.mode_group.addButton(self.rb_app) - self.mode_group.addButton(self.rb_full) - self.rb_app.setChecked(True) - app_note = QLabel("Replaces the firmware only. Keeps the Matter pairing, the OTA registration and the " - "settings stored on the box.", wordWrap=True) - full_note = QLabel("Writes everything like the factory. Erases the Matter pairing and the OTA " - "registration - the box must be paired again afterwards.", wordWrap=True) - for n in (app_note, full_note): - n.setContentsMargins(24, 0, 0, 6) - n.setStyleSheet("color:#555") - self.confirm_full = QCheckBox("I understand the pairing and OTA registration will be erased") - self.backup = QCheckBox("Back up the box's complete flash first (8 MB, about 3 minutes)") - ml = QVBoxLayout(modebox) - for w in (self.rb_app, app_note, self.rb_full, full_note, self.confirm_full, self.backup): - ml.addWidget(w) - - # 4. run - runbox = QGroupBox("4. Flash") - self.plan_label = QLabel(wordWrap=True) - self.start_btn = QPushButton("Start flashing") - self.start_btn.setMinimumHeight(36) - self.start_btn.clicked.connect(self._start) - self.step_label = QLabel() - self.bar = QProgressBar() - self.result = QLabel(wordWrap=True) - self.result.setMargin(8) - self.result.setTextFormat(Qt.TextFormat.RichText) - self.details_btn = QPushButton("Show esptool output") - self.details_btn.setCheckable(True) - self.output = QPlainTextEdit(readOnly=True) - self.output.setVisible(False) - self.details_btn.toggled.connect(self.output.setVisible) - rl = QVBoxLayout(runbox) - for w in (self.plan_label, self.start_btn, self.step_label, self.bar, self.result, self.details_btn, self.output): - rl.addWidget(w) - help_lbl = QLabel(HELP, wordWrap=True) - help_lbl.setStyleSheet("color:#555") - rl.addWidget(help_lbl) - rl.addStretch(1) # keep the run column packed at the top - - left = QVBoxLayout() - for w in (box, fwbox, modebox): - left.addWidget(w) - left.addStretch(1) - lay = QHBoxLayout(self) - lay.addLayout(left, 1) - lay.addWidget(runbox, 1) - - self._bridge = _Bridge() - self._bridge.step.connect(self._on_step) - self._bridge.progress.connect(self.bar.setValue) - self._bridge.output.connect(self.output.appendPlainText) - self._bridge.done.connect(self._on_done) - self._verify_timer = QTimer(self, interval=500) - self._verify_timer.timeout.connect(self._check_verify) - - for sig in (self.rb_app.toggled, self.confirm_full.toggled, self.backup.toggled): - sig.connect(self._update) - self.rb_full.toggled.connect(lambda on: self.backup.setChecked(on)) # backup default on for Full - self.image_combo.currentIndexChanged.connect(self._update) - self.port_combo.currentIndexChanged.connect(self._update) - - self._load_images() - self.refresh_ports() - self._show_result("", None) - - # ------------------------------------------------------------ setup - def _load_images(self, select_sha: str | None = None) -> None: - self._images, self._problems = folder_images() - for img in self._images: # lets the Serial monitor show a box build's compile date - register_local_build(img.app.elf_sha, img.app.version, img.app.built, img.name) - self.image_combo.blockSignals(True) - self.image_combo.clear() - for img in self._images: - self.image_combo.addItem(img.label()) - if not self._images: - self.image_combo.addItem("No firmware file in the firmware folder") - idx = next((i for i, im in enumerate(self._images) if im.sha256 == select_sha), 0) - self.image_combo.setCurrentIndex(idx) # default: newest compile date - self.image_combo.blockSignals(False) - self._update() - - def _selected_sha(self) -> str | None: - img = self._selected_image() - return img.sha256 if img else None - - def refresh_ports(self) -> None: - current = self.port_combo.currentData() - self._ports = list_serial_ports() - self.port_combo.clear() - for p in self._ports: - self.port_combo.addItem(p.label, p.device) - if not self._ports: - self.port_combo.addItem("No sensorbox found - check USB cable and power switch", None) - self.port_combo.setCurrentIndex(max(self.port_combo.findData(current), 0)) - self._update() - - def showEvent(self, event): # noqa: N802 - refresh when the tab is opened - self.refresh_ports() - super().showEvent(event) - - # ------------------------------------------------------------ state - def _selected_image(self) -> FirmwareImage | None: - i = self.image_combo.currentIndex() - return self._images[i] if 0 <= i < len(self._images) else None - - def _selected_port(self) -> PortInfo | None: - dev = self.port_combo.currentData() - return next((p for p in self._ports if p.device == dev), None) - - def _update(self) -> None: - img = self._selected_image() - info = "" - if img: - a = img.app - kind = "complete image (bootloader + layout + app)" if img.kind == "merged" else "app only" - info = (f"Compiled {a.built} · {a.project} v{a.version}, ESP-IDF {a.idf}, {kind}
    " - f"file firmware/{img.name} · build {a.elf_sha[:8]} · " - f"SHA-256 {img.sha256[:16]}... · checksum OK") - else: - info = (f"Put the firmware .bin file from the firmware team into
    {FIRMWARE_DIR}
    " - "and press Refresh list. Only files in this folder can be flashed.") - if self._problems: - info += ("
    Not offered: " - + "; ".join(self._problems) + "") - self.image_info.setText(info) - # An app-only file has no bootloader/layout: Full mode is impossible. - full_ok = bool(img and img.kind == "merged") - self.rb_full.setEnabled(full_ok and self._runner is None) - if not full_ok and self.rb_full.isChecked(): - self.rb_app.setChecked(True) - self.box_fw.setText(self._box_text(img)) - - self.confirm_full.setVisible(self.rb_full.isChecked()) - ok = bool(img and self._selected_port() and self._runner is None - and (self.rb_app.isChecked() or self.confirm_full.isChecked())) - self.start_btn.setEnabled(ok) - if img and self._selected_port(): - mode = Mode.APP_ONLY if self.rb_app.isChecked() else Mode.FULL - steps = ["Back up the complete flash (~3 min)"] if self.backup.isChecked() else [] - if mode == Mode.APP_ONLY: - steps += ["Check the box has the same partition layout", "Write the firmware app (~20 s)"] - else: - steps += ["Write the complete image (~25 s)"] - steps += ["Restart the box and verify the new firmware boots"] - self.plan_label.setText("Plan:
      " + "".join(f"
    1. {s}
    2. " for s in steps) + "
    ") - else: - self.plan_label.setText("Select a sensorbox and a firmware image.") - - def _box_text(self, img: FirmwareImage | None) -> str: - fw = self.serial_tab.engine.firmware - if not fw.elf_sha: - return "Firmware on the box: unknown - connect in the Serial monitor and press Reset & capture to see it." - msg = f"Box currently runs: {fw.describe()}" - if not img: - return msg - if same_build(fw.elf_sha, img.app.elf_sha): - return msg + "
    It already has this firmware. Re-flash only if you suspect the firmware is damaged." - local = local_build(fw.elf_sha) - box_built, img_built = _parse_built(fw.built or (local[1] if local else None)), img.app.built_at - if box_built and img_built and img_built > box_built: - return msg + f"
    The selected firmware is newer (built {img.app.built})." - if box_built and img_built and img_built < box_built: - return msg + ("
    The selected firmware is OLDER than the box's (the box was " - "probably updated over OTA). The box updates itself again once online.") - return msg + "
    The box runs a different build." - - # ------------------------------------------------------------ flash - def _start(self) -> None: - img, port = self._selected_image(), self._selected_port() - if not img or not port: - return - mode = Mode.APP_ONLY if self.rb_app.isChecked() else Mode.FULL - what = ("the firmware app (pairing and settings are kept)" if mode == Mode.APP_ONLY - else "the COMPLETE image (pairing and OTA registration are erased)") - if QMessageBox.question(self, "Flash firmware", - f"Write {what}\n\nFirmware: v{img.app.version}, compiled {img.app.built}\n" - f"File: firmware/{img.name}\nBox: {port.label}\n\n" - "Keep the box powered and the USB cable connected until it finishes.", - QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel - ) != QMessageBox.StandardButton.Ok: - return - backup_to = None - if self.backup.isChecked(): - BACKUP_DIR.mkdir(parents=True, exist_ok=True) - stamp = dt.datetime.now().strftime("%Y%m%d_%H%M%S") - backup_to = BACKUP_DIR / f"{(port.mac or port.device).replace(':', '')}_{stamp}.bin" - plan = build_plan(img, mode, port.device, Path(tempfile.mkdtemp(prefix="kota_flash_")), backup_to) - - self._port, self._image = port, img - self.serial_tab.release_port() # esptool needs the port exclusively - self.output.clear() - self.output.appendPlainText("\n".join(plan.summary())) - self._show_result("", None) - self._runner = FlashRunner(plan, self._bridge.step.emit, self._bridge.progress.emit, - self._bridge.output.emit, self._bridge.done.emit) - self._set_busy(True) - self._runner.start() - - def _set_busy(self, busy: bool) -> None: - for w in (self.port_combo, self.image_combo, self.rb_app, self.backup, self.confirm_full, - self.reload_btn): - w.setEnabled(not busy) - self._update() - - def _on_step(self, i: int, title: str) -> None: - n = len(self._runner.plan.steps) if self._runner else 0 - self.step_label.setText(f"Step {i + 1} of {n + 1}: {title}") - - def _on_done(self, res: Result) -> None: - self._runner = None - if not res.ok: - self._set_busy(False) - self.step_label.setText("Stopped.") - hint = f"

    {res.hint}

    " if res.hint else "" - self._show_result(f"Flashing failed. {res.message}{hint}", "error") - self.details_btn.setChecked(True) - return - self.step_label.setText("Final step: restarting the box and checking which firmware it boots...") - self.bar.setValue(0) - self._show_result("Firmware written. Waiting for the box to start...", "info") - self.serial_tab.connect_port(self._port, capture=True) - self._verify_ms = 0 - self._verify_timer.start() - - def _check_verify(self) -> None: - self._verify_ms += self._verify_timer.interval() - self.bar.setValue(min(100, int(100 * self._verify_ms / VERIFY_TIMEOUT_MS))) - fw = self.serial_tab.engine.firmware - want = self._image.app.elf_sha[:8] - done = same_build(fw.elf_sha, self._image.app.elf_sha) - if done or self._verify_ms >= VERIFY_TIMEOUT_MS: - self._verify_timer.stop() - self._set_busy(False) - self.bar.setValue(100) - if done: - self.step_label.setText("Done.") - self._show_result(f"Success. The box boots the new firmware (build {want}). " - "Check the Diagnosis panel in the Serial monitor.", "ok") - else: - seen = fw.elf_sha[:8] if fw.elf_sha else "no boot seen" - self._show_result(f"Written, but not verified. Expected build {want}, saw: {seen}. " - "Open the Serial monitor and press Reset & capture.", "warning") - - def _show_result(self, html: str, kind: str | None) -> None: - colours = {"ok": ("#2e9e44", "#e3f4e7"), "info": ("#2e6da4", "#e3edf7"), - "warning": ("#c77c02", "#fdf0d6"), "error": ("#d9534f", "#f9e0df")} - self.result.setVisible(bool(html)) - if html: - border, bg = colours[kind or "info"] - self.result.setStyleSheet(f"QLabel {{ border:2px solid {border}; background:{bg}; border-radius:6px; }}") - self.result.setText(html) - - -def _parse_built(s: str | None) -> dt.datetime | None: - """'Jun 29 2026 11:03:32' (app_init "Compile time") -> datetime.""" - if not s: - return None - try: - return dt.datetime.strptime(" ".join(s.split()), "%b %d %Y %H:%M:%S") - except ValueError: - return None +"""Flash firmware tab: only the .bin files in the tool's firmware/ folder, +chosen by their compile date; App-only by default.""" + +from __future__ import annotations + +import datetime as dt +import tempfile +from pathlib import Path + +from PySide6.QtCore import QObject, Qt, QTimer, QUrl, Signal +from PySide6.QtGui import QDesktopServices +from PySide6.QtWidgets import ( + QButtonGroup, + QCheckBox, + QComboBox, + QGroupBox, + QHBoxLayout, + QLabel, + QMessageBox, + QPlainTextEdit, + QProgressBar, + QPushButton, + QRadioButton, + QVBoxLayout, + QWidget, +) + +from ..core.session import SESSIONS_DIR +from ..diag.firmware import local_build, register_local_build, same_build +from ..flash.image import FIRMWARE_DIR, FirmwareImage, folder_images +from ..flash.plan import Mode, build_plan +from ..flash.runner import FlashRunner, Result +from ..serialio.ports import PortInfo, list_serial_ports + +BACKUP_DIR = SESSIONS_DIR.parent / "backups" +VERIFY_TIMEOUT_MS = 45_000 + +HELP = ( + "If flashing cannot connect: use a data-capable USB cable directly on the laptop, close " + "other programs using the port, then retry. Last resort (case must be opened): hold BOOT, " + "press and release RESET, release BOOT, and press Start again." +) + + +class _Bridge(QObject): + step = Signal(int, str) + progress = Signal(int) + output = Signal(str) + done = Signal(object) + + +class FlashTab(QWidget): + def __init__(self, serial_tab, parent: QWidget | None = None): + super().__init__(parent) + self.serial_tab = serial_tab + self._images: list[FirmwareImage] = [] + self._ports: list[PortInfo] = [] # before any signal can call _update() + self._runner: FlashRunner | None = None + self._port: PortInfo | None = None + self._image: FirmwareImage | None = None + + # 1. box + box = QGroupBox("1. Sensorbox") + self.port_combo = QComboBox(minimumWidth=360) + refresh = QPushButton("Refresh") + refresh.clicked.connect(self.refresh_ports) + self.box_fw = QLabel(wordWrap=True) + bl = QVBoxLayout(box) + row = QHBoxLayout() + row.addWidget(self.port_combo) + row.addWidget(refresh) + row.addStretch(1) + bl.addLayout(row) + bl.addWidget(self.box_fw) + + # 2. firmware - only files in the tool's firmware/ folder + fwbox = QGroupBox("2. Firmware (by compile date)") + self.image_combo = QComboBox(minimumWidth=360) + self.image_combo.setToolTip(f"Firmware files in {FIRMWARE_DIR}") + self.reload_btn = QPushButton("Refresh list") + self.reload_btn.setToolTip("Read the firmware folder again (after the firmware team added a file)") + self.reload_btn.clicked.connect(lambda: self._load_images(self._selected_sha())) + self.folder_btn = QPushButton("Open firmware folder") + self.folder_btn.clicked.connect( + lambda: QDesktopServices.openUrl(QUrl.fromLocalFile(str(FIRMWARE_DIR)))) + self.image_info = QLabel(wordWrap=True) + self.image_info.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) + fl = QVBoxLayout(fwbox) + fl.addWidget(self.image_combo) + brow = QHBoxLayout() + brow.addWidget(self.reload_btn) + brow.addWidget(self.folder_btn) + brow.addStretch(1) + fl.addLayout(brow) + fl.addWidget(self.image_info) + self._problems: list[str] = [] + + # 3. mode + modebox = QGroupBox("3. What to write") + self.rb_app = QRadioButton("App only (recommended for repairs)") + self.rb_full = QRadioButton("Full image (new or completely wiped boards)") + self.mode_group = QButtonGroup(self) + self.mode_group.addButton(self.rb_app) + self.mode_group.addButton(self.rb_full) + self.rb_app.setChecked(True) + app_note = QLabel("Replaces the firmware only. Keeps the Matter pairing, the OTA registration and the " + "settings stored on the box.", wordWrap=True) + full_note = QLabel("Writes everything like the factory. Erases the Matter pairing and the OTA " + "registration - the box must be paired again afterwards.", wordWrap=True) + for n in (app_note, full_note): + n.setContentsMargins(24, 0, 0, 6) + n.setStyleSheet("color:#555") + self.confirm_full = QCheckBox("I understand the pairing and OTA registration will be erased") + self.backup = QCheckBox("Back up the box's complete flash first (8 MB, about 3 minutes)") + ml = QVBoxLayout(modebox) + for w in (self.rb_app, app_note, self.rb_full, full_note, self.confirm_full, self.backup): + ml.addWidget(w) + + # 4. run + runbox = QGroupBox("4. Flash") + self.plan_label = QLabel(wordWrap=True) + self.start_btn = QPushButton("Start flashing") + self.start_btn.setMinimumHeight(36) + self.start_btn.clicked.connect(self._start) + self.step_label = QLabel() + self.bar = QProgressBar() + self.result = QLabel(wordWrap=True) + self.result.setMargin(8) + self.result.setTextFormat(Qt.TextFormat.RichText) + self.details_btn = QPushButton("Show esptool output") + self.details_btn.setCheckable(True) + self.output = QPlainTextEdit(readOnly=True) + self.output.setVisible(False) + self.details_btn.toggled.connect(self.output.setVisible) + rl = QVBoxLayout(runbox) + for w in (self.plan_label, self.start_btn, self.step_label, self.bar, self.result, self.details_btn, self.output): + rl.addWidget(w) + help_lbl = QLabel(HELP, wordWrap=True) + help_lbl.setStyleSheet("color:#555") + rl.addWidget(help_lbl) + rl.addStretch(1) # keep the run column packed at the top + + left = QVBoxLayout() + for w in (box, fwbox, modebox): + left.addWidget(w) + left.addStretch(1) + lay = QHBoxLayout(self) + lay.addLayout(left, 1) + lay.addWidget(runbox, 1) + + self._bridge = _Bridge() + self._bridge.step.connect(self._on_step) + self._bridge.progress.connect(self.bar.setValue) + self._bridge.output.connect(self.output.appendPlainText) + self._bridge.done.connect(self._on_done) + self._verify_timer = QTimer(self, interval=500) + self._verify_timer.timeout.connect(self._check_verify) + + for sig in (self.rb_app.toggled, self.confirm_full.toggled, self.backup.toggled): + sig.connect(self._update) + self.rb_full.toggled.connect(lambda on: self.backup.setChecked(on)) # backup default on for Full + self.image_combo.currentIndexChanged.connect(self._update) + self.port_combo.currentIndexChanged.connect(self._update) + + self._load_images() + self.refresh_ports() + self._show_result("", None) + + # ------------------------------------------------------------ setup + def _load_images(self, select_sha: str | None = None) -> None: + self._images, self._problems = folder_images() + for img in self._images: # lets the Serial monitor show a box build's compile date + register_local_build(img.app.elf_sha, img.app.version, img.app.built, img.name) + self.image_combo.blockSignals(True) + self.image_combo.clear() + for img in self._images: + self.image_combo.addItem(img.label()) + if not self._images: + self.image_combo.addItem("No firmware file in the firmware folder") + idx = next((i for i, im in enumerate(self._images) if im.sha256 == select_sha), 0) + self.image_combo.setCurrentIndex(idx) # default: newest compile date + self.image_combo.blockSignals(False) + self._update() + + def _selected_sha(self) -> str | None: + img = self._selected_image() + return img.sha256 if img else None + + def refresh_ports(self) -> None: + current = self.port_combo.currentData() + self._ports = list_serial_ports() + self.port_combo.clear() + for p in self._ports: + self.port_combo.addItem(p.label, p.device) + if not self._ports: + self.port_combo.addItem("No sensorbox found - check USB cable and power switch", None) + self.port_combo.setCurrentIndex(max(self.port_combo.findData(current), 0)) + self._update() + + def showEvent(self, event): # noqa: N802 - refresh when the tab is opened + self.refresh_ports() + super().showEvent(event) + + # ------------------------------------------------------------ state + def _selected_image(self) -> FirmwareImage | None: + i = self.image_combo.currentIndex() + return self._images[i] if 0 <= i < len(self._images) else None + + def _selected_port(self) -> PortInfo | None: + dev = self.port_combo.currentData() + return next((p for p in self._ports if p.device == dev), None) + + def _update(self) -> None: + img = self._selected_image() + info = "" + if img: + a = img.app + kind = "complete image (bootloader + layout + app)" if img.kind == "merged" else "app only" + info = (f"Compiled {a.built} · {a.project} v{a.version}, ESP-IDF {a.idf}, {kind}
    " + f"file firmware/{img.name} · build {a.elf_sha[:8]} · " + f"SHA-256 {img.sha256[:16]}... · checksum OK") + else: + info = (f"Put the firmware .bin file from the firmware team into
    {FIRMWARE_DIR}
    " + "and press Refresh list. Only files in this folder can be flashed.") + if self._problems: + info += ("
    Not offered: " + + "; ".join(self._problems) + "") + self.image_info.setText(info) + # An app-only file has no bootloader/layout: Full mode is impossible. + full_ok = bool(img and img.kind == "merged") + self.rb_full.setEnabled(full_ok and self._runner is None) + if not full_ok and self.rb_full.isChecked(): + self.rb_app.setChecked(True) + self.box_fw.setText(self._box_text(img)) + + self.confirm_full.setVisible(self.rb_full.isChecked()) + ok = bool(img and self._selected_port() and self._runner is None + and (self.rb_app.isChecked() or self.confirm_full.isChecked())) + self.start_btn.setEnabled(ok) + if img and self._selected_port(): + mode = Mode.APP_ONLY if self.rb_app.isChecked() else Mode.FULL + steps = ["Back up the complete flash (~3 min)"] if self.backup.isChecked() else [] + if mode == Mode.APP_ONLY: + steps += ["Check the box has the same partition layout", "Write the firmware app (~20 s)"] + else: + steps += ["Write the complete image (~25 s)"] + steps += ["Restart the box and verify the new firmware boots"] + self.plan_label.setText("Plan:
      " + "".join(f"
    1. {s}
    2. " for s in steps) + "
    ") + else: + self.plan_label.setText("Select a sensorbox and a firmware image.") + + def _box_text(self, img: FirmwareImage | None) -> str: + fw = self.serial_tab.engine.firmware + if not fw.elf_sha: + return "Firmware on the box: unknown - connect in the Serial monitor and press Reset & capture to see it." + msg = f"Box currently runs: {fw.describe()}" + if not img: + return msg + if same_build(fw.elf_sha, img.app.elf_sha): + return msg + "
    It already has this firmware. Re-flash only if you suspect the firmware is damaged." + local = local_build(fw.elf_sha) + box_built, img_built = _parse_built(fw.built or (local[1] if local else None)), img.app.built_at + if box_built and img_built and img_built > box_built: + return msg + f"
    The selected firmware is newer (built {img.app.built})." + if box_built and img_built and img_built < box_built: + return msg + ("
    The selected firmware is OLDER than the box's (the box was " + "probably updated over OTA). The box updates itself again once online.") + return msg + "
    The box runs a different build." + + # ------------------------------------------------------------ flash + def _start(self) -> None: + img, port = self._selected_image(), self._selected_port() + if not img or not port: + return + mode = Mode.APP_ONLY if self.rb_app.isChecked() else Mode.FULL + what = ("the firmware app (pairing and settings are kept)" if mode == Mode.APP_ONLY + else "the COMPLETE image (pairing and OTA registration are erased)") + if QMessageBox.question(self, "Flash firmware", + f"Write {what}\n\nFirmware: v{img.app.version}, compiled {img.app.built}\n" + f"File: firmware/{img.name}\nBox: {port.label}\n\n" + "Keep the box powered and the USB cable connected until it finishes.", + QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel + ) != QMessageBox.StandardButton.Ok: + return + backup_to = None + if self.backup.isChecked(): + BACKUP_DIR.mkdir(parents=True, exist_ok=True) + stamp = dt.datetime.now().strftime("%Y%m%d_%H%M%S") + backup_to = BACKUP_DIR / f"{(port.mac or port.device).replace(':', '')}_{stamp}.bin" + plan = build_plan(img, mode, port.device, Path(tempfile.mkdtemp(prefix="kota_flash_")), backup_to) + + self._port, self._image = port, img + self.serial_tab.release_port() # esptool needs the port exclusively + self.output.clear() + self.output.appendPlainText("\n".join(plan.summary())) + self._show_result("", None) + self._runner = FlashRunner(plan, self._bridge.step.emit, self._bridge.progress.emit, + self._bridge.output.emit, self._bridge.done.emit) + self._set_busy(True) + self._runner.start() + + def _set_busy(self, busy: bool) -> None: + for w in (self.port_combo, self.image_combo, self.rb_app, self.backup, self.confirm_full, + self.reload_btn): + w.setEnabled(not busy) + self._update() + + def _on_step(self, i: int, title: str) -> None: + n = len(self._runner.plan.steps) if self._runner else 0 + self.step_label.setText(f"Step {i + 1} of {n + 1}: {title}") + + def _on_done(self, res: Result) -> None: + self._runner = None + if not res.ok: + self._set_busy(False) + self.step_label.setText("Stopped.") + hint = f"

    {res.hint}

    " if res.hint else "" + self._show_result(f"Flashing failed. {res.message}{hint}", "error") + self.details_btn.setChecked(True) + return + self.step_label.setText("Final step: restarting the box and checking which firmware it boots...") + self.bar.setValue(0) + self._show_result("Firmware written. Waiting for the box to start...", "info") + self.serial_tab.connect_port(self._port, capture=True) + self._verify_ms = 0 + self._verify_timer.start() + + def _check_verify(self) -> None: + self._verify_ms += self._verify_timer.interval() + self.bar.setValue(min(100, int(100 * self._verify_ms / VERIFY_TIMEOUT_MS))) + fw = self.serial_tab.engine.firmware + want = self._image.app.elf_sha[:8] + done = same_build(fw.elf_sha, self._image.app.elf_sha) + if done or self._verify_ms >= VERIFY_TIMEOUT_MS: + self._verify_timer.stop() + self._set_busy(False) + self.bar.setValue(100) + if done: + self.step_label.setText("Done.") + self._show_result(f"Success. The box boots the new firmware (build {want}). " + "Check the Diagnosis panel in the Serial monitor.", "ok") + else: + seen = fw.elf_sha[:8] if fw.elf_sha else "no boot seen" + self._show_result(f"Written, but not verified. Expected build {want}, saw: {seen}. " + "Open the Serial monitor and press Reset & capture.", "warning") + + def _show_result(self, html: str, kind: str | None) -> None: + colours = {"ok": ("#2e9e44", "#e3f4e7"), "info": ("#2e6da4", "#e3edf7"), + "warning": ("#c77c02", "#fdf0d6"), "error": ("#d9534f", "#f9e0df")} + self.result.setVisible(bool(html)) + if html: + border, bg = colours[kind or "info"] + self.result.setStyleSheet(f"QLabel {{ border:2px solid {border}; background:{bg}; border-radius:6px; }}") + self.result.setText(html) + + +def _parse_built(s: str | None) -> dt.datetime | None: + """'Jun 29 2026 11:03:32' (app_init "Compile time") -> datetime.""" + if not s: + return None + try: + return dt.datetime.strptime(" ".join(s.split()), "%b %d %Y %H:%M:%S") + except ValueError: + return None diff --git a/kota_tech/ui/main_window.py b/kota_tech/ui/main_window.py index 6ca784b..2dd9359 100644 --- a/kota_tech/ui/main_window.py +++ b/kota_tech/ui/main_window.py @@ -1,424 +1,424 @@ -from __future__ import annotations - -from pathlib import Path - -from PySide6.QtCore import QCoreApplication, QObject, QSettings, Qt, QTimer, QUrl, Signal -from PySide6.QtGui import QColor, QDesktopServices, QFont, QGuiApplication, QTextCharFormat, QTextCursor -from PySide6.QtWidgets import ( - QCheckBox, - QComboBox, - QFileDialog, - QHBoxLayout, - QInputDialog, - QLabel, - QMainWindow, - QMessageBox, - QPlainTextEdit, - QPushButton, - QSplitter, - QTabWidget, - QTextBrowser, - QVBoxLayout, - QWidget, -) - -from .. import __version__ -from ..core.logfile import read_log_file -from ..core.logline import LineKind, LogLine, host_event -from ..core.session import SessionLog -from ..diag.engine import DiagnosticsEngine -from ..diag.guides import GuideRunner -from ..serialio.manager import SerialManager, State -from ..serialio.ports import PortInfo, list_serial_ports -from ..matter.payload import SetupPayload, payload_from_log -from ..service.report import DEFAULT_BASE, normalize_mac, report_url -from .cheatsheet import cheatsheet_html -from .diag_panel import DiagnosticsPanel -from .flash_tab import FlashTab -from .matter_qr import MatterQrDialog -from .guide_panel import GuidePanel -from .sd_tab import SdTab - -MAX_LINES = 20000 -# "KOTA" = the firmware's structured diagnostic lines (1.0.16+) plus errors. -LEVEL_FILTERS = {"All lines": None, "KOTA diagnostics + errors": "KOTA", - "Warnings + errors": {"W", "E"}, "Errors only": {"E"}} -COLORS = { - "E": "#ff6b6b", - "W": "#f5c451", - "I": "#d4d4d4", - LineKind.BOOT: "#6fc3ff", - LineKind.RESET: "#6fc3ff", - LineKind.PANIC: "#ff4dd2", - LineKind.HOST: "#7ee07e", - LineKind.KOTA: "#5fd7e8", - LineKind.RAW: "#a8a8a8", -} - - -class _Bridge(QObject): - """Marshals SerialManager callbacks (reader thread) onto the UI thread.""" - - line = Signal(object) - state = Signal(object, str) - - -class SerialTab(QWidget): - def __init__(self, parent: QWidget | None = None): - super().__init__(parent) - self._bridge = _Bridge() - self._bridge.line.connect(self._on_line) - self._bridge.state.connect(self._on_state) - self._manager = SerialManager(self._bridge.line.emit, self._bridge.state.emit) - self._session = SessionLog() - self._ports: list[PortInfo] = [] - self._counts = {"E": 0, "W": 0, "boots": 0} - self._engine = DiagnosticsEngine() - self._diag_dirty = True - self._runner = GuideRunner(self._engine, on_action=self._guide_action, on_log=self._guide_log) - - # --- controls - self.port_combo = QComboBox(minimumWidth=320) - self.all_ports = QCheckBox("Show all ports") - self.refresh_btn = QPushButton("Refresh") - self.connect_btn = QPushButton("Connect") - self.reset_btn = QPushButton("Reset && capture") - self.reset_btn.setToolTip("Reboot the sensorbox and record the log from the first boot line") - self.filter_combo = QComboBox() - self.filter_combo.addItems(LEVEL_FILTERS) - self.clear_btn = QPushButton("Clear view") - self.save_btn = QPushButton("Save view as...") - self.folder_btn = QPushButton("Open logs folder") - self.analyze_btn = QPushButton("Analyze log file...") - self.analyze_btn.setToolTip("Load a saved session log or serial capture and diagnose it") - self.copy_btn = QPushButton("Copy report") - self.copy_btn.setToolTip("Copy the diagnosis as text (e.g. to send to the firmware team)") - self.service_btn = QPushButton("Service report...") - self.service_btn.setToolTip("Open this box's page in the KOTA service system with the diagnosis " - "filled in; check it and press Save there") - self._settings = QSettings("KOTA", "TechnicianTool") - self.auto_service = QCheckBox("Open service report on disconnect") - self.auto_service.setChecked(self._settings.value("service/auto", False, type=bool)) - self.auto_service.toggled.connect(lambda on: self._settings.setValue("service/auto", on)) - self._active_port: PortInfo | None = None - - row1 = QHBoxLayout() - for w in (QLabel("Port:"), self.port_combo, self.all_ports, self.refresh_btn, - self.connect_btn, self.reset_btn): - row1.addWidget(w) - row1.addStretch(1) - row2 = QHBoxLayout() - for w in (QLabel("Show:"), self.filter_combo, self.clear_btn, self.save_btn, self.folder_btn, - self.analyze_btn, self.copy_btn, self.service_btn, self.auto_service): - row2.addWidget(w) - row2.addStretch(1) - - self.view = QPlainTextEdit(readOnly=True) - self.view.setMaximumBlockCount(MAX_LINES) - self.view.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap) - mono = QFont("Menlo" if QFont("Menlo").exactMatch() else "Consolas") - mono.setStyleHint(QFont.StyleHint.Monospace) - mono.setPointSize(10) - self.view.setFont(mono) - self.view.setStyleSheet("QPlainTextEdit { background:#1e1e1e; color:#d4d4d4; }") - - self.diag = DiagnosticsPanel() - self.guide = GuidePanel(self._runner, self._engine, matter_payload=self.matter_payload) - self.side_tabs = QTabWidget() - self.side_tabs.addTab(self.diag, "Diagnosis") - self.side_tabs.addTab(self.guide, "Guided troubleshooting") - split = QSplitter(Qt.Orientation.Horizontal) - split.addWidget(self.view) - split.addWidget(self.side_tabs) - split.setStretchFactor(0, 3) - split.setStretchFactor(1, 2) - split.setSizes([700, 480]) - - self.status = QLabel() - layout = QVBoxLayout(self) - layout.addLayout(row1) - layout.addLayout(row2) - layout.addWidget(split, 1) - layout.addWidget(self.status) - - # --- wiring - self.refresh_btn.clicked.connect(self.refresh_ports) - self.all_ports.toggled.connect(self.refresh_ports) - self.connect_btn.clicked.connect(self._toggle_connect) - self.reset_btn.clicked.connect(self._manager.reset_board) - self.clear_btn.clicked.connect(self.view.clear) - self.save_btn.clicked.connect(self._save_view) - self.folder_btn.clicked.connect(self._open_folder) - self.analyze_btn.clicked.connect(self._analyze_file) - self.copy_btn.clicked.connect(self._copy_report) - self.service_btn.clicked.connect(lambda: self._open_service_report(auto=False)) - self.diag.qr_btn.clicked.connect(lambda: MatterQrDialog(self.matter_payload(), self).exec()) - - # Report refresh is throttled; lines can arrive at >100/s. - self._diag_timer = QTimer(self, interval=400) - self._diag_timer.timeout.connect(self._refresh_diag) - self._diag_timer.start() - - # Auto-detect a box being plugged in while idle. - self._poll = QTimer(self, interval=2000) - self._poll.timeout.connect(self._auto_refresh) - self._poll.start() - - self.refresh_ports() - self._on_state(State.DISCONNECTED, "") - - # ---- ports ------------------------------------------------------- - def refresh_ports(self) -> None: - current = self.port_combo.currentData() - self._ports = list_serial_ports(include_all=self.all_ports.isChecked()) - self.port_combo.clear() - for p in self._ports: - self.port_combo.addItem(p.label, p.device) - if not self._ports: - self.port_combo.addItem("No sensorbox found - check USB cable and power switch", None) - idx = self.port_combo.findData(current) - self.port_combo.setCurrentIndex(max(idx, 0)) - - def _auto_refresh(self) -> None: - if self._manager.is_running: - return - devices = [p.device for p in list_serial_ports(self.all_ports.isChecked())] - if devices != [p.device for p in self._ports]: - self.refresh_ports() - - def _selected_port(self) -> PortInfo | None: - dev = self.port_combo.currentData() - return next((p for p in self._ports if p.device == dev), None) - - # ---- connection -------------------------------------------------- - def _toggle_connect(self) -> None: - if self._manager.is_running: - # Session file is closed in _on_state(DISCONNECTED), after the - # reader thread's final queued lines have been written. - self._manager.disconnect() - return - port = self._selected_port() - if port is None: - return - self._connect(port) - - def _connect(self, port: PortInfo) -> None: - self._counts = {"E": 0, "W": 0, "boots": 0} - self._engine.reset() - self._runner.cancel() - self._diag_dirty = True - self._session.start(port.mac or port.device) - self._active_port = port - self._manager.connect(port) - - def matter_payload(self) -> SetupPayload: - """Pairing payload reported by this box (KOTA: matter), from its log, or the defaults.""" - return payload_from_log([self._engine.matter_advert] if self._engine.matter_advert else [], - self._engine.matter_qr, self._engine.matter_manual) - - # ---- service system ------------------------------------------------- - def _service_base(self) -> str: - return str(self._settings.value("service/base", DEFAULT_BASE)) - - def _open_service_report(self, auto: bool) -> None: - mac = normalize_mac(self._engine.mac or (self._active_port.mac if self._active_port else "") or "") - if len(mac) != 12: - if not auto: - QMessageBox.information(self, "Service report", "The box's MAC address is not known yet. " - "Connect to the box and press Reset & capture first.") - return - url = report_url(mac, self._engine.report(), self._service_base()) - QDesktopServices.openUrl(QUrl(url)) - # Log that it happened, not the report itself (it is already in the session log). - self._guide_log(f"service report for {mac} opened in the browser - check it and press Save there") - - # ---- hand-off with the flash tab ------------------------------------ - @property - def engine(self) -> DiagnosticsEngine: - return self._engine - - def release_port(self) -> None: - """Stop monitoring so esptool can open the port exclusively.""" - if self._manager.is_running: - self._no_auto_service = True # a flash hand-off is not the end of a session - self._manager.disconnect() # joins the reader thread - QCoreApplication.processEvents() # deliver its queued final lines/state now - self._no_auto_service = False - - def connect_port(self, port: PortInfo, capture: bool = True) -> None: - """Monitor `port`; with capture, restart the box once connected to record its boot.""" - self.release_port() - idx = self.port_combo.findData(port.device) - if idx < 0: - self.port_combo.addItem(port.label, port.device) - self._ports.append(port) - idx = self.port_combo.count() - 1 - self.port_combo.setCurrentIndex(idx) - self._reset_on_connect = capture - self._connect(port) - - def _on_state(self, state: State, detail: str) -> None: - running = state != State.DISCONNECTED - if not running and self._manager.is_running: - return # stale event from a session that was already replaced - if state == State.CONNECTED and getattr(self, "_reset_on_connect", False): - self._reset_on_connect = False - QTimer.singleShot(300, self._manager.reset_board) - if not running and self._session.path is not None: - self._session.write_report(self._engine.report().to_text()) - if (self.auto_service.isChecked() and self._engine.lines - and not getattr(self, "_no_auto_service", False)): - self._open_service_report(auto=True) - self._session.stop() - self.connect_btn.setText("Disconnect" if running else "Connect") - self.reset_btn.setEnabled(state == State.CONNECTED) - self.port_combo.setEnabled(not running) - self.all_ports.setEnabled(not running) - self.refresh_btn.setEnabled(not running) - # Offline analysis would mix with the live session; disconnect first. - self.analyze_btn.setEnabled(not running) - self._state_text = f"{state.value}" + (f" ({detail})" if detail else "") - self._update_status() - - def _update_status(self) -> None: - sess = f" | Session log: {self._session.path}" if self._session.path else "" - c = self._counts - self.status.setText( - f"{getattr(self, '_state_text', '')} | boots seen: {c['boots']} " - f"errors: {c['E']} warnings: {c['W']}{sess}" - ) - - # ---- lines ------------------------------------------------------- - def _on_line(self, line: LogLine) -> None: - self._session.write(line) - self._engine.feed(line) - self._runner.feed(line) - self._diag_dirty = True - if line.kind == LineKind.BOOT: - self._counts["boots"] += 1 - if line.level in ("E", "W"): - self._counts[line.level] += 1 - self._update_status() - - if self._visible(line): - self._append(line) - - def _visible(self, line: LogLine) -> bool: - allowed = LEVEL_FILTERS[self.filter_combo.currentText()] - if allowed is None or line.kind in (LineKind.HOST, LineKind.PANIC, LineKind.BOOT, LineKind.RESET): - return True - if allowed == "KOTA": - return line.kind == LineKind.KOTA or line.level == "E" - return line.level in allowed - - def _append(self, line: LogLine) -> None: - sb = self.view.verticalScrollBar() - at_bottom = sb.value() >= sb.maximum() - 4 - fmt = QTextCharFormat() - colour = COLORS.get(line.kind) if line.kind == LineKind.KOTA and line.level == "I" else None - fmt.setForeground(QColor(colour or COLORS.get(line.level) or COLORS.get(line.kind, "#d4d4d4"))) - if line.kind in (LineKind.PANIC, LineKind.BOOT, LineKind.HOST) or line.record in ("boot", "status"): - fmt.setFontWeight(QFont.Weight.Bold) - cur = QTextCursor(self.view.document()) - cur.movePosition(QTextCursor.MoveOperation.End) - if not self.view.document().isEmpty(): - cur.insertBlock() - cur.insertText(line.text, fmt) - if at_bottom: - sb.setValue(sb.maximum()) - - # ---- diagnostics ------------------------------------------------- - def _refresh_diag(self) -> None: - if self._diag_dirty: - self._diag_dirty = False - self.diag.show_report(self._engine.report()) - self.guide.refresh() # countdowns and timeouts need a steady tick - - # ---- guided troubleshooting hooks ------------------------------------ - def _guide_action(self, action: str) -> bool: - if action == "reset" and self._manager.state == State.CONNECTED: - self._manager.reset_board() - return True - return False - - def _guide_log(self, text: str) -> None: - # Technician answers go into the session log and the log view. - line = host_event(text) - self._session.write(line) - self._append(line) - self._diag_dirty = True - - def _copy_report(self) -> None: - QGuiApplication.clipboard().setText(self._engine.report().to_text()) - - def _analyze_file(self) -> None: - path, _ = QFileDialog.getOpenFileName( - self, "Analyze log file", str(self._session.root), "Logs (*.log *.txt);;All files (*)") - if not path: - return - lines = read_log_file(Path(path)) - self.view.clear() - self._engine.reset() - self._runner.cancel() - self._counts = {"E": 0, "W": 0, "boots": 0} - for line in lines: - self._engine.feed(line) - if line.kind == LineKind.BOOT: - self._counts["boots"] += 1 - if line.level in ("E", "W"): - self._counts[line.level] += 1 - self._append(line) - self._state_text = f"Analyzed file: {Path(path).name} ({len(lines)} lines)" - self._update_status() - self._diag_dirty = True - self._refresh_diag() - - # ---- misc -------------------------------------------------------- - def _save_view(self) -> None: - path, _ = QFileDialog.getSaveFileName(self, "Save log view", "sensorbox_log.txt", "Text (*.txt)") - if path: - with open(path, "w", encoding="utf-8") as fh: - fh.write(self.view.toPlainText()) - - def _open_folder(self) -> None: - self._session.root.mkdir(parents=True, exist_ok=True) - QDesktopServices.openUrl(QUrl.fromLocalFile(str(self._session.root))) - - def shutdown(self) -> None: - self._manager.disconnect() - self._session.stop() - - -class MainWindow(QMainWindow): - def __init__(self): - super().__init__() - self.setWindowTitle(f"KOTA Sensorbox Technician Tool v{__version__}") - self.resize(1200, 800) - tabs = QTabWidget() - guide = QTextBrowser(openExternalLinks=True) - guide.setHtml(cheatsheet_html()) - self.serial_tab = SerialTab() - self.flash_tab = FlashTab(self.serial_tab) - tabs.addTab(guide, "Quick start") - tabs.addTab(self.serial_tab, "Serial monitor") - tabs.addTab(self.flash_tab, "Flash firmware") - self.sd_tab = SdTab(self.serial_tab) - tabs.addTab(self.sd_tab, "SD card") - self.setCentralWidget(tabs) - settings_menu = self.menuBar().addMenu("Settings") - settings_menu.addAction("Service system address...", self._edit_service_base) - - def _edit_service_base(self) -> None: - st = self.serial_tab - text, ok = QInputDialog.getText( - self, "Service system address", - "Address of the KOTA service system (the box MAC is added at the end):", - text=st._service_base()) - if ok and text.strip().startswith(("https://", "http://")): - st._settings.setValue("service/base", text.strip()) - elif ok: - QMessageBox.warning(self, "Service system address", "The address must start with https://") - - def closeEvent(self, event): # noqa: N802 (Qt naming) - self.serial_tab.shutdown() - super().closeEvent(event) +from __future__ import annotations + +from pathlib import Path + +from PySide6.QtCore import QCoreApplication, QObject, QSettings, Qt, QTimer, QUrl, Signal +from PySide6.QtGui import QColor, QDesktopServices, QFont, QGuiApplication, QTextCharFormat, QTextCursor +from PySide6.QtWidgets import ( + QCheckBox, + QComboBox, + QFileDialog, + QHBoxLayout, + QInputDialog, + QLabel, + QMainWindow, + QMessageBox, + QPlainTextEdit, + QPushButton, + QSplitter, + QTabWidget, + QTextBrowser, + QVBoxLayout, + QWidget, +) + +from .. import __version__ +from ..core.logfile import read_log_file +from ..core.logline import LineKind, LogLine, host_event +from ..core.session import SessionLog +from ..diag.engine import DiagnosticsEngine +from ..diag.guides import GuideRunner +from ..serialio.manager import SerialManager, State +from ..serialio.ports import PortInfo, list_serial_ports +from ..matter.payload import SetupPayload, payload_from_log +from ..service.report import DEFAULT_BASE, normalize_mac, report_url +from .cheatsheet import cheatsheet_html +from .diag_panel import DiagnosticsPanel +from .flash_tab import FlashTab +from .matter_qr import MatterQrDialog +from .guide_panel import GuidePanel +from .sd_tab import SdTab + +MAX_LINES = 20000 +# "KOTA" = the firmware's structured diagnostic lines (1.0.16+) plus errors. +LEVEL_FILTERS = {"All lines": None, "KOTA diagnostics + errors": "KOTA", + "Warnings + errors": {"W", "E"}, "Errors only": {"E"}} +COLORS = { + "E": "#ff6b6b", + "W": "#f5c451", + "I": "#d4d4d4", + LineKind.BOOT: "#6fc3ff", + LineKind.RESET: "#6fc3ff", + LineKind.PANIC: "#ff4dd2", + LineKind.HOST: "#7ee07e", + LineKind.KOTA: "#5fd7e8", + LineKind.RAW: "#a8a8a8", +} + + +class _Bridge(QObject): + """Marshals SerialManager callbacks (reader thread) onto the UI thread.""" + + line = Signal(object) + state = Signal(object, str) + + +class SerialTab(QWidget): + def __init__(self, parent: QWidget | None = None): + super().__init__(parent) + self._bridge = _Bridge() + self._bridge.line.connect(self._on_line) + self._bridge.state.connect(self._on_state) + self._manager = SerialManager(self._bridge.line.emit, self._bridge.state.emit) + self._session = SessionLog() + self._ports: list[PortInfo] = [] + self._counts = {"E": 0, "W": 0, "boots": 0} + self._engine = DiagnosticsEngine() + self._diag_dirty = True + self._runner = GuideRunner(self._engine, on_action=self._guide_action, on_log=self._guide_log) + + # --- controls + self.port_combo = QComboBox(minimumWidth=320) + self.all_ports = QCheckBox("Show all ports") + self.refresh_btn = QPushButton("Refresh") + self.connect_btn = QPushButton("Connect") + self.reset_btn = QPushButton("Reset && capture") + self.reset_btn.setToolTip("Reboot the sensorbox and record the log from the first boot line") + self.filter_combo = QComboBox() + self.filter_combo.addItems(LEVEL_FILTERS) + self.clear_btn = QPushButton("Clear view") + self.save_btn = QPushButton("Save view as...") + self.folder_btn = QPushButton("Open logs folder") + self.analyze_btn = QPushButton("Analyze log file...") + self.analyze_btn.setToolTip("Load a saved session log or serial capture and diagnose it") + self.copy_btn = QPushButton("Copy report") + self.copy_btn.setToolTip("Copy the diagnosis as text (e.g. to send to the firmware team)") + self.service_btn = QPushButton("Service report...") + self.service_btn.setToolTip("Open this box's page in the KOTA service system with the diagnosis " + "filled in; check it and press Save there") + self._settings = QSettings("KOTA", "TechnicianTool") + self.auto_service = QCheckBox("Open service report on disconnect") + self.auto_service.setChecked(self._settings.value("service/auto", False, type=bool)) + self.auto_service.toggled.connect(lambda on: self._settings.setValue("service/auto", on)) + self._active_port: PortInfo | None = None + + row1 = QHBoxLayout() + for w in (QLabel("Port:"), self.port_combo, self.all_ports, self.refresh_btn, + self.connect_btn, self.reset_btn): + row1.addWidget(w) + row1.addStretch(1) + row2 = QHBoxLayout() + for w in (QLabel("Show:"), self.filter_combo, self.clear_btn, self.save_btn, self.folder_btn, + self.analyze_btn, self.copy_btn, self.service_btn, self.auto_service): + row2.addWidget(w) + row2.addStretch(1) + + self.view = QPlainTextEdit(readOnly=True) + self.view.setMaximumBlockCount(MAX_LINES) + self.view.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap) + mono = QFont("Menlo" if QFont("Menlo").exactMatch() else "Consolas") + mono.setStyleHint(QFont.StyleHint.Monospace) + mono.setPointSize(10) + self.view.setFont(mono) + self.view.setStyleSheet("QPlainTextEdit { background:#1e1e1e; color:#d4d4d4; }") + + self.diag = DiagnosticsPanel() + self.guide = GuidePanel(self._runner, self._engine, matter_payload=self.matter_payload) + self.side_tabs = QTabWidget() + self.side_tabs.addTab(self.diag, "Diagnosis") + self.side_tabs.addTab(self.guide, "Guided troubleshooting") + split = QSplitter(Qt.Orientation.Horizontal) + split.addWidget(self.view) + split.addWidget(self.side_tabs) + split.setStretchFactor(0, 3) + split.setStretchFactor(1, 2) + split.setSizes([700, 480]) + + self.status = QLabel() + layout = QVBoxLayout(self) + layout.addLayout(row1) + layout.addLayout(row2) + layout.addWidget(split, 1) + layout.addWidget(self.status) + + # --- wiring + self.refresh_btn.clicked.connect(self.refresh_ports) + self.all_ports.toggled.connect(self.refresh_ports) + self.connect_btn.clicked.connect(self._toggle_connect) + self.reset_btn.clicked.connect(self._manager.reset_board) + self.clear_btn.clicked.connect(self.view.clear) + self.save_btn.clicked.connect(self._save_view) + self.folder_btn.clicked.connect(self._open_folder) + self.analyze_btn.clicked.connect(self._analyze_file) + self.copy_btn.clicked.connect(self._copy_report) + self.service_btn.clicked.connect(lambda: self._open_service_report(auto=False)) + self.diag.qr_btn.clicked.connect(lambda: MatterQrDialog(self.matter_payload(), self).exec()) + + # Report refresh is throttled; lines can arrive at >100/s. + self._diag_timer = QTimer(self, interval=400) + self._diag_timer.timeout.connect(self._refresh_diag) + self._diag_timer.start() + + # Auto-detect a box being plugged in while idle. + self._poll = QTimer(self, interval=2000) + self._poll.timeout.connect(self._auto_refresh) + self._poll.start() + + self.refresh_ports() + self._on_state(State.DISCONNECTED, "") + + # ---- ports ------------------------------------------------------- + def refresh_ports(self) -> None: + current = self.port_combo.currentData() + self._ports = list_serial_ports(include_all=self.all_ports.isChecked()) + self.port_combo.clear() + for p in self._ports: + self.port_combo.addItem(p.label, p.device) + if not self._ports: + self.port_combo.addItem("No sensorbox found - check USB cable and power switch", None) + idx = self.port_combo.findData(current) + self.port_combo.setCurrentIndex(max(idx, 0)) + + def _auto_refresh(self) -> None: + if self._manager.is_running: + return + devices = [p.device for p in list_serial_ports(self.all_ports.isChecked())] + if devices != [p.device for p in self._ports]: + self.refresh_ports() + + def _selected_port(self) -> PortInfo | None: + dev = self.port_combo.currentData() + return next((p for p in self._ports if p.device == dev), None) + + # ---- connection -------------------------------------------------- + def _toggle_connect(self) -> None: + if self._manager.is_running: + # Session file is closed in _on_state(DISCONNECTED), after the + # reader thread's final queued lines have been written. + self._manager.disconnect() + return + port = self._selected_port() + if port is None: + return + self._connect(port) + + def _connect(self, port: PortInfo) -> None: + self._counts = {"E": 0, "W": 0, "boots": 0} + self._engine.reset() + self._runner.cancel() + self._diag_dirty = True + self._session.start(port.mac or port.device) + self._active_port = port + self._manager.connect(port) + + def matter_payload(self) -> SetupPayload: + """Pairing payload reported by this box (KOTA: matter), from its log, or the defaults.""" + return payload_from_log([self._engine.matter_advert] if self._engine.matter_advert else [], + self._engine.matter_qr, self._engine.matter_manual) + + # ---- service system ------------------------------------------------- + def _service_base(self) -> str: + return str(self._settings.value("service/base", DEFAULT_BASE)) + + def _open_service_report(self, auto: bool) -> None: + mac = normalize_mac(self._engine.mac or (self._active_port.mac if self._active_port else "") or "") + if len(mac) != 12: + if not auto: + QMessageBox.information(self, "Service report", "The box's MAC address is not known yet. " + "Connect to the box and press Reset & capture first.") + return + url = report_url(mac, self._engine.report(), self._service_base()) + QDesktopServices.openUrl(QUrl(url)) + # Log that it happened, not the report itself (it is already in the session log). + self._guide_log(f"service report for {mac} opened in the browser - check it and press Save there") + + # ---- hand-off with the flash tab ------------------------------------ + @property + def engine(self) -> DiagnosticsEngine: + return self._engine + + def release_port(self) -> None: + """Stop monitoring so esptool can open the port exclusively.""" + if self._manager.is_running: + self._no_auto_service = True # a flash hand-off is not the end of a session + self._manager.disconnect() # joins the reader thread + QCoreApplication.processEvents() # deliver its queued final lines/state now + self._no_auto_service = False + + def connect_port(self, port: PortInfo, capture: bool = True) -> None: + """Monitor `port`; with capture, restart the box once connected to record its boot.""" + self.release_port() + idx = self.port_combo.findData(port.device) + if idx < 0: + self.port_combo.addItem(port.label, port.device) + self._ports.append(port) + idx = self.port_combo.count() - 1 + self.port_combo.setCurrentIndex(idx) + self._reset_on_connect = capture + self._connect(port) + + def _on_state(self, state: State, detail: str) -> None: + running = state != State.DISCONNECTED + if not running and self._manager.is_running: + return # stale event from a session that was already replaced + if state == State.CONNECTED and getattr(self, "_reset_on_connect", False): + self._reset_on_connect = False + QTimer.singleShot(300, self._manager.reset_board) + if not running and self._session.path is not None: + self._session.write_report(self._engine.report().to_text()) + if (self.auto_service.isChecked() and self._engine.lines + and not getattr(self, "_no_auto_service", False)): + self._open_service_report(auto=True) + self._session.stop() + self.connect_btn.setText("Disconnect" if running else "Connect") + self.reset_btn.setEnabled(state == State.CONNECTED) + self.port_combo.setEnabled(not running) + self.all_ports.setEnabled(not running) + self.refresh_btn.setEnabled(not running) + # Offline analysis would mix with the live session; disconnect first. + self.analyze_btn.setEnabled(not running) + self._state_text = f"{state.value}" + (f" ({detail})" if detail else "") + self._update_status() + + def _update_status(self) -> None: + sess = f" | Session log: {self._session.path}" if self._session.path else "" + c = self._counts + self.status.setText( + f"{getattr(self, '_state_text', '')} | boots seen: {c['boots']} " + f"errors: {c['E']} warnings: {c['W']}{sess}" + ) + + # ---- lines ------------------------------------------------------- + def _on_line(self, line: LogLine) -> None: + self._session.write(line) + self._engine.feed(line) + self._runner.feed(line) + self._diag_dirty = True + if line.kind == LineKind.BOOT: + self._counts["boots"] += 1 + if line.level in ("E", "W"): + self._counts[line.level] += 1 + self._update_status() + + if self._visible(line): + self._append(line) + + def _visible(self, line: LogLine) -> bool: + allowed = LEVEL_FILTERS[self.filter_combo.currentText()] + if allowed is None or line.kind in (LineKind.HOST, LineKind.PANIC, LineKind.BOOT, LineKind.RESET): + return True + if allowed == "KOTA": + return line.kind == LineKind.KOTA or line.level == "E" + return line.level in allowed + + def _append(self, line: LogLine) -> None: + sb = self.view.verticalScrollBar() + at_bottom = sb.value() >= sb.maximum() - 4 + fmt = QTextCharFormat() + colour = COLORS.get(line.kind) if line.kind == LineKind.KOTA and line.level == "I" else None + fmt.setForeground(QColor(colour or COLORS.get(line.level) or COLORS.get(line.kind, "#d4d4d4"))) + if line.kind in (LineKind.PANIC, LineKind.BOOT, LineKind.HOST) or line.record in ("boot", "status"): + fmt.setFontWeight(QFont.Weight.Bold) + cur = QTextCursor(self.view.document()) + cur.movePosition(QTextCursor.MoveOperation.End) + if not self.view.document().isEmpty(): + cur.insertBlock() + cur.insertText(line.text, fmt) + if at_bottom: + sb.setValue(sb.maximum()) + + # ---- diagnostics ------------------------------------------------- + def _refresh_diag(self) -> None: + if self._diag_dirty: + self._diag_dirty = False + self.diag.show_report(self._engine.report()) + self.guide.refresh() # countdowns and timeouts need a steady tick + + # ---- guided troubleshooting hooks ------------------------------------ + def _guide_action(self, action: str) -> bool: + if action == "reset" and self._manager.state == State.CONNECTED: + self._manager.reset_board() + return True + return False + + def _guide_log(self, text: str) -> None: + # Technician answers go into the session log and the log view. + line = host_event(text) + self._session.write(line) + self._append(line) + self._diag_dirty = True + + def _copy_report(self) -> None: + QGuiApplication.clipboard().setText(self._engine.report().to_text()) + + def _analyze_file(self) -> None: + path, _ = QFileDialog.getOpenFileName( + self, "Analyze log file", str(self._session.root), "Logs (*.log *.txt);;All files (*)") + if not path: + return + lines = read_log_file(Path(path)) + self.view.clear() + self._engine.reset() + self._runner.cancel() + self._counts = {"E": 0, "W": 0, "boots": 0} + for line in lines: + self._engine.feed(line) + if line.kind == LineKind.BOOT: + self._counts["boots"] += 1 + if line.level in ("E", "W"): + self._counts[line.level] += 1 + self._append(line) + self._state_text = f"Analyzed file: {Path(path).name} ({len(lines)} lines)" + self._update_status() + self._diag_dirty = True + self._refresh_diag() + + # ---- misc -------------------------------------------------------- + def _save_view(self) -> None: + path, _ = QFileDialog.getSaveFileName(self, "Save log view", "sensorbox_log.txt", "Text (*.txt)") + if path: + with open(path, "w", encoding="utf-8") as fh: + fh.write(self.view.toPlainText()) + + def _open_folder(self) -> None: + self._session.root.mkdir(parents=True, exist_ok=True) + QDesktopServices.openUrl(QUrl.fromLocalFile(str(self._session.root))) + + def shutdown(self) -> None: + self._manager.disconnect() + self._session.stop() + + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle(f"KOTA Sensorbox Technician Tool v{__version__}") + self.resize(1200, 800) + tabs = QTabWidget() + guide = QTextBrowser(openExternalLinks=True) + guide.setHtml(cheatsheet_html()) + self.serial_tab = SerialTab() + self.flash_tab = FlashTab(self.serial_tab) + tabs.addTab(guide, "Quick start") + tabs.addTab(self.serial_tab, "Serial monitor") + tabs.addTab(self.flash_tab, "Flash firmware") + self.sd_tab = SdTab(self.serial_tab) + tabs.addTab(self.sd_tab, "SD card") + self.setCentralWidget(tabs) + settings_menu = self.menuBar().addMenu("Settings") + settings_menu.addAction("Service system address...", self._edit_service_base) + + def _edit_service_base(self) -> None: + st = self.serial_tab + text, ok = QInputDialog.getText( + self, "Service system address", + "Address of the KOTA service system (the box MAC is added at the end):", + text=st._service_base()) + if ok and text.strip().startswith(("https://", "http://")): + st._settings.setValue("service/base", text.strip()) + elif ok: + QMessageBox.warning(self, "Service system address", "The address must start with https://") + + def closeEvent(self, event): # noqa: N802 (Qt naming) + self.serial_tab.shutdown() + super().closeEvent(event) diff --git a/kota_tech/ui/matter_qr.py b/kota_tech/ui/matter_qr.py index 18e2f2e..3b5d214 100644 --- a/kota_tech/ui/matter_qr.py +++ b/kota_tech/ui/matter_qr.py @@ -1,76 +1,76 @@ -"""Matter pairing QR code on screen, so the technician can scan it from the laptop.""" - -from __future__ import annotations - -import segno -from PySide6.QtCore import Qt -from PySide6.QtGui import QColor, QImage, QPainter, QPixmap -from PySide6.QtWidgets import QDialog, QDialogButtonBox, QLabel, QVBoxLayout, QWidget - -from ..matter.payload import SetupPayload - - -def qr_pixmap(text: str, size: int = 260) -> QPixmap: - """Black-on-white QR with the quiet zone phones need (4 modules).""" - qr = segno.make(text, error="m") - matrix = [list(row) for row in qr.matrix_iter(scale=1, border=4)] - n = len(matrix) - module = max(1, size // n) - img = QImage(n * module, n * module, QImage.Format.Format_RGB32) - img.fill(QColor("white")) - p = QPainter(img) - p.setPen(Qt.PenStyle.NoPen) - p.setBrush(QColor("black")) - for y, row in enumerate(matrix): - for x, dark in enumerate(row): - if dark: - p.drawRect(x * module, y * module, module, module) - p.end() - return QPixmap.fromImage(img) - - -class MatterQrWidget(QWidget): - def __init__(self, payload: SetupPayload, size: int = 260, parent: QWidget | None = None): - super().__init__(parent) - self.image = QLabel() - self.image.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.code = QLabel() - self.code.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.code.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) - f = self.code.font() - f.setPointSize(f.pointSize() + 4) - f.setBold(True) - self.code.setFont(f) - self.note = QLabel(wordWrap=True) - self.note.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.note.setStyleSheet("color:#555") - lay = QVBoxLayout(self) - lay.addWidget(self.image) - lay.addWidget(self.code) - lay.addWidget(self.note) - self._size = size - self.set_payload(payload) - - def set_payload(self, payload: SetupPayload) -> None: - self.payload = payload - self.image.setPixmap(qr_pixmap(payload.qr_text(), self._size)) - self.code.setText(f"Manual code: {payload.manual_code_pretty()}") - if payload.from_box: - src = "QR and manual code exactly as reported by the box (KOTA: matter line)" - else: - src = f"Discriminator {payload.discriminator} " + ( - "(from this box's log)" if payload.from_log else - "(standard code of the v1.0 firmware - connect the box to confirm)") - self.note.setText(f"Scan with the Matter app (Apple Home, Google Home, ...), or type the manual code. " - f"{src}. Same as the sticker on the box.") - - -class MatterQrDialog(QDialog): - def __init__(self, payload: SetupPayload, parent: QWidget | None = None): - super().__init__(parent) - self.setWindowTitle("Matter pairing code") - lay = QVBoxLayout(self) - lay.addWidget(MatterQrWidget(payload, 320)) - buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close) - buttons.rejected.connect(self.reject) - lay.addWidget(buttons) +"""Matter pairing QR code on screen, so the technician can scan it from the laptop.""" + +from __future__ import annotations + +import segno +from PySide6.QtCore import Qt +from PySide6.QtGui import QColor, QImage, QPainter, QPixmap +from PySide6.QtWidgets import QDialog, QDialogButtonBox, QLabel, QVBoxLayout, QWidget + +from ..matter.payload import SetupPayload + + +def qr_pixmap(text: str, size: int = 260) -> QPixmap: + """Black-on-white QR with the quiet zone phones need (4 modules).""" + qr = segno.make(text, error="m") + matrix = [list(row) for row in qr.matrix_iter(scale=1, border=4)] + n = len(matrix) + module = max(1, size // n) + img = QImage(n * module, n * module, QImage.Format.Format_RGB32) + img.fill(QColor("white")) + p = QPainter(img) + p.setPen(Qt.PenStyle.NoPen) + p.setBrush(QColor("black")) + for y, row in enumerate(matrix): + for x, dark in enumerate(row): + if dark: + p.drawRect(x * module, y * module, module, module) + p.end() + return QPixmap.fromImage(img) + + +class MatterQrWidget(QWidget): + def __init__(self, payload: SetupPayload, size: int = 260, parent: QWidget | None = None): + super().__init__(parent) + self.image = QLabel() + self.image.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.code = QLabel() + self.code.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.code.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) + f = self.code.font() + f.setPointSize(f.pointSize() + 4) + f.setBold(True) + self.code.setFont(f) + self.note = QLabel(wordWrap=True) + self.note.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.note.setStyleSheet("color:#555") + lay = QVBoxLayout(self) + lay.addWidget(self.image) + lay.addWidget(self.code) + lay.addWidget(self.note) + self._size = size + self.set_payload(payload) + + def set_payload(self, payload: SetupPayload) -> None: + self.payload = payload + self.image.setPixmap(qr_pixmap(payload.qr_text(), self._size)) + self.code.setText(f"Manual code: {payload.manual_code_pretty()}") + if payload.from_box: + src = "QR and manual code exactly as reported by the box (KOTA: matter line)" + else: + src = f"Discriminator {payload.discriminator} " + ( + "(from this box's log)" if payload.from_log else + "(standard code of the v1.0 firmware - connect the box to confirm)") + self.note.setText(f"Scan with the Matter app (Apple Home, Google Home, ...), or type the manual code. " + f"{src}. Same as the sticker on the box.") + + +class MatterQrDialog(QDialog): + def __init__(self, payload: SetupPayload, parent: QWidget | None = None): + super().__init__(parent) + self.setWindowTitle("Matter pairing code") + lay = QVBoxLayout(self) + lay.addWidget(MatterQrWidget(payload, 320)) + buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close) + buttons.rejected.connect(self.reject) + lay.addWidget(buttons) diff --git a/tests/fixtures/live_boot_A4CB8FC946F8_fw1.0.16_uncommissioned.txt b/tests/fixtures/live_boot_A4CB8FC946F8_fw1.0.16_uncommissioned.txt index d1dd4d3..f8454e5 100644 --- a/tests/fixtures/live_boot_A4CB8FC946F8_fw1.0.16_uncommissioned.txt +++ b/tests/fixtures/live_boot_A4CB8FC946F8_fw1.0.16_uncommissioned.txt @@ -1,1064 +1,1064 @@ ---- connected to COM5 --- -W (7411) wifi:Haven't to connect to a suitable AP now! ---- reset issued by tool --- -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0x15 (USB_UART_CHIP_RESET),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x4037f35e -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (25) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (28) boot: efuse block revision: v1.4 -I (32) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (43) boot: Enabling RNG early entropy source... -I (48) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (63) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (76) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (89) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (102) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (109) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c190020 size=66d30h (421168) map -I (194) esp_image: segment 1: paddr=000a6d58 vaddr=3fc9c700 size=05d00h ( 23808) load -I (199) esp_image: segment 2: paddr=000aca60 vaddr=40374000 size=035b8h ( 13752) load -I (203) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=1862d0h (1598160) map -I (487) esp_image: segment 4: paddr=002362f8 vaddr=403775b8 size=15054h ( 86100) load -I (506) esp_image: segment 5: paddr=0024b354 vaddr=5000002c size=00020h ( 32) load -I (516) boot: Loaded app from partition at offset 0x40000 -I (517) boot: Disabling RNG early entropy source... -I (527) octal_psram: vendor id : 0x0d (AP) -I (527) octal_psram: dev id : 0x02 (generation 3) -I (528) octal_psram: density : 0x03 (64 Mbit) -I (529) octal_psram: good-die : 0x01 (Pass) -I (534) octal_psram: Latency : 0x01 (Fixed) -I (538) octal_psram: VCC : 0x01 (3V) -I (542) octal_psram: SRF : 0x01 (Fast Refresh) -I (547) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (552) octal_psram: BurstLen : 0x01 (32 Byte) -I (556) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (562) octal_psram: DriveStrength: 0x00 (1/1) -I (566) esp_psram: Found 8MB PSRAM device -I (569) esp_psram: Speed: 40MHz -I (572) cpu_start: Multicore app -I (1306) esp_psram: SPI SRAM memory test OK -I (1314) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1315) cpu_start: Pro cpu start user code -I (1315) cpu_start: cpu freq: 160000000 Hz -I (1317) app_init: Application information: -I (1321) app_init: Project name: kota-sensorboard -I (1326) app_init: App version: 1.0.16 -I (1330) app_init: Compile time: Sep 28 2026 15:07:57 -I (1335) app_init: ELF file SHA256: 4f10989e2... -I (1339) app_init: ESP-IDF: v5.5.3-dirty -I (1344) efuse_init: Min chip rev: v0.0 -I (1347) efuse_init: Max chip rev: v0.99 -I (1352) efuse_init: Chip rev: v0.2 -I (1356) heap_init: Initializing. RAM available for dynamic allocation: -I (1362) heap_init: At 3FCB4AD0 len 00034C40 (211 KiB): RAM -I (1367) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1372) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1378) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1383) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1391) spi_flash: detected chip: generic -I (1393) spi_flash: flash io: dio -I (1399) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1403) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1418) main_task: Started on CPU0 -I (1421) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1429) main_task: Calling app_main() -E (1432) ID: [E-MAIN-04] Device MAC address: A4:CB:8F:C9:46:F8 -I (1458) KOTA: led color=green pattern=solid -I (1659) I2C: Creating I2C mutex -I (1676) KOTA: i2c SDA=1 SCL=1 0x39=AS7341 0x68=DS1307 0x6B=SEN66 0x76=BME688 -I (1677) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1679) SPI: Creating SPI mutex -I (1722) SD_CARD: SD card mounted successfully -I (1723) SD_CARD: /sdcard exists, SD card initialized successfully -I (1788) KOTA: sd type=SDHC size=29.3GB fs=FAT32 free=29.3GB env_keys=INFLUX_TOKEN,INFLUX_URL buffer_lines=764 -I (1792) WIFI_CREDENTIALS: SSID/PASSWORD not set in .env; Matter will be used for WiFi provisioning -I (1802) MAIN: Matter node created successfully -W (1803) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1810) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1818) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1827) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1836) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1845) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1854) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1862) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1871) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1880) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1888) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1897) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1906) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1915) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1924) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1932) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1941) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1950) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1960) pp: pp rom version: e7ae62f -I (1962) net80211: net80211 rom version: e7ae62f -I (1967) wifi:wifi driver task: 3fcc7d40, prio:23, stack:6656, core=0 -I (1985) wifi:wifi firmware version: 4df78f2 -I (1985) wifi:wifi certification version: v7.0 -I (1986) wifi:config NVS flash: enabled -I (1986) wifi:config nano formatting: disabled -I (1990) wifi:Init data frame dynamic rx buffer num: 32 -I (1995) wifi:Init static rx mgmt buffer num: 5 -I (1999) wifi:Init management short buffer num: 32 -I (2004) wifi:Init dynamic tx buffer num: 32 -I (2008) wifi:Init static tx FG buffer num: 2 -I (2012) wifi:Init static rx buffer size: 1600 -I (2016) wifi:Init static rx buffer num: 10 -I (2020) wifi:Init dynamic rx buffer num: 32 -I (2025) wifi_init: rx ba win: 6 -I (2027) wifi_init: accept mbox: 6 -I (2030) wifi_init: tcpip mbox: 32 -I (2033) wifi_init: udp mbox: 6 -I (2036) wifi_init: tcp mbox: 6 -I (2039) wifi_init: tcp tx win: 5760 -I (2042) wifi_init: tcp rx win: 5760 -I (2045) wifi_init: tcp mss: 1440 -I (2057) chip[DL]: NVS set: chip-counters/reboot-count = 15857 (0x3DF1) -I (2060) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (2063) BLE_INIT: BT controller compile version [1bb2f50] -I (2067) BLE_INIT: Using main XTAL as clock source -I (2072) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (2080) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (2085) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -I (2133) NimBLE: GAP procedure initiated: stop advertising. - -I (2135) CHIP[DL]: BLE host-controller synced -I (2635) chip[DL]: Starting ESP WiFi layer -I (2637) wifi:mode : sta (a4:cb:8f:c9:46:f8) -I (2638) wifi:enable tsf -W (2639) wifi:Haven't to connect to a suitable AP now! -I (2639) chip[DL]: -I (2639) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43Done driving station state, nothing else to do... - -W (2652) wifi:Haven't to connect to a suitable AP now! -I (2657) chip[DL]: Done driving station state, nothing else to do... -I (2657) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 -I (2663) chip[SVR]: -Initializing subscription resumption storage... -I (2676) chip[SVR]: Server initializing... -I (2680) chip[TS]: Last Known Good Time: 2026-09-28T13:29:12 -I (2685) chip[DMG]: AccessControl: initializing -I (2688) chip[DMG]: Examples::AccessControlDelegate::Init -I (2693) chip[DMG]: AccessControl: setting -I (2697) chip[DMG]: DefaultAclStorage: initializing -I (2702) chip[DMG]: DefaultAclStorage: 0 entries loaded -I (2707) chip[SVR]: WARNING: mTestEventTriggerDelegate is null -I (2726) chip[ZCL]: Using ZAP configuration... -I (2726) esp_matter_cluster: Cluster plugin init common callback -I (2727) chip[DMG]: AccessControlCluster: initializing -I (2730) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported -I (2740) chip[DIS]: Updating services using commissioning mode 1 -I (2744) chip[DIS]: CHIP minimal mDNS started advertising. -I (2749) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 -I (2759) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: A180A7E5D58D9F80. -I (2769) chip[DIS]: mDNS service published: _matterc._udp -I (2774) chip[IN]: CASE Server enabling CASE session setups -I (2780) chip[SVR]: Joining Multicast groups -I (2783) chip[SVR]: Server Listening... -I (2788) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** -I (2798) data_model: Dynamic endpoint 0 added -E (2801) data_model: Cluster cannot be NULL. -I (2806) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** -I (2816) data_model: Dynamic endpoint 1 added -E (2820) data_model: Cluster cannot be NULL. -I (2825) MAIN: ESP-Matter started successfully -I (2826) chip[DL]: WIFI_EVENT_STA_START -W (2832) wifi:Haven't to connect to a suitable AP now! -I (2837) chip[DL]: Done driving station state, nothing else to do... -I (2843) KOTA: boot fw=1.0.16 build=4f10989e hw=2 mac=A4CB8FC946F8 reset=USB_UART_CHIP_RESET mode=matter commissioned=0 last_restart=none -I (2845) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) -I (2862) NimBLE: GAP procedure initiated: advertise; -I (2866) NimBLE: disc_mode=2 -I (2869) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 -I (2878) NimBLE: - -I (2881) chip[DL]: CHIPoBLE advertising started -I (2886) KOTA: matter fabrics=0 window=open qr=MT:Y.K9042C00KA0648G00 manual=34970112332 -I (2892) MAIN: Commissioning window opened -W (2893) wifi:Haven't to connect to a suitable AP now! -I (2897) KOTA: matter fabrics=0 window=open qr=MT:Y.K9042C00KA0648G00 manual=34970112332 -I (2905) KOTA: led color=cyan pattern=breathe period=3000ms -I (2901) KOTA: health up=1s status=OK wifi=disconnected rssi=0 time_valid=0 sd=ok buffer=0 heap=123883 psram=7.9M last_upload=never ota=unregistered -W (3394) wifi:Haven't to connect to a suitable AP now! -I (3894) KOTA: rtc running=0 time=2000-01-01T00:00:00 -W (4395) wifi:Haven't to connect to a suitable AP now! -W (5396) wifi:Haven't to connect to a suitable AP now! -W (6396) wifi:Haven't to connect to a suitable AP now! -W (7397) wifi:Haven't to connect to a suitable AP now! -W (8398) wifi:Haven't to connect to a suitable AP now! -W (9399) wifi:Haven't to connect to a suitable AP now! -W (10400) wifi:Haven't to connect to a suitable AP now! -W (11401) wifi:Haven't to connect to a suitable AP now! -W (12402) wifi:Haven't to connect to a suitable AP now! -W (13403) wifi:Haven't to connect to a suitable AP now! -I (13895) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota -W (13962) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota -I (13975) BME688: Delaying for 10000 microseconds -W (14404) wifi:Haven't to connect to a suitable AP now! -I (14591) KOTA: mic enabled=1 (GPIO3=0, expected 0) -I (14593) Flicker: Flicker sensor initialized on pin 14 -I (14594) temperature_sensor: Range [20°C ~ 100°C], error < 2°C -I (14597) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data -W (14601) wifi: -I (14597) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data -Haven't to connect to a suitable AP now! -I (14598) SEN66: Creating sensor mutex - -I (14605) INFLUX_BUFFER: Flushing chunk starting at line 10: -W (14611) AS7341: Reading spectral data from AS7341 -I (14616) SEN66: Waking up sensor by setting power GPIO to enabled level... -I (14617) DeviceStatus: Active tasks: 26 -I (14621) INFLUX: Writing to InfluxDB -I (14641) DeviceStatus: Task list: -main R 1 1132 4 -gather_sensor_t X 1 1480 24 -IDLE1 R 0 700 6 -IDLE0 R 0 692 5 -gather_sensor_t B 1 2552 22 -reset_task B 0 3344 9 -ds1307_update B 1 768 17 -tiT B 18 2420 11 -gather_task B 1 1984 19 -gather_task B 1 14292 20 -gather_sensor_t B 1 1364 25 -gather_sensor_t B 1 1948 23 -ota_task B 1 7504 18 -CHIP B 1 3636 13 -health B 1 928 16 -Tmr Svc B 1 1364 7 -led_task B 5 964 8 -sys_evt B 20 2676 10 -ipc0 S 24 372 1 -ipc1 S 24 524 2 -esp_timer S 22 3332 3 -gather_sensor_t B 1 2544 21 -gather_sensor_t B 1 2552 26 -wifi B 23 4324 12 -btController B 23 1960 14 -nimble_host B 21 1476 15 - -W (14710) DeviceStatus: RTC is uninitialized, using current time as timestamp -W (14973) AS7341: Reading basic counts data from AS7341 -W (14989) AS7341: Reading flicker detection state from AS7341 -W (15206) as7341: FD Status Register: 0x20 (0b00100000) -I (15211) AS7341: Updating Matter attributes with AS7341 data: 14946 -I (15211) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms -I (15212) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** -I (15217) Flicker: Starting to collect samples in gather task -I (15228) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 14946 ********** -E (15245) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 -E (15254) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 -W (15264) AS7341: Formatting AS7341 data in line protocol -AS7341 line protocol: -as7341_adc,device=A4CB8FC946F8 f1=1,f2=2,f3=2,f4=3,f5=3,f6=3,f7=6,f8=4,clear=14,nir=2 946684801000000000 -as7341_basic_counts,device=A4CB8FC946F8 f1=2.23,f2=4.46,f3=4.46,f4=6.69,f5=6.69,f6=6.69,f7=13.39,f8=8.92,clear=31.23,nir=4.46 946684801000000000 -as7341_flicker,device=A4CB8FC946F8 state="unknown" 946684801000000000 -W (15404) wifi:Haven't to connect to a suitable AP now! -W (15717) DeviceStatus: RTC is uninitialized, using current time as timestamp -I (16256) Flicker: Finished collecting samples, notifying main task -I (16257) Flicker: Collected 1024 samples for dominant frequency analysis -I (16264) SENSOR_GATHERER: Sensor should now be asleep -I (16276) BME688: Delaying for 199517 microseconds -I (16276) SENSOR_GATHERER: Sensor should now be asleep -W (16405) wifi:Haven't to connect to a suitable AP now! -I (16714) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** -I (16715) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1018 ********** -E (16726) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 -I (16717) SENSOR_GATHERER: Sensor should now be asleep -E (16735) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 -W (16750) BME688: RTC is uninitialized, using current time as timestamp -W (17406) wifi:Haven't to connect to a suitable AP now! -W (17756) BME688: RTC is uninitialized, using current time as timestamp -W (18407) wifi:Haven't to connect to a suitable AP now! -W (18756) BME688: RTC is uninitialized, using current time as timestamp -W (19408) wifi:Haven't to connect to a suitable AP now! -I (19633) SEN66: Sent start measurement command, waiting for sensor to be ready... -W (19756) BME688: RTC is uninitialized, using current time as timestamp -W (20409) wifi:Haven't to connect to a suitable AP now! -W (20756) BME688: RTC is uninitialized, using current time as timestamp -W (21410) wifi:Haven't to connect to a suitable AP now! -E (21642) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (21642) esp-tls: Failed to open new connection -E (21645) transport_base: Failed to open a new connection -E (21650) HTTP_CLIENT: Connection failed, sock < 0 -I (21655) INFLUX: HTTP request performed, status code: 0 -E (21659) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT -E (21667) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL -W (21756) BME688: RTC is uninitialized, using current time as timestamp -W (22411) wifi:Haven't to connect to a suitable AP now! -I (22602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (22756) BME688: RTC is uninitialized, using current time as timestamp -I (23102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (23412) wifi:Haven't to connect to a suitable AP now! -I (23602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (23674) INFLUX: Writing to InfluxDB -W (23756) BME688: RTC is uninitialized, using current time as timestamp -I (24102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (24413) wifi:Haven't to connect to a suitable AP now! -I (24596) OTA_CLIENT: Device is not registered, attempting to register... -I (24598) OTA_CLIENT: HTTP client handle: 0x3fcf0fe4 -I (24602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (24738) MICROPHONE: Read 600000 bytes in 9940 ms -I (24738) MICROPHONE: Read 600000 bytes of audio data -W (24756) BME688: RTC is uninitialized, using current time as timestamp -I (25102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (25414) wifi:Haven't to connect to a suitable AP now! -I (25602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (25756) BME688: RTC is uninitialized, using current time as timestamp -I (26102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (26415) wifi:Haven't to connect to a suitable AP now! -I (26602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (26756) BME688: RTC is uninitialized, using current time as timestamp -I (27102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (27416) wifi:Haven't to connect to a suitable AP now! -I (27602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (27756) BME688: RTC is uninitialized, using current time as timestamp -I (28102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (28187) MICROPHONE: Raw block stats: mean=-81.8 min=-41376 max=37946 -I (28188) MICROPHONE: Sum SQ: 0.032256, RMS pressure: 0.000402, Normalized: 0.000402, dB peak: -67.92 -W (28192) SEN66: RTC is uninitialized, using current time as timestamp -W (28417) wifi:Haven't to connect to a suitable AP now! -I (28602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (28756) BME688: RTC is uninitialized, using current time as timestamp -I (29102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (29198) SEN66: RTC is uninitialized, using current time as timestamp -W (29418) wifi:Haven't to connect to a suitable AP now! -I (29602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (29756) BME688: RTC is uninitialized, using current time as timestamp -I (30102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (30198) SEN66: RTC is uninitialized, using current time as timestamp -W (30419) wifi:Haven't to connect to a suitable AP now! -I (30602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -E (30675) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (30675) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (30676) esp-tls: Failed to open new connection -E (30683) esp-tls: Failed to open new connection -E (30687) transport_base: Failed to open a new connection -E (30691) transport_base: Failed to open a new connection -E (30696) HTTP_CLIENT: Connection failed, sock < 0 -E (30701) HTTP_CLIENT: Connection failed, sock < 0 -E (30706) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 -I (30710) INFLUX: HTTP request performed, status code: 0 -E (30719) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT -E (30723) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT -E (30738) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL -W (30756) BME688: RTC is uninitialized, using current time as timestamp -I (31102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (31198) SEN66: RTC is uninitialized, using current time as timestamp -W (31420) wifi:Haven't to connect to a suitable AP now! -I (31602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (31756) BME688: RTC is uninitialized, using current time as timestamp -I (32102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (32198) SEN66: RTC is uninitialized, using current time as timestamp -W (32421) wifi:Haven't to connect to a suitable AP now! -I (32602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (32741) chip[DL]: bleAdv Timeout : Start slow advertisement -I (32743) chip[DL]: Configuring CHIPoBLE advertising (interval 500 ms, connectable) -I (32744) chip[DL]: Device already advertising, stop active advertisement and restart -I (32745) INFLUX: Writing to InfluxDB -I (32751) NimBLE: GAP procedure initiated: stop advertising. - -W (32756) BME688: RTC is uninitialized, using current time as timestamp -I (32760) NimBLE: GAP procedure initiated: advertise; -I (32770) NimBLE: disc_mode=2 -I (32773) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=800 adv_itvl_max=800 -I (32783) NimBLE: - -I (33102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (33198) SEN66: RTC is uninitialized, using current time as timestamp -W (33425) wifi:Haven't to connect to a suitable AP now! -I (33602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (33785) BME688: RTC is uninitialized, using current time as timestamp -I (34102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (34198) SEN66: RTC is uninitialized, using current time as timestamp -W (34426) wifi:Haven't to connect to a suitable AP now! -I (34602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (34785) BME688: RTC is uninitialized, using current time as timestamp -I (35102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (35198) SEN66: RTC is uninitialized, using current time as timestamp -W (35427) wifi:Haven't to connect to a suitable AP now! -I (35602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (35785) BME688: RTC is uninitialized, using current time as timestamp -I (36102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (36198) SEN66: RTC is uninitialized, using current time as timestamp -W (36428) wifi:Haven't to connect to a suitable AP now! -I (36602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (36785) BME688: RTC is uninitialized, using current time as timestamp -I (37102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (37198) SEN66: RTC is uninitialized, using current time as timestamp -W (37429) wifi:Haven't to connect to a suitable AP now! -I (37602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (37785) BME688: RTC is uninitialized, using current time as timestamp -I (38102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (38198) SEN66: RTC is uninitialized, using current time as timestamp -W (38430) wifi:Haven't to connect to a suitable AP now! -I (38602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (38785) BME688: RTC is uninitialized, using current time as timestamp -I (39102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (39198) SEN66: RTC is uninitialized, using current time as timestamp -W (39431) wifi:Haven't to connect to a suitable AP now! -I (39602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -E (39754) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (39754) esp-tls: Failed to open new connection -E (39757) transport_base: Failed to open a new connection -E (39762) HTTP_CLIENT: Connection failed, sock < 0 -I (39766) INFLUX: HTTP request performed, status code: 0 -E (39771) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT -E (39779) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL -W (39785) BME688: RTC is uninitialized, using current time as timestamp -I (40102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (40198) SEN66: RTC is uninitialized, using current time as timestamp -W (40432) wifi:Haven't to connect to a suitable AP now! -I (40602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (40731) OTA_CLIENT: Device is not registered, attempting to register... -I (40733) OTA_CLIENT: HTTP client handle: 0x3fcf02e4 -W (40792) BME688: RTC is uninitialized, using current time as timestamp -I (41102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (41198) SEN66: RTC is uninitialized, using current time as timestamp -W (41433) wifi:Haven't to connect to a suitable AP now! -I (41602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (41786) INFLUX: Writing to InfluxDB -W (41792) BME688: RTC is uninitialized, using current time as timestamp -I (42102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (42198) SEN66: RTC is uninitialized, using current time as timestamp -W (42434) wifi:Haven't to connect to a suitable AP now! -I (42602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (42792) BME688: RTC is uninitialized, using current time as timestamp -I (43102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (43198) SEN66: RTC is uninitialized, using current time as timestamp -W (43435) wifi:Haven't to connect to a suitable AP now! -I (43602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (43792) BME688: RTC is uninitialized, using current time as timestamp -I (44102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (44198) SEN66: RTC is uninitialized, using current time as timestamp -W (44436) wifi:Haven't to connect to a suitable AP now! -I (44602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (44792) BME688: RTC is uninitialized, using current time as timestamp -I (45102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (45198) SEN66: RTC is uninitialized, using current time as timestamp -W (45437) wifi:Haven't to connect to a suitable AP now! -I (45602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (45792) BME688: RTC is uninitialized, using current time as timestamp -I (46102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (46198) SEN66: RTC is uninitialized, using current time as timestamp -W (46438) wifi:Haven't to connect to a suitable AP now! -I (46602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (46792) BME688: RTC is uninitialized, using current time as timestamp -I (47102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (47198) SEN66: RTC is uninitialized, using current time as timestamp -W (47439) wifi:Haven't to connect to a suitable AP now! -I (47602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -E (47734) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (47735) esp-tls: Failed to open new connection -E (47736) transport_base: Failed to open a new connection -E (47742) HTTP_CLIENT: Connection failed, sock < 0 -E (47746) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 -E (47754) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT -W (47792) BME688: RTC is uninitialized, using current time as timestamp -I (48102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (48198) SEN66: RTC is uninitialized, using current time as timestamp -W (48440) wifi:Haven't to connect to a suitable AP now! -I (48602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -E (48734) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (48735) esp-tls: Failed to open new connection -E (48737) transport_base: Failed to open a new connection -E (48742) HTTP_CLIENT: Connection failed, sock < 0 -I (48746) INFLUX: HTTP request performed, status code: 0 -E (48751) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT -E (48759) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL -W (48792) BME688: RTC is uninitialized, using current time as timestamp -I (49102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (49198) SEN66: RTC is uninitialized, using current time as timestamp -W (49441) wifi:Haven't to connect to a suitable AP now! -I (49602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (49792) BME688: RTC is uninitialized, using current time as timestamp -I (50102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (50198) SEN66: RTC is uninitialized, using current time as timestamp -W (50442) wifi:Haven't to connect to a suitable AP now! -I (50602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (50766) INFLUX: Writing to InfluxDB -W (50792) BME688: RTC is uninitialized, using current time as timestamp -I (51102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (51198) SEN66: RTC is uninitialized, using current time as timestamp -W (51443) wifi:Haven't to connect to a suitable AP now! -I (51602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (51792) BME688: RTC is uninitialized, using current time as timestamp -I (52102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (52198) SEN66: RTC is uninitialized, using current time as timestamp -W (52444) wifi:Haven't to connect to a suitable AP now! -I (52602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (52792) BME688: RTC is uninitialized, using current time as timestamp -I (53102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (53198) SEN66: RTC is uninitialized, using current time as timestamp -W (53445) wifi:Haven't to connect to a suitable AP now! -I (53602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (53792) BME688: RTC is uninitialized, using current time as timestamp -I (54102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (54198) SEN66: RTC is uninitialized, using current time as timestamp -W (54446) wifi:Haven't to connect to a suitable AP now! -I (54602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (54792) BME688: RTC is uninitialized, using current time as timestamp -I (55102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (55198) SEN66: RTC is uninitialized, using current time as timestamp -W (55447) wifi:Haven't to connect to a suitable AP now! -I (55602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (55792) BME688: RTC is uninitialized, using current time as timestamp -I (56102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (56198) SEN66: RTC is uninitialized, using current time as timestamp -W (56448) wifi:Haven't to connect to a suitable AP now! -I (56602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (56792) BME688: RTC is uninitialized, using current time as timestamp -I (57102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (57198) SEN66: RTC is uninitialized, using current time as timestamp -W (57449) wifi:Haven't to connect to a suitable AP now! -I (57602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (57761) OTA_CLIENT: Device is not registered, attempting to register... -I (57763) OTA_CLIENT: HTTP client handle: 0x3fcf0aac -E (57767) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (57771) esp-tls: Failed to open new connection -E (57774) transport_base: Failed to open a new connection -E (57780) HTTP_CLIENT: Connection failed, sock < 0 -I (57784) INFLUX: HTTP request performed, status code: 0 -E (57789) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT -E (57797) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL -W (57792) BME688: RTC is uninitialized, using current time as timestamp -I (58102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (58198) SEN66: RTC is uninitialized, using current time as timestamp -W (58450) wifi:Haven't to connect to a suitable AP now! -I (58602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (58810) BME688: RTC is uninitialized, using current time as timestamp -I (59102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (59198) SEN66: RTC is uninitialized, using current time as timestamp -W (59451) wifi:Haven't to connect to a suitable AP now! -I (59602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -E (59804) INFLUX_BUFFER: [E-INF-18] Failed to write chunk with 5 retries to Influx: ESP_FAIL -I (59804) KOTA: status OK -> CONNECTION_ERROR reason=influx_flush_failed -E (59808) MAIN: [E-MAIN-27] Failed to flush Influx buffer: ESP_FAIL -W (59810) BME688: RTC is uninitialized, using current time as timestamp -I (60102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (60198) SEN66: RTC is uninitialized, using current time as timestamp -W (60452) wifi:Haven't to connect to a suitable AP now! -I (60602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (60820) BME688: RTC is uninitialized, using current time as timestamp -I (61102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (61198) SEN66: RTC is uninitialized, using current time as timestamp -W (61453) wifi:Haven't to connect to a suitable AP now! -I (61602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (61820) BME688: RTC is uninitialized, using current time as timestamp -I (62102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (62198) SEN66: RTC is uninitialized, using current time as timestamp -W (62454) wifi:Haven't to connect to a suitable AP now! -I (62602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (62820) BME688: RTC is uninitialized, using current time as timestamp -W (62927) wifi:Haven't to connect to a suitable AP now! -I (62927) KOTA: health up=61s status=CONNECTION_ERROR wifi=disconnected rssi=0 time_valid=0 sd=ok buffer=764 heap=46611 psram=7.3M last_upload=never ota=unregistered -I (63102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (63198) SEN66: RTC is uninitialized, using current time as timestamp -W (63455) wifi:Haven't to connect to a suitable AP now! -I (63602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -E (63767) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (63767) esp-tls: Failed to open new connection -E (63769) transport_base: Failed to open a new connection -E (63775) HTTP_CLIENT: Connection failed, sock < 0 -E (63779) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 -E (63787) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT -W (63820) BME688: RTC is uninitialized, using current time as timestamp -I (64102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (64198) SEN66: RTC is uninitialized, using current time as timestamp -W (64456) wifi:Haven't to connect to a suitable AP now! -I (64602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (64634) SEN66: First measurement, waiting additional 120 seconds for sensor to stabilize... -W (64820) BME688: RTC is uninitialized, using current time as timestamp -I (65102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (65198) SEN66: RTC is uninitialized, using current time as timestamp -W (65457) wifi:Haven't to connect to a suitable AP now! -I (65602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (65820) BME688: RTC is uninitialized, using current time as timestamp -I (66102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (66198) SEN66: RTC is uninitialized, using current time as timestamp -W (66458) wifi:Haven't to connect to a suitable AP now! -I (66602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (66820) BME688: RTC is uninitialized, using current time as timestamp -I (67102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (67198) SEN66: RTC is uninitialized, using current time as timestamp -W (67459) wifi:Haven't to connect to a suitable AP now! -I (67602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (67820) BME688: RTC is uninitialized, using current time as timestamp -I (68102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (68198) SEN66: RTC is uninitialized, using current time as timestamp -W (68460) wifi:Haven't to connect to a suitable AP now! -I (68602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (68820) BME688: RTC is uninitialized, using current time as timestamp -I (69102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (69198) SEN66: RTC is uninitialized, using current time as timestamp -W (69461) wifi:Haven't to connect to a suitable AP now! -I (69602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (69820) BME688: RTC is uninitialized, using current time as timestamp -I (70102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (70198) SEN66: RTC is uninitialized, using current time as timestamp -W (70462) wifi:Haven't to connect to a suitable AP now! -I (70602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (70820) BME688: RTC is uninitialized, using current time as timestamp -I (71102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (71198) SEN66: RTC is uninitialized, using current time as timestamp -W (71463) wifi:Haven't to connect to a suitable AP now! -I (71602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (71820) BME688: RTC is uninitialized, using current time as timestamp -I (72102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (72198) SEN66: RTC is uninitialized, using current time as timestamp -W (72464) wifi:Haven't to connect to a suitable AP now! -I (72602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (72820) BME688: RTC is uninitialized, using current time as timestamp -I (73102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (73198) SEN66: RTC is uninitialized, using current time as timestamp -W (73465) wifi:Haven't to connect to a suitable AP now! -I (73602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (73794) OTA_CLIENT: Device is not registered, attempting to register... -I (73796) OTA_CLIENT: HTTP client handle: 0x3fcf03a8 -W (73820) BME688: RTC is uninitialized, using current time as timestamp -I (74102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (74198) SEN66: RTC is uninitialized, using current time as timestamp -W (74466) wifi:Haven't to connect to a suitable AP now! -I (74602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (74609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (74820) BME688: RTC is uninitialized, using current time as timestamp -I (75102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (75109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (75198) SEN66: RTC is uninitialized, using current time as timestamp -W (75467) wifi:Haven't to connect to a suitable AP now! -I (75602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (75609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (75820) BME688: RTC is uninitialized, using current time as timestamp -I (76102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (76109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (76198) SEN66: RTC is uninitialized, using current time as timestamp -W (76468) wifi:Haven't to connect to a suitable AP now! -I (76602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (76609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (76820) BME688: RTC is uninitialized, using current time as timestamp -I (77102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (77109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (77198) SEN66: RTC is uninitialized, using current time as timestamp -W (77469) wifi:Haven't to connect to a suitable AP now! -I (77602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (77609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (77820) BME688: RTC is uninitialized, using current time as timestamp -I (78102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (78109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (78198) SEN66: RTC is uninitialized, using current time as timestamp -W (78470) wifi:Haven't to connect to a suitable AP now! -I (78602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (78609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (78820) BME688: RTC is uninitialized, using current time as timestamp -I (79102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (79109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (79198) SEN66: RTC is uninitialized, using current time as timestamp -W (79471) wifi:Haven't to connect to a suitable AP now! -I (79602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (79609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (79820) BME688: RTC is uninitialized, using current time as timestamp -I (80102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (80109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (80198) SEN66: RTC is uninitialized, using current time as timestamp -W (80472) wifi:Haven't to connect to a suitable AP now! -I (80602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (80609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -E (80797) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (80798) esp-tls: Failed to open new connection -E (80799) transport_base: Failed to open a new connection -E (80805) HTTP_CLIENT: Connection failed, sock < 0 -E (80809) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 -E (80817) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT -W (80820) BME688: RTC is uninitialized, using current time as timestamp -I (81102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (81109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (81198) SEN66: RTC is uninitialized, using current time as timestamp -W (81473) wifi:Haven't to connect to a suitable AP now! -I (81602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (81609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (81831) BME688: RTC is uninitialized, using current time as timestamp -I (82102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (82109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (82198) SEN66: RTC is uninitialized, using current time as timestamp -W (82474) wifi:Haven't to connect to a suitable AP now! -I (82602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (82609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (82831) BME688: RTC is uninitialized, using current time as timestamp -I (83102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (83109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (83198) SEN66: RTC is uninitialized, using current time as timestamp -W (83475) wifi:Haven't to connect to a suitable AP now! -I (83602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (83609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (83831) BME688: RTC is uninitialized, using current time as timestamp -I (84102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (84109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (84198) SEN66: RTC is uninitialized, using current time as timestamp -W (84476) wifi:Haven't to connect to a suitable AP now! -I (84602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (84609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (84831) BME688: RTC is uninitialized, using current time as timestamp -I (85102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (85109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (85198) SEN66: RTC is uninitialized, using current time as timestamp -W (85477) wifi:Haven't to connect to a suitable AP now! -I (85602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (85609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (85831) BME688: RTC is uninitialized, using current time as timestamp -I (86102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (86109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (86198) SEN66: RTC is uninitialized, using current time as timestamp -W (86478) wifi:Haven't to connect to a suitable AP now! -I (86602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (86609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (86831) BME688: RTC is uninitialized, using current time as timestamp -I (87102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (87109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (87198) SEN66: RTC is uninitialized, using current time as timestamp -W (87479) wifi:Haven't to connect to a suitable AP now! -I (87602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (87609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (87831) BME688: RTC is uninitialized, using current time as timestamp -I (88102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (88109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (88198) SEN66: RTC is uninitialized, using current time as timestamp -W (88480) wifi:Haven't to connect to a suitable AP now! -I (88602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (88609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (88831) BME688: RTC is uninitialized, using current time as timestamp -I (89102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (89109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (89198) SEN66: RTC is uninitialized, using current time as timestamp -W (89481) wifi:Haven't to connect to a suitable AP now! -I (89602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (89609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (89831) BME688: RTC is uninitialized, using current time as timestamp -I (90102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (90109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (90198) SEN66: RTC is uninitialized, using current time as timestamp -W (90482) wifi:Haven't to connect to a suitable AP now! -I (90602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (90609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -I (90824) OTA_CLIENT: Device is not registered, attempting to register... -I (90826) OTA_CLIENT: HTTP client handle: 0x3fcf03a8 -W (90831) BME688: RTC is uninitialized, using current time as timestamp -I (91102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (91109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (91198) SEN66: RTC is uninitialized, using current time as timestamp -W (91483) wifi:Haven't to connect to a suitable AP now! -I (91602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (91609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (91831) BME688: RTC is uninitialized, using current time as timestamp -I (92102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (92109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (92198) SEN66: RTC is uninitialized, using current time as timestamp -W (92484) wifi:Haven't to connect to a suitable AP now! -I (92602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (92609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (92831) BME688: RTC is uninitialized, using current time as timestamp -I (93102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (93109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (93198) SEN66: RTC is uninitialized, using current time as timestamp -W (93485) wifi:Haven't to connect to a suitable AP now! -I (93602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (93609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (93831) BME688: RTC is uninitialized, using current time as timestamp -I (94102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (94109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (94198) SEN66: RTC is uninitialized, using current time as timestamp -W (94486) wifi:Haven't to connect to a suitable AP now! -I (94602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (94609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (94831) BME688: RTC is uninitialized, using current time as timestamp -I (95102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (95109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (95198) SEN66: RTC is uninitialized, using current time as timestamp -W (95487) wifi:Haven't to connect to a suitable AP now! -I (95602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (95609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (95831) BME688: RTC is uninitialized, using current time as timestamp -I (96102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (96109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (96198) SEN66: RTC is uninitialized, using current time as timestamp -W (96488) wifi:Haven't to connect to a suitable AP now! -I (96602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (96609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (96831) BME688: RTC is uninitialized, using current time as timestamp -I (97102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (97109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (97198) SEN66: RTC is uninitialized, using current time as timestamp -W (97489) wifi:Haven't to connect to a suitable AP now! -I (97602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (97609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -E (97827) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (97827) esp-tls: Failed to open new connection -E (97829) transport_base: Failed to open a new connection -W (97831) BME688: RTC is uninitialized, using current time as timestamp -E (97835) HTTP_CLIENT: Connection failed, sock < 0 -E (97845) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 -E (97854) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT -I (98102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (98109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (98198) SEN66: RTC is uninitialized, using current time as timestamp -W (98490) wifi:Haven't to connect to a suitable AP now! -I (98602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (98609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (98841) BME688: RTC is uninitialized, using current time as timestamp -I (99102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (99109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (99198) SEN66: RTC is uninitialized, using current time as timestamp -W (99491) wifi:Haven't to connect to a suitable AP now! -I (99602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (99609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (99841) BME688: RTC is uninitialized, using current time as timestamp -I (100102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (100109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (100198) SEN66: RTC is uninitialized, using current time as timestamp -W (100492) wifi:Haven't to connect to a suitable AP now! -I (100602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (100609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (100841) BME688: RTC is uninitialized, using current time as timestamp -I (101102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (101109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (101198) SEN66: RTC is uninitialized, using current time as timestamp -W (101493) wifi:Haven't to connect to a suitable AP now! -I (101602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (101609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (101841) BME688: RTC is uninitialized, using current time as timestamp -I (102102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (102109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (102198) SEN66: RTC is uninitialized, using current time as timestamp -W (102494) wifi:Haven't to connect to a suitable AP now! -I (102602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (102609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (102841) BME688: RTC is uninitialized, using current time as timestamp -I (103102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (103109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (103198) SEN66: RTC is uninitialized, using current time as timestamp -W (103495) wifi:Haven't to connect to a suitable AP now! -I (103602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (103609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (103841) BME688: RTC is uninitialized, using current time as timestamp -I (104102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (104109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (104198) SEN66: RTC is uninitialized, using current time as timestamp -W (104496) wifi:Haven't to connect to a suitable AP now! -I (104602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (104609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (104841) BME688: RTC is uninitialized, using current time as timestamp -I (105102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (105109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (105198) SEN66: RTC is uninitialized, using current time as timestamp -W (105497) wifi:Haven't to connect to a suitable AP now! -I (105602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (105609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (105841) BME688: RTC is uninitialized, using current time as timestamp -I (106102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (106109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (106198) SEN66: RTC is uninitialized, using current time as timestamp -W (106498) wifi:Haven't to connect to a suitable AP now! -I (106602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (106609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (106841) BME688: RTC is uninitialized, using current time as timestamp -I (107102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (107109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (107198) SEN66: RTC is uninitialized, using current time as timestamp -W (107499) wifi:Haven't to connect to a suitable AP now! -I (107602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (107609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (107841) BME688: RTC is uninitialized, using current time as timestamp -I (107861) OTA_CLIENT: Device is not registered, attempting to register... -I (107863) OTA_CLIENT: HTTP client handle: 0x3fcf03a8 -I (108102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (108109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (108198) SEN66: RTC is uninitialized, using current time as timestamp -W (108500) wifi:Haven't to connect to a suitable AP now! -I (108602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (108609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (108841) BME688: RTC is uninitialized, using current time as timestamp -I (109102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (109109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (109198) SEN66: RTC is uninitialized, using current time as timestamp -W (109501) wifi:Haven't to connect to a suitable AP now! -I (109602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (109609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (109841) BME688: RTC is uninitialized, using current time as timestamp -I (110102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (110109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (110198) SEN66: RTC is uninitialized, using current time as timestamp -W (110502) wifi:Haven't to connect to a suitable AP now! -I (110602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (110609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (110841) BME688: RTC is uninitialized, using current time as timestamp -I (111102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (111109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (111198) SEN66: RTC is uninitialized, using current time as timestamp -W (111503) wifi:Haven't to connect to a suitable AP now! -I (111602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (111609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (111841) BME688: RTC is uninitialized, using current time as timestamp -I (112102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (112109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (112198) SEN66: RTC is uninitialized, using current time as timestamp -W (112504) wifi:Haven't to connect to a suitable AP now! -I (112602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (112609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (112841) BME688: RTC is uninitialized, using current time as timestamp -I (113102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (113109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (113198) SEN66: RTC is uninitialized, using current time as timestamp -W (113505) wifi:Haven't to connect to a suitable AP now! -I (113602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (113609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (113841) BME688: RTC is uninitialized, using current time as timestamp -I (114102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (114109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (114198) SEN66: RTC is uninitialized, using current time as timestamp -W (114506) wifi:Haven't to connect to a suitable AP now! -I (114602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (114609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (114841) BME688: RTC is uninitialized, using current time as timestamp -E (114864) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (114864) esp-tls: Failed to open new connection -E (114867) transport_base: Failed to open a new connection -E (114872) HTTP_CLIENT: Connection failed, sock < 0 -E (114876) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 -E (114885) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT -I (115102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (115109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (115198) SEN66: RTC is uninitialized, using current time as timestamp -W (115507) wifi:Haven't to connect to a suitable AP now! -I (115602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (115609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (115841) BME688: RTC is uninitialized, using current time as timestamp -I (116102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (116109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (116198) SEN66: RTC is uninitialized, using current time as timestamp -W (116508) wifi:Haven't to connect to a suitable AP now! -I (116602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (116609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (116841) BME688: RTC is uninitialized, using current time as timestamp -I (117102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (117109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (117198) SEN66: RTC is uninitialized, using current time as timestamp -W (117509) wifi:Haven't to connect to a suitable AP now! -I (117602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (117609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (117841) BME688: RTC is uninitialized, using current time as timestamp -I (118102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (118109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (118198) SEN66: RTC is uninitialized, using current time as timestamp -W (118510) wifi:Haven't to connect to a suitable AP now! -I (118602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (118609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (118841) BME688: RTC is uninitialized, using current time as timestamp -I (119102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (119109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (119198) SEN66: RTC is uninitialized, using current time as timestamp -W (119511) wifi:Haven't to connect to a suitable AP now! -I (119602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (119609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -I (119817) INFLUX_BUFFER: Flushing chunk starting at line 10: -I (119818) INFLUX: Writing to InfluxDB -W (119841) BME688: RTC is uninitialized, using current time as timestamp -I (120102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (120109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (120198) SEN66: RTC is uninitialized, using current time as timestamp -W (120512) wifi:Haven't to connect to a suitable AP now! -I (120602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (120609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (120841) BME688: RTC is uninitialized, using current time as timestamp -I (121102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (121109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (121198) SEN66: RTC is uninitialized, using current time as timestamp -W (121513) wifi:Haven't to connect to a suitable AP now! -I (121602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (121609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (121841) BME688: RTC is uninitialized, using current time as timestamp -I (122102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (122109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (122198) SEN66: RTC is uninitialized, using current time as timestamp -W (122514) wifi:Haven't to connect to a suitable AP now! -I (122602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (122609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (122841) BME688: RTC is uninitialized, using current time as timestamp -W (122935) wifi:Haven't to connect to a suitable AP now! -I (122935) KOTA: health up=121s status=CONNECTION_ERROR wifi=disconnected rssi=0 time_valid=0 sd=ok buffer=764 heap=43167 psram=7.3M last_upload=never ota=unregistered -I (123102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (123109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (123198) SEN66: RTC is uninitialized, using current time as timestamp -W (123515) wifi:Haven't to connect to a suitable AP now! -I (123602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (123609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (123841) BME688: RTC is uninitialized, using current time as timestamp -I (124102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (124109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (124198) SEN66: RTC is uninitialized, using current time as timestamp -W (124516) wifi:Haven't to connect to a suitable AP now! -I (124602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (124609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (124841) BME688: RTC is uninitialized, using current time as timestamp -I (124892) OTA_CLIENT: Device is not registered, attempting to register... -I (124894) OTA_CLIENT: HTTP client handle: 0x3fcf1210 -I (125102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (125109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (125198) SEN66: RTC is uninitialized, using current time as timestamp -W (125517) wifi:Haven't to connect to a suitable AP now! -I (125602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (125609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (125841) BME688: RTC is uninitialized, using current time as timestamp -I (126102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (126109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (126198) SEN66: RTC is uninitialized, using current time as timestamp -W (126518) wifi:Haven't to connect to a suitable AP now! -I (126602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (126609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -E (126819) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (126820) esp-tls: Failed to open new connection -E (126822) transport_base: Failed to open a new connection -E (126828) HTTP_CLIENT: Connection failed, sock < 0 -I (126832) INFLUX: HTTP request performed, status code: 0 -E (126837) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT -E (126845) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL -W (126841) BME688: RTC is uninitialized, using current time as timestamp -I (127102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (127109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (127198) SEN66: RTC is uninitialized, using current time as timestamp -W (127519) wifi:Haven't to connect to a suitable AP now! -I (127602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (127609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (127858) BME688: RTC is uninitialized, using current time as timestamp -I (128102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (128109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (128198) SEN66: RTC is uninitialized, using current time as timestamp -W (128520) wifi:Haven't to connect to a suitable AP now! -I (128602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (128609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -I (128852) INFLUX: Writing to InfluxDB -W (128858) BME688: RTC is uninitialized, using current time as timestamp -I (129102) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (129109) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (129198) SEN66: RTC is uninitialized, using current time as timestamp -W (129521) wifi:Haven't to connect to a suitable AP now! -I (129602) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (129609) SENSOR_GATHERER: 2/4 tasks still running, waiting... -W (129858) BME688: RTC is uninitialized, using current time as timestamp +--- connected to COM5 --- +W (7411) wifi:Haven't to connect to a suitable AP now! +--- reset issued by tool --- +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x15 (USB_UART_CHIP_RESET),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x4037f35e +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (25) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (28) boot: efuse block revision: v1.4 +I (32) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (43) boot: Enabling RNG early entropy source... +I (48) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (63) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (76) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (89) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (102) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (109) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c190020 size=66d30h (421168) map +I (194) esp_image: segment 1: paddr=000a6d58 vaddr=3fc9c700 size=05d00h ( 23808) load +I (199) esp_image: segment 2: paddr=000aca60 vaddr=40374000 size=035b8h ( 13752) load +I (203) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=1862d0h (1598160) map +I (487) esp_image: segment 4: paddr=002362f8 vaddr=403775b8 size=15054h ( 86100) load +I (506) esp_image: segment 5: paddr=0024b354 vaddr=5000002c size=00020h ( 32) load +I (516) boot: Loaded app from partition at offset 0x40000 +I (517) boot: Disabling RNG early entropy source... +I (527) octal_psram: vendor id : 0x0d (AP) +I (527) octal_psram: dev id : 0x02 (generation 3) +I (528) octal_psram: density : 0x03 (64 Mbit) +I (529) octal_psram: good-die : 0x01 (Pass) +I (534) octal_psram: Latency : 0x01 (Fixed) +I (538) octal_psram: VCC : 0x01 (3V) +I (542) octal_psram: SRF : 0x01 (Fast Refresh) +I (547) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (552) octal_psram: BurstLen : 0x01 (32 Byte) +I (556) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (562) octal_psram: DriveStrength: 0x00 (1/1) +I (566) esp_psram: Found 8MB PSRAM device +I (569) esp_psram: Speed: 40MHz +I (572) cpu_start: Multicore app +I (1306) esp_psram: SPI SRAM memory test OK +I (1314) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1315) cpu_start: Pro cpu start user code +I (1315) cpu_start: cpu freq: 160000000 Hz +I (1317) app_init: Application information: +I (1321) app_init: Project name: kota-sensorboard +I (1326) app_init: App version: 1.0.16 +I (1330) app_init: Compile time: Sep 28 2026 15:07:57 +I (1335) app_init: ELF file SHA256: 4f10989e2... +I (1339) app_init: ESP-IDF: v5.5.3-dirty +I (1344) efuse_init: Min chip rev: v0.0 +I (1347) efuse_init: Max chip rev: v0.99 +I (1352) efuse_init: Chip rev: v0.2 +I (1356) heap_init: Initializing. RAM available for dynamic allocation: +I (1362) heap_init: At 3FCB4AD0 len 00034C40 (211 KiB): RAM +I (1367) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1372) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1378) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1383) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1391) spi_flash: detected chip: generic +I (1393) spi_flash: flash io: dio +I (1399) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1403) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1418) main_task: Started on CPU0 +I (1421) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1429) main_task: Calling app_main() +E (1432) ID: [E-MAIN-04] Device MAC address: A4:CB:8F:C9:46:F8 +I (1458) KOTA: led color=green pattern=solid +I (1659) I2C: Creating I2C mutex +I (1676) KOTA: i2c SDA=1 SCL=1 0x39=AS7341 0x68=DS1307 0x6B=SEN66 0x76=BME688 +I (1677) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1679) SPI: Creating SPI mutex +I (1722) SD_CARD: SD card mounted successfully +I (1723) SD_CARD: /sdcard exists, SD card initialized successfully +I (1788) KOTA: sd type=SDHC size=29.3GB fs=FAT32 free=29.3GB env_keys=INFLUX_TOKEN,INFLUX_URL buffer_lines=764 +I (1792) WIFI_CREDENTIALS: SSID/PASSWORD not set in .env; Matter will be used for WiFi provisioning +I (1802) MAIN: Matter node created successfully +W (1803) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1810) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1818) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1827) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1836) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1845) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1854) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1862) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1871) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1880) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1888) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1897) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1906) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1915) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1924) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1932) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1941) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1950) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1960) pp: pp rom version: e7ae62f +I (1962) net80211: net80211 rom version: e7ae62f +I (1967) wifi:wifi driver task: 3fcc7d40, prio:23, stack:6656, core=0 +I (1985) wifi:wifi firmware version: 4df78f2 +I (1985) wifi:wifi certification version: v7.0 +I (1986) wifi:config NVS flash: enabled +I (1986) wifi:config nano formatting: disabled +I (1990) wifi:Init data frame dynamic rx buffer num: 32 +I (1995) wifi:Init static rx mgmt buffer num: 5 +I (1999) wifi:Init management short buffer num: 32 +I (2004) wifi:Init dynamic tx buffer num: 32 +I (2008) wifi:Init static tx FG buffer num: 2 +I (2012) wifi:Init static rx buffer size: 1600 +I (2016) wifi:Init static rx buffer num: 10 +I (2020) wifi:Init dynamic rx buffer num: 32 +I (2025) wifi_init: rx ba win: 6 +I (2027) wifi_init: accept mbox: 6 +I (2030) wifi_init: tcpip mbox: 32 +I (2033) wifi_init: udp mbox: 6 +I (2036) wifi_init: tcp mbox: 6 +I (2039) wifi_init: tcp tx win: 5760 +I (2042) wifi_init: tcp rx win: 5760 +I (2045) wifi_init: tcp mss: 1440 +I (2057) chip[DL]: NVS set: chip-counters/reboot-count = 15857 (0x3DF1) +I (2060) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (2063) BLE_INIT: BT controller compile version [1bb2f50] +I (2067) BLE_INIT: Using main XTAL as clock source +I (2072) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (2080) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (2085) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +I (2133) NimBLE: GAP procedure initiated: stop advertising. + +I (2135) CHIP[DL]: BLE host-controller synced +I (2635) chip[DL]: Starting ESP WiFi layer +I (2637) wifi:mode : sta (a4:cb:8f:c9:46:f8) +I (2638) wifi:enable tsf +W (2639) wifi:Haven't to connect to a suitable AP now! +I (2639) chip[DL]: +I (2639) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43Done driving station state, nothing else to do... + +W (2652) wifi:Haven't to connect to a suitable AP now! +I (2657) chip[DL]: Done driving station state, nothing else to do... +I (2657) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 +I (2663) chip[SVR]: +Initializing subscription resumption storage... +I (2676) chip[SVR]: Server initializing... +I (2680) chip[TS]: Last Known Good Time: 2026-09-28T13:29:12 +I (2685) chip[DMG]: AccessControl: initializing +I (2688) chip[DMG]: Examples::AccessControlDelegate::Init +I (2693) chip[DMG]: AccessControl: setting +I (2697) chip[DMG]: DefaultAclStorage: initializing +I (2702) chip[DMG]: DefaultAclStorage: 0 entries loaded +I (2707) chip[SVR]: WARNING: mTestEventTriggerDelegate is null +I (2726) chip[ZCL]: Using ZAP configuration... +I (2726) esp_matter_cluster: Cluster plugin init common callback +I (2727) chip[DMG]: AccessControlCluster: initializing +I (2730) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported +I (2740) chip[DIS]: Updating services using commissioning mode 1 +I (2744) chip[DIS]: CHIP minimal mDNS started advertising. +I (2749) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 +I (2759) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: A180A7E5D58D9F80. +I (2769) chip[DIS]: mDNS service published: _matterc._udp +I (2774) chip[IN]: CASE Server enabling CASE session setups +I (2780) chip[SVR]: Joining Multicast groups +I (2783) chip[SVR]: Server Listening... +I (2788) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** +I (2798) data_model: Dynamic endpoint 0 added +E (2801) data_model: Cluster cannot be NULL. +I (2806) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** +I (2816) data_model: Dynamic endpoint 1 added +E (2820) data_model: Cluster cannot be NULL. +I (2825) MAIN: ESP-Matter started successfully +I (2826) chip[DL]: WIFI_EVENT_STA_START +W (2832) wifi:Haven't to connect to a suitable AP now! +I (2837) chip[DL]: Done driving station state, nothing else to do... +I (2843) KOTA: boot fw=1.0.16 build=4f10989e hw=2 mac=A4CB8FC946F8 reset=USB_UART_CHIP_RESET mode=matter commissioned=0 last_restart=none +I (2845) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) +I (2862) NimBLE: GAP procedure initiated: advertise; +I (2866) NimBLE: disc_mode=2 +I (2869) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 +I (2878) NimBLE: + +I (2881) chip[DL]: CHIPoBLE advertising started +I (2886) KOTA: matter fabrics=0 window=open qr=MT:Y.K9042C00KA0648G00 manual=34970112332 +I (2892) MAIN: Commissioning window opened +W (2893) wifi:Haven't to connect to a suitable AP now! +I (2897) KOTA: matter fabrics=0 window=open qr=MT:Y.K9042C00KA0648G00 manual=34970112332 +I (2905) KOTA: led color=cyan pattern=breathe period=3000ms +I (2901) KOTA: health up=1s status=OK wifi=disconnected rssi=0 time_valid=0 sd=ok buffer=0 heap=123883 psram=7.9M last_upload=never ota=unregistered +W (3394) wifi:Haven't to connect to a suitable AP now! +I (3894) KOTA: rtc running=0 time=2000-01-01T00:00:00 +W (4395) wifi:Haven't to connect to a suitable AP now! +W (5396) wifi:Haven't to connect to a suitable AP now! +W (6396) wifi:Haven't to connect to a suitable AP now! +W (7397) wifi:Haven't to connect to a suitable AP now! +W (8398) wifi:Haven't to connect to a suitable AP now! +W (9399) wifi:Haven't to connect to a suitable AP now! +W (10400) wifi:Haven't to connect to a suitable AP now! +W (11401) wifi:Haven't to connect to a suitable AP now! +W (12402) wifi:Haven't to connect to a suitable AP now! +W (13403) wifi:Haven't to connect to a suitable AP now! +I (13895) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +W (13962) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (13975) BME688: Delaying for 10000 microseconds +W (14404) wifi:Haven't to connect to a suitable AP now! +I (14591) KOTA: mic enabled=1 (GPIO3=0, expected 0) +I (14593) Flicker: Flicker sensor initialized on pin 14 +I (14594) temperature_sensor: Range [20°C ~ 100°C], error < 2°C +I (14597) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +W (14601) wifi: +I (14597) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data +Haven't to connect to a suitable AP now! +I (14598) SEN66: Creating sensor mutex + +I (14605) INFLUX_BUFFER: Flushing chunk starting at line 10: +W (14611) AS7341: Reading spectral data from AS7341 +I (14616) SEN66: Waking up sensor by setting power GPIO to enabled level... +I (14617) DeviceStatus: Active tasks: 26 +I (14621) INFLUX: Writing to InfluxDB +I (14641) DeviceStatus: Task list: +main R 1 1132 4 +gather_sensor_t X 1 1480 24 +IDLE1 R 0 700 6 +IDLE0 R 0 692 5 +gather_sensor_t B 1 2552 22 +reset_task B 0 3344 9 +ds1307_update B 1 768 17 +tiT B 18 2420 11 +gather_task B 1 1984 19 +gather_task B 1 14292 20 +gather_sensor_t B 1 1364 25 +gather_sensor_t B 1 1948 23 +ota_task B 1 7504 18 +CHIP B 1 3636 13 +health B 1 928 16 +Tmr Svc B 1 1364 7 +led_task B 5 964 8 +sys_evt B 20 2676 10 +ipc0 S 24 372 1 +ipc1 S 24 524 2 +esp_timer S 22 3332 3 +gather_sensor_t B 1 2544 21 +gather_sensor_t B 1 2552 26 +wifi B 23 4324 12 +btController B 23 1960 14 +nimble_host B 21 1476 15 + +W (14710) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (14973) AS7341: Reading basic counts data from AS7341 +W (14989) AS7341: Reading flicker detection state from AS7341 +W (15206) as7341: FD Status Register: 0x20 (0b00100000) +I (15211) AS7341: Updating Matter attributes with AS7341 data: 14946 +I (15211) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms +I (15212) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** +I (15217) Flicker: Starting to collect samples in gather task +I (15228) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 14946 ********** +E (15245) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +E (15254) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +W (15264) AS7341: Formatting AS7341 data in line protocol +AS7341 line protocol: +as7341_adc,device=A4CB8FC946F8 f1=1,f2=2,f3=2,f4=3,f5=3,f6=3,f7=6,f8=4,clear=14,nir=2 946684801000000000 +as7341_basic_counts,device=A4CB8FC946F8 f1=2.23,f2=4.46,f3=4.46,f4=6.69,f5=6.69,f6=6.69,f7=13.39,f8=8.92,clear=31.23,nir=4.46 946684801000000000 +as7341_flicker,device=A4CB8FC946F8 state="unknown" 946684801000000000 +W (15404) wifi:Haven't to connect to a suitable AP now! +W (15717) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (16256) Flicker: Finished collecting samples, notifying main task +I (16257) Flicker: Collected 1024 samples for dominant frequency analysis +I (16264) SENSOR_GATHERER: Sensor should now be asleep +I (16276) BME688: Delaying for 199517 microseconds +I (16276) SENSOR_GATHERER: Sensor should now be asleep +W (16405) wifi:Haven't to connect to a suitable AP now! +I (16714) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16715) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1018 ********** +E (16726) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +I (16717) SENSOR_GATHERER: Sensor should now be asleep +E (16735) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16750) BME688: RTC is uninitialized, using current time as timestamp +W (17406) wifi:Haven't to connect to a suitable AP now! +W (17756) BME688: RTC is uninitialized, using current time as timestamp +W (18407) wifi:Haven't to connect to a suitable AP now! +W (18756) BME688: RTC is uninitialized, using current time as timestamp +W (19408) wifi:Haven't to connect to a suitable AP now! +I (19633) SEN66: Sent start measurement command, waiting for sensor to be ready... +W (19756) BME688: RTC is uninitialized, using current time as timestamp +W (20409) wifi:Haven't to connect to a suitable AP now! +W (20756) BME688: RTC is uninitialized, using current time as timestamp +W (21410) wifi:Haven't to connect to a suitable AP now! +E (21642) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (21642) esp-tls: Failed to open new connection +E (21645) transport_base: Failed to open a new connection +E (21650) HTTP_CLIENT: Connection failed, sock < 0 +I (21655) INFLUX: HTTP request performed, status code: 0 +E (21659) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (21667) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL +W (21756) BME688: RTC is uninitialized, using current time as timestamp +W (22411) wifi:Haven't to connect to a suitable AP now! +I (22602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (22756) BME688: RTC is uninitialized, using current time as timestamp +I (23102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (23412) wifi:Haven't to connect to a suitable AP now! +I (23602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (23674) INFLUX: Writing to InfluxDB +W (23756) BME688: RTC is uninitialized, using current time as timestamp +I (24102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (24413) wifi:Haven't to connect to a suitable AP now! +I (24596) OTA_CLIENT: Device is not registered, attempting to register... +I (24598) OTA_CLIENT: HTTP client handle: 0x3fcf0fe4 +I (24602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (24738) MICROPHONE: Read 600000 bytes in 9940 ms +I (24738) MICROPHONE: Read 600000 bytes of audio data +W (24756) BME688: RTC is uninitialized, using current time as timestamp +I (25102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (25414) wifi:Haven't to connect to a suitable AP now! +I (25602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (25756) BME688: RTC is uninitialized, using current time as timestamp +I (26102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (26415) wifi:Haven't to connect to a suitable AP now! +I (26602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (26756) BME688: RTC is uninitialized, using current time as timestamp +I (27102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (27416) wifi:Haven't to connect to a suitable AP now! +I (27602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (27756) BME688: RTC is uninitialized, using current time as timestamp +I (28102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (28187) MICROPHONE: Raw block stats: mean=-81.8 min=-41376 max=37946 +I (28188) MICROPHONE: Sum SQ: 0.032256, RMS pressure: 0.000402, Normalized: 0.000402, dB peak: -67.92 +W (28192) SEN66: RTC is uninitialized, using current time as timestamp +W (28417) wifi:Haven't to connect to a suitable AP now! +I (28602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (28756) BME688: RTC is uninitialized, using current time as timestamp +I (29102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (29198) SEN66: RTC is uninitialized, using current time as timestamp +W (29418) wifi:Haven't to connect to a suitable AP now! +I (29602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (29756) BME688: RTC is uninitialized, using current time as timestamp +I (30102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (30198) SEN66: RTC is uninitialized, using current time as timestamp +W (30419) wifi:Haven't to connect to a suitable AP now! +I (30602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +E (30675) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (30675) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (30676) esp-tls: Failed to open new connection +E (30683) esp-tls: Failed to open new connection +E (30687) transport_base: Failed to open a new connection +E (30691) transport_base: Failed to open a new connection +E (30696) HTTP_CLIENT: Connection failed, sock < 0 +E (30701) HTTP_CLIENT: Connection failed, sock < 0 +E (30706) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 +I (30710) INFLUX: HTTP request performed, status code: 0 +E (30719) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT +E (30723) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (30738) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL +W (30756) BME688: RTC is uninitialized, using current time as timestamp +I (31102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (31198) SEN66: RTC is uninitialized, using current time as timestamp +W (31420) wifi:Haven't to connect to a suitable AP now! +I (31602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (31756) BME688: RTC is uninitialized, using current time as timestamp +I (32102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (32198) SEN66: RTC is uninitialized, using current time as timestamp +W (32421) wifi:Haven't to connect to a suitable AP now! +I (32602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (32741) chip[DL]: bleAdv Timeout : Start slow advertisement +I (32743) chip[DL]: Configuring CHIPoBLE advertising (interval 500 ms, connectable) +I (32744) chip[DL]: Device already advertising, stop active advertisement and restart +I (32745) INFLUX: Writing to InfluxDB +I (32751) NimBLE: GAP procedure initiated: stop advertising. + +W (32756) BME688: RTC is uninitialized, using current time as timestamp +I (32760) NimBLE: GAP procedure initiated: advertise; +I (32770) NimBLE: disc_mode=2 +I (32773) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=800 adv_itvl_max=800 +I (32783) NimBLE: + +I (33102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (33198) SEN66: RTC is uninitialized, using current time as timestamp +W (33425) wifi:Haven't to connect to a suitable AP now! +I (33602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (33785) BME688: RTC is uninitialized, using current time as timestamp +I (34102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (34198) SEN66: RTC is uninitialized, using current time as timestamp +W (34426) wifi:Haven't to connect to a suitable AP now! +I (34602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (34785) BME688: RTC is uninitialized, using current time as timestamp +I (35102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (35198) SEN66: RTC is uninitialized, using current time as timestamp +W (35427) wifi:Haven't to connect to a suitable AP now! +I (35602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (35785) BME688: RTC is uninitialized, using current time as timestamp +I (36102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (36198) SEN66: RTC is uninitialized, using current time as timestamp +W (36428) wifi:Haven't to connect to a suitable AP now! +I (36602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (36785) BME688: RTC is uninitialized, using current time as timestamp +I (37102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (37198) SEN66: RTC is uninitialized, using current time as timestamp +W (37429) wifi:Haven't to connect to a suitable AP now! +I (37602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (37785) BME688: RTC is uninitialized, using current time as timestamp +I (38102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (38198) SEN66: RTC is uninitialized, using current time as timestamp +W (38430) wifi:Haven't to connect to a suitable AP now! +I (38602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (38785) BME688: RTC is uninitialized, using current time as timestamp +I (39102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (39198) SEN66: RTC is uninitialized, using current time as timestamp +W (39431) wifi:Haven't to connect to a suitable AP now! +I (39602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +E (39754) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (39754) esp-tls: Failed to open new connection +E (39757) transport_base: Failed to open a new connection +E (39762) HTTP_CLIENT: Connection failed, sock < 0 +I (39766) INFLUX: HTTP request performed, status code: 0 +E (39771) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (39779) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL +W (39785) BME688: RTC is uninitialized, using current time as timestamp +I (40102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (40198) SEN66: RTC is uninitialized, using current time as timestamp +W (40432) wifi:Haven't to connect to a suitable AP now! +I (40602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (40731) OTA_CLIENT: Device is not registered, attempting to register... +I (40733) OTA_CLIENT: HTTP client handle: 0x3fcf02e4 +W (40792) BME688: RTC is uninitialized, using current time as timestamp +I (41102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (41198) SEN66: RTC is uninitialized, using current time as timestamp +W (41433) wifi:Haven't to connect to a suitable AP now! +I (41602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (41786) INFLUX: Writing to InfluxDB +W (41792) BME688: RTC is uninitialized, using current time as timestamp +I (42102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (42198) SEN66: RTC is uninitialized, using current time as timestamp +W (42434) wifi:Haven't to connect to a suitable AP now! +I (42602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (42792) BME688: RTC is uninitialized, using current time as timestamp +I (43102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (43198) SEN66: RTC is uninitialized, using current time as timestamp +W (43435) wifi:Haven't to connect to a suitable AP now! +I (43602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (43792) BME688: RTC is uninitialized, using current time as timestamp +I (44102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (44198) SEN66: RTC is uninitialized, using current time as timestamp +W (44436) wifi:Haven't to connect to a suitable AP now! +I (44602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (44792) BME688: RTC is uninitialized, using current time as timestamp +I (45102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (45198) SEN66: RTC is uninitialized, using current time as timestamp +W (45437) wifi:Haven't to connect to a suitable AP now! +I (45602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (45792) BME688: RTC is uninitialized, using current time as timestamp +I (46102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (46198) SEN66: RTC is uninitialized, using current time as timestamp +W (46438) wifi:Haven't to connect to a suitable AP now! +I (46602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (46792) BME688: RTC is uninitialized, using current time as timestamp +I (47102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (47198) SEN66: RTC is uninitialized, using current time as timestamp +W (47439) wifi:Haven't to connect to a suitable AP now! +I (47602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +E (47734) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (47735) esp-tls: Failed to open new connection +E (47736) transport_base: Failed to open a new connection +E (47742) HTTP_CLIENT: Connection failed, sock < 0 +E (47746) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 +E (47754) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT +W (47792) BME688: RTC is uninitialized, using current time as timestamp +I (48102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (48198) SEN66: RTC is uninitialized, using current time as timestamp +W (48440) wifi:Haven't to connect to a suitable AP now! +I (48602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +E (48734) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (48735) esp-tls: Failed to open new connection +E (48737) transport_base: Failed to open a new connection +E (48742) HTTP_CLIENT: Connection failed, sock < 0 +I (48746) INFLUX: HTTP request performed, status code: 0 +E (48751) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (48759) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL +W (48792) BME688: RTC is uninitialized, using current time as timestamp +I (49102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (49198) SEN66: RTC is uninitialized, using current time as timestamp +W (49441) wifi:Haven't to connect to a suitable AP now! +I (49602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (49792) BME688: RTC is uninitialized, using current time as timestamp +I (50102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (50198) SEN66: RTC is uninitialized, using current time as timestamp +W (50442) wifi:Haven't to connect to a suitable AP now! +I (50602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (50766) INFLUX: Writing to InfluxDB +W (50792) BME688: RTC is uninitialized, using current time as timestamp +I (51102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (51198) SEN66: RTC is uninitialized, using current time as timestamp +W (51443) wifi:Haven't to connect to a suitable AP now! +I (51602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (51792) BME688: RTC is uninitialized, using current time as timestamp +I (52102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (52198) SEN66: RTC is uninitialized, using current time as timestamp +W (52444) wifi:Haven't to connect to a suitable AP now! +I (52602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (52792) BME688: RTC is uninitialized, using current time as timestamp +I (53102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (53198) SEN66: RTC is uninitialized, using current time as timestamp +W (53445) wifi:Haven't to connect to a suitable AP now! +I (53602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (53792) BME688: RTC is uninitialized, using current time as timestamp +I (54102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (54198) SEN66: RTC is uninitialized, using current time as timestamp +W (54446) wifi:Haven't to connect to a suitable AP now! +I (54602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (54792) BME688: RTC is uninitialized, using current time as timestamp +I (55102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (55198) SEN66: RTC is uninitialized, using current time as timestamp +W (55447) wifi:Haven't to connect to a suitable AP now! +I (55602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (55792) BME688: RTC is uninitialized, using current time as timestamp +I (56102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (56198) SEN66: RTC is uninitialized, using current time as timestamp +W (56448) wifi:Haven't to connect to a suitable AP now! +I (56602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (56792) BME688: RTC is uninitialized, using current time as timestamp +I (57102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (57198) SEN66: RTC is uninitialized, using current time as timestamp +W (57449) wifi:Haven't to connect to a suitable AP now! +I (57602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (57761) OTA_CLIENT: Device is not registered, attempting to register... +I (57763) OTA_CLIENT: HTTP client handle: 0x3fcf0aac +E (57767) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (57771) esp-tls: Failed to open new connection +E (57774) transport_base: Failed to open a new connection +E (57780) HTTP_CLIENT: Connection failed, sock < 0 +I (57784) INFLUX: HTTP request performed, status code: 0 +E (57789) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (57797) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL +W (57792) BME688: RTC is uninitialized, using current time as timestamp +I (58102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (58198) SEN66: RTC is uninitialized, using current time as timestamp +W (58450) wifi:Haven't to connect to a suitable AP now! +I (58602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (58810) BME688: RTC is uninitialized, using current time as timestamp +I (59102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (59198) SEN66: RTC is uninitialized, using current time as timestamp +W (59451) wifi:Haven't to connect to a suitable AP now! +I (59602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +E (59804) INFLUX_BUFFER: [E-INF-18] Failed to write chunk with 5 retries to Influx: ESP_FAIL +I (59804) KOTA: status OK -> CONNECTION_ERROR reason=influx_flush_failed +E (59808) MAIN: [E-MAIN-27] Failed to flush Influx buffer: ESP_FAIL +W (59810) BME688: RTC is uninitialized, using current time as timestamp +I (60102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (60198) SEN66: RTC is uninitialized, using current time as timestamp +W (60452) wifi:Haven't to connect to a suitable AP now! +I (60602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (60820) BME688: RTC is uninitialized, using current time as timestamp +I (61102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (61198) SEN66: RTC is uninitialized, using current time as timestamp +W (61453) wifi:Haven't to connect to a suitable AP now! +I (61602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (61820) BME688: RTC is uninitialized, using current time as timestamp +I (62102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (62198) SEN66: RTC is uninitialized, using current time as timestamp +W (62454) wifi:Haven't to connect to a suitable AP now! +I (62602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (62820) BME688: RTC is uninitialized, using current time as timestamp +W (62927) wifi:Haven't to connect to a suitable AP now! +I (62927) KOTA: health up=61s status=CONNECTION_ERROR wifi=disconnected rssi=0 time_valid=0 sd=ok buffer=764 heap=46611 psram=7.3M last_upload=never ota=unregistered +I (63102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (63198) SEN66: RTC is uninitialized, using current time as timestamp +W (63455) wifi:Haven't to connect to a suitable AP now! +I (63602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +E (63767) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (63767) esp-tls: Failed to open new connection +E (63769) transport_base: Failed to open a new connection +E (63775) HTTP_CLIENT: Connection failed, sock < 0 +E (63779) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 +E (63787) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT +W (63820) BME688: RTC is uninitialized, using current time as timestamp +I (64102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (64198) SEN66: RTC is uninitialized, using current time as timestamp +W (64456) wifi:Haven't to connect to a suitable AP now! +I (64602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (64634) SEN66: First measurement, waiting additional 120 seconds for sensor to stabilize... +W (64820) BME688: RTC is uninitialized, using current time as timestamp +I (65102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (65198) SEN66: RTC is uninitialized, using current time as timestamp +W (65457) wifi:Haven't to connect to a suitable AP now! +I (65602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (65820) BME688: RTC is uninitialized, using current time as timestamp +I (66102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (66198) SEN66: RTC is uninitialized, using current time as timestamp +W (66458) wifi:Haven't to connect to a suitable AP now! +I (66602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (66820) BME688: RTC is uninitialized, using current time as timestamp +I (67102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (67198) SEN66: RTC is uninitialized, using current time as timestamp +W (67459) wifi:Haven't to connect to a suitable AP now! +I (67602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (67820) BME688: RTC is uninitialized, using current time as timestamp +I (68102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (68198) SEN66: RTC is uninitialized, using current time as timestamp +W (68460) wifi:Haven't to connect to a suitable AP now! +I (68602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (68820) BME688: RTC is uninitialized, using current time as timestamp +I (69102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (69198) SEN66: RTC is uninitialized, using current time as timestamp +W (69461) wifi:Haven't to connect to a suitable AP now! +I (69602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (69820) BME688: RTC is uninitialized, using current time as timestamp +I (70102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (70198) SEN66: RTC is uninitialized, using current time as timestamp +W (70462) wifi:Haven't to connect to a suitable AP now! +I (70602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (70820) BME688: RTC is uninitialized, using current time as timestamp +I (71102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (71198) SEN66: RTC is uninitialized, using current time as timestamp +W (71463) wifi:Haven't to connect to a suitable AP now! +I (71602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (71820) BME688: RTC is uninitialized, using current time as timestamp +I (72102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (72198) SEN66: RTC is uninitialized, using current time as timestamp +W (72464) wifi:Haven't to connect to a suitable AP now! +I (72602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (72820) BME688: RTC is uninitialized, using current time as timestamp +I (73102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (73198) SEN66: RTC is uninitialized, using current time as timestamp +W (73465) wifi:Haven't to connect to a suitable AP now! +I (73602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (73794) OTA_CLIENT: Device is not registered, attempting to register... +I (73796) OTA_CLIENT: HTTP client handle: 0x3fcf03a8 +W (73820) BME688: RTC is uninitialized, using current time as timestamp +I (74102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (74198) SEN66: RTC is uninitialized, using current time as timestamp +W (74466) wifi:Haven't to connect to a suitable AP now! +I (74602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (74609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (74820) BME688: RTC is uninitialized, using current time as timestamp +I (75102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (75109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (75198) SEN66: RTC is uninitialized, using current time as timestamp +W (75467) wifi:Haven't to connect to a suitable AP now! +I (75602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (75609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (75820) BME688: RTC is uninitialized, using current time as timestamp +I (76102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (76109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (76198) SEN66: RTC is uninitialized, using current time as timestamp +W (76468) wifi:Haven't to connect to a suitable AP now! +I (76602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (76609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (76820) BME688: RTC is uninitialized, using current time as timestamp +I (77102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (77109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (77198) SEN66: RTC is uninitialized, using current time as timestamp +W (77469) wifi:Haven't to connect to a suitable AP now! +I (77602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (77609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (77820) BME688: RTC is uninitialized, using current time as timestamp +I (78102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (78109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (78198) SEN66: RTC is uninitialized, using current time as timestamp +W (78470) wifi:Haven't to connect to a suitable AP now! +I (78602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (78609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (78820) BME688: RTC is uninitialized, using current time as timestamp +I (79102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (79109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (79198) SEN66: RTC is uninitialized, using current time as timestamp +W (79471) wifi:Haven't to connect to a suitable AP now! +I (79602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (79609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (79820) BME688: RTC is uninitialized, using current time as timestamp +I (80102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (80109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (80198) SEN66: RTC is uninitialized, using current time as timestamp +W (80472) wifi:Haven't to connect to a suitable AP now! +I (80602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (80609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +E (80797) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (80798) esp-tls: Failed to open new connection +E (80799) transport_base: Failed to open a new connection +E (80805) HTTP_CLIENT: Connection failed, sock < 0 +E (80809) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 +E (80817) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT +W (80820) BME688: RTC is uninitialized, using current time as timestamp +I (81102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (81109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (81198) SEN66: RTC is uninitialized, using current time as timestamp +W (81473) wifi:Haven't to connect to a suitable AP now! +I (81602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (81609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (81831) BME688: RTC is uninitialized, using current time as timestamp +I (82102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (82109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (82198) SEN66: RTC is uninitialized, using current time as timestamp +W (82474) wifi:Haven't to connect to a suitable AP now! +I (82602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (82609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (82831) BME688: RTC is uninitialized, using current time as timestamp +I (83102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (83109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (83198) SEN66: RTC is uninitialized, using current time as timestamp +W (83475) wifi:Haven't to connect to a suitable AP now! +I (83602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (83609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (83831) BME688: RTC is uninitialized, using current time as timestamp +I (84102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (84109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (84198) SEN66: RTC is uninitialized, using current time as timestamp +W (84476) wifi:Haven't to connect to a suitable AP now! +I (84602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (84609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (84831) BME688: RTC is uninitialized, using current time as timestamp +I (85102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (85109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (85198) SEN66: RTC is uninitialized, using current time as timestamp +W (85477) wifi:Haven't to connect to a suitable AP now! +I (85602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (85609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (85831) BME688: RTC is uninitialized, using current time as timestamp +I (86102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (86109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (86198) SEN66: RTC is uninitialized, using current time as timestamp +W (86478) wifi:Haven't to connect to a suitable AP now! +I (86602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (86609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (86831) BME688: RTC is uninitialized, using current time as timestamp +I (87102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (87109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (87198) SEN66: RTC is uninitialized, using current time as timestamp +W (87479) wifi:Haven't to connect to a suitable AP now! +I (87602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (87609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (87831) BME688: RTC is uninitialized, using current time as timestamp +I (88102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (88109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (88198) SEN66: RTC is uninitialized, using current time as timestamp +W (88480) wifi:Haven't to connect to a suitable AP now! +I (88602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (88609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (88831) BME688: RTC is uninitialized, using current time as timestamp +I (89102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (89109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (89198) SEN66: RTC is uninitialized, using current time as timestamp +W (89481) wifi:Haven't to connect to a suitable AP now! +I (89602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (89609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (89831) BME688: RTC is uninitialized, using current time as timestamp +I (90102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (90109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (90198) SEN66: RTC is uninitialized, using current time as timestamp +W (90482) wifi:Haven't to connect to a suitable AP now! +I (90602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (90609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +I (90824) OTA_CLIENT: Device is not registered, attempting to register... +I (90826) OTA_CLIENT: HTTP client handle: 0x3fcf03a8 +W (90831) BME688: RTC is uninitialized, using current time as timestamp +I (91102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (91109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (91198) SEN66: RTC is uninitialized, using current time as timestamp +W (91483) wifi:Haven't to connect to a suitable AP now! +I (91602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (91609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (91831) BME688: RTC is uninitialized, using current time as timestamp +I (92102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (92109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (92198) SEN66: RTC is uninitialized, using current time as timestamp +W (92484) wifi:Haven't to connect to a suitable AP now! +I (92602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (92609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (92831) BME688: RTC is uninitialized, using current time as timestamp +I (93102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (93109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (93198) SEN66: RTC is uninitialized, using current time as timestamp +W (93485) wifi:Haven't to connect to a suitable AP now! +I (93602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (93609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (93831) BME688: RTC is uninitialized, using current time as timestamp +I (94102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (94109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (94198) SEN66: RTC is uninitialized, using current time as timestamp +W (94486) wifi:Haven't to connect to a suitable AP now! +I (94602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (94609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (94831) BME688: RTC is uninitialized, using current time as timestamp +I (95102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (95109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (95198) SEN66: RTC is uninitialized, using current time as timestamp +W (95487) wifi:Haven't to connect to a suitable AP now! +I (95602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (95609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (95831) BME688: RTC is uninitialized, using current time as timestamp +I (96102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (96109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (96198) SEN66: RTC is uninitialized, using current time as timestamp +W (96488) wifi:Haven't to connect to a suitable AP now! +I (96602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (96609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (96831) BME688: RTC is uninitialized, using current time as timestamp +I (97102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (97109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (97198) SEN66: RTC is uninitialized, using current time as timestamp +W (97489) wifi:Haven't to connect to a suitable AP now! +I (97602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (97609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +E (97827) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (97827) esp-tls: Failed to open new connection +E (97829) transport_base: Failed to open a new connection +W (97831) BME688: RTC is uninitialized, using current time as timestamp +E (97835) HTTP_CLIENT: Connection failed, sock < 0 +E (97845) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 +E (97854) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT +I (98102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (98109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (98198) SEN66: RTC is uninitialized, using current time as timestamp +W (98490) wifi:Haven't to connect to a suitable AP now! +I (98602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (98609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (98841) BME688: RTC is uninitialized, using current time as timestamp +I (99102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (99109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (99198) SEN66: RTC is uninitialized, using current time as timestamp +W (99491) wifi:Haven't to connect to a suitable AP now! +I (99602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (99609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (99841) BME688: RTC is uninitialized, using current time as timestamp +I (100102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (100109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (100198) SEN66: RTC is uninitialized, using current time as timestamp +W (100492) wifi:Haven't to connect to a suitable AP now! +I (100602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (100609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (100841) BME688: RTC is uninitialized, using current time as timestamp +I (101102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (101109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (101198) SEN66: RTC is uninitialized, using current time as timestamp +W (101493) wifi:Haven't to connect to a suitable AP now! +I (101602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (101609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (101841) BME688: RTC is uninitialized, using current time as timestamp +I (102102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (102109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (102198) SEN66: RTC is uninitialized, using current time as timestamp +W (102494) wifi:Haven't to connect to a suitable AP now! +I (102602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (102609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (102841) BME688: RTC is uninitialized, using current time as timestamp +I (103102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (103109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (103198) SEN66: RTC is uninitialized, using current time as timestamp +W (103495) wifi:Haven't to connect to a suitable AP now! +I (103602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (103609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (103841) BME688: RTC is uninitialized, using current time as timestamp +I (104102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (104109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (104198) SEN66: RTC is uninitialized, using current time as timestamp +W (104496) wifi:Haven't to connect to a suitable AP now! +I (104602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (104609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (104841) BME688: RTC is uninitialized, using current time as timestamp +I (105102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (105109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (105198) SEN66: RTC is uninitialized, using current time as timestamp +W (105497) wifi:Haven't to connect to a suitable AP now! +I (105602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (105609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (105841) BME688: RTC is uninitialized, using current time as timestamp +I (106102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (106109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (106198) SEN66: RTC is uninitialized, using current time as timestamp +W (106498) wifi:Haven't to connect to a suitable AP now! +I (106602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (106609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (106841) BME688: RTC is uninitialized, using current time as timestamp +I (107102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (107109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (107198) SEN66: RTC is uninitialized, using current time as timestamp +W (107499) wifi:Haven't to connect to a suitable AP now! +I (107602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (107609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (107841) BME688: RTC is uninitialized, using current time as timestamp +I (107861) OTA_CLIENT: Device is not registered, attempting to register... +I (107863) OTA_CLIENT: HTTP client handle: 0x3fcf03a8 +I (108102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (108109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (108198) SEN66: RTC is uninitialized, using current time as timestamp +W (108500) wifi:Haven't to connect to a suitable AP now! +I (108602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (108609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (108841) BME688: RTC is uninitialized, using current time as timestamp +I (109102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (109109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (109198) SEN66: RTC is uninitialized, using current time as timestamp +W (109501) wifi:Haven't to connect to a suitable AP now! +I (109602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (109609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (109841) BME688: RTC is uninitialized, using current time as timestamp +I (110102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (110109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (110198) SEN66: RTC is uninitialized, using current time as timestamp +W (110502) wifi:Haven't to connect to a suitable AP now! +I (110602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (110609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (110841) BME688: RTC is uninitialized, using current time as timestamp +I (111102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (111109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (111198) SEN66: RTC is uninitialized, using current time as timestamp +W (111503) wifi:Haven't to connect to a suitable AP now! +I (111602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (111609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (111841) BME688: RTC is uninitialized, using current time as timestamp +I (112102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (112109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (112198) SEN66: RTC is uninitialized, using current time as timestamp +W (112504) wifi:Haven't to connect to a suitable AP now! +I (112602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (112609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (112841) BME688: RTC is uninitialized, using current time as timestamp +I (113102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (113109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (113198) SEN66: RTC is uninitialized, using current time as timestamp +W (113505) wifi:Haven't to connect to a suitable AP now! +I (113602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (113609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (113841) BME688: RTC is uninitialized, using current time as timestamp +I (114102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (114109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (114198) SEN66: RTC is uninitialized, using current time as timestamp +W (114506) wifi:Haven't to connect to a suitable AP now! +I (114602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (114609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (114841) BME688: RTC is uninitialized, using current time as timestamp +E (114864) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (114864) esp-tls: Failed to open new connection +E (114867) transport_base: Failed to open a new connection +E (114872) HTTP_CLIENT: Connection failed, sock < 0 +E (114876) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:228 +E (114885) OTA_CLIENT: [E-OTA-12] Device registration failed: ESP_ERR_HTTP_CONNECT +I (115102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (115109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (115198) SEN66: RTC is uninitialized, using current time as timestamp +W (115507) wifi:Haven't to connect to a suitable AP now! +I (115602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (115609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (115841) BME688: RTC is uninitialized, using current time as timestamp +I (116102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (116109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (116198) SEN66: RTC is uninitialized, using current time as timestamp +W (116508) wifi:Haven't to connect to a suitable AP now! +I (116602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (116609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (116841) BME688: RTC is uninitialized, using current time as timestamp +I (117102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (117109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (117198) SEN66: RTC is uninitialized, using current time as timestamp +W (117509) wifi:Haven't to connect to a suitable AP now! +I (117602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (117609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (117841) BME688: RTC is uninitialized, using current time as timestamp +I (118102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (118109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (118198) SEN66: RTC is uninitialized, using current time as timestamp +W (118510) wifi:Haven't to connect to a suitable AP now! +I (118602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (118609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (118841) BME688: RTC is uninitialized, using current time as timestamp +I (119102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (119109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (119198) SEN66: RTC is uninitialized, using current time as timestamp +W (119511) wifi:Haven't to connect to a suitable AP now! +I (119602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (119609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +I (119817) INFLUX_BUFFER: Flushing chunk starting at line 10: +I (119818) INFLUX: Writing to InfluxDB +W (119841) BME688: RTC is uninitialized, using current time as timestamp +I (120102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (120109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (120198) SEN66: RTC is uninitialized, using current time as timestamp +W (120512) wifi:Haven't to connect to a suitable AP now! +I (120602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (120609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (120841) BME688: RTC is uninitialized, using current time as timestamp +I (121102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (121109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (121198) SEN66: RTC is uninitialized, using current time as timestamp +W (121513) wifi:Haven't to connect to a suitable AP now! +I (121602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (121609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (121841) BME688: RTC is uninitialized, using current time as timestamp +I (122102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (122109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (122198) SEN66: RTC is uninitialized, using current time as timestamp +W (122514) wifi:Haven't to connect to a suitable AP now! +I (122602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (122609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (122841) BME688: RTC is uninitialized, using current time as timestamp +W (122935) wifi:Haven't to connect to a suitable AP now! +I (122935) KOTA: health up=121s status=CONNECTION_ERROR wifi=disconnected rssi=0 time_valid=0 sd=ok buffer=764 heap=43167 psram=7.3M last_upload=never ota=unregistered +I (123102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (123109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (123198) SEN66: RTC is uninitialized, using current time as timestamp +W (123515) wifi:Haven't to connect to a suitable AP now! +I (123602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (123609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (123841) BME688: RTC is uninitialized, using current time as timestamp +I (124102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (124109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (124198) SEN66: RTC is uninitialized, using current time as timestamp +W (124516) wifi:Haven't to connect to a suitable AP now! +I (124602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (124609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (124841) BME688: RTC is uninitialized, using current time as timestamp +I (124892) OTA_CLIENT: Device is not registered, attempting to register... +I (124894) OTA_CLIENT: HTTP client handle: 0x3fcf1210 +I (125102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (125109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (125198) SEN66: RTC is uninitialized, using current time as timestamp +W (125517) wifi:Haven't to connect to a suitable AP now! +I (125602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (125609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (125841) BME688: RTC is uninitialized, using current time as timestamp +I (126102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (126109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (126198) SEN66: RTC is uninitialized, using current time as timestamp +W (126518) wifi:Haven't to connect to a suitable AP now! +I (126602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (126609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +E (126819) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (126820) esp-tls: Failed to open new connection +E (126822) transport_base: Failed to open a new connection +E (126828) HTTP_CLIENT: Connection failed, sock < 0 +I (126832) INFLUX: HTTP request performed, status code: 0 +E (126837) INFLUX: [E-INF-03] Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (126845) INFLUX_BUFFER: [E-INF-17] Failed to write chunk to Influx: ESP_FAIL +W (126841) BME688: RTC is uninitialized, using current time as timestamp +I (127102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (127109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (127198) SEN66: RTC is uninitialized, using current time as timestamp +W (127519) wifi:Haven't to connect to a suitable AP now! +I (127602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (127609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (127858) BME688: RTC is uninitialized, using current time as timestamp +I (128102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (128109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (128198) SEN66: RTC is uninitialized, using current time as timestamp +W (128520) wifi:Haven't to connect to a suitable AP now! +I (128602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (128609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +I (128852) INFLUX: Writing to InfluxDB +W (128858) BME688: RTC is uninitialized, using current time as timestamp +I (129102) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (129109) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (129198) SEN66: RTC is uninitialized, using current time as timestamp +W (129521) wifi:Haven't to connect to a suitable AP now! +I (129602) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (129609) SENSOR_GATHERER: 2/4 tasks still running, waiting... +W (129858) BME688: RTC is uninitialized, using current time as timestamp --- disconnected --- \ No newline at end of file diff --git a/tests/fixtures/live_brownout_loop_A4CB8FC946F8_fw1.0.txt b/tests/fixtures/live_brownout_loop_A4CB8FC946F8_fw1.0.txt index bdff502..0fc1d3a 100644 --- a/tests/fixtures/live_brownout_loop_A4CB8FC946F8_fw1.0.txt +++ b/tests/fixtures/live_brownout_loop_A4CB8FC946F8_fw1.0.txt @@ -1,1936 +1,1936 @@ ---- connected to COM5 --- -I (16926) SENSOR_GATHERER: Sensor should now be asleep ---- reset issued by tool --- -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0x15 (USB_UART_CHIP_RESET),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x4037f34a -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (25) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (28) boot: efuse block revision: v1.4 -I (32) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (43) boot: Enabling RNG early entropy source... -I (48) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (63) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (76) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (89) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (102) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (109) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map -I (191) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load -I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load -I (203) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map -I (469) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load -I (485) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load -I (495) boot: Loaded app from partition at offset 0x40000 -I (495) boot: Disabling RNG early entropy source... -I (505) octal_psram: vendor id : 0x0d (AP) -I (505) octal_psram: dev id : 0x02 (generation 3) -I (506) octal_psram: density : 0x03 (64 Mbit) -I (508) octal_psram: good-die : 0x01 (Pass) -I (512) octal_psram: Latency : 0x01 (Fixed) -I (516) octal_psram: VCC : 0x01 (3V) -I (520) octal_psram: SRF : 0x01 (Fast Refresh) -I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (530) octal_psram: BurstLen : 0x01 (32 Byte) -I (535) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (540) octal_psram: DriveStrength: 0x00 (1/1) -I (544) esp_psram: Found 8MB PSRAM device -I (548) esp_psram: Speed: 40MHz -I (551) cpu_start: Multicore app -I (1285) esp_psram: SPI SRAM memory test OK -I (1293) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1293) cpu_start: Pro cpu start user code -I (1294) cpu_start: cpu freq: 160000000 Hz -I (1295) app_init: Application information: -I (1299) app_init: Project name: kota-sensorboard -I (1304) app_init: App version: 1.0 -I (1308) app_init: Compile time: Jun 29 2026 11:03:32 -I (1313) app_init: ELF file SHA256: 45fa9d5b9... -I (1317) app_init: ESP-IDF: v5.5.3-dirty -I (1322) efuse_init: Min chip rev: v0.0 -I (1326) efuse_init: Max chip rev: v0.99 -I (1330) efuse_init: Chip rev: v0.2 -I (1334) heap_init: Initializing. RAM available for dynamic allocation: -I (1340) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM -I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1356) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1369) spi_flash: detected chip: generic -I (1371) spi_flash: flash io: dio -I (1377) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1397) main_task: Started on CPU0 -I (1400) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1408) main_task: Calling app_main() -E (1411) ID: Device MAC address: A4:CB:8F:C9:46:F8 -I (1436) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1441) MAIN: Matter node created successfully -W (1442) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1445) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1454) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1463) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1472) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1480) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1489) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1498) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1507) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1515) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1524) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1533) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1542) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1551) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1559) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1568) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1577) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1586) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1596) pp: pp rom version: e7ae62f -I (1598) net80211: net80211 rom version: e7ae62f -I (1603) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 -I (1621) wifi:wifi firmware version: 4df78f2 -I (1621) wifi:wifi certification version: v7.0 -I (1621) wifi:config NVS flash: enabled -I (1622) wifi:config nano formatting: disabled -I (1626) wifi:Init data frame dynamic rx buffer num: 32 -I (1631) wifi:Init static rx mgmt buffer num: 5 -I (1635) wifi:Init management short buffer num: 32 -I (1639) wifi:Init dynamic tx buffer num: 32 -I (1644) wifi:Init static tx FG buffer num: 2 -I (1648) wifi:Init static rx buffer size: 1600 -I (1652) wifi:Init static rx buffer num: 10 -I (1656) wifi:Init dynamic rx buffer num: 32 -I (1660) wifi_init: rx ba win: 6 -I (1663) wifi_init: accept mbox: 6 -I (1666) wifi_init: tcpip mbox: 32 -I (1669) wifi_init: udp mbox: 6 -I (1672) wifi_init: tcp mbox: 6 -I (1675) wifi_init: tcp tx win: 5760 -I (1678) wifi_init: tcp rx win: 5760 -I (1681) wifi_init: tcp mss: 1440 -I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15834 (0x3DDA) -I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (1698) BLE_INIT: BT controller compile version [1bb2f50] -I (1703) BLE_INIT: Using main XTAL as clock source -I (1707) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (1716) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (1721) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -E BOD: Brownout detector was triggered - - -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x40376756 -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (24) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (27) boot: efuse block revision: v1.4 -I (31) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (42) boot: Enabling RNG early entropy source... -I (47) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (108) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map -I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load -I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load -I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map -I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load -I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load -I (494) boot: Loaded app from partition at offset 0x40000 -I (495) boot: Disabling RNG early entropy source... -I (505) octal_psram: vendor id : 0x0d (AP) -I (505) octal_psram: dev id : 0x02 (generation 3) -I (505) octal_psram: density : 0x03 (64 Mbit) -I (507) octal_psram: good-die : 0x01 (Pass) -I (511) octal_psram: Latency : 0x01 (Fixed) -I (516) octal_psram: VCC : 0x01 (3V) -I (520) octal_psram: SRF : 0x01 (Fast Refresh) -I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (530) octal_psram: BurstLen : 0x01 (32 Byte) -I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (539) octal_psram: DriveStrength: 0x00 (1/1) -I (544) esp_psram: Found 8MB PSRAM device -I (547) esp_psram: Speed: 40MHz -I (550) cpu_start: Multicore app -I (1284) esp_psram: SPI SRAM memory test OK -I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1293) cpu_start: Pro cpu start user code -I (1293) cpu_start: cpu freq: 160000000 Hz -I (1295) app_init: Application information: -I (1299) app_init: Project name: kota-sensorboard -I (1304) app_init: App version: 1.0 -I (1307) app_init: Compile time: Jun 29 2026 11:03:32 -I (1312) app_init: ELF file SHA256: 45fa9d5b9... -I (1317) app_init: ESP-IDF: v5.5.3-dirty -I (1321) efuse_init: Min chip rev: v0.0 -I (1325) efuse_init: Max chip rev: v0.99 -I (1329) efuse_init: Chip rev: v0.2 -I (1333) heap_init: Initializing. RAM available for dynamic allocation: -I (1340) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM -I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1368) spi_flash: detected chip: generic -I (1371) spi_flash: flash io: dio -I (1377) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1396) main_task: Started on CPU0 -I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1407) main_task: Calling app_main() -E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 -I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1439) MAIN: Matter node created successfully -W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1595) pp: pp rom version: e7ae62f -I (1597) net80211: net80211 rom version: e7ae62f -I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 -I (1619) wifi:wifi firmware version: 4df78f2 -I (1620) wifi:wifi certification version: v7.0 -I (1620) wifi:config NVS flash: enabled -I (1621) wifi:config nano formatting: disabled -I (1625) wifi:Init data frame dynamic rx buffer num: 32 -I (1630) wifi:Init static rx mgmt buffer num: 5 -I (1634) wifi:Init management short buffer num: 32 -I (1638) wifi:Init dynamic tx buffer num: 32 -I (1642) wifi:Init static tx FG buffer num: 2 -I (1647) wifi:Init static rx buffer size: 1600 -I (1651) wifi:Init static rx buffer num: 10 -I (1655) wifi:Init dynamic rx buffer num: 32 -I (1659) wifi_init: rx ba win: 6 -I (1661) wifi_init: accept mbox: 6 -I (1665) wifi_init: tcpip mbox: 32 -I (1668) wifi_init: udp mbox: 6 -I (1671) wifi_init: tcp mbox: 6 -I (1673) wifi_init: tcp tx win: 5760 -I (1677) wifi_init: tcp rx win: 5760 -I (1680) wifi_init: tcp mss: 1440 -I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15835 (0x3DDB) -I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (1697) BLE_INIT: BT controller compile version [1bb2f50] -I (1702) BLE_INIT: Using main XTAL as clock source -I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -E BOD: Brownout detector was triggered - - -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x40376756 -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (24) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (27) boot: efuse block revision: v1.4 -I (31) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (42) boot: Enabling RNG early entropy source... -I (47) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (108) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map -I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load -I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load -I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map -I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load -I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load -I (494) boot: Loaded app from partition at offset 0x40000 -I (494) boot: Disabling RNG early entropy source... -I (505) octal_psram: vendor id : 0x0d (AP) -I (505) octal_psram: dev id : 0x02 (generation 3) -I (505) octal_psram: density : 0x03 (64 Mbit) -I (507) octal_psram: good-die : 0x01 (Pass) -I (511) octal_psram: Latency : 0x01 (Fixed) -I (516) octal_psram: VCC : 0x01 (3V) -I (520) octal_psram: SRF : 0x01 (Fast Refresh) -I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (530) octal_psram: BurstLen : 0x01 (32 Byte) -I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (539) octal_psram: DriveStrength: 0x00 (1/1) -I (543) esp_psram: Found 8MB PSRAM device -I (547) esp_psram: Speed: 40MHz -I (550) cpu_start: Multicore app -I (1284) esp_psram: SPI SRAM memory test OK -I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1293) cpu_start: Pro cpu start user code -I (1293) cpu_start: cpu freq: 160000000 Hz -I (1295) app_init: Application information: -I (1299) app_init: Project name: kota-sensorboard -I (1303) app_init: App version: 1.0 -I (1307) app_init: Compile time: Jun 29 2026 11:03:32 -I (1312) app_init: ELF file SHA256: 45fa9d5b9... -I (1317) app_init: ESP-IDF: v5.5.3-dirty -I (1321) efuse_init: Min chip rev: v0.0 -I (1325) efuse_init: Max chip rev: v0.99 -I (1329) efuse_init: Chip rev: v0.2 -I (1333) heap_init: Initializing. RAM available for dynamic allocation: -I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM -I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1368) spi_flash: detected chip: generic -I (1371) spi_flash: flash io: dio -I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1396) main_task: Started on CPU0 -I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1407) main_task: Calling app_main() -E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 -I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1439) MAIN: Matter node created successfully -W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1550) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1595) pp: pp rom version: e7ae62f -I (1597) net80211: net80211 rom version: e7ae62f -I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 -I (1619) wifi:wifi firmware version: 4df78f2 -I (1620) wifi:wifi certification version: v7.0 -I (1620) wifi:config NVS flash: enabled -I (1621) wifi:config nano formatting: disabled -I (1625) wifi:Init data frame dynamic rx buffer num: 32 -I (1630) wifi:Init static rx mgmt buffer num: 5 -I (1634) wifi:Init management short buffer num: 32 -I (1638) wifi:Init dynamic tx buffer num: 32 -I (1642) wifi:Init static tx FG buffer num: 2 -I (1647) wifi:Init static rx buffer size: 1600 -I (1651) wifi:Init static rx buffer num: 10 -I (1655) wifi:Init dynamic rx buffer num: 32 -I (1659) wifi_init: rx ba win: 6 -I (1661) wifi_init: accept mbox: 6 -I (1665) wifi_init: tcpip mbox: 32 -I (1668) wifi_init: udp mbox: 6 -I (1671) wifi_init: tcp mbox: 6 -I (1673) wifi_init: tcp tx win: 5760 -I (1677) wifi_init: tcp rx win: 5760 -I (1680) wifi_init: tcp mss: 1440 -I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15836 (0x3DDC) -I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (1697) BLE_INIT: BT controller compile version [1bb2f50] -I (1702) BLE_INIT: Using main XTAL as clock source -I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -E BOD: Brownout detector was triggered - - -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x40376756 -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (24) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (27) boot: efuse block revision: v1.4 -I (31) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (42) boot: Enabling RNG early entropy source... -I (47) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (108) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map -I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load -I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load -I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map -I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load -I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load -I (494) boot: Loaded app from partition at offset 0x40000 -I (494) boot: Disabling RNG early entropy source... -I (505) octal_psram: vendor id : 0x0d (AP) -I (505) octal_psram: dev id : 0x02 (generation 3) -I (505) octal_psram: density : 0x03 (64 Mbit) -I (507) octal_psram: good-die : 0x01 (Pass) -I (511) octal_psram: Latency : 0x01 (Fixed) -I (516) octal_psram: VCC : 0x01 (3V) -I (520) octal_psram: SRF : 0x01 (Fast Refresh) -I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (530) octal_psram: BurstLen : 0x01 (32 Byte) -I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (539) octal_psram: DriveStrength: 0x00 (1/1) -I (543) esp_psram: Found 8MB PSRAM device -I (547) esp_psram: Speed: 40MHz -I (550) cpu_start: Multicore app -I (1284) esp_psram: SPI SRAM memory test OK -I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1293) cpu_start: Pro cpu start user code -I (1293) cpu_start: cpu freq: 160000000 Hz -I (1295) app_init: Application information: -I (1299) app_init: Project name: kota-sensorboard -I (1303) app_init: App version: 1.0 -I (1307) app_init: Compile time: Jun 29 2026 11:03:32 -I (1312) app_init: ELF file SHA256: 45fa9d5b9... -I (1317) app_init: ESP-IDF: v5.5.3-dirty -I (1321) efuse_init: Min chip rev: v0.0 -I (1325) efuse_init: Max chip rev: v0.99 -I (1329) efuse_init: Chip rev: v0.2 -I (1333) heap_init: Initializing. RAM available for dynamic allocation: -I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM -I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1368) spi_flash: detected chip: generic -I (1371) spi_flash: flash io: dio -I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1396) main_task: Started on CPU0 -I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1407) main_task: Calling app_main() -E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 -I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1439) MAIN: Matter node created successfully -W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1595) pp: pp rom version: e7ae62f -I (1597) net80211: net80211 rom version: e7ae62f -I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 -I (1619) wifi:wifi firmware version: 4df78f2 -I (1620) wifi:wifi certification version: v7.0 -I (1620) wifi:config NVS flash: enabled -I (1621) wifi:config nano formatting: disabled -I (1625) wifi:Init data frame dynamic rx buffer num: 32 -I (1630) wifi:Init static rx mgmt buffer num: 5 -I (1634) wifi:Init management short buffer num: 32 -I (1638) wifi:Init dynamic tx buffer num: 32 -I (1642) wifi:Init static tx FG buffer num: 2 -I (1647) wifi:Init static rx buffer size: 1600 -I (1651) wifi:Init static rx buffer num: 10 -I (1655) wifi:Init dynamic rx buffer num: 32 -I (1659) wifi_init: rx ba win: 6 -I (1662) wifi_init: accept mbox: 6 -I (1665) wifi_init: tcpip mbox: 32 -I (1668) wifi_init: udp mbox: 6 -I (1671) wifi_init: tcp mbox: 6 -I (1673) wifi_init: tcp tx win: 5760 -I (1677) wifi_init: tcp rx win: 5760 -I (1680) wifi_init: tcp mss: 1440 -I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15837 (0x3DDD) -I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (1697) BLE_INIT: BT controller compile version [1bb2f50] -I (1702) BLE_INIT: Using main XTAL as clock source -I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -E BOD: Brownout detector was triggered - - -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x40376756 -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (24) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (27) boot: efuse block revision: v1.4 -I (31) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (42) boot: Enabling RNG early entropy source... -I (47) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (108) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map -I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load -I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load -I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map -I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load -I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load -I (494) boot: Loaded app from partition at offset 0x40000 -I (494) boot: Disabling RNG early entropy source... -I (505) octal_psram: vendor id : 0x0d (AP) -I (505) octal_psram: dev id : 0x02 (generation 3) -I (505) octal_psram: density : 0x03 (64 Mbit) -I (507) octal_psram: good-die : 0x01 (Pass) -I (511) octal_psram: Latency : 0x01 (Fixed) -I (516) octal_psram: VCC : 0x01 (3V) -I (520) octal_psram: SRF : 0x01 (Fast Refresh) -I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (530) octal_psram: BurstLen : 0x01 (32 Byte) -I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (539) octal_psram: DriveStrength: 0x00 (1/1) -I (543) esp_psram: Found 8MB PSRAM device -I (547) esp_psram: Speed: 40MHz -I (550) cpu_start: Multicore app -I (1284) esp_psram: SPI SRAM memory test OK -I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1293) cpu_start: Pro cpu start user code -I (1293) cpu_start: cpu freq: 160000000 Hz -I (1295) app_init: Application information: -I (1299) app_init: Project name: kota-sensorboard -I (1303) app_init: App version: 1.0 -I (1307) app_init: Compile time: Jun 29 2026 11:03:32 -I (1312) app_init: ELF file SHA256: 45fa9d5b9... -I (1317) app_init: ESP-IDF: v5.5.3-dirty -I (1321) efuse_init: Min chip rev: v0.0 -I (1325) efuse_init: Max chip rev: v0.99 -I (1329) efuse_init: Chip rev: v0.2 -I (1333) heap_init: Initializing. RAM available for dynamic allocation: -I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM -I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1368) spi_flash: detected chip: generic -I (1371) spi_flash: flash io: dio -I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1396) main_task: Started on CPU0 -I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1407) main_task: Calling app_main() -E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 -I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1439) MAIN: Matter node created successfully -W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1595) pp: pp rom version: e7ae62f -I (1597) net80211: net80211 rom version: e7ae62f -I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 -I (1619) wifi:wifi firmware version: 4df78f2 -I (1620) wifi:wifi certification version: v7.0 -I (1620) wifi:config NVS flash: enabled -I (1621) wifi:config nano formatting: disabled -I (1625) wifi:Init data frame dynamic rx buffer num: 32 -I (1630) wifi:Init static rx mgmt buffer num: 5 -I (1634) wifi:Init management short buffer num: 32 -I (1638) wifi:Init dynamic tx buffer num: 32 -I (1642) wifi:Init static tx FG buffer num: 2 -I (1647) wifi:Init static rx buffer size: 1600 -I (1651) wifi:Init static rx buffer num: 10 -I (1655) wifi:Init dynamic rx buffer num: 32 -I (1659) wifi_init: rx ba win: 6 -I (1661) wifi_init: accept mbox: 6 -I (1665) wifi_init: tcpip mbox: 32 -I (1668) wifi_init: udp mbox: 6 -I (1671) wifi_init: tcp mbox: 6 -I (1673) wifi_init: tcp tx win: 5760 -I (1677) wifi_init: tcp rx win: 5760 -I (1680) wifi_init: tcp mss: 1440 -I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15838 (0x3DDE) -I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (1697) BLE_INIT: BT controller compile version [1bb2f50] -I (1702) BLE_INIT: Using main XTAL as clock source -I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -I (1768) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(0) -I (1785) NimBLE: GAP procedure initiated: stop advertising. - -I (1787) CHIP[DL]: BLE host-controller synced -I (2287) chip[DL]: Starting ESP WiFi layer -I (2289) wifi:mode : sta (a4:cb:8f:c9:46:f8) -I (2290) wifi:enable tsf -W (2290) wifi:Haven't to connect to a suitable AP now! -I (2291) chip[DL]: -I (2291) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43 -Done driving station state, nothing else to do... -I (2300) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 -W (2304) wifi:Haven't to connect to a suitable AP now! -I (2315) chip[DL]: Done driving station state, nothing else to do... -I (2321) chip[SVR]: Initializing subscription resumption storage... -I (2328) chip[SVR]: Server initializing... -I (2331) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06 -I (2337) chip[DMG]: AccessControl: initializing -I (2340) chip[DMG]: Examples::AccessControlDelegate::Init -I (2345) chip[DMG]: AccessControl: setting -I (2349) chip[DMG]: DefaultAclStorage: initializing -I (2354) chip[DMG]: DefaultAclStorage: 0 entries loaded -I (2359) chip[SVR]: WARNING: mTestEventTriggerDelegate is null -I (2378) chip[ZCL]: Using ZAP configuration... -I (2378) esp_matter_cluster: Cluster plugin init common callback -I (2379) chip[DMG]: AccessControlCluster: initializing -I (2382) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported -I (2392) chip[DIS]: Updating services using commissioning mode 1 -I (2396) chip[DIS]: CHIP minimal mDNS started advertising. -I (2401) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 -I (2411) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 37BE0E5B446CAB54. -I (2421) chip[DIS]: mDNS service published: _matterc._udp -I (2426) chip[IN]: CASE Server enabling CASE session setups -I (2432) chip[SVR]: Joining Multicast groups -I (2435) chip[SVR]: Server Listening... -I (2440) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** -I (2450) data_model: Dynamic endpoint 0 added -E (2453) data_model: Cluster cannot be NULL. -I (2458) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** -I (2468) data_model: Dynamic endpoint 1 added -E (2472) data_model: Cluster cannot be NULL. -I (2477) chip[DL]: WIFI_EVENT_STA_START -I (2478) MAIN: ESP-Matter started successfully -W (2480) wifi:Haven't to connect to a suitable AP now! -I (2489) chip[DL]: Done driving station state, nothing else to do... -I (2496) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) -I (2506) NimBLE: GAP procedure initiated: advertise; -I (2506) NimBLE: disc_mode=2 - -I (2507) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 -I (2511) NimBLE: - -I (2513) chip[DL]: CHIPoBLE advertising started -I (2514) MAIN: Commissioning window opened -> -I (4060) I2C: Creating I2C mutex -I (14061) SPI: Creating SPI mutex -I (14103) INFLUX_BUFFER: SD card mounted successfully -I (14104) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully -I (14107) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota -W (14121) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota -I (14145) BME688: Delaying for 10000 microseconds -I (14763) Flicker: Flicker sensor initialized on pin 14 -I (14765) temperature_sensor: Range [20°C ~ 100°C], error < 2°C -I (14767) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data -I (14767) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data -W (14772) wifi:Haven't to connect to a suitable AP now! -I (14768) SEN66: Creating sensor mutex -W (14782) AS7341: Reading spectral data from AS7341 -I (14775) INFLUX_BUFFER: Flushing chunk starting at line 10: -I (14796) INFLUX: Writing to InfluxDB -I (14784) SEN66: Waking up sensor by setting power GPIO to enabled level... -I (14783) DeviceStatus: Active tasks: 26 -I (14813) DeviceStatus: Task list: -gather_sensor_t X 1 1216 22 -IDLE1 R 0 812 6 -IDLE0 R 0 692 5 -gather_sensor_t B 1 2552 21 -ds1307_update B 1 800 17 -gather_task B 1 1756 19 -gather_task B 1 14032 20 -alive_diode_tas B 5 352 8 -reset_task B 0 3432 15 -tiT B 18 2420 10 -gather_sensor_t B 1 1688 24 -ota_task B 1 7516 18 -CHIP B 1 3700 12 -Tmr Svc B 1 1364 7 -gather_sensor_t B 1 1092 25 -sys_evt B 20 2684 9 -ipc0 S 24 372 1 -ipc1 S 24 524 2 -console B 5 648 16 -esp_timer S 22 3332 3 -gather_sensor_t B 1 2284 23 -gather_sensor_t B 1 2544 26 -wifi B 23 4276 11 -btController B 23 1960 13 -main B 1 876 4 -nimble_host B 21 1480 14 - -W (14878) DeviceStatus: RTC is uninitialized, using current time as timestamp -W (15105) AS7341: Reading basic counts data from AS7341 -W (15121) AS7341: Reading flicker detection state from AS7341 -W (15339) as7341: FD Status Register: 0x20 (0b00100000) -I (15345) AS7341: Updating Matter attributes with AS7341 data: 16909 -I (15345) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms -I (15359) Flicker: Starting to collect samples in gather task -I (15355) BME688: Delaying for 199517 microseconds -I (15347) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** -I (15374) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 16909 ********** -E (15386) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 -E (15398) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 -W (15400) AS7341: Formatting AS7341 data in line protocol -AS7341 line protocol: -as7341_adc,device=A4CB8FC946F8 f1=1,f2=2,f3=2,f4=3,f5=3,f6=4,f7=7,f8=9,clear=22,nir=5 946684801000000000 -as7341_basic_counts,device=A4CB8FC946F8 f1=2.23,f2=4.46,f3=4.46,f4=6.69,f5=6.69,f6=8.92,f7=15.62,f8=20.08,clear=49.08,nir=11.15 946684801000000000 -as7341_flicker,device=A4CB8FC946F8 state="unknown" 946684801000000000 -W (15890) DeviceStatus: RTC is uninitialized, using current time as timestamp -I (16383) Flicker: Finished collecting samples, notifying main task -I (16385) Flicker: Collected 1024 samples for dominant frequency analysis -I (16392) SENSOR_GATHERER: Sensor should now be asleep -I (16404) SENSOR_GATHERER: Sensor should now be asleep -I (16444) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** -I (16446) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1018 ********** -E (16459) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 -E (16471) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 -W (16473) BME688: RTC is uninitialized, using current time as timestamp -I (16891) SENSOR_GATHERER: Sensor should now be asleep -W (17484) BME688: RTC is uninitialized, using current time as timestamp -W (18485) BME688: RTC is uninitialized, using current time as timestamp -W (19486) BME688: RTC is uninitialized, using current time as timestamp -I (19809) SEN66: Sent start measurement command, waiting for sensor to be ready... -I (19905) MICROPHONE: Read 300000 bytes in 4936 ms -I (19906) MICROPHONE: Read 300000 bytes of audio data -I (20098) MICROPHONE: Sum SQ: 0.049147, RMS pressure: 0.000701, Normalized: 0.000701, dB peak: -63.09 -W (20100) SEN66: RTC is uninitialized, using current time as timestamp -W (20487) BME688: RTC is uninitialized, using current time as timestamp -W (21112) SEN66: RTC is uninitialized, using current time as timestamp -W (21488) BME688: RTC is uninitialized, using current time as timestamp -E (21798) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (21800) esp-tls: Failed to open new connection -E (21801) transport_base: Failed to open a new connection -E (21812) HTTP_CLIENT: Connection failed, sock < 0 -I (21813) INFLUX: HTTP request performed, status code: 0 -E (21824) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT -E (21826) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL -W (22113) SEN66: RTC is uninitialized, using current time as timestamp -W (22489) BME688: RTC is uninitialized, using current time as timestamp -I (22769) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (23114) SEN66: RTC is uninitialized, using current time as timestamp -I (23270) SENSOR_GATHERER: 1/2 tasks still running, waiting... -E BOD: Brownout detector was triggered - - -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x40376756 -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (24) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (27) boot: efuse block revision: v1.4 -I (31) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (42) boot: Enabling RNG early entropy source... -I (47) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (108) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map -I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load -I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load -I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map -I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load -I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load -I (494) boot: Loaded app from partition at offset 0x40000 -I (494) boot: Disabling RNG early entropy source... -I (505) octal_psram: vendor id : 0x0d (AP) -I (505) octal_psram: dev id : 0x02 (generation 3) -I (505) octal_psram: density : 0x03 (64 Mbit) -I (507) octal_psram: good-die : 0x01 (Pass) -I (511) octal_psram: Latency : 0x01 (Fixed) -I (516) octal_psram: VCC : 0x01 (3V) -I (520) octal_psram: SRF : 0x01 (Fast Refresh) -I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (530) octal_psram: BurstLen : 0x01 (32 Byte) -I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (539) octal_psram: DriveStrength: 0x00 (1/1) -I (543) esp_psram: Found 8MB PSRAM device -I (547) esp_psram: Speed: 40MHz -I (550) cpu_start: Multicore app -I (1284) esp_psram: SPI SRAM memory test OK -I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1293) cpu_start: Pro cpu start user code -I (1293) cpu_start: cpu freq: 160000000 Hz -I (1295) app_init: Application information: -I (1299) app_init: Project name: kota-sensorboard -I (1303) app_init: App version: 1.0 -I (1307) app_init: Compile time: Jun 29 2026 11:03:32 -I (1312) app_init: ELF file SHA256: 45fa9d5b9... -I (1317) app_init: ESP-IDF: v5.5.3-dirty -I (1321) efuse_init: Min chip rev: v0.0 -I (1325) efuse_init: Max chip rev: v0.99 -I (1329) efuse_init: Chip rev: v0.2 -I (1333) heap_init: Initializing. RAM available for dynamic allocation: -I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM -I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1368) spi_flash: detected chip: generic -I (1371) spi_flash: flash io: dio -I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1396) main_task: Started on CPU0 -I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1407) main_task: Calling app_main() -E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 -I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1439) MAIN: Matter node created successfully -W (1440) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1470) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1505) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1584) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1595) pp: pp rom version: e7ae62f -I (1596) net80211: net80211 rom version: e7ae62f -I (1602) wifi:wifi driver task: 3fcc524c, prio:23, stack:6656, core=0 -I (1619) wifi:wifi firmware version: 4df78f2 -I (1620) wifi:wifi certification version: v7.0 -I (1620) wifi:config NVS flash: enabled -I (1620) wifi:config nano formatting: disabled -I (1625) wifi:Init data frame dynamic rx buffer num: 32 -I (1630) wifi:Init static rx mgmt buffer num: 5 -I (1634) wifi:Init management short buffer num: 32 -I (1638) wifi:Init dynamic tx buffer num: 32 -I (1642) wifi:Init static tx FG buffer num: 2 -I (1646) wifi:Init static rx buffer size: 1600 -I (1651) wifi:Init static rx buffer num: 10 -I (1654) wifi:Init dynamic rx buffer num: 32 -I (1659) wifi_init: rx ba win: 6 -I (1661) wifi_init: accept mbox: 6 -I (1664) wifi_init: tcpip mbox: 32 -I (1667) wifi_init: udp mbox: 6 -I (1670) wifi_init: tcp mbox: 6 -I (1673) wifi_init: tcp tx win: 5760 -I (1677) wifi_init: tcp rx win: 5760 -I (1680) wifi_init: tcp mss: 1440 -I (1691) chip[DL]: NVS set: chip-counters/reboot-count = 15839 (0x3DDF) -I (1694) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (1697) BLE_INIT: BT controller compile version [1bb2f50] -I (1701) BLE_INIT: Using main XTAL as clock source -I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -E BOD: Brownout detector was triggered - - -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x40376756 -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (24) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (27) boot: efuse block revision: v1.4 -I (31) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (42) boot: Enabling RNG early entropy source... -I (47) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (108) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map -I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load -I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load -I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map -I (469) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load -I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load -I (494) boot: Loaded app from partition at offset 0x40000 -I (495) boot: Disabling RNG early entropy source... -I (505) octal_psram: vendor id : 0x0d (AP) -I (505) octal_psram: dev id : 0x02 (generation 3) -I (505) octal_psram: density : 0x03 (64 Mbit) -I (507) octal_psram: good-die : 0x01 (Pass) -I (511) octal_psram: Latency : 0x01 (Fixed) -I (516) octal_psram: VCC : 0x01 (3V) -I (520) octal_psram: SRF : 0x01 (Fast Refresh) -I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (530) octal_psram: BurstLen : 0x01 (32 Byte) -I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (539) octal_psram: DriveStrength: 0x00 (1/1) -I (544) esp_psram: Found 8MB PSRAM device -I (547) esp_psram: Speed: 40MHz -I (550) cpu_start: Multicore app -I (1284) esp_psram: SPI SRAM memory test OK -I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1293) cpu_start: Pro cpu start user code -I (1293) cpu_start: cpu freq: 160000000 Hz -I (1295) app_init: Application information: -I (1299) app_init: Project name: kota-sensorboard -I (1304) app_init: App version: 1.0 -I (1307) app_init: Compile time: Jun 29 2026 11:03:32 -I (1312) app_init: ELF file SHA256: 45fa9d5b9... -I (1317) app_init: ESP-IDF: v5.5.3-dirty -I (1321) efuse_init: Min chip rev: v0.0 -I (1325) efuse_init: Max chip rev: v0.99 -I (1329) efuse_init: Chip rev: v0.2 -I (1333) heap_init: Initializing. RAM available for dynamic allocation: -I (1340) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM -I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1368) spi_flash: detected chip: generic -I (1371) spi_flash: flash io: dio -I (1377) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1396) main_task: Started on CPU0 -I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1407) main_task: Calling app_main() -E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 -I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1439) MAIN: Matter node created successfully -W (1440) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1461) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1470) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1505) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1540) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1575) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1584) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1595) pp: pp rom version: e7ae62f -I (1596) net80211: net80211 rom version: e7ae62f -I (1602) wifi:wifi driver task: 3fcc524c, prio:23, stack:6656, core=0 -I (1619) wifi:wifi firmware version: 4df78f2 -I (1620) wifi:wifi certification version: v7.0 -I (1620) wifi:config NVS flash: enabled -I (1620) wifi:config nano formatting: disabled -I (1625) wifi:Init data frame dynamic rx buffer num: 32 -I (1630) wifi:Init static rx mgmt buffer num: 5 -I (1634) wifi:Init management short buffer num: 32 -I (1638) wifi:Init dynamic tx buffer num: 32 -I (1642) wifi:Init static tx FG buffer num: 2 -I (1646) wifi:Init static rx buffer size: 1600 -I (1650) wifi:Init static rx buffer num: 10 -I (1654) wifi:Init dynamic rx buffer num: 32 -I (1659) wifi_init: rx ba win: 6 -I (1661) wifi_init: accept mbox: 6 -I (1664) wifi_init: tcpip mbox: 32 -I (1667) wifi_init: udp mbox: 6 -I (1670) wifi_init: tcp mbox: 6 -I (1673) wifi_init: tcp tx win: 5760 -I (1676) wifi_init: tcp rx win: 5760 -I (1680) wifi_init: tcp mss: 1440 -I (1691) chip[DL]: NVS set: chip-counters/reboot-count = 15840 (0x3DE0) -I (1694) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (1696) BLE_INIT: BT controller compile version [1bb2f50] -I (1701) BLE_INIT: Using main XTAL as clock source -I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (1714) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -I (1767) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(0) -I (1804) NimBLE: GAP procedure initiated: stop advertising. - -I (1806) CHIP[DL]: BLE host-controller synced -I (2306) chip[DL]: Starting ESP WiFi layer -I (2308) wifi:mode : sta (a4:cb:8f:c9:46:f8) -I (2309) wifi:enable tsf -W (2309) wifi:Haven't to connect to a suitable AP now! -I (2310) chip[DL]: -I (2310) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43 -Done driving station state, nothing else to do... -I (2319) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 -W (2323) wifi:Haven't to connect to a suitable AP now! -I (2334) chip[DL]: Done driving station state, nothing else to do... -I (2340) chip[SVR]: Initializing subscription resumption storage... -I (2347) chip[SVR]: Server initializing... -I (2350) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06 -I (2356) chip[DMG]: AccessControl: initializing -I (2359) chip[DMG]: Examples::AccessControlDelegate::Init -I (2364) chip[DMG]: AccessControl: setting -I (2368) chip[DMG]: DefaultAclStorage: initializing -I (2373) chip[DMG]: DefaultAclStorage: 0 entries loaded -I (2378) chip[SVR]: WARNING: mTestEventTriggerDelegate is null -I (2397) chip[ZCL]: Using ZAP configuration... -I (2397) esp_matter_cluster: Cluster plugin init common callback -I (2398) chip[DMG]: AccessControlCluster: initializing -I (2401) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported -I (2411) chip[DIS]: Updating services using commissioning mode 1 -I (2415) chip[DIS]: CHIP minimal mDNS started advertising. -I (2420) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 -I (2430) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 67895F766C4DDEDA. -I (2440) chip[DIS]: mDNS service published: _matterc._udp -I (2445) chip[IN]: CASE Server enabling CASE session setups -I (2451) chip[SVR]: Joining Multicast groups -I (2454) chip[SVR]: Server Listening... -I (2459) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** -I (2469) data_model: Dynamic endpoint 0 added -E (2472) data_model: Cluster cannot be NULL. -I (2477) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** -I (2487) data_model: Dynamic endpoint 1 added -E (2491) data_model: Cluster cannot be NULL. -I (2496) chip[DL]: WIFI_EVENT_STA_START -I (2497) MAIN: ESP-Matter started successfully -W (2499) wifi:Haven't to connect to a suitable AP now! -I (2508) chip[DL]: Done driving station state, nothing else to do... -I (2515) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) -I (2525) NimBLE: GAP procedure initiated: advertise; -I (2525) NimBLE: disc_mode=2 - -I (2526) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 -I (2530) NimBLE: - -I (2532) chip[DL]: CHIPoBLE advertising started -I (2533) MAIN: Commissioning window opened -> -I (4079) I2C: Creating I2C mutex -I (14080) SPI: Creating SPI mutex -I (14122) INFLUX_BUFFER: SD card mounted successfully -I (14123) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully -I (14126) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota -W (14140) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota -I (14164) BME688: Delaying for 10000 microseconds -I (14782) Flicker: Flicker sensor initialized on pin 14 -I (14784) temperature_sensor: Range [20°C ~ 100°C], error < 2°C -I (14786) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data -W (14791) wifi:Haven't to connect to a suitable AP now! -I (14794) INFLUX_BUFFER: Flushing chunk starting at line 10: -I (14786) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data -W (14799) AS7341: Reading spectral data from AS7341 -I (14787) SEN66: Creating sensor mutex -I (14815) SEN66: Waking up sensor by setting power GPIO to enabled level... -I (14802) INFLUX: Writing to InfluxDB -I (14802) DeviceStatus: Active tasks: 26 -I (14832) DeviceStatus: Task list: -main R 1 876 4 -gather_sensor_t X 1 1352 24 -IDLE1 R 0 700 6 -IDLE0 R 0 692 5 -gather_sensor_t B 1 2544 21 -ds1307_update B 1 800 17 -gather_task B 1 1756 19 -gather_task B 1 14032 20 -alive_diode_tas B 5 356 8 -reset_task B 0 3432 15 -tiT B 18 2424 10 -gather_sensor_t B 1 1724 23 -ota_task B 1 7516 18 -CHIP B 1 3708 12 -Tmr Svc B 1 1364 7 -gather_sensor_t B 1 1300 25 -sys_evt B 20 2672 9 -ipc0 S 24 372 1 -ipc1 S 24 524 2 -console B 5 648 16 -esp_timer S 22 3332 3 -gather_sensor_t B 1 2332 22 -gather_sensor_t B 1 2544 26 -wifi B 23 4280 11 -btController B 23 1952 13 -nimble_host B 21 1480 14 - -W (14898) DeviceStatus: RTC is uninitialized, using current time as timestamp -W (15125) AS7341: Reading basic counts data from AS7341 -W (15141) AS7341: Reading flicker detection state from AS7341 -W (15359) as7341: FD Status Register: 0x20 (0b00100000) -I (15365) AS7341: Updating Matter attributes with AS7341 data: 16909 -I (15365) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms -I (15379) Flicker: Starting to collect samples in gather task -I (15375) BME688: Delaying for 199517 microseconds -I (15366) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** -I (15394) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 16909 ********** -E (15406) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 -E (15418) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 -W (15420) AS7341: Formatting AS7341 data in line protocol -AS7341 line protocol: -as7341_adc,device=A4CB8FC946F8 f1=1,f2=2,f3=3,f4=4,f5=3,f6=4,f7=7,f8=9,clear=22,nir=5 946684801000000000 -as7341_basic_counts,device=A4CB8FC946F8 f1=2.23,f2=4.46,f3=6.69,f4=8.92,f5=6.69,f6=8.92,f7=15.62,f8=20.08,clear=49.08,nir=11.15 946684801000000000 -as7341_flicker,device=A4CB8FC946F8 state="unknown" 946684801000000000 -W (15910) DeviceStatus: RTC is uninitialized, using current time as timestamp -I (16403) Flicker: Finished collecting samples, notifying main task -I (16405) Flicker: Collected 1024 samples for dominant frequency analysis -I (16412) SENSOR_GATHERER: Sensor should now be asleep -I (16424) SENSOR_GATHERER: Sensor should now be asleep -I (16464) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** -I (16466) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1018 ********** -E (16479) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 -E (16491) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 -W (16493) BME688: RTC is uninitialized, using current time as timestamp -I (16911) SENSOR_GATHERER: Sensor should now be asleep -W (17504) BME688: RTC is uninitialized, using current time as timestamp -W (18505) BME688: RTC is uninitialized, using current time as timestamp -W (19506) BME688: RTC is uninitialized, using current time as timestamp -I (19826) SEN66: Sent start measurement command, waiting for sensor to be ready... -I (19923) MICROPHONE: Read 300000 bytes in 4936 ms -I (19924) MICROPHONE: Read 300000 bytes of audio data -I (20116) MICROPHONE: Sum SQ: 0.135236, RMS pressure: 0.001163, Normalized: 0.001163, dB peak: -58.69 -W (20118) SEN66: RTC is uninitialized, using current time as timestamp -W (20507) BME688: RTC is uninitialized, using current time as timestamp -W (21130) SEN66: RTC is uninitialized, using current time as timestamp -W (21508) BME688: RTC is uninitialized, using current time as timestamp -E (21833) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (21835) esp-tls: Failed to open new connection -E (21846) transport_base: Failed to open a new connection -E (21847) HTTP_CLIENT: Connection failed, sock < 0 -I (21848) INFLUX: HTTP request performed, status code: 0 -E (21859) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT -E (21861) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL -W (22131) SEN66: RTC is uninitialized, using current time as timestamp -W (22509) BME688: RTC is uninitialized, using current time as timestamp -I (22799) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (23132) SEN66: RTC is uninitialized, using current time as timestamp -I (23300) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (23510) BME688: RTC is uninitialized, using current time as timestamp -E BOD: Brownout detector was triggered - - -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x40376756 -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (24) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (27) boot: efuse block revision: v1.4 -I (31) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (42) boot: Enabling RNG early entropy source... -I (47) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (108) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map -I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load -I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load -I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map -I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load -I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load -I (494) boot: Loaded app from partition at offset 0x40000 -I (494) boot: Disabling RNG early entropy source... -I (505) octal_psram: vendor id : 0x0d (AP) -I (505) octal_psram: dev id : 0x02 (generation 3) -I (505) octal_psram: density : 0x03 (64 Mbit) -I (507) octal_psram: good-die : 0x01 (Pass) -I (511) octal_psram: Latency : 0x01 (Fixed) -I (516) octal_psram: VCC : 0x01 (3V) -I (520) octal_psram: SRF : 0x01 (Fast Refresh) -I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (530) octal_psram: BurstLen : 0x01 (32 Byte) -I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (539) octal_psram: DriveStrength: 0x00 (1/1) -I (543) esp_psram: Found 8MB PSRAM device -I (547) esp_psram: Speed: 40MHz -I (550) cpu_start: Multicore app -I (1284) esp_psram: SPI SRAM memory test OK -I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1293) cpu_start: Pro cpu start user code -I (1293) cpu_start: cpu freq: 160000000 Hz -I (1295) app_init: Application information: -I (1299) app_init: Project name: kota-sensorboard -I (1303) app_init: App version: 1.0 -I (1307) app_init: Compile time: Jun 29 2026 11:03:32 -I (1312) app_init: ELF file SHA256: 45fa9d5b9... -I (1317) app_init: ESP-IDF: v5.5.3-dirty -I (1321) efuse_init: Min chip rev: v0.0 -I (1325) efuse_init: Max chip rev: v0.99 -I (1329) efuse_init: Chip rev: v0.2 -I (1333) heap_init: Initializing. RAM available for dynamic allocation: -I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM -I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1368) spi_flash: detected chip: generic -I (1371) spi_flash: flash io: dio -I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1396) main_task: Started on CPU0 -I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1407) main_task: Calling app_main() -E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 -I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1439) MAIN: Matter node created successfully -W (1440) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1470) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1505) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1540) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1575) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1584) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1595) pp: pp rom version: e7ae62f -I (1596) net80211: net80211 rom version: e7ae62f -I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 -I (1619) wifi:wifi firmware version: 4df78f2 -I (1620) wifi:wifi certification version: v7.0 -I (1620) wifi:config NVS flash: enabled -I (1620) wifi:config nano formatting: disabled -I (1625) wifi:Init data frame dynamic rx buffer num: 32 -I (1630) wifi:Init static rx mgmt buffer num: 5 -I (1634) wifi:Init management short buffer num: 32 -I (1638) wifi:Init dynamic tx buffer num: 32 -I (1642) wifi:Init static tx FG buffer num: 2 -I (1646) wifi:Init static rx buffer size: 1600 -I (1650) wifi:Init static rx buffer num: 10 -I (1654) wifi:Init dynamic rx buffer num: 32 -I (1659) wifi_init: rx ba win: 6 -I (1661) wifi_init: accept mbox: 6 -I (1664) wifi_init: tcpip mbox: 32 -I (1667) wifi_init: udp mbox: 6 -I (1670) wifi_init: tcp mbox: 6 -I (1673) wifi_init: tcp tx win: 5760 -I (1676) wifi_init: tcp rx win: 5760 -I (1680) wifi_init: tcp mss: 1440 -I (1691) chip[DL]: NVS set: chip-counters/reboot-count = 15841 (0x3DE1) -I (1694) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (1696) BLE_INIT: BT controller compile version [1bb2f50] -I (1701) BLE_INIT: Using main XTAL as clock source -I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (1714) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -I (1766) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(0) -I (1806) NimBLE: GAP procedure initiated: stop advertising. - -I (1807) CHIP[DL]: BLE host-controller synced -I (2308) chip[DL]: Starting ESP WiFi layer -I (2310) wifi:mode : sta (a4:cb:8f:c9:46:f8) -I (2311) wifi:enable tsf -W (2311) wifi:Haven't to connect to a suitable AP now! -I (2312) chip[DL]: -I (2312) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43 -Done driving station state, nothing else to do... -I (2321) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 -W (2325) wifi:Haven't to connect to a suitable AP now! -I (2336) chip[DL]: Done driving station state, nothing else to do... -I (2342) chip[SVR]: Initializing subscription resumption storage... -I (2349) chip[SVR]: Server initializing... -I (2352) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06 -I (2358) chip[DMG]: AccessControl: initializing -I (2361) chip[DMG]: Examples::AccessControlDelegate::Init -I (2366) chip[DMG]: AccessControl: setting -I (2370) chip[DMG]: DefaultAclStorage: initializing -I (2375) chip[DMG]: DefaultAclStorage: 0 entries loaded -I (2380) chip[SVR]: WARNING: mTestEventTriggerDelegate is null -I (2399) chip[ZCL]: Using ZAP configuration... -I (2399) esp_matter_cluster: Cluster plugin init common callback -I (2400) chip[DMG]: AccessControlCluster: initializing -I (2403) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported -I (2413) chip[DIS]: Updating services using commissioning mode 1 -I (2417) chip[DIS]: CHIP minimal mDNS started advertising. -I (2422) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 -I (2432) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 3B5442BDAD98C80C. -I (2442) chip[DIS]: mDNS service published: _matterc._udp -I (2447) chip[IN]: CASE Server enabling CASE session setups -I (2453) chip[SVR]: Joining Multicast groups -I (2456) chip[SVR]: Server Listening... -I (2461) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** -I (2471) data_model: Dynamic endpoint 0 added -E (2474) data_model: Cluster cannot be NULL. -I (2479) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** -I (2489) data_model: Dynamic endpoint 1 added -E (2493) data_model: Cluster cannot be NULL. -I (2498) chip[DL]: WIFI_EVENT_STA_START -I (2499) MAIN: ESP-Matter started successfully -W (2501) wifi:Haven't to connect to a suitable AP now! -I (2510) chip[DL]: Done driving station state, nothing else to do... -I (2517) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) -I (2527) NimBLE: GAP procedure initiated: advertise; -I (2527) NimBLE: disc_mode=2 - -I (2528) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 -I (2532) NimBLE: - -I (2534) chip[DL]: CHIPoBLE advertising started -I (2535) MAIN: Commissioning window opened -> -I (4082) I2C: Creating I2C mutex -I (14083) SPI: Creating SPI mutex -I (14125) INFLUX_BUFFER: SD card mounted successfully -I (14126) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully -I (14129) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota -W (14143) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota -I (14167) BME688: Delaying for 10000 microseconds -I (14785) Flicker: Flicker sensor initialized on pin 14 -I (14787) temperature_sensor: Range [20°C ~ 100°C], error < 2°C -I (14789) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data -I (14789) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data -W (14794) wifi: -I (14790) SEN66: Creating sensor mutex -Haven't to connect to a suitable AP now! -I (14805) SEN66: Waking up sensor by setting power GPIO to enabled level... - -I (14803) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms -I (14830) Flicker: Starting to collect samples in gather task -I (14818) DeviceStatus: Active tasks: 26 -I (14797) INFLUX_BUFFER: Flushing chunk starting at line 10: -I (14847) INFLUX: Writing to InfluxDB -I (14846) DeviceStatus: Task list: -gather_sensor_t X 1 1140 22 -main R 1 876 4 -IDLE1 R 0 700 6 -IDLE0 R 0 692 5 -ds1307_update B 1 808 17 -gather_task B 1 1860 19 -gather_task B 1 14040 20 -alive_diode_tas B 5 352 8 -reset_task B 0 3424 15 -tiT B 18 2420 10 -gather_sensor_t B 23 1720 24 -ota_task B 1 7508 18 -CHIP B 1 3636 12 -Tmr Svc B 1 1364 7 -gather_sensor_t B 1 2544 21 -sys_evt B 20 2684 9 -ipc0 S 24 372 1 -ipc1 S 24 524 2 -console B 5 640 16 -esp_timer S 22 3332 3 -gather_sensor_t B 1 2284 23 -gather_sensor_t B 1 2324 25 -wifi B 23 4324 11 -btController B 23 1960 13 -gather_sensor_t S 1 1344 26 -flicker_gather_ S 24 2060 27 -nimble_host B 21 1472 14 - -W (14924) DeviceStatus: RTC is uninitialized, using current time as timestamp -I (15857) Flicker: Finished collecting samples, notifying main task -I (15859) Flicker: Collected 1024 samples for dominant frequency analysis -W (15866) AS7341: Reading spectral data from AS7341 -I (15867) SENSOR_GATHERER: Sensor should now be asleep -I (15876) BME688: Delaying for 199517 microseconds -W (15926) DeviceStatus: RTC is uninitialized, using current time as timestamp -W (16176) AS7341: Reading basic counts data from AS7341 -W (16192) AS7341: Reading flicker detection state from AS7341 -W (16410) as7341: FD Status Register: 0x20 (0b00100000) -I (16416) AS7341: Updating Matter attributes with AS7341 data: 16707 -I (16417) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** -I (16430) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 16707 ********** -E (16443) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 -E (16445) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 -W (16457) AS7341: RTC is uninitialized, using current time as timestamp -I (16458) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** -I (16471) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1018 ********** -E (16484) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 -E (16496) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 -W (16497) BME688: RTC is uninitialized, using current time as timestamp -I (16927) SENSOR_GATHERER: Sensor should now be asleep -W (17458) AS7341: RTC is uninitialized, using current time as timestamp -W (17509) BME688: RTC is uninitialized, using current time as timestamp -W (18459) AS7341: RTC is uninitialized, using current time as timestamp -W (18510) BME688: RTC is uninitialized, using current time as timestamp -W (19460) AS7341: RTC is uninitialized, using current time as timestamp -W (19511) BME688: RTC is uninitialized, using current time as timestamp -I (19817) SEN66: Sent start measurement command, waiting for sensor to be ready... -I (19927) MICROPHONE: Read 300000 bytes in 4936 ms -I (19928) MICROPHONE: Read 300000 bytes of audio data -I (20119) MICROPHONE: Sum SQ: 0.185352, RMS pressure: 0.001361, Normalized: 0.001361, dB peak: -57.32 -W (20121) SEN66: RTC is uninitialized, using current time as timestamp -W (20461) AS7341: RTC is uninitialized, using current time as timestamp -W (20512) BME688: RTC is uninitialized, using current time as timestamp -W (21133) SEN66: RTC is uninitialized, using current time as timestamp -W (21462) AS7341: RTC is uninitialized, using current time as timestamp -W (21513) BME688: RTC is uninitialized, using current time as timestamp -E (21849) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 -E (21851) esp-tls: Failed to open new connection -E (21852) transport_base: Failed to open a new connection -E (21863) HTTP_CLIENT: Connection failed, sock < 0 -I (21864) INFLUX: HTTP request performed, status code: 0 -E (21875) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT -E (21877) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL -W (22134) SEN66: RTC is uninitialized, using current time as timestamp -W (22463) AS7341: RTC is uninitialized, using current time as timestamp -W (22514) BME688: RTC is uninitialized, using current time as timestamp -I (22791) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (23135) SEN66: RTC is uninitialized, using current time as timestamp -I (23292) SENSOR_GATHERER: 1/2 tasks still running, waiting... -W (23464) AS7341: RTC is uninitialized, using current time as timestamp -W (23515) BME688: RTC is uninitialized, using current time as timestamp -I (23793) SENSOR_GATHERER: 1/2 tasks still running, waiting... -I (23888) INFLUX: Writing to InfluxDB -E BOD: Brownout detector was triggered - -Guru Meditation Error: Core 0 panic'ed (Interrupt wdt timeout on CPU0). - -Core 0 register dump: -PC : 0x4037f211 PS : 0x00060d34 A0 : 0x80382ff0 A1 : 0x3fc9ec40 -A2 : 0x3fc9c7c8 A3 : 0xb33fffff A4 : 0x0000cdcd A5 : 0x00060923 -A6 : 0x3fceff68 A7 : 0x0000cdcd A8 : 0xfffffff1 A9 : 0x3fc9c7c8 -A10 : 0x01ffffff A11 : 0x0000cdcd A12 : 0x00001eae A13 : 0x40383206 -A14 : 0x3fcef74c A15 : 0x3fc9e9a0 SAR : 0x0000000c EXCCAUSE: 0x00000005 -EXCVADDR: 0x00000000 LBEG : 0x00000000 LEND : 0x00000000 LCOUNT : 0x00000000 -Core 0 was running in ISR context: -EPC1 : 0x42156637 EPC2 : 0x4037f34a EPC3 : 0x40041a76 EPC4 : 0x4037f211 - - -Backtrace: 0x4037f20e:0x3fc9ec40 0x40382fed:0x3fc9ec60 0x40384c1c:0x3fc9ec90 0x40382a9d:0x3fc9ecb0 0x40377011:0x3fc9ecd0 0x4037b2f9:0x3fc9ecf0 0x4037b32b:0x3fc9ed20 0x4037768d:0x3fc9ed40 0x4037b965:0x3fc9ed60 0x40041a73:0x3fc9ee20 0x40049059:0x3fc9ee40 0x40048818:0x3fc9ee60 0x40048c4b:0x3fc9ee80 0x40043cf3:0x3fc9eea0 0x40043cd5:0x3fc9eec0 0x40044183:0x3fc9eee0 0x40044281:0x3fc9ef60 0x40376715:0x3fc9efb0 0x4037664b:0x3fc9efd0 0x4037b819:0x3fc9eff0 0x4037f347:0x3fcb80e0 0x4216eb41:0x3fcb8100 0x4038429b:0x3fcb8120 0x40382e6d:0x3fcb8140 - - - - -ELF file SHA256: 45fa9d5b9 - -Rebooting... -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0xc (RTC_SW_CPU_RST),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x4038236c -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (24) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (27) boot: efuse block revision: v1.4 -I (31) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (42) boot: Enabling RNG early entropy source... -I (47) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (108) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map -I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load -I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load -I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map -I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load -I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load -I (494) boot: Loaded app from partition at offset 0x40000 -I (494) boot: Disabling RNG early entropy source... -I (505) octal_psram: vendor id : 0x0d (AP) -I (505) octal_psram: dev id : 0x02 (generation 3) -I (505) octal_psram: density : 0x03 (64 Mbit) -I (507) octal_psram: good-die : 0x01 (Pass) -I (511) octal_psram: Latency : 0x01 (Fixed) -I (516) octal_psram: VCC : 0x01 (3V) -I (520) octal_psram: SRF : 0x01 (Fast Refresh) -I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (530) octal_psram: BurstLen : 0x01 (32 Byte) -I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (539) octal_psram: DriveStrength: 0x00 (1/1) -I (543) esp_psram: Found 8MB PSRAM device -I (547) esp_psram: Speed: 40MHz -I (550) cpu_start: Multicore app -I (1284) esp_psram: SPI SRAM memory test OK -I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1293) cpu_start: Pro cpu start user code -I (1293) cpu_start: cpu freq: 160000000 Hz -I (1295) app_init: Application information: -I (1299) app_init: Project name: kota-sensorboard -I (1303) app_init: App version: 1.0 -I (1307) app_init: Compile time: Jun 29 2026 11:03:32 -I (1312) app_init: ELF file SHA256: 45fa9d5b9... -I (1317) app_init: ESP-IDF: v5.5.3-dirty -I (1321) efuse_init: Min chip rev: v0.0 -I (1325) efuse_init: Max chip rev: v0.99 -I (1329) efuse_init: Chip rev: v0.2 -I (1333) heap_init: Initializing. RAM available for dynamic allocation: -I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM -I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1368) spi_flash: detected chip: generic -I (1371) spi_flash: flash io: dio -I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1396) main_task: Started on CPU0 -I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1407) main_task: Calling app_main() -E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 -I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1440) MAIN: Matter node created successfully -W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1550) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1595) pp: pp rom version: e7ae62f -I (1597) net80211: net80211 rom version: e7ae62f -I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 -I (1620) wifi:wifi firmware version: 4df78f2 -I (1620) wifi:wifi certification version: v7.0 -I (1620) wifi:config NVS flash: enabled -I (1621) wifi:config nano formatting: disabled -I (1625) wifi:Init data frame dynamic rx buffer num: 32 -I (1630) wifi:Init static rx mgmt buffer num: 5 -I (1634) wifi:Init management short buffer num: 32 -I (1638) wifi:Init dynamic tx buffer num: 32 -I (1643) wifi:Init static tx FG buffer num: 2 -I (1647) wifi:Init static rx buffer size: 1600 -I (1651) wifi:Init static rx buffer num: 10 -I (1655) wifi:Init dynamic rx buffer num: 32 -I (1659) wifi_init: rx ba win: 6 -I (1662) wifi_init: accept mbox: 6 -I (1665) wifi_init: tcpip mbox: 32 -I (1668) wifi_init: udp mbox: 6 -I (1671) wifi_init: tcp mbox: 6 -I (1674) wifi_init: tcp tx win: 5760 -I (1677) wifi_init: tcp rx win: 5760 -I (1680) wifi_init: tcp mss: 1440 -I (1691) chip[DL]: NVS set: chip-counters/reboot-count = 15842 (0x3DE2) -I (1694) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (1697) BLE_INIT: BT controller compile version [1bb2f50] -I (1702) BLE_INIT: Using main XTAL as clock source -I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -E BOD: Brownout detector was triggered - - -ESP-ROM:esp32s3-20210327 -Build:Mar 27 2021 -rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) -Saved PC:0x40376756 -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fce2820,len:0x14f0 -load:0x403c8700,len:0xd24 -load:0x403cb700,len:0x2f48 -entry 0x403c8924 -I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader -I (24) boot: compile time Jun 29 2026 11:03:24 -I (24) boot: Multicore bootloader -I (25) boot: chip revision: v0.2 -I (27) boot: efuse block revision: v1.4 -I (31) boot.esp32s3: Boot SPI Speed : 80MHz -I (35) boot.esp32s3: SPI Mode : DIO -I (39) boot.esp32s3: SPI Flash Size : 8MB -I (42) boot: Enabling RNG early entropy source... -I (47) boot: Partition Table: -I (50) boot: ## Label Usage Type ST Offset Length -I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 -I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 -I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 -I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 -I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 -I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 -I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 -I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 -I (108) boot: End of partition table -I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map -I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load -I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load -I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map -I (469) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load -I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load -I (494) boot: Loaded app from partition at offset 0x40000 -I (495) boot: Disabling RNG early entropy source... -I (505) octal_psram: vendor id : 0x0d (AP) -I (505) octal_psram: dev id : 0x02 (generation 3) -I (505) octal_psram: density : 0x03 (64 Mbit) -I (507) octal_psram: good-die : 0x01 (Pass) -I (511) octal_psram: Latency : 0x01 (Fixed) -I (516) octal_psram: VCC : 0x01 (3V) -I (520) octal_psram: SRF : 0x01 (Fast Refresh) -I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) -I (530) octal_psram: BurstLen : 0x01 (32 Byte) -I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) -I (539) octal_psram: DriveStrength: 0x00 (1/1) -I (544) esp_psram: Found 8MB PSRAM device -I (547) esp_psram: Speed: 40MHz -I (550) cpu_start: Multicore app -I (1284) esp_psram: SPI SRAM memory test OK -I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins -I (1293) cpu_start: Pro cpu start user code -I (1293) cpu_start: cpu freq: 160000000 Hz -I (1295) app_init: Application information: -I (1299) app_init: Project name: kota-sensorboard -I (1304) app_init: App version: 1.0 -I (1307) app_init: Compile time: Jun 29 2026 11:03:32 -I (1312) app_init: ELF file SHA256: 45fa9d5b9... -I (1317) app_init: ESP-IDF: v5.5.3-dirty -I (1321) efuse_init: Min chip rev: v0.0 -I (1325) efuse_init: Max chip rev: v0.99 -I (1329) efuse_init: Chip rev: v0.2 -I (1333) heap_init: Initializing. RAM available for dynamic allocation: -I (1340) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM -I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM -I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM -I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM -I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator -I (1368) spi_flash: detected chip: generic -I (1371) spi_flash: flash io: dio -I (1377) sleep_gpio: Configure to isolate all GPIO pins in sleep state -I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration -I (1396) main_task: Started on CPU0 -I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations -I (1407) main_task: Calling app_main() -E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 -I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device -I (1439) MAIN: Matter node created successfully -W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. -W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. -W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. -W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. -W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. -W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. -I (1595) pp: pp rom version: e7ae62f -I (1597) net80211: net80211 rom version: e7ae62f -I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 -I (1619) wifi:wifi firmware version: 4df78f2 -I (1620) wifi:wifi certification version: v7.0 -I (1620) wifi:config NVS flash: enabled -I (1621) wifi:config nano formatting: disabled -I (1625) wifi:Init data frame dynamic rx buffer num: 32 -I (1630) wifi:Init static rx mgmt buffer num: 5 -I (1634) wifi:Init management short buffer num: 32 -I (1638) wifi:Init dynamic tx buffer num: 32 -I (1642) wifi:Init static tx FG buffer num: 2 -I (1647) wifi:Init static rx buffer size: 1600 -I (1651) wifi:Init static rx buffer num: 10 -I (1655) wifi:Init dynamic rx buffer num: 32 -I (1659) wifi_init: rx ba win: 6 -I (1661) wifi_init: accept mbox: 6 -I (1665) wifi_init: tcpip mbox: 32 -I (1668) wifi_init: udp mbox: 6 -I (1671) wifi_init: tcp mbox: 6 -I (1673) wifi_init: tcp tx win: 5760 -I (1677) wifi_init: tcp rx win: 5760 -I (1680) wifi_init: tcp mss: 1440 -I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15843 (0x3DE3) -I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) -I (1697) BLE_INIT: BT controller compile version [1bb2f50] -I (1702) BLE_INIT: Using main XTAL as clock source -I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 -I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa -I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 -I (1767) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(0) -I (1784) NimBLE: GAP procedure initiated: stop advertising. - -I (1786) CHIP[DL]: BLE host-controller synced -I (2286) chip[DL]: Starting ESP WiFi layer -I (2288) wifi:mode : sta (a4:cb:8f:c9:46:f8) -I (2289) wifi:enable tsf -W (2289) wifi:Haven't to connect to a suitable AP now! -I (2290) chip[DL]: -I (2290) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43 -Done driving station state, nothing else to do... -I (2299) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 -W (2303) wifi:Haven't to connect to a suitable AP now! -I (2314) chip[DL]: Done driving station state, nothing else to do... -I (2320) chip[SVR]: Initializing subscription resumption storage... -I (2327) chip[SVR]: Server initializing... -I (2330) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06 -I (2336) chip[DMG]: AccessControl: initializing -I (2339) chip[DMG]: Examples::AccessControlDelegate::Init -I (2344) chip[DMG]: AccessControl: setting -I (2348) chip[DMG]: DefaultAclStorage: initializing -I (2353) chip[DMG]: DefaultAclStorage: 0 entries loaded -I (2358) chip[SVR]: WARNING: mTestEventTriggerDelegate is null -I (2377) chip[ZCL]: Using ZAP configuration... -I (2378) esp_matter_cluster: Cluster plugin init common callback -I (2378) chip[DMG]: AccessControlCluster: initializing -I (2381) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported -I (2393) chip[DIS]: Updating services using commissioning mode 1 -I (2395) chip[DIS]: CHIP minimal mDNS started advertising. -I (2400) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 -I (2410) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 73BA585012FF9068. -I (2420) chip[DIS]: mDNS service published: _matterc._udp -I (2425) chip[IN]: CASE Server enabling CASE session setups -I (2431) chip[SVR]: Joining Multicast groups -I (2434) chip[SVR]: Server Listening... -I (2439) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** -I (2449) data_model: Dynamic endpoint 0 added -E (2452) data_model: Cluster cannot be NULL. -I (2457) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** -I (2467) data_model: Dynamic endpoint 1 added -E (2471) data_model: Cluster cannot be NULL. -I (2476) chip[DL]: WIFI_EVENT_STA_START -I (2477) MAIN: ESP-Matter started successfully -W (2479) wifi:Haven't to connect to a suitable AP now! -I (2488) chip[DL]: Done driving station state, nothing else to do... -I (2495) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) -I (2505) NimBLE: GAP procedure initiated: advertise; -I (2506) NimBLE: disc_mode=2 - -I (2506) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 -I (2511) NimBLE: - -I (2512) chip[DL]: CHIPoBLE advertising started -I (2513) MAIN: Commissioning window opened -> -I (4060) I2C: Creating I2C mutex -I (14061) SPI: Creating SPI mutex -I (14103) INFLUX_BUFFER: SD card mounted successfully -I (14104) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully -I (14107) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota -W (14121) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota -I (14145) BME688: Delaying for 10000 microseconds -I (14763) Flicker: Flicker sensor initialized on pin 14 -I (14765) temperature_sensor: Range [20°C ~ 100°C], error < 2°C -I (14767) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data -I (14767) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data -W (14772) wifi:Haven't to connect to a suitable AP now! -I (14768) SEN66: Creating sensor mutex -W (14782) AS7341: Reading spectral data from AS7341 -I (14775) INFLUX_BUFFER: Flushing chunk starting at line 10: -I (14796) INFLUX: Writing to InfluxDB -I (14784) SEN66: Waking up sensor by setting power GPIO to enabled level... -I (14783) DeviceStatus: Active tasks: 26 -I (14813) DeviceStatus: Task list: -gather_sensor_t X 1 1344 22 -IDLE1 R 0 812 6 -IDLE0 R 0 692 5 -gather_sensor_t B 1 2552 21 -ds1307_update B 1 800 17 -gather_task B 1 1964 19 -gather_task B 1 14032 20 -alive_diode_tas B 5 352 8 -reset_task B 0 3432 15 -tiT B 18 2420 10 -gather_sensor_t B 1 1720 24 -ota_task B 1 7516 18 -CHIP B 1 3700 12 -Tmr Svc B 1 1364 7 -gather_sensor_t B 1 1092 25 -sys_evt B 20 2684 9 -ipc0 S 24 372 1 -ipc1 S 24 524 2 -console B 5 648 16 -esp_timer S 22 3332 3 -gather_sensor_t B 1 2332 23 -gather_sensor_t B 1 2544 26 -wifi B 23 4324 11 -btController B 23 1960 13 -main B 1 876 4 -nimble_host B 21 1480 14 - -W (14878) DeviceStatus: RTC is uninitialized, using current time as timestamp -W (15105) AS7341: Reading basic counts data from AS7341 -W (15121) AS7341: Reading flicker detection state from AS7341 -W (15339) as7341: FD Status Register: 0x20 (0b00100000) -I (15345) AS7341: Updating Matter attributes with AS7341 data: 16707 -I (15345) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms -I (15359) Flicker: Starting to collect samples in gather task -I (15355) BME688: Delaying for 199517 microseconds -I (15347) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** -I (15374) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 16707 ********** -E (15386) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 -E (15398) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 -W (15400) AS7341: Formatting AS7341 data in line protocol -AS7341 line protocol: -as7341_adc,device=A4CB8FC946F8 f1=1,f2=2,f3=2,f4=3,f5=3,f6=4,f7=7,f8=9,clear=21,nir=5 946684801000000000 -as7341_basic_counts,device=A4CB8FC946F8 f1=2.23,f2=4.46,f3=4.46,f4=6.69,f5=6.69,f6=8.92,f7=15.62,f8=20.08,clear=46.85,nir=11.15 946684801000000000 -as7341_flicker,device=A4CB8FC946F8 state="unknown" 946684801000000000 -W (15890) DeviceStatus: RTC is uninitialized, using current time as timestamp -I (16383) Flicker: Finished collecting samples, notifying main task -I (16385) Flicker: Collected 1024 samples for dominant frequency analysis -I (16392) SENSOR_GATHERER: Sensor should now be asleep -I (16404) SENSOR_GATHERER: Sensor should now be asleep -I (16444) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** -I (16446) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1018 ********** -E (16459) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 -E (16471) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 -W (16473) BME688: RTC is uninitialized, using current time as timestamp -I (16891) SENSOR_GATHERER: Sensor should now be asleep -W (17484) BME688: RTC is uninitialized, using current time as timestamp +--- connected to COM5 --- +I (16926) SENSOR_GATHERER: Sensor should now be asleep +--- reset issued by tool --- +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x15 (USB_UART_CHIP_RESET),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x4037f34a +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (25) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (28) boot: efuse block revision: v1.4 +I (32) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (43) boot: Enabling RNG early entropy source... +I (48) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (63) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (76) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (89) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (102) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (109) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map +I (191) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load +I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load +I (203) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map +I (469) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load +I (485) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load +I (495) boot: Loaded app from partition at offset 0x40000 +I (495) boot: Disabling RNG early entropy source... +I (505) octal_psram: vendor id : 0x0d (AP) +I (505) octal_psram: dev id : 0x02 (generation 3) +I (506) octal_psram: density : 0x03 (64 Mbit) +I (508) octal_psram: good-die : 0x01 (Pass) +I (512) octal_psram: Latency : 0x01 (Fixed) +I (516) octal_psram: VCC : 0x01 (3V) +I (520) octal_psram: SRF : 0x01 (Fast Refresh) +I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (530) octal_psram: BurstLen : 0x01 (32 Byte) +I (535) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (540) octal_psram: DriveStrength: 0x00 (1/1) +I (544) esp_psram: Found 8MB PSRAM device +I (548) esp_psram: Speed: 40MHz +I (551) cpu_start: Multicore app +I (1285) esp_psram: SPI SRAM memory test OK +I (1293) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1293) cpu_start: Pro cpu start user code +I (1294) cpu_start: cpu freq: 160000000 Hz +I (1295) app_init: Application information: +I (1299) app_init: Project name: kota-sensorboard +I (1304) app_init: App version: 1.0 +I (1308) app_init: Compile time: Jun 29 2026 11:03:32 +I (1313) app_init: ELF file SHA256: 45fa9d5b9... +I (1317) app_init: ESP-IDF: v5.5.3-dirty +I (1322) efuse_init: Min chip rev: v0.0 +I (1326) efuse_init: Max chip rev: v0.99 +I (1330) efuse_init: Chip rev: v0.2 +I (1334) heap_init: Initializing. RAM available for dynamic allocation: +I (1340) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM +I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1356) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1369) spi_flash: detected chip: generic +I (1371) spi_flash: flash io: dio +I (1377) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1397) main_task: Started on CPU0 +I (1400) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1408) main_task: Calling app_main() +E (1411) ID: Device MAC address: A4:CB:8F:C9:46:F8 +I (1436) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1441) MAIN: Matter node created successfully +W (1442) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1445) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1454) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1463) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1472) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1480) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1489) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1498) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1507) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1515) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1524) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1533) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1542) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1551) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1559) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1568) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1577) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1586) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1596) pp: pp rom version: e7ae62f +I (1598) net80211: net80211 rom version: e7ae62f +I (1603) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 +I (1621) wifi:wifi firmware version: 4df78f2 +I (1621) wifi:wifi certification version: v7.0 +I (1621) wifi:config NVS flash: enabled +I (1622) wifi:config nano formatting: disabled +I (1626) wifi:Init data frame dynamic rx buffer num: 32 +I (1631) wifi:Init static rx mgmt buffer num: 5 +I (1635) wifi:Init management short buffer num: 32 +I (1639) wifi:Init dynamic tx buffer num: 32 +I (1644) wifi:Init static tx FG buffer num: 2 +I (1648) wifi:Init static rx buffer size: 1600 +I (1652) wifi:Init static rx buffer num: 10 +I (1656) wifi:Init dynamic rx buffer num: 32 +I (1660) wifi_init: rx ba win: 6 +I (1663) wifi_init: accept mbox: 6 +I (1666) wifi_init: tcpip mbox: 32 +I (1669) wifi_init: udp mbox: 6 +I (1672) wifi_init: tcp mbox: 6 +I (1675) wifi_init: tcp tx win: 5760 +I (1678) wifi_init: tcp rx win: 5760 +I (1681) wifi_init: tcp mss: 1440 +I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15834 (0x3DDA) +I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1698) BLE_INIT: BT controller compile version [1bb2f50] +I (1703) BLE_INIT: Using main XTAL as clock source +I (1707) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1716) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1721) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +E BOD: Brownout detector was triggered + + +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x40376756 +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (24) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (27) boot: efuse block revision: v1.4 +I (31) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (42) boot: Enabling RNG early entropy source... +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (108) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map +I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load +I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load +I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map +I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load +I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load +I (494) boot: Loaded app from partition at offset 0x40000 +I (495) boot: Disabling RNG early entropy source... +I (505) octal_psram: vendor id : 0x0d (AP) +I (505) octal_psram: dev id : 0x02 (generation 3) +I (505) octal_psram: density : 0x03 (64 Mbit) +I (507) octal_psram: good-die : 0x01 (Pass) +I (511) octal_psram: Latency : 0x01 (Fixed) +I (516) octal_psram: VCC : 0x01 (3V) +I (520) octal_psram: SRF : 0x01 (Fast Refresh) +I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (530) octal_psram: BurstLen : 0x01 (32 Byte) +I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (539) octal_psram: DriveStrength: 0x00 (1/1) +I (544) esp_psram: Found 8MB PSRAM device +I (547) esp_psram: Speed: 40MHz +I (550) cpu_start: Multicore app +I (1284) esp_psram: SPI SRAM memory test OK +I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1293) cpu_start: Pro cpu start user code +I (1293) cpu_start: cpu freq: 160000000 Hz +I (1295) app_init: Application information: +I (1299) app_init: Project name: kota-sensorboard +I (1304) app_init: App version: 1.0 +I (1307) app_init: Compile time: Jun 29 2026 11:03:32 +I (1312) app_init: ELF file SHA256: 45fa9d5b9... +I (1317) app_init: ESP-IDF: v5.5.3-dirty +I (1321) efuse_init: Min chip rev: v0.0 +I (1325) efuse_init: Max chip rev: v0.99 +I (1329) efuse_init: Chip rev: v0.2 +I (1333) heap_init: Initializing. RAM available for dynamic allocation: +I (1340) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM +I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1368) spi_flash: detected chip: generic +I (1371) spi_flash: flash io: dio +I (1377) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1396) main_task: Started on CPU0 +I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1407) main_task: Calling app_main() +E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 +I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1439) MAIN: Matter node created successfully +W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1595) pp: pp rom version: e7ae62f +I (1597) net80211: net80211 rom version: e7ae62f +I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 +I (1619) wifi:wifi firmware version: 4df78f2 +I (1620) wifi:wifi certification version: v7.0 +I (1620) wifi:config NVS flash: enabled +I (1621) wifi:config nano formatting: disabled +I (1625) wifi:Init data frame dynamic rx buffer num: 32 +I (1630) wifi:Init static rx mgmt buffer num: 5 +I (1634) wifi:Init management short buffer num: 32 +I (1638) wifi:Init dynamic tx buffer num: 32 +I (1642) wifi:Init static tx FG buffer num: 2 +I (1647) wifi:Init static rx buffer size: 1600 +I (1651) wifi:Init static rx buffer num: 10 +I (1655) wifi:Init dynamic rx buffer num: 32 +I (1659) wifi_init: rx ba win: 6 +I (1661) wifi_init: accept mbox: 6 +I (1665) wifi_init: tcpip mbox: 32 +I (1668) wifi_init: udp mbox: 6 +I (1671) wifi_init: tcp mbox: 6 +I (1673) wifi_init: tcp tx win: 5760 +I (1677) wifi_init: tcp rx win: 5760 +I (1680) wifi_init: tcp mss: 1440 +I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15835 (0x3DDB) +I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1697) BLE_INIT: BT controller compile version [1bb2f50] +I (1702) BLE_INIT: Using main XTAL as clock source +I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +E BOD: Brownout detector was triggered + + +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x40376756 +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (24) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (27) boot: efuse block revision: v1.4 +I (31) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (42) boot: Enabling RNG early entropy source... +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (108) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map +I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load +I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load +I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map +I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load +I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load +I (494) boot: Loaded app from partition at offset 0x40000 +I (494) boot: Disabling RNG early entropy source... +I (505) octal_psram: vendor id : 0x0d (AP) +I (505) octal_psram: dev id : 0x02 (generation 3) +I (505) octal_psram: density : 0x03 (64 Mbit) +I (507) octal_psram: good-die : 0x01 (Pass) +I (511) octal_psram: Latency : 0x01 (Fixed) +I (516) octal_psram: VCC : 0x01 (3V) +I (520) octal_psram: SRF : 0x01 (Fast Refresh) +I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (530) octal_psram: BurstLen : 0x01 (32 Byte) +I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (539) octal_psram: DriveStrength: 0x00 (1/1) +I (543) esp_psram: Found 8MB PSRAM device +I (547) esp_psram: Speed: 40MHz +I (550) cpu_start: Multicore app +I (1284) esp_psram: SPI SRAM memory test OK +I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1293) cpu_start: Pro cpu start user code +I (1293) cpu_start: cpu freq: 160000000 Hz +I (1295) app_init: Application information: +I (1299) app_init: Project name: kota-sensorboard +I (1303) app_init: App version: 1.0 +I (1307) app_init: Compile time: Jun 29 2026 11:03:32 +I (1312) app_init: ELF file SHA256: 45fa9d5b9... +I (1317) app_init: ESP-IDF: v5.5.3-dirty +I (1321) efuse_init: Min chip rev: v0.0 +I (1325) efuse_init: Max chip rev: v0.99 +I (1329) efuse_init: Chip rev: v0.2 +I (1333) heap_init: Initializing. RAM available for dynamic allocation: +I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM +I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1368) spi_flash: detected chip: generic +I (1371) spi_flash: flash io: dio +I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1396) main_task: Started on CPU0 +I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1407) main_task: Calling app_main() +E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 +I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1439) MAIN: Matter node created successfully +W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1550) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1595) pp: pp rom version: e7ae62f +I (1597) net80211: net80211 rom version: e7ae62f +I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 +I (1619) wifi:wifi firmware version: 4df78f2 +I (1620) wifi:wifi certification version: v7.0 +I (1620) wifi:config NVS flash: enabled +I (1621) wifi:config nano formatting: disabled +I (1625) wifi:Init data frame dynamic rx buffer num: 32 +I (1630) wifi:Init static rx mgmt buffer num: 5 +I (1634) wifi:Init management short buffer num: 32 +I (1638) wifi:Init dynamic tx buffer num: 32 +I (1642) wifi:Init static tx FG buffer num: 2 +I (1647) wifi:Init static rx buffer size: 1600 +I (1651) wifi:Init static rx buffer num: 10 +I (1655) wifi:Init dynamic rx buffer num: 32 +I (1659) wifi_init: rx ba win: 6 +I (1661) wifi_init: accept mbox: 6 +I (1665) wifi_init: tcpip mbox: 32 +I (1668) wifi_init: udp mbox: 6 +I (1671) wifi_init: tcp mbox: 6 +I (1673) wifi_init: tcp tx win: 5760 +I (1677) wifi_init: tcp rx win: 5760 +I (1680) wifi_init: tcp mss: 1440 +I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15836 (0x3DDC) +I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1697) BLE_INIT: BT controller compile version [1bb2f50] +I (1702) BLE_INIT: Using main XTAL as clock source +I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +E BOD: Brownout detector was triggered + + +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x40376756 +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (24) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (27) boot: efuse block revision: v1.4 +I (31) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (42) boot: Enabling RNG early entropy source... +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (108) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map +I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load +I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load +I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map +I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load +I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load +I (494) boot: Loaded app from partition at offset 0x40000 +I (494) boot: Disabling RNG early entropy source... +I (505) octal_psram: vendor id : 0x0d (AP) +I (505) octal_psram: dev id : 0x02 (generation 3) +I (505) octal_psram: density : 0x03 (64 Mbit) +I (507) octal_psram: good-die : 0x01 (Pass) +I (511) octal_psram: Latency : 0x01 (Fixed) +I (516) octal_psram: VCC : 0x01 (3V) +I (520) octal_psram: SRF : 0x01 (Fast Refresh) +I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (530) octal_psram: BurstLen : 0x01 (32 Byte) +I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (539) octal_psram: DriveStrength: 0x00 (1/1) +I (543) esp_psram: Found 8MB PSRAM device +I (547) esp_psram: Speed: 40MHz +I (550) cpu_start: Multicore app +I (1284) esp_psram: SPI SRAM memory test OK +I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1293) cpu_start: Pro cpu start user code +I (1293) cpu_start: cpu freq: 160000000 Hz +I (1295) app_init: Application information: +I (1299) app_init: Project name: kota-sensorboard +I (1303) app_init: App version: 1.0 +I (1307) app_init: Compile time: Jun 29 2026 11:03:32 +I (1312) app_init: ELF file SHA256: 45fa9d5b9... +I (1317) app_init: ESP-IDF: v5.5.3-dirty +I (1321) efuse_init: Min chip rev: v0.0 +I (1325) efuse_init: Max chip rev: v0.99 +I (1329) efuse_init: Chip rev: v0.2 +I (1333) heap_init: Initializing. RAM available for dynamic allocation: +I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM +I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1368) spi_flash: detected chip: generic +I (1371) spi_flash: flash io: dio +I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1396) main_task: Started on CPU0 +I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1407) main_task: Calling app_main() +E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 +I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1439) MAIN: Matter node created successfully +W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1595) pp: pp rom version: e7ae62f +I (1597) net80211: net80211 rom version: e7ae62f +I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 +I (1619) wifi:wifi firmware version: 4df78f2 +I (1620) wifi:wifi certification version: v7.0 +I (1620) wifi:config NVS flash: enabled +I (1621) wifi:config nano formatting: disabled +I (1625) wifi:Init data frame dynamic rx buffer num: 32 +I (1630) wifi:Init static rx mgmt buffer num: 5 +I (1634) wifi:Init management short buffer num: 32 +I (1638) wifi:Init dynamic tx buffer num: 32 +I (1642) wifi:Init static tx FG buffer num: 2 +I (1647) wifi:Init static rx buffer size: 1600 +I (1651) wifi:Init static rx buffer num: 10 +I (1655) wifi:Init dynamic rx buffer num: 32 +I (1659) wifi_init: rx ba win: 6 +I (1662) wifi_init: accept mbox: 6 +I (1665) wifi_init: tcpip mbox: 32 +I (1668) wifi_init: udp mbox: 6 +I (1671) wifi_init: tcp mbox: 6 +I (1673) wifi_init: tcp tx win: 5760 +I (1677) wifi_init: tcp rx win: 5760 +I (1680) wifi_init: tcp mss: 1440 +I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15837 (0x3DDD) +I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1697) BLE_INIT: BT controller compile version [1bb2f50] +I (1702) BLE_INIT: Using main XTAL as clock source +I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +E BOD: Brownout detector was triggered + + +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x40376756 +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (24) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (27) boot: efuse block revision: v1.4 +I (31) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (42) boot: Enabling RNG early entropy source... +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (108) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map +I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load +I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load +I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map +I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load +I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load +I (494) boot: Loaded app from partition at offset 0x40000 +I (494) boot: Disabling RNG early entropy source... +I (505) octal_psram: vendor id : 0x0d (AP) +I (505) octal_psram: dev id : 0x02 (generation 3) +I (505) octal_psram: density : 0x03 (64 Mbit) +I (507) octal_psram: good-die : 0x01 (Pass) +I (511) octal_psram: Latency : 0x01 (Fixed) +I (516) octal_psram: VCC : 0x01 (3V) +I (520) octal_psram: SRF : 0x01 (Fast Refresh) +I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (530) octal_psram: BurstLen : 0x01 (32 Byte) +I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (539) octal_psram: DriveStrength: 0x00 (1/1) +I (543) esp_psram: Found 8MB PSRAM device +I (547) esp_psram: Speed: 40MHz +I (550) cpu_start: Multicore app +I (1284) esp_psram: SPI SRAM memory test OK +I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1293) cpu_start: Pro cpu start user code +I (1293) cpu_start: cpu freq: 160000000 Hz +I (1295) app_init: Application information: +I (1299) app_init: Project name: kota-sensorboard +I (1303) app_init: App version: 1.0 +I (1307) app_init: Compile time: Jun 29 2026 11:03:32 +I (1312) app_init: ELF file SHA256: 45fa9d5b9... +I (1317) app_init: ESP-IDF: v5.5.3-dirty +I (1321) efuse_init: Min chip rev: v0.0 +I (1325) efuse_init: Max chip rev: v0.99 +I (1329) efuse_init: Chip rev: v0.2 +I (1333) heap_init: Initializing. RAM available for dynamic allocation: +I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM +I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1368) spi_flash: detected chip: generic +I (1371) spi_flash: flash io: dio +I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1396) main_task: Started on CPU0 +I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1407) main_task: Calling app_main() +E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 +I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1439) MAIN: Matter node created successfully +W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1595) pp: pp rom version: e7ae62f +I (1597) net80211: net80211 rom version: e7ae62f +I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 +I (1619) wifi:wifi firmware version: 4df78f2 +I (1620) wifi:wifi certification version: v7.0 +I (1620) wifi:config NVS flash: enabled +I (1621) wifi:config nano formatting: disabled +I (1625) wifi:Init data frame dynamic rx buffer num: 32 +I (1630) wifi:Init static rx mgmt buffer num: 5 +I (1634) wifi:Init management short buffer num: 32 +I (1638) wifi:Init dynamic tx buffer num: 32 +I (1642) wifi:Init static tx FG buffer num: 2 +I (1647) wifi:Init static rx buffer size: 1600 +I (1651) wifi:Init static rx buffer num: 10 +I (1655) wifi:Init dynamic rx buffer num: 32 +I (1659) wifi_init: rx ba win: 6 +I (1661) wifi_init: accept mbox: 6 +I (1665) wifi_init: tcpip mbox: 32 +I (1668) wifi_init: udp mbox: 6 +I (1671) wifi_init: tcp mbox: 6 +I (1673) wifi_init: tcp tx win: 5760 +I (1677) wifi_init: tcp rx win: 5760 +I (1680) wifi_init: tcp mss: 1440 +I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15838 (0x3DDE) +I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1697) BLE_INIT: BT controller compile version [1bb2f50] +I (1702) BLE_INIT: Using main XTAL as clock source +I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +I (1768) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(0) +I (1785) NimBLE: GAP procedure initiated: stop advertising. + +I (1787) CHIP[DL]: BLE host-controller synced +I (2287) chip[DL]: Starting ESP WiFi layer +I (2289) wifi:mode : sta (a4:cb:8f:c9:46:f8) +I (2290) wifi:enable tsf +W (2290) wifi:Haven't to connect to a suitable AP now! +I (2291) chip[DL]: +I (2291) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43 +Done driving station state, nothing else to do... +I (2300) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 +W (2304) wifi:Haven't to connect to a suitable AP now! +I (2315) chip[DL]: Done driving station state, nothing else to do... +I (2321) chip[SVR]: Initializing subscription resumption storage... +I (2328) chip[SVR]: Server initializing... +I (2331) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06 +I (2337) chip[DMG]: AccessControl: initializing +I (2340) chip[DMG]: Examples::AccessControlDelegate::Init +I (2345) chip[DMG]: AccessControl: setting +I (2349) chip[DMG]: DefaultAclStorage: initializing +I (2354) chip[DMG]: DefaultAclStorage: 0 entries loaded +I (2359) chip[SVR]: WARNING: mTestEventTriggerDelegate is null +I (2378) chip[ZCL]: Using ZAP configuration... +I (2378) esp_matter_cluster: Cluster plugin init common callback +I (2379) chip[DMG]: AccessControlCluster: initializing +I (2382) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported +I (2392) chip[DIS]: Updating services using commissioning mode 1 +I (2396) chip[DIS]: CHIP minimal mDNS started advertising. +I (2401) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 +I (2411) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 37BE0E5B446CAB54. +I (2421) chip[DIS]: mDNS service published: _matterc._udp +I (2426) chip[IN]: CASE Server enabling CASE session setups +I (2432) chip[SVR]: Joining Multicast groups +I (2435) chip[SVR]: Server Listening... +I (2440) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** +I (2450) data_model: Dynamic endpoint 0 added +E (2453) data_model: Cluster cannot be NULL. +I (2458) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** +I (2468) data_model: Dynamic endpoint 1 added +E (2472) data_model: Cluster cannot be NULL. +I (2477) chip[DL]: WIFI_EVENT_STA_START +I (2478) MAIN: ESP-Matter started successfully +W (2480) wifi:Haven't to connect to a suitable AP now! +I (2489) chip[DL]: Done driving station state, nothing else to do... +I (2496) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) +I (2506) NimBLE: GAP procedure initiated: advertise; +I (2506) NimBLE: disc_mode=2 + +I (2507) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 +I (2511) NimBLE: + +I (2513) chip[DL]: CHIPoBLE advertising started +I (2514) MAIN: Commissioning window opened +> +I (4060) I2C: Creating I2C mutex +I (14061) SPI: Creating SPI mutex +I (14103) INFLUX_BUFFER: SD card mounted successfully +I (14104) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully +I (14107) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +W (14121) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (14145) BME688: Delaying for 10000 microseconds +I (14763) Flicker: Flicker sensor initialized on pin 14 +I (14765) temperature_sensor: Range [20°C ~ 100°C], error < 2°C +I (14767) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +I (14767) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data +W (14772) wifi:Haven't to connect to a suitable AP now! +I (14768) SEN66: Creating sensor mutex +W (14782) AS7341: Reading spectral data from AS7341 +I (14775) INFLUX_BUFFER: Flushing chunk starting at line 10: +I (14796) INFLUX: Writing to InfluxDB +I (14784) SEN66: Waking up sensor by setting power GPIO to enabled level... +I (14783) DeviceStatus: Active tasks: 26 +I (14813) DeviceStatus: Task list: +gather_sensor_t X 1 1216 22 +IDLE1 R 0 812 6 +IDLE0 R 0 692 5 +gather_sensor_t B 1 2552 21 +ds1307_update B 1 800 17 +gather_task B 1 1756 19 +gather_task B 1 14032 20 +alive_diode_tas B 5 352 8 +reset_task B 0 3432 15 +tiT B 18 2420 10 +gather_sensor_t B 1 1688 24 +ota_task B 1 7516 18 +CHIP B 1 3700 12 +Tmr Svc B 1 1364 7 +gather_sensor_t B 1 1092 25 +sys_evt B 20 2684 9 +ipc0 S 24 372 1 +ipc1 S 24 524 2 +console B 5 648 16 +esp_timer S 22 3332 3 +gather_sensor_t B 1 2284 23 +gather_sensor_t B 1 2544 26 +wifi B 23 4276 11 +btController B 23 1960 13 +main B 1 876 4 +nimble_host B 21 1480 14 + +W (14878) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (15105) AS7341: Reading basic counts data from AS7341 +W (15121) AS7341: Reading flicker detection state from AS7341 +W (15339) as7341: FD Status Register: 0x20 (0b00100000) +I (15345) AS7341: Updating Matter attributes with AS7341 data: 16909 +I (15345) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms +I (15359) Flicker: Starting to collect samples in gather task +I (15355) BME688: Delaying for 199517 microseconds +I (15347) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** +I (15374) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 16909 ********** +E (15386) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +E (15398) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +W (15400) AS7341: Formatting AS7341 data in line protocol +AS7341 line protocol: +as7341_adc,device=A4CB8FC946F8 f1=1,f2=2,f3=2,f4=3,f5=3,f6=4,f7=7,f8=9,clear=22,nir=5 946684801000000000 +as7341_basic_counts,device=A4CB8FC946F8 f1=2.23,f2=4.46,f3=4.46,f4=6.69,f5=6.69,f6=8.92,f7=15.62,f8=20.08,clear=49.08,nir=11.15 946684801000000000 +as7341_flicker,device=A4CB8FC946F8 state="unknown" 946684801000000000 +W (15890) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (16383) Flicker: Finished collecting samples, notifying main task +I (16385) Flicker: Collected 1024 samples for dominant frequency analysis +I (16392) SENSOR_GATHERER: Sensor should now be asleep +I (16404) SENSOR_GATHERER: Sensor should now be asleep +I (16444) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16446) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1018 ********** +E (16459) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16471) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16473) BME688: RTC is uninitialized, using current time as timestamp +I (16891) SENSOR_GATHERER: Sensor should now be asleep +W (17484) BME688: RTC is uninitialized, using current time as timestamp +W (18485) BME688: RTC is uninitialized, using current time as timestamp +W (19486) BME688: RTC is uninitialized, using current time as timestamp +I (19809) SEN66: Sent start measurement command, waiting for sensor to be ready... +I (19905) MICROPHONE: Read 300000 bytes in 4936 ms +I (19906) MICROPHONE: Read 300000 bytes of audio data +I (20098) MICROPHONE: Sum SQ: 0.049147, RMS pressure: 0.000701, Normalized: 0.000701, dB peak: -63.09 +W (20100) SEN66: RTC is uninitialized, using current time as timestamp +W (20487) BME688: RTC is uninitialized, using current time as timestamp +W (21112) SEN66: RTC is uninitialized, using current time as timestamp +W (21488) BME688: RTC is uninitialized, using current time as timestamp +E (21798) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (21800) esp-tls: Failed to open new connection +E (21801) transport_base: Failed to open a new connection +E (21812) HTTP_CLIENT: Connection failed, sock < 0 +I (21813) INFLUX: HTTP request performed, status code: 0 +E (21824) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (21826) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (22113) SEN66: RTC is uninitialized, using current time as timestamp +W (22489) BME688: RTC is uninitialized, using current time as timestamp +I (22769) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (23114) SEN66: RTC is uninitialized, using current time as timestamp +I (23270) SENSOR_GATHERER: 1/2 tasks still running, waiting... +E BOD: Brownout detector was triggered + + +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x40376756 +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (24) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (27) boot: efuse block revision: v1.4 +I (31) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (42) boot: Enabling RNG early entropy source... +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (108) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map +I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load +I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load +I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map +I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load +I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load +I (494) boot: Loaded app from partition at offset 0x40000 +I (494) boot: Disabling RNG early entropy source... +I (505) octal_psram: vendor id : 0x0d (AP) +I (505) octal_psram: dev id : 0x02 (generation 3) +I (505) octal_psram: density : 0x03 (64 Mbit) +I (507) octal_psram: good-die : 0x01 (Pass) +I (511) octal_psram: Latency : 0x01 (Fixed) +I (516) octal_psram: VCC : 0x01 (3V) +I (520) octal_psram: SRF : 0x01 (Fast Refresh) +I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (530) octal_psram: BurstLen : 0x01 (32 Byte) +I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (539) octal_psram: DriveStrength: 0x00 (1/1) +I (543) esp_psram: Found 8MB PSRAM device +I (547) esp_psram: Speed: 40MHz +I (550) cpu_start: Multicore app +I (1284) esp_psram: SPI SRAM memory test OK +I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1293) cpu_start: Pro cpu start user code +I (1293) cpu_start: cpu freq: 160000000 Hz +I (1295) app_init: Application information: +I (1299) app_init: Project name: kota-sensorboard +I (1303) app_init: App version: 1.0 +I (1307) app_init: Compile time: Jun 29 2026 11:03:32 +I (1312) app_init: ELF file SHA256: 45fa9d5b9... +I (1317) app_init: ESP-IDF: v5.5.3-dirty +I (1321) efuse_init: Min chip rev: v0.0 +I (1325) efuse_init: Max chip rev: v0.99 +I (1329) efuse_init: Chip rev: v0.2 +I (1333) heap_init: Initializing. RAM available for dynamic allocation: +I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM +I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1368) spi_flash: detected chip: generic +I (1371) spi_flash: flash io: dio +I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1396) main_task: Started on CPU0 +I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1407) main_task: Calling app_main() +E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 +I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1439) MAIN: Matter node created successfully +W (1440) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1470) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1505) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1584) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1595) pp: pp rom version: e7ae62f +I (1596) net80211: net80211 rom version: e7ae62f +I (1602) wifi:wifi driver task: 3fcc524c, prio:23, stack:6656, core=0 +I (1619) wifi:wifi firmware version: 4df78f2 +I (1620) wifi:wifi certification version: v7.0 +I (1620) wifi:config NVS flash: enabled +I (1620) wifi:config nano formatting: disabled +I (1625) wifi:Init data frame dynamic rx buffer num: 32 +I (1630) wifi:Init static rx mgmt buffer num: 5 +I (1634) wifi:Init management short buffer num: 32 +I (1638) wifi:Init dynamic tx buffer num: 32 +I (1642) wifi:Init static tx FG buffer num: 2 +I (1646) wifi:Init static rx buffer size: 1600 +I (1651) wifi:Init static rx buffer num: 10 +I (1654) wifi:Init dynamic rx buffer num: 32 +I (1659) wifi_init: rx ba win: 6 +I (1661) wifi_init: accept mbox: 6 +I (1664) wifi_init: tcpip mbox: 32 +I (1667) wifi_init: udp mbox: 6 +I (1670) wifi_init: tcp mbox: 6 +I (1673) wifi_init: tcp tx win: 5760 +I (1677) wifi_init: tcp rx win: 5760 +I (1680) wifi_init: tcp mss: 1440 +I (1691) chip[DL]: NVS set: chip-counters/reboot-count = 15839 (0x3DDF) +I (1694) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1697) BLE_INIT: BT controller compile version [1bb2f50] +I (1701) BLE_INIT: Using main XTAL as clock source +I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +E BOD: Brownout detector was triggered + + +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x40376756 +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (24) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (27) boot: efuse block revision: v1.4 +I (31) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (42) boot: Enabling RNG early entropy source... +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (108) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map +I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load +I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load +I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map +I (469) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load +I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load +I (494) boot: Loaded app from partition at offset 0x40000 +I (495) boot: Disabling RNG early entropy source... +I (505) octal_psram: vendor id : 0x0d (AP) +I (505) octal_psram: dev id : 0x02 (generation 3) +I (505) octal_psram: density : 0x03 (64 Mbit) +I (507) octal_psram: good-die : 0x01 (Pass) +I (511) octal_psram: Latency : 0x01 (Fixed) +I (516) octal_psram: VCC : 0x01 (3V) +I (520) octal_psram: SRF : 0x01 (Fast Refresh) +I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (530) octal_psram: BurstLen : 0x01 (32 Byte) +I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (539) octal_psram: DriveStrength: 0x00 (1/1) +I (544) esp_psram: Found 8MB PSRAM device +I (547) esp_psram: Speed: 40MHz +I (550) cpu_start: Multicore app +I (1284) esp_psram: SPI SRAM memory test OK +I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1293) cpu_start: Pro cpu start user code +I (1293) cpu_start: cpu freq: 160000000 Hz +I (1295) app_init: Application information: +I (1299) app_init: Project name: kota-sensorboard +I (1304) app_init: App version: 1.0 +I (1307) app_init: Compile time: Jun 29 2026 11:03:32 +I (1312) app_init: ELF file SHA256: 45fa9d5b9... +I (1317) app_init: ESP-IDF: v5.5.3-dirty +I (1321) efuse_init: Min chip rev: v0.0 +I (1325) efuse_init: Max chip rev: v0.99 +I (1329) efuse_init: Chip rev: v0.2 +I (1333) heap_init: Initializing. RAM available for dynamic allocation: +I (1340) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM +I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1368) spi_flash: detected chip: generic +I (1371) spi_flash: flash io: dio +I (1377) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1396) main_task: Started on CPU0 +I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1407) main_task: Calling app_main() +E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 +I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1439) MAIN: Matter node created successfully +W (1440) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1461) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1470) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1505) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1540) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1575) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1584) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1595) pp: pp rom version: e7ae62f +I (1596) net80211: net80211 rom version: e7ae62f +I (1602) wifi:wifi driver task: 3fcc524c, prio:23, stack:6656, core=0 +I (1619) wifi:wifi firmware version: 4df78f2 +I (1620) wifi:wifi certification version: v7.0 +I (1620) wifi:config NVS flash: enabled +I (1620) wifi:config nano formatting: disabled +I (1625) wifi:Init data frame dynamic rx buffer num: 32 +I (1630) wifi:Init static rx mgmt buffer num: 5 +I (1634) wifi:Init management short buffer num: 32 +I (1638) wifi:Init dynamic tx buffer num: 32 +I (1642) wifi:Init static tx FG buffer num: 2 +I (1646) wifi:Init static rx buffer size: 1600 +I (1650) wifi:Init static rx buffer num: 10 +I (1654) wifi:Init dynamic rx buffer num: 32 +I (1659) wifi_init: rx ba win: 6 +I (1661) wifi_init: accept mbox: 6 +I (1664) wifi_init: tcpip mbox: 32 +I (1667) wifi_init: udp mbox: 6 +I (1670) wifi_init: tcp mbox: 6 +I (1673) wifi_init: tcp tx win: 5760 +I (1676) wifi_init: tcp rx win: 5760 +I (1680) wifi_init: tcp mss: 1440 +I (1691) chip[DL]: NVS set: chip-counters/reboot-count = 15840 (0x3DE0) +I (1694) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1696) BLE_INIT: BT controller compile version [1bb2f50] +I (1701) BLE_INIT: Using main XTAL as clock source +I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1714) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +I (1767) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(0) +I (1804) NimBLE: GAP procedure initiated: stop advertising. + +I (1806) CHIP[DL]: BLE host-controller synced +I (2306) chip[DL]: Starting ESP WiFi layer +I (2308) wifi:mode : sta (a4:cb:8f:c9:46:f8) +I (2309) wifi:enable tsf +W (2309) wifi:Haven't to connect to a suitable AP now! +I (2310) chip[DL]: +I (2310) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43 +Done driving station state, nothing else to do... +I (2319) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 +W (2323) wifi:Haven't to connect to a suitable AP now! +I (2334) chip[DL]: Done driving station state, nothing else to do... +I (2340) chip[SVR]: Initializing subscription resumption storage... +I (2347) chip[SVR]: Server initializing... +I (2350) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06 +I (2356) chip[DMG]: AccessControl: initializing +I (2359) chip[DMG]: Examples::AccessControlDelegate::Init +I (2364) chip[DMG]: AccessControl: setting +I (2368) chip[DMG]: DefaultAclStorage: initializing +I (2373) chip[DMG]: DefaultAclStorage: 0 entries loaded +I (2378) chip[SVR]: WARNING: mTestEventTriggerDelegate is null +I (2397) chip[ZCL]: Using ZAP configuration... +I (2397) esp_matter_cluster: Cluster plugin init common callback +I (2398) chip[DMG]: AccessControlCluster: initializing +I (2401) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported +I (2411) chip[DIS]: Updating services using commissioning mode 1 +I (2415) chip[DIS]: CHIP minimal mDNS started advertising. +I (2420) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 +I (2430) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 67895F766C4DDEDA. +I (2440) chip[DIS]: mDNS service published: _matterc._udp +I (2445) chip[IN]: CASE Server enabling CASE session setups +I (2451) chip[SVR]: Joining Multicast groups +I (2454) chip[SVR]: Server Listening... +I (2459) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** +I (2469) data_model: Dynamic endpoint 0 added +E (2472) data_model: Cluster cannot be NULL. +I (2477) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** +I (2487) data_model: Dynamic endpoint 1 added +E (2491) data_model: Cluster cannot be NULL. +I (2496) chip[DL]: WIFI_EVENT_STA_START +I (2497) MAIN: ESP-Matter started successfully +W (2499) wifi:Haven't to connect to a suitable AP now! +I (2508) chip[DL]: Done driving station state, nothing else to do... +I (2515) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) +I (2525) NimBLE: GAP procedure initiated: advertise; +I (2525) NimBLE: disc_mode=2 + +I (2526) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 +I (2530) NimBLE: + +I (2532) chip[DL]: CHIPoBLE advertising started +I (2533) MAIN: Commissioning window opened +> +I (4079) I2C: Creating I2C mutex +I (14080) SPI: Creating SPI mutex +I (14122) INFLUX_BUFFER: SD card mounted successfully +I (14123) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully +I (14126) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +W (14140) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (14164) BME688: Delaying for 10000 microseconds +I (14782) Flicker: Flicker sensor initialized on pin 14 +I (14784) temperature_sensor: Range [20°C ~ 100°C], error < 2°C +I (14786) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +W (14791) wifi:Haven't to connect to a suitable AP now! +I (14794) INFLUX_BUFFER: Flushing chunk starting at line 10: +I (14786) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data +W (14799) AS7341: Reading spectral data from AS7341 +I (14787) SEN66: Creating sensor mutex +I (14815) SEN66: Waking up sensor by setting power GPIO to enabled level... +I (14802) INFLUX: Writing to InfluxDB +I (14802) DeviceStatus: Active tasks: 26 +I (14832) DeviceStatus: Task list: +main R 1 876 4 +gather_sensor_t X 1 1352 24 +IDLE1 R 0 700 6 +IDLE0 R 0 692 5 +gather_sensor_t B 1 2544 21 +ds1307_update B 1 800 17 +gather_task B 1 1756 19 +gather_task B 1 14032 20 +alive_diode_tas B 5 356 8 +reset_task B 0 3432 15 +tiT B 18 2424 10 +gather_sensor_t B 1 1724 23 +ota_task B 1 7516 18 +CHIP B 1 3708 12 +Tmr Svc B 1 1364 7 +gather_sensor_t B 1 1300 25 +sys_evt B 20 2672 9 +ipc0 S 24 372 1 +ipc1 S 24 524 2 +console B 5 648 16 +esp_timer S 22 3332 3 +gather_sensor_t B 1 2332 22 +gather_sensor_t B 1 2544 26 +wifi B 23 4280 11 +btController B 23 1952 13 +nimble_host B 21 1480 14 + +W (14898) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (15125) AS7341: Reading basic counts data from AS7341 +W (15141) AS7341: Reading flicker detection state from AS7341 +W (15359) as7341: FD Status Register: 0x20 (0b00100000) +I (15365) AS7341: Updating Matter attributes with AS7341 data: 16909 +I (15365) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms +I (15379) Flicker: Starting to collect samples in gather task +I (15375) BME688: Delaying for 199517 microseconds +I (15366) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** +I (15394) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 16909 ********** +E (15406) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +E (15418) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +W (15420) AS7341: Formatting AS7341 data in line protocol +AS7341 line protocol: +as7341_adc,device=A4CB8FC946F8 f1=1,f2=2,f3=3,f4=4,f5=3,f6=4,f7=7,f8=9,clear=22,nir=5 946684801000000000 +as7341_basic_counts,device=A4CB8FC946F8 f1=2.23,f2=4.46,f3=6.69,f4=8.92,f5=6.69,f6=8.92,f7=15.62,f8=20.08,clear=49.08,nir=11.15 946684801000000000 +as7341_flicker,device=A4CB8FC946F8 state="unknown" 946684801000000000 +W (15910) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (16403) Flicker: Finished collecting samples, notifying main task +I (16405) Flicker: Collected 1024 samples for dominant frequency analysis +I (16412) SENSOR_GATHERER: Sensor should now be asleep +I (16424) SENSOR_GATHERER: Sensor should now be asleep +I (16464) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16466) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1018 ********** +E (16479) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16491) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16493) BME688: RTC is uninitialized, using current time as timestamp +I (16911) SENSOR_GATHERER: Sensor should now be asleep +W (17504) BME688: RTC is uninitialized, using current time as timestamp +W (18505) BME688: RTC is uninitialized, using current time as timestamp +W (19506) BME688: RTC is uninitialized, using current time as timestamp +I (19826) SEN66: Sent start measurement command, waiting for sensor to be ready... +I (19923) MICROPHONE: Read 300000 bytes in 4936 ms +I (19924) MICROPHONE: Read 300000 bytes of audio data +I (20116) MICROPHONE: Sum SQ: 0.135236, RMS pressure: 0.001163, Normalized: 0.001163, dB peak: -58.69 +W (20118) SEN66: RTC is uninitialized, using current time as timestamp +W (20507) BME688: RTC is uninitialized, using current time as timestamp +W (21130) SEN66: RTC is uninitialized, using current time as timestamp +W (21508) BME688: RTC is uninitialized, using current time as timestamp +E (21833) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (21835) esp-tls: Failed to open new connection +E (21846) transport_base: Failed to open a new connection +E (21847) HTTP_CLIENT: Connection failed, sock < 0 +I (21848) INFLUX: HTTP request performed, status code: 0 +E (21859) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (21861) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (22131) SEN66: RTC is uninitialized, using current time as timestamp +W (22509) BME688: RTC is uninitialized, using current time as timestamp +I (22799) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (23132) SEN66: RTC is uninitialized, using current time as timestamp +I (23300) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (23510) BME688: RTC is uninitialized, using current time as timestamp +E BOD: Brownout detector was triggered + + +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x40376756 +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (24) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (27) boot: efuse block revision: v1.4 +I (31) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (42) boot: Enabling RNG early entropy source... +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (108) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map +I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load +I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load +I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map +I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load +I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load +I (494) boot: Loaded app from partition at offset 0x40000 +I (494) boot: Disabling RNG early entropy source... +I (505) octal_psram: vendor id : 0x0d (AP) +I (505) octal_psram: dev id : 0x02 (generation 3) +I (505) octal_psram: density : 0x03 (64 Mbit) +I (507) octal_psram: good-die : 0x01 (Pass) +I (511) octal_psram: Latency : 0x01 (Fixed) +I (516) octal_psram: VCC : 0x01 (3V) +I (520) octal_psram: SRF : 0x01 (Fast Refresh) +I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (530) octal_psram: BurstLen : 0x01 (32 Byte) +I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (539) octal_psram: DriveStrength: 0x00 (1/1) +I (543) esp_psram: Found 8MB PSRAM device +I (547) esp_psram: Speed: 40MHz +I (550) cpu_start: Multicore app +I (1284) esp_psram: SPI SRAM memory test OK +I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1293) cpu_start: Pro cpu start user code +I (1293) cpu_start: cpu freq: 160000000 Hz +I (1295) app_init: Application information: +I (1299) app_init: Project name: kota-sensorboard +I (1303) app_init: App version: 1.0 +I (1307) app_init: Compile time: Jun 29 2026 11:03:32 +I (1312) app_init: ELF file SHA256: 45fa9d5b9... +I (1317) app_init: ESP-IDF: v5.5.3-dirty +I (1321) efuse_init: Min chip rev: v0.0 +I (1325) efuse_init: Max chip rev: v0.99 +I (1329) efuse_init: Chip rev: v0.2 +I (1333) heap_init: Initializing. RAM available for dynamic allocation: +I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM +I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1368) spi_flash: detected chip: generic +I (1371) spi_flash: flash io: dio +I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1396) main_task: Started on CPU0 +I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1407) main_task: Calling app_main() +E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 +I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1439) MAIN: Matter node created successfully +W (1440) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1470) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1505) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1540) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1575) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1584) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1595) pp: pp rom version: e7ae62f +I (1596) net80211: net80211 rom version: e7ae62f +I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 +I (1619) wifi:wifi firmware version: 4df78f2 +I (1620) wifi:wifi certification version: v7.0 +I (1620) wifi:config NVS flash: enabled +I (1620) wifi:config nano formatting: disabled +I (1625) wifi:Init data frame dynamic rx buffer num: 32 +I (1630) wifi:Init static rx mgmt buffer num: 5 +I (1634) wifi:Init management short buffer num: 32 +I (1638) wifi:Init dynamic tx buffer num: 32 +I (1642) wifi:Init static tx FG buffer num: 2 +I (1646) wifi:Init static rx buffer size: 1600 +I (1650) wifi:Init static rx buffer num: 10 +I (1654) wifi:Init dynamic rx buffer num: 32 +I (1659) wifi_init: rx ba win: 6 +I (1661) wifi_init: accept mbox: 6 +I (1664) wifi_init: tcpip mbox: 32 +I (1667) wifi_init: udp mbox: 6 +I (1670) wifi_init: tcp mbox: 6 +I (1673) wifi_init: tcp tx win: 5760 +I (1676) wifi_init: tcp rx win: 5760 +I (1680) wifi_init: tcp mss: 1440 +I (1691) chip[DL]: NVS set: chip-counters/reboot-count = 15841 (0x3DE1) +I (1694) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1696) BLE_INIT: BT controller compile version [1bb2f50] +I (1701) BLE_INIT: Using main XTAL as clock source +I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1714) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +I (1766) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(0) +I (1806) NimBLE: GAP procedure initiated: stop advertising. + +I (1807) CHIP[DL]: BLE host-controller synced +I (2308) chip[DL]: Starting ESP WiFi layer +I (2310) wifi:mode : sta (a4:cb:8f:c9:46:f8) +I (2311) wifi:enable tsf +W (2311) wifi:Haven't to connect to a suitable AP now! +I (2312) chip[DL]: +I (2312) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43 +Done driving station state, nothing else to do... +I (2321) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 +W (2325) wifi:Haven't to connect to a suitable AP now! +I (2336) chip[DL]: Done driving station state, nothing else to do... +I (2342) chip[SVR]: Initializing subscription resumption storage... +I (2349) chip[SVR]: Server initializing... +I (2352) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06 +I (2358) chip[DMG]: AccessControl: initializing +I (2361) chip[DMG]: Examples::AccessControlDelegate::Init +I (2366) chip[DMG]: AccessControl: setting +I (2370) chip[DMG]: DefaultAclStorage: initializing +I (2375) chip[DMG]: DefaultAclStorage: 0 entries loaded +I (2380) chip[SVR]: WARNING: mTestEventTriggerDelegate is null +I (2399) chip[ZCL]: Using ZAP configuration... +I (2399) esp_matter_cluster: Cluster plugin init common callback +I (2400) chip[DMG]: AccessControlCluster: initializing +I (2403) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported +I (2413) chip[DIS]: Updating services using commissioning mode 1 +I (2417) chip[DIS]: CHIP minimal mDNS started advertising. +I (2422) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 +I (2432) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 3B5442BDAD98C80C. +I (2442) chip[DIS]: mDNS service published: _matterc._udp +I (2447) chip[IN]: CASE Server enabling CASE session setups +I (2453) chip[SVR]: Joining Multicast groups +I (2456) chip[SVR]: Server Listening... +I (2461) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** +I (2471) data_model: Dynamic endpoint 0 added +E (2474) data_model: Cluster cannot be NULL. +I (2479) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** +I (2489) data_model: Dynamic endpoint 1 added +E (2493) data_model: Cluster cannot be NULL. +I (2498) chip[DL]: WIFI_EVENT_STA_START +I (2499) MAIN: ESP-Matter started successfully +W (2501) wifi:Haven't to connect to a suitable AP now! +I (2510) chip[DL]: Done driving station state, nothing else to do... +I (2517) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) +I (2527) NimBLE: GAP procedure initiated: advertise; +I (2527) NimBLE: disc_mode=2 + +I (2528) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 +I (2532) NimBLE: + +I (2534) chip[DL]: CHIPoBLE advertising started +I (2535) MAIN: Commissioning window opened +> +I (4082) I2C: Creating I2C mutex +I (14083) SPI: Creating SPI mutex +I (14125) INFLUX_BUFFER: SD card mounted successfully +I (14126) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully +I (14129) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +W (14143) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (14167) BME688: Delaying for 10000 microseconds +I (14785) Flicker: Flicker sensor initialized on pin 14 +I (14787) temperature_sensor: Range [20°C ~ 100°C], error < 2°C +I (14789) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +I (14789) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data +W (14794) wifi: +I (14790) SEN66: Creating sensor mutex +Haven't to connect to a suitable AP now! +I (14805) SEN66: Waking up sensor by setting power GPIO to enabled level... + +I (14803) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms +I (14830) Flicker: Starting to collect samples in gather task +I (14818) DeviceStatus: Active tasks: 26 +I (14797) INFLUX_BUFFER: Flushing chunk starting at line 10: +I (14847) INFLUX: Writing to InfluxDB +I (14846) DeviceStatus: Task list: +gather_sensor_t X 1 1140 22 +main R 1 876 4 +IDLE1 R 0 700 6 +IDLE0 R 0 692 5 +ds1307_update B 1 808 17 +gather_task B 1 1860 19 +gather_task B 1 14040 20 +alive_diode_tas B 5 352 8 +reset_task B 0 3424 15 +tiT B 18 2420 10 +gather_sensor_t B 23 1720 24 +ota_task B 1 7508 18 +CHIP B 1 3636 12 +Tmr Svc B 1 1364 7 +gather_sensor_t B 1 2544 21 +sys_evt B 20 2684 9 +ipc0 S 24 372 1 +ipc1 S 24 524 2 +console B 5 640 16 +esp_timer S 22 3332 3 +gather_sensor_t B 1 2284 23 +gather_sensor_t B 1 2324 25 +wifi B 23 4324 11 +btController B 23 1960 13 +gather_sensor_t S 1 1344 26 +flicker_gather_ S 24 2060 27 +nimble_host B 21 1472 14 + +W (14924) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (15857) Flicker: Finished collecting samples, notifying main task +I (15859) Flicker: Collected 1024 samples for dominant frequency analysis +W (15866) AS7341: Reading spectral data from AS7341 +I (15867) SENSOR_GATHERER: Sensor should now be asleep +I (15876) BME688: Delaying for 199517 microseconds +W (15926) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (16176) AS7341: Reading basic counts data from AS7341 +W (16192) AS7341: Reading flicker detection state from AS7341 +W (16410) as7341: FD Status Register: 0x20 (0b00100000) +I (16416) AS7341: Updating Matter attributes with AS7341 data: 16707 +I (16417) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** +I (16430) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 16707 ********** +E (16443) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +E (16445) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +W (16457) AS7341: RTC is uninitialized, using current time as timestamp +I (16458) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16471) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1018 ********** +E (16484) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16496) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16497) BME688: RTC is uninitialized, using current time as timestamp +I (16927) SENSOR_GATHERER: Sensor should now be asleep +W (17458) AS7341: RTC is uninitialized, using current time as timestamp +W (17509) BME688: RTC is uninitialized, using current time as timestamp +W (18459) AS7341: RTC is uninitialized, using current time as timestamp +W (18510) BME688: RTC is uninitialized, using current time as timestamp +W (19460) AS7341: RTC is uninitialized, using current time as timestamp +W (19511) BME688: RTC is uninitialized, using current time as timestamp +I (19817) SEN66: Sent start measurement command, waiting for sensor to be ready... +I (19927) MICROPHONE: Read 300000 bytes in 4936 ms +I (19928) MICROPHONE: Read 300000 bytes of audio data +I (20119) MICROPHONE: Sum SQ: 0.185352, RMS pressure: 0.001361, Normalized: 0.001361, dB peak: -57.32 +W (20121) SEN66: RTC is uninitialized, using current time as timestamp +W (20461) AS7341: RTC is uninitialized, using current time as timestamp +W (20512) BME688: RTC is uninitialized, using current time as timestamp +W (21133) SEN66: RTC is uninitialized, using current time as timestamp +W (21462) AS7341: RTC is uninitialized, using current time as timestamp +W (21513) BME688: RTC is uninitialized, using current time as timestamp +E (21849) esp-tls: couldn't get hostname for :influx.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (21851) esp-tls: Failed to open new connection +E (21852) transport_base: Failed to open a new connection +E (21863) HTTP_CLIENT: Connection failed, sock < 0 +I (21864) INFLUX: HTTP request performed, status code: 0 +E (21875) INFLUX: Failed to perform HTTP request: 0, ESP_ERR_HTTP_CONNECT +E (21877) INFLUX_BUFFER: Failed to write chunk to Influx: ESP_FAIL +W (22134) SEN66: RTC is uninitialized, using current time as timestamp +W (22463) AS7341: RTC is uninitialized, using current time as timestamp +W (22514) BME688: RTC is uninitialized, using current time as timestamp +I (22791) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (23135) SEN66: RTC is uninitialized, using current time as timestamp +I (23292) SENSOR_GATHERER: 1/2 tasks still running, waiting... +W (23464) AS7341: RTC is uninitialized, using current time as timestamp +W (23515) BME688: RTC is uninitialized, using current time as timestamp +I (23793) SENSOR_GATHERER: 1/2 tasks still running, waiting... +I (23888) INFLUX: Writing to InfluxDB +E BOD: Brownout detector was triggered + +Guru Meditation Error: Core 0 panic'ed (Interrupt wdt timeout on CPU0). + +Core 0 register dump: +PC : 0x4037f211 PS : 0x00060d34 A0 : 0x80382ff0 A1 : 0x3fc9ec40 +A2 : 0x3fc9c7c8 A3 : 0xb33fffff A4 : 0x0000cdcd A5 : 0x00060923 +A6 : 0x3fceff68 A7 : 0x0000cdcd A8 : 0xfffffff1 A9 : 0x3fc9c7c8 +A10 : 0x01ffffff A11 : 0x0000cdcd A12 : 0x00001eae A13 : 0x40383206 +A14 : 0x3fcef74c A15 : 0x3fc9e9a0 SAR : 0x0000000c EXCCAUSE: 0x00000005 +EXCVADDR: 0x00000000 LBEG : 0x00000000 LEND : 0x00000000 LCOUNT : 0x00000000 +Core 0 was running in ISR context: +EPC1 : 0x42156637 EPC2 : 0x4037f34a EPC3 : 0x40041a76 EPC4 : 0x4037f211 + + +Backtrace: 0x4037f20e:0x3fc9ec40 0x40382fed:0x3fc9ec60 0x40384c1c:0x3fc9ec90 0x40382a9d:0x3fc9ecb0 0x40377011:0x3fc9ecd0 0x4037b2f9:0x3fc9ecf0 0x4037b32b:0x3fc9ed20 0x4037768d:0x3fc9ed40 0x4037b965:0x3fc9ed60 0x40041a73:0x3fc9ee20 0x40049059:0x3fc9ee40 0x40048818:0x3fc9ee60 0x40048c4b:0x3fc9ee80 0x40043cf3:0x3fc9eea0 0x40043cd5:0x3fc9eec0 0x40044183:0x3fc9eee0 0x40044281:0x3fc9ef60 0x40376715:0x3fc9efb0 0x4037664b:0x3fc9efd0 0x4037b819:0x3fc9eff0 0x4037f347:0x3fcb80e0 0x4216eb41:0x3fcb8100 0x4038429b:0x3fcb8120 0x40382e6d:0x3fcb8140 + + + + +ELF file SHA256: 45fa9d5b9 + +Rebooting... +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0xc (RTC_SW_CPU_RST),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x4038236c +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (24) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (27) boot: efuse block revision: v1.4 +I (31) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (42) boot: Enabling RNG early entropy source... +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (108) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map +I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load +I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load +I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map +I (468) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load +I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load +I (494) boot: Loaded app from partition at offset 0x40000 +I (494) boot: Disabling RNG early entropy source... +I (505) octal_psram: vendor id : 0x0d (AP) +I (505) octal_psram: dev id : 0x02 (generation 3) +I (505) octal_psram: density : 0x03 (64 Mbit) +I (507) octal_psram: good-die : 0x01 (Pass) +I (511) octal_psram: Latency : 0x01 (Fixed) +I (516) octal_psram: VCC : 0x01 (3V) +I (520) octal_psram: SRF : 0x01 (Fast Refresh) +I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (530) octal_psram: BurstLen : 0x01 (32 Byte) +I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (539) octal_psram: DriveStrength: 0x00 (1/1) +I (543) esp_psram: Found 8MB PSRAM device +I (547) esp_psram: Speed: 40MHz +I (550) cpu_start: Multicore app +I (1284) esp_psram: SPI SRAM memory test OK +I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1293) cpu_start: Pro cpu start user code +I (1293) cpu_start: cpu freq: 160000000 Hz +I (1295) app_init: Application information: +I (1299) app_init: Project name: kota-sensorboard +I (1303) app_init: App version: 1.0 +I (1307) app_init: Compile time: Jun 29 2026 11:03:32 +I (1312) app_init: ELF file SHA256: 45fa9d5b9... +I (1317) app_init: ESP-IDF: v5.5.3-dirty +I (1321) efuse_init: Min chip rev: v0.0 +I (1325) efuse_init: Max chip rev: v0.99 +I (1329) efuse_init: Chip rev: v0.2 +I (1333) heap_init: Initializing. RAM available for dynamic allocation: +I (1339) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM +I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1368) spi_flash: detected chip: generic +I (1371) spi_flash: flash io: dio +I (1376) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1396) main_task: Started on CPU0 +I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1407) main_task: Calling app_main() +E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 +I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1440) MAIN: Matter node created successfully +W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1550) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1595) pp: pp rom version: e7ae62f +I (1597) net80211: net80211 rom version: e7ae62f +I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 +I (1620) wifi:wifi firmware version: 4df78f2 +I (1620) wifi:wifi certification version: v7.0 +I (1620) wifi:config NVS flash: enabled +I (1621) wifi:config nano formatting: disabled +I (1625) wifi:Init data frame dynamic rx buffer num: 32 +I (1630) wifi:Init static rx mgmt buffer num: 5 +I (1634) wifi:Init management short buffer num: 32 +I (1638) wifi:Init dynamic tx buffer num: 32 +I (1643) wifi:Init static tx FG buffer num: 2 +I (1647) wifi:Init static rx buffer size: 1600 +I (1651) wifi:Init static rx buffer num: 10 +I (1655) wifi:Init dynamic rx buffer num: 32 +I (1659) wifi_init: rx ba win: 6 +I (1662) wifi_init: accept mbox: 6 +I (1665) wifi_init: tcpip mbox: 32 +I (1668) wifi_init: udp mbox: 6 +I (1671) wifi_init: tcp mbox: 6 +I (1674) wifi_init: tcp tx win: 5760 +I (1677) wifi_init: tcp rx win: 5760 +I (1680) wifi_init: tcp mss: 1440 +I (1691) chip[DL]: NVS set: chip-counters/reboot-count = 15842 (0x3DE2) +I (1694) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1697) BLE_INIT: BT controller compile version [1bb2f50] +I (1702) BLE_INIT: Using main XTAL as clock source +I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +E BOD: Brownout detector was triggered + + +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x40376756 +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Jun 29 2026 11:03:24 +I (24) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (27) boot: efuse block revision: v1.4 +I (31) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (42) boot: Enabling RNG early entropy source... +I (47) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (62) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (75) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (88) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (101) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (108) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c170020 size=63194h (405908) map +I (190) esp_image: segment 1: paddr=000a31bc vaddr=3fc9c600 size=05cc0h ( 23744) load +I (196) esp_image: segment 2: paddr=000a8e84 vaddr=40374000 size=07194h ( 29076) load +I (202) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=16ff80h (1507200) map +I (469) esp_image: segment 4: paddr=0021ffa8 vaddr=4037b194 size=113a0h ( 70560) load +I (484) esp_image: segment 5: paddr=00231350 vaddr=50000000 size=00020h ( 32) load +I (494) boot: Loaded app from partition at offset 0x40000 +I (495) boot: Disabling RNG early entropy source... +I (505) octal_psram: vendor id : 0x0d (AP) +I (505) octal_psram: dev id : 0x02 (generation 3) +I (505) octal_psram: density : 0x03 (64 Mbit) +I (507) octal_psram: good-die : 0x01 (Pass) +I (511) octal_psram: Latency : 0x01 (Fixed) +I (516) octal_psram: VCC : 0x01 (3V) +I (520) octal_psram: SRF : 0x01 (Fast Refresh) +I (525) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (530) octal_psram: BurstLen : 0x01 (32 Byte) +I (534) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (539) octal_psram: DriveStrength: 0x00 (1/1) +I (544) esp_psram: Found 8MB PSRAM device +I (547) esp_psram: Speed: 40MHz +I (550) cpu_start: Multicore app +I (1284) esp_psram: SPI SRAM memory test OK +I (1292) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1293) cpu_start: Pro cpu start user code +I (1293) cpu_start: cpu freq: 160000000 Hz +I (1295) app_init: Application information: +I (1299) app_init: Project name: kota-sensorboard +I (1304) app_init: App version: 1.0 +I (1307) app_init: Compile time: Jun 29 2026 11:03:32 +I (1312) app_init: ELF file SHA256: 45fa9d5b9... +I (1317) app_init: ESP-IDF: v5.5.3-dirty +I (1321) efuse_init: Min chip rev: v0.0 +I (1325) efuse_init: Max chip rev: v0.99 +I (1329) efuse_init: Chip rev: v0.2 +I (1333) heap_init: Initializing. RAM available for dynamic allocation: +I (1340) heap_init: At 3FCB3FD0 len 00035740 (213 KiB): RAM +I (1345) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1350) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1355) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1361) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1368) spi_flash: detected chip: generic +I (1371) spi_flash: flash io: dio +I (1377) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1381) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1396) main_task: Started on CPU0 +I (1399) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1407) main_task: Calling app_main() +E (1410) ID: Device MAC address: A4:CB:8F:C9:46:F8 +I (1435) OTA_CLIENT: No OTA token found in NVS, need to register device +I (1439) MAIN: Matter node created successfully +W (1441) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1444) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1453) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1462) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1471) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1479) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1488) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1497) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1506) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1514) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1523) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1532) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1541) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1549) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1558) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1567) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1576) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1585) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1595) pp: pp rom version: e7ae62f +I (1597) net80211: net80211 rom version: e7ae62f +I (1602) wifi:wifi driver task: 3fcc52d0, prio:23, stack:6656, core=0 +I (1619) wifi:wifi firmware version: 4df78f2 +I (1620) wifi:wifi certification version: v7.0 +I (1620) wifi:config NVS flash: enabled +I (1621) wifi:config nano formatting: disabled +I (1625) wifi:Init data frame dynamic rx buffer num: 32 +I (1630) wifi:Init static rx mgmt buffer num: 5 +I (1634) wifi:Init management short buffer num: 32 +I (1638) wifi:Init dynamic tx buffer num: 32 +I (1642) wifi:Init static tx FG buffer num: 2 +I (1647) wifi:Init static rx buffer size: 1600 +I (1651) wifi:Init static rx buffer num: 10 +I (1655) wifi:Init dynamic rx buffer num: 32 +I (1659) wifi_init: rx ba win: 6 +I (1661) wifi_init: accept mbox: 6 +I (1665) wifi_init: tcpip mbox: 32 +I (1668) wifi_init: udp mbox: 6 +I (1671) wifi_init: tcp mbox: 6 +I (1673) wifi_init: tcp tx win: 5760 +I (1677) wifi_init: tcp rx win: 5760 +I (1680) wifi_init: tcp mss: 1440 +I (1692) chip[DL]: NVS set: chip-counters/reboot-count = 15843 (0x3DE3) +I (1695) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (1697) BLE_INIT: BT controller compile version [1bb2f50] +I (1702) BLE_INIT: Using main XTAL as clock source +I (1706) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (1715) BLE_INIT: Bluetooth MAC: a4:cb:8f:c9:46:fa +I (1720) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +I (1767) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(0) +I (1784) NimBLE: GAP procedure initiated: stop advertising. + +I (1786) CHIP[DL]: BLE host-controller synced +I (2286) chip[DL]: Starting ESP WiFi layer +I (2288) wifi:mode : sta (a4:cb:8f:c9:46:f8) +I (2289) wifi:enable tsf +W (2289) wifi:Haven't to connect to a suitable AP now! +I (2290) chip[DL]: +I (2290) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43 +Done driving station state, nothing else to do... +I (2299) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 +W (2303) wifi:Haven't to connect to a suitable AP now! +I (2314) chip[DL]: Done driving station state, nothing else to do... +I (2320) chip[SVR]: Initializing subscription resumption storage... +I (2327) chip[SVR]: Server initializing... +I (2330) chip[TS]: Last Known Good Time: 2026-08-04T05:56:06 +I (2336) chip[DMG]: AccessControl: initializing +I (2339) chip[DMG]: Examples::AccessControlDelegate::Init +I (2344) chip[DMG]: AccessControl: setting +I (2348) chip[DMG]: DefaultAclStorage: initializing +I (2353) chip[DMG]: DefaultAclStorage: 0 entries loaded +I (2358) chip[SVR]: WARNING: mTestEventTriggerDelegate is null +I (2377) chip[ZCL]: Using ZAP configuration... +I (2378) esp_matter_cluster: Cluster plugin init common callback +I (2378) chip[DMG]: AccessControlCluster: initializing +I (2381) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported +I (2393) chip[DIS]: Updating services using commissioning mode 1 +I (2395) chip[DIS]: CHIP minimal mDNS started advertising. +I (2400) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 +I (2410) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 73BA585012FF9068. +I (2420) chip[DIS]: mDNS service published: _matterc._udp +I (2425) chip[IN]: CASE Server enabling CASE session setups +I (2431) chip[SVR]: Joining Multicast groups +I (2434) chip[SVR]: Server Listening... +I (2439) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** +I (2449) data_model: Dynamic endpoint 0 added +E (2452) data_model: Cluster cannot be NULL. +I (2457) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** +I (2467) data_model: Dynamic endpoint 1 added +E (2471) data_model: Cluster cannot be NULL. +I (2476) chip[DL]: WIFI_EVENT_STA_START +I (2477) MAIN: ESP-Matter started successfully +W (2479) wifi:Haven't to connect to a suitable AP now! +I (2488) chip[DL]: Done driving station state, nothing else to do... +I (2495) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) +I (2505) NimBLE: GAP procedure initiated: advertise; +I (2506) NimBLE: disc_mode=2 + +I (2506) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 +I (2511) NimBLE: + +I (2512) chip[DL]: CHIPoBLE advertising started +I (2513) MAIN: Commissioning window opened +> +I (4060) I2C: Creating I2C mutex +I (14061) SPI: Creating SPI mutex +I (14103) INFLUX_BUFFER: SD card mounted successfully +I (14104) INFLUX_BUFFER: /sdcard exists, SD card initialized successfully +I (14107) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +W (14121) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (14145) BME688: Delaying for 10000 microseconds +I (14763) Flicker: Flicker sensor initialized on pin 14 +I (14765) temperature_sensor: Range [20°C ~ 100°C], error < 2°C +I (14767) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +I (14767) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data +W (14772) wifi:Haven't to connect to a suitable AP now! +I (14768) SEN66: Creating sensor mutex +W (14782) AS7341: Reading spectral data from AS7341 +I (14775) INFLUX_BUFFER: Flushing chunk starting at line 10: +I (14796) INFLUX: Writing to InfluxDB +I (14784) SEN66: Waking up sensor by setting power GPIO to enabled level... +I (14783) DeviceStatus: Active tasks: 26 +I (14813) DeviceStatus: Task list: +gather_sensor_t X 1 1344 22 +IDLE1 R 0 812 6 +IDLE0 R 0 692 5 +gather_sensor_t B 1 2552 21 +ds1307_update B 1 800 17 +gather_task B 1 1964 19 +gather_task B 1 14032 20 +alive_diode_tas B 5 352 8 +reset_task B 0 3432 15 +tiT B 18 2420 10 +gather_sensor_t B 1 1720 24 +ota_task B 1 7516 18 +CHIP B 1 3700 12 +Tmr Svc B 1 1364 7 +gather_sensor_t B 1 1092 25 +sys_evt B 20 2684 9 +ipc0 S 24 372 1 +ipc1 S 24 524 2 +console B 5 648 16 +esp_timer S 22 3332 3 +gather_sensor_t B 1 2332 23 +gather_sensor_t B 1 2544 26 +wifi B 23 4324 11 +btController B 23 1960 13 +main B 1 876 4 +nimble_host B 21 1480 14 + +W (14878) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (15105) AS7341: Reading basic counts data from AS7341 +W (15121) AS7341: Reading flicker detection state from AS7341 +W (15339) as7341: FD Status Register: 0x20 (0b00100000) +I (15345) AS7341: Updating Matter attributes with AS7341 data: 16707 +I (15345) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms +I (15359) Flicker: Starting to collect samples in gather task +I (15355) BME688: Delaying for 199517 microseconds +I (15347) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** +I (15374) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 16707 ********** +E (15386) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +E (15398) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +W (15400) AS7341: Formatting AS7341 data in line protocol +AS7341 line protocol: +as7341_adc,device=A4CB8FC946F8 f1=1,f2=2,f3=2,f4=3,f5=3,f6=4,f7=7,f8=9,clear=21,nir=5 946684801000000000 +as7341_basic_counts,device=A4CB8FC946F8 f1=2.23,f2=4.46,f3=4.46,f4=6.69,f5=6.69,f6=8.92,f7=15.62,f8=20.08,clear=46.85,nir=11.15 946684801000000000 +as7341_flicker,device=A4CB8FC946F8 state="unknown" 946684801000000000 +W (15890) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (16383) Flicker: Finished collecting samples, notifying main task +I (16385) Flicker: Collected 1024 samples for dominant frequency analysis +I (16392) SENSOR_GATHERER: Sensor should now be asleep +I (16404) SENSOR_GATHERER: Sensor should now be asleep +I (16444) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16446) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1018 ********** +E (16459) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16471) MAIN: Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16473) BME688: RTC is uninitialized, using current time as timestamp +I (16891) SENSOR_GATHERER: Sensor should now be asleep +W (17484) BME688: RTC is uninitialized, using current time as timestamp --- disconnected --- \ No newline at end of file From ab420220e1f00a9ed174ac043fc4f2d8bb5ce769 Mon Sep 17 00:00:00 2001 From: Krzysztof Kallas Date: Tue, 29 Sep 2026 17:29:30 +0200 Subject: [PATCH 2/3] Wi-Fi over USB tab for firmware with the USB console (v13) Writes / reads / removes the box's Wi-Fi over the serial port with the usb_console commands (kota ping, wifi set|get|clear, health) and follows the box through the restart until it joins the network. The password is sent hex-encoded and never logged. firmware/ now holds merged-binary-v13.bin (build 3ae273ec) instead of the previous 1.0.16 build; error_ids.csv synced with the firmware. Co-Authored-By: Claude Opus 5.5 --- README.md | 9 + docs/CHANGES-v0.10.md | 14 + ...erged-binary.bin => merged-binary-v13.bin} | Bin 2405280 -> 2411504 bytes kota_tech/diag/engine.py | 22 ++ kota_tech/diag/error_ids.csv | 1 + kota_tech/diag/kota.py | 1 + kota_tech/serialio/commands.py | 283 ++++++++++++++++++ kota_tech/ui/main_window.py | 40 +++ kota_tech/ui/wifi_tab.py | 207 +++++++++++++ tests/test_commands.py | 162 ++++++++++ tests/test_ui.py | 19 ++ 11 files changed, 758 insertions(+) create mode 100644 docs/CHANGES-v0.10.md rename firmware/{merged-binary.bin => merged-binary-v13.bin} (61%) create mode 100644 kota_tech/serialio/commands.py create mode 100644 kota_tech/ui/wifi_tab.py create mode 100644 tests/test_commands.py create mode 100644 tests/test_ui.py diff --git a/README.md b/README.md index 64b29fd..d0e843d 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,15 @@ Sesje, kopie zapasowe flash i ustawienia są w `~/KotaTechnicianTool` (Windows: Kabel USB musi przenosić dane (nie tylko ładowanie). Przy restartach z komunikatem o brownout użyj innego portu lub zasilacza. +## Wi-Fi przez USB (bez otwierania obudowy) + +Zakładka **Wi-Fi (USB)**: podłącz box kablem USB, kliknij **Connect to the box**, wpisz nazwę sieci i hasło, +kliknij **Write to the box and connect**. Box zapisuje je na karcie SD, restartuje się i łączy z siecią +(zamiast Matter); zakładka pokazuje każdy krok aż do „Connected ... (signal -58 dBm)”. +**Read current Wi-Fi settings** pokazuje zapisaną sieć (bez hasła), **Remove Wi-Fi** wraca do Matter. +Wymaga firmware z komendami USB (plik `firmware/merged-binary-v13.bin` lub nowszy); starszy firmware +wgraj w zakładce **Flash firmware** (tryb App only zachowuje parowanie i ustawienia). + ## Firmware Wgrać można tylko pliki `.bin` z folderu `firmware/`. Lista pokazuje datę kompilacji zapisaną w pliku, diff --git a/docs/CHANGES-v0.10.md b/docs/CHANGES-v0.10.md new file mode 100644 index 0000000..9b8ed13 --- /dev/null +++ b/docs/CHANGES-v0.10.md @@ -0,0 +1,14 @@ +# Technician tool v0.10 - changes (draft) + +- New tab **Wi-Fi (USB)**: write the Wi-Fi name and password to the box's SD card over the USB cable, without + opening the case (firmware usb_console, `kota wifi set`). Shows each step: command support check, stored, + restart in Wi-Fi mode, connected with signal strength (polls `kota health`). Also reads the stored network + (`kota wifi get`, password never shown) and removes it (`kota wifi clear`, back to Matter). +- The password is sent hex-encoded, never written to the session log or the service report. +- Diagnosis: restarts `usb_wifi_set` / `usb_wifi_clear` are intentional (no "unexpected restart"); box + self-report shows the stored Wi-Fi network; `cmd health` replies update the health line. +- firmware/: `merged-binary-v13.bin` (usb_console, build 3ae273ec) replaces `merged-binary.bin` (build 4f10989e, + still in git tag v0.9). Note: v13 reports the same version 1.0.16 and compile date as the previous build; + the firmware team should bump the version for the next build. +- error_ids.csv updated from the firmware (E-CON-01). +- Test that builds the main window (catches start-up errors). diff --git a/firmware/merged-binary.bin b/firmware/merged-binary-v13.bin similarity index 61% rename from firmware/merged-binary.bin rename to firmware/merged-binary-v13.bin index 106443dcc54529ef7c1e1b4a7d150fa9e133f410..bbb3ab65eca6661d8a87f7011d4b326614ed5ed1 100644 GIT binary patch delta 581056 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

    K6ht|QDAPzKVb5a-uNlO0iQ)FL3B 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�As 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%0Dk#~~2#k%6WP+D~X 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 Qt8Y!Yl5*hvxBs*9|J)qvJ^%m! delta 575428 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

    6?Pu!vG-TN_+ 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�S8n28PGfyC#Gz`ekJH=?u|x2221DG< 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 ? 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 jb>I{|dCrT4O%J2nV)tH=Him;0G$ diff --git a/kota_tech/diag/engine.py b/kota_tech/diag/engine.py index 3805f34..d06c2af 100644 --- a/kota_tech/diag/engine.py +++ b/kota_tech/diag/engine.py @@ -335,6 +335,8 @@ class DiagnosticsEngine: self.matter_qr: str | None = None # "KOTA: matter ... qr=MT:..." self.matter_manual: str | None = None self.kota_seen = False # firmware prints KOTA: lines + self.console_api: int | None = None # "cmd ping ... api=N": box accepts USB commands + self.wifi_config: dict[str, str] | None = None # last "cmd wifi_get" reply (never a password) self._reset_led_state() def _reset_led_state(self) -> None: @@ -714,6 +716,20 @@ class DiagnosticsEngine: fd = self._custom(key, Severity[sev.upper()], title, meaning, actions, expl) self._touch(fd, line) + def _kota_cmd(self, line: LogLine, f: dict[str, str]) -> None: + """Replies to commands sent by this tool (firmware usb_console).""" + name = line.words[0] if line.words else "" + if f.get("ok") != "1": + return # the Wi-Fi tab reports failures itself + if name == "ping": + api = f.get("api", "") + self.console_api = int(api) if api.isdigit() else None + elif name == "health": + self.boot.kota["health"] = line # same fields as the periodic health line + self._kota_health(line, f) + elif name == "wifi_get": + self.wifi_config = {k: f.get(k, "?") for k in ("ssid", "password_set", "mode")} + def _kota_led(self, line: LogLine, f: dict[str, str]) -> None: if "previous_boot" in line.words: code = f.get("code", "0") @@ -845,6 +861,12 @@ class DiagnosticsEngine: if ln := k.get("mic"): on = ln.fields.get("enabled") == "1" out.append(("Microphone", "on" if on else "switched OFF (switch on the back)", "ok" if on else "info")) + if self.wifi_config: + from ..serialio.commands import unhex_utf8 + w = self.wifi_config + ssid = unhex_utf8(w["ssid"]) + out.append(("Wi-Fi config", (f"network '{ssid}', password {'set' if w['password_set'] == '1' else 'NOT set'}" + if ssid else "no network on the SD card") + f", mode {w['mode']}", "info")) if ln := k.get("health"): f = ln.fields wifi = f.get("wifi", "?") diff --git a/kota_tech/diag/error_ids.csv b/kota_tech/diag/error_ids.csv index b38ceaf..a7de0ef 100644 --- a/kota_tech/diag/error_ids.csv +++ b/kota_tech/diag/error_ids.csv @@ -191,3 +191,4 @@ E-GATH-01,GATH,error,Failed to take line protocol mutex,Internal error - send th E-GATH-02,GATH,error,Failed to get line protocol from sensor,Device ran out of memory; restart it and send the log to the firmware team E-GATH-03,GATH,error,Failed to create task for sensor,Internal error - send the log to the firmware team E-GATH-04,GATH,error,Failed to create main gather task,Internal error - send the log to the firmware team +E-CON-01,CON,error,Failed to install USB-Serial-JTAG driver: %s,Internal error - send the log to the firmware team diff --git a/kota_tech/diag/kota.py b/kota_tech/diag/kota.py index 95c730a..7eb84fd 100644 --- a/kota_tech/diag/kota.py +++ b/kota_tech/diag/kota.py @@ -39,6 +39,7 @@ I2C_DEVICES = {"0x39": "AS7341", "0x68": "DS1307", "0x6B": "SEN66", "0x76": "BME INTENTIONAL_RESTARTS = { "ota_update", "button_provisioning_abort", "button_wifi_cleared", "button_provisioning_toggle", "provisioning_saved", + "usb_wifi_set", "usb_wifi_clear", # Wi-Fi changed from the technician tool (usb_console) } MODULES = { diff --git a/kota_tech/serialio/commands.py b/kota_tech/serialio/commands.py new file mode 100644 index 0000000..56b4f8a --- /dev/null +++ b/kota_tech/serialio/commands.py @@ -0,0 +1,283 @@ +"""Commands to the box over the USB serial port (firmware with usb_console, api=1). + +Protocol (kota-sensorboard main/usb_console.hpp): ASCII lines "kota ...", +one reply line each, logged as "KOTA: cmd ok=1|0 [key=value ...]": + + kota ping -> cmd ping ok=1 fw= api=1 + kota health -> cmd health ok=1 | ok=0 err=not_ready + kota wifi get -> cmd wifi_get ok=1 ssid= password_set=0|1 mode=... + kota wifi set -> cmd wifi_set ok=1 restart=1, then restart (usb_wifi_set) + kota wifi clear -> cmd wifi_clear ok=1 restart=1, then restart (usb_wifi_clear) + +Firmware without the console never answers. The password is only ever sent +hex-encoded and is never written to the log by the box or by this tool. + +WifiFlow runs a whole "write / clear Wi-Fi" job as a small state machine +driven by `feed(line)` and `tick(now)`, so it is testable without Qt or a box. +""" + +from __future__ import annotations + +import enum +import time +from collections.abc import Callable +from dataclasses import dataclass, field + +from ..core.logline import LineKind, LogLine + +API_VERSION = 1 + +# Technician-facing text for the firmware's err= codes. +ERRORS = { + "bad_args": "The box did not understand the command (tool/firmware mismatch).", + "bad_hex": "The box could not decode the settings (tool/firmware mismatch).", + "bad_char": "The name or password contains a line break.", + "ssid_length": "The network name must be 1-32 bytes.", + "password_length": "The password must be 8-63 characters.", + "sd_not_mounted": "The box cannot read its SD card, so it cannot store the settings.", + "write_failed": "Writing ENV.TXT on the SD card failed.", + "verify_failed": "ENV.TXT did not read back correctly after writing.", + "unknown_command": "This firmware does not know the command.", + "not_ready": "The box is still starting; try again in a few seconds.", +} + + +def hex_utf8(text: str) -> str: + return text.encode("utf-8").hex() + + +def unhex_utf8(value: str) -> str | None: + if not value or value == "-": + return None + try: + return bytes.fromhex(value).decode("utf-8", errors="replace") + except ValueError: + return None + + +def validate_wifi(ssid: str, password: str) -> list[str]: + """Same limits as the firmware (usb_console.cpp); empty list = OK.""" + problems = [] + if not ssid: + problems.append("Enter the Wi-Fi network name (SSID).") + elif len(ssid.encode("utf-8")) > 32: + problems.append("The network name is longer than 32 bytes.") + if not 8 <= len(password.encode("utf-8")) <= 63: + problems.append("The password must be 8-63 characters (WPA2). Open networks are not supported.") + if any(c in ssid + password for c in "\r\n\0"): + problems.append("The name or password contains a line break.") + return problems + + +def wifi_set_command(ssid: str, password: str) -> str: + problems = validate_wifi(ssid, password) + if problems: + raise ValueError(" ".join(problems)) + return f"kota wifi set {hex_utf8(ssid)} {hex_utf8(password)}" + + +def loggable(command: str) -> str: + """The command as it may appear in the session log: never the password.""" + parts = command.split() + if parts[:3] == ["kota", "wifi", "set"] and len(parts) == 5: + return f"kota wifi set (network '{unhex_utf8(parts[3])}', password hidden)" + return command + + +@dataclass(frozen=True) +class Reply: + name: str # ping | health | wifi_get | wifi_set | wifi_clear | unknown + ok: bool + fields: dict[str, str] + + @property + def error(self) -> str: + code = self.fields.get("err", "") + return ERRORS.get(code, f"The box reported an error ({code or 'unknown'}).") + + +def parse_reply(line: LogLine) -> Reply | None: + """'I (5) KOTA: cmd wifi_set ok=1 restart=1' -> Reply('wifi_set', True, {...}).""" + if line.kind != LineKind.KOTA or line.record != "cmd" or not line.words: + return None + return Reply(line.words[0], line.fields.get("ok") == "1", dict(line.fields)) + + +@dataclass(frozen=True) +class WifiInfo: + """What `kota wifi get` reported.""" + ssid: str | None + password_set: bool + mode: str + + @classmethod + def from_reply(cls, r: Reply) -> WifiInfo: + return cls(unhex_utf8(r.fields.get("ssid", "-")), r.fields.get("password_set") == "1", + r.fields.get("mode", "?")) + + def describe(self) -> str: + mode = {"wifi": "local Wi-Fi from the SD card", "matter": "Matter (Wi-Fi given by the Matter app)", + "provisioning": "Wi-Fi setup portal"}.get(self.mode, self.mode) + net = f"network '{self.ssid}'" + (", password set" if self.password_set else ", NO password") + return f"Mode: {mode}. " + (net if self.ssid else "No network stored on the SD card.") + + +# ------------------------------------------------------------------ flows + +class Step(str, enum.Enum): + PING = "Checking the box accepts USB commands" + READ = "Reading the Wi-Fi settings" + SEND = "Storing the settings on the box's SD card" + RESTART = "Waiting for the box to restart" + CONNECT = "Waiting for the box to join the Wi-Fi network" + DONE = "Done" + FAILED = "Failed" + + +@dataclass +class FlowEvent: + step: Step + text: str + ok: bool | None = None # None = in progress, True = finished OK, False = failed + + +PING_TIMEOUT_S = 3.0 +REPLY_TIMEOUT_S = 8.0 +RESTART_TIMEOUT_S = 40.0 +CONNECT_TIMEOUT_S = 90.0 +HEALTH_POLL_S = 5.0 + + +@dataclass +class WifiFlow: + """One technician job: "read", "set" (ssid+password) or "clear". + + `send(text)` writes one command line to the box (returns False when the + port is not connected); `on_event` reports progress for the UI. + """ + action: str + send: Callable[[str], bool] + on_event: Callable[[FlowEvent], None] + ssid: str = "" + password: str = "" + now: Callable[[], float] = time.monotonic + step: Step = Step.PING + info: WifiInfo | None = None + _deadline: float = 0.0 + _next_poll: float = 0.0 + _events: list[FlowEvent] = field(default_factory=list) + + @property + def finished(self) -> bool: + return self.step in (Step.DONE, Step.FAILED) + + def start(self) -> None: + if self.action == "set": + problems = validate_wifi(self.ssid, self.password) + if problems: + return self._fail(" ".join(problems)) + self._go(Step.PING, "kota ping", PING_TIMEOUT_S) + + def cancel(self) -> None: + if not self.finished: + self._fail("Cancelled.") + + # -------------------------------------------------------- driving + def feed(self, line: LogLine) -> None: + if self.finished: + return + if self.step == Step.RESTART and line.kind == LineKind.KOTA and line.record == "boot": + return self._on_boot(line.fields) + r = parse_reply(line) + if r is None: + return + if self.step == Step.PING and r.name == "ping": + if not r.ok or int(r.fields.get("api", "0") or 0) < API_VERSION: + return self._fail("The box answered, but its command version is not supported.") + self._emit(f"Firmware {r.fields.get('fw', '?')} accepts USB commands.", True) + if self.action == "read": + return self._go(Step.READ, "kota wifi get", REPLY_TIMEOUT_S) + if self.action == "set": + return self._go(Step.SEND, wifi_set_command(self.ssid, self.password), REPLY_TIMEOUT_S) + return self._go(Step.SEND, "kota wifi clear", REPLY_TIMEOUT_S) + if self.step == Step.READ and r.name == "wifi_get": + if not r.ok: + return self._fail(r.error) + self.info = WifiInfo.from_reply(r) + return self._finish(self.info.describe()) + if self.step == Step.SEND and r.name in ("wifi_set", "wifi_clear"): + if not r.ok: + return self._fail(r.error) + self._emit("The box stored the settings and is restarting.", True) + self.step, self._deadline = Step.RESTART, self.now() + RESTART_TIMEOUT_S + return self._emit(Step.RESTART.value + "...") + if self.step == Step.CONNECT and r.name == "health" and r.ok: + return self._on_health(r.fields) + + def tick(self) -> None: + """Call periodically (e.g. every 0.5 s): timeouts and health polling.""" + if self.finished: + return + t = self.now() + if self.step == Step.CONNECT and t >= self._next_poll: + self._next_poll = t + HEALTH_POLL_S + self.send("kota health") + if t < self._deadline: + return + if self.step == Step.PING: + self._fail("The box does not answer USB commands. It needs firmware with the USB console " + "(Flash firmware tab), or it is still starting - try again in 10 s.") + elif self.step == Step.RESTART: + self._fail("The box stored the settings but no restart was seen. Press Reset & capture in the " + "Serial monitor and check the Diagnosis panel.") + elif self.step == Step.CONNECT: + self._fail(f"The box did not join '{self.ssid}' within {int(CONNECT_TIMEOUT_S)} s. Check the network " + "name and password, that it is a 2.4 GHz network and that it is in range. The settings stay " + "on the box; it keeps retrying.") + else: + self._fail("The box did not answer. Check the USB connection and try again.") + + # -------------------------------------------------------- internals + def _on_boot(self, f: dict[str, str]) -> None: + mode, last = f.get("mode", "?"), f.get("last_restart", "?") + want = "usb_wifi_set" if self.action == "set" else "usb_wifi_clear" + if last != want: + return self._fail(f"The box restarted for another reason ({last}); the settings may not be applied.") + self._emit(f"Restarted in {mode} mode.", True) + if self.action == "clear": + ok = mode == "matter" + return self._finish("Wi-Fi removed; the box uses Matter again (pair it in the Matter app if needed).") \ + if ok else self._fail(f"The box restarted in {mode} mode instead of Matter.") + if mode != "wifi": + return self._fail(f"The box restarted in {mode} mode instead of local Wi-Fi.") + self.step = Step.CONNECT + self._deadline = self.now() + CONNECT_TIMEOUT_S + self._next_poll = self.now() + HEALTH_POLL_S + self._emit(Step.CONNECT.value + f" '{self.ssid}'...") + + def _on_health(self, f: dict[str, str]) -> None: + if f.get("wifi") != "connected": + return + rssi = f.get("rssi", "?") + note = " - weak signal, consider moving the box or the router" if rssi.lstrip("-").isdigit() \ + and int(rssi) < -75 else "" + self._finish(f"Connected to '{self.ssid}' (signal {rssi} dBm{note}). Data upload starts within a minute.") + + def _go(self, step: Step, command: str, timeout: float) -> None: + self.step, self._deadline = step, self.now() + timeout + self._emit(step.value + "...") + if not self.send(command): + self._fail("The box is not connected. Connect it first.") + + def _emit(self, text: str, ok: bool | None = None) -> None: + ev = FlowEvent(self.step, text, ok) + self._events.append(ev) + self.on_event(ev) + + def _finish(self, text: str) -> None: + self.step = Step.DONE + self._emit(text, True) + + def _fail(self, text: str) -> None: + self.step = Step.FAILED + self._emit(text, False) diff --git a/kota_tech/ui/main_window.py b/kota_tech/ui/main_window.py index 2dd9359..7ae146c 100644 --- a/kota_tech/ui/main_window.py +++ b/kota_tech/ui/main_window.py @@ -28,6 +28,7 @@ from ..core.logline import LineKind, LogLine, host_event from ..core.session import SessionLog from ..diag.engine import DiagnosticsEngine from ..diag.guides import GuideRunner +from ..serialio.commands import loggable from ..serialio.manager import SerialManager, State from ..serialio.ports import PortInfo, list_serial_ports from ..matter.payload import SetupPayload, payload_from_log @@ -38,6 +39,7 @@ from .flash_tab import FlashTab from .matter_qr import MatterQrDialog from .guide_panel import GuidePanel from .sd_tab import SdTab +from .wifi_tab import WifiTab MAX_LINES = 20000 # "KOTA" = the firmware's structured diagnostic lines (1.0.16+) plus errors. @@ -76,6 +78,7 @@ class SerialTab(QWidget): self._engine = DiagnosticsEngine() self._diag_dirty = True self._runner = GuideRunner(self._engine, on_action=self._guide_action, on_log=self._guide_log) + self._line_listeners: list = [] # other tabs following the live log (Wi-Fi over USB) # --- controls self.port_combo = QComboBox(minimumWidth=320) @@ -237,6 +240,39 @@ class SerialTab(QWidget): def engine(self) -> DiagnosticsEngine: return self._engine + # ---- commands to the box (firmware usb_console) ----------------------- + @property + def is_connected(self) -> bool: + return self._manager.state == State.CONNECTED + + @property + def active_port(self) -> PortInfo | None: + return self._active_port if self._manager.is_running else None + + def add_line_listener(self, callback) -> None: + self._line_listeners.append(callback) + + def send_command(self, command: str) -> bool: + """Write one "kota ..." line to the box; False when not connected. + The session log gets the command without the Wi-Fi password.""" + if not self.is_connected: + return False + self._manager.write((command + "\n").encode("ascii")) + if not command.startswith(("kota health", "kota ping")): # polled; would flood the log + self._guide_log(f"sent to the box: {loggable(command)}") + return True + + def connect_first_box(self) -> bool: + """Connect to the first sensorbox port found (for tabs that need the box).""" + if self._manager.is_running: + return True + self.refresh_ports() + port = next((p for p in self._ports if p.is_sensorbox), None) + if port is None: + return False + self.connect_port(port, capture=False) + return True + def release_port(self) -> None: """Stop monitoring so esptool can open the port exclusively.""" if self._manager.is_running: @@ -293,6 +329,8 @@ class SerialTab(QWidget): self._session.write(line) self._engine.feed(line) self._runner.feed(line) + for listener in self._line_listeners: + listener(line) self._diag_dirty = True if line.kind == LineKind.BOOT: self._counts["boots"] += 1 @@ -402,6 +440,8 @@ class MainWindow(QMainWindow): tabs.addTab(guide, "Quick start") tabs.addTab(self.serial_tab, "Serial monitor") tabs.addTab(self.flash_tab, "Flash firmware") + self.wifi_tab = WifiTab(self.serial_tab) + tabs.addTab(self.wifi_tab, "Wi-Fi (USB)") self.sd_tab = SdTab(self.serial_tab) tabs.addTab(self.sd_tab, "SD card") self.setCentralWidget(tabs) diff --git a/kota_tech/ui/wifi_tab.py b/kota_tech/ui/wifi_tab.py new file mode 100644 index 0000000..259ebd9 --- /dev/null +++ b/kota_tech/ui/wifi_tab.py @@ -0,0 +1,207 @@ +"""Wi-Fi over USB: read / write / remove the box's Wi-Fi without opening the case. + +Needs firmware with the USB console (usb_console, api=1). The job itself is +serialio.commands.WifiFlow; this tab only collects the input and shows the +steps. Lines arrive from the Serial monitor's connection (one port owner). +""" + +from __future__ import annotations + +from PySide6.QtCore import Qt, QTimer +from PySide6.QtWidgets import ( + QCheckBox, + QFormLayout, + QGroupBox, + QHBoxLayout, + QLabel, + QLineEdit, + QListWidget, + QListWidgetItem, + QMessageBox, + QPushButton, + QVBoxLayout, + QWidget, +) +from PySide6.QtGui import QBrush, QColor + +from ..core.logline import LogLine +from ..serialio.commands import FlowEvent, WifiFlow, validate_wifi + +INTRO = ( + "Stores the Wi-Fi name and password on the box's SD card over the USB cable - no need to open the " + "case. The box then restarts and uses this network instead of Matter. Needs firmware with USB commands " + "(Flash firmware tab, newest file). Only 2.4 GHz networks with a password (8-63 characters)." +) +MARK = {True: ("✔", "#1b7a33"), False: ("✖", "#b03a2e"), None: ("…", "#2e6da4")} + + +class WifiTab(QWidget): + def __init__(self, serial_tab, parent: QWidget | None = None): + super().__init__(parent) + self.serial_tab = serial_tab + self._flow: WifiFlow | None = None + + intro = QLabel(INTRO, wordWrap=True) + + # 1. box + box = QGroupBox("1. Sensorbox") + self.box_state = QLabel(wordWrap=True) + self.connect_btn = QPushButton("Connect to the box") + self.connect_btn.clicked.connect(self._connect) + self.read_btn = QPushButton("Read current Wi-Fi settings") + self.read_btn.clicked.connect(lambda: self._run("read")) + bl = QVBoxLayout(box) + bl.addWidget(self.box_state) + brow = QHBoxLayout() + brow.addWidget(self.connect_btn) + brow.addWidget(self.read_btn) + brow.addStretch(1) + bl.addLayout(brow) + + # 2. network + net = QGroupBox("2. Wi-Fi network for this box") + self.ssid = QLineEdit(placeholderText="network name (SSID), exactly as on the router") + self.password = QLineEdit(placeholderText="8-63 characters") + self.password.setEchoMode(QLineEdit.EchoMode.Password) + show = QCheckBox("Show password") + show.toggled.connect(lambda on: self.password.setEchoMode( + QLineEdit.EchoMode.Normal if on else QLineEdit.EchoMode.Password)) + form = QFormLayout() + form.addRow("Network (SSID)", self.ssid) + prow = QHBoxLayout() + prow.addWidget(self.password, 1) + prow.addWidget(show) + form.addRow("Password", prow) + self.input_problem = QLabel(wordWrap=True) + self.input_problem.setStyleSheet("color:#b03a2e") + self.write_btn = QPushButton("Write to the box and connect") + self.write_btn.setMinimumHeight(34) + self.write_btn.clicked.connect(lambda: self._run("set")) + self.clear_btn = QPushButton("Remove Wi-Fi (back to Matter)") + self.clear_btn.clicked.connect(self._clear) + nl = QVBoxLayout(net) + nl.addLayout(form) + nl.addWidget(self.input_problem) + arow = QHBoxLayout() + arow.addWidget(self.write_btn) + arow.addWidget(self.clear_btn) + arow.addStretch(1) + nl.addLayout(arow) + + # 3. progress + prog = QGroupBox("3. Progress") + self.steps = QListWidget() + self.steps.setSelectionMode(QListWidget.SelectionMode.NoSelection) + self.result = QLabel(wordWrap=True) + self.result.setMargin(8) + self.result.setVisible(False) + self.cancel_btn = QPushButton("Cancel") + self.cancel_btn.clicked.connect(self._cancel) + pl = QVBoxLayout(prog) + pl.addWidget(self.steps) + pl.addWidget(self.result) + pl.addWidget(self.cancel_btn, 0, Qt.AlignmentFlag.AlignLeft) + + lay = QVBoxLayout(self) + lay.addWidget(intro) + lay.addWidget(box) + lay.addWidget(net) + lay.addWidget(prog, 1) + + serial_tab.add_line_listener(self._on_line) + for w in (self.ssid, self.password): + w.textChanged.connect(self._update) + self._timer = QTimer(self, interval=500) + self._timer.timeout.connect(self._tick) + self._timer.start() + self._update() + + # ------------------------------------------------------------ state + def showEvent(self, event): # noqa: N802 - refresh when the tab is opened + self._update() + super().showEvent(event) + + def _update(self) -> None: + connected = self.serial_tab.is_connected + busy = self._flow is not None and not self._flow.finished + port = self.serial_tab.active_port + eng = self.serial_tab.engine + if connected and port: + fw = eng.firmware.describe() if eng.firmware.elf_sha else "firmware not identified yet" + api = {None: "", 1: " - USB commands supported"}.get(eng.console_api, f" - USB commands api {eng.console_api}") + self.box_state.setText(f"Connected: {port.label}
    {fw}{api}") + elif self.serial_tab.active_port: + self.box_state.setText("Waiting for the box... (it may be restarting)") + else: + self.box_state.setText("Not connected. Plug in the sensorbox (native USB) and press " + "Connect to the box.") + problems = validate_wifi(self.ssid.text(), self.password.text()) + typed = bool(self.ssid.text() or self.password.text()) + self.input_problem.setText(" ".join(problems) if typed else "") + self.connect_btn.setVisible(not self.serial_tab.active_port) + for w in (self.read_btn, self.clear_btn): + w.setEnabled(connected and not busy) + self.write_btn.setEnabled(connected and not busy and not problems) + self.ssid.setEnabled(not busy) + self.password.setEnabled(not busy) + self.cancel_btn.setVisible(busy) + + def _tick(self) -> None: + if self._flow is not None: + self._flow.tick() + self._update() + + # ------------------------------------------------------------ actions + def _connect(self) -> None: + if not self.serial_tab.connect_first_box(): + QMessageBox.information(self, "Wi-Fi over USB", "No sensorbox found. Check the USB cable (data-capable) " + "and that the box is powered, then try again.") + self._update() + + def _clear(self) -> None: + if QMessageBox.question( + self, "Remove Wi-Fi", + "Remove the Wi-Fi network from the box?\n\nThe box restarts and uses Matter again. If it was never " + "paired in Matter, it has no network until you pair it in the Matter app.", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) == QMessageBox.StandardButton.Yes: + self._run("clear") + + def _run(self, action: str) -> None: + if action == "set" and QMessageBox.question( + self, "Write Wi-Fi", + f"Store network '{self.ssid.text()}' on the box?\n\nThe box restarts and connects to this Wi-Fi " + "instead of Matter (about 30 s).", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) != QMessageBox.StandardButton.Yes: + return + self.steps.clear() + self.result.setVisible(False) + self._flow = WifiFlow(action, self.serial_tab.send_command, self._on_event, + ssid=self.ssid.text(), password=self.password.text()) + self._flow.start() + self._update() + + def _cancel(self) -> None: + if self._flow is not None: + self._flow.cancel() + self._update() + + # ------------------------------------------------------------ events + def _on_line(self, line: LogLine) -> None: + if self._flow is not None: + self._flow.feed(line) + + def _on_event(self, ev: FlowEvent) -> None: + mark, colour = MARK[ev.ok] + item = QListWidgetItem(f"{mark} {ev.text}") + item.setForeground(QBrush(QColor(colour))) + self.steps.addItem(item) + self.steps.scrollToBottom() + if self._flow is not None and self._flow.finished or ev.ok is False: + ok = ev.ok is True + border, bg = ("#2e9e44", "#e3f4e7") if ok else ("#d9534f", "#f9e0df") + self.result.setStyleSheet(f"QLabel {{ border:2px solid {border}; background:{bg}; border-radius:6px; }}") + self.result.setText(("Done. " if ok else "Not done. ") + ev.text) + self.result.setVisible(True) + if ok and self._flow and self._flow.info and self._flow.info.ssid and not self.ssid.text(): + self.ssid.setText(self._flow.info.ssid) # read: prefill the stored network name + self._update() diff --git a/tests/test_commands.py b/tests/test_commands.py new file mode 100644 index 0000000..4b97c96 --- /dev/null +++ b/tests/test_commands.py @@ -0,0 +1,162 @@ +"""Wi-Fi over USB (firmware usb_console api=1): encoding, replies and the whole +write/clear/read job, driven with reply lines in the firmware's exact format.""" + +import pytest + +from kota_tech.core.logline import parse_line +from kota_tech.diag.engine import DiagnosticsEngine +from kota_tech.serialio.commands import ( + CONNECT_TIMEOUT_S, PING_TIMEOUT_S, Step, WifiFlow, loggable, parse_reply, validate_wifi, wifi_set_command) + +PING = "I (5000) KOTA: cmd ping ok=1 fw=1.0.16 api=1" +BOOT = ("I (2843) KOTA: boot fw=1.0.16 build=3ae273ec hw=2 mac=A4CB8FC946F8 reset=RTC_SW_SYS_RST " + "mode={mode} commissioned=- last_restart={last}") +HEALTH = ("I (9000) KOTA: cmd health ok=1 up=9s status=OK wifi={wifi} rssi={rssi} time_valid=1 sd=ok buffer=0 " + "heap=90000 psram=7.6M last_upload=never ota=registered") + + +class Clock: + def __init__(self): + self.t = 0.0 + + def __call__(self): + return self.t + + +def make(action, **kw): + sent, events, clock = [], [], Clock() + flow = WifiFlow(action, lambda c: sent.append(c) or True, events.append, now=clock, **kw) + return flow, sent, events, clock + + +def feed(flow, text): + flow.feed(parse_line(text)) + + +# ------------------------------------------------------------- encoding + +def test_wifi_set_command_is_hex_utf8(): + assert wifi_set_command("Biuro 2.4", "tajne=haslo1") == \ + "kota wifi set 426975726f20322e34 74616a6e653d6861736c6f31" + assert wifi_set_command("Łąka", "zażółć1234").split()[3] == "c581c4856b61" + + +@pytest.mark.parametrize("ssid,password,problem", [ + ("", "12345678", "network name"), + ("x" * 33, "12345678", "32 bytes"), + ("net", "1234567", "8-63"), + ("net", "x" * 64, "8-63"), + ("net\n", "12345678", "line break"), +]) +def test_validation_matches_firmware_limits(ssid, password, problem): + assert any(problem in p for p in validate_wifi(ssid, password)) + with pytest.raises(ValueError): + wifi_set_command(ssid, password) + + +def test_password_never_logged(): + cmd = wifi_set_command("Biuro", "sekretne-haslo") + assert "sekretne" not in loggable(cmd) and "7365" not in loggable(cmd) + assert "Biuro" in loggable(cmd) + + +def test_reply_parsing(): + r = parse_reply(parse_line("I (1) KOTA: cmd wifi_set ok=0 err=password_length")) + assert r.name == "wifi_set" and not r.ok and "8-63" in r.error + assert parse_reply(parse_line("I (1) KOTA: health up=1s")) is None + + +# ----------------------------------------------------------------- flows + +def test_write_wifi_happy_path(): + flow, sent, events, clock = make("set", ssid="Biuro", password="haslo1234") + flow.start() + assert sent == ["kota ping"] + feed(flow, PING) + assert sent[-1] == wifi_set_command("Biuro", "haslo1234") and flow.step == Step.SEND + feed(flow, "I (5100) KOTA: cmd wifi_set ok=1 restart=1") + assert flow.step == Step.RESTART + feed(flow, "W (5101) KOTA: restart reason=usb_wifi_set") + feed(flow, BOOT.format(mode="wifi", last="usb_wifi_set")) + assert flow.step == Step.CONNECT + clock.t += 6 + flow.tick() + assert sent[-1] == "kota health" + feed(flow, HEALTH.format(wifi="disconnected", rssi=0)) + assert flow.step == Step.CONNECT + feed(flow, HEALTH.format(wifi="connected", rssi=-58)) + assert flow.step == Step.DONE and "Connected to 'Biuro'" in events[-1].text and "-58" in events[-1].text + + +def test_old_firmware_without_console_times_out_on_ping(): + flow, sent, events, clock = make("read") + flow.start() + clock.t += PING_TIMEOUT_S + 0.1 + flow.tick() + assert flow.step == Step.FAILED and "USB console" in events[-1].text + + +def test_box_rejects_settings(): + flow, sent, events, _ = make("set", ssid="Biuro", password="haslo1234") + flow.start() + feed(flow, PING) + feed(flow, "I (5100) KOTA: cmd wifi_set ok=0 err=sd_not_mounted") + assert flow.step == Step.FAILED and "SD card" in events[-1].text + + +def test_wrong_password_does_not_connect(): + flow, _, events, clock = make("set", ssid="Biuro", password="zlehaslo1") + flow.start() + feed(flow, PING) + feed(flow, "I (5100) KOTA: cmd wifi_set ok=1 restart=1") + feed(flow, BOOT.format(mode="wifi", last="usb_wifi_set")) + clock.t += CONNECT_TIMEOUT_S + 1 + flow.tick() + assert flow.step == Step.FAILED and "2.4 GHz" in events[-1].text + + +def test_clear_returns_to_matter(): + flow, sent, events, _ = make("clear") + flow.start() + feed(flow, PING) + assert sent[-1] == "kota wifi clear" + feed(flow, "I (5100) KOTA: cmd wifi_clear ok=1 restart=1") + feed(flow, BOOT.format(mode="matter", last="usb_wifi_clear")) + assert flow.step == Step.DONE and "Matter" in events[-1].text + + +def test_read_current_settings(): + flow, sent, events, _ = make("read") + flow.start() + feed(flow, PING) + assert sent[-1] == "kota wifi get" + feed(flow, "I (5100) KOTA: cmd wifi_get ok=1 ssid=426975726f password_set=1 mode=wifi") + assert flow.step == Step.DONE and flow.info.ssid == "Biuro" and flow.info.password_set + assert "local Wi-Fi" in events[-1].text + + +def test_not_connected(): + events = [] + flow = WifiFlow("read", lambda c: False, events.append) + flow.start() + assert flow.step == Step.FAILED and "not connected" in events[-1].text + + +# ---------------------------------------------------------------- engine + +def test_engine_treats_usb_wifi_restart_as_intentional_and_reads_replies(): + e = DiagnosticsEngine() + for t in ["ESP-ROM:esp32s3-20210327", "I (1790) KOTA: i2c SDA=1 SCL=1 0x39=AS7341", + BOOT.format(mode="matter", last="none"), PING, + "I (5100) KOTA: cmd wifi_set ok=1 restart=1", "W (5101) KOTA: restart reason=usb_wifi_set", + "ESP-ROM:esp32s3-20210327", "rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT)", + BOOT.format(mode="wifi", last="usb_wifi_set"), + "I (6000) KOTA: cmd wifi_get ok=1 ssid=426975726f password_set=1 mode=wifi", + HEALTH.format(wifi="connected", rssi=-60)]: + e.feed(parse_line(t)) + r = e.report() + assert not any(f.rule_id.startswith("reboot.") for rc in r.roots for f in [rc.finding, *rc.consequences]) + assert e.console_api == 1 + box = dict((t, x) for t, x, _ in r.box) + assert "network 'Biuro'" in box["Wi-Fi config"] and "Wi-Fi connected (-60 dBm)" in box["Health"] + assert any(m == "wifi_connected" and t is not None for m, _, t in r.milestones) diff --git a/tests/test_ui.py b/tests/test_ui.py new file mode 100644 index 0000000..0779e9d --- /dev/null +++ b/tests/test_ui.py @@ -0,0 +1,19 @@ +"""The whole window builds (catches errors that only show when the app starts).""" + +import os + +os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") + + +def test_main_window_builds_with_all_tabs(): + from PySide6.QtWidgets import QApplication + + from kota_tech.ui.main_window import MainWindow + app = QApplication.instance() or QApplication([]) + w = MainWindow() + tabs = w.centralWidget() + assert [tabs.tabText(i) for i in range(tabs.count())] == \ + ["Quick start", "Serial monitor", "Flash firmware", "Wi-Fi (USB)", "SD card"] + assert not w.wifi_tab.write_btn.isEnabled() # nothing connected, nothing typed + w.serial_tab.shutdown() + app.processEvents() From 5bd14e1d5b2ba01888bba531495b1ada1a85a427 Mon Sep 17 00:00:00 2001 From: Krzysztof Kallas Date: Tue, 29 Sep 2026 18:38:02 +0200 Subject: [PATCH 3/3] Test: live v13 box answers USB commands Capture from box 14:C1:9F:57:72:48 after flashing merged-binary-v13.bin: ping and wifi get replies, logs intact through the USB-Serial-JTAG driver. Co-Authored-By: Claude Opus 5.5 --- ...live_boot_14C19F577248_v13_usb_console.txt | 973 ++++++++++++++++++ tests/test_commands.py | 15 + 2 files changed, 988 insertions(+) create mode 100644 tests/fixtures/live_boot_14C19F577248_v13_usb_console.txt diff --git a/tests/fixtures/live_boot_14C19F577248_v13_usb_console.txt b/tests/fixtures/live_boot_14C19F577248_v13_usb_console.txt new file mode 100644 index 0000000..bdeb74c --- /dev/null +++ b/tests/fixtures/live_boot_14C19F577248_v13_usb_console.txt @@ -0,0 +1,973 @@ +--- connected to COM14 --- +W (9426) wifi:Haven't to connect to a suitable AP now! +--- reset issued by tool --- +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x15 (USB_UART_CHIP_RESET),boot:0xb (SPI_FAST_FLASH_BOOT) +Saved PC:0x4037f366 +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce2820,len:0x14f0 +load:0x403c8700,len:0xd24 +load:0x403cb700,len:0x2f48 +entry 0x403c8924 +I (24) boot: ESP-IDF v5.5.3-dirty 2nd stage bootloader +I (24) boot: compile time Sep 10 2026 13:24:39 +I (25) boot: Multicore bootloader +I (25) boot: chip revision: v0.2 +I (28) boot: efuse block revision: v1.4 +I (32) boot.esp32s3: Boot SPI Speed : 80MHz +I (35) boot.esp32s3: SPI Mode : DIO +I (39) boot.esp32s3: SPI Flash Size : 8MB +I (43) boot: Enabling RNG early entropy source... +I (48) boot: Partition Table: +I (50) boot: ## Label Usage Type ST Offset Length +I (56) boot: 0 esp_secure_cert unknown 3f 06 0000d000 00002000 +I (63) boot: 1 nvs WiFi data 01 02 00010000 00020000 +I (69) boot: 2 nvs_keys NVS keys 01 04 00030000 00001000 +I (76) boot: 3 otadata OTA data 01 00 00031000 00002000 +I (82) boot: 4 phy_init RF data 01 01 00033000 00001000 +I (89) boot: 5 ota_0 OTA app 00 10 00040000 00300000 +I (95) boot: 6 ota_1 OTA app 00 11 00340000 00300000 +I (102) boot: 7 fctry WiFi data 01 02 00640000 00006000 +I (109) boot: End of partition table +I (112) esp_image: segment 0: paddr=00040020 vaddr=3c190020 size=67420h (422944) map +I (194) esp_image: segment 1: paddr=000a7448 vaddr=3fc9c700 size=05d00h ( 23808) load +I (200) esp_image: segment 2: paddr=000ad150 vaddr=40374000 size=02ec8h ( 11976) load +I (203) esp_image: segment 3: paddr=000b0020 vaddr=42000020 size=187370h (1602416) map +I (488) esp_image: segment 4: paddr=00237398 vaddr=40376ec8 size=1580ch ( 88076) load +I (507) esp_image: segment 5: paddr=0024cbac vaddr=5000002c size=00020h ( 32) load +I (517) boot: Loaded app from partition at offset 0x40000 +I (518) boot: Disabling RNG early entropy source... +I (528) octal_psram: vendor id : 0x0d (AP) +I (528) octal_psram: dev id : 0x02 (generation 3) +I (529) octal_psram: density : 0x03 (64 Mbit) +I (530) octal_psram: good-die : 0x01 (Pass) +I (535) octal_psram: Latency : 0x01 (Fixed) +I (539) octal_psram: VCC : 0x01 (3V) +I (543) octal_psram: SRF : 0x01 (Fast Refresh) +I (548) octal_psram: BurstType : 0x01 (Hybrid Wrap) +I (553) octal_psram: BurstLen : 0x01 (32 Byte) +I (557) octal_psram: Readlatency : 0x02 (10 cycles@Fixed) +I (562) octal_psram: DriveStrength: 0x00 (1/1) +I (567) esp_psram: Found 8MB PSRAM device +I (570) esp_psram: Speed: 40MHz +I (573) cpu_start: Multicore app +I (1307) esp_psram: SPI SRAM memory test OK +I (1315) cpu_start: GPIO 44 and 43 are used as console UART I/O pins +I (1316) cpu_start: Pro cpu start user code +I (1316) cpu_start: cpu freq: 160000000 Hz +I (1318) app_init: Application information: +I (1322) app_init: Project name: kota-sensorboard +I (1327) app_init: App version: 1.0.16 +I (1331) app_init: Compile time: Sep 28 2026 15:07:57 +I (1336) app_init: ELF file SHA256: 3ae273ec1... +I (1340) app_init: ESP-IDF: v5.5.3-dirty +I (1345) efuse_init: Min chip rev: v0.0 +I (1348) efuse_init: Max chip rev: v0.99 +I (1353) efuse_init: Chip rev: v0.2 +I (1357) heap_init: Initializing. RAM available for dynamic allocation: +I (1363) heap_init: At 3FCB4AD0 len 00034C40 (211 KiB): RAM +I (1368) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM +I (1373) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM +I (1379) heap_init: At 600FE000 len 00001FE8 (7 KiB): RTCRAM +I (1384) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator +I (1392) spi_flash: detected chip: generic +I (1394) spi_flash: flash io: dio +I (1400) sleep_gpio: Configure to isolate all GPIO pins in sleep state +I (1404) sleep_gpio: Enable automatic switching of GPIO sleep configuration +I (1419) main_task: Started on CPU0 +I (1422) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations +I (1430) main_task: Calling app_main() +E (1433) ID: [E-MAIN-04] Device MAC address: 14:C1:9F:57:72:48 +I (1475) KOTA: led color=green pattern=solid +I (1676) I2C: Creating I2C mutex +I (1694) KOTA: i2c SDA=1 SCL=1 0x39=AS7341 0x68=DS1307 0x6B=SEN66 0x76=BME688 +I (1696) OTA_CLIENT: OTA token loaded from NVS: +I (1698) OTA_CLIENT: Device is not in selfcheck mode +I (1702) OTA_CLIENT: Update metadata loaded from NVS: version=15, status=2 +I (1707) OTA_CLIENT: Device is in factory state +I (1711) SPI: Creating SPI mutex +I (1755) SD_CARD: SD card mounted successfully +I (1756) SD_CARD: /sdcard exists, SD card initialized successfully +I (1758) INFLUX_BUFFER: Buffer file does not exist, returning count of 0 +I (1763) KOTA: sd type=SDHC size=29.2GB fs=FAT32 free=29.1GB env_keys=INFLUX_TOKEN,INFLUX_URL buffer_lines=0 +I (1774) WIFI_CREDENTIALS: SSID/PASSWORD not set in .env; Matter will be used for WiFi provisioning +I (1785) MAIN: Matter node created successfully +W (1787) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1793) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1802) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1811) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1819) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1828) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1837) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1846) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1854) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1863) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1872) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1881) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1890) data_model: Server Cluster 0x00000003 on endpoint 0x0001 already exists. Not creating again. +W (1898) data_model: Attribute 0x0000FFFC on cluster 0x00000003 already exists. Not creating again. +W (1907) data_model: Attribute 0x0000FFFD on cluster 0x00000003 already exists. Not creating again. +W (1916) data_model: Attribute 0x00000000 on cluster 0x00000003 already exists. Not creating again. +W (1925) data_model: Attribute 0x00000001 on cluster 0x00000003 already exists. Not creating again. +W (1933) data_model: Command 0x00000000 on cluster 0x00000003 already exists. Not creating again. +I (1944) pp: pp rom version: e7ae62f +I (1945) net80211: net80211 rom version: e7ae62f +I (1951) wifi:wifi driver task: 3fcc9610, prio:23, stack:6656, core=0 +I (1969) wifi:wifi firmware version: 4df78f2 +I (1970) wifi:wifi certification version: v7.0 +I (1971) wifi:config NVS flash: enabled +I (1971) wifi:config nano formatting: disabled +I (1975) wifi:Init data frame dynamic rx buffer num: 32 +I (1980) wifi:Init static rx mgmt buffer num: 5 +I (1984) wifi:Init management short buffer num: 32 +I (1988) wifi:Init dynamic tx buffer num: 32 +I (1992) wifi:Init static tx FG buffer num: 2 +I (1997) wifi:Init static rx buffer size: 1600 +I (2001) wifi:Init static rx buffer num: 10 +I (2004) wifi:Init dynamic rx buffer num: 32 +I (2009) wifi_init: rx ba win: 6 +I (2011) wifi_init: accept mbox: 6 +I (2014) wifi_init: tcpip mbox: 32 +I (2018) wifi_init: udp mbox: 6 +I (2020) wifi_init: tcp mbox: 6 +I (2023) wifi_init: tcp tx win: 5760 +I (2027) wifi_init: tcp rx win: 5760 +I (2030) wifi_init: tcp mss: 1440 +I (2045) chip[DL]: NVS set: chip-counters/reboot-count = 13 (0xD) +I (2048) chip[DL]: Real time clock set to 946684800 (2000-01-01 00:00:00 UTC) +I (2050) BLE_INIT: BT controller compile version [1bb2f50] +I (2053) BLE_INIT: Using main XTAL as clock source +I (2058) BLE_INIT: Feature Config, ADV:1, BLE_50:1, DTM:1, SCAN:1, CCA:0, SMP:1, CONNECT:1 +I (2067) BLE_INIT: Bluetooth MAC: 14:c1:9f:57:72:4a +I (2072) phy_init: phy_version 711,97bcf0a2,Aug 25 2025,19:04:10 +I (2119) NimBLE: GAP procedure initiated: stop advertising. + +I (2122) CHIP[DL]: BLE host-controller synced +I (2623) chip[DL]: Starting ESP WiFi layer +I (2626) wifi:mode : sta (14:c1:9f:57:72:48) +I (2627) wifi:enable tsf +W (2628) wifi:Haven't to connect to a suitable AP now! +I (2629) chip[DL]: +I (2629) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 43Attempting to connect WiFi station interface + +I (2641) chip[DL]: WiFi station state change: NotConnected -> Connecting +I (2642) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 2 +I (2647) chip[DL]: +Done driving station state, nothing else to do... +I (2659) chip[SVR]: Initializing subscription resumption storage... +I (2666) chip[SVR]: Server initializing... +I (2669) chip[TS]: Last Known Good Time: 2026-09-29T14:59:23 +I (2674) chip[DMG]: AccessControl: initializing +I (2678) chip[DMG]: Examples::AccessControlDelegate::Init +I (2683) chip[DMG]: AccessControl: setting +I (2687) chip[DMG]: DefaultAclStorage: initializing +I (2691) chip[DMG]: DefaultAclStorage: 0 entries loaded +I (2696) chip[SVR]: WARNING: mTestEventTriggerDelegate is null +I (2715) chip[ZCL]: Using ZAP configuration... +I (2716) esp_matter_cluster: Cluster plugin init common callback +I (2718) chip[DMG]: AccessControlCluster: initializing +I (2720) chip[ZCL]: WRITE ERR: ep 0 clus 0x0000_0030 attr 0x0000_0000 not supported +I (2730) chip[DIS]: Updating services using commissioning mode 1 +I (2733) chip[DIS]: CHIP minimal mDNS started advertising. +I (2739) chip[DIS]: Advertise commission parameter vendorID=65521 productID=32768 discriminator=3840/15 cm=1 cp=0 +I (2749) chip[DIS]: CHIP minimal mDNS configured as 'Commissionable node device'; instance name: 995B80B34BC8AAD9. +I (2758) chip[DIS]: mDNS service published: _matterc._udp +I (2763) chip[IN]: CASE Server enabling CASE session setups +I (2769) chip[SVR]: Joining Multicast groups +I (2772) chip[SVR]: Server Listening... +I (2778) esp_matter_attribute: ********** R : Endpoint 0x0000's Cluster 0x0000003C's Attribute 0x0000FFFC is 0 ********** +I (2788) data_model: Dynamic endpoint 0 added +E (2790) data_model: Cluster cannot be NULL. +I (2795) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000003's Attribute 0x00000001 is 2 ********** +I (2806) data_model: Dynamic endpoint 1 added +E (2809) data_model: Cluster cannot be NULL. +I (2815) chip[DL]: +I (2815) MAIN: ESP-Matter started successfully +WIFI_EVENT_STA_START +W (2821) wifi:Haven't to connect to a suitable AP now! +I (2826) chip[DL]: Attempting to connect WiFi station interface +E (2832) wifi:sta is connecting, return error +E (2836) chip[DL]: esp_wifi_connect() failed: ESP_ERR_WIFI_CONN +I (2842) KOTA: boot fw=1.0.16 build=3ae273ec hw=2 mac=14C19F577248 reset=USB_UART_CHIP_RESET mode=matter commissioned=0 last_restart=none +I (2843) chip[DL]: Configuring CHIPoBLE advertising (interval 25 ms, connectable) +I (2861) NimBLE: GAP procedure initiated: advertise; +I (2865) NimBLE: disc_mode=2 +I (2867) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=40 adv_itvl_max=40 +I (2877) NimBLE: + +I (2880) chip[DL]: CHIPoBLE advertising started +I (2883) MAIN: Commissioning window opened +I (2888) KOTA: matter fabrics=0 window=open qr=MT:Y.K9042C00KA0648G00 manual=34970112332 +I (2896) KOTA: led color=cyan pattern=breathe period=3000ms +I (2901) KOTA: matter fabrics=0 window=open qr=MT:Y.K9042C00KA0648G00 manual=34970112332 +W (2909) wifi:Haven't to connect to a suitable AP now! +I (2908) KOTA: health up=1s status=OK wifi=disconnected rssi=0 time_valid=0 sd=ok buffer=0 heap=117491 psram=7.9M last_upload=never ota=registered +W (3401) wifi:Haven't to connect to a suitable AP now! +I (3910) KOTA: rtc running=0 time=2000-01-01T00:00:00 +W (4402) wifi:Haven't to connect to a suitable AP now! +W (5403) wifi:Haven't to connect to a suitable AP now! +W (6404) wifi:Haven't to connect to a suitable AP now! +I (7219) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (7222) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (7223) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (7226) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (7233) wifi:Haven't to connect to a suitable AP now! +E (7238) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (7248) wifi:Haven't to connect to a suitable AP now! +I (7252) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (7259) wifi:Haven't to connect to a suitable AP now! +E (7264) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (7273) chip[DL]: Next WiFi station reconnect in 100 ms +I (7278) chip[DL]: Done driving station state, nothing else to do... +W (7379) wifi:Haven't to connect to a suitable AP now! +I (7380) chip[DL]: Attempting to connect WiFi station interface +I (7383) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (7386) wifi:Haven't to connect to a suitable AP now! +E (7391) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (7400) chip[DL]: Done driving station state, nothing else to do... +W (7405) wifi:Haven't to connect to a suitable AP now! +W (8411) wifi:Haven't to connect to a suitable AP now! +W (9415) wifi:Haven't to connect to a suitable AP now! +W (10417) wifi:Haven't to connect to a suitable AP now! +W (11419) wifi:Haven't to connect to a suitable AP now! +I (12201) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (12204) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (12205) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (12208) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (12215) wifi:Haven't to connect to a suitable AP now! +E (12220) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (12230) wifi:Haven't to connect to a suitable AP now! +I (12234) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (12242) wifi:Haven't to connect to a suitable AP now! +E (12247) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (12256) chip[DL]: Next WiFi station reconnect in 100 ms +I (12261) chip[DL]: Done driving station state, nothing else to do... +W (12361) wifi:Haven't to connect to a suitable AP now! +I (12362) chip[DL]: Attempting to connect WiFi station interface +I (12366) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (12368) wifi:Haven't to connect to a suitable AP now! +E (12373) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (12383) chip[DL]: Done driving station state, nothing else to do... +W (12427) wifi:Haven't to connect to a suitable AP now! +W (13428) wifi:Haven't to connect to a suitable AP now! +I (13912) INFLUX_BUFFER: Read INFLUX_URL from .env file: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (13917) INFLUX_BUFFER: Buffer file does not exist, returning count of 0 +W (13920) INFLUX: Initializing Influx client with URL: https://influx.kota-app.com/api/v2/write?bucket=sensorbox&org=kota +I (13943) BME688: Delaying for 10000 microseconds +W (14430) wifi:Haven't to connect to a suitable AP now! +I (14563) KOTA: mic enabled=1 (GPIO3=0, expected 0) +I (14566) Flicker: Flicker sensor initialized on pin 14 +I (14567) temperature_sensor: Range [20°C ~ 100°C], error < 2°C +I (14569) SENSOR_GATHERER: Resumed 2 tasks to gather sensor data +W (14573) wifi: +I (14569) SENSOR_GATHERER: Resumed 4 tasks to gather sensor data +Haven't to connect to a suitable AP now! +I (14570) SEN66: Creating sensor mutex +I (14572) Flicker: Collecting 1024 samples at 1000 Hz for dominant frequency analysis for total measurement window of 1000 ms +I (14600) Flicker: Starting to collect samples in gather task +I (14585) DeviceStatus: Active tasks: 27 +I (14588) SEN66: Waking up sensor by setting power GPIO to enabled level... +I (14570) INFLUX_BUFFER: Buffer file does not exist, returning empty buffer +I (14622) INFLUX_BUFFER: Buffer is empty, nothing to flush +I (14613) DeviceStatus: Task list: +gather_sensor_t R 1 1920 25 +gather_sensor_t X 1 1368 23 +IDLE1 R 0 700 6 +IDLE0 R 0 692 5 +gather_sensor_t B 1 2556 22 +reset_task B 0 3356 10 +ds1307_update B 1 776 18 +tiT B 18 2420 12 +gather_task B 1 1916 20 +gather_task B 1 14288 21 +ota_task B 1 7516 19 +CHIP B 1 3700 14 +health B 1 728 17 +Tmr Svc B 1 1364 7 +led_task B 5 868 8 +nimble_host B 21 1348 16 +ipc0 S 24 372 1 +ipc1 S 24 524 2 +sys_evt B 20 2676 11 +gather_sensor_t B 1 3180 26 +esp_timer S 22 3044 3 +gather_sensor_t B 1 2324 24 +main B 1 892 4 +gather_sensor_t S 1 1480 27 +wifi B 23 3796 13 +btController B 23 1864 15 +flicker_gather_ S 24 1988 28 +usb_console B 1 2840 9 + +W (14702) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (15433) wifi:Haven't to connect to a suitable AP now! +I (15630) Flicker: Finished collecting samples, notifying main task +I (15632) Flicker: Collected 1024 samples for dominant frequency analysis +I (15639) SENSOR_GATHERER: Sensor should now be asleep +W (15650) AS7341: Reading spectral data from AS7341 +I (15659) BME688: Delaying for 199517 microseconds +W (15710) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (15958) AS7341: Reading basic counts data from AS7341 +W (15974) AS7341: Reading flicker detection state from AS7341 +W (16194) as7341: FD Status Register: 0x20 (0b00100000) +I (16201) AS7341: Updating Matter attributes with AS7341 data: 10474 +I (16202) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 0 ********** +I (16208) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 10474 ********** +E (16219) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +E (16229) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x400, attribute_id: 0x0 +W (16238) AS7341: RTC is uninitialized, using current time as timestamp +I (16241) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is null ********** +I (16256) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000403's Attribute 0x00000000 is 1021 ********** +E (16267) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +E (16276) MAIN: [E-MAIN-03] Attribute update callback: endpoint_id: 1, cluster_id: 0x403, attribute_id: 0x0 +W (16286) BME688: RTC is uninitialized, using current time as timestamp +W (16434) wifi:Haven't to connect to a suitable AP now! +W (16711) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (17183) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (17186) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (17187) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (17190) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (17198) wifi:Haven't to connect to a suitable AP now! +E (17203) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (17212) wifi:Haven't to connect to a suitable AP now! +I (17217) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (17224) wifi:Haven't to connect to a suitable AP now! +E (17229) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (17239) chip[DL]: Next WiFi station reconnect in 100 ms +I (17243) chip[DL]: Done driving station state, nothing else to do... +W (17244) AS7341: RTC is uninitialized, using current time as timestamp +W (17291) BME688: RTC is uninitialized, using current time as timestamp +W (17344) wifi:Haven't to connect to a suitable AP now! +I (17346) chip[DL]: Attempting to connect WiFi station interface +I (17349) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (17351) wifi:Haven't to connect to a suitable AP now! +E (17356) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (17366) chip[DL]: Done driving station state, nothing else to do... +W (17437) wifi:Haven't to connect to a suitable AP now! +W (17712) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (18256) AS7341: RTC is uninitialized, using current time as timestamp +W (18292) BME688: RTC is uninitialized, using current time as timestamp +W (18439) wifi:Haven't to connect to a suitable AP now! +W (18713) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (19257) AS7341: RTC is uninitialized, using current time as timestamp +W (19293) BME688: RTC is uninitialized, using current time as timestamp +W (19440) wifi:Haven't to connect to a suitable AP now! +I (19615) SEN66: Sent start measurement command, waiting for sensor to be ready... +W (19714) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (19928) KOTA: cmd ping ok=1 fw=1.0.16 api=1 +I (19933) KOTA: cmd wifi_get ok=1 ssid=- password_set=0 mode=matter +W (20258) AS7341: RTC is uninitialized, using current time as timestamp +W (20294) BME688: RTC is uninitialized, using current time as timestamp +W (20441) wifi:Haven't to connect to a suitable AP now! +W (20715) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (21259) AS7341: RTC is uninitialized, using current time as timestamp +W (21295) BME688: RTC is uninitialized, using current time as timestamp +W (21442) wifi:Haven't to connect to a suitable AP now! +W (21716) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (22166) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (22169) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (22170) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (22173) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (22181) wifi:Haven't to connect to a suitable AP now! +E (22186) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (22196) wifi:Haven't to connect to a suitable AP now! +I (22200) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (22207) wifi:Haven't to connect to a suitable AP now! +E (22212) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (22222) chip[DL]: Next WiFi station reconnect in 100 ms +I (22226) chip[DL]: Done driving station state, nothing else to do... +W (22260) AS7341: RTC is uninitialized, using current time as timestamp +W (22296) BME688: RTC is uninitialized, using current time as timestamp +W (22327) wifi:Haven't to connect to a suitable AP now! +I (22328) chip[DL]: Attempting to connect WiFi station interface +I (22332) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (22334) wifi:Haven't to connect to a suitable AP now! +E (22340) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (22349) chip[DL]: Done driving station state, nothing else to do... +W (22446) wifi:Haven't to connect to a suitable AP now! +I (22574) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (22717) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (23075) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (23261) AS7341: RTC is uninitialized, using current time as timestamp +W (23297) BME688: RTC is uninitialized, using current time as timestamp +W (23447) wifi:Haven't to connect to a suitable AP now! +I (23576) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (23718) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (24077) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (24262) AS7341: RTC is uninitialized, using current time as timestamp +W (24299) BME688: RTC is uninitialized, using current time as timestamp +W (24448) wifi:Haven't to connect to a suitable AP now! +I (24570) OTA_CLIENT: HTTP client handle: 0x3fcf02e4 +I (24578) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (24710) MICROPHONE: Read 600000 bytes in 9940 ms +I (24712) MICROPHONE: Read 600000 bytes of audio data +W (24719) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (25079) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (25264) AS7341: RTC is uninitialized, using current time as timestamp +W (25300) BME688: RTC is uninitialized, using current time as timestamp +W (25449) wifi:Haven't to connect to a suitable AP now! +I (25580) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (25720) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (26081) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (26265) AS7341: RTC is uninitialized, using current time as timestamp +W (26301) BME688: RTC is uninitialized, using current time as timestamp +W (26450) wifi:Haven't to connect to a suitable AP now! +I (26582) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (26721) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (27083) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (27150) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (27152) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (27154) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (27157) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (27164) wifi:Haven't to connect to a suitable AP now! +E (27169) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (27179) wifi:Haven't to connect to a suitable AP now! +I (27183) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (27190) wifi:Haven't to connect to a suitable AP now! +E (27196) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (27205) chip[DL]: Next WiFi station reconnect in 100 ms +I (27209) chip[DL]: Done driving station state, nothing else to do... +W (27266) AS7341: RTC is uninitialized, using current time as timestamp +W (27302) BME688: RTC is uninitialized, using current time as timestamp +W (27310) wifi:Haven't to connect to a suitable AP now! +I (27311) chip[DL]: Attempting to connect WiFi station interface +I (27315) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (27318) wifi:Haven't to connect to a suitable AP now! +E (27323) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (27332) chip[DL]: Done driving station state, nothing else to do... +W (27456) wifi:Haven't to connect to a suitable AP now! +I (27584) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (27722) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (28085) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (28160) MICROPHONE: Raw block stats: mean=-17.8 min=-146018 max=134018 +I (28161) MICROPHONE: Sum SQ: 0.578925, RMS pressure: 0.001701, Normalized: 0.001701, dB peak: -55.38 +W (28166) SEN66: RTC is uninitialized, using current time as timestamp +W (28267) AS7341: RTC is uninitialized, using current time as timestamp +W (28303) BME688: RTC is uninitialized, using current time as timestamp +W (28457) wifi:Haven't to connect to a suitable AP now! +I (28586) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (28723) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (29087) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (29172) SEN66: RTC is uninitialized, using current time as timestamp +W (29268) AS7341: RTC is uninitialized, using current time as timestamp +W (29304) BME688: RTC is uninitialized, using current time as timestamp +W (29458) wifi:Haven't to connect to a suitable AP now! +I (29588) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (29724) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (30089) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (30173) SEN66: RTC is uninitialized, using current time as timestamp +W (30269) AS7341: RTC is uninitialized, using current time as timestamp +W (30305) BME688: RTC is uninitialized, using current time as timestamp +W (30459) wifi:Haven't to connect to a suitable AP now! +I (30590) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (30725) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (31091) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (31174) SEN66: RTC is uninitialized, using current time as timestamp +W (31270) AS7341: RTC is uninitialized, using current time as timestamp +W (31306) BME688: RTC is uninitialized, using current time as timestamp +W (31460) wifi:Haven't to connect to a suitable AP now! +E (31572) esp-tls: couldn't get hostname for :bin.kota-app.com: getaddrinfo() returns 202, addrinfo=0x0 +E (31573) esp-tls: Failed to open new connection +E (31575) transport_base: Failed to open a new connection +E (31580) HTTP_CLIENT: Connection failed, sock < 0 +E (31585) OTA_CLIENT: [E-OTA-01] Error ESP_ERR_HTTP_CONNECT at ./main/ota_client.cpp:355 +I (31592) SENSOR_GATHERER: 2/2 tasks still running, waiting... +E (31593) OTA_CLIENT: [E-OTA-13] Failed to check for updates: ESP_ERR_HTTP_CONNECT +I (31606) KOTA: status OK -> CONNECTION_ERROR reason=ota_check_failed +W (31726) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (32098) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (32133) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (32135) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (32137) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (32140) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (32147) wifi:Haven't to connect to a suitable AP now! +E (32152) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (32162) wifi:Haven't to connect to a suitable AP now! +I (32166) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (32173) wifi:Haven't to connect to a suitable AP now! +W (32175) SEN66: RTC is uninitialized, using current time as timestamp +E (32179) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (32194) chip[DL]: Next WiFi station reconnect in 100 ms +I (32199) chip[DL]: Done driving station state, nothing else to do... +W (32271) AS7341: RTC is uninitialized, using current time as timestamp +W (32300) wifi:Haven't to connect to a suitable AP now! +I (32301) chip[DL]: Attempting to connect WiFi station interface +I (32305) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (32307) BME688: RTC is uninitialized, using current time as timestamp +W (32307) wifi:Haven't to connect to a suitable AP now! +E (32319) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (32328) chip[DL]: Done driving station state, nothing else to do... +W (32462) wifi:Haven't to connect to a suitable AP now! +I (32599) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (32727) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (32730) chip[DL]: bleAdv Timeout : Start slow advertisement +I (32732) chip[DL]: Configuring CHIPoBLE advertising (interval 500 ms, connectable) +I (32737) chip[DL]: Device already advertising, stop active advertisement and restart +I (32744) NimBLE: GAP procedure initiated: stop advertising. + +I (32750) NimBLE: GAP procedure initiated: advertise; +I (32754) NimBLE: disc_mode=2 +I (32756) NimBLE: adv_channel_map=0 own_addr_type=1 adv_filter_policy=0 adv_itvl_min=800 adv_itvl_max=800 +I (32767) NimBLE: + +I (33100) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (33184) SEN66: RTC is uninitialized, using current time as timestamp +W (33272) AS7341: RTC is uninitialized, using current time as timestamp +W (33320) BME688: RTC is uninitialized, using current time as timestamp +W (33463) wifi:Haven't to connect to a suitable AP now! +I (33601) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (33728) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (34102) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (34185) SEN66: RTC is uninitialized, using current time as timestamp +W (34274) AS7341: RTC is uninitialized, using current time as timestamp +W (34321) BME688: RTC is uninitialized, using current time as timestamp +W (34464) wifi:Haven't to connect to a suitable AP now! +I (34603) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (34729) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (35104) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (35186) SEN66: RTC is uninitialized, using current time as timestamp +W (35275) AS7341: RTC is uninitialized, using current time as timestamp +W (35322) BME688: RTC is uninitialized, using current time as timestamp +W (35465) wifi:Haven't to connect to a suitable AP now! +I (35605) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (35730) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (36106) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (36187) SEN66: RTC is uninitialized, using current time as timestamp +W (36276) AS7341: RTC is uninitialized, using current time as timestamp +W (36323) BME688: RTC is uninitialized, using current time as timestamp +W (36466) wifi:Haven't to connect to a suitable AP now! +I (36607) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (36731) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (37107) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (37122) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (37125) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (37126) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (37129) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (37137) wifi:Haven't to connect to a suitable AP now! +E (37142) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (37151) wifi:Haven't to connect to a suitable AP now! +I (37156) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (37163) wifi:Haven't to connect to a suitable AP now! +E (37168) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (37178) chip[DL]: Next WiFi station reconnect in 100 ms +I (37182) chip[DL]: Done driving station state, nothing else to do... +W (37188) SEN66: RTC is uninitialized, using current time as timestamp +W (37277) AS7341: RTC is uninitialized, using current time as timestamp +W (37283) wifi:Haven't to connect to a suitable AP now! +I (37284) chip[DL]: Attempting to connect WiFi station interface +I (37287) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (37290) wifi:Haven't to connect to a suitable AP now! +E (37295) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (37305) chip[DL]: Done driving station state, nothing else to do... +W (37324) BME688: RTC is uninitialized, using current time as timestamp +W (37468) wifi:Haven't to connect to a suitable AP now! +I (37608) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (37732) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (38109) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (38195) SEN66: RTC is uninitialized, using current time as timestamp +W (38278) AS7341: RTC is uninitialized, using current time as timestamp +W (38325) BME688: RTC is uninitialized, using current time as timestamp +W (38470) wifi:Haven't to connect to a suitable AP now! +I (38610) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (38733) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (39111) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (39196) SEN66: RTC is uninitialized, using current time as timestamp +W (39279) AS7341: RTC is uninitialized, using current time as timestamp +W (39326) BME688: RTC is uninitialized, using current time as timestamp +W (39471) wifi:Haven't to connect to a suitable AP now! +I (39612) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (39736) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (40113) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (40197) SEN66: RTC is uninitialized, using current time as timestamp +W (40280) AS7341: RTC is uninitialized, using current time as timestamp +W (40326) BME688: RTC is uninitialized, using current time as timestamp +W (40473) wifi:Haven't to connect to a suitable AP now! +I (40614) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (40737) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (41115) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (41198) SEN66: RTC is uninitialized, using current time as timestamp +W (41281) AS7341: RTC is uninitialized, using current time as timestamp +W (41327) BME688: RTC is uninitialized, using current time as timestamp +W (41474) wifi:Haven't to connect to a suitable AP now! +I (41616) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (41738) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (42105) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (42108) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (42109) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (42112) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +I (42116) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (42120) wifi:Haven't to connect to a suitable AP now! +E (42130) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (42140) wifi:Haven't to connect to a suitable AP now! +I (42144) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (42151) wifi:Haven't to connect to a suitable AP now! +E (42156) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (42166) chip[DL]: Next WiFi station reconnect in 100 ms +I (42170) chip[DL]: Done driving station state, nothing else to do... +W (42199) SEN66: RTC is uninitialized, using current time as timestamp +W (42271) wifi:Haven't to connect to a suitable AP now! +I (42272) chip[DL]: Attempting to connect WiFi station interface +I (42275) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (42278) wifi:Haven't to connect to a suitable AP now! +W (42283) AS7341: RTC is uninitialized, using current time as timestamp +E (42283) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (42299) chip[DL]: Done driving station state, nothing else to do... +W (42328) BME688: RTC is uninitialized, using current time as timestamp +W (42476) wifi:Haven't to connect to a suitable AP now! +I (42625) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (42739) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (43126) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (43200) SEN66: RTC is uninitialized, using current time as timestamp +W (43290) AS7341: RTC is uninitialized, using current time as timestamp +W (43329) BME688: RTC is uninitialized, using current time as timestamp +W (43482) wifi:Haven't to connect to a suitable AP now! +I (43627) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (43740) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (44128) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (44201) SEN66: RTC is uninitialized, using current time as timestamp +W (44291) AS7341: RTC is uninitialized, using current time as timestamp +W (44330) BME688: RTC is uninitialized, using current time as timestamp +W (44486) wifi:Haven't to connect to a suitable AP now! +I (44629) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (44741) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (45130) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (45202) SEN66: RTC is uninitialized, using current time as timestamp +W (45292) AS7341: RTC is uninitialized, using current time as timestamp +W (45331) BME688: RTC is uninitialized, using current time as timestamp +W (45487) wifi:Haven't to connect to a suitable AP now! +I (45631) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (45742) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (46132) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (46203) SEN66: RTC is uninitialized, using current time as timestamp +W (46293) AS7341: RTC is uninitialized, using current time as timestamp +W (46332) BME688: RTC is uninitialized, using current time as timestamp +W (46488) wifi:Haven't to connect to a suitable AP now! +I (46633) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (46743) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (47093) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (47095) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (47097) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (47100) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (47107) wifi:Haven't to connect to a suitable AP now! +E (47112) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (47122) wifi:Haven't to connect to a suitable AP now! +I (47126) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +I (47134) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (47134) wifi:Haven't to connect to a suitable AP now! +E (47144) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (47154) chip[DL]: Next WiFi station reconnect in 100 ms +I (47158) chip[DL]: Done driving station state, nothing else to do... +W (47204) SEN66: RTC is uninitialized, using current time as timestamp +W (47259) wifi:Haven't to connect to a suitable AP now! +I (47260) chip[DL]: Attempting to connect WiFi station interface +I (47263) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (47266) wifi:Haven't to connect to a suitable AP now! +E (47271) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (47281) chip[DL]: Done driving station state, nothing else to do... +W (47294) AS7341: RTC is uninitialized, using current time as timestamp +W (47333) BME688: RTC is uninitialized, using current time as timestamp +W (47493) wifi:Haven't to connect to a suitable AP now! +I (47639) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (47744) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (48140) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (48205) SEN66: RTC is uninitialized, using current time as timestamp +W (48295) AS7341: RTC is uninitialized, using current time as timestamp +W (48334) BME688: RTC is uninitialized, using current time as timestamp +W (48495) wifi:Haven't to connect to a suitable AP now! +I (48641) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (48747) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (49142) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (49206) SEN66: RTC is uninitialized, using current time as timestamp +W (49296) AS7341: RTC is uninitialized, using current time as timestamp +W (49335) BME688: RTC is uninitialized, using current time as timestamp +W (49496) wifi:Haven't to connect to a suitable AP now! +I (49643) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (49748) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (50144) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (50207) SEN66: RTC is uninitialized, using current time as timestamp +W (50297) AS7341: RTC is uninitialized, using current time as timestamp +W (50336) BME688: RTC is uninitialized, using current time as timestamp +W (50498) wifi:Haven't to connect to a suitable AP now! +I (50645) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (50749) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (51146) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (51208) SEN66: RTC is uninitialized, using current time as timestamp +W (51298) AS7341: RTC is uninitialized, using current time as timestamp +W (51337) BME688: RTC is uninitialized, using current time as timestamp +W (51500) wifi:Haven't to connect to a suitable AP now! +I (51647) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (51750) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (52081) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (52084) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (52085) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (52088) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (52096) wifi:Haven't to connect to a suitable AP now! +E (52100) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (52110) wifi:Haven't to connect to a suitable AP now! +I (52115) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (52122) wifi:Haven't to connect to a suitable AP now! +E (52127) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (52136) chip[DL]: Next WiFi station reconnect in 100 ms +I (52141) chip[DL]: Done driving station state, nothing else to do... +I (52148) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (52209) SEN66: RTC is uninitialized, using current time as timestamp +W (52242) wifi:Haven't to connect to a suitable AP now! +I (52243) chip[DL]: Attempting to connect WiFi station interface +I (52246) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (52249) wifi:Haven't to connect to a suitable AP now! +E (52254) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (52264) chip[DL]: Done driving station state, nothing else to do... +W (52299) AS7341: RTC is uninitialized, using current time as timestamp +W (52338) BME688: RTC is uninitialized, using current time as timestamp +W (52508) wifi:Haven't to connect to a suitable AP now! +I (52653) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (52751) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (53154) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (53210) SEN66: RTC is uninitialized, using current time as timestamp +W (53300) AS7341: RTC is uninitialized, using current time as timestamp +W (53339) BME688: RTC is uninitialized, using current time as timestamp +W (53509) wifi:Haven't to connect to a suitable AP now! +I (53655) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (53752) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (54156) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (54212) SEN66: RTC is uninitialized, using current time as timestamp +W (54301) AS7341: RTC is uninitialized, using current time as timestamp +W (54340) BME688: RTC is uninitialized, using current time as timestamp +W (54510) wifi:Haven't to connect to a suitable AP now! +I (54657) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (54753) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (55158) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (55213) SEN66: RTC is uninitialized, using current time as timestamp +W (55302) AS7341: RTC is uninitialized, using current time as timestamp +W (55341) BME688: RTC is uninitialized, using current time as timestamp +W (55511) wifi:Haven't to connect to a suitable AP now! +I (55659) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (55754) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (56160) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (56214) SEN66: RTC is uninitialized, using current time as timestamp +W (56303) AS7341: RTC is uninitialized, using current time as timestamp +W (56342) BME688: RTC is uninitialized, using current time as timestamp +W (56512) wifi:Haven't to connect to a suitable AP now! +I (56661) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (56755) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (57064) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (57067) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (57068) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (57071) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (57079) wifi:Haven't to connect to a suitable AP now! +E (57083) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (57093) wifi:Haven't to connect to a suitable AP now! +I (57097) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (57105) wifi:Haven't to connect to a suitable AP now! +E (57110) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (57119) chip[DL]: Next WiFi station reconnect in 100 ms +I (57124) chip[DL]: Done driving station state, nothing else to do... +I (57162) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (57215) SEN66: RTC is uninitialized, using current time as timestamp +W (57225) wifi:Haven't to connect to a suitable AP now! +I (57226) chip[DL]: Attempting to connect WiFi station interface +I (57230) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (57232) wifi:Haven't to connect to a suitable AP now! +E (57237) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (57247) chip[DL]: Done driving station state, nothing else to do... +W (57304) AS7341: RTC is uninitialized, using current time as timestamp +W (57344) BME688: RTC is uninitialized, using current time as timestamp +W (57517) wifi:Haven't to connect to a suitable AP now! +I (57663) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (57756) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (58164) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (58216) SEN66: RTC is uninitialized, using current time as timestamp +W (58305) AS7341: RTC is uninitialized, using current time as timestamp +W (58345) BME688: RTC is uninitialized, using current time as timestamp +W (58518) wifi:Haven't to connect to a suitable AP now! +I (58665) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (58757) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (59166) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (59217) SEN66: RTC is uninitialized, using current time as timestamp +W (59306) AS7341: RTC is uninitialized, using current time as timestamp +W (59346) BME688: RTC is uninitialized, using current time as timestamp +W (59519) wifi:Haven't to connect to a suitable AP now! +I (59667) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (59758) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (60168) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (60218) SEN66: RTC is uninitialized, using current time as timestamp +W (60307) AS7341: RTC is uninitialized, using current time as timestamp +W (60346) BME688: RTC is uninitialized, using current time as timestamp +W (60520) wifi:Haven't to connect to a suitable AP now! +I (60669) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (60759) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (61170) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (61219) SEN66: RTC is uninitialized, using current time as timestamp +W (61308) AS7341: RTC is uninitialized, using current time as timestamp +W (61347) BME688: RTC is uninitialized, using current time as timestamp +W (61521) wifi:Haven't to connect to a suitable AP now! +I (61671) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (61760) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (62047) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (62050) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (62051) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (62054) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (62062) wifi:Haven't to connect to a suitable AP now! +E (62067) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (62076) wifi:Haven't to connect to a suitable AP now! +I (62081) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (62088) wifi:Haven't to connect to a suitable AP now! +E (62093) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (62102) chip[DL]: Next WiFi station reconnect in 100 ms +I (62107) chip[DL]: Done driving station state, nothing else to do... +I (62172) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (62208) wifi:Haven't to connect to a suitable AP now! +I (62209) chip[DL]: Attempting to connect WiFi station interface +I (62212) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (62215) wifi:Haven't to connect to a suitable AP now! +W (62220) SEN66: RTC is uninitialized, using current time as timestamp +E (62220) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (62236) chip[DL]: Done driving station state, nothing else to do... +W (62309) AS7341: RTC is uninitialized, using current time as timestamp +W (62348) BME688: RTC is uninitialized, using current time as timestamp +W (62527) wifi:Haven't to connect to a suitable AP now! +I (62673) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (62761) DeviceStatus: RTC is uninitialized, using current time as timestamp +W (62925) wifi:Haven't to connect to a suitable AP now! +I (62925) KOTA: health up=61s status=CONNECTION_ERROR wifi=disconnected rssi=0 time_valid=0 sd=ok buffer=0 heap=47259 psram=7.3M last_upload=never ota=registered +I (63174) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (63227) SEN66: RTC is uninitialized, using current time as timestamp +W (63310) AS7341: RTC is uninitialized, using current time as timestamp +W (63349) BME688: RTC is uninitialized, using current time as timestamp +W (63528) wifi:Haven't to connect to a suitable AP now! +I (63675) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (63762) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (64176) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (64228) SEN66: RTC is uninitialized, using current time as timestamp +W (64311) AS7341: RTC is uninitialized, using current time as timestamp +W (64350) BME688: RTC is uninitialized, using current time as timestamp +W (64529) wifi:Haven't to connect to a suitable AP now! +I (64617) SEN66: First measurement, waiting additional 120 seconds for sensor to stabilize... +I (64677) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (64763) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (65178) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (65229) SEN66: RTC is uninitialized, using current time as timestamp +W (65312) AS7341: RTC is uninitialized, using current time as timestamp +W (65351) BME688: RTC is uninitialized, using current time as timestamp +W (65530) wifi:Haven't to connect to a suitable AP now! +I (65679) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (65764) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (66180) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (66230) SEN66: RTC is uninitialized, using current time as timestamp +W (66313) AS7341: RTC is uninitialized, using current time as timestamp +W (66352) BME688: RTC is uninitialized, using current time as timestamp +W (66531) wifi:Haven't to connect to a suitable AP now! +I (66681) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (66765) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (67030) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (67033) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (67034) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (67037) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (67045) wifi:Haven't to connect to a suitable AP now! +E (67049) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (67059) wifi:Haven't to connect to a suitable AP now! +I (67063) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (67071) wifi:Haven't to connect to a suitable AP now! +E (67076) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (67085) chip[DL]: Next WiFi station reconnect in 100 ms +I (67090) chip[DL]: Done driving station state, nothing else to do... +I (67182) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (67191) wifi:Haven't to connect to a suitable AP now! +I (67192) chip[DL]: Attempting to connect WiFi station interface +I (67195) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (67198) wifi:Haven't to connect to a suitable AP now! +E (67203) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (67213) chip[DL]: Done driving station state, nothing else to do... +W (67231) SEN66: RTC is uninitialized, using current time as timestamp +W (67314) AS7341: RTC is uninitialized, using current time as timestamp +W (67353) BME688: RTC is uninitialized, using current time as timestamp +W (67537) wifi:Haven't to connect to a suitable AP now! +I (67683) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (67766) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (68184) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (68232) SEN66: RTC is uninitialized, using current time as timestamp +W (68315) AS7341: RTC is uninitialized, using current time as timestamp +W (68354) BME688: RTC is uninitialized, using current time as timestamp +W (68542) wifi:Haven't to connect to a suitable AP now! +I (68685) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (68767) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (69186) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (69233) SEN66: RTC is uninitialized, using current time as timestamp +W (69316) AS7341: RTC is uninitialized, using current time as timestamp +W (69355) BME688: RTC is uninitialized, using current time as timestamp +W (69546) wifi:Haven't to connect to a suitable AP now! +I (69687) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (69768) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (70188) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (70234) SEN66: RTC is uninitialized, using current time as timestamp +W (70317) AS7341: RTC is uninitialized, using current time as timestamp +W (70356) BME688: RTC is uninitialized, using current time as timestamp +W (70547) wifi:Haven't to connect to a suitable AP now! +I (70689) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (70769) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (71190) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (71235) SEN66: RTC is uninitialized, using current time as timestamp +W (71318) AS7341: RTC is uninitialized, using current time as timestamp +W (71357) BME688: RTC is uninitialized, using current time as timestamp +W (71548) wifi:Haven't to connect to a suitable AP now! +I (71691) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (71770) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (72013) wifi:Coexist: Wi-Fi connect fail, apply reconnect coex policy + +I (72015) chip[DL]: Posting ESPSystemEvent: Wifi Event with eventId : 5 +I (72017) chip[DL]: WIFI_EVENT_STA_DISCONNECTED +I (72020) chip[DL]: WiFi station state change: Connecting -> Connecting_Failed +W (72027) wifi:Haven't to connect to a suitable AP now! +E (72032) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +W (72042) wifi:Haven't to connect to a suitable AP now! +I (72046) chip[DL]: WiFi station state change: Connecting_Failed -> NotConnected +W (72054) wifi:Haven't to connect to a suitable AP now! +E (72059) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (72068) chip[DL]: Next WiFi station reconnect in 100 ms +I (72073) chip[DL]: Done driving station state, nothing else to do... +W (72173) wifi:Haven't to connect to a suitable AP now! +I (72175) chip[DL]: Attempting to connect WiFi station interface +I (72178) chip[DL]: WiFi station state change: NotConnected -> Connecting +W (72181) wifi:Haven't to connect to a suitable AP now! +E (72185) chip[DL]: Failed to get configured network when updating network status: Error ESP32:0x0500300F +I (72191) SENSOR_GATHERER: 2/2 tasks still running, waiting... +I (72195) chip[DL]: Done driving station state, nothing else to do... +W (72236) SEN66: RTC is uninitialized, using current time as timestamp +W (72319) AS7341: RTC is uninitialized, using current time as timestamp +W (72358) BME688: RTC is uninitialized, using current time as timestamp +W (72552) wifi:Haven't to connect to a suitable AP now! +I (72700) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (72771) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (73201) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (73237) SEN66: RTC is uninitialized, using current time as timestamp +W (73320) AS7341: RTC is uninitialized, using current time as timestamp +W (73359) BME688: RTC is uninitialized, using current time as timestamp +W (73553) wifi:Haven't to connect to a suitable AP now! +I (73702) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (73772) DeviceStatus: RTC is uninitialized, using current time as timestamp +I (74203) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (74238) SEN66: RTC is uninitialized, using current time as timestamp +W (74321) AS7341: RTC is uninitialized, using current time as timestamp +W (74360) BME688: RTC is uninitialized, using current time as timestamp +W (74555) wifi:Haven't to connect to a suitable AP now! +I (74581) SENSOR_GATHERER: 3/4 tasks still running, waiting... +I (74627) INFLUX_BUFFER: Buffer file does not exist, returning empty buffer +I (74628) INFLUX_BUFFER: Buffer is empty, nothing to flush +I (74704) SENSOR_GATHERER: 2/2 tasks still running, waiting... +W (74773) DeviceStatus: RTC is uninitialized, using current time as timestamp +--- disconnected --- \ No newline at end of file diff --git a/tests/test_commands.py b/tests/test_commands.py index 4b97c96..7c1f042 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -160,3 +160,18 @@ def test_engine_treats_usb_wifi_restart_as_intentional_and_reads_replies(): box = dict((t, x) for t, x, _ in r.box) assert "network 'Biuro'" in box["Wi-Fi config"] and "Wi-Fi connected (-60 dBm)" in box["Health"] assert any(m == "wifi_connected" and t is not None for m, _, t in r.milestones) + + +def test_live_v13_box_answers_usb_commands(): + # Real capture (2026-09-29): box 14:C1:9F:57:72:48 after flashing merged-binary-v13.bin App-only; + # the Wi-Fi tab's read flow sent "kota ping" and "kota wifi get". + from pathlib import Path + + from kota_tech.core.logfile import read_log_file + e = DiagnosticsEngine() + for ln in read_log_file(Path(__file__).parent / "fixtures" / "live_boot_14C19F577248_v13_usb_console.txt"): + e.feed(ln) + r = e.report() + assert e.firmware.elf_sha.startswith("3ae273ec") and e.console_api == 1 + assert dict((t, x) for t, x, _ in r.box)["Wi-Fi config"] == "no network on the SD card, mode matter" + assert not any(f.rule_id.startswith("other.") for f in e.findings.values()) # logs intact via the driver

    rN}1mL6Z5=l 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