old codes
This commit is contained in:
716
old/nodmcu1/nodmcu1.ino
Normal file
716
old/nodmcu1/nodmcu1.ino
Normal file
@@ -0,0 +1,716 @@
|
||||
// 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
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <Wire.h>
|
||||
#include "RTClib.h"
|
||||
#include <RCSwitch.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
// ---------------- CONFIG ----------------
|
||||
const char* ssid = "ESP8266_AP";
|
||||
const char* password = "12345678";
|
||||
WiFiServer server(80);
|
||||
|
||||
// ---------------- HW ----------------
|
||||
#define LED_PIN 1 // TX = GPIO1
|
||||
#define NUM_LEDS 8
|
||||
|
||||
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
|
||||
|
||||
RTC_DS3231 rtc;
|
||||
bool rtcReady = false;
|
||||
|
||||
RCSwitch rx; // RCSwitch for RXB45
|
||||
const uint8_t RX_PIN = D1;
|
||||
|
||||
#define SD_CS_PIN D8 // safe for boot (GPIO15)
|
||||
|
||||
const int buttonPin = 16; // D0
|
||||
|
||||
|
||||
|
||||
bool sdReady = false;
|
||||
const char* CONFIG_FILE = "config.txt";
|
||||
bool wifiEnabled = true; // وضعیت WiFi
|
||||
bool buttonPressed = false; // وضعیت فشار داده شده
|
||||
int lastButtonState = HIGH;
|
||||
unsigned long lastDebounceTime = 0;
|
||||
const unsigned long debounceDelay = 50; // 200ms برای حذف لرزش
|
||||
// ---------------- Protocol & storage ----------------
|
||||
const String PRIVATE_KEY = "as23f"; // shared key
|
||||
const String ALLOWED_DEVICES[] = {"dr142","abcde","fghij"};
|
||||
const int NUM_ALLOWED = 3;
|
||||
|
||||
#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 (same logic as earlier RCSwitch-based receiver)
|
||||
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; // minutes (configurable)
|
||||
int retentionDays = 90; // days (configurable)
|
||||
|
||||
unsigned long lastSaveMillis = 0;
|
||||
unsigned long lastCleanupMillis = 0;
|
||||
|
||||
// ---------------- 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;
|
||||
}
|
||||
//-----------------------------------
|
||||
void setLed(uint8_t idx, bool condition, uint32_t colorTrue, uint32_t colorFalse) {
|
||||
if (condition) {
|
||||
strip.setPixelColor(idx, colorTrue);
|
||||
} else {
|
||||
strip.setPixelColor(idx, colorFalse);
|
||||
}
|
||||
}
|
||||
//-----------------------------------
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
// XOR decrypt (same as earlier)
|
||||
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 ----------------
|
||||
void loadConfigFromSD() {
|
||||
if (!sdReady) return;
|
||||
Serial.println("InloadConfigFromSD1");
|
||||
File f = SD.open(CONFIG_FILE, "r");
|
||||
if (!f) return;
|
||||
while (f.available()) {
|
||||
Serial.println("InloadConfigFromSD1.1");
|
||||
String line = f.readStringUntil('\n');
|
||||
line.trim();
|
||||
if (line.startsWith("save_interval_minutes=")) {
|
||||
saveIntervalMinutes = (unsigned long) line.substring(22).toInt();
|
||||
if (saveIntervalMinutes == 0) saveIntervalMinutes = 5;
|
||||
} else if (line.startsWith("retention_days=")) {
|
||||
retentionDays = line.substring(15).toInt();
|
||||
if (retentionDays == 0) retentionDays = 90;
|
||||
}
|
||||
}
|
||||
Serial.println("InloadConfigFromSD2");
|
||||
|
||||
f.close();
|
||||
Serial.println("InloadConfigFromSD3");
|
||||
}
|
||||
|
||||
void saveConfigToSD() {
|
||||
if (!sdReady) return;
|
||||
File f = SD.open(CONFIG_FILE, "w");
|
||||
if (!f) return;
|
||||
f.print("save_interval_minutes=");
|
||||
f.println(saveIntervalMinutes);
|
||||
f.print("retention_days=");
|
||||
f.println(retentionDays);
|
||||
f.close();
|
||||
}
|
||||
|
||||
// // append data lines to today's file
|
||||
void appendDataToSD(const String &line) {
|
||||
if (!sdReady) return;
|
||||
String fname = safeFileNameForNow();
|
||||
File f = SD.open(fname, FILE_WRITE);
|
||||
if (!f) {
|
||||
// try open with "a" string if FILE_WRITE macro missing
|
||||
f = SD.open(fname, "a");
|
||||
if (!f) return;
|
||||
}
|
||||
f.println(line);
|
||||
f.close();
|
||||
}
|
||||
|
||||
// // compute used bytes (simple sum of file sizes)
|
||||
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;
|
||||
}
|
||||
|
||||
// // cleanup files named YYYYMMDD.txt older than retentionDays, skip config
|
||||
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 (RCSwitch pulses) ----------------
|
||||
void processRemote() {
|
||||
bool rxa=rx.available();
|
||||
//Serial.println("rxa:"+rxa);
|
||||
if (rxa) {
|
||||
unsigned long value = rx.getReceivedValue();
|
||||
unsigned int bitlen = rx.getReceivedBitlength();
|
||||
|
||||
// debug to ensure we see raw values
|
||||
// Serial.print(F("[DEBUG] got value="));
|
||||
// Serial.print(value);
|
||||
// Serial.print(F(" bits="));
|
||||
// Serial.println(bitlen);
|
||||
|
||||
if (bitlen == 8) {
|
||||
char c = (char)value;
|
||||
if (receivingPublicKey) {
|
||||
if (receivedKey.length() < 5) receivedKey += c;
|
||||
if (receivedKey.length() == 5) {
|
||||
receivingPublicKey = false;
|
||||
Serial.println(F("[DEBUG] public key received -> switching to HEX"));
|
||||
}
|
||||
} else {
|
||||
if (isHexChar(c)) receivedHex += c;
|
||||
else {
|
||||
// noise
|
||||
Serial.print(F("[DEBUG] ignored non-hex char: 0x"));
|
||||
Serial.println((int)(uint8_t)c, HEX);
|
||||
}
|
||||
}
|
||||
receivedChars++;
|
||||
lastReceiveTime = millis();
|
||||
}
|
||||
|
||||
rx.resetAvailable();
|
||||
}
|
||||
|
||||
// when frame timeout -> process
|
||||
if (receivedChars >= 5 && (millis() - lastReceiveTime) > FRAME_TIMEOUT_MS) {
|
||||
Serial.println(F("[DEBUG] frame timeout -> processing..."));
|
||||
|
||||
if (receivedKey != PRIVATE_KEY) {
|
||||
Serial.println(F("[WARN] key mismatch"));
|
||||
resetFrame(); return;
|
||||
}
|
||||
if ((receivedHex.length() % 2) != 0) {
|
||||
Serial.println(F("[WARN] odd hex length"));
|
||||
resetFrame(); return;
|
||||
}
|
||||
|
||||
// hex -> bytes
|
||||
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' };
|
||||
char b = (char) strtol(hex2, NULL, 16);
|
||||
decoded += b;
|
||||
}
|
||||
|
||||
if (decoded.length() != EXPECTED_PLAIN_LEN) {
|
||||
Serial.print(F("[WARN] decoded length != expected: "));
|
||||
Serial.println(decoded.length());
|
||||
resetFrame(); return;
|
||||
}
|
||||
|
||||
String plain = decryptData(decoded, PRIVATE_KEY);
|
||||
// check format markers (positions 5,10,15,20 should be S,G,T,H)
|
||||
if (plain.length() != EXPECTED_PLAIN_LEN ||
|
||||
plain[5] != 'S' || plain[10] != 'G' || plain[15] != 'T' || plain[20] != 'H') {
|
||||
Serial.println(F("[WARN] format check failed"));
|
||||
resetFrame(); return;
|
||||
}
|
||||
|
||||
String deviceId = plain.substring(0,5);
|
||||
if (!isDeviceAllowed(deviceId)) {
|
||||
Serial.print(F("[WARN] device not allowed: ")); Serial.println(deviceId);
|
||||
resetFrame(); return;
|
||||
}
|
||||
|
||||
// parse values
|
||||
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;
|
||||
if (rtcReady) ts = dateTimeToISO(rtc.now());
|
||||
else ts = String(millis());
|
||||
|
||||
// PRINT ALWAYS
|
||||
Serial.print(F("[RX] device=")); Serial.print(deviceId);
|
||||
Serial.print(F(" soil=")); Serial.print(soil);
|
||||
Serial.print(F(" gas=")); Serial.print(gas);
|
||||
Serial.print(F(" temp=")); Serial.print(temp);
|
||||
Serial.print(F(" hum=")); Serial.print(hum);
|
||||
Serial.print(F(" time=")); Serial.println(ts);
|
||||
|
||||
// update array (replace if exists else append)
|
||||
bool updated = false;
|
||||
for (int i=0;i<deviceCount;i++) {
|
||||
if (lastRemoteData[i].deviceId == deviceId) {
|
||||
lastRemoteData[i].deviceId = deviceId;
|
||||
lastRemoteData[i].soil = soil;
|
||||
lastRemoteData[i].gas = gas;
|
||||
lastRemoteData[i].temp = temp;
|
||||
lastRemoteData[i].hum = hum;
|
||||
lastRemoteData[i].timestamp = ts;
|
||||
updated = true; break;
|
||||
}
|
||||
}
|
||||
if (!updated && deviceCount < MAX_DEVICES) {
|
||||
lastRemoteData[deviceCount].deviceId = deviceId;
|
||||
lastRemoteData[deviceCount].soil = soil;
|
||||
lastRemoteData[deviceCount].gas = gas;
|
||||
lastRemoteData[deviceCount].temp = temp;
|
||||
lastRemoteData[deviceCount].hum = hum;
|
||||
lastRemoteData[deviceCount].timestamp = ts;
|
||||
deviceCount++;
|
||||
}
|
||||
|
||||
// save to SD if it's time and SD ready
|
||||
if ((millis() - lastSaveMillis) >= (saveIntervalMinutes * 60000UL)) {
|
||||
if (sdReady) {
|
||||
// prepare JSON line
|
||||
String j = "{\"device\":\"" + deviceId + "\",\"soil\":" + String(soil) +
|
||||
",\"gas\":" + String(gas) + ",\"temp\":" + String(temp) +
|
||||
",\"hum\":" + String(hum) + ",\"time\":\"" + ts + "\"}";
|
||||
appendDataToSD(j);
|
||||
Serial.println(F("[INFO] appended to SD"));
|
||||
} else {
|
||||
Serial.println(F("[INFO] SD not ready, skipped save"));
|
||||
}
|
||||
lastSaveMillis = millis();
|
||||
}
|
||||
|
||||
resetFrame();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- Page mapping to integer (user requested) ----------------
|
||||
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;
|
||||
}
|
||||
|
||||
// URL-decode (simple)
|
||||
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(WiFiClient &client) {
|
||||
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(F("[HTTP] ")); Serial.println(firstLine);
|
||||
|
||||
// extract path, e.g. GET /?page=info HTTP/1.1
|
||||
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");
|
||||
int pid = getPageID(page);
|
||||
String response = "";
|
||||
|
||||
switch (pid) {
|
||||
case 1: // info
|
||||
response = "{\"status\":\"ok\",\"data\":\"ESP ready\"}";
|
||||
break;
|
||||
case 2: // sensor (A0)
|
||||
response = "{\"status\":\"ok\",\"sensor\":" + String(analogRead(A0)) + "}";
|
||||
break;
|
||||
case 3: // time
|
||||
if (!rtcReady) response = "{\"status\":\"error\",\"message\":\"RTC not ready\"}";
|
||||
else response = "{\"status\":\"ok\",\"datetime\":\"" + dateTimeToISO(rtc.now()) + "\"}";
|
||||
break;
|
||||
case 4: { // settime (iso)
|
||||
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\",\"datetime\":\"" + dateTimeToISO(rtc.now()) + "\"}";
|
||||
} else response = "{\"status\":\"error\",\"message\":\"invalid iso\"}";
|
||||
break;
|
||||
}
|
||||
case 5: { // lastremote
|
||||
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: { // readfile?name=filename
|
||||
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:
|
||||
if (page.length() == 0) response = "{\"status\":\"error\",\"message\":\"missing page\"}";
|
||||
else response = "{\"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(F("[HTTP] client disconnected"));
|
||||
}
|
||||
|
||||
// ---------------- Setup & Loop ----------------
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(2000);
|
||||
|
||||
//led
|
||||
strip.begin();
|
||||
strip.show();
|
||||
|
||||
//چراغ اول روشن شود. یعنی دستگاه روشن است
|
||||
setLed(0, true, strip.Color(0,255,0), strip.Color(0,0,0));
|
||||
|
||||
|
||||
pinMode(buttonPin, INPUT_PULLUP); // کلید به GND وصل میشه
|
||||
|
||||
Wire.begin(D2, D3);
|
||||
rtcReady = rtc.begin();
|
||||
if (!rtcReady) Serial.println(F("RTC not found"));
|
||||
else if (rtc.lostPower()) {
|
||||
Serial.println(F("RTC lost power — setting to compile time"));
|
||||
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
||||
}
|
||||
|
||||
// RCSwitch init
|
||||
rx.enableReceive(digitalPinToInterrupt(RX_PIN)); // use interrupt on D1
|
||||
rx.setProtocol(1);
|
||||
rx.setPulseLength(300);
|
||||
Serial.println(F("[INFO] RCSwitch receiver enabled on D5"));
|
||||
|
||||
// SD init
|
||||
sdReady = SD.begin(SD_CS_PIN);
|
||||
if (sdReady) {
|
||||
Serial.println("[INFO] SD ready");
|
||||
//loadConfigFromSD();
|
||||
} else {
|
||||
Serial.println("[WARN] SD init failed — will continue without SD");
|
||||
}
|
||||
|
||||
// WiFi AP + server
|
||||
//WiFi.softAP(ssid, password);
|
||||
//server.begin();
|
||||
if (wifiEnabled) {
|
||||
WiFi.softAP(ssid, password);
|
||||
server.begin();
|
||||
Serial.print(F("AP: ")); Serial.println(ssid);
|
||||
Serial.print(F("IP: ")); Serial.println(WiFi.softAPIP());
|
||||
}
|
||||
lastSaveMillis = millis();
|
||||
lastCleanupMillis = millis();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
int reading = digitalRead(buttonPin);
|
||||
|
||||
if (reading != lastButtonState) {
|
||||
lastDebounceTime = millis();
|
||||
}
|
||||
|
||||
if ((millis() - lastDebounceTime) > debounceDelay) {
|
||||
// تشخیص لبه فشار واقعی (HIGH → LOW)
|
||||
if (reading == LOW && !buttonPressed) {
|
||||
buttonPressed = true; // علامتگذاری فشار داده شده
|
||||
wifiEnabled = !wifiEnabled; // تغییر وضعیت AP
|
||||
|
||||
if (wifiEnabled) {
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
WiFi.softAP("ESP-Server", "12345678");
|
||||
Serial.println("✅ سرور (AP) فعال شد");
|
||||
Serial.print("IP سرور: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
} else {
|
||||
WiFi.softAPdisconnect(true);
|
||||
WiFi.mode(WIFI_STA);
|
||||
Serial.println("❌ سرور (AP) غیرفعال شد");
|
||||
}
|
||||
}
|
||||
|
||||
// وقتی دکمه رها شد، آماده فشار بعدی
|
||||
if (reading == HIGH) {
|
||||
buttonPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
lastButtonState = reading;
|
||||
// int buttonState = digitalRead(buttonPin);
|
||||
// if (buttonState == LOW && (millis() - lastDebounceTime) > debounceDelay) {
|
||||
// lastDebounceTime = millis();
|
||||
|
||||
// wifiEnabled = !wifiEnabled; // تغییر وضعیت سرور
|
||||
|
||||
// if (wifiEnabled) {
|
||||
// WiFi.mode(WIFI_AP_STA); // مودم + AP
|
||||
// WiFi.softAP("ESP-Server", "12345678"); // روشن کردن سرور
|
||||
// Serial.println("✅ سرور (AP) فعال شد");
|
||||
// Serial.print("IP سرور: ");
|
||||
// Serial.println(WiFi.softAPIP());
|
||||
// } else {
|
||||
// WiFi.softAPdisconnect(true); // خاموش کردن AP
|
||||
// WiFi.mode(WIFI_STA); // فقط مودم
|
||||
// Serial.println("❌ سرور (AP) غیرفعال شد");
|
||||
// }
|
||||
// }
|
||||
|
||||
if (wifiEnabled) {
|
||||
WiFiClient client = server.available();
|
||||
if (client) handleClient(client);
|
||||
}
|
||||
|
||||
processRemote();
|
||||
|
||||
// WiFiClient client = server.available();
|
||||
//if (client) handleClient(client);
|
||||
|
||||
// periodic cleanup every 12 hours
|
||||
if ((millis() - lastCleanupMillis) >= (12UL * 60UL * 60UL * 1000UL)) {
|
||||
//cleanupOldFilesOnSD();
|
||||
lastCleanupMillis = millis();
|
||||
}
|
||||
//---------------leds---------------------------------
|
||||
//دریافت کننده فعال است؟
|
||||
setLed(0, rtcReady, strip.Color(0,255,0), strip.Color(255,0,0));
|
||||
//کارتخوان فعال است؟
|
||||
setLed(0, sdReady, strip.Color(0,255,0), strip.Color(255,0,0));
|
||||
//wifi فعال است؟
|
||||
setLed(0, wifiEnabled, strip.Color(0,255,0), strip.Color(0,0,0));
|
||||
//مودم فعال است یا خیر و اگر فعال است متصل است؟
|
||||
setLed(0, wifiEnabled, strip.Color(0,255,0), strip.Color(0,0,0));
|
||||
|
||||
//---------------leds---------------------------------
|
||||
}
|
||||
Reference in New Issue
Block a user