-
Notifications
You must be signed in to change notification settings - Fork 511
/
Copy pathpimoroni.py
254 lines (203 loc) · 7.49 KB
/
pimoroni.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import time
from machine import Pin, PWM, ADC
BREAKOUT_GARDEN_I2C_PINS = {"sda": 4, "scl": 5}
PICO_EXPLORER_I2C_PINS = {"sda": 20, "scl": 21}
HEADER_I2C_PINS = {"sda": 20, "scl": 21}
PICOVISION_I2C_PINS = {"sda": 6, "scl": 7}
# Motor and encoder directions
NORMAL_DIR = 0x00
REVERSED_DIR = 0x01
BREAKOUT_GARDEN_SPI_SLOT_FRONT = 0
BREAKOUT_GARDEN_SPI_SLOT_BACK = 1
PICO_EXPLORER_SPI_ONBOARD = 2
class Analog:
def __init__(self, pin, amplifier_gain=1, resistor=0, offset=0):
self.gain = amplifier_gain
self.resistor = resistor
self.offset = offset
self.pin = ADC(pin)
def read_voltage(self):
return max((((self.pin.read_u16() * 3.3) / 65535) + self.offset) / self.gain, 0.0)
def read_current(self):
if self.resistor > 0:
return self.read_voltage() / self.resistor
else:
return self.read_voltage()
class AnalogMux:
def __init__(self, addr0, addr1=None, addr2=None, en=None, muxed_pin=None):
self.addr0_pin = Pin(addr0, Pin.OUT)
self.addr1_pin = Pin(addr1, Pin.OUT) if addr1 is not None else None
self.addr2_pin = Pin(addr2, Pin.OUT) if addr2 is not None else None
self.en_pin = Pin(en, Pin.OUT) if en is not None else None
self.max_address = 0b001
if addr1 is not None:
self.max_address = 0b011
if addr2 is not None:
self.max_address = 0b111
self.pulls = [None] * (self.max_address + 1)
self.muxed_pin = muxed_pin
def select(self, address):
if address < 0:
raise ValueError("address is less than zero")
elif address > self.max_address:
raise ValueError("address is greater than number of available addresses")
else:
if self.muxed_pin and self.pulls[address] is None:
self.muxed_pin.init(Pin.IN, None)
self.addr0_pin.value(address & 0b001)
if self.addr1_pin is not None:
self.addr1_pin.value(address & 0b010)
if self.addr2_pin is not None:
self.addr2_pin.value(address & 0b100)
if self.en_pin is not None:
self.en_pin.value(1)
if self.muxed_pin and self.pulls[address] is not None:
self.muxed_pin.init(Pin.IN, self.pulls[address])
def disable(self):
if self.en_pin is not None:
self.en_pin.value(0)
else:
raise RuntimeError("there is no enable pin assigned to this mux")
def configure_pull(self, address, pull=None):
if address < 0:
raise ValueError("address is less than zero")
elif address > self.max_address:
raise ValueError("address is greater than number of available addresses")
else:
self.pulls[address] = pull
def read(self):
if self.muxed_pin is not None:
return self.muxed_pin.value()
else:
raise RuntimeError("there is no muxed pin assigned to this mux")
class Button:
def __init__(self, button, invert=True, repeat_time=200, hold_time=1000):
self.invert = invert
self.repeat_time = repeat_time
self.hold_time = hold_time
self.pin = Pin(button, Pin.IN, Pin.PULL_UP if invert else Pin.PULL_DOWN)
self.last_state = False
self.pressed = False
self.pressed_time = 0
def read(self):
current_time = time.ticks_ms()
state = self.raw()
changed = state != self.last_state
self.last_state = state
if changed:
if state:
self.pressed_time = current_time
self.pressed = True
self.last_time = current_time
return True
else:
self.pressed_time = 0
self.pressed = False
self.last_time = 0
if self.repeat_time == 0:
return False
if self.pressed:
repeat_rate = self.repeat_time
if self.hold_time > 0 and current_time - self.pressed_time > self.hold_time:
repeat_rate /= 3
if current_time - self.last_time > repeat_rate:
self.last_time = current_time
return True
return False
def raw(self):
if self.invert:
return not self.pin.value()
else:
return self.pin.value()
@property
def is_pressed(self):
return self.raw()
class RGBLED:
def __init__(self, r, g, b, invert=True):
self.invert = invert
self.led_r = PWM(Pin(r))
self.led_r.freq(1000)
self.led_g = PWM(Pin(g))
self.led_g.freq(1000)
self.led_b = PWM(Pin(b))
self.led_b.freq(1000)
def set_rgb(self, r, g, b):
if self.invert:
r = 255 - r
g = 255 - g
b = 255 - b
self.led_r.duty_u16(int((r * 65535) / 255))
self.led_g.duty_u16(int((g * 65535) / 255))
self.led_b.duty_u16(int((b * 65535) / 255))
# A simple class for handling Proportional, Integral & Derivative (PID) control calculations
class PID:
def __init__(self, kp, ki, kd, sample_rate):
self.kp = kp
self.ki = ki
self.kd = kd
self.setpoint = 0
self._error_sum = 0
self._last_value = 0
self._sample_rate = sample_rate
def calculate(self, value, value_change=None):
error = self.setpoint - value
self._error_sum += error * self._sample_rate
if value_change is None:
rate_error = (value - self._last_value) / self._sample_rate
else:
rate_error = value_change
self._last_value = value
return (error * self.kp) + (self._error_sum * self.ki) - (rate_error * self.kd)
class Buzzer:
def __init__(self, pin):
self.pwm = PWM(Pin(pin))
def set_tone(self, freq, duty=0.5):
if freq < 50.0: # uh... https://github.com/micropython/micropython/blob/af64c2ddbd758ab6bac0fcca94c66d89046663be/ports/rp2/machine_pwm.c#L105-L119
self.pwm.duty_u16(0)
return False
self.pwm.freq(freq)
self.pwm.duty_u16(int(65535 * duty))
return True
class ShiftRegister:
def __init__(self, clk, lat, dat):
self.clk = Pin(clk, Pin.OUT)
self.lat = Pin(lat, Pin.OUT)
self.dat = Pin(dat, Pin.IN)
def __iter__(self):
self.lat.off()
self.lat.on()
for _ in range(8):
yield self.dat.value()
self.clk.on()
self.clk.off()
def __getitem__(self, k):
return list(self)[k]
def read(self):
out = 0
for bit in self:
out <<= 1
out += bit
return out
def is_set(self, mask):
return self.read() & mask == mask
# A basic wrapper for PWM with regular on/off and toggle functions from Pin
# Intended to be used for driving LEDs with brightness control & compatibility with Pin
class PWMLED:
def __init__(self, pin, invert=False):
self._invert = invert
self._led = PWM(Pin(pin, Pin.OUT))
self._led.freq(1000)
self._brightness = 0
self.brightness(0)
def brightness(self, brightness):
brightness = min(1.0, max(0.0, brightness))
self._brightness = brightness
if self._invert:
brightness = 1.0 - brightness
self._led.duty_u16(int(65535 * brightness))
def on(self):
self.brightness(1)
def off(self):
self.brightness(0)
def toggle(self):
self.brightness(1 - self._brightness)