Skip to content

Commit aba733e

Browse files
authored
Merge pull request #2267 from adafruit/TheKitty-patch-1
Code for new outside guide Tiny LED WiFi cube
2 parents fb01d61 + 261472d commit aba733e

File tree

3 files changed

+345
-0
lines changed

3 files changed

+345
-0
lines changed

QT_Py_Cube/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## Tiny LED WiFi cube
2+
3+
This is the repository for the Adafruit Learning System Guide Tiny LED WiFi cube by Charlyn G.
4+
5+
https://learn.adafruit.com/tiny-led-wifi-cube/overview

QT_Py_Cube/code.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# SPDX-FileCopyrightText: 2022 Charlyn Gonda for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
from secrets import secrets
5+
import ssl
6+
import busio
7+
import board
8+
import adafruit_lis3dh
9+
import wifi
10+
import socketpool
11+
import adafruit_requests
12+
13+
from adafruit_led_animation.color import (
14+
PURPLE, AMBER, JADE, CYAN, BLUE, GOLD, PINK)
15+
from adafruit_io.adafruit_io import IO_HTTP
16+
17+
from cube import Cube
18+
19+
# Specify pins
20+
top_cin = board.A0
21+
top_din = board.A1
22+
side_panels_cin = board.A2
23+
side_panels_din = board.A3
24+
bottom_cin = board.A4
25+
bottom_din = board.A5
26+
27+
# Initialize cube with pins
28+
cube = Cube(top_cin,
29+
top_din,
30+
side_panels_cin,
31+
side_panels_din,
32+
bottom_cin,
33+
bottom_din)
34+
35+
# Initial display to indicate the cube is on
36+
cube.waiting_mode()
37+
38+
# Setup for Accelerometer
39+
i2c = busio.I2C(board.SCL1, board.SDA1)
40+
lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c)
41+
42+
connected = False
43+
while not connected:
44+
try:
45+
wifi.radio.connect(secrets["ssid"], secrets["password"])
46+
print("Connected to %s!" % secrets["ssid"])
47+
print("My IP address is", wifi.radio.ipv4_address)
48+
connected = True
49+
# pylint: disable=broad-except
50+
except Exception as error:
51+
print(error)
52+
connected = False
53+
54+
55+
# Setup for http requests
56+
pool = socketpool.SocketPool(wifi.radio)
57+
REQUESTS = adafruit_requests.Session(pool, ssl.create_default_context())
58+
IO = IO_HTTP(secrets["aio_username"], secrets["aio_key"], REQUESTS)
59+
60+
# Data for top pixels, will be updated by update_data()
61+
TOP_PIXELS_ON = []
62+
TOP_PIXELS_COLOR = CYAN
63+
TOP_PIXELS_COLOR_MAP = {
64+
"PURPLE": PURPLE,
65+
"AMBER": AMBER,
66+
"JADE": JADE,
67+
"CYAN": CYAN,
68+
"BLUE": BLUE,
69+
"GOLD": GOLD,
70+
"PINK": PINK,
71+
}
72+
# Data for scrolling word, will be updated by update_data()
73+
CUBE_WORD = "... ..."
74+
75+
76+
def update_data():
77+
# pylint: disable=global-statement
78+
global CUBE_WORD, TOP_PIXELS_ON, TOP_PIXELS_COLOR
79+
if connected:
80+
print("Updating data from Adafruit IO")
81+
try:
82+
quote_feed = IO.get_feed('cube-words')
83+
quotes_data = IO.receive_data(quote_feed["key"])
84+
CUBE_WORD = quotes_data["value"]
85+
86+
pixel_feed = IO.get_feed('cube-pixels')
87+
pixel_data = IO.receive_data(pixel_feed["key"])
88+
color, pixels_list = pixel_data["value"].split("-")
89+
TOP_PIXELS_ON = pixels_list.split(",")
90+
TOP_PIXELS_COLOR = TOP_PIXELS_COLOR_MAP[color]
91+
# pylint: disable=broad-except
92+
except Exception as update_error:
93+
print(update_error)
94+
95+
96+
orientations = [
97+
"UP",
98+
"DOWN",
99+
"RIGHT",
100+
"LEFT",
101+
"FRONT",
102+
"BACK"
103+
]
104+
105+
# pylint: disable=inconsistent-return-statements
106+
107+
108+
def orientation(curr_x, curr_y, curr_z):
109+
absX = abs(curr_x)
110+
absY = abs(curr_y)
111+
absZ = abs(curr_z)
112+
113+
if absX > absY and absX > absZ:
114+
if x >= 0:
115+
return orientations[1] # up
116+
117+
return orientations[0] # down
118+
119+
if absZ > absY and absZ > absX: # when "down" is "up"
120+
if z >= 0:
121+
return orientations[2] # left
122+
123+
return orientations[3] # right
124+
125+
if absY > absX and absY > absZ:
126+
if y >= 0:
127+
return orientations[4] # front
128+
129+
return orientations[5] # back
130+
131+
132+
upside_down = False
133+
while True:
134+
x, y, z = lis3dh.acceleration
135+
oriented = orientation(x, y, z)
136+
137+
# clear cube when on one side
138+
# this orientation can be used while charging
139+
if oriented == orientations[5]: # "back" side
140+
cube.clear_cube(True)
141+
continue
142+
143+
if oriented == orientations[1]:
144+
upside_down = True
145+
else:
146+
upside_down = False
147+
148+
if not upside_down:
149+
if cube.done_scrolling:
150+
update_data()
151+
152+
cube.update(CUBE_WORD, TOP_PIXELS_COLOR, TOP_PIXELS_ON)
153+
cube.scroll_word_and_update_top()
154+
155+
else:
156+
cube.upside_down_mode()

QT_Py_Cube/cube.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# SPDX-FileCopyrightText: 2022 Charlyn Gonda for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
import time
5+
import adafruit_dotstar
6+
7+
from adafruit_led_animation.animation.rainbowchase import RainbowChase
8+
from adafruit_led_animation.color import AMBER, JADE, CYAN, GOLD, PINK
9+
from adafruit_pixel_framebuf import PixelFramebuffer
10+
11+
12+
class Cube():
13+
def __init__(
14+
self,
15+
top_cin,
16+
top_din,
17+
side_panels_cin,
18+
side_panels_din,
19+
bottom_cin,
20+
bottom_din):
21+
22+
# static numbers
23+
self.num_pixels = 64*4
24+
self.num_pixels_topbottom = 64
25+
self.pixel_width = 8*4
26+
self.pixel_height = 8
27+
28+
# top pixels
29+
top_pixels = adafruit_dotstar.DotStar(
30+
top_cin, top_din, self.num_pixels_topbottom,
31+
brightness=0.03, auto_write=False)
32+
self.pixel_framebuf_top = PixelFramebuffer(
33+
top_pixels,
34+
self.pixel_height,
35+
self.pixel_height,
36+
rotation=1,
37+
alternating=False,
38+
)
39+
40+
# side pixels
41+
self.side_pixels = adafruit_dotstar.DotStar(
42+
side_panels_cin, side_panels_din, self.num_pixels,
43+
brightness=0.03, auto_write=False)
44+
self.pixel_framebuf_sides = PixelFramebuffer(
45+
self.side_pixels,
46+
self.pixel_height,
47+
self.pixel_width,
48+
rotation=1,
49+
alternating=False,
50+
reverse_y=True,
51+
reverse_x=False
52+
)
53+
self.rainbow_sides = RainbowChase(
54+
self.side_pixels, speed=0.1, size=3, spacing=6)
55+
56+
# bottom pixels
57+
pixels_bottom = adafruit_dotstar.DotStar(
58+
bottom_cin, bottom_din, self.num_pixels_topbottom,
59+
brightness=0.03, auto_write=False)
60+
self.pixel_framebuf_bottom = PixelFramebuffer(
61+
pixels_bottom,
62+
self.pixel_height,
63+
self.pixel_height,
64+
rotation=0,
65+
alternating=False,
66+
reverse_y=False,
67+
reverse_x=True
68+
)
69+
70+
# scrolling word state vars
71+
self.last_color_time = -1
72+
self.color_wait = 1
73+
self.word = ''
74+
self.total_scroll_len = 0
75+
self.scroll_x_pos = -self.pixel_width
76+
self.color_idx = 0
77+
self.color_list = [AMBER, JADE, CYAN, PINK, GOLD]
78+
self.done_scrolling = False
79+
80+
# whether or not the cube is already clear
81+
self.clear = False
82+
83+
# top pixel vars
84+
self.top_pixel_coords = []
85+
self.top_pixel_color = CYAN
86+
87+
# upside down mode
88+
self.bottom_last = -1
89+
self.bottom_wait = 1
90+
self.bottom_squares = [[0, 0, 8, 8], [
91+
1, 1, 6, 6], [2, 2, 4, 4], [3, 3, 2, 2]]
92+
self.bottom_squares_idx = 0
93+
94+
def update(self, word, color, coords):
95+
self.word = word
96+
self.total_scroll_len = (len(self.word) * 5) + len(self.word)
97+
self.top_pixel_coords = coords
98+
self.top_pixel_color = color
99+
100+
def scroll_word_and_update_top(self):
101+
if self.scroll_x_pos >= self.total_scroll_len:
102+
self.scroll_x_pos = -self.pixel_width
103+
self.clear_cube()
104+
self.done_scrolling = True
105+
else:
106+
self.done_scrolling = False
107+
self.clear = False
108+
109+
self.__scroll_framebuf(self.word, self.scroll_x_pos, 0)
110+
self.__display_top_pixels()
111+
self.scroll_x_pos = self.scroll_x_pos + 1
112+
113+
def clear_cube(self, clear_top=False):
114+
if not self.clear:
115+
self.pixel_framebuf_sides.fill(0)
116+
self.pixel_framebuf_sides.display()
117+
self.side_pixels.fill(0)
118+
self.side_pixels.show()
119+
self.pixel_framebuf_bottom.fill(0)
120+
self.pixel_framebuf_bottom.display()
121+
if clear_top:
122+
self.pixel_framebuf_top.fill(0)
123+
self.pixel_framebuf_top.display()
124+
self.clear = True
125+
126+
def upside_down_mode(self):
127+
self.clear_cube(True)
128+
self.rainbow_sides.animate()
129+
now = time.monotonic()
130+
self.__bottom_square_animation(now)
131+
132+
def waiting_mode(self):
133+
self.pixel_framebuf_top.pixel(3, 3, CYAN)
134+
self.pixel_framebuf_top.pixel(4, 4, PINK)
135+
self.pixel_framebuf_top.display()
136+
137+
def __bottom_square_animation(self, now):
138+
self.pixel_framebuf_bottom.fill(0)
139+
color_int = self._rgb_to_int(CYAN)
140+
if now > self.bottom_last + self.bottom_wait:
141+
self.__coord_wrap()
142+
self.bottom_last = now
143+
x, y, w, h = self.bottom_squares[self.bottom_squares_idx]
144+
self.pixel_framebuf_bottom.rect(x, y, w, h, color_int)
145+
self.pixel_framebuf_bottom.display()
146+
147+
def __coord_wrap(self):
148+
self.bottom_squares_idx = self.bottom_squares_idx + 1
149+
if self.bottom_squares_idx >= len(self.bottom_squares):
150+
self.bottom_squares_idx = 0
151+
152+
def __display_top_pixels(self):
153+
self.pixel_framebuf_top.fill(0)
154+
155+
for coord in self.top_pixel_coords:
156+
x, y = coord.split(":")
157+
self.pixel_framebuf_top.pixel(int(x), int(y), self.top_pixel_color)
158+
self.pixel_framebuf_top.display()
159+
160+
def __scroll_framebuf(self, word, shift_x, shift_y):
161+
self.pixel_framebuf_sides.fill(0)
162+
163+
color = self.__next_color()
164+
color_int = self._rgb_to_int(color)
165+
166+
# negate x so that the word can be shown from left to right
167+
self.pixel_framebuf_sides.text(word, -shift_x, shift_y, color_int)
168+
self.pixel_framebuf_sides.display()
169+
170+
def __next_color(self):
171+
if self.color_idx >= len(self.color_list):
172+
self.color_idx = 0
173+
174+
result = self.color_list[self.color_idx]
175+
now = time.monotonic()
176+
if now >= self.last_color_time + self.color_wait:
177+
self.color_idx = self.color_idx + 1
178+
self.last_color_time = now
179+
180+
return result
181+
182+
@staticmethod
183+
def _rgb_to_int(rgb):
184+
return rgb[0] << 16 | rgb[1] << 8 | rgb[2]

0 commit comments

Comments
 (0)