Scheduling Real-Time Events with Simpy#

Simpy is a Python package for discrete-event simulation in Python. Simpy includes a provision for real-time simulation which provides an potentially useful tool for coding laboratory experiments with complex scheduling requirements.

Keep in mind that Python is not a designed for real-time use, and Simpy should not be trusted for applications requiring time accuracy tighter than, say, 100ms. Futher, it is not an asynchronous implementation, so your interpreter will be blocked during the course of the experiment. But for quick-and-dirty applications with modest performance requirements, Simpy real-time may be a simple solution.

!pip install simpy
Requirement already satisfied: simpy in /Users/jeff/opt/anaconda3/lib/python3.8/site-packages (4.0.1)

Blinkers#

from pymata4 import pymata4
import simpy.rt
import time

led0 = 13
led1 = 9

def blinker(env, board, pin, period):
    board.set_pin_mode_digital_output(pin)
    while True:
        board.digital_write(pin, 1)
        end = time.perf_counter()
        print(f"led {pin:2d}  on at {end-start:5.3f}")
        yield env.timeout(period/2)
        board.digital_write(pin, 0)
        end = time.perf_counter()
        print(f"led {pin:2d} off at {end-start:5.3f}")
        yield env.timeout(period/2)

board = pymata4.Pymata4()

env = simpy.rt.RealtimeEnvironment()
env.process(blinker(env, board, led0, 2.0))
env.process(blinker(env, board, led1, 2.0))
start = time.perf_counter()
env.run(until=20)
board.shutdown()
pymata4:  Version 1.10

Copyright (c) 2020 Alan Yorinks All Rights Reserved.

Opening all potential serial ports...
	/dev/cu.usbmodem14301

Waiting 4 seconds(arduino_wait) for Arduino devices to reset...

Searching for an Arduino configured with an arduino_instance = 1
Arduino compatible device found and connected to /dev/cu.usbmodem14301

Retrieving Arduino Firmware ID...
Arduino Firmware ID: 2.5 StandardFirmata.ino

Retrieving analog map...
Auto-discovery complete. Found 30 Digital Pins and 12 Analog Pins


led 13  on at 0.001
led  9  on at 0.001
led 13 off at 1.203
led  9 off at 1.405
led 13  on at 2.001
led  9  on at 2.002
led 13 off at 3.204
led  9 off at 3.407
led 13  on at 4.004
led  9  on at 4.004
led 13 off at 5.206
led  9 off at 5.410
led 13  on at 6.002
led  9  on at 6.002
led 13 off at 7.207
led  9 off at 7.411
led 13  on at 8.002
led  9  on at 8.002
led 13 off at 9.200
led  9 off at 9.402
led 13  on at 10.000
led  9  on at 10.000
led 13 off at 11.202
led  9 off at 11.407
led 13  on at 12.004
led  9  on at 12.004
led 13 off at 13.203
led  9 off at 13.403
led 13  on at 14.003
led  9  on at 14.003
led 13 off at 15.200
led  9 off at 15.402
led 13  on at 16.003
led  9  on at 16.004
led 13 off at 17.204
led  9 off at 17.407
led 13  on at 18.001
led  9  on at 18.001
led 13 off at 19.205
led  9 off at 19.407

Asyncio#

import asyncio
from pymata4 import pymata4
import time

led0 = 13
led1 = 9

async def blinker(board, pin, period, start):
    board.set_pin_mode_digital_output(pin)
    while time.perf_counter() < start + 20:
        board.digital_write(pin, 1)
        print(f"led {pin:2d}  on at {round(time.perf_counter()-start, 2)}")
        k = round((time.perf_counter() - start)/(period/2))
        dt = (k+1)*period/2 - (time.perf_counter() - start)
        await asyncio.sleep(dt-0.05)
        board.digital_write(pin, 0)
        print(f"led {pin:2d} off at {round(time.perf_counter()-start, 2)}")
        k = round((time.perf_counter() - start)/(period/2))
        dt = (k+1)*period/2 - (time.perf_counter() - start)
        await asyncio.sleep(dt-0.05)

board = pymata4.Pymata4()

async def expt():
    start = time.perf_counter()
    coroutines = [
        blinker(board, led0, 2.0, start),
        blinker(board, led1, 2.0, start)
    ]
    await asyncio.gather(*coroutines)
    
await expt()
board.shutdown()
pymata4:  Version 1.10

Copyright (c) 2020 Alan Yorinks All Rights Reserved.

Opening all potential serial ports...
	/dev/cu.usbmodem14301

Waiting 4 seconds(arduino_wait) for Arduino devices to reset...

Searching for an Arduino configured with an arduino_instance = 1
Arduino compatible device found and connected to /dev/cu.usbmodem14301

Retrieving Arduino Firmware ID...
Arduino Firmware ID: 2.5 StandardFirmata.ino

Retrieving analog map...
Auto-discovery complete. Found 30 Digital Pins and 12 Analog Pins


led 13  on at 0.04
led  9  on at 0.05
led  9 off at 0.98
led 13 off at 1.03
led  9  on at 1.97
led 13  on at 2.02
led 13 off at 3.05
led  9 off at 3.09
led  9  on at 4.05
led 13  on at 4.11
led 13 off at 5.0
led  9 off at 5.06
led  9  on at 6.03
led 13  on at 6.1
led 13 off at 7.0
led  9 off at 7.08
led  9  on at 8.0
led 13  on at 8.08
led 13 off at 8.97
led  9 off at 9.01
led  9  on at 10.01
led 13  on at 10.11
led 13 off at 11.01
led  9 off at 11.1
led  9  on at 11.95
led 13  on at 12.03
led 13 off at 12.99
led  9 off at 13.12
led  9  on at 13.98
led 13  on at 13.99
led  9 off at 15.01
led 13 off at 15.06
led 13  on at 16.02
led  9  on at 16.1
led  9 off at 16.98
led 13 off at 17.0
led 13  on at 17.99
led  9  on at 18.04
led  9 off at 19.01
led 13 off at 19.08
led 13  on at 19.99
led 13 off at 20.99