#ifndef GLOBAL_H
#define GLOBAL_H
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
#include "Config.h"
// -------------------- EEPROM layout --------------------
// 0..31 - SSID (макс 32 байта, включая '\0')
// 32..95 - PASS (макс 64 байта, включая '\0')
// 96 - isOn (1 байт)
// 97 - deviceMode (1 байт)
// 98..129 - deviceName (макс 32 байта, включая '\0')
// 130..193 - authToken (макс 64 байта, включая '\0')
const uint16_t EEPROM_SIZE = 256; // было 128, теперь немного с запасом
const uint16_t SSID_ADDR = 0;
const uint16_t SSID_MAX_LEN = 32;
const uint16_t PASS_ADDR = SSID_ADDR + SSID_MAX_LEN; // 32
const uint16_t PASS_MAX_LEN = 64; // 32..95
const uint16_t IS_ON_ADDR = PASS_ADDR + PASS_MAX_LEN; // 96
const uint16_t DEVICE_MODE_ADDR = IS_ON_ADDR + 1; // 97
const uint16_t DEVICE_NAME_ADDR = DEVICE_MODE_ADDR + 1; // 98
const uint16_t DEVICE_NAME_MAX_LEN = 32; // 98..129
const uint16_t AUTH_TOKEN_ADDR = DEVICE_NAME_ADDR + DEVICE_NAME_MAX_LEN; // 130
const uint16_t AUTH_TOKEN_MAX_LEN = 64; // 130..193
// -------------------- Глобальные объекты ядра --------------------
ESP8266WebServer server(80);
// WiFi-конфиг, прочитанный из EEPROM
String savedSSID = "";
String savedPASS = "";
// -------------------- Device mode & config --------------------
enum DeviceMode : uint8_t {
DEVICE_MODE_SETUP = 0,
DEVICE_MODE_NORMAL = 1,
DEVICE_MODE_ERROR = 2,
DEVICE_MODE_UPDATING = 3
};
// Глобальная конфигурация устройства (живёт в RAM, но сохраняется в EEPROM)
DeviceMode deviceMode = DEVICE_MODE_SETUP; // по умолчанию setup
String deviceName = "Relay 1"; // дефолтное имя
String serverBaseUrl = ""; // пока держим только в RAM
String authToken = ""; // токен авторизации
// Состояние устройства (канала 1)
bool isOn = false;
// -------------------- Внешние функции, которые реализует устройство --------------------
// Реализация задаётся в конкретном проекте (реле, кнопка и т.п.)
void setOn(bool on);
// Эти функции реализованы в других .h (WebHandlers / REST_API),
// но вызываются из ядра:
void registerWebUiRoutes();
void registerRestApiRoutes();
// -------------------- helpers для EEPROM --------------------
inline String readStringFromEEPROM(uint16_t addr, uint16_t maxLen) {
char buf[100];
if (maxLen > sizeof(buf)) maxLen = sizeof(buf);
for (uint16_t i = 0; i < maxLen; i++) {
buf[i] = EEPROM.read(addr + i);
if (buf[i] == '\0') {
return String(buf);
}
}
buf[maxLen - 1] = '\0';
return String(buf);
}
inline void writeStringToEEPROM(uint16_t addr, const String &str, uint16_t maxLen) {
uint16_t len = str.length();
if (len >= maxLen) len = maxLen - 1;
for (uint16_t i = 0; i < len; i++) {
EEPROM.write(addr + i, str[i]);
}
EEPROM.write(addr + len, 0);
for (uint16_t i = len + 1; i < maxLen; i++) {
EEPROM.write(addr + i, 0);
}
}
inline void saveWiFiConfig(const String &ssid, const String &pass) {
EEPROM.begin(EEPROM_SIZE);
writeStringToEEPROM(SSID_ADDR, ssid, SSID_MAX_LEN);
writeStringToEEPROM(PASS_ADDR, pass, PASS_MAX_LEN);
EEPROM.commit();
EEPROM.end();
}
inline void loadWiFiConfig() {
EEPROM.begin(EEPROM_SIZE);
savedSSID = readStringFromEEPROM(SSID_ADDR, SSID_MAX_LEN);
savedPASS = readStringFromEEPROM(PASS_ADDR, PASS_MAX_LEN);
EEPROM.end();
}
// -------------------- Device config save/load --------------------
inline void loadDeviceConfig() {
EEPROM.begin(EEPROM_SIZE);
// читаем режим
uint8_t rawMode = EEPROM.read(DEVICE_MODE_ADDR);
// читаем имя и токен
String name = readStringFromEEPROM(DEVICE_NAME_ADDR, DEVICE_NAME_MAX_LEN);
String token = readStringFromEEPROM(AUTH_TOKEN_ADDR, AUTH_TOKEN_MAX_LEN);
EEPROM.end();
// режим
if (rawMode <= DEVICE_MODE_UPDATING) {
deviceMode = static_cast<DeviceMode>(rawMode);
} else {
deviceMode = DEVICE_MODE_SETUP; // 0xFF / мусор -> считаем как первый запуск
}
// имя устройства
if (name.length() > 0) {
deviceName = name;
} // иначе оставляем "Relay 1"
// токен
if (token.length() > 0) {
authToken = token;
}
}
inline void saveDeviceConfig() {
EEPROM.begin(EEPROM_SIZE);
EEPROM.write(DEVICE_MODE_ADDR, static_cast<uint8_t>(deviceMode));
writeStringToEEPROM(DEVICE_NAME_ADDR, deviceName, DEVICE_NAME_MAX_LEN);
writeStringToEEPROM(AUTH_TOKEN_ADDR, authToken, AUTH_TOKEN_MAX_LEN);
EEPROM.commit();
EEPROM.end();
}
inline void saveIsOn(bool on) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.write(IS_ON_ADDR, on ? 1 : 0);
EEPROM.commit();
EEPROM.end();
}
inline void loadIsOn() {
EEPROM.begin(EEPROM_SIZE);
uint8_t v = EEPROM.read(IS_ON_ADDR);
EEPROM.end();
isOn = (v != 0);
}
// -------------------- WiFi helpers --------------------
inline bool tryConnectWiFi() {
if (savedSSID.length() == 0) {
Serial.println(F("No saved SSID, skipping STA connect"));
return false;
}
Serial.print(F("Connecting to WiFi SSID: "));
Serial.println(savedSSID);
WiFi.mode(WIFI_STA);
WiFi.begin(savedSSID.c_str(), savedPASS.c_str());
unsigned long start = millis();
const unsigned long timeout = 15000; // 15 сек
while (WiFi.status() != WL_CONNECTED && millis() - start < timeout) {
delay(500);
Serial.print(".");
}
Serial.println();
if (WiFi.status() == WL_CONNECTED) {
Serial.print(F("WiFi connected, IP: "));
Serial.println(WiFi.localIP());
return true;
} else {
Serial.println(F("WiFi connect failed"));
return false;
}
}
inline void startAPMode() {
WiFi.mode(WIFI_AP);
String apSSID = "ESP-RELAY-";
apSSID += String(ESP.getChipId(), HEX);
const char* apPass = "noAccess";
bool ok = WiFi.softAP(apSSID.c_str(), apPass);
if (ok) {
Serial.print(F("AP started, SSID: "));
Serial.println(apSSID);
Serial.print(F("AP IP: "));
Serial.println(WiFi.softAPIP());
} else {
Serial.println(F("AP start FAILED"));
}
}
// -------------------- Utils --------------------
inline String getUniqueID() {
return String(ESP.getChipId(), HEX);
}
inline String getMAC() {
uint8_t mac[6];
WiFi.macAddress(mac);
char buf[13];
sprintf(buf, "%02X%02X%02X%02X%02X%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return String(buf);
}
// -------------------- Core setup / loop --------------------
inline void coreSetup() {
Serial.begin(115200);
delay(1200);
Serial.println();
Serial.println(F("Booting..."));
Serial.print(F("Firmware: "));
Serial.println(FW_VERSION);
// читаем WiFi-конфиг и состояние реле из EEPROM
loadWiFiConfig();
loadIsOn();
loadDeviceConfig();
setOn(isOn); // применяем состояние к пину (реализует устройство)
Serial.print(F("Saved SSID: "));
Serial.println(savedSSID);
// пытаемся подключиться, иначе AP
if (!(savedSSID.length() > 0 && tryConnectWiFi())) {
startAPMode();
}
// регистрируем роуты веб-панели и REST API
registerWebUiRoutes();
registerRestApiRoutes();
server.begin();
Serial.println(F("HTTP server started"));
}
inline void coreLoop() {
server.handleClient();
// авто-реконнект WiFi
if (savedSSID.length() > 0 &&
WiFi.getMode() == WIFI_STA &&
WiFi.status() != WL_CONNECTED)
{
static uint32_t lastReconnectAttempt = 0;
if (millis() - lastReconnectAttempt > 10000UL) {
lastReconnectAttempt = millis();
tryConnectWiFi();
}
}
}
#endif // GLOBAL_H