コード例 #1
0
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()
コード例 #2
0
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
コード例 #3
0
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
コード例 #5
0
    """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)
コード例 #6
0
    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
コード例 #7
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
コード例 #8
0
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
コード例 #9
0
#-------------------- 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
コード例 #10
0
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
コード例 #11
0
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
コード例 #12
0
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
コード例 #13
0
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()
コード例 #14
0
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
コード例 #15
0
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()
コード例 #16
0
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)
コード例 #17
0
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