我决定自行编写示例,说明如何将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);
  •     }
  •   }
  • }
  • 复制代码
    RTC 和传感器接线图




    来源:digikey