# See if any existing connections are providing UARTService. if ble.connected: for connection in ble.connections: if UARTService in connection: uart_connection = connection break while True: if not uart_connection: for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=5): if UARTService in adv.services: uart_connection = ble.connect(adv) break # Stop scanning whether or not we are connected. ble.stop_scan() while uart_connection and uart_connection.connected: r = scale(a4.value) g = scale(a5.value) b = scale(a6.value) color = (r, g, b) print(color) color_packet = ColorPacket(color) try: uart_connection[UARTService].write(color_packet.to_bytes()) except OSError: pass time.sleep(0.3)
break while True: blue_led.value = False # BLE connection if not uart_connection or not uart_connection.connected: # If not connected... print("Scanning...") for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=5): # Scan... if UARTService in adv.services: # If UARTService found... print("Found a UARTService advertisement.") blue_led.value = True # LED turns on when connected uart_connection = ble.connect( adv) # Create a UART connection... break ble.stop_scan() # And stop scanning. # while connected.. while uart_connection and uart_connection.connected: # iterate through buttons and colors for switch_pin in switch_array: i = switch_array.index(switch_pin) switches_pressed_state = switches_pressed[i] colors = color[i] # if the button is released # worked best if placed before the button press portion if switch_pin.value and switches_pressed_state: print("button off") # send button packet to stop tone & color (happens on CPB) if not send_packet( uart_connection, ButtonPacket(ButtonPacket.RIGHT, pressed=True)):
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 PestCamera: def __init__(self): self.connection = sqlite3.connect('pest_alert.db') self.cursor = self.connection.cursor() # Define radio for finding deivice self.ble = BLERadio() # set global variable uart_connection, will be replaced with the service once connected self.uart_connection = None # creating the database, this needs to happen before the while loop command1 = """CREATE TABLE IF NOT EXISTS info(picture_path TEXT, humidity FLOAT, temperature FLOAT)""" self.cursor.execute(command1) # on launch, connect to the device and ask for input noCon = True while noCon: # imageName = 'IMAGE00.JPG' # If there is no uart connection yet, connect to an available device with a UART service, # TODO: Make it connect specifically to our project, and not any UART Device, not important if not self.uart_connection: print("Trying to connect...") for adv in self.ble.start_scan(ProvideServicesAdvertisement): if UARTService in adv.services: self.uart_connection = self.ble.connect(adv) print("Connected") break self.ble.stop_scan() # if there is a Uart device connected, ask the user for an input, then call the function corresponding to the input # TODO: Make this refresh automaticaly and with a button press, needed for CDR if self.uart_connection and self.uart_connection.connected: self.uart_service = self.uart_connection[UARTService] noCon = False # add image, temp, and humidity to database once all are recived # commented out for testing, will need to be working for actual project def addToDB(self, textString): vals = textString.split(", ") print(vals) self.cursor.execute("INSERT INTO info VALUES (?, ?, ?)", (vals[0], vals[1], vals[2])) self.connection.commit() self.cursor.execute("SELECT * FROM info") results = self.cursor.fetchall() # render_template('template.html', value=results) print(results) # Testing function to get text from the device, can be removed once we know everything works def getText(self): self.uart_service.write("w".encode("utf-8")) textString = self.uart_service.readline().decode("utf-8") print(textString) self.addToDB(textString) def runAll(self): BUFFSIZE = 4 # bytes received, used to tell if we are getting all of the data byteNum = 0 # send the user input that was passed thru as s, this tells the arduino what we want to do # happens in each run function to ensure command isnt sent before we are ready to get the data self.uart_service.write("r".encode("utf-8")) # wait until the arduino sends an c back, to tell the rpi we are about to get data while (True): val = self.uart_service.read(nbytes=1) if (val == None): continue if (val.decode("utf-8") == 'c'): break # debug statement to know we are getting the name first print("readyForImgName") # read the image name, used to be readLine, but for some reason that broke # When possible use read(nbytes) instead of readline to fix errors if size of data is known # arduino will send a line with the image name, temperature, and humidity seperated by a comma # TODO: textString is sending an unknown number of bytes, should be 19, but for somereason its not imageName = self.uart_service.read(nbytes=12).decode("utf-8") fileName = Path("static/" + imageName) fileName.touch(exist_ok=True) # print the name to make sure its the right one print(imageName) # textString = uart_service.readline().decode("utf-8") # a file with the name of the image that we will save data to f = open(fileName, 'wb+') # get the first 4 bytes, then delay, print statemtns are for debugging val = self.uart_service.read(nbytes=BUFFSIZE) print("delay") time.sleep(.01) print("dd") # Get the data from the uart service, then write them to teh file, 4 bytes at a time for now, seems to be reliable while (val != None): # print(val) f.write(val) val = self.uart_service.read(nbytes=BUFFSIZE) byteNum = byteNum + BUFFSIZE # we need this delay to allow the arduino to send data, if we dont we might read the same data in # time.sleep(.005) # print the number of bytes recived, # close the file f.close()
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 PolymathTraining(): """ Core class used for collecting data from the Polymath system.""" def __init__(self, filename="out.csv"): self.filename = filename self.ble = BLERadio() self.uart_connection = None self.config = None self.sample_rate = 0 # In Hz self.samples_per_packet = 0 self.columns = [] self.sequence = 0 self.data = [] pass def connect(self): result = False if not self.uart_connection: for adv in self.ble.start_scan(ProvideServicesAdvertisement): if UARTServiceCustom in adv.services: print("Found a device to connect to: {0}".format(adv)) self.uart_connection = self.ble.connect(adv, timeout=30) print("Connected Successfully") result = True break self.ble.stop_scan() return result def connected(self): return (self.uart_connection and self.uart_connection.connected) def disconnect(self): self.uart_connection.disconnect() def run(self): if self.connected(): uart_service = self.uart_connection[UARTServiceCustom] if (uart_service.in_waiting > 0): line = uart_service.readline().decode() print(line) if ((line is not None) and (len(line) > 0)): if not self.config: # First try getting the configuration for the system. try: read_config = json.loads(line) print(read_config) print("Parsed JSON successfully") # for key in read_config: # print("Key: {0}, Value {1}".format(key, read_config[key])) if ( ("sample_rate" in read_config) and ("samples_per_packet" in read_config) and ("column_location" in read_config)): self.sample_rate = read_config["sample_rate"] self.samples_per_packet = read_config["samples_per_packet"] self.column_locations = [] for i in range(0, self.samples_per_packet): self.column_locations.append("") for key in read_config["column_location"]: self.column_locations[read_config["column_location"][key]] = key self.config = read_config print("Read a valid config. Columns are {0}".format(self.column_locations)) # Initiate a connection uart_service.write("connect".encode()) # Set up the output file with open(self.filename, "w") as f: f.write("sequence,{0}\n".format(','.join(self.column_locations))) else: print("Not all of the expected keys were present.") except ValueError as e: print("Failed to decode JSON due to: {0}".format(e)) pass else: # Collect data. data = line.strip().split(',') packet = [] add_packet = True #Assume if (len(data) == self.samples_per_packet): for sample in data: try: packet.append(int(sample)) except ValueError: add_packet = False break else: add_packet = False if (add_packet): self.data.append(data) with open(self.filename, "a") as f: f.write("{0},{1}\n".format(self.sequence, ','.join(data))) print("Appended {0}".format(data)) self.sequence += 1 pass