Home

Awesome

book-cover

workers-framework-micropython

Workers-Framework: A simpler way to MultiTask in MicroPython

The link WF-Book-Sample.pdf will take you to a free sample of the book, which includes the contents, introduction, and appendices.

The book PDF can be purchased at https://leanpub.com/Workers_Framework if you are interested in purchasing the full version.

If you'd prefer to read this book as a flipbook online whenever and wherever you want, go to https://shariltumin.gumroad.com/l/rbkkbn and get one.

What's the big deal? You might be asking me. Let me give you a simple example of sequential versus concurrent execution.

# s_abcd.py - sequential script
def fun(n,m):
    for i in range(m):
       print(f'{n} at {i}')

fun('A',3)
fun('B',3)
fun('C',3)
fun('D',3)

and

# c_abcd.py - concurrent script
from worker import task, MT

@task
def fun(p):
    n,m = p
    c = yield
    for i in range(m):
       print(f'{n} at {i}')
       yield
    yield 'OK'

mt=MT(4)

mt.worker(fun, ('A',3)) # creat worker
mt.worker(fun, ('B',3))
mt.worker(fun, ('C',3))
mt.worker(fun, ('D',3))
mt.start()              # start all workers
print(mt.log())         # check for any error

In the table below, the outputs of these two scripts are shown side by side.

s_abcd.pyc_abcd.py
A at 0A at 0
A at 1B at 0
A at 2C at 0
B at 0D at 0
B at 1A at 1
B at 2B at 1
C at 0C at 1
C at 1D at 1
C at 2A at 2
D at 0B at 2
D at 1C at 2
D at 2D at 2

I hope you recognize the significance of these results.

Instead of four for loops, how about four while True loops? Tasks running as workers will solve the problem nicely.

Here's a simple example of using REPL keyboard input to control the rate at which the Raspberry Pi Pico's onboard LED blinks.


# blink_rp2040.py
from worker import MT, task
import sys, uselect
from machine import Pin

# poll is better than uselect.select
spoll=uselect.poll()
spoll.register(sys.stdin,uselect.POLLIN)

@task
def kb(pm):
   c=yield
   poll=spoll.poll
   read=sys.stdin.read
   v=c.v
   v.D=500 # 0.5 sec delay at start
   while True:
      if poll(0):
         w=read(1)
         print('w:', w)
         if (w=='+' or w=='f') and v.D<1000: # faster rate
            v.D -= 10
         elif (w=='-' or w=='s') and v.D>10: # slower rate
            v.D += 10
         print('c.v.D:', v.D)
      yield

@task
def blink(pm):
   p=pm[0]
   led = Pin(p, Pin.OUT)
   c=yield
   v=c.v
   while True:
      led.toggle()
      if v.D>0: # if delay is 0 then the program hang
         wait=c.delay(v.D)
         while wait():yield

mt=MT(2)                # we need only 2 workers
mt.worker(kb, ())       # worker for keyboard (in)
mt.worker(blink, (25,)) # worker for LED (out)
mt.start()

Here's an example of how we can use UART to connect two esp32 boards. Three jumper wires will be required. The two tx and rx pins must be crossed, i.e. the first esp32's tx-pin is connected to the second esp32's rx-pin, and the second esp32's rx-pin is connected to the first esp32's tx-pin. Connect the GND-pin of the first esp32 to the GND-pin of the second esp32. These two esp32 boards communicate via serial lines by sending and receiving messages.


from worker import task, MT
from machine import UART
from urandom import random

# Need 3 jumper wires
# Connect A:16  to  B:17
#         A:17  to  B:16
#         A:GND to  B:GND

uart = UART(2, baudrate=576000, tx=16, rx=17)
name = 'A' # either 'A' or 'B' (or anything you want)

@task
def get(p):
    u=p[0]
    c=yield
    while True:
      if u.any()==0: 
         yield
      else:
         m=u.read()
         print('GET:', m.decode())
         if m[:2]!=b'##': 
             mt.worker(rep, (u, m))

@task
def rep(p):
    u,m=p
    c=yield
    wait=c.delay(random()*10+200)
    while wait(): yield
    u.write(b'##'+m.upper())
    yield 'done'

@task
def put(p):
    u,n=p
    c=yield
    cnt=0
    while True:
      m=f'The count at {n} now is {cnt}.'
      print('PUT:', m)
      w=u.write(m)
      if w!=len(m):
         print(f'Write error: {w} chars written out of {len(m)}')
      cnt+=1
      wait=c.delay(random()*1000+200)
      while wait(): yield

mt=MT(10)
mt.worker(get, (uart,))
mt.worker(put, (uart, name))
mt.start()