Programming
Imports
There is a strange way that variables are imported.
If you import a variable , then you are simple making a copy.
If you change your copy, the data in the import is not updated,
and functions in the import are referring to their original variable.
Likewise if a function in the import runs, and changes the variable ,
you won't see that value in the main program.
# file main.py from imp import * print("local:",AAA) # print local AAA copy printa() # print value inside imp # print("setting imp to 1") seta(1) # print("local:",AAA) # print local AAA copy printa() # print value inside imp # print("setting local to 2") AAA=2 # print("local:",AAA) # print local AAA copy printa() # print value inside imp
# file imp.py AAA = 0 def printa(): global AAA print("imported:",AAA) def seta(value): global AAA AAA=value
Results of running main.py:
local: 0
imported: 0
setting imp to 1
local: 0
imported: 1
setting local to 2
local: 2
imported: 1
Interrupts
The input pins on a Pico can be programmed to run a given Python function, when they change state.
They must be digital inputs, and they can cause the function to run either when switching on, or off, or both on and off.
There are problems if interrupts occur while the program is writing a log file
see below for how to solve this
The following commands switch on the wheel sensor interrupts:
See a program to go a certain distance and stop.
And one to turn left 90 degrees.
# the following pins are connected to the encoder inputs: lwheel = Pin(13,Pin.IN,Pin.PULL_UP) rwheel = Pin(14,Pin.IN,Pin.PULL_UP) # the following are the counts. One for each wheel LDistance=0 RDistance=0 #These 2 functions handle the interrupts: def Lwheelirq(which): global LDistance # count of wheel markers ledL.value(lwheel.value()) # light the sensor bar LED accordingly LDistance += 1 # increase count def Rwheelirq(which): global RDistance # count of wheel markers ledR.value(rwheel.value()) # light the sensor bar LED accordingly RDistance += 1 # increase count # set up interrupt handler for each pin lwheel.irq(handler=Lwheelirq,trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING) rwheel.irq(handler=Rwheelirq,trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING)
How to disable interrupts over a section of code: (You will need to import library "machine")
# this disables all interrupts and saves the settings in "irqvals" irqvals=machine.disable_irq() # this is the code that must be protected f = open("log.txt", "a") f.write(str(L)+","+str(F)+","+str(R)+"\n") f.close() # this re-enables the interrupts previously disabled using "irqvals" machine.enable_irq(irqvals)
Interrupt problems
We had some Problems With the forward() routineIt was exiting before travelling the required distance.
This happened very infrequently.
It occurred less often when there was a pause 0.2 seconds between calls to forward().
It is possibly caused by the fact that forward() resets LDistance and RDistance
to zero at the start of the routine, but it does not stop interrupts while it does so.
If the motors are turning then there is a clash
From ://docs.micropython.org/en/latest/reference/isr_rules.htm See the "Critical sections" paragraph
"Where data is shared between the main program and an ISR(Interrupt Service Routine),
consider disabling interrupts prior to accessing the data in the main program
and re-enabling them immediately afterwards (see Critical Sections)."
It seems the interrupt is clashing with the statement at the start of forward()
preventing it from actually setting the distances to zero.
This would cause forward() to return very quickly without travelling any distance.
Solutions.
A)
Disable interrupts while the distances are being set to zero in each of the 4 movement subroutines.
# this disables all interrupts and saves the settings in "irqvals" irqvals=machine.disable_irq() global LDistance, RDistance LDistance=0 RDistance=0 # re-enable the interrupts previously disabled using "irqvals" machine.enable_irq(irqvals)
Or
B)
Don't set the distances to zero. - Just let them increase, and calculate a stopping point, from the value on entry.
Serial input
The input pins on a Pico can be used as a Universal Asynchronous Receive Transmit port. (or UART)..
It can communicate with a serial device, such as a Bluetooth module, or a TV Remote receiver.
These plug into the side of the robot.
The following is a program to receive the TV remote signals and toggle the LED when the TV remote "power" button: is pressed.
from machine import UART,Pin import time uart = UART(0, 2400) # TV Remote. Speed init with given baudrate. 0 = TX/RX pins led = Pin(25, Pin.OUT) # inbuilt LED while True: time.sleep(.01) if uart.any(): # if a character is received bytes = uart.read(1) # read 1 byte from the UART try: char = bytes.decode('utf-8') # turn into a string character except: char="?" print(str("'"+char+"'"),ord(char)) # print out what came in if char=="P": led.toggle() # toggle the LED if Power button pressed
Going in a straight line
If your robot doesn't go in a straight line, when the motors are set the same "speed" ,
you can program it to use the wheel encoders to find out the necessary settings required
to get the wheels to actually go the same speeds.
This "auto-straightening" program makes small adjustments to the speed settings for the motors,
such that each wheel goes the same distance in a given time.
After a certain distance it stores the final settings in a file called "speeds.py" - This file can be imported into your program to get the right speeds
for straight running.
The auto-steering program is shown below:
# Auto-straightening program # 4/3/2023 J Fisher from UKMARS import * # the following pins are connected to the encoder inputs: # (Old piggyback) lwheel = Pin(13,Pin.IN,Pin.PULL_UP) rwheel = Pin(14,Pin.IN,Pin.PULL_UP) lmotor = Motor("L") rmotor = Motor("R") # the following are the counts. One for each wheel LDistance=0 RDistance=0 #These 2 functions handle the interrupts: def Lwheelirq(which): global LDistance # count of wheel markers ledL.value(lwheel.value()) # light the sensor bar LED accordingly LDistance += 1 # increase count def Rwheelirq(which): global RDistance # count of wheel markers ledR.value(rwheel.value()) # light the sensor bar LED accordingly RDistance += 1 # increase count # set up interrupt handler for each pin lwheel.irq(handler=Lwheelirq,trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING) rwheel.irq(handler=Rwheelirq,trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING) lmotor.speed(0) rmotor.speed(0) while Button.value() == 0: (left,front,right)= ReadWALLS() time.sleep(0.1) LDistance=0 RDistance=0 lspeed = 25 rspeed = 30 lmotor.speed(lspeed) rmotor.speed(rspeed) LOld=0 ROld=0 def logspeeds(): pfile = open("speeds.py","w") pfile.write("lspeed="+str(lspeed)+"\r\n") pfile.write("rspeed="+str(rspeed)+"\r\n") pfile.close() print("\r\nspeeds.py written") while LDistance <400: err = LDistance-LOld - RDistance+ROld if (LDistance-LOld) > (RDistance-ROld): lspeed= lspeed+(err/5) elif (RDistance-ROld) > (LDistance-LOld): lspeed= lspeed-(err/5) print(LDistance-LOld,RDistance-ROld,lspeed,rspeed) LOld=LDistance ROld=RDistance lmotor.speed(lspeed) rmotor.speed(rspeed) time.sleep(0.2) lmotor.speed(0) rmotor.speed(0) logspeeds()
New Motor Attribute
The UKMARS Motor object has acquired a new Attribute : "correction"
it multiplies all future speed() settings.
It can be used to speed up (or slow down) a motor, to make the robot
go in a straight line when the Motors are give equal speed() s