3
3
1 . [ Scheduling tasks] ( ./SCHEDULE.md#1-scheduling-tasks )
4
4
2 . [ Overview] ( ./SCHEDULE.md#2-overview )
5
5
3 . [ Installation] ( ./SCHEDULE.md#3-installation )
6
- 4 . [ The schedule function ] ( ./SCHEDULE.md#4-the-schedule-function ) The primary interface for asyncio
6
+ 4 . [ The schedule coroutine ] ( ./SCHEDULE.md#4-the-schedule-coroutine ) The primary interface for asyncio.
7
7
4.1 [ Time specifiers] ( ./SCHEDULE.md#41-time-specifiers )
8
8
4.2 [ Calendar behaviour] ( ./SCHEDULE.md#42-calendar-behaviour ) Calendars can be tricky...
9
9
  ;  ;  ;  ;  ; 4.2.1 [ Behaviour of mday and wday values] ( ./SCHEDULE.md#421-behaviour-of-mday-and-wday-values )
10
10
  ;  ;  ;  ;  ; 4.2.2 [ Time causing month rollover] ( ./SCHEDULE.md#422-time-causing-month-rollover )
11
11
4.3 [ Limitations] ( ./SCHEDULE.md#43-limitations )
12
12
4.4 [ The Unix build] ( ./SCHEDULE.md#44-the-unix-build )
13
- 5 . [ The cron object] ( ./SCHEDULE.md#5-the-cron-object ) For hackers and synchronous coders
13
+ 4.5 [ Callback interface] ( ./SCHEDULE.md#45-callback-interface ) Alternative interface using callbacks.
14
+ 4.6 [ Event interface] ( ./SCHEDULE.md#46-event-interface ) Alternative interface using Event instances.
15
+ 5 . [ The cron object] ( ./SCHEDULE.md#5-the-cron-object ) The rest of this doc is for hackers and synchronous coders.
14
16
5.1 [ The time to an event] ( ./SCHEDULE.md#51-the-time-to-an-event )
15
17
5.2 [ How it works] ( ./SCHEDULE.md#52-how-it-works )
16
18
6 . [ Hardware timing limitations] ( ./SCHEDULE.md#6-hardware-timing-limitations )
19
21
8 . [ The simulate script] ( ./SCHEDULE.md#8-the-simulate-script ) Rapidly test sequences.
20
22
21
23
Release note:
24
+ 23rd Nov 2023 Add asynchronous iterator interface.
22
25
3rd April 2023 Fix issue #100 . Where an iterable is passed to ` secs ` , triggers
23
26
must now be at least 10s apart (formerly 2s).
24
27
@@ -38,34 +41,40 @@ latter it is less capable but is small, fast and designed for microcontroller
38
41
use. Repetitive and one-shot events may be created.
39
42
40
43
It is ideally suited for use with ` asyncio ` and basic use requires minimal
41
- ` asyncio ` knowledge. Users intending only to schedule callbacks can simply
42
- adapt the example code. It can be used in synchronous code and an example is
43
- provided.
44
+ ` asyncio ` knowledge. Example code is provided offering various ways of
45
+ responding to timing triggers including running callbacks. The module can be
46
+ also be used in synchronous code and an example is provided.
44
47
45
48
It is cross-platform and has been tested on Pyboard, Pyboard D, ESP8266, ESP32
46
49
and the Unix build.
47
50
48
51
# 2. Overview
49
52
50
- The ` schedule ` function (` sched/sched.py ` ) is the interface for use with
51
- ` asyncio ` . The function takes a callback and causes that callback to run at
52
- specified times. A coroutine may be substituted for the callback - at the
53
- specified times it will be promoted to a ` Task ` and run.
53
+ The ` schedule ` coroutine (` sched/sched.py ` ) is the interface for use with
54
+ ` asyncio ` . Three interface alternatives are offered which vary in the behaviour:
55
+ which occurs when a scheduled trigger occurs:
56
+ 1 . An asynchronous iterator is triggered.
57
+ 2 . A user defined ` Event ` is set.
58
+ 3 . A user defined callback or coroutine is launched.
54
59
55
- The ` schedule ` function instantiates a ` cron ` object (in ` sched/cron.py ` ). This
56
- is the core of the scheduler: it is a closure created with a time specifier and
57
- returning the time to the next scheduled event. Users of ` asyncio ` do not need
58
- to deal with ` cron ` instances.
60
+ One or more ` schedule ` tasks may be assigned to a ` Sequence ` instance. This
61
+ enables an ` async for ` statement to be triggered whenever any of the ` schedule `
62
+ tasks is triggered.
59
63
60
- This library can also be used in synchronous code, in which case ` cron `
61
- instances must explicitly be created.
64
+ Under the hood the ` schedule ` function instantiates a ` cron ` object (in
65
+ ` sched/cron.py ` ). This is the core of the scheduler: it is a closure created
66
+ with a time specifier and returning the time to the next scheduled event. Users
67
+ of ` asyncio ` do not need to deal with ` cron ` instances. This library can also be
68
+ used in synchronous code, in which case ` cron ` instances must explicitly be
69
+ created.
62
70
63
71
##### [ Top] ( ./SCHEDULE.md#0-contents )
64
72
65
73
# 3. Installation
66
74
67
- Copy the ` sched ` directory and contents to the target's filesystem. This may be
68
- done with the official [ mpremote] ( https://docs.micropython.org/en/latest/reference/mpremote.html ) :
75
+ The ` sched ` directory and contents must be copied to the target's filesystem.
76
+ This may be done with the official
77
+ [ mpremote] ( https://docs.micropython.org/en/latest/reference/mpremote.html ) :
69
78
``` bash
70
79
$ mpremote mip install " github:peterhinch/micropython-async/v3/as_drivers/sched"
71
80
```
@@ -75,7 +84,7 @@ On networked platforms it may be installed with [mip](https://docs.micropython.o
75
84
```
76
85
Currently these tools install to ` /lib ` on the built-in Flash memory. To install
77
86
to a Pyboard's SD card [ rshell] ( https://github.com/dhylands/rshell ) may be used.
78
- Move to the SD card root , run ` rshell ` and issue:
87
+ Move to ` as_drivers ` on the PC , run ` rshell ` and issue:
79
88
```
80
89
> rsync sched /sd/sched
81
90
```
@@ -94,16 +103,19 @@ The following files are installed in the `sched` directory.
94
103
The ` crontest ` script is only of interest to those wishing to adapt ` cron.py ` .
95
104
It will run on any MicroPython target.
96
105
97
- # 4. The schedule function
106
+ # 4. The schedule coroutine
98
107
99
- This enables a callback or coroutine to be run at intervals. The callable can
100
- be specified to run forever, once only or a fixed number of times. ` schedule `
101
- is an asynchronous function.
108
+ This enables a response to be triggered at intervals. The response can be
109
+ specified to occur forever, once only or a fixed number of times. ` schedule `
110
+ is a coroutine and is typically run as a background task as follows:
111
+ ``` python
112
+ asyncio.create_task(schedule(foo, ' every 4 mins' , hrs = None , mins = range (0 , 60 , 4 )))
113
+ ```
102
114
103
115
Positional args:
104
- 1 . ` func ` The callable (callback or coroutine) to run. Alternatively an
105
- ` Event ` may be passed (see below) .
106
- 2 . Any further positional args are passed to the callable.
116
+ 1 . ` func ` This may be a callable (callback or coroutine) to run, a user defined
117
+ ` Event ` or an instance of a ` Sequence ` .
118
+ 2 . Any further positional args are passed to the callable or the ` Sequence ` .
107
119
108
120
Keyword-only args. Args 1..6 are
109
121
[ Time specifiers] ( ./SCHEDULE.md#41-time-specifiers ) : a variety of data types
@@ -125,65 +137,37 @@ the value returned by that run of the callable.
125
137
Because ` schedule ` does not terminate promptly it is usually started with
126
138
` asyncio.create_task ` , as in the following example where a callback is
127
139
scheduled at various times. The code below may be run by issuing
128
- ``` python
129
- import sched.asynctest
130
- ```
131
- This is the demo code.
132
- ``` python
133
- import asyncio as asyncio
134
- from sched.sched import schedule
135
- from time import localtime
136
-
137
- def foo (txt ): # Demonstrate callback
138
- yr, mo, md, h, m, s, wd = localtime()[:7 ]
139
- fst = ' Callback {} {:02d } :{:02d } :{:02d } on {:02d } /{:02d } /{:02d } '
140
- print (fst.format(txt, h, m, s, md, mo, yr))
140
+ The event-based interface can be simpler than using callables:
141
141
142
- async def bar (txt ): # Demonstrate coro launch
143
- yr, mo, md, h, m, s, wd = localtime()[:7 ]
144
- fst = ' Coroutine {} {:02d } :{:02d } :{:02d } on {:02d } /{:02d } /{:02d } '
145
- print (fst.format(txt, h, m, s, md, mo, yr))
146
- await asyncio.sleep(0 )
142
+ The remainder of this section describes the asynchronous iterator interface as
143
+ this is the simplest to use. The other interfaces are discussed in
144
+ * [ 4.5 Callback interface] ( ./SCHEDULE.md#45-callback-interface )
145
+ * [ 4.6 Event interface] ( ./SCHEDULE.md#46-event-interface )
147
146
148
- async def main ():
149
- print (' Asynchronous test running...' )
150
- asyncio.create_task(schedule(foo, ' every 4 mins' , hrs = None , mins = range (0 , 60 , 4 )))
151
- asyncio.create_task(schedule(foo, ' every 5 mins' , hrs = None , mins = range (0 , 60 , 5 )))
152
- # Launch a coroutine
153
- asyncio.create_task(schedule(bar, ' every 3 mins' , hrs = None , mins = range (0 , 60 , 3 )))
154
- # Launch a one-shot task
155
- asyncio.create_task(schedule(foo, ' one shot' , hrs = None , mins = range (0 , 60 , 2 ), times = 1 ))
156
- await asyncio.sleep(900 ) # Quit after 15 minutes
157
-
158
- try :
159
- asyncio.run(main())
160
- finally :
161
- _ = asyncio.new_event_loop()
162
- ```
163
- The event-based interface can be simpler than using callables:
147
+ One or more ` schedule ` instances are collected in a ` Sequence ` object. This
148
+ supports the asynchronous iterator interface:
164
149
``` python
165
- import asyncio as asyncio
166
- from sched.sched import schedule
150
+ import uasyncio as asyncio
151
+ from sched.sched import schedule, Sequence
167
152
from time import localtime
168
153
169
154
async def main ():
170
155
print (" Asynchronous test running..." )
171
- evt = asyncio.Event()
172
- asyncio.create_task(schedule(evt, hrs = 10 , mins = range (0 , 60 , 4 )))
173
- while True :
174
- await evt.wait() # Multiple tasks may wait on an Event
175
- evt.clear() # It must be cleared.
156
+ seq = Sequence() # A Sequence comprises one or more schedule instances
157
+ asyncio.create_task(schedule(seq, ' every 4 mins' , hrs = None , mins = range (0 , 60 , 4 )))
158
+ asyncio.create_task(schedule(seq, ' every 5 mins' , hrs = None , mins = range (0 , 60 , 5 )))
159
+ asyncio.create_task(schedule(seq, ' every 3 mins' , hrs = None , mins = range (0 , 60 , 3 )))
160
+ # A one-shot trigger
161
+ asyncio.create_task(schedule(seq, ' one shot' , hrs = None , mins = range (0 , 60 , 2 ), times = 1 ))
162
+ async for args in seq:
176
163
yr, mo, md, h, m, s, wd = localtime()[:7 ]
177
- print (f " Event { h:02d } : { m:02d } : { s:02d } on { md:02d } / { mo:02d } / { yr} " )
164
+ print (f " Event { h:02d } : { m:02d } : { s:02d } on { md:02d } / { mo:02d } / { yr} args: { args } " )
178
165
179
166
try :
180
167
asyncio.run(main())
181
168
finally :
182
169
_ = asyncio.new_event_loop()
183
170
```
184
- See [ tutorial] ( https://github.com/peterhinch/micropython-async/blob/master/v3/docs/TUTORIAL.md#32-event ) .
185
- Also [ this doc] ( https://github.com/peterhinch/micropython-async/blob/master/v3/docs/EVENTS.md )
186
- for a discussion of event-based programming.
187
171
188
172
##### [ Top] ( ./SCHEDULE.md#0-contents )
189
173
@@ -286,6 +270,81 @@ is to avoid scheduling the times in your region where this occurs (01.00.00 to
286
270
287
271
##### [ Top] ( ./SCHEDULE.md#0-contents )
288
272
273
+ ## 4.5 Callback interface
274
+
275
+ In this instance a user defined ` callable ` is passed as the first ` schedule ` arg.
276
+ A ` callable ` may be a function or a coroutine. It is possible for multiple
277
+ ` schedule ` instances to call the same callback, as in the example below. The
278
+ code is included in the library as ` sched/asyntest.py ` and may be run as below.
279
+ ``` python
280
+ import sched.asynctest
281
+ ```
282
+ This is the demo code.
283
+ ``` python
284
+ import uasyncio as asyncio
285
+ from sched.sched import schedule
286
+ from time import localtime
287
+
288
+ def foo (txt ): # Demonstrate callback
289
+ yr, mo, md, h, m, s, wd = localtime()[:7 ]
290
+ fst = ' Callback {} {:02d } :{:02d } :{:02d } on {:02d } /{:02d } /{:02d } '
291
+ print (fst.format(txt, h, m, s, md, mo, yr))
292
+
293
+ async def bar (txt ): # Demonstrate coro launch
294
+ yr, mo, md, h, m, s, wd = localtime()[:7 ]
295
+ fst = ' Coroutine {} {:02d } :{:02d } :{:02d } on {:02d } /{:02d } /{:02d } '
296
+ print (fst.format(txt, h, m, s, md, mo, yr))
297
+ await asyncio.sleep(0 )
298
+
299
+ async def main ():
300
+ print (' Asynchronous test running...' )
301
+ asyncio.create_task(schedule(foo, ' every 4 mins' , hrs = None , mins = range (0 , 60 , 4 )))
302
+ asyncio.create_task(schedule(foo, ' every 5 mins' , hrs = None , mins = range (0 , 60 , 5 )))
303
+ # Launch a coroutine
304
+ asyncio.create_task(schedule(bar, ' every 3 mins' , hrs = None , mins = range (0 , 60 , 3 )))
305
+ # Launch a one-shot task
306
+ asyncio.create_task(schedule(foo, ' one shot' , hrs = None , mins = range (0 , 60 , 2 ), times = 1 ))
307
+ await asyncio.sleep(900 ) # Quit after 15 minutes
308
+
309
+ try :
310
+ asyncio.run(main())
311
+ finally :
312
+ _ = asyncio.new_event_loop()
313
+ ```
314
+ ##### [ Top] ( ./SCHEDULE.md#0-contents )
315
+
316
+ ## 4.6 Event interface
317
+
318
+ In this instance a user defined ` Event ` is passed as the first ` schedule ` arg.
319
+ It is possible for multiple ` schedule ` instances to trigger the same ` Event ` .
320
+ The user is responsible for clearing the ` Event ` . This interface has a drawback
321
+ in that extra positional args passed to ` schedule ` are lost.
322
+ ``` python
323
+ import uasyncio as asyncio
324
+ from sched.sched import schedule
325
+ from time import localtime
326
+
327
+ async def main ():
328
+ print (" Asynchronous test running..." )
329
+ evt = asyncio.Event()
330
+ asyncio.create_task(schedule(evt, hrs = 10 , mins = range (0 , 60 , 4 )))
331
+ while True :
332
+ await evt.wait() # Multiple tasks may wait on an Event
333
+ evt.clear() # It must be cleared.
334
+ yr, mo, md, h, m, s, wd = localtime()[:7 ]
335
+ print (f " Event { h:02d } : { m:02d } : { s:02d } on { md:02d } / { mo:02d } / { yr} " )
336
+
337
+ try :
338
+ asyncio.run(main())
339
+ finally :
340
+ _ = asyncio.new_event_loop()
341
+ ```
342
+ See [ tutorial] ( https://github.com/peterhinch/micropython-async/blob/master/v3/docs/TUTORIAL.md#32-event ) .
343
+ Also [ this doc] ( https://github.com/peterhinch/micropython-async/blob/master/v3/docs/EVENTS.md )
344
+ for a discussion of event-based programming.
345
+
346
+ ##### [ Top] ( ./SCHEDULE.md#0-contents )
347
+
289
348
# 5. The cron object
290
349
291
350
This is the core of the scheduler. Users of ` asyncio ` do not need to concern
@@ -450,9 +509,9 @@ def wait_for(**kwargs):
450
509
451
510
# 8. The simulate script
452
511
453
- This enables the behaviour of sets of args to ` schedule ` to be rapidly checked.
454
- The ` sim ` function should be adapted to reflect the application specifics. The
455
- default is:
512
+ In ` sched/simulate.py ` . This enables the behaviour of sets of args to ` schedule `
513
+ to be rapidly checked. The ` sim ` function should be adapted to reflect the
514
+ application specifics. The default is:
456
515
``` python
457
516
def sim (* args ):
458
517
set_time(* args)
0 commit comments