peterhinch/micropython-async

I'm trying to use aremote.py togother with picoweb, and thing works werid

Closed this issue · 3 comments

fruch commented

I'm doing something like this:

ROUTES = [
    # You can specify exact URI string matches...
    ("/", lambda req, resp: (yield from app.sendfile(resp, "index.html"))),
    ("/favicon.ico", lambda req, resp: (yield from app.sendfile(resp, "favicon.ico"))),
    ("/tinycolorpicker.css", lambda req, resp: (yield from app.sendfile(resp, "tinycolorpicker.css"))),
    ("/tinycolorpicker.js", lambda req, resp: (yield from app.sendfile(resp, "tinycolorpicker.js"))),
    ("/text-color.png", lambda req, resp: (yield from app.sendfile(resp, "text-color.png"))),
    ("/rgb/", rgb),
]
            
def cb(data, addr):
    if data == REPEAT:
        print('Repeat')
    elif data >= 0:
        print(hex(data), hex(addr))
    else:
        print('{} Address: {}'.format(data, hex(addr)))

print('Test for IR receiver. Assumes NEC protocol.')
machine.freq(160000000)
p = machine.Pin(14, machine.Pin.IN)
ir = NEC_IR(p, cb, True)  # Assume r/c uses extended addressing
    
app = picoweb.WebApp(__name__, ROUTES)
app.run(debug=False, host="0.0.0.0")

and aremote seem to be working fine, but every http request is blocked until pressing a remote key.
couldn't figure out what fishy here...

That's got me beat, too. I haven't used picoweb but from a quick look at the code they should play together. While waiting for a signal from the remote, at line 57 in aremote.py, it waits on the Event instance's __iter__ / __await__ method which waits on sleep(0). So other coroutines should be scheduled in round-robin fashion. You could try putting debug print statements there to verify that's what's happening.

I'd also be tempted as a test to omit the IR stuff and write a simple coro which does something like periodically print "hello" to prove picoweb behaves sociably.

It may be a few days before I get a chance to look at this more closely.

OK, I found some time to look at this. I think we have a bug in uasyncio. For a quick workround/test please can you alter asyn.py so that the Event object's __await__ method reads:

    def __await__(self):
        while not self._flag:
            yield from asyncio.sleep_ms(5)

It seems that an awaitable object yielding 0 blocks streamreaders (although the scheduler is still running and I could schedule other coros). To prove the point I produced the following, which has the Event class stripped down to an awaitable Bar which just waits forever. The foo coro works correctly regardless of the sleep time. But with a sleep time of zero the webapp fails. With it > 0 it runs.

import ure as re
import picoweb
import uasyncio as asyncio

def index(req, resp):
    # You can construct an HTTP response completely yourself, having
    # a full control of headers sent...
    yield from resp.awrite("HTTP/1.0 200 OK\r\n")
    yield from resp.awrite("Content-Type: text/html\r\n")
    yield from resp.awrite("\r\n")
    yield from resp.awrite("I can show you a table of <a href='squares'>squares</a>.<br/>")
    yield from resp.awrite("Or my <a href='file'>source</a>.")


def squares(req, resp):
    # Or can use a convenience function start_response() (see its source for
    # extra params it takes).
    yield from picoweb.start_response(resp)
    yield from app.render_template(resp, "squares.tpl", (req,))


def hello(req, resp):
    yield from picoweb.start_response(resp)
    # Here's how you extract matched groups from a regex URI match
    yield from resp.awrite("Hello " + req.url_match.group(1))


ROUTES = [
    # You can specify exact URI string matches...
    ("/", index),
    ("/squares", squares),
    ("/file", lambda req, resp: (yield from app.sendfile(resp, "example_webapp.py"))),
    # ... or match using a regex, a match result available as req.url_match
    # for group extraction in your view.
    (re.compile("^/iam/(.+)"), hello),
]

import logging
logging.basicConfig(level=logging.INFO)
#logging.basicConfig(level=logging.DEBUG)

class Bar():

    def __await__(self):
        while True:
            yield from asyncio.sleep_ms(5)  # Change this to 0 and the webapp fails

    __iter__ = __await__

bar = Bar()

async def foo():
    print('Starting foo. Should never finish.')
    await bar
    print('foo finished.')

loop = asyncio.get_event_loop()
loop.create_task(foo())
app = picoweb.WebApp(__name__, ROUTES)
app.run(debug=True, host='0.0.0.0')

I'll try to find a simpler testcase and raise an issue.