Editorial Solution

From the task, we understand that the system needs to stay in a low-power state and perform the following tasks:

  • Read the ADC value from the potentiometer.
  • Print the ADC value on the Serial Monitor every 1 minute.
  • Return to sleep mode after completing the task.

To achieve power efficiency and time accuracy, we will use Timer1, which is both power-efficient and provides accurate timing for periodic wake-ups.

The Arduino UNO (ATmega328P) supports six sleep modes, each offering different power-saving levels:

  • IDLE Mode – The CPU stops, but peripherals (Timer, ADC, Serial, etc.) keep running, allowing for a fast wake-up with minimal power saving.
  • ADC Noise Reduction Mode – Reduces noise for ADC conversions by stopping the CPU and some peripherals.
  • Power-down Mode – Provides maximum power saving, halting all clocks except asynchronous modules. Only external interrupts, watchdog reset/interrupt, brown-out reset, 2-wire serial interface match, INT0/INT1 level interrupt, or pin change interrupts can wake the MCU.
  • Power-save Mode – Similar to Power-down but keeps Timer2 running for periodic wake-ups.
  • Standby Mode – Like Power-down, but with an active oscillator for faster wake-up.
  • Extended Standby Mode – Same as Standby, but keeps Timer2 running for periodic tasks.

Given the need for periodic wake-ups with precise timing, we will use IDLE Mode, which allows Timer1 to trigger interrupts for periodic wake-ups while keeping the system in a low-power state. This balances power efficiency with accurate timing for the task.

Here is a comparison of all sleep modes available in the Arduino UNO (ATmega328P):

 

 

Hardware connection

  1. Connect the Arduino UNO board to the PC using a USB cable to establish communication with the Serial Monitor.
  2. Potentiometer:
  • Terminal 1 → Vcc.
  • Terminal 2 → Pin A0.
  • Terminal 3 → GND.

 

Hardware Circuit Connection

 

Firmware

The system is implemented using sleep modes and timer interrupts to ensure efficient power management.

To achieve a 1-minute time-based event, a watchdog timer can be used. However, it has an accuracy of 10%. It will be very inaccurate.

Thus, we will utilize Timer1, which has a duration limit of approximately 4 seconds per cycle. Therefore, the system performs 15 wake-up cycles (15 × 4s = 60s) before executing the main task.

🔹 Timer1 Configuration (CTC Mode)

  • Timer1 is set to CTC (Clear Timer on Compare Match) mode.
  • Uses a 1024 prescaler with OCR1A set to 62500, generating an interrupt approximately every 4 seconds.
  • The ISR (Interrupt Service Routine) increments a counter to track elapsed time.

🔹 Sleep Mode (Idle Mode)

  • The system enters Idle Mode, where the CPU sleeps, but Timer1 remains active.
  • Only essential peripherals stay ON, while ADC, SPI, I2C, and unused timers are disabled to save power.

🔹 Wake-Up and ADC Measurement

  • Every 4 seconds, the system wakes up, increments a counter, and returns to sleep.
  • After 15 cycles (1 minute total), the system reads the ADC value from the potentiometer and prints it to the Serial Monitor.

This approach ensures accurate timing while optimizing power consumption.

 

Code

#include <avr/sleep.h>      // Library for sleep modes
#include <avr/interrupt.h>  // Library for interrupts
#include <avr/power.h>      // Library for power management

volatile uint8_t sleepCounter = 0;  // Counts 4-second intervals for a total of 60 seconds

// Function to configure Timer1 in CTC mode to wake up the CPU every 4 seconds
void setupTimer1() {
  cli();  // Disable global interrupts while configuring

  TCCR1A = 0;                                         // Set Timer1 to Normal mode
  TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10);  // CTC Mode, Prescaler = 1024

  OCR1A = 62500;           // Compare Match A value for 4-second interval (16MHz clock)
  TIMSK1 = (1 << OCIE1A);  // Enable Timer1 Compare Match A interrupt

  sei();  // Enable global interrupts
}

// Function to disable all unnecessary peripherals to save power
void disableUnusedPeripherals() {
  power_adc_disable();     // Disable ADC (Analog to Digital Converter)
  power_spi_disable();     // Disable SPI (Serial Peripheral Interface)
  power_twi_disable();     // Disable TWI (I2C)
  power_timer0_disable();  // Disable Timer0 (Used for delay and millis functions)
  power_timer2_disable();  // Disable Timer2 (Used for PWM on pins 3 and 11)
                           // Note: Timer1 remains enabled as it's used for wake-up
}

// Function to put the MCU into sleep mode (Idle mode)
void enterIdleMode() {
  delay(10);                   // Short delay before entering sleep mode
  disableUnusedPeripherals();  // Disable unused peripherals to save power

  noInterrupts();  // Disable interrupts while configuring sleep

  set_sleep_mode(SLEEP_MODE_IDLE);  // Set CPU to Idle mode (Timer1 remains active)
  sleep_enable();                   // Enable sleep mode

  interrupts();  // Re-enable interrupts

  sleep_mode();  // Put CPU to sleep (execution pauses here)

  // Execution resumes here after wake-up
  sleep_disable();     // Disable sleep mode after waking up
  power_all_enable();  // Re-enable all peripherals
}

void setup() {
  Serial.begin(115200);
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);  //Turning off the on board LED for power saving
  Serial.println("System Initialized.");

  setupTimer1();  // Initialize Timer1 for periodic wake-ups
}

void loop() {
  enterIdleMode();  // Enter Idle Mode (CPU sleeps, wakes up every 4 seconds)

  // Every 15 wake-up cycles (~60 seconds), read ADC and print the value
  if (sleepCounter == 15) {
    int adcValue = analogRead(A0);  // Read ADC value
    Serial.print("ADC Value: ");
    Serial.println(adcValue);  // Print ADC value
    sleepCounter = 0;          // Reset counter for the next 60-second cycle
    delay(10);                 // Short delay to stabilize
  }
}

// Timer1 Compare Match Interrupt (Executes every 4 seconds, wakes CPU)
ISR(TIMER1_COMPA_vect) {
  sleepCounter++;  // Increment counter every 4 seconds
  TCNT1 = 0;       // Reset Timer1 counter
}

 

Code Explanation

Setup Function (setup()):

  • Initialize Serial communication (Serial.begin(115200)).
  • Configure Timer1 (setupTimer1()) for 4-second interrupts.

Timer1 Configuration (setupTimer1()):

  • Disable global interrupts (cli()) to prevent setup conflicts.
  • Set CTC mode (WGM12 = 1) for precise timing.
  • Use a prescaler of 1024 and set OCR1A = 62500 for a 4-second interval.
  • Enable Timer1 Compare Match Interrupt (TIMSK1 |= (1 << OCIE1A)).
  • Re-enable interrupts (sei()).

Power Optimization (disableUnusedPeripherals()):

  • Disable unused peripherals (ADCSPII2CTimer0Timer2).
  • Keep Timer1 active for periodic wake-ups.

Entering Sleep Mode (enterIdleMode()):

  • Set Idle Mode to keep Timer1 running while CPU sleeps.
  • Call sleep_mode() to pause execution until an interrupt occurs.
  • After wake-up, disable sleep and re-enable peripherals.

Main Loop (loop()):

  • Enter Idle Mode for low power.
  • Every 15 wake-ups (approx. 60 seconds), read and print the ADC value.

Timer1 Compare Match Interrupt (ISR(TIMER1_COMPA_vect)):

  • Triggered every 4 seconds, incrementing a counter to track elapsed time and trigger ADC reading every 60 seconds.

 

Output

Power Saving Analysis

  • Active Mode Consumption27-30 mA.
  • Sleep Mode Consumption19-22 mA.
  • Power Reduction: Significant decrease in the current draw by enabling sleep mode.
  • Conclusion: Implementing sleep mode enhances energy efficiency.


 

Hardware Setup

 

Serial Monitor output

ADC values are printing after every 1 minute

Video


 

Submit Your Solution