아두이노를 슬립모드가 되도록 설정하는 방법을 설명하는 예제를 직접 쓰려고 합니다. 슬립모드가 왜 필요한지 궁금할 수도 있지만, 여러분이 제작 중인 프로젝트가 배터리로 구동되는 경우라면 아주 유용할 수 있습니다. 아두이노를 슬립모드로 전환하면 전류 소모량이 낮아져 결국 새 배터리로 교체하기 전의 프로젝트 실행 시간이 보다 길어집니다. 마이크로컨트롤러 또는 외부 소자를 항상 실행할 필요가 없는 여러 IOT 프로젝트에서 매우 유용할 수도 있습니다.
예제 코드를 시작하기 위해, 두 개의 LED와 함께 두 개의 푸시 버튼을 사용해서 아두이노가 깨어 있을 때와 인터럽트 버튼이 눌러 졌을 때를 나타낼 것입니다. 아두이노가 깨어 있으면 13번 핀에 연결된 LED가 깜박일 것입니다. 11번 핀에 연결된 푸시 버튼을 누르면 아두이노는 슬립 모드로 전환되고 13번 핀의 LED는 더 이상 깜박이지 않을 것입니다. 아두이노를 깨우려면 2번 핀에 연결된 푸시 버튼을 눌러야 합니다. 이 버튼을 누르면 10번 핀에 연결된 LED가 켜지며 인터럽트가 활성화되었음을 나타냅니다.
예제 코드 시작
//These are the two libraries that are needed
#include <avr/interrupt.h>
#include <avr/sleep.h>
/* Here we set up our inputs and outputs. LEDs connected to pins 10 and 13 and pushbuttons attached to 2 and 12 */
int ledPin = 13;
int sleepPin = 12;
int interruptPin = 10;
int wakePin = 2;
//sleepStatus is set up to keep track of the button input on pin 12.
int sleepStatus = 0;
void setup()
{
pinMode(ledPin, OUTPUT);
pinMode(interruptPin, OUTPUT);
pinMode(sleepPin, INPUT_PULLUP);
pinMode(wakePin, INPUT_PULLUP);
/* Next we have to enable an interrupt.
The function is set up like this attachInterrupt(pin, function, triggerMode)
PIN – can be either a 0 to call out digital pin 2 or 1 to call out digital pin 3.
FUNCTION – This is the function that will be run while in the interrupt
TRIGGER MODE – this will be the mode of the interrupt pin.
It can be one the following:
LOW – a low level trigger
CHANGE – a change in level trigger
RISING – a rising edge trigger
FALLING – a falling edge trigger
The IDLE sleep mode is the only mode that can use CHANGE, RISING, and FALLING modes.*/
attachInterrupt(0, wakeUpNow, LOW);
}
void sleepNow()
{
//print message to serial monitor to let the user know board has gone to sleep
Serial.println("going to sleep");
//delay is added to allow user to get the full message on the serial monitor before going to sleep
delay(15);
//enables the sleep mode
sleep_enable();
// This is where we enable the interrupt, the reason it is done here is so that if the button is pressed accidently it doesn’t interrupt the running program.
attachInterrupt(0,wakeUpNow, LOW);
/* The next line is where we choose the sleep mode we want to use for this code. There are a few options to choose from, each with their own uses. For more information on the sleep modes, please review the Atmega8 datasheet at [http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf](http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf)
The 5 different options for sleep modes, they are listed below from least power savings to most power savings:
SLEEP_MODE_IDLE
SLEEP_MODE_ADC
SLEEP_MODE_PWR_SAVE
SLEEP_MODE_STANDBY
SLEEP_MODE_PWR_DOWN
For this sketch, we will be using the most power savings possible so we choose SLEEP_MODE_PWR_DOWN */
//sleep mode is set here
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
//This is where the device is actually put to sleep
sleep_mode();
//Here is where the device begins to wake up.
//First thing that is done is to disable the sleep mode
sleep_disable();
//disables the interrupt on pin 2 so the wakeUpNow code will not be executed during normal run time
detachInterrupt(0);
//wait 1 second so the user can notice the LED signaling the interrupt
delay(1000);
digitalWrite (interruptPin, LOW);
}
void wakeUpNow() //This is the code that runs when the interrupt button is pressed and interrupts are enabled
{
digitalWrite(interruptPin, HIGH);
}
void loop()
{
// turns the LED on
digitalWrite(ledPin, HIGH);
// waits for a second
delay(1000);
// turns the LED off
digitalWrite(ledPin, LOW);
// waits for a second
delay(1000);
//This is where the sleep pin is read. It is only active when the LED is off.
sleepStatus = digitalRead(sleepPin);
//If button is pressed, device will run the sleepNow function
if (sleepStatus == LOW) {
sleepNow();
}
}
초기 코드의 배선도
Scheme-it 회로도 링크: Arduino Uno
아두이노를 깨우기 위한 실시간 클록(real time clock, RTC) 추가
다음으로, 아두이노를 슬립모드로 만들고 깨우는데 버튼을 사용하는 대신 RTC를 추가하여 제어할 것입니다. 이 프로젝트에는 Adafruit사의 부품 1528-1598-ND를 사용할 것입니다. 이 RTC를 선택한 이유는 인터럽트를 내장하고 있기 때문입니다. 원래 DS1307 확장 기판을 가지고 있었는데 인터럽트가 포함되어 있지 않다는 것을 재빨리 알아차렸으며, 따라서 이 프로젝트에서는 제대로 동작하지 않았을 것입니다. 이 프로젝트가 동작하도록 하기위해 라이브러리 몇 개도 다운로드 했습니다. 아래는 .zip 라이브러리 파일을 다운로드한 링크입니다.
라이브러리 링크:
GitHub - PaulStoffregen/Time: Time library for Arduino - 아두이노의 시간을 기록하기 위한 라이브러리입니다.
GitHub - JChristensen/DS3232RTC: Arduino Library for Maxim Integrated DS3232 and DS3231 Real-Time Clocks - DS3231용 라이브러리이며 아두이노를 깨우는 데 필요한 알람을 포함하고 있습니다.
RTC 추가 코드
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <DS3232RTC.h> // https://github.com/JChristensen/DS3232RTC this is the library for the DS2331 RTC
//RTC Module global variables
// Sets the wakeup interval in minutes
const int time_interval = 5;
// LED connected to digital pin 13
int ledPin = 13;
// active LOW, RTC will interrupt this pin momentarily to wake up
int wakePin = 2;
void setup() {
// set up the serial monitor
Serial.begin(9600);
//Set up the led pin as an output
pinMode(ledPin, OUTPUT);
//Set pin d2 to input using the built-in pullup resistor
pinMode(wakePin, INPUT_PULLUP);
//turning LED on
digitalWrite(ledPin, HIGH);
// These next few lines of code initialize the alarms to known values, clear the flags, and clear the alarm interrupt flags
RTC.setAlarm(ALM1_MATCH_DATE, 0, 0, 0, 1);
RTC.setAlarm(ALM2_MATCH_DATE, 0, 0, 0, 1);
RTC.alarm(ALARM_1);
RTC.alarm(ALARM_2);
RTC.alarmInterrupt(ALARM_1, false);
RTC.alarmInterrupt(ALARM_2, false);
RTC.squareWave(SQWAVE_NONE);
/* Uncomment this section to set the time on the RTC. Make sure to comment out after the first time
it is set or you will continue to reset the time everytime the sketch is uploaded. Also note that the clock is 24 hour format.
tmElements_t tm;
// the next few lines set the clock to the correct hour, minute, and second. Remember 24 hour format so 4pm = hour 16
tm.Hour = 8;
tm.Minute = 19;
tm.Second = 00;
// set the correct date on the RTC
tm.Day = 04;
tm.Month = 5;
tm.Year = 2019 - 1970; // in order to set the year correctly, just change the 2019 and leave the “- 1970” to get the correct offset
RTC.write(tm); // write the date and time to the RTC
*/
time_t t; //create a temporary time variable so we can set the time and read the time from the RTC
t = RTC.get(); //Gets the current time of the RTC
RTC.setAlarm(ALM1_MATCH_MINUTES , 0, minute(t) + time_interval, 0, 0); // Setting alarm 1 to go off in the amount of minutes that we have the time interval constant set to
RTC.alarm(ALARM_1); // clear the alarm flag
RTC.squareWave(SQWAVE_NONE); // configure the INT/SQW pin for "interrupt" operation (disable square wave output)
RTC.alarmInterrupt(ALARM_1, true); // enable interrupt output for Alarm 1
}
void loop() {
delay(5000);//wait 5 seconds before going to sleep. When in a project, we would it is best to make this short as possible
sleepNow(); // run the sleepNow function
}
void sleepNow() {
sleep_enable();//Enabling sleep mode
attachInterrupt(0, wakeUpNow, LOW);//attaching a interrupt to pin d2
set_sleep_mode(SLEEP_MODE_PWR_DOWN);//Setting the sleep mode, in our case full sleep
digitalWrite(ledPin, LOW); //turning LED off
time_t t;// creates temporary time variable
t = RTC.get(); //gets current time from RTC
Serial.println("Sleep Time: " + String(hour(t)) + ":" + String(minute(t)) + ":" + String(second(t))); //prints time stamp on serial monitor
delay(1000); //wait one second to allow the LED to be turned off before going to sleep
sleep_cpu();//heres where the Arduino is actually put to sleep
Serial.println("just woke up!");//next line of code executed after the interrupt
digitalWrite(ledPin, HIGH); //turns the LED on
t = RTC.get();//get the new time from the RTC
Serial.println("WakeUp Time: " + String(hour(t)) + ":" + String(minute(t)) + ":" + String(second(t))); //Prints time stamp
//Set New Alarm
int alarmTime = 0; //temporary variable to store the new alarm time in minutes
//the next few lines are to roll the alarm over when it gets near the next hour
if (minute(t) <= (60-time_interval))
{
alarmTime = minute(t) + time_interval;
}
else
{
alarmTime = (minute(t) + time_interval) - 60;
}
RTC.setAlarm(ALM1_MATCH_MINUTES , 0, alarmTime, 0, 0); // set new alarm
// The next few lines of code I use for troubleshooting. This way I can make sure the clock is waking up at the correct time
Serial.print("The next alarm will go off at: ");
//These lines are to print the correct hour that the next alarm will go off
if ((minute(t) <= 60-time_interval) && (hour(t) <= 22))
{
Serial.print(hour(t));
}
else if ((minute(t) >= 60-time_interval) && (hour(t) <= 22))
{
Serial.print(hour(t) + 1);
}
else
{
Serial.print(0);
}
Serial.print(":"); // print a colon symbol
//print the correct minute, including leading zero if less than 10
if (alarmTime <= 9)
{
Serial.print("0");
}
Serial.println(alarmTime);
//Last thing we do is clear the alarm flag
RTC.alarm(ALARM_1);
}
void wakeUpNow() //This is the code that happens when the interrupt is activated
{
Serial.println("Interrrupt Fired");//Print message to serial monitor
sleep_disable();//Disable sleep mode
detachInterrupt(0); //Removes the interrupt from pin 2;
}
RTC 배선도
Scheme-it 회로도 링크: Arduino Uno-RTC Sleep
온도/습도 센서와 함께 토양 수분 센서 추가
이 프로젝트가 어떻게 사용될 수 있는지 예를 들어 보기 위해, 독립형 식물 모니터링 시스템을 만들기로 했습니다. 이 프로젝트를 완료하기 위해 온도/습도 센서 1528-1172-ND와 함께 토양 수분 프로브 1568-1670-ND를 사용할 것입니다. 이제 이 프로그램이 할 일은 온도와 함께 토양의 수분 함량을 확인하는 것입니다. 이번 스케치에서는 습도를 빼기로 했지만 코드 몇 줄만 있으면 습도도 추가할 수 있습니다.
그런 다음 이 모니터링 시스템은 토양 수분이 너무 낮지 않도록 확인할 것입니다. 수분 함량이 일정 수준 이하가 되면 워터 펌프 또는 솔레노이드를 작동시킬 것입니다. 이 예제에서는 워터 펌프 또는 솔레노이드 대신에 지시기로써 LED를 사용하고 있습니다.
이 스케치를 위해서는 MPL115A2 라이브러리를 설치해야 하며, 이 라이브러리는 Include Library 아래 Sketch 메뉴 아래에 있는 Manage Libraries 버튼을 통해 설치할 수 있습니다. 또는 .zip 파일을 아래에서 다운로드 받을 수 있습니다:
온도/습도 센서 및 토양 수분 센서 추가 코드
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <DS3232RTC.h> // https://github.com/JChristensen/DS3232RTC this is the library for the DS2331 RTC
#include <Wire.h>
#include <Adafruit_MPL115A2.h>
Adafruit_MPL115A2 mpl115a2;
//RTC Module global variables
// Sets the wakeup interval in minutes
const int time_interval = 5;
// LED connected to digital pin 13
int ledPin = 13;
// active LOW, RTC will interrupt this pin momentarily to wake up
int wakePin = 2;
// LED connected to digital pin 10
int pumpLED = 10;
//variable for storing the soil moisture value
int moistureVal = 0;
//input for the moisture sensor
int soilPin = A0;
//pin used to power the soil moisture sensor
int soilPower = 7;
void setup() {
// set up the serial monitor
Serial.begin(9600);
//Set up the led pin as an output
pinMode(ledPin, OUTPUT);
//Set pin d2 to input using the built-in pullup resistor
pinMode(wakePin, INPUT_PULLUP);
//turning LED on
digitalWrite(ledPin, HIGH);
//Set D7 as an OUTPUT
pinMode(soilPower, OUTPUT);
// Set to LOW so no power is flowing through the sensor
digitalWrite(soilPower, LOW);
//Set D10 as an output
pinMode(pumpLED, OUTPUT);
//starting the temp/humidity sensor
mpl115a2.begin();
// These next few lines of code initialize the alarms to known values, clear the flags, and clear the alarm interrupt flags
RTC.setAlarm(ALM1_MATCH_DATE, 0, 0, 0, 1);
RTC.setAlarm(ALM2_MATCH_DATE, 0, 0, 0, 1);
RTC.alarm(ALARM_1);
RTC.alarm(ALARM_2);
RTC.alarmInterrupt(ALARM_1, false);
RTC.alarmInterrupt(ALARM_2, false);
RTC.squareWave(SQWAVE_NONE);
/* Uncomment this section to set the time on the RTC. Make sure to comment out after the first time
it is set or you will continue to reset the time everytime the sketch is uploaded. Also note that the clock is 24 hour format.
tmElements_t tm;
// the next few lines set the clock to the correct hour, minute, and second. Remember 24 hour format so 4pm = hour 16
tm.Hour = 8;
tm.Minute = 19;
tm.Second = 00;
// set the correct date on the RTC
tm.Day = 04;
tm.Month = 5;
tm.Year = 2019 - 1970; // in order to set the year correctly, just change the 2019 and leave the “- 1970” to get the correct offset
RTC.write(tm); // write the date and time to the RTC
*/
time_t t; //create a temporary time variable so we can set the time and read the time from the RTC
t = RTC.get(); //Gets the current time of the RTC
RTC.setAlarm(ALM1_MATCH_MINUTES , 0, minute(t) + time_interval, 0, 0); // Setting alarm 1 to go off in the amount of minutes that we have the time interval constant set to
RTC.alarm(ALARM_1); // clear the alarm flag
RTC.squareWave(SQWAVE_NONE); // configure the INT/SQW pin for "interrupt" operation (disable square wave output)
RTC.alarmInterrupt(ALARM_1, true); // enable interrupt output for Alarm 1
}
void loop() {
delay(5000);//wait 5 seconds before going to sleep. When in a project, we would it is best to make this short as possible
sleepNow(); // run the sleepNow function
}
void sleepNow() {
sleep_enable();//Enabling sleep mode
attachInterrupt(0, wakeUpNow, LOW);//attaching a interrupt to pin d2
set_sleep_mode(SLEEP_MODE_PWR_DOWN);//Setting the sleep mode, in our case full sleep
digitalWrite(ledPin, LOW); //turning LED off
time_t t;// creates temporary time variable
t = RTC.get(); //gets current time from RTC
Serial.println("Sleep Time: " + String(hour(t)) + ":" + String(minute(t)) + ":" + String(second(t))); //prints time stamp on serial monitor
delay(1000); //wait one second to allow the LED to be turned off before going to sleep
sleep_cpu();//heres where the Arduino is actually put to sleep
Serial.println("just woke up!");//next line of code executed after the interrupt
digitalWrite(ledPin, HIGH); //turns the LED on
t = RTC.get();//get the new time from the RTC
Serial.println("WakeUp Time: " + String(hour(t)) + ":" + String(minute(t)) + ":" + String(second(t))); //Prints time stamp
getTempAndSoil(); //Run the TempAndSoil function
//Set New Alarm
int alarmTime = 0; //temporary variable to store the new alarm time in minutes
//the next few lines are to roll the alarm over when it gets near the next hour
if (minute(t) <= (60-time_interval))
{
alarmTime = minute(t) + time_interval;
}
else
{
alarmTime = (minute(t) + time_interval) - 60;
}
RTC.setAlarm(ALM1_MATCH_MINUTES , 0, alarmTime, 0, 0); // set new alarm
// The next few lines of code I use for troubleshooting. This way I can make sure the clock is waking up at the correct time
Serial.print("The next alarm will go off at: ");
//These lines are to print the correct hour that the next alarm will go off
if ((minute(t) <= 60-time_interval) && (hour(t) <= 22))
{
Serial.print(hour(t));
}
else if ((minute(t) >= 60-time_interval) && (hour(t) <= 22))
{
Serial.print(hour(t) + 1);
}
else
{
Serial.print(0);
}
Serial.print(":"); // print a colon symbol
//print the correct minute, including leading zero if less than 10
if (alarmTime <= 9)
{
Serial.print("0");
}
Serial.println(alarmTime);
//Last thing we do is clear the alarm flag
RTC.alarm(ALARM_1);
}
void wakeUpNow() //This is the code that happens when the interrupt is activated
{
Serial.println("Interrrupt Fired");//Print message to serial monitor
sleep_disable();//Disable sleep mode
detachInterrupt(0); //Removes the interrupt from pin 2;
}
void getTempAndSoil()
{
float temperatureC = 0, tempF = 0; //set up float variable to store Celsius and Fahrenheit temp
temperatureC = mpl115a2.getTemperature(); //get temperature from the sensor
tempF = (temperatureC * 1.8) + 32; //convert the temperature from Celsius to Fahrenheit.
Serial.print("Temp (*F): "); Serial.print(tempF, 1); Serial.println(" *F"); //Print the temperature to the Serial Monitor
Serial.print("Soil Moisture = ");
//get soil moisture value from the function below and print it
Serial.println(readSoil());
addWater(); // Run the add water function
}
int readSoil()
{
digitalWrite(soilPower, HIGH);//turn the soil moisture sensor on
delay(10);//wait 10 milliseconds
moistureVal = analogRead(soilPin);//Read the value from sensor
digitalWrite(soilPower, LOW);//turn the soil moisture sensor off
return moistureVal;//send current moisture value
}
void addWater()
{
if (moistureVal <= 100) //If the soil moisture gets too low (The value will need to be set to accompany the soil that you are using)
{
//This is where you could change the code to run a relay, or run a water pump to water your plant. I just flashed a LED as an example to show that the plant would need to be watered.
for (int num = 0; num <= 10; num++)
{
digitalWrite(pumpLED, HIGH);
delay(1000);
digitalWrite(pumpLED, LOW);
delay(1000);
Serial.print("PUMP ON");
Serial.println(num);
}
}
}
RTC와 센서의 배선도
Scheme-it 회로도 링크: Arduino Uno-RTC and sensors Sleep
자재 명세서
장바구니 링크: Digi-Key 장바구니
영문 원본: Arduino Sleep Example