25
25
*/
26
26
27
27
#include "py/obj.h"
28
+ #include "py/reload.h"
28
29
#include "py/runtime.h"
29
30
30
31
#include "shared-bindings/alarm/__init__.h"
31
32
#include "shared-bindings/alarm/pin/PinAlarm.h"
32
33
#include "shared-bindings/alarm/time/TimeAlarm.h"
34
+ #include "shared-bindings/supervisor/Runtime.h"
33
35
#include "shared-bindings/time/__init__.h"
34
- #include "supervisor/shared/rgb_led_status .h"
36
+ #include "supervisor/shared/autoreload .h"
35
37
#include "supervisor/shared/workflow.h"
36
38
37
- // Wait this long to see if USB is being connected (enumeration starting).
38
- #define CIRCUITPY_USB_CONNECTING_DELAY 1
39
- // Wait this long before going into deep sleep if connected. This
40
- // allows the user to ctrl-C before deep sleep starts.
41
- #define CIRCUITPY_USB_CONNECTED_DEEP_SLEEP_DELAY 5
39
+ // Wait this long imediately after startup to see if we are connected to USB.
40
+ #define CIRCUITPY_USB_CONNECTED_SLEEP_DELAY 5
42
41
43
- //| """Power-saving light and deep sleep. Alarms can be set to wake up from sleep.
42
+ //| """Alarms and sleep
44
43
//|
45
- //| The `alarm` module provides sleep related functionality. There are two supported levels of
46
- //| sleep, light and deep.
44
+ //| Provides alarms that trigger based on time intervals or on external events, such as pin
45
+ //| changes.
46
+ //| The program can simply wait for these alarms, or go into a sleep state and
47
+ //| and be awoken when they trigger.
47
48
//|
48
- //| Light sleep leaves the CPU and RAM powered so that CircuitPython can resume where it left off
49
- //| after being woken up. CircuitPython automatically goes into a light sleep when `time.sleep()` is
50
- //| called. To light sleep until a non-time alarm use `alarm.sleep_until_alarms()`. Any active
51
- //| peripherals, such as I2C, are left on.
49
+ //| There are two supported levels of sleep: light sleep and deep sleep.
52
50
//|
53
- //| Deep sleep shuts down power to nearly all of the chip including the CPU and RAM. This can save
54
- //| a more significant amount of power, but CircuitPython must start ``code.py`` from the beginning when
51
+ //| Light sleep leaves the CPU and RAM powered so the program can resume after sleeping.
52
+ //|
53
+ //| *However, note that on some platforms, light sleep will shut down some communications, including
54
+ //| WiFi and/or Bluetooth.*
55
+ //|
56
+ //| Deep sleep shuts down power to nearly all of the microcontroller including the CPU and RAM. This can save
57
+ //| a more significant amount of power, but CircuitPython must restart ``code.py`` from the beginning when
55
58
//| awakened.
56
59
//| """
57
60
58
61
//|
59
62
//| wake_alarm: Alarm
60
- //| """The most recent alarm to wake us up from a sleep (light or deep.) """
63
+ //| """The most recently triggered alarm. If CircuitPython was sleeping, the alarm the woke it from sleep. """
61
64
//|
65
+
66
+ // wake_alarm is implemented as a dictionary entry, so there's no code here.
67
+
62
68
void validate_objs_are_alarms (size_t n_args , const mp_obj_t * objs ) {
63
69
for (size_t i = 0 ; i < n_args ; i ++ ) {
64
70
if (MP_OBJ_IS_TYPE (objs [i ], & alarm_pin_pin_alarm_type ) ||
@@ -69,9 +75,36 @@ void validate_objs_are_alarms(size_t n_args, const mp_obj_t *objs) {
69
75
}
70
76
}
71
77
78
+ //| def wait_until_alarms(*alarms: Alarm) -> Alarm:
79
+ //| """Wait for one of the alarms to trigger. The triggering alarm is returned.
80
+ //| is returned, and is also available as `alarm.wake_alarm`. Nothing is shut down
81
+ //| or interrupted. Power consumption will be reduced if possible.
82
+ //|
83
+ //| If no alarms are specified, return immediately.
84
+ //| """
85
+ //| ...
86
+ //|
87
+ STATIC mp_obj_t alarm_wait_until_alarms (size_t n_args , const mp_obj_t * args ) {
88
+ validate_objs_are_alarms (n_args , args );
89
+ common_hal_alarm_wait_until_alarms (n_args , args );
90
+ return mp_const_none ;
91
+ }
92
+ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (alarm_wait_until_alarms_obj , 1 , MP_OBJ_FUN_ARGS_MAX , alarm_wait_until_alarms );
93
+
72
94
//| def sleep_until_alarms(*alarms: Alarm) -> Alarm:
73
95
//| """Go into a light sleep until awakened one of the alarms. The alarm causing the wake-up
74
- //| is returned, and is also available as `alarm.wake_alarm`.
96
+ //| is returned, and is also available as `alarm.wake_alarm`.
97
+ //|
98
+ //| Some functionality may be shut down during sleep. On ESP32-S2, WiFi is turned off,
99
+ //| and existing connections are broken.
100
+ //|
101
+ //| If no alarms are specified, return immediately.
102
+ //|
103
+ //| **If CircuitPython is connected to a host computer,** `alarm.sleep_until_alarms()`
104
+ //| **does not go into light sleep.**
105
+ //| Instead, light sleep is simulated by doing `alarm.wait_until_alarms()`,
106
+ //| This allows the user to interrupt an existing program with ctrl-C,
107
+ //| and to edit the files in CIRCUITPY, which would not be possible in true light sleep
75
108
//| """
76
109
//| ...
77
110
//|
@@ -84,47 +117,60 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alarm_sleep_until_alarms_obj, 1, MP_OBJ_FUN_
84
117
85
118
//| def exit_and_deep_sleep_until_alarms(*alarms: Alarm) -> None:
86
119
//| """Exit the program and go into a deep sleep, until awakened by one of the alarms.
120
+ //| This function does not return.
87
121
//|
88
122
//| When awakened, the microcontroller will restart and will run ``boot.py`` and ``code.py``
89
123
//| from the beginning.
90
124
//|
91
- //| An alarm equivalent to the one that caused the wake-up is available as `alarm.wake_alarm`.
125
+ //| After restart, an alarm *equivalent* to the one that caused the wake-up
126
+ //| will be available as `alarm.wake_alarm`.
92
127
//| Its type and/or attributes may not correspond exactly to the original alarm.
93
128
//| For time-base alarms, currently, an `alarm.time.TimeAlarm()` is created.
94
129
//|
95
130
//| If no alarms are specified, the microcontroller will deep sleep until reset.
96
131
//|
97
- //| If CircuitPython is unconnected to a host computer, go into deep sleep immediately.
98
- //| But if it already connected or in the process of connecting to a host computer, wait at least
99
- //| five seconds after starting code.py before entering deep sleep.
100
- //| This allows interrupting a program that would otherwise go into deep sleep too quickly
101
- //| to interrupt from the keyboard.
132
+ //| **If CircuitPython is connected to a host computer, `alarm.exit_and_deep_sleep_until_alarms()`
133
+ //| does not go into deep sleep.**
134
+ //| Instead, deep sleep is simulated by first doing `alarm.wait_until_alarms()`,
135
+ //| and then, when an alarm triggers, by restarting CircuitPython.
136
+ //| This allows the user to interrupt an existing program with ctrl-C,
137
+ //| and to edit the files in CIRCUITPY, which would not be possible in true deep sleep.
138
+ //|
139
+ //| Here is skeletal example that deep-sleeps and restarts every 60 seconds:
140
+ //|
141
+ //| .. code-block:: python
142
+ //|
143
+ //| import alarm
144
+ //| import time
145
+ //|
146
+ //| print("Waking up")
147
+ //|
148
+ //| # Set an alarm for 60 seconds from now.
149
+ //| time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 60)
150
+ //|
151
+ //| # Deep sleep until the alarm goes off. Then restart the program.
152
+ //| alarm.exit_and_deep_sleep_until_alarms(time_alarm)
102
153
//| """
103
154
//| ...
104
155
//|
105
156
STATIC mp_obj_t alarm_exit_and_deep_sleep_until_alarms (size_t n_args , const mp_obj_t * args ) {
106
157
validate_objs_are_alarms (n_args , args );
107
158
108
- int64_t connecting_delay_msec = CIRCUITPY_USB_CONNECTING_DELAY * 1024 - supervisor_ticks_ms64 ();
159
+ // Make sure we have been awake long enough for USB to connect (enumeration delay).
160
+ int64_t connecting_delay_msec = CIRCUITPY_USB_CONNECTED_SLEEP_DELAY * 1024 - supervisor_ticks_ms64 ();
109
161
if (connecting_delay_msec > 0 ) {
110
162
common_hal_time_delay_ms (connecting_delay_msec * 1000 / 1024 );
111
163
}
112
164
113
- // If connected, wait for the program to be running at least as long as
114
- // CIRCUITPY_USB_CONNECTED_DEEP_SLEEP_DELAY. This allows a user to ctrl-C the running
115
- // program in case it is in a tight deep sleep loop that would otherwise be difficult
116
- // or impossible to interrupt.
117
- // Indicate that we're delaying with the SAFE_MODE color.
118
- int64_t delay_before_sleeping_msec =
119
- supervisor_ticks_ms64 () - CIRCUITPY_USB_CONNECTED_DEEP_SLEEP_DELAY * 1000 ;
120
- if (supervisor_workflow_connecting () && delay_before_sleeping_msec > 0 ) {
121
- temp_status_color (SAFE_MODE );
122
- common_hal_time_delay_ms (delay_before_sleeping_msec );
123
- clear_temp_status ();
165
+ if (supervisor_workflow_active ()) {
166
+ common_hal_alarm_wait_until_alarms (n_args , args );
167
+ reload_requested = true;
168
+ supervisor_set_run_reason (RUN_REASON_STARTUP );
169
+ mp_raise_reload_exception ();
170
+ } else {
171
+ common_hal_alarm_exit_and_deep_sleep_until_alarms (n_args , args );
172
+ // Does not return.
124
173
}
125
-
126
- common_hal_alarm_exit_and_deep_sleep_until_alarms (n_args , args );
127
- // Does not return.
128
174
return mp_const_none ;
129
175
}
130
176
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (alarm_exit_and_deep_sleep_until_alarms_obj , 1 , MP_OBJ_FUN_ARGS_MAX , alarm_exit_and_deep_sleep_until_alarms );
0 commit comments