-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Integration of Pi Pico into the Donkeycar pin ecosystem
Because PiGPIO is not supported on RPi 5 any longer we have to move the pin support for realtime pin tasks to the Pi Pico. This allows to migrate PWM input pins onto the Pico for using an RC controller to drive the car, as well as using the Pico's PWM pins to provide output pins for the servo and ESC. Other pins that required realtime signals like odometery or IR receivers can also be migrated to the Pico because we also support a PulseIn pin. In addition we aim to add support for the 4 analog pins to the pico, which for example could be used to monitor the battery voltage.
Changes in the pins module
The following new pins will be added:
class InputPinPico
class OutputPinPico
class InputPwmPinPico
class PwmPinPico
class PulseInPico
class AnalogInPicoThe pico serial communicator
The following new class will be added. This is not a DonkeyCar part, but a low-level serial communication link between the above mentioned pins and the Pico board, connected to the RPi. It will send serial data as fast as possible back and forth between the Pico and the internal pin cache of the car, such that signals can be transferred at much higher rates than the car frequency. This class runs the serial connection in its own thread.
class Pico:
"""
Pi Pico class to communicate with the Pico over usb. We use the same usb
cable for power and data. The Pico is connected to the computer and can
handle file copy / repl on /dev/ttyACM0 while doing bidirectional data
transfer on /dev/ttyACM1.
To set up the pins on the pico, we need to send a configuration dictionary.
An example is here:
pin_configuration = {
'input_pins': {
'GP13': dict(mode='INPUT', pull_up=False),
'GP18': dict(mode='PULSE_IN', maxlen=8, auto_clear=True),
'GP16': dict(mode='PULSE_IN', maxlen=4),
'GP17': dict(mode='PWM_IN', duty=0.09),
'GP28': dict(mode='ANALOG_IN'),
},
'output_pins': {
'GP14': dict(mode='OUTPUT', value=0),
'GP15': dict(mode='PWM', frequency=60, duty_cycle=0.06),
},
}
The dictionary is required to have either 'input_pins' or 'output_pins'
or both as keys. Input and output refers to the pins on the pico.
The values of 'input_pins' and 'output_pins' contain dictionaries with
the pin name as key and the pin configuration as value. The pin
configuration is a dictionary with key-values depending on the mode of
the pin. The 'mode' key is required for all pins. The different supported
input modes are:
'INPUT': for digital input
'PULSE_IN': for pulse input
'PWM_IN': for pwm input from an RC controller
'ANALOG_IN': for analog input
The different output modes are:
'OUTPUT': for digital output
'PWM': for pulse width modulation output
See above examples for the required keys for each mode.
"""
def __init__(self, port: str = '/dev/ttyACM1'):
"""
Initialize the Pico communicator.
:param port: port for data connection
"""
self.serial = serial.Serial(port, 115200)
self.counter = 0
self.running = True
self.pin_configuration = dict()
self.send_dict = dict()
self.receive_dict = dict()
self.lock = Lock()
self.start = None
logger.info(f"Pico created on port: {port}")
# send the initial setup dictionary to clear all pins
pack = json.dumps(dict(input_pins={}, output_pins={})) + '\n'
self.serial.write(pack.encode())
self.t = Thread(target=self.loop, args=(), daemon=True)
self.t.start()
def loop(self):
"""
Donkey parts interface. We are sending newline delimited json strings
and expect the same in return.
"""
# clear the input buffer
self.serial.reset_input_buffer()
self.start = time.time()
# start loop of continuous communication
while self.running:
try:
pack = None
with self.lock:
pack = json.dumps(self.send_dict) + '\n'
self.serial.write(pack.encode())
time.sleep(0)
bytes_in = self.serial.read_until()
time.sleep(0)
str_in = bytes_in.decode()[:-1]
received_dict = json.loads(str_in)
with self.lock:
self.receive_dict.update(received_dict)
if self.counter % 1000 == 0:
logger.debug(f'Last sent: {pack}')
logger.debug(f'Last received: {str_in}')
except ValueError as e:
logger.error(f'Failed to load json in loop {self.counter} '
f'because of {e}. Expected json, but got: '
f'+++{str_in}+++')
except Exception as e:
logger.error(f'Problem with serial input {e}')
self.counter += 1
logger.info('Pico loop stopped.')
def write(self, gpio: str, value: float or int) -> None:
"""
:param gpio: the gpio pin to write to
:param value: the value to write
"""
# Wait until threaded loop has at least run once, so we don't have to)
# process None values. This blocks until the first data is received.
while self.counter == 0:
time.sleep(0.1)
assert gpio in self.send_dict, f"Pin {gpio} not in send_dict."
with self.lock:
self.send_dict[gpio] = value
def read(self, gpio):
"""
:param gpio: the gpio pin to read from
:return: the value of the pin
"""
# Wait until threaded loop has at least run once, so we don't have to
# process None values. This blocks until the first data is received.
while self.counter == 0:
time.sleep(0.1)
with self.lock:
if gpio not in self.receive_dict:
msg = (f"Pin {gpio} not in receive_dict. Known pins: "
f"{', '.join(self.receive_dict.keys())}")
logger.error(msg)
raise RuntimeError(msg)
return self.receive_dict[gpio]
def stop(self):
logger.info("Stopping Pico communication.")
self.running = False
time.sleep(0.1)
self.t.join()
logger.info("Pico communication stopped.")
self.serial.close()
total_time = time.time() - self.start
logger.info(f"Pico communication disconnected, ran {self.counter} "
f"loops, each loop taking "
f"{total_time * 1000 / self.counter:5.1f} ms.")
def setup_input_pin(self, gpio: str, mode: str, **kwargs) -> None:
"""
:param gpio: the gpio pin to set up
:param mode: the mode of the pin
:param kwargs: additional arguments for the mode
"""
assert mode in ('INPUT', 'PULSE_IN', 'ANALOG_IN', 'PWM_IN'), \
f"Mode {mode} not supported for input pins."
setup_dict = dict(input_pins={gpio: dict(mode=mode, **kwargs)})
logger.info(f"Setting up input pin {gpio} in mode {mode} using "
f"setup dict {setup_dict}")
with self.lock:
# send the setup dictionary
pack = json.dumps(setup_dict) + '\n'
self.serial.write(pack.encode())
self.receive_dict[gpio] = 0
def setup_output_pin(self, gpio: str, mode: str, **kwargs) -> None:
"""
:param gpio: the gpio pin to set up
:param mode: the mode of the pin
:param kwargs: additional arguments for the mode
"""
assert mode in ('OUTPUT', 'PWM'), \
f"Mode {mode} not supported for output pins on Pico"
setup_dict = dict(output_pins={gpio: dict(mode=mode, **kwargs)})
logger.info(f"Setting up output pin {gpio} in mode {mode} using "
f"setup dict {setup_dict}")
with self.lock:
# send the setup dictionary
pack = json.dumps(setup_dict) + '\n'
self.serial.write(pack.encode())
self.send_dict[gpio] = 0 if mode == 'OUTPUT' else kwargs['duty']
def remove_pin(self, gpio: str) -> None:
"""
:param gpio: the gpio pin to remove
"""
setup_dict = dict()
logger.info(f"Removing pin {gpio}")
if gpio in self.receive_dict:
setup_dict['input_pins'] = {gpio: {}}
del self.receive_dict[gpio]
elif gpio in self.send_dict:
setup_dict['output_pins'] = {gpio: {}}
del self.send_dict[gpio]
else:
logger.warning(f"Pin {gpio} not in send or receive dict.")
return
with self.lock:
# send the setup dictionary
pack = json.dumps(setup_dict) + '\n'
self.serial.write(pack.encode())Circuitpython code and usage for the Pico
Circuitpython code to be installed on the Pico by copying files. Note, this has to be done only once. All pin creations and deletions will then be handled in software only. The boot.py file enables the second usb channel for communication. That channel will usually show up on the Pi under /dev/ttyACM1. The shell for accessing the REPL or the print command outputs can be accessed under /dev/ttyACM0.
# file boot.py
import usb_cdc
usb_cdc.enable(console=True, data=True)# file code.py
import time
import board
import digitalio
import analogio
import pulseio
import pwmio
import usb_cdc
import json
import microcontroller
class PulseInResettable:
def __init__(self, gpio, maxlen=2, auto_clear=False, **kwargs):
self.pin = pulseio.PulseIn(pin=gpio, maxlen=maxlen, **kwargs)
self.auto_clear = auto_clear
self.pin.clear()
def get_readings(self):
l = len(self.pin)
res = list(self.pin[i] for i in range(l))
if self.auto_clear:
self.pin.clear()
return res
def deinit(self):
self.pin.deinit()
class PWMIn(PulseInResettable):
def __init__(self, gpio, frequency=60, duty=0.09, min_us=1000,
max_us=2000, **kwargs):
super().__init__(gpio, maxlen=2, auto_clear=False, **kwargs)
self.duty = duty
self.min_us = min_us
self.max_us = max_us
self.frequency = frequency
def get_readings(self):
"""
Get the duty cycle from the last two readings. Assuming min and max
us are 1000 and 2000, respectively,
"""
r = super().get_readings()
if len(r) > 1:
duty_us = min(r[-2], r[-1])
# High signals should be between min_us and max_us. As we
# occasionally see duplicated readings like [1000, 1000] instead
# of [15666, 1000] we ignore readings which are out by more than
# 10% of the min_us or max_us.
if duty_us < 0.9 * self.min_us or duty_us > 1.1 * self.max_us:
return self.duty
self.duty = duty_us * self.frequency * 1e-6
return self.duty
class PWMOut:
def __init__(self, gpio, frequency=60, duty_cycle=0.09, **kwargs):
self.pin = pwmio.PWMOut(pin=gpio, frequency=frequency, **kwargs)
self.pin.duty_cycle = int(duty_cycle * 65535)
def deinit(self):
self.pin.deinit()
def set_duty_cycle(self, value):
""" Set the duty cycle of the PWM output. """
self.pin.duty_cycle = int(value * 65535)
def bytes_to_dict(byte_data, count):
if byte_data == b'':
return {}
str_in = byte_data.decode()[:-1]
if not str_in:
return {}
try:
out_dict = json.loads(str_in)
return out_dict
except ValueError as e:
print(f'Failed to decode JSON because of {e}',
f'from {str_in} in loop {count}.')
return {}
def dict_to_bytes(dict_data):
str_out = json.dumps(dict_data) + '\n'
byte_out = str_out.encode()
return byte_out
def pin_from_dict(pin_name, d):
print(f'Creating pin {pin_name} from dict: {d}')
# convert from pin_name string to board pin object
gpio = getattr(board, pin_name)
assert gpio != board.LED, 'Cannot assign LED pin as input or output.'
pin = None
if d['mode'] == 'INPUT':
pin = digitalio.DigitalInOut(gpio)
pin.direction = digitalio.Direction.INPUT
pull = d.get('pull') # None will work here, otherwise map
if pull == 1:
pull = digitalio.Pull.UP
elif pull == 2:
pull = digitalio.Pull.DOWN
pin.pull = pull
print(f'Configured digital input pin, gpio: {gpio}, pull: {pin.pull}')
elif d['mode'] == 'PULSE_IN':
pin = PulseInResettable(gpio, maxlen=d.get('maxlen', 2),
auto_clear=d.get('auto_clear', False))
print(f'Configured pulse-in pin, gpio: {gpio}, maxlen:',
f'{pin.pin.maxlen}, auto_clear: {pin.auto_clear}')
elif d['mode'] == 'PWM_IN':
pin = PWMIn(gpio, duty=d.get('duty_center', 0.09))
print(f'Configured pwm-in pin, gpio: {gpio}, duty_center: {pin.duty}')
elif d['mode'] == 'ANALOG_IN':
pin = analogio.AnalogIn(gpio)
print(f'Configured analog input pin, gpio: {gpio}')
elif d['mode'] == 'OUTPUT':
pin = digitalio.DigitalInOut(gpio)
pin.direction = digitalio.Direction.OUTPUT
pin.value = False
print(f'Configured digital output pin, gpio: {gpio},',
f'value: {pin.value}')
elif d['mode'] == 'PWM':
duty_cycle = d.get('duty_cycle', 0.09)
freq = int(d.get('frequency', 60))
pin = PWMOut(gpio, frequency=freq, duty_cycle=duty_cycle)
print(f'Configured pwm output pin, gpio: {gpio},',
f'frequency: {pin.pin.frequency},',
f'duty_cycle: {pin.pin.duty_cycle / 65535}')
return pin
def deinit_pin(pin):
try:
pin.deinit()
print(f'De-initialise pin: {pin}')
except AttributeError as e:
print(f'Pin has no deinit method: {e}')
except Exception as e:
print(f'Pin deinit failed: {e}')
def reset_all_pins(input_pins, output_pins):
for pin in (input_pins | output_pins).values():
deinit_pin(pin)
input_pins.clear()
output_pins.clear()
print(f'Reset all pins.')
def setup(setup_dict, input_pins, output_pins):
if not setup_dict:
return False
# if both input_pins and output_pins are empty, we are clearing all pins
print(f'Starting setup -------->')
print(f'Received setup dict: {setup_dict}')
in_dict, out_dict = tuple(setup_dict.get(key, {})
for key in ['input_pins', 'output_pins'])
if len(in_dict) == 0 and len(out_dict) == 0:
reset_all_pins(input_pins, output_pins)
# merge pins from setup dict into input_pins and output_pins
t_list = zip((in_dict, out_dict), (input_pins, output_pins))
for setup_io_dict, pins in t_list:
for pin_name, pin_dict in setup_io_dict.items():
if pin_name in pins:
deinit_pin(pins[pin_name])
if len(pin_dict) == 0:
print(f'Removing pin {pin_name}')
del pins[pin_name]
continue
else:
print(f'Overwriting {pin_name}')
try:
pins[pin_name] = pin_from_dict(pin_name, pin_dict)
except Exception as e:
print(f'Setup of {pin_name} failed because of {e}.')
print(f'Finished setup unexpectedly <--------')
return False
print(f'Updated input pins: {input_pins}')
print(f'Updated output pins: {output_pins}')
print(f'Finished setup <--------')
return True
def update_output_pins(output_data, output_pins):
for pin_name, value in output_data.items():
out_pin = output_pins.get(pin_name)
try:
if isinstance(out_pin, digitalio.DigitalInOut):
out_pin.value = bool(value)
elif isinstance(out_pin, PWMOut):
out_pin.set_duty_cycle(value)
else:
print(f'Cannot update pin out_pin: {pin_name} \
because of unknown type {type(out_pin)}.')
except ValueError as e:
print(f'Failed update output pin {pin_name} because of {e}')
def read(serial, input_pins, output_pins, led, is_setup, count):
if serial.in_waiting > 0:
led.value = True
bytes_in = serial.readline()
#serial.reset_input_buffer()
read_dict = bytes_to_dict(bytes_in, count)
# if setup dict sent, this contains 'input_pins' or 'output_pins'
if 'input_pins' in read_dict or 'output_pins' in read_dict:
is_setup = setup(read_dict, input_pins, output_pins)
# only call update_output_pins if setup has been done
elif is_setup:
update_output_pins(read_dict, output_pins)
else:
led.value = False
return is_setup
def write(serial, input_pins, write_dict):
""" Return list if no error or return error as string"""
for name, pin in input_pins.items():
if type(pin) in (digitalio.DigitalInOut, analogio.AnalogIn):
write_dict[name] = pin.value
elif type(pin) in (PulseInResettable, PWMIn):
write_dict[name] = pin.get_readings()
byte_out = dict_to_bytes(write_dict)
n = serial.write(byte_out)
return n
def main():
print('\n************ Starting pi pico ************')
microcontroller.cpu.frequency = 180000000
print(f'Current CPU frequency: {microcontroller.cpu.frequency}')
serial = usb_cdc.data
serial.timeout = 0.01
serial.reset_input_buffer()
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
led.value = False
input_pins = {}
output_pins = {}
write_dict = {}
is_setup = False
count = 0
tic = time.monotonic()
total_time = 0
try:
while True:
# reading input
is_setup = read(serial, input_pins, output_pins, led,
is_setup, count)
# sending output, catching number of bytes written
if is_setup:
n = write(serial, input_pins, write_dict)
toc = time.monotonic()
total_time += toc - tic
tic = toc
count += 1
except KeyboardInterrupt:
led.value = False
if __name__ == '__main__':
main()Running on the Pico
Here is a shell output from running above code on the Pico:
************ Starting pi pico ************
Current CPU frequency: 180000000
Starting setup -------->
Received setup dict: {'input_pins': {}, 'output_pins': {}}
Reset all pins.
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {'mode': 'PWM_IN', 'duty': 0.09}}}
Creating pin GP19 from dict: {'mode': 'PWM_IN', 'duty': 0.09}
Configured pwm-in pin, gpio: board.GP19, duty_center: 0.09
Updated input pins: {'GP19': <PWMIn object at 0x2000ef70>}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {}}}
De-initialise pin: <PWMIn object at 0x2000ef70>
Removing pin GP19
Updated input pins: {}
Updated output pins: {}
Finished setup <--------As you can see, this was a simple session where only a single PWM pin was created and deleted. Deletion happens by calling stop() on the pin. Here is the output of another session where multiple pins were created and deleted. This is the output created by running the pins.py script multiple times with different arguments creating different pins.
************ Starting pi pico ************
Current CPU frequency: 180000000
Starting setup -------->
Received setup dict: {'output_pins': {'GP1': {'mode': 'OUTPUT'}}}
Creating pin GP1 from dict: {'mode': 'OUTPUT'}
Configured digital output pin, gpio: board.GP1, value: False
Updated input pins: {}
Updated output pins: {'GP1': <DigitalInOut>}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP0': {'mode': 'INPUT', 'pull': 1}}}
Creating pin GP0 from dict: {'mode': 'INPUT', 'pull': 1}
Configured digital input pin, gpio: board.GP0, pull: digitalio.Pull.UP
Updated input pins: {'GP0': <DigitalInOut>}
Updated output pins: {'GP1': <DigitalInOut>}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP0': {}}}
De-initialise pin: <DigitalInOut>
Removing pin GP0
Updated input pins: {}
Updated output pins: {'GP1': <DigitalInOut>}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP0': {'mode': 'INPUT', 'pull': 1}}}
Creating pin GP0 from dict: {'mode': 'INPUT', 'pull': 1}
Configured digital input pin, gpio: board.GP0, pull: digitalio.Pull.UP
Updated input pins: {'GP0': <DigitalInOut>}
Updated output pins: {'GP1': <DigitalInOut>}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP0': {}}}
De-initialise pin: <DigitalInOut>
Removing pin GP0
Updated input pins: {}
Updated output pins: {'GP1': <DigitalInOut>}
Finished setup <--------
Starting setup -------->
Received setup dict: {'output_pins': {'GP1': {}}}
De-initialise pin: <DigitalInOut>
Removing pin GP1
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP18': {'mode': 'PWM_IN', 'duty': 0.09}}}
Creating pin GP18 from dict: {'mode': 'PWM_IN', 'duty': 0.09}
Configured pwm-in pin, gpio: board.GP18, duty_center: 0.09
Updated input pins: {'GP18': <PWMIn object at 0x200108d0>}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP18': {}}}
De-initialise pin: <PWMIn object at 0x200108d0>
Removing pin GP18
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {'mode': 'PWM_IN', 'duty': 0.09}}}
Creating pin GP19 from dict: {'mode': 'PWM_IN', 'duty': 0.09}
Configured pwm-in pin, gpio: board.GP19, duty_center: 0.09
Updated input pins: {'GP19': <PWMIn object at 0x20012a00>}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {}}}
De-initialise pin: <PWMIn object at 0x20012a00>
Removing pin GP19
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {'mode': 'PWM_IN', 'duty': 0.09}}}
Creating pin GP19 from dict: {'mode': 'PWM_IN', 'duty': 0.09}
Configured pwm-in pin, gpio: board.GP19, duty_center: 0.09
Updated input pins: {'GP19': <PWMIn object at 0x20012410>}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {}}}
De-initialise pin: <PWMIn object at 0x20012410>
Removing pin GP19
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP20': {'mode': 'PWM_IN', 'duty': 0.09}}}
Creating pin GP20 from dict: {'mode': 'PWM_IN', 'duty': 0.09}
Configured pwm-in pin, gpio: board.GP20, duty_center: 0.09
Updated input pins: {'GP20': <PWMIn object at 0x2000fd60>}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP20': {}}}
De-initialise pin: <PWMIn object at 0x2000fd60>
Removing pin GP20
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {'mode': 'PWM_IN', 'duty': 0.09}}}
Creating pin GP19 from dict: {'mode': 'PWM_IN', 'duty': 0.09}
Configured pwm-in pin, gpio: board.GP19, duty_center: 0.09
Updated input pins: {'GP19': <PWMIn object at 0x200116d0>}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {}}}
De-initialise pin: <PWMIn object at 0x200116d0>
Removing pin GP19
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {}, 'output_pins': {}}
Reset all pins.
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {'mode': 'PWM_IN', 'duty': 0.09}}}
Creating pin GP19 from dict: {'mode': 'PWM_IN', 'duty': 0.09}
Configured pwm-in pin, gpio: board.GP19, duty_center: 0.09
Updated input pins: {'GP19': <PWMIn object at 0x20013680>}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {}}}
De-initialise pin: <PWMIn object at 0x20013680>
Removing pin GP19
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {}, 'output_pins': {}}
Reset all pins.
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {'mode': 'PWM_IN', 'duty': 0.09}}}
Creating pin GP19 from dict: {'mode': 'PWM_IN', 'duty': 0.09}
Configured pwm-in pin, gpio: board.GP19, duty_center: 0.09
Updated input pins: {'GP19': <PWMIn object at 0x20015d20>}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {}}}
De-initialise pin: <PWMIn object at 0x20015d20>
Removing pin GP19
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {}, 'output_pins': {}}
Reset all pins.
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {'mode': 'PWM_IN', 'duty': 0.09}}}
Creating pin GP19 from dict: {'mode': 'PWM_IN', 'duty': 0.09}
Configured pwm-in pin, gpio: board.GP19, duty_center: 0.09
Updated input pins: {'GP19': <PWMIn object at 0x2000ebd0>}
Updated output pins: {}
Finished setup <--------
Starting setup -------->
Received setup dict: {'input_pins': {'GP19': {}}}
De-initialise pin: <PWMIn object at 0x2000ebd0>
Removing pin GP19
Updated input pins: {}
Updated output pins: {}
Finished setup <--------
Code done running.
Press any key to enter the REPL. Use CTRL-D to reload.
soft reboot
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
code.py output:The output above was created by initialising and running two digital input and output pins in the python interpreter (GP0 and GP1) and afterwards calling the pin.py script with different parameters like:
pins.py -w PICO.BCM.19 -tm 10Viewing serial output for debugging
The easiest way to view the above output is by installing and running tio, a linux terminal program that can be attached to the serial port by tio /dev/ttyACM0. Note, that tio provides you with a terminal, which means you can type Ctrl-C to enter the REPL and work on the Pico interactively or press Ctrl-D to return to the above program. The corresponding familiar terminal code shown on the end of that last session.