old codes

This commit is contained in:
2025-12-19 11:50:13 +03:30
parent 735258f6ec
commit 66a02f4584
15 changed files with 5446 additions and 0 deletions

519
old/STM32-03/STM32-03.ino Normal file
View File

@@ -0,0 +1,519 @@
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_SHT31.h>
#include <BH1750.h>
#include <SPI.h>
// -------------------- پیکربندی حافظه SPI Flash --------------------
#define FLASH_CS PA4
#define FLASH_MOSI PA7
#define FLASH_MISO PA6
#define FLASH_SCK PA5
// کتابخانه جایگزین برای حافظه SPI
#include <SerialFlash.h>
// -------------------- پیکربندی OLED --------------------
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// -------------------- پیکربندی سنسورها --------------------
#define SOIL_PIN PA1
#define MQ7_PIN PA2
#define SDA_PIN PB9
#define SCL_PIN PB8
BH1750 lightMeter;
Adafruit_SHT31 sht31 = Adafruit_SHT31();
// -------------------- پیکربندی EC200U --------------------
#define PWRKEY_PIN PB5
HardwareSerial EC200U(USART3);
// -------------------- پارامترهای قابل تنظیم --------------------
#define SENSOR_READ_INTERVAL 60000 // خواندن سنسورها هر 1 دقیقه
// -------------------- ساختار تنظیمات --------------------
struct DeviceConfig {
char signature[4];
char apn[32];
char serverUrl[64];
char deviceName[16];
char smsNumber[16];
unsigned long webInterval; // دقیقه
unsigned long smsInterval; // دقیقه
unsigned long saveInterval; // ثانیه - فاصله ذخیره در قطعی
bool smsEnabled;
bool valid;
};
// -------------------- ساختار داده سنسورها --------------------
struct SensorData {
float temperature;
float humidity;
int soilMoisture;
float coPPM;
float lightLux;
unsigned long timestamp;
};
// -------------------- متغیرهای سراسری --------------------
DeviceConfig config;
SensorData currentData;
SensorData storedData;
unsigned long lastSensorRead = 0;
unsigned long lastUpload = 0;
unsigned long lastSMS = 0;
unsigned long lastSave = 0;
bool networkConnected = false;
bool dataStored = false;
int displayMode = 0;
unsigned long lastDisplayChange = 0;
// -------------------- توابع حافظه SPI Flash --------------------
void initFlash() {
SPI.setMOSI(FLASH_MOSI);
SPI.setMISO(FLASH_MISO);
SPI.setSCLK(FLASH_SCK);
SPI.begin();
pinMode(FLASH_CS, OUTPUT);
digitalWrite(FLASH_CS, HIGH);
delay(100);
// مقداردهی اولیه SerialFlash
if (!SerialFlash.begin(FLASH_CS)) {
Serial1.println("❌ Flash init failed!");
return;
}
Serial1.println("✅ Flash OK");
// نمایش اطلاعات حافظه
SerialFlash::ID id;
SerialFlash.readID(&id);
Serial1.print("Flash ID: ");
Serial1.print(id.id, HEX);
Serial1.print(" Size: ");
Serial1.print(SerialFlash.capacity(id) / 1024);
Serial1.println("KB");
}
void readConfig() {
// ابتدا از EEPROM داخلی بخوان
EEPROM.get(0, config);
// اگر تنظیمات معتبر نیست، از حافظه SPI بخوان
if (strcmp(config.signature, "CFG") != 0) {
if (SerialFlash.exists("config.bin")) {
SerialFlashFile file = SerialFlash.open("config.bin");
if (file) {
file.read(&config, sizeof(DeviceConfig));
file.close();
Serial1.println("✅ Config read from SPI Flash");
}
}
}
// اگر هنوز تنظیمات معتبر نیست، مقادیر پیش‌فرض
if (strcmp(config.signature, "CFG") != 0) {
strcpy(config.signature, "CFG");
strcpy(config.apn, "mcinet");
strcpy(config.serverUrl, "https://ghback.nabaksoft.ir/api/Telemetry/AddData");
strcpy(config.smsNumber, "+989120000000");
strcpy(config.deviceName, "ST-");
config.webInterval = 1;
config.smsInterval = 5;
config.saveInterval = 60; // پیش‌فرض 60 ثانیه
config.smsEnabled = false;
config.valid = false;
}
}
void saveConfig() {
config.valid = true;
// در EEPROM داخلی ذخیره کن
EEPROM.put(0, config);
EEPROM.commit();
// در حافظه SPI هم ذخیره کن
if (SerialFlash.exists("config.bin")) {
SerialFlash.remove("config.bin");
}
SerialFlashFile file = SerialFlash.create("config.bin", sizeof(DeviceConfig));
if (file) {
file.write((byte*)&config, sizeof(DeviceConfig));
file.close();
Serial1.println("✅ Config saved to SPI Flash");
}
}
void saveDataToFlash() {
if (!dataStored) {
if (SerialFlash.exists("data.bin")) {
SerialFlash.remove("data.bin");
}
dataStored = true;
}
currentData.timestamp = millis();
SerialFlashFile file = SerialFlash.open("data.bin");
if (!file) {
file = SerialFlash.create("data.bin", sizeof(SensorData));
}
if (file) {
file.seek(0);
file.write((byte*)&currentData, sizeof(SensorData));
file.close();
Serial1.println("💾 Data saved to flash");
}
}
void readDataFromFlash() {
if (SerialFlash.exists("data.bin")) {
SerialFlashFile file = SerialFlash.open("data.bin");
if (file) {
file.read(&storedData, sizeof(SensorData));
file.close();
// بررسی اعتبار داده
if (storedData.timestamp > 0) {
Serial1.println("📖 Stored data found");
}
}
}
}
void clearStoredData() {
if (SerialFlash.exists("data.bin")) {
SerialFlash.remove("data.bin");
storedData.timestamp = 0;
dataStored = false;
}
}
// -------------------- توابع EC200U --------------------
bool sendAT(String cmd, uint16_t wait = 2000) {
Serial1.print(">> ");
Serial1.println(cmd);
EC200U.println(cmd);
String response = "";
unsigned long start = millis();
while (millis() - start < wait) {
while (EC200U.available()) {
char c = EC200U.read();
response += c;
Serial1.write(c);
}
if (response.indexOf("OK") != -1) return true;
if (response.indexOf("ERROR") != -1) return false;
}
return false;
}
bool waitForResponse(String expected, unsigned long timeout = 10000) {
unsigned long startTime = millis();
String response = "";
while (millis() - startTime < timeout) {
while (EC200U.available()) {
char c = EC200U.read();
response += c;
Serial1.write(c);
if (response.indexOf(expected) != -1) return true;
if (response.indexOf("ERROR") != -1) return false;
}
}
return false;
}
bool sendSMS(String number, String message) {
if (!sendAT("AT+CMGF=1", 2000)) return false;
String cmd = "AT+CMGS=\"" + number + "\"";
if (!sendAT(cmd, 2000)) return false;
delay(100);
EC200U.print(message);
EC200U.write(26);
return waitForResponse("+CMGS:", 10000);
}
void initEC200U() {
Serial1.println("🚀 Starting EC200U...");
digitalWrite(PWRKEY_PIN, HIGH);
delay(2000);
digitalWrite(PWRKEY_PIN, LOW);
delay(5000);
if (!sendAT("AT", 3000)) {
Serial1.println("❌ EC200U not responding!");
return;
}
String apnCmd = "AT+CGDCONT=1,\"IP\",\"" + String(config.apn) + "\"";
sendAT(apnCmd, 2000);
if (sendAT("AT+QIACT=1", 15000)) {
networkConnected = true;
Serial1.println("✅ Network connected");
// اگر داده ذخیره شده وجود دارد، ارسال کن
if (storedData.timestamp > 0) {
Serial1.println("📤 Sending stored data...");
sendStoredData();
}
} else {
networkConnected = false;
Serial1.println("❌ Network failed");
}
}
// -------------------- توابع سنسورها --------------------
void readSensors() {
currentData.temperature = sht31.readTemperature();
currentData.humidity = sht31.readHumidity();
currentData.soilMoisture = readSoil();
currentData.coPPM = MQ7_ReadPPM();
currentData.lightLux = lightMeter.readLightLevel();
if (isnan(currentData.temperature)) currentData.temperature = -999;
if (isnan(currentData.humidity)) currentData.humidity = -999;
if (isnan(currentData.lightLux)) currentData.lightLux = 0;
lastSensorRead = millis();
}
int readSoil() {
int adcValue = analogRead(SOIL_PIN);
const int adcDry = 4095;
const int adcWet = 1200;
float soilPercent = (float)(adcDry - adcValue) / (adcDry - adcWet) * 100.0;
if (soilPercent > 100.0) soilPercent = 100.0;
if (soilPercent < 0.0) soilPercent = 0.0;
return (int)soilPercent;
}
float MQ7_ReadPPM() {
long sum = 0;
for (int i = 0; i < 10; i++) {
sum += analogRead(MQ7_PIN);
delay(5);
}
float adcValue = sum / 10.0;
float voltage = (adcValue / 4095.0) * 3.3;
float ppm;
if (voltage < 0.1) ppm = 0;
else if (voltage < 0.2) ppm = 10 * (voltage / 0.2);
else if (voltage < 0.5) ppm = 50 + (voltage - 0.2) * 300;
else if (voltage < 1.0) ppm = 140 + (voltage - 0.5) * 800;
else if (voltage < 2.0) ppm = 500 + (voltage - 1.0) * 1500;
else ppm = 2000 + (voltage - 2.0) * 2000;
if (ppm < 0) ppm = 0;
if (ppm > 10000) ppm = 10000;
return ppm;
}
// -------------------- توابع ارسال داده --------------------
bool sendData(SensorData data) {
if (data.temperature == -999 || data.humidity == -999) return false;
String dataStr = "temperatureC=" + String(data.temperature, 1) +
"&humidityPercent=" + String(data.humidity, 1) +
"&soilPercent=" + String(data.soilMoisture) +
"&gasPPM=" + String(data.coPPM, 0) +
"&lux=" + String(data.lightLux, 1);
String url = String(config.serverUrl) + "?deviceName=" + String(config.deviceName) + "&" + dataStr;
String httpCmd = "AT+QHTTPURL=" + String(url.length()) + ",80";
if (!sendAT(httpCmd, 5000)) return false;
delay(100);
EC200U.print(url);
if (!waitForResponse("OK", 5000)) return false;
return sendAT("AT+QHTTPGET=60", 15000);
}
void sendStoredData() {
if (sendData(storedData)) {
Serial1.println("✅ Stored data sent successfully");
clearStoredData();
}
}
// -------------------- توابع نمایش OLED --------------------
void updateDisplay() {
if (millis() - lastDisplayChange > 5000) {
displayMode = (displayMode + 1) % 3;
lastDisplayChange = millis();
}
display.clearDisplay();
display.setTextSize(1);
switch(displayMode) {
case 0:
display.setTextSize(2);
display.setCursor(20, 10);
display.println("DEVICE");
display.setCursor(30, 35);
display.println(config.deviceName);
break;
case 1:
display.setCursor(0, 0);
display.print("T:");
display.print(currentData.temperature, 1);
display.print("C ");
display.print("H:");
display.print(currentData.humidity, 1);
display.println("%");
display.setCursor(0, 16);
display.print("S:");
display.print(currentData.soilMoisture);
display.print("% ");
display.print("C:");
display.print(currentData.coPPM, 0);
display.println("P");
display.setCursor(0, 32);
display.print("L:");
display.print(currentData.lightLux, 0);
display.println("Lx");
display.setCursor(0, 48);
display.print(networkConnected ? "ONLINE" : "OFFLINE");
if (dataStored) display.print(" *");
break;
case 2:
display.setTextSize(3);
display.setCursor(10, 25);
display.print(currentData.temperature, 1);
display.setTextSize(2);
display.setCursor(100, 30);
display.print("C");
break;
}
display.display();
}
// -------------------- setup --------------------
void setup() {
Serial1.begin(115200);
delay(1000);
Serial1.println("\n\n🚀 System Starting...");
pinMode(PWRKEY_PIN, OUTPUT);
pinMode(SOIL_PIN, INPUT);
pinMode(MQ7_PIN, INPUT);
digitalWrite(PWRKEY_PIN, LOW);
Wire.setSDA(SDA_PIN);
Wire.setSCL(SCL_PIN);
Wire.begin();
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial1.println("❌ OLED failed!");
}
sht31.begin(0x44);
lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
// مقداردهی حافظه
initFlash();
readConfig();
readDataFromFlash();
// اگر نام دستگاه تنظیم نشده، از UID استفاده کن
if (strlen(config.deviceName) < 3) {
uint32_t uid0 = *(uint32_t*)0x1FFFF7E8;
uint32_t uid1 = *(uint32_t*)0x1FFFF7EC;
uint32_t uid2 = *(uint32_t*)0x1FFFF7F0;
sprintf(config.deviceName, "ST-%08X", (unsigned int)(uid0 ^ uid1 ^ uid2));
saveConfig();
}
EC200U.begin(115200);
if (config.valid) {
initEC200U();
}
Serial1.println("✅ System Ready");
}
// -------------------- loop --------------------
void loop() {
// خواندن سنسورها در بازه زمانی مشخص
if (millis() - lastSensorRead > SENSOR_READ_INTERVAL) {
readSensors();
Serial1.println("📊 Sensors read");
// اگر شبکه وصل است، داده را ارسال کن
if (networkConnected) {
if (millis() - lastUpload > config.webInterval * 60000) {
if (sendData(currentData)) {
Serial1.println("✅ Web data sent");
lastUpload = millis();
}
}
}
// اگر شبکه قطع است، داده را ذخیره کن
else {
if (config.saveInterval > 0) {
if (millis() - lastSave > config.saveInterval * 1000) {
saveDataToFlash();
lastSave = millis();
}
}
// ارسال SMS در صورت فعال بودن
if (config.smsEnabled && millis() - lastSMS > config.smsInterval * 60000) {
String smsMsg = String(config.deviceName) +
" T:" + String(currentData.temperature, 1) +
" H:" + String(currentData.humidity, 1) +
" S:" + String(currentData.soilMoisture) + "%";
if (sendSMS(config.smsNumber, smsMsg)) {
Serial1.println("✅ SMS sent");
lastSMS = millis();
}
}
}
}
updateDisplay();
// تلاش برای اتصال مجدد شبکه
static unsigned long lastReconnect = 0;
if (!networkConnected && millis() - lastReconnect > 60000) {
initEC200U();
lastReconnect = millis();
}
delay(1000);
}

View File

@@ -0,0 +1,210 @@
#include <Arduino.h>
#include <Wire.h>
#include <BH1750.h>
#include "DHT.h"
// --------------------------- تنظیمات ---------------------------
#define DHTPIN A0
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
#define SOIL_PIN A1
#define MQ7_PIN A2
#define VREF 5.0 // ولتاژ مرجع ADC
#define ADC_MAX 4095.0 // برای STM32 (۱۲ بیتی)
#define SDA_PIN PB9
#define SCL_PIN PB8
BH1750 lightMeter;
// UART EC200U
HardwareSerial EC200U(1);
#define PWRKEY_PIN PB5
// ارسال هر n دقیقه
#define UPLOAD_INTERVAL_MIN 1
// ذخیره ساعت شبکه
String networkTime = "";
// --------------------------- توابع ---------------------------
// روشن کردن ماژول EC200U
void powerEC200U() {
pinMode(PWRKEY_PIN, OUTPUT);
digitalWrite(PWRKEY_PIN, HIGH);
delay(1000); // نگه داشتن HIGH حدود 1 ثانیه برای روشن کردن
digitalWrite(PWRKEY_PIN, LOW);
delay(2000); // کمی صبر قبل از AT
}
// ارسال دستور AT و نمایش پاسخ
void sendAT(String cmd, uint16_t wait = 1000) {
Serial.print(">> "); Serial.println(cmd);
EC200U.println(cmd);
unsigned long start = millis();
while (millis() - start < wait) {
while (EC200U.available()) Serial.write(EC200U.read());
}
Serial.println();
}
// آماده‌سازی ماژول و اینترنت
void initEC200U() {
Serial.println("🚀 Initializing EC200U...");
sendAT("AT",1000);
sendAT("ATI",1000);
sendAT("AT+CPIN?",1000);
sendAT("AT+CSQ",1000);
sendAT("AT+CREG?",1000);
sendAT("AT+CGATT=1",2000);
sendAT("AT+CGDCONT=1,\"IP\",\"mcinet\"",1000);
sendAT("AT+QIACT=1",5000);
sendAT("AT+QIACT?",2000);
Serial.println("✅ EC200U Ready");
}
// خواندن ساعت شبکه
void getNetworkTime() {
sendAT("AT+QLTS=2", 3000);
networkTime = "";
unsigned long t = millis();
while (millis()-t < 3000) {
while(EC200U.available()) {
char c = EC200U.read();
Serial.write(c);
networkTime += c;
}
}
}
// خواندن سنسورها
float readDHT22() { return dht.readTemperature(); }
float readHumidity() { return dht.readHumidity(); }
// رطوبت خاک به درصد
int readSoilPercent() {
int val = analogRead(SOIL_PIN); // 0-4095
int perc = map(val, 3000, 1500, 0, 100); // خشک: 3000, خیس:1500 (تنظیم شود)
if(perc>100) perc=100;
if(perc<0) perc=0;
return perc;
}
float readMQ7ppm() {
// --- 1. میانگین‌گیری چند قرائت برای پایداری ---
long sum = 0;
for (int i = 0; i < 10; i++) {
sum += analogRead(MQ7_PIN);
delay(5);
}
float adcValue = sum / 10.0;
// --- 2. محاسبه ولتاژ خروجی ---
float voltage = (adcValue / ADC_MAX) * VREF;
// --- 3. تبدیل تقریبی به ppm ---
// منحنی تجربی بر اساس تست ماژول‌های MQ7 آماده (ولتاژ به ppm CO)
// این ضرایب با ولتاژ 0.1V ~ 1V برای ppm=50~5000 کالیبره شده‌اند.
// اگر مقدار خروجی بیش از 4V باشد، سنسور اشباع است.
float ppm;
if (voltage < 0.1) ppm = 0; // خیلی پاک
else if (voltage < 0.2) ppm = 10 * (voltage / 0.2); // محدوده پایین
else if (voltage < 0.5) ppm = 50 + (voltage - 0.2) * 300; // 50140ppm
else if (voltage < 1.0) ppm = 140 + (voltage - 0.5) * 800; // تا حدود 500ppm
else if (voltage < 2.0) ppm = 500 + (voltage - 1.0) * 1500; // تا حدود 2000ppm
else ppm = 2000 + (voltage - 2.0) * 2000; // تا ~4000ppm
// --- 4. محدود کردن مقدار ---
if (ppm < 0) ppm = 0;
if (ppm > 10000) ppm = 10000;
// --- 5. نمایش برای دیباگ ---
Serial.print("MQ7 voltage: ");
Serial.print(voltage, 3);
Serial.print(" V, CO ≈ ");
Serial.print(ppm, 1);
Serial.println(" ppm");
return ppm;
}
float readLight() { return lightMeter.readLightLevel(); }
// ارسال داده HTTP GET
void sendData(float temp, float hum, int soilPerc, float mq7ppm, float lux, String timeStr) {
String dataStr = String(temp,1) + "," + String(hum,1) + "," + String(soilPerc) + "," + String(mq7ppm,0) + "," + String(lux,1) + "," + timeStr;
// 'http://localhost:5064/api/Telemetry/AddData?deviceName=dr110&temperatureC=24.5&humidityPercent=26.3&soilPercent=12&gasPPM=2&lux=103'
String url = "http://nabaksoft.ir/api/Telemetry/AddData?deviceName=dr110&temperatureC="+String(temp,1)
+"&humidityPercent="+String(hum,1)+"&soilPercent="+String(soilPerc)+"&gasPPM="+String(mq7ppm,0)+"&lux="+String(lux,1);
Serial.println("🌐 Sending data: " + dataStr);
sendAT("AT+QHTTPURL=" + String(url.length()) + ",80", 500);
delay(500);
EC200U.print(url);
delay(1000);
sendAT("AT+QHTTPGET=80", 5000);
sendAT("AT+QHTTPREAD=80", 5000);
}
// --------------------------- راه‌اندازی ---------------------------
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("🚀 STM32 + EC200U + Sensors");
// روشن کردن ماژول EC200U
powerEC200U();
// UART EC200U
EC200U.setRx(PA10);
EC200U.setTx(PA9);
EC200U.begin(115200);
delay(1000);
// سنسورها
dht.begin();
Wire.setSDA(SDA_PIN);
Wire.setSCL(SCL_PIN);
Wire.begin();
if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) {
Serial.println("BH1750 Initialized successfully!");
} else {
Serial.println("Error initializing BH1750!");
}
// آماده سازی ماژول و اینترنت
initEC200U();
// خواندن ساعت شبکه
getNetworkTime();
}
// --------------------------- حلقه اصلی ---------------------------
unsigned long lastUpload = 0;
void loop() {
unsigned long now = millis();
if (now - lastUpload > UPLOAD_INTERVAL_MIN*60UL*1000UL || lastUpload==0) {
// خواندن سنسورها
float temp = readDHT22();
float hum = readHumidity();
int soil = readSoilPercent();
float mq7 = readMQ7ppm();
float lux = readLight();
// دریافت ساعت شبکه
getNetworkTime();
// ارسال به سرور
sendData(temp, hum, soil, mq7, lux, networkTime);
lastUpload = now;
}
delay(500);
}

156
old/newWifi/newWifi.ino Normal file
View File

@@ -0,0 +1,156 @@
#include <SoftwareSerial.h>
SoftwareSerial esp8266(4, 5); // RX, TX
// صفحه پیش‌فرض HTML
const char* HTML_PAGE = "<!DOCTYPE html><html><body><h2>Hello from ESP8266</h2></body></html>";
void setup() {
Serial.begin(9600);
esp8266.begin(9600);
delay(2000); // آماده‌سازی ESP
// تنظیمات اولیه ESP8266
sendCommand("AT", "OK", 2000);
sendCommand("ATE0", "OK", 2000);
sendCommand("AT+CWMODE=2", "OK", 2000);
sendCommand("AT+CWSAP=\"ESP8266_AP\",\"12345678\",1,0", "OK", 5000);
sendCommand("AT+CIPMUX=1", "OK", 3000);
sendCommand("AT+CIPSERVER=1,80", "OK", 3000);
Serial.println(F("ESP8266 Ready. Connect to SSID: ESP8266_AP, Pass: 12345678"));
Serial.println(F("Then open http://192.168.4.1/ in your browser"));
}
void loop() {
if (esp8266.available()) {
String line = esp8266.readStringUntil('\n');
line.trim();
if (line.length() == 0) return;
Serial.println("[ESP8266] " + line);
if (line.startsWith("+IPD,")) {
int len = 0;
int linkId = parseIPDLine(line, len);
if (linkId < 0) {
Serial.println("Failed to parse IPD line.");
return;
}
drainPayload(len);
// فراخوانی تابع پردازش و ارسال پاسخ
processAndSend(linkId, line);
}
}
}
// =================== توابع کمکی ===================
// تابع پردازش داده و ارسال پاسخ به کلاینت
void processAndSend(int linkId, const String &requestLine) {
String html;
// بررسی پارامتر صفحه در URL
if (requestLine.indexOf("GET /?page=") != -1) {
int start = requestLine.indexOf("GET /?page=") + 10;
int end = requestLine.indexOf(" ", start);
int pageNum = requestLine.substring(start, end).toInt();
switch (pageNum) {
case 1:
html = "<!DOCTYPE html><html><body><h2>صفحه ۱</h2></body></html>";
break;
case 2:
html = "<!DOCTYPE html><html><body><h2>صفحه ۲</h2></body></html>";
break;
case 3:
html = "<!DOCTYPE html><html><body><h2>صفحه ۳</h2></body></html>";
break;
default:
html = "<!DOCTYPE html><html><body><h2>صفحه پیش‌فرض</h2></body></html>";
break;
}
} else {
html = HTML_PAGE; // صفحه پیش‌فرض
}
// ساخت پاسخ HTTP
String response = "HTTP/1.1 200 OK\r\n";
response += "Content-Type: text/html\r\n";
response += "Content-Length: " + String(html.length()) + "\r\n";
response += "Connection: close\r\n\r\n";
response += html;
// ارسال پاسخ
String cmd = "AT+CIPSEND=" + String(linkId) + "," + String(response.length());
if (sendCommand(cmd, ">", 5000)) {
esp8266.print(response);
if (waitFor("SEND OK", 5000)) {
Serial.println("Response sent to client.");
} else {
Serial.println("SEND OK not received (timeout).");
}
} else {
Serial.println("CIPSEND prompt not received (timeout).");
}
// بستن کانکشن
String closeCmd = "AT+CIPCLOSE=" + String(linkId);
sendCommand(closeCmd, "OK", 2000);
}
// استخراج linkId و len از خط +IPD
int parseIPDLine(const String& line, int &lenOut) {
int ipdPos = line.indexOf("+IPD,");
if (ipdPos == -1) return -1;
int firstComma = line.indexOf(',', ipdPos + 4);
int secondComma = line.indexOf(',', firstComma + 1);
if (firstComma == -1 || secondComma == -1) return -1;
int colon = line.indexOf(':', secondComma + 1);
if (colon == -1) {
lenOut = line.substring(secondComma + 1).toInt();
return line.substring(ipdPos + 4, firstComma).toInt();
} else {
lenOut = line.substring(firstComma + 1, secondComma).toInt();
return line.substring(ipdPos + 4, firstComma).toInt();
}
}
// پاک‌سازی payload بعد از +IPD
void drainPayload(int len) {
unsigned long t0 = millis();
int remaining = len;
while (millis() - t0 < 1000 && remaining > 0) {
if (esp8266.available()) {
esp8266.read();
remaining--;
}
}
}
// ارسال فرمان AT و انتظار ACK
bool sendCommand(const String& cmd, const char* ack, unsigned long timeout) {
esp8266.println(cmd);
return waitFor(ack, timeout);
}
// منتظر بودن برای وجود یک ACK/کلمه کلیدی تا مهلت معین
bool waitFor(const char* target, unsigned long timeout) {
unsigned long start = millis();
String buffer = "";
while (millis() - start < timeout) {
while (esp8266.available()) {
char c = esp8266.read();
buffer += c;
if (buffer.indexOf(target) != -1) return true;
}
}
Serial.print("Timeout waiting for: "); Serial.println(target);
return false;
}

759
old/nodemcu3.ino Normal file
View File

@@ -0,0 +1,759 @@
// 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 <RH_ASK.h>
#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>
// ---------------- 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;
#define RF_RECEIVE_PIN D1
RH_ASK driver(1200, RF_RECEIVE_PIN, -1, -1);
#define LED_PIN 1 // TX = GPIO1
#define NUM_LEDS 8
//Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
#define SD_CS_PIN D8
bool sdReady = false;
const char* CONFIG_FILE = "config.txt";
const int buttonPin = 16; // D0
bool wifiEnabled = true;
bool ConnectToModem = 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 ALLOWED_DEVICES[] = {"dr142","abcde","fghij"};
const int NUM_ALLOWED = 3;
#define MAX_DEVICES 4
struct RemoteData {
String deviceId;
int soil;
int gas;
int temp;
int hum;
unsigned long timestamp;
};
RemoteData lastRemoteData[MAX_DEVICES];
/*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;
// ---------------- 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() {
uint8_t buf[32]; // حداکثر طول بسته
uint8_t buflen = sizeof(buf);
if (driver.recv(buf, &buflen)) {
buf[buflen] = '\0'; // تبدیل به رشته
String plain = String((char*)buf);
Serial.print("[RX] Received: ");
Serial.println(plain);
// 📌 فرض: فرمت مثل "dr142S0345G0123T0254H0456"
if (plain.length() >= 25) {
RemoteData _remoteData;
_remoteData.soil = plain.substring(6, 10).toInt();
_remoteData.gas = plain.substring(11, 15).toInt();
_remoteData.deviceId = plain.substring(0, 5);
_remoteData.temp = plain.substring(16, 20).toInt();
_remoteData.hum = plain.substring(21, 25).toInt();
_remoteData.timestamp = millis();
bool updated=false;
for (int i=0;i<deviceCount;i++) {
if (lastRemoteData[i].deviceId==_remoteData.deviceId) {
lastRemoteData[i]=_remoteData;
updated=true; break;
}
}
if (!updated && deviceCount<MAX_DEVICES) lastRemoteData[deviceCount++]=_remoteData;
if ((millis()-lastSaveMillis) >= saveIntervalMinutes*60000UL) {
if (sdReady) {
String j="{\"device\":\""+_remoteData.deviceId+"\",\"soil\":"+String(_remoteData.soil)+",\"gas\":"+String(_remoteData.gas)+
",\"temp\":"+String(_remoteData.temp)+",\"hum\":"+String(_remoteData.hum)+",\"time\":\""+_remoteData.timestamp+"\"}";
appendDataToSD(j);
Serial.println("[INFO] appended to SD");
}
lastSaveMillis=millis();
}
// String logLine = plain + "," + String(millis());
// saveToSD(logLine);
//digitalWrite(LED_GREEN, HIGH);
delay(50);
//digitalWrite(LED_GREEN, LOW);
}
}
}
// ---------------- 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(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("[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;
ConnectToModem=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\"}"; break;
case 2: response="{\"status\":\"ok\",\"sensor\":"+String(analogRead(A0))+"}"; break;
case 3: response=rtcReady?"{\"status\":\"ok\",\"datetime\":\""+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\",\"datetime\":\""+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+=","; RemoteData 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");
}
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 setup() {
Serial.begin(115200); delay(2000);
//led
// strip.begin();
// strip.show();
//چراغ اول روشن شود. یعنی دستگاه روشن است
// setLed(0, true, strip.Color(0,255,0), strip.Color(0,0,0));
// strip.show();
pinMode(buttonPin, INPUT_PULLUP);
Wire.begin(D2,D3);
rtcReady=rtc.begin();
if(rtcReady && rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
if (!driver.init()) {
Serial.println("[ERR] RH_ASK init failed!");
} else {
Serial.println("[INFO] RH_ASK ready");
}
// rx.enableReceive(digitalPinToInterrupt(RX_PIN));
// rx.setProtocol(1);
// rx.setPulseLength(300);
Serial.println("[INFO] RCSwitch enabled on D5");
sdReady=SD.begin(SD_CS_PIN);
if(sdReady){ Serial.println("[INFO] SD ready"); loadConfigFromSD(); }
else Serial.println("[WARN] SD init failed");
if(wifiEnabled){ WiFi.softAP(apSSID,apPassword); server.begin(); Serial.print("AP IP: "); Serial.println(WiFi.softAPIP()); }
if(staSSID.length()>0){ ConnectToModem=WiFi.begin(staSSID.c_str(),staPassword.c_str()); }
lastSaveMillis=millis(); lastCleanupMillis=millis(); lastSendMillis=millis();
}
void loop() {
// ---------------- 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);
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;
// ---------------- HTTP server handling ----------------
if (wifiEnabled) {
WiFiClient client = server.available();
if (client) handleClient(client);
}
// ---------------- Process RF data ----------------
processRemote();
// ---------------- Periodic cleanup every 12 hours ----------------
if ((millis() - lastCleanupMillis) >= (12UL * 60UL * 60UL * 1000UL)) {
cleanupOldFilesOnSD();
lastCleanupMillis = millis();
}
// ---------------- Debug info before sending ----------------
// ---------------- Periodic send to server ----------------
// ---------------- Periodic send to server ----------------
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 += ",";
RemoteData 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();
}
}
//دریافت کننده فعال است؟
// setLed(1, rtcReady, strip.Color(0,255,0), strip.Color(255,0,0));
// //کارتخوان فعال است؟
// setLed(2, sdReady, strip.Color(0,255,0), strip.Color(255,0,0));
// //wifi فعال است؟
// setLed(3, wifiEnabled, strip.Color(0,255,0), strip.Color(0,0,0));
// //مودم فعال است یا خیر و اگر فعال است متصل است؟
// if(staSSID.length()>0)
// setLed(4, ConnectToModem, strip.Color(0,255,0), strip.Color(255,0,0));
// else
// setLed(4, true, strip.Color(0,0,0), strip.Color(0,0,0));
// strip.show();
}

759
old/nodemcu3/nodemcu3.ino Normal file
View File

@@ -0,0 +1,759 @@
// 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 <RH_ASK.h>
#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>
// ---------------- 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;
#define RF_RECEIVE_PIN D1
RH_ASK driver(1200, RF_RECEIVE_PIN, -1, -1);
#define LED_PIN 1 // TX = GPIO1
#define NUM_LEDS 8
//Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
#define SD_CS_PIN D8
bool sdReady = false;
const char* CONFIG_FILE = "config.txt";
const int buttonPin = 16; // D0
bool wifiEnabled = true;
bool ConnectToModem = 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 ALLOWED_DEVICES[] = {"dr142","abcde","fghij"};
const int NUM_ALLOWED = 3;
#define MAX_DEVICES 4
struct RemoteData {
String deviceId;
int soil;
int gas;
int temp;
int hum;
unsigned long timestamp;
};
RemoteData lastRemoteData[MAX_DEVICES];
/*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;
// ---------------- 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() {
uint8_t buf[32]; // حداکثر طول بسته
uint8_t buflen = sizeof(buf);
if (driver.recv(buf, &buflen)) {
buf[buflen] = '\0'; // تبدیل به رشته
String plain = String((char*)buf);
Serial.print("[RX] Received: ");
Serial.println(plain);
// 📌 فرض: فرمت مثل "dr142S0345G0123T0254H0456"
if (plain.length() >= 25) {
RemoteData _remoteData;
_remoteData.soil = plain.substring(6, 10).toInt();
_remoteData.gas = plain.substring(11, 15).toInt();
_remoteData.deviceId = plain.substring(0, 5);
_remoteData.temp = plain.substring(16, 20).toInt();
_remoteData.hum = plain.substring(21, 25).toInt();
_remoteData.timestamp = millis();
bool updated=false;
for (int i=0;i<deviceCount;i++) {
if (lastRemoteData[i].deviceId==_remoteData.deviceId) {
lastRemoteData[i]=_remoteData;
updated=true; break;
}
}
if (!updated && deviceCount<MAX_DEVICES) lastRemoteData[deviceCount++]=_remoteData;
if ((millis()-lastSaveMillis) >= saveIntervalMinutes*60000UL) {
if (sdReady) {
String j="{\"device\":\""+_remoteData.deviceId+"\",\"soil\":"+String(_remoteData.soil)+",\"gas\":"+String(_remoteData.gas)+
",\"temp\":"+String(_remoteData.temp)+",\"hum\":"+String(_remoteData.hum)+",\"time\":\""+_remoteData.timestamp+"\"}";
appendDataToSD(j);
Serial.println("[INFO] appended to SD");
}
lastSaveMillis=millis();
}
// String logLine = plain + "," + String(millis());
// saveToSD(logLine);
//digitalWrite(LED_GREEN, HIGH);
delay(50);
//digitalWrite(LED_GREEN, LOW);
}
}
}
// ---------------- 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(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("[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;
ConnectToModem=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\"}"; break;
case 2: response="{\"status\":\"ok\",\"sensor\":"+String(analogRead(A0))+"}"; break;
case 3: response=rtcReady?"{\"status\":\"ok\",\"datetime\":\""+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\",\"datetime\":\""+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+=","; RemoteData 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");
}
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 setup() {
Serial.begin(115200); delay(2000);
//led
// strip.begin();
// strip.show();
//چراغ اول روشن شود. یعنی دستگاه روشن است
// setLed(0, true, strip.Color(0,255,0), strip.Color(0,0,0));
// strip.show();
pinMode(buttonPin, INPUT_PULLUP);
Wire.begin(D2,D3);
rtcReady=rtc.begin();
if(rtcReady && rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
if (!driver.init()) {
Serial.println("[ERR] RH_ASK init failed!");
} else {
Serial.println("[INFO] RH_ASK ready");
}
// rx.enableReceive(digitalPinToInterrupt(RX_PIN));
// rx.setProtocol(1);
// rx.setPulseLength(300);
Serial.println("[INFO] RCSwitch enabled on D5");
sdReady=SD.begin(SD_CS_PIN);
if(sdReady){ Serial.println("[INFO] SD ready"); loadConfigFromSD(); }
else Serial.println("[WARN] SD init failed");
if(wifiEnabled){ WiFi.softAP(apSSID,apPassword); server.begin(); Serial.print("AP IP: "); Serial.println(WiFi.softAPIP()); }
if(staSSID.length()>0){ ConnectToModem=WiFi.begin(staSSID.c_str(),staPassword.c_str()); }
lastSaveMillis=millis(); lastCleanupMillis=millis(); lastSendMillis=millis();
}
void loop() {
// ---------------- 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);
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;
// ---------------- HTTP server handling ----------------
if (wifiEnabled) {
WiFiClient client = server.available();
if (client) handleClient(client);
}
// ---------------- Process RF data ----------------
processRemote();
// ---------------- Periodic cleanup every 12 hours ----------------
if ((millis() - lastCleanupMillis) >= (12UL * 60UL * 60UL * 1000UL)) {
cleanupOldFilesOnSD();
lastCleanupMillis = millis();
}
// ---------------- Debug info before sending ----------------
// ---------------- Periodic send to server ----------------
// ---------------- Periodic send to server ----------------
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 += ",";
RemoteData 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();
}
}
//دریافت کننده فعال است؟
// setLed(1, rtcReady, strip.Color(0,255,0), strip.Color(255,0,0));
// //کارتخوان فعال است؟
// setLed(2, sdReady, strip.Color(0,255,0), strip.Color(255,0,0));
// //wifi فعال است؟
// setLed(3, wifiEnabled, strip.Color(0,255,0), strip.Color(0,0,0));
// //مودم فعال است یا خیر و اگر فعال است متصل است؟
// if(staSSID.length()>0)
// setLed(4, ConnectToModem, strip.Color(0,255,0), strip.Color(255,0,0));
// else
// setLed(4, true, strip.Color(0,0,0), strip.Color(0,0,0));
// strip.show();
}

716
old/nodmcu1/nodmcu1.ino Normal file
View 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---------------------------------
}

928
old/nodmcu2/nodmcu2.ino Normal file
View File

@@ -0,0 +1,928 @@
// 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);
// }

196
old/reciver01/reciver01.ino Normal file
View File

@@ -0,0 +1,196 @@
#include <RCSwitch.h>
RCSwitch rx = RCSwitch();
// باید با فرستنده یکسان باشد
const String PRIVATE_KEY = "as23f";
// دستگاه‌های مجاز
const String ALLOWED_DEVICES[] = {"dr142", "abcde", "fghij"};
const int NUM_ALLOWED_DEVICES = 3;
// بافرهای دریافت
String receivedKey = "";
String receivedHex = "";
// کنترل فریم
bool receivingPublicKey = true;
int receivedChars = 0;
unsigned long lastReceiveTime = 0;
const unsigned long TIMEOUT_MS = 500;
// طول متن اصلی انتظار می‌رود 25 باشد
const int EXPECTED_PLAIN_LEN = 25;
// XOR ساده
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;
}
bool isDeviceAllowed(const String &deviceId) {
for (int i = 0; i < NUM_ALLOWED_DEVICES; i++) {
if (deviceId == ALLOWED_DEVICES[i]) return true;
}
return false;
}
static inline bool isHexChar(char c) {
return (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'F') ||
(c >= 'a' && c <= 'f');
}
void resetFrame() {
receivedKey = "";
receivedHex = "";
receivingPublicKey = true;
receivedChars = 0;
}
void setup() {
Serial.begin(9600);
rx.enableReceive(0); // interrupt 0 → پایه 2 در آردوینو UNO
rx.setProtocol(1);
rx.setPulseLength(300);
resetFrame();
Serial.println(F("=== Receiver Started ==="));
Serial.print(F("Private Key: ")); Serial.println(PRIVATE_KEY);
Serial.print(F("Allowed IDs: "));
for (int i = 0; i < NUM_ALLOWED_DEVICES; i++) { Serial.print(ALLOWED_DEVICES[i]); Serial.print(' '); }
Serial.println();
}
void loop() {
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;
//Serial.print(F("Received key char: ")); Serial.println(c);
receivedChars++;
if (receivedKey.length() == 5) {
receivingPublicKey = false;
Serial.println(F("Now receiving encrypted HEX data..."));
}
}
} else {
// فقط کاراکتر HEX معتبر ذخیره شود
if (isHexChar(c)) {
receivedHex += c;
//Serial.print(F("Received HEX char: ")); Serial.println(c);
receivedChars++;
} else {
// نویز را رد کن
Serial.print(F("Ignored non-HEX char: 0x"));
Serial.println((int)(uint8_t)c, HEX);
}
}
lastReceiveTime = millis();
}
rx.resetAvailable();
}
// اگر وقفه افتاد، پردازش کن
if (receivedChars >= 5 && (millis() - lastReceiveTime) > TIMEOUT_MS) {
Serial.println(F("=== Processing Data ==="));
Serial.print(F("Received Public Key: ")); Serial.println(receivedKey);
if (receivedKey != PRIVATE_KEY) {
Serial.println(F("Key verification: FAILED"));
resetFrame();
return;
}
Serial.println(F("Key verification: SUCCESS"));
// طول HEX باید زوج باشد (هر دو کاراکتر → یک بایت)
if ((receivedHex.length() % 2) != 0) {
Serial.print(F("Odd HEX length: ")); Serial.println(receivedHex.length());
resetFrame();
return;
}
// تبدیل HEX → بایت‌ها
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;
}
Serial.print(F("Decoded length: ")); Serial.println(decoded.length());
Serial.print(F("Decoded (raw XORed) bytes: "));
for (int i = 0; i < decoded.length(); i++) {
Serial.print((int)(uint8_t)decoded[i]); Serial.print(' ');
}
Serial.println();
// باید دقیقاً به طول 25 بایت باشد
if (decoded.length() != EXPECTED_PLAIN_LEN) {
Serial.println(F("Unexpected decoded length (should be 25)."));
resetFrame();
return;
}
// رمزگشایی
String plain = decryptData(decoded, PRIVATE_KEY);
Serial.print(F("Decrypted Data: ")); Serial.println(plain);
// اعتبارسنجی قالب
if (plain.length() != EXPECTED_PLAIN_LEN ||
plain[5] != 'S' || plain[10] != 'G' ||
plain[15] != 'T' || plain[20] != 'H') {
Serial.println(F("Format check failed."));
resetFrame();
return;
}
String deviceId = plain.substring(0, 5);
Serial.print(F("Device ID: ")); Serial.println(deviceId);
if (!isDeviceAllowed(deviceId)) {
Serial.println(F("Device authorization: FAILED"));
resetFrame();
return;
}
Serial.println(F("Device authorization: SUCCESS"));
// پارس مقادیر
String sSoil = plain.substring(6, 10); // بین S و G
String sGas = plain.substring(11, 15); // بین G و T
String sTemp = plain.substring(16, 20); // بین T و H
String sHum = plain.substring(21, 25); // بعد از H
int soilMoisture = sSoil.toInt();
int gasValue = sGas.toInt();
float temperature = sTemp.toInt() / 10.0;
float humidity = sHum.toInt() / 10.0;
Serial.println(F("=== Authorized Data ==="));
Serial.print(F("Soil Moisture: ")); Serial.print(soilMoisture);
Serial.print(F(" - Gas Value : ")); Serial.print(gasValue);
Serial.print(F(" - Temperature : ")); Serial.print(temperature); Serial.print(F(" C"));
Serial.print(F(" - Humidity : ")); Serial.print(humidity); Serial.println(F(" %"));
Serial.println(F("======================="));
// آماده دریافت بعدی
resetFrame();
}
delay(5);
}

165
old/sender01/sender01.ino Normal file
View File

@@ -0,0 +1,165 @@
#include <RCSwitch.h>
#include <DHT.h>
// پین‌ها
#define DHTPIN 2
#define DHTTYPE DHT11
#define SOIL_MOISTURE_PIN A3
#define GAS_SENSOR_PIN A2
#define RF_TRANSMIT_PIN 9
const float RL = 10.0; // مقاومت Load روی ماژول (کیلو اهم)
float R0 = 50; // بعد از کالیبراسیون مقدار واقعی جایگزین کنید
// متن ثابت و کلیدها
const String PUBLIC_KEY = "as23f";
const String DEVICE_ID = "dr142";
// اشیاء
DHT dht(DHTPIN, DHTTYPE);
RCSwitch tx = RCSwitch();
// زمان‌بندی
unsigned long lastSendTime = 0;
const unsigned long sendInterval = 10000;
// تاخیر بین ارسال هر کاراکتر (ms)
const unsigned int INTER_CHAR_DELAY_MS = 1;
// XOR ساده
String encryptData(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;
}
static inline void sendChar(uint8_t c) {
tx.send((unsigned long)c, 8);
delay(INTER_CHAR_DELAY_MS);
}
static inline void sendHexByte(uint8_t b) {
char hex[3];
sprintf(hex, "%02X", b); // حروف بزرگ
sendChar(hex[0]);
sendChar(hex[1]);
}
void setup() {
Serial.begin(9600);
dht.begin();
tx.enableTransmit(RF_TRANSMIT_PIN);
tx.setProtocol(1);
tx.setPulseLength(300);
tx.setRepeatTransmit(3);
Serial.println(F("=== Transmitter Started ==="));
Serial.print(F("Public Key: ")); Serial.println(PUBLIC_KEY);
Serial.print(F("Device ID : ")); Serial.println(DEVICE_ID);
}
int getSoilMoistureInt() {
int adcValue = analogRead(SOIL_MOISTURE_PIN);
// مقادیر کالیبراسیون: خاک خشک و خاک خیس
const int adcDry = 1023; // خاک کاملاً خشک
const int adcWet = 400; // خاک کاملاً خیس
// محاسبه درصد رطوبت با یک رقم اعشار
float soilPercent = (float)(adcDry - adcValue) / (adcDry - adcWet) * 100.0;
// محدود کردن بین 0 تا 100
if (soilPercent > 100.0) soilPercent = 100.0;
if (soilPercent < 0.0) soilPercent = 0.0;
// ضرب در 10 و تبدیل به عدد صحیح
int soilInt = (int)(soilPercent * 10 + 0.5); // +0.5 برای گرد کردن صحیح
return soilInt;
}
int readGas() {
long sum = 0;
for (int i = 0; i < 5; i++) {
sum += analogRead(GAS_SENSOR_PIN);
delay(10);
}
float adcValue = sum / 5.0; // میانگین ADC
float vrl = (adcValue / 1023.0) * 5.0; // ولتاژ خروجی
if (vrl < 0.001) vrl = 0.001; // جلوگیری از تقسیم بر صفر
float Rs = (5.0 - vrl) * RL / vrl; // مقاومت سنسور (kΩ)
float ratio = Rs / R0; // نسبت Rs/R0
// ثابت‌های تقریبی از دیتاشیت MQ7 (CO curve)
float m = -0.77;
float b = 0.36;
float ppm_log = (log10(ratio) - b) / m;
int ppm = round(pow(10, ppm_log));
return ppm; // خروجی اعشاری
}
void loop() {
// خواندن سنسورها
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
int soilMoisture = getSoilMoistureInt();
int gasValue = readGas(); //analogRead(GAS_SENSOR_PIN);
if (isnan(temperature) || isnan(humidity)) {
temperature = 0; humidity = 0;
}
soilMoisture = constrain(soilMoisture, 0, 9999);
gasValue = constrain(gasValue, 0, 9999);//1023
int tempInt = constrain((int)(temperature * 10), 0, 9999); // چهار رقمی
int humidityInt = constrain((int)(humidity * 10), 0, 9999); // چهار رقمی
// ⚠️ خیلی مهم: ۲۶ بایت برای نَل ترمینیتور
char dataString[26];
// قالب دقیقاً 25 کاراکتر تولید می‌کند: 5 + (S####) + (G####) + (T####) + (H####)
// 5 + 1+4 + 1+4 + 1+4 + 1+4 = 25
int n = snprintf(
dataString, sizeof(dataString),
"%sS%04dG%04dT%04dH%04d",
DEVICE_ID.c_str(), soilMoisture, gasValue, tempInt, humidityInt
);
// اگر بنا به هر دلیلی طول غیرمنتظره شد، همین‌جا خروج
if (n != 25) {
Serial.print(F("Format length unexpected: ")); Serial.println(n);
delay(500);
return;
}
String plain = String(dataString);
String enc = encryptData(plain, PUBLIC_KEY);
if (millis() - lastSendTime >= sendInterval) {
Serial.println(F("=== Data to Send ==="));
Serial.print(F("Original: ")); Serial.println(plain);
Serial.print(F("Length : ")); Serial.println(plain.length());
// 1) ارسال 5 کاراکتر کلید عمومی
for (int i = 0; i < PUBLIC_KEY.length(); i++) {
sendChar((uint8_t)PUBLIC_KEY[i]);
}
// 2) ارسال دیتای رمز شده به صورت HEX (هر بایت → دو کاراکتر)
Serial.print(F("Encrypted HEX: "));
for (int i = 0; i < enc.length(); i++) {
uint8_t b = (uint8_t)enc[i];
char hex[3]; sprintf(hex, "%02X", b);
Serial.print(hex); Serial.print(' ');
sendHexByte(b);
}
Serial.println();
lastSendTime = millis();
}
delay(100);
}

132
old/sender02/sender02.ino Normal file
View File

@@ -0,0 +1,132 @@
#include <RCSwitch.h>
#include <DHT.h>
// پین‌ها
#define DHTPIN 2
#define DHTTYPE DHT11
#define SOIL_MOISTURE_PIN A3
#define GAS_SENSOR_PIN A2
#define RF_TRANSMIT_PIN 9
// متن ثابت و کلیدها
const String PUBLIC_KEY = "as23f";
const String DEVICE_ID = "dr142";
// اشیاء
DHT dht(DHTPIN, DHTTYPE);
RCSwitch tx = RCSwitch();
// زمان‌بندی
unsigned long lastSendTime = 0;
const unsigned long sendInterval = 5000;
// تاخیر بین ارسال هر کاراکتر (ms)
const unsigned int INTER_CHAR_DELAY_MS = 15;
// شمارهٔ بسته (در صورت نیاز بعداً)
uint8_t seqNumber = 0;
// XOR ساده
String encryptData(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;
}
static inline void sendChar(uint8_t c) {
tx.send((unsigned long)c, 8);
delay(INTER_CHAR_DELAY_MS);
}
void setup() {
Serial.begin(9600);
dht.begin();
tx.enableTransmit(RF_TRANSMIT_PIN);
tx.setProtocol(1);
tx.setPulseLength(300);
tx.setRepeatTransmit(3); // کتابخانه هر بایت را 3 بار تکرار می‌کند
Serial.println(F("=== Transmitter Started (with 1-byte checksum) ==="));
Serial.print(F("Public Key: ")); Serial.println(PUBLIC_KEY);
Serial.print(F("Device ID : ")); Serial.println(DEVICE_ID);
randomSeed(analogRead(0)); // برای ایجاد تاخیر تصادفی احتمالی
}
void loop() {
// خواندن سنسورها
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
int soilMoisture = analogRead(SOIL_MOISTURE_PIN);
int gasValue = analogRead(GAS_SENSOR_PIN);
if (isnan(temperature) || isnan(humidity)) {
temperature = 0; humidity = 0;
}
soilMoisture = constrain(soilMoisture, 0, 1023);
gasValue = constrain(gasValue, 0, 1023);
int tempInt = constrain((int)(temperature * 10), 0, 9999); // چهار رقمی
int humidityInt = constrain((int)(humidity * 10), 0, 9999); // چهار رقمی
// قالب دقیقاً 25 کاراکتر تولید می‌کند: 5 + (S####) + (G####) + (T####) + (H####)
char dataString[26];
int n = snprintf(
dataString, sizeof(dataString),
"%sS%04dG%04dT%04dH%04d",
DEVICE_ID.c_str(), soilMoisture, gasValue, tempInt, humidityInt
);
if (n != 25) {
Serial.print(F("Format length unexpected: ")); Serial.println(n);
delay(500);
return;
}
String plain = String(dataString);
String enc = encryptData(plain, PUBLIC_KEY); // length 25, may contain non-printables
if (millis() - lastSendTime >= sendInterval) {
Serial.println(F("=== Data to Send ==="));
Serial.print(F("Plain : ")); Serial.println(plain);
Serial.print(F("Enc : "));
for (int i = 0; i < enc.length(); i++) {
uint8_t b = (uint8_t)enc[i];
if (b < 0x10) Serial.print('0');
Serial.print(b, HEX); Serial.print(' ');
}
Serial.println();
// ساخت packet: [PUBLIC_KEY(5)] + [ENC(25)] + [CHK(1)] => مجموع 31 بایت
const int PACKET_LEN = 31;
uint8_t packet[PACKET_LEN];
// PUBLIC_KEY (5 bytes)
for (int i = 0; i < 5; i++) packet[i] = (uint8_t)PUBLIC_KEY[i];
// ENC (25 bytes)
for (int i = 0; i < 25; i++) packet[5 + i] = (uint8_t)enc[i];
// محاسبه چک‌سام ساده (sum of bytes 0..29) & 0xFF
uint16_t sum = 0;
for (int i = 0; i < 30; i++) sum += packet[i];
packet[30] = (uint8_t)(sum & 0xFF);
// ارسال تمام بایت‌ها متوالی (فقط یک بار، بدون حلقهٔ 3تایی دستی)
for (int i = 0; i < PACKET_LEN; i++) {
sendChar(packet[i]);
}
Serial.print(F("Checksum sent: 0x"));
if (packet[30] < 0x10) Serial.print('0');
Serial.println(packet[30], HEX);
lastSendTime = millis();
}
delay(100);
}

View File

@@ -0,0 +1,307 @@
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_SHT31.h>
#include <DHT.h>
// ==================== پین‌ها ====================
#define SOIL_MOISTURE_PIN PA1
#define DHT11_PIN PA0
#define SHT31_SCL PB6
#define SHT31_SDA PB7
#define OLED_SCL PB10
#define OLED_SDA PB11
// ==================== OLED ====================
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDRESS 0x3C
TwoWire Wire2(OLED_SDA, OLED_SCL);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire2, -1);
// ==================== سنسورها ====================
#define SHT31_ADDRESS 0x44
Adafruit_SHT31 sht31(&Wire);
#define DHTTYPE DHT11
DHT dht(DHT11_PIN, DHTTYPE);
// ==================== کالیبراسیون ====================
#define SOIL_WET_VALUE 520
#define SOIL_DRY_VALUE 800
// ==================== متغیرها ====================
int soilMoisturePercent = 0;
float dht11_temp = 0, dht11_humidity = 0;
float sht31_temp = 0, sht31_humidity = 0;
bool sht31Found = false;
unsigned long lastRead = 0;
// ==================== تنظیمات نمایش ====================
#define LARGE_FONT_SIZE 2 // فونت بزرگ برای اعداد
#define SMALL_FONT_SIZE 1 // فونت کوچک برای متن
void setup() {
Serial1.begin(115200);
delay(100);
Serial1.println("========================================");
Serial1.println("STM32F103 - Three Sensor System");
Serial1.println("========================================");
// I2C1 برای SHT31
Wire.setSDA(SHT31_SDA);
Wire.setSCL(SHT31_SCL);
Wire.begin();
// I2C2 برای OLED
Wire2.begin();
Wire2.setClock(400000);
// سنسورها
pinMode(SOIL_MOISTURE_PIN, INPUT_ANALOG);
dht.begin();
// OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
Serial1.println("OLED initialization failed!");
while (1);
}
Serial1.println("OLED initialized successfully");
// SHT31
sht31Found = sht31.begin(SHT31_ADDRESS);
if (!sht31Found) {
sht31Found = sht31.begin(0x45); // تست آدرس دیگر
}
if (sht31Found) {
Serial1.println("SHT31 found!");
} else {
Serial1.println("SHT31 not found!");
}
displayStartup();
}
void loop() {
if (millis() - lastRead >= 2000) {
lastRead = millis();
readSensors();
updateDisplay();
printSerial();
}
delay(100);
}
void readSensors() {
// خاک - میانگین‌گیری برای دقت بیشتر
long soilSum = 0;
for (int i = 0; i < 5; i++) {
soilSum += analogRead(SOIL_MOISTURE_PIN);
delay(1);
}
int soilRaw = soilSum / 5;
if (soilRaw <= SOIL_WET_VALUE) {
soilMoisturePercent = 100;
} else if (soilRaw >= SOIL_DRY_VALUE) {
soilMoisturePercent = 0;
} else {
soilMoisturePercent = map(soilRaw, SOIL_DRY_VALUE, SOIL_WET_VALUE, 0, 100);
}
soilMoisturePercent = constrain(soilMoisturePercent, 0, 100);
// DHT11
dht11_temp = dht.readTemperature();
dht11_humidity = dht.readHumidity();
if (isnan(dht11_temp) || isnan(dht11_humidity)) {
dht11_temp = dht11_humidity = -999;
}
// SHT31
if (sht31Found) {
sht31_temp = sht31.readTemperature();
sht31_humidity = sht31.readHumidity();
if (isnan(sht31_temp) || isnan(sht31_humidity)) {
sht31_temp = sht31_humidity = -999;
}
}
}
void updateDisplay() {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
// ==================== خط اول: خاک ====================
// عنوان خاک
display.setTextSize(SMALL_FONT_SIZE);
display.setCursor(0, 0);
display.print("Soil:");
// مقدار خاک (بزرگ)
display.setTextSize(LARGE_FONT_SIZE);
if (soilMoisturePercent < 10) {
display.setCursor(35, 0);
} else if (soilMoisturePercent < 100) {
display.setCursor(25, 0);
} else {
display.setCursor(15, 0);
}
display.print(soilMoisturePercent);
// علامت درصد
display.setTextSize(SMALL_FONT_SIZE);
display.print("%");
// ==================== خط دوم: DHT11 ====================
// عنوان DHT11
display.setTextSize(SMALL_FONT_SIZE);
display.setCursor(0, 20);
display.print("DHT:");
// دما DHT (بزرگ)
display.setTextSize(LARGE_FONT_SIZE);
if (dht11_temp != -999) {
if (dht11_temp >= 0 && dht11_temp < 10) {
display.setCursor(40, 20);
} else if (dht11_temp >= 10 && dht11_temp < 100) {
display.setCursor(30, 20);
} else {
display.setCursor(20, 20);
}
display.print(dht11_temp, 1);
} else {
display.setCursor(40, 20);
display.print("---");
}
// درجه سانتیگراد
display.setTextSize(SMALL_FONT_SIZE);
display.print("C");
// رطوبت DHT (بزرگ)
display.setTextSize(LARGE_FONT_SIZE);
display.setCursor(80, 20);
if (dht11_humidity != -999) {
if (dht11_humidity < 10) {
display.setCursor(85, 20);
} else if (dht11_humidity < 100) {
display.setCursor(80, 20);
} else {
display.setCursor(75, 20);
}
display.print(dht11_humidity, 0);
} else {
display.print("---");
}
// علامت درصد
display.setTextSize(SMALL_FONT_SIZE);
display.print("%");
// ==================== خط سوم: SHT31 ====================
// عنوان SHT31
display.setTextSize(SMALL_FONT_SIZE);
display.setCursor(0, 40);
display.print("SHT:");
// دما SHT (بزرگ)
display.setTextSize(LARGE_FONT_SIZE);
if (sht31Found && sht31_temp != -999) {
if (sht31_temp >= 0 && sht31_temp < 10) {
display.setCursor(40, 40);
} else if (sht31_temp >= 10 && sht31_temp < 100) {
display.setCursor(30, 40);
} else {
display.setCursor(20, 40);
}
display.print(sht31_temp, 1);
} else {
display.setCursor(40, 40);
display.print("---");
}
// درجه سانتیگراد
display.setTextSize(SMALL_FONT_SIZE);
display.print("C");
// رطوبت SHT (بزرگ)
display.setTextSize(LARGE_FONT_SIZE);
if (sht31Found && sht31_humidity != -999) {
if (sht31_humidity < 10) {
display.setCursor(85, 40);
} else if (sht31_humidity < 100) {
display.setCursor(80, 40);
} else {
display.setCursor(75, 40);
}
display.print(sht31_humidity, 0);
} else {
display.setCursor(80, 40);
display.print("---");
}
// علامت درصد
display.setTextSize(SMALL_FONT_SIZE);
display.print("%");
// ==================== خط جداساز ====================
display.drawFastHLine(0, 15, 128, SSD1306_WHITE); // بین خاک و DHT
display.drawFastHLine(0, 35, 128, SSD1306_WHITE); // بین DHT و SHT
display.display();
}
void printSerial() {
Serial1.print("Time: ");
Serial1.print(millis() / 1000);
Serial1.print("s | ");
Serial1.print("Soil: ");
Serial1.print(soilMoisturePercent);
Serial1.print("% | ");
Serial1.print("DHT: ");
if (dht11_temp != -999) {
Serial1.print(dht11_temp, 1);
Serial1.print("C ");
Serial1.print(dht11_humidity, 0);
Serial1.print("%");
} else {
Serial1.print("---");
}
Serial1.print(" | ");
Serial1.print("SHT: ");
if (sht31Found && sht31_temp != -999) {
Serial1.print(sht31_temp, 1);
Serial1.print("C ");
Serial1.print(sht31_humidity, 0);
Serial1.print("%");
} else {
Serial1.print("---");
}
Serial1.println();
}
void displayStartup() {
display.clearDisplay();
// نمایش عنوان
display.setTextSize(LARGE_FONT_SIZE);
display.setCursor(15, 10);
display.println("SENSOR");
display.setCursor(20, 30);
display.println("SYSTEM");
display.setTextSize(SMALL_FONT_SIZE);
display.setCursor(40, 50);
display.println("READY");
display.display();
delay(1500);
}

123
old/test1/test1.ino Normal file
View File

@@ -0,0 +1,123 @@
#include <RCSwitch.h>
#include <DHT.h>
// پین‌ها
#define DHTPIN 2
#define DHTTYPE DHT11
#define SOIL_MOISTURE_PIN A0
#define GAS_SENSOR_PIN A1
#define RF_TRANSMIT_PIN 9
// متن ثابت و کلیدها
const String PUBLIC_KEY = "as23f";
const String DEVICE_ID = "dr142";
// اشیاء
DHT dht(DHTPIN, DHTTYPE);
RCSwitch tx = RCSwitch();
// زمان‌بندی
unsigned long lastSendTime = 0;
const unsigned long sendInterval = 5000;
// تاخیر بین ارسال هر کاراکتر (ms)
const unsigned int INTER_CHAR_DELAY_MS = 15;
// XOR ساده
String encryptData(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;
}
static inline void sendChar(uint8_t c) {
tx.send((unsigned long)c, 8);
delay(INTER_CHAR_DELAY_MS);
}
static inline void sendHexByte(uint8_t b) {
char hex[3];
sprintf(hex, "%02X", b); // حروف بزرگ
sendChar(hex[0]);
sendChar(hex[1]);
}
void setup() {
Serial.begin(9600);
dht.begin();
tx.enableTransmit(RF_TRANSMIT_PIN);
tx.setProtocol(1);
tx.setPulseLength(300);
tx.setRepeatTransmit(3);
Serial.println(F("=== Transmitter Started ==="));
Serial.print(F("Public Key: ")); Serial.println(PUBLIC_KEY);
Serial.print(F("Device ID : ")); Serial.println(DEVICE_ID);
}
void loop() {
// خواندن سنسورها
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
int soilMoisture = analogRead(SOIL_MOISTURE_PIN);
int gasValue = analogRead(GAS_SENSOR_PIN);
if (isnan(temperature) || isnan(humidity)) {
temperature = 0; humidity = 0;
}
soilMoisture = constrain(soilMoisture, 0, 1023);
gasValue = constrain(gasValue, 0, 1023);
int tempInt = constrain((int)(temperature * 10), 0, 9999); // چهار رقمی
int humidityInt = constrain((int)(humidity * 10), 0, 9999); // چهار رقمی
// ⚠️ خیلی مهم: ۲۶ بایت برای نَل ترمینیتور
char dataString[26];
// قالب دقیقاً 25 کاراکتر تولید می‌کند: 5 + (S####) + (G####) + (T####) + (H####)
// 5 + 1+4 + 1+4 + 1+4 + 1+4 = 25
int n = snprintf(
dataString, sizeof(dataString),
"%sS%04dG%04dT%04dH%04d",
DEVICE_ID.c_str(), soilMoisture, gasValue, tempInt, humidityInt
);
// اگر بنا به هر دلیلی طول غیرمنتظره شد، همین‌جا خروج
if (n != 25) {
Serial.print(F("Format length unexpected: ")); Serial.println(n);
delay(500);
return;
}
String plain = String(dataString);
String enc = encryptData(plain, PUBLIC_KEY);
if (millis() - lastSendTime >= sendInterval) {
Serial.println(F("=== Data to Send ==="));
Serial.print(F("Original: ")); Serial.println(plain);
Serial.print(F("Length : ")); Serial.println(plain.length());
// 1) ارسال 5 کاراکتر کلید عمومی
for (int i = 0; i < PUBLIC_KEY.length(); i++) {
sendChar((uint8_t)PUBLIC_KEY[i]);
}
// 2) ارسال دیتای رمز شده به صورت HEX (هر بایت → دو کاراکتر)
Serial.print(F("Encrypted HEX: "));
for (int i = 0; i < enc.length(); i++) {
uint8_t b = (uint8_t)enc[i];
char hex[3]; sprintf(hex, "%02X", b);
Serial.print(hex); Serial.print(' ');
sendHexByte(b);
}
Serial.println();
lastSendTime = millis();
}
delay(100);
}

148
old/test2/test2.ino Normal file
View File

@@ -0,0 +1,148 @@
#include <SoftwareSerial.h>
#define RX_PIN D3 // RX NodeMCU -> TX M66
#define TX_PIN D4 // TX NodeMCU -> RX M66
SoftwareSerial M66(RX_PIN, TX_PIN);
const String url = "http://amlakmodaberan.ir/1.html";
// =========================
// Timeout ها
// =========================
const unsigned long AT_TIMEOUT = 15000;
const unsigned long HTTP_TIMEOUT = 30000;
// =========================
// ارسال دستور AT با انتظار پاسخ
// =========================
bool sendAT(String cmd, String expected = "OK", unsigned long timeout = AT_TIMEOUT) {
Serial.print("[TX] "); Serial.println(cmd);
while (M66.available()) M66.read(); // پاکسازی بافر
M66.println(cmd);
unsigned long start = millis();
String response = "";
while (millis() - start < timeout) {
yield();
while (M66.available()) {
char c = M66.read();
response += c;
Serial.write(c);
if (response.indexOf(expected) != -1) return true;
if (response.indexOf("ERROR") != -1 || response.indexOf("+CME ERROR") != -1) {
Serial.println("❌ Command failed");
return false;
}
}
}
Serial.println("⏱ Timeout waiting for response");
return false;
}
// =========================
// راه‌اندازی GPRS و HTTP با QIREGAPP
// =========================
bool initGPRS() {
Serial.println("=== Initializing GPRS ===");
delay(2000);
sendAT("ATE0");
sendAT("AT+CMEE=2");
sendAT("AT+CFUN=1");
delay(2000);
if (!sendAT("AT+CPIN?", "READY")) return false;
// شبکه ثبت شده
for (int i = 0; i < 10; i++) {
if (sendAT("AT+CREG?", "+CREG: 0,1")) break;
delay(2000);
}
if (!sendAT("AT+CGATT=1")) return false;
delay(5000);
// reset context
sendAT("AT+QIFGCNT=0");
if (!sendAT("AT+QICSGP=1,\"mcinet\",\"\",\"\"")) return false;
// فعال کردن QIREGAPP به جای QIACT
if (!sendAT("AT+QIREGAPP", "OK", 30000)) return false;
// بررسی IP state
sendAT("AT+QISTAT", "STATE: IP GPRSACT");
// پیکربندی HTTP
sendAT("AT+QHTTPCFG=\"contextid\",1");
sendAT("AT+QHTTPCFG=\"responseheader\",1");
sendAT("AT+QHTTPCFG=\"timeout\",30000");
sendAT("AT+QHTTPCFG=\"requestheader\",1");
Serial.println("✅ GPRS ready!");
return true;
}
// =========================
// ارسال HTTP GET
// =========================
bool sendHTTPRequest() {
Serial.println("=== Sending HTTP Request ===");
int len = url.length();
if (!sendAT("AT+QHTTPURL=" + String(len) + ",60", "CONNECT", 15000)) {
Serial.println("❌ URL setup failed");
return false;
}
M66.print(url);
delay(200);
M66.write(0x1A); // Ctrl+Z
delay(1500);
if (!sendAT("AT+QHTTPGET=80", "OK", HTTP_TIMEOUT)) {
Serial.println("❌ HTTP GET failed");
return false;
}
if (!sendAT("AT+QHTTPREAD", "+QHTTPREAD:", HTTP_TIMEOUT)) {
Serial.println("⚠️ No HTTP response body");
return false;
}
Serial.println("✅ HTTP request done!");
return true;
}
// =========================
// setup
// =========================
void setup() {
Serial.begin(115200);
M66.begin(9600);
delay(2000);
int retry = 0;
while (retry < 3) {
if (initGPRS()) {
if (sendHTTPRequest()) break;
else Serial.println("⚠️ HTTP failed, retrying...");
} else {
Serial.println("❌ GPRS init failed, retrying...");
}
retry++;
delay(5000);
}
}
// =========================
// loop
// =========================
void loop() {
yield(); // جلوگیری از WDT
}

116
old/teststm03/teststm03.ino Normal file
View File

@@ -0,0 +1,116 @@
/*
MQ-7 Module - با ضرایب صحیح
*/
#include <Arduino.h>
const uint8_t ADC_PIN = PA2;
const uint8_t LED_PIN = PC13;
const float ADC_REF = 3.3;
const uint32_t ADC_MAX = 4095;
const float RL = 10000.0;
float R0 = -1.0;
const float CO_THRESHOLD = 50.0;
// ضرایب صحیح برای MQ-7 و گاز CO
const float CO_A = 99.042;
const float CO_B = -1.518;
float readVoltage() {
const int N = 10;
uint32_t sum = 0;
for (int i = 0; i < N; i++) {
sum += analogRead(ADC_PIN);
delay(3);
}
float adc_avg = sum / float(N);
return adc_avg * (ADC_REF / ADC_MAX);
}
float calcRs(float Vout) {
if (Vout <= 0.001) return 1e6;
// فرمول اصلاح شده: ما Vout را از سنسور می‌خوانیم که نسبت به 5V داخلی ماژول است
// بنابراین باید از 5.0 در فرمول استفاده کنیم، نه ADC_REF
return RL * (5.0 - Vout) / Vout; // این فرمول صحیح است
}
float toRealPPM(float Rs) {
if (R0 <= 0) return -1;
float ratio = Rs / R0;
// فرمول صحیح براساس دیتاشیت MQ-7
float ppm = CO_A * pow(ratio, CO_B);
return ppm;
}
void calibrateR0() {
Serial1.println("=== Calibrating R0 in clean air ===");
const int N = 50;
float sum_R0 = 0;
for (int i = 0; i < N; i++) {
float V = readVoltage();
float Rs = calcRs(V);
// در هوای پاک: نسبت Rs/R0 ≈ 27.5 (طبق دیتاشیت)
// بنابراین: R0 = Rs / 27.5
sum_R0 += Rs / 27.5;
delay(100);
}
R0 = sum_R0 / N;
Serial1.print("Calibration complete. R0 = ");
Serial1.println(R0, 2);
}
void setup() {
Serial1.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
Serial1.println("MQ-7 CO Monitor - Waiting for warm up...");
delay(30000); // 30 ثانیه پیش‌گرمایش
Serial1.println("Ready. Send 'c' to calibrate.");
calibrateR0();
}
void loop() {
if (Serial1.available()) {
char c = Serial1.read();
if (c == 'c' || c == 'C') calibrateR0();
}
float V = readVoltage();
float Rs = calcRs(V);
float ppm = toRealPPM(Rs);
Serial1.print("Vout=");
Serial1.print(V, 3);
Serial1.print("V, Rs=");
Serial1.print(Rs, 1);
Serial1.print("Ω");
if (R0 > 0) {
Serial1.print(", Rs/R0=");
Serial1.print(Rs/R0, 3);
Serial1.print(", CO=");
Serial1.print(ppm, 1);
Serial1.println("ppm");
// هشدار
if (ppm > CO_THRESHOLD) {
digitalWrite(LED_PIN, HIGH);
Serial1.println("⚠️ WARNING: High CO level!");
} else {
digitalWrite(LED_PIN, LOW);
}
} else {
Serial1.println(" [Not calibrated]");
}
delay(2000);
}

212
old/wifi1/wifi1.ino Normal file
View File

@@ -0,0 +1,212 @@
#include <SoftwareSerial.h>
#include <Wire.h>
#include "RTClib.h"
#include <SPI.h>
#include <SD.h>
// تعریف نام مستعار برای حل مشکل تداخل
using SDLibFile = File;
SoftwareSerial espSerial(4, 5);
const int chipSelect = 10;
RTC_DS3231 rtc;
// کاهش مصرف حافظه با استفاده از PROGMEM
const char daysOfTheWeek[7][4] PROGMEM = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
// پیش‌تعریف توابع
bool saveDataToFile(const char* filename, const char* data, bool append = true);
bool formatSDCard();
void sendCommand(const char* cmd, int delayTime);
void sendHTTPResponse(int connectionId, const char* content);
const char* readSensorData();
const char* handleSaveData(const char* query);
const char* handleReadData(const char* query);
const char* handleFormatCard();
void setup() {
Serial.begin(9600);
// راه اندازی کارت SD
if (!SD.begin(chipSelect)) {
Serial.println(F("SD Card Error!"));
} else {
Serial.println(F("SD Card OK"));
}
espSerial.begin(9600);
// راه اندازی RTC
if (!rtc.begin()) {
Serial.println(F("RTC Error!"));
while (1);
}
if (rtc.lostPower()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
delay(1000);
Serial.println(F("Initializing ESP..."));
// کاهش دستورات ESP
const char* commands[] = {
"AT", "AT+RST", "AT+CWMODE=2",
"AT+CWSAP=\"ESP_CTRL\",\"12345678\",1,3",
"AT+CIPMUX=1", "AT+CIPSERVER=1,80"
};
for (int i = 0; i < 6; i++) {
sendCommand(commands[i], 1000);
}
Serial.println(F("AP Ready!"));
}
void loop() {
checkESP();
delay(1000);
}
void sendCommand(const char* cmd, int delayTime) {
Serial.print(F("Sending: "));
Serial.println(cmd);
espSerial.println(cmd);
delay(delayTime);
}
void sendHTTPResponse(int connectionId, const char* content) {
char response[512];
snprintf(response, sizeof(response),
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n%s",
content);
char cmd[50];
snprintf(cmd, sizeof(cmd), "AT+CIPSEND=%d,%d", connectionId, strlen(response));
sendCommand(cmd, 50);
sendCommand(response, 100);
snprintf(cmd, sizeof(cmd), "AT+CIPCLOSE=%d", connectionId);
sendCommand(cmd, 50);
}
void checkESP() {
static char buffer[128];
static int index = 0;
while (espSerial.available()) {
char c = espSerial.read();
if (index < sizeof(buffer) - 1) {
buffer[index++] = c;
}
if (c == '\n') {
buffer[index] = '\0';
if (strstr(buffer, "+IPD") != NULL) {
int connId = atoi(&buffer[5]);
handleRequest(connId, buffer);
}
index = 0;
}
}
}
void handleRequest(int connectionId, char* request) {
char* getStart = strstr(request, "GET /");
if (!getStart) return;
char* pathStart = getStart + 5;
char* pathEnd = strchr(pathStart, ' ');
if (!pathEnd) return;
char path[32] = {0};
strncpy(path, pathStart, min((int)(pathEnd - pathStart), 31));
char* query = strchr(path, '?');
if (query) {
*query = '\0';
query++;
}
Serial.print(F("Path: "));
Serial.println(path);
const char* response;
if (strcmp(path, "se") == 0) {
response = readSensorData();
}
else if (strcmp(path, "save") == 0) {
response = handleSaveData(query);
}
else if (strcmp(path, "format") == 0) {
response = handleFormatCard();
}
else {
response = "Available endpoints: /se, /save, /format";
}
sendHTTPResponse(connectionId, response);
}
const char* readSensorData() {
static char buffer[64];
DateTime now = rtc.now();
snprintf(buffer, sizeof(buffer), "Temp: %.1fC, Time: %02d:%02d:%02d",
rtc.getTemperature(), now.hour(), now.minute(), now.second());
return buffer;
}
const char* handleSaveData(const char* query) {
if (query && strlen(query) > 0) {
if (saveDataToFile("data.txt", query, true)) {
return "Data saved";
}
return "Save error";
}
return "No data";
}
const char* handleFormatCard() {
return formatSDCard() ? "Formatted" : "Format error";
}
bool saveDataToFile(const char* filename, const char* data, bool append) {
SDLibFile dataFile;
if (append) {
dataFile = SD.open(filename, FILE_WRITE);
} else {
if (SD.exists(filename)) {
SD.remove(filename);
}
dataFile = SD.open(filename, FILE_WRITE);
}
if (!dataFile) return false;
bool result = dataFile.println(data);
dataFile.close();
return result;
}
bool formatSDCard() {
// فقط حذف فایل‌های اصلی
const char* files[] = {"data.txt", "test.txt"};
for (int i = 0; i < 2; i++) {
if (SD.exists(files[i])) {
SD.remove(files[i]);
}
}
// تست نوشتن
return saveDataToFile("test.txt", "Formatted", false);
}