summaryrefslogtreecommitdiff
path: root/shared-bindings
diff options
context:
space:
mode:
authorDan Halbert <halbert@halwitz.org>2020-11-26 22:06:37 -0500
committerDan Halbert <halbert@halwitz.org>2020-11-26 22:06:37 -0500
commit104a089677d62de3554deea7850796dfbb29b1fc (patch)
tree35580c830283fc5eaea1e01623892eb24224b444 /shared-bindings
parentef0830bfe241b544b05bd3081f5bba03af57fe0d (diff)
deep sleep working; deep sleep delay when connected
Diffstat (limited to 'shared-bindings')
-rw-r--r--shared-bindings/_typing/__init__.pyi4
-rw-r--r--shared-bindings/alarm/__init__.c43
-rw-r--r--shared-bindings/alarm/__init__.h3
-rw-r--r--shared-bindings/alarm/pin/PinAlarm.c2
-rw-r--r--shared-bindings/alarm/time/TimeAlarm.c78
-rw-r--r--shared-bindings/microcontroller/__init__.h2
6 files changed, 105 insertions, 27 deletions
diff --git a/shared-bindings/_typing/__init__.pyi b/shared-bindings/_typing/__init__.pyi
index 02839ab47..271693686 100644
--- a/shared-bindings/_typing/__init__.pyi
+++ b/shared-bindings/_typing/__init__.pyi
@@ -54,12 +54,12 @@ FrameBuffer = Union[rgbmatrix.RGBMatrix]
"""
Alarm = Union[
- alarm.pin.PinAlarm, alarm.time.DurationAlarm
+ alarm.pin.PinAlarm, alarm.time.TimeAlarm
]
"""Classes that implement alarms for sleeping and asynchronous notification.
- `alarm.pin.PinAlarm`
- - `alarm.time.DurationAlarm`
+ - `alarm.time.TimeAlarm`
You can use these alarms to wake from light or deep sleep.
"""
diff --git a/shared-bindings/alarm/__init__.c b/shared-bindings/alarm/__init__.c
index 4aa6c8457..4c2189c0d 100644
--- a/shared-bindings/alarm/__init__.c
+++ b/shared-bindings/alarm/__init__.c
@@ -30,6 +30,15 @@
#include "shared-bindings/alarm/__init__.h"
#include "shared-bindings/alarm/pin/PinAlarm.h"
#include "shared-bindings/alarm/time/TimeAlarm.h"
+#include "shared-bindings/time/__init__.h"
+#include "supervisor/shared/rgb_led_status.h"
+#include "supervisor/shared/workflow.h"
+
+// Wait this long to see if USB is being connected (enumeration starting).
+#define CIRCUITPY_USB_CONNECTING_DELAY 1
+// Wait this long before going into deep sleep if connected. This
+// allows the user to ctrl-C before deep sleep starts.
+#define CIRCUITPY_USB_CONNECTED_DEEP_SLEEP_DELAY 5
//| """Power-saving light and deep sleep. Alarms can be set to wake up from sleep.
//|
@@ -44,16 +53,12 @@
//| Deep sleep shuts down power to nearly all of the chip including the CPU and RAM. This can save
//| a more significant amount of power, but CircuitPython must start ``code.py`` from the beginning when
//| awakened.
+//| """
//|
-//| An error includes an uncaught exception, or sys.exit() called with a non-zero argument
-//|
-//| To set alarms for deep sleep use `alarm.set_deep_sleep_alarms()` they will apply to next deep sleep only."""
-//|
//| wake_alarm: Alarm
//| """The most recent alarm to wake us up from a sleep (light or deep.)"""
//|
-
void validate_objs_are_alarms(size_t n_args, const mp_obj_t *objs) {
for (size_t i = 0; i < n_args; i++) {
if (MP_OBJ_IS_TYPE(objs[i], &alarm_pin_pin_alarm_type) ||
@@ -88,12 +93,38 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alarm_sleep_until_alarms_obj, 1, MP_OBJ_FUN_
//| For time-base alarms, currently, an `alarm.time.TimeAlarm()` is created.
//|
//| If no alarms are specified, the microcontroller will deep sleep until reset.
+//|
+//| If CircuitPython is unconnected to a host computer, go into deep sleep immediately.
+//| But if it already connected or in the process of connecting to a host computer, wait at least
+//| five seconds after starting code.py before entering deep sleep.
+//| This allows interrupting a program that would otherwise go into deep sleep too quickly
+//| to interrupt from the keyboard.
//| """
//| ...
//|
STATIC mp_obj_t alarm_exit_and_deep_sleep_until_alarms(size_t n_args, const mp_obj_t *args) {
validate_objs_are_alarms(n_args, args);
- common_hal_exit_and_deep_sleep_until_alarms(n_args, args);
+
+ int64_t connecting_delay_msec = CIRCUITPY_USB_CONNECTING_DELAY * 1024 - supervisor_ticks_ms64();
+ if (connecting_delay_msec > 0) {
+ common_hal_time_delay_ms(connecting_delay_msec * 1000 / 1024);
+ }
+
+ // If connected, wait for the program to be running at least as long as
+ // CIRCUITPY_USB_CONNECTED_DEEP_SLEEP_DELAY. This allows a user to ctrl-C the running
+ // program in case it is in a tight deep sleep loop that would otherwise be difficult
+ // or impossible to interrupt.
+ // Indicate that we're delaying with the SAFE_MODE color.
+ int64_t delay_before_sleeping_msec =
+ supervisor_ticks_ms64() - CIRCUITPY_USB_CONNECTED_DEEP_SLEEP_DELAY * 1000;
+ if (supervisor_workflow_connecting() && delay_before_sleeping_msec > 0) {
+ temp_status_color(SAFE_MODE);
+ common_hal_time_delay_ms(delay_before_sleeping_msec);
+ clear_temp_status();
+ }
+
+ common_hal_alarm_exit_and_deep_sleep_until_alarms(n_args, args);
+ // Does not return.
return mp_const_none;
}
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);
diff --git a/shared-bindings/alarm/__init__.h b/shared-bindings/alarm/__init__.h
index 0f084c78e..968136345 100644
--- a/shared-bindings/alarm/__init__.h
+++ b/shared-bindings/alarm/__init__.h
@@ -32,8 +32,7 @@
#include "common-hal/alarm/__init__.h"
extern mp_obj_t common_hal_alarm_sleep_until_alarms(size_t n_alarms, const mp_obj_t *alarms);
-extern bool common_hal_alarm_enable_deep_sleep_alarms(void);
-extern void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms);
+extern void common_hal_alarm_exit_and_deep_sleep_until_alarms(size_t n_alarms, const mp_obj_t *alarms);
// Used by wake-up code.
extern void common_hal_alarm_set_wake_alarm(mp_obj_t alarm);
diff --git a/shared-bindings/alarm/pin/PinAlarm.c b/shared-bindings/alarm/pin/PinAlarm.c
index ff7b19ca1..fadd1b0d4 100644
--- a/shared-bindings/alarm/pin/PinAlarm.c
+++ b/shared-bindings/alarm/pin/PinAlarm.c
@@ -41,7 +41,7 @@
//| def __init__(self, *pins: microcontroller.Pin, value: bool, all_same_value: bool = False, edge: bool = False, pull: bool = False) -> None:
//| """Create an alarm triggered by a `microcontroller.Pin` level. The alarm is not active
//| until it is passed to an `alarm`-enabling function, such as `alarm.sleep_until_alarms()` or
-//| `alarm.set_deep_sleep_alarms()`.
+//| `alarm.exit_and_deep_sleep_until_alarms()`.
//|
//| :param microcontroller.Pin \*pins: The pins to monitor. On some ports, the choice of pins
//| may be limited due to hardware restrictions, particularly for deep-sleep alarms.
diff --git a/shared-bindings/alarm/time/TimeAlarm.c b/shared-bindings/alarm/time/TimeAlarm.c
index 864ece284..6339b850c 100644
--- a/shared-bindings/alarm/time/TimeAlarm.c
+++ b/shared-bindings/alarm/time/TimeAlarm.c
@@ -24,25 +24,32 @@
* THE SOFTWARE.
*/
-#include "shared-bindings/board/__init__.h"
-#include "shared-bindings/microcontroller/__init__.h"
-#include "shared-bindings/alarm/time/TimeAlarm.h"
-
#include "py/nlr.h"
#include "py/obj.h"
#include "py/objproperty.h"
#include "py/runtime.h"
+
+#include "shared-bindings/time/__init__.h"
+#include "shared-bindings/alarm/time/TimeAlarm.h"
+
#include "supervisor/shared/translate.h"
+#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
+mp_obj_t MP_WEAK rtc_get_time_source_time(void) {
+ mp_raise_RuntimeError(translate("RTC is not supported on this board"));
+}
+#endif
+
//| class TimeAlarm:
-//| """Trigger an alarm when `time.monotonic()` reaches the given value."""
+//| """Trigger an alarm when the specified time is reached."""
//|
-//| def __init__(self, monotonic_time: float) -> None:
+//| def __init__(self, monotonic_time: Optional[Float] = None, epoch_time: Optional[int] = None) -> None:
//| """Create an alarm that will be triggered when `time.monotonic()` would equal
-//| ``monotonic_time``.
+//| ``monotonic_time``, or when `time.time()` would equal ``epoch_time``.
+//| Only one of the two arguments can be given.
//| The alarm is not active until it is passed to an
//| `alarm`-enabling function, such as `alarm.sleep_until_alarms()` or
-//| `alarm.set_deep_sleep_alarms()`.
+//| `alarm.exit_and_deep_sleep_until_alarms()`.
//|
//| If the given time is in the past when sleep occurs, the alarm will be triggered
//| immediately.
@@ -50,21 +57,64 @@
//| ...
//|
STATIC mp_obj_t alarm_time_time_alarm_make_new(const mp_obj_type_t *type,
- mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
- mp_arg_check_num(n_args, kw_args, 1, 1, false);
-
+ mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
alarm_time_time_alarm_obj_t *self = m_new_obj(alarm_time_time_alarm_obj_t);
self->base.type = &alarm_time_time_alarm_type;
- mp_float_t secs = mp_obj_get_float(args[0]);
+ enum { ARG_monotonic_time, ARG_epoch_time };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_monotonic_time, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_epoch_time, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ bool have_monotonic = args[ARG_monotonic_time].u_obj != mp_const_none;
+ bool have_epoch = args[ARG_epoch_time].u_obj != mp_const_none;
+
+ if (!(have_monotonic ^ have_epoch)) {
+ mp_raise_ValueError(translate("Supply one of monotonic_time or epoch_time"));
+ }
+
+ mp_float_t monotonic_time = 0; // To avoid compiler warning.
+ if (have_monotonic) {
+ monotonic_time = mp_obj_get_float(args[ARG_monotonic_time].u_obj);
+ }
+
+ mp_float_t monotonic_time_now = common_hal_time_monotonic_ms() / 1000.0;
+
+ if (have_epoch) {
+#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE
+ mp_raise_ValueError(translate("epoch_time not supported on this board"));
+#else
+ mp_uint_t epoch_time_secs = mp_obj_int_get_checked(args[ARG_epoch_time].u_obj);
+
+ timeutils_struct_time_t tm;
+ struct_time_to_tm(rtc_get_time_source_time(), &tm);
+ mp_uint_t epoch_secs_now = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ // How far in the future (in secs) is the requested time?
+ mp_int_t epoch_diff = epoch_time_secs - epoch_secs_now;
+ // Convert it to a future monotonic time.
+ monotonic_time = monotonic_time_now + epoch_diff;
+#endif
+ }
+
+ if (monotonic_time < monotonic_time_now) {
+ mp_raise_ValueError(translate("Time is in the past."));
+ }
- common_hal_alarm_time_time_alarm_construct(self, secs);
+ common_hal_alarm_time_time_alarm_construct(self, monotonic_time);
return MP_OBJ_FROM_PTR(self);
}
//| monotonic_time: float
-//| """The time at which to trigger, based on the `time.monotonic()` clock."""
+//| """When this time is reached, the alarm will trigger, based on the `time.monotonic()` clock.
+//| The time may be given as ``epoch_time`` in the constructor, but it is returned
+//| by this property only as a `time.monotonic()` time.
+//| """
//|
STATIC mp_obj_t alarm_time_time_alarm_obj_get_monotonic_time(mp_obj_t self_in) {
alarm_time_time_alarm_obj_t *self = MP_OBJ_TO_PTR(self_in);
diff --git a/shared-bindings/microcontroller/__init__.h b/shared-bindings/microcontroller/__init__.h
index 87284fc2e..ac71de424 100644
--- a/shared-bindings/microcontroller/__init__.h
+++ b/shared-bindings/microcontroller/__init__.h
@@ -43,8 +43,6 @@ extern void common_hal_mcu_enable_interrupts(void);
extern void common_hal_mcu_on_next_reset(mcu_runmode_t runmode);
extern void common_hal_mcu_reset(void);
-extern void NORETURN common_hal_mcu_deep_sleep(void);
-
extern const mp_obj_dict_t mcu_pin_globals;
extern const mcu_processor_obj_t common_hal_mcu_processor_obj;