summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_dotstar.py198
-rwxr-xr-xports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_pypixelbuf.py374
-rwxr-xr-xports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_requests.py311
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_feathers2/feathers2.py47
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.h5
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.mk4
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_feathers2/pins.c64
-rwxr-xr-xports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/adafruit_dotstar.py198
-rwxr-xr-xports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/adafruit_pypixelbuf.py374
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/board.c56
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/feathers2.py47
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.h40
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.mk21
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/pins.c56
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/sdkconfig35
-rwxr-xr-xports/esp32s2/boards/unexpectedmaker_pros2/adafruit_dotstar.py198
-rwxr-xr-xports/esp32s2/boards/unexpectedmaker_pros2/adafruit_pypixelbuf.py374
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_pros2/board.c56
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_pros2/mpconfigboard.h35
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_pros2/mpconfigboard.mk21
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_pros2/pins.c58
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_pros2/pros2.py63
-rw-r--r--ports/esp32s2/boards/unexpectedmaker_pros2/sdkconfig35
23 files changed, 2659 insertions, 11 deletions
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_dotstar.py b/ports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_dotstar.py
new file mode 100755
index 000000000..65fbbe868
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_dotstar.py
@@ -0,0 +1,198 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Damien P. George (original Neopixel object)
+# Copyright (c) 2017 Ladyada
+# Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
+# Copyright (c) 2019 Roy Hooper
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""
+`adafruit_dotstar` - DotStar strip driver (for CircuitPython 5.0+ with _pixelbuf)
+=================================================================================
+
+* Author(s): Damien P. George, Limor Fried, Scott Shawcroft & Roy Hooper
+"""
+
+# pylint: disable=ungrouped-imports
+import sys
+import busio
+import digitalio
+
+if sys.implementation.version[0] < 5:
+ import adafruit_pypixelbuf as _pixelbuf
+else:
+ try:
+ import _pixelbuf
+ except ImportError:
+ import adafruit_pypixelbuf as _pixelbuf
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DotStar.git"
+
+START_HEADER_SIZE = 4
+
+# Pixel color order constants
+RBG = "PRBG"
+"""Red Blue Green"""
+RGB = "PRGB"
+"""Red Green Blue"""
+GRB = "PGRB"
+"""Green Red Blue"""
+GBR = "PGBR"
+"""Green Blue Red"""
+BRG = "PBRG"
+"""Blue Red Green"""
+BGR = "PBGR"
+"""Blue Green Red"""
+
+
+class DotStar(_pixelbuf.PixelBuf):
+ """
+ A sequence of dotstars.
+
+ :param ~microcontroller.Pin clock: The pin to output dotstar clock on.
+ :param ~microcontroller.Pin data: The pin to output dotstar data on.
+ :param int n: The number of dotstars in the chain
+ :param float brightness: Brightness of the pixels between 0.0 and 1.0
+ :param bool auto_write: True if the dotstars should immediately change when
+ set. If False, `show` must be called explicitly.
+ :param str pixel_order: Set the pixel order on the strip - different
+ strips implement this differently. If you send red, and it looks blue
+ or green on the strip, modify this! It should be one of the values above.
+ :param int baudrate: Desired clock rate if using hardware SPI (ignored if
+ using 'soft' SPI). This is only a recommendation; the actual clock
+ rate may be slightly different depending on what the system hardware
+ can provide.
+
+ Example for Gemma M0:
+
+ .. code-block:: python
+
+ import adafruit_dotstar
+ import time
+ from board import *
+
+ RED = 0x100000
+
+ with adafruit_dotstar.DotStar(APA102_SCK, APA102_MOSI, 1) as pixels:
+ pixels[0] = RED
+ time.sleep(2)
+
+ .. py:method:: DotStar.show()
+
+ Shows the new colors on the dotstars themselves if they haven't already
+ been autowritten.
+
+ The colors may or may not be showing after this function returns because
+ it may be done asynchronously.
+
+ .. py:method:: DotStar.fill(color)
+
+ Colors all dotstars the given ***color***.
+
+ .. py:attribute:: brightness
+
+ Overall brightness of all dotstars (0 to 1.0)
+ """
+
+ def __init__(
+ self,
+ clock,
+ data,
+ n,
+ *,
+ brightness=1.0,
+ auto_write=True,
+ pixel_order=BGR,
+ baudrate=4000000
+ ):
+ self._spi = None
+ try:
+ self._spi = busio.SPI(clock, MOSI=data)
+ while not self._spi.try_lock():
+ pass
+ self._spi.configure(baudrate=baudrate)
+
+ except (NotImplementedError, ValueError):
+ self.dpin = digitalio.DigitalInOut(data)
+ self.cpin = digitalio.DigitalInOut(clock)
+ self.dpin.direction = digitalio.Direction.OUTPUT
+ self.cpin.direction = digitalio.Direction.OUTPUT
+ self.cpin.value = False
+
+ # Supply one extra clock cycle for each two pixels in the strip.
+ trailer_size = n // 16
+ if n % 16 != 0:
+ trailer_size += 1
+
+ # Four empty bytes for the header.
+ header = bytearray(START_HEADER_SIZE)
+ # 0xff bytes for the trailer.
+ trailer = bytearray(b"\xff") * trailer_size
+
+ super().__init__(
+ n,
+ byteorder=pixel_order,
+ brightness=brightness,
+ auto_write=auto_write,
+ header=header,
+ trailer=trailer,
+ )
+
+ def deinit(self):
+ """Blank out the DotStars and release the resources."""
+ self.fill(0)
+ self.show()
+ if self._spi:
+ self._spi.deinit()
+ else:
+ self.dpin.deinit()
+ self.cpin.deinit()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exception_type, exception_value, traceback):
+ self.deinit()
+
+ def __repr__(self):
+ return "[" + ", ".join([str(x) for x in self]) + "]"
+
+ @property
+ def n(self):
+ """
+ The number of dotstars in the chain (read-only)
+ """
+ return len(self)
+
+ def _transmit(self, buffer):
+ if self._spi:
+ self._spi.write(buffer)
+ else:
+ self._ds_writebytes(buffer)
+
+ def _ds_writebytes(self, buffer):
+ for b in buffer:
+ for _ in range(8):
+ self.dpin.value = b & 0x80
+ self.cpin.value = True
+ self.cpin.value = False
+ b = b << 1
+ self.cpin.value = False
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_pypixelbuf.py b/ports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_pypixelbuf.py
new file mode 100755
index 000000000..3cb83e970
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_pypixelbuf.py
@@ -0,0 +1,374 @@
+# The MIT License (MIT)
+#
+# Based on the Adafruit NeoPixel and Adafruit Dotstar CircuitPython drivers.
+# Copyright (c) 2019-2020 Roy Hooper
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""
+`adafruit_pypixelbuf` - A pure python implementation of _pixelbuf
+=================================================================
+This class is used when _pixelbuf is not available in CircuitPython. It is based on the work
+in neopixel.py and adafruit_dotstar.py.
+
+* Author(s): Damien P. George & Limor Fried & Scott Shawcroft & Roy Hooper
+"""
+
+DOTSTAR_LED_START_FULL_BRIGHT = 0xFF
+DOTSTAR_LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits
+DOTSTAR_LED_BRIGHTNESS = 0b00011111
+
+
+class PixelBuf: # pylint: disable=too-many-instance-attributes
+ """
+ A sequence of RGB/RGBW pixels.
+
+ This is the pure python implementation of CircuitPython's _pixelbuf.
+
+ :param ~int n: Number of pixels
+ :param ~str byteorder: Byte order string constant (also sets bpp)
+ :param ~float brightness: Brightness (0 to 1.0, default 1.0)
+ :param ~bool auto_write: Whether to automatically write pixels (Default False)
+ :param bytes header: Sequence of bytes to always send before pixel values.
+ :param bytes trailer: Sequence of bytes to always send after pixel values.
+ """
+
+ def __init__( # pylint: disable=too-many-locals,too-many-arguments
+ self,
+ n,
+ byteorder="BGR",
+ brightness=1.0,
+ auto_write=False,
+ header=None,
+ trailer=None,
+ ):
+
+ bpp, byteorder_tuple, has_white, dotstar_mode = self.parse_byteorder(byteorder)
+
+ self.auto_write = False
+
+ effective_bpp = 4 if dotstar_mode else bpp
+ _bytes = effective_bpp * n
+ buf = bytearray(_bytes)
+ offset = 0
+
+ if header is not None:
+ if not isinstance(header, bytearray):
+ raise TypeError("header must be a bytearray")
+ buf = header + buf
+ offset = len(header)
+
+ if trailer is not None:
+ if not isinstance(trailer, bytearray):
+ raise TypeError("trailer must be a bytearray")
+ buf += trailer
+
+ self._pixels = n
+ self._bytes = _bytes
+ self._byteorder = byteorder_tuple
+ self._byteorder_string = byteorder
+ self._has_white = has_white
+ self._bpp = bpp
+ self._pre_brightness_buffer = None
+ self._post_brightness_buffer = buf
+ self._offset = offset
+ self._dotstar_mode = dotstar_mode
+ self._pixel_step = effective_bpp
+
+ if dotstar_mode:
+ self._byteorder_tuple = (
+ byteorder_tuple[0] + 1,
+ byteorder_tuple[1] + 1,
+ byteorder_tuple[2] + 1,
+ 0,
+ )
+ # Initialize the buffer with the dotstar start bytes.
+ for i in range(self._offset, self._bytes + self._offset, 4):
+ self._post_brightness_buffer[i] = DOTSTAR_LED_START_FULL_BRIGHT
+
+ self._brightness = 1.0
+ self.brightness = brightness
+
+ self.auto_write = auto_write
+
+ @staticmethod
+ def parse_byteorder(byteorder):
+ """
+ Parse a Byteorder string for validity and determine bpp, byte order, and
+ dostar brightness bits.
+
+ Byteorder strings may contain the following characters:
+ R - Red
+ G - Green
+ B - Blue
+ W - White
+ P - PWM (PWM Duty cycle for pixel - dotstars 0 - 1.0)
+
+ :param: ~str bpp: bpp string.
+ :return: ~tuple: bpp, byteorder, has_white, dotstar_mode
+ """
+ bpp = len(byteorder)
+ dotstar_mode = False
+ has_white = False
+
+ if byteorder.strip("RGBWP") != "":
+ raise ValueError("Invalid Byteorder string")
+
+ try:
+ r = byteorder.index("R")
+ g = byteorder.index("G")
+ b = byteorder.index("B")
+ except ValueError:
+ raise ValueError("Invalid Byteorder string")
+ if "W" in byteorder:
+ w = byteorder.index("W")
+ byteorder = (r, g, b, w)
+ has_white = True
+ elif "P" in byteorder:
+ lum = byteorder.index("P")
+ byteorder = (r, g, b, lum)
+ dotstar_mode = True
+ else:
+ byteorder = (r, g, b)
+
+ return bpp, byteorder, has_white, dotstar_mode
+
+ @property
+ def bpp(self):
+ """
+ The number of bytes per pixel in the buffer (read-only).
+ """
+ return self._bpp
+
+ @property
+ def brightness(self):
+ """
+ Float value between 0 and 1. Output brightness.
+
+ When brightness is less than 1.0, a second buffer will be used to store the color values
+ before they are adjusted for brightness.
+ """
+ return self._brightness
+
+ @brightness.setter
+ def brightness(self, value):
+ value = min(max(value, 0.0), 1.0)
+ change = value - self._brightness
+ if -0.001 < change < 0.001:
+ return
+
+ self._brightness = value
+
+ if self._pre_brightness_buffer is None:
+ self._pre_brightness_buffer = bytearray(self._post_brightness_buffer)
+
+ # Adjust brightness of existing pixels
+ offset_check = self._offset % self._pixel_step
+ for i in range(self._offset, self._bytes + self._offset):
+ # Don't adjust per-pixel luminance bytes in dotstar mode
+ if self._dotstar_mode and (i % 4 != offset_check):
+ continue
+ self._post_brightness_buffer[i] = int(
+ self._pre_brightness_buffer[i] * self._brightness
+ )
+
+ if self.auto_write:
+ self.show()
+
+ @property
+ def byteorder(self):
+ """
+ ByteOrder string for the buffer (read-only)
+ """
+ return self._byteorder_string
+
+ def __len__(self):
+ """
+ Number of pixels.
+ """
+ return self._pixels
+
+ def show(self):
+ """
+ Call the associated write function to display the pixels
+ """
+ return self._transmit(self._post_brightness_buffer)
+
+ def fill(self, color):
+ """
+ Fills the given pixelbuf with the given color.
+ :param pixelbuf: A pixel object.
+ :param color: Color to set.
+ """
+ r, g, b, w = self._parse_color(color)
+ for i in range(self._pixels):
+ self._set_item(i, r, g, b, w)
+ if self.auto_write:
+ self.show()
+
+ def _parse_color(self, value):
+ r = 0
+ g = 0
+ b = 0
+ w = 0
+ if isinstance(value, int):
+ r = value >> 16
+ g = (value >> 8) & 0xFF
+ b = value & 0xFF
+ w = 0
+
+ if self._dotstar_mode:
+ w = 1.0
+ else:
+ if len(value) < 3 or len(value) > 4:
+ raise ValueError(
+ "Expected tuple of length {}, got {}".format(self._bpp, len(value))
+ )
+ if len(value) == self._bpp:
+ if self._bpp == 3:
+ r, g, b = value
+ else:
+ r, g, b, w = value
+ elif len(value) == 3:
+ r, g, b = value
+ if self._dotstar_mode:
+ w = 1.0
+
+ if self._bpp == 4:
+ if self._dotstar_mode:
+ # LED startframe is three "1" bits, followed by 5 brightness bits
+ # then 8 bits for each of R, G, and B. The order of those 3 are configurable and
+ # vary based on hardware
+ w = (int(w * 31) & 0b00011111) | DOTSTAR_LED_START
+ elif (
+ self._has_white
+ and (isinstance(value, int) or len(value) == 3)
+ and r == g
+ and g == b
+ ):
+ # If all components are the same and we have a white pixel then use it
+ # instead of the individual components when all 4 values aren't explicitly given.
+ w = r
+ r = 0
+ g = 0
+ b = 0
+
+ return (r, g, b, w)
+
+ def _set_item(
+ self, index, r, g, b, w
+ ): # pylint: disable=too-many-locals,too-many-branches,too-many-arguments
+ if index < 0:
+ index += len(self)
+ if index >= self._pixels or index < 0:
+ raise IndexError
+ offset = self._offset + (index * self._bpp)
+
+ if self._pre_brightness_buffer is not None:
+ if self._bpp == 4:
+ self._pre_brightness_buffer[offset + self._byteorder[3]] = w
+ self._pre_brightness_buffer[offset + self._byteorder[0]] = r
+ self._pre_brightness_buffer[offset + self._byteorder[1]] = g
+ self._pre_brightness_buffer[offset + self._byteorder[2]] = b
+
+ if self._bpp == 4:
+ # Only apply brightness if w is actually white (aka not DotStar.)
+ if not self._dotstar_mode:
+ w = int(w * self._brightness)
+ self._post_brightness_buffer[offset + self._byteorder[3]] = w
+
+ self._post_brightness_buffer[offset + self._byteorder[0]] = int(
+ r * self._brightness
+ )
+ self._post_brightness_buffer[offset + self._byteorder[1]] = int(
+ g * self._brightness
+ )
+ self._post_brightness_buffer[offset + self._byteorder[2]] = int(
+ b * self._brightness
+ )
+
+ def __setitem__(self, index, val):
+ if isinstance(index, slice):
+ start, stop, step = index.indices(self._pixels)
+ for val_i, in_i in enumerate(range(start, stop, step)):
+ r, g, b, w = self._parse_color(val[val_i])
+ self._set_item(in_i, r, g, b, w)
+ else:
+ r, g, b, w = self._parse_color(val)
+ self._set_item(index, r, g, b, w)
+
+ if self.auto_write:
+ self.show()
+
+ def _getitem(self, index):
+ start = self._offset + (index * self._bpp)
+ buffer = (
+ self._pre_brightness_buffer
+ if self._pre_brightness_buffer is not None
+ else self._post_brightness_buffer
+ )
+ value = [
+ buffer[start + self._byteorder[0]],
+ buffer[start + self._byteorder[1]],
+ buffer[start + self._byteorder[2]],
+ ]
+ if self._has_white:
+ value.append(buffer[start + self._byteorder[3]])
+ elif self._dotstar_mode:
+ value.append(
+ (buffer[start + self._byteorder[3]] & DOTSTAR_LED_BRIGHTNESS) / 31.0
+ )
+ return value
+
+ def __getitem__(self, index):
+ if isinstance(index, slice):
+ out = []
+ for in_i in range(
+ *index.indices(len(self._post_brightness_buffer) // self._bpp)
+ ):
+ out.append(self._getitem(in_i))
+ return out
+ if index < 0:
+ index += len(self)
+ if index >= self._pixels or index < 0:
+ raise IndexError
+ return self._getitem(index)
+
+ def _transmit(self, buffer):
+ raise NotImplementedError("Must be subclassed")
+
+
+def wheel(pos):
+ """
+ Helper to create a colorwheel.
+
+ :param pos: int 0-255 of color value to return
+ :return: tuple of RGB values
+ """
+ # Input a value 0 to 255 to get a color value.
+ # The colours are a transition r - g - b - back to r.
+ if pos < 0 or pos > 255:
+ return 0, 0, 0
+ if pos < 85:
+ return 255 - pos * 3, pos * 3, 0
+ if pos < 170:
+ pos -= 85
+ return 0, 255 - pos * 3, pos * 3
+ pos -= 170
+ return pos * 3, 0, 255 - pos * 3
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_requests.py b/ports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_requests.py
new file mode 100755
index 000000000..0888937bf
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2/adafruit_requests.py
@@ -0,0 +1,311 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2019 ladyada for Adafruit Industries
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+"""
+`adafruit_requests`
+================================================================================
+
+A requests-like library for web interfacing
+
+
+* Author(s): ladyada, Paul Sokolovsky
+
+Implementation Notes
+--------------------
+
+Adapted from https://github.com/micropython/micropython-lib/tree/master/urequests
+
+micropython-lib consists of multiple modules from different sources and
+authors. Each module comes under its own licensing terms. Short name of
+a license can be found in a file within a module directory (usually
+metadata.txt or setup.py). Complete text of each license used is provided
+at https://github.com/micropython/micropython-lib/blob/master/LICENSE
+
+author='Paul Sokolovsky'
+license='MIT'
+
+**Software and Dependencies:**
+
+* Adafruit CircuitPython firmware for the supported boards:
+ https://github.com/adafruit/circuitpython/releases
+
+"""
+
+import gc
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Requests.git"
+
+_the_interface = None # pylint: disable=invalid-name
+_the_sock = None # pylint: disable=invalid-name
+
+
+def set_socket(sock, iface=None):
+ """Helper to set the global socket and optionally set the global network interface.
+ :param sock: socket object.
+ :param iface: internet interface object
+
+ """
+ global _the_sock # pylint: disable=invalid-name, global-statement
+ _the_sock = sock
+ if iface:
+ global _the_interface # pylint: disable=invalid-name, global-statement
+ _the_interface = iface
+ _the_sock.set_interface(iface)
+
+
+class Response:
+ """The response from a request, contains all the headers/content"""
+
+ encoding = None
+
+ def __init__(self, sock):
+ self.socket = sock
+ self.encoding = "utf-8"
+ self._cached = None
+ self.status_code = None
+ self.reason = None
+ self._read_so_far = 0
+ self.headers = {}
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.close()
+
+ def close(self):
+ """Close, delete and collect the response data"""
+ if self.socket:
+ self.socket.close()
+ del self.socket
+ del self._cached
+ gc.collect()
+
+ @property
+ def content(self):
+ """The HTTP content direct from the socket, as bytes"""
+ # print(self.headers)
+ try:
+ content_length = int(self.headers["content-length"])
+ except KeyError:
+ content_length = 0
+ # print("Content length:", content_length)
+ if self._cached is None:
+ try:
+ self._cached = self.socket.recv(content_length)
+ finally:
+ self.socket.close()
+ self.socket = None
+ # print("Buffer length:", len(self._cached))
+ return self._cached
+
+ @property
+ def text(self):
+ """The HTTP content, encoded into a string according to the HTTP
+ header encoding"""
+ return str(self.content, self.encoding)
+
+ def json(self):
+ """The HTTP content, parsed into a json dictionary"""
+ # pylint: disable=import-outside-toplevel
+ try:
+ import json as json_module
+ except ImportError:
+ import ujson as json_module
+ return json_module.loads(self.content)
+
+ def iter_content(self, chunk_size=1, decode_unicode=False):
+ """An iterator that will stream data by only reading 'chunk_size'
+ bytes and yielding them, when we can't buffer the whole datastream"""
+ if decode_unicode:
+ raise NotImplementedError("Unicode not supported")
+
+ while True:
+ chunk = self.socket.recv(chunk_size)
+ if chunk:
+ yield chunk
+ else:
+ return
+
+
+# pylint: disable=too-many-branches, too-many-statements, unused-argument, too-many-arguments, too-many-locals
+def request(method, url, data=None, json=None, headers=None, stream=False, timeout=1):
+ """Perform an HTTP request to the given url which we will parse to determine
+ whether to use SSL ('https://') or not. We can also send some provided 'data'
+ or a json dictionary which we will stringify. 'headers' is optional HTTP headers
+ sent along. 'stream' will determine if we buffer everything, or whether to only
+ read only when requested
+ """
+ global _the_interface # pylint: disable=global-statement, invalid-name
+ global _the_sock # pylint: disable=global-statement, invalid-name
+
+ if not headers:
+ headers = {}
+
+ try:
+ proto, dummy, host, path = url.split("/", 3)
+ # replace spaces in path
+ path = path.replace(" ", "%20")
+ except ValueError:
+ proto, dummy, host = url.split("/", 2)
+ path = ""
+ if proto == "http:":
+ port = 80
+ elif proto == "https:":
+ port = 443
+ else:
+ raise ValueError("Unsupported protocol: " + proto)
+
+ if ":" in host:
+ host, port = host.split(":", 1)
+ port = int(port)
+
+ addr_info = _the_sock.getaddrinfo(host, port, 0, _the_sock.SOCK_STREAM)[0]
+ sock = _the_sock.socket(addr_info[0], addr_info[1], addr_info[2])
+ resp = Response(sock) # our response
+
+ sock.settimeout(timeout) # socket read timeout
+
+ try:
+ if proto == "https:":
+ conntype = _the_interface.TLS_MODE
+ sock.connect(
+ (host, port), conntype
+ ) # for SSL we need to know the host name
+ else:
+ conntype = _the_interface.TCP_MODE
+ sock.connect(addr_info[-1], conntype)
+ sock.send(
+ b"%s /%s HTTP/1.0\r\n" % (bytes(method, "utf-8"), bytes(path, "utf-8"))
+ )
+ if "Host" not in headers:
+ sock.send(b"Host: %s\r\n" % bytes(host, "utf-8"))
+ if "User-Agent" not in headers:
+ sock.send(b"User-Agent: Adafruit CircuitPython\r\n")
+ # Iterate over keys to avoid tuple alloc
+ for k in headers:
+ sock.send(k.encode())
+ sock.send(b": ")
+ sock.send(headers[k].encode())
+ sock.send(b"\r\n")
+ if json is not None:
+ assert data is None
+ # pylint: disable=import-outside-toplevel
+ try:
+ import json as json_module
+ except ImportError:
+ import ujson as json_module
+ # pylint: enable=import-outside-toplevel
+ data = json_module.dumps(json)
+ sock.send(b"Content-Type: application/json\r\n")
+ if data:
+ if isinstance(data, dict):
+ sock.send(b"Content-Type: application/x-www-form-urlencoded\r\n")
+ _post_data = ""
+ for k in data:
+ _post_data = "{}&{}={}".format(_post_data, k, data[k])
+ data = _post_data[1:]
+ sock.send(b"Content-Length: %d\r\n" % len(data))
+ sock.send(b"\r\n")
+ if data:
+ if isinstance(data, bytearray):
+ sock.send(bytes(data))
+ else:
+ sock.send(bytes(data, "utf-8"))
+
+ line = sock.readline()
+ # print(line)
+ line = line.split(None, 2)
+ status = int(line[1])
+ reason = ""
+ if len(line) > 2:
+ reason = line[2].rstrip()
+ resp.headers = parse_headers(sock)
+ if resp.headers.get("transfer-encoding"):
+ if "chunked" in resp.headers.get("transfer-encoding"):
+ raise ValueError("Unsupported " + resp.headers.get("transfer-encoding"))
+ elif resp.headers.get("location") and not 200 <= status <= 299:
+ raise NotImplementedError("Redirects not yet supported")
+
+ except:
+ sock.close()
+ raise
+
+ resp.status_code = status
+ resp.reason = reason
+ return resp
+
+
+def parse_headers(sock):
+ """
+ Parses the header portion of an HTTP request/response from the socket.
+ Expects first line of HTTP request/response to have been read already
+ return: header dictionary
+ rtype: Dict
+ """
+ headers = {}
+ while True:
+ line = sock.readline()
+ if not line or line == b"\r\n":
+ break
+
+ # print("**line: ", line)
+ splits = line.split(b": ", 1)
+ title = splits[0]
+ content = ""
+ if len(splits) > 1:
+ content = splits[1]
+ if title and content:
+ title = str(title.lower(), "utf-8")
+ content = str(content, "utf-8")
+ headers[title] = content
+ return headers
+
+
+def head(url, **kw):
+ """Send HTTP HEAD request"""
+ return request("HEAD", url, **kw)
+
+
+def get(url, **kw):
+ """Send HTTP GET request"""
+ return request("GET", url, **kw)
+
+
+def post(url, **kw):
+ """Send HTTP POST request"""
+ return request("POST", url, **kw)
+
+
+def put(url, **kw):
+ """Send HTTP PUT request"""
+ return request("PUT", url, **kw)
+
+
+def patch(url, **kw):
+ """Send HTTP PATCH request"""
+ return request("PATCH", url, **kw)
+
+
+def delete(url, **kw):
+ """Send HTTP DELETE request"""
+ return request("DELETE", url, **kw)
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2/feathers2.py b/ports/esp32s2/boards/unexpectedmaker_feathers2/feathers2.py
new file mode 100644
index 000000000..0631cad7f
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2/feathers2.py
@@ -0,0 +1,47 @@
+# FeatherS2 Helper Library
+# 2020 Seon Rozenblum, Unexpected Maker
+#
+# Project home:
+# https://feathers2.io
+#
+
+# Import required libraries
+import time
+import board
+from digitalio import DigitalInOut, Direction, Pull
+
+
+# Helper functions
+
+def enable_LDO2(state):
+ """Set the power for the second on-board LDO to allow no current draw when not needed."""
+
+ # Grab a reference to the LDO2 IO (21 in this case)
+ ldo2 = DigitalInOut(board.LDO2)
+ ldo2.direction = Direction.OUTPUT
+
+ # Set the LDO2 power pin on / off
+ ldo2.value = state
+
+ # A small delay to let the IO change state
+ time.sleep(0.035)
+
+
+# Dotstar rainbow colour wheel
+def dotstar_color_wheel(wheel_pos):
+ """Color wheel to allow for cycling through the rainbow of RGB colors."""
+ wheel_pos = wheel_pos % 255
+
+ if wheel_pos < 85:
+ return 255 - wheel_pos * 3, 0, wheel_pos * 3
+ elif wheel_pos < 170:
+ wheel_pos -= 85
+ return 0, wheel_pos * 3, 255 - wheel_pos * 3
+ else:
+ wheel_pos -= 170
+ return wheel_pos * 3, 255 - wheel_pos * 3, 0
+
+
+
+# Disable LDO2 by default
+enable_LDO2(False)
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.h b/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.h
index e5fed4e32..a77de1511 100644
--- a/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.h
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.h
@@ -35,6 +35,5 @@
#define AUTORESET_DELAY_MS 500
-// Doesn't work with this on.
-// #define MICROPY_HW_APA102_MOSI (&pin_GPIO44)
-// #define MICROPY_HW_APA102_SCK (&pin_GPIO45)
+#define MICROPY_HW_APA102_MOSI (&pin_GPIO40)
+#define MICROPY_HW_APA102_SCK (&pin_GPIO45)
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.mk b/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.mk
index 294736d17..91c321af9 100644
--- a/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.mk
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.mk
@@ -16,3 +16,7 @@ CIRCUITPY_ESP_FLASH_FREQ=40m
CIRCUITPY_ESP_FLASH_SIZE=16MB
CIRCUITPY_BITBANG_APA102 = 1
+
+# Include these Python libraries in firmware.
+FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DotStar
+
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2/pins.c b/ports/esp32s2/boards/unexpectedmaker_feathers2/pins.c
index e8dd2edf6..5a29e029c 100644
--- a/ports/esp32s2/boards/unexpectedmaker_feathers2/pins.c
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2/pins.c
@@ -1,55 +1,103 @@
#include "shared-bindings/board/__init__.h"
STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_IO17), MP_ROM_PTR(&pin_GPIO17) },
{ MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO17) },
{ MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO17) },
+ { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) },
{ MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO18) },
{ MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO18) },
- { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO14) },
- { MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO14) },
+ { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) },
+ { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO13) },
+ { MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO13) },
+ { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) },
{ MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO12) },
{ MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO12) },
+ { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) },
{ MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO6) },
{ MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO6) },
+ { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) },
{ MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO5) },
{ MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO5) },
+ { MP_ROM_QSTR(MP_QSTR_IO36), MP_ROM_PTR(&pin_GPIO36) },
{ MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO36) },
{ MP_ROM_QSTR(MP_QSTR_D25), MP_ROM_PTR(&pin_GPIO36) },
+ { MP_ROM_QSTR(MP_QSTR_IO35), MP_ROM_PTR(&pin_GPIO35) },
{ MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO35) },
{ MP_ROM_QSTR(MP_QSTR_D24), MP_ROM_PTR(&pin_GPIO35) },
+ { MP_ROM_QSTR(MP_QSTR_IO37), MP_ROM_PTR(&pin_GPIO37) },
{ MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO37) },
{ MP_ROM_QSTR(MP_QSTR_D23), MP_ROM_PTR(&pin_GPIO37) },
+ { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) },
{ MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO44) },
{ MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) },
+ { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) },
{ MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO43) },
{ MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) },
- // Moving to 9 and 8
- { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO38) },
- { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO33) },
+ { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) },
+ { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO8) },
+ { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO8) },
+
+ { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) },
+ { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO9) },
+ { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO9) },
+
+ { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) },
+ { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO0) },
+
+ { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) },
{ MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO1) },
+ { MP_ROM_QSTR(MP_QSTR_A10), MP_ROM_PTR(&pin_GPIO1) },
+
+ { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) },
{ MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO3) },
+ { MP_ROM_QSTR(MP_QSTR_A9), MP_ROM_PTR(&pin_GPIO3) },
+
+ { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) },
{ MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO7) },
- { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO8) },
- { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO9) },
+ { MP_ROM_QSTR(MP_QSTR_A8), MP_ROM_PTR(&pin_GPIO7) },
+
+ { MP_ROM_QSTR(MP_QSTR_IO33), MP_ROM_PTR(&pin_GPIO33) },
+ { MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO33) },
+
+ { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) },
+ { MP_ROM_QSTR(MP_QSTR_D21), MP_ROM_PTR(&pin_GPIO38) },
+
+ { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) },
{ MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO10) },
+ { MP_ROM_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO10) },
+
+ { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) },
{ MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO11) },
+ { MP_ROM_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO11) },
- { MP_ROM_QSTR(MP_QSTR_APA102_MOSI), MP_ROM_PTR(&pin_GPIO44) }, // MTDO
+ // { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, // MTDO
+ { MP_ROM_QSTR(MP_QSTR_APA102_MOSI), MP_ROM_PTR(&pin_GPIO40) }, // MTDO
+
+ // { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) },
{ MP_ROM_QSTR(MP_QSTR_APA102_SCK), MP_ROM_PTR(&pin_GPIO45) },
+
+ { MP_ROM_QSTR(MP_QSTR_LDO2), MP_ROM_PTR(&pin_GPIO21) },
+ { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) },
+
+ { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) },
{ MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO13) },
+ { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) },
+ { MP_ROM_QSTR(MP_QSTR_AMB), MP_ROM_PTR(&pin_GPIO4) },
+
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) },
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/adafruit_dotstar.py b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/adafruit_dotstar.py
new file mode 100755
index 000000000..65fbbe868
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/adafruit_dotstar.py
@@ -0,0 +1,198 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Damien P. George (original Neopixel object)
+# Copyright (c) 2017 Ladyada
+# Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
+# Copyright (c) 2019 Roy Hooper
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""
+`adafruit_dotstar` - DotStar strip driver (for CircuitPython 5.0+ with _pixelbuf)
+=================================================================================
+
+* Author(s): Damien P. George, Limor Fried, Scott Shawcroft & Roy Hooper
+"""
+
+# pylint: disable=ungrouped-imports
+import sys
+import busio
+import digitalio
+
+if sys.implementation.version[0] < 5:
+ import adafruit_pypixelbuf as _pixelbuf
+else:
+ try:
+ import _pixelbuf
+ except ImportError:
+ import adafruit_pypixelbuf as _pixelbuf
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DotStar.git"
+
+START_HEADER_SIZE = 4
+
+# Pixel color order constants
+RBG = "PRBG"
+"""Red Blue Green"""
+RGB = "PRGB"
+"""Red Green Blue"""
+GRB = "PGRB"
+"""Green Red Blue"""
+GBR = "PGBR"
+"""Green Blue Red"""
+BRG = "PBRG"
+"""Blue Red Green"""
+BGR = "PBGR"
+"""Blue Green Red"""
+
+
+class DotStar(_pixelbuf.PixelBuf):
+ """
+ A sequence of dotstars.
+
+ :param ~microcontroller.Pin clock: The pin to output dotstar clock on.
+ :param ~microcontroller.Pin data: The pin to output dotstar data on.
+ :param int n: The number of dotstars in the chain
+ :param float brightness: Brightness of the pixels between 0.0 and 1.0
+ :param bool auto_write: True if the dotstars should immediately change when
+ set. If False, `show` must be called explicitly.
+ :param str pixel_order: Set the pixel order on the strip - different
+ strips implement this differently. If you send red, and it looks blue
+ or green on the strip, modify this! It should be one of the values above.
+ :param int baudrate: Desired clock rate if using hardware SPI (ignored if
+ using 'soft' SPI). This is only a recommendation; the actual clock
+ rate may be slightly different depending on what the system hardware
+ can provide.
+
+ Example for Gemma M0:
+
+ .. code-block:: python
+
+ import adafruit_dotstar
+ import time
+ from board import *
+
+ RED = 0x100000
+
+ with adafruit_dotstar.DotStar(APA102_SCK, APA102_MOSI, 1) as pixels:
+ pixels[0] = RED
+ time.sleep(2)
+
+ .. py:method:: DotStar.show()
+
+ Shows the new colors on the dotstars themselves if they haven't already
+ been autowritten.
+
+ The colors may or may not be showing after this function returns because
+ it may be done asynchronously.
+
+ .. py:method:: DotStar.fill(color)
+
+ Colors all dotstars the given ***color***.
+
+ .. py:attribute:: brightness
+
+ Overall brightness of all dotstars (0 to 1.0)
+ """
+
+ def __init__(
+ self,
+ clock,
+ data,
+ n,
+ *,
+ brightness=1.0,
+ auto_write=True,
+ pixel_order=BGR,
+ baudrate=4000000
+ ):
+ self._spi = None
+ try:
+ self._spi = busio.SPI(clock, MOSI=data)
+ while not self._spi.try_lock():
+ pass
+ self._spi.configure(baudrate=baudrate)
+
+ except (NotImplementedError, ValueError):
+ self.dpin = digitalio.DigitalInOut(data)
+ self.cpin = digitalio.DigitalInOut(clock)
+ self.dpin.direction = digitalio.Direction.OUTPUT
+ self.cpin.direction = digitalio.Direction.OUTPUT
+ self.cpin.value = False
+
+ # Supply one extra clock cycle for each two pixels in the strip.
+ trailer_size = n // 16
+ if n % 16 != 0:
+ trailer_size += 1
+
+ # Four empty bytes for the header.
+ header = bytearray(START_HEADER_SIZE)
+ # 0xff bytes for the trailer.
+ trailer = bytearray(b"\xff") * trailer_size
+
+ super().__init__(
+ n,
+ byteorder=pixel_order,
+ brightness=brightness,
+ auto_write=auto_write,
+ header=header,
+ trailer=trailer,
+ )
+
+ def deinit(self):
+ """Blank out the DotStars and release the resources."""
+ self.fill(0)
+ self.show()
+ if self._spi:
+ self._spi.deinit()
+ else:
+ self.dpin.deinit()
+ self.cpin.deinit()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exception_type, exception_value, traceback):
+ self.deinit()
+
+ def __repr__(self):
+ return "[" + ", ".join([str(x) for x in self]) + "]"
+
+ @property
+ def n(self):
+ """
+ The number of dotstars in the chain (read-only)
+ """
+ return len(self)
+
+ def _transmit(self, buffer):
+ if self._spi:
+ self._spi.write(buffer)
+ else:
+ self._ds_writebytes(buffer)
+
+ def _ds_writebytes(self, buffer):
+ for b in buffer:
+ for _ in range(8):
+ self.dpin.value = b & 0x80
+ self.cpin.value = True
+ self.cpin.value = False
+ b = b << 1
+ self.cpin.value = False
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/adafruit_pypixelbuf.py b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/adafruit_pypixelbuf.py
new file mode 100755
index 000000000..3cb83e970
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/adafruit_pypixelbuf.py
@@ -0,0 +1,374 @@
+# The MIT License (MIT)
+#
+# Based on the Adafruit NeoPixel and Adafruit Dotstar CircuitPython drivers.
+# Copyright (c) 2019-2020 Roy Hooper
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""
+`adafruit_pypixelbuf` - A pure python implementation of _pixelbuf
+=================================================================
+This class is used when _pixelbuf is not available in CircuitPython. It is based on the work
+in neopixel.py and adafruit_dotstar.py.
+
+* Author(s): Damien P. George & Limor Fried & Scott Shawcroft & Roy Hooper
+"""
+
+DOTSTAR_LED_START_FULL_BRIGHT = 0xFF
+DOTSTAR_LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits
+DOTSTAR_LED_BRIGHTNESS = 0b00011111
+
+
+class PixelBuf: # pylint: disable=too-many-instance-attributes
+ """
+ A sequence of RGB/RGBW pixels.
+
+ This is the pure python implementation of CircuitPython's _pixelbuf.
+
+ :param ~int n: Number of pixels
+ :param ~str byteorder: Byte order string constant (also sets bpp)
+ :param ~float brightness: Brightness (0 to 1.0, default 1.0)
+ :param ~bool auto_write: Whether to automatically write pixels (Default False)
+ :param bytes header: Sequence of bytes to always send before pixel values.
+ :param bytes trailer: Sequence of bytes to always send after pixel values.
+ """
+
+ def __init__( # pylint: disable=too-many-locals,too-many-arguments
+ self,
+ n,
+ byteorder="BGR",
+ brightness=1.0,
+ auto_write=False,
+ header=None,
+ trailer=None,
+ ):
+
+ bpp, byteorder_tuple, has_white, dotstar_mode = self.parse_byteorder(byteorder)
+
+ self.auto_write = False
+
+ effective_bpp = 4 if dotstar_mode else bpp
+ _bytes = effective_bpp * n
+ buf = bytearray(_bytes)
+ offset = 0
+
+ if header is not None:
+ if not isinstance(header, bytearray):
+ raise TypeError("header must be a bytearray")
+ buf = header + buf
+ offset = len(header)
+
+ if trailer is not None:
+ if not isinstance(trailer, bytearray):
+ raise TypeError("trailer must be a bytearray")
+ buf += trailer
+
+ self._pixels = n
+ self._bytes = _bytes
+ self._byteorder = byteorder_tuple
+ self._byteorder_string = byteorder
+ self._has_white = has_white
+ self._bpp = bpp
+ self._pre_brightness_buffer = None
+ self._post_brightness_buffer = buf
+ self._offset = offset
+ self._dotstar_mode = dotstar_mode
+ self._pixel_step = effective_bpp
+
+ if dotstar_mode:
+ self._byteorder_tuple = (
+ byteorder_tuple[0] + 1,
+ byteorder_tuple[1] + 1,
+ byteorder_tuple[2] + 1,
+ 0,
+ )
+ # Initialize the buffer with the dotstar start bytes.
+ for i in range(self._offset, self._bytes + self._offset, 4):
+ self._post_brightness_buffer[i] = DOTSTAR_LED_START_FULL_BRIGHT
+
+ self._brightness = 1.0
+ self.brightness = brightness
+
+ self.auto_write = auto_write
+
+ @staticmethod
+ def parse_byteorder(byteorder):
+ """
+ Parse a Byteorder string for validity and determine bpp, byte order, and
+ dostar brightness bits.
+
+ Byteorder strings may contain the following characters:
+ R - Red
+ G - Green
+ B - Blue
+ W - White
+ P - PWM (PWM Duty cycle for pixel - dotstars 0 - 1.0)
+
+ :param: ~str bpp: bpp string.
+ :return: ~tuple: bpp, byteorder, has_white, dotstar_mode
+ """
+ bpp = len(byteorder)
+ dotstar_mode = False
+ has_white = False
+
+ if byteorder.strip("RGBWP") != "":
+ raise ValueError("Invalid Byteorder string")
+
+ try:
+ r = byteorder.index("R")
+ g = byteorder.index("G")
+ b = byteorder.index("B")
+ except ValueError:
+ raise ValueError("Invalid Byteorder string")
+ if "W" in byteorder:
+ w = byteorder.index("W")
+ byteorder = (r, g, b, w)
+ has_white = True
+ elif "P" in byteorder:
+ lum = byteorder.index("P")
+ byteorder = (r, g, b, lum)
+ dotstar_mode = True
+ else:
+ byteorder = (r, g, b)
+
+ return bpp, byteorder, has_white, dotstar_mode
+
+ @property
+ def bpp(self):
+ """
+ The number of bytes per pixel in the buffer (read-only).
+ """
+ return self._bpp
+
+ @property
+ def brightness(self):
+ """
+ Float value between 0 and 1. Output brightness.
+
+ When brightness is less than 1.0, a second buffer will be used to store the color values
+ before they are adjusted for brightness.
+ """
+ return self._brightness
+
+ @brightness.setter
+ def brightness(self, value):
+ value = min(max(value, 0.0), 1.0)
+ change = value - self._brightness
+ if -0.001 < change < 0.001:
+ return
+
+ self._brightness = value
+
+ if self._pre_brightness_buffer is None:
+ self._pre_brightness_buffer = bytearray(self._post_brightness_buffer)
+
+ # Adjust brightness of existing pixels
+ offset_check = self._offset % self._pixel_step
+ for i in range(self._offset, self._bytes + self._offset):
+ # Don't adjust per-pixel luminance bytes in dotstar mode
+ if self._dotstar_mode and (i % 4 != offset_check):
+ continue
+ self._post_brightness_buffer[i] = int(
+ self._pre_brightness_buffer[i] * self._brightness
+ )
+
+ if self.auto_write:
+ self.show()
+
+ @property
+ def byteorder(self):
+ """
+ ByteOrder string for the buffer (read-only)
+ """
+ return self._byteorder_string
+
+ def __len__(self):
+ """
+ Number of pixels.
+ """
+ return self._pixels
+
+ def show(self):
+ """
+ Call the associated write function to display the pixels
+ """
+ return self._transmit(self._post_brightness_buffer)
+
+ def fill(self, color):
+ """
+ Fills the given pixelbuf with the given color.
+ :param pixelbuf: A pixel object.
+ :param color: Color to set.
+ """
+ r, g, b, w = self._parse_color(color)
+ for i in range(self._pixels):
+ self._set_item(i, r, g, b, w)
+ if self.auto_write:
+ self.show()
+
+ def _parse_color(self, value):
+ r = 0
+ g = 0
+ b = 0
+ w = 0
+ if isinstance(value, int):
+ r = value >> 16
+ g = (value >> 8) & 0xFF
+ b = value & 0xFF
+ w = 0
+
+ if self._dotstar_mode:
+ w = 1.0
+ else:
+ if len(value) < 3 or len(value) > 4:
+ raise ValueError(
+ "Expected tuple of length {}, got {}".format(self._bpp, len(value))
+ )
+ if len(value) == self._bpp:
+ if self._bpp == 3:
+ r, g, b = value
+ else:
+ r, g, b, w = value
+ elif len(value) == 3:
+ r, g, b = value
+ if self._dotstar_mode:
+ w = 1.0
+
+ if self._bpp == 4:
+ if self._dotstar_mode:
+ # LED startframe is three "1" bits, followed by 5 brightness bits
+ # then 8 bits for each of R, G, and B. The order of those 3 are configurable and
+ # vary based on hardware
+ w = (int(w * 31) & 0b00011111) | DOTSTAR_LED_START
+ elif (
+ self._has_white
+ and (isinstance(value, int) or len(value) == 3)
+ and r == g
+ and g == b
+ ):
+ # If all components are the same and we have a white pixel then use it
+ # instead of the individual components when all 4 values aren't explicitly given.
+ w = r
+ r = 0
+ g = 0
+ b = 0
+
+ return (r, g, b, w)
+
+ def _set_item(
+ self, index, r, g, b, w
+ ): # pylint: disable=too-many-locals,too-many-branches,too-many-arguments
+ if index < 0:
+ index += len(self)
+ if index >= self._pixels or index < 0:
+ raise IndexError
+ offset = self._offset + (index * self._bpp)
+
+ if self._pre_brightness_buffer is not None:
+ if self._bpp == 4:
+ self._pre_brightness_buffer[offset + self._byteorder[3]] = w
+ self._pre_brightness_buffer[offset + self._byteorder[0]] = r
+ self._pre_brightness_buffer[offset + self._byteorder[1]] = g
+ self._pre_brightness_buffer[offset + self._byteorder[2]] = b
+
+ if self._bpp == 4:
+ # Only apply brightness if w is actually white (aka not DotStar.)
+ if not self._dotstar_mode:
+ w = int(w * self._brightness)
+ self._post_brightness_buffer[offset + self._byteorder[3]] = w
+
+ self._post_brightness_buffer[offset + self._byteorder[0]] = int(
+ r * self._brightness
+ )
+ self._post_brightness_buffer[offset + self._byteorder[1]] = int(
+ g * self._brightness
+ )
+ self._post_brightness_buffer[offset + self._byteorder[2]] = int(
+ b * self._brightness
+ )
+
+ def __setitem__(self, index, val):
+ if isinstance(index, slice):
+ start, stop, step = index.indices(self._pixels)
+ for val_i, in_i in enumerate(range(start, stop, step)):
+ r, g, b, w = self._parse_color(val[val_i])
+ self._set_item(in_i, r, g, b, w)
+ else:
+ r, g, b, w = self._parse_color(val)
+ self._set_item(index, r, g, b, w)
+
+ if self.auto_write:
+ self.show()
+
+ def _getitem(self, index):
+ start = self._offset + (index * self._bpp)
+ buffer = (
+ self._pre_brightness_buffer
+ if self._pre_brightness_buffer is not None
+ else self._post_brightness_buffer
+ )
+ value = [
+ buffer[start + self._byteorder[0]],
+ buffer[start + self._byteorder[1]],
+ buffer[start + self._byteorder[2]],
+ ]
+ if self._has_white:
+ value.append(buffer[start + self._byteorder[3]])
+ elif self._dotstar_mode:
+ value.append(
+ (buffer[start + self._byteorder[3]] & DOTSTAR_LED_BRIGHTNESS) / 31.0
+ )
+ return value
+
+ def __getitem__(self, index):
+ if isinstance(index, slice):
+ out = []
+ for in_i in range(
+ *index.indices(len(self._post_brightness_buffer) // self._bpp)
+ ):
+ out.append(self._getitem(in_i))
+ return out
+ if index < 0:
+ index += len(self)
+ if index >= self._pixels or index < 0:
+ raise IndexError
+ return self._getitem(index)
+
+ def _transmit(self, buffer):
+ raise NotImplementedError("Must be subclassed")
+
+
+def wheel(pos):
+ """
+ Helper to create a colorwheel.
+
+ :param pos: int 0-255 of color value to return
+ :return: tuple of RGB values
+ """
+ # Input a value 0 to 255 to get a color value.
+ # The colours are a transition r - g - b - back to r.
+ if pos < 0 or pos > 255:
+ return 0, 0, 0
+ if pos < 85:
+ return 255 - pos * 3, pos * 3, 0
+ if pos < 170:
+ pos -= 85
+ return 0, 255 - pos * 3, pos * 3
+ pos -= 170
+ return pos * 3, 0, 255 - pos * 3
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/board.c b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/board.c
new file mode 100644
index 000000000..1dc30b5af
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/board.c
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "boards/board.h"
+#include "mpconfigboard.h"
+#include "shared-bindings/microcontroller/Pin.h"
+
+void board_init(void) {
+ // USB
+ common_hal_never_reset_pin(&pin_GPIO19);
+ common_hal_never_reset_pin(&pin_GPIO20);
+
+ // Debug UART
+ common_hal_never_reset_pin(&pin_GPIO43);
+ common_hal_never_reset_pin(&pin_GPIO44);
+
+ // SPI Flash and RAM
+ common_hal_never_reset_pin(&pin_GPIO26);
+ common_hal_never_reset_pin(&pin_GPIO27);
+ common_hal_never_reset_pin(&pin_GPIO28);
+ common_hal_never_reset_pin(&pin_GPIO29);
+ common_hal_never_reset_pin(&pin_GPIO30);
+ common_hal_never_reset_pin(&pin_GPIO31);
+ common_hal_never_reset_pin(&pin_GPIO32);
+}
+
+bool board_requests_safe_mode(void) {
+ return false;
+}
+
+void reset_board(void) {
+
+}
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/feathers2.py b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/feathers2.py
new file mode 100644
index 000000000..0631cad7f
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/feathers2.py
@@ -0,0 +1,47 @@
+# FeatherS2 Helper Library
+# 2020 Seon Rozenblum, Unexpected Maker
+#
+# Project home:
+# https://feathers2.io
+#
+
+# Import required libraries
+import time
+import board
+from digitalio import DigitalInOut, Direction, Pull
+
+
+# Helper functions
+
+def enable_LDO2(state):
+ """Set the power for the second on-board LDO to allow no current draw when not needed."""
+
+ # Grab a reference to the LDO2 IO (21 in this case)
+ ldo2 = DigitalInOut(board.LDO2)
+ ldo2.direction = Direction.OUTPUT
+
+ # Set the LDO2 power pin on / off
+ ldo2.value = state
+
+ # A small delay to let the IO change state
+ time.sleep(0.035)
+
+
+# Dotstar rainbow colour wheel
+def dotstar_color_wheel(wheel_pos):
+ """Color wheel to allow for cycling through the rainbow of RGB colors."""
+ wheel_pos = wheel_pos % 255
+
+ if wheel_pos < 85:
+ return 255 - wheel_pos * 3, 0, wheel_pos * 3
+ elif wheel_pos < 170:
+ wheel_pos -= 85
+ return 0, wheel_pos * 3, 255 - wheel_pos * 3
+ else:
+ wheel_pos -= 170
+ return wheel_pos * 3, 255 - wheel_pos * 3, 0
+
+
+
+# Disable LDO2 by default
+enable_LDO2(False)
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.h b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.h
new file mode 100644
index 000000000..a65d00206
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+//Micropython setup
+
+#define MICROPY_HW_BOARD_NAME "FeatherS2"
+#define MICROPY_HW_MCU_NAME "ESP32S2"
+
+#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO0)
+
+#define BOARD_USER_SAFE_MODE_ACTION "pressing boot button at start up.\n"
+
+#define AUTORESET_DELAY_MS 500
+
+// Doesn't work with this on.
+// #define MICROPY_HW_APA102_MOSI (&pin_GPIO44)
+// #define MICROPY_HW_APA102_SCK (&pin_GPIO45)
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.mk b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.mk
new file mode 100644
index 000000000..4c809471b
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.mk
@@ -0,0 +1,21 @@
+USB_VID = 0x239A
+USB_PID = 0x80AC
+USB_PRODUCT = "FeatherS2"
+USB_MANUFACTURER = "UnexpectedMaker"
+USB_DEVICES = "CDC,MSC,HID"
+
+INTERNAL_FLASH_FILESYSTEM = 1
+LONGINT_IMPL = MPZ
+
+# The default queue depth of 16 overflows on release builds,
+# so increase it to 32.
+CFLAGS += -DCFG_TUD_TASK_QUEUE_SZ=32
+
+CIRCUITPY_ESP_FLASH_MODE=qio
+CIRCUITPY_ESP_FLASH_FREQ=40m
+CIRCUITPY_ESP_FLASH_SIZE=16MB
+
+CIRCUITPY_BITBANG_APA102 = 1
+
+# Include these Python libraries in firmware.
+FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DotStar \ No newline at end of file
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/pins.c b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/pins.c
new file mode 100644
index 000000000..9476cafdf
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/pins.c
@@ -0,0 +1,56 @@
+#include "shared-bindings/board/__init__.h"
+
+STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO17) },
+ { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO17) },
+
+ { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO18) },
+ { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO18) },
+
+ { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO12) },
+ { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO12) },
+
+ { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO6) },
+ { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO6) },
+
+ { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO5) },
+ { MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO5) },
+
+ { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO36) },
+ { MP_ROM_QSTR(MP_QSTR_D25), MP_ROM_PTR(&pin_GPIO36) },
+
+ { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO35) },
+ { MP_ROM_QSTR(MP_QSTR_D24), MP_ROM_PTR(&pin_GPIO35) },
+
+ { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO37) },
+ { MP_ROM_QSTR(MP_QSTR_D23), MP_ROM_PTR(&pin_GPIO37) },
+
+ { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO44) },
+ { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) },
+
+ { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO43) },
+ { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) },
+
+ // Moving to 9 and 8
+ { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO38) },
+ { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO33) },
+ { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO1) },
+ { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO3) },
+ { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO7) },
+ { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO8) },
+ { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO9) },
+ { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO10) },
+ { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO11) },
+
+ { MP_ROM_QSTR(MP_QSTR_APA102_MOSI), MP_ROM_PTR(&pin_GPIO40) }, // MTDO
+ { MP_ROM_QSTR(MP_QSTR_APA102_SCK), MP_ROM_PTR(&pin_GPIO45) },
+ { MP_ROM_QSTR(MP_QSTR_LDO2), MP_ROM_PTR(&pin_GPIO21) },
+
+ { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO13) },
+ { MP_ROM_QSTR(MP_QSTR_AMB), MP_ROM_PTR(&pin_GPIO4) },
+
+ { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
+ { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
+ { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) },
+};
+MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);
diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/sdkconfig b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/sdkconfig
new file mode 100644
index 000000000..b73c4a8c2
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/sdkconfig
@@ -0,0 +1,35 @@
+CONFIG_ESP32S2_SPIRAM_SUPPORT=y
+
+#
+# SPI RAM config
+#
+# CONFIG_SPIRAM_TYPE_AUTO is not set
+# CONFIG_SPIRAM_TYPE_ESPPSRAM16 is not set
+# CONFIG_SPIRAM_TYPE_ESPPSRAM32 is not set
+CONFIG_SPIRAM_TYPE_ESPPSRAM64=y
+CONFIG_SPIRAM_SIZE=8388608
+
+#
+# PSRAM clock and cs IO for ESP32S2
+#
+CONFIG_DEFAULT_PSRAM_CLK_IO=30
+CONFIG_DEFAULT_PSRAM_CS_IO=26
+# end of PSRAM clock and cs IO for ESP32S2
+
+CONFIG_SPIRAM_SPIWP_SD3_PIN=28
+# CONFIG_SPIRAM_FETCH_INSTRUCTIONS is not set
+# CONFIG_SPIRAM_RODATA is not set
+# CONFIG_SPIRAM_USE_AHB_DBUS3 is not set
+# CONFIG_SPIRAM_SPEED_80M is not set
+CONFIG_SPIRAM_SPEED_40M=y
+# CONFIG_SPIRAM_SPEED_26M is not set
+# CONFIG_SPIRAM_SPEED_20M is not set
+CONFIG_SPIRAM=y
+CONFIG_SPIRAM_BOOT_INIT=y
+# CONFIG_SPIRAM_IGNORE_NOTFOUND is not set
+CONFIG_SPIRAM_USE_MEMMAP=y
+# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set
+# CONFIG_SPIRAM_USE_MALLOC is not set
+CONFIG_SPIRAM_MEMTEST=y
+# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set
+# end of SPI RAM config
diff --git a/ports/esp32s2/boards/unexpectedmaker_pros2/adafruit_dotstar.py b/ports/esp32s2/boards/unexpectedmaker_pros2/adafruit_dotstar.py
new file mode 100755
index 000000000..65fbbe868
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_pros2/adafruit_dotstar.py
@@ -0,0 +1,198 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Damien P. George (original Neopixel object)
+# Copyright (c) 2017 Ladyada
+# Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
+# Copyright (c) 2019 Roy Hooper
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""
+`adafruit_dotstar` - DotStar strip driver (for CircuitPython 5.0+ with _pixelbuf)
+=================================================================================
+
+* Author(s): Damien P. George, Limor Fried, Scott Shawcroft & Roy Hooper
+"""
+
+# pylint: disable=ungrouped-imports
+import sys
+import busio
+import digitalio
+
+if sys.implementation.version[0] < 5:
+ import adafruit_pypixelbuf as _pixelbuf
+else:
+ try:
+ import _pixelbuf
+ except ImportError:
+ import adafruit_pypixelbuf as _pixelbuf
+
+__version__ = "0.0.0-auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DotStar.git"
+
+START_HEADER_SIZE = 4
+
+# Pixel color order constants
+RBG = "PRBG"
+"""Red Blue Green"""
+RGB = "PRGB"
+"""Red Green Blue"""
+GRB = "PGRB"
+"""Green Red Blue"""
+GBR = "PGBR"
+"""Green Blue Red"""
+BRG = "PBRG"
+"""Blue Red Green"""
+BGR = "PBGR"
+"""Blue Green Red"""
+
+
+class DotStar(_pixelbuf.PixelBuf):
+ """
+ A sequence of dotstars.
+
+ :param ~microcontroller.Pin clock: The pin to output dotstar clock on.
+ :param ~microcontroller.Pin data: The pin to output dotstar data on.
+ :param int n: The number of dotstars in the chain
+ :param float brightness: Brightness of the pixels between 0.0 and 1.0
+ :param bool auto_write: True if the dotstars should immediately change when
+ set. If False, `show` must be called explicitly.
+ :param str pixel_order: Set the pixel order on the strip - different
+ strips implement this differently. If you send red, and it looks blue
+ or green on the strip, modify this! It should be one of the values above.
+ :param int baudrate: Desired clock rate if using hardware SPI (ignored if
+ using 'soft' SPI). This is only a recommendation; the actual clock
+ rate may be slightly different depending on what the system hardware
+ can provide.
+
+ Example for Gemma M0:
+
+ .. code-block:: python
+
+ import adafruit_dotstar
+ import time
+ from board import *
+
+ RED = 0x100000
+
+ with adafruit_dotstar.DotStar(APA102_SCK, APA102_MOSI, 1) as pixels:
+ pixels[0] = RED
+ time.sleep(2)
+
+ .. py:method:: DotStar.show()
+
+ Shows the new colors on the dotstars themselves if they haven't already
+ been autowritten.
+
+ The colors may or may not be showing after this function returns because
+ it may be done asynchronously.
+
+ .. py:method:: DotStar.fill(color)
+
+ Colors all dotstars the given ***color***.
+
+ .. py:attribute:: brightness
+
+ Overall brightness of all dotstars (0 to 1.0)
+ """
+
+ def __init__(
+ self,
+ clock,
+ data,
+ n,
+ *,
+ brightness=1.0,
+ auto_write=True,
+ pixel_order=BGR,
+ baudrate=4000000
+ ):
+ self._spi = None
+ try:
+ self._spi = busio.SPI(clock, MOSI=data)
+ while not self._spi.try_lock():
+ pass
+ self._spi.configure(baudrate=baudrate)
+
+ except (NotImplementedError, ValueError):
+ self.dpin = digitalio.DigitalInOut(data)
+ self.cpin = digitalio.DigitalInOut(clock)
+ self.dpin.direction = digitalio.Direction.OUTPUT
+ self.cpin.direction = digitalio.Direction.OUTPUT
+ self.cpin.value = False
+
+ # Supply one extra clock cycle for each two pixels in the strip.
+ trailer_size = n // 16
+ if n % 16 != 0:
+ trailer_size += 1
+
+ # Four empty bytes for the header.
+ header = bytearray(START_HEADER_SIZE)
+ # 0xff bytes for the trailer.
+ trailer = bytearray(b"\xff") * trailer_size
+
+ super().__init__(
+ n,
+ byteorder=pixel_order,
+ brightness=brightness,
+ auto_write=auto_write,
+ header=header,
+ trailer=trailer,
+ )
+
+ def deinit(self):
+ """Blank out the DotStars and release the resources."""
+ self.fill(0)
+ self.show()
+ if self._spi:
+ self._spi.deinit()
+ else:
+ self.dpin.deinit()
+ self.cpin.deinit()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exception_type, exception_value, traceback):
+ self.deinit()
+
+ def __repr__(self):
+ return "[" + ", ".join([str(x) for x in self]) + "]"
+
+ @property
+ def n(self):
+ """
+ The number of dotstars in the chain (read-only)
+ """
+ return len(self)
+
+ def _transmit(self, buffer):
+ if self._spi:
+ self._spi.write(buffer)
+ else:
+ self._ds_writebytes(buffer)
+
+ def _ds_writebytes(self, buffer):
+ for b in buffer:
+ for _ in range(8):
+ self.dpin.value = b & 0x80
+ self.cpin.value = True
+ self.cpin.value = False
+ b = b << 1
+ self.cpin.value = False
diff --git a/ports/esp32s2/boards/unexpectedmaker_pros2/adafruit_pypixelbuf.py b/ports/esp32s2/boards/unexpectedmaker_pros2/adafruit_pypixelbuf.py
new file mode 100755
index 000000000..3cb83e970
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_pros2/adafruit_pypixelbuf.py
@@ -0,0 +1,374 @@
+# The MIT License (MIT)
+#
+# Based on the Adafruit NeoPixel and Adafruit Dotstar CircuitPython drivers.
+# Copyright (c) 2019-2020 Roy Hooper
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""
+`adafruit_pypixelbuf` - A pure python implementation of _pixelbuf
+=================================================================
+This class is used when _pixelbuf is not available in CircuitPython. It is based on the work
+in neopixel.py and adafruit_dotstar.py.
+
+* Author(s): Damien P. George & Limor Fried & Scott Shawcroft & Roy Hooper
+"""
+
+DOTSTAR_LED_START_FULL_BRIGHT = 0xFF
+DOTSTAR_LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits
+DOTSTAR_LED_BRIGHTNESS = 0b00011111
+
+
+class PixelBuf: # pylint: disable=too-many-instance-attributes
+ """
+ A sequence of RGB/RGBW pixels.
+
+ This is the pure python implementation of CircuitPython's _pixelbuf.
+
+ :param ~int n: Number of pixels
+ :param ~str byteorder: Byte order string constant (also sets bpp)
+ :param ~float brightness: Brightness (0 to 1.0, default 1.0)
+ :param ~bool auto_write: Whether to automatically write pixels (Default False)
+ :param bytes header: Sequence of bytes to always send before pixel values.
+ :param bytes trailer: Sequence of bytes to always send after pixel values.
+ """
+
+ def __init__( # pylint: disable=too-many-locals,too-many-arguments
+ self,
+ n,
+ byteorder="BGR",
+ brightness=1.0,
+ auto_write=False,
+ header=None,
+ trailer=None,
+ ):
+
+ bpp, byteorder_tuple, has_white, dotstar_mode = self.parse_byteorder(byteorder)
+
+ self.auto_write = False
+
+ effective_bpp = 4 if dotstar_mode else bpp
+ _bytes = effective_bpp * n
+ buf = bytearray(_bytes)
+ offset = 0
+
+ if header is not None:
+ if not isinstance(header, bytearray):
+ raise TypeError("header must be a bytearray")
+ buf = header + buf
+ offset = len(header)
+
+ if trailer is not None:
+ if not isinstance(trailer, bytearray):
+ raise TypeError("trailer must be a bytearray")
+ buf += trailer
+
+ self._pixels = n
+ self._bytes = _bytes
+ self._byteorder = byteorder_tuple
+ self._byteorder_string = byteorder
+ self._has_white = has_white
+ self._bpp = bpp
+ self._pre_brightness_buffer = None
+ self._post_brightness_buffer = buf
+ self._offset = offset
+ self._dotstar_mode = dotstar_mode
+ self._pixel_step = effective_bpp
+
+ if dotstar_mode:
+ self._byteorder_tuple = (
+ byteorder_tuple[0] + 1,
+ byteorder_tuple[1] + 1,
+ byteorder_tuple[2] + 1,
+ 0,
+ )
+ # Initialize the buffer with the dotstar start bytes.
+ for i in range(self._offset, self._bytes + self._offset, 4):
+ self._post_brightness_buffer[i] = DOTSTAR_LED_START_FULL_BRIGHT
+
+ self._brightness = 1.0
+ self.brightness = brightness
+
+ self.auto_write = auto_write
+
+ @staticmethod
+ def parse_byteorder(byteorder):
+ """
+ Parse a Byteorder string for validity and determine bpp, byte order, and
+ dostar brightness bits.
+
+ Byteorder strings may contain the following characters:
+ R - Red
+ G - Green
+ B - Blue
+ W - White
+ P - PWM (PWM Duty cycle for pixel - dotstars 0 - 1.0)
+
+ :param: ~str bpp: bpp string.
+ :return: ~tuple: bpp, byteorder, has_white, dotstar_mode
+ """
+ bpp = len(byteorder)
+ dotstar_mode = False
+ has_white = False
+
+ if byteorder.strip("RGBWP") != "":
+ raise ValueError("Invalid Byteorder string")
+
+ try:
+ r = byteorder.index("R")
+ g = byteorder.index("G")
+ b = byteorder.index("B")
+ except ValueError:
+ raise ValueError("Invalid Byteorder string")
+ if "W" in byteorder:
+ w = byteorder.index("W")
+ byteorder = (r, g, b, w)
+ has_white = True
+ elif "P" in byteorder:
+ lum = byteorder.index("P")
+ byteorder = (r, g, b, lum)
+ dotstar_mode = True
+ else:
+ byteorder = (r, g, b)
+
+ return bpp, byteorder, has_white, dotstar_mode
+
+ @property
+ def bpp(self):
+ """
+ The number of bytes per pixel in the buffer (read-only).
+ """
+ return self._bpp
+
+ @property
+ def brightness(self):
+ """
+ Float value between 0 and 1. Output brightness.
+
+ When brightness is less than 1.0, a second buffer will be used to store the color values
+ before they are adjusted for brightness.
+ """
+ return self._brightness
+
+ @brightness.setter
+ def brightness(self, value):
+ value = min(max(value, 0.0), 1.0)
+ change = value - self._brightness
+ if -0.001 < change < 0.001:
+ return
+
+ self._brightness = value
+
+ if self._pre_brightness_buffer is None:
+ self._pre_brightness_buffer = bytearray(self._post_brightness_buffer)
+
+ # Adjust brightness of existing pixels
+ offset_check = self._offset % self._pixel_step
+ for i in range(self._offset, self._bytes + self._offset):
+ # Don't adjust per-pixel luminance bytes in dotstar mode
+ if self._dotstar_mode and (i % 4 != offset_check):
+ continue
+ self._post_brightness_buffer[i] = int(
+ self._pre_brightness_buffer[i] * self._brightness
+ )
+
+ if self.auto_write:
+ self.show()
+
+ @property
+ def byteorder(self):
+ """
+ ByteOrder string for the buffer (read-only)
+ """
+ return self._byteorder_string
+
+ def __len__(self):
+ """
+ Number of pixels.
+ """
+ return self._pixels
+
+ def show(self):
+ """
+ Call the associated write function to display the pixels
+ """
+ return self._transmit(self._post_brightness_buffer)
+
+ def fill(self, color):
+ """
+ Fills the given pixelbuf with the given color.
+ :param pixelbuf: A pixel object.
+ :param color: Color to set.
+ """
+ r, g, b, w = self._parse_color(color)
+ for i in range(self._pixels):
+ self._set_item(i, r, g, b, w)
+ if self.auto_write:
+ self.show()
+
+ def _parse_color(self, value):
+ r = 0
+ g = 0
+ b = 0
+ w = 0
+ if isinstance(value, int):
+ r = value >> 16
+ g = (value >> 8) & 0xFF
+ b = value & 0xFF
+ w = 0
+
+ if self._dotstar_mode:
+ w = 1.0
+ else:
+ if len(value) < 3 or len(value) > 4:
+ raise ValueError(
+ "Expected tuple of length {}, got {}".format(self._bpp, len(value))
+ )
+ if len(value) == self._bpp:
+ if self._bpp == 3:
+ r, g, b = value
+ else:
+ r, g, b, w = value
+ elif len(value) == 3:
+ r, g, b = value
+ if self._dotstar_mode:
+ w = 1.0
+
+ if self._bpp == 4:
+ if self._dotstar_mode:
+ # LED startframe is three "1" bits, followed by 5 brightness bits
+ # then 8 bits for each of R, G, and B. The order of those 3 are configurable and
+ # vary based on hardware
+ w = (int(w * 31) & 0b00011111) | DOTSTAR_LED_START
+ elif (
+ self._has_white
+ and (isinstance(value, int) or len(value) == 3)
+ and r == g
+ and g == b
+ ):
+ # If all components are the same and we have a white pixel then use it
+ # instead of the individual components when all 4 values aren't explicitly given.
+ w = r
+ r = 0
+ g = 0
+ b = 0
+
+ return (r, g, b, w)
+
+ def _set_item(
+ self, index, r, g, b, w
+ ): # pylint: disable=too-many-locals,too-many-branches,too-many-arguments
+ if index < 0:
+ index += len(self)
+ if index >= self._pixels or index < 0:
+ raise IndexError
+ offset = self._offset + (index * self._bpp)
+
+ if self._pre_brightness_buffer is not None:
+ if self._bpp == 4:
+ self._pre_brightness_buffer[offset + self._byteorder[3]] = w
+ self._pre_brightness_buffer[offset + self._byteorder[0]] = r
+ self._pre_brightness_buffer[offset + self._byteorder[1]] = g
+ self._pre_brightness_buffer[offset + self._byteorder[2]] = b
+
+ if self._bpp == 4:
+ # Only apply brightness if w is actually white (aka not DotStar.)
+ if not self._dotstar_mode:
+ w = int(w * self._brightness)
+ self._post_brightness_buffer[offset + self._byteorder[3]] = w
+
+ self._post_brightness_buffer[offset + self._byteorder[0]] = int(
+ r * self._brightness
+ )
+ self._post_brightness_buffer[offset + self._byteorder[1]] = int(
+ g * self._brightness
+ )
+ self._post_brightness_buffer[offset + self._byteorder[2]] = int(
+ b * self._brightness
+ )
+
+ def __setitem__(self, index, val):
+ if isinstance(index, slice):
+ start, stop, step = index.indices(self._pixels)
+ for val_i, in_i in enumerate(range(start, stop, step)):
+ r, g, b, w = self._parse_color(val[val_i])
+ self._set_item(in_i, r, g, b, w)
+ else:
+ r, g, b, w = self._parse_color(val)
+ self._set_item(index, r, g, b, w)
+
+ if self.auto_write:
+ self.show()
+
+ def _getitem(self, index):
+ start = self._offset + (index * self._bpp)
+ buffer = (
+ self._pre_brightness_buffer
+ if self._pre_brightness_buffer is not None
+ else self._post_brightness_buffer
+ )
+ value = [
+ buffer[start + self._byteorder[0]],
+ buffer[start + self._byteorder[1]],
+ buffer[start + self._byteorder[2]],
+ ]
+ if self._has_white:
+ value.append(buffer[start + self._byteorder[3]])
+ elif self._dotstar_mode:
+ value.append(
+ (buffer[start + self._byteorder[3]] & DOTSTAR_LED_BRIGHTNESS) / 31.0
+ )
+ return value
+
+ def __getitem__(self, index):
+ if isinstance(index, slice):
+ out = []
+ for in_i in range(
+ *index.indices(len(self._post_brightness_buffer) // self._bpp)
+ ):
+ out.append(self._getitem(in_i))
+ return out
+ if index < 0:
+ index += len(self)
+ if index >= self._pixels or index < 0:
+ raise IndexError
+ return self._getitem(index)
+
+ def _transmit(self, buffer):
+ raise NotImplementedError("Must be subclassed")
+
+
+def wheel(pos):
+ """
+ Helper to create a colorwheel.
+
+ :param pos: int 0-255 of color value to return
+ :return: tuple of RGB values
+ """
+ # Input a value 0 to 255 to get a color value.
+ # The colours are a transition r - g - b - back to r.
+ if pos < 0 or pos > 255:
+ return 0, 0, 0
+ if pos < 85:
+ return 255 - pos * 3, pos * 3, 0
+ if pos < 170:
+ pos -= 85
+ return 0, 255 - pos * 3, pos * 3
+ pos -= 170
+ return pos * 3, 0, 255 - pos * 3
diff --git a/ports/esp32s2/boards/unexpectedmaker_pros2/board.c b/ports/esp32s2/boards/unexpectedmaker_pros2/board.c
new file mode 100644
index 000000000..1dc30b5af
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_pros2/board.c
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "boards/board.h"
+#include "mpconfigboard.h"
+#include "shared-bindings/microcontroller/Pin.h"
+
+void board_init(void) {
+ // USB
+ common_hal_never_reset_pin(&pin_GPIO19);
+ common_hal_never_reset_pin(&pin_GPIO20);
+
+ // Debug UART
+ common_hal_never_reset_pin(&pin_GPIO43);
+ common_hal_never_reset_pin(&pin_GPIO44);
+
+ // SPI Flash and RAM
+ common_hal_never_reset_pin(&pin_GPIO26);
+ common_hal_never_reset_pin(&pin_GPIO27);
+ common_hal_never_reset_pin(&pin_GPIO28);
+ common_hal_never_reset_pin(&pin_GPIO29);
+ common_hal_never_reset_pin(&pin_GPIO30);
+ common_hal_never_reset_pin(&pin_GPIO31);
+ common_hal_never_reset_pin(&pin_GPIO32);
+}
+
+bool board_requests_safe_mode(void) {
+ return false;
+}
+
+void reset_board(void) {
+
+}
diff --git a/ports/esp32s2/boards/unexpectedmaker_pros2/mpconfigboard.h b/ports/esp32s2/boards/unexpectedmaker_pros2/mpconfigboard.h
new file mode 100644
index 000000000..2cfb7b6a0
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_pros2/mpconfigboard.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+//Micropython setup
+
+#define MICROPY_HW_BOARD_NAME "ProS2"
+#define MICROPY_HW_MCU_NAME "ESP32S2"
+
+#define AUTORESET_DELAY_MS 500
+
+#define MICROPY_HW_APA102_MOSI (&pin_GPIO40)
+#define MICROPY_HW_APA102_SCK (&pin_GPIO45)
diff --git a/ports/esp32s2/boards/unexpectedmaker_pros2/mpconfigboard.mk b/ports/esp32s2/boards/unexpectedmaker_pros2/mpconfigboard.mk
new file mode 100644
index 000000000..cb6823ba6
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_pros2/mpconfigboard.mk
@@ -0,0 +1,21 @@
+USB_VID = 0x239A
+USB_PID = 0x80AA
+USB_PRODUCT = "ProS2"
+USB_MANUFACTURER = "UnexpectedMaker"
+USB_DEVICES = "CDC,MSC,HID"
+
+INTERNAL_FLASH_FILESYSTEM = 1
+LONGINT_IMPL = MPZ
+
+# The default queue depth of 16 overflows on release builds,
+# so increase it to 32.
+CFLAGS += -DCFG_TUD_TASK_QUEUE_SZ=32
+
+CIRCUITPY_ESP_FLASH_MODE=qio
+CIRCUITPY_ESP_FLASH_FREQ=40m
+CIRCUITPY_ESP_FLASH_SIZE=16MB
+
+CIRCUITPY_BITBANG_APA102 = 1
+
+# Include these Python libraries in firmware.
+FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DotStar
diff --git a/ports/esp32s2/boards/unexpectedmaker_pros2/pins.c b/ports/esp32s2/boards/unexpectedmaker_pros2/pins.c
new file mode 100644
index 000000000..349ad1bd5
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_pros2/pins.c
@@ -0,0 +1,58 @@
+#include "shared-bindings/board/__init__.h"
+
+STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO17) },
+ { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO17) },
+
+ { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO18) },
+ { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO18) },
+
+ { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO4) },
+ { MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO4) },
+
+ { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO12) },
+ { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO12) },
+
+ { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO6) },
+ { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO6) },
+
+ { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO5) },
+ { MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO5) },
+
+ { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO36) },
+ { MP_ROM_QSTR(MP_QSTR_D25), MP_ROM_PTR(&pin_GPIO36) },
+
+ { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO35) },
+ { MP_ROM_QSTR(MP_QSTR_D24), MP_ROM_PTR(&pin_GPIO35) },
+
+ { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO37) },
+ { MP_ROM_QSTR(MP_QSTR_D23), MP_ROM_PTR(&pin_GPIO37) },
+
+ { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO44) },
+ { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) },
+
+ { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO43) },
+ { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) },
+
+ { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO8) },
+ { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO9) },
+ { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO1) },
+ { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO3) },
+ { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO7) },
+ { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO8) },
+ { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO9) },
+ { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO10) },
+ { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO11) },
+
+ { MP_ROM_QSTR(MP_QSTR_APA102_MOSI), MP_ROM_PTR(&pin_GPIO40) }, // MTDO
+ { MP_ROM_QSTR(MP_QSTR_APA102_SCK), MP_ROM_PTR(&pin_GPIO45) },
+
+ { MP_ROM_QSTR(MP_QSTR_LDO2), MP_ROM_PTR(&pin_GPIO21) },
+
+ { MP_ROM_QSTR(MP_QSTR_VBAT_SENSE), MP_ROM_PTR(&pin_GPIO14) },
+
+ { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
+ { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
+ { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) },
+};
+MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);
diff --git a/ports/esp32s2/boards/unexpectedmaker_pros2/pros2.py b/ports/esp32s2/boards/unexpectedmaker_pros2/pros2.py
new file mode 100644
index 000000000..3d2397d5d
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_pros2/pros2.py
@@ -0,0 +1,63 @@
+# Pros2 Helper Library
+# 2020 Seon Rozenblum, Unexpected Maker
+#
+# Project home:
+# https://pros2.io
+#
+
+# Import required libraries
+import time
+import board
+from digitalio import DigitalInOut, Direction, Pull
+from analogio import AnalogIn
+
+
+# Helper functions
+
+def enable_LDO2(state):
+ """Set the power for the second on-board LDO to allow no current draw when not needed."""
+
+ # Grab a reference to the LDO2 IO (21 in this case)
+ ldo2 = DigitalInOut(board.LDO2)
+ ldo2.direction = Direction.OUTPUT
+
+ # Set the LDO2 power pin on / off
+ ldo2.value = state
+
+ # A small delay to let the IO change state
+ time.sleep(0.035)
+
+
+# Dotstar rainbow colour wheel
+def dotstar_color_wheel(wheel_pos):
+ """Color wheel to allow for cycling through the rainbow of RGB colors."""
+ wheel_pos = wheel_pos % 255
+
+ if wheel_pos < 85:
+ return 255 - wheel_pos * 3, 0, wheel_pos * 3
+ elif wheel_pos < 170:
+ wheel_pos -= 85
+ return 0, wheel_pos * 3, 255 - wheel_pos * 3
+ else:
+ wheel_pos -= 170
+ return wheel_pos * 3, 255 - wheel_pos * 3, 0
+
+
+# Get a *rough* estimate of the current battery voltage
+# If the battery is not present, the charge IC will still report it's trying to charge at X voltage
+# so it will still show a voltage.
+def get_battery_voltage():
+ """
+ Returns the current battery voltage. If no battery is connected, returns 3.7V
+ This is an approximation only, but useful to detect of the charge state of the battery is getting low.
+ """
+ adc = AnalogIn(board.VBAT_SENSE)
+
+ measured_vbat = adc.value # Read the value
+ measured_vbat /= 4095 # divide by 4095 as we are using the default ADC voltage range of 0-1V
+ measured_vbat *= 3.7 # Multiply by 3.7V, our reference voltage
+ return measured_vbat
+
+
+# Disable LDO2 by default
+enable_LDO2(False)
diff --git a/ports/esp32s2/boards/unexpectedmaker_pros2/sdkconfig b/ports/esp32s2/boards/unexpectedmaker_pros2/sdkconfig
new file mode 100644
index 000000000..b73c4a8c2
--- /dev/null
+++ b/ports/esp32s2/boards/unexpectedmaker_pros2/sdkconfig
@@ -0,0 +1,35 @@
+CONFIG_ESP32S2_SPIRAM_SUPPORT=y
+
+#
+# SPI RAM config
+#
+# CONFIG_SPIRAM_TYPE_AUTO is not set
+# CONFIG_SPIRAM_TYPE_ESPPSRAM16 is not set
+# CONFIG_SPIRAM_TYPE_ESPPSRAM32 is not set
+CONFIG_SPIRAM_TYPE_ESPPSRAM64=y
+CONFIG_SPIRAM_SIZE=8388608
+
+#
+# PSRAM clock and cs IO for ESP32S2
+#
+CONFIG_DEFAULT_PSRAM_CLK_IO=30
+CONFIG_DEFAULT_PSRAM_CS_IO=26
+# end of PSRAM clock and cs IO for ESP32S2
+
+CONFIG_SPIRAM_SPIWP_SD3_PIN=28
+# CONFIG_SPIRAM_FETCH_INSTRUCTIONS is not set
+# CONFIG_SPIRAM_RODATA is not set
+# CONFIG_SPIRAM_USE_AHB_DBUS3 is not set
+# CONFIG_SPIRAM_SPEED_80M is not set
+CONFIG_SPIRAM_SPEED_40M=y
+# CONFIG_SPIRAM_SPEED_26M is not set
+# CONFIG_SPIRAM_SPEED_20M is not set
+CONFIG_SPIRAM=y
+CONFIG_SPIRAM_BOOT_INIT=y
+# CONFIG_SPIRAM_IGNORE_NOTFOUND is not set
+CONFIG_SPIRAM_USE_MEMMAP=y
+# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set
+# CONFIG_SPIRAM_USE_MALLOC is not set
+CONFIG_SPIRAM_MEMTEST=y
+# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set
+# end of SPI RAM config