NEC_IR/aremote conflict with asyncio.start_server

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
sloathog
Posts: 9
Joined: Tue Mar 10, 2020 10:30 pm

NEC_IR/aremote conflict with asyncio.start_server

Post by sloathog » Thu Sep 23, 2021 11:34 am

I'm have a Lolin D32 that I'm using to control some LEDS, I added a web server to allow for control over wifi - all is working fine. I've tried to add a TSOP4838 to allow for a NEC IR remote to turn the lights on/off as well, but got stuck.

Remote is a standard NEC remote with 16 bit address, so I have commented out the 8 bit code
The code for the IR remote is based from pythoncoder - https://github.com/peterhinch/micropyth ... ers/nec_ir
I changed it slightly, so the decoded message is printed for deugging and it places the commands in a Queue for later processing.

If I disable the webserver and just use the remote, everything works fine.

Below is output from pressing off and on button.
_run: starting NEC_IR
_decode: Val: hex - da25fbe2
Button press: data:25 addr:fbe2 - LED off
_decode: Val: hex - d926fbe2
Button press: data:26 addr:fbe2 - LED on


If I add the tcp server using server = await asyncio.start_server(client_server, '0.0.0.0', 80) , then the IR decode message is garbled.

main: started http server
_run: starting NEC_IR
_decode: Val: hex - 22222223
Button press: data:-6 addr:2223 - Incorrect remote
_decode: Val: hex - 0000000a
Button press: data:-6 addr:000a - Incorrect remote
_decode: Val: hex - 72236900


This happens with no connections active on the tcp server. I'm assuming the IRQ interrupts are being delayed by the tcp server which is waiting for connections. The tcp server works normally.

Does anyone know if there is a solution for this?
I can think of two, but don't like either:
  1. rewrite the aremote to by synchronous and disable the IRQ for the duration - this appears to be ~80ms; which doesn't sound too bad.
  2. Add a 2nd microcontroller to handle the IR and pass it through UART to the esp32 - but it seems a waste of hardware.

Code: Select all

# aremote.py Decoder for NEC protocol IR remote control
# e.g.https://www.adafruit.com/products/389
# Hacked to add a http server - but doesn't work

# Author: Peter Hinch
# Copyright Peter Hinch 2017 Released under the MIT license

from sys import platform
import uasyncio as asyncio
from primitives.message import Message
from primitives.queue import Queue
from micropython import const
from array import array
from utime import ticks_ms, ticks_us, ticks_diff
from machine import Pin
import gc

ESP32 = platform == 'esp32' or platform == 'esp32_LoBo'

# Save RAM
# from micropython import alloc_emergency_exception_buf
# alloc_emergency_exception_buf(100)

# Result codes (accessible to application)
# Repeat button code
REPEAT = -1
# Error codes
BADSTART = -2
BADBLOCK = -3
BADREP = -4
OVERRUN = -5
BADDATA = -6
BADADDR = -7

_EDGECOUNT = const(68)  # No. of edges in data block


# On 1st edge start a block timer. When it times out decode the data. Time must
# exceed the worst case block transmission time, but (with asyncio latency) be
# less than the interval between a block start and a repeat code start (108ms)
# Value of 73 allows for up to 35ms latency.
class NEC_IR():
    def __init__(self, pin, callback, extended, msg_queue, *args):  # Optional args for callback
        self._ev_start = Message()
        self._callback = callback
        self._extended = extended
        self._addr = 0
        self._args = args
        self.block_time = 80 if extended else 73  # Allow for some tx tolerance (?)
        self._times = array('i',  (0 for _ in range(_EDGECOUNT + 1)))  # +1 for overrun
        pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING))
        self._edge = 0
        self._ev_start.clear()
        self._msg_queue = msg_queue

    async def _run(self):
        print("_run: starting NEC_IR")
        try:
            while True:
                await self._ev_start  # Wait until data collection has started
                # Compensate for asyncio latency
                latency = ticks_diff(ticks_ms(), self._ev_start.value())
                await asyncio.sleep_ms(self.block_time - latency)  # Data block should have ended
                self._decode()  # decode, clear event, prepare for new rx, call cb
        except Exception as err:
            print("_run: Exception: {}".format(err))
        finally:
            print("_run: finshed")

    # Pin interrupt. Save time of each edge for later decode.
    def _cb_pin(self, line):
        t = ticks_us()
        # On overrun ignore pulses until software timer times out
        if self._edge <= _EDGECOUNT:  # Allow 1 extra pulse to record overrun
            if not self._ev_start.is_set():  # First edge received
                self._ev_start.set(ticks_ms())  # asyncio latency compensation
            self._times[self._edge] = t
            self._edge += 1

    def _decode(self):
        try:
            overrun = self._edge > _EDGECOUNT
            val = OVERRUN if overrun else BADSTART
            if not overrun:
                width = ticks_diff(self._times[1], self._times[0])
                if width > 4000:  # 9ms leading mark for all valid data
                    width = ticks_diff(self._times[2], self._times[1])
                    if width > 3000: # 4.5ms space for normal data
                        if self._edge < _EDGECOUNT:
                            # Haven't received the correct number of edges
                            val = BADBLOCK
                        else:
                            # Time spaces only (marks are always 562.5µs)
                            # Space is 1.6875ms (1) or 562.5µs (0)
                            # Skip last bit which is always 1
                            val = 0
                            for edge in range(3, _EDGECOUNT - 2, 2):
                                val >>= 1
                                if ticks_diff(self._times[edge + 1], self._times[edge]) > 1120:
                                    val |= 0x80000000
                    elif width > 1700: # 2.5ms space for a repeat code. Should have exactly 4 edges.
                        val = REPEAT if self._edge == 4 else BADREP
            addr = 0
            if val >= 0:  # validate. Byte layout of val ~cmd cmd ~addr addr
                print("_decode: Val: hex - {:08x}".format(val))
                addr = val & 0xff
                cmd = (val >> 16) & 0xff
#                 if addr == ((val >> 8) ^ 0xff) & 0xff:  # 8 bit address OK
#                     val = cmd if cmd == (val >> 24) ^ 0xff else BADDATA
#                     self._addr = addr
#                 else:
                addr |= val & 0xff00  # pass assumed 16 bit address to callback
#                 if self._extended:
                val = cmd if cmd == (val >> 24) ^ 0xff else BADDATA
                self._addr = addr
#                 else:
#                     val = BADADDR
            if val == REPEAT:
                addr = self._addr  # Last valid addresss
            self._edge = 0  # Set up for new data burst and run user callback
            self._ev_start.clear()
#             self._callback(val, addr, *self._args)
            self._msg_queue.put_nowait((val, addr))
        except Exception as e:
            print("_decode: exception: {}".format(e))
        

async def ir_cb(led, ir_rx_queue):
    while True:
        try:
#             await asyncio.sleep_ms(1)
            (data, addr) = await ir_rx_queue.get()
            print("Button press: data:{:02x} addr:{:04x}".format(data, addr), end=' - ')
            if addr == 0xfbe2:  # Adapt for your remote
                if data == 0x26:  # Button 1. Adapt for your remote/buttons
                    print('LED on')
                    led(1)
                elif data == 0x25:
                    print('LED off')
                    led(0)
                elif data == REPEAT:
                    print('Repeat')
                elif data < REPEAT:
                    print('Bad IR data')
                else:
                    print("Unnown data")
            else:
                print('Incorrect remote')
        except Exception as e:
            print("ir_cb: exception: {}".format(e))

async def client_server(reader, writer):
    cid = ticks_ms()
    print("client({}) - connection established".format(cid))
    req = await reader.readline()
    print(req)
    method, uri, proto = req.split(b" ")
    while True:
        h = await reader.readline()
        if h == b"" or h == b"\r\n":
            break
        print("client({}) - Received {}".format(cid,h))

    writer.write(b'HTTP/1.0 200 OK\r\n')
    writer.write("Content-Type: application/json\r\n\r\n")
    writer.write(b'{"msg":"HELLO"}\r\n')
    writer.write(b'\r\n')
    await writer.drain()
    writer.close()
    await writer.wait_closed()
    print("client({}) - connection closed".format(cid))
    gc.collect()
    
async def main(ir, ir_rx_queue):
    asyncio.create_task(ir._run())
    asyncio.create_task(ir_cb(led, ir_rx_queue))
    try:
        print("main: started http server")
        server = await asyncio.start_server(client_server, '0.0.0.0', 80)
        while True:
            await asyncio.sleep(60)
    except Exception as err:
        print("main: Exception: ",err)
    finally:
        print("main: closing server")
        server.close()
        await server.wait_closed()
        print("main: server closed")

    
p = Pin(23, Pin.IN)
led = Pin(16, Pin.OUT)  # LED with 220Ω series resistor between 3.3V and pin 21
led(1)
ir_rx_queue = Queue()
ir = NEC_IR(p, ir_cb, True, ir_rx_queue, False)  # Assume extended address mode r/c


try:
    asyncio.run(main(ir, ir_rx_queue))
except KeyboardInterrupt:
    print('Interrupted')  # This mechanism doesn't work on Unix build.
except Exception as err:
    print("Exception: ",err)
finally:
    _ = asyncio.new_event_loop()


User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: NEC_IR/aremote conflict with asyncio.start_server

Post by pythoncoder » Thu Sep 23, 2021 5:41 pm

If you are @adamoell on GitHub, did you try my suggestion for identifying whether the problem is hardware or firmware? If so, what was the outcome?
Peter Hinch
Index to my micropython libraries.

sloathog
Posts: 9
Joined: Tue Mar 10, 2020 10:30 pm

Re: NEC_IR/aremote conflict with asyncio.start_server

Post by sloathog » Fri Sep 24, 2021 8:19 am

Thanks for the quick reply, but no, not the same person and apparently not the same issue.
Same version: MicroPython v1.17 on 2021-09-02; ESP32 module with ESP32
If I understand the instruction, I connected Pin 23 direct to GND and check to see if I'm getting an irq callbacks while running wifi. Result is negative, no IRQ callbacks while pin is connected to GND. I put a print statement in the callback and didn't observe anything.

I had wifi/sta_if enabled all the time, system boots, connect to STA_IF, sets time via ntp and starts webrpl and the code works while I've connected via webrepl, it's only failing when I use the asyncio.start_server cmd.

I've added an array to store the ticks_diff for each time, using following code:

Code: Select all

self._times_diff[1]=ticks_diff(self._times[1], self._times[0])
self._times_diff[2]=ticks_diff(self._times[2], self._times[1])
for edge in range(3, _EDGECOUNT - 2, 2):
    self._times_diff[edge]=ticks_diff(self._times[edge + 1], self._times[edge])
Note: self._times_diff is printed using indexes: 1, 2 and range(3, _EDGECOUNT - 2, 2) - to remove the 0s

Results for with asyncio.start_server - fails to decode:
main: started http server
_run: starting NEC_IR
_decode: new signal
self._times_diff:8592,4573,830,826,839,828,822,822,828,836,829,1352,1339,834,825,2395,829,1353,829,842,828,834,825,833,829,829,828,822,828,829,829,831,829,822,
_decode: Val: hex - 0000a600
Button press: data:-6 addr:a600 - Incorrect remote


Result without asyncio.start_server - works fine, i get the occasional BAD_DATA, but it works 9 out of 10 times.
_run: starting NEC_IR
_decode: new signal
self._times_diff:9339,4333,822,1934,246,246,249,1420,1420,1420,1423,1420,245,1420,1420,1419,1420,1920,245,1419,1419,246,245,1420,250,251,1433,253,250,1419,1420,252,1433,1420,
_decode: Val: hex - d926fbe2
Button press: data:26 addr:fbe2 - LED on


Both have the 9ms and 4.5ms preamble, but the address/command decode fails.
From the success (without start_server) it looks like a short burst is ~250ticks (0), and long ~1420 ticks (1).
From the failure above (with start server), it looks like it has a delay in the irq callbacks for the short burst, increasing the short burst time from ~250 to 820, I can then assume the callbacks are overlapping with the next.

Boot sequence:
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:5656
load:0x40078000,len:12696
load:0x40080400,len:4292
entry 0x400806b0
connecting to network...
connecting....network config: ('192.168.1.226', '255.255.255.0', '192.168.1.1', '192.168.1.33')
WebREPL daemon started on ws://192.168.1.226:8266
Started webrepl in normal mode
Time set: 2021-09-24 07:27:37 GMT
MicroPython v1.17 on 2021-09-02; ESP32 module with ESP32

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: NEC_IR/aremote conflict with asyncio.start_server

Post by pythoncoder » Fri Sep 24, 2021 11:00 am

Your test seems to confirm it's a software issue.

I can only assume that something in your http server is preventing pin interrupts from being serviced quickly. The ESP32 and ESP8266 only support soft IRQ's. This means that sometimes they will be serviced very late (several ms) if a garbage collect is in progress. In the context of IR reception (which is never 100% perfect) this goes unnoticed. However something is causing mayhem here. I wonder if your server uses blocking sockets, and these affect soft IRQ's?

I'm afraid I have no ready-made answer to this one, other than to perhaps put a warning in the docs that, owing to soft IRQ's on ESPx, this code may not play well with other concurrent applications on those platforms.
Peter Hinch
Index to my micropython libraries.

sloathog
Posts: 9
Joined: Tue Mar 10, 2020 10:30 pm

Re: NEC_IR/aremote conflict with asyncio.start_server

Post by sloathog » Fri Sep 24, 2021 1:09 pm

I'm using the asyncio.start_server(client_server, '0.0.0.0', 80) - which I assume is non-blocking; so I don't understand why it's blocking.

I came up with a solution - i swapped from IRQ to hard polling:
When the IRQ callback is called, I then clear the IRQ handler and process IR input synchronously by polling.
I poll in Pin value every ~100us, store results in an array, then process the array by calling a function named get_bit to find the bits in the array. In total collecting 768 values, which takes ~76.8ms - longer than the expected length of the signal.
The following bit takes 100us to process on my ESP32 and it's run 768 times.

Code: Select all

bit_list[i]=pin.value() 
sleep_us(80) 
function get_bit is looking for a sequence of 0s followed by 1s, debug shows:
  • 6-7 0s and 5 or 6 1s which relates to a 0 bit.
  • 6-7 0s and 15-17 1s, which it relates to a 1 bit
NEC spec states zero bit is a 562.5µs bust and 562.5µs space, while a 1 is a 562.5µs burst and 1.6875ms space (3x562.5us)
I'm polling ~10-11 times for a 0 and ~20-24 times for a 1.
Function then checks to see if number 1s is more than twice the number of zeros (should be 3 times).
It then returns the bit value, and the index in the array for the start of the next bit.
I could reduce the polling, by increasing the sleep_us, but the ESP cant doing anything else, as it's blocking.
  1. Ignore the preamable - 9ms, and 4,5ms, in this case a series of ~80 0 bits and 45 1 bits - seen as 1 pattern by get_bit
  2. Find the next 16 bits for the address
  3. Find the next 8 bits for cmd
  4. Find the next 8 bits for cmd inverse, which I inverse - should compare these at this point.
  5. put the result in the queue
Any spam that's picked up by the IR decoder, bad codes or just light - git_bit generates an index error if it fails to find 33 patterns expected.
Repeat codes come up as index error as well and are ignored.

Full test code is below, not very pretty.
Does anyone think this method is a bad idea? Essentially I'm blocking all the rest of the asyncio code, but it's only for 76ms per button press.

In it's entirety, the ESP controls a set of downfacing white LED lights using PWM to dim via a MOSFET and a set of neopixels facing upwards - which I run patterns on. It has a web front end for controlling the lights, I'm adding the IR remote, and then will add mqtt_as.

Code: Select all

from machine import Pin
import utime as time
from utime import ticks_ms, ticks_us, ticks_diff, sleep_us, sleep_ms
from primitives.queue import Queue
import gc
import uasyncio as asyncio

msg_queue = Queue()
_RANGE = 768
bit_list = [0] * _RANGE
bit_time = [0] * _RANGE
bit_time_diff = [0] * _RANGE
add_list = [0] * 16
cmd_list = [0] * 8
cmd_list_rev = [0] * 8


def get_bit(start):
    i=start
    count_zeros = 0
    count_ones = 0
    try:
        while bit_list[i] == 0:
            count_zeros +=1
            i+=1
        while bit_list[i] == 1:
            count_ones +=1
            i+=1
    except Exception as err:
        print("get_bit(): {}".format(err))
        raise
    bit_length=count_zeros+count_ones
    if count_zeros * 2 > count_ones: # ones should be 3 times length of zeros
        bit = 0
    else:
        bit = 1
    print("Index:{} 0s:{} 1s:{} res:{} next: {}, array:{}".format(start,count_zeros, count_ones, bit, i, bit_list[start:i]))

    return (bit, i) # bit value and next bit start index

def process_ir_cmd(addr, cmd, cmd_rev):
    print("Cmd: a:{} c:{} c_r:{}".format(addr, cmd, cmd_rev))
    pass

def cb_pin(pin):
    global msg_queue
    p.irq(handler = None)
    triggered_us = ticks_us()
    for i in range(_RANGE): # total command takes ~100us, value() and sleep_us(), 768 times - capture takes ~ 76800us
        bit_list[i]=pin.value()
        sleep_us(80) 
    end_us = ticks_us()    
    try:
        (__, address_start) = get_bit(0) # 9ms burst and 4.5ms space, value not important
#         print("Find address starts at {}".format(address_start))
        index = address_start
        for j in range(15,-1,-1):
            (bit, index) = get_bit(index)
            add_list[j]=bit
        out = 0
        for bit in add_list:
            out = (out << 1) | bit
        address_int = out
#         print("Address: h:{} d:{} b:{}".format(hex(out), out, add_list))

#         print("Find command starts at {}".format(index))
        for j in range(7,-1,-1):
            (bit, index) = get_bit(index)
            cmd_list[j]=bit
        out = 0
        for bit in cmd_list:
            out = (out << 1) | bit
        cmd_int = out
#         print("Cmd: h:{} d:{} b:{}".format(hex(out), out, cmd_list))

#         print("Find inverse command starts at {}".format(index))
        for j in range(7,-1,-1):
            (bit, index) = get_bit(index)
            cmd_list_rev[j]= not bit
        out = 0
        for bit in cmd_list_rev:
            out = (out << 1) | bit
        cmd_int_rev = out
#         print("Cmd Inverse: h:{} d:{} b:{}".format(hex(out), out, cmd_list_rev))
    except IndexError as err:
        pass # ignore
    except Exception as err:
        print("Failed to decode {}:\n{}".format(err,bit_list)) # shouldn't happen
    else:
        try:
            msg_queue.put_nowait((address_int, cmd_int))
        except Exception as err2:
            print("Failed to queue message ({},{},{}): err: {}".format(address_int, cmd_int, cmd_int_rev, err2)) # shouldn't happen
#         else:
#             print("Queue: {}".format(msg_queue.qsize()))
    finally:
#         print("Total run time: {}, capture time: {}".format(ticks_diff(ticks_us(),triggered_us),ticks_diff(end_us,triggered_us)))
        p.irq(handler = cb_pin)


async def client_server(reader, writer):
    cid = ticks_ms()
    print("client({}) - connection established".format(cid))
    req = await reader.readline()
    print(req)
    method, uri, proto = req.split(b" ")
    while True:
        h = await reader.readline()
        if h == b"" or h == b"\r\n":
            break
        print("client({}) - Received {}".format(cid,h))

    writer.write(b'HTTP/1.0 200 OK\r\n')
    writer.write("Content-Type: application/json\r\n\r\n")
    writer.write(b'{"msg":"HELLO"}\r\n')
    writer.write(b'\r\n')
    await writer.drain()
    writer.close()
    await writer.wait_closed()
    print("client({}) - connection closed".format(cid))
    gc.collect()
    
async def process_msg_queue(led):
    global msg_queue
    print("process_msg_queue(): started")
    await asyncio.sleep(5)
    while True:
        try:
            print("Waiting for data in queue... qsize:{}".format(msg_queue.qsize()))
            (addr, cmd) = await msg_queue.get()
            print("Button press: data:{:02x} addr:{:04x}".format(cmd, addr), end=' - ')
            if addr == 0xfbe2:  # Adapt for your remote
                if cmd == 0x26:  # Button 1. Adapt for your remote/buttons
                    print('LED on')
                    led(1)
                elif cmd == 0x25:
                    print('LED off')
                    led(0)
                else:
                    print("Unnown data")
            else:
                print('Incorrect remote address')
        except Exception as e:
            print("process_msg_queue: exception: {}".format(e))
    print("process_msg_queue(): finshed")

   
async def main(led):
    try:
        asyncio.create_task(process_msg_queue(led))
        print("main: started http server")
        server = await asyncio.start_server(client_server, '0.0.0.0', 80)
        while True:
            await asyncio.sleep(60)
    except Exception as err:
        print("main: Exception: ",err)
    finally:
        print("main: closing server")
        server.close()
        await server.wait_closed()
        print("main: server closed")
        asyncio.new_event_loop()

try:
    print("Starting")
    p = Pin(23, Pin.IN)
    p.irq(handler = cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING))
    led = Pin(16, Pin.OUT, 0)  # LED with 220Ω series resistor between 3.3V and pin 21
    asyncio.run(main(led))
except KeyboardInterrupt:
    print('Interrupted')  # This mechanism doesn't work on Unix build.
except Exception as err:
    print("Exception: ",err)
finally:
    p.irq(handler = None)
    print("Done")
    gc.collect()
Last edited by sloathog on Fri Sep 24, 2021 1:31 pm, edited 1 time in total.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: NEC_IR/aremote conflict with asyncio.start_server

Post by pythoncoder » Fri Sep 24, 2021 1:28 pm

It seems to me that you have an ISR which blocks for 76.8ms. This may cause trouble with other uasyncio code. I'm not sure how mqtt_as will appreciate delays of that length appearing at random times. It may be OK, but I've never tested it in such an environment.

The ideal (albeit expensive) solution would be to use a Pyboard D which uses hard IRQ's.
Peter Hinch
Index to my micropython libraries.

sloathog
Posts: 9
Joined: Tue Mar 10, 2020 10:30 pm

Re: NEC_IR/aremote conflict with asyncio.start_server

Post by sloathog » Fri Sep 24, 2021 1:50 pm

Just out of interest, I ran the following code:
Purpose was to test how long await asyncio.sleep_ms(0) takes to run 1000 times.
I performed this with asyncio.start_server running, then closed the server and ran again.
~950us for await asyncio.sleep_ms(0) per while loop iteration, each time without the server.
~1700us for await asyncio.sleep_ms(0) per while loop iteration, each time with the server running.
~100us for a blocking while loop with just sleep_ms(0), per while loop iteration,

main: started http server
main: closing server
main: server closed
Await Sleep with start_server Result: First:205158292 Last:206856230, duration: 1697938: avg:1697.938, cycles:1000
main: running test after server had closed
Await Sleep Result without server: First:207869405 Last:208805202, duration: 935797: avg:935.797, cycles:1000
main: running test with just sleep - blocking
Sleep Result: First:209814160 Last:209924979, duration: 110819: avg:110.819, cycles:1000



Code: Select all

async def client_server(reader, writer):
    pass


async def main():
    try:
        print("main: started http server")
        server = await asyncio.start_server(client_server, '0.0.0.0', 80)
        i=0
        _EDGECOUNT = 1000
        start_time=ticks_us()
        while i <= _EDGECOUNT:
            await asyncio.sleep_ms(0)
            i+=1
        end_time=ticks_us()
        print("main: closing server")
        server.close()
        await server.wait_closed()
        print("main: server closed")
        print("Await Sleep with start_server Result: First:{} Last:{}, duration: {}: avg:{}, cycles:{}".format(start_time,end_time,ticks_diff(end_time,start_time),ticks_diff(end_time,start_time)/_EDGECOUNT, _EDGECOUNT))
        
        await asyncio.sleep(1)
        print("main: running test after server had closed")
        i=0
        start_time=ticks_us()
        while i <= _EDGECOUNT:
            await asyncio.sleep_ms(0)
            i+=1
        end_time=ticks_us()
        print("Await Sleep Result without server: First:{} Last:{}, duration: {}: avg:{}, cycles:{}".format(start_time,end_time,ticks_diff(end_time,start_time),ticks_diff(end_time,start_time)/_EDGECOUNT, _EDGECOUNT))


        await asyncio.sleep(1)
        print("main: running test with just sleep - blocking")
        i=0
        start_time=ticks_us()
        while i <= _EDGECOUNT:
            sleep_ms(0)
            i+=1
        end_time=ticks_us()
        print("Sleep Result: First:{} Last:{}, duration: {}: avg:{}, cycles:{}".format(start_time,end_time,ticks_diff(end_time,start_time),ticks_diff(end_time,start_time)/_EDGECOUNT, _EDGECOUNT))


    except Exception as err:
        print("main: Exception: ",err)

        
        
try:
    asyncio.run(main())
except KeyboardInterrupt:
    print('Interrupted')  # This mechanism doesn't work on Unix build.
except Exception as err:
    print("Exception: ",err)
finally:
    _ = asyncio.new_event_loop()

 

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: NEC_IR/aremote conflict with asyncio.start_server

Post by pythoncoder » Sat Sep 25, 2021 8:02 am

I wrote this benchmark for uasyncio which produces this outcome on a Pyboard 1.1
# Outcome on a Pyboard 1.1
# 100 minimal coros are scheduled at an interval of 195μs on uasyncio V3
I've just tried it on a Pico and get 481μs, on the ESP32 reference board I get 920μs which accords with your test.

I have added this warning to the IR README.
Peter Hinch
Index to my micropython libraries.

Post Reply