sumorobot-firmware/hal.py
2018-01-11 19:35:09 +01:00

218 lines
6.0 KiB
Python

from utime import sleep_us, sleep_ms
from machine import Pin, PWM, ADC, time_pulse_us
# LEDs
ENEMY = 0
STATUS = 1
LEFT_LINE = 2
RIGHT_LINE = 3
# directions
STOP = 0
LEFT = 1
RIGHT = 2
FORWARD = 3
BACKWARD = 4
# blockl highlight
BLOCK_HIGHLIGHT = True
# Battery resistor ladder ratio
BATTERY_COEFF = 2.25
# Ultrasonic sensor calibration
ULTRASONIC_OFFSET = 800
# Servo timing
MOTOR_LEFT_TUNING = 33
MOTOR_RIGHT_TUNING = 33
# Calibrate line sensors
LINE_LEFT_THRESHOLD = 1000
LINE_RIGHT_THRESHOLD = 1000
class Sumorobot(object):
# Ultrasonic distance sensor
echo = Pin(14, Pin.IN)
trigger = Pin(27, Pin.OUT)
# Motor PWM-s
pwm_left = PWM(Pin(15), freq=50, duty=0)
pwm_right = PWM(Pin(4), freq=50, duty=0)
# bottom LED
bottom_led = Pin(5, Pin.OUT)
# bottom LED is in reverse polarity
bottom_led.value(1)
# sensor LEDs
enemy_led = Pin(16, Pin.OUT)
left_line_led = Pin(17, Pin.OUT)
right_line_led = Pin(12, Pin.OUT)
# Battery gauge
adc_battery = ADC(Pin(32))
# Optek sensors
adc_line_left = ADC(Pin(34))
adc_line_right = ADC(Pin(33))
# Set reference voltage to 3.3V
adc_battery.atten(ADC.ATTN_11DB)
adc_line_left.atten(ADC.ATTN_11DB)
adc_line_right.atten(ADC.ATTN_11DB)
# for highlighting blocks
highlight_block = None
# for terminating sleep
terminate = False
def __init__(self, highlight_block):
self.highlight_block = highlight_block
def set_led(self, led, state):
if led == STATUS:
self.bottom_led.value(0 if state else 1)
elif led == ENEMY:
self.enemy_led.value(state)
elif led == LEFT_LINE:
self.left_line_led.value(state)
elif led == RIGHT_LINE:
self.right_line_led.value(state)
def get_battery_voltage(self):
return round(BATTERY_COEFF * (self.adc_battery.read() * 3.3 / 4096), 2)
def get_enemy_distance(self):
# send a pulse
self.trigger.value(0)
sleep_us(5)
self.trigger.value(1)
sleep_us(10)
self.trigger.value(0)
# wait for the pulse and calculate the distance
return (time_pulse_us(self.echo, 1, 30000) / 2) / 29.1
enemy_score = 0
def is_enemy(self, block_id = None):
if block_id and BLOCK_HIGHLIGHT:
self.highlight_block(block_id)
# get the enemy distance
self.enemy_distance = self.get_enemy_distance()
# if the enemy is close and the ping actually returned
if self.enemy_distance < 60 and self.enemy_distance > 0:
# if not maximum score
if self.enemy_score < 5:
# increase the enemy score
self.enemy_score += 1
# if no enemy was detected
else:
# if not lowest score
if self.enemy_score > 0:
# decrease the enemy score
self.enemy_score -= 1
# if the sensor saw something more than 2 times
enemy = True if self.enemy_score > 2 else False
# trigger enemy LED
self.set_led(ENEMY, enemy)
return enemy
def get_line(self, dir):
# check for valid direction
assert dir == LEFT or dir == RIGHT
if dir == LEFT:
return self.adc_line_left.read()
elif dir == RIGHT:
return self.adc_line_right.read()
def is_line(self, dir, block_id = None):
# check for valid direction
assert dir == LEFT or dir == RIGHT
# if block_id given and BLOCK_HIGHLIGHT
if block_id and BLOCK_HIGHLIGHT:
self.highlight_block(block_id)
if dir == LEFT:
line = abs(self.adc_line_left.read() - LINE_LEFT_THRESHOLD) > 1000
self.set_led(LEFT_LINE, line)
return line
elif dir == RIGHT:
line = abs(self.adc_line_right.read() - LINE_RIGHT_THRESHOLD) > 1000
self.set_led(RIGHT_LINE, line)
return line
def detach_servos(self):
self.set_servo(LEFT, 0)
self.set_servo(RIGHT, 0)
prev_speed = {LEFT: 0, RIGHT: 0}
def set_servo(self, dir, speed):
# check for valid direction
assert dir == LEFT or dir == RIGHT
# check for valid speed
assert speed <= 100 and speed >= -100
# when the speed didn't change
if speed == self.prev_speed[dir]:
return
# record the new speed
self.prev_speed[dir] = speed
if dir == LEFT:
if speed == 0:
self.pwm_left.duty(0)
else:
self.pwm_left.duty(int(33 + MOTOR_LEFT_TUNING + speed * 33 / 100)) # -100 ... 100 to 33 .. 102
elif dir == RIGHT:
if speed == 0:
self.pwm_right.duty(0)
else:
self.pwm_right.duty(int(33 + MOTOR_RIGHT_TUNING + speed * 33 / 100)) # -100 ... 100 to 33 .. 102
def move(self, dir, block_id = None):
# check for valid direction
assert dir == STOP or dir == RIGHT or dir == LEFT or dir == BACKWARD or dir == FORWARD
# if block_id given and BLOCK_HIGHLIGHT
if block_id and BLOCK_HIGHLIGHT:
self.highlight_block(block_id)
if dir == STOP:
self.set_servo(LEFT, 0)
self.set_servo(RIGHT, 0)
elif dir == LEFT:
self.set_servo(LEFT, -100)
self.set_servo(RIGHT, -100)
elif dir == RIGHT:
self.set_servo(LEFT, 100)
self.set_servo(RIGHT, 100)
elif dir == FORWARD:
self.set_servo(LEFT, 100)
self.set_servo(RIGHT, -100)
elif dir == BACKWARD:
self.set_servo(LEFT, -100)
self.set_servo(RIGHT, 100)
def sleep(self, delay, block_id = None):
# check for valid delay
assert delay > 0
# if block_id given and BLOCK_HIGHLIGHT
if block_id and BLOCK_HIGHLIGHT:
self.highlight_block(block_id)
for j in range(0, delay, 50):
if self.terminate:
return # TODO: raise exception
else:
sleep_ms(50)