pupremote 2.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lpf2.py +545 -0
- pupremote-2.1.1.dist-info/METADATA +174 -0
- pupremote-2.1.1.dist-info/RECORD +7 -0
- pupremote-2.1.1.dist-info/WHEEL +5 -0
- pupremote-2.1.1.dist-info/licenses/LICENSE.txt +674 -0
- pupremote-2.1.1.dist-info/top_level.txt +2 -0
- pupremote.py +589 -0
lpf2.py
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
# LPF2 class allows communication between LEGO SPIKE Prime and third party devices.
|
|
2
|
+
__author__ = "Anton Vanhoucke & Ste7an"
|
|
3
|
+
__copyright__ = "Copyright 2023, 2024 AntonsMindstorms.com"
|
|
4
|
+
__license__ = "GPL"
|
|
5
|
+
__version__ = "1.5"
|
|
6
|
+
__status__ = "Production"
|
|
7
|
+
|
|
8
|
+
import machine
|
|
9
|
+
import struct
|
|
10
|
+
import utime
|
|
11
|
+
try:
|
|
12
|
+
from micropython import const
|
|
13
|
+
except ImportError:
|
|
14
|
+
def const(i):
|
|
15
|
+
return i
|
|
16
|
+
|
|
17
|
+
from sys import implementation
|
|
18
|
+
|
|
19
|
+
# OpenMV board platform type
|
|
20
|
+
# sys.implementation[2]: OPENMV4P-STM32H743
|
|
21
|
+
OPENMV = const(0)
|
|
22
|
+
# LMS-ESP32 board platform type
|
|
23
|
+
# sys.implementation[2]: ESP32 module (lvgl,ulab,spiram) with ESP32
|
|
24
|
+
ESP32 = const(1)
|
|
25
|
+
# OpenMV RT board platform type
|
|
26
|
+
# sys.implementation[2]: OpenMV IMXRT1060-MIMXRT1062DVJ6A
|
|
27
|
+
OPENMVRT = const(2)
|
|
28
|
+
|
|
29
|
+
MAX_PKT = const(32)
|
|
30
|
+
|
|
31
|
+
BYTE_NACK = const(0x02)
|
|
32
|
+
BYTE_ACK = const(0x04)
|
|
33
|
+
CMD_Type = const(0x40) # @, set sensor type command
|
|
34
|
+
CMD_Select = const(0x43) # C, sets modes on the fly
|
|
35
|
+
CMD_MODES = const(0x41) # I, set mode type command
|
|
36
|
+
CMD_EXT_MODE = const(0x46)
|
|
37
|
+
CMD_Baud = const(0x52) # R, set the transmission baud rate
|
|
38
|
+
CMD_Vers = const(0x5F) # _, set the version number
|
|
39
|
+
MSG_INFO = const(0x80) # name command
|
|
40
|
+
MSG_DATA = const(0xC0) # data command
|
|
41
|
+
MSG_EXT_MODE = const(0x46)
|
|
42
|
+
EXT_MODE_0 = const(0x00)
|
|
43
|
+
EXT_MODE_8 = const(0x08) # only used for extended mode > 7
|
|
44
|
+
CMD_LLL_SHIFT = const(3)
|
|
45
|
+
MSG_INFO_PLUS8 = const(0x20)
|
|
46
|
+
|
|
47
|
+
LEN_4 = const(2 << CMD_LLL_SHIFT)
|
|
48
|
+
LEN_2 = const(1 << CMD_LLL_SHIFT)
|
|
49
|
+
LEN_8 = const(3 << CMD_LLL_SHIFT)
|
|
50
|
+
|
|
51
|
+
NAME = const(0x0)
|
|
52
|
+
RAW = const(0x1)
|
|
53
|
+
PCT = const(0x2)
|
|
54
|
+
SI = const(0x3)
|
|
55
|
+
SYM = const(0x4)
|
|
56
|
+
FUNCTION_MAP = const(0x5)
|
|
57
|
+
FMT = const(0x80)
|
|
58
|
+
|
|
59
|
+
DATA8 = const(0)
|
|
60
|
+
DATA16 = const(1)
|
|
61
|
+
DATA32 = const(2)
|
|
62
|
+
DATAF = const(3)
|
|
63
|
+
|
|
64
|
+
# Input/Output Mapping flags, can be combined with |
|
|
65
|
+
WITH_NULL = const(2**7) # Supports NULL value
|
|
66
|
+
FUNC_2 = const(2**6) # Supports Functional Mapping 2.0+
|
|
67
|
+
ABSOLUTE = const(16) # ABS (Absolute [min..max])
|
|
68
|
+
RELATIVE = const(8) # REL (Relative [-1..1])
|
|
69
|
+
DISCRETE = const(4) # DIS (Discrete [0, 1, 2, 3])
|
|
70
|
+
|
|
71
|
+
STRUCT_FMT = ("B", "H", "I", "f")
|
|
72
|
+
|
|
73
|
+
HEARTBEAT_PERIOD = const(1000) # time of inactivity after which we reset sensor
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def __num_bits(x):
|
|
77
|
+
# Return the number of bits required to represent x
|
|
78
|
+
n = 0
|
|
79
|
+
while x > 0:
|
|
80
|
+
x >>= 1
|
|
81
|
+
n += 1
|
|
82
|
+
return n
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class LPF2(object):
|
|
86
|
+
def __init__(
|
|
87
|
+
self,
|
|
88
|
+
modes,
|
|
89
|
+
sensor_id=62,
|
|
90
|
+
debug=False,
|
|
91
|
+
max_packet_size=MAX_PKT,
|
|
92
|
+
rx=None,
|
|
93
|
+
tx=None,
|
|
94
|
+
uart_n=None,
|
|
95
|
+
):
|
|
96
|
+
self.modes = modes
|
|
97
|
+
self.current_mode = 0
|
|
98
|
+
self.sensor_id = sensor_id
|
|
99
|
+
self.connected = False
|
|
100
|
+
self.payloads = {}
|
|
101
|
+
self.last_nack = 0
|
|
102
|
+
self.debug = debug
|
|
103
|
+
self.max_packet_size = max_packet_size
|
|
104
|
+
self.UART_N = uart_n
|
|
105
|
+
self.TX_PIN_N = tx
|
|
106
|
+
self.RX_PIN_N = rx
|
|
107
|
+
if "RT1060" in implementation[2]:
|
|
108
|
+
self.BOARD = OPENMVRT
|
|
109
|
+
if uart_n == None:
|
|
110
|
+
self.UART_N = 1
|
|
111
|
+
print("OpenMV RT defaults loaded")
|
|
112
|
+
elif "OPENMV4" in implementation[2]:
|
|
113
|
+
self.BOARD = OPENMV
|
|
114
|
+
import pyb
|
|
115
|
+
self.pyb = pyb
|
|
116
|
+
if uart_n == None:
|
|
117
|
+
self.UART_N = 3
|
|
118
|
+
print("OpenMV H7 defaults loaded")
|
|
119
|
+
else:
|
|
120
|
+
self.BOARD = ESP32
|
|
121
|
+
try:
|
|
122
|
+
from lms_esp32 import RX_PIN,TX_PIN
|
|
123
|
+
except ImportError:
|
|
124
|
+
RX_PIN = 18
|
|
125
|
+
TX_PIN = 19
|
|
126
|
+
if tx == None:
|
|
127
|
+
self.TX_PIN_N = TX_PIN
|
|
128
|
+
if rx == None:
|
|
129
|
+
self.RX_PIN_N = RX_PIN
|
|
130
|
+
if uart_n == None:
|
|
131
|
+
self.UART_N = 2
|
|
132
|
+
print(
|
|
133
|
+
"LMS-ESP32 defaults loaded, with rx={}, tx={}".format(self.RX_PIN_N, self.TX_PIN_N)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def mode(
|
|
138
|
+
name,
|
|
139
|
+
size=1,
|
|
140
|
+
data_type=DATA8,
|
|
141
|
+
writable=0, # Leaving this for bw compatibility
|
|
142
|
+
format="3.0",
|
|
143
|
+
raw_range=[0, 100],
|
|
144
|
+
percent_range=[0, 100],
|
|
145
|
+
si_range=[0, 100],
|
|
146
|
+
symbol="",
|
|
147
|
+
functionmap=[ABSOLUTE, ABSOLUTE], # [in (to hub), out (from hub)]
|
|
148
|
+
view=True,
|
|
149
|
+
):
|
|
150
|
+
fig, dec = format.split(".")
|
|
151
|
+
total_data_size = size * 2**data_type # Byte size of data set.
|
|
152
|
+
# Find the power of 2 that is greater than the length of the data
|
|
153
|
+
# -1 because of the header byte.
|
|
154
|
+
bit_size = __num_bits(total_data_size - 1)
|
|
155
|
+
mode_list = [
|
|
156
|
+
name, # 0
|
|
157
|
+
[size, data_type, int(fig), int(dec)], # 1
|
|
158
|
+
raw_range, # 2
|
|
159
|
+
percent_range, # 3
|
|
160
|
+
si_range, # 4
|
|
161
|
+
symbol, # 5
|
|
162
|
+
functionmap, # 6
|
|
163
|
+
view and functionmap[0], # 7
|
|
164
|
+
total_data_size, # 8
|
|
165
|
+
bit_size, # 9
|
|
166
|
+
]
|
|
167
|
+
return mode_list
|
|
168
|
+
|
|
169
|
+
def init_pins(self):
|
|
170
|
+
if self.BOARD == ESP32:
|
|
171
|
+
self.rx_pin = machine.Pin(self.RX_PIN_N, machine.Pin.IN)
|
|
172
|
+
self.tx_pin = machine.Pin(
|
|
173
|
+
self.TX_PIN_N, machine.Pin.OUT, machine.Pin.PULL_DOWN
|
|
174
|
+
)
|
|
175
|
+
elif self.BOARD == OPENMVRT:
|
|
176
|
+
self.rx_pin = machine.Pin("P5", machine.Pin.IN)
|
|
177
|
+
self.tx_pin = machine.Pin("P4", machine.Pin.OUT, machine.Pin.PULL_DOWN)
|
|
178
|
+
elif self.BOARD == OPENMV:
|
|
179
|
+
self.rx_pin = self.pyb.Pin("P5", self.pyb.Pin.IN)
|
|
180
|
+
self.tx_pin = self.pyb.Pin("P4", self.pyb.Pin.OUT_PP)
|
|
181
|
+
|
|
182
|
+
def wrt_tx_pin(self, val, wait):
|
|
183
|
+
# Reinit pin to deal with cable unplugging and re-plugging
|
|
184
|
+
self.tx_pin.value(val)
|
|
185
|
+
utime.sleep_ms(wait)
|
|
186
|
+
|
|
187
|
+
def slow_uart(self):
|
|
188
|
+
if self.BOARD == ESP32:
|
|
189
|
+
self.uart = machine.UART(
|
|
190
|
+
self.UART_N,
|
|
191
|
+
baudrate=2400,
|
|
192
|
+
rx=self.RX_PIN_N,
|
|
193
|
+
tx=self.TX_PIN_N,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
elif self.BOARD == OPENMVRT:
|
|
197
|
+
self.uart = machine.UART(self.UART_N, 2400)
|
|
198
|
+
|
|
199
|
+
elif self.BOARD == OPENMV:
|
|
200
|
+
self.uart = self.pyb.UART(self.UART_N, 2400)
|
|
201
|
+
|
|
202
|
+
def fast_uart(self):
|
|
203
|
+
if self.BOARD == ESP32:
|
|
204
|
+
self.uart = machine.UART(
|
|
205
|
+
self.UART_N,
|
|
206
|
+
baudrate=115200,
|
|
207
|
+
rx=self.RX_PIN_N,
|
|
208
|
+
tx=self.TX_PIN_N,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
elif self.BOARD == OPENMVRT:
|
|
212
|
+
self.uart = machine.UART(self.UART_N, 115200)
|
|
213
|
+
utime.sleep_ms(5)
|
|
214
|
+
|
|
215
|
+
elif self.BOARD == OPENMV:
|
|
216
|
+
self.uart = self.pyb.UART(self.UART_N, 115200)
|
|
217
|
+
|
|
218
|
+
# -------- Payload definition
|
|
219
|
+
|
|
220
|
+
def load_payload(self, data, mode=None):
|
|
221
|
+
if mode is None:
|
|
222
|
+
mode = self.current_mode
|
|
223
|
+
data_type = self.modes[mode][1][1]
|
|
224
|
+
if isinstance(data, bytes):
|
|
225
|
+
bin_data = data
|
|
226
|
+
elif isinstance(data, bytearray):
|
|
227
|
+
bin_data = data
|
|
228
|
+
elif isinstance(data, list):
|
|
229
|
+
# We have a list of integers. Pack them as bytes.
|
|
230
|
+
bin_data = struct.pack("%d" % len(data) + STRUCT_FMT[data_type], *data)
|
|
231
|
+
elif isinstance(data, float) or isinstance(data, int):
|
|
232
|
+
bin_data = struct.pack(STRUCT_FMT[data_type], data)
|
|
233
|
+
elif isinstance(data, str):
|
|
234
|
+
# String. Convert to bytes of max size.
|
|
235
|
+
bin_data = bytes(data, "UTF-8")[: self.max_packet_size]
|
|
236
|
+
else:
|
|
237
|
+
raise ValueError("Wrong data type: %s" % type(data))
|
|
238
|
+
|
|
239
|
+
bytesize = self.modes[mode][8]
|
|
240
|
+
bit = self.modes[mode][9]
|
|
241
|
+
|
|
242
|
+
assert len(bin_data) > 0, "Payload is empty"
|
|
243
|
+
assert len(bin_data) <= bytesize, "Wrong payload size"
|
|
244
|
+
|
|
245
|
+
payload = bytearray(2**bit + 5)
|
|
246
|
+
payload[0] = MSG_EXT_MODE
|
|
247
|
+
payload[1] = EXT_MODE_0 if mode < 8 else EXT_MODE_8
|
|
248
|
+
payload[2] = 0xFF ^ payload[0] ^ payload[1]
|
|
249
|
+
cksm = 0xFF
|
|
250
|
+
payload[3] = MSG_DATA | (bit << CMD_LLL_SHIFT) | (mode & 7)
|
|
251
|
+
cksm ^= payload[3]
|
|
252
|
+
for i in range(len(bin_data)):
|
|
253
|
+
payload[i + 4] = bin_data[i]
|
|
254
|
+
cksm ^= bin_data[i]
|
|
255
|
+
payload[-1] = cksm # No need to checksum zero bytes.
|
|
256
|
+
|
|
257
|
+
self.payloads[mode] = payload
|
|
258
|
+
|
|
259
|
+
def send_payload(self, data=None, mode=None):
|
|
260
|
+
"""
|
|
261
|
+
Convert bytes of data to a proper LPF2 payload,
|
|
262
|
+
save it to the payload of the current mode,
|
|
263
|
+
and write it to the hub. If there is no data, just
|
|
264
|
+
send current payload.
|
|
265
|
+
"""
|
|
266
|
+
if not self.connected:
|
|
267
|
+
if self.debug:
|
|
268
|
+
print("Write payload, but not connected.")
|
|
269
|
+
return
|
|
270
|
+
if mode == None:
|
|
271
|
+
mode = self.current_mode
|
|
272
|
+
if data != None:
|
|
273
|
+
self.load_payload(data, mode)
|
|
274
|
+
self.write(self.payloads[mode])
|
|
275
|
+
|
|
276
|
+
def update_payload(self, data, mode):
|
|
277
|
+
if mode == self.current_mode:
|
|
278
|
+
self.send_payload(data, mode)
|
|
279
|
+
else:
|
|
280
|
+
self.load_payload(data, mode)
|
|
281
|
+
|
|
282
|
+
# ----- comm stuff
|
|
283
|
+
|
|
284
|
+
def flush(self):
|
|
285
|
+
return self.uart.read(self.uart.any())
|
|
286
|
+
|
|
287
|
+
@staticmethod
|
|
288
|
+
def str_b(b):
|
|
289
|
+
return " ".join([hex(c) for c in b])
|
|
290
|
+
|
|
291
|
+
def readchar(self):
|
|
292
|
+
if self.uart.any():
|
|
293
|
+
c = self.uart.read(1)
|
|
294
|
+
else: # Try again once
|
|
295
|
+
utime.sleep_ms(1)
|
|
296
|
+
if self.uart.any():
|
|
297
|
+
c = self.uart.read(1)
|
|
298
|
+
else:
|
|
299
|
+
return -1
|
|
300
|
+
if c == None:
|
|
301
|
+
return -1
|
|
302
|
+
else:
|
|
303
|
+
if self.debug:
|
|
304
|
+
print(f"\033[91m {self.str_b(c)}\033[0m", end=" ")
|
|
305
|
+
return ord(c)
|
|
306
|
+
|
|
307
|
+
def heartbeat(self):
|
|
308
|
+
if not self.connected:
|
|
309
|
+
print("Checking heartbeat, but not connected. Initializing.")
|
|
310
|
+
self.connect()
|
|
311
|
+
return
|
|
312
|
+
|
|
313
|
+
if (utime.ticks_ms() - self.last_nack) > HEARTBEAT_PERIOD:
|
|
314
|
+
print("Checking heartbeat, but line is dead. Re-initializing.")
|
|
315
|
+
self.connected = False
|
|
316
|
+
self.connect()
|
|
317
|
+
return
|
|
318
|
+
|
|
319
|
+
b = self.readchar() # Read in any heartbeat or command bytes
|
|
320
|
+
if b > 0: # There is data, let's see what it is.
|
|
321
|
+
if b == BYTE_NACK:
|
|
322
|
+
# Regular heartbeat pulse from the hub.
|
|
323
|
+
self.last_nack = utime.ticks_ms() # reset heartbeat timer
|
|
324
|
+
# Resend latest data, just in case
|
|
325
|
+
self.send_payload()
|
|
326
|
+
|
|
327
|
+
elif b == CMD_Select:
|
|
328
|
+
self.last_nack = utime.ticks_ms() # reset heartbeat timer
|
|
329
|
+
# The hub is asking us to change mode.
|
|
330
|
+
mode = self.readchar()
|
|
331
|
+
cksm = self.readchar()
|
|
332
|
+
# Calculate the checksum for two bytes.
|
|
333
|
+
if cksm == 0xFF ^ CMD_Select ^ mode:
|
|
334
|
+
self.current_mode = mode
|
|
335
|
+
self.send_payload()
|
|
336
|
+
if self.debug:
|
|
337
|
+
print(f"Mode switched to {mode}")
|
|
338
|
+
|
|
339
|
+
elif b == CMD_EXT_MODE:
|
|
340
|
+
self.last_nack = utime.ticks_ms() # reset heartbeat timer
|
|
341
|
+
ext_mode = self.readchar() # 0x00 or 0x08
|
|
342
|
+
cksm = self.readchar() # 0xb9 or 0xb1
|
|
343
|
+
|
|
344
|
+
if cksm == 0xFF ^ CMD_EXT_MODE ^ ext_mode:
|
|
345
|
+
b = self.readchar() # CMD_Data | LENGTH | MODE
|
|
346
|
+
|
|
347
|
+
# Bitmask and then shift to get the LENGTH (=size exponent) of the data
|
|
348
|
+
size = 2 ** ((b & 0b111000) >> 3)
|
|
349
|
+
|
|
350
|
+
# Bitmask to get the mode number
|
|
351
|
+
# TODO test if setting current mode is part of the protocol
|
|
352
|
+
wrt_mode = (b & 0b111) + ext_mode
|
|
353
|
+
|
|
354
|
+
# Keep track of the checksum while reading data
|
|
355
|
+
ck = 0xFF ^ b
|
|
356
|
+
|
|
357
|
+
buf = bytearray(size)
|
|
358
|
+
for i in range(size):
|
|
359
|
+
buf[i] = self.readchar()
|
|
360
|
+
# Keep track of the checksum
|
|
361
|
+
ck ^= buf[i]
|
|
362
|
+
|
|
363
|
+
if ck == self.readchar():
|
|
364
|
+
return buf, wrt_mode
|
|
365
|
+
else:
|
|
366
|
+
print(
|
|
367
|
+
"Checksum error. Try reducing max_packet_size to 16 if using Pybricks."
|
|
368
|
+
)
|
|
369
|
+
else:
|
|
370
|
+
if self.debug:
|
|
371
|
+
buf = self.flush()
|
|
372
|
+
print(f"Unhandled data from hub {hex(b)} {self.str_b(buf)}")
|
|
373
|
+
|
|
374
|
+
def write(self, array):
|
|
375
|
+
if self.debug:
|
|
376
|
+
print("\n>> ", self.str_b(array))
|
|
377
|
+
return self.uart.write(array)
|
|
378
|
+
|
|
379
|
+
@staticmethod
|
|
380
|
+
def calc_cksm(array):
|
|
381
|
+
chksm = 0xFF
|
|
382
|
+
for b in array:
|
|
383
|
+
chksm ^= b
|
|
384
|
+
return chksm
|
|
385
|
+
|
|
386
|
+
def addChksm(self, array):
|
|
387
|
+
return array + self.calc_cksm(array).to_bytes(1, "little")
|
|
388
|
+
|
|
389
|
+
# ---- settup definitions
|
|
390
|
+
|
|
391
|
+
def setType(self, sensorType):
|
|
392
|
+
return self.addChksm(bytearray([CMD_Type, sensorType]))
|
|
393
|
+
|
|
394
|
+
def defineBaud(self, baud):
|
|
395
|
+
rate = baud.to_bytes(4, "little")
|
|
396
|
+
return self.addChksm(bytearray([CMD_Baud]) + rate)
|
|
397
|
+
|
|
398
|
+
@staticmethod
|
|
399
|
+
def str_vers_to_4_bytes(str_vers: str) -> bytes:
|
|
400
|
+
stvb = bytes([int(n)&0xFF for n in str_vers.split(".")])
|
|
401
|
+
if len(stvb) >= 4:
|
|
402
|
+
return stvb[:4]
|
|
403
|
+
return b"\x00" * (4 - len(stvb)) + stvb
|
|
404
|
+
|
|
405
|
+
def defineVers(self, hardware: str, software: str):
|
|
406
|
+
return self.addChksm(
|
|
407
|
+
bytearray([CMD_Vers])
|
|
408
|
+
+ self.str_vers_to_4_bytes(hardware)
|
|
409
|
+
+ self.str_vers_to_4_bytes(software)
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
def str_info(self, data, num, info_type):
|
|
413
|
+
if isinstance(data, str): # Convert and truncate
|
|
414
|
+
dt = bytearray(data, "UTF-8")[: self.max_packet_size]
|
|
415
|
+
else: # Bytes, or bytearray. Just truncate.
|
|
416
|
+
dt = bytearray(data)[: self.max_packet_size]
|
|
417
|
+
exp = __num_bits(len(dt) - 1)
|
|
418
|
+
pl = bytearray(2**exp)
|
|
419
|
+
pl[: len(dt)] = dt
|
|
420
|
+
return self.addChksm(
|
|
421
|
+
bytearray([MSG_INFO | exp << CMD_LLL_SHIFT | num, info_type]) + pl
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
def buildFunctMap(self, fmap, num, info_type):
|
|
425
|
+
return self.addChksm(
|
|
426
|
+
bytearray([MSG_INFO | LEN_2 | num, info_type, fmap[0], fmap[1]])
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
def buildFormat(self, fmt, num, info_type):
|
|
430
|
+
sampleSize = fmt[0] & 0xFF
|
|
431
|
+
dataType = fmt[1] & 0xFF
|
|
432
|
+
figures = fmt[2] & 0xFF
|
|
433
|
+
decimals = fmt[3] & 0xFF
|
|
434
|
+
return self.addChksm(
|
|
435
|
+
bytearray(
|
|
436
|
+
[
|
|
437
|
+
MSG_INFO | LEN_4 | num,
|
|
438
|
+
info_type,
|
|
439
|
+
sampleSize,
|
|
440
|
+
dataType,
|
|
441
|
+
figures,
|
|
442
|
+
decimals,
|
|
443
|
+
]
|
|
444
|
+
)
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
def buildRange(self, settings, num, rangeType):
|
|
448
|
+
exp = 3 << CMD_LLL_SHIFT
|
|
449
|
+
minVal = struct.pack("<f", settings[0])
|
|
450
|
+
maxVal = struct.pack("<f", settings[1])
|
|
451
|
+
return self.addChksm(
|
|
452
|
+
bytearray([MSG_INFO | exp | num, rangeType]) + minVal + maxVal
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
def defineModes(self):
|
|
456
|
+
n_modes = len(self.modes) - 1
|
|
457
|
+
n_views = [m[7] for m in self.modes].count(True) - 1
|
|
458
|
+
return self.addChksm(
|
|
459
|
+
bytearray(
|
|
460
|
+
[
|
|
461
|
+
CMD_MODES | LEN_4,
|
|
462
|
+
min(n_modes, 7),
|
|
463
|
+
min(n_views, 7),
|
|
464
|
+
n_modes,
|
|
465
|
+
n_views,
|
|
466
|
+
]
|
|
467
|
+
)
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
def setupMode(self, mode, num):
|
|
471
|
+
self.load_payload(b"\x00" * mode[8], num) # Store empty payload for this mode
|
|
472
|
+
plus_8 = 0x00
|
|
473
|
+
if num > 7:
|
|
474
|
+
num -= 8
|
|
475
|
+
plus_8 = MSG_INFO_PLUS8
|
|
476
|
+
self.write(self.str_info(mode[0], num, NAME | plus_8)) # write name
|
|
477
|
+
self.write(self.buildRange(mode[2], num, RAW | plus_8)) # write RAW range
|
|
478
|
+
self.write(self.buildRange(mode[3], num, PCT | plus_8)) # write Percent range
|
|
479
|
+
self.write(self.buildRange(mode[4], num, SI | plus_8)) # write SI range
|
|
480
|
+
self.write(self.str_info(mode[5], num, SYM | plus_8)) # write symbol
|
|
481
|
+
self.write(
|
|
482
|
+
self.buildFunctMap(mode[6], num, FUNCTION_MAP | plus_8)
|
|
483
|
+
) # write Function Map
|
|
484
|
+
self.write(self.buildFormat(mode[1], num, FMT | plus_8)) # write format
|
|
485
|
+
|
|
486
|
+
# ----- Start everything up
|
|
487
|
+
|
|
488
|
+
def connect(self):
|
|
489
|
+
assert len(self.modes) > 0, "No modes (commands) defined"
|
|
490
|
+
fast_uart_hub = False
|
|
491
|
+
self.init_pins()
|
|
492
|
+
self.wrt_tx_pin(1, 5) # Say hello!
|
|
493
|
+
self.wrt_tx_pin(0, 0)
|
|
494
|
+
for i in range(25): # Wait for AOK
|
|
495
|
+
n = 0
|
|
496
|
+
while self.rx_pin.value() == 1:
|
|
497
|
+
utime.sleep_ms(1)
|
|
498
|
+
if n > 20:
|
|
499
|
+
break
|
|
500
|
+
n += 1
|
|
501
|
+
if self.debug:
|
|
502
|
+
print(i, "falling after ms high:", n)
|
|
503
|
+
if i > 10 and (n > 21 or n < 16):
|
|
504
|
+
fast_uart_hub = True
|
|
505
|
+
if self.debug:
|
|
506
|
+
print("Fast uart handshake after drops: ",n)
|
|
507
|
+
break
|
|
508
|
+
while self.rx_pin.value() == 0:
|
|
509
|
+
utime.sleep_ms(1)
|
|
510
|
+
# Wait until rise again
|
|
511
|
+
|
|
512
|
+
if fast_uart_hub:
|
|
513
|
+
self.fast_uart()
|
|
514
|
+
utime.sleep_ms(5)
|
|
515
|
+
self.write(b"\x04")
|
|
516
|
+
else:
|
|
517
|
+
self.slow_uart()
|
|
518
|
+
self.write(b"\x00")
|
|
519
|
+
self.write(self.setType(self.sensor_id))
|
|
520
|
+
self.write(self.defineModes()) # tell how many modes
|
|
521
|
+
self.write(self.defineBaud(115200))
|
|
522
|
+
self.write(self.defineVers("0.1", __version__))
|
|
523
|
+
num = len(self.modes) - 1
|
|
524
|
+
for mode in reversed(self.modes):
|
|
525
|
+
utime.sleep_ms(20)
|
|
526
|
+
self.setupMode(mode, num)
|
|
527
|
+
num -= 1
|
|
528
|
+
|
|
529
|
+
self.write(b"\x04") # ACK
|
|
530
|
+
end = utime.ticks_ms() + 2500
|
|
531
|
+
while utime.ticks_ms() < end: # Wait for ack
|
|
532
|
+
data = self.readchar()
|
|
533
|
+
if data == BYTE_ACK:
|
|
534
|
+
self.connected = True
|
|
535
|
+
break
|
|
536
|
+
|
|
537
|
+
if self.connected:
|
|
538
|
+
self.last_nack = utime.ticks_ms()
|
|
539
|
+
print("\nSuccessfully connected to hub with sensor id {}".format(self.sensor_id))
|
|
540
|
+
if not fast_uart_hub:
|
|
541
|
+
self.fast_uart()
|
|
542
|
+
else:
|
|
543
|
+
print("\nFailed to connect to hub")
|
|
544
|
+
|
|
545
|
+
|