Hello! This is my first post on this forum so please go easy on me lol.
So basically I have a program that will work outdoors by rotating a servo motor in and out every 8 hours. Since it's outdoors, it will run on a 9V battery requiring me to conserve as much power as possible (which I am doing using power down). Unfortunately I cannot replace/get more components at this time unless absolutely neccessary. I am using an arduino uno r3, a servo motor and a 9V battery.
So, I understand that these components + my lack of an RTC make this a difficult situation. However, my real problem doesn't lie in that. Even if I use a WDT the problem still continues. That problem being in the way my servo motor rotates. In the program, the servo will move to 90* just fine but then when returning to 0 the code just stops instead of continuing on. This isn't my final code for the entire project but it demonstrates the problem. (And yes I tried checking if there's any issues with my servo and it works just fine for any type of rotation in another program).
It's set to 5 seconds right now for testing. Here's the code please let me know about any errors (I am only 2 months into robotics so it might be a really silly mistake lol):
#include <Servo.h>
#include <avr/sleep.h>
Servo myServo;
const unsigned long targetInterval = 5000;
unsigned long previousMillis = 0;
void setup() {
myServo.attach(9);
Serial.begin(9600);
Serial.println("Setup complete.");
previousMillis = millis(); // Initialize previousMillis with the current time
}
void loop() {
unsigned long currentMillis = millis();
// Check if the target interval has passed
if ((currentMillis - previousMillis) >= targetInterval) {
Serial.println("Moving Servo...");
// Move the servo to 90 degrees
myServo.write(90); // Move to 90 degrees
Serial.println("Servo moving to 90 degrees...");
delay(2000); // Wait for the servo to reach position
Serial.println("Servo at 90 degrees.");
// Move back to 0 degrees
Serial.println("Moving Servo back to 0 degrees...");
myServo.write(0); // Move back to 0 degrees
delay(2000); // Wait for the servo to reach position
// Check the servo position by adding a delay and rechecking
delay(500); // Allow time for the servo to move
Serial.println("Checking if servo has moved back...");
Serial.println("Expected position: 0 degrees.");
// Reset previousMillis for the next cycle
previousMillis = currentMillis;
Serial.println("Going to sleep...");
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set to power-down mode for lowest power
sleep_enable();
sleep_mode(); // Go to sleep
sleep_disable();
Serial.println("Waking up...");
} else {
unsigned long timeLeft = targetInterval - (currentMillis - previousMillis);
Serial.print("Time left until next move: ");
Serial.print(timeLeft / 1000); // Print time left in seconds
Serial.println(" seconds.");
}
}
Most servos run at between 4.8 and 6V. What kind of servo do you have that runs at 9V? And what is its stall current? In general servos require a good amount of current each time they start up (500mA and up is typical; 1A and up is not uncommon) and you're simply not going to get anywhere near that amount of current out of a PP3 9V battery. Or are you using some uncommon 9V battery that can supply the necessary current?
Sorry I forgot to specify. The battery is powering the arduino microcontroller not the servo specifically. The servo is plugged into the 5V port, GND and on pin 9. Unfortunately I don’t have a multimeter so I can’t measure the current across the circuit but I’m sure there’s enough flowing. Right now as I’m programming it it’s connected to my computer. The servo is running just fine right now it’s just in this block of code that will be added into my final code it stops abruptly
Watch dog timer in my final code but for now I’m using a regular timer and that’s been working fine up until it’s time to move the servo back to 0*.
In my final code I plan to use a watchdog timer set to 8 seconds to check every 8 seconds if 8 hours have elapsed and then move the servo and go back to the power down/wdt cycle
But it went to sleep and woke up just fine when I ran it it just stopped midway through after waking up. I’m gonna run a simulation on it now but can you suggest me something that would work for this issue? Probably using a WDT to wake up the circuit but I’m not sure (again new to robotics)
Yes that is possible but first you need to fix the power issue with your servo, it is probably causing problems
I suggect you use 4 AA bateries to power the servo.
Here is an example of how to use the WDT but you need prober power for the servo.
/*
This program uses the Watchdog timer (WDT) to wake the Arduino from sleep.
*/
#include <avr/sleep.h>
#include <avr/wdt.h>
/* WDT timeouts bits WDP[3:0] are bit 5 and bits 2:0 of the WDTCSR
0x05 = 0.5 seconds
0x06 = 1
0x07 = 2
0x20 = 4
0x21 = 8 seconds
*/
// I'm using 4 seconds for testing
const byte TIME_OUT = 0x20;
void setup(void)
{
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT); // Set as output
digitalWrite(LED_BUILTIN, HIGH); // LED ON
// Set sleep mode to full power down.
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
// Setup the WDT
WDTSetup(TIME_OUT);
// Reduce the clock frequency to save power
//CLKPR = 0x80;
//CLKPR = 0x01; // Set clock prescaler to 2
Serial.println(F("Setup complete"));
}
void loop(void)
{
// Your code goes here
// --->
/* This is just dummy code for testing
You can delete everyrhing between the arrows
and insert your code */
Serial.println(F("=Awake"));
Serial.println(F("Working"));
for (int i = 0; i < 25; i++)
{
digitalWrite(LED_BUILTIN, HIGH); // LED On
delay(50);
digitalWrite(LED_BUILTIN, LOW); // LED Off
delay(50);
}
Serial.println(F("Done Working"));
// end of your code
//<---
Serial.println("=Sleeping");
delay(10); // This makes sure that serial.println has
// finished before going to sleep
GoToSleep(); // Call this at the end of loop()
}
//=======================================================
void GoToSleep(void)
{
WDTCSR |= _BV(WDIF); // Clear the flag
WDTCSR |= _BV(WDIE); //Enable WDT
wdt_reset(); // Reset WDT
// Enable sleep: Puts cpu to sleep and disbles sleep when awoken
sleep_bod_disable(); // Disble BOD; saves power
sleep_mode();
// We are now asleep
// ZZZZZZZZZZZZZZZZZZZZZZZZZZZ
// TIMEOUT seconds later we are awake
WDTCSR &= ~_BV(WDIE); // Disable WDT
}
//=======================================================
//=======================================================
void WDTSetup(byte TIME_OUT)
{
// Setup the watchdog timer to set an interrupt which wakes the Arduino
// from sleep every TIME_OUT seconds
// WDTCSR register Bits: WDIF WDIE WDP3 WDCE WDE WDP2 WDP1 WDP0;
MCUSR = 0x00; // Clear the MCU Status Reg (including the WDRF)
cli(); // This next section of code is timing critical, so interrupts are disabled
// Start the timed Sequence for configuring the WDT
WDTCSR = (1 << WDCE ) | (1 << WDE ); // Enable configuration change.
WDTCSR = (0 << WDIE) | (1 << WDIF) | // Disble Interrupts and clear the flag
(0 << WDE ) | // Disable Watchdog System Reset Mode
TIME_OUT; // Set Watchdog Timeout
wdt_reset(); // Reset WDT
sei(); // Enable interrupts
}
//=======================================================
// Watchdog timer ISR. Hardware clears the WDIF
ISR(WDT_vect)
{
//digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Toggle the LED
}
That is just the voltage. You need to also consider the current. Pulling that many amps through the 5V pin will cause problems. You're probably browning out and resetting the board when you move the servo. You're lucky if you don't fry your board.
Yeah I think the problem might be how my servo is powered. Also, I used some of your code and tried to use it implemented to how I'm trying this project and now it seems that the servo rotates just fine and everything is working well except the servo rotates in and out too fast even if I increase the delay between movements. Again, this might be a power problem so I'm going to try to get another power source for the servo but just in case it's something else I'll post the code in here.
#include <Servo.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
Servo myServo;
const int SERVO_PIN = 9;
const int WDT_TIMEOUT = 9; // 8-second Watchdog Timer interval
const int SLEEP_CYCLES = 1; // Wake after 8 seconds for testing
const int HOLD_OPEN_TIME = 3000; // Time to hold servo open (3 seconds)
volatile bool watchdogInterrupt = false;
volatile unsigned long sleepCounter = 0;
void setup() {
Serial.begin(9600); // Debug output
myServo.attach(SERVO_PIN); // Attach servo once in setup
// Initial movement of the servo
moveServoOut();
delay(HOLD_OPEN_TIME); // Hold position for 3 seconds
moveServoIn();
// Set up Watchdog Timer interrupt
setupWatchdog(WDT_TIMEOUT);
Serial.println("Setup complete, entering sleep...");
}
void loop() {
if (sleepCounter >= SLEEP_CYCLES) {
Serial.println("Woke up, moving servo...");
// Move the servo out, hold, then move it back in within the same wake cycle
moveServoOut();
delay(HOLD_OPEN_TIME); // Hold position for 3 seconds
moveServoIn();
sleepCounter = 0; // Reset counter after completing the movements
Serial.println("Servo movement complete, going back to sleep...");
}
// Enter sleep mode
enterSleep();
}
// Watchdog Timer setup
void setupWatchdog(int interval) {
cli();
MCUSR &= ~(1 << WDRF);
WDTCSR |= (1 << WDCE) | (1 << WDE);
WDTCSR = (1 << WDP3) | (1 << WDP0) | (1 << WDIE);
sei(); // Enable interrupts
}
// Sleep mode function
void enterSleep() {
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set power-down sleep mode
sleep_enable();
sleep_mode(); // Go to sleep
// Execution resumes here after the watchdog interrupt
sleep_disable();
if (watchdogInterrupt) {
watchdogInterrupt = false;
sleepCounter++;
Serial.println("Woke up from watchdog, incrementing counter...");
}
}
// Watchdog Timer interrupt
ISR(WDT_vect) {
watchdogInterrupt = true; // Flag to wake up and increment sleep counter
}
// Servo movement functions
void moveServoOut() {
myServo.write(180);
Serial.println("Servo moved out.");
}
void moveServoIn() {
myServo.write(0);
Serial.println("Servo moved in.");
}
The only way you can make it slower using the standard servo library is to move it in steps with a delay between each step. Say move 2 degrees at a time with 20ms between steps.
See the Sweep.ino example.
I think I worded it weirdly. The problem isn't the speed of the actual rotations but the fact that the servo will instantly move back it after moving out in this line here
moveServoOut();
delay(HOLD_OPEN_TIME); // Hold position for 3 seconds
moveServoIn();
The delay is 3 seconds meaning it's supposaed to be servo rotates to 180 --> waits 3 seconds ---> rotates to 0 but instead it's servo rotates to 180 --> servo rotates to 0 with no delay.
ohhh I see. I replaced the delay and in my simulation everything runs fine now but when I use the actual circuit but in my actual circuit the servo just jitters a bit and only prints ("Servo moved out") which is part of the moveServoOut(). I wonder if this is the same problem with it rotating to 0* as my last case? The rest of the code runs perfectly it just doesn't execute moveServoIn() completely I'm guessing?
I don't have the proper battery pack for a 4 AA battery pack right now but I'm going to order one, I guess. Hopefully that will finally solve the issue but with the Arduino connected to my computer and the servo in the 5V port I thought it would have enough voltage/current supplied.