-
Notifications
You must be signed in to change notification settings - Fork 87
/
Copy pathscope.py
133 lines (103 loc) · 4.19 KB
/
scope.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import math
from time import sleep
from europi_script import EuroPiScript
try:
# local dev
from software.firmware.europi import (
MAX_INPUT_VOLTAGE,
OLED_HEIGHT,
OLED_WIDTH,
ain,
b1,
b2,
cv1,
cv2,
cv4,
din,
k1,
k2,
oled,
)
except ImportError:
from europi import MAX_INPUT_VOLTAGE, OLED_HEIGHT, OLED_WIDTH, ain, b1, b2, cv1, cv2, cv4, din, k1, k2, oled
MAX_RATE = OLED_WIDTH
Y_TRUE = int(OLED_HEIGHT / 3 * 2)
Y_FALSE = int(OLED_HEIGHT / 3)
D_WAVE_HEIGHT = Y_TRUE - Y_FALSE + 1
RIGHT_EDGE = OLED_WIDTH - 1
Y_PIXELS = OLED_HEIGHT - 1
class Scope(EuroPiScript):
"""
Can we make a functional oscilloscope on the EuroPi? Kinda! This script displays the inputs received on both the analog
and digital inputs. Both waves can be toggled, and when no waves are displayed we switch to a text output, which also
includes the knob values. The knobs each control an aspect of the x and y resolution respectively, allowing you to coax
a little more detail out of the display.
Note that the digital wave is displayed at a fixed y position that is not related to the value of the analog wave.
Limitations include:
- negative voltages are clipped to 0
- screen height is only 32 pixels, so limited resolution
- we can only refresh the screen so fast
- analog pass through will be 'digitized'
knob1 - samples per screen refresh. This effectively lets you 'zoom in' on the x-axis. Start at the lowest setting, 1 sample.
knob2 - y voltage scale (only affects analog wave). This effectively lets you 'zoom in' on the low voltages. Start at
the highest setting, 10v.
button1 - toggle digital wave display
button2 - toggle analog wave display
out1 - digital in pass through
out2 - analog in pass through
out4 - inverted digital pass through
"""
def __init__(self) -> None:
super().__init__()
self.enabled = [True, True] # digital, analog
def toggle(self, index):
def f():
self.enabled[index] = not self.enabled[index]
return f
@staticmethod
def read_sample_rate():
return k1.read_position(MAX_RATE) + 1
@staticmethod
def read_max_disp_voltage():
return k2.read_position(math.ceil(MAX_INPUT_VOLTAGE)) + 1
@staticmethod
def calc_y_pos(max_disp_voltage, a_voltage):
return Y_PIXELS - int(a_voltage / max_disp_voltage * Y_PIXELS)
def main(self):
b1.handler(self.toggle(0))
b2.handler(self.toggle(1))
oled.fill(0)
old_value = din.value()
while True:
rate = self.read_sample_rate()
max_disp_voltage = self.read_max_disp_voltage()
for _ in range(rate):
if any(self.enabled):
oled.scroll(-1, 0)
oled.vline(RIGHT_EDGE, 0, OLED_HEIGHT, 0)
d_value = din.value()
cv2.value(d_value)
cv4.value(not d_value)
if self.enabled[0]: # digital wave
y_pos = Y_TRUE if d_value else Y_FALSE
oled.pixel(RIGHT_EDGE, OLED_HEIGHT - y_pos, 1)
if d_value != old_value:
oled.vline(RIGHT_EDGE, Y_FALSE + 1, D_WAVE_HEIGHT, 1)
old_value = d_value
a_voltage = ain.read_voltage(1)
cv1.voltage(a_voltage)
if self.enabled[1]: # analog wave
y_pos = self.calc_y_pos(max_disp_voltage, a_voltage)
oled.pixel(RIGHT_EDGE, y_pos, 1)
if not self.enabled[0] and not self.enabled[1]: # details output
rate = self.read_sample_rate()
max_disp_voltage = self.read_max_disp_voltage()
oled.fill(0)
oled.text(f"a: {a_voltage:4.1f}v d:{d_value}", 2, 3, 1)
oled.text(f"samples: {rate:3}", 2, 13, 1)
oled.text(f"y scale: {max_disp_voltage:4.1f}v", 2, 23, 1)
oled.show()
sleep(0.001)
oled.show()
if __name__ == "__main__":
Scope().main()