-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrover.py
584 lines (482 loc) · 16 KB
/
rover.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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
#!/usr/bin/python
#
# rover.py
#
# Python Module to support 4tronix M.A.R.S. Rover
#
# Copyright 4tronix
#
# This code is in the public domain and may be freely copied and used
# No warranty is provided or implied
#
#======================================================================
#======================================================================
# General Functions
# (Both versions)
#
# init(brightness). Initialises GPIO pins, switches motors and LEDs Off. If brightness is 0, no LEDs are initialised
# cleanup(). Sets all motors and LEDs off and sets GPIO to standard values
# version(). Returns 4 for M.A.R.S. Rover. Invalid until after init() has been called
#======================================================================
#======================================================================
# Motor Functions
#
# stop(): Stops both motors
# forward(speed): Sets both motors to move forward at speed. 0 <= speed <= 100
# reverse(speed): Sets both motors to reverse at speed. 0 <= speed <= 100
# spinLeft(speed): Sets motors to turn opposite directions at speed. 0 <= speed <= 100
# spinRight(speed): Sets motors to turn opposite directions at speed. 0 <= speed <= 100
# turnForward(leftSpeed, rightSpeed): Moves forwards in an arc by setting different speeds. 0 <= leftSpeed,rightSpeed <= 100
# turnreverse(leftSpeed, rightSpeed): Moves backwards in an arc by setting different speeds. 0 <= leftSpeed,rightSpeed <= 100
#======================================================================
#======================================================================
# FIRELED Functions
#
# setColor(color): Sets all LEDs to color - requires show()
# setPixel(ID, color): Sets pixel ID to color - requires show()
# show(): Updates the LEDs with state of LED array
# clear(): Clears all LEDs to off - requires show()
# rainbow(): Sets the LEDs to rainbow colors - requires show()
# fromRGB(red, green, blue): Creates a color value from R, G and B values
# toRGB(color): Converts a color value to separate R, G and B
# wheel(pos): Generates rainbow colors across 0-255 positions
#======================================================================
#======================================================================
# UltraSonic Functions
#
# getDistance(). Returns the distance in cm to the nearest reflecting object. 0 == no object
#======================================================================
#======================================================================
# Servo Functions
#
# getLight(Sensor). Returns the value 0..1023 for the selected sensor, 0 <= Sensor <= 3
# getLightFL(). Returns the value 0..1023 for Front-Left light sensor
# getLightFR(). Returns the value 0..1023 for Front-Right light sensor
# getLightBL(). Returns the value 0..1023 for Back-Left light sensor
# getLightBR(). Returns the value 0..1023 for Back-Right light sensor
# getBattery(). Returns the voltage of the battery pack (>7.2V is good, less is bad)
#======================================================================
#======================================================================
# Keypad Functions
#
# getSwitch(). Returns the value of the tact switch: True==pressed
#======================================================================
# Import all necessary libraries
import RPi.GPIO as GPIO, sys, time, os
from rpi_ws281x import *
import pca9685
import smbus
# Define Model
PGNone = 0
PGFull = 1
PGLite = 2
PG2 = 3
ROVER = 4
PGType = PGNone # Set to None until we confirm during init()
# Pins 26, 19 Left Motor
# Pins 36, 40 Right Motor
# If using PiBit, then L1 and L2 are swapped
L1 = 16
L2 = 19
R1 = 12
R2 = 13
# Variables to track movements to prevent sudden forward/reverse current surges. -1 reverse, 0 stop, +1 forward
lDir = 0
rDir = 0
# Define RGB LEDs
leds = None
_brightness = 40
numPixels = 4
# Define Sonar Pin (same pin for both Ping and Echo)
sonar = 23 # mast sonar
# Pins used for keypad
keypadOut = 5
keypadIn = 25
# EEROM
EEROM = 0x50
bus = smbus.SMBus(1)
offsets = [0]*16
#======================================================================
# General Functions
#
# init(). Initialises GPIO pins, switches motors and LEDs Off, etc
def init(brightness, PiBit=False):
global p, q, a, b, pwm, pcfADC, PGType
global irFL, irFR, irMID, lineLeft, lineRight
global leds, L1, L2
GPIO.setwarnings(False)
PGType = PG2
#use physical pin numbering
GPIO.setmode(GPIO.BCM)
# Initialise LEDs
if (leds == None and brightness>0):
_brightness = brightness
leds = Adafruit_NeoPixel(numPixels, 18, 800000, 5, False, _brightness)
leds.begin()
pca9685.init()
if PiBit:
t = L1
L1 = L2
L2 = t
#use pwm for motor outputs
GPIO.setup(L1, GPIO.OUT)
p = GPIO.PWM(L1, 20)
p.start(0)
GPIO.setup(L2, GPIO.OUT)
q = GPIO.PWM(L2, 20)
q.start(0)
GPIO.setup(R1, GPIO.OUT)
a = GPIO.PWM(R1, 20)
a.start(0)
GPIO.setup(R2, GPIO.OUT)
b = GPIO.PWM(R2, 20)
b.start(0)
# set Keypad Clock as an Output and Data as an input
GPIO.setup(keypadOut, GPIO.OUT)
GPIO.setup(keypadIn, GPIO.IN)
loadOffsets()
# cleanup(). Sets all motors and LEDs off and sets GPIO to standard values
def cleanup():
global running
running = False
stop()
if (leds != None):
clear()
show()
time.sleep(0.1)
GPIO.cleanup()
# version(). Returns 3 for Pi2Go2. Invalid until after init() has been called
def version():
return PGType
# End of General Functions
#======================================================================
#======================================================================
# Motor Functions
#
# stop(): Stops both motors - coasts slowly to a stop
def stop():
global lDir, rDir
p.ChangeDutyCycle(0)
q.ChangeDutyCycle(0)
a.ChangeDutyCycle(0)
b.ChangeDutyCycle(0)
lDir = 0
rDir = 0
# brake(): Stops both motors - regenrative braking to stop quickly
def brake():
global lDir, rDir
p.ChangeDutyCycle(100)
q.ChangeDutyCycle(100)
a.ChangeDutyCycle(100)
b.ChangeDutyCycle(100)
lDir = 0
rDir = 0
# forward(speed): Sets both motors to move forward at speed. 0 <= speed <= 100
def forward(speed):
global lDir, rDir
if (lDir == -1 or rDir == -1):
brake()
time.sleep(0.2)
p.ChangeDutyCycle(speed)
q.ChangeDutyCycle(0)
a.ChangeDutyCycle(speed)
b.ChangeDutyCycle(0)
p.ChangeFrequency(max(speed/2, 10))
a.ChangeFrequency(max(speed/2, 10))
lDir = 1
rDir = 1
# reverse(speed): Sets both motors to reverse at speed. 0 <= speed <= 100
def reverse(speed):
global lDir, rDir
if (lDir == 1 or rDir == 1):
brake()
time.sleep(0.2)
p.ChangeDutyCycle(0)
q.ChangeDutyCycle(speed)
a.ChangeDutyCycle(0)
b.ChangeDutyCycle(speed)
q.ChangeFrequency(max(speed/2, 10))
b.ChangeFrequency(max(speed/2, 10))
lDir = -1
rDir = -1
# spinLeft(speed): Sets motors to turn opposite directions at speed. 0 <= speed <= 100
def spinLeft(speed):
global lDir, rDir
if (lDir == 1 or rDir == -1):
brake()
time.sleep(0.2)
p.ChangeDutyCycle(0)
q.ChangeDutyCycle(speed)
a.ChangeDutyCycle(speed)
b.ChangeDutyCycle(0)
q.ChangeFrequency(min(speed+5, 20))
a.ChangeFrequency(min(speed+5, 20))
lDir = -1
rDir = 1
# spinRight(speed): Sets motors to turn opposite directions at speed. 0 <= speed <= 100
def spinRight(speed):
global lDir, rDir
if (lDir == -1 or rDir == 1):
brake()
time.sleep(0.2)
p.ChangeDutyCycle(speed)
q.ChangeDutyCycle(0)
a.ChangeDutyCycle(0)
b.ChangeDutyCycle(speed)
p.ChangeFrequency(min(speed+5, 20))
b.ChangeFrequency(min(speed+5, 20))
lDir = 1
rDir = -1
# turnForward(leftSpeed, rightSpeed): Moves forwards in an arc by setting different speeds. 0 <= leftSpeed,rightSpeed <= 100
def turnForward(leftSpeed, rightSpeed):
global lDir, rDir
if (lDir == -1 or rDir == -1):
brake()
time.sleep(0.2)
p.ChangeDutyCycle(leftSpeed)
q.ChangeDutyCycle(0)
a.ChangeDutyCycle(rightSpeed)
b.ChangeDutyCycle(0)
p.ChangeFrequency(min(leftSpeed+5, 20))
a.ChangeFrequency(min(rightSpeed+5, 20))
lDir = 1
rDir = 1
# turnReverse(leftSpeed, rightSpeed): Moves backwards in an arc by setting different speeds. 0 <= leftSpeed,rightSpeed <= 100
def turnReverse(leftSpeed, rightSpeed):
global lDir, rDir
if (lDir == 1 or rDir == 1):
brake()
time.sleep(0.2)
p.ChangeDutyCycle(0)
q.ChangeDutyCycle(leftSpeed)
a.ChangeDutyCycle(0)
b.ChangeDutyCycle(rightSpeed)
q.ChangeFrequency(min(leftSpeed+5, 20))
b.ChangeFrequency(min(rightSpeed+5, 20))
lDir = -1
rDir = -1
# End of Motor Functions
#======================================================================
#======================================================================
# Wheel Sensor Functions
def stopL():
p.ChangeDutyCycle(100)
q.ChangeDutyCycle(100)
def stopR():
a.ChangeDutyCycle(100)
b.ChangeDutyCycle(100)
def lCounter(pin):
global countL, targetL
countL += 1
if countL == targetL:
stopL()
targetL = -1
def rCounter(pin):
global countR, targetR
countR += 1
if countR == targetR:
stopR()
targetR = -1
# stepForward(speed, steps): Moves forward specified number of counts, then stops
def stepForward(speed, counts):
global countL, countR, targetL, targetR
countL = 0
countR = 0
targetL = counts-(speed/20)
targetR = counts-(speed/20)
forward(speed)
while (targetL != -1) or (targetR != -1):
time.sleep(0.002)
# stepReverse(speed, steps): Moves backward specified number of counts, then stops
def stepReverse(speed, counts):
global countL, countR, targetL, targetR
countL = 0
countR = 0
targetL = counts-(speed/20)
targetR = counts-(speed/20)
reverse(speed)
while (targetL != -1) or (targetR != -1):
time.sleep(0.002)
# stepSpinL(speed, steps): Spins left specified number of counts, then stops
def stepSpinL(speed, counts):
global countL, countR, targetL, targetR
countL = 0
countR = 0
targetL = counts-(speed/20)
targetR = counts-(speed/20)
spinLeft(speed)
while (targetL != -1) or (targetR != -1):
time.sleep(0.002)
# stepSpinR(speed, steps): Spins right specified number of counts, then stops
def stepSpinR(speed, counts):
global countL, countR, targetL, targetR
countL = 0
countR = 0
targetL = counts-(speed/20)
targetR = counts-(speed/20)
spinRight(speed)
while (targetL != -1) or (targetR != -1):
time.sleep(0.002)
# End of Wheel Sensor Functions
#======================================================================
#======================================================================
# IR Sensor Functions
#
# irLeft(): Returns state of Left IR Obstacle sensor
def irLeft():
if GPIO.input(irFL)==0:
return True
else:
return False
# irRight(): Returns state of Right IR Obstacle sensor
def irRight():
if GPIO.input(irFR)==0:
return True
else:
return False
# irAll(): Returns true if either of the Obstacle sensors are triggered
def irAll():
if GPIO.input(irFL)==0 or GPIO.input(irFR)==0:
return True
else:
return False
# irLeftLine(): Returns state of Left IR Line sensor
def irLeftLine():
if GPIO.input(lineLeft)==0:
return True
else:
return False
# irRightLine(): Returns state of Right IR Line sensor
def irRightLine():
if GPIO.input(lineRight)==0:
return True
else:
return False
# End of IR Sensor Functions
#======================================================================
#======================================================================
# UltraSonic Functions
#
# getDistance(). Returns the distance in cm to the nearest reflecting object. 0 == no object
#
def getDistance(): # default to front sensor
GPIO.setup(sonar, GPIO.OUT)
# Send 10us pulse to trigger
GPIO.output(sonar, True)
time.sleep(0.00001)
GPIO.output(sonar, False)
start = time.time()
count=time.time()
GPIO.setup(sonar,GPIO.IN)
while GPIO.input(sonar)==0 and time.time()-count<0.1:
start = time.time()
count=time.time()
stop=count
while GPIO.input(sonar)==1 and time.time()-count<0.1:
stop = time.time()
# Calculate pulse length
elapsed = stop-start
# Distance pulse travelled in that time is time
# multiplied by the speed of sound 34000(cm/s) divided by 2
distance = elapsed * 17000
return distance
# End of UltraSonic Functions
#======================================================================
#======================================================================
# RGB LED Functions
#
def setColor(color):
for i in range(numPixels):
setPixel(i, color)
def setPixel(ID, color):
if (ID <= numPixels):
leds.setPixelColor(ID, color)
def show():
leds.show()
def clear():
for i in range(numPixels):
setPixel(i, 0)
def rainbow():
i = 0
for x in range(numPixels):
setPixel(x, int(wheel(x * 256 / numPixels)))
def fromRGB(red, green, blue):
return ((int(red)<<16) + (int(green)<<8) + blue)
def toRGB(color):
return (((color & 0xff0000) >> 16), ((color & 0x00ff00) >> 8), (color & 0x0000ff))
def wheel(pos):
"""Generate rainbow colors across 0-255 positions."""
if pos < 85:
return fromRGB(255 - pos * 3, pos * 3, 0) # Red -> Green
elif pos < 170:
pos -= 85
return fromRGB(0, 255 - pos * 3, pos * 3) # Green -> Blue
else:
pos -= 170
return fromRGB(pos * 3, 0, 255 - pos * 3) # Blue -> Red
#
# End of RGB LED Functions
#======================================================================
#======================================================================
# Servo Functions
def setServo(Servo, Degrees):
pca9685.setServo(Servo, Degrees + offsets[Servo])
def stopServos():
for i in range(16):
pca9685.stopServo(i)
# End of Servo Functions
#======================================================================
#======================================================================
# Keypad Functions
def getKey():
keys = 0
count = 0
fred = 0
GPIO.output(keypadOut, 0)
while (keys == 0):
#GPIO.output(keypadOut, 1)
time.sleep(0.00001)
while (GPIO.input(keypadIn) == 1):
count += 1
if (count > 1000):
count = 0
time.sleep(0.001)
while (GPIO.input(keypadIn) == 0):
pass
for index in range(16):
GPIO.output(keypadOut, 0)
fred += 1 # dummy
keys = (keys << 1) + GPIO.input(keypadIn)
GPIO.output(keypadOut, 1)
fred -= 1 # dummy
keys = 65535 - keys
return keys
# End of Keypad Functions
#======================================================================
#======================================================================
# EEROM Functions
# First 16 bytes are used for servo offsets (signed bytes)
# Low level read function. Reads data from actual Address
def rdEEROM(Address):
bus.write_i2c_block_data(EEROM, Address >> 8, [Address & 0xff])
return ((bus.read_byte(EEROM) + 0x80) & 0xff) - 0x80 # sign extend
# Low level write function. Writes Data to actual Address
def wrEEROM(Address, Data):
bus.write_i2c_block_data(EEROM, Address >> 8, [Address & 0xff, Data])
time.sleep(0.01)
# General Read Function. Ignores first 16 bytes
def readEEROM(Address):
return rdEEROM(Address + 16)
# General Write Function. Ignores first 16 bytes
def writeEEROM(Address, Data):
wrEEROM(Address + 16, Data)
# Load all servo Offsets
def loadOffsets():
for idx in range(16):
offsets[idx] = rdEEROM(idx)
# Save all servo Offsets
def saveOffsets():
for idx in range(16):
wrEEROM(idx, offsets[idx])
# End of EEROM Functions
#======================================================================