Merge branch 'feature/light_sleep_example' into 'master'

examples: add light sleep example

See merge request idf/esp-idf!3179
This commit is contained in:
Angus Gratton 2018-09-13 09:00:59 +08:00
commit 514487e9a1
9 changed files with 240 additions and 0 deletions

View file

@ -0,0 +1,8 @@
# The following four lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(MAIN_SRCS main/light_sleep_example_main.c)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(light_sleep_example)

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := light_sleep_example
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,67 @@
# Light Sleep Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example illustrates usage of light sleep mode. Unlike deep sleep mode, light sleep preserves the state of the memory, CPU, and peripherals. Execution of code on both CPUs is stopped when `esp_light_sleep_start` function is called. When the chip exits light sleep mode, execution continues at the point where it was stopped, and `esp_light_sleep_start` function returns.
The example enables the following wakeup sources:
- Timer: wake up the chip in 2 seconds
- EXT0: wake up the chip if a button attached to GPIO0 is pressed (i.e. if GPIO0 goes low)
The example also prints time spent in light sleep mode to illustrate that timekeeping continues while the chip is in light sleep.
## How to Use Example
### Hardware Required
This example can be used with any ESP32 development board. Most boards have a button attached to GPIO0, often labelled "BOOT". If the board does not have such button, an external button can be connected, along with a 10k pull-up resistor, and a 100nF capacitor to ground for debouncing.
### Configure the Project
Run `make menuconfig` and set serial port under Serial Flasher Options.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
Entering light sleep
Returned from light sleep, reason: timer, t=2014 ms, slept for 2000 ms
Entering light sleep
Returned from light sleep, reason: timer, t=4023 ms, slept for 2000 ms
Entering light sleep
Returned from light sleep, reason: pin, t=5297 ms, slept for 1266 ms
Waiting for GPIO0 to go high...
Entering light sleep
Returned from light sleep, reason: timer, t=10072 ms, slept for 2000 ms
Entering light sleep
Returned from light sleep, reason: timer, t=12080 ms, slept for 2000 ms
Entering light sleep
```
In the scenario above, the button attached to GPIO0 was pressed and held for about 3 seconds, after the 2nd wakeup from light sleep. The program has indicated the wakeup reason after each sleep iteration.
## Current Consumption
In this example, current consumption in light sleep mode is in the range of 0.8 — 1.1 mA. Current consumption in active mode is 28 — 32 mA. Average current consumption is 1.1 - 1.3 mA.
![Current consumption overview graph](image/light_sleep_scope.png)
![Current consumption in active mode](image/light_sleep_scope_zoom.png)
## Troubleshooting
If pressing the button attached to GPIO0 does not affect program behavior, check DTR/RTS configuration in the serial monitor. This is not necessary for IDF monitor, but for other tools it might be necessary to set DTR and RTS line state to "disabled" or "de-asserted".

View file

@ -0,0 +1,62 @@
from __future__ import print_function
import re
import os
import sys
import time
test_fw_path = os.getenv('TEST_FW_PATH')
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import TinyFW
import IDF
ENTERING_SLEEP_STR = 'Entering light sleep'
EXIT_SLEEP_REGEX = re.compile(r'Returned from light sleep, reason: (\w+), t=(\d+) ms, slept for (\d+) ms')
WAITING_FOR_GPIO_STR = 'Waiting for GPIO0 to go high...'
WAKEUP_INTERVAL_MS = 2000
@IDF.idf_example_test(env_tag='Example_WIFI')
def test_examples_system_light_sleep(env, extra_data):
dut = env.get_dut('light_sleep_example', 'examples/system/light_sleep')
dut.start_app()
# Ensure DTR and RTS are de-asserted for proper control of GPIO0
dut.port_inst.setDTR(False)
dut.port_inst.setRTS(False)
# enter sleep first time
dut.expect(ENTERING_SLEEP_STR, timeout=30)
# don't check timing here, might be cache dependent
dut.expect(EXIT_SLEEP_REGEX)
print('Got first sleep period')
# enter sleep second time
dut.expect(ENTERING_SLEEP_STR)
groups = dut.expect(EXIT_SLEEP_REGEX)
print('Got second sleep period, wakeup from {}, slept for {}'.format(groups[0], groups[2]))
# sleep time error should be less than 1ms
assert(groups[0] == 'timer' and int(groups[2]) == WAKEUP_INTERVAL_MS)
# this time we'll test gpio wakeup
dut.expect(ENTERING_SLEEP_STR)
print('Pulling GPIO0 low using DTR')
dut.port_inst.setDTR(True)
time.sleep(1)
groups = dut.expect(EXIT_SLEEP_REGEX)
print('Got third sleep period, wakeup from {}, slept for {}'.format(groups[0], groups[2]))
assert(groups[0] == 'pin' and int(groups[2]) < WAKEUP_INTERVAL_MS)
dut.expect(WAITING_FOR_GPIO_STR)
print('Is waiting for GPIO...')
dut.port_inst.setDTR(False)
dut.expect(ENTERING_SLEEP_STR)
print('Went to sleep again')
groups = dut.expect(EXIT_SLEEP_REGEX)
assert(groups[0] == 'timer' and int(groups[2]) == WAKEUP_INTERVAL_MS)
print('Woke up from timer again')
if __name__ == '__main__':
test_examples_system_light_sleep()

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View file

@ -0,0 +1,90 @@
/* Light sleep example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "rom/uart.h"
#include "driver/rtc_io.h"
/* Most development boards have "boot" button attached to GPIO0.
* You can also change this to another pin.
*/
#define BUTTON_GPIO_NUM_DEFAULT 0
/* "Boot" button on GPIO0 is active low */
#define BUTTON_WAKEUP_LEVEL_DEFAULT 0
void app_main()
{
/* Configure the button GPIO as input, enable wakeup */
const int button_gpio_num = BUTTON_GPIO_NUM_DEFAULT;
const int wakeup_level = BUTTON_WAKEUP_LEVEL_DEFAULT;
gpio_config_t config = {
.pin_bit_mask = BIT64(button_gpio_num),
.mode = GPIO_MODE_INPUT
};
ESP_ERROR_CHECK(gpio_config(&config));
gpio_wakeup_enable(button_gpio_num,
wakeup_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
while (true) {
/* Wake up in 2 seconds, or when button is pressed */
esp_sleep_enable_timer_wakeup(2000000);
esp_sleep_enable_gpio_wakeup();
/* Wait until GPIO goes high */
if (rtc_gpio_get_level(button_gpio_num) == wakeup_level) {
printf("Waiting for GPIO%d to go high...\n", button_gpio_num);
do {
vTaskDelay(pdMS_TO_TICKS(10));
} while (rtc_gpio_get_level(button_gpio_num) == wakeup_level);
}
printf("Entering light sleep\n");
/* To make sure the complete line is printed before entering sleep mode,
* need to wait until UART TX FIFO is empty:
*/
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
/* Get timestamp before entering sleep */
int64_t t_before_us = esp_timer_get_time();
/* Enter sleep mode */
esp_light_sleep_start();
/* Execution continues here after wakeup */
/* Get timestamp after waking up from sleep */
int64_t t_after_us = esp_timer_get_time();
/* Determine wake up reason */
const char* wakeup_reason;
switch (esp_sleep_get_wakeup_cause()) {
case ESP_SLEEP_WAKEUP_TIMER:
wakeup_reason = "timer";
break;
case ESP_SLEEP_WAKEUP_GPIO:
wakeup_reason = "pin";
break;
default:
wakeup_reason = "other";
break;
}
printf("Returned from light sleep, reason: %s, t=%lld ms, slept for %lld ms\n",
wakeup_reason, t_after_us / 1000, (t_after_us - t_before_us) / 1000);
}
}

View file

@ -0,0 +1 @@
CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y