class NightLight(nonblocking_timer): def __init__(self): super(NightLight, self).__init__() pixels = neopixel.NeoPixel(NEOPIXEL, 10, auto_write=1, brightness=1.0) pixels.fill((0, 0, 0)) pixels.show() self._on = False self._animator = PixelAnimator(pixels) self._irSensor = AnalogIn(IR_PROXIMITY) self._irInput = DigitalInOut(REMOTEIN) self._irInput.direction = Direction.INPUT self._irOutput = DigitalInOut(REMOTEOUT) self._irOutput.direction = Direction.OUTPUT self._microphone = DigitalInOut(MICROPHONE_DATA) self._irOutput.direction = Direction.INPUT def stop(self): print('stop') self._irSensor.deinit() self._irInput.deinit() def next(self): # print("ir: %d mic: %d" % (self._irSensor.value, self._irOutput.value)) self._animator.next()
def blink_color(led_ref): """ Function to control the LED and perform a simple blink Total delay = 1 second This encapsulates activating the pin and de-activating it to avoid color mashing :param led_ref: """ led = DigitalInOut(led_ref) led.direction = Direction.OUTPUT led.value = True time.sleep(0.1) led.value = False time.sleep(0.1) led.deinit()
class DigitalIn(object): """Basic digital input.""" def __init__(self, pin, pull=None): self._pin = DigitalInOut(pin) if pull == PULL_UP: self._pin.switch_to_input(pull=Pull.UP) elif pull == PULL_DOWN: self._pin.switch_to_input(pull=Pull.DOWN) else: self._pin.switch_to_input() def deinit(self): self._pin.deinit() @property def value(self): return self._pin.value
class Flasher: def __init__(self, config=argon_config): self.config = config def __enter__(self): config = self.config self.uart = busio.UART(tx=config['tx'], rx=config['rx'], baudrate=115200, timeout=1) self.resetpin = DigitalInOut(config['reset']) self.gpio0pin = DigitalInOut(config['io0']) esptool = miniesptool(self.uart, self.gpio0pin, self.resetpin, flashsize=4 * 1024 * 1024) esptool.debug = False esptool.debug_check_command = False esptool.sync() if esptool.chip_name != "ESP32": raise RuntimeError("Expected ESP32, got {}".format( esptool.chip_name)) esptool.baudrate = baudrate print("MAC ADDR:", ":".join("{:02x}".format(x) for x in esptool.mac_addr)) self.esptool = esptool return self def __exit__(self): self.uart.deinit() self.resetpin.deinit() self.gpio0pin.deinit() esptool.reset() time.sleep(0.5) def flash_file(self, firmware, md5sum, dry_run=False): self.esptool.flash_file(firmware, 0x1000, md5=md5, dry_run=dry_run) def erase(self, dry_run=False): meg = 1024 * 1024 for sector in range(4): esptool.erase(size=meg, offset=sector * meg) print()
class PiperJoystickZ: def __init__(self, joy_z_pin=board.D2): self.joy_z_pin = DigitalInOut(joy_z_pin) self.joy_z_pin.direction = Direction.INPUT self.joy_z_pin.pull = Pull.UP self.joy_z = Debouncer(self.joy_z_pin) def deinit(self): self.joy_z_pin.deinit() def update(self): self.joy_z.update() def zPressed(self): return not self.joy_z.value def zPressedEvent(self): return self.joy_z.fell def zReleasedEvent(self): return self.joy_z.rose
class DigitalOut(object): """Basic digital output.""" def __init__(self, pin, value=False): self._pin = DigitalInOut(pin) self._pin.switch_to_output(value=value) def deinit(self): self._pin.deinit() @property def value(self): return self._pin.value @value.setter def value(self, value): self._pin.value = value def on(self): self._pin.value = 1 def off(self): self._pin.value = 0
class HCSR04: """Control a HC-SR04 ultrasonic range sensor. Example use: :: with HCSR04(trig, echo) as sonar: try: while True: print(sonar.dist_cm()) sleep(2) except KeyboardInterrupt: pass """ def __init__(self, trig_pin, echo_pin, timeout_sec=.1): """ :param trig_pin: The pin on the microcontroller that's connected to the ``Trig`` pin on the HC-SR04. :type trig_pin: str or microcontroller.Pin :param echo_pin: The pin on the microcontroller that's connected to the ``Echo`` pin on the HC-SR04. :type echo_pin: str or microcontroller.Pin :param float timeout_sec: Max seconds to wait for a response from the sensor before assuming it isn't going to answer. Should *not* be set to less than 0.05 seconds! """ if isinstance(trig_pin, str): trig_pin = getattr(board, trig_pin) if isinstance(echo_pin, str): echo_pin = getattr(board, echo_pin) self.dist_cm = self._dist_two_wire self.timeout_sec = timeout_sec self.trig = DigitalInOut(trig_pin) self.trig.switch_to_output(value=False, drive_mode=DriveMode.PUSH_PULL) self.echo = PulseIn(echo_pin) self.echo.pause() self.echo.clear() def __enter__(self): """Allows for use in context managers.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Automatically de-initialize after a context manager.""" self.deinit() def deinit(self): """De-initialize the trigger and echo pins.""" self.trig.deinit() self.echo.deinit() def dist_cm(self): """Return the distance measured by the sensor in cm. This is the function that will be called most often in user code. The distance is calculated by timing a pulse from the sensor, indicating how long between when the sensor sent out an ultrasonic signal and when it bounced back and was received again. If no signal is received, the return value will be ``-1``. This means either the sensor was moving too fast to be pointing in the right direction to pick up the ultrasonic signal when it bounced back (less likely), or the object off of which the signal bounced is too far away for the sensor to handle. In my experience, the sensor can detect objects over 460 cm away. :return: Distance in centimeters. :rtype: float """ # This method only exists to make it easier to document. See either # _dist_one_wire or _dist_two_wire for the actual implementation. One # of those two methods will be assigned to be used in place of this # method on instantiation. pass def _dist_two_wire(self): self.echo.clear() # Discard any previous pulse values self.trig.value = 1 # Set trig high time.sleep(0.00001) # 10 micro seconds 10/1000/1000 self.trig.value = 0 # Set trig low timeout = time.monotonic() self.echo.resume() while len(self.echo) == 0: # Wait for a pulse if (time.monotonic() - timeout) > self.timeout_sec: self.echo.pause() return -1 self.echo.pause() if self.echo[0] == 65535: return -1 return (self.echo[0] / 2) / (291 / 10)
class I2C(_BitBangIO): """Software-based implementation of the I2C protocol over GPIO pins.""" def __init__(self, scl, sda, *, frequency=400000, timeout=1): """Initialize bitbang (or software) based I2C. Must provide the I2C clock, and data pin numbers. """ super().__init__() # Set pins as outputs/inputs. self._scl = DigitalInOut(scl) self._scl.switch_to_output() self._scl.value = 1 # SDA flips between being input and output self._sda = DigitalInOut(sda) self._sda.switch_to_output() self._sda.value = 1 self._delay = 1 / frequency / 2 self._timeout = timeout def deinit(self): """Free any hardware used by the object.""" self._sda.deinit() self._scl.deinit() def scan(self): """Perform an I2C Device Scan""" found = [] if self._check_lock(): for address in range(0, 0x80): if self._probe(address): found.append(address) return found def writeto(self, address, buffer, *, start=0, end=None): """Write data from the buffer to an address""" if end is None: end = len(buffer) if self._check_lock(): self._write(address, buffer[start:end], True) def readfrom_into(self, address, buffer, *, start=0, end=None): """Read data from an address and into the buffer""" if end is None: end = len(buffer) if self._check_lock(): readin = self._read(address, end - start) for i in range(end - start): buffer[i + start] = readin[i] def writeto_then_readfrom(self, address, buffer_out, buffer_in, *, out_start=0, out_end=None, in_start=0, in_end=None): """Write data from buffer_out to an address and then read data from an address and into buffer_in """ if out_end is None: out_end = len(buffer_out) if in_end is None: in_end = len(buffer_in) if self._check_lock(): self.writeto(address, buffer_out, start=out_start, end=out_end) self.readfrom_into(address, buffer_in, start=in_start, end=in_end) def _scl_low(self): self._scl.value = 0 def _sda_low(self): self._sda.value = 0 def _scl_release(self): """Release and let the pullups lift""" # Use self._timeout to add clock stretching self._scl.value = 1 def _sda_release(self): """Release and let the pullups lift""" # Use self._timeout to add clock stretching self._sda.value = 1 def _start(self): self._sda_release() self._scl_release() sleep(self._delay) self._sda_low() sleep(self._delay) def _stop(self): self._scl_low() sleep(self._delay) self._sda_low() sleep(self._delay) self._scl_release() sleep(self._delay) self._sda_release() sleep(self._delay) def _repeated_start(self): self._scl_low() sleep(self._delay) self._sda_release() sleep(self._delay) self._scl_release() sleep(self._delay) self._sda_low() sleep(self._delay) def _write_byte(self, byte): for bit_position in range(8): self._scl_low() sleep(self._delay) if byte & (0x80 >> bit_position): self._sda_release() else: self._sda_low() sleep(self._delay) self._scl_release() sleep(self._delay) self._scl_low() sleep(self._delay * 2) self._scl_release() sleep(self._delay) self._sda.switch_to_input() ack = self._sda.value self._sda.switch_to_output() sleep(self._delay) self._scl_low() return not ack def _read_byte(self, ack=False): self._scl_low() sleep(self._delay) data = 0 self._sda.switch_to_input() for _ in range(8): self._scl_release() sleep(self._delay) data = (data << 1) | int(self._sda.value) sleep(self._delay) self._scl_low() sleep(self._delay) self._sda.switch_to_output() if ack: self._sda_low() else: self._sda_release() sleep(self._delay) self._scl_release() sleep(self._delay) return data & 0xFF def _probe(self, address): self._start() ok = self._write_byte(address << 1) self._stop() return ok > 0 def _write(self, address, buffer, transmit_stop): self._start() if not self._write_byte(address << 1): raise RuntimeError( "Device not responding at 0x{:02X}".format(address)) for byte in buffer: self._write_byte(byte) if transmit_stop: self._stop() def _read(self, address, length): self._start() if not self._write_byte(address << 1 | 1): raise RuntimeError( "Device not responding at 0x{:02X}".format(address)) buffer = bytearray(length) for byte_position in range(length): buffer[byte_position] = self._read_byte( ack=(byte_position != length - 1)) self._stop() return buffer
class SPI(_BitBangIO): """Software-based implementation of the SPI protocol over GPIO pins.""" def __init__(self, clock, MOSI=None, MISO=None): """Initialize bit bang (or software) based SPI. Must provide the SPI clock, and optionally MOSI and MISO pin numbers. If MOSI is set to None then writes will be disabled and fail with an error, likewise for MISO reads will be disabled. """ super().__init__() while self.try_lock(): pass self._mosi = None self._miso = None self.configure() self.unlock() # Set pins as outputs/inputs. self._sclk = DigitalInOut(clock) self._sclk.switch_to_output() if MOSI is not None: self._mosi = DigitalInOut(MOSI) self._mosi.switch_to_output() if MISO is not None: self._miso = DigitalInOut(MISO) self._miso.switch_to_input() def deinit(self): """Free any hardware used by the object.""" self._sclk.deinit() if self._miso: self._miso.deinit() if self._mosi: self._mosi.deinit() def configure(self, *, baudrate=100000, polarity=0, phase=0, bits=8): """Configures the SPI bus. Only valid when locked.""" if self._check_lock(): if not isinstance(baudrate, int): raise ValueError("baudrate must be an integer") if not isinstance(bits, int): raise ValueError("bits must be an integer") if bits < 1 or bits > 8: raise ValueError("bits must be in the range of 1-8") if polarity not in (0, 1): raise ValueError("polarity must be either 0 or 1") if phase not in (0, 1): raise ValueError("phase must be either 0 or 1") self._baudrate = baudrate self._polarity = polarity self._phase = phase self._bits = bits self._half_period = (1 / self._baudrate) / 2 # 50% Duty Cyle delay def _wait(self, start=None): """Wait for up to one half cycle""" while (start + self._half_period) > monotonic(): pass return monotonic() # Return current time def write(self, buffer, start=0, end=None): """Write the data contained in buf. Requires the SPI being locked. If the buffer is empty, nothing happens. """ # Fail MOSI is not specified. if self._mosi is None: raise RuntimeError("Write attempted with no MOSI pin specified.") if end is None: end = len(buffer) if self._check_lock(): start_time = monotonic() for byte in buffer[start:end]: for bit_position in range(self._bits): bit_value = byte & 0x80 >> bit_position # Set clock to base if not self._phase: # Mode 0, 2 self._mosi.value = bit_value self._sclk.value = self._polarity start_time = self._wait(start_time) # Flip clock off base if self._phase: # Mode 1, 3 self._mosi.value = bit_value self._sclk.value = not self._polarity start_time = self._wait(start_time) # Return pins to base positions self._mosi.value = 0 self._sclk.value = self._polarity # pylint: disable=too-many-branches def readinto(self, buffer, start=0, end=None, write_value=0): """Read into the buffer specified by buf while writing zeroes. Requires the SPI being locked. If the number of bytes to read is 0, nothing happens. """ if self._miso is None: raise RuntimeError("Read attempted with no MISO pin specified.") if end is None: end = len(buffer) if self._check_lock(): start_time = monotonic() for byte_position, _ in enumerate(buffer[start:end]): for bit_position in range(self._bits): bit_mask = 0x80 >> bit_position bit_value = write_value & 0x80 >> bit_position # Return clock to base self._sclk.value = self._polarity start_time = self._wait(start_time) # Handle read on leading edge of clock. if not self._phase: # Mode 0, 2 if self._mosi is not None: self._mosi.value = bit_value if self._miso.value: # Set bit to 1 at appropriate location. buffer[byte_position] |= bit_mask else: # Set bit to 0 at appropriate location. buffer[byte_position] &= ~bit_mask # Flip clock off base self._sclk.value = not self._polarity start_time = self._wait(start_time) # Handle read on trailing edge of clock. if self._phase: # Mode 1, 3 if self._mosi is not None: self._mosi.value = bit_value if self._miso.value: # Set bit to 1 at appropriate location. buffer[byte_position] |= bit_mask else: # Set bit to 0 at appropriate location. buffer[byte_position] &= ~bit_mask # Return pins to base positions self._mosi.value = 0 self._sclk.value = self._polarity def write_readinto(self, buffer_out, buffer_in, *, out_start=0, out_end=None, in_start=0, in_end=None): """Write out the data in buffer_out while simultaneously reading data into buffer_in. The lengths of the slices defined by buffer_out[out_start:out_end] and buffer_in[in_start:in_end] must be equal. If buffer slice lengths are both 0, nothing happens. """ if self._mosi is None: raise RuntimeError("Write attempted with no MOSI pin specified.") if self._miso is None: raise RuntimeError("Read attempted with no MISO pin specified.") if out_end is None: out_end = len(buffer_out) if in_end is None: in_end = len(buffer_in) if len(buffer_out[out_start:out_end]) != len( buffer_in[in_start:in_end]): raise RuntimeError("Buffer slices must be equal length") if self._check_lock(): start_time = monotonic() for byte_position, _ in enumerate(buffer_out[out_start:out_end]): for bit_position in range(self._bits): bit_mask = 0x80 >> bit_position bit_value = (buffer_out[byte_position + out_start] & 0x80 >> bit_position) in_byte_position = byte_position + in_start # Return clock to 0 self._sclk.value = self._polarity start_time = self._wait(start_time) # Handle read on leading edge of clock. if not self._phase: # Mode 0, 2 self._mosi.value = bit_value if self._miso.value: # Set bit to 1 at appropriate location. buffer_in[in_byte_position] |= bit_mask else: # Set bit to 0 at appropriate location. buffer_in[in_byte_position] &= ~bit_mask # Flip clock off base self._sclk.value = not self._polarity start_time = self._wait(start_time) # Handle read on trailing edge of clock. if self._phase: # Mode 1, 3 self._mosi.value = bit_value if self._miso.value: # Set bit to 1 at appropriate location. buffer_in[in_byte_position] |= bit_mask else: # Set bit to 0 at appropriate location. buffer_in[in_byte_position] &= ~bit_mask # Return pins to base positions self._mosi.value = 0 self._sclk.value = self._polarity # pylint: enable=too-many-branches @property def frequency(self): """Return the currently configured baud rate""" return self._baudrate
class PiperCommandCenter: def __init__(self, joy_x_pin=board.A4, joy_y_pin=board.A3, joy_z_pin=board.D2, joy_gnd_pin=board.A5, dpad_l_pin=board.D3, dpad_r_pin=board.D4, dpad_u_pin=board.D1, dpad_d_pin=board.D0, outputScale=20.0, deadbandCutoff=0.1, weight=0.2): self.x_axis = PiperJoystickAxis(joy_x_pin, outputScale=outputScale, deadbandCutoff=deadbandCutoff, weight=weight) self.y_axis = PiperJoystickAxis(joy_y_pin, outputScale=outputScale, deadbandCutoff=deadbandCutoff, weight=weight) self.joy_z = PiperJoystickZ(joy_z_pin) self.dpad = PiperDpad(dpad_l_pin, dpad_r_pin, dpad_u_pin, dpad_d_pin) # Drive pin low if requested for easier joystick wiring if joy_gnd_pin is not None: # Provide a ground for the joystick - this is to facilitate # easier wiring self.joystick_gnd = DigitalInOut(joy_gnd_pin) self.joystick_gnd.direction = Direction.OUTPUT self.joystick_gnd.value = 0 self.keyboard = Keyboard(usb_hid.devices) self.keyboard_layout = KeyboardLayoutUS( self.keyboard) # Change for non-US self.mouse = Mouse(usb_hid.devices) # State # self.state = _UNWIRED self.timer = time.monotonic() self.last_mouse_wheel = time.monotonic() self.last_mouse = time.monotonic() self.dotstar_led = adafruit_dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1) self.dotstar_led.brightness = 0.2 self.up_pressed = False self.down_pressed = False self.left_pressed = False self.right_pressed = False def process(self): # Call the debouncing library frequently self.joy_z.update() self.dpad.update() dx = self.x_axis.readJoystickAxis() dy = self.y_axis.readJoystickAxis() # Command Center State Machine # if self.state == _UNWIRED: self.dotstar_led[0] = ((time.monotonic_ns() >> 23) % 256, 0, 0) if dx == 0 and dy == 0: self.state = _WAITING self.timer = time.monotonic() elif self.state == _WAITING: self.dotstar_led[0] = ((time.monotonic_ns() >> 23) % 256, 0, 0) if dx != 0 or dy != 0: self.state = _UNWIRED else: if time.monotonic() - self.timer > 0.5: self.state = _JOYSTICK elif self.state == _JOYSTICK: self.dotstar_led[0] = (0, 255, 0) if self.joy_z.zPressed(): self.timer = time.monotonic() self.state = _JWAITING elif self.state == _JWAITING: if not self.joy_z.zPressed(): self.state = _JOYSTICK else: if time.monotonic() - self.timer > 1.0: self.mouse.release(Mouse.LEFT_BUTTON) self.mouse.release(Mouse.RIGHT_BUTTON) self.state = _USERCODE elif self.state == _USERCODE: self.dotstar_led[0] = (0, 0, 0) self.dotstar_led.deinit() self.joystick_gnd.deinit() self.x_axis.deinit() self.y_axis.deinit() self.dpad.deinit() self.joy_z.deinit() try: # Load usercode.py __import__("usercode") except ImportError: print("Missing usercode.py file") # If we get here due to an exception or the user code exiting # then restart as it's probably going to by the most stable # strategy # supervisor.reload() # Command Center Joystick Handling # if self.state == _JOYSTICK or self.state == _JWAITING: # Determine mouse wheel direction # dwheel = 0 if self.dpad.upPressed(): dwheel = -1 elif self.dpad.downPressed(): dwheel = 1 # Initial quick and dirty mouse movement pacing # if time.monotonic() - self.last_mouse > 0.005: self.last_mouse = time.monotonic() self.mouse.move(x=dx, y=dy) # Initial quick and dirty mouse scroll wheel pacing # if time.monotonic() - self.last_mouse_wheel > 0.1: self.last_mouse_wheel = time.monotonic() self.mouse.move(wheel=dwheel) if self.dpad.leftPressedEvent(): self.mouse.press(Mouse.LEFT_BUTTON) elif self.dpad.leftReleasedEvent(): self.mouse.release(Mouse.LEFT_BUTTON) if self.dpad.rightPressedEvent(): self.mouse.press(Mouse.RIGHT_BUTTON) elif self.dpad.rightReleasedEvent(): self.mouse.release(Mouse.RIGHT_BUTTON)
class PMS5003(): #def __init__(self, baudrate=9600, pin_enable=board.D10, pin_reset=board.D11): MAX_RESET_TIME = 20.0 # 9.2 seconds seen in testing MIN_CMD_INTERVAL = 0.1 # mode changes with interval < 50ms break a PMS5003 @staticmethod def _build_cmd_frame(cmd_bytes): """ Builds a valid command frame byte array with checksum for given command bytes """ if len(cmd_bytes) != 3: raise RuntimeError("Malformed command frame") cmd_frame = bytearray() cmd_frame.extend(PMS5003_SOF) cmd_frame.extend(cmd_bytes) cmd_frame.extend(sum(cmd_frame).to_bytes(2, "big")) return cmd_frame def __init__(self, serial=None, pin_reset=pimoroni_physical_feather_pins.pin23(), pin_enable=pimoroni_physical_feather_pins.pin22(), mode='active', retries=5, baudrate=9600): self._serial = None self._mode = 'active' # device starts up in active mode self._baudrate = baudrate self._pin_enable = pin_enable self._enable = None self._pin_reset = pin_reset self._reset = None self._attempts = retries + 1 if retries else 1 if mode not in ('active', 'passive'): raise ValueError("Invalid mode") # Exceptions are caught here as constructor has not # raised them in the prior versions try: self.setup(serial) if mode == 'passive': self.cmd_mode_passive() except RuntimeError: pass def cmd_mode_passive(self): """ Sends command to device to enable 'passive' mode. In passive mode data frames are only sent in response to a read command. """ self._mode = 'passive' time.sleep(self.MIN_CMD_INTERVAL) self._serial.reset_input_buffer() self._serial.write(self._build_cmd_frame(PMS5003_CMD_MODE_PASSIVE)) # In rare cases a single data frame sneaks in giving FrameLengthError try: resp = self._read_data(PMS5003CmdResponse) except FrameLengthError: resp = self._read_data(PMS5003CmdResponse) time.sleep(self.MIN_CMD_INTERVAL) return resp def cmd_mode_active(self): """ Sends command to device to enable 'active' mode. In active mode data frames are streamed continuously at intervals ranging from 200ms to 2.3 seconds. """ self._mode = 'active' # mode changes with interval < 50ms break on a PMS5003 time.sleep(self.MIN_CMD_INTERVAL) self._serial.reset_input_buffer() self._serial.write(self._build_cmd_frame(PMS5003_CMD_MODE_ACTIVE)) # In rare cases a single data frame sneaks in giving FrameLengthError try: resp = self._read_data(PMS5003CmdResponse) except FrameLengthError: resp = self._read_data(PMS5003CmdResponse) time.sleep(self.MIN_CMD_INTERVAL) return resp def setup(self, serial=None): if self._pin_enable: self._enable = DigitalInOut(self._pin_enable) self._enable.direction = Direction.OUTPUT self._enable.value = True if self._pin_reset: self._reset = DigitalInOut(self._pin_reset) self._reset.direction = Direction.OUTPUT self._reset.value = True if self._serial is not None: self._serial.deinit() self._serial = busio.UART( board.TX, board.RX, baudrate=self._baudrate, timeout=4) if serial is None else serial self.reset() def reset(self): """This resets the device via a pin if one is defined. It restores passive mode as necessary.""" if self._reset is None: return False time.sleep(0.1) self._reset.value = False self._serial.reset_input_buffer() time.sleep(0.1) self._reset.value = True # Wait for first data frame from the device # CircuitPython 6.0.0 on nRF52840 sometimes picks up 2 bogus bytes here start = time.monotonic() while True: if self.data_available(): break elapsed = time.monotonic() - start if elapsed > self.MAX_RESET_TIME: raise ReadTimeoutError( "PMS5003 Read Timeout: No response after reset") # After a reset device will be in active mode, restore passive mode if self._mode == "passive": _ = self._read_data() # discard buffered active data frame self.cmd_mode_passive() return True def deinit(self): if self._enable is not None: self._enable.deinit() if self._reset is not None: self._reset.deinit() if self._serial is not None: self._serial.deinit() def data_available(self): """Returns boolean indicating if one or more data frames are waiting. Only for use in active mode.""" return self._serial.in_waiting >= PMS5003Data.FRAME_LEN def read(self): """Read a data frame. In passive mode this will transmit a request for one. This will make additional attempts based on retries value in constructor if there are exceptions and only raise the first exception if all fail.""" read_ex = None for _ in range(self._attempts): if self._mode == 'passive': self._cmd_passive_read() try: return self._read_data() except RuntimeError as ex: if read_ex is None: read_ex = ex raise read_ex if read_ex else RuntimeError( "read failed - internal error") def _read_data(self, response_class=PMS5003Data): start = time.monotonic() sof_index = 0 while True: elapsed = time.monotonic() - start if elapsed > 5: raise ReadTimeoutError( "PMS5003 Read Timeout: Could not find start of frame") one_byte = self._serial.read(1) if one_byte is None or len(one_byte) == 0: raise SerialTimeoutError( "PMS5003 Read Timeout: Failed to read start of frame byte") if ord(one_byte) == PMS5003_SOF[sof_index]: if sof_index == 0: sof_index = 1 elif sof_index == 1: break else: sof_index = 0 len_data = self._serial.read(2) # Get frame length packet if len_data is None or len(len_data) != 2: raise SerialTimeoutError( "PMS5003 Read Timeout: Could not find length packet") frame_length = struct.unpack(">H", len_data)[0] response_class.check_data_len(frame_length, desc="Length field") raw_data = self._serial.read(frame_length) if raw_data is None or len(raw_data) != frame_length: read_len = "TIMEOUT" if raw_data is None else len(raw_data) raise SerialTimeoutError( "PMS5003 Read Timeout: Invalid frame length. " "Got {} bytes, expected {}.".format(read_len, frame_length)) return response_class(raw_data, frame_length_bytes=len_data) def _cmd_passive_read(self): """ Sends command to request a data frame while in 'passive' mode and immediately reads in frame. """ self._serial.reset_input_buffer() self._serial.write(self._build_cmd_frame(PMS5003_CMD_READ))
if EPD_present: display.fill(Adafruit_EPD.WHITE) if Battery_Low: x = 60 y = 40 display.text('LOW', x, y, Adafruit_EPD.BLACK, size=5) x = 60 y = 120 display.text('BAT', x, y, Adafruit_EPD.BLACK, size=5) else: x = 60 y = 80 display.text('OFF', x, y, Adafruit_EPD.BLACK, size=5) display.display() display.power_down() io_pwr.value = False v33_pwr = DigitalInOut(SOC_GPIO_PIN_3V3_PWR) v33_pwr.direction = Direction.OUTPUT v33_pwr.value = False button.deinit() import alarm pin_alarm = alarm.pin.PinAlarm(pin=SOC_GPIO_PIN_BUTTON, value=False, pull=False) alarm.exit_and_deep_sleep_until_alarms(pin_alarm)
class Peripherals: """Peripherals Helper Class for the FunHouse Library""" # pylint: disable=too-many-instance-attributes, too-many-locals, too-many-branches, too-many-statements def __init__(self): # Dotstars self.dotstars = adafruit_dotstar.DotStar(board.DOTSTAR_CLOCK, board.DOTSTAR_DATA, 5, brightness=0.3) # Light Sensor self._light = AnalogIn(board.LIGHT) # Buttons self._buttons = [] for pin in (board.BUTTON_DOWN, board.BUTTON_SELECT, board.BUTTON_UP): switch = DigitalInOut(pin) switch.direction = Direction.INPUT switch.pull = Pull.DOWN self._buttons.append(switch) # Cap Tocuh Pads self._ctp = [] for pin in ( board.CAP6, board.CAP7, board.CAP8, board.CAP13, board.CAP12, board.CAP11, board.CAP10, board.CAP9, ): cap = touchio.TouchIn(pin) self._ctp.append(cap) # LED self._led = DigitalInOut(board.LED) self._led.direction = Direction.OUTPUT # PIR Sensor self._pir = DigitalInOut(board.PIR_SENSE) self._pir.direction = Direction.INPUT @staticmethod def play_tone(frequency, duration): """Automatically Enable/Disable the speaker and play a tone at the specified frequency for the specified duration It will attempt to play the sound up to 3 times in the case of an error. """ if frequency <= 0: raise ValueError("The frequency has to be greater than 0.") attempt = 0 # Try up to 3 times to play the sound while attempt < 3: try: simpleio.tone(board.SPEAKER, frequency, duration) break except NameError: pass attempt += 1 def set_dotstars(self, *values): """Set the dotstar values to the provided values""" for i, value in enumerate(values[:len(self.dotstars)]): self.dotstars[i] = value def deinit(self): """Call deinit on all resources to free them""" self.dotstars.deinit() for button in self._buttons: button.deinit() for ctp in self._ctp: ctp.deinit() self._light.deinit() self._led.deinit() self._pir.deinit() @property def button_down(self): """ Return whether Down Button is pressed """ return self._buttons[0].value @property def button_sel(self): """ Return whether Sel Button is pressed """ return self._buttons[1].value @property def button_up(self): """ Return whether Up Button is pressed """ return self._buttons[2].value @property def any_button_pressed(self): """ Return whether any button is pressed """ return True in [button.value for button in enumerate(self._buttons)] @property def captouch6(self): """ Return whether CT6 Touch Pad is touched """ return self._ctp[0].value @property def captouch7(self): """ Return whether CT7 Touch Pad is touched """ return self._ctp[1].value @property def captouch8(self): """ Return whether CT8 Touch Pad is touched """ return self._ctp[2].value @property def slider(self): """ Return the slider position value in the range of 0.0-1.0 or None if not touched """ val = 0 cap_map = b"\x01\x03\x02\x05\x04\x0c\x08\x18\x10" for cap in range(5): raw = self._ctp[cap + 3].raw_value if raw > 15000: val += 1 << (cap) for i, pos in enumerate(tuple(cap_map)): if val == pos: print(i, len(cap_map) - 1) return round(i / (len(cap_map) - 1), 1) return None @property def light(self): """ Return the value of the light sensor. The neopixel_disable property must be false to get a value. .. code-block:: python import time from adafruit_funhouse import FunHouse funhouse = FunHouse() while True: print(funhouse.peripherals.light) time.sleep(0.01) """ return self._light.value @property def led(self): """ Return or set the value of the LED """ return self._led.value @led.setter def led(self, value): self._led.value = bool(value) @property def pir_sensor(self): """ Return the value of the PIR Sensor """ return self._pir.value
class HCSR04: """Control a HC-SR04 ultrasonic range sensor. Example use: :: import time import board import adafruit_hcsr04 sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.D2, echo_pin=board.D3) while True: try: print((sonar.distance,)) except RuntimeError: print("Retrying!") pass time.sleep(0.1) """ def __init__(self, trigger_pin, echo_pin, *, timeout=0.1): """ :param trigger_pin: The pin on the microcontroller that's connected to the ``Trig`` pin on the HC-SR04. :type trig_pin: microcontroller.Pin :param echo_pin: The pin on the microcontroller that's connected to the ``Echo`` pin on the HC-SR04. :type echo_pin: microcontroller.Pin :param float timeout: Max seconds to wait for a response from the sensor before assuming it isn't going to answer. Should *not* be set to less than 0.05 seconds! """ self._timeout = timeout self._trig = DigitalInOut(trigger_pin) self._trig.direction = Direction.OUTPUT if _USE_PULSEIO: self._echo = PulseIn(echo_pin) self._echo.pause() self._echo.clear() else: self._echo = DigitalInOut(echo_pin) self._echo.direction = Direction.INPUT def __enter__(self): """Allows for use in context managers.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Automatically de-initialize after a context manager.""" self.deinit() def deinit(self): """De-initialize the trigger and echo pins.""" self._trig.deinit() self._echo.deinit() @property def distance(self): """Return the distance measured by the sensor in cm. This is the function that will be called most often in user code. The distance is calculated by timing a pulse from the sensor, indicating how long between when the sensor sent out an ultrasonic signal and when it bounced back and was received again. If no signal is received, we'll throw a RuntimeError exception. This means either the sensor was moving too fast to be pointing in the right direction to pick up the ultrasonic signal when it bounced back (less likely), or the object off of which the signal bounced is too far away for the sensor to handle. In my experience, the sensor can detect objects over 460 cm away. :return: Distance in centimeters. :rtype: float """ return self._dist_two_wire() # at this time we only support 2-wire meausre def _dist_two_wire(self): if _USE_PULSEIO: self._echo.clear() # Discard any previous pulse values self._trig.value = True # Set trig high time.sleep(0.00001) # 10 micro seconds 10/1000/1000 self._trig.value = False # Set trig low pulselen = None timestamp = time.monotonic() if _USE_PULSEIO: self._echo.resume() while not self._echo: # Wait for a pulse if (time.monotonic() - timestamp) > self._timeout: self._echo.pause() raise RuntimeError("Timed out") self._echo.pause() pulselen = self._echo[0] else: # OK no hardware pulse support, we'll just do it by hand! # hang out while the pin is low while not self._echo.value: if time.monotonic() - timestamp > self._timeout: raise RuntimeError("Timed out") timestamp = time.monotonic() # track how long pin is high while self._echo.value: if time.monotonic() - timestamp > self._timeout: raise RuntimeError("Timed out") pulselen = time.monotonic() - timestamp pulselen *= 1000000 # convert to us to match pulseio if pulselen >= 65535: raise RuntimeError("Timed out") # positive pulse time, in seconds, times 340 meters/sec, then # divided by 2 gives meters. Multiply by 100 for cm # 1/1000000 s/us * 340 m/s * 100 cm/m * 2 = 0.017 return pulselen * 0.017
class Peripherals: """Peripherals Helper Class for the MagTag Library""" # pylint: disable=too-many-instance-attributes, too-many-locals, too-many-branches, too-many-statements def __init__(self): # Neopixels self.neopixels = neopixel.NeoPixel(board.NEOPIXEL, 4, brightness=0.3) self._neopixel_disable = DigitalInOut(board.NEOPIXEL_POWER) self._neopixel_disable.direction = Direction.OUTPUT self._neopixel_disable.value = False # Battery Voltage self._batt_monitor = AnalogIn(board.BATTERY) # Speaker Enable self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE) self._speaker_enable.direction = Direction.OUTPUT self._speaker_enable.value = False # Light Sensor self._light = AnalogIn(board.LIGHT) # Buttons self.buttons = [] for pin in (board.BUTTON_A, board.BUTTON_B, board.BUTTON_C, board.BUTTON_D): switch = DigitalInOut(pin) switch.direction = Direction.INPUT switch.pull = Pull.UP self.buttons.append(switch) def play_tone(self, frequency, duration): """Automatically Enable/Disable the speaker and play a tone at the specified frequency for the specified duration It will attempt to play the sound up to 3 times in the case of an error. """ if frequency <= 0: raise ValueError("The frequency has to be greater than 0.") self._speaker_enable.value = True attempt = 0 # Try up to 3 times to play the sound while attempt < 3: try: simpleio.tone(board.SPEAKER, frequency, duration) break except NameError: pass attempt += 1 self._speaker_enable.value = False def deinit(self): """Call deinit on all resources to free them""" self.neopixels.deinit() self._neopixel_disable.deinit() self._speaker_enable.deinit() for button in self.buttons: button.deinit() self._batt_monitor.deinit() self._light.deinit() @property def battery(self): """Return the voltage of the battery""" return (self._batt_monitor.value / 65535.0) * 3.3 * 2 @property def neopixel_disable(self): """ Enable or disable the neopixels for power savings """ return self._neopixel_disable.value @neopixel_disable.setter def neopixel_disable(self, value): self._neopixel_disable.value = value @property def speaker_disable(self): """ Enable or disable the speaker for power savings """ return not self._speaker_enable.value @speaker_disable.setter def speaker_disable(self, value): self._speaker_enable.value = not value @property def button_a_pressed(self): """ Return whether Button A is pressed """ return not self.buttons[0].value @property def button_b_pressed(self): """ Return whether Button B is pressed """ return not self.buttons[1].value @property def button_c_pressed(self): """ Return whether Button C is pressed """ return not self.buttons[2].value @property def button_d_pressed(self): """ Return whether Button D is pressed """ return not self.buttons[3].value @property def any_button_pressed(self): """ Return whether any button is pressed """ return False in [self.buttons[i].value for i in range(0, 4)] @property def light(self): """ Return the value of the light sensor. The neopixel_disable property must be false to get a value. .. code-block:: python import time from adafruit_magtag.magtag import MagTag magtag = MagTag() while True: print(magtag.peripherals.light) time.sleep(0.01) """ return self._light.value
class Ohmite: """ Types: 0 = round, 1 = long , 2 = short """ # expect _wiper, _ref, _v_1, _v_2, _v_3, _type=0): # or _wiper, _ref, _v_1, _v_2, _type=(1/2): # def __init__(self, *args, **kwargs): if "type" in kwargs: self._type = kwargs["type"] else: self._type = 0 self._ref = DigitalInOut(args[1]) self._ANALOG_RESOLUTION = 65536 self._VOLTAGE = 3.3 if self._type == 0: # this is a round sensor self._ZERO_OFFSET = 800 self._READ_RANGE = 1450 self._wiper = AnalogIn(args[0]) self._d0 = DigitalInOut(args[2]) self._d120 = DigitalInOut(args[3]) self._d240 = DigitalInOut(args[4]) self._ZERO_OFFSET = 800 self._READ_RANGE = 1450 self._LENGTH = 120 else: # this is a linear sensor self._wiper_pin = args[0] self._wiper = DigitalInOut(args[0]) self._v1 = DigitalInOut(args[2]) self._v2 = DigitalInOut(args[3]) if self._type == 1: self._ZERO_OFFSET = 200 self._READ_RANGE = 2600 self._LENGTH = 100 else: self._ZERO_OFFSET = 500 self._READ_RANGE = 1900 self._LENGTH = 55 # print(args, kwargs) def begin(self): if self._type == 0: self._ref.direction = Direction.OUTPUT self._ref.value = 1 self._d0.direction = Direction.OUTPUT self._d0.value = 1 self._d120.direction = Direction.OUTPUT self._d120.value = 1 self._d240.direction = Direction.OUTPUT self._d240.value = 0 else: self._v1.direction = Direction.OUTPUT self._v1.value = 0 self._v2.direction = Direction.OUTPUT self._v2.value = 0 self._wiper.direction = Direction.OUTPUT self._wiper.value = 0 self._ref.direction = Direction.OUTPUT self._ref.value = 0 # Generic command def get_position(self, tail_to_tip=True): if self._type == 0: return self._get_round_position() if self._type == 1 or self._type == 2: return self._get_linear_position(tail_to_tip) def get_force(self): if self._type == 0: return self._get_round_force() if self._type == 1 or self._type == 2: return self._get_linear_force() # Linear sensors def _get_linear_position(self, tail_to_tip=True): self._wiper.deinit() l_wiper = AnalogIn(self._wiper_pin) self._ref.switch_to_input(pull=Pull.DOWN) if tail_to_tip: # Read from tail end self._v1.value = 1 time.sleep(0.001) value = self._get_voltage(l_wiper) self._v1.value = 0 else: self._v2.value = 1 time.sleep(0.001) value = self._get_voltage(l_wiper) self._v2.value = 0 l_wiper.deinit() self._wiper = DigitalInOut(self._wiper_pin) self._wiper.direction = Direction.OUTPUT self._wiper.value = 0 self._ref.switch_to_output(value=False) return self._get_millimeters(value) def _get_millimeters(self, voltage): value = int((((voltage * 1000) - self._ZERO_OFFSET) * self._LENGTH) / self._READ_RANGE) if value < 0: value = 0 if value > self._LENGTH: value = self._LENGTH return value # this is method 3 from the implementation guide. # Section 4.2.3, page 5 # https://www.mouser.com/pdfdocs/Ohmite-FSP-Integration-Guide-V1-0_27-03-18.pdf def _get_linear_force(self): self._wiper.deinit() l_wiper = AnalogIn(self._wiper_pin) self._ref.value = 0 self._v1.switch_to_output(value=True) self._v2.switch_to_input() time.sleep(0.001) wiper_1 = l_wiper.value self._v2.switch_to_output(value=True) self._v1.switch_to_input() time.sleep(0.001) wiper_2 = l_wiper.value l_wiper.deinit() self._wiper = DigitalInOut(self._wiper_pin) self._wiper.direction = Direction.OUTPUT self._wiper.value = 0 self._v1.direction = Direction.OUTPUT self._v1.value = 0 self._v2.value = 0 return (( (wiper_1 + wiper_2) / 2) * self._VOLTAGE) / self._ANALOG_RESOLUTION # Round sensor helpers def _calc_position(self, low, high, off): # off should be the DX pin Disable off.switch_to_input() high.value = 1 low.value = 0 time.sleep(0.001) wiper_v = self._get_voltage(self._wiper) # print(wiper_v) # Convert to milli volts and apply offsets to wiper_v _angle = ((((wiper_v * 1000) - self._ZERO_OFFSET) * self._LENGTH) / self._READ_RANGE) # print(angle) # off should be reset to output off.switch_to_output(value=False) return int(_angle) def _get_round_position(self): # Read analog voltage on D 1 self._d0.value = 1 self._d120.value = 1 self._d240.value = 0 time.sleep(0.001) d3 = self._wiper.value self._d0.value = 0 self._d120.value = 1 self._d240.value = 1 time.sleep(0.001) d1 = self._wiper.value self._d0.value = 1 self._d120.value = 0 self._d240.value = 1 time.sleep(0.001) d2 = self._wiper.value _angle = 0 # which voltage is the lowest: # print(d1, d2, d3, f1) if d1 < d2 and d1 < d3: if d2 < d3: # d1 and d2 # print ("d1:d2") _angle = self._calc_position(self._d0, self._d120, self._d240) else: # d1 and d3 # print("d1:d3") _angle = self._calc_position(self._d240, self._d0, self._d120) _angle = _angle + 240 if d2 < d1 and d2 < d3: if d1 < d3: # print ("d2:d1") _angle = self._calc_position(self._d0, self._d120, self._d240) else: # print ("d2:d3") _angle = self._calc_position(self._d120, self._d240, self._d0) _angle = _angle + 120 if d3 < d1 and d3 < d2: if d1 < d2: # print ("d3:d1") _angle = self._calc_position(self._d240, self._d0, self._d120) _angle = _angle + 240 else: # print ("d3:d2") _angle = self._calc_position(self._d120, self._d240, self._d0) _angle = _angle + 120 if _angle < 0 or _angle > 360: _angle = 0 return _angle def _get_round_force(self): # read force self._d0.value = 1 self._d120.value = 1 self._d240.value = 1 self._ref.switch_to_output(value=False) time.sleep(0.001) _f = self._get_voltage(self._wiper) self._ref.switch_to_input() return _f def _get_voltage(self, pin): return (pin.value * self._VOLTAGE) / self._ANALOG_RESOLUTION
selected -= 4 rect.y -= 62 display.refresh() print("up") time.sleep(5) continue if not btnC.value and selected < 4: selected += 4 rect.y += 62 display.refresh() print("down") time.sleep(5) continue if not btnD.value and selected != 3 and selected != 7: selected += 1 rect.x += 74 display.refresh() print("right") time.sleep(5) continue btnA.deinit() btnB.deinit() btnC.deinit() btnD.deinit() print("Starting ", projects[int(alarm.sleep_memory[0])]) __import__("/projects/" + projects[int(alarm.sleep_memory[0])])
class GroveUltrasonicRanger: """Control a Grove ultrasonic range sensor. Example use: :: import time import board import grove_ultrasonic_ranger sonar = grove_ultrasonic_ranger.GroveUltrasonicRanger(sig_pin=board.D2) while True: try: print((sonar.distance,)) except RuntimeError as e: print("Retrying due to exception =", e) pass time.sleep(0.1) """ def __init__(self, sig_pin, unit=1.0, timeout=1.0): """ :param sig_pin: The pin on the microcontroller that's connected to the ``Sig`` pin on the GroveUltrasonicRanger. :type sig_pin: microcontroller.Pin :param float unit: pass in conversion factor for unit conversions from cm for example 2.54 would convert to inches. :param float timeout: Max seconds to wait for a response from the sensor before assuming it isn't going to answer. Should *not* be set to less than 0.05 seconds! """ self._unit = unit self._timeout = timeout * TICKS_PER_SEC if _USE_PULSEIO: self._sig = PulseIn(sig_pin) self._sig.pause() self._sig.clear() else: self._sig = DigitalInOut(sig_pin) self._sig.direction = Direction.OUTPUT self._sig.value = False # Set trig low def __enter__(self): """Allows for use in context managers.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Automatically de-initialize after a context manager.""" self.deinit() def deinit(self): """De-initialize the sig pin.""" self._sig.deinit() @property def distance(self): """Return the distance measured by the sensor in cm (or user specified units.) This is the function that will be called most often in user code. The distance is calculated by timing a pulse from the sensor, indicating how long between when the sensor sent out an ultrasonic signal and when it bounced back and was received again. If no signal is received, we'll throw a RuntimeError exception. This means either the sensor was moving too fast to be pointing in the right direction to pick up the ultrasonic signal when it bounced back (less likely), or the object off of which the signal bounced is too far away for the sensor to handle. In my experience, the sensor can detect objects over 460 cm away. :return: Distance in centimeters (can be divided by a unit conv factor.) :rtype: float """ return self._dist_one_wire() def _dist_one_wire(self): if _USE_PULSEIO: self._sig.pause() self._sig.clear() # Discard any previous pulse values else: #self._sig.direction = Direction.OUTPUT self._sig.value = True # Set trig high time.sleep(0.00001) # 10 micro seconds 10/1000/1000 self._sig.value = False # Set trig low self._sig.direction = Direction.INPUT pulselen = None timestamp = MONOTONIC_TICKS() if _USE_PULSEIO: self._sig.resume(10) while not self._sig: # Wait for a pulse if (MONOTONIC_TICKS() - timestamp) > self._timeout: self._sig.pause() raise RuntimeError( "Timed out (pulseio waiting for a pulse)") self._sig.pause() pulselen = self._sig[0] else: # OK no hardware pulse support, we'll just do it by hand! # hang out while the pin is low while not self._sig.value: if MONOTONIC_TICKS() - timestamp > self._timeout: self._sig.direction = Direction.OUTPUT raise RuntimeError( "Timed out (gpio, waiting for pulse leading edge)") timestamp = MONOTONIC_TICKS() # track how long pin is high while self._sig.value: if MONOTONIC_TICKS() - timestamp > self._timeout: self._sig.direction = Direction.OUTPUT raise RuntimeError( "Timed out (gpio, waiting for pulse trailing edge)") pulselen = MONOTONIC_TICKS() - timestamp self._sig.direction = Direction.OUTPUT pulselen *= (1000000 / TICKS_PER_SEC ) # convert to us to match pulseio if pulselen >= 65535: raise RuntimeError("Timed out (unreasonable pulse length)") # positive pulse time, in seconds, times 340 meters/sec, then # divided by 2 gives meters. Multiply by 100 for cm # 1/1000000 s/us * 340 m/s * 100 cm/m * 2 = 0.017 # Divide by the supplied unit conversion factor return (pulselen * 0.017) / self._unit
class HCSR04: """Control a HC-SR04 ultrasonic range sensor. Example use: :: import time import board import adafruit_hcsr04 sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.D2, echo_pin=board.D3) while True: try: print((sonar.distance,)) except RuntimeError: print("Retrying!") pass time.sleep(0.1) """ def __init__(self, trigger_pin, echo_pin, *, timeout=0.1): """ :param trigger_pin: The pin on the microcontroller that's connected to the ``Trig`` pin on the HC-SR04. :type trig_pin: microcontroller.Pin :param echo_pin: The pin on the microcontroller that's connected to the ``Echo`` pin on the HC-SR04. :type echo_pin: microcontroller.Pin :param float timeout: Max seconds to wait for a response from the sensor before assuming it isn't going to answer. Should *not* be set to less than 0.05 seconds! """ self._timeout = timeout self._trig = DigitalInOut(trigger_pin) self._trig.direction = Direction.OUTPUT if _USE_PULSEIO: self._echo = PulseIn(echo_pin) self._echo.pause() self._echo.clear() else: self._echo = DigitalInOut(echo_pin) self._echo.direction = Direction.INPUT def __enter__(self): """Allows for use in context managers.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Automatically de-initialize after a context manager.""" self.deinit() def deinit(self): """De-initialize the trigger and echo pins.""" self._trig.deinit() self._echo.deinit() @property def distance(self): """Return the distance measured by the sensor in cm. This is the function that will be called most often in user code. The distance is calculated by timing a pulse from the sensor, indicating how long between when the sensor sent out an ultrasonic signal and when it bounced back and was received again. If no signal is received, we'll throw a RuntimeError exception. This means either the sensor was moving too fast to be pointing in the right direction to pick up the ultrasonic signal when it bounced back (less likely), or the object off of which the signal bounced is too far away for the sensor to handle. In my experience, the sensor can detect objects over 460 cm away. :return: Distance in centimeters. :rtype: float """ return self._dist_two_wire( ) # at this time we only support 2-wire meausre def _dist_two_wire(self): if _USE_PULSEIO: self._echo.clear() # Discard any previous pulse values self._trig.value = True # Set trig high time.sleep(0.00001) # 10 micro seconds 10/1000/1000 self._trig.value = False # Set trig low pulselen = None timestamp = time.monotonic() if _USE_PULSEIO: self._echo.resume() while not self._echo: # Wait for a pulse if (time.monotonic() - timestamp) > self._timeout: self._echo.pause() raise RuntimeError("Timed out") self._echo.pause() pulselen = self._echo[0] else: # OK no hardware pulse support, we'll just do it by hand! # hang out while the pin is low while not self._echo.value: if time.monotonic() - timestamp > self._timeout: raise RuntimeError("Timed out") timestamp = time.monotonic() # track how long pin is high while self._echo.value: if time.monotonic() - timestamp > self._timeout: raise RuntimeError("Timed out") pulselen = time.monotonic() - timestamp pulselen *= 1000000 # convert to us to match pulseio if pulselen >= 65535: raise RuntimeError("Timed out") # positive pulse time, in seconds, times 340 meters/sec, then # divided by 2 gives meters. Multiply by 100 for cm # 1/1000000 s/us * 340 m/s * 100 cm/m * 2 = 0.017 return pulselen * 0.017
class PiperDpad: def __init__(self, dpad_l_pin=board.D3, dpad_r_pin=board.D4, dpad_u_pin=board.D1, dpad_d_pin=board.D0): # Setup DPAD # self.left_pin = DigitalInOut(dpad_l_pin) self.left_pin.direction = Direction.INPUT self.left_pin.pull = Pull.UP self.left = Debouncer(self.left_pin) self.right_pin = DigitalInOut(dpad_r_pin) self.right_pin.direction = Direction.INPUT self.right_pin.pull = Pull.UP self.right = Debouncer(self.right_pin) self.up_pin = DigitalInOut(dpad_u_pin) self.up_pin.direction = Direction.INPUT self.up_pin.pull = Pull.UP self.up = Debouncer(self.up_pin) self.down_pin = DigitalInOut(dpad_d_pin) self.down_pin.direction = Direction.INPUT self.down_pin.pull = Pull.UP self.down = Debouncer(self.down_pin) def deinit(self): self.up_pin.deinit() self.down_pin.deinit() self.left_pin.deinit() self.right_pin.deinit() def update(self): self.left.update() self.right.update() self.up.update() self.down.update() def leftPressed(self): return not self.left.value def leftPressedEvent(self): return self.left.fell def leftReleasedEvent(self): return self.left.rose def rightPressed(self): return not self.right.value def rightPressedEvent(self): return self.right.fell def rightReleasedEvent(self): return self.right.rose def upPressed(self): return not self.up.value def upPressedEvent(self): return self.up.fell def upReleasedEvent(self): return self.up.rose def downPressed(self): return not self.down.value def downPressedEvent(self): return self.down.fell def downReleasedEvent(self): return self.down.rose