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() routine
It 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



from UKMARS import *
import time

lmotor = Motor("L")
rmotor = Motor("R")

# make left motor slightly faster
lmotor.correction = 1.08

lmotor.speed(30)
rmotor.speed(30)

time.sleep(0.4)
lmotor.speed(0)
rmotor.speed(0)