Skip to content

Commit 469d661

Browse files
authored
Merge pull request #12 from makermelissa/master
Added middle graphics layer and additional Adafruit IO functions
2 parents 0e1529d + c66123f commit 469d661

File tree

3 files changed

+239
-66
lines changed

3 files changed

+239
-66
lines changed

adafruit_matrixportal/graphics.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams, written for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
"""
5+
`adafruit_matrixportal.graphics`
6+
================================================================================
7+
8+
Helper library for the Adafruit RGB Matrix Shield + Metro M4 Airlift Lite.
9+
10+
* Author(s): Melissa LeBlanc-Williams
11+
12+
Implementation Notes
13+
--------------------
14+
15+
**Hardware:**
16+
17+
* `Adafruit Metro M4 Express AirLift <https://www.adafruit.com/product/4000>`_
18+
* `Adafruit RGB Matrix Shield <https://www.adafruit.com/product/2601>`_
19+
* `64x32 RGB LED Matrix <https://www.adafruit.com/product/2278>`_
20+
21+
**Software and Dependencies:**
22+
23+
* Adafruit CircuitPython firmware for the supported boards:
24+
https://github.com/adafruit/circuitpython/releases
25+
26+
"""
27+
28+
import gc
29+
import displayio
30+
from adafruit_matrixportal.matrix import Matrix
31+
32+
__version__ = "0.0.0-auto.0"
33+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MatrixPortal.git"
34+
35+
36+
class Graphics:
37+
"""Graphics Helper Class for the MatrixPortal Library
38+
39+
:param default_bg: The path to your default background image file or a hex color.
40+
Defaults to 0x000000.
41+
:param width: The width of the display in Pixels. Defaults to 64.
42+
:param height: The height of the display in Pixels. Defaults to 32.
43+
:param int bit_depth: The number of bits per color channel. Defaults to 2.
44+
:param debug: Turn on debug print outs. Defaults to False.
45+
46+
"""
47+
48+
# pylint: disable=too-many-instance-attributes, too-many-locals, too-many-branches, too-many-statements
49+
def __init__(
50+
self, *, default_bg=0x000000, width=64, height=32, bit_depth=2, debug=False
51+
):
52+
53+
self._debug = debug
54+
matrix = Matrix(bit_depth=bit_depth, width=width, height=height)
55+
self.display = matrix.display
56+
57+
if self._debug:
58+
print("Init display")
59+
self.splash = displayio.Group(max_size=15)
60+
61+
if self._debug:
62+
print("Init background")
63+
self._bg_group = displayio.Group(max_size=1)
64+
self._bg_file = None
65+
self._default_bg = default_bg
66+
self.splash.append(self._bg_group)
67+
68+
# set the default background
69+
self.set_background(self._default_bg)
70+
self.display.show(self.splash)
71+
72+
gc.collect()
73+
74+
def set_background(self, file_or_color, position=None):
75+
"""The background image to a bitmap file.
76+
77+
:param file_or_color: The filename of the chosen background image, or a hex color.
78+
79+
"""
80+
print("Set background to ", file_or_color)
81+
while self._bg_group:
82+
self._bg_group.pop()
83+
84+
if not position:
85+
position = (0, 0) # default in top corner
86+
87+
if not file_or_color:
88+
return # we're done, no background desired
89+
if self._bg_file:
90+
self._bg_file.close()
91+
if isinstance(file_or_color, str): # its a filenme:
92+
self._bg_file = open(file_or_color, "rb")
93+
background = displayio.OnDiskBitmap(self._bg_file)
94+
self._bg_sprite = displayio.TileGrid(
95+
background,
96+
pixel_shader=displayio.ColorConverter(),
97+
x=position[0],
98+
y=position[1],
99+
)
100+
elif isinstance(file_or_color, int):
101+
# Make a background color fill
102+
color_bitmap = displayio.Bitmap(self.display.width, self.display.height, 1)
103+
color_palette = displayio.Palette(1)
104+
color_palette[0] = file_or_color
105+
self._bg_sprite = displayio.TileGrid(
106+
color_bitmap, pixel_shader=color_palette, x=position[0], y=position[1],
107+
)
108+
else:
109+
raise RuntimeError("Unknown type of background")
110+
self._bg_group.append(self._bg_sprite)
111+
self.display.refresh()
112+
gc.collect()

adafruit_matrixportal/matrixportal.py

Lines changed: 66 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@
2626
"""
2727

2828
import gc
29+
from time import sleep
2930
import terminalio
30-
import displayio
3131
from adafruit_bitmap_font import bitmap_font
3232
from adafruit_display_text.label import Label
3333
from adafruit_matrixportal.network import Network
34-
from adafruit_matrixportal.matrix import Matrix
34+
from adafruit_matrixportal.graphics import Graphics
3535

3636
__version__ = "0.0.0-auto.0"
3737
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MatrixPortal.git"
@@ -79,8 +79,10 @@ def __init__(
7979
):
8080

8181
self._debug = debug
82-
matrix = Matrix(bit_depth=bit_depth, width=64, height=32)
83-
self.display = matrix.display
82+
self._graphics = Graphics(
83+
default_bg=default_bg, bit_depth=bit_depth, width=64, height=32, debug=debug
84+
)
85+
self.display = self._graphics.display
8486

8587
self._network = Network(
8688
status_neopixel=status_neopixel,
@@ -98,20 +100,7 @@ def __init__(
98100

99101
self._regexp_path = regexp_path
100102

101-
if self._debug:
102-
print("Init display")
103-
self.splash = displayio.Group(max_size=15)
104-
105-
if self._debug:
106-
print("Init background")
107-
self._bg_group = displayio.Group(max_size=1)
108-
self._bg_file = None
109-
self._default_bg = default_bg
110-
self.splash.append(self._bg_group)
111-
112-
# set the default background
113-
self.set_background(self._default_bg)
114-
self.display.show(self.splash)
103+
self.splash = self._graphics.splash
115104

116105
# Add any JSON translators
117106
if json_transform:
@@ -200,39 +189,7 @@ def set_background(self, file_or_color, position=None):
200189
:param file_or_color: The filename of the chosen background image, or a hex color.
201190
202191
"""
203-
print("Set background to ", file_or_color)
204-
while self._bg_group:
205-
self._bg_group.pop()
206-
207-
if not position:
208-
position = (0, 0) # default in top corner
209-
210-
if not file_or_color:
211-
return # we're done, no background desired
212-
if self._bg_file:
213-
self._bg_file.close()
214-
if isinstance(file_or_color, str): # its a filenme:
215-
self._bg_file = open(file_or_color, "rb")
216-
background = displayio.OnDiskBitmap(self._bg_file)
217-
self._bg_sprite = displayio.TileGrid(
218-
background,
219-
pixel_shader=displayio.ColorConverter(),
220-
x=position[0],
221-
y=position[1],
222-
)
223-
elif isinstance(file_or_color, int):
224-
# Make a background color fill
225-
color_bitmap = displayio.Bitmap(self.display.width, self.display.height, 1)
226-
color_palette = displayio.Palette(1)
227-
color_palette[0] = file_or_color
228-
self._bg_sprite = displayio.TileGrid(
229-
color_bitmap, pixel_shader=color_palette, x=position[0], y=position[1],
230-
)
231-
else:
232-
raise RuntimeError("Unknown type of background")
233-
self._bg_group.append(self._bg_sprite)
234-
self.display.refresh()
235-
gc.collect()
192+
self._graphics.set_background(file_or_color, position)
236193

237194
def preload_font(self, glyphs=None):
238195
# pylint: disable=line-too-long
@@ -307,25 +264,77 @@ def _get_next_scrollable_text_index(self):
307264
if index == self._scrolling_index:
308265
return None
309266

267+
def push_to_io(self, feed_key, data):
268+
"""Push data to an adafruit.io feed
269+
270+
:param str feed_key: Name of feed key to push data to.
271+
:param data: data to send to feed
272+
273+
"""
274+
275+
self._network.push_to_io(feed_key, data)
276+
277+
def get_io_data(self, feed_key):
278+
"""Return all values from the Adafruit IO Feed Data that matches the feed key
279+
280+
:param str feed_key: Name of feed key to receive data from.
281+
282+
"""
283+
284+
return self._network.get_io_data(feed_key)
285+
286+
def get_io_feed(self, feed_key, detailed=False):
287+
"""Return the Adafruit IO Feed that matches the feed key
288+
289+
:param str feed_key: Name of feed key to match.
290+
:param bool detailed: Whether to return additional detailed information
291+
292+
"""
293+
return self._network.get_io_feed(feed_key, detailed)
294+
295+
def get_io_group(self, group_key):
296+
"""Return the Adafruit IO Group that matches the group key
297+
298+
:param str group_key: Name of group key to match.
299+
300+
"""
301+
return self._network.get_io_group(group_key)
302+
310303
def scroll(self):
311-
"""Scroll any text that needs scrolling. We also want to queue up
312-
multiple lines one after another. To get simultaneous lines, we can
313-
simply use a line break."""
304+
"""Scroll any text that needs scrolling by a single frame. We also
305+
we want to queue up multiple lines one after another. To get
306+
simultaneous lines, we can simply use a line break.
307+
"""
314308

315309
if self._scrolling_index is None: # Not initialized yet
316310
next_index = self._get_next_scrollable_text_index()
317311
if next_index is None:
318312
return
319313
self._scrolling_index = next_index
320314

321-
# set line to label with self._scrolling_index
322-
323315
self._text[self._scrolling_index].x = self._text[self._scrolling_index].x - 1
324316
line_width = self._text[self._scrolling_index].bounding_box[2]
325317
if self._text[self._scrolling_index].x < -line_width:
326318
# Find the next line
327319
self._scrolling_index = self._get_next_scrollable_text_index()
328-
self._text[self._scrolling_index].x = self.display.width
320+
self._text[self._scrolling_index].x = self._graphics.display.width
321+
322+
def scroll_text(self, frame_delay=0.02):
323+
"""Scroll the entire text all the way across. We also
324+
we want to queue up multiple lines one after another. To get
325+
simultaneous lines, we can simply use a line break.
326+
"""
327+
if self._scrolling_index is None: # Not initialized yet
328+
next_index = self._get_next_scrollable_text_index()
329+
if next_index is None:
330+
return
331+
self._scrolling_index = next_index
332+
333+
self._text[self._scrolling_index].x = self._graphics.display.width
334+
line_width = self._text[self._scrolling_index].bounding_box[2]
335+
for _ in range(self._graphics.display.width + line_width + 1):
336+
self.scroll()
337+
sleep(frame_delay)
329338

330339
def fetch(self, refresh_url=None, timeout=10):
331340
"""Fetch data from the url we initialized with, perfom any parsing,

adafruit_matrixportal/network.py

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -279,14 +279,7 @@ def connect(self):
279279
print("Retrying in 3 seconds...")
280280
time.sleep(3)
281281

282-
def push_to_io(self, feed_key, data):
283-
"""Push data to an adafruit.io feed
284-
285-
:param str feed_key: Name of feed key to push data to.
286-
:param data: data to send to feed
287-
288-
"""
289-
282+
def _get_io_client(self):
290283
try:
291284
aio_username = secrets["aio_username"]
292285
aio_key = secrets["aio_key"]
@@ -295,7 +288,17 @@ def push_to_io(self, feed_key, data):
295288
"Adafruit IO secrets are kept in secrets.py, please add them there!\n\n"
296289
) from KeyError
297290

298-
io_client = IO_HTTP(aio_username, aio_key, self._wifi.manager(secrets))
291+
return IO_HTTP(aio_username, aio_key, self._wifi.manager(secrets))
292+
293+
def push_to_io(self, feed_key, data):
294+
"""Push data to an adafruit.io feed
295+
296+
:param str feed_key: Name of feed key to push data to.
297+
:param data: data to send to feed
298+
299+
"""
300+
301+
io_client = self._get_io_client()
299302

300303
while True:
301304
try:
@@ -319,6 +322,55 @@ def push_to_io(self, feed_key, data):
319322
continue
320323
break
321324

325+
def get_io_feed(self, feed_key, detailed=False):
326+
"""Return the Adafruit IO Feed that matches the feed key
327+
328+
:param str feed_key: Name of feed key to match.
329+
:param bool detailed: Whether to return additional detailed information
330+
331+
"""
332+
io_client = self._get_io_client()
333+
334+
while True:
335+
try:
336+
return io_client.get_feed(feed_key, detailed=detailed)
337+
except RuntimeError as exception:
338+
print("An error occured, retrying! 1 -", exception)
339+
continue
340+
break
341+
342+
def get_io_group(self, group_key):
343+
"""Return the Adafruit IO Group that matches the group key
344+
345+
:param str group_key: Name of group key to match.
346+
347+
"""
348+
io_client = self._get_io_client()
349+
350+
while True:
351+
try:
352+
return io_client.get_group(group_key)
353+
except RuntimeError as exception:
354+
print("An error occured, retrying! 1 -", exception)
355+
continue
356+
break
357+
358+
def get_io_data(self, feed_key):
359+
"""Return all values from Adafruit IO Feed Data that matches the feed key
360+
361+
:param str feed_key: Name of feed key to receive data from.
362+
363+
"""
364+
io_client = self._get_io_client()
365+
366+
while True:
367+
try:
368+
return io_client.receive_all_data(feed_key)
369+
except RuntimeError as exception:
370+
print("An error occured, retrying! 1 -", exception)
371+
continue
372+
break
373+
322374
def fetch(self, url, *, headers=None, timeout=10):
323375
"""Fetch data from the specified url and return a response object"""
324376
gc.collect()

0 commit comments

Comments
 (0)