fix errot

This commit is contained in:
2026-01-06 19:08:38 +03:30

785
stm32f103.ino Normal file
View File

@@ -0,0 +1,785 @@
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_SHT31.h>
#include <BH1750.h>
#include <SPI.h>
// -------------------- پیکربندی --------------------
#define FLASH_CS PA4
#define FLASH_MOSI PA7
#define FLASH_MISO PA6
#define FLASH_SCK PA5
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SOIL_PIN PA1
#define MQ7_PIN PA2
#define SDA_PIN PB9
#define SCL_PIN PB8
#define PWRKEY_PIN PB5
#define SENSOR_READ_INTERVAL 60000
// -------------------- آدرس‌های حافظه --------------------
#define CONFIG_ADDRESS 0x000000
#define DATA_ADDRESS 0x010000
#define MAX_STORED_DATA 100
// -------------------- انوم‌ها --------------------
enum SIMType { SIM_UNKNOWN = 0, SIM_HAMRAHE_AVAL = 1, SIM_IRANCELL = 2, SIM_RIGHTEL = 3 };
// -------------------- ساختارها --------------------
struct DeviceConfig {
char signature[4];
char deviceId[16];
char serverPhoneNumber[16];
char serverUrl[64];
unsigned long uploadInterval;
unsigned long smsInterval;
unsigned long saveInterval;
SIMType simType;
bool smsEnabled;
bool verified;
bool valid;
};
struct SensorData {
float temperature;
float humidity;
int soilMoisture;
float coPPM;
float lightLux;
unsigned long timestamp;
uint8_t sent;
};
// -------------------- متغیرهای سراسری --------------------
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
BH1750 lightMeter;
Adafruit_SHT31 sht31;
HardwareSerial EC200U(USART3);
SPIClass flashSPI(FLASH_MOSI, FLASH_MISO, FLASH_SCK);
DeviceConfig config;
SensorData currentData;
SensorData storedData[MAX_STORED_DATA];
// متغیرهای موقت برای پردازش پیامک‌ها
String tempPhoneNumber = "";
String tempTokenCode = "";
bool awaitingSMS2 = false;
unsigned long lastSensorRead = 0;
unsigned long lastUpload = 0;
unsigned long lastSMS = 0;
unsigned long lastSave = 0;
unsigned long lastDisplayChange = 0;
unsigned long lastReconnectAttempt = 0;
bool networkConnected = false;
int displayMode = 0;
int connectionAttempts = 0;
String lastError = "";
String currentAPN = "";
String deviceUID = "";
String lastMessage = "";
int storedDataCount = 0;
// -------------------- توابع حافظه Flash --------------------
void flashInit() {
pinMode(FLASH_CS, OUTPUT);
digitalWrite(FLASH_CS, HIGH);
flashSPI.begin();
flashSPI.setClockDivider(SPI_CLOCK_DIV4);
delay(100);
}
bool flashIsBusy() {
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x05);
uint8_t status = flashSPI.transfer(0);
digitalWrite(FLASH_CS, HIGH);
return (status & 0x01);
}
void flashWaitForReady() {
while (flashIsBusy()) delay(1);
}
void flashRead(uint32_t addr, uint8_t *data, uint32_t len) {
flashWaitForReady();
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x03);
flashSPI.transfer((addr >> 16) & 0xFF);
flashSPI.transfer((addr >> 8) & 0xFF);
flashSPI.transfer(addr & 0xFF);
for (uint32_t i = 0; i < len; i++) data[i] = flashSPI.transfer(0);
digitalWrite(FLASH_CS, HIGH);
}
void flashWrite(uint32_t addr, uint8_t *data, uint32_t len) {
uint32_t offset = 0;
while (offset < len) {
// Write Enable
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x06);
digitalWrite(FLASH_CS, HIGH);
uint32_t pageOffset = addr % 256;
uint32_t remaining = 256 - pageOffset;
uint32_t writeSize = min(remaining, len - offset);
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x02);
flashSPI.transfer((addr >> 16) & 0xFF);
flashSPI.transfer((addr >> 8) & 0xFF);
flashSPI.transfer(addr & 0xFF);
for (uint32_t i = 0; i < writeSize; i++) {
flashSPI.transfer(data[offset + i]);
}
digitalWrite(FLASH_CS, HIGH);
flashWaitForReady();
addr += writeSize;
offset += writeSize;
}
}
void flashSectorErase(uint32_t addr) {
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x06);
digitalWrite(FLASH_CS, HIGH);
digitalWrite(FLASH_CS, LOW);
flashSPI.transfer(0x20);
flashSPI.transfer((addr >> 16) & 0xFF);
flashSPI.transfer((addr >> 8) & 0xFF);
flashSPI.transfer(addr & 0xFF);
digitalWrite(FLASH_CS, HIGH);
flashWaitForReady();
}
// -------------------- توابع مدیریت داده --------------------
void readConfig() {
uint8_t buffer[sizeof(DeviceConfig)];
flashRead(CONFIG_ADDRESS, buffer, sizeof(DeviceConfig));
memcpy(&config, buffer, sizeof(DeviceConfig));
if (strcmp(config.signature, "CFG") != 0) {
strcpy(config.signature, "CFG");
strcpy(config.deviceId, "");
strcpy(config.serverPhoneNumber, "");
strcpy(config.serverUrl, "https://ghback.nabaksoft.ir");
config.uploadInterval = 5;
config.smsInterval = 10;
config.saveInterval = 60;
config.simType = SIM_UNKNOWN;
config.smsEnabled = false;
config.verified = false;
config.valid = false;
saveConfig();
}
}
void saveConfig() {
config.valid = true;
flashSectorErase(CONFIG_ADDRESS);
uint8_t buffer[sizeof(DeviceConfig)];
memcpy(buffer, &config, sizeof(DeviceConfig));
flashWrite(CONFIG_ADDRESS, buffer, sizeof(DeviceConfig));
Serial1.println("✅ Config saved to Flash");
}
// -------------------- توابع کمکی --------------------
String simTypeToString(SIMType type) {
switch(type) {
case SIM_HAMRAHE_AVAL: return "HA";
case SIM_IRANCELL: return "IR";
case SIM_RIGHTEL: return "RT";
default: return "UK";
}
}
String simTypeToAPN(SIMType type) {
switch(type) {
case SIM_HAMRAHE_AVAL: return "mcinet";
case SIM_IRANCELL: return "mtnirancell";
case SIM_RIGHTEL: return "rightel";
default: return "mcinet";
}
}
String generateVerificationCode(String tokenCode) {
long token = tokenCode.toInt();
long verification = (token * 7 + 12345) % 100000;
char buffer[6];
sprintf(buffer, "%05ld", verification);
Serial1.println("=== Code Calculation ===");
Serial1.println("Token: " + tokenCode);
Serial1.println("Result: " + String(verification));
Serial1.println("Formatted: " + String(buffer));
Serial1.println("========================");
return String(buffer);
}
// -------------------- پردازش SMS --------------------
// استخراج همه اعداد از متن پیامک (برای SMS1 و SMS2)
void extractNumbersFromSMS2(String message, String numbers[], int &count) {
count = 0;
String current = "";
for (int i = 0; i < message.length(); i++) {
char c = message.charAt(i);
if (c >= '0' && c <= '9') {
current += c;
} else {
if (current.length() > 0) {
if (count < 10) {
numbers[count] = current;
count++;
}
current = "";
}
}
}
if (current.length() > 0 && count < 10) {
numbers[count] = current;
count++;
}
}
// تشخیص نوع پیامک (SMS1 یا SMS2) بر اساس تعداد اعداد
int detectSMSType(String message) {
int count = 0;
String current = "";
for (int i = 0; i < message.length(); i++) {
char c = message.charAt(i);
if (c >= '0' && c <= '9') {
current += c;
} else {
if (current.length() > 0) {
count++;
current = "";
}
}
}
if (current.length() > 0) {
count++;
}
// اگر حداقل ۵ تا عدد داشته باشیم، SMS2 فرض می‌کنیم
return (count >= 5) ? 2 : 1;
}
// -------------------- توابع EC200U --------------------
bool sendAT(String cmd, uint16_t wait = 2000, bool showResponse = true) {
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;
if (showResponse) Serial1.write(c);
}
if (response.indexOf("OK") != -1) return true;
if (response.indexOf("ERROR") != -1) return false;
}
return false;
}
bool sendSMS(String number, String message) {
if (!sendAT("AT+CMGF=1", 3000, true)) {
Serial1.println("❌ Failed to set SMS mode");
return false;
}
delay(200);
EC200U.print("AT+CMGS=\"");
EC200U.print(number);
EC200U.println("\"");
String response = "";
unsigned long start = millis();
bool gotPrompt = false;
while (millis() - start < 5000) {
while (EC200U.available()) {
char c = EC200U.read();
response += c;
Serial1.write(c);
if (response.indexOf(">") != -1) {
gotPrompt = true;
break;
}
}
if (gotPrompt) break;
}
if (!gotPrompt) {
Serial1.println("❌ No prompt received");
return false;
}
EC200U.print(message);
EC200U.write(26);
response = "";
start = millis();
bool gotResponse = false;
while (millis() - start < 15000) {
while (EC200U.available()) {
char c = EC200U.read();
response += c;
Serial1.write(c);
if (response.indexOf("+CMGS:") != -1) {
gotResponse = true;
break;
}
if (response.indexOf("ERROR") != -1) return false;
}
if (gotResponse) break;
}
if (!gotResponse) return false;
delay(1000);
sendAT("AT+CMGD=1,4", 3000, true);
return true;
}
// -------------------- پردازش پیامک‌ها --------------------
void processSMS1(String message) {
Serial1.println("🔐 PROCESSING SMS1 (Verification)");
// فقط اعداد را به‌ترتیب از متن استخراج می‌کنیم
String numbers[10];
int count = 0;
extractNumbersFromSMS2(message, numbers, count);
String phone = "";
String token = "";
if (count >= 2) {
phone = numbers[0]; // شماره تلفن (عدد اول)
token = numbers[1]; // کد ورود (عدد دوم)
}
Serial1.println("Phone: " + phone);
Serial1.println("Token: " + token);
if (phone.length() >= 10 && token.length() == 5) {
tempPhoneNumber = phone;
tempTokenCode = token;
String verifyCode = generateVerificationCode(token);
String reply = "Code: " + verifyCode;
if (sendSMS(phone, reply)) {
Serial1.println("✅ Verification code sent");
awaitingSMS2 = true;
lastMessage = "Code Sent";
Serial1.println("⏳ Now awaiting SMS2...");
Serial1.println("tempPhone: " + tempPhoneNumber);
Serial1.println("tempToken: " + tempTokenCode);
}
}
}
void processSMS2(String message) {
Serial1.println("⚙️ PROCESSING SMS2 (Configuration)");
String numbers[10];
int count = 0;
extractNumbersFromSMS2(message, numbers, count);
Serial1.print("Numbers: ");
for (int i = 0; i < count; i++) {
Serial1.print(numbers[i] + " ");
}
Serial1.println("(Count: " + String(count) + ")");
// پیامک دوم معمولاً ۵ یا ۶ عدد دارد
// ترتیب: [0]=نابک, [1]=اینتروال, [2]=پیامک, [3]=سیستم(بی‌استفاده), [4]=سیم, [5]=لغو(بی‌استفاده)
if (count >= 5) {
// 1. Device ID (نابک) - عدد اول
String deviceId = numbers[0];
if (deviceId.length() > 2) {
deviceId = deviceId.substring(0, deviceId.length() - 2);
}
deviceId.toCharArray(config.deviceId, 16);
Serial1.println("✓ Device ID: " + String(config.deviceId));
// 2. Upload Interval (اینتروال) - عدد دوم
if (numbers[1].length() > 1) {
config.uploadInterval = numbers[1].substring(1).toInt();
} else {
config.uploadInterval = numbers[1].toInt();
}
Serial1.println("✓ Upload Interval: " + String(config.uploadInterval));
// 3. SMS Settings (پیامک) - عدد سوم
if (numbers[2].length() >= 2) {
config.smsEnabled = (numbers[2].charAt(0) == '1');
config.smsInterval = numbers[2].substring(1).toInt();
} else {
config.smsEnabled = false;
config.smsInterval = 0;
}
Serial1.println("✓ SMS: " + String(config.smsEnabled ? "ON" : "OFF") + ", Interval: " + String(config.smsInterval));
// 4. عدد چهارم (سیستم) - بی‌استفاده است، رد می‌شود
Serial1.println("✓ System ID (ignored): " + numbers[3]);
// 5. SIM Type (سیم) - عدد پنجم (یا اگر ۶ تا بود، پنجم)
int simIndex = (count >= 6) ? 4 : (count - 1); // اگر ۶ تا بود index=4، وگرنه آخری
String simValue = numbers[simIndex];
// دو رقم آخر را بگیر
if (simValue.length() >= 2) {
String simCode = simValue.substring(simValue.length() - 2);
Serial1.println("✓ SIM Code: " + simCode);
if (simCode == "21") config.simType = SIM_HAMRAHE_AVAL;
else if (simCode == "22") config.simType = SIM_IRANCELL;
else if (simCode == "23") config.simType = SIM_RIGHTEL;
else if (simCode == "71") config.simType = SIM_HAMRAHE_AVAL;
else config.simType = SIM_UNKNOWN;
} else {
// اگر کمتر از ۲ رقم بود، خود عدد را چک کن
int simInt = simValue.toInt();
if (simInt == 21 || simInt == 71) config.simType = SIM_HAMRAHE_AVAL;
else if (simInt == 22) config.simType = SIM_IRANCELL;
else if (simInt == 23) config.simType = SIM_RIGHTEL;
else config.simType = SIM_UNKNOWN;
}
// 6. شماره تلفن (از SMS1 یا اگر نبود، خالی می‌ماند)
if (tempPhoneNumber.length() > 0) {
tempPhoneNumber.toCharArray(config.serverPhoneNumber, 16);
Serial1.println("✓ Server Phone: " + tempPhoneNumber);
} else {
strcpy(config.serverPhoneNumber, "");
Serial1.println("⚠️ No phone number (SMS1 was skipped)");
}
// 7. تایید نهایی
config.verified = true;
saveConfig();
currentAPN = simTypeToAPN(config.simType);
awaitingSMS2 = false;
Serial1.println("\n✅✅✅ DEVICE FULLY CONFIGURED! ✅✅✅");
Serial1.println("Device ID: " + String(config.deviceId));
Serial1.println("Phone: " + String(config.serverPhoneNumber));
Serial1.println("Upload Interval: " + String(config.uploadInterval) + " minutes");
Serial1.println("SMS Enabled: " + String(config.smsEnabled ? "YES" : "NO"));
Serial1.println("SMS Interval: " + String(config.smsInterval) + " minutes");
Serial1.println("SIM Type: " + simTypeToString(config.simType));
Serial1.println("APN: " + currentAPN);
Serial1.println("✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅\n");
lastMessage = "Configured";
// اتصال به شبکه
initEC200U();
} else {
Serial1.println("❌ Not enough numbers in SMS2 (need at least 5)");
}
}
void checkSMS() {
if (!sendAT("AT", 2000, false)) return;
sendAT("AT+CMGF=1", 2000, true);
delay(100);
EC200U.println("AT+CMGL=\"REC UNREAD\"");
String response = "";
unsigned long start = millis();
while (millis() - start < 5000) {
while (EC200U.available()) {
char c = EC200U.read();
response += c;
Serial1.write(c);
}
if (response.indexOf("OK") != -1) break;
}
if (response.indexOf("+CMGL:") != -1) {
int msgIndex = response.indexOf("+CMGL:");
int comma1 = response.indexOf(',', msgIndex);
int comma2 = response.indexOf(',', comma1 + 1);
int quote1 = response.indexOf('\"', comma2 + 1);
int quote2 = response.indexOf('\"', quote1 + 1);
if (quote1 != -1 && quote2 != -1) {
int msgStart = response.indexOf('\n', quote2) + 1;
int msgEnd = response.indexOf('\n', msgStart);
if (msgEnd == -1) msgEnd = response.length();
String message = response.substring(msgStart, msgEnd);
message.trim();
Serial1.println("\n📱 NEW SMS: " + message);
Serial1.println("Verified: " + String(config.verified));
Serial1.println("Awaiting SMS2: " + String(awaitingSMS2));
// استخراج اعداد برای دیباگ
String debugNumbers[10];
int debugCount = 0;
extractNumbersFromSMS2(message, debugNumbers, debugCount);
Serial1.print("🔢 Numbers found: ");
for (int i = 0; i < debugCount; i++) {
Serial1.print(debugNumbers[i]);
if (i < debugCount - 1) Serial1.print(", ");
}
Serial1.println(" (Total: " + String(debugCount) + ")");
int smsType = detectSMSType(message);
Serial1.println("📋 SMS Type detected: " + String(smsType));
if (config.verified) {
Serial1.println("⚠️ Already verified, ignoring");
} else if (smsType == 1 && !awaitingSMS2) {
processSMS1(message);
} else if (smsType == 2 && awaitingSMS2) {
// حالت استاندارد: پیامک اول آمده و الان پیامک دوم
processSMS2(message);
} else if (smsType == 2 && !awaitingSMS2 && !config.verified) {
// حالت مستقیم: پیامک دوم بدون پیامک اول (برای تست یا تنظیم سریع)
Serial1.println("⚡ Direct SMS2 (without SMS1) - Processing anyway");
processSMS2(message);
} else {
Serial1.println("❌ Wrong SMS type or state");
Serial1.println(" Expected: SMS type=" + String(awaitingSMS2 ? "2" : "1") + ", Got: " + String(smsType));
}
String indexStr = response.substring(msgIndex + 6, comma1);
indexStr.trim();
sendAT("AT+CMGD=" + indexStr, 2000, true);
}
}
}
// -------------------- اتصال شبکه --------------------
void initEC200U() {
if (!config.verified) return;
Serial1.println("📡 Connecting to network...");
lastMessage = "Connecting";
digitalWrite(PWRKEY_PIN, HIGH);
delay(2000);
digitalWrite(PWRKEY_PIN, LOW);
delay(5000);
if (!sendAT("AT", 3000)) {
Serial1.println("❌ Modem error");
return;
}
currentAPN = simTypeToAPN(config.simType);
String apnCmd = "AT+CGDCONT=1,\"IP\",\"" + currentAPN + "\"";
sendAT(apnCmd, 2000);
if (sendAT("AT+QIACT=1", 15000)) {
networkConnected = true;
lastMessage = "Connected";
Serial1.println("✅ Network connected");
} else {
networkConnected = false;
Serial1.println("❌ Network failed");
}
}
// -------------------- سنسورها --------------------
void readSensors() {
currentData.temperature = sht31.readTemperature();
currentData.humidity = sht31.readHumidity();
// خاک
int adc = analogRead(SOIL_PIN);
float soil = (4095 - adc) / (4095.0 - 1200.0) * 100.0;
if (soil > 100) soil = 100;
if (soil < 0) soil = 0;
currentData.soilMoisture = (int)soil;
// CO
long sum = 0;
for (int i = 0; i < 10; i++) {
sum += analogRead(MQ7_PIN);
delay(5);
}
float voltage = (sum / 10.0 / 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 > 10000) ppm = 10000;
currentData.coPPM = ppm;
// نور
currentData.lightLux = lightMeter.readLightLevel();
if (isnan(currentData.lightLux)) currentData.lightLux = 0;
lastSensorRead = millis();
}
// -------------------- نمایشگر --------------------
void updateDisplay() {
if (millis() - lastDisplayChange > 5000) {
displayMode = (displayMode + 1) % 3;
lastDisplayChange = millis();
}
display.clearDisplay();
display.setTextSize(1);
switch(displayMode) {
case 0:
display.setCursor(0, 0);
if (config.verified) {
display.println("ID: " + String(config.deviceId));
display.println("SIM: " + simTypeToString(config.simType));
display.println("NET: " + String(networkConnected ? "OK" : "NO"));
} else if (awaitingSMS2) {
display.println("AWAITING SMS2");
display.println("Phone: " + tempPhoneNumber);
} else {
display.println("WAIT SMS1");
display.println("Send: NUM#CODE");
}
break;
case 1:
display.setCursor(0, 0);
display.print("T:");
display.print(currentData.temperature, 1);
display.print("C H:");
display.print(currentData.humidity, 1);
display.println("%");
display.print("S:");
display.print(currentData.soilMoisture);
display.print("% C:");
display.print(currentData.coPPM, 0);
display.println("PPM");
display.print("L:");
display.print(currentData.lightLux, 0);
display.println("Lx");
break;
case 2:
display.setTextSize(2);
display.setCursor(0, 20);
display.print(currentData.temperature, 1);
display.println("C");
break;
}
display.display();
}
// -------------------- Setup & Loop --------------------
void setup() {
Serial1.begin(115200);
delay(1000);
Serial1.println("\n\n🚀 IoT Device 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);
flashInit();
readConfig();
// همیشه در شروع awaitingSMS2 = false
awaitingSMS2 = false;
tempPhoneNumber = "";
tempTokenCode = "";
EC200U.begin(115200);
Serial1.println("\n=== INITIAL STATUS ===");
Serial1.println("Verified: " + String(config.verified));
Serial1.println("Awaiting SMS2: " + String(awaitingSMS2));
Serial1.println("=====================");
if (config.verified) {
currentAPN = simTypeToAPN(config.simType);
initEC200U();
}
Serial1.println("✅ Ready");
}
void loop() {
checkSMS();
if (millis() - lastSensorRead > SENSOR_READ_INTERVAL) {
readSensors();
if (config.verified && networkConnected) {
if (millis() - lastUpload > config.uploadInterval * 60000) {
// ارسال داده به API
String data = "deviceId=" + String(config.deviceId) +
"&temp=" + String(currentData.temperature, 1) +
"&hum=" + String(currentData.humidity, 1) +
"&soil=" + String(currentData.soilMoisture) +
"&co=" + String(currentData.coPPM, 0) +
"&lux=" + String(currentData.lightLux, 1);
String url = String(config.serverUrl) + "/api/Telemetry/AddData?" + data;
String cmd = "AT+QHTTPURL=" + String(url.length()) + ",80";
if (sendAT(cmd, 5000)) {
delay(100);
EC200U.print(url);
if (sendAT("", 5000)) {
if (sendAT("AT+QHTTPGET=60", 10000)) {
lastUpload = millis();
lastMessage = "Data Sent";
}
}
}
}
}
}
updateDisplay();
delay(1000);
}