Untitled
unknown
plain_text
2 months ago
12 kB
6
Indexable
/* * SMS Control System with Sleep Mode and Network Recovery * * Features: * - Deep sleep when GSM not connected (few µA consumption) * - 15-minute network check intervals using Watchdog Timer * - Full-step stepper control for maximum torque * - Battery monitoring with SMS alerts * - LED status indication * * Hardware: * - Arduino Uno * - SIM800L GSM Module * - 28BYJ-48 Stepper with ULN2003 * - Servo Motor * - 12V to 5V Step-down (3A recommended) * - Voltage divider (100kΩ + 10kΩ) */ #include <avr/wdt.h> #include <avr/sleep.h> #include <avr/power.h> #include <SoftwareSerial.h> #include <Servo.h> // System constants const char* SMS_COMMAND = "move:"; // SMS command prefix const char* ADMIN_NUMBER = "+1234567890"; // Alert recipient const float VOLTAGE_THRESHOLD = 11.0; // Low voltage threshold const int GSM_INIT_TIMEOUT = 30000; // GSM initialization timeout const int STEPS_PER_REV = 2048; // Steps for full revolution (full-step mode) const int STEP_DELAY_MS = 2; // Minimum step delay // Check intervals const unsigned long NETWORK_CHECK_INTERVAL = 900000; // 15 minutes const unsigned long VOLTAGE_CHECK_INTERVAL = 60000; // 1 minute // Pin definitions #define SIM800_RX 7 #define SIM800_TX 8 #define SERVO_PIN 9 #define VOLTAGE_PIN A0 #define LED_PIN LED_BUILTIN // Stepper pins #define IN1 3 #define IN2 4 #define IN3 5 #define IN4 6 // Voltage divider const float R1 = 100000.0; // 100kΩ const float R2 = 10000.0; // 10kΩ const float VREF = 5.0; // Reference voltage // System state bool gsmRegistered = false; bool lowBatteryAlerted = false; volatile bool watchdogActivated = false; unsigned int watchdogCounter = 0; unsigned long lastVoltageCheck = 0; unsigned long lastNetworkCheck = 0; // Objects SoftwareSerial sim800(SIM800_RX, SIM800_TX); Servo myservo; // Full-step sequence for maximum torque const byte stepsSequence[4][4] = { {1, 1, 0, 0}, // A + B {0, 1, 1, 0}, // B + C {0, 0, 1, 1}, // C + D {1, 0, 0, 1} // D + A }; // Movement structures struct ServoMove { int degrees; // Target angle float seconds; // Duration }; struct StepperMove { int degrees; // Rotation (positive = clockwise) float seconds; // Duration }; struct Pattern { ServoMove* servoMoves; int servoMovesCount; StepperMove* stepperMoves; int stepperMovesCount; const char* description; }; // Pattern definitions ServoMove pattern1_servo[] = { {45, 2.0}, // 45° for 2s {90, 2.0}, // 90° for 2s {45, 1.5}, // 45° for 1.5s {90, 3.0} // 90° for 3s }; StepperMove pattern1_stepper[] = { {180, 4.0}, // 180° clockwise {-90, 2.0} // 90° counterclockwise }; Pattern patterns[] = { { .servoMoves = pattern1_servo, .servoMovesCount = sizeof(pattern1_servo) / sizeof(ServoMove), .stepperMoves = pattern1_stepper, .stepperMovesCount = sizeof(pattern1_stepper) / sizeof(StepperMove), .description = "Basic movement pattern" } }; // Watchdog ISR ISR(WDT_vect) { watchdogActivated = true; wdt_disable(); // Disable watchdog until next sleep } void setup() { // Initialize pins pinMode(LED_PIN, OUTPUT); pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT); pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT); pinMode(VOLTAGE_PIN, INPUT); // Initialize communications Serial.begin(9600); sim800.begin(9600); // Initialize hardware myservo.attach(SERVO_PIN); disableStepper(); Serial.println(F("Starting GSM initialization...")); // Initialize GSM if (!initGSM()) { Serial.println(F("Initial GSM setup failed")); // Will enter sleep mode in loop } else { Serial.println(F("GSM initialized")); blinkLED(5); } } void loop() { if (!gsmRegistered) { // Deep sleep when GSM not connected enterSleepMode(); if (watchdogActivated) { watchdogCounter++; watchdogActivated = false; // Try network registration every 15 minutes if (watchdogCounter >= 112) { // ~15 minutes watchdogCounter = 0; Serial.println(F("Checking network...")); if (waitForNetwork()) { Serial.println(F("Network recovered")); blinkLED(5); } } } } else { // Normal operation when GSM connected unsigned long currentMillis = millis(); // Regular network check if (currentMillis - lastNetworkCheck >= NETWORK_CHECK_INTERVAL) { if (!checkNetworkRegistration()) { Serial.println(F("Network lost")); gsmRegistered = false; } lastNetworkCheck = currentMillis; } // Voltage monitoring if (currentMillis - lastVoltageCheck >= VOLTAGE_CHECK_INTERVAL) { checkVoltage(); lastVoltageCheck = currentMillis; } // Process any SMS commands processMessages(); } } void setupWatchdog() { // Clear previous settings MCUSR &= ~(1<<WDRF); // Allow changes WDTCSR |= (1<<WDCE) | (1<<WDE); // Set for 8 seconds WDTCSR = (1<<WDIE) | (1<<WDP3) | (1<<WDP0); } void enterSleepMode() { // Disable ADC and other peripherals ADCSRA &= ~(1<<ADEN); power_all_disable(); // Configure sleep set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); // Enable watchdog setupWatchdog(); // Enter sleep sleep_mode(); // Resume here after wake sleep_disable(); power_all_enable(); ADCSRA |= (1<<ADEN); } bool initGSM() { // One-time GSM setup if (!sendATCommand("AT", 1000)) return false; // Disable unnecessary features sendATCommand("AT+CGATT=0", 1000); // No GPRS sendATCommand("AT+CIPSHUT", 1000); // No IP // Force home network only, deny roaming // AT+COPS=1,0,"operator_name" - Forces registration to specific operator // AT+COPS=3,0 - Set format to numeric // AT+COPS=1,2,"operator_code" - Force registration using numeric code if (!sendATCommand("AT+COPS=3,0", 1000)) return false; // Set format to long alphanumeric if (!sendATCommand("AT+COPS=1,0,\"<YOUR_OPERATOR_NAME_HERE>\"", 1000)) return false; // Force home network // Configure for SMS if (!sendATCommand("AT+CNMP=13", 1000)) return false; // GSM only if (!sendATCommand("AT+CMGF=1", 1000)) return false; // Text mode if (!sendATCommand("AT+CNMI=1,2,0,0,0", 1000)) return false; return waitForNetwork(); } bool waitForNetwork() { unsigned long startTime = millis(); while(millis() - startTime < GSM_INIT_TIMEOUT) { if (checkNetworkRegistration()) { return true; } pulseLED(); delay(1000); } return false; } bool checkNetworkRegistration() { sim800.println("AT+CREG?"); delay(100); if (sim800.available()) { String response = sim800.readString(); // Now only accept home network (1), reject roaming (5) gsmRegistered = (response.indexOf("+CREG: 0,1") != -1); return gsmRegistered; } gsmRegistered = false; return false; } void checkVoltage() { float voltage = readInputVoltage(); Serial.print(F("Voltage: ")); Serial.println(voltage); if (voltage < VOLTAGE_THRESHOLD && !lowBatteryAlerted) { sendSMS(ADMIN_NUMBER, ("Low battery: " + String(voltage, 1) + "V").c_str()); lowBatteryAlerted = true; } else if (voltage >= VOLTAGE_THRESHOLD && lowBatteryAlerted) { lowBatteryAlerted = false; } } float readInputVoltage() { int rawValue = analogRead(VOLTAGE_PIN); float voltageAtPin = (rawValue / 1023.0) * VREF; return voltageAtPin * ((R1 + R2) / R2); } bool sendATCommand(const char* command, int timeout) { sim800.println(command); long int time = millis(); while ((time + timeout) > millis()) { while (sim800.available()) { String response = sim800.readString(); if (response.indexOf("OK") != -1) return true; } } return false; } void sendSMS(const char* number, const char* message) { sim800.println("AT+CMGS=\"" + String(number) + "\""); delay(100); sim800.println(message); delay(100); sim800.write(0x1A); delay(1000); } void pulseLED() { for (int i = 0; i < 255; i++) { analogWrite(LED_PIN, i); delay(2); } for (int i = 255; i >= 0; i--) { analogWrite(LED_PIN, i); delay(2); } } void blinkLED(int times) { for (int i = 0; i < times; i++) { digitalWrite(LED_PIN, HIGH); delay(100); digitalWrite(LED_PIN, LOW); delay(100); } } void processMessages() { if (sim800.available()) { String message = sim800.readString(); Serial.println("Received: " + message); if (message.indexOf("+CMT:") != -1 && message.indexOf(SMS_COMMAND) != -1) { int patternStart = message.lastIndexOf(SMS_COMMAND) + strlen(SMS_COMMAND); String patternStr = message.substring(patternStart); patternStr.trim(); int patternIndex = patternStr.toInt(); if (patternIndex > 0 && patternIndex <= sizeof(patterns)/sizeof(Pattern)) { executePattern(patternIndex - 1); } } } } void disableStepper() { digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); } void moveStepperStep(int step) { digitalWrite(IN1, stepsSequence[step][0]); digitalWrite(IN2, stepsSequence[step][1]); digitalWrite(IN3, stepsSequence[step][2]); digitalWrite(IN4, stepsSequence[step][3]); } void moveStepperSteps(int steps, float seconds) { int currentStep = 0; int direction = steps > 0 ? 1 : -1; steps = abs(steps); float stepDelay = (seconds * 1000.0) / steps; stepDelay = max(stepDelay, (float)STEP_DELAY_MS); for (int i = 0; i < steps; i++) { currentStep = (currentStep + direction + 8) % 8; moveStepperStep(currentStep); delay(stepDelay); } } void executePattern(int index) { Pattern* pattern = &patterns[index]; unsigned long startTime = millis(); int servoIndex = 0; int stepperIndex = 0; Serial.print(F("Executing pattern: ")); Serial.println(pattern->description); while (servoIndex < pattern->servoMovesCount || stepperIndex < pattern->stepperMovesCount) { // Handle servo if (servoIndex < pattern->servoMovesCount) { ServoMove* move = &pattern->servoMoves[servoIndex]; myservo.write(move->degrees); if ((millis() - startTime) >= (move->seconds * 1000)) { servoIndex++; startTime = millis(); } } // Handle stepper if (stepperIndex < pattern->stepperMovesCount) { StepperMove* move = &pattern->stepperMoves[stepperIndex]; int steps = (move->degrees * STEPS_PER_REV) / 360; moveStepperSteps(steps, move->seconds); stepperIndex++; } delay(10); } disableStepper(); Serial.println(F("Pattern complete")); }
Editor is loading...
Leave a Comment