đ About This Project
ESP8266 D1 Large with DHT22 sensor for temperature and humidity monitoring. Features 5 independent relay controls on dedicated pins (D1, D2, D3, D4, D5). Connects to OceanRemote platform for remote access with token authentication, session management, and offline backup.
**Features:**
- Real-time temperature and humidity monitoring
- 5 independent relay controls
- Secure token-based authentication
- Session management with auto-refresh
- Offline backup (stores last relay states)
- WiFi auto-reconnect
- Persistent storage in EEPROM
- DHT22 sensor support
- Persistent Device ID storage
**Pin Mapping:**
- Relay 1: D1 (GPIO5)
- Relay 2: D2 (GPIO4)
- Relay 3: D3 (GPIO0)
- Relay 4: D4 (GPIO2)
- Relay 5: D5 (GPIO14)
- DHT22 DATA: D2 (GPIO4)
**Wiring Instructions:**
- DHT22 VCC â D1 Large 3.3V
- DHT22 DATA â D2 (GPIO4)
- DHT22 GND â D1 Large GND
- Add 10kΊ pull-up resistor between DATA and VCC
- Relays connect to 5V power supply (external)
- Relay signal pins to D1, D2, D3, D4, D5
- Connect relay GND to D1 Large GND
**Use Cases:**
- Home climate control
- Greenhouse automation
- Server room monitoring
- Industrial temperature logging
- Remote relay switching
- Smart home HVAC control
đť Firmware Code
// OceanicRemote Secure Firmware - ESP8266 D1 Large
// Version 5.0 - Persistent Device ID & Recovery Logic
// With Session Authentication & Custom Relay Names & Per-Relay Logic
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#include <DHT.h>
#define DHTPIN D2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// ========== EEPROM CONFIGURATION ==========
#define EEPROM_SIZE 512
#define TOKEN_ADDRESS 0
#define TOKEN_MAGIC 0xAA55
#define SESSION_ADDRESS 128
#define SESSION_MAGIC 0xBB66
#define FLAG_ADDRESS 256
#define FLAG_MAGIC 0xCC77
#define VERSION_ADDRESS 260
#define DEVICE_ID_ADDRESS 300
#define DEVICE_ID_MAGIC 0xDD88
#define FIRMWARE_VERSION 50
// ========== RELAY NAMES ==========
const char* relayNames[5] = {
"FAN",
"Relay 2",
"Relay 3",
"Relay 4",
"Relay 5"
};
// ========== DEVICE STATE ==========
enum DeviceState {
STATE_PROVISIONING = 0,
STATE_REGISTERED = 1,
STATE_ERROR = 2,
STATE_RECONNECTING = 3
};
DeviceState currentState = STATE_PROVISIONING;
// ========== WIFI CONFIGURATION ==========
const char* WIFI_SSID = "SSIDWIFI";
const char* WIFI_PASSWORD = "PASSWORDJIO";
// ========== SERVER CONFIGURATION ==========
const char* serverHost = "www.oceanremote.net";
// ========== REGISTRATION TOKEN ==========
const char* REGISTRATION_TOKEN = "oc_reg_SurIG9GAo_BCqaJF23ssRZXBxeUDy7abTlQme3du6RM";
// ========== OPTIMIZED TIMING ==========
const unsigned long BASE_POLL_INTERVAL = 3000;
unsigned long nextCheckTime = 0;
const int JITTER_RANGE_MS = 1500;
unsigned long lastWiFiReconnect = 0;
const unsigned long wifiReconnectInterval = 30000;
int wifiRetryCount = 0;
const int MAX_WIFI_RETRIES = 5;
int registrationRetryCount = 0;
const int MAX_REGISTRATION_RETRIES = 3;
int consecutiveFailures = 0;
// ========== SESSION MANAGEMENT ==========
String sessionId = "";
String permanentToken = "";
bool sessionValid = false;
bool registrationTokenUsed = false;
// ========== DEVICE ID ==========
String deviceId = "";
String macAddress = "";
// ========== RELAY STATES ==========
bool relayStates[5] = {false, false, false, false, false};
bool deviceRegistered = false;
// ========== PIN DEFINITIONS ==========
const int relayPins[] = {D1, D2, D3, D4, D5};
const int relayCount = 5;
#define LED_PIN D4
#define ERROR_LED_PIN D4
// ========== SENSOR VARIABLES ==========
float temperature = -999;
float humidity = -999;
// ========== ERROR HANDLING ==========
void enterErrorState(String reason) {
Serial.println("");
Serial.println("========================================");
Serial.print("[ERROR] Entering error state: ");
Serial.println(reason);
Serial.println("========================================");
currentState = STATE_ERROR;
for (int i = 0; i < 3; i++) {
digitalWrite(ERROR_LED_PIN, HIGH);
delay(500);
digitalWrite(ERROR_LED_PIN, LOW);
delay(500);
}
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
// ========== EEPROM STORAGE FUNCTIONS ==========
void initEEPROM() {
Serial.println("[EEPROM] Initializing...");
EEPROM.begin(EEPROM_SIZE);
int storedVersion;
EEPROM.get(VERSION_ADDRESS, storedVersion);
Serial.printf("[EEPROM] Stored version: %d, Current: %d\n", storedVersion, FIRMWARE_VERSION);
if (storedVersion != FIRMWARE_VERSION) {
Serial.println("[EEPROM] Version mismatch - CLEARING EEPROM!");
for (int i = 0; i < EEPROM_SIZE; i++) {
EEPROM.write(i, 0);
}
EEPROM.put(VERSION_ADDRESS, FIRMWARE_VERSION);
EEPROM.commit();
Serial.println("[EEPROM] EEPROM cleared!");
}
}
void saveTokenToEEPROM(String token) {
Serial.println("[EEPROM] Saving token...");
uint16_t magic = TOKEN_MAGIC;
EEPROM.put(TOKEN_ADDRESS, magic);
int addr = TOKEN_ADDRESS + sizeof(magic);
int len = token.length();
EEPROM.put(addr, len);
addr += sizeof(len);
for (int i = 0; i < len; i++) {
EEPROM.write(addr + i, token[i]);
}
uint16_t checksum = 0;
for (int i = 0; i < len; i++) {
checksum += token[i];
}
EEPROM.put(addr + len, checksum);
EEPROM.commit();
Serial.printf("[EEPROM] Token saved\n");
}
String loadTokenFromEEPROM() {
Serial.println("[EEPROM] Loading token...");
uint16_t magic;
EEPROM.get(TOKEN_ADDRESS, magic);
if (magic != TOKEN_MAGIC) {
Serial.println("[EEPROM] No valid token found");
return "";
}
int addr = TOKEN_ADDRESS + sizeof(magic);
int len;
EEPROM.get(addr, len);
if (len <= 0 || len > 200) {
Serial.println("[EEPROM] Invalid token length");
return "";
}
addr += sizeof(len);
char buffer[256];
for (int i = 0; i < len; i++) {
buffer[i] = EEPROM.read(addr + i);
}
buffer[len] = '\0';
uint16_t storedChecksum;
EEPROM.get(addr + len, storedChecksum);
uint16_t calculatedChecksum = 0;
for (int i = 0; i < len; i++) {
calculatedChecksum += buffer[i];
}
if (storedChecksum != calculatedChecksum) {
Serial.println("[EEPROM] Checksum mismatch!");
return "";
}
String token = String(buffer);
Serial.printf("[EEPROM] Loaded token (length: %d)\n", token.length());
return token;
}
void clearTokenFromEEPROM() {
Serial.println("[EEPROM] Clearing token...");
for (int i = 0; i < 64; i++) {
EEPROM.write(TOKEN_ADDRESS + i, 0);
}
EEPROM.commit();
}
void saveSessionToEEPROM(String session) {
Serial.printf("[EEPROM] Saving session: %s\n", session.c_str());
uint16_t magic = SESSION_MAGIC;
EEPROM.put(SESSION_ADDRESS, magic);
int addr = SESSION_ADDRESS + sizeof(magic);
int len = session.length();
EEPROM.put(addr, len);
addr += sizeof(len);
for (int i = 0; i < len; i++) {
EEPROM.write(addr + i, session[i]);
}
EEPROM.commit();
}
String loadSessionFromEEPROM() {
uint16_t magic;
EEPROM.get(SESSION_ADDRESS, magic);
if (magic != SESSION_MAGIC) return "";
int addr = SESSION_ADDRESS + sizeof(magic);
int len;
EEPROM.get(addr, len);
if (len <= 0 || len > 200) return "";
addr += sizeof(len);
char buffer[256];
for (int i = 0; i < len; i++) {
buffer[i] = EEPROM.read(addr + i);
}
buffer[len] = '\0';
return String(buffer);
}
void clearSessionFromEEPROM() {
for (int i = 0; i < 32; i++) {
EEPROM.write(SESSION_ADDRESS + i, 0);
}
EEPROM.commit();
}
void markRegistrationUsed() {
Serial.println("[EEPROM] Marking registration as used...");
uint16_t magic = FLAG_MAGIC;
EEPROM.put(FLAG_ADDRESS, magic);
EEPROM.commit();
}
bool isRegistrationUsed() {
uint16_t magic;
EEPROM.get(FLAG_ADDRESS, magic);
return (magic == FLAG_MAGIC);
}
void clearAllStorage() {
Serial.println("[EEPROM] Clearing all storage...");
clearTokenFromEEPROM();
clearSessionFromEEPROM();
for (int i = 0; i < 32; i++) {
EEPROM.write(FLAG_ADDRESS + i, 0);
}
EEPROM.commit();
}
// ========== PERSISTENT DEVICE ID FUNCTIONS ==========
void saveDeviceIdToEEPROM(String id) {
Serial.println("[EEPROM] Saving Device ID...");
uint16_t magic = DEVICE_ID_MAGIC;
EEPROM.put(DEVICE_ID_ADDRESS, magic);
int addr = DEVICE_ID_ADDRESS + sizeof(magic);
int len = id.length();
EEPROM.put(addr, len);
addr += sizeof(len);
for (int i = 0; i < len; i++) {
EEPROM.write(addr + i, id[i]);
}
uint16_t checksum = 0;
for (int i = 0; i < len; i++) {
checksum += id[i];
}
EEPROM.put(addr + len, checksum);
EEPROM.commit();
Serial.printf("[EEPROM] Device ID saved: %s\n", id.c_str());
}
String loadDeviceIdFromEEPROM() {
uint16_t magic;
EEPROM.get(DEVICE_ID_ADDRESS, magic);
if (magic != DEVICE_ID_MAGIC) {
Serial.println("[EEPROM] No saved Device ID found");
return "";
}
int addr = DEVICE_ID_ADDRESS + sizeof(magic);
int len;
EEPROM.get(addr, len);
if (len <= 0 || len > 50) return "";
addr += sizeof(len);
char buffer[64];
for (int i = 0; i < len; i++) {
buffer[i] = EEPROM.read(addr + i);
}
buffer[len] = '\0';
uint16_t storedChecksum;
EEPROM.get(addr + len, storedChecksum);
uint16_t calculatedChecksum = 0;
for (int i = 0; i < len; i++) {
calculatedChecksum += buffer[i];
}
if (storedChecksum != calculatedChecksum) {
Serial.println("[EEPROM] Device ID checksum mismatch!");
return "";
}
String id = String(buffer);
Serial.printf("[EEPROM] Loaded Device ID: %s\n", id.c_str());
return id;
}
String generateRandomDeviceId() {
uint8_t mac[6];
WiFi.macAddress(mac);
// Generate random numbers
uint32_t random1 = ESP.getCycleCount();
uint32_t random2 = ESP.getCycleCount();
// Create a unique ID combining MAC and random numbers
char buffer[32];
snprintf(buffer, sizeof(buffer), "D1L_%02X%02X%02X%02X%02X%02X_%08X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], random1);
String deviceId = String(buffer);
Serial.printf("[DEVICE_ID] Generated random ID: %s\n", deviceId.c_str());
return deviceId;
}
String getOrCreateDeviceId() {
// First, try to load from EEPROM
String savedId = loadDeviceIdFromEEPROM();
if (savedId.length() > 0) {
return savedId;
}
// If not found, generate a new random ID and save it
String newId = generateRandomDeviceId();
saveDeviceIdToEEPROM(newId);
return newId;
}
// ========== HELPER FUNCTIONS ==========
uint8_t packRelays() {
uint8_t packed = 0;
if (relayStates[0]) packed |= 1 << 0;
if (relayStates[1]) packed |= 1 << 1;
if (relayStates[2]) packed |= 1 << 2;
if (relayStates[3]) packed |= 1 << 3;
if (relayStates[4]) packed |= 1 << 4;
return packed;
}
void unpackRelays(uint8_t packed) {
for (int i = 0; i < 5; i++) {
bool state = (packed >> i) & 1;
if (state != relayStates[i]) {
setRelay(i, state);
Serial.printf("[RELAY] %s set to %s\n", relayNames[i], state ? "ON" : "OFF");
}
}
}
void setRelay(int index, bool state) {
// Apply per-relay logic
// Relay 1: Positive logic (HIGH = ON)
digitalWrite(relayPins[0], state ? HIGH : LOW);
// Relay 2: Positive logic (HIGH = ON)
digitalWrite(relayPins[1], state ? HIGH : LOW);
// Relay 3: Positive logic (HIGH = ON)
digitalWrite(relayPins[2], state ? HIGH : LOW);
// Relay 4: Positive logic (HIGH = ON)
digitalWrite(relayPins[3], state ? HIGH : LOW);
// Relay 5: Positive logic (HIGH = ON)
digitalWrite(relayPins[4], state ? HIGH : LOW);
relayStates[index] = state;
}
void initPins() {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
for (int i = 0; i < relayCount; i++) {
pinMode(relayPins[i], OUTPUT);
setRelay(i, false);
}
dht.begin();
Serial.println("[INIT] Pins initialized");
Serial.println("[RELAY] Custom relay names configured:");
for (int i = 0; i < 5; i++) {
Serial.printf(" Relay %d: %s\n", i+1, relayNames[i]);
}
}
void readSensor() {
static unsigned long lastDHTRead = 0;
unsigned long now = millis();
if (now - lastDHTRead >= 2000) {
lastDHTRead = now;
temperature = dht.readTemperature();
humidity = dht.readHumidity();
if (isnan(temperature) || isnan(humidity)) {
temperature = -999;
humidity = -999;
Serial.println("[SENSOR] DHT22 read failed - sending error code -999");
} else if (temperature < -50 || temperature > 80) {
temperature = -999;
Serial.println("[SENSOR] DHT22 temperature out of range - sending error code -999");
} else {
Serial.printf("[SENSOR] DHT22 - Temp: %.1f°C, Hum: %.1f%%\n", temperature, humidity);
}
}
}
bool connectToWiFi() {
if (WiFi.status() == WL_CONNECTED) return true;
Serial.printf("[WiFi] Attempting connection to SSID: %s\n", WIFI_SSID);
Serial.print("[WiFi] Connecting");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 40) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println(" Connected!");
Serial.printf("[WiFi] IP Address: %s\n", WiFi.localIP().toString().c_str());
Serial.printf("[WiFi] Signal Strength (RSSI): %d dBm\n", WiFi.RSSI());
wifiRetryCount = 0;
consecutiveFailures = 0;
return true;
}
Serial.printf(" Failed! (Reason: %d)\n", WiFi.status());
wifiRetryCount++;
return false;
}
void registerDevice() {
if (currentState != STATE_PROVISIONING) return;
if (registrationTokenUsed) return;
if (registrationRetryCount >= MAX_REGISTRATION_RETRIES) {
enterErrorState("Registration failed");
return;
}
if (WiFi.status() != WL_CONNECTED) return;
Serial.println("[REG] Registering device...");
Serial.printf("[REG] Registration token: %s\n", REGISTRATION_TOKEN);
Serial.printf("[REG] Device ID: %s\n", deviceId.c_str());
WiFiClientSecure client;
client.setInsecure();
HTTPClient http;
String url = "https://";
url += serverHost;
url += "/api/device/register/";
Serial.printf("[REG] URL: %s\n", url.c_str());
JsonDocument doc;
doc["token"] = REGISTRATION_TOKEN;
doc["device_id"] = deviceId;
doc["mac_address"] = "";
doc["ip"] = WiFi.localIP().toString();
doc["cpu_freq"] = ESP.getCpuFreqMHz();
doc["free_heap"] = ESP.getFreeHeap();
doc["flash_size"] = ESP.getFlashChipSize();
String jsonString;
serializeJson(doc, jsonString);
Serial.printf("[REG] Payload: %s\n", jsonString.c_str());
if (http.begin(client, url)) {
http.addHeader("Content-Type", "application/json");
int code = http.POST(jsonString);
Serial.printf("[REG] HTTP Response: %d\n", code);
if (code == 200) {
String payload = http.getString();
JsonDocument response;
deserializeJson(response, payload);
if (response["success"]) {
permanentToken = response["token"].as<String>();
deviceRegistered = true;
registrationTokenUsed = true;
currentState = STATE_REGISTERED;
saveTokenToEEPROM(permanentToken);
markRegistrationUsed();
String verifyToken = loadTokenFromEEPROM();
if (verifyToken == permanentToken) {
Serial.println("[EEPROM] Token verified!");
} else {
Serial.println("[EEPROM] Token verification FAILED!");
}
Serial.println("[REG] â Registration successful!");
registrationRetryCount = 0;
getSession();
}
} else if (code == 429) {
registrationRetryCount++;
} else if (code == 401) {
Serial.println("[REG] Token already used or expired");
registrationTokenUsed = true;
}
http.end();
}
}
void getSession() {
if (WiFi.status() != WL_CONNECTED) return;
if (permanentToken.length() == 0) return;
Serial.println("[SESSION] Getting session...");
WiFiClientSecure client;
client.setInsecure();
HTTPClient http;
String url = "https://";
url += serverHost;
url += "/api/device/state/?token=";
url += permanentToken;
url += "&device_id=";
url += deviceId;
url += "&ip=";
url += WiFi.localIP().toString();
url += "&cpu_freq=";
url += String(ESP.getCpuFreqMHz());
url += "&free_heap=";
url += String(ESP.getFreeHeap());
url += "&flash_size=";
url += String(ESP.getFlashChipSize());
if (http.begin(client, url)) {
int code = http.GET();
Serial.printf("[SESSION] HTTP Response: %d\n", code);
if (code == 200) {
String payload = http.getString();
JsonDocument doc;
deserializeJson(doc, payload);
if (doc["session"].is<String>()) {
sessionId = doc["session"].as<String>();
sessionValid = true;
saveSessionToEEPROM(sessionId);
Serial.printf("[SESSION] Got session ID: %s\n", sessionId.c_str());
}
} else if (code == 401) {
Serial.println("[SESSION] Token invalid - will re-register");
deviceRegistered = false;
sessionValid = false;
permanentToken = "";
sessionId = "";
clearAllStorage();
currentState = STATE_PROVISIONING;
}
http.end();
}
}
void updateDeviceState() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("[UPDATE] WiFi not connected - skipping update");
return;
}
if (!deviceRegistered) {
Serial.println("[UPDATE] Device not registered - skipping update");
return;
}
if (currentState != STATE_REGISTERED) {
Serial.printf("[UPDATE] Invalid state: %d - skipping update\n", currentState);
return;
}
readSensor();
WiFiClientSecure client;
client.setInsecure();
HTTPClient http;
String url = "https://";
url += serverHost;
url += "/api/device/state/";
if (sessionValid && sessionId.length() > 0) {
url += "?session=";
url += sessionId;
Serial.printf("[UPDATE] Using session: %s\n", sessionId.c_str());
} else if (permanentToken.length() > 0) {
url += "?token=";
url += permanentToken;
Serial.printf("[UPDATE] Using token\n");
} else {
Serial.println("[UPDATE] No session or token available!");
return;
}
url += "&device_id=";
url += deviceId;
uint8_t packed = packRelays();
url += "&s=";
url += String(packed, HEX);
// ALWAYS send temperature data - even if it's -999 (error code)
int tempInt = (int)(temperature * 10);
url += "&t=";
url += String(tempInt);
Serial.printf("[UPDATE] Sending temperature: %.1f°C (raw: %d)\n", temperature, tempInt);
if (humidity >= 0 && humidity <= 100) {
url += "&h=";
url += String((int)humidity);
}
url += "&ip=";
url += WiFi.localIP().toString();
url += "&cpu_freq=";
url += String(ESP.getCpuFreqMHz());
url += "&free_heap=";
url += String(ESP.getFreeHeap());
url += "&flash_size=";
url += String(ESP.getFlashChipSize());
Serial.println("[UPDATE] ========== FULL URL ==========");
Serial.println(url);
Serial.println("[UPDATE] ===============================");
if (http.begin(client, url)) {
http.setTimeout(8000);
int code = http.GET();
Serial.printf("[UPDATE] HTTP Response Code: %d\n", code);
if (code == 200) {
String payload = http.getString();
JsonDocument doc;
deserializeJson(doc, payload);
Serial.printf("[UPDATE] Response received, size: %d\n", payload.length());
if (doc["session"].is<String>()) {
sessionId = doc["session"].as<String>();
sessionValid = true;
saveSessionToEEPROM(sessionId);
Serial.printf("[UPDATE] New session received: %s\n", sessionId.c_str());
}
// Process relay states
bool relayChanged = false;
for (int i = 0; i < 5; i++) {
String key = "relay" + String(i+1);
if (doc[key].is<bool>()) {
bool newState = doc[key];
if (newState != relayStates[i]) {
setRelay(i, newState);
relayChanged = true;
Serial.printf("[RELAY] %s set to %s (from server)\n", relayNames[i], newState ? "ON" : "OFF");
}
}
}
if (!relayChanged && doc["s"].is<String>()) {
uint8_t serverState = (uint8_t)strtol(doc["s"], NULL, 16);
uint8_t currentRelayState = packRelays();
if (serverState != currentRelayState) {
unpackRelays(serverState);
Serial.printf("[RELAY] Updated from packed state: %02X\n", serverState);
}
}
consecutiveFailures = 0;
Serial.print(".");
} else if (code == 401) {
Serial.println("[UPDATE] Session invalid - attempting to recover with token");
// Try to get a new session using the permanent token
if (permanentToken.length() > 0) {
getSession();
if (sessionValid) {
Serial.println("[UPDATE] Successfully renewed session!");
consecutiveFailures = 0;
} else {
Serial.println("[UPDATE] Failed to renew session. Will re-register.");
deviceRegistered = false;
sessionValid = false;
currentState = STATE_PROVISIONING;
clearAllStorage();
}
} else {
Serial.println("[UPDATE] No permanent token available. Forcing re-registration.");
deviceRegistered = false;
sessionValid = false;
currentState = STATE_PROVISIONING;
clearAllStorage();
}
consecutiveFailures++;
} else if (code == 0) {
Serial.println("[UPDATE] Connection failed - no response");
consecutiveFailures++;
} else {
Serial.printf("[UPDATE] Unexpected response: %d\n", code);
consecutiveFailures++;
}
http.end();
} else {
Serial.println("[UPDATE] HTTP connection initialization failed!");
consecutiveFailures++;
}
// If too many failures, force re-registration
if (consecutiveFailures > 10) {
Serial.println("[UPDATE] Too many failures - forcing re-registration");
deviceRegistered = false;
sessionValid = false;
permanentToken = "";
sessionId = "";
clearAllStorage();
currentState = STATE_PROVISIONING;
consecutiveFailures = 0;
}
}
void checkWiFiAndReconnect() {
if (WiFi.status() != WL_CONNECTED) {
Serial.printf("[WiFi] Connection lost! Status: %d\n", WiFi.status());
if (connectToWiFi()) {
Serial.println("[WiFi] Reconnected successfully!");
// After reconnection, try to recover session
if (deviceRegistered && !sessionValid) {
getSession();
}
} else {
Serial.println("[WiFi] Reconnection failed!");
}
}
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("");
Serial.println("########################################");
Serial.println("# OceanicRemote v5.0 - ESP8266 D1 Large #");
Serial.println("# Persistent Device ID & Recovery Logic #");
Serial.println("########################################");
initEEPROM();
randomSeed(analogRead(A0));
// Get or create persistent Device ID (stored in EEPROM)
deviceId = getOrCreateDeviceId();
initPins();
Serial.printf("[SYSTEM] Device ID: %s\n", deviceId.c_str());
Serial.printf("[SYSTEM] Server: %s\n", serverHost);
Serial.printf("[SYSTEM] Polling interval: %d ms\n", BASE_POLL_INTERVAL);
Serial.printf("[SYSTEM] WiFi SSID: %s\n", WIFI_SSID);
Serial.printf("[SYSTEM] Free heap: %d bytes\n", ESP.getFreeHeap());
String savedToken = loadTokenFromEEPROM();
if (savedToken.length() > 0) {
permanentToken = savedToken;
deviceRegistered = true;
currentState = STATE_REGISTERED;
Serial.printf("[BOOT] Found saved token (length: %d)\n", permanentToken.length());
String savedSession = loadSessionFromEEPROM();
if (savedSession.length() > 0) {
sessionId = savedSession;
sessionValid = true;
Serial.printf("[BOOT] Found saved session: %s\n", sessionId.c_str());
} else {
Serial.println("[BOOT] No saved session found");
}
} else {
if (isRegistrationUsed()) {
Serial.println("[BOOT] Device was registered but token missing! Will re-register.");
} else {
Serial.println("[BOOT] No token found - will register as NEW device");
currentState = STATE_PROVISIONING;
registrationTokenUsed = false;
registrationRetryCount = 0;
}
}
Serial.println("[PHASE 1] Connecting to WiFi...");
if (!connectToWiFi()) {
Serial.println("[ERROR] Cannot connect to WiFi - will retry in loop");
}
Serial.println("[PHASE 2] Establishing server connection...");
if (!deviceRegistered && currentState == STATE_PROVISIONING) {
registerDevice();
}
if (deviceRegistered && currentState == STATE_REGISTERED) {
if (!sessionValid || sessionId.length() == 0) {
getSession();
}
updateDeviceState();
}
nextCheckTime = millis() + random(0, 5000);
Serial.println("[PHASE 3] Entering main loop...");
Serial.println("[INFO] Device will now poll server every 3 seconds");
}
void loop() {
if (currentState == STATE_ERROR) {
delay(5000);
return;
}
unsigned long currentMillis = millis();
// Check WiFi connection periodically
static unsigned long lastWiFiCheck = 0;
if (currentMillis - lastWiFiCheck > 10000) {
lastWiFiCheck = currentMillis;
checkWiFiAndReconnect();
}
if (WiFi.status() == WL_CONNECTED) {
wifiRetryCount = 0;
if (currentMillis >= nextCheckTime) {
int jitter = random(-JITTER_RANGE_MS, JITTER_RANGE_MS);
nextCheckTime = currentMillis + BASE_POLL_INTERVAL + jitter;
if (deviceRegistered && currentState == STATE_REGISTERED) {
updateDeviceState();
} else if (!deviceRegistered && currentState == STATE_PROVISIONING) {
registerDevice();
}
}
} else {
if (currentMillis - lastWiFiReconnect >= wifiReconnectInterval) {
lastWiFiReconnect = currentMillis;
if (wifiRetryCount < MAX_WIFI_RETRIES) {
Serial.printf("[WiFi] Attempting reconnect (%d/%d)...\n", wifiRetryCount + 1, MAX_WIFI_RETRIES);
connectToWiFi();
if (WiFi.status() == WL_CONNECTED && deviceRegistered && !sessionValid) {
getSession();
}
} else {
Serial.println("[WiFi] Max retries reached. Restarting...");
delay(1000);
ESP.restart();
}
}
}
delay(10);
}