我决定自行编写示例,说明如何将Arduino设为睡眠状态。你可能会感到疑惑,这有什么必要呢?答案是:如果你制作的项目是由电池供电的话,那么这一设置就会非常有用。让Arduino进入睡眠模式状态可降低电流消耗,从而延长项目的运行时间,而不必频繁更换电池。在许多IOT项目中亦是如此,因为并不需要单片机或外部器件一直运行。
在启动示例代码中,我们用了两个按钮和两个LED来显示何时唤醒电路板以及何时按下中断按钮。当电路板被唤醒时,连接到引脚13的LED将开始闪烁。当按下连接到引脚11的按钮时,将使Arduino进入睡眠模式,而引脚13的LED也将停止闪烁。要唤醒电路板,只需按下连接到引脚2的按钮即可。按下此按钮时,连接到引脚10的LED将亮起,以表示中断已激活。

启动示例代码


  1. //These are the two libraries that are needed
  2. #include <avr/interrupt.h>
  3. #include <avr/sleep.h>

  4. /* Here we set up our inputs and outputs.  LEDs connected to pins 10 and 13 and pushbuttons attached to 2 and 12 */
  5. int ledPin = 13;
  6. int sleepPin = 12;
  7. int interruptPin = 10;
  8. int wakePin = 2;
  9. //sleepStatus is set up to keep track of the button input on pin 12.
  10. int sleepStatus = 0;

  11. void setup()
  12. {
  13.   pinMode(ledPin, OUTPUT);        
  14.   pinMode(interruptPin, OUTPUT);   
  15.   pinMode(sleepPin, INPUT_PULLUP);  
  16.   pinMode(wakePin, INPUT_PULLUP);

  17. /* Next we have to enable an interrupt.  

  18. The function is set up like this attachInterrupt(pin, function, triggerMode)  

  19. PIN – can be either a 0 to call out digital pin 2 or 1 to call out digital pin 3.

  20. FUNCTION – This is the function that will be run while in the interrupt

  21. TRIGGER MODE – this will be the mode of the interrupt pin.  
  22. It can be one the following:
  23.         LOW – a low level trigger
  24.         CHANGE – a change in level trigger
  25.         RISING – a rising edge trigger
  26.         FALLING – a falling edge trigger

  27. The IDLE sleep mode is the only mode that can use CHANGE, RISING, and FALLING modes.*/


  28. attachInterrupt(0, wakeUpNow, LOW);
  29. }

  30. void sleepNow()
  31. {
  32. //print message to serial monitor to let the user know board has gone to sleep
  33. Serial.println("going to sleep");
  34. //delay is added to allow user to get the full message on the serial monitor before going to sleep
  35. delay(15);

  36. //enables the sleep mode
  37. sleep_enable();

  38. // 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.  
  39. attachInterrupt(0,wakeUpNow, LOW);


  40. /* 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)

  41. The 5 different options for sleep modes, they are listed below from least power savings to most power savings:
  42.         SLEEP_MODE_IDLE
  43.         SLEEP_MODE_ADC
  44.         SLEEP_MODE_PWR_SAVE
  45.         SLEEP_MODE_STANDBY
  46.         SLEEP_MODE_PWR_DOWN

  47. For this sketch, we will be using the most power savings possible so we choose SLEEP_MODE_PWR_DOWN */

  48. //sleep mode is set here
  49. set_sleep_mode(SLEEP_MODE_PWR_DOWN);


  50. //This is where the device is actually put to sleep
  51. sleep_mode();

  52. //Here is where the device begins to wake up.

  53. //First thing that is done is to disable the sleep mode
  54. sleep_disable();

  55. //disables the interrupt on pin 2 so the wakeUpNow code will not be executed during normal run time
  56. detachInterrupt(0);

  57. //wait 1 second so the user can notice the LED signaling the interrupt
  58. delay(1000);
  59. digitalWrite (interruptPin, LOW);
  60. }

  61. void wakeUpNow()  //This is the code that runs when the interrupt button is pressed and interrupts are enabled
  62. {
  63. digitalWrite(interruptPin, HIGH);
  64. }

  65. void loop()
  66. {
  67. // turns the LED on
  68.   digitalWrite(ledPin, HIGH);   
  69. // waits for a second         
  70.   delay(1000);         
  71. // turns the LED off                  
  72.   digitalWrite(ledPin, LOW);
  73. // waits for a second            
  74.   delay(1000);         
  75. //This is where the sleep pin is read.  It is only active when the LED is off.                 
  76.   sleepStatus = digitalRead(sleepPin);   

  77. //If button is pressed, device will run the sleepNow function                                         
  78.   if (sleepStatus == LOW) {            
  79.       sleepNow();                     
  80.       }
  81. }

初始代码接线图

添加实时时钟以唤醒 Arduino


接下来,我们将添加一个RTC(而非按钮)来控制Arduino的睡眠模式和唤醒。我在此项目中使用的是来自Adafruit的物料1528-1598-ND 15。选择它的主要原因是其配备了内置中断。我原来使用的是DS1307分线板,但我很快发现它并不支持中断,因此无法用于此项目。我还针对此项目下载了一些库。以下是指向我下载.zip库文件的链接。

指向库的链接:
https://github.com/PaulStoffregen/Time——Arduino的计时库
https://github.com/JChristensen/DS3232RTC——适用于DS3231,包含唤醒Arduino所需的提醒

添加 RTC 的代码
  1. #include <avr/interrupt.h>
  2. #include <avr/sleep.h>
  3. #include <DS3232RTC.h>      // https://github.com/JChristensen/DS3232RTC  this is the library for the DS2331 RTC


  4. //RTC Module global variables

  5. // Sets the wakeup interval in minutes
  6. const int time_interval = 5;
  7. // LED connected to digital pin 13
  8. int ledPin = 13;            
  9. // active LOW, RTC will interrupt this pin momentarily to wake up
  10. int wakePin = 2;            


  11. void setup() {

  12.   // set up the serial monitor
  13. Serial.begin(9600);
  14. //Set up the led pin as an output
  15.   pinMode(ledPin, OUTPUT);
  16. //Set pin d2 to input using the built-in pullup resistor
  17.   pinMode(wakePin, INPUT_PULLUP);
  18. //turning LED on
  19.   digitalWrite(ledPin, HIGH);


  20. // These next few lines of code initialize the alarms to known values, clear the flags, and clear the alarm interrupt flags
  21.   RTC.setAlarm(ALM1_MATCH_DATE, 0, 0, 0, 1);
  22.   RTC.setAlarm(ALM2_MATCH_DATE, 0, 0, 0, 1);
  23.   RTC.alarm(ALARM_1);
  24.   RTC.alarm(ALARM_2);
  25.   RTC.alarmInterrupt(ALARM_1, false);
  26.   RTC.alarmInterrupt(ALARM_2, false);
  27.   RTC.squareWave(SQWAVE_NONE);

  28.   /* Uncomment this section to set the time on the RTC.  Make sure to comment out after the first time
  29.      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.

  30.     tmElements_t tm;
  31.    // the next few lines set the clock to the correct hour, minute, and second.  Remember 24 hour format so 4pm = hour 16
  32.     tm.Hour = 8;               
  33.     tm.Minute = 19;
  34.     tm.Second = 00;
  35.    
  36.    // set the correct date on the RTC
  37.     tm.Day = 04;
  38.     tm.Month = 5;
  39.     tm.Year = 2019 - 1970; // in order to set the year correctly, just change the 2019 and leave the “- 1970” to get the correct offset
  40.    
  41.     RTC.write(tm);    // write the date and time to the RTC
  42.          
  43.   */

  44.   
  45.   time_t t; //create a temporary time variable so we can set the time and read the time from the RTC
  46.   t = RTC.get(); //Gets the current time of the RTC
  47.   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
  48.   RTC.alarm(ALARM_1); // clear the alarm flag
  49.   RTC.squareWave(SQWAVE_NONE); // configure the INT/SQW pin for "interrupt" operation (disable square wave output)
  50.   RTC.alarmInterrupt(ALARM_1, true); // enable interrupt output for Alarm 1
  51. }



  52. void loop() {
  53.   delay(5000);//wait 5 seconds before going to sleep. When in a project, we would it is best to make this short as possible
  54.   sleepNow();  // run the sleepNow function

  55. }


  56. void sleepNow() {
  57.   sleep_enable();//Enabling sleep mode
  58.   attachInterrupt(0, wakeUpNow, LOW);//attaching a interrupt to pin d2
  59.   set_sleep_mode(SLEEP_MODE_PWR_DOWN);//Setting the sleep mode, in our case full sleep

  60.   digitalWrite(ledPin, LOW); //turning LED off
  61.   time_t t;// creates temporary time variable
  62.   t = RTC.get(); //gets current time from RTC
  63.   Serial.println("Sleep  Time: " + String(hour(t)) + ":" + String(minute(t)) + ":" + String(second(t))); //prints time stamp on serial monitor
  64.   delay(1000); //wait one second to allow the LED to be turned off before going to sleep
  65.   sleep_cpu();//heres where the Arduino is actually put to sleep
  66.   

  67.   Serial.println("just woke up!");//next line of code executed after the interrupt
  68.   digitalWrite(ledPin, HIGH); //turns the LED on
  69.   t = RTC.get();//get the new time from the RTC
  70.   Serial.println("WakeUp Time: " + String(hour(t)) + ":" + String(minute(t)) + ":" + String(second(t))); //Prints time stamp
  71.   
  72. //Set New Alarm
  73.   int alarmTime = 0; //temporary variable to store the new alarm time in minutes
  74.   
  75. //the next few lines are to roll the alarm over when it gets near the next hour
  76. if (minute(t) <= (60-time_interval))
  77.   {
  78.     alarmTime = minute(t) + time_interval;
  79.   }
  80.   else
  81.   {
  82.     alarmTime = (minute(t) + time_interval) - 60;
  83.   }

  84.   RTC.setAlarm(ALM1_MATCH_MINUTES , 0, alarmTime, 0, 0); // set new alarm


  85. // 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

  86. Serial.print("The next alarm will go off at: ");
  87.    
  88. //These lines are to print the correct hour that the next alarm will go off
  89. if ((minute(t) <= 60-time_interval) && (hour(t) <= 22))
  90.     {
  91.       Serial.print(hour(t));
  92.     }
  93.     else if ((minute(t) >= 60-time_interval) && (hour(t) <= 22))
  94.     {
  95.       Serial.print(hour(t) + 1);
  96.     }
  97.     else
  98.     {
  99.       Serial.print(0);
  100.     }
  101.   
  102.     Serial.print(":"); // print a colon symbol
  103.    
  104. //print the correct minute, including leading zero if less than 10
  105. if (alarmTime <= 9)
  106.     {
  107.       Serial.print("0");
  108.     }
  109.     Serial.println(alarmTime);

  110.   //Last thing we do is clear the alarm flag
  111.   RTC.alarm(ALARM_1);

  112. }

  113. void wakeUpNow()  //This is the code that happens when the interrupt is activated
  114. {
  115.   Serial.println("Interrrupt Fired");//Print message to serial monitor
  116.   sleep_disable();//Disable sleep mode
  117.   detachInterrupt(0); //Removes the interrupt from pin 2;
  118. }

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

添加温度 / 湿度传感器和土壤湿度传感器的代码

  1. #include <avr/interrupt.h>
  2. #include <avr/sleep.h>
  3. #include <DS3232RTC.h>      // https://github.com/JChristensen/DS3232RTC  this is the library for the DS2331 RTC
  4. #include <Wire.h>
  5. #include <Adafruit_MPL115A2.h>

  6. Adafruit_MPL115A2 mpl115a2;

  7. //RTC Module global variables

  8. // Sets the wakeup interval in minutes
  9. const int time_interval = 5;
  10. // LED connected to digital pin 13
  11. int ledPin = 13;            
  12. // active LOW, RTC will interrupt this pin momentarily to wake up
  13. int wakePin = 2;            
  14. // LED connected to digital pin 10
  15. int pumpLED = 10;
  16. //variable for storing the soil moisture value
  17. int moistureVal = 0;
  18. //input for the moisture sensor
  19. int soilPin = A0;
  20. //pin used to power the soil moisture sensor
  21. int soilPower = 7;

  22. void setup() {

  23. // set up the serial monitor
  24.   Serial.begin(9600);
  25. //Set up the led pin as an output
  26.   pinMode(ledPin, OUTPUT);
  27. //Set pin d2 to input using the built-in pullup resistor
  28.   pinMode(wakePin, INPUT_PULLUP);
  29. //turning LED on
  30.   digitalWrite(ledPin, HIGH);
  31. //Set D7 as an OUTPUT
  32.   pinMode(soilPower, OUTPUT);
  33. // Set to LOW so no power is flowing through the sensor
  34.   digitalWrite(soilPower, LOW);
  35. //Set D10 as an output
  36.   pinMode(pumpLED, OUTPUT);
  37. //starting the temp/humidity sensor
  38.   mpl115a2.begin();

  39. // These next few lines of code initialize the alarms to known values, clear the flags, and clear the alarm interrupt flags
  40.   RTC.setAlarm(ALM1_MATCH_DATE, 0, 0, 0, 1);
  41.   RTC.setAlarm(ALM2_MATCH_DATE, 0, 0, 0, 1);
  42.   RTC.alarm(ALARM_1);
  43.   RTC.alarm(ALARM_2);
  44.   RTC.alarmInterrupt(ALARM_1, false);
  45.   RTC.alarmInterrupt(ALARM_2, false);
  46.   RTC.squareWave(SQWAVE_NONE);

  47.   /* Uncomment this section to set the time on the RTC.  Make sure to comment out after the first time
  48.      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.

  49.     tmElements_t tm;
  50.    // the next few lines set the clock to the correct hour, minute, and second.  Remember 24 hour format so 4pm = hour 16
  51.     tm.Hour = 8;               
  52.     tm.Minute = 19;
  53.     tm.Second = 00;
  54.    
  55.    // set the correct date on the RTC
  56.     tm.Day = 04;
  57.     tm.Month = 5;
  58.     tm.Year = 2019 - 1970; // in order to set the year correctly, just change the 2019 and leave the “- 1970” to get the correct offset
  59.    
  60.     RTC.write(tm);    // write the date and time to the RTC
  61.          
  62.   */

  63.   
  64.   time_t t; //create a temporary time variable so we can set the time and read the time from the RTC
  65.   t = RTC.get(); //Gets the current time of the RTC
  66.   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
  67.   RTC.alarm(ALARM_1); // clear the alarm flag
  68.   RTC.squareWave(SQWAVE_NONE); // configure the INT/SQW pin for "interrupt" operation (disable square wave output)
  69.   RTC.alarmInterrupt(ALARM_1, true); // enable interrupt output for Alarm 1
  70. }



  71. void loop() {
  72.   delay(5000);//wait 5 seconds before going to sleep. When in a project, we would it is best to make this short as possible
  73.   sleepNow();  // run the sleepNow function

  74. }


  75. void sleepNow() {
  76.   sleep_enable();//Enabling sleep mode
  77.   attachInterrupt(0, wakeUpNow, LOW);//attaching a interrupt to pin d2
  78.   set_sleep_mode(SLEEP_MODE_PWR_DOWN);//Setting the sleep mode, in our case full sleep

  79.   digitalWrite(ledPin, LOW); //turning LED off
  80.   time_t t;// creates temporary time variable
  81.   t = RTC.get(); //gets current time from RTC
  82.   Serial.println("Sleep  Time: " + String(hour(t)) + ":" + String(minute(t)) + ":" + String(second(t))); //prints time stamp on serial monitor
  83.   delay(1000); //wait one second to allow the LED to be turned off before going to sleep
  84.   sleep_cpu();//heres where the Arduino is actually put to sleep

  85.   Serial.println("just woke up!");//next line of code executed after the interrupt
  86.   digitalWrite(ledPin, HIGH); //turns the LED on
  87.   t = RTC.get();//get the new time from the RTC
  88.   Serial.println("WakeUp Time: " + String(hour(t)) + ":" + String(minute(t)) + ":" + String(second(t))); //Prints time stamp

  89.   getTempAndSoil(); //Run the TempAndSoil function
  90.   
  91. //Set New Alarm
  92.   int alarmTime = 0; //temporary variable to store the new alarm time in minutes
  93.   
  94. //the next few lines are to roll the alarm over when it gets near the next hour
  95. if (minute(t) <= (60-time_interval))
  96.   {
  97.     alarmTime = minute(t) + time_interval;
  98.   }
  99.   else
  100.   {
  101.     alarmTime = (minute(t) + time_interval) - 60;
  102.   }

  103.   RTC.setAlarm(ALM1_MATCH_MINUTES , 0, alarmTime, 0, 0); // set new alarm


  104. // 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

  105. Serial.print("The next alarm will go off at: ");
  106.    
  107. //These lines are to print the correct hour that the next alarm will go off
  108. if ((minute(t) <= 60-time_interval) && (hour(t) <= 22))
  109.     {
  110.       Serial.print(hour(t));
  111.     }
  112.     else if ((minute(t) >= 60-time_interval) && (hour(t) <= 22))
  113.     {
  114.       Serial.print(hour(t) + 1);
  115.     }
  116.     else
  117.     {
  118.       Serial.print(0);
  119.     }
  120.   
  121.     Serial.print(":"); // print a colon symbol
  122.    
  123. //print the correct minute, including leading zero if less than 10
  124. if (alarmTime <= 9)
  125.     {
  126.       Serial.print("0");
  127.     }
  128.     Serial.println(alarmTime);

  129.   //Last thing we do is clear the alarm flag
  130.   RTC.alarm(ALARM_1);

  131. }

  132. void wakeUpNow()  //This is the code that happens when the interrupt is activated
  133. {
  134.   Serial.println("Interrrupt Fired");//Print message to serial monitor
  135.   sleep_disable();//Disable sleep mode
  136.   detachInterrupt(0); //Removes the interrupt from pin 2;
  137. }

  138. void getTempAndSoil()
  139. {
  140.   float  temperatureC = 0, tempF = 0;  //set up float variable to store Celsius and Fahrenheit temp

  141.   temperatureC = mpl115a2.getTemperature(); //get temperature from the sensor
  142.   tempF = (temperatureC * 1.8) + 32;  //convert the temperature from Celsius to Fahrenheit.
  143.   Serial.print("Temp (*F): "); Serial.print(tempF, 1); Serial.println(" *F");  //Print the temperature to the Serial Monitor
  144.   Serial.print("Soil Moisture = ");  
  145.   //get soil moisture value from the function below and print it
  146.   Serial.println(readSoil());
  147.   addWater();  // Run the add water function
  148. }

  149. int readSoil()
  150. {

  151.   digitalWrite(soilPower, HIGH);//turn the soil moisture sensor on
  152.   delay(10);//wait 10 milliseconds
  153.   moistureVal = analogRead(soilPin);//Read the value from sensor
  154.   digitalWrite(soilPower, LOW);//turn the soil moisture sensor off
  155.   return moistureVal;//send current moisture value
  156. }

  157. void addWater()
  158. {
  159.   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)
  160.   {

  161. //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.
  162.     for (int num = 0; num <= 10; num++)
  163.     {
  164.       digitalWrite(pumpLED, HIGH);
  165.       delay(1000);
  166.       digitalWrite(pumpLED, LOW);
  167.       delay(1000);
  168.       Serial.print("PUMP ON");
  169.       Serial.println(num);
  170.     }
  171.   }
  172. }
RTC 和传感器接线图




来源:digikey