@tigerpython/robotics-libraries 1.4.0

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.
Files changed (66) hide show
  1. package/CHANGELOG +68 -0
  2. package/LICENSE +373 -0
  3. package/README.md +81 -0
  4. package/calliope/README.md +15 -0
  5. package/calliope/callibot.py +176 -0
  6. package/calliope/callibotmot.py +46 -0
  7. package/calliope/callimk.py +143 -0
  8. package/calliope/cbalarm.py +14 -0
  9. package/calliope/cpglow.py +76 -0
  10. package/calliope/cpmike.py +16 -0
  11. package/calliope/cprover.py +33 -0
  12. package/calliope/cputils.py +23 -0
  13. package/calliope/libraries.json +10 -0
  14. package/calliope/libraries.raw.json +10 -0
  15. package/calliope/min/callibot.py +97 -0
  16. package/calliope/min/callibotmot.py +23 -0
  17. package/calliope/min/callimk.py +47 -0
  18. package/calliope/min/cbalarm.py +6 -0
  19. package/calliope/min/cpglow.py +29 -0
  20. package/calliope/min/cpmike.py +8 -0
  21. package/calliope/min/cprover.py +9 -0
  22. package/calliope/min/cputils.py +7 -0
  23. package/dist/index.d.mts +124 -0
  24. package/dist/index.d.ts +124 -0
  25. package/dist/index.js +2896 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/index.mjs +2862 -0
  28. package/dist/index.mjs.map +1 -0
  29. package/microbit/README.md +48 -0
  30. package/microbit/controller.py +212 -0
  31. package/microbit/huskylens.py +479 -0
  32. package/microbit/libraries.json +19 -0
  33. package/microbit/libraries.raw.json +19 -0
  34. package/microbit/mbalarm.py +15 -0
  35. package/microbit/mbbitbot.py +127 -0
  36. package/microbit/mbglow.py +77 -0
  37. package/microbit/mbled.py +56 -0
  38. package/microbit/mbmarsrover.py +216 -0
  39. package/microbit/mbminibit.py +139 -0
  40. package/microbit/mbrobot.py +401 -0
  41. package/microbit/mbrobot_legacy.py +90 -0
  42. package/microbit/mbrobot_plus.py +179 -0
  43. package/microbit/mbrobot_plusV2.py +490 -0
  44. package/microbit/mbrobot_plusV3.py +427 -0
  45. package/microbit/mbrobotmot.py +49 -0
  46. package/microbit/mbthetabot.py +167 -0
  47. package/microbit/mbwait.py +50 -0
  48. package/microbit/mbxgo.py +173 -0
  49. package/microbit/min/controller.py +42 -0
  50. package/microbit/min/huskylens.py +145 -0
  51. package/microbit/min/mbalarm.py +6 -0
  52. package/microbit/min/mbbitbot.py +40 -0
  53. package/microbit/min/mbglow.py +29 -0
  54. package/microbit/min/mbled.py +18 -0
  55. package/microbit/min/mbmarsrover.py +62 -0
  56. package/microbit/min/mbminibit.py +50 -0
  57. package/microbit/min/mbrobot.py +82 -0
  58. package/microbit/min/mbrobot_legacy.py +45 -0
  59. package/microbit/min/mbrobot_plus.py +75 -0
  60. package/microbit/min/mbrobot_plusV2.py +102 -0
  61. package/microbit/min/mbrobot_plusV3.py +194 -0
  62. package/microbit/min/mbrobotmot.py +25 -0
  63. package/microbit/min/mbthetabot.py +47 -0
  64. package/microbit/min/mbwait.py +23 -0
  65. package/microbit/min/mbxgo.py +37 -0
  66. package/package.json +54 -0
@@ -0,0 +1,427 @@
1
+ # mbrobot_plusV3.py
2
+ # Date 25/11/25
3
+
4
+ from microbit import i2c, sleep, running_time, pin0, pin1, pin2
5
+ import neopixel
6
+ import music
7
+
8
+ _motorState = bytearray(5)
9
+ _speedPercent = 50
10
+ _powerByteL = 50
11
+ _powerByteR = 50
12
+ _powerBytesLUT = bytes(b'\x00\x0f\x10\x10\x11\x12\x12\x13\x13\x14\x15\x15\x16\x16\x17\x18\x18\x19\x19\x1a\x1b\x1b\x1c\x1c\x1d\x1e\x1e\x1f\x1f\x20\x21\x21\x22\x22\x23\x24\x24\x25\x25\x26\x27\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3b\x3c\x3e\x3f\x41\x43\x45\x47\x49\x4b\x4d\x50\x52\x55\x58\x5b\x5e\x61\x65\x69\x6d\x71\x75\x7a\x7f\x84\x89\x8f\x95\x9b\xa2\xa9\xb1\xb9\xc1\xca\xd3\xdd\xe8\xf3\xff')
13
+ _arcScaling = 0
14
+ _UNCONNECTEDERRORMSG = "Please connect to Maqueen robot and switch it on."
15
+ _underglowNP = neopixel.NeoPixel(pin1, 4)
16
+ _alarmSequence = ['c5:1', 'r', 'c5,1', 'r:3']
17
+ _buff1 = bytearray(1)
18
+ _buff2 = bytearray(2)
19
+ _add_mq = 0x10
20
+ _servoMinPulse=25
21
+ _servoMaxPulse=131
22
+ _lidarMode=8
23
+
24
+ def _wr1(reg):
25
+ _buff1[0] = reg
26
+ i2c.write(_add_mq, _buff1)
27
+
28
+ def _wr2(reg, val):
29
+ _buff2[0] = reg
30
+ _buff2[1] = val
31
+ i2c.write(_add_mq, _buff2)
32
+
33
+ def _setMotors(dirL, powerL, dirR, powerR):
34
+ global _motorState
35
+ _motorState[1] = dirL
36
+ _motorState[2] = powerL
37
+ _motorState[3] = dirR
38
+ _motorState[4] = powerR
39
+ try:
40
+ i2c.write(0x10, _motorState)
41
+ except:
42
+ raise RuntimeError(_UNCONNECTEDERRORMSG)
43
+
44
+ def _setSingleMotor(side, dir, power):
45
+ global _motorState
46
+ _motorState[1 + side] = dir
47
+ _motorState[2 + side] = power
48
+ try:
49
+ i2c.write(0x10, _motorState)
50
+ except:
51
+ raise RuntimeError(_UNCONNECTEDERRORMSG)
52
+
53
+ def setSpeed(speed):
54
+ global _speedPercent
55
+ global _powerByteL
56
+ global _powerByteR
57
+ _speedPercent = int(min(max(speed,0),100))
58
+ _powerByteL = int(round(2.4*speed+14))
59
+ _powerByteR = int(round(2.4*speed+14))
60
+
61
+ def resetSpeed():
62
+ setSpeed(50)
63
+
64
+ def stop():
65
+ _setMotors(0,0,0,0)
66
+
67
+ def forward():
68
+ _setMotors(0, _powerByteL, 0, _powerByteR)
69
+
70
+ def backward():
71
+ _setMotors(1, _powerByteL, 1, _powerByteR)
72
+
73
+ def left():
74
+ _setMotors(1, _powerByteL, 0, _powerByteR)
75
+
76
+ def right():
77
+ _setMotors(0, _powerByteL, 1, _powerByteR)
78
+
79
+ def _getPowerByteLUT(speed, offset):
80
+ return min(_powerBytesLUT[speed] + offset, 255)
81
+
82
+ def _getArcBytes(r):
83
+ rmm = int(r * 100)
84
+ outerSpeed = _speedPercent
85
+ if outerSpeed < 25:
86
+ outerSpeed = 25
87
+ reducedSpeed = 0
88
+ if rmm > 5:
89
+ n = outerSpeed * (3 * _arcScaling - outerSpeed - 9 * rmm + 220)
90
+ d = -14 * _arcScaling + outerSpeed - 200 + 3 * outerSpeed - 10 * rmm - 290
91
+ reducedSpeed = int(n/d)
92
+ if reducedSpeed < 2:
93
+ reducedSpeed = 2 if rmm > 15 else 1
94
+ innerByte = _getPowerByteLUT(int(reducedSpeed), 0)
95
+ outerByte = _getPowerByteLUT(int(outerSpeed), 0)
96
+ return (innerByte, outerByte)
97
+
98
+ def rightArc(radius):
99
+ inner, outer = _getArcBytes(radius)
100
+ _setMotors(0, outer, 0, inner)
101
+
102
+
103
+ def leftArc(radius):
104
+ inner, outer = _getArcBytes(radius)
105
+ _setMotors(0, inner, 0, outer)
106
+
107
+ class Motor:
108
+ def __init__(self, side):
109
+ self._side = side
110
+
111
+ def rotate(self, speed):
112
+ speedClamped = int(min(max(abs(speed), 0), 100))
113
+ power = speedClamped
114
+ direction = 0 if speed > 0 else 1
115
+ _setSingleMotor(self._side, direction, power)
116
+
117
+ def setServo(servo,angle):
118
+ if angle < 0 or angle >180:
119
+ raise ValueError("Invalid angle. Must be between 0 and 180")
120
+ if servo in ["P0", "S1"]:
121
+ pin = pin0
122
+ elif servo in ["P1", "S2"]:
123
+ pin = pin1
124
+ elif servo in ["P2", "S3"]:
125
+ pin = pin2
126
+ else:
127
+ raise ValueError("Valid servo names: S1, S2, S3 or P0, P1, P2")
128
+ frac = (_servoMaxPulse - _servoMinPulse) * int(angle)
129
+ offset = (frac >> 8)+ (frac >> 10) + (frac >> 11)+ (frac >> 12) # / 180
130
+ usPulseTime = _servoMinPulse + offset
131
+ pin.set_analog_period(20)
132
+ pin.write_analog(usPulseTime)
133
+
134
+ class IRSensor:
135
+ _address = bytes(b'\x1D')
136
+
137
+ def __init__(self, index):
138
+ self.index = index
139
+
140
+ def read_digital(self):
141
+ try:
142
+ i2c.write(0x10, IRSensor._address)
143
+ except:
144
+ raise RuntimeError(_UNCONNECTEDERRORMSG)
145
+ byte = ~i2c.read(0x10, 1)[0]
146
+ return (byte & (2 ** self.index)) >> self.index
147
+
148
+ def read_analog(self):
149
+ try:
150
+ _wr1(0x1D)
151
+ except:
152
+ raise RuntimeError(_UNCONNECTEDERRORMSG)
153
+ buffer = i2c.read(0x10,11)
154
+ return buffer[2+2*self.index] << 8 | buffer[1+2*self.index]
155
+
156
+ def setLEDs(rgbl, rgbr):
157
+ _wr2(11, rgbl)
158
+ _wr2(12, rgbr)
159
+
160
+ def setLED(state, stateR=None):
161
+ stateR = stateR if stateR != None else state
162
+ setLEDs(state, stateR)
163
+
164
+ def setLEDLeft(rgbl):
165
+ _wr2(11, rgbl)
166
+
167
+ def setLEDRight(rgbr):
168
+ _wr2(12, rgbr)
169
+
170
+ def fillRGB(red, green, blue):
171
+ _underglowNP.clear()
172
+ _underglowNP.fill((red,green,blue))
173
+ _underglowNP.show()
174
+
175
+ def setRGB(r,g,b):
176
+ fillRGB(r,g,b)
177
+
178
+ def clearRGB():
179
+ _underglowNP.clear()
180
+
181
+ def posRGB(position, red, green, blue):
182
+ if position < 0 or position > 3:
183
+ raise ValueError("invalid RGB-LED position. Must be 0,1,2 or 3.")
184
+ _underglowNP[position] = (red, green, blue)
185
+ _underglowNP.show()
186
+
187
+ def setAlarm(state):
188
+ if state:
189
+ music.play(_alarmSequence, wait=False, loop=True)
190
+ else:
191
+ music.stop()
192
+
193
+ def beep():
194
+ music.pitch(440, 200, wait=False)
195
+
196
+ def readLightIntensity(side):
197
+ _wr1(78)
198
+ LightBuffer = i2c.read(0x10, 4, repeat=False)
199
+ if side == 1:
200
+
201
+ return LightBuffer[0] << 8 | LightBuffer[1]
202
+ else:
203
+ return LightBuffer[2] << 8 | LightBuffer[3]
204
+
205
+ def setPatrolSpeed(speed):
206
+ _wr2(63, speed)
207
+
208
+ def setIntersectionRunMode(mode):
209
+ _wr2(69, mode)
210
+
211
+ def setTRordRunMode(mode):
212
+ _wr2(70, mode)
213
+
214
+ def setLeftOrStraightRunMode(mode):
215
+ _wr2(71, mode)
216
+
217
+ def setRightOrStraightRunMode(mode):
218
+ _wr2(72, mode)
219
+
220
+ def patrolling(patrol):
221
+ if patrol == 1:
222
+ val = 0x04 | 0x01
223
+ else:
224
+ val = 0x08
225
+ _wr2(60, val)
226
+
227
+ def intersectionDetecting():
228
+ _wr1(61)
229
+ data = i2c.read(0x10, 1)[0]
230
+ return data
231
+
232
+ def pidControlDistance(dir, distance, interruption):
233
+ speed = 2
234
+ if distance >= 6000:
235
+ distance = 60000
236
+ _wr2(64, dir)
237
+ _wr2(85, speed)
238
+ _wr2(65, distance >> 8)
239
+ _wr2(66, distance)
240
+ _wr2(60, 0x04 | 0x02)
241
+ if (interruption == 1):
242
+ _wr1(0x57)
243
+ flagBuffer = i2c.read(0x10, 1)
244
+ while flagBuffer[0] == 1:
245
+ sleep(10)
246
+ flagBuffer = i2c.read(0x10, 1)
247
+
248
+ def pidControlAngle(angle, interruption):
249
+ speed = 2
250
+ if angle >= 0:
251
+ buf = 1
252
+ else:
253
+ buf = 2
254
+ angle = -angle
255
+ _wr2(67, buf)
256
+ _wr2(86, speed)
257
+ _wr2(68, angle)
258
+ _wr2(60, 0x04 | 0x02)
259
+ if interruption == 1:
260
+ _wr1(0x57)
261
+ buff = i2c.read(0x10, 1)
262
+ while buff[0] == 1:
263
+ sleep(10)
264
+ buff = i2c.read(0x10, 1)
265
+
266
+ def pidControlStop():
267
+ _wr2(60, 0x10)
268
+
269
+ def readRealTimeSpeed(type):
270
+ _wr2(76, 1)
271
+ buff = i2c.read(0x10, 2)
272
+ if type == 1:
273
+ return buff[0] / 5
274
+ else:
275
+ return buff[1] / 5
276
+
277
+ def _sendLidarCommand(cmd, args=[]):
278
+ args_len = len(args)
279
+ packet_len = args_len + 1
280
+ packet = bytearray(4 + args_len)
281
+ packet[0] = 0x55
282
+ packet[1] = (packet_len >> 8) & 0xFF
283
+ packet[2] = packet_len & 0xFF
284
+ packet[3] = cmd
285
+ for i, arg in enumerate(args):
286
+ packet[4 + i] = arg
287
+ # chunkwise sending? TODO
288
+ i2c.write(0x33, packet)
289
+
290
+ def _receiveLidarData(expectedCommand):
291
+ TIMEOUT = 1000
292
+ MAX_SIZE = 32
293
+ start_time = running_time()
294
+ success = False
295
+ data = None
296
+ started = False
297
+
298
+ while running_time() - start_time < TIMEOUT and started == False:
299
+ status = i2c.read(0x33, 1)[0]
300
+ if status == 0x53:
301
+ started = True
302
+ elif status == 0x63:
303
+ return success, data
304
+ sleep(16)
305
+
306
+ if started == True:
307
+ header = i2c.read(0x33, 3)
308
+ cmd = header[0]
309
+ data_length = header[1] | (header[2] << 8)
310
+
311
+ if cmd == expectedCommand:
312
+ success = True
313
+ data = bytearray()
314
+ remaining_bytes = data_length
315
+ chunk_size = min(remaining_bytes, MAX_SIZE)
316
+ while running_time() - start_time < TIMEOUT and remaining_bytes > 0:
317
+ try:
318
+ chunk = i2c.read(0x33, chunk_size)
319
+ data.extend(chunk)
320
+ remaining_bytes -= len(chunk)
321
+ except:
322
+ sleep(1)
323
+ return success, data
324
+
325
+ def setLidarMode(mode=8):
326
+ global _lidarMode
327
+ if mode not in [4, 8]:
328
+ raise ValueError("Lidar mode must be 4 or 8")
329
+ mode_text = "4x4" if mode == 4 else "8x8"
330
+ print("Switching Lidar Mode to " + mode_text + ".\nPlease wait up to 10 seconds.")
331
+ success = False
332
+
333
+ for i in range(10):
334
+ _sendLidarCommand(1, [0, 0, 0, mode])
335
+ success, _ = _receiveLidarData(1)
336
+
337
+ if success == True: break
338
+ sleep(17)
339
+
340
+ if success:
341
+ _lidarMode = mode
342
+ sleep(5000)
343
+ else:
344
+ raise RuntimeError("Failed to switch Lidar Mode")
345
+
346
+ def getDistanceAt(x_pos, y_pos):
347
+ _sendLidarCommand(0x3, [x_pos, y_pos])
348
+ success, data = _receiveLidarData(0x3)
349
+ if success and len(data) >= 2:
350
+ distance = (data[0] | (data[1] << 8)) // 10
351
+ return distance
352
+ else:
353
+ return 1023
354
+
355
+ def getDistanceList():
356
+ _sendLidarCommand(0x2)
357
+ success, data = _receiveLidarData(0x2)
358
+ if success and len(data) >= 32:
359
+ distList = []
360
+ for i in range(0,len(data),2):
361
+ d = data[i] | (data[i+1] << 8)
362
+ distList.append(d // 10)
363
+ return distList
364
+ else:
365
+ return []
366
+
367
+ def getDistance():
368
+ global _lidarMode
369
+ mid = int(_lidarMode/2)
370
+ topLeft = getDistanceAt(mid-1, mid-1)
371
+ topRight = getDistanceAt(mid, mid-1)
372
+ bottomLeft = getDistanceAt(mid-1, mid)
373
+ bottomRight = getDistanceAt(mid, mid)
374
+ distanceList = [topLeft, topRight, bottomLeft, bottomRight]
375
+ return min(distanceList)
376
+
377
+
378
+ def getDistanceGrid():
379
+ _sendLidarCommand(0x2)
380
+ success, data = _receiveLidarData(0x2)
381
+ if success and len(data) >= 32:
382
+ distGrid = []
383
+ stride = 16 if len(data) == 128 else 8
384
+ for i in range(0,len(data),stride):
385
+ row = []
386
+ for j in range(0,stride,2):
387
+ d = data[i+j] | (data[i+j+1] << 8)
388
+ row.append(d // 10)
389
+ distGrid.append(row)
390
+ return distGrid
391
+ else:
392
+ return []
393
+
394
+ def getDistanceColumn(index):
395
+ _sendLidarCommand(0x5, [index])
396
+ success, data = _receiveLidarData(0x5)
397
+ if success and len(data) >= 8:
398
+ row = []
399
+ for i in range(0, len(data), 2):
400
+ distance = data[i] | (data[i+1] << 8)
401
+ row.append(distance // 10)
402
+ return row
403
+ return []
404
+
405
+ def getDistanceRow(index):
406
+ _sendLidarCommand(0x6, [index])
407
+ success, data = _receiveLidarData(0x6)
408
+ if success and len(data) >= 8:
409
+ col = []
410
+ for i in range(0, len(data), 2):
411
+ distance = data[i] | (data[i+1] << 8)
412
+ col.append(distance // 10)
413
+ return col
414
+ return []
415
+
416
+
417
+ pin2.set_pull(pin2.NO_PULL)
418
+ delay = sleep
419
+ irR2 = IRSensor(0)
420
+ irR1 = IRSensor(1)
421
+ irRight = irR1
422
+ irM = IRSensor(2)
423
+ irL1 = IRSensor(3)
424
+ irLeft = irL1
425
+ irL2 = IRSensor(4)
426
+ motL = Motor(0)
427
+ motR = Motor(2)
@@ -0,0 +1,49 @@
1
+ # mbrobotmot.py
2
+ # Version 1.2, Aug 9, 2019
3
+
4
+ import gc
5
+ from microbit import i2c, pin1, pin2, pin8, pin12, pin13, pin14, sleep
6
+ import machine
7
+
8
+ class Motor:
9
+ def __init__(self, id):
10
+ self._id = 2 * id
11
+
12
+ def rotate(self, s):
13
+ v = abs(s)
14
+ if s > 0:
15
+ self._w(0, v)
16
+ elif s < 0:
17
+ self._w(1, v)
18
+ else:
19
+ self._w(0, 0)
20
+
21
+
22
+ def _w(self, d, s):
23
+ try:
24
+ i2c.write(0x10, bytearray([self._id, d, s]))
25
+ except:
26
+ print("Please switch on mbRobot!")
27
+ while True:
28
+ pass
29
+
30
+ delay = sleep
31
+
32
+ def getDistance():
33
+ pin1.write_digital(1)
34
+ pin1.write_digital(0)
35
+ p = machine.time_pulse_us(pin2, 1, 50000)
36
+ cm = int(p / 58.2 + 0.5)
37
+ return cm if cm > 0 else 255
38
+
39
+ def setLED(on):
40
+ pin8.write_digital(on)
41
+ pin12.write_digital(on)
42
+
43
+ pin2.set_pull(pin2.NO_PULL)
44
+ irLeft = pin13
45
+ irRight = pin14
46
+ ledLeft = pin8
47
+ ledRight = pin12
48
+ motL = Motor(0)
49
+ motR = Motor(1)
@@ -0,0 +1,167 @@
1
+ from microbit import *
2
+ from utime import ticks_us, sleep_us
3
+ from neopixel import *
4
+
5
+ _speedPercent = 50
6
+ _powerByteL = 40
7
+ _powerByteR = 40
8
+ _powerBytesLUT = bytes(b'\x00\x0b\x0b\x0c\x0c\x0d\x0d\x0d\x0e\x0e\x0f\x0f\x0f\x10\x10\x11\x11\x11\x12\x12\x13\x13\x13\x14\x14\x15\x15\x15\x16\x16\x17\x17\x17\x18\x19\x1a\x1b\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x36\x38\x3a\x3c\x3f\x41\x44\x46\x49\x4c\x4f\x53\x56\x5a\x5e\x62\x67\x6b\x70\x75\x7b\x81\x87\x8d\x94\x9b\xa3\xab\xb4\xbd\xc6\xd0\xdb\xe6\xf2\xff')
9
+
10
+ _powerOffset = 0
11
+ _powerDifferential = 0
12
+ _arcScaling = 0
13
+
14
+ _ADDR_ATM = 0x22
15
+ _nbLeds = 14
16
+
17
+
18
+ def _setMotors(dirL, powerL, dirR, powerR):
19
+ pinsL = (pin14, pin13)
20
+ pinsR = (pin16, pin15)
21
+
22
+ pinsL[dirL].write_analog(powerL)
23
+ pinsL[1 - dirL].write_analog(0)
24
+
25
+ pinsR[dirR].write_analog(powerR)
26
+ pinsR[1 - dirR].write_analog(0)
27
+
28
+
29
+ def _setSingleMotor(side, direction, power):
30
+ if side == 0:
31
+ pins = (pin14, pin13)
32
+ elif side == 2:
33
+ pins = (pin16, pin15)
34
+
35
+ pins[direction].write_analog(power)
36
+ pins[1 - direction].write_analog(0)
37
+
38
+
39
+ def _getArcBytes(r):
40
+ outerSpeed = _speedPercent
41
+ rCm = int(r * 100)
42
+ threshold = outerSpeed - max(rCm + 20, 40)
43
+ if threshold <= 0:
44
+ outerSpeed = min(max(rCm + 40, 40), 100)
45
+ reducedSpeed = 0
46
+ if rCm >= 4:
47
+ flattening = (100 - outerSpeed) // 2
48
+ reducedSpeed = (rCm * 10 - 35) / \
49
+ (rCm * (11 + (_arcScaling-4)/10) + 90 + flattening)
50
+ reducedSpeed = reducedSpeed * outerSpeed
51
+ innerByte = _getPowerByteLUT(int(reducedSpeed), 0)
52
+ outerByte = _getPowerByteLUT(int(outerSpeed), 0)
53
+ return (innerByte, outerByte)
54
+
55
+
56
+ def calibrate(offset, differential=0, arcScaling=0):
57
+ global _powerDifferential
58
+ global _powerOffset
59
+ global _arcScaling
60
+
61
+ _powerOffset = max(min(int(offset), 150), -10)
62
+ _powerDifferential = max(min(int(differential), 150), -150)
63
+ _arcScaling = max(min(arcScaling, 50), -15)
64
+ setSpeed(_speedPercent)
65
+
66
+
67
+ def setSpeed(speed):
68
+ global _speedPercent
69
+ global _powerByteL
70
+ global _powerByteR
71
+
72
+ _speedPercent = int(min(max(speed, 0), 100))
73
+ powerByte = _getPowerByteLUT(_speedPercent, _powerOffset)
74
+ boost = round((1 - _speedPercent / 100) *
75
+ abs(_powerDifferential)) if _speedPercent > 0 else 0
76
+ reduction = round((_speedPercent / 100) * abs(_powerDifferential))
77
+ if _powerDifferential > 0:
78
+ _powerByteL = powerByte - reduction
79
+ _powerByteR = powerByte + boost
80
+ else:
81
+ _powerByteL = powerByte + boost
82
+ _powerByteR = powerByte - reduction
83
+
84
+
85
+ def _getPowerByteLUT(speed, offset):
86
+ speedIndex = int(speed * (len(_powerBytesLUT) - 1) / 100)
87
+ return min(_powerBytesLUT[speedIndex] + offset, 1023)
88
+
89
+
90
+ def stop():
91
+ _setMotors(0, 0, 0, 0)
92
+
93
+
94
+ def forward():
95
+ _setMotors(0, _powerByteL, 0, _powerByteR)
96
+
97
+
98
+ def backward():
99
+ _setMotors(1, _powerByteL, 1, _powerByteR)
100
+
101
+
102
+ def left():
103
+ _setMotors(1, _powerByteL, 0, _powerByteR)
104
+
105
+
106
+ def right():
107
+ _setMotors(0, _powerByteL, 1, _powerByteR)
108
+
109
+
110
+ def rightArc(radius):
111
+ inner, outer = _getArcBytes(radius)
112
+ _setMotors(0, outer, 0, inner)
113
+
114
+
115
+ def leftArc(radius):
116
+ inner, outer = _getArcBytes(radius)
117
+ _setMotors(0, inner, 0, outer)
118
+
119
+
120
+ def getDistance():
121
+ pin12.write_digital(1)
122
+ sleep_us(10)
123
+ pin12.write_digital(0)
124
+ pin12.set_pull(pin15.NO_PULL)
125
+ while pin12.read_digital() == 0:
126
+ pass
127
+ start = ticks_us()
128
+ while pin12.read_digital() == 1:
129
+ pass
130
+ end = ticks_us()
131
+ echo = end-start
132
+ distance = int(0.01715 * echo)
133
+ return distance
134
+
135
+
136
+ def setLED(position, red, green, blue):
137
+ global _ADDR_ATM
138
+ i2c_data = bytearray(5)
139
+
140
+ i2c_data[0] = 1
141
+
142
+ i2c_data[1] = position
143
+
144
+ i2c_data[2] = red
145
+ i2c_data[3] = green
146
+ i2c_data[4] = blue
147
+
148
+ i2c.write(_ADDR_ATM, i2c_data)
149
+
150
+
151
+ def fill(red, green, blue):
152
+ for position in range(_nbLeds):
153
+ setLED(position, red, green, blue)
154
+
155
+
156
+ def readLine(side): # 0=left 1=right
157
+ i2c.write(_ADDR_ATM, bytearray([side + 1]), False)
158
+ result = i2c.read(_ADDR_ATM, 2)
159
+ result = result[0] + (result[1] << 8)
160
+ return result
161
+
162
+
163
+ def readLight(side): # 0=left 1=right
164
+ i2c.write(_ADDR_ATM, bytearray([side + 3]), False)
165
+ result = i2c.read(_ADDR_ATM, 2)
166
+ result = result[0] + (result[1] << 8)
167
+ return result
@@ -0,0 +1,50 @@
1
+ from microbit import button_a, button_b, pin0, pin1, pin2, pin_logo, sleep
2
+
3
+ # choose the reactivity of the press/touch sensing in ms. Default 100ms is 10 checks per second.
4
+ POLLING_DELAY = 100
5
+
6
+ # Let the microbit wait until a certain button of choice was pressed (and released).
7
+
8
+
9
+ def wait_for_press(button='any'):
10
+ button_a.was_pressed()
11
+ button_b.was_pressed()
12
+ # busy wait until a chosen button was pressed.
13
+ if button == 'a' or button == 'A':
14
+ while not button_a.was_pressed():
15
+ sleep(POLLING_DELAY)
16
+ elif button == 'b' or button == 'B':
17
+ while not button_b.was_pressed():
18
+ sleep(POLLING_DELAY)
19
+ elif button == 'any' or button == 'ANY':
20
+ while (not button_a.was_pressed()) and (not button_b.was_pressed()):
21
+ sleep(POLLING_DELAY)
22
+ elif button == 'both' or button == 'ab' or button == 'AB':
23
+ while not (button_a.is_pressed() and button_b.is_pressed()):
24
+ sleep(POLLING_DELAY)
25
+ while button_a.is_pressed() or button_b.is_pressed():
26
+ sleep(POLLING_DELAY)
27
+ else:
28
+ raise RuntimeError(
29
+ "Button to wait for must be either: 'a', 'b', 'any' or 'both'.")
30
+
31
+ # Let the microbit wait until a certain touch-sensitive pin of choice is touched.
32
+
33
+
34
+ def wait_for_touch(pin='logo'):
35
+ # busy wait until a given pin/logo is pressed.
36
+ p = pin_logo
37
+ if pin == 'pin0':
38
+ p = pin0
39
+ elif pin == 'pin1':
40
+ p = pin1
41
+ elif pin == 'pin2':
42
+ p = pin2
43
+ elif pin == 'logo' or pin == 'pin_logo' or pin == 'pinLogo':
44
+ p = pin_logo
45
+ else:
46
+ raise RuntimeError(
47
+ "Argument 'pin' must be one of: 'pin_logo', 'pin0', 'pin1' or 'pin2'.")
48
+ p.set_touch_mode(p.CAPACITIVE)
49
+ while not p.is_touched():
50
+ sleep(POLLING_DELAY)