Coder Social home page Coder Social logo

circuitpython_async's People

Contributors

kvc0 avatar maholli avatar xadhoom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

circuitpython_async's Issues

means to schedule function that runs every x seconds

I've just started using Tasko. Looks pretty cool and looking forward to trying it out.

I have a situation where I want something to run every 60 seconds.
Or maybe every five minutes.

It would be nice to be able to schedule these functions in some manner like "min" or "sec" instead of "hz".

testing tasko using time.monotonic() with Circuitpython on nRF

FYI, following up on the last issue i raised (since closed). Apologies to not respond to your reply until now.

The following is a test of time.monotonic() vs time.montonic_ns() module on CP on nRF:

import time

def	sleep_ns(delta_ns):
	then = time.monotonic_ns()
	while  time.monotonic_ns() - then <= delta_ns:
		pass

def	sleep(delta_s):
	delta_ns = int(delta_s * 1.0e9)
	then = time.monotonic_ns()
	while  time.monotonic_ns() - then <= delta_ns:
		pass

then = time.monotonic()
then_ns = time.monotonic_ns()

for j in range(100):
	# val = j * 0.001
	# val = j * 100000
	val = j * 0.0001
	for i in range(3):
		now = time.monotonic()
		now_ns = time.monotonic_ns()
		print(f"j: {j}; n: {now:6.6f}; n_ns: {now_ns}; d: {now - then:.5f}; d_ns: {now_ns - then_ns}; d_s: {(now_ns - then_ns) * 1.0e-9:.10f}; d_st: {(now_ns - then_ns) * 1.0e-9:.4f}")
		then = time.monotonic()
		then_ns = time.monotonic_ns()
		# time.sleep(val)
		# sleep_ns(val)
		sleep(val)

Sample output:

j: 0; n: 164261.684418; n_ns: 164261680511486; d: 0.00000; d_ns: 213626; d_s: 0.0002136259; d_st: 0.0002
j: 0; n: 164261.684418; n_ns: 164261683654786; d: 0.00000; d_ns: 335685; d_s: 0.0003356849; d_st: 0.0003
j: 0; n: 164261.684418; n_ns: 164261686676027; d: 0.00000; d_ns: 305166; d_s: 0.0003051659; d_st: 0.0003
j: 1; n: 164261.684418; n_ns: 164261689788823; d: 0.00000; d_ns: 366203; d_s: 0.0003662028; d_st: 0.0004
j: 1; n: 164261.684418; n_ns: 164261692901618; d: 0.00000; d_ns: 396734; d_s: 0.0003967339; d_st: 0.0004
j: 1; n: 164261.684418; n_ns: 164261696014414; d: 0.00000; d_ns: 396734; d_s: 0.0003967339; d_st: 0.0004
j: 2; n: 164261.684418; n_ns: 164261699157727; d: 0.00000; d_ns: 396734; d_s: 0.0003967339; d_st: 0.0004
j: 2; n: 164261.684418; n_ns: 164261702331545; d: 0.00000; d_ns: 396720; d_s: 0.0003967198; d_st: 0.0004
j: 2; n: 164261.684418; n_ns: 164261705444341; d: 0.00000; d_ns: 396721; d_s: 0.0003967208; d_st: 0.0004
j: 3; n: 164261.684418; n_ns: 164261708587654; d: 0.00000; d_ns: 427252; d_s: 0.0004272518; d_st: 0.0004
j: 3; n: 164261.684418; n_ns: 164261711853040; d: 0.00000; d_ns: 549324; d_s: 0.0005493236; d_st: 0.0005
j: 3; n: 164261.684418; n_ns: 164261715087894; d: 0.00000; d_ns: 518793; d_s: 0.0005187928; d_st: 0.0005
j: 4; n: 164261.684418; n_ns: 164261718353279; d: 0.00000; d_ns: 549324; d_s: 0.0005493236; d_st: 0.0005
j: 4; n: 164261.684418; n_ns: 164261721740723; d: 0.00000; d_ns: 640864; d_s: 0.0006408638; d_st: 0.0006
j: 4; n: 164261.684418; n_ns: 164261725067145; d: 0.00000; d_ns: 610347; d_s: 0.0006103467; d_st: 0.0006
j: 5; n: 164261.684418; n_ns: 164261728424084; d: 0.00000; d_ns: 640878; d_s: 0.0006408778; d_st: 0.0006
j: 5; n: 164261.684418; n_ns: 164261731872564; d: 0.00000; d_ns: 701900; d_s: 0.0007018996; d_st: 0.0007
j: 5; n: 164261.684418; n_ns: 164261735290540; d: 0.00000; d_ns: 732432; d_s: 0.0007324317; d_st: 0.0007
j: 6; n: 164261.746407; n_ns: 164261738739020; d: 0.00000; d_ns: 732419; d_s: 0.0007324186; d_st: 0.0007
j: 6; n: 164261.746407; n_ns: 164261742340090; d: 0.00000; d_ns: 854491; d_s: 0.0008544905; d_st: 0.0009
j: 6; n: 164261.746407; n_ns: 164261745880137; d: 0.00000; d_ns: 823972; d_s: 0.0008239716; d_st: 0.0008
j: 7; n: 164261.746407; n_ns: 164261749511725; d: 0.00000; d_ns: 915526; d_s: 0.0009155255; d_st: 0.0009
j: 7; n: 164261.746407; n_ns: 164261753143313; d: 0.00000; d_ns: 915526; d_s: 0.0009155255; d_st: 0.0009
j: 7; n: 164261.746407; n_ns: 164261756774915; d: 0.00000; d_ns: 885022; d_s: 0.0008850216; d_st: 0.0009
j: 8; n: 164261.746407; n_ns: 164261760467539; d: 0.00000; d_ns: 946045; d_s: 0.0009460444; d_st: 0.0009
j: 8; n: 164261.746407; n_ns: 164261764251717; d: 0.00000; d_ns: 1037599; d_s: 0.0010375986; d_st: 0.0010
j: 8; n: 164261.746407; n_ns: 164261768035895; d: 0.00000; d_ns: 1037599; d_s: 0.0010375986; d_st: 0.0010
j: 9; n: 164261.746407; n_ns: 164261771911627; d: 0.00000; d_ns: 1129153; d_s: 0.0011291523; d_st: 0.0011
j: 9; n: 164261.746407; n_ns: 164261775787359; d: 0.00000; d_ns: 1159671; d_s: 0.0011596702; d_st: 0.0012
j: 9; n: 164261.746407; n_ns: 164261779663091; d: 0.00000; d_ns: 1159671; d_s: 0.0011596702; d_st: 0.0012
j: 10; n: 164261.746407; n_ns: 164261783569341; d: 0.00000; d_ns: 1159671; d_s: 0.0011596702; d_st: 0.0012

Floating point resolution seems to be the cause of the issue that I had before. The time difference calculation using time.monotonic() produces zero unless delta time gets big enough.

I ended up replacing time in tasko with the following code (not at all optimized) and started to see what I expected.

import time

class Time_ns:
	def __init__(self):
		pass

	def monotonic_ns(self):
		return time.monotonic_ns()

	def monotonic(self):
		ns, lhs, rhs = self._monotonic_float()
		lhs = lhs - int(lhs * 1.0e-4) * 1.0e4
		return lhs + rhs

	def _monotonic_float(self):
		tm_ns = time.monotonic_ns()
		tm_lhs = int(tm_ns * 1.0e-9) * 1000000000
		tm_rhs = tm_ns - tm_lhs
		return tm_ns ,tm_lhs * 1.0e-9, (tm_rhs * 1.0e-4) * 0.00001

	def _monotonic_binary(self):
		# this is not accurate
		tm_ns = time.monotonic_ns()
		tm_lhs = (tm_ns >> 29) << 29
		tm_rhs = tm_ns - tm_lhs
		return tm_ns, tm_lhs * 1.0e-9, (tm_rhs * 1.0e-4) * 0.00001

	def sleep_ns(self, delta_t_ns):
		then = time.monotonic_ns()
		while  time.monotonic_ns() - then <= delta_ns:
			pass

	def sleep(self, delta_s):
		delta_ns = int(delta_s * 1.0e9)
		then = time.monotonic_ns()
		while  time.monotonic_ns() - then <= delta_ns:
			pass

In loop.py in tasko replaced time module as follows:

from Time_ns import Time_ns
time = Time_ns()
# import time

Results:

task1: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024880
task2: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024880
task3: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024880
task4: 50Hz; index: 51; spot_per: 0.00000; avg_per 0.01987, avg_var: -0.019867


task1: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024881
task2: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024881
task3: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024881
task4: 50Hz; index: 51; spot_per: 0.06250; avg_per 0.01987, avg_var: +0.042634


task1: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10015, avg_var: +0.024854
task2: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10015, avg_var: +0.024854
task3: 10Hz; index: 11; spot_per: 0.06250; avg_per 0.10013, avg_var: -0.037629
task4: 50Hz; index: 51; spot_per: 0.06250; avg_per 0.01987, avg_var: +0.042630


task1: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024882
task2: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024882
task3: 10Hz; index: 9; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024882
task4: 50Hz; index: 48; spot_per: 0.06250; avg_per 0.01987, avg_var: +0.042632


task1: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024882
task2: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024882
task3: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024882
task4: 50Hz; index: 52; spot_per: 0.00000; avg_per 0.01987, avg_var: -0.019866


task1: 10Hz; index: 10; spot_per: 0.06250; avg_per 0.10009, avg_var: -0.037591
task2: 10Hz; index: 10; spot_per: 0.06250; avg_per 0.10009, avg_var: -0.037591
task3: 10Hz; index: 10; spot_per: 0.12500; avg_per 0.10012, avg_var: +0.024883
task4: 50Hz; index: 51; spot_per: 0.06250; avg_per 0.01986, avg_var: +0.042635

I am sure there are better ways to deal with this, but thought this might be of interest to anyone traveling through here.

Support for starting task later

First of all thanks for this great library, is very helpful.

Coming from a past python/twisted tasks experience, I'm missing the possibility to start tasks "later" (at the end of the specified interval)

Right now tasko immediately run all defined tasks when tasko.run is called. Why not add a flag like immediate=False in the schedule fun in order to delay first task run at the end of the defined hz ?

An use case is to periodically publish acquired data, which makes no sense doing immediately. Right now I've just added a "first_run" flag in my publisher helper that just skips on first invocation, but if tasko can support it can be very helpful :)

Removing/stopping a scheduled task

I keep finding myself in situations where I want to stop/remove a scheduled task, but it's quite tedious to remove a task from tasko at the moment. Maybe you can set me straight?

For lack of a better example...

class Widget:
    lowbatt=False

    async def power_hungry(self):
        # something that takes a lot of power
        pass

    async def check_battery(self):
        if battery_percent() < 10 
            if not self.lowbatt:
                self.lowbatt=True
                # remove battery-intensive tasks from scheduler
                # tasko.remove(coroutine_function)
        elif self.lowbatt:
            self.lowbatt=False
            # add the battery-intensive task back
            tasko.schedule(hz=1,coroutine_function=self.power_hungry)

widget=Widget()

tasko.schedule(hz=1,coroutine_function=widget.power_hungry)
tasko.schedule(hz=0.03,coroutine_function=widget.check_battery)

tasko.run()

The intent here is that our widget will stop doing its power_hungry task every second if the periodic check_battery task determines the widget has a low battery voltage.

  • I'd think removing the task is preferred over suspending to avoid a bunch of old tasks from hanging around. But then I suppose there's the concern of dynamic allocations when potentially adding tasks back.

timing of tasks.

ISSUE: At above 16Hz the system freezes. If i set the task frequency above 8hz the measured freq gets pegged to 16hz no matter what the setting. However the iteration count seems to match the report interval for the task that is doing the reporting. i put some time.monotonic_ns timing around all of these. The tasks are all completing in under 0.5 ms on average.

Background:

Thank you for this interesting and helpful library i have been building an application around it.

Things have generally been working as described in the documentation but at some point when my application grew to have multiple modules and objects i started to see the scheduler freeze.

i have an event loop and usb, hardware uart and ble uart buffer parsing and and event loop for a total of 4x tasks. orginally the slower usb, ble and event tasks were at 10hz and the uart was at 100hz, because of a lot of serial data streaming. I had all of this working great balanced the buffers etc.

Change duration of scheduled event

Hello, I'm trying to make a midi arpeggiator. I tried other methods but your library is really precise in terms of midi latency. Thanks for your efforts! Is there a way to change the duration after the run starts? I've added a screenshot; the bpm value will be changing here. I'm very new on GitHub so I'm not sure if this is the right place to ask this, but I couldn't find any other contact info.

def run():
    duR = (1000/PPQN_MAX)/(get_bpm()/60)          ## = 60000/(24*bpmM) = 2500/bpm
    asynccp.schedule(frequency=Duration.of_milliseconds(duR), coroutine_function=count_PPQN)
    asynccp.schedule(frequency=80, coroutine_function=updates_UE)
    # asynccp.add_task(main())
    asynccp.run()

if __name__ == '__main__':
    run()

Screen Shot 2021-10-25 at 18 56 19

asynccp with sockets example

Hi, great library!

I'm attempting to handle a socket connection and pass that connection between two async functions to pose as a reader and writer. my reader (GetCommand) is receiving data but my writer (DoTask) doesn't seem to be calling. Is there a problem with me sharing sockets across async functions?

Here's an example of what i'm trying in case there's any glaringly obvious errors or protocols - CircuitPython code on ESP32-S2-WROVER-I:

import wifi
import asynccp
import socketpool
import time

bufSize = 40
buf = bytearray(bufSize)
mv_buf = memoryview(buf)
count = 0

async def DoTask(conn): 
    # originally had custom functions for spi to attached device, 
    # would poll for new data before incrementing through buffer
    global count
    mv_buf[count] = count
    count += 1
    time.sleep(0.01)
    if count == 40:  
        conn.send(mv_buf)
        count = 0
        print("sent buffer")

async def GetCommand(conn):
    buffer = bytearray(256) 
    size = conn.recv_into(buffer, 256)
    print(buffer[:size])
 
async def loop(conn):
    await GetCommand(conn)
    await DoTask(conn)
    

def StartWiFiServer():
    ssid = "esp32-s2"
    password = "password123"
    wifi.radio.enabled = True
    wifi.radio.start_ap(ssid, password)  
   
if __name__ == "__main__":   
    print("Running main.py")
    StartWiFiServer()
    pool = socketpool.SocketPool(wifi.radio) 
    s = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
    s.setblocking(1)
    HOST = "192.168.4.1"
    s.bind((HOST, 99)) 
    s.listen(1)
    while True: 
        conn, addr = s.accept()
        with conn: 
            print("Connected by", addr)
            asynccp.add_task(loop(conn)) 
            asynccp.run()

PC code:

import socket

HOST = "192.168.4.1" # The server's hostname or IP address
PORT = 99  # The port used by the server

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.settimeout(None)

    print("Connecting")
    s.connect((HOST, PORT))

    array = [1,2,3,5,4]  
    s.send(bytes(array))    
    
    #s.rea
    res = s.recv(40, socket.MSG_WAITALL)
    print(res)

Microcontroller performances

First off @WarriorOfWire, absolutely AWESOME work with tasko!! ⭐⭐

I wanted to report the performances I'm seeing on the few microcontroller boards I have around.

  • Tested using test_schedule_rate from test_loop.py (9811f53) and this unittest for CP. All boards running adafruit/circuitpython@7df5d74
  • Throughput reported for maximum tasks achievable before expected tps < actual tps OR tasks miss their scheduled count with the default +- 5 iterations. (duration=1s)
Board Microcontroller Task Count Throughput
Feather M4 Express SAMD51J19 (120 MHz, 192KB SRAM) 17 544 Hz
PyCubed v05 SAMD51J19 (120 MHz, 192KB SRAM) 19 665 Hz
Feather S2 ESP32-S2 (240 MHz, 320KB SRAM) 16 488 Hz

It's very curious to me that the 240 MHz board performs the worst! Any thoughts?

Raising StopIteration from inside a task causes a RuntimeError

This is my first time dabbling with async/await-style programming in Python, so if I'm simply failing to understand something here, please feel free to tell me to RTFM. :)

With that caveat: From reading the source code for asynccp/loop.py, I was under the impression I could raise StopIteration from inside the coroutine_function passed to a ScheduledTask to achieve the equivalent of calling my_task.stop(). However, when I actually raise StopIteration I get a stack trace:

Traceback (most recent call last):
  File "code.py", line 307, in <module>
  File "/lib/asynccp/loop.py", line 268, in run
  File "/lib/asynccp/loop.py", line 275, in _step
  File "/lib/asynccp/loop.py", line 303, in _run_task
  File "/lib/asynccp/loop.py", line 101, in _run_at_fixed_rate
RuntimeError: generator raised StopIteration

I believe that somewhere between raising StopIteration and the exception being handled in _run_task it's being turned into a RuntimeError.

I'm currently running this on Adafruit CircuitPython 7.0.0 on 2021-09-20; TinyS2 with ESP32S2

Let me know if some sample code (or any other information) would be helpful.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.