ble = BLERadio() ble.name = "UART With BLE" uart_service = UARTService() advertisement = ProvideServicesAdvertisement(uart_service) advertisement.short_name = SERVICE_NAME # advertisement.complete_name = "UART BLE" # no more than 8 to not go into extended adv ? was_connected = False while True: # Advertise BLE when not connected. if not ble.connected: was_connected = False if not ble.advertising: print(f'Start advertising as "{SERVICE_NAME}"') ble.start_advertising(advertisement, interval=0.5, timeout=5) # dismiss uart buffer when not connected if uart.in_waiting: uart.reset_input_buffer() else: if not was_connected: was_connected = True print("Connected") ble.stop_advertising() # pass-through uart data when connected if nbytes := uart.in_waiting: # data = uart.read(nbytes) data = uart.readline()
class Ble(): def __init__(self, onConnectionStateChanged, onAdvertising=None, onWrite=None): self._ble = BLERadio() self._uart_server = UARTService() self._onAdvertising = onAdvertising self._onWrite = onWrite self._advertisement = ProvideServicesAdvertisement(self._uart_server) self._isAdvertising = False self._onAdvertising = onAdvertising self._onWrite = onWrite self.__oldConnectionState = False self._onConnectionStateChanged = onConnectionStateChanged self._enabled = False def write(self, data): if self._ble.connected: self._uart_server.write(data) if self._onWrite: self._onWrite(data) def startAdvertising(self): if not self._ble.connected: self.stopAdvertising() self._ble.start_advertising(self._advertisement) display("Start Phone App and connect") display("Started Advertising to BLE devices") if self._onAdvertising: self._onAdvertising() self._isAdvertising = True def stopAdvertising(self): # if self._ble.connected: display("Stopped Advertising") self._ble.stop_advertising() self._isAdvertising = False def read(self): if self._ble.connected != self.__oldConnectionState: self._onConnectionStateChanged() self.__oldConnectionState = self._ble.connected def enable(self): self.startAdvertising() self._enabled = True def disable(self): self.stopAdvertising() self._enabled = False def toggle(self): if self._enabled: self.disable() else: self.enable() @property def isAdvertising(self): return self._isAdvertising @property def connected(self): return self._ble.connected @property def enabled(self): return self._enabled
animations = AnimationSequence( AnimationGroup( Comet(strip_pixels, COMET_SPEED, color.TEAL, tail_length=STRIP_COMET_TAIL_LENGTH, bounce=STRIP_COMET_BOUNCE)), AnimationGroup(Sparkle(strip_pixels, SPARKLE_SPEED, color.TEAL)), ) animation_color = None mode = 0 blanked = False while True: ble.start_advertising(advertisement) # Start advertising. was_connected = False while not was_connected or ble.connected: if not blanked: # If LED-off signal is not being sent... animations.animate() # Run the animations. if ble.connected: # If BLE is connected... was_connected = True if uart.in_waiting: # Check to see if any data is available from the Remote Control. try: packet = Packet.from_stream( uart) # Create the packet object. except ValueError: continue if isinstance(packet, ColorPacket): # If the packet is color packet... if mode == 0: # And mode is 0...
ble = BLERadio() # Turn on Bluetooth uart_server = UARTService() # Turn on UART advertisement = ProvideServicesAdvertisement(uart_server) # Set up notice for other devices that Clue has a Bluetooth UART connection maxSpeed = 35 clue.sea_level_pressure = 1020 # Set sea level pressure for Clue's Altitude sensor. ###################################################### # Main Code ###################################################### while True: print("WAITING for BlueFruit device...") # Advertise when not connected. ble.start_advertising(advertisement) # Tell other devices that Clue has a Bluetooth UART connection. while not ble.connected: # Check to see if another device has connected with the Clue via Bluetooth. if clue.button_a: break pass # Do nothing this loop. # Connected ble.stop_advertising() # Stop telling other devices about the Clue's Bluetooth UART Connection. print("CONNECTED") # Loop and read packets while ble.connected: # Check to see if we are still connected. if clue.button_a: break
"""RPS choice.""" ble = BLERadio() ##ble.name = ? choices = ("rock", "paper", "scissors") while True: if button_left(): tx_message = RpsTestAdvertisement() my_choice = choices[random.randrange(len(choices))] tx_message.test_string = my_choice d_print(2, "RTA txing", my_choice) ble.start_advertising(tx_message, interval=0.05) sending_ns = time.monotonic_ns() ##print("ssssssssss") ##message.test32bit = "ssssssss" ##ble.start_advertising(message) ##time.sleep(5) ##ble.stop_advertising() ### timeout in seconds ### -100 is probably minimum for adv in ble.start_scan(RpsTestAdvertisement, minimum_rssi=-127, timeout=15): d_print(2, "RTA rxed", adv.test_string)
if button_left(): while button_left(): pass my_choice_idx = (my_choice_idx + 1) % len(choices) setCursor(my_choice_idx, "mine") if button_right(): tx_message = RpsAdvertisement() choice = choices[my_choice_idx] tx_message.test_string = choice d_print(2, "TXing RTA", choice) opponent_choice = None ble.start_advertising(tx_message, interval=MIN_AD_INTERVAL) sending_ns = time.monotonic_ns() # Timeout value is in seconds # RSSI -100 is probably minimum, -128 would be 8bit signed min # window and interval are 0.1 by default - same value means # continuous scanning (sending Advertisement will interrupt this) for adv in ble.start_scan(RpsAdvertisement, minimum_rssi=-90, timeout=MAX_SEND_TIME_S): received_ns = time.monotonic_ns() d_print(2, "RXed RTA", adv.test_string) opponent_choice_bytes = adv.test_string # Trim trailing NUL chars from bytes idx = 0
class Radio: """ Represents a connection through which one can send or receive strings and bytes. The radio can be tuned to a specific channel upon initialisation or via the `configure` method. """ def __init__(self, **args): """ Takes the same configuration arguments as the `configure` method. """ # For BLE related operations. self.ble = BLERadio() # The uid for outgoing message. Incremented by one on each send, up to # 255 when it's reset to 0. self.uid = 0 # Contains timestamped message metadata to mitigate report of # receiving of duplicate messages within AD_DURATION time frame. self.msg_pool = set() # Handle user related configuration. self.configure(**args) def configure(self, channel=42): """ Set configuration values for the radio. :param int channel: The channel (0-255) the radio is listening / broadcasting on. """ if -1 < channel < 256: self._channel = channel else: raise ValueError("Channel must be in range 0-255") def send(self, message): """ Send a message string on the channel to which the radio is broadcasting. :param str message: The message string to broadcast. """ return self.send_bytes(message.encode("utf-8")) def send_bytes(self, message): """ Send bytes on the channel to which the radio is broadcasting. :param bytes message: The bytes to broadcast. """ # Ensure length of message. if len(message) > MAX_LENGTH: raise ValueError("Message too long (max length = {})".format(MAX_LENGTH)) advertisement = _RadioAdvertisement() # Concatenate the bytes that make up the advertised message. advertisement.msg = struct.pack("<BB", self._channel, self.uid) + message self.uid = (self.uid + 1) % 255 # Advertise (block) for AD_DURATION period of time. self.ble.start_advertising(advertisement) time.sleep(AD_DURATION) self.ble.stop_advertising() def receive(self): """ Returns a message received on the channel on which the radio is listening. :return: A string representation of the received message, or else None. """ msg = self.receive_full() if msg: return msg[0].decode("utf-8").replace("\x00", "") return None def receive_full(self): """ Returns a tuple containing three values representing a message received on the channel on which the radio is listening. If no message was received then `None` is returned. The three values in the tuple represent: * the bytes received. * the RSSI (signal strength: 0 = max, -255 = min). * a microsecond timestamp: the value returned by time.monotonic() when the message was received. :return: A tuple representation of the received message, or else None. """ try: for entry in self.ble.start_scan( _RadioAdvertisement, minimum_rssi=-255, timeout=1, extended=True ): # Extract channel and unique message ID bytes. chan, uid = struct.unpack("<BB", entry.msg[:2]) if chan == self._channel: now = time.monotonic() addr = entry.address.address_bytes # Ensure this message isn't a duplicate. Message metadata # is a tuple of (now, chan, uid, addr), to (mostly) # uniquely identify a specific message in a certain time # window. expired_metadata = set() duplicate = False for msg_metadata in self.msg_pool: if msg_metadata[0] < now - AD_DURATION: # Ignore expired entries and mark for removal. expired_metadata.add(msg_metadata) elif (chan, uid, addr) == msg_metadata[1:]: # Ignore matched messages to avoid duplication. duplicate = True # Remove expired entries. self.msg_pool = self.msg_pool - expired_metadata if not duplicate: # Add new message's metadata to the msg_pool and # return it as a result. self.msg_pool.add((now, chan, uid, addr)) msg = entry.msg[2:] return (msg, entry.rssi, now) finally: self.ble.stop_scan() return None
class Room: def __init__(self, use_debug=False): self.use_debug = use_debug self.subscription_ids = {} self.subscription_messages_on_reconnect = [] self.ble = BLERadio() self.uart_server = UARTService() self.advertisement = ProvideServicesAdvertisement(self.uart_server) self.chunk_size = 20 # adafruit_ble can only write in 20 byte chunks self.recv_msg_cache = "" def debug(self, msg): if self.use_debug: print(msg) def cleanup(self): self.uart_server.write('~:\n'.encode("utf-8")) def claim(self, claim_str): # Cleanup claim = cleanup all previous claims self.uart_server.write('N:{}\n'.format(claim_str).encode("utf-8")) def when(self, query_strings, callback): x = str(random.randint(0, 9999)) subscription_id = '0'*(4-len(x)) + x # like 0568 self.subscription_ids[subscription_id] = callback # S:0568:$ $ value is $x::$ $ $x is open x = 'S:{}:{}\n'.format(subscription_id, '::'.join(query_strings)).encode("utf-8") self.subscription_messages_on_reconnect.append(x) def parse_results(self, val): self.debug("parsing results: {}".format(val)) result_vals = val[1:-1].split("},{") results = [] for result_val in result_vals: result = {} rvs = result_val.split(",") for rv in rvs: kv = rv.strip().split(":") result[kv[0].replace('"', '').replace("'", '')] = kv[1].strip() results.append(result) return results def check_read_msg_cache_and_callbacks(self): lines = self.recv_msg_cache.split("\n") self.debug("lines: {}".format(lines)) if len(lines) > 1: # trim off the last line (either '' if \n is the last char or a partial string) # because it is not part of a string that ends in a \n full_lines = lines[:-1] for line_msg in full_lines: self.debug("Proccesing new sub update: {}".format(line_msg)) # 1234[{x:"5",y:"1"},{"x":1,"y":2}] sub_id = line_msg[:4] # first four characters of message are sub id val = line_msg[4:] if sub_id not in self.subscription_ids: print("Unknown sub id {}".format(sub_id)) continue callback = self.subscription_ids[sub_id] callback(self.parse_results(val)) self.recv_msg_cache = lines[-1] def listen_and_update_subscriptions(self): # self.debug("listening~~~~~") if self.uart_server.in_waiting: read_msg = self.uart_server.read(self.uart_server.in_waiting) self.recv_msg_cache += read_msg.decode("utf-8") self.check_read_msg_cache_and_callbacks() self.debug("sub update: {}".format(self.recv_msg_cache)) def connected(self): if not self.ble.connected: # Advertise when not connected. print("BLE not connected, advertising...") self.ble.start_advertising(self.advertisement) while not self.ble.connected: pass self.ble.stop_advertising() print("BLE now connected") time.sleep(1.0) # Give BLE connector time setup before sending data for sub_msg in self.subscription_messages_on_reconnect: self.debug("Sending sub message: {}".format(sub_msg)) self.uart_server.write(sub_msg) self.listen_and_update_subscriptions() return True
#-------------------- Run once on startup print('Hello Halo') pixels.fill(RED) pixels.show() time.sleep(1.5) pixels.fill(BLACK) pixels.show() #-------------------- forever while power while True: print("Sword waiting, connect with Adafruit Bluefruit app") was_connected = False # Advertise when not connected. ble.start_advertising(advertisement, scan_response) while not ble.connected: if cycling: run_animation(animation_num) # set_palette(palette_choice, brightness) # offset = (offset + offset_increment) % OFFSET_MAX while ble.connected: if not was_connected: # blink blue first time connect was_connected = True print('BLE inititated\n') pixels.fill(BLUE) pixels.show() time.sleep(1.5) run_animation(0) # black out
ble.name = 'infrapale' # uart_server = UARTServer() uart = UARTService() advertisement = ProvideServicesAdvertisement(uart) def bytes_to_ble_addr(byte_arr): b = list(byte_arr) if len(b) == 6: hex_6 = ''.join('{:02X}:'.format(a) for a in reversed(b))[:-1] else: hex_6 = '' return hex_6 print(bytes_to_ble_addr(ble.address_bytes)) while True: ble.start_advertising(advertisement, interval=1.0) # Advertise when not connected. print('ble.start_advertising') while not ble.connected: # Wait for connection pass print('connected') while ble.connected: # Connected try: packet = Packet.from_stream(uart) except ValueError: continue if isinstance(packet, ButtonPacket): print(packet) if packet.button == '1' and packet.pressed: solenoid.value = True # Activate solenoid for 1 second sleep(1) solenoid.value = False
class Split(Module): '''Enables splitting keyboards wirelessly, or wired''' def __init__( self, split_flip=True, split_side=None, split_type=SplitType.UART, split_target_left=True, uart_interval=20, data_pin=None, data_pin2=None, target_left=True, uart_flip=True, debug_enabled=False, ): self._is_target = True self._uart_buffer = [] self.split_flip = split_flip self.split_side = split_side self.split_type = split_type self.split_target_left = split_target_left self.split_offset = None self.data_pin = data_pin self.data_pin2 = data_pin2 self.target_left = target_left self.uart_flip = uart_flip self._is_target = True self._uart = None self._uart_interval = uart_interval self._debug_enabled = debug_enabled if self.split_type == SplitType.BLE: try: from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ( ProvideServicesAdvertisement, ) from adafruit_ble.services.nordic import UARTService self.ProvideServicesAdvertisement = ProvideServicesAdvertisement self.UARTService = UARTService except ImportError: print('BLE Import error') return # BLE isn't supported on this platform self._ble = BLERadio() self._ble_last_scan = ticks_ms() - 5000 self._connection_count = 0 self._uart_connection = None self._advertisment = None self._advertising = False self._psave_enable = False def during_bootup(self, keyboard): # Set up name for target side detection and BLE advertisment name = str(getmount('/').label) if self.split_type == SplitType.BLE: self._ble.name = name else: # Try to guess data pins if not supplied if not self.data_pin: self.data_pin = keyboard.data_pin # Detect split side from name if self.split_side is None: if name.endswith('L'): # If name ends in 'L' assume left and strip from name self._is_target = bool(self.split_target_left) self.split_side = SplitSide.LEFT elif name.endswith('R'): # If name ends in 'R' assume right and strip from name self._is_target = not bool(self.split_target_left) self.split_side = SplitSide.RIGHT # if split side was given, find master from split_side. elif self.split_side == SplitSide.LEFT: self._is_target = bool(self.split_target_left) elif self.split_side == SplitSide.RIGHT: self._is_target = not bool(self.split_target_left) # Flips the col pins if PCB is the same but flipped on right if self.split_flip and self.split_side == SplitSide.RIGHT: keyboard.col_pins = list(reversed(keyboard.col_pins)) self.split_offset = len(keyboard.col_pins) if self.split_type == SplitType.UART and self.data_pin is not None: if self._is_target: self._uart = busio.UART( tx=self.data_pin2, rx=self.data_pin, timeout=self._uart_interval ) else: self._uart = busio.UART( tx=self.data_pin, rx=self.data_pin2, timeout=self._uart_interval ) # Attempt to sanely guess a coord_mapping if one is not provided. if not keyboard.coord_mapping: keyboard.coord_mapping = [] rows_to_calc = len(keyboard.row_pins) * 2 cols_to_calc = len(keyboard.col_pins) * 2 for ridx in range(rows_to_calc): for cidx in range(cols_to_calc): keyboard.coord_mapping.append(intify_coordinate(ridx, cidx)) def before_matrix_scan(self, keyboard): if self.split_type == SplitType.BLE: self._check_all_connections() self._receive_ble(keyboard) elif self.split_type == SplitType.UART: if self._is_target or self.data_pin2: self._receive_uart(keyboard) elif self.split_type == SplitType.ONEWIRE: pass # Protocol needs written return def after_matrix_scan(self, keyboard): if keyboard.matrix_update: if self.split_type == SplitType.UART and self._is_target: pass # explicit pass just for dev sanity... elif self.split_type == SplitType.UART and ( self.data_pin2 or not self._is_target ): self._send_uart(keyboard.matrix_update) elif self.split_type == SplitType.BLE: self._send_ble(keyboard.matrix_update) elif self.split_type == SplitType.ONEWIRE: pass # Protocol needs written else: print('Unexpected case in after_matrix_scan') return def before_hid_send(self, keyboard): if not self._is_target: keyboard.hid_pending = False return def after_hid_send(self, keyboard): return def on_powersave_enable(self, keyboard): if self.split_type == SplitType.BLE: if self._uart_connection and not self._psave_enable: self._uart_connection.connection_interval = self._uart_interval self._psave_enable = True def on_powersave_disable(self, keyboard): if self.split_type == SplitType.BLE: if self._uart_connection and self._psave_enable: self._uart_connection.connection_interval = 11.25 self._psave_enable = False def _check_all_connections(self): '''Validates the correct number of BLE connections''' self._connection_count = len(self._ble.connections) if self._is_target and self._connection_count < 2: self._target_advertise() elif not self._is_target and self._connection_count < 1: self._initiator_scan() def _initiator_scan(self): '''Scans for target device''' self._uart = None self._uart_connection = None # See if any existing connections are providing UARTService. self._connection_count = len(self._ble.connections) if self._connection_count > 0 and not self._uart: for connection in self._ble.connections: if self.UARTService in connection: self._uart_connection = connection self._uart_connection.connection_interval = 11.25 self._uart = self._uart_connection[self.UARTService] break if not self._uart: if self._debug_enabled: print('Scanning') self._ble.stop_scan() for adv in self._ble.start_scan( self.ProvideServicesAdvertisement, timeout=20 ): if self._debug_enabled: print('Scanning') if self.UARTService in adv.services and adv.rssi > -70: self._uart_connection = self._ble.connect(adv) self._uart_connection.connection_interval = 11.25 self._uart = self._uart_connection[self.UARTService] self._ble.stop_scan() if self._debug_enabled: print('Scan complete') break self._ble.stop_scan() def _target_advertise(self): '''Advertises the target for the initiator to find''' self._ble.stop_advertising() if self._debug_enabled: print('Advertising') # Uart must not change on this connection if reconnecting if not self._uart: self._uart = self.UARTService() advertisement = self.ProvideServicesAdvertisement(self._uart) self._ble.start_advertising(advertisement) self.ble_time_reset() while not self.ble_rescan_timer(): self._connection_count = len(self._ble.connections) if self._connection_count > 1: self.ble_time_reset() if self._debug_enabled: print('Advertising complete') break self._ble.stop_advertising() def ble_rescan_timer(self): '''If true, the rescan timer is up''' return bool(ticks_diff(ticks_ms(), self._ble_last_scan) > 5000) def ble_time_reset(self): '''Resets the rescan timer''' self._ble_last_scan = ticks_ms() def _send_ble(self, update): if self._uart: try: if not self._is_target: update[1] += self.split_offset self._uart.write(update) except OSError: try: self._uart.disconnect() except: # noqa: E722 if self._debug_enabled: print('UART disconnect failed') if self._debug_enabled: print('Connection error') self._uart_connection = None self._uart = None def _receive_ble(self, keyboard): if self._uart is not None and self._uart.in_waiting > 0 or self._uart_buffer: while self._uart.in_waiting >= 3: self._uart_buffer.append(self._uart.read(3)) if self._uart_buffer: keyboard.secondary_matrix_update = bytearray(self._uart_buffer.pop(0)) return def _send_uart(self, update): # Change offsets depending on where the data is going to match the correct # matrix location of the receiever if self._is_target: if self.split_target_left: update[1] += self.split_offset else: update[1] -= self.split_offset else: if self.split_target_left: update[1] += self.split_offset else: update[1] -= self.split_offset if self._uart is not None: self._uart.write(update) def _receive_uart(self, keyboard): if self._uart is not None and self._uart.in_waiting > 0 or self._uart_buffer: if self._uart.in_waiting >= 60: # This is a dirty hack to prevent crashes in unrealistic cases import microcontroller microcontroller.reset() while self._uart.in_waiting >= 3: self._uart_buffer.append(self._uart.read(3)) if self._uart_buffer: keyboard.secondary_matrix_update = bytearray(self._uart_buffer.pop(0)) return
class Aquarium: def __init__(self): # Set up watchdog timer self.watchdog = microcontroller.watchdog self.watchdog.deinit() self.watchdog.timeout = WATCHDOG_TIMEOUT self.watchdog.mode = WATCHDOG_MODE # Set up heartbeat output (i.e red LED) self._heartbeat = digitalio.DigitalInOut(HEARTBEAT_PIN) self._heartbeat.direction = digitalio.Direction.OUTPUT self._heartbeat_duration = HEARTBEAT_DURATION # Set up I2C bus i2c = busio.I2C(board.SCL, board.SDA) # Set up SPI bus spi = busio.SPI(board.SCK, board.MOSI, board.MISO) # Set up real time clock as source for time.time() or time.localtime() calls. print("Initialising real time clock.\n\n\n\n") clock = PCF8523(i2c) rtc.set_time_source(clock) print("Initialising display.\n\n\n\n") self.display = Display(self, DISPLAY_TIMEOUT, i2c, spi) print("Initialising lights.\n\n\n\n") self.lights = Lights(LIGHTS_ON_TIME, LIGHTS_OFF_TIME, LIGHTS_ENABLE_PIN, LIGHTS_DISABLE_PIN) print("Initialising feeder.\n\n\n\n") self.feeder = Feeder(FEEDING_TIMES, PORTIONS_PER_MEAL, FEEDER_MOTOR, FEEDER_STEPS_PER_ROTATION, FEEDER_STEP_DELAY, FEEDER_STEP_STYLE, i2c) print("Initialising temperature sensors.\n\n\n\n") ow_bus = OneWireBus(OW_PIN) self.water_sensor = TemperatureSensor(ow_bus, WATER_SN, WATER_OFFSET) self.air_sensor = TemperatureSensor(ow_bus, AIR_SN, AIR_OFFSET) # Set up SD card print("Setting up logging.\n\n\n\n") cs = digitalio.DigitalInOut(SD_CS) sdcard = SDCard(spi, cs) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") self._log_data = LOG_DATA self._log_interval = time_tuple_to_secs(LOG_INTERVAL) self._last_log = None print("Initialising Bluetooth.\n\n\n\n") self._ble = BLERadio() self._ble._adapter.name = BLE_NAME self._ble_uart = UARTService() self._ble_ad = ProvideServicesAdvertisement(self._ble_uart) def heartbeat(self): self.watchdog.feed() self._heartbeat.value = True time.sleep(self._heartbeat_duration) self._heartbeat.value = False def update_temps(self): self.water_temp = self.water_sensor.temperature self.air_temp = self.air_sensor.temperature def update_log(self): if not self._log_data: return if self._last_log: last_log_secs = time_struct_to_secs(self._last_log) current_secs = time_struct_to_secs(self._now) if current_secs - last_log_secs < self._log_interval: return print("Updating log:") datestamp, timestamp, log_line = self._get_status_strings() filename = datestamp + ".log" print(filename) print(log_line) with open("/sd/scales_logs/" + filename, mode="at", buffering=1) as logfile: logfile.write(log_line + "\n") self._last_log = self._now self._last_log = self._now print("Done.\n") def blelele(self): if not self._ble.connected: # Not connected, so make sure we're advertising for connections. try: self._ble.start_advertising(self._ble_ad) except BluetoothError: # Already advertising. Probably. pass return if self._ble_uart.in_waiting: # There's a command waiting. ble_command = self._ble_uart.readline() if ble_command: # First echo command, then respond. self._ble_uart.write(ble_command + b'\n') self._ble_respond(ble_command) def run_once(self): self.heartbeat() self._now = time.localtime() self.lights.update() self.feeder.update() self.update_temps() self.display.update() self.update_log() self.blelele() time.sleep(1) def run(self): while True: self.run_once() def _ble_respond(self, ble_command): if ble_command == b"v?": response = bytes(f"{BLE_NAME} v{__version__}\n", 'ascii') elif ble_command == b"s?": _, _, response = self._get_status_strings() response = bytes(response, 'ascii') elif ble_command == b"f?": response = f"{self.feeder.feeding_times}, {self.feeder.portions_per_meal}" response = bytes(response, 'ascii') elif ble_command == b"ff": self.feeder.feed() response = bytes("Fed 1 portion.", 'ascii') elif len(ble_command) > 2 and ble_command[:2] == b"fp": portions = int(str(ble_command[2:], 'ascii')) self.feeder.portions_per_meal = portions response = bytes(f"Set portions per meal to {portions}.", 'ascii') else: command = str(ble_command, 'ascii') response = bytes("ERROR: Invalid command '{}'\n".format(command), 'ascii') self._ble_uart.write(response) def _get_status_strings(self): datestamp = "{:04d}-{:02d}-{:02d}".format(self._now.tm_year, self._now.tm_mon, self._now.tm_mday) timestamp = datestamp + "T{:02d}:{:02d}:{:02d}".format(self._now.tm_hour, self._now.tm_min, self._now.tm_sec) status = "{}, {:d}, {:7.4f}, {:7.4f}".format(timestamp, self.lights.is_enabled, self.water_temp, self.air_temp) return datestamp, timestamp, status
class BLEHID(AbstractHID): BLE_APPEARANCE_HID_KEYBOARD = const(961) # Hardcoded in CPy MAX_CONNECTIONS = const(2) def __init__(self, ble_name=str(getmount('/').label), **kwargs): self.ble_name = ble_name super().__init__() def post_init(self): self.ble = BLERadio() self.ble.name = self.ble_name self.hid = HIDService() self.hid.protocol_mode = 0 # Boot protocol # Security-wise this is not right. While you're away someone turns # on your keyboard and they can pair with it nice and clean and then # listen to keystrokes. # On the other hand we don't have LESC so it's like shouting your # keystrokes in the air if not self.ble.connected or not self.hid.devices: self.start_advertising() @property def devices(self): '''Search through the provided list of devices to find the ones with the send_report attribute.''' if not self.ble.connected: return {} result = {} for device in self.hid.devices: if not hasattr(device, 'send_report'): continue us = device.usage up = device.usage_page if up == HIDUsagePage.CONSUMER and us == HIDUsage.CONSUMER: result[HIDReportTypes.CONSUMER] = device continue if up == HIDUsagePage.KEYBOARD and us == HIDUsage.KEYBOARD: result[HIDReportTypes.KEYBOARD] = device continue if up == HIDUsagePage.MOUSE and us == HIDUsage.MOUSE: result[HIDReportTypes.MOUSE] = device continue if up == HIDUsagePage.SYSCONTROL and us == HIDUsage.SYSCONTROL: result[HIDReportTypes.SYSCONTROL] = device continue return result def hid_send(self, evt): if not self.ble.connected: return # int, can be looked up in HIDReportTypes reporting_device_const = evt[0] device = self.devices[reporting_device_const] report_size = len(device._characteristic.value) while len(evt) < report_size + 1: evt.append(0) return device.send_report(evt[1:report_size + 1]) def clear_bonds(self): import _bleio _bleio.adapter.erase_bonding() def start_advertising(self): if not self.ble.advertising: advertisement = ProvideServicesAdvertisement(self.hid) advertisement.appearance = self.BLE_APPEARANCE_HID_KEYBOARD self.ble.start_advertising(advertisement) def stop_advertising(self): self.ble.stop_advertising()
ble = BLERadio() # The Web Bluetooth dashboard identifies known boards by their # advertised name, not by advertising manufacturer data. ble.name = "CLUE" # The Bluefruit Playground app looks in the manufacturer data # in the advertisement. That data uses the USB PID as a unique ID. # Adafruit CLUE USB PID: # Arduino: 0x8071, CircuitPython: 0x8072, app supports either adv = AdafruitServerAdvertisement() adv.pid = 0x8072 while True: # Advertise when not connected. ble.start_advertising(adv) while not ble.connected: pass ble.stop_advertising() while ble.connected: now_msecs = time.monotonic_ns() // 1000000 # pylint: disable=no-member if now_msecs - accel_last_update >= accel_svc.measurement_period: accel_svc.acceleration = clue.acceleration accel_last_update = now_msecs if now_msecs - baro_last_update >= baro_svc.measurement_period: baro_svc.pressure = clue.pressure baro_last_update = now_msecs
def main(): DEBUG = True BOARD = True #flag indicating whether attached to a board. try: import board except NotImplementedError: # no board attached so mock sensors, services etc import mock as mk print('No valid board. Using mock sensors and services') BOARD = False # sensors import adafruit_lsm6ds.lsm6ds33 # motion import adafruit_lis3mdl # magnetometer import adafruit_apds9960.apds9960 # EMR import time dummy_sensor = DummySensor() if BOARD: # valid board present use real sensors import analogio battery = analogio.AnalogIn(board.VOLTAGE_MONITOR) motion = adafruit_lsm6ds.lsm6ds33.LSM6DS33(board.I2C()) magnet = adafruit_lis3mdl.LIS3MDL(board.I2C()) emr = adafruit_apds9960.apds9960.APDS9960(board.I2C()) # emr.enable_proximity = True emr.enable_color = True # Create and initialize the available services. ble = BLERadio() battery_svc = BatteryService() motion_svc = MotionService() magnet_svc = MagnetService() emr_svc = EMRService() dummy_svc = DummyService() adv = PhysBrykServerAdvertisement() else: #use mock sensors and services # Accelerometer and gyro motion = mk.Sensor() magnet = mk.Sensor() emr = mk.Sensor() battery = mk.Sensor() ble = mk.Service() battery_svc = mk.Service() motion_svc = mk.Service() magnet_svc = mk.Service() emr_svc = mk.Service() dummy_svc = mk.Service() adv = mk.Service() ble.name = "PhysBryk_Alpha" last_update = 0 while True: # Advertise when not connected. ble.start_advertising(adv) if DEBUG: print('Connecting...') while not ble.connected: pass ble.stop_advertising() if DEBUG: print('Connected!') while ble.connected: now_msecs = time.monotonic_ns() // 1000000 # pylint: disable=no-member if now_msecs - last_update >= MEASUREMENT_PERIOD: battery_svc.voltage = battery_svc.get_voltage(battery) motion_svc.acceleration = motion.acceleration # m/s/s motion_svc.gyro = motion.gyro # rad/s magnet_svc.magnetic = magnet.magnetic # microT emr_svc.intensity = emr_svc.get_lux(emr.color_data) emr_svc.spectrum = emr.color_data emr_svc.proximity = emr.proximity dummy_svc.value = 42 dummy_sensor.update() last_update = now_msecs if DEBUG: print(f'motion acceleration: {motion_svc.acceleration}') print(f'motion gyro: {motion_svc.gyro}') print(f'magnet magnet: {magnet_svc.magnetic}') print(f'emr intensity: {emr_svc.intensity}') print(f'emr spectrum: {emr_svc.spectrum}') print(f'emr proximity: {emr_svc.proximity}') print(f'battery: {battery_svc.voltage}') print(f'dummy: {dummy_svc.value}') if not BOARD: for s in mk.sensors: s.update()
import board import neopixel from adafruit_bluefruit_connect.packet import Packet from adafruit_bluefruit_connect.color_packet import ColorPacket from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService ble = BLERadio() uart_service = UARTService() advertisement = ProvideServicesAdvertisement(uart_service) pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=0.1) while True: # Advertise when not connected. ble.start_advertising(advertisement) while not ble.connected: pass ble.stop_advertising() while ble.connected: if uart_service.in_waiting: packet = Packet.from_stream(uart_service) if isinstance(packet, ColorPacket): print(packet.color) pixels.fill(packet.color)
class MD_BLE: """[summary] """ def __init__(self, pin=board.D13, sample_num=20, use_ble=False): # Might not want to use BLE, perhaps for testing purposes self.use_ble = use_ble self.sample_num = sample_num try: self.pulses = pulseio.PulseIn(pin, self.sample_num) except ValueError as e: raise ValueError(e) self.pulses.pause() # The samples list holds the readings that will be used in calculating the distance. Pulse readings that # are determined to be way off (say 655355....) will not be included so len(samples) is <= sample_num. self.samples = [] if self.use_ble: # Set up BLE based on Adafruit's CircuitPython libraries. self.ble = BLERadio() uart_service = UARTService() advertisement = ProvideServicesAdvertisement(uart_service) # Advertise when not connected. self.ble.start_advertising(advertisement) def _get_samples(self): """ Internal. Uses the pulses instance created in __init__ to gather 'legitimate' pulse readings into the samples list. """ print('--> _get_samples') # Empty the list containing previous samples. self.samples = [] # Get pulse readings. (i.e.: Using pulse width method to detect distance.) self.pulses.clear() self.pulses.resume() # Wait until there are sample_num pulses. while len(self.pulses) < self.sample_num: pass self.pulses.pause() # Add 'legitimate' readings to the sample list. for i in range(self.sample_num): # According to the sensor's datarange, valid values are 300 to 5000. # Note: Values of 300 most likely mean the sensor is too close to the # object since objects must be at least 300mm from the sensor. print('{} pulse value: {}'.format(i, self.pulses[i])) # Using 301 because when testing we found occassionally readings # that should have been 300 would be 301. if self.pulses[i] > 301 and self.pulses[i] <= 5000: self.samples.append(self.pulses[i]) print('valid: {}'.format(self.pulses[i])) if len(self.samples) == 0: raise Exception( 'None of the readings were in the 300mm to 5000mm range.') @property def mode(self): """ Copied from Adafruit Learning Guide (see above for link). Then modified. find the mode (most common value reported) will return median (center of sorted list) should mode not be found """ self.samples = sorted(self.samples) n = len(self.samples) print('number of samples is {}'.format(n)) max_count = 0 mode = 0 bimodal = 0 counter = 0 index = 0 while index < (n - 1): prev_count = counter counter = 0 while (self.samples[index]) == (self.samples[index + 1]): counter += 1 index += 1 if (counter > prev_count) and (counter > max_count): mode = self.samples[index] max_count = counter bimodal = 0 if counter == 0: index += 1 # If the dataset has 2 or more modes. if counter == max_count: bimodal = 1 # Return the median if there is no mode. if (mode == 0) or (bimodal == 1): print('using the median. mode: {} bimodal: {} '.format( mode, bimodal)) mode = self.samples[int(n / 2)] return mode @property def distance(self): """ To be used by the caller. example: from MD_BLE import MD_BLE md = MD_BLE() distance_from_object = md.distance Returns: float: The distance from the object. """ if self.use_ble: print("WAITING FOR BLE CONNECTION...") while not self.ble.connected: pass self._get_samples() return self.mode