Skip to content

Commit 5be59d4

Browse files
authored
Create scope_xy_adafruitlogo.py
1 parent 772b3cf commit 5be59d4

1 file changed

Lines changed: 198 additions & 0 deletions

File tree

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
### scope-xy-adafruitlogo v1.0
2+
3+
"""Output a logo to an oscilloscope in X-Y mode on an Adafruit M4
4+
board like Feather M4 or PyGamer (best to disconnect headphones).
5+
"""
6+
7+
### copy this file to PyGamer (or other M4 board) as code.py
8+
9+
### MIT License
10+
11+
### Copyright (c) 2019 Kevin J. Walters
12+
13+
### Permission is hereby granted, free of charge, to any person obtaining a copy
14+
### of this software and associated documentation files (the "Software"), to deal
15+
### in the Software without restriction, including without limitation the rights
16+
### to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
### copies of the Software, and to permit persons to whom the Software is
18+
### furnished to do so, subject to the following conditions:
19+
20+
### The above copyright notice and this permission notice shall be included in all
21+
### copies or substantial portions of the Software.
22+
23+
### THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
### IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
### FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
### AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
### LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
### OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
### SOFTWARE.
30+
31+
import time
32+
import math
33+
import array
34+
35+
import board
36+
import audioio
37+
import analogio
38+
39+
### Vector data for logo
40+
import adafruit_logo_vector
41+
42+
VECTOR_POINT_SPACING = 3
43+
44+
def addpoints(points, min_dist):
45+
"""Add extra points to any lines if length is greater than min_dist"""
46+
47+
newpoints = []
48+
original_len = len(points)
49+
for pidx in range(original_len):
50+
px1, py1 = points[pidx]
51+
px2, py2 = points[(pidx + 1) % original_len]
52+
53+
### Always keep the original point
54+
newpoints.append((px1, py1))
55+
56+
diff_x = px2 - px1
57+
diff_y = py2 - py1
58+
dist = math.sqrt(diff_x ** 2 + diff_y ** 2)
59+
if dist > min_dist:
60+
### Calculate extra intermediate points plus one
61+
extrasp1 = int(dist // min_dist) + 1
62+
for extra_idx in range(1, extrasp1):
63+
ratio = extra_idx / extrasp1
64+
newpoints.append((px1 + diff_x * ratio,
65+
py1 + diff_y * ratio))
66+
### Two points define a straight line
67+
### so no need to connect final point back to first
68+
if original_len == 2:
69+
break
70+
71+
return newpoints
72+
73+
### pylint: disable=invalid-name
74+
### If logo is off centre then correct it here
75+
if adafruit_logo_vector.offset_x != 0 or adafruit_logo_vector.offset_y != 0:
76+
data = []
77+
for part in adafruit_logo_vector.data:
78+
newpart = []
79+
for point in part:
80+
newpart.append((point[0] - adafruit_logo_vector.offset_x,
81+
point[1] - adafruit_logo_vector.offset_y))
82+
data.append(newpart)
83+
else:
84+
data = adafruit_logo_vector.data
85+
86+
87+
### Add intermediate points to make line segments for each part
88+
### look like continuous lines on x-y oscilloscope output
89+
display_data = []
90+
for part in data:
91+
display_data.extend(addpoints(part, VECTOR_POINT_SPACING))
92+
93+
### PyPortal DACs seem to stop around 53000 and there's 2 100 ohm resistors
94+
### on output so maybe large values aren't good idea?
95+
### 32768 and 32000 exhibit this bug but 25000 so far appears to be a
96+
### workaround, albeit a mysterious one
97+
### https://github.com/adafruit/circuitpython/issues/1992
98+
### Using "h" for audioio.RawSample() DAC range will be 20268 to 45268
99+
dac_x_min = 0
100+
dac_y_min = 0
101+
dac_x_max = 25000
102+
dac_y_max = 25000
103+
dac_x_mid = dac_x_max // 2
104+
dac_y_mid = dac_y_max // 2
105+
106+
### Convert the points into format suitable for audio library
107+
### and scale to the DAC range used by the library
108+
### Intentionally using "h" data representation here as this happens to
109+
### cause the CircuitPython audio libraries to make a copy of
110+
### rawdata which is useful to allow animating code to modify rawdata
111+
### without affecting current DAC output
112+
rawdata = array.array("h", (2 * len(display_data)) * [0])
113+
114+
range_x = 512.0
115+
range_y = 512.0
116+
halfrange_x = range_x / 2
117+
halfrange_y = range_y / 2
118+
mid_x = 256.0
119+
mid_y = 256.0
120+
mult_x = dac_x_max / range_x
121+
mult_y = dac_y_max / range_y
122+
123+
### https://github.com/adafruit/circuitpython/issues/1992
124+
print("length of rawdata", len(rawdata))
125+
126+
use_wav = True
127+
poor_wav_bug_workaround = False
128+
leave_wav_looping = True
129+
130+
### A0 will be x, A1 will be y
131+
if use_wav:
132+
print("Using audioio.RawSample for DACs")
133+
dacs = audioio.AudioOut(board.A0, right_channel=board.A1)
134+
else:
135+
print("Using analogio.AnalogOut for DACs")
136+
a0 = analogio.AnalogOut(board.A0)
137+
a1 = analogio.AnalogOut(board.A1)
138+
139+
### 10Hz is about ok for AudioOut, optimistic for AnalogOut
140+
frame_t = 1/10
141+
prev_t = time.monotonic()
142+
angle = 0 ### in radians
143+
frame = 1
144+
while True:
145+
##print("Transforming data for frame:", frame, "at", prev_t)
146+
147+
### Rotate the points of the vector graphic around its centre
148+
idx = 0
149+
sine = math.sin(angle)
150+
cosine = math.cos(angle)
151+
for px, py in display_data:
152+
pcx = px - mid_x
153+
pcy = py - mid_y
154+
dac_a0_x = round((-sine * pcx + cosine * pcy + halfrange_x) * mult_x)
155+
### Keep x position within legal values (if needed)
156+
##dac_a0_x = min(dac_a0_x, dac_x_max)
157+
##dac_a0_x = max(dac_a0_x, 0)
158+
dac_a1_y = round((sine * pcy + cosine * pcx + halfrange_y) * mult_y)
159+
### Keep y position within legal values (if needed)
160+
##dac_a1_y = min(dac_a1_y, dac_y_max)
161+
##dac_a1_y = max(dac_a1_y, 0)
162+
rawdata[idx] = dac_a0_x - dac_x_mid ### adjust for "h" array
163+
rawdata[idx + 1] = dac_a1_y - dac_y_mid ### adjust for "h" array
164+
idx += 2
165+
166+
if use_wav:
167+
### 200k (maybe 166.667k) seems to be practical limit
168+
### 1M permissible but seems same as around 200k
169+
output_wave = audioio.RawSample(rawdata,
170+
channel_count=2,
171+
sample_rate=200 * 1000)
172+
173+
### The image may "warp" sometimes with loop=True due to a strange bug
174+
### https://github.com/adafruit/circuitpython/issues/1992
175+
if poor_wav_bug_workaround:
176+
while True:
177+
dacs.play(output_wave)
178+
if time.monotonic() - prev_t >= frame_t:
179+
break
180+
else:
181+
dacs.play(output_wave, loop=True)
182+
while time.monotonic() - prev_t < frame_t:
183+
pass
184+
if not leave_wav_looping:
185+
dacs.stop()
186+
else:
187+
while True:
188+
### This gives a very flickery image with 4932 points
189+
### slight flicker at 2552
190+
### might be ok for 1000
191+
for idx in range(0, len(rawdata), 2):
192+
a0.value = rawdata[idx]
193+
a1.value = rawdata[idx + 1]
194+
if time.monotonic() - prev_t >= frame_t:
195+
break
196+
prev_t = time.monotonic()
197+
angle += math.pi / 180 * 3 ### 72 degrees per frame
198+
frame += 1

0 commit comments

Comments
 (0)