Untitled
unknown
plain_text
8 months ago
12 kB
7
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