def __init__(self,
                 width: int,
                 height: int,
                 spi: busio.SPI,
                 dc: digitalio.DigitalInOut,
                 reset: Optional[digitalio.DigitalInOut],
                 cs: digitalio.DigitalInOut,
                 *,
                 external_vcc: bool = False,
                 baudrate: int = 8000000,
                 polarity: int = 0,
                 phase: int = 0,
                 page_addressing: bool = False):
        self.page_addressing = page_addressing
        if self.page_addressing:
            raise NotImplementedError(
                "Page addressing mode with SPI has not yet been implemented.")

        self.rate = 10 * 1024 * 1024
        dc.switch_to_output(value=0)
        self.spi_device = spi_device.SPIDevice(spi,
                                               cs,
                                               baudrate=baudrate,
                                               polarity=polarity,
                                               phase=phase)
        self.dc_pin = dc
        self.buffer = bytearray((height // 8) * width)
        super().__init__(
            memoryview(self.buffer),
            width,
            height,
            external_vcc=external_vcc,
            reset=reset,
            page_addressing=self.page_addressing,
        )
 def __init__(
     self,
     width: int,
     height: int,
     spi: SPI,
     dc: DigitalInOut,
     reset: DigitalInOut,
     cs: DigitalInOut,
     *,
     external_vcc: bool = False,
     baudrate: int = 8000000,
     polarity: int = 0,
     phase: int = 0
 ):
     self.rate = 10 * 1024 * 1024
     dc.switch_to_output(value=False)
     self.spi_device = spi_device.SPIDevice(
         spi, cs, baudrate=baudrate, polarity=polarity, phase=phase
     )
     self.dc_pin = dc
     self.buffer = bytearray((height // 8) * width)
     super().__init__(
         memoryview(self.buffer),
         width,
         height,
         external_vcc=external_vcc,
         reset=reset,
     )
Exemple #3
0
 def enableBacklight(self, onOff=True):
     # Turn on the Backlight
     if self.display:
         backlight = DigitalInOut(board.D26)
         backlight.switch_to_output()
         backlight.value = onOff
     else:
         print("No display setup. Unable to setup backlight.")
Exemple #4
0
 def builtin_bus_factory():
     cs = DigitalInOut(board.D5)
     cs.switch_to_output()
     return CAN(board.SPI(),
                cs,
                baudrate=1000000,
                loopback=True,
                silent=True)
Exemple #5
0
    def __enter__(self):
        self.device = busio.UART(tx=board.ESP_RX, rx=board.ESP_TX, \
                            baudrate=115200, timeout=1,
                            receiver_buffer_size=1024)
        reset = DigitalInOut(board.ESP_WIFI_EN)
        reset.switch_to_output(value=True, drive_mode=DriveMode.OPEN_DRAIN)
        self.reset_pin = reset

        return self
Exemple #6
0
class HeaterRelay:
    'Controls the relay connected to the heater'

    def __init__(self, pin=A5):
        self.heater = DigitalInOut(pin)
        self.heater.switch_to_output()
        self.heater.value = True

    def enable(self, on=True):
        self.heater.value = not on  # False value turns on relay
Exemple #7
0
class Relay:
    'Controls a relay connected to the heater, A/C or fan'

    def __init__(self, name: str, pin):
        self.name = name
        self.control = DigitalInOut(pin)
        self.control.switch_to_output()
        self.control.value = True

    def enable(self, on=True):
        self.control.value = not on  # False value turns on relay
Exemple #8
0
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
Exemple #9
0
def setup_audio():
    _speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
    _speaker_enable.switch_to_output(value=False)
    _speaker_enable.value = True
Exemple #10
0
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 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, stop=True):
        """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], stop)

    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,
                              stop=True):
        """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,
                         stop=stop)
            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 _set_values(self, *, scl, sda, delay=None):
        if delay is None:
            delay = self._delay
        self._scl.value = scl
        self._scl.value = sda
        sleep(delay)

    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
Exemple #11
0
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.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 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
Exemple #12
0
        from audiopwmio import PWMAudioOut as AudioOut
    except ImportError:
        pass  # not always supported by every board!

# color rgb values
OFF = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# neopixels
pixels = NeoPixel(board.NEOPIXEL, 10, brightness=0.2)

# red LED
led = DigitalInOut(board.D13)
led.switch_to_output()

# face button 1 (left)
button_1 = DigitalInOut(board.BUTTON_A)
button_1.switch_to_input(pull=Pull.DOWN)

# feed button
button_feed = DigitalInOut(board.A1)
button_feed.direction = Direction.INPUT
button_feed.pull = Pull.UP

# motor controller
motor = DigitalInOut(board.A2)
motor.direction = Direction.OUTPUT

# enable audio speaker
Exemple #13
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)
Exemple #14
0
class PYOA_Graphics():
    """A choose your own adventure game framework."""
    def __init__(self):
        self.root_group = displayio.Group(max_size=15)

        self._background_group = displayio.Group(max_size=1)
        self.root_group.append(self._background_group)
        self._text_group = displayio.Group(max_size=1)
        self.root_group.append(self._text_group)
        self._button_group = displayio.Group(max_size=2)
        self.root_group.append(self._button_group)

        self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
        self._speaker_enable.switch_to_output(False)
        if hasattr(board, 'AUDIO_OUT'):
            self.audio = audioio.AudioOut(board.AUDIO_OUT)
        elif hasattr(board, 'SPEAKER'):
            self.audio = audioio.AudioOut(board.SPEAKER)
        else:
            raise AttributeError('Board does not have an audio output!')

        self._background_file = None
        self._wavfile = None

        board.DISPLAY.auto_brightness = False
        self.backlight_fade(0)
        board.DISPLAY.show(self.root_group)
        self.touchscreen = None
        if hasattr(board, 'TOUCH_XL'):
            self.touchscreen = adafruit_touchscreen.Touchscreen(
                board.TOUCH_XL,
                board.TOUCH_XR,
                board.TOUCH_YD,
                board.TOUCH_YU,
                calibration=((5200, 59000), (5800, 57000)),
                size=(320, 240))
        elif hasattr(board, 'BUTTON_CLOCK'):
            self.mouse_cursor = Cursor(board.DISPLAY,
                                       display_group=self.root_group,
                                       cursor_speed=8)
            self.cursor = CursorManager(self.mouse_cursor)
        else:
            raise AttributeError('PYOA requires a touchscreen or cursor.')
        self._gamedirectory = None
        self._gamefilename = None
        self._game = None
        self._text = None
        self._background_sprite = None
        self._text_font = None
        self._left_button = None
        self._right_button = None
        self._middle_button = None

    def load_game(self, game_directory):
        """Load a game.

        :param game_directory: where the game files are stored
        """
        self._gamedirectory = game_directory
        self._text_font = terminalio.FONT
        # Button Attributes
        btn_left = 10
        btn_right = btn_left + 180
        btn_mid = btn_left + 90
        button_y = 195
        button_width = 120
        button_height = 40
        if board.DISPLAY.height < 200:
            button_y /= 2
            button_y += 10
            button_width /= 2
            button_height /= 2
            btn_right /= 2
            btn_mid /= 2
        self._left_button = Button(x=int(btn_left),
                                   y=int(button_y),
                                   width=int(button_width),
                                   height=int(button_height),
                                   label="Left",
                                   label_font=self._text_font,
                                   style=Button.SHADOWROUNDRECT)
        self._right_button = Button(x=int(btn_right),
                                    y=int(button_y),
                                    width=int(button_width),
                                    height=int(button_height),
                                    label="Right",
                                    label_font=self._text_font,
                                    style=Button.SHADOWROUNDRECT)
        self._middle_button = Button(x=int(btn_mid),
                                     y=int(button_y),
                                     width=int(button_width),
                                     height=int(button_height),
                                     label="Middle",
                                     label_font=self._text_font,
                                     style=Button.SHADOWROUNDRECT)
        self._gamefilename = game_directory + "/cyoa.json"
        try:
            game_file = open(self._gamefilename, "r")
        except OSError:
            raise OSError("Could not open game file " + self._gamefilename)
        self._game = json.load(game_file)
        game_file.close()

    def _fade_to_black(self):
        """Turn down the lights."""
        self.mouse_cursor.is_hidden = True
        self.backlight_fade(0)
        # turn off background so we can render the text
        self.set_background(None, with_fade=False)
        self.set_text(None, None)
        for _ in range(len(self._button_group)):
            self._button_group.pop()
        self.mouse_cursor.is_hidden = False

    def _display_buttons(self, card):
        """Display the buttons of a card.

        :param card: The active card
        """
        button01_text = card.get('button01_text', None)
        button02_text = card.get('button02_text', None)
        self._left_button.label = button01_text
        self._middle_button.label = button01_text
        self._right_button.label = button02_text
        if button01_text and not button02_text:
            # show only middle button
            self._button_group.append(self._middle_button.group)
        if button01_text and button02_text:
            self._button_group.append(self._right_button.group)
            self._button_group.append(self._left_button.group)

    def _display_background_for(self, card):
        """If there's a background on card, display it.

        :param card: The active card
        """
        self.set_background(card.get('background_image', None),
                            with_fade=False)

    def _display_text_for(self, card):
        """Display the main text of a card.

        :param card: The active card
        """
        text = card.get('text', None)
        text_color = card.get('text_color', 0x0)  # default to black
        if text:
            try:
                text_color = int(
                    text_color)  # parse the JSON string to hex int
            except ValueError:
                text_color = 0x0
            self.set_text(text, text_color)

    def _play_sound_for(self, card):
        """If there's a sound, start playing it.

        :param card: The active card
        """
        sound = card.get('sound', None)
        loop = card.get('sound_repeat', False)
        if sound:
            loop = loop == "True"
            print("Loop:", loop)
            self.play_sound(sound, wait_to_finish=False, loop=loop)

    def _wait_for_press(self, card):
        """Wait for a button to be pressed.

        :param card: The active card

        Return the id of the destination card.
        """
        button01_text = card.get('button01_text', None)
        button02_text = card.get('button02_text', None)
        point_touched = None
        while True:
            if self.touchscreen is not None:
                point_touched = self.touchscreen.touch_point
            else:
                self.cursor.update()
                if self.cursor.is_clicked is True:
                    point_touched = self.mouse_cursor.x, self.mouse_cursor.y
            if point_touched is not None:
                print("touch: ", point_touched)
                if button01_text and not button02_text:
                    # showing only middle button
                    if self._middle_button.contains(point_touched):
                        print("Middle button")
                        return card.get('button01_goto_card_id', None)
                if button01_text and button02_text:
                    if self._left_button.contains(point_touched):
                        print("Left button")
                        return card.get('button01_goto_card_id', None)
                    if self._right_button.contains(point_touched):
                        print("Right button")
                        return card.get('button02_goto_card_id', None)
            time.sleep(0.1)

    def display_card(self, card_num):
        """Display and handle input on a card.

        :param card_num: the index of the card to process
        """
        card = self._game[card_num]
        print(card)
        print("*" * 32)
        print('****{:^24s}****'.format(card['card_id']))
        print("*" * 32)

        self._fade_to_black()
        self._display_buttons(card)
        self._display_background_for(card)
        self.backlight_fade(1.0)
        self._display_text_for(card)

        board.DISPLAY.refresh_soon()
        board.DISPLAY.wait_for_frame()

        self._play_sound_for(card)

        auto_adv = card.get('auto_advance', None)
        if auto_adv is not None:
            auto_adv = float(auto_adv)
            print("Auto advancing after %0.1f seconds" % auto_adv)
            time.sleep(auto_adv)
            return card_num + 1

        destination_card_id = self._wait_for_press(card)

        self.play_sound(None)  # stop playing any sounds
        for card_number, card_struct in enumerate(self._game):
            if card_struct.get('card_id', None) == destination_card_id:
                return card_number  # found the matching card!
        # eep if we got here something went wrong
        raise RuntimeError("Could not find card with matching 'card_id': ",
                           destination_card_id)

    def play_sound(self, filename, *, wait_to_finish=True, loop=False):
        """Play a sound

        :param filename: The filename of the sound to play
        :param wait_to_finish: Whether playing the sound should block
        :param loop: Whether the sound should loop
        """
        self._speaker_enable.value = False
        self.audio.stop()
        if self._wavfile:
            self._wavfile.close()
            self._wavfile = None

        if not filename:
            return  # nothing more to do, just stopped
        filename = self._gamedirectory + "/" + filename
        print("Playing sound", filename)
        board.DISPLAY.wait_for_frame()
        try:
            self._wavfile = open(filename, "rb")
        except OSError:
            raise OSError("Could not locate sound file", filename)

        wavedata = audioio.WaveFile(self._wavfile)
        self._speaker_enable.value = True
        self.audio.play(wavedata, loop=loop)
        if loop or not wait_to_finish:
            return
        while self.audio.playing:
            pass
        self._wavfile.close()
        self._wavfile = None
        self._speaker_enable.value = False

    def set_text(self, text, color):
        """Display the test for a card.

        :param text: the text to display
        :param color: the text color

        """
        if self._text_group:
            self._text_group.pop()
        if not text or not color:
            return  # nothing to do!
        text_wrap = 37
        if board.DISPLAY.height < 130:
            text_wrap = 25
        text = self.wrap_nicely(text, text_wrap)
        text = '\n'.join(text)
        print("Set text to", text, "with color", hex(color))
        text_x = 8
        text_y = 95
        if board.DISPLAY.height < 130:
            text_y = 38
            text_x = 3
        if text:
            self._text = Label(self._text_font, text=str(text))
            self._text.x = text_x
            self._text.y = text_y
            self._text.color = color
            self._text_group.append(self._text)

    def set_background(self, filename, *, with_fade=True):
        """The background image to a bitmap file.

        :param filename: The filename of the chosen background
        """
        print("Set background to", filename)
        if with_fade:
            self.backlight_fade(0)
        if self._background_group:
            self._background_group.pop()

        if filename:
            if self._background_file:
                self._background_file.close()
            self._background_file = open(self._gamedirectory + "/" + filename,
                                         "rb")
            background = displayio.OnDiskBitmap(self._background_file)
            self._background_sprite = displayio.TileGrid(
                background, pixel_shader=displayio.ColorConverter(), x=0, y=0)
            self._background_group.append(self._background_sprite)
        if with_fade:
            board.DISPLAY.refresh_soon()
            board.DISPLAY.wait_for_frame()
            self.backlight_fade(1.0)

    def backlight_fade(self, to_light):
        """Adjust the TFT backlight. Fade from one value to another
        """
        from_light = board.DISPLAY.brightness
        from_light = int(from_light * 100)
        to_light = max(0, min(1.0, to_light))
        to_light = int(to_light * 100)
        delta = 1
        if from_light > to_light:
            delta = -1
        for val in range(from_light, to_light, delta):
            board.DISPLAY.brightness = val / 100
            time.sleep(0.003)
        board.DISPLAY.brightness = to_light / 100

    # return a list of lines with wordwrapping
    #pylint: disable=invalid-name
    @staticmethod
    def wrap_nicely(string, max_chars):
        """A helper that will return a list of lines with word-break wrapping.

        :param str string: The text to be wrapped.
        :param int max_chars: The maximum number of characters on a line before wrapping.

        """
        #string = string.replace('\n', '').replace('\r', '') # strip confusing newlines
        words = string.split(' ')
        the_lines = []
        the_line = ""
        for w in words:
            if '\n' in w:
                w1, w2 = w.split('\n')
                the_line += ' ' + w1
                the_lines.append(the_line)
                the_line = w2
            elif len(the_line + ' ' + w) > max_chars:
                the_lines.append(the_line)
                the_line = '' + w
            else:
                the_line += ' ' + w
        if the_line:  # last line remaining
            the_lines.append(the_line)
        # remove first space from first line:
        the_lines[0] = the_lines[0][1:]
        return the_lines
Exemple #15
0
import board
from time import sleep
from digitalio import DigitalInOut, Pull, Direction
import neopixel
from random import randint, choice
import audioio
from microcontroller import reset

btn_a = DigitalInOut(board.BUTTON_A)
btn_a.switch_to_input(pull=Pull.DOWN)
btn_b = DigitalInOut(board.BUTTON_B)
btn_b.switch_to_input(pull=Pull.DOWN)

spkrenable = DigitalInOut(board.SPEAKER_ENABLE)
spkrenable.switch_to_output()
spkrenable.value = True

np = neopixel.NeoPixel(board.A1, 64, brightness=.3, auto_write=0)

PICTURES = [
    'main_screen_1.txt', '3.txt', '2.txt', '1.txt', 'go.txt', 'game1.txt',
    'heart.txt', 'end.txt', 'a.txt', 'main_screen_2.txt'
]

R, G, B, Y, M, W, T, O, BL  = (64, 0, 0), (0, 32, 0), (0, 0, 32), (32, 32, 0), (32, 0, 32),\
(20, 20, 20), (0, 32, 32), (42, 10, 0), (0, 0, 0)
COLORS = [R, G, B, Y, M, W, T, O]
FLOOR = [56, 57, 58, 59, 60, 61, 62, 63]
PAD_pos, DIG = 58, 51

color = choice(COLORS)
    board.NEOPIXEL, 1, brightness=0.2
)  # Uncomment for Most Boards
"""Uncomment below for ItsyBitsy M4"""
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# Uncomment below for an externally defined RGB LED
# import adafruit_rgbled
# from adafruit_esp32spi import PWMOut
# RED_LED = PWMOut.PWMOut(esp, 26)
# GREEN_LED = PWMOut.PWMOut(esp, 27)
# BLUE_LED = PWMOut.PWMOut(esp, 25)
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)

# Set up a pin for controlling the relay
power_pin = DigitalInOut(board.D3)
power_pin.switch_to_output()

### Feeds ###
# Set up a feed named Relay for subscribing to the relay feed on Adafruit IO
feed_relay = secrets["aio_username"] + "/feeds/relay"

### Code ###

# Define callback methods which are called when events occur
# pylint: disable=unused-argument, redefined-outer-name
def connected(client, userdata, flags, rc):
    # This function will be called when the client is connected
    # successfully to the broker.
    print("Connected to Adafruit IO!")

Exemple #17
0
class PyPortal:
    """Class representing the Adafruit PyPortal.

    :param url: The URL of your data source. Defaults to ``None``.
    :param headers: The headers for authentication, typically used by Azure API's.
    :param json_path: The list of json traversal to get data out of. Can be list of lists for
                      multiple data points. Defaults to ``None`` to not use json.
    :param regexp_path: The list of regexp strings to get data out (use a single regexp group). Can
                        be list of regexps for multiple data points. Defaults to ``None`` to not
                        use regexp.
    :param default_bg: The path to your default background image file or a hex color.
                       Defaults to 0x000000.
    :param status_neopixel: The pin for the status NeoPixel. Use ``board.NEOPIXEL`` for the on-board
                            NeoPixel. Defaults to ``None``, no status LED
    :param str text_font: The path to your font file for your data text display.
    :param text_position: The position of your extracted text on the display in an (x, y) tuple.
                          Can be a list of tuples for when there's a list of json_paths, for example
    :param text_color: The color of the text, in 0xRRGGBB format. Can be a list of colors for when
                       there's multiple texts. Defaults to ``None``.
    :param text_wrap: Whether or not to wrap text (for long text data chunks). Defaults to
                      ``False``, no wrapping.
    :param text_maxlen: The max length of the text for text wrapping. Defaults to 0.
    :param text_transform: A function that will be called on the text before display
    :param json_transform: A function or a list of functions to call with the parsed JSON.
                           Changes and additions are permitted for the ``dict`` object.
    :param image_json_path: The JSON traversal path for a background image to display. Defaults to
                            ``None``.
    :param image_resize: What size to resize the image we got from the json_path, make this a tuple
                         of the width and height you want. Defaults to ``None``.
    :param image_position: The position of the image on the display as an (x, y) tuple. Defaults to
                           ``None``.
    :param success_callback: A function we'll call if you like, when we fetch data successfully.
                             Defaults to ``None``.
    :param str caption_text: The text of your caption, a fixed text not changed by the data we get.
                             Defaults to ``None``.
    :param str caption_font: The path to the font file for your caption. Defaults to ``None``.
    :param caption_position: The position of your caption on the display as an (x, y) tuple.
                             Defaults to ``None``.
    :param caption_color: The color of your caption. Must be a hex value, e.g. ``0x808000``.
    :param image_url_path: The HTTP traversal path for a background image to display.
                             Defaults to ``None``.
    :param esp: A passed ESP32 object, Can be used in cases where the ESP32 chip needs to be used
                             before calling the pyportal class. Defaults to ``None``.
    :param busio.SPI external_spi: A previously declared spi object. Defaults to ``None``.
    :param debug: Turn on debug print outs. Defaults to False.

    """

    # pylint: disable=too-many-instance-attributes, too-many-locals, too-many-branches, too-many-statements
    def __init__(self,
                 *,
                 url=None,
                 headers=None,
                 json_path=None,
                 regexp_path=None,
                 default_bg=0x000000,
                 status_neopixel=None,
                 text_font=None,
                 text_position=None,
                 text_color=0x808080,
                 text_wrap=False,
                 text_maxlen=0,
                 text_transform=None,
                 json_transform=None,
                 image_json_path=None,
                 image_resize=None,
                 image_position=None,
                 caption_text=None,
                 caption_font=None,
                 caption_position=None,
                 caption_color=0x808080,
                 image_url_path=None,
                 success_callback=None,
                 esp=None,
                 external_spi=None,
                 debug=False):

        self._debug = debug

        try:
            if hasattr(board, 'TFT_BACKLIGHT'):
                self._backlight = pulseio.PWMOut(board.TFT_BACKLIGHT)  # pylint: disable=no-member
            elif hasattr(board, 'TFT_LITE'):
                self._backlight = pulseio.PWMOut(board.TFT_LITE)  # pylint: disable=no-member
        except ValueError:
            self._backlight = None
        self.set_backlight(1.0)  # turn on backlight

        self._url = url
        self._headers = headers
        if json_path:
            if isinstance(json_path[0], (list, tuple)):
                self._json_path = json_path
            else:
                self._json_path = (json_path, )
        else:
            self._json_path = None

        self._regexp_path = regexp_path
        self._success_callback = success_callback

        if status_neopixel:
            self.neopix = neopixel.NeoPixel(status_neopixel, 1, brightness=0.2)
        else:
            self.neopix = None
        self.neo_status(0)

        try:
            os.stat(LOCALFILE)
            self._uselocal = True
        except OSError:
            self._uselocal = False

        if self._debug:
            print("Init display")
        self.splash = displayio.Group(max_size=15)

        if self._debug:
            print("Init background")
        self._bg_group = displayio.Group(max_size=1)
        self._bg_file = None
        self._default_bg = default_bg
        self.splash.append(self._bg_group)

        # show thank you and bootup file if available
        for bootscreen in ("/thankyou.bmp", "/pyportal_startup.bmp"):
            try:
                os.stat(bootscreen)
                board.DISPLAY.show(self.splash)
                for i in range(100, -1, -1):  # dim down
                    self.set_backlight(i / 100)
                    time.sleep(0.005)
                self.set_background(bootscreen)
                board.DISPLAY.wait_for_frame()
                for i in range(100):  # dim up
                    self.set_backlight(i / 100)
                    time.sleep(0.005)
                time.sleep(2)
            except OSError:
                pass  # they removed it, skip!

        self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
        self._speaker_enable.switch_to_output(False)
        if hasattr(board, 'AUDIO_OUT'):
            self.audio = audioio.AudioOut(board.AUDIO_OUT)
        elif hasattr(board, 'SPEAKER'):
            self.audio = audioio.AudioOut(board.SPEAKER)
        else:
            raise AttributeError('Board does not have a builtin speaker!')
        try:
            self.play_file("pyportal_startup.wav")
        except OSError:
            pass  # they deleted the file, no biggie!

        if esp:  # If there was a passed ESP Object
            if self._debug:
                print("Passed ESP32 to PyPortal")
            self._esp = esp
            if external_spi:  #If SPI Object Passed
                spi = external_spi
            else:  # Else: Make ESP32 connection
                spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
        else:
            if self._debug:
                print("Init ESP32")
            esp32_ready = DigitalInOut(board.ESP_BUSY)
            esp32_gpio0 = DigitalInOut(board.ESP_GPIO0)
            esp32_reset = DigitalInOut(board.ESP_RESET)
            esp32_cs = DigitalInOut(board.ESP_CS)
            spi = busio.SPI(board.SCK, board.MOSI, board.MISO)

            self._esp = adafruit_esp32spi.ESP_SPIcontrol(
                spi, esp32_cs, esp32_ready, esp32_reset, esp32_gpio0)
        #self._esp._debug = 1
        for _ in range(3):  # retries
            try:
                print("ESP firmware:", self._esp.firmware_version)
                break
            except RuntimeError:
                print("Retrying ESP32 connection")
                time.sleep(1)
                self._esp.reset()
        else:
            raise RuntimeError("Was not able to find ESP32")
        requests.set_socket(socket, self._esp)

        if url and not self._uselocal:
            self._connect_esp()

        if self._debug:
            print("My IP address is",
                  self._esp.pretty_ip(self._esp.ip_address))

        # set the default background
        self.set_background(self._default_bg)
        board.DISPLAY.show(self.splash)

        if self._debug:
            print("Init SD Card")
        sd_cs = DigitalInOut(board.SD_CS)
        self._sdcard = None
        try:
            self._sdcard = adafruit_sdcard.SDCard(spi, sd_cs)
            vfs = storage.VfsFat(self._sdcard)
            storage.mount(vfs, "/sd")
        except OSError as error:
            print("No SD card found:", error)

        self._qr_group = None
        # Tracks whether we've hidden the background when we showed the QR code.
        self._qr_only = False

        if self._debug:
            print("Init caption")
        self._caption = None
        if caption_font:
            self._caption_font = bitmap_font.load_font(caption_font)
        self.set_caption(caption_text, caption_position, caption_color)

        if text_font:
            if isinstance(text_position[0], (list, tuple)):
                num = len(text_position)
                if not text_wrap:
                    text_wrap = [0] * num
                if not text_maxlen:
                    text_maxlen = [0] * num
                if not text_transform:
                    text_transform = [None] * num
            else:
                num = 1
                text_position = (text_position, )
                text_color = (text_color, )
                text_wrap = (text_wrap, )
                text_maxlen = (text_maxlen, )
                text_transform = (text_transform, )
            self._text = [None] * num
            self._text_color = [None] * num
            self._text_position = [None] * num
            self._text_wrap = [None] * num
            self._text_maxlen = [None] * num
            self._text_transform = [None] * num
            self._text_font = bitmap_font.load_font(text_font)
            if self._debug:
                print("Loading font glyphs")
            # self._text_font.load_glyphs(b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
            #                             b'0123456789:/-_,. ')
            gc.collect()

            for i in range(num):
                if self._debug:
                    print("Init text area", i)
                self._text[i] = None
                self._text_color[i] = text_color[i]
                self._text_position[i] = text_position[i]
                self._text_wrap[i] = text_wrap[i]
                self._text_maxlen[i] = text_maxlen[i]
                self._text_transform[i] = text_transform[i]
        else:
            self._text_font = None
            self._text = None

        # Add any JSON translators
        self._json_transform = []
        if json_transform:
            if callable(json_transform):
                self._json_transform.append(json_transform)
            else:
                self._json_transform.extend(filter(callable, json_transform))

        self._image_json_path = image_json_path
        self._image_url_path = image_url_path
        self._image_resize = image_resize
        self._image_position = image_position
        if image_json_path or image_url_path:
            if self._debug:
                print("Init image path")
            if not self._image_position:
                self._image_position = (0, 0)  # default to top corner
            if not self._image_resize:
                self._image_resize = (320, 240)  # default to full screen
        if hasattr(board, 'TOUCH_XL'):
            if self._debug:
                print("Init touchscreen")
            # pylint: disable=no-member
            self.touchscreen = adafruit_touchscreen.Touchscreen(
                board.TOUCH_XL,
                board.TOUCH_XR,
                board.TOUCH_YD,
                board.TOUCH_YU,
                calibration=((5200, 59000), (5800, 57000)),
                size=(320, 240))
            # pylint: enable=no-member

            self.set_backlight(1.0)  # turn on backlight
        elif hasattr(board, 'BUTTON_CLOCK'):
            if self._debug:
                print("Init cursor")
            self.mouse_cursor = Cursor(board.DISPLAY,
                                       display_group=self.splash,
                                       cursor_speed=8)
            self.mouse_cursor.hide()
            self.cursor = CursorManager(self.mouse_cursor)
        else:
            raise AttributeError(
                'PyPortal module requires either a touchscreen or gamepad.')

        gc.collect()

    def set_headers(self, headers):
        """Set the headers used by fetch().

        :param headers: The new header dictionary
        """
        self._headers = headers

    def set_background(self, file_or_color, position=None):
        """The background image to a bitmap file.

        :param file_or_color: The filename of the chosen background image, or a hex color.

        """
        print("Set background to ", file_or_color)
        while self._bg_group:
            self._bg_group.pop()

        if not position:
            position = (0, 0)  # default in top corner

        if not file_or_color:
            return  # we're done, no background desired
        if self._bg_file:
            self._bg_file.close()
        if isinstance(file_or_color, str):  # its a filenme:
            self._bg_file = open(file_or_color, "rb")
            background = displayio.OnDiskBitmap(self._bg_file)
            try:
                self._bg_sprite = displayio.TileGrid(
                    background,
                    pixel_shader=displayio.ColorConverter(),
                    position=position)
            except TypeError:
                self._bg_sprite = displayio.TileGrid(
                    background,
                    pixel_shader=displayio.ColorConverter(),
                    x=position[0],
                    y=position[1])
        elif isinstance(file_or_color, int):
            # Make a background color fill
            color_bitmap = displayio.Bitmap(320, 240, 1)
            color_palette = displayio.Palette(1)
            color_palette[0] = file_or_color
            try:
                self._bg_sprite = displayio.TileGrid(
                    color_bitmap, pixel_shader=color_palette, position=(0, 0))
            except TypeError:
                self._bg_sprite = displayio.TileGrid(
                    color_bitmap,
                    pixel_shader=color_palette,
                    x=position[0],
                    y=position[1])
        else:
            raise RuntimeError("Unknown type of background")
        self._bg_group.append(self._bg_sprite)
        board.DISPLAY.refresh_soon()
        gc.collect()
        board.DISPLAY.wait_for_frame()

    def set_backlight(self, val):
        """Adjust the TFT backlight.

        :param val: The backlight brightness. Use a value between ``0`` and ``1``, where ``0`` is
                    off, and ``1`` is 100% brightness.

        """
        val = max(0, min(1.0, val))
        if self._backlight:
            self._backlight.duty_cycle = int(val * 65535)
        else:
            board.DISPLAY.auto_brightness = False
            board.DISPLAY.brightness = val

    def preload_font(self, glyphs=None):
        # pylint: disable=line-too-long
        """Preload font.

        :param glyphs: The font glyphs to load. Defaults to ``None``, uses alphanumeric glyphs if
                       None.

        """
        # pylint: enable=line-too-long
        if not glyphs:
            glyphs = b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-!,. "\'?!'
        print("Preloading font glyphs:", glyphs)
        if self._text_font:
            self._text_font.load_glyphs(glyphs)

    def set_caption(self, caption_text, caption_position, caption_color):
        # pylint: disable=line-too-long
        """A caption. Requires setting ``caption_font`` in init!

        :param caption_text: The text of the caption.
        :param caption_position: The position of the caption text.
        :param caption_color: The color of your caption text. Must be a hex value, e.g.
                              ``0x808000``.

        """
        # pylint: enable=line-too-long
        if self._debug:
            print("Setting caption to", caption_text)

        if (not caption_text) or (not self._caption_font) or (
                not caption_position):
            return  # nothing to do!

        if self._caption:
            self._caption._update_text(str(caption_text))  # pylint: disable=protected-access
            board.DISPLAY.refresh_soon()
            board.DISPLAY.wait_for_frame()
            return

        self._caption = Label(self._caption_font, text=str(caption_text))
        self._caption.x = caption_position[0]
        self._caption.y = caption_position[1]
        self._caption.color = caption_color
        self.splash.append(self._caption)

    def set_text(self, val, index=0):
        """Display text, with indexing into our list of text boxes.

        :param str val: The text to be displayed
        :param index: Defaults to 0.

        """
        if self._text_font:
            string = str(val)
            if self._text_maxlen[index]:
                string = string[:self._text_maxlen[index]]
            if self._text[index]:
                # print("Replacing text area with :", string)
                # self._text[index].text = string
                # return
                try:
                    text_index = self.splash.index(self._text[index])
                except AttributeError:
                    for i in range(len(self.splash)):
                        if self.splash[i] == self._text[index]:
                            text_index = i
                            break

                self._text[index] = Label(self._text_font, text=string)
                self._text[index].color = self._text_color[index]
                self._text[index].x = self._text_position[index][0]
                self._text[index].y = self._text_position[index][1]
                self.splash[text_index] = self._text[index]
                return

            if self._text_position[index]:  # if we want it placed somewhere...
                print("Making text area with string:", string)
                self._text[index] = Label(self._text_font, text=string)
                self._text[index].color = self._text_color[index]
                self._text[index].x = self._text_position[index][0]
                self._text[index].y = self._text_position[index][1]
                self.splash.append(self._text[index])

    def neo_status(self, value):
        """The status NeoPixel.

        :param value: The color to change the NeoPixel.

        """
        if self.neopix:
            self.neopix.fill(value)

    def play_file(self, file_name, wait_to_finish=True):
        """Play a wav file.

        :param str file_name: The name of the wav file to play on the speaker.

        """
        wavfile = open(file_name, "rb")
        wavedata = audioio.WaveFile(wavfile)
        self._speaker_enable.value = True
        self.audio.play(wavedata)
        if not wait_to_finish:
            return
        while self.audio.playing:
            pass
        wavfile.close()
        self._speaker_enable.value = False

    @staticmethod
    def _json_traverse(json, path):
        value = json
        for x in path:
            value = value[x]
            gc.collect()
        return value

    def get_local_time(self, location=None):
        # pylint: disable=line-too-long
        """Fetch and "set" the local time of this microcontroller to the local time at the location, using an internet time API.

        :param str location: Your city and country, e.g. ``"New York, US"``.

        """
        # pylint: enable=line-too-long
        self._connect_esp()
        api_url = None
        try:
            aio_username = secrets['aio_username']
            aio_key = secrets['aio_key']
        except KeyError:
            raise KeyError("\n\nOur time service requires a login/password to rate-limit. Please register for a free adafruit.io account and place the user/key in your secrets file under 'aio_username' and 'aio_key'")  # pylint: disable=line-too-long

        location = secrets.get('timezone', location)
        if location:
            print("Getting time for timezone", location)
            api_url = (TIME_SERVICE + "&tz=%s") % (aio_username, aio_key,
                                                   location)
        else:  # we'll try to figure it out from the IP address
            print("Getting time from IP address")
            api_url = TIME_SERVICE % (aio_username, aio_key)
        api_url += TIME_SERVICE_STRFTIME
        try:
            response = requests.get(api_url)
            if self._debug:
                print("Time request: ", api_url)
                print("Time reply: ", response.text)
            times = response.text.split(' ')
            the_date = times[0]
            the_time = times[1]
            year_day = int(times[2])
            week_day = int(times[3])
            is_dst = None  # no way to know yet
        except KeyError:
            raise KeyError("Was unable to lookup the time, try setting secrets['timezone'] according to http://worldtimeapi.org/timezones")  # pylint: disable=line-too-long
        year, month, mday = [int(x) for x in the_date.split('-')]
        the_time = the_time.split('.')[0]
        hours, minutes, seconds = [int(x) for x in the_time.split(':')]
        now = time.struct_time((year, month, mday, hours, minutes, seconds,
                                week_day, year_day, is_dst))
        print(now)
        rtc.RTC().datetime = now

        # now clean up
        response.close()
        response = None
        gc.collect()

    def wget(self, url, filename, *, chunk_size=12000):
        """Download a url and save to filename location, like the command wget.

        :param url: The URL from which to obtain the data.
        :param filename: The name of the file to save the data to.
        :param chunk_size: how much data to read/write at a time.

        """
        print("Fetching stream from", url)

        self.neo_status((100, 100, 0))
        r = requests.get(url, stream=True)

        if self._debug:
            print(r.headers)
        content_length = int(r.headers['content-length'])
        remaining = content_length
        print("Saving data to ", filename)
        stamp = time.monotonic()
        file = open(filename, "wb")
        for i in r.iter_content(min(remaining, chunk_size)):  # huge chunks!
            self.neo_status((0, 100, 100))
            remaining -= len(i)
            file.write(i)
            if self._debug:
                print("Read %d bytes, %d remaining" %
                      (content_length - remaining, remaining))
            else:
                print(".", end='')
            if not remaining:
                break
            self.neo_status((100, 100, 0))
        file.close()

        r.close()
        stamp = time.monotonic() - stamp
        print("Created file of %d bytes in %0.1f seconds" %
              (os.stat(filename)[6], stamp))
        self.neo_status((0, 0, 0))
        if not content_length == os.stat(filename)[6]:
            raise RuntimeError

    def _connect_esp(self):
        self.neo_status((0, 0, 100))
        while not self._esp.is_connected:
            # secrets dictionary must contain 'ssid' and 'password' at a minimum
            print("Connecting to AP", secrets['ssid'])
            if secrets['ssid'] == 'CHANGE ME' or secrets['ssid'] == 'CHANGE ME':
                change_me = "\n" + "*" * 45
                change_me += "\nPlease update the 'secrets.py' file on your\n"
                change_me += "CIRCUITPY drive to include your local WiFi\n"
                change_me += "access point SSID name in 'ssid' and SSID\n"
                change_me += "password in 'password'. Then save to reload!\n"
                change_me += "*" * 45
                raise OSError(change_me)
            self.neo_status((100, 0, 0))  # red = not connected
            try:
                self._esp.connect(secrets)
            except RuntimeError as error:
                print("Could not connect to internet", error)
                print("Retrying in 3 seconds...")
                time.sleep(3)

    @staticmethod
    def image_converter_url(image_url, width, height, color_depth=16):
        """Generate a converted image url from the url passed in,
           with the given width and height. aio_username and aio_key must be
           set in secrets."""
        try:
            aio_username = secrets['aio_username']
            aio_key = secrets['aio_key']
        except KeyError:
            raise KeyError("\n\nOur image converter service require a login/password to rate-limit. Please register for a free adafruit.io account and place the user/key in your secrets file under 'aio_username' and 'aio_key'")  # pylint: disable=line-too-long

        return IMAGE_CONVERTER_SERVICE % (aio_username, aio_key, width, height,
                                          color_depth, image_url)

    def push_to_io(self, feed_key, data):
        # pylint: disable=line-too-long
        """Push data to an adafruit.io feed

        :param str feed_key: Name of feed key to push data to.
        :param data: data to send to feed

        """
        # pylint: enable=line-too-long

        try:
            aio_username = secrets['aio_username']
            aio_key = secrets['aio_key']
        except KeyError:
            raise KeyError(
                "Adafruit IO secrets are kept in secrets.py, please add them there!\n\n"
            )

        wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(
            self._esp, secrets, None)
        io_client = IO_HTTP(aio_username, aio_key, wifi)

        while True:
            try:
                feed_id = io_client.get_feed(feed_key)
            except AdafruitIO_RequestError:
                # If no feed exists, create one
                feed_id = io_client.create_new_feed(feed_key)
            except RuntimeError as exception:
                print("An error occured, retrying! 1 -", exception)
                continue
            break

        while True:
            try:
                io_client.send_data(feed_id['key'], data)
            except RuntimeError as exception:
                print("An error occured, retrying! 2 -", exception)
                continue
            except NameError as exception:
                print(feed_id['key'], data, exception)
                continue
            break

    def fetch(self, refresh_url=None):
        """Fetch data from the url we initialized with, perfom any parsing,
        and display text or graphics. This function does pretty much everything
        Optionally update the URL
        """
        if refresh_url:
            self._url = refresh_url
        json_out = None
        image_url = None
        values = []

        gc.collect()
        if self._debug:
            print("Free mem: ", gc.mem_free())  # pylint: disable=no-member

        r = None
        if self._uselocal:
            print("*** USING LOCALFILE FOR DATA - NOT INTERNET!!! ***")
            r = Fake_Requests(LOCALFILE)

        if not r:
            self._connect_esp()
            # great, lets get the data
            print("Retrieving data...", end='')
            self.neo_status((100, 100, 0))  # yellow = fetching data
            gc.collect()
            r = requests.get(self._url, headers=self._headers)
            gc.collect()
            self.neo_status((0, 0, 100))  # green = got data
            print("Reply is OK!")

        if self._debug:
            print(r.text)

        if self._image_json_path or self._json_path:
            try:
                gc.collect()
                json_out = r.json()
                gc.collect()
            except ValueError:  # failed to parse?
                print("Couldn't parse json: ", r.text)
                raise
            except MemoryError:
                supervisor.reload()

        if self._regexp_path:
            import re

        if self._image_url_path:
            image_url = self._image_url_path

        # optional JSON post processing, apply any transformations
        # these MAY change/add element
        for idx, json_transform in enumerate(self._json_transform):
            try:
                json_transform(json_out)
            except Exception as error:
                print("Exception from json_transform: ", idx, error)
                raise

        # extract desired text/values from json
        if self._json_path:
            for path in self._json_path:
                try:
                    values.append(PyPortal._json_traverse(json_out, path))
                except KeyError:
                    print(json_out)
                    raise
        elif self._regexp_path:
            for regexp in self._regexp_path:
                values.append(re.search(regexp, r.text).group(1))
        else:
            values = r.text

        if self._image_json_path:
            try:
                image_url = PyPortal._json_traverse(json_out,
                                                    self._image_json_path)
            except KeyError as error:
                print("Error finding image data. '" + error.args[0] +
                      "' not found.")
                self.set_background(self._default_bg)

        # we're done with the requests object, lets delete it so we can do more!
        json_out = None
        r = None
        gc.collect()

        if image_url:
            try:
                print("original URL:", image_url)
                image_url = self.image_converter_url(image_url,
                                                     self._image_resize[0],
                                                     self._image_resize[1])
                print("convert URL:", image_url)
                # convert image to bitmap and cache
                #print("**not actually wgetting**")
                filename = "/cache.bmp"
                chunk_size = 12000  # default chunk size is 12K (for QSPI)
                if self._sdcard:
                    filename = "/sd" + filename
                    chunk_size = 512  # current bug in big SD writes -> stick to 1 block
                try:
                    self.wget(image_url, filename, chunk_size=chunk_size)
                except OSError as error:
                    print(error)
                    raise OSError("""\n\nNo writable filesystem found for saving datastream. Insert an SD card or set internal filesystem to be unsafe by setting 'disable_concurrent_write_protection' in the mount options in boot.py""")  # pylint: disable=line-too-long
                except RuntimeError as error:
                    print(error)
                    raise RuntimeError("wget didn't write a complete file")
                self.set_background(filename, self._image_position)
            except ValueError as error:
                print("Error displaying cached image. " + error.args[0])
                self.set_background(self._default_bg)
            finally:
                image_url = None
                gc.collect()

        # if we have a callback registered, call it now
        if self._success_callback:
            self._success_callback(values)

        # fill out all the text blocks
        if self._text:
            for i in range(len(self._text)):
                string = None
                if self._text_transform[i]:
                    func = self._text_transform[i]
                    string = func(values[i])
                else:
                    try:
                        string = "{:,d}".format(int(values[i]))
                    except (TypeError, ValueError):
                        string = values[i]  # ok its a string
                if self._debug:
                    print("Drawing text", string)
                if self._text_wrap[i]:
                    if self._debug:
                        print("Wrapping text")
                    lines = PyPortal.wrap_nicely(string, self._text_wrap[i])
                    string = '\n'.join(lines)
                self.set_text(string, index=i)
        if len(values) == 1:
            return values[0]
        return values

    def show_QR(self, qr_data, *, qr_size=1, x=0, y=0, hide_background=False):  # pylint: disable=invalid-name
        """Display a QR code on the TFT

        :param qr_data: The data for the QR code.
        :param int qr_size: The scale of the QR code.
        :param x: The x position of upper left corner of the QR code on the display.
        :param y: The y position of upper left corner of the QR code on the display.
        :param hide_background: Show the QR code on a black background if True.

        """
        import adafruit_miniqr
        # generate the QR code
        qrcode = adafruit_miniqr.QRCode()
        qrcode.add_data(qr_data)
        qrcode.make()

        # monochrome (2 color) palette
        palette = displayio.Palette(2)
        palette[0] = 0xFFFFFF
        palette[1] = 0x000000

        # pylint: disable=invalid-name
        # bitmap the size of the matrix, plus border, monochrome (2 colors)
        qr_bitmap = displayio.Bitmap(qrcode.matrix.width + 2,
                                     qrcode.matrix.height + 2, 2)
        for i in range(qr_bitmap.width * qr_bitmap.height):
            qr_bitmap[i] = 0

        # transcribe QR code into bitmap
        for xx in range(qrcode.matrix.width):
            for yy in range(qrcode.matrix.height):
                qr_bitmap[xx + 1, yy + 1] = 1 if qrcode.matrix[xx, yy] else 0

        # display the QR code
        qr_sprite = displayio.TileGrid(qr_bitmap, pixel_shader=palette)
        if self._qr_group:
            try:
                self._qr_group.pop()
            except IndexError:  # later test if empty
                pass
        else:
            self._qr_group = displayio.Group()
            self.splash.append(self._qr_group)
        self._qr_group.scale = qr_size
        self._qr_group.x = x
        self._qr_group.y = y
        self._qr_group.append(qr_sprite)
        if hide_background:
            board.DISPLAY.show(self._qr_group)
        self._qr_only = hide_background

    def hide_QR(self):  # pylint: disable=invalid-name
        """Clear any QR codes that are currently on the screen
        """

        if self._qr_only:
            board.DISPLAY.show(self.splash)
        else:
            try:
                self._qr_group.pop()
            except (IndexError, AttributeError):  # later test if empty
                pass

    # return a list of lines with wordwrapping
    @staticmethod
    def wrap_nicely(string, max_chars):
        """A helper that will return a list of lines with word-break wrapping.

        :param str string: The text to be wrapped.
        :param int max_chars: The maximum number of characters on a line before wrapping.

        """
        string = string.replace('\n',
                                '').replace('\r',
                                            '')  # strip confusing newlines
        words = string.split(' ')
        the_lines = []
        the_line = ""
        for w in words:
            if len(the_line + ' ' + w) <= max_chars:
                the_line += ' ' + w
            else:
                the_lines.append(the_line)
                the_line = '' + w
        if the_line:  # last line remaining
            the_lines.append(the_line)
        # remove first space from first line:
        the_lines[0] = the_lines[0][1:]
        return the_lines
Exemple #18
0
def get_status_led(npixels=None, *, brightness=None):
    """
	Can force the number of pixels used (to 1 for example on the pybadge LC)
	"""
    global pixels

    if npixels is None:
        npixels = get_npixels()

    if hasattr(board, "NEOPIXEL"):
        """
		For a board that has a neopixel (eg: QT PY M0)
		"""
        import neopixel
        pixels = neopixel.NeoPixel(board.NEOPIXEL, npixels)
    elif hasattr(board, "APA102_SCK"):
        """
		For a board that has a APA102 (eg: UnexpectedMaker Feather S2, Trinket M0)
		"""
        import adafruit_dotstar
        pixels = adafruit_dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI,
                                          npixels)
    elif hasattr(board, "DOTSTAR_CLOCK"):
        """
		For a board that has DOTSTAR pins (eg: FunHouse)
		"""
        import adafruit_dotstar
        pixels = adafruit_dotstar.DotStar(board.DOTSTAR_CLOCK,
                                          board.DOTSTAR_DATA, npixels)
    else:
        raise OSError("No hardware pixel identified")

    if hasattr(board, 'LDO2'):
        """
		Enable LDO2 on the Feather S2 so we can use the status LED
		"""
        from digitalio import DigitalInOut
        ldo2 = DigitalInOut(board.LDO2)
        ldo2.switch_to_output()
        ldo2.value = True
        keep_pins["ldo2"] = ldo2
        time.sleep(0.035)

    if hasattr(board, "NEOPIXEL_POWER"):
        """
		Some boards have a NEOPIXEL_POWER pin
		"""
        from digitalio import DigitalInOut
        neopower = DigitalInOut(board.NEOPIXEL_POWER)
        neopower.switch_to_output()
        keep_pins["neopower"] = neopower
        if "MagTag" in machine:
            # On MagTag, pull it down
            neopower.value = False
        elif "TinyS2" in machine:
            # On TinyS2, pull it up
            neopower.value = True
        else:
            # Assume up by default maybe ?
            neopower.value = True

    if brightness is not None:
        pixels.brightness = brightness

    return pixels
Exemple #19
0
# SPDX-FileCopyrightText: Copyright (c) 2020 Bryan Siepert for Adafruit Industries
#
# SPDX-License-Identifier: MIT
from time import sleep
import board
import busio
from digitalio import DigitalInOut
from adafruit_mcp2515.canio import Message, RemoteTransmissionRequest
from adafruit_mcp2515 import MCP2515 as CAN

cs = DigitalInOut(board.D5)
cs.switch_to_output()
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)

can_bus = CAN(spi, cs, loopback=True,
              silent=True)  # use loopback to test without another device
while True:
    with can_bus.listen(timeout=1.0) as listener:

        message = Message(id=0x1234ABCD, data=b"adafruit", extended=True)
        send_success = can_bus.send(message)
        print("Send success:", send_success)
        message_count = listener.in_waiting()
        print(message_count, "messages available")
        for _i in range(message_count):
            msg = listener.receive()
            print("Message from ", hex(msg.id))
            if isinstance(msg, Message):
                print("message data:", msg.data)
            if isinstance(msg, RemoteTransmissionRequest):
                print("RTR length:", msg.length)
Exemple #20
0
# Set black background
setBackground(0x000000)

updateBootProgress("Setting up Neopixel", 0)

# Setup neopixel on back of unit
statusNeopixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)

progressLabel._update_text(str("Setting up Audio")) 
board.DISPLAY.refresh_soon()
board.DISPLAY.wait_for_frame()

# Enable audio and built in speaker
_speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
_speaker_enable.switch_to_output(False)
audio = audioio.AudioOut(board.AUDIO_OUT)

updateBootProgress("Setting up WIFI", 1)

# Enable connection to esp32
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_gpio0 = DigitalInOut(board.ESP_GPIO0)
esp32_reset = DigitalInOut(board.ESP_RESET)
esp32_cs = DigitalInOut(board.ESP_CS)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)

esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready,
                                       esp32_reset, esp32_gpio0)
for _ in range(3):  # retries
    try:
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
Exemple #22
0
class PYOA_Graphics:
    # pylint: disable=too-many-instance-attributes
    """A choose your own adventure game framework."""
    def __init__(self) -> None:
        self.root_group = displayio.Group()
        self._display = board.DISPLAY
        self._background_group = displayio.Group()
        self.root_group.append(self._background_group)
        self._text_group = displayio.Group()
        self.root_group.append(self._text_group)
        self._button_group = displayio.Group()
        self.root_group.append(self._button_group)

        if self._display.height > 250:
            self._text_group.scale = 2
            self._button_group.scale = 2

        self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
        self._speaker_enable.switch_to_output(False)
        if hasattr(board, "AUDIO_OUT"):
            self.audio = audioio.AudioOut(board.AUDIO_OUT)
        elif hasattr(board, "SPEAKER"):
            self.audio = audioio.AudioOut(board.SPEAKER)
        else:
            raise AttributeError("Board does not have an audio output!")

        self._background_file = None
        self._wavfile = None

        self._display.auto_brightness = False
        self.backlight_fade(0)
        self._display.show(self.root_group)
        self.touchscreen = None
        self.mouse_cursor = None
        if hasattr(board, "TOUCH_XL"):
            self.touchscreen = adafruit_touchscreen.Touchscreen(
                board.TOUCH_XL,
                board.TOUCH_XR,
                board.TOUCH_YD,
                board.TOUCH_YU,
                calibration=((5200, 59000), (5800, 57000)),
                size=(self._display.width, self._display.height),
            )
        elif hasattr(board, "BUTTON_CLOCK"):
            self.mouse_cursor = Cursor(self._display,
                                       display_group=self.root_group,
                                       cursor_speed=8)
            self.cursor = CursorManager(self.mouse_cursor)
        else:
            raise AttributeError("PYOA requires a touchscreen or cursor.")
        self._gamedirectory = None
        self._gamefilename = None
        self._game = None
        self._text = None
        self._background_sprite = None
        self._text_font = None
        self._left_button = None
        self._right_button = None
        self._middle_button = None

    def load_game(self, game_directory: str) -> None:
        """Load a game.

        :param str game_directory: where the game files are stored
        """
        self._gamedirectory = game_directory
        self._text_font = terminalio.FONT
        # Possible Screen Sizes are:
        # 320x240 PyPortal and PyPortal Pynt
        # 160x128 PyBadge and PyGamer
        # 480x320 PyPortal Titano
        # 240x240 if we wanted to use HalloWing M4

        # Button Attributes
        btn_left = 10
        btn_right = btn_left + 180
        btn_mid = btn_left + 90
        button_y = 195
        button_width = 120
        button_height = 40
        if self._display.height < 200:
            button_y //= 2
            button_y += 10
            button_width //= 2
            button_height //= 2
            btn_right //= 2
            btn_mid //= 2
        elif self._display.height > 250:
            button_y = (button_y * 3) // 4
            button_y -= 20
            button_width = (button_width * 3) // 4
            button_height = (button_height * 3) // 4
            btn_right = (btn_right * 3) // 4
            btn_mid = (btn_right * 3) // 4
        self._left_button = Button(
            x=btn_left,
            y=button_y,
            width=button_width,
            height=button_height,
            label="Left",
            label_font=self._text_font,
            style=Button.SHADOWROUNDRECT,
        )
        self._right_button = Button(
            x=btn_right,
            y=button_y,
            width=button_width,
            height=button_height,
            label="Right",
            label_font=self._text_font,
            style=Button.SHADOWROUNDRECT,
        )
        self._middle_button = Button(
            x=btn_mid,
            y=button_y,
            width=button_width,
            height=button_height,
            label="Middle",
            label_font=self._text_font,
            style=Button.SHADOWROUNDRECT,
        )
        self._gamefilename = game_directory + "/cyoa.json"
        try:
            with open(  # pylint: disable=unspecified-encoding
                    self._gamefilename, "r") as game_file:
                self._game = json.load(game_file)
        except OSError as err:
            raise OSError("Could not open game file " +
                          self._gamefilename) from err

    def _fade_to_black(self) -> None:
        """Turn down the lights."""
        if self.mouse_cursor:
            self.mouse_cursor.is_hidden = True
        self.backlight_fade(0)
        # turn off background so we can render the text
        self.set_background(None, with_fade=False)
        self.set_text(None, None)
        for _ in range(len(self._button_group)):
            self._button_group.pop()
        if self.mouse_cursor:
            self.mouse_cursor.is_hidden = False

    def _display_buttons(self, card: Dict[str, str]) -> None:
        """Display the buttons of a card.

        :param card: The active card
        :type card: dict(str, str)
        """
        button01_text = card.get("button01_text", None)
        button02_text = card.get("button02_text", None)
        self._left_button.label = button01_text
        self._middle_button.label = button01_text
        self._right_button.label = button02_text
        if button01_text and not button02_text:
            # show only middle button
            self._button_group.append(self._middle_button)
        if button01_text and button02_text:
            self._button_group.append(self._right_button)
            self._button_group.append(self._left_button)

    def _display_background_for(self, card: Dict[str, str]) -> None:
        """If there's a background on card, display it.

        :param card: The active card
        :type card: dict(str, str)
        """
        self.set_background(card.get("background_image", None),
                            with_fade=False)

    def _display_text_for(self, card: Dict[str, str]) -> None:
        """Display the main text of a card.

        :param card: The active card
        :type card: dict(str, str)
        """
        text = card.get("text", None)
        text_color = card.get("text_color", 0x0)  # default to black
        text_background_color = card.get("text_background_color", None)
        if text:
            try:
                text_color = int(
                    text_color)  # parse the JSON string to hex int
            except ValueError:
                text_color = 0x0

            try:
                text_background_color = int(
                    text_background_color)  # parse the JSON string to hex int
            except ValueError:
                text_background_color = None
            except TypeError:
                text_background_color = None

            self.set_text(text,
                          text_color,
                          background_color=text_background_color)

    def _play_sound_for(self, card: Dict[str, str]) -> None:
        """If there's a sound, start playing it.

        :param card: The active card
        :type card: dict(str, str)
        """
        sound = card.get("sound", None)
        loop = card.get("sound_repeat", False)
        if sound:
            loop = loop == "True"
            print("Loop:", loop)
            self.play_sound(sound, wait_to_finish=False, loop=loop)

    def _wait_for_press(self, card: Dict[str, str]) -> str:
        """Wait for a button to be pressed.

        :param card: The active card
        :type card: dict(str, str)
        :return: The id of the destination card
        :rtype: str
        """
        button01_text = card.get("button01_text", None)
        button02_text = card.get("button02_text", None)
        point_touched = None
        while True:
            if self.touchscreen is not None:
                point_touched = self.touchscreen.touch_point
            else:
                self.cursor.update()
                if self.cursor.is_clicked is True:
                    point_touched = self.mouse_cursor.x, self.mouse_cursor.y
            if point_touched is not None:
                point_touched = (
                    point_touched[0] // self._button_group.scale,
                    point_touched[1] // self._button_group.scale,
                )
                print("touch: ", point_touched)
                if button01_text and not button02_text:
                    # showing only middle button
                    if self._middle_button.contains(point_touched):
                        print("Middle button")
                        return card.get("button01_goto_card_id", None)
                if button01_text and button02_text:
                    if self._left_button.contains(point_touched):
                        print("Left button")
                        return card.get("button01_goto_card_id", None)
                    if self._right_button.contains(point_touched):
                        print("Right button")
                        return card.get("button02_goto_card_id", None)
            time.sleep(0.1)

    def display_card(self, card_num: int) -> int:
        """Display and handle input on a card.

        :param int card_num: the index of the card to process
        :return: the card number of the selected card
        :rtype: int
        """
        card = self._game[card_num]
        print(card)
        print("*" * 32)
        print("****{:^24s}****".format(card["card_id"]))
        print("*" * 32)

        self._fade_to_black()
        self._display_buttons(card)
        self._display_background_for(card)
        self.backlight_fade(1.0)
        self._display_text_for(card)
        self._display.refresh(target_frames_per_second=60)

        self._play_sound_for(card)

        auto_adv = card.get("auto_advance", None)
        if auto_adv is not None:
            auto_adv = float(auto_adv)
            print("Auto advancing after %0.1f seconds" % auto_adv)
            time.sleep(auto_adv)
            return card_num + 1

        destination_card_id = self._wait_for_press(card)

        self.play_sound(None)  # stop playing any sounds
        for card_number, card_struct in enumerate(self._game):
            if card_struct.get("card_id", None) == destination_card_id:
                return card_number  # found the matching card!
        # eep if we got here something went wrong
        raise RuntimeError("Could not find card with matching 'card_id': ",
                           destination_card_id)

    def play_sound(self,
                   filename: Optional[str],
                   *,
                   wait_to_finish: bool = True,
                   loop: bool = False) -> None:
        """Play a sound

        :param filename: The filename of the sound to play. Use `None` to stop
            playing anything.
        :type filename: str or None
        :param bool wait_to_finish: Whether playing the sound should block
        :param bool loop: Whether the sound should loop
        """
        self._speaker_enable.value = False
        self.audio.stop()
        if self._wavfile:
            self._wavfile.close()
            self._wavfile = None

        if not filename:
            return  # nothing more to do, just stopped
        filename = self._gamedirectory + "/" + filename
        print("Playing sound", filename)
        self._display.refresh(target_frames_per_second=60)
        try:
            self._wavfile = open(filename, "rb")  # pylint: disable=consider-using-with
        except OSError as err:
            raise OSError("Could not locate sound file", filename) from err

        wavedata = audiocore.WaveFile(self._wavfile)
        self._speaker_enable.value = True
        self.audio.play(wavedata, loop=loop)
        if loop or not wait_to_finish:
            return
        while self.audio.playing:
            pass
        self._wavfile.close()
        self._wavfile = None
        self._speaker_enable.value = False

    def set_text(
        self,
        text: Optional[str],
        color: Optional[str],
        background_color: Optional[int] = None,
    ) -> None:
        """Display the test for a card.

        :param text: the text to display
        :type text: str or None
        :param color: the text color
        :type color: str or None
        :param background_color: the background color of the text
        :type background_color: int or None
        """
        if self._text_group:
            self._text_group.pop()
        if not text or not color:
            return  # nothing to do!
        text_wrap = 37
        if self._display.height < 130:
            text_wrap = 25
        text = self.wrap_nicely(text, text_wrap)
        text = "\n".join(text)
        print("Set text to", text, "with color", hex(color))
        text_x = 8
        text_y = 95
        if self._display.height < 130:
            text_x = 3
            text_y = 38
        elif self._display.height > 250:
            text_y = 50
        if text:
            self._text = Label(self._text_font, text=str(text))
            self._text.x = text_x
            self._text.y = text_y
            self._text.color = color
            if background_color:
                self._text.background_color = background_color
            self._text_group.append(self._text)

    def set_background(self,
                       filename: Optional[str],
                       *,
                       with_fade: bool = True) -> None:
        """The background image to a bitmap file.

        :param filename: The filename of the chosen background
        :type filename: str or None
        :param bool with_fade: If `True` fade out the backlight while loading the new background
            and fade in the backlight once completed.
        """
        print("Set background to", filename)
        if with_fade:
            self.backlight_fade(0)
        if self._background_group:
            self._background_group.pop()

        if filename:
            background = displayio.OnDiskBitmap(self._gamedirectory + "/" +
                                                filename)
            self._background_sprite = displayio.TileGrid(
                background,
                pixel_shader=background.pixel_shader,
            )
            self._background_group.append(self._background_sprite)
        if with_fade:
            self._display.refresh(target_frames_per_second=60)
            self.backlight_fade(1.0)

    def backlight_fade(self, to_light: float) -> None:
        """
        Adjust the TFT backlight. Fade from the current value to the ``to_light`` value

        :param float to_light: the desired backlight brightness between :py:const:`0.0` and
            :py:const:`1.0`.
        """
        from_light = self._display.brightness
        from_light = int(from_light * 100)
        to_light = max(0, min(100, int(to_light * 100)))
        delta = 1
        if from_light > to_light:
            delta = -1
        for val in range(from_light, to_light, delta):
            self._display.brightness = val / 100
            time.sleep(0.003)
        self._display.brightness = to_light / 100

    # return a list of lines with wordwrapping
    @staticmethod
    def wrap_nicely(string: str, max_chars: int) -> List[str]:
        """A helper that will return a list of lines with word-break wrapping.

        :param str string: The text to be wrapped.
        :param int max_chars: The maximum number of characters on a line before wrapping.
        :return: The list of lines
        :rtype: list(str)
        """
        # string = string.replace('\n', '').replace('\r', '') # strip confusing newlines
        words = string.split(" ")
        the_lines = []
        the_line = ""
        for w in words:
            if "\n" in w:
                _w1, _w2 = w.split("\n")
                the_line += " " + _w1
                the_lines.append(the_line)
                the_line = _w2
            elif len(the_line + " " + w) > max_chars:
                the_lines.append(the_line)
                the_line = "" + w
            else:
                the_line += " " + w
        if the_line:  # last line remaining
            the_lines.append(the_line)
        # remove first space from first line:
        the_lines[0] = the_lines[0][1:]
        return the_lines
Exemple #23
0
def bus():
    cs = DigitalInOut(CS_PIN)
    cs.switch_to_output()
    return CAN(SPI(), cs, loopback=True, silent=True)
Exemple #24
0
button_R = DigitalInOut(board.D23)
button_R.direction = Direction.INPUT

button_U = DigitalInOut(board.D17)
button_U.direction = Direction.INPUT

button_D = DigitalInOut(board.D22)
button_D.direction = Direction.INPUT

button_C = DigitalInOut(board.D4)
button_C.direction = Direction.INPUT

# Turn on the Backlight
backlight = DigitalInOut(board.D26)
backlight.switch_to_output()
backlight.value = True

# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for color.
width = disp.width
height = disp.height
image = Image.new("RGB", (width, height))

# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)

# Clear display.
draw.rectangle((0, 0, width, height), outline=0, fill=(255, 0, 0))
disp.image(image)
Exemple #25
0
class Peripherals:
    """Peripherals Helper Class for the PyPortal Library"""

    # pylint: disable=too-many-instance-attributes, too-many-locals, too-many-branches, too-many-statements
    def __init__(self, spi, display, splash_group, debug=False):
        # Speaker Enable
        self._speaker_enable = DigitalInOut(board.SPEAKER_ENABLE)
        self._speaker_enable.switch_to_output(False)

        self._display = display

        if hasattr(board, "AUDIO_OUT"):
            self.audio = audioio.AudioOut(board.AUDIO_OUT)
        elif hasattr(board, "SPEAKER"):
            self.audio = audioio.AudioOut(board.SPEAKER)
        else:
            raise AttributeError("Board does not have a builtin speaker!")

        if debug:
            print("Init SD Card")
        sd_cs = board.SD_CS
        if not NATIVE_SD:
            sd_cs = DigitalInOut(sd_cs)
        self._sdcard = None

        try:
            self._sdcard = sdcardio.SDCard(spi, sd_cs)
            vfs = storage.VfsFat(self._sdcard)
            storage.mount(vfs, "/sd")
        except OSError as error:
            print("No SD card found:", error)

        try:
            if hasattr(board, "TFT_BACKLIGHT"):
                self._backlight = pulseio.PWMOut(board.TFT_BACKLIGHT)  # pylint: disable=no-member
            elif hasattr(board, "TFT_LITE"):
                self._backlight = pulseio.PWMOut(board.TFT_LITE)  # pylint: disable=no-member
        except ValueError:
            self._backlight = None
        self.set_backlight(1.0)  # turn on backlight
        # pylint: disable=import-outside-toplevel
        if hasattr(board, "TOUCH_XL"):
            import adafruit_touchscreen

            if debug:
                print("Init touchscreen")
            # pylint: disable=no-member
            self.touchscreen = adafruit_touchscreen.Touchscreen(
                board.TOUCH_XL,
                board.TOUCH_XR,
                board.TOUCH_YD,
                board.TOUCH_YU,
                calibration=((5200, 59000), (5800, 57000)),
                size=(board.DISPLAY.width, board.DISPLAY.height),
            )
            # pylint: enable=no-member

            self.set_backlight(1.0)  # turn on backlight
        elif hasattr(board, "BUTTON_CLOCK"):
            from adafruit_cursorcontrol.cursorcontrol import Cursor
            from adafruit_cursorcontrol.cursorcontrol_cursormanager import CursorManager

            if debug:
                print("Init cursor")
            self.mouse_cursor = Cursor(board.DISPLAY,
                                       display_group=splash_group,
                                       cursor_speed=8)
            self.mouse_cursor.hide()
            self.cursor = CursorManager(self.mouse_cursor)
        else:
            raise AttributeError(
                "PyPortal module requires either a touchscreen or gamepad.")
        # pylint: enable=import-outside-toplevel

        gc.collect()

    def set_backlight(self, val):
        """Adjust the TFT backlight.

        :param val: The backlight brightness. Use a value between ``0`` and ``1``, where ``0`` is
                    off, and ``1`` is 100% brightness.

        """
        val = max(0, min(1.0, val))
        if self._backlight:
            self._backlight.duty_cycle = int(val * 65535)
        else:
            self._display.auto_brightness = False
            self._display.brightness = val

    def play_file(self, file_name, wait_to_finish=True):
        """Play a wav file.

        :param str file_name: The name of the wav file to play on the speaker.

        """
        wavfile = open(file_name, "rb")
        wavedata = audiocore.WaveFile(wavfile)
        self._speaker_enable.value = True
        self.audio.play(wavedata)
        if not wait_to_finish:
            return
        while self.audio.playing:
            pass
        wavfile.close()
        self._speaker_enable.value = False

    def sd_check(self):
        """Returns True if there is an SD card preset and False
        if there is no SD card. The _sdcard value is set in _init
        """
        if self._sdcard:
            return True
        return False

    @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
Exemple #26
0
RIGHT = board.D9
UP = board.D10
LEFT = board.D11
DOWN = board.D12
COMB = board.D13

#  mode slide switch pin
SWITCH = board.A1

# Rotary encoder setup
encoder = rotaryio.IncrementalEncoder(ENCA, ENCB)
last_position = 0

# setting the COMA and COMB pins to LOW aka GND
com_a = DigitalInOut(COMA)
com_a.switch_to_output()
com_a = False
com_b = DigitalInOut(COMB)
com_b.switch_to_output()
com_b = False

#  mode switch setup
SWITCH = DigitalInOut(board.A1)
SWITCH.direction = Direction.INPUT
SWITCH.pull = Pull.UP

#  encoder button pins
enc_buttons = (
    CENTER,
    UP,
    LEFT,
Exemple #27
0
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

# Raspberry Pi RP2040
esp32_cs = DigitalInOut(board.GP13)
esp32_ready = DigitalInOut(board.GP14)
esp32_reset = DigitalInOut(board.GP15)

spi = busio.SPI(board.GP10, board.GP11, board.GP12)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)

wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets)

# Configure the RP2040 Pico LED Pin as an output
led_pin = DigitalInOut(board.LED)
led_pin.switch_to_output()


# Define callback functions which will be called when certain events happen.
# pylint: disable=unused-argument
def connected(client):
    # Connected function will be called when the client is connected to Adafruit IO.
    print("Connected to Adafruit IO! ")


def subscribe(client, userdata, topic, granted_qos):
    # This method is called when the client subscribes to a new feed.
    print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos))


# pylint: disable=unused-argument
Exemple #28
0
class ESP32:
    """Class to manage ESP32 running NINA firmware for WiFi or Bluetooth."""

    NOT_IN_USE = 0
    """Not currently being used."""
    BOOTLOADER = 1
    """Put ESP32 into bootloader mode."""
    BLUETOOTH = 2
    """HCI Bluetooth mode."""
    WIFI = 3
    """WiFi mode."""
    _MODES = (NOT_IN_USE, BOOTLOADER, BLUETOOTH, WIFI)

    # pylint: disable=invalid-name
    def __init__(self,
                 *,
                 reset: Optional[Pin] = None,
                 reset_high: bool = False,
                 gpio0: Optional[Pin] = None,
                 busy: Optional[Pin] = None,
                 chip_select: Optional[Pin] = None,
                 tx: Optional[Pin] = None,
                 rx: Optional[Pin] = None,
                 spi: Optional[SPI] = None):
        """Create an ESP32 instance, passing the objects needed to reset and communicate
        with the adapter.

        :param reset ~microcontroller.Pin: ESP32 RESET pin.
           If `None`, use ``board.ESP_RESET``.
        :param reset_high bool: True if `reset` is brought high to reset;
            `False` if brought low.
        :param gpio0 ~microcontroller.Pin: ESP32 GPIO0 pin.
           Used for ESP32 boot selection when reset, and as RTS for UART communication.
           If `None`, use ``board.ESP_GPIO0``.
        :param busy ~microcontroller.Pin: ESP32 BUSY pin (sometimes called READY).
           Used as CTS indicator for UART communication.
           If `None`, use ``board.ESP_BUSY``.
        :param chip_select ~microcontroller.Pin: ESP32 CS (chip select) pin.
            Also used for ESP32 mode selection when reset.
            If `None`, use ``board.ESP_CS``.
        :param tx ~microcontroller.Pin: ESP32 TX pin for Bluetooth UART communication.
           If `None`, use ``board.ESP_TX`` when in Bluetooth mode.
        :param rx ~microcontroller.Pin: ESP32 RX pin for Bluetooth UART communication.
           If `None`, use ``board.ESP_RX`` when in Bluetooth mode.
        :param spi busio.SPI: Used for communication with the ESP32.
          If not supplied, ``board.SPI()`` is used when in WiFi mode.
        """
        self._mode = ESP32.NOT_IN_USE

        # We can't use board.ESP_RESET, etc. as defaults, because they may not exist.
        self._reset = DigitalInOut(reset or board.ESP_RESET)
        # Turn off ESP32 by holding reset line
        self._reset.switch_to_output(reset_high)
        self._reset_high = reset_high

        # These will be set to input or input as necessary.
        self._gpio0_rts = DigitalInOut(gpio0 or board.ESP_GPIO0)
        self._busy_cts = DigitalInOut(busy or board.ESP_BUSY)
        self._chip_select = DigitalInOut(chip_select or board.ESP_CS)

        # Used for Bluetooth mode.
        self._tx = tx
        self._rx = rx
        self._uart = None
        self._bleio_adapter = None

        # Used for WiFi mode.
        self._spi = spi

    def reset(self, mode: int, debug: bool = False) -> None:
        """Do hard reset of the ESP32.

        :param mode: One of `ESP32.NOT_IN_USE`, `ESP32.BOOTLOADER`, `ESP32.BLUETOOTH`, `ESP32.WIFI`.
        """
        if mode not in ESP32._MODES:
            raise ValueError("Invalid mode")

        # GPIO0 high means boot from SPI flash.
        # Low means go into bootloader mode.
        self._gpio0_rts.switch_to_output(mode != ESP32.BOOTLOADER)

        if mode == ESP32.NOT_IN_USE:
            # Turn of ESP32 by holding reset line.
            self._reset.switch_to_output(self._reset_high)
            self._mode = mode
            return

        if mode == ESP32.BLUETOOTH:
            self._chip_select.switch_to_output(False)
        elif mode == ESP32.WIFI:
            self._chip_select.switch_to_output(True)

        # Initial mode. Changed if reset is successful.
        self._mode = ESP32.NOT_IN_USE

        # Reset by toggling reset pin for 100ms
        self._reset.switch_to_output(self._reset_high)
        time.sleep(0.1)
        self._reset.value = not self._reset_high

        #  Wait 1 second for startup.
        time.sleep(1.0)

        if mode == ESP32.BOOTLOADER:
            # No startup message expected.
            return

        startup_message = b""
        while self._uart.in_waiting:  # pylint: disable=no-member
            more = self._uart.read()
            if more:
                startup_message += more

        if not startup_message:
            raise RuntimeError("ESP32 did not respond with a startup message")
        if debug:
            try:
                print(startup_message.decode("utf-8"))
            except UnicodeError:
                raise RuntimeError(
                    "Garbled ESP32 startup message") from UnicodeError

        # Everything's fine. Remember mode.
        self._mode = mode

    # pylint: disable=invalid-name
    def start_bluetooth(self, debug: bool = False) -> Adapter:
        """Set up the ESP32 in HCI Bluetooth mode, if it is not already doing something else.

        :param debug bool: Print out some debugging information.
        :return: A `_bleio.Adapter`, to be passed to ``_bleio.set_adapter()``.
        """
        # Will fail with ImportError if _bleio is not on the board.
        # That exception is probably good enough.
        # pylint: disable=import-outside-toplevel
        import _bleio

        if self._mode == ESP32.BLUETOOTH:
            # Already started.
            return _bleio.adapter

        if self._mode == ESP32.WIFI:
            raise RuntimeError("ESP32 is in WiFi mode; use stop_wifi() first")

        # Choose Bluetooth mode.
        self._chip_select.switch_to_output(False)

        self._uart = busio.UART(
            self._tx or board.ESP_TX,
            self._rx or board.ESP_RX,
            baudrate=115200,
            timeout=0,
            receiver_buffer_size=512,
        )

        # Reset into Bluetooth mode.
        self.reset(ESP32.BLUETOOTH, debug=debug)

        self._busy_cts.switch_to_input()
        self._gpio0_rts.switch_to_output()
        # pylint: disable=no-member
        # pylint: disable=unexpected-keyword-arg
        self._bleio_adapter = _bleio.Adapter(uart=self._uart,
                                             rts=self._gpio0_rts,
                                             cts=self._busy_cts)
        self._bleio_adapter.enabled = True
        return self._bleio_adapter

    def stop_bluetooth(self):
        """Stop Bluetooth on the ESP32. Deinitialize the ~busio.UART used for communication"""
        if self._mode != ESP32.BLUETOOTH:
            return
        self._bleio_adapter.enabled = False
        self.reset(ESP32.NOT_IN_USE)
        self._uart.deinit()
        self._uart = None

    def start_wifi(self, debug: bool = False) -> SPI:
        """Start WiFi on the ESP32.

        :return: the ``busio.SPI`` object that will be used to communicate with the ESP32.
        :rtype: busio.SPI
        """
        if self._mode == ESP32.WIFI:
            # Already started.
            return self._spi

        if self._mode == ESP32.BLUETOOTH:
            raise RuntimeError(
                "ESP32 is in Bluetooth mode; use stop_bluetooth() first")

        self.reset(ESP32.WIFI, debug=debug)
        if self._spi is None:
            self._spi = board.SPI()
        return self._spi

    def stop_wifi(self):
        """Stop WiFi on the ESP32.
        The `busio.SPI` object used is not deinitialized, since it may be in use for other devices.
        """
        if self._mode != ESP32.WIFI:
            return
        self.reset(ESP32.NOT_IN_USE)
Exemple #29
0
def enable_speakers():
    speaker_control = DigitalInOut(board.SPEAKER_ENABLE)
    speaker_control.switch_to_output(value=True)
Exemple #30
0
from adafruit_ble_adafruit.light_sensor_service import LightSensorService
from adafruit_ble_adafruit.temperature_service import TemperatureService
from adafruit_ble_adafruit.tone_service import ToneService

accel_svc = AccelerometerService()
accel_svc.measurement_period = 100
accel_last_update = 0

# 3 RGB bytes * 10 pixels.
NEOPIXEL_BUF_LENGTH = 3 * 10
neopixel_svc = AddressablePixelService()
neopixel_buf = bytearray(NEOPIXEL_BUF_LENGTH)
# Take over NeoPixel control from cp.
cp._pixels.deinit()  # pylint: disable=protected-access
neopixel_out = DigitalInOut(board.NEOPIXEL)
neopixel_out.switch_to_output()

button_svc = ButtonService()
button_svc.set_pressed(cp.switch, cp.button_a, cp.button_b)

light_svc = LightSensorService()
light_svc.measurement_period = 100
light_last_update = 0

temp_svc = TemperatureService()
temp_svc.measurement_period = 100
temp_last_update = 0

tone_svc = ToneService()

ble = BLERadio()