@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.
- package/CHANGELOG +68 -0
- package/LICENSE +373 -0
- package/README.md +81 -0
- package/calliope/README.md +15 -0
- package/calliope/callibot.py +176 -0
- package/calliope/callibotmot.py +46 -0
- package/calliope/callimk.py +143 -0
- package/calliope/cbalarm.py +14 -0
- package/calliope/cpglow.py +76 -0
- package/calliope/cpmike.py +16 -0
- package/calliope/cprover.py +33 -0
- package/calliope/cputils.py +23 -0
- package/calliope/libraries.json +10 -0
- package/calliope/libraries.raw.json +10 -0
- package/calliope/min/callibot.py +97 -0
- package/calliope/min/callibotmot.py +23 -0
- package/calliope/min/callimk.py +47 -0
- package/calliope/min/cbalarm.py +6 -0
- package/calliope/min/cpglow.py +29 -0
- package/calliope/min/cpmike.py +8 -0
- package/calliope/min/cprover.py +9 -0
- package/calliope/min/cputils.py +7 -0
- package/dist/index.d.mts +124 -0
- package/dist/index.d.ts +124 -0
- package/dist/index.js +2896 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2862 -0
- package/dist/index.mjs.map +1 -0
- package/microbit/README.md +48 -0
- package/microbit/controller.py +212 -0
- package/microbit/huskylens.py +479 -0
- package/microbit/libraries.json +19 -0
- package/microbit/libraries.raw.json +19 -0
- package/microbit/mbalarm.py +15 -0
- package/microbit/mbbitbot.py +127 -0
- package/microbit/mbglow.py +77 -0
- package/microbit/mbled.py +56 -0
- package/microbit/mbmarsrover.py +216 -0
- package/microbit/mbminibit.py +139 -0
- package/microbit/mbrobot.py +401 -0
- package/microbit/mbrobot_legacy.py +90 -0
- package/microbit/mbrobot_plus.py +179 -0
- package/microbit/mbrobot_plusV2.py +490 -0
- package/microbit/mbrobot_plusV3.py +427 -0
- package/microbit/mbrobotmot.py +49 -0
- package/microbit/mbthetabot.py +167 -0
- package/microbit/mbwait.py +50 -0
- package/microbit/mbxgo.py +173 -0
- package/microbit/min/controller.py +42 -0
- package/microbit/min/huskylens.py +145 -0
- package/microbit/min/mbalarm.py +6 -0
- package/microbit/min/mbbitbot.py +40 -0
- package/microbit/min/mbglow.py +29 -0
- package/microbit/min/mbled.py +18 -0
- package/microbit/min/mbmarsrover.py +62 -0
- package/microbit/min/mbminibit.py +50 -0
- package/microbit/min/mbrobot.py +82 -0
- package/microbit/min/mbrobot_legacy.py +45 -0
- package/microbit/min/mbrobot_plus.py +75 -0
- package/microbit/min/mbrobot_plusV2.py +102 -0
- package/microbit/min/mbrobot_plusV3.py +194 -0
- package/microbit/min/mbrobotmot.py +25 -0
- package/microbit/min/mbthetabot.py +47 -0
- package/microbit/min/mbwait.py +23 -0
- package/microbit/min/mbxgo.py +37 -0
- 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)
|