Commit Graph
53 Commits
Author SHA1 Message Date
oskarvitalii f77ff376d4 Merge dev into master: переработанный интерфейс и текст события в webhook
Веб-панель переделана в световое табло: главный ответ читается с
расстояния, цвета по конвенции сигнальных ламп, настройки свёрнуты.
Исправлено: лампа выхода «Авария» горела зелёным при срабатывании,
обновление стирало ввод в форме, шрифты тянулись с Google Fonts и на
объекте не загружались.

Страница переехала во флеш (PROGMEM) и в отдельный файл web_page.h -
последнее вынужденно: генератор прототипов Arduino не понимает сырые
строковые литералы и ломался на JavaScript внутри.

OLED: верхняя строка отдана статусу кеглем 2, добавлено состояние реле.

В теле webhook поле status теперь содержит человеческую фразу
(«Насосы стоят, подачу держит резерв»), машиночитаемое значение
переехало в новое поле state.

Прошито и проверено на приборе: COM7, LOLIN D1 ESP-WROOM-02, 2MB FS:64KB.
2026-08-08 01:40:10 +07:00
oskarvitaliiandClaude Opus 5 0c06ba1032 feat(webhook): слать в status человеческую фразу вместо кода
В теле уведомления status теперь содержит ту же формулировку, что видит
оператор в панели: «Насосы стоят, подачу держит резерв». Прежние
машиночитаемые значения переехали в новое поле state (ALARM / OK /
STARTED / STOPPED), чтобы сценарий на сервере ветвился по нему, а не
разбирал русский текст.

ЛОМАЮЩЕЕ ИЗМЕНЕНИЕ: если сценарий в n8n сравнивал status с "ALARM",
условие надо переключить на state.

Формулировки собраны в одном месте прошивки (statusKind / statusWord /
statusSub / statusPhrase) и оттуда попадают и в webhook, и в панель через
новые поля k, w, s в /status. Раньше та же логика была продублирована в
JS панели; два набора фраз разошлись бы при первой правке. Побочно JS
сократился почти вдвое.

Прошито и проверено на приборе: /status отдаёт
{"k":"ok","w":"Подача есть","s":"работает насос 1"}, /config по-прежнему
отдаёт только имя хоста без токена.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 01:33:48 +07:00
oskarvitaliiandClaude Opus 5 6240862c27 fix(build): вынести HTML в web_page.h - сборка через arduino-cli падала
Генератор прототипов Arduino не понимает сырые строковые литералы: внутри
JS он нашёл "function draw(d){" и "function poll(){", принял их за
определения функций C++ и вставил для них прототипы в начало файла.
Сборка падала с "error: 'function' does not name a type", а директивы
#line показывали ошибку на строках внутри HTML.

Заголовочные файлы препроцессор не сканирует, поэтому страница переехала
в web_page.h. Тот же приём используется в соседнем проекте.

Ошибка не всплывала раньше потому, что я проверял скетч через
g++ -fsyntax-only по .ino: так файл компилируется как обычный .cpp и
препроцессор Arduino не участвует. Проверять сборку нужно arduino-cli,
это записано в README 11.1.

Прошито и проверено на железе (COM7, LOLIN D1 ESP-WROOM-02, 2MB FS:64KB):
размеры флеша совпадают, LittleFS монтируется, ранее заданные через панель
настройки пережили перепрошивку, Wi-Fi поднимается, датчики читаются.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 01:08:17 +07:00
oskarvitaliiandClaude Opus 5 cb151d2e30 feat(ui): переработать веб-панель и макет OLED
Панель как световое табло, а не дашборд: главный ответ занимает верх
страницы и читается с расстояния. Прежде самым крупным элементом были
часы браузера - единственное, что не несло информации о приборе, - а
ответ "есть подача или нет" был набран 11 кеглем в панели размером с
кнопку. Норма и авария выглядели почти одинаково.

Исправлены три содержательные ошибки:

- лампа выхода "Авария" горела ЗЕЛЁНЫМ при срабатывании: стиль
  "включено" был общим для всех ламп, и сработавшая сигнализация
  подсвечивалась цветом "всё хорошо";
- страница делала location.reload() при любом изменении состояния,
  стирая недописанный адрес в форме вместе с прокруткой и фокусом;
- CSS подключал шрифты с Google Fonts, тогда как сеть на объекте не
  разрешает внешние имена - нарисованный макет там не появлялся никогда.

Страница стала статической и уехала во флеш (PAGE_HTML PROGMEM, ~8 КБ,
отдаётся через send_P). Раньше HTML собирался строкой в куче на каждый
запрос: ~14 КБ при ~40 КБ свободной кучи. Состояние подставляет JS,
забирая /status и новый /config. В /status добавлено поле rdy.

/config отдаёт только имя хоста webhook: полный URL содержит секретный
токен, и выводить его на экран, видимый всем в помещении, незачем.

Цвета по конвенции сигнальных ламп: резерв теперь ЖЁЛТЫЙ, а не красный -
работающий резерв это предупреждение, система ещё справляется. Красный
оставлен для остановки основных. Убраны декоративные коды SYS_OK / 0xFF,
не кодировавшие ничего, и тройное дублирование состояния резерва.
Настройки свёрнуты в details.

OLED: верхняя строка отдана статусу кеглем 2 - прежде её постоянно
занимал заголовок "PUMP CONTROLLER v8", который никогда не менялся.
Три строки датчиков сжаты в одну, освободившееся место отдано состоянию
обоих реле. Текст остаётся латиницей: встроенный шрифт Adafruit GFX
кириллицы не содержит.

Проверено: сборка -Wall -Wextra без предупреждений; все три состояния
панели просмотрены в браузере на 375 и 800 px. На железе не проверялось.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 00:43:34 +07:00
oskarvitalii d53035a5ef Merge dev into master: pump_controller_8_2_OLED_DONE
Добавлен проект контроллера насосной станции на Wemos D1 mini вместе с
его историей: мониторинг двух основных и резервного насоса, два реле
аварий на D3/D4 (модули active-LOW), OLED, веб-панель с настройками и
состоянием реле, уведомления на HTTPS-webhook, настройки в LittleFS.

Также: .gitignore переведён на белый список, чтобы рабочий каталог
D:\Soft\Arduino мог служить рабочим деревом репозитория, не втягивая
в него старые версии скетчей, архивы и драйверы.
2026-08-07 23:20:29 +07:00
oskarvitaliiandClaude Opus 5 42cbb82492 chore: allowlist .gitignore, рабочий каталог D:\Soft\Arduino как корень репо
Локально все скетчи лежат вперемешку в одной папке: рабочие проекты,
старые версии, эксперименты, архивы и драйверы. Чтобы этот каталог мог
служить рабочим деревом репозитория, .gitignore инвертирован — по
умолчанию игнорируется всё, проекты добавляются точечно строкой
"!/ИмяПапки/".

Прежние правила (артефакты сборки, мусор ОС, secrets.h) сохранены и
перенесены ниже белого списка: более поздний шаблон побеждает, поэтому
они продолжают действовать внутри отслеживаемых папок.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 23:13:58 +07:00
oskarvitaliiandClaude Opus 5 2aa7e20fd4 docs: add pump_controller_8_2_OLED_DONE to the projects index
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 22:56:50 +07:00
oskarvitalii e91d13b8eb Добавить проект pump_controller_8_2_OLED_DONE
Контроллер насосной станции на Wemos D1 mini: мониторинг двух основных
и резервного насоса, два реле аварий, OLED, веб-панель с настройками,
уведомления на HTTPS-webhook.

История проекта влита целиком, в сообщениях коммитов разобраны причины
принятых решений: выбор пинов D3/D4 под реле active-LOW, ограничение
блокировки loop() при отправке webhook, перенос настроек в LittleFS и
найденная причина несохранения настроек (чип 2 МБ при настройке IDE 4 МБ).
2026-08-07 22:56:09 +07:00
oskarvitaliiandClaude Opus 5 3f21b44a65 Уточнить в коде причину отказа от EEPROM
Комментарий утверждал, что запись в EEPROM на этой плате проваливается,
что читается как «EEPROM сломана». На деле причина была в неверном
Flash Size, и с верной настройкой работали бы оба механизма. Формулировка
исправлена, чтобы следующий читатель не пошёл менять хранилище вместо
проверки размера чипа.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 20:05:42 +07:00
oskarvitaliiandClaude Opus 5 703c4d46c6 Зафиксировать проверенную на железе раскладку 2MB (FS:64KB)
Прошивка запущена на целевой плате: LittleFS монтируется и отдаёт
45056 Б, файл настроек читается при старте. В документации указан
фактически использованный вариант Flash Size вместо предполагавшегося
FS:128KB.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 20:04:42 +07:00
oskarvitaliiandClaude Opus 5 963209dcc9 Найдена причина: чип 2 МБ при настройке IDE 4 МБ
Плата имеет физически 2 МБ флеша, в IDE было выбрано 4MB (FS:2MB).
При этой раскладке сектор EEPROM лежит по смещению 0x3FB000, а начало
LittleFS — по 0x400000, тогда как чип заканчивается на 0x200000. Обе
области вне микросхемы, поэтому spi_flash_erase_sector() проваливался
и EEPROM.commit() возвращал false.

Лечится настройкой Flash Size = 2MB (FS:128KB OTA:~960KB), а не кодом:
любая раскладка 2MB кладёт файловую систему ниже 0x1FB000, а EEPROM в
сектор 511. С верной настройкой работали бы оба механизма хранения.

Ранее записанная в README гипотеза про «щель между FS и областью SDK»
была неверной и заменена подтверждённым разбором. Переход на LittleFS
остаётся оправданным по своим качествам, но проблему решает настройка
IDE — в документации это разделено явно, чтобы не ввести в заблуждение
при похожем симптоме на другой плате.

Документация исправлена: плата 2 МБ, а не 4 МБ; требование совпадения
выбранного размера с физическим; предупреждение о потере учётных данных
Wi-Fi при смене раскладки.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 19:51:38 +07:00
oskarvitaliiandClaude Opus 5 4c0f9672f3 Перенести настройки из EEPROM в LittleFS
EEPROM.commit() на целевой плате возвращает false — падает сам вызов SDK.
Проверкой исходников исключены ошибка в библиотеке, перекрытие адресов и
стирание сектора со стороны WiFiManager. Физический размер флеша тоже ни
при чём: LittleFS на этом же чипе работает, что подтверждено соседним
проектом на такой же плате.

Наиболее вероятный механизм: при раскладке 4MB/FS:2MB сектор EEPROM (1019,
0x3FB000) попадает в щель между концом ФС (0x3FA000) и служебной областью
SDK (0x3FC000) и оказывается защищён от стирания. Доказать не удалось,
поэтому в документации гипотеза помечена как гипотеза. Переход на LittleFS
выбран не как обход симптома, а как переезд на подтверждённо работающий
на этом железе механизм.

Настройки теперь в /settings.json (ArduinoJson v7). Загрузка
отказоустойчива: отсутствующий или битый ключ оставляет значение по
умолчанию, поэтому файл от прошлой версии прошивки не ломается при
добавлении полей. Имена ключей вынесены в константы KEY_*, используемые и
при чтении, и при записи.

Молчаливой потери настроек больше нет: при недоступной ФС взводится
settingsFsReady = false, и веб-панель показывает предупреждение, что
настройки не переживут перезагрузку. В лог при старте выводится геометрия
ФС и предупреждение при выборе FS:none.

Проверено: xtensa-lx106-elf-g++ -fsyntax-only -Wall -Wextra против ядра
3.1.2 — предупреждений нет. На железе не проверялось.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 19:41:05 +07:00
oskarvitaliiandClaude Opus 5 ca02a48684 Диагностика: печатать сектор EEPROM и границы FS против физического флеша
commit() возвращает false, то есть падает сам вызов SDK
(spi_flash_erase_sector либо spi_flash_write). Самая вероятная причина —
сектор EEPROM за пределами физической микросхемы.

При раскладке 4MB/FS:2MB сектор EEPROM это 1019 (смещение 0x3FB000), что
требует чипа не меньше 4 МБ. Теперь лог сам печатает это смещение и
сравнивает с ESP.getFlashChipRealSize(), а также границы файловой системы —
последнее важно, потому что при том же дефекте LittleFS по адресу
0x400000 окажется недоступен ровно так же.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 17:21:25 +07:00
oskarvitaliiandClaude Opus 5 fa54e5790f Диагностика сохранения EEPROM: проверять commit() и перечитывание
Настройки не сохраняются после перезагрузки. Причина пока не установлена,
это шаг сбора улик, а не исправление.

Найден дефект, мешавший диагностике: результат EEPROM.commit()
игнорировался, поэтому лог печатал «Конфигурация сохранена» безусловно —
даже при провалившейся записи. Теперь запись подтверждается повторным
чтением сектора из флеша и сравнением всех полей; при расхождении в лог
идут записанные и прочитанные значения.

Добавлена проверка геометрии флеша при старте: несовпадение размера по
настройке IDE с физическим уводит сектор EEPROM за пределы микросхемы,
и запись молча теряется — симптом ровно тот же.

Проверкой чтения исключены: корректность библиотеки EEPROM, раскладка
адресов (258 из 512, без перекрытий), стирание сектора со стороны
WiFiManager::resetSettings() — он вызывает только WiFi.disconnect().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 16:29:47 +07:00
oskarvitaliiandClaude Opus 5 cf61617a46 Настройки через веб, состояние реле в UI, распиновка под реле active-LOW
Распиновка. Пина D0, который использовался под выход «Резерв», в доступном
наборе нет. Кнопка сброса Wi-Fi удалена как избыточная: при пропавшей сети
wm.autoConnect() сам поднимает портал. Освободившийся D3 закрыл дефицит.

Выходы переехали на D3 и D4 и стали инверсными. Причина: для реле active-LOW
безопасное состояние — пин в HIGH, а D3/D4 подтянуты к HIGH внешними
резисторами платы и держат этот уровень всю загрузку. D8, наоборот, подтянут
к LOW и щёлкал бы реле при каждом включении питания. В setup() digitalWrite()
идёт до pinMode(), иначе защёлка выхода даёт короткий LOW.

Инверсия живёт только в setRelay(); логическое состояние дублируется в
relayMainOn/relayEmergOn, чтобы панель показывала смысл, а не уровень пина.

Настройки. device, выдержка аварии и выдержка резерва вынесены в /set_config
и EEPROM. Раскладка EEPROM версионирована: magic 0xA55B, старый 0xA55A
распознаётся и переносится, поэтому прошитые приборы не теряют webhook.
Значения по умолчанию воспроизводят прежнее поведение (5 с и 0 с).

Для резерва добавлена выдержка, которой раньше не было, симметрично основной
аварии. Обе выдержки гасят реле и вебхук одновременно.

device_name фильтруется до [A-Za-z0-9_-] вместо экранирования: такой набор
безопасен и в JSON, и в HTML. Пустое или неверное поле формы означает
«не менять», поэтому частичное заполнение не сбрасывает остальное.

Состояние реле выведено в /status и в панель.

Проверено: xtensa-lx106-elf-g++ -fsyntax-only -Wall -Wextra против ядра
3.1.2 — предупреждений в скетче нет (попутно убран неиспользуемый
isEmergency); валидация настроек прогнана на 22 граничных случаях;
раскладка EEPROM проверена на перекрытия. На железе не проверялось.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 16:02:21 +07:00
oskarvitaliiandClaude Opus 5 4c760e943f Задокументировать два принятых ограничения
Отсутствие ретраев webhook — сознательное решение: автономная работа
без отправки событий это штатный режим.

Пауза мониторинга до 2 минут при старте без сохранённой сети
(блокирующий портал WiFiManager в setup()) — принята как есть,
с указанием способа лечения, если понадобится.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 14:59:57 +07:00
oskarvitaliiandClaude Opus 5 355a9912ab Ограничить блокировку loop() при отправке webhook бюджетом 3 с
BearSSL делает TLS-хендшейк синхронно, поэтому вместо асинхронности
блокировка ограничивается по времени.

Главный источник залипания — не таймауты HTTPClient, а DNS:
WiFiClientSecureCtx::connect(name, port) резолвит имя через
WiFi.hostByName() без таймаута и подвешивает loop() на ~10 с.
Теперь имя разрешается заранее с лимитом 800 мс, результат попадает
в кэш lwIP, и внутренний резолв возвращается мгновенно. Соединение
по-прежнему идёт по имени, поэтому SNI и заголовок Host сохраняются.

Остаток бюджета уходит в http.setTimeout(): он ограничивает и
connect+TLS (через _client->setTimeout() до connect), и чтение
ответа (собственный цикл HTTPClient по _tcpTimeout).

Худший случай: ~3.5 с вместо прежних 10-15 с. Выходы аварии не
затронуты — digitalWrite() и раньше выполнялся до отправки.

Проверено: xtensa-lx106-elf-g++ -fsyntax-only -Wall -Wextra против
ядра 3.1.2 — в скетче замечаний нет; разбор URL прогнан на 15
граничных случаях.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 14:40:42 +07:00
oskarvitaliiandClaude Opus 5 75430a3732 Initial commit: контроллер насосов v8.2 + документация
Прошивка Wemos D1 Mini для мониторинга трёх насосов: OLED,
веб-панель, webhook-уведомления, сброс Wi-Fi кнопкой.

Адрес webhook задаётся через веб-панель и хранится в EEPROM;
в исходнике не хранится, так как содержит секретный токен.

Добавлены README.md (техдокументация) и MANUAL.md (руководство).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 13:51:22 +07:00
oskarvitaliiandClaude Opus 4.8 bda2ecb7a3 Merge dev into master: carousel dot fix
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 13:56:38 +07:00
oskarvitaliiandClaude Opus 4.8 d1f96007de fix(display): erase full icon height in boot carousel
The sun's lowest ray reached y+H, one row below the H-tall erase box,
leaving a stray dot/trail. Erase H+4 so nothing is left behind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 13:55:53 +07:00
oskarvitaliiandClaude Opus 4.8 208669d19f Merge dev into master: boot icon carousel
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:51:03 +07:00
oskarvitaliiandClaude Opus 4.8 a0f96fc52d feat(display): boot icon carousel
On power-up, after the title splash the screen clears and each weather
icon slides in from the right to centre with its colour-coded label, then
slides out to the left — Fine, Fair, Changeable, Rain, Storm.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:48:28 +07:00
oskarvitaliiandClaude Opus 4.8 68bdfdc879 Merge dev into master: RU/EN web interface + scrollable forecast log
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 11:27:09 +07:00
oskarvitaliiandClaude Opus 4.8 98e8170b8b feat(web): RU/EN language toggle (Russian default)
Client-side i18n: static labels via data-i18n, dial zone words, and the
26 Zambretti phrases + categories + trend words translated in the browser.
Choice persists in localStorage. Device firmware unchanged (API stays
English; TFT stays Latin).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 11:15:26 +07:00
oskarvitaliiandClaude Opus 4.8 e9ec610f1c feat(web): scrollable Forecast log with fixed height
Cap the log panel at ~216px with a thin themed scrollbar so a long
forecast history does not stretch the page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 10:43:02 +07:00
oskarvitaliiandClaude Opus 4.8 2bf49c14ac revert: restore pre-4b59b89 web server (drop chunked API + low-memory auto-reboot)
The chunked API handlers and the maxblock<6000 auto-restart from 4b59b89
broke the web UI on-device; restore the working String-based handlers and
plain loop while keeping the scrollable forecast log.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 08:48:25 +07:00
oskarvitaliiandClaude Opus 4.8 f8294d4aed feat(web): scrollable Forecast log with fixed height
Cap the log panel at ~216px with a thin themed scrollbar so a long
forecast history does not stretch the page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 07:08:21 +07:00
oskarvitaliiandClaude Opus 4.8 4b59b8936b fix: stream large API responses to stop heap-exhaustion truncation
Serving /api/history (and /api/forecasts) built a ~12 KB String +
JsonDocument every 15 s; over days this fragmented the ESP8266 heap until
the TCP stack could no longer send a full response, causing the web page
to fail with ERR_CONTENT_LENGTH_MISMATCH. Stream both array endpoints via
chunked transfer (small stack buffer, no big allocations). Add free-heap
to /api/current and Serial, plus a low-memory safety restart (history is
persisted first).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 06:57:09 +07:00
oskarvitaliiandClaude Opus 4.8 1e923249f6 docs: add web UI screenshot to project README
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 12:20:36 +07:00
oskarvitaliiandClaude Opus 4.8 11f57a244d chore: make repo an Arduino collection with WeatherPredictor project
Flatten so the repo root is the Arduino sketchbook; the project lives in
WeatherPredictor/. Add a collection index README at the root and move the
detailed project README into WeatherPredictor/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 11:58:33 +07:00
oskarvitaliiandClaude Opus 4.8 fc72f9b0bd docs: add project README
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 11:52:33 +07:00
oskarvitaliiandClaude Opus 4.8 482a788f10 chore: nest project under Arduino/WeatherPredictor
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 11:41:01 +07:00
oskarvitaliiandClaude Opus 4.8 2e3af80188 feat(display): mini forecast icon + Wi-Fi IP, BGR colour fix
Rework the 160x80 status screen: large predicted-weather icon in the
right window (scaled to fill), Wi-Fi/IP line at the bottom (or 'offline'),
colour-coded forecast category, and the trend arrow shown only when
rising/falling. Colours are built R/B-swapped for this BGR ST7735S panel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 23:05:13 +07:00
oskarvitaliiandClaude Opus 4.8 f1fe0b728f feat: forecast change log
Record each forecast change (Zambretti letter transition) with a
timestamp into a 24-entry ring buffer, persisted to LittleFS and
restored on boot. New /api/forecasts endpoint and a 'Forecast log'
panel in the web UI (newest first, category colour, timestamp).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:40:28 +07:00
oskarvitaliiandClaude Opus 4.8 f4a9a04207 feat(web): city presets for station settings
Add a City preset dropdown (Astana, Karaganda, Almaty, Bishkek,
Tashkent, Yekaterinburg, Omsk, Novosibirsk, Krasnoyarsk, Moscow,
Saint Petersburg) that fills altitude, coordinates and time zone from
average values. Manual entry and Custom remain; the matching preset is
auto-selected on load when coordinates match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:21:32 +07:00
oskarvitaliiandClaude Opus 4.8 8dc469be3d feat(web): UTC-offset time zone picker and labelled barograph axes
Time zone is now a UTC+/-HH dropdown (e.g. UTC+7) instead of raw
minutes. Barograph gains dual value axes (pressure left in hPa,
temperature right in C), start/end time labels and gridlines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:10:02 +07:00
oskarvitaliiandClaude Opus 4.8 26a5071e86 feat(web): redesign UI as a barometer instrument
Dark dusk-indigo panel with brass/enamel accents, serif almanac
headings + monospace instrument readouts. Signature element: an
antique barometer dial with a live needle and a ghost 'set hand' at
the pressure 3h ago -- the trend made physical. Barograph, stat tiles
and settings kept quiet. Fully self-contained (no external fonts/CDNs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:42:34 +07:00
oskarvitaliiandClaude Opus 4.8 74121a2ef2 fix: robust 3h trend across non-monotonic epochs (NTP jumps)
Scan all samples for the newest one >=3h old instead of assuming
strictly increasing timestamps, so an NTP time correction no longer
breaks trend detection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:27:10 +07:00
oskarvitaliiandClaude Opus 4.8 e29faf641a feat: self-contained web UI with live view, SVG chart, settings
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:21:59 +07:00
oskarvitaliiandClaude Opus 4.8 396688cd98 feat: HTTP server with REST API for current/history/settings
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:15:16 +07:00
oskarvitaliiandClaude Opus 4.8 ea0256a5b9 feat: NTP time sync writing to DS3231 (by IP, DNS-independent)
This LAN's router does not resolve external hostnames, so NTP uses
hard-coded Cloudflare/Google anycast IPs instead of names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:05:29 +07:00
oskarvitaliiandClaude Opus 4.8 cb3a06382e feat: Wi-Fi provisioning via WiFiManager captive portal
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 20:41:30 +07:00
oskarvitaliiandClaude Opus 4.8 77d0a35d69 feat: persist pressure history to LittleFS across reboots
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 20:31:53 +07:00
oskarvitaliiandClaude Opus 4.8 0a8dbe198d feat: integrate scheduler and full status display (landscape 160x80)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 20:18:36 +07:00
oskarvitaliiandClaude Opus 4.8 4e53e7388f feat: persistent settings in LittleFS JSON
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 20:08:03 +07:00
oskarvitaliiandClaude Opus 4.8 5c96cfc1f3 feat: in-RAM pressure history ring buffer with 3h trend
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 20:02:40 +07:00
oskarvitaliiandClaude Opus 4.8 5819739fcb feat: Zambretti forecast with trend classification
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 19:59:17 +07:00
oskarvitaliiandClaude Opus 4.8 2d52fcd851 feat: DS3231 RTC read/set with lost-power detection
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 19:55:47 +07:00
oskarvitaliiandClaude Opus 4.8 d042a94378 feat: BMP180 read and sea-level pressure conversion
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 19:43:03 +07:00
oskarvitaliiandClaude Opus 4.8 906adaddfe feat: TFT display init and splash screen
Landscape 160x80 (rotation 3, no invert) per this panel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 19:39:13 +07:00