Coding Paradigms for Device Control
Contents
Coding Paradigms for Device Control#
%serialconnect
Found serial ports: /dev/cu.usbmodem14101, /dev/cu.Bluetooth-Incoming-Port
Connecting to --port=/dev/cu.usbmodem14101 --baud=115200
Ready.
The Coding Challenge#
PD control for a Ball on beam device. The device is to sense the position of a ball on a 50cm beam, compare to a setpoint, and adjust beam position with servo motor. The setpoint and control constant is to be given by the device user. Display all relevant data. Use a button to start and stop operation.
Devices:
Distance sensor - sense ball position
Analog actuator - change beam angle
Analog sensor - proportional gain
Analog sensor - derivative time
Analog sensor - setpoint
Display - display position, angle
Display - display control parameters
Button - Start/Stop
Create code to:
Measure the ball position
Perform an action in response to the analog signal
Display state on LCD
Use on-board LED to show operational status
Coding Paradigms#
Single threaded, imperative coding
Python classes
further modularizes coding
data logging classes* Python classes
further modularizes coding
data logging classes
Python generators
separates event loop from device details
modularizes the device coding
each device can maintain a separate state
Asynchronous coding
further abstraction the event loop
non-blocking
multi-threading
Single threaded, imperative coding#
from machine import Pin, PWM
import time
class Servo(object):
def __init__(self, gpio, freq=50):
self.gpio = gpio
self.pwm = PWM(Pin(gpio, Pin.IN))
self.pwm.freq(freq)
self.pwm.duty_ns(0)
def set_value(self, value):
self.pulse_us = 500 + 20*max(0, min(100, value))
self.pwm.duty_ns(int(1000*self.pulse_us))
def off(self):
self.pwm.duty_ns(0)
servo = Servo(16)
servo.set_value(0)
time.sleep(2)
servo.set_value(100)
time.sleep(2)
servo.off()
.
from machine import Pin, I2C
from lcd1602 import LCD1602 as LCD
class Screen(object):
def __init__(self, id, sda, scl):
self.sda = Pin(sda, Pin.OUT)
self.scl = Pin(scl, Pin.OUT)
self.i2c = I2C(id, sda=self.sda, scl=self.scl)
self.lcd = LCD(self.i2c, 2, 16)
self.lcd.clear()
def print(self, lines):
for k, line in enumerate(lines):
if line is not None:
self.lcd.setCursor(0, k)
self.lcd.print(line)
screen0 = Screen(0, sda=8, scl=9)
screen1 = Screen(1, sda=6, scl=7)
screen0.print(("Hello World", "Go Irish!"))
screen1.print(["", "Hello"])
from machine import Pin
class PWM_motor(object):
def __init__(self, gpio):
self.pwm = PWM(Pin(gpio))
class Servo(PWM_motor):
def __init__(self, gpio, freq=50):
super(Y, self).__init__(gpio)
self.pwm.freq(freq)
def set_value(self, value):
self.pulse_us = 500 + 20*max(0, min(100, value))
self.pwm.duty_ns(int(1000*self.pulse_us))
servo = Servo(16)
servo.set_value(50)
[missing-OK]O
[missing-OK]
[missing-OK]
Traceback (most recent call last):
[missing-OK]
File "<stdin>
[missing-OK]
", line 16, in <module>
[missing-OK]
File "<stdin>
[missing-OK]
", line 9, in __init__
[missing-OK]
NameError: name 'Y' isn't defined
[missing-OK]
>
[missing-OK]
**[ys]
[missing-OK]
<class 'serial.serialutil.SerialException'>
[missing-OK]
**[ys]
[missing-OK]
read failed: [Errno 6] Device not configured
[missing-OK]
**[ys]
[missing-OK]
<class 'serial.serialutil.SerialException'>
[missing-OK]
**[ys]
[missing-OK]
read failed: [Errno 6] Device not configured
import machine
import time
class UltrasonicSensor(object):
def __init__(self, gpio):
self.pin = Pin(gpio)
def get_distance_cm(self):
# send pulse
self.pin.init(Pin.OUT)
self.pin.value(0)
time.sleep_us(2)
self.pin.value(1)
time.sleep_us(10)
self.pin.value(0)
# listen for response
self.pin.init(Pin.IN)
# wait for on
t0 = time.ticks_us()
count = 0
while count < 10000:
if self.pin.value():
break
count += 1
# wait for off
t1 = time.ticks_us()
count = 0
while count < 10000:
if not self.pin.value():
break
count += 1
t2 = time.ticks_us()
if t1 - t2 < 530:
return (t2 - t1) / 29 / 2
else:
return 0
sensor = UltrasonicSensor(20)
print(sensor.get_distance_cm())
12.7069
from machine import Pin, I2C, ADC, PWM
import time
from lcd1602 import LCD1602 as LCD
from knob import Knob
class Servo(object):
def __init__(self, gpio, freq=50):
self.gpio = gpio
self.pwm = PWM(Pin(gpio))
self.pwm.freq(freq)
self.pwm.duty_ns(0)
def set_value(self, value):
self.pulse_us = 500 + 20*max(0, min(100, value))
self.pwm.duty_ns(int(1000*self.pulse_us))
def off(self):
self.pwm.duty_ns(0)
class Screen(object):
def __init__(self, id, sda, scl):
self.sda = Pin(sda, Pin.OUT)
self.scl = Pin(scl, Pin.OUT)
self.i2c = I2C(id, sda=self.sda, scl=self.scl)
self.lcd = LCD(self.i2c, 2, 16)
self.lcd.clear()
def print(self, lines):
for k, line in enumerate(lines):
if line is not None:
self.lcd.setCursor(0, k)
self.lcd.print(line)
## set up led
led = Pin(25, Pin.OUT)
## set up lcd display 0
display0 = Screen(0, sda=8, scl=9)
display1 = Screen(1, sda=6, scl=7)
## setup rotary angle sensors
knob0 = Knob(26)
knob1 = Knob(27)
## setup ultra-sonic distance sensor on Pin 20
sensor = UltrasonicSensor(20)
## set up servo motor
servo = Servo(16)
start = time.time()
ball_position = 0
while time.time() - start < 20:
ball_position = sensor.get_distance_cm()
ball_setpoint = 50*knob0.get_value()/100
display0.print([f"SP = {ball_setpoint:0.2f} cm",
f"PV = {ball_position:0.2f} cm"])
Kp = knob1.get_value()
u = Kp*(ball_setpoint - ball_position)
servo.set_value(u)
display1.print([f"Kp = {Kp}", f"MV = {dt_us}"])
time.sleep(0.1)
servo.off()
[missing-OK]O....
[missing-OK]
[missing-OK]
>
..............................................................................................................................................................................
[missing-OK]
aw
.
[missing-OK]
a
..
[missing-OK]
**[ys]
[missing-OK]
<class 'serial.serialutil.SerialException'>
[missing-OK]
**[ys]
[missing-OK]
device reports readiness to read but returned no data (device disconnected or multiple access on port?)
.....
[missing-OK]
ra REPL;
.
[missing-OK]
aw
...
[missing-OK]
raw REPL; CTRL-B to exit
[missing-OK]
aw>
...
[missing-OK]
**[ys]
[missing-OK]
<class 'serial.serialutil.SerialException'>
[missing-OK]
**[ys]
[missing-OK]
device reports readiness to read but returned no data (device disconnected or multiple access on port?)
Discuss#
Does this code provide a working prototype?
Is this code maintaina