async def _connect(connection, timeout_ms): device = connection.device if device in _connecting: return # Enable BLE and cancel in-progress scans. ensure_active() await _cancel_pending() # Allow the connected IRQ to find the device by address. _connecting.add(device) # Event will be set in the connected IRQ, and then later # re-used to notify disconnection. connection._event = connection._event or asyncio.ThreadSafeFlag() try: with DeviceTimeout(None, timeout_ms): ble.gap_connect(device.addr_type, device.addr) # Wait for the connected IRQ. await connection._event.wait() assert connection._conn_handle is not None # Register connection handle -> device. DeviceConnection._connected[connection._conn_handle] = connection finally: # After timeout, don't hold a reference and ignore future events. _connecting.remove(device)
def __init__(self, pin_x, pin_y, v=0, vmin=None, vmax=None, div=1, callback=lambda a, b: None, args=()): self._pin_x = pin_x self._pin_y = pin_y self._v = 0 # Hardware value always starts at 0 self._cv = v # Current (divided) value if ((vmin is not None) and v < min) or ((vmax is not None) and v > vmax): raise ValueError('Incompatible args: must have vmin <= v <= vmax') self._tsf = asyncio.ThreadSafeFlag() trig = Pin.IRQ_RISING | Pin.IRQ_FALLING try: xirq = pin_x.irq(trigger=trig, handler=self._x_cb, hard=True) yirq = pin_y.irq(trigger=trig, handler=self._y_cb, hard=True) except TypeError: # hard arg is unsupported on some hosts xirq = pin_x.irq(trigger=trig, handler=self._x_cb) yirq = pin_y.irq(trigger=trig, handler=self._y_cb) asyncio.create_task(self._run(vmin, vmax, div, callback, args))
async def main(): flag = asyncio.ThreadSafeFlag() # Set the flag from within the loop. t = asyncio.create_task(task(1, flag)) print("yield") await asyncio.sleep(0) print("set event") flag.set() print("yield") await asyncio.sleep(0) print("wait task") await t # Set the flag from scheduler context. print("----") t = asyncio.create_task(task(2, flag)) print("yield") await asyncio.sleep(0) print("set event") micropython.schedule(set_from_schedule, flag) print("yield") await asyncio.sleep(0) print("wait task") await t # Flag already set. print("----") print("set event") flag.set() t = asyncio.create_task(task(3, flag)) print("yield") await asyncio.sleep(0) print("wait task") await t
def __init__(self, connection): if not connection.is_connected(): raise ValueError("Not connected") if connection._l2cap_channel: raise ValueError("Already has channel") connection._l2cap_channel = self self._connection = connection # Maximum size that the other side can send to us. self.our_mtu = 0 # Maximum size that we can send. self.peer_mtu = 0 # Set back to None on disconnection. self._cid = None # Set during disconnection. self._status = 0 # If true, must wait for _IRQ_L2CAP_SEND_READY IRQ before sending. self._stalled = False # Has received a _IRQ_L2CAP_RECV since the buffer was last emptied. self._data_ready = False self._event = asyncio.ThreadSafeFlag()
def __init__( self, service, uuid, read=False, write=False, write_no_response=False, notify=False, indicate=False, initial=None, capture=False, ): service.characteristics.append(self) self.descriptors = [] flags = 0 if read: flags |= _FLAG_READ if write or write_no_response: flags |= (_FLAG_WRITE if write else 0) | ( _FLAG_WRITE_NO_RESPONSE if write_no_response else 0) if capture: # Capture means that we keep track of all writes, and capture # their values (and connection) in a queue. Otherwise we just # track the most recent connection. flags |= _FLAG_WRITE_CAPTURE self._write_event = asyncio.ThreadSafeFlag() self._write_queue = deque( (), _WRITE_CAPTURE_QUEUE_LIMIT if capture else 1) if notify: flags |= _FLAG_NOTIFY if indicate: flags |= _FLAG_INDICATE # TODO: This should probably be a dict of connection to (ev, status). # Right now we just support a single indication at a time. self._indicate_connection = None self._indicate_event = asyncio.ThreadSafeFlag() self._indicate_status = None self.uuid = uuid self.flags = flags self._value_handle = None self._initial = initial
async def exchange_mtu(self, mtu=None): if not self.is_connected(): raise ValueError("Not connected") if mtu: ble.config(mtu=mtu) self._mtu_event = self._mtu_event or asyncio.ThreadSafeFlag() ble.gattc_exchange_mtu(self._conn_handle) await self._mtu_event.wait() return self.mtu
def __init__(self, func=None, args=(), duration=1000): self._func = func self._args = args self._durn = duration # Default duration self._retn = None # Return value of launched callable self._tend = None # Stop time (absolute ms). self._busy = False self._trig = asyncio.ThreadSafeFlag() self._tout = asyncio.Event() # Timeout event self.wait = self._tout.wait # Allow: await wait_ms.wait() self._ttask = self._fake # Timer task asyncio.create_task(self._run())
async def pair( connection, bond=True, le_secure=True, mitm=False, io=_IO_CAPABILITY_NO_INPUT_OUTPUT, timeout_ms=20000, ): ble.config(bond=bond, le_secure=le_secure, mitm=mitm, io=io) with connection.timeout(timeout_ms): connection._pair_event = asyncio.ThreadSafeFlag() ble.gap_pair(connection._conn_handle) await connection._pair_event.wait()
def __init__(self, service, def_handle, value_handle, properties, uuid): self.service = service self.connection = service.connection # Used for read/write/notify ops. self._def_handle = def_handle self._value_handle = value_handle # Which operations are supported. self.properties = properties # Allows comparison to a known uuid. self.uuid = uuid if properties & _FLAG_READ: # Fired for each read result and read done IRQ. self._read_event = None self._read_data = None # Used to indicate that the read is complete. self._read_status = None if (properties & _FLAG_WRITE) or (properties & _FLAG_WRITE_NO_RESPONSE): # Fired for the write done IRQ. self._write_event = None # Used to indicate that the write is complete. self._write_status = None if properties & _FLAG_NOTIFY: # Fired when a notification arrives. self._notify_event = asyncio.ThreadSafeFlag() # Data for the most recent notification. self._notify_queue = deque((), 1) if properties & _FLAG_INDICATE: # Same for indications. self._indicate_event = asyncio.ThreadSafeFlag() self._indicate_queue = deque((), 1)
def __init__(self, characteristic, uuid, read=False, write=False, initial=None): characteristic.descriptors.append(self) # Workaround for https://github.com/micropython/micropython/issues/6864 flags = 0 if read: flags |= _FLAG_DESC_READ if write: self._write_connection = None self._write_event = asyncio.ThreadSafeFlag() flags |= _FLAG_DESC_WRITE self.uuid = uuid self.flags = flags self._value_handle = None self._initial = initial
async def read(self, timeout_ms=1000): self._check(_FLAG_READ) # Make sure this conn_handle/value_handle is known. self._register_with_connection() # This will be set by the done IRQ. self._read_status = None # This will be set by the result and done IRQs. Re-use if possible. self._read_event = self._read_event or asyncio.ThreadSafeFlag() # Issue the read. ble.gattc_read(self._connection()._conn_handle, self._value_handle) with self._connection().timeout(timeout_ms): # The event will be set for each read result, then a final time for done. while self._read_status is None: await self._read_event.wait() if self._read_status != 0: raise GattError(self._read_status) return self._read_data
def __init__(self, duration_ms, interval_us=None, window_us=None, active=False): self._queue = [] self._event = asyncio.ThreadSafeFlag() self._done = False # Keep track of what we've already seen. self._results = set() # Ideally we'd start the scan here and avoid having to save these # values, but we need to stop any previous scan first via awaiting # _cancel_pending(), but __init__ isn't async. self._duration_ms = duration_ms self._interval_us = interval_us or 1280000 self._window_us = window_us or 11250 self._active = active
async def write(self, data, response=False, timeout_ms=1000): self._check(_FLAG_WRITE | _FLAG_WRITE_NO_RESPONSE) # If we only support write-with-response, then force sensible default. if response is None and (self.properties & _FLAGS_WRITE) and not (self.properties & _FLAG_WRITE_NO_RESPONSE): response = True if response: # Same as read. self._register_with_connection() self._write_status = None self._write_event = self._write_event or asyncio.ThreadSafeFlag() # Issue the write. ble.gattc_write(self._connection()._conn_handle, self._value_handle, data, response) if response: with self._connection().timeout(timeout_ms): # The event will be set for the write done IRQ. await self._write_event.wait() if self._write_status != 0: raise GattError(self._write_status)
def __init__(self, connection, disc_type, parent, timeout_ms, *args): self._connection = connection # Each result IRQ will append to this. self._queue = [] # This will be set by the done IRQ. self._status = None # Tell the generator to process new events. self._event = asyncio.ThreadSafeFlag() # Must implement the _start_discovery static method. Instances of this # type are returned by __anext__. self._disc_type = disc_type # This will be the connection for a service discovery, and the service for a characteristic discovery. self._parent = parent # Timeout for the discovery process. # TODO: Not implemented. self._timeout_ms = timeout_ms # Additional arguments to pass to the _start_discovery method on disc_type. self._args = args
import uasyncio as asyncio from machine import Timer tsf = asyncio.ThreadSafeFlag() def cb(_): tsf.set() async def foo(): while True: await tsf.wait() # Could set an Event here to trigger multiple tasks print('Triggered') tim = Timer(1) tim.init(period=1000, callback=cb) asyncio.run(foo())
async def advertise( interval_us, adv_data=None, resp_data=None, connectable=True, limited_disc=False, br_edr=False, name=None, services=None, appearance=0, manufacturer=None, timeout_ms=None, ): global _incoming_connection, _connect_event ensure_active() if not adv_data and not resp_data: # If the user didn't manually specify adv_data / resp_data then # construct them from the kwargs. Keep adding fields to adv_data, # overflowing to resp_data if necessary. # TODO: Try and do better bin-packing than just concatenating in # order? adv_data = bytearray() resp_data = _append( adv_data, resp_data, _ADV_TYPE_FLAGS, struct.pack("B", (0x01 if limited_disc else 0x02) + (0x18 if br_edr else 0x04)), ) if name: resp_data = _append(adv_data, resp_data, _ADV_TYPE_NAME, name) if services: for uuid in services: b = bytes(uuid) if len(b) == 2: resp_data = _append(adv_data, resp_data, _ADV_TYPE_UUID16_COMPLETE, b) elif len(b) == 4: resp_data = _append(adv_data, resp_data, _ADV_TYPE_UUID32_COMPLETE, b) elif len(b) == 16: resp_data = _append(adv_data, resp_data, _ADV_TYPE_UUID128_COMPLETE, b) if appearance: # See org.bluetooth.characteristic.gap.appearance.xml resp_data = _append(adv_data, resp_data, _ADV_TYPE_APPEARANCE, struct.pack("<H", appearance)) if manufacturer: resp_data = _append( adv_data, resp_data, _ADV_TYPE_MANUFACTURER, struct.pack("<H", manufacturer[0]) + manufacturer[1], ) _connect_event = _connect_event or asyncio.ThreadSafeFlag() ble.gap_advertise(interval_us, adv_data=adv_data, resp_data=resp_data, connectable=connectable) try: # Allow optional timeout for a central to connect to us (or just to stop advertising). with DeviceTimeout(None, timeout_ms): await _connect_event.wait() # Get the newly connected connection to the central and start a task # to wait for disconnection. result = _incoming_connection _incoming_connection = None # This mirrors what connecting to a central does. result._run_task() return result except asyncio.CancelledError: # Something else cancelled this task (to manually stop advertising). ble.gap_advertise(None) except asyncio.TimeoutError: # DeviceTimeout waiting for connection. ble.gap_advertise(None) raise
raise SystemExit import micropython try: micropython.schedule except AttributeError: print("SKIP") raise SystemExit try: # Unix port can't select/poll on user-defined types. import uselect as select poller = select.poll() poller.register(asyncio.ThreadSafeFlag()) except TypeError: print("SKIP") raise SystemExit async def task(id, flag): print("task", id) await flag.wait() print("task", id, "done") def set_from_schedule(flag): print("schedule") flag.set() print("schedule done")
def __init__(self, pin, trigger): self.pin = pin self.flag = asyncio.ThreadSafeFlag() self.pin.irq(lambda pin: self.flag.set(), trigger, hard=True)
def _run_task(self): # Event will be already created this if we initiated connection. self._event = self._event or asyncio.ThreadSafeFlag() self._task = asyncio.create_task(self.device_task())