Servo motor that runs once every 8 hours

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

The manufacturer of my servo says the starting current of my motor should be >1A. Sorry for the jumbled/multiple replies

@thekoyo

so it might be a really silly mistake

Exactly what wakes-up the processor?

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

And

Well that would be a probable source of your problem.

Wait how? The voltage range for the servo is 4.8-6V so isn’t the 5V port proper?

The code you posted uses sleep_enable() which puts the CPU to sleep but nothing wakes it up. That is why the code stops.

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.

Delay takes an unsigned long, you are using int

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?

Do you have the proper power supply for the servo?

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.