Files
Arduino/old/nodmcu2/nodmcu2.ino
2025-12-19 11:50:13 +03:30

928 lines
31 KiB
C++

// Clean, full sketch for ESP8266 (NodeMCU)
// - RCSwitch receiver on D5
// - RTC DS3231 on I2C (SDA=D2, SCL=D1)
// - Optional SD on CS = D8 (GPIO15)
// - Always prints received RF data to Serial
// - Saves to SD every N minutes if SD present
// - Cleans files older than M days every 12 hours
// - getPageID(...) returns numeric page id; switch-case used
// - STA + periodic sending to server
// - API to set STA & send interval
#include <ESP8266WiFi.h>
#include <Wire.h>
#include "RTClib.h"
#include <RCSwitch.h>
#include <SD.h>
#include <SPI.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>
#include <Adafruit_NeoPixel.h>
#include <TimeLib.h>
// ---------------- CONFIG ----------------
String apSSID = "ESP8266_AP";
String apPassword = "12345678";
WiFiServer server(80);
// ---------------- HW ----------------
RTC_DS3231 rtc;
bool rtcReady = false;
RCSwitch rx;
const uint8_t RX_PIN = D1;
//-------------------------------------led----------------------------------
#define LED_PIN 2
#define NUM_LEDS 8
uint32_t lastLedState[NUM_LEDS] = {0};
// uint8_t currentLedIndex = 0; // LED فعلی برای ارسال
// bool ledUpdateNeeded = false;
uint32_t targetColors[NUM_LEDS]; // رنگ‌های هدف
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
uint32_t On_color = strip.Color(0,255,127);
uint32_t off_color = strip.Color(0, 0, 0);
//uint32_t off_color = strip.Color(210, 105, 30);
uint32_t false_color = strip.Color(255, 255, 0);
uint32_t Ok_color = strip.Color(0,255,127);
uint32_t Red_color = strip.Color(255,0,0);
//-----------------------------------------------------------------------
#define SD_CS_PIN D8
bool sdReady = false;
const char* CONFIG_FILE = "config.txt";
const int buttonPin = 16; // D0
bool wifiEnabled = true;
bool StartConnectToModem = false;
bool buttonPressed = false;
int lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
// ---------------- Protocol & storage ----------------
const String PRIVATE_KEY = "as23f";
const String device_1 = "dr142";
const String device_2 = "dv154";
uint8_t device1_recived=0;
uint8_t device2_recived=0;
//bool device2_recived=false;
const String ALLOWED_DEVICES[] = {device_1,device_2};
const int NUM_ALLOWED = 2;
#define MAX_DEVICES 16
struct DeviceData {
String deviceId;
int soil;
int gas;
float temp;
float hum;
String timestamp;
};
DeviceData lastRemoteData[MAX_DEVICES];
int deviceCount = 0;
// RX frame parsing
String receivedKey = "";
String receivedHex = "";
bool receivingPublicKey = true;
int receivedChars = 0;
unsigned long lastReceiveTime = 0;
const unsigned long FRAME_TIMEOUT_MS = 500;
const int EXPECTED_PLAIN_LEN = 25;
// ---------------- Settings (default) ----------------
unsigned long saveIntervalMinutes = 5;
int retentionDays = 90;
unsigned long lastSaveMillis = 0;
unsigned long lastCleanupMillis = 0;
unsigned long lastCheckDevice = 0;
// ---------------- WiFi client (STA) ----------------
String staSSID = "";
String staPassword = "";
unsigned long lastSendMillis = 0;
unsigned long sendIntervalMinutes = 1; // پیش فرض
// ---------------- Helpers ----------------
bool isHexChar(char c) {
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
}
bool isDeviceAllowed(const String &id) {
for (int i=0;i<NUM_ALLOWED;i++) if (id == ALLOWED_DEVICES[i]) return true;
return false;
}
void resetFrame() {
receivedKey = "";
receivedHex = "";
receivingPublicKey = true;
receivedChars = 0;
}
String dateTimeToISO(const DateTime &dt) {
char buf[25];
sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d",
dt.year(), dt.month(), dt.day(),
dt.hour(), dt.minute(), dt.second());
return String(buf);
}
String safeFileNameForNow() {
if (rtcReady) {
DateTime now = rtc.now();
char fn[20];
sprintf(fn, "%04d%02d%02d.txt", now.year(), now.month(), now.day());
return String(fn);
} else {
unsigned long s = millis() / 1000UL;
return String("t") + String(s) + ".txt";
}
}
String decryptData(const String &data, const String &key) {
String out;
out.reserve(data.length());
for (int i=0;i<data.length();++i) out += (char)(data[i] ^ key[i % key.length()]);
return out;
}
// ---------------- SD / config helpers ----------------
// -------------------- loadConfigFromSD --------------------
// -------------------- loadConfigFromSD --------------------
void loadConfigFromSD() {
if (!sdReady) {
Serial.println("[CONFIG] SD not ready, skipping load");
return;
}
Serial.println("[CONFIG] Loading configuration from SD...");
File f = SD.open(CONFIG_FILE, "r");
if (!f) {
Serial.println("[CONFIG] Config file not found, using defaults");
return;
}
while (f.available()) {
String line = f.readStringUntil('\n');
line.trim();
if (line.length() == 0) continue;
Serial.print("[CONFIG] Line: ");
Serial.println(line);
if (line.startsWith("save_interval_minutes=")) {
saveIntervalMinutes = line.substring(strlen("save_interval_minutes=")).toInt();
Serial.print("[CONFIG] Loaded save_interval_minutes = ");
Serial.println(saveIntervalMinutes);
}
else if (line.startsWith("retention_days=")) {
retentionDays = line.substring(strlen("retention_days=")).toInt();
Serial.print("[CONFIG] Loaded retention_days = ");
Serial.println(retentionDays);
}
else if (line.startsWith("wifi_ssid=")) {
apSSID = line.substring(strlen("wifi_ssid="));
Serial.print("[CONFIG] Loaded wifi_ssid = ");
Serial.println(apSSID);
}
else if (line.startsWith("wifi_password=")) {
apPassword = line.substring(strlen("wifi_password="));
Serial.print("[CONFIG] Loaded wifi_password = ");
Serial.println(apPassword);
}
else if (line.startsWith("sta_ssid=")) {
staSSID = line.substring(strlen("sta_ssid="));
Serial.print("[CONFIG] Loaded sta_ssid = ");
Serial.println(staSSID);
}
else if (line.startsWith("sta_password=")) {
staPassword = line.substring(strlen("sta_password="));
Serial.print("[CONFIG] Loaded sta_password = ");
Serial.println(staPassword);
}
else if (line.startsWith("sendIntervalMinutes=")) {
sendIntervalMinutes = line.substring(strlen("sendIntervalMinutes=")).toInt();
Serial.print("[CONFIG] Loaded sendIntervalMinutes = ");
Serial.println(sendIntervalMinutes);
}
else {
Serial.print("[CONFIG] Unknown line ignored: ");
Serial.println(line);
}
}
f.close();
Serial.println("[CONFIG] Finished loading configuration");
}
// -------------------- saveConfigToSD --------------------
void saveConfigToSD() {
if (!sdReady) {
Serial.println("[CONFIG] SD not ready, cannot save config");
return;
}
Serial.println("[CONFIG] Saving configuration to SD...");
// اول فایل قبلی رو پاک کن
if (SD.exists(CONFIG_FILE)) {
SD.remove(CONFIG_FILE);
Serial.println("[CONFIG] Old config file removed");
}
File f = SD.open(CONFIG_FILE, FILE_WRITE);
if (!f) {
Serial.println("[CONFIG] Failed to open config file for writing");
return;
}
// همه مقادیر رو یکجا می‌نویسیم
f.print("save_interval_minutes=");
f.println(saveIntervalMinutes);
f.print("retention_days=");
f.println(retentionDays);
f.print("sendIntervalMinutes=");
f.println(sendIntervalMinutes);
f.print("wifi_ssid=");
f.println(apSSID);
f.print("wifi_password=");
f.println(apPassword);
f.print("sta_ssid=");
f.println(staSSID);
f.print("sta_password=");
f.println(staPassword);
f.close();
Serial.println("[CONFIG] Configuration saved successfully (file rewritten)");
}
void appendDataToSD(const String &line) {
if (!sdReady) return;
String fname = safeFileNameForNow();
File f = SD.open(fname, FILE_WRITE);
if (!f) f = SD.open(fname, "a");
if (!f) return;
f.println(line);
f.close();
}
uint64_t computeSDUsedBytes() {
if (!sdReady) return 0;
uint64_t used = 0;
File root = SD.open("/");
if (!root) return 0;
File entry = root.openNextFile();
while (entry) { if (!entry.isDirectory()) used += (uint64_t)entry.size(); entry.close(); entry = root.openNextFile(); }
root.close();
return used;
}
void cleanupOldFilesOnSD() {
if (!sdReady || !rtcReady) return;
File root = SD.open("/");
if (!root) return;
File entry = root.openNextFile();
DateTime now = rtc.now();
while (entry) {
String name = entry.name();
if (name.length() && name[0] == '/') name = name.substring(1);
if (name != String(CONFIG_FILE) && !entry.isDirectory()) {
if (name.length() >= 8 && isDigit(name[0])) {
int y = name.substring(0,4).toInt();
int m = name.substring(4,6).toInt();
int d = name.substring(6,8).toInt();
if (y > 2000 && m>=1 && m<=12 && d>=1 && d<=31) {
DateTime fileDate(y,m,d,0,0,0);
TimeSpan diff = now - fileDate;
if (diff.days() > retentionDays) SD.remove(name);
}
}
}
entry.close();
entry = root.openNextFile();
}
root.close();
}
// ---------------- Remote receiver ----------------
void processRemote() {
if (rx.available()) {
unsigned long value = rx.getReceivedValue();
unsigned int bitlen = rx.getReceivedBitlength();
if (bitlen == 8) {
char c = (char)value;
if (receivingPublicKey) {
if (receivedKey.length() < 5) receivedKey += c;
if (receivedKey.length() == 5) receivingPublicKey = false;
} else {
if (isHexChar(c)) receivedHex += c;
}
receivedChars++;
lastReceiveTime = millis();
}
rx.resetAvailable();
}
if (receivedChars >= 5 && (millis() - lastReceiveTime) > FRAME_TIMEOUT_MS) {
if (receivedKey != PRIVATE_KEY || (receivedHex.length() % 2) != 0) { resetFrame(); return; }
String decoded; decoded.reserve(receivedHex.length()/2);
for (int i=0; i+1<receivedHex.length(); i+=2) {
char hex2[3] = { receivedHex[i], receivedHex[i+1], '\0' };
decoded += (char)strtol(hex2,NULL,16);
}
if (decoded.length() != EXPECTED_PLAIN_LEN) { resetFrame(); return; }
String plain = decryptData(decoded, PRIVATE_KEY);
if (plain[5]!='S'||plain[10]!='G'||plain[15]!='T'||plain[20]!='H') { resetFrame(); return; }
String deviceId = plain.substring(0,5);
if (!isDeviceAllowed(deviceId)) { resetFrame(); return; }
int soil = plain.substring(6,10).toInt();
int gas = plain.substring(11,15).toInt();
float temp = plain.substring(16,20).toInt()/10.0;
float hum = plain.substring(21,25).toInt()/10.0;
String ts = rtcReady?dateTimeToISO(rtc.now()):String(millis());
Serial.printf("[RX] %s soil=%d gas=%d temp=%.1f hum=%.1f time=%s\n", deviceId.c_str(),soil,gas,temp,hum,ts.c_str());
bool updated=false;
for (int i=0;i<deviceCount;i++) {
if (lastRemoteData[i].deviceId==deviceId) {
lastRemoteData[i]={deviceId,soil,gas,temp,hum,ts};
updated=true; break;
}
}
if (!updated && deviceCount<MAX_DEVICES) lastRemoteData[deviceCount++]={deviceId,soil,gas,temp,hum,ts};
checkDeviceData();
if ((millis()-lastSaveMillis) >= saveIntervalMinutes*60000UL) {
if (sdReady) {
String j="{\"device\":\""+deviceId+"\",\"soil\":"+String(soil)+",\"gas\":"+String(gas)+
",\"temp\":"+String(temp)+",\"hum\":"+String(hum)+",\"time\":\""+ts+"\"}";
appendDataToSD(j);
Serial.println("[INFO] appended to SD");
}
lastSaveMillis=millis();
}
resetFrame();
}
}
// ---------------- Page mapping ----------------
int getPageID(const String &page) {
if (page == "info") return 1;
if (page == "sensor") return 2;
if (page == "time") return 3;
if (page == "settime") return 4;
if (page == "lastremote") return 5;
if (page == "readfile") return 6;
if (page == "files") return 7;
if (page == "format") return 8;
if (page == "set_save_interval") return 9;
if (page == "set_retention_days") return 10;
if (page == "sd_status") return 11;
return 0;
}
String urlDecode(const String &input) {
String s = input;
s.replace("+", " ");
for (int i = 0; i + 2 < s.length(); ++i) {
if (s[i] == '%') {
String hx = s.substring(i+1, i+3);
char c = (char) strtol(hx.c_str(), NULL, 16);
s = s.substring(0,i) + String(c) + s.substring(i+3);
}
}
return s;
}
String getParamFromPath(const String &path, const String &key) {
int q = path.indexOf('?');
if (q == -1) return "";
String qstr = path.substring(q+1);
int start = 0;
while (start < qstr.length()) {
int amp = qstr.indexOf('&', start);
if (amp == -1) amp = qstr.length();
int eq = qstr.indexOf('=', start);
if (eq != -1 && eq < amp) {
String k = qstr.substring(start, eq);
String v = qstr.substring(eq+1, amp);
if (k == key) return urlDecode(v);
}
start = amp + 1;
}
return "";
}
// ---------------- HTTP handler ----------------
void handleClient() {
if (!wifiEnabled)
return;
WiFiClient client = server.available();
if (!client)
return;
String req = "";
unsigned long start = millis();
while (client.connected() && millis() - start < 1500) {
while (client.available()) {
char c = client.read();
req += c;
if (req.endsWith("\r\n\r\n")) break;
}
if (req.endsWith("\r\n\r\n")) break;
}
if (req.length() == 0) return;
int lineEnd = req.indexOf("\r\n");
String firstLine = (lineEnd == -1) ? req : req.substring(0, lineEnd);
Serial.print("[HTTP] "); Serial.println(firstLine);
String path = "";
int sp1 = firstLine.indexOf(' ');
int sp2 = firstLine.indexOf(" HTTP/");
if (sp1 != -1 && sp2 != -1) path = firstLine.substring(sp1+1, sp2);
String page = getParamFromPath(path,"page");
String response="";
// --- API: set STA ---
if(page=="set_sta") {
String ssid = getParamFromPath(path,"ssid");
String pass = getParamFromPath(path,"pass");
if(ssid.length()>0) {
staSSID=ssid; staPassword=pass;
StartConnectToModem=WiFi.begin(staSSID.c_str(),staPassword.c_str());
if(sdReady) saveConfigToSD();
response="{\"status\":\"ok\",\"message\":\"STA updated, connecting...\"}";
} else response="{\"status\":\"error\",\"message\":\"SSID missing\"}";
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: application/json"));
client.print(F("Content-Length: ")); client.println(response.length());
client.println(F("Connection: close")); client.println();
client.println(response);
client.stop();
return;
}
// --- API: set send interval ---
if(page=="set_send_interval") {
String v = getParamFromPath(path,"minutes");
int m=v.toInt();
if(m>0) {
sendIntervalMinutes=m;
if(sdReady) saveConfigToSD();
response="{\"status\":\"ok\",\"send_interval_minutes\":"+String(m)+"}";
} else response="{\"status\":\"error\",\"message\":\"invalid minutes\"}";
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: application/json"));
client.print(F("Content-Length: ")); client.println(response.length());
client.println(F("Connection: close")); client.println();
client.println(response);
client.stop();
return;
}
// --- old page switch ---
int pid = getPageID(page);
switch(pid) {
case 1:
response="{\"status\":\"ok\",\"data\":\"ESP ready,StartConnectToModem:"+String(StartConnectToModem)+",wifiEnabled:"+String(wifiEnabled)+"\"}"; break;
case 2: response="{\"status\":\"ok\",\"sensor\":"+String(analogRead(A0))+"}"; break;
case 3: response=rtcReady?"{\"status\":\"ok\",\"data\":\""+dateTimeToISO(rtc.now())+"\"}":"{\"status\":\"error\",\"message\":\"RTC not ready\"}"; break;
case 4: {
String iso=getParamFromPath(path,"iso");
if(!rtcReady){ response="{\"status\":\"error\",\"message\":\"RTC not ready\"}"; break; }
if(iso.length()>=19){
int y=iso.substring(0,4).toInt(), m=iso.substring(5,7).toInt(), d=iso.substring(8,10).toInt();
int hh=iso.substring(11,13).toInt(), mm=iso.substring(14,16).toInt(), ss=iso.substring(17,19).toInt();
rtc.adjust(DateTime(y,m,d,hh,mm,ss));
response="{\"status\":\"ok\",\"data\":\""+dateTimeToISO(rtc.now())+"\"}";
} else response="{\"status\":\"error\",\"message\":\"invalid iso\"}";
break;
}
case 5: {
String arr="[";
for(int i=0;i<deviceCount;i++){ if(i) arr+=","; DeviceData &d=lastRemoteData[i]; arr+="{\"device\":\""+d.deviceId+"\",\"soil\":"+String(d.soil)+",\"gas\":"+String(d.gas)+",\"temp\":"+String(d.temp)+",\"hum\":"+String(d.hum)+",\"time\":\""+d.timestamp+"\"}"; }
arr+="]";
response="{\"status\":\"ok\",\"data\":"+arr+"}"; break;
}
case 6: {
if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; }
String name = getParamFromPath(path, "name");
if (name.length() == 0) { response = "{\"status\":\"error\",\"message\":\"missing name\"}"; break; }
File f = SD.open(name, "r");
if (!f) { response = "{\"status\":\"error\",\"message\":\"file not found\"}"; break; }
String content = "";
while (f.available()) {
char c = f.read();
if (c == '"') content += "\""; else content += c;
if (c == '}')
{
//content += c;
content += ",";
}
}
if (content.length() > 0) {
content.remove(content.length() - 3);
}
f.close();
response = "{\"status\":\"ok\",\"content\":[" + content + "]}";
break;
}
case 7: { // files
if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; }
File root = SD.open("/");
String arr = "[";
bool first = true;
File entry = root.openNextFile();
while (entry) {
String name = entry.name();
if (name.length() && name[0] == '/') name = name.substring(1);
if (!first) arr += ",";
arr += "\"" + name + "\"";
first = false;
entry.close();
entry = root.openNextFile();
}
root.close();
arr += "]";
response = "{\"status\":\"ok\",\"files\":" + arr + "}";
break;
}
case 8: { // format (remove all files except config)
if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; }
File root = SD.open("/");
File entry = root.openNextFile();
while (entry) {
String name = entry.name();
if (name.length() && name[0] == '/') name = name.substring(1);
if (name != String(CONFIG_FILE) && !entry.isDirectory()) SD.remove(name);
entry.close();
entry = root.openNextFile();
}
root.close();
response = "{\"status\":\"ok\",\"message\":\"formatted (config preserved)\"}";
break;
}
case 9: { // set_save_interval?min=NUM
String v = getParamFromPath(path, "min");
int m = v.toInt();
if (m <= 0) response = "{\"status\":\"error\",\"message\":\"invalid min\"}";
else {
saveIntervalMinutes = (unsigned long)m;
if (sdReady) saveConfigToSD();
response = "{\"status\":\"ok\",\"save_interval_minutes\":" + String(m) + "}";
}
break;
}
case 10: { // set_retention_days?days=NUM
String v = getParamFromPath(path, "days");
int d = v.toInt();
if (d <= 0) response = "{\"status\":\"error\",\"message\":\"invalid days\"}";
else {
retentionDays = d;
if (sdReady) saveConfigToSD();
response = "{\"status\":\"ok\",\"retention_days\":" + String(d) + "}";
}
break;
}
case 11: { // sd_status
if (!sdReady) response = "{\"status\":\"error\",\"message\":\"SD not ready\"}";
else
{
uint64_t used = computeSDUsedBytes();
//total/free not reliably available via SD.h on ESP8266
response = "{\"status\":\"ok\",\"used_bytes\":" + String((unsigned long)used) + "}";
}
break;
}
default: response = page.length()==0?"{\"status\":\"error\",\"message\":\"missing page\"}":"{\"status\":\"error\",\"message\":\"unknown page\"}"; break;
}
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: application/json"));
client.print(F("Content-Length: ")); client.println(response.length());
client.println(F("Connection: close"));
client.println();
client.println(response);
delay(5);
client.stop();
Serial.println("[HTTP] client disconnected");
}
time_t parseTimestamp(String ts) {
int year = ts.substring(0, 4).toInt();
int month = ts.substring(5, 7).toInt();
int day = ts.substring(8, 10).toInt();
int hour = ts.substring(11, 13).toInt();
int min = ts.substring(14, 16).toInt();
int sec = ts.substring(17, 19).toInt();
tmElements_t tm;
tm.Year = year - 1970;
tm.Month = month;
tm.Day = day;
tm.Hour = hour;
tm.Minute = min;
tm.Second = sec;
return makeTime(tm);
}
void checkDeviceData() {
time_t now = rtc.now().unixtime();
int device1Index=-1;
int device2Index=-1;
for (int i = 0; i < MAX_DEVICES; i++)
{
if(i<deviceCount)
{
if(lastRemoteData[i].deviceId==device_1)
device1Index=i;
if(lastRemoteData[i].deviceId==device_2)
device2Index=i;
}
}
if(device1Index==-1)
{
device1_recived=0;//دریافتی از دستگاه 1 نداشتیم
Serial.println(device_1 + " not exist");
}
else
{
int i=device1Index;
if (lastRemoteData[i].timestamp.length() > 0) {
time_t t = parseTimestamp(lastRemoteData[i].timestamp);
long diff = now - t; // اختلاف زمان به ثانیه
if (diff < 60) {
device1_recived=2;
Serial.println(lastRemoteData[i].deviceId + " updated less than 60 seconds ago");
} else {
device1_recived=1;
Serial.println(lastRemoteData[i].deviceId + "--- updated more than 60 seconds ago");
}
}
else{
device1_recived=1;
Serial.println(lastRemoteData[i].deviceId + "--- noDataRecive updated more than 60 seconds ago");
}
}
if(device2Index==-1)
{
device2_recived=0;//دریافتی از دستگاه 2 نداشتیم
Serial.println(device_2 + " not exist");
}
else
{
int i=device2Index;
if (lastRemoteData[i].timestamp.length() > 0) {
time_t t = parseTimestamp(lastRemoteData[i].timestamp);
long diff = now - t; // اختلاف زمان به ثانیه
if (diff < 60) {
device2_recived=2;
Serial.println(lastRemoteData[i].deviceId + " updated less than 60 seconds ago");
} else {
device2_recived=1;
Serial.println(lastRemoteData[i].deviceId + "--- updated more than 60 seconds ago");
}
}
else{
device1_recived=1;
Serial.println(lastRemoteData[i].deviceId + "--- noDataRecive updated more than 60 seconds ago");
}
}
}
void setLed(uint8_t idx, bool condition, uint32_t colorTrue, uint32_t colorFalse) {
if (condition) {
strip.setPixelColor(idx, colorTrue);
} else {
strip.setPixelColor(idx, colorFalse);
}
}
// ---------------- Setup & Loop ----------------
void startEsp()
{
}
void setup() {
Serial.begin(115200); delay(100);
//--------------------------------Start Led---------------------------------------------------------
strip.begin();
strip.show();
//چراغ اول روشن شود. یعنی دستگاه روشن است
//setLed(0, true, On_color, On_color);
//strip.show();
//-----------------------------------------------------------------------------------------
//-------------------------------------Button Config----------------------------------------------------
pinMode(buttonPin, INPUT_PULLUP);
//-----------------------------------------------------------------------------------------
//------------------------------------Start Clock-----------------------------------------------------
Wire.begin(D2,D3);
rtcReady=rtc.begin();
if(rtcReady && rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
//-----------------------------------------------------------------------------------------
//--------------------------------------Start Sdk---------------------------------------------------
rx.enableReceive(digitalPinToInterrupt(RX_PIN));
rx.setProtocol(1);
rx.setPulseLength(300);
Serial.println("[INFO] RCSwitch enabled on D1");
//-----------------------------------------------------------------------------------------
//-----------------------------------------Start SD Card------------------------------------------------
sdReady=SD.begin(SD_CS_PIN);
if(sdReady){ Serial.println("[INFO] SD ready"); loadConfigFromSD(); }
else Serial.println("[WARN] SD init failed");
//-----------------------------------------------------------------------------------------
//-----------------------------------Start ESP------------------------------------------------------
if(wifiEnabled){ WiFi.softAP(apSSID,apPassword,6); server.begin(); Serial.print("AP IP: "); Serial.println(WiFi.softAPIP()); }
if(staSSID.length()>0){ StartConnectToModem=WiFi.begin(staSSID.c_str(),staPassword.c_str()); }
//-----------------------------------------------------------------------------------------
lastSaveMillis=millis(); lastCleanupMillis=millis(); lastSendMillis=millis();lastCheckDevice=millis();
}
void CheckWifiBtn()
{
// ---------------- Button handling (toggle AP) ----------------
int reading = digitalRead(buttonPin);
if (reading != lastButtonState) lastDebounceTime = millis();
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading == LOW && !buttonPressed) {
buttonPressed = true;
wifiEnabled = !wifiEnabled;
if (wifiEnabled) {
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(apSSID, apPassword,6);
server.begin();
Serial.println("✅ AP enabled");
Serial.print("IP: "); Serial.println(WiFi.softAPIP());
} else {
WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_STA);
Serial.println("❌ AP disabled");
}
}
if (reading == HIGH) buttonPressed = false;
}
lastButtonState = reading;
}
void SendDataToServer()
{
if (deviceCount > 0 && sendIntervalMinutes > 0) {
if (lastSendMillis == 0) lastSendMillis = millis();
if ((millis() - lastSendMillis) >= sendIntervalMinutes * 60000UL) {
Serial.println("[DEBUG] Entering send block");
Serial.print("[DEBUG] WiFi.status: "); Serial.println(WiFi.status());
if (WiFi.status() == WL_CONNECTED) {
// Prepare JSON payload
String payload = "[";
for (int i = 0; i < deviceCount; i++) {
if (i) payload += ",";
DeviceData &d = lastRemoteData[i];
payload += "{\"device\":\"" + d.deviceId + "\",\"soil\":" + String(d.soil) +
",\"gas\":" + String(d.gas) + ",\"temp\":" + String(d.temp) +
",\"hum\":" + String(d.hum) + ",\"time\":\"" + d.timestamp + "\"}";
}
payload += "]";
// Use WiFiClientSecure for HTTPS
WiFiClientSecure client;
client.setInsecure(); // SSL certificate not verified
HTTPClient http;
// Use URL with www if nabaksoft.ir ریدایرکت می‌کند
String url = "https://www.nabaksoft.ir/greenhome/mygreenhome.php?aid="+PRIVATE_KEY+"&data=" + payload;
http.begin(client, url);
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); // Follow redirects automatically
Serial.println("[HTTP] Sending data to server...");
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] Response code: %d\n", httpCode);
String resp = http.getString();
Serial.printf("[HTTP] Server response: %s\n", resp.c_str());
} else {
Serial.printf("[HTTP] Send failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
} else {
Serial.println("[HTTP] WiFi not connected, skipping send");
}
lastSendMillis = millis();
}
}
}
void SetLedStates()
{
//دریافت کننده فعال است؟
/*setLed(1, rtcReady, Ok_color, off_color);
//کارتخوان فعال است؟
setLed(2, sdReady, Ok_color, off_color);
//wifi فعال است؟
setLed(3, wifiEnabled, Ok_color, off_color);
//مودم فعال است یا خیر و اگر فعال است متصل است؟
if(staSSID.length()>0)
{
//اگر رمز دارد پس باید متصل بشه.اگر شده که سبز. اگر نشده قرمز
setLed(4, WiFi.status()==WL_CONNECTED, Ok_color, false_color);
}
else
setLed(4, true, off_color, off_color);
setLed(5, true, off_color, off_color);
setLed(6, true, off_color, off_color);
setLed(7, true, off_color, off_color);
//شدت روشنایی
strip.setBrightness(20);
strip.show();*/
int32_t colors[NUM_LEDS];
colors[0] = On_color; // دستگاه روشن
colors[1] = rtcReady ? Ok_color : off_color; // وضعیت RTC
colors[2] = sdReady ? Ok_color : off_color; // وضعیت SD
colors[3] = wifiEnabled ? Ok_color : off_color; // وضعیت AP
colors[4] = (staSSID.length() > 0 && WiFi.status() == WL_CONNECTED) ? Ok_color : false_color; // STA
colors[5] = (device1_recived==2)?Ok_color:(device1_recived==1?false_color:Red_color);
colors[6] = (device2_recived == 2)?Ok_color:(device2_recived==1?false_color:Red_color);
colors[7] = off_color;
// بررسی اینکه آیا تغییری نسبت به وضعیت قبلی وجود دارد
bool changed = false;
for (int i = 0; i < NUM_LEDS; i++) {
if (colors[i] != lastLedState[i]) {
lastLedState[i] = colors[i];
strip.setPixelColor(i, colors[i]);
changed = true;
}
}
// فقط وقتی تغییر بوده، strip.show() اجرا شود
if (changed) {
strip.setBrightness(20);
strip.show(); // این تابع اکنون به حداقل فراخوانی کاهش یافته
}
}
void loop() {
CheckWifiBtn();
// ---------------- HTTP server handling ----------------
handleClient();
// ---------------- Process RF data ----------------
processRemote();
//---------------- Periodic cleanup every 12 hours ----------------
if ((millis() - lastCleanupMillis) >= (12UL * 60UL * 60UL * 1000UL)) {
cleanupOldFilesOnSD();
lastCleanupMillis = millis();
}
//هر یک دقیقه چک کنه وضعیت دو فرستنده چطور هست
if ((millis() - lastCheckDevice) >= (60UL * 1000UL)) {
checkDeviceData();
lastCheckDevice = millis();
}
// ---------------- Periodic send to server ----------------
SendDataToServer();
SetLedStates();
}
// if (wifiEnabled) {
// WiFiClient client = server.available();
// if (client) handleClient(client);
// }