我决定自行编写示例,说明如何将Arduino设为睡眠状态。你可能会感到疑惑,这有什么必要呢?答案是:如果你制作的项目是由电池供电的话,那么这一设置就会非常有用。让Arduino进入睡眠模式状态可降低电流消耗,从而延长项目的运行时间,而不必频繁更换电池。在许多IOT项目中亦是如此,因为并不需要单片机或外部器件一直运行。
在启动示例代码中,我们用了两个按钮和两个LED来显示何时唤醒电路板以及何时按下中断按钮。当电路板被唤醒时,连接到引脚13的LED将开始闪烁。当按下连接到引脚11的按钮时,将使Arduino进入睡眠模式,而引脚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();
- }
- }
初始代码接线图
添加实时时钟以唤醒 Arduino
接下来,我们将添加一个RTC(而非按钮)来控制Arduino的睡眠模式和唤醒。我在此项目中使用的是来自Adafruit的物料1528-1598-ND 15。选择它的主要原因是其配备了内置中断。我原来使用的是DS1307分线板,但我很快发现它并不支持中断,因此无法用于此项目。我还针对此项目下载了一些库。以下是指向我下载.zip库文件的链接。
指向库的链接:
https://github.com/PaulStoffregen/Time——Arduino的计时库
https://github.com/JChristensen/DS3232RTC——适用于DS3231,包含唤醒Arduino所需的提醒
添加 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 接线图
添加土壤湿度传感器和温度 / 湿度传感器
为了展示这类传感器的使用示例,我决定制作一个独立的装置监控系统。为此,我将使用土壤湿度探针(1568-1670-ND 1)和温度/湿度传感器(1528-1172-ND 1)。该程序现在要做的是检查土壤水份含量和温度。我决定省略此代码草稿中的湿度功能,但只需几行代码就可以再添加回来。
该监控系统会进行检查,以确保土壤湿度不会过低。如果水分含量低于一定水平,系统就会启动水泵或电磁阀。在本例中,我将LED用作指示器,以代替水泵或电磁阀。
对于此代码草稿,你需要安装MPL115A2库,该库可通过Manage Libraries Button进行安装(位于Include Library下的Sketch Menu中),或者你也可以通过以下链接找到zip文件:GitHub - adafruit/Adafruit_MPL115A2: Driver for the Adafruit MPL115A2 barometric pressure sensor breakout
- #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);
- }
- }
- }
来源:digikey