Untitled

 avatar
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