/ adafruit_trellism4.py
adafruit_trellism4.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
  4  #
  5  # Permission is hereby granted, free of charge, to any person obtaining a copy
  6  # of this software and associated documentation files (the "Software"), to deal
  7  # in the Software without restriction, including without limitation the rights
  8  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9  # copies of the Software, and to permit persons to whom the Software is
 10  # furnished to do so, subject to the following conditions:
 11  #
 12  # The above copyright notice and this permission notice shall be included in
 13  # all copies or substantial portions of the Software.
 14  #
 15  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 21  # THE SOFTWARE.
 22  """
 23  `adafruit_trellism4`
 24  ====================================================
 25  
 26  CircuitPython library for the Trellis M4 Express.
 27  
 28  * Author(s): Scott Shawcroft, Kattni Rembor
 29  
 30  Implementation Notes
 31  --------------------
 32  
 33  **Hardware:**
 34  
 35  # Add link to Trellis M4 Express when product is released.
 36  
 37  **Software and Dependencies:**
 38  
 39  * Adafruit CircuitPython firmware for the supported boards:
 40    https://github.com/adafruit/circuitpython/releases
 41  
 42  
 43  """
 44  
 45  import board
 46  import digitalio
 47  import neopixel
 48  import adafruit_matrixkeypad
 49  
 50  __version__ = "0.0.0-auto.0"
 51  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_TrellisM4.git"
 52  
 53  
 54  class _NeoPixelArray:
 55      """Creates a NeoPixel array for use in the ``TrellisM4Express`` class."""
 56  
 57      def __init__(self, pin, *, width, height, rotation=0):
 58          self._neopixel = neopixel.NeoPixel(pin, width * height, auto_write=True)
 59          if rotation % 90 != 0:
 60              raise ValueError("Only 90 degree rotations supported")
 61          self._rotation = rotation % 360
 62          if self._rotation in (90, 270):
 63              width, height = height, width
 64          self._width = width
 65          self._height = height
 66  
 67      def __setitem__(self, index, value):
 68          if not isinstance(index, tuple) or len(index) != 2:
 69              raise IndexError("Index must be tuple")
 70          if index[0] >= self.width or index[1] >= self.height:
 71              raise IndexError("Pixel assignment outside available coordinates.")
 72  
 73          offset = self._calculate_pixel_offset(index)
 74  
 75          self._neopixel[offset] = value
 76  
 77      def __getitem__(self, index):
 78          if not isinstance(index, tuple) or len(index) != 2:
 79              raise IndexError("Index must be tuple")
 80          if index[0] >= self.width or index[1] >= self.height:
 81              raise IndexError("Pixel outside available coordinates.")
 82  
 83          offset = self._calculate_pixel_offset(index)
 84  
 85          return self._neopixel[offset]
 86  
 87      def _calculate_pixel_offset(self, index):
 88          if self._rotation == 0 or self._rotation == 180:
 89              offset = self.width * index[1] + index[0]
 90              if self._rotation == 180:
 91                  offset = self.width * self.height - offset - 1
 92          elif self._rotation == 270:
 93              offset = self.height * index[0] + (self.height - index[1] - 1)
 94          elif self._rotation == 90:
 95              offset = self.height * (self.width - index[0] - 1) + index[1]
 96  
 97          if offset < 0:
 98              raise IndexError("Pixel outside available coordinates.")
 99  
100          return offset
101  
102      def show(self):
103          """
104          Shows the new colors on the pixels themselves if they haven't already
105          been autowritten.
106  
107          Use when ``auto_write`` is set to ``False``.
108  
109          The colors may or may not be showing after this function returns because
110          it may be done asynchronously.
111          """
112          self._neopixel.show()
113  
114      @property
115      def auto_write(self):
116          """
117          True if the neopixels should immediately change when set. If False,
118          ``show`` must be called explicitly.
119  
120          This example disables ``auto_write``, sets every pixel, calls ``show()``, then
121          re-enables ``auto-write``.
122  
123          .. code-block:: python
124  
125              import adafruit_trellism4
126  
127              trellis = adafruit_trellism4.TrellisM4Express()
128  
129              trellis.pixels.auto_write = False
130  
131              for x in range(trellis.pixels.width):
132                  for y in range(trellis.pixels.height):
133                      trellis.pixels[x, y] = (0, 255, 0)
134  
135              trellis.pixels.show() # must call show() when auto_write == False
136  
137              trellis.pixels.auto_write = True
138          """
139          return self._neopixel.auto_write
140  
141      @auto_write.setter
142      def auto_write(self, val):
143          self._neopixel.auto_write = val
144  
145      @property
146      def brightness(self):
147          """
148          The overall brightness of the pixel. Must be a number between 0 and
149          1, where the number represents a percentage between 0 and 100, i.e. ``0.3`` is 30%.
150  
151          This example sets the brightness to ``0.3`` and turns all the LEDs red:
152  
153          .. code-block:: python
154  
155              import adafruit_trellism4
156  
157              trellis = adafruit_trellism4.TrellisM4Express()
158  
159              trellis.pixels.brightness = 0.3
160  
161              trellis.pixels.fill((255, 0, 0))
162          """
163          return self._neopixel.brightness
164  
165      @brightness.setter
166      def brightness(self, brightness):
167          self._neopixel.brightness = brightness
168  
169      def fill(self, color):
170          """
171          Colors all the pixels a given color.
172  
173          :param color: An (R, G, B) color tuple (such as (255, 0, 0) for red), or a hex color value
174                        (such as 0xff0000 for red).
175  
176          .. code-block:: python
177  
178              import adafruit_trellism4
179  
180              trellis = adafruit_trellism4.TrellisM4Express()
181  
182              trellis.pixels.fill((255, 0, 0))
183  
184          """
185          self._neopixel.fill(color)
186  
187      @property
188      def width(self):
189          """
190          The width of the grid. When ``rotation`` is 0, ``width`` is 8.
191  
192          .. code-block:: python
193  
194              import adafruit_trellism4
195  
196              trellis = adafruit_trellism4.TrellisM4Express()
197  
198              for x in range(trellis.pixels.width):
199                  for y in range(trellis.pixels.height):
200                      trellis.pixels[x, y] = (0, 0, 255)
201          """
202          return self._width
203  
204      @property
205      def height(self):
206          """The height of the grid. When ``rotation`` is 0, ``height`` is 4.
207  
208          .. code-block:: python
209  
210              import adafruit_trellism4
211  
212              trellis = adafruit_trellism4.TrellisM4Express()
213  
214              for x in range(trellis.pixels.width):
215                  for y in range(trellis.pixels.height):
216                      trellis.pixels[x, y] = (0, 0, 255)
217          """
218          return self._height
219  
220  
221  class TrellisM4Express:
222      """
223      Represents a single Trellis M4 Express. Do not use more than one at a time.
224  
225      :param rotation: Allows for rotating the Trellis M4 Express in 90 degree increments to different
226                       positions and utilising the grid from that position. Supports ``0``, ``90``,
227                       ``180``, and ``270``. ``0`` degrees is when the USB facing away from you.
228                       Default is 0.
229  
230      .. code-block:: python
231  
232           import time
233           import adafruit_trellism4
234  
235           trellis = adafruit_trellism4.TrellisM4Express()
236  
237           current_press = set()
238           while True:
239               pressed = set(trellis.pressed_keys)
240               for press in pressed - current_press:
241                  print("Pressed:", press)
242              for release in current_press - pressed:
243                  print("Released:", release)
244              time.sleep(0.08)
245              current_press = pressed
246      """
247  
248      def __init__(self, rotation=0):
249          self._rotation = rotation
250  
251          # Define NeoPixels
252          self.pixels = _NeoPixelArray(
253              board.NEOPIXEL, width=8, height=4, rotation=rotation
254          )
255          """Sequence like object representing the 32 NeoPixels on the Trellis M4 Express, Provides a
256          two dimensional representation of the NeoPixel grid.
257  
258          This example lights up the first pixel green:
259  
260          .. code-block:: python
261  
262              import adafruit_trellism4
263  
264              trellis = adafruit_trellism4.TrellisM4Express()
265  
266              trellis.pixels[0, 0] = (0, 255, 0)
267  
268          **Options for** ``pixels``:
269  
270          ``pixels.fill``: Colors all the pixels a given color. Provide an (R, G, B) color tuple
271          (such as (255, 0, 0) for red), or a hex color value (such as 0xff0000 for red).
272  
273              This example colors all pixels red:
274  
275              .. code-block:: python
276  
277                  import adafruit_trellism4
278  
279                  trellis = adafruit_trellism4.TrellisM4Express()
280  
281                  trellis.pixels.fill((255, 0, 0))
282  
283          ``pixels.width`` and ``pixels.height``: The width and height of the grid. When ``rotation``
284          is 0, ``width`` is 8 and ``height`` is 4.
285  
286              This example colors all pixels blue:
287  
288              .. code-block:: python
289  
290                  import adafruit_trellism4
291  
292                  trellis = adafruit_trellism4.TrellisM4Express()
293  
294                  for x in range(trellis.pixels.width):
295                      for y in range(trellis.pixels.height):
296                          trellis.pixels[x, y] = (0, 0, 255)
297  
298          ``pixels.brightness``: The overall brightness of the pixel. Must be a number between 0 and
299          1, where the number represents a percentage between 0 and 100, i.e. ``0.3`` is 30%.
300  
301              This example sets the brightness to ``0.3`` and turns all the LEDs red:
302  
303              .. code-block:: python
304  
305                  import adafruit_trellism4
306  
307                  trellis = adafruit_trellism4.TrellisM4Express()
308  
309                  trellis.pixels.brightness = 0.3
310  
311                  trellis.pixels.fill((255, 0, 0))
312          """
313  
314          cols = []
315          for x in range(8):
316              col = digitalio.DigitalInOut(getattr(board, "COL{}".format(x)))
317              cols.append(col)
318  
319          rows = []
320          for y in range(4):
321              row = digitalio.DigitalInOut(getattr(board, "ROW{}".format(y)))
322              rows.append(row)
323  
324          key_names = []
325          for y in range(8):
326              row = []
327              for x in range(4):
328                  if rotation == 0:
329                      coord = (y, x)
330                  elif rotation == 180:
331                      coord = (7 - y, 3 - x)
332                  elif rotation == 90:
333                      coord = (3 - x, y)
334                  elif rotation == 270:
335                      coord = (x, 7 - y)
336                  row.append(coord)
337              key_names.append(row)
338  
339          self._matrix = adafruit_matrixkeypad.Matrix_Keypad(cols, rows, key_names)
340  
341      @property
342      def pressed_keys(self):
343          """A list of tuples of currently pressed button coordinates.
344  
345          .. code-block:: python
346  
347              import time
348              import adafruit_trellism4
349  
350              trellis = adafruit_trellism4.TrellisM4Express()
351  
352              current_press = set()
353              while True:
354                  pressed = set(trellis.pressed_keys)
355                  for press in pressed - current_press:
356                      print("Pressed:", press)
357                  for release in current_press - pressed:
358                      print("Released:", release)
359                  time.sleep(0.08)
360                  current_press = pressed
361          """
362          return self._matrix.pressed_keys