From c555a32f61656fda2a7c7424c199efcadc17aca6 Mon Sep 17 00:00:00 2001 From: vlapa Date: Wed, 15 Jul 2026 01:11:18 +0300 Subject: first --- src/config-source.h | 40 +++++ src/main.cpp | 474 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 514 insertions(+) create mode 100644 src/config-source.h create mode 100644 src/main.cpp (limited to 'src') diff --git a/src/config-source.h b/src/config-source.h new file mode 100644 index 0000000..628439d --- /dev/null +++ b/src/config-source.h @@ -0,0 +1,40 @@ +////////////////////////////////////////////////////////// +// +// Файл нужно заполнить и переименовать в config.h !!! +// +///////////////////////////////////////////////////////// + +#ifndef CONFIG_H +#define CONFIG_H + +// WiFi настройки: +static const char *WIFI_SSID = ""; +static const char *WIFI_PASS = ""; + +// NTP настройки: +static const char *NTP_SERVER = "1.ru.pool.ntp.org"; // "ntp3.vniiftri.ru"; + +// Серверные настройки: +static const char *SERVER_HOST = ""; +static const int SERVER_PORT = 8000; +static const char *SERVER_PATH = "/"; + +// Настройки табло +static const uint8_t TIME_DAY = 8; +static const uint8_t TIME_NIGHT = 18; +static const uint8_t BRIGHT_DAY = 4; +static const uint8_t BRIGHT_NIGHT = 1; + +// Названия датчиков +static const char* SENSOR_1 = ""; // улица +static const char* SENSOR_2 = ""; // комната + +// Цвета (можно вынести для удобства) +static const uint32_t COLOR_TIME = 16766720; // желтый +static const uint32_t COLOR_TEMP = 16711935; // красный +static const uint32_t COLOR_TEMP_OUT = 9127187; // коричневый +static const uint32_t COLOR_PRESS = 9830400; // розовый +static const uint32_t COLOR_WIFI = 16728935; // томато +static const uint32_t COLOR_HUM = 52945; // бирюзовый + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..51cfe96 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,474 @@ +///////////////////////////////////////////////// +// +// 2026-06-12 ... 2026-07-08 v.012 +// +///////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "tablo_ws2812.h" + +const uint32_t utcOffsetInSeconds = 10800; +const uint32_t utcPeriodMseconds = 86400000; // 86400000-р/сут + +WiFiUDP ntpUDP; +NTPClient timeClient(ntpUDP, NTP_SERVER, utcOffsetInSeconds, utcPeriodMseconds); + +String sensorKeys; + +WiFiClient espClient; +HTTPClient http; + +uint32_t timeUpdateH = 02; // время обновления NTP +uint32_t timeUpdateM = 05; // время обновления NTP +String z = ""; // строка данных для отображения + +bool flagVis = true; +bool flagSec = true; +uint8_t brightVis = 0; +uint32_t timeReadOld = 0; +uint8_t visData[] = {5, 2, 2, 2, 2}; +uint8_t displayMode = 0; +unsigned long lastModeSwitch = 0; + +unsigned long lastHTTPRequest = 0; +const unsigned long HTTP_UPDATE_INTERVAL = 60000; // Обновляем раз в минуту + +struct +{ + float temp_in = 0.0; + uint16_t pres = 0; + uint8_t hum = 0; + float temp_out = 0.0; +} dataSensors; + +const uint8_t NUMBER_OF_ATTEMPTS = 20; + +ESP8266WebServer server(80); +ESP8266HTTPUpdateServer httpUpdater; + +//=================================================== +// подключение к WiFi +bool setupWiFi() +{ + z = "ABABA"; + visibleWork(z, COLOR_WIFI, BRIGHT_DAY); + strip.show(); + + digitalWrite(LED_BUILTIN, LOW); + Serial.println("\n\nSetup WiFi: "); + + WiFi.begin(WIFI_SSID, WIFI_PASS); + uint8_t count = NUMBER_OF_ATTEMPTS; + while (WiFi.status() != WL_CONNECTED) + { + Serial.print(count); + Serial.print('>'); + if (!count--) + return false; + delay(500); + } + digitalWrite(LED_BUILTIN, HIGH); + + //----------------------------------- + // индикация IP + z = "DDDDD"; + visibleWork(z, COLOR_WIFI, BRIGHT_DAY); + strip.show(); + String l = WiFi.localIP().toString(); + l = l.substring(l.lastIndexOf('.') + 1, l.length()); + (l.toInt() < 100) ? l = "DDE" + l : l = "DE" + l; + visibleWork(l, COLOR_WIFI, BRIGHT_DAY); + strip.show(); + Serial.print("\nWiFi connected !\nIP: "); + Serial.println(WiFi.localIP()); + delay(2000); + visibleWork("DDDDD", COLOR_WIFI, BRIGHT_DAY); + strip.show(); + + //----------------------------------- + // индикация силы сигнала + int8_t RSSI_MAX = -50; + int8_t RSSI_MIN = -100; + int8_t dBm = WiFi.RSSI(); + Serial.print("RSSI dBm = "); + Serial.println(dBm); + l = ""; + (dBm <= RSSI_MIN) ? l += 0 : (dBm >= RSSI_MAX) ? l += 100 + : l += 2 * (dBm + 100); + Serial.print("RSSI % = "); + Serial.println(l); + Serial.println(); + (l.toInt() < 100) ? l = "DDE" + l : l = "DE" + l; + + visibleWork(l, COLOR_WIFI, BRIGHT_DAY); + strip.show(); + delay(2000); + visibleWork("DDDDD", COLOR_WIFI, BRIGHT_DAY); + strip.show(); + + return true; +} + +//=================================================== +// синхронизация с NTP: +bool setupNTP() +{ + Serial.println("\nСинхронизируем время: "); + uint8_t count = NUMBER_OF_ATTEMPTS; + while (!timeClient.update()) + { + Serial.print(count); + Serial.print('.'); + if (!count--) + return false; + delay(500); + } + Serial.println(); + Serial.println(timeClient.getFormattedTime()); + Serial.println(); + + return true; +} + +//=================================================== +// Запрос POST на сервер: +String http_response() +{ + String payload; + String url = "http://" + String(SERVER_HOST) + ":" + String(SERVER_PORT) + SERVER_PATH; + + Serial.println("Sending POST to: " + url); + Serial.println("Data: " + sensorKeys); + + http.begin(espClient, url); + http.addHeader("Content-Type", "text/plain"); + http.setTimeout(5000); + + // Отправляем POST с нашими ключами + int httpCode = http.POST(sensorKeys); + + // Анализируем ответ + if (httpCode == HTTP_CODE_OK) + { + Serial.printf("Response code: %d\n", httpCode); + payload = http.getString(); + Serial.println("Server response: \n" + payload); + http.end(); + return payload; + } + else + { + Serial.printf("POST failed, error: %s\n", + http.errorToString(httpCode).c_str()); + http.end(); + return ""; // Возвращаем пустую строку при ошибке + } +} + +//=================================================== +// Парсим ответ сервера: +bool parse_data_http(String data) +{ + if (data.length() == 0) + { + Serial.println("Warning: Empty data received"); + return false; + } + + // Находим все разделители для первой части (до первой *) + int idx1 = data.indexOf(';'); // после temp_in + int idx2 = data.indexOf(';', idx1 + 1); // после pres + int idx3 = data.indexOf(';', idx2 + 1); // после hum + + // Находим вторую звездочку (перед temp_out) + int firstStar = data.indexOf('*'); + int secondStar = data.indexOf('*', firstStar + 1); + + if (idx1 == -1 || idx2 == -1 || idx3 == -1 || secondStar == -1) + { + Serial.println("Warning: Invalid data format: " + data); + return false; + } + + // Парсим первую часть + dataSensors.temp_in = data.substring(1, idx1).toFloat(); // Пропускаем первый * + dataSensors.pres = data.substring(idx1 + 1, idx2).toInt(); + dataSensors.hum = data.substring(idx2 + 1, idx3).toInt(); + + // Парсим вторую температуру (после второй звездочки) + int startTempOut = secondStar + 1; + int endTempOut = data.indexOf(';', startTempOut); + + if (endTempOut == -1) + { + endTempOut = data.indexOf('*', startTempOut); + } + if (endTempOut == -1) + { + endTempOut = data.length(); + } + + dataSensors.temp_out = data.substring(startTempOut, endTempOut).toFloat(); + + Serial.printf("Parsed: T_in=%.2f, P=%d, H=%d, T_out=%.2f\n", + dataSensors.temp_in, dataSensors.pres, + dataSensors.hum, dataSensors.temp_out); + + return true; +} + +//=================================================== +// Форматируем время: +void TimeMillis() +{ + z = ""; + + if (timeClient.getHours() < 10) + z += "D"; + z += timeClient.getHours(); + + (flagSec) ? z += "B" : z += "D"; + + if (timeClient.getMinutes() < 10) + z += "0"; + + z += timeClient.getMinutes(); + + if ((timeClient.getHours() >= TIME_DAY) && (timeClient.getHours() < TIME_NIGHT)) + { + brightVis = BRIGHT_DAY; + } + else + { + brightVis = BRIGHT_NIGHT; + } +} + +//=================================================== +// Форматируем температуру: +void fTemp(float t) +{ + z = ""; + + // Отрицательная температура + bool isNegative = (t < 0); + if (isNegative) + { + t = -t; + z += "B"; // Минус + } + + // Целая часть + int whole = (int)t; + if (whole < 10 && !isNegative) + { + z += "D"; // Ведущий ноль для выравнивания + } + z += String(whole); + + // Дробная часть (с правильным округлением) + int frac = (int)((t - whole) * 10 + 0.5); + if (frac > 0) + { + z += "E"; // Точка + z += String(frac); + } + + // Градус + z += "A"; + + // Дополняем до 5 символов (если нужно) + while (z.length() < 5) + { + z = "D" + z; + } +} + +// Форматируем давление, влажность: +void fOther(int o) +{ + if (o < 10) + { + z = "DDDD"; + } + else if (o < 100) + { + z = "DDD"; + } + else + { + z = "DD"; + } + z += o; +} + +//=================================================== +// Обновляем данные отображения: +void updateDisplay() +{ + if (!flagVis) + return; + + switch (displayMode) + { + case 0: + TimeMillis(); + visibleWork(z, COLOR_TIME, brightVis); + strip.show(); + flagVis = false; + break; + + case 1: + fTemp(dataSensors.temp_in); + visibleWork(z, COLOR_TEMP, brightVis); + strip.show(); + flagVis = false; + + // Обновляем данные с сервера не чаще чем раз в минуту + if (millis() - lastHTTPRequest >= HTTP_UPDATE_INTERVAL) + { + String response = http_response(); + if (response.length() > 0) + { + if (parse_data_http(response)) + { + lastHTTPRequest = millis(); + } + } + else + { + Serial.println("Warning: Using cached sensor data"); + } + } + break; + + case 2: + fOther(dataSensors.pres); + visibleWork(z, COLOR_PRESS, brightVis); + strip.show(); + flagVis = false; + break; + + case 3: + fOther(dataSensors.hum); + visibleWork(z, COLOR_HUM, brightVis); + strip.show(); + flagVis = false; + break; + + case 4: + fTemp(dataSensors.temp_out); + visibleWork(z, COLOR_TEMP_OUT, brightVis); + strip.show(); + flagVis = false; + break; + + default: + break; + } +} + +//=================================================== +void setup() +{ + Serial.begin(115200); + Serial.println("\n\n=== TABLO STARTING ==="); + + ESP.wdtEnable(5000); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + + strip.begin(); + z = "DDDDD"; + visibleWork(z, COLOR_WIFI, BRIGHT_DAY); + strip.show(); + + if (!setupWiFi()) + { + Serial.println("WiFi setup failed, restarting..."); + ESP.restart(); + } + + timeClient.begin(); + if (!setupNTP()) + { + Serial.println("NTP setup failed, restarting..."); + ESP.restart(); + } + + sensorKeys = String(SENSOR_1) + "," + String(SENSOR_2); + + // Первый запрос к серверу + String response = http_response(); + if (response.length() > 0) + { + parse_data_http(response); + } + + server.begin(); + httpUpdater.setup(&server); + Serial.println("HTTP server started"); + + lastModeSwitch = millis(); +} + +//=================================================== +void loop() +{ + // Проверка WiFi + if (WiFi.status() != WL_CONNECTED) + { + Serial.println("WiFi lost, reconnecting..."); + if (!setupWiFi()) + ESP.restart(); + } + + // Синхронизация NTP каждые 6 часов + static unsigned long lastNTPSync = 0; + if (millis() - lastNTPSync > 21600000) + { + if (timeClient.update()) + { + lastNTPSync = millis(); + Serial.println("NTP synchronized"); + } + } + + // Обновление дисплея + if (flagVis) + updateDisplay(); + + // Отображение секундного тире (только для часов) + if (displayMode == 0) + { + if (millis() - timeReadOld >= 500) + { + flagSec = !flagSec; + timeReadOld = millis(); + flagVis = true; + } + } + + // Переключение режимов + unsigned long current = millis(); + if (current - lastModeSwitch >= (unsigned long)visData[displayMode] * 1000) + { + displayMode = (displayMode + 1) % (sizeof(visData) / sizeof(visData[0])); + lastModeSwitch = current; + flagVis = true; + timeReadOld = current; + } + + // Feed watchdog + ESP.wdtFeed(); + + server.handleClient(); + delay(1); // Уменьшил до 1 мс для более быстрого реагирования +} \ No newline at end of file -- cgit v1.2.3