1+ #include <time.h>
2+ #include <sys/time.h>
3+
4+ #include "pico/stdlib.h"
5+ #include "pico/util/datetime.h"
6+ #include "powman.h"
7+
8+ #include "mphalport.h"
9+ #include "py/runtime.h"
10+ #include "shared/timeutils/timeutils.h"
11+
12+ #define GPIO_I2C_POWER 2
13+ #define GPIO_WAKE 3
14+ #define GPIO_EXT_CLK 12
15+ #define GPIO_LED_A 10
16+
17+ enum {
18+ WAKE_BUTTON_A = 0x00 ,
19+ WAKE_BUTTON_B ,
20+ WAKE_BUTTON_C ,
21+ WAKE_TIMER = 0xf0 ,
22+ WAKE_UNKNOWN = 0xff ,
23+ };
24+
25+ mp_obj_t _sleep_get_wake_reason (void ) {
26+ uint8_t wake_reason = powman_get_wake_reason ();
27+ if (wake_reason & POWMAN_WAKE_ALARM ) {
28+ return MP_ROM_INT (WAKE_TIMER );
29+ }
30+ if (wake_reason & POWMAN_WAKE_PWRUP0 ) return MP_ROM_INT (WAKE_BUTTON_A );
31+ if (wake_reason & POWMAN_WAKE_PWRUP1 ) return MP_ROM_INT (WAKE_BUTTON_B );
32+ if (wake_reason & POWMAN_WAKE_PWRUP2 ) return MP_ROM_INT (WAKE_BUTTON_C );
33+ return MP_ROM_INT (WAKE_UNKNOWN );
34+ }
35+ static MP_DEFINE_CONST_FUN_OBJ_0 (_sleep_get_wake_reason_obj , _sleep_get_wake_reason ) ;
36+
37+ /*! \brief Send system to sleep until the specified GPIO changes
38+ *
39+ * \param gpio_pin The pin to provide the wake up
40+ * \param edge true for leading edge, false for trailing edge
41+ * \param high true for active high, false for active low
42+ * \param timeout wakeup after timeout milliseconds if no edge occurs
43+ */
44+ mp_obj_t _sleep_goto_dormant_until_pin (size_t n_args , const mp_obj_t * args ) {
45+ enum { ARG_pin , ARG_edge , ARG_high , ARG_timeout };
46+
47+ uint pin = UINT16_MAX ;
48+ if (args [ARG_pin ] != mp_const_none ) {
49+ pin = mp_hal_get_pin_obj (args [ARG_pin ]);
50+ }
51+ bool edge = mp_obj_is_true (args [ARG_edge ]);
52+ bool high = mp_obj_is_true (args [ARG_high ]);
53+ uint64_t timeout_ms = 0 ;
54+
55+ if (n_args == 4 ) {
56+ timeout_ms = (uint64_t )mp_obj_get_float (args [ARG_timeout ]) * 1000 ;
57+ }
58+
59+ powman_init ();
60+
61+ if (pin != UINT16_MAX ) {
62+ powman_setup_gpio_wakeup (POWMAN_WAKE_PWRUP0_CH , pin , edge , high , 1000 );
63+ } else {
64+ int err = 0 ;
65+ err = powman_setup_gpio_wakeup (POWMAN_WAKE_PWRUP0_CH , 12 , edge , high , 1000 ); // Tufty Button A
66+ if (err == -1 ) {mp_raise_msg (& mp_type_RuntimeError , MP_ERROR_TEXT ("Timeout waiting for Button A" ));}
67+ err = powman_setup_gpio_wakeup (POWMAN_WAKE_PWRUP1_CH , 13 , edge , high , 1000 ); // Tufty Button B
68+ if (err == -1 ) {mp_raise_msg (& mp_type_RuntimeError , MP_ERROR_TEXT ("Timeout waiting for Button B" ));}
69+ err = powman_setup_gpio_wakeup (POWMAN_WAKE_PWRUP2_CH , 14 , edge , high , 1000 ); // Tufty Button C
70+ if (err == -1 ) {mp_raise_msg (& mp_type_RuntimeError , MP_ERROR_TEXT ("Timeout waiting for Button C" ));}
71+ }
72+
73+ // power off
74+ int rc = 0 ;
75+ if (timeout_ms > 0 ) {
76+ absolute_time_t timeout = make_timeout_time_ms (timeout_ms );
77+ rc = powman_off_until_time (timeout );
78+ } else {
79+ rc = powman_off ();
80+ }
81+ hard_assert (rc == PICO_OK );
82+ hard_assert (false); // should never get here!
83+
84+ return mp_const_none ;
85+ }
86+ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (_sleep_goto_dormant_until_pin_obj , 3 , 4 , _sleep_goto_dormant_until_pin ) ;
87+
88+ /*! \brief Send system to dormant until the specified time, note for RP2040 the RTC must be driven by an external clock
89+ *
90+ * \param ts The time to wake up
91+ * \param callback Function to call on wakeup.
92+ */
93+ mp_obj_t _sleep_goto_dormant_until (mp_obj_t absolute_time_in ) {
94+ // Borrowed from https://github.com/micropython/micropython/blob/master/ports/rp2/machine_rtc.c#L83C1-L96
95+ mp_obj_t * items ;
96+ mp_obj_get_array_fixed_n (absolute_time_in , 8 , & items );
97+ timeutils_struct_time_t tm = {
98+ .tm_year = mp_obj_get_int (items [0 ]),
99+ .tm_mon = mp_obj_get_int (items [1 ]),
100+ .tm_mday = mp_obj_get_int (items [2 ]),
101+ .tm_hour = mp_obj_get_int (items [4 ]),
102+ .tm_min = mp_obj_get_int (items [5 ]),
103+ .tm_sec = mp_obj_get_int (items [6 ]),
104+ };
105+ struct timespec ts = { 0 , 0 };
106+ ts .tv_sec = timeutils_seconds_since_epoch (tm .tm_year , tm .tm_mon , tm .tm_mday , tm .tm_hour , tm .tm_min , tm .tm_sec );
107+
108+ int rc = powman_off_until_time (timespec_to_ms (& ts ));
109+ hard_assert (rc == PICO_OK );
110+ hard_assert (false); // should never get here!
111+
112+ return mp_const_none ;
113+ }
114+ static MP_DEFINE_CONST_FUN_OBJ_1 (_sleep_goto_dormant_until_obj , _sleep_goto_dormant_until ) ;
115+
116+ /*! \brief Send system to dormant until the specified time, note for RP2040 the RTC must be driven by an external clock
117+ *
118+ * \param ts The time to wake up
119+ * \param callback Function to call on wakeup.
120+ */
121+ mp_obj_t _sleep_goto_dormant_for (mp_obj_t time_seconds_in ) {
122+ uint64_t ms = (uint64_t )(mp_obj_get_float (time_seconds_in ) * 1000 );
123+ int rc = powman_off_for_ms (ms );
124+ hard_assert (rc == PICO_OK );
125+ hard_assert (false); // should never get here!
126+ return mp_const_none ;
127+ }
128+ static MP_DEFINE_CONST_FUN_OBJ_1 (_sleep_goto_dormant_for_obj , _sleep_goto_dormant_for ) ;
129+
130+ static const mp_map_elem_t sleep_globals_table [] = {
131+ { MP_ROM_QSTR (MP_QSTR___name__ ), MP_ROM_QSTR (MP_QSTR_powman ) },
132+ { MP_ROM_QSTR (MP_QSTR_goto_dormant_until_pin ), MP_ROM_PTR (& _sleep_goto_dormant_until_pin_obj ) },
133+ { MP_ROM_QSTR (MP_QSTR_goto_dormant_until ), MP_ROM_PTR (& _sleep_goto_dormant_until_obj ) },
134+ { MP_ROM_QSTR (MP_QSTR_goto_dormant_for ), MP_ROM_PTR (& _sleep_goto_dormant_for_obj ) },
135+ { MP_ROM_QSTR (MP_QSTR_get_wake_reason ), MP_ROM_PTR (& _sleep_get_wake_reason_obj ) },
136+
137+ { MP_ROM_QSTR (MP_QSTR_WAKE_BUTTON_A ), MP_ROM_INT (WAKE_BUTTON_A ) },
138+ { MP_ROM_QSTR (MP_QSTR_WAKE_BUTTON_B ), MP_ROM_INT (WAKE_BUTTON_B ) },
139+ { MP_ROM_QSTR (MP_QSTR_WAKE_BUTTON_C ), MP_ROM_INT (WAKE_BUTTON_C ) },
140+ { MP_ROM_QSTR (MP_QSTR_WAKE_TIMER ), MP_ROM_INT (WAKE_TIMER ) }, // TODO: Rename to ALARM?
141+ { MP_ROM_QSTR (MP_QSTR_WAKE_UNKNOWN ), MP_ROM_INT (WAKE_UNKNOWN ) },
142+ };
143+ static MP_DEFINE_CONST_DICT (mp_module_sleep_globals , sleep_globals_table ) ;
144+
145+ const mp_obj_module_t sleep_user_cmodule = {
146+ .base = { & mp_type_module },
147+ .globals = (mp_obj_dict_t * )& mp_module_sleep_globals ,
148+ };
149+
150+ MP_REGISTER_MODULE (MP_QSTR_powman , sleep_user_cmodule );
0 commit comments