Making a Rpi Pico Based Smart Vehicle - Part 2

Introduction

This is Part 2 of the Project with the same title.



Summary of Part 1

Part 1 microPython program now has 4 python modules:

  1. Pico system Uart

  2. HC12 RF xmit/recv

  3. TB6612FNG 4WD motor driving

  4. Pico system interrupt


# *** pico_4wd_v90.py - tlfong01, 2021nov19hkt1719 ***

# Brief Description
# 1. Smart 4WD based on Rpi Pico, TB6612FNG, DC/BLDC motors, 433MHz HC12, 2.6GHz BLE HC02, Androd smart phone with
#    acceleromter and magnetometer

# Contents
# 1. Pico 4WD with TB6612 DC Motor Drivers and gear encoder motors N20, tt130, and TM310
#    1.1 Interrupt functions
#    1.2 DC motor function
# 2. Pico 4Wd with BLDC motors with built motor drivers (ie, TB6612FNG is not required
# 3. HC12 433MHz RF transceivers
#    3.1  uart loop back functions
#    3.2  HC12 transmit/receive echo functions
# 4. HC02 2.6GHz BLE slave modules
# 5. Integrating Parts 1, 2 with Parts 3, 4

# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========

from machine import Pin, PWM
import utime

# *** Pico GP pin assignments ***

uart0TxDNum  = 0 # HC12 #1 TxD
uart0RdDNum  = 1 # HC12 #2 RxD

hc12Setup1   = 2 # Reserved for SPI0 Sck
hc12Setup2   = 3 # Reserved for SPI0 Tx
i2c0Sda      = 4 # Reserved for SPI0 Rx
i2c0Scl      = 5 # Reserved for SPI0 Csn  

# *** PWM Pins ***

aPwmPinNum0  = 6 #0
bPwmPinNum0  = 7 #1

uart1TxdNum  = 8 # HC12 #2 TxD
uart1RxdNum  = 9 # HC12 #2 RxD

aPwmPinNum1  = 11 #2
bPwmPinNum1  = 10 #3

# *** Inrerrupt Pins ***

intPinNum0 = 12 #4 
intPinNum1 = 13 #5 
intPinNum2 = 14 #6 
intPinNum3 = 15 #7 

# *** Motor Driver Control Pins (TB6612FNG) ***

stdByPinNum0 = 16 #8
stdByPinNum1 = 17 #9

aIn1PinNum0  = 18 #10
aIn2PinNum0  = 19 #11
bIn1PinNum0  = 20 #12
bIn2PinNum0  = 21 #13

aIn1PinNum1  = 27 #14
aIn2PinNum1  = 28 #15
bIn1PinNum1  = 22 #16
bIn2PinNum1  = 26 #17

# *** Interrupt Pin Dicts ***

intGpPinNumDict = {'0': intPinNum0, 
                   '1': intPinNum1, 
                   '2': intPinNum2, 
                   '3': intPinNum3, 
                  }

intPinNumList = [intPinNum0, intPinNum1, intPinNum2, intPinNum3]

# Comments
# 1. There are 4 GP pins to read interrupts: intPinNum0, inPinNum1, intPinNum2, intPinNum3.

# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========

# *** Sleep time dicts ***

secondsDict = {'OneSecond'    :   1,
               'TwoSeconds'   :   2,
               'FourSeconds'  :   4,
               'TenSeconds'   :  10,               
               'SixtySeconds' :  60,
               'ThreeMinutes' : 180,
              }

def hold(secondsName):
    utime.sleep(secondsDict[secondsName])
    return

# Comments
# 1. Hold function is friendly version of utime.sleep.

# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========

# *** Interrupt global variables and dicts ***

global intCount0 
global intCount1
global intCount2
global intCount3

intCount0 = 0
intCount1 = 0
intCount2 = 0
intCount3 = 0

intCountDict = {
    '0': intCount0,
    '1': intCount1,
    '2': intCount2,
    '3': intCount3,
    }

# Comments
# 1. There are four global variable to hold interrupt event counts.
# 2. For this 4WD, the four interrupt signals are the four motor's rotary encoder output signals.
    
# *** Interrupt Callback Functions ***   

def intCallBack0(pin):
    global intCount0
    intCount0 = intCount0 + 1    
    return

def intCallBack1(pin):
    global intCount1
    intCount1 = intCount1 + 1    
    return

def intCallBack2(pin):
    global intCount2
    intCount2 = intCount2 + 1    
    return

def intCallBack3(pin):
    global intCount3
    intCount3 = intCount3 + 1    
    return

intCallBackDict = {
    '0': intCallBack0,
    '1': intCallBack1,
    '2': intCallBack2,
    '3': intCallBack3,    
    }

def countIntPinIntPeriod(intPinNum, countPeriod):
    global intCount0
    global intCount1
    global intCount2
    global intCount3
    
    intCount0 = 0
    intCount1 = 0
    intCount2 = 0
    intCount3 = 0

    utime.sleep(countPeriod)
 
    if intPinNum  == 0:
        intCount = intCount0
    elif intPinNum == 1:    
        intCount = intCount1
    elif intPinNum == 2:    
        intCount = intCount2
    else:    
        intCount = intCount3                
    return intCount

def countIntPinNumListIntPeriod(intPinNumList, countPeriod):
    intCountList = [0] * len(intPinNumList)
    for index in range(len(intPinNumList)):
        intCountList[index] = countIntPinIntPeriod(intPinNumList[index], countPeriod)                       
    return intCountList

# Comments
# 1. countIntPinIntPeriod(intPinNum, countPeriod) is to count the number of interrupts at the interrupt pin denoted by the
#      interrupt pin number.

# *** Test functions ***

def repeatCountIntPinNumListIntPeriod(intPinNumList, countPeriod, pauseTime, repeatTimes):
    print('\n  countIntPinNumListIntPeriod()')
    intGpPinNumList = [0] * len(intPinNumList)
    for index in range(len(intGpPinNumList)):
        intGpPinNumList[index]     = intGpPinNumDict[str(index)]
    print('    intPinNumList           =', intPinNumList)
    print('    intGpPinNumList         =', intGpPinNumList)
    print('    countPeriod (seconds)   =', countPeriod)
    print('    pauseTime (seconds)     =', pauseTime)    
    print('    repeat count times      =', repeatTimes)
    print('')
    
    for count in range(repeatTimes):
        ppsList             = countIntPinNumListIntPeriod(intPinNumList, countPeriod)
        print('    countList =', ppsList, end = '')
        print(' , min ', min(ppsList), end = '')
        print(' , max ', max(ppsList), end = '')
        print(' , dif ', max(ppsList) - min(ppsList), end = '')
        print(' , avg ', int(sum(ppsList) / len(ppsList)))      
        utime.sleep(pauseTime)    
    return

# Comments
# 1. repeatCountIntPinNumListIntPeriod(intPinNumList, countPeriod, pauseTime, repeatTimes)
#      repeatedly counts and prints tinterrupt events in countPeriod and p

# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========

# *** Part 3 - TB6612FNG Motor Driver Functions ***

# Comments
# 1. This 4WD uses two TB6612FNG DC motor drivers, each with two driver channels, with a total of 4 channels for four motors.
# 2. BLDC motor have built in motor drivers, therefor is not handled by TB6612 motor driver.

defaultDirection = 'Forward'
defaultSpeed     = 'VertHighSpeed'
defaultPwmFreq   = 1000
defaultDutyCycle = 90

dutyCycleDict = {
    'VeryVerySlow' : 95,
    'VeryFast'     : 90,
    'Fast'         : 80,
    'Normal'       : 50,
    'Slow'         : 40,
    'VerySlow'     : 30,
    'VeryVerySlow' : 25,
    }

motorConfigDictDict = \
    {
        '0': {'MotorDriverNum': 0, 'ChannelNum': 0},
        '1': {'MotorDriverNum': 0, 'ChannelNum': 1},
        '2': {'MotorDriverNum': 1, 'ChannelNum': 0},
        '3': {'MotorDriverNum': 1, 'ChannelNum': 1},        
    }

# Comment
# 1. There are four DC motors, each denoted/represented by a Motor Config Dictionary.
# 2. A motor config dict has two elementa:
#      (a) MotorDriverNum which the the number of one of the two TB6612FNG motor drivers.
#      (b) Channel Number, denoting each of the two channel of the motor driver.
# 3. motorConfigDictList01 = motorConfigDictList_00_01_10_11 = the list of all four motor confict dictionaries.
# 4. motorConfigDictListDict is the Dict of motorConfigDictLists (only one such dict list for now)

motorDriverGpPinNumDict = { \
    '0': {'StdByPinNum'  : stdByPinNum0,             
          '0' : {'In1PinNum'      : aIn1PinNum0,  
                 'In2PinNum'      : aIn2PinNum0,  
                 'PwmPinNum'      : aPwmPinNum0,
                 'PwmFreq'        : defaultPwmFreq,
                 'DutyCycle'      : defaultDutyCycle,
                 'IntPinNum'      : intPinNum0,
                 'IntPinCallBack' : intCallBack0,                 
                },
          '1' : {'In1PinNum'      : bIn1PinNum0,  
                 'In2PinNum'      : bIn2PinNum0,  
                 'PwmPinNum'      : bPwmPinNum0,
                 'PwmFreq'        : defaultPwmFreq,
                 'DutyCycle'      : defaultDutyCycle,
                 'IntPinNum'      : intPinNum1,
                 'IntPinCallBack' : intCallBack1,                  
                },  
          },                   
    '1': {'StdByPinNum'  : stdByPinNum1,             
          '0' : {'In1PinNum'      : aIn1PinNum1,  
                 'In2PinNum'      : aIn2PinNum1,  
                 'PwmPinNum'      : aPwmPinNum1,
                 'PwmFreq'        : defaultPwmFreq,
                 'DutyCycle'      : defaultDutyCycle,
                 'IntPinNum'      : intPinNum2,
                 'IntPinCallBack' : intCallBack2,                  
                },
          '1' : {'In1PinNum'      : bIn1PinNum1,  
                 'In2PinNum'      : bIn2PinNum1,  
                 'PwmPinNum'      : bPwmPinNum1,
                 'PwmFreq'        : defaultPwmFreq,
                 'DutyCycle'      : defaultDutyCycle,
                 'IntPinNum'      : intPinNum3,
                 'IntPinCallBack' : intCallBack3                  
                },  
          }, 
    }

# Comment
# 1. motorDriverGpPinNumDict specifies
#    (a) the GP pin numbers for the two TB6612FNG motor drivers ecah with two channels.
#    (b) the default values such as PWM frequency, duty cycles

def setupMotor(motorNum):
    motorConfigDict = motorConfigDictDict[str(motorNum)]
    motorDriverNum  = motorConfigDict['MotorDriverNum']
    channelNum      = motorConfigDict['ChannelNum']
    
    stdByPinNum     = motorDriverGpPinNumDict[str(motorDriverNum)]['StdByPinNum']
    in1PinNum       = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['In1PinNum']
    in2PinNum       = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['In2PinNum']
    
    pwmPinNum       = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['PwmPinNum']
    pwmFreq         = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['PwmFreq']    
    dutyCycle       = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['DutyCycle']     
    
    intPinNum       = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['IntPinNum']
    intPinCallBack  = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['IntPinCallBack']

    # *** Motor Driver Control Pin Setup ***    
    
    stdByPin    = Pin(stdByPinNum, Pin.OUT)
    in1Pin      = Pin(in1PinNum, Pin.OUT)    
    in2Pin      = Pin(in2PinNum, Pin.OUT)
    intPin      = Pin(intPinNum, Pin.IN, Pin.PULL_DOWN)
  
    stdByPin.high()
    in1Pin.low()
    in2Pin.low()

    # *** PWM Pin Setup ***      
    
    pwmPin      = PWM(Pin(pwmPinNum))
    pwmPin.freq(pwmFreq)
    u16DutyCycle = int((dutyCycle / 100) * 65536)  
    pwmPin.duty_u16(u16DutyCycle)
    
    # *** Interrupt Pin Setup ***
    
    intPin.irq(intPinCallBack, Pin.IRQ_FALLING)
    
    # *** Motor Control Dict ***
    
    motorControlDict = {'StdByPin': stdByPin,
                        'In1Pin'  : in1Pin,
                        'In2Pin'  : in2Pin,
                        'PwmPin'  : pwmPin,
                        'IntPin'  : intPin,
                       }
    
    motorStatusDict = {'Direction'   : defaultDirection,
                       'Speed'       : defaultSpeed,
                       'PwmFrequency': defaultPwmFreq,
                       'DutyCycle'   : defaultDutyCycle,
                      }                       

    motorDict = {'MotorConfictDict' : motorConfigDict,
                 'MotorControlDict' : motorControlDict,
                 'MotorStatusDict'  : motorStatusDict
                }    

    return motorDict

# Comments
# 1. setupMotor(motorNum) perfroms the following things:
#    1.1 setup and intiralize control/pwm/interrupt pin objects from controlPinNum controlPin objects
#    1.2 setup interrupt call back functions
#    1.3 returns motorControlDict which is a dictionary of the motor's pin objects (stdBy, Input, Pwm, Interrupt)

def setupMotorList(motorNumList):
    motorControlDictList = [0] * len(motorNumList)        
    for motorNum in motorNumList:
        motorControlDict = setupMotor(motorNum)
        motorControlDictList[motorNum] = motorControlDict
    return motorControlDictList

def setupMotorDirection(motorControlDict, directionName):
    if directionName =='Forward':
        motorControlDict['In1Pin'].low()
        motorControlDict['In2Pin'].high()
    elif directionName == 'Backward':
        motorControlDict['In1Pin'].high()
        motorControlDict['In2Pin'].low()       
    return
    
def setupMotorSpeed(motorControlDict, speedName):
    pwmPin = motorControlDict['PwmPin']
    dutyCycle = dutyCycleDict[speedName]
    pwmPin.duty_u16(int((dutyCycle / 100) * 65536) )
    return

def moveMotorDirectionSpeed(motorControlDict, directionName, speedName):      
    moveMotorDirection(motorControlDict, directionName)
    moveMotorSpeed(motorControlDict, speedName)
    return

def moveMotorDirectionSpeedList(motorControlDictList, directionName, speedName):
    for motorControlDict in motorControlDictList:
        moveMotorDirectionSpeed(motorControlDict, directionName, speedName)
    return

def stopMotor(motorControlDict):  
    motorControlDict['In1Pin'].low()
    motorControlDict['In2Pin'].low()
    return

def moveMotorForward(motorControlDict):  
    motorControlDict['In1Pin'].high()
    motorControlDict['In2Pin'].low()
    return

def stopMotorList(motorControlDictList):
    for motorControlDict in motorControlDictList:
        stopMotor(motorControlDict)
    return

# *** Test setup/stop/move motor functions ***

def testSetupMotor(motorNum):
    print('Begin testSetupMotor(), ', 'motorNum =', motorNum)
    motorControlDict = setupMotor(motorNum)['MotorControlDict']
    stopMotor(motorControlDict)
    print('End   testSetupMotor().')
    return

def testSetupMotorList(motorNumList):
    for motorNum in motorNumList:
        testSetupMotor(motorNum)
    return

def testSetupMoveMotor(motorNum, directionName, speedName, holdSecondsName):
    print('Begiun testSetupMoveMotor(), ', 'motorNum =', motorNum)
    motorDict = setupMotor(motorNum)
    motorControlDict = motorDict['MotorControlDict']
    stopMotor(motorControlDict)
    setupMotorDirection(motorControlDict, directionName)
    setupMotorSpeed(motorControlDict, speedName)    
    hold(holdSecondsName)
    stopMotor(motorControlDict)
    print('End   testSetupMoveMotor().')    
    return

def testSetupMoveMotorList(motorNumList, directionName, speedName, holdSecondsName):
    print('Begiun testSetupMoveMotorNumList(), ...')
    for motorNum in motorNumList:
        motorDict = setupMotor(motorNum)
        motorControlDict = motorDict['MotorControlDict']
        stopMotor(motorControlDict)
        setupMotorDirection(motorControlDict, directionName)
        setupMotorSpeed(motorControlDict, speedName)    
        hold(holdSecondsName)
        stopMotor(motorControlDict)
    print('End   testSetupMoveMotorNumList().')    
    return

# ========= ========= ========= ========= ========= ========= ========= ========= 
# ========= ========= ========= ========= ========= ========= ========= =========

'''
# *** BLDC Config/Fuctnions ***

dutyCycleDictBldc = {
    'VeryVerySlow' : 95,
    'VeryFast'     : 90,
    'Fast'         : 80,
    'Normal'       : 50,
    'Slow'         : 40,
    'VerySlow'     : 30,
    'VeryVerySlow' : 25,
    }

def changeBldcMotorSpeed(motorControlDict, speedName):
    pwmPin = motorControlDict['PwmPin']
    dutyCycle = dutyCycleDict[speedName]
    pwmPin.duty_u16(int((dutyCycle / 100) * 65536) )
    return

def setupBldcMotor(motorConfigDict):
    motorDriverNum = motorConfigDict['MotorDriverNum']
    channelNum     = motorConfigDict['ChannelNum']
    
    stdByPinNum    = motorDriverGpPinNumDict[str(motorDriverNum)]['StdByPinNum']
    in1PinNum      = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['In1PinNum']
    in2PinNum      = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['In2PinNum']
    
    pwmPinNum      = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['PwmPinNum']
    pwmFreq        = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['PwmFreq']    
    dutyCycle      = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['DutyCycle']     
    
    intPinNum      = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['IntPinNum']
    intPinCallBack = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['IntPinCallBack']

    # *** Motor Driver Control Pin Setup ***    
    
    stdByPin    = Pin(stdByPinNum, Pin.OUT)
    in1Pin      = Pin(in1PinNum, Pin.OUT)    
    in2Pin      = Pin(in2PinNum, Pin.OUT)
    intPin      = Pin(intPinNum, Pin.IN, Pin.PULL_DOWN)
  
    stdByPin.high()
    in1Pin.low()
    in2Pin.high()

    # *** PWM Pin Setup ***      
    
    pwmPin      = PWM(Pin(pwmPinNum))
    pwmPin.freq(pwmFreq)
    u16DutyCycle = int((dutyCycle / 100) * 65536)  
    pwmPin.duty_u16(u16DutyCycle)
    
    # *** Interrupt Pin Setup ***
    
    intPin.irq(intPinCallBack, Pin.IRQ_FALLING)
    
    # *** Motor Control Dict ***
    
    motorControlDictBldc = {'StdByPin': stdByPin,
                            'In1Pin'  : in1Pin,
                            'In2Pin'  : in2Pin,
                            'PwmPin'  : pwmPin,
                            'IntPin'  : intPin,
                           }
# Test functions 

def testBldcMotor00():
    print('  Begin testBldcMotor00(), ...')
    motorConfigDict = {'MotorDriverNum': 0, 'ChannelNum': 0}
    setupBldcMotor(motorConfigDict)
    
    hold('TwoSeconds')
    changeBldcMotorSpeed(motorConfigDict, 'VeryFast')
    hold('TwoSeconds')
    utime.sleep(2)
    changeBldcMotorDirection(motorConfigDict, 'CCW')    
    utime.sleep(2)
    changeBldcMotorDirection(motorConfigDict, 'CW') 
    changeBldcMotorSpeed(motorConfigDict, 'VerySlow')
    utime.sleep(2)
    
    print('  End   testSetupMotor00(), ...')
    return

# *** Sample test function call

#testBldcMotor00()

'''
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========

''' Old main test sample outputs

*** Sample Output  tlfong01  2021oct11hkt1955 ***
>>> %Run -c $EDITOR_CONTENT
Begin testSetupMoveMotorList(), ...
After 2 seconds, ...

  countIntPinNumListIntPeriod()
    intPinNumList           = [0, 1, 2, 3]
    intGpPinNumList         = [4, 5, 6, 7]
    countPeriod (seconds)   = 0.1
    pauseTime (seconds)     = 0.1
    repeat count times      = 10

    countList = [296, 219, 291, 205] , min  205 , max  296 , dif  91 , avg  252
    countList = [286, 215, 295, 208] , min  208 , max  295 , dif  87 , avg  251
    countList = [288, 217, 300, 213] , min  213 , max  300 , dif  87 , avg  254
    countList = [301, 223, 298, 207] , min  207 , max  301 , dif  94 , avg  257
    countList = [271, 223, 308, 207] , min  207 , max  308 , dif  101 , avg  252
    countList = [287, 223, 305, 203] , min  203 , max  305 , dif  102 , avg  254
    countList = [277, 233, 306, 226] , min  226 , max  306 , dif  80 , avg  260
    countList = [282, 240, 299, 222] , min  222 , max  299 , dif  77 , avg  260
    countList = [294, 225, 307, 209] , min  209 , max  307 , dif  98 , avg  258
    countList = [299, 223, 303, 229] , min  223 , max  303 , dif  80 , avg  263
End   testSetupMoveMotorList(), ...
>>> 
'''

# *** Main Test ***

# ========= ========= ========= ========= ========= ========= ========= ========= 
# ========= ========= ========= ========= ========= ========= ========= =========

# *** Main ***

#testSetupMotor(motorNum = 0)
#testSetupMotorList(motorNumList = [0, 1, 2, 3])

#testSetupMoveMotor(motorNum = 0, directionName = 'Forward', speedName = 'VeryFast', holdSecondsName = 'TwoSeconds')
#testSetupMoveMotor(motorNum = 1, directionName = 'Forward', speedName = 'VeryFast', holdSecondsName = 'TwoSeconds')
#testSetupMoveMotor(motorNum = 2, directionName = 'Forward', speedName = 'VeryFast', holdSecondsName = 'TwoSeconds')
#testSetupMoveMotor(motorNum = 3, directionName = 'Forward', speedName = 'VeryFast', holdSecondsName = 'TwoSeconds')

testSetupMoveMotorList([0, 1, 2, 3], 'Forward', 'VeryFast', 'TwoSeconds')

# *** End ***

'''
>>> %Run -c $EDITOR_CONTENT
Begiun testSetupMoveMotorNumList(), ...
End   testSetupMoveMotorNumList().
>>>
'''

''' Sample output  tlfong01  2021nov17hkt2033
>>> %Run -c $EDITOR_CONTENT
Begin setupMotor(), ...  tlfong01 2021nov1701
Begin testSetupMoveMotorList(), ...
After 2 seconds, ...

  countIntPinNumListIntPeriod()
    intPinNumList           = [0, 1, 2, 3]
    intGpPinNumList         = [12, 13, 14, 15]
    countPeriod (seconds)   = 0.1
    pauseTime (seconds)     = 0.1
    repeat count times      = 10

    countList = [0, 0, 0, 0] , min  0 , max  0 , dif  0 , avg  0
    countList = [0, 0, 0, 0] , min  0 , max  0 , dif  0 , avg  0
    countList = [0, 0, 0, 0] , min  0 , max  0 , dif  0 , avg  0
    countList = [0, 0, 0, 0] , min  0 , max  0 , dif  0 , avg  0
    countList = [0, 0, 0, 0] , min  0 , max  0 , dif  0 , avg  0
    countList = [0, 0, 0, 0] , min  0 , max  0 , dif  0 , avg  0
    countList = [0, 0, 0, 0] , min  0 , max  0 , dif  0 , avg  0
    countList = [0, 0, 0, 0] , min  0 , max  0 , dif  0 , avg  0
    countList = [0, 0, 0, 0] , min  0 , max  0 , dif  0 , avg  0
    countList = [0, 0, 0, 0] , min  0 , max  0 , dif  0 , avg  0
End   testSetupMoveMotorList(), ...
End   setupMotor(), ...
>>>
'''

/ to continue, …

Now Testing Driving 4 wheels and RF transmission at the same time.

testSetupMoveMotorListSequential([frontLeftWheel, frontRightWheel, rearLeftWheel, rearRightWheel], 'Forward', 'VeryFast', 'TwoSeconds')
testSetupMoveMotorListConcurrent([frontLeftWheel, frontRightWheel, rearLeftWheel, rearRightWheel], 'Backward', 'VerySlow', 'FourSeconds')


# *** pico_4wd_v95.py - tlfong01, 2021nov20hkt1516 ***

from machine import Pin, PWM, UART
import utime

# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========

# *** UART GP pin assignment and setup/configuration ***

uart0 = UART(0, baudrate = 9600, tx=Pin(0), rx=Pin(1))
uart1 = UART(1, baudrate = 9600, tx=Pin(8), rx=Pin(9))

uartPortDict = \
    {
     '0': uart0,
     '1': uart1,
    }

# HC12 GP pin assignment and setup/configuration ***

hc12SetupPinNum0 = 2
hc12SetupPinNum1 = 3

hc12SetupPin0 = Pin(hc12SetupPinNum0, Pin.OUT) 
hc12SetupPin1 = Pin(hc12SetupPinNum1, Pin.OUT)

hc12SetupPinDict = \
    {
        '0': hc12SetupPin0,
        '1': hc12SetupPin1,
    }

hc12ConfigParameterDict = \
    {
        'CheckAtReply'           : 'AT',
        'CheckAllConfigSetting'  : 'AT+RX',
        'SetChannel#1'           : 'AT+C001',
        'SetChannel#2'           : 'AT+C002',        
    }

hc12TextMsgDict = \
    {
        'HelloWorld'             : 'Hello World',
        'HelloPico0'             : 'Hello Pico #0',
        'HelloPico1'             : 'Hello Pico #1',
        'MoveForward'            : 'Move Forward',
        'MoveBackward'           : 'Move Backward',
        'Stop'                   : 'Stop'
    }

# *** Uart Functions ***

def uartWrite(uartPortNum, writeBytes):
    uartPort = uartPortDict[str(uartPortNum)]
    uartPort.write(writeBytes)
    return 

def uartRead(uartPortNum):
    uartPort = uartPortDict[str(uartPortNum)]
    uartAny = uartPort.any()
    print('    uartAny   =', uartAny)
    readBytes = uartPort.read()
    return readBytes

# *** test uart loopback functions ***

def testUartLoopBack(uartPortNum, writeBytes):
    print('    Begin testUartLoopBack(), ...')
    print('      uartPortNum        =', uartPortNum)       
    
    uartWrite(uartPortNum, writeBytes)
    print('        writeBytes       =', writeBytes)    
     
    utime.sleep(0.5)
    
    readBytes = uartRead(uartPortNum)     
    print('        readBytes        =', readBytes)
    
    print('    End   testUartLoopBack().')
    return

# *** UARTTest Functions ***

#testUartLoopBack(uartPortNum = 0, writeBytes = 'uart0 Hello World')
#testUartLoopBack(uartPortNum = 1, writeBytes = 'uart1 Hello World')

# *** Sample output ***
'''
>>> %Run -c $EDITOR_CONTENT
    Begin testUartLoopBack(), ...
      uartPortNum        = 0
        writeBytes       = uart0 Hello World
    uartAny   = 17
        readBytes        = b'uart0 Hello World'
    End   testUartLoopBack().
    Begin testUartLoopBack(), ...
      uartPortNum        = 1
        writeBytes       = uart1 Hello World
    uartAny   = 17
        readBytes        = b'uart1 Hello World'
    End   testUartLoopBack().
>>>
'''

# ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= =========

hc12UartPortNumDict = \
           {
             '0': 0,
             '1': 1,
           }            

# *** HC12 Functions ***

def setHc12ConfigMode(hc12Num):
    uartPortNum = hc12UartPortNumDict[str(hc12Num)]
    hc12SetupPin = hc12SetupPinDict[str(uartPortNum)]
    hc12SetupPin.low()
    return

def setHc12CommMode(hc12Num):
    uartPortNum = hc12UartPortNumDict[str(hc12Num)]    
    hc12SetupPin = hc12SetupPinDict[str(uartPortNum)]
    hc12SetupPin.high()
    return

# *** HC12 Test Functions ***

def testHc12Config(hc12Num, writeBytes):
    print('Begin testHc12Config(), ...')
    uartPortNum = hc12UartPortNumDict[str(hc12Num)]
    setHc12ConfigMode(uartPortNum)
    utime.sleep(0.1)  
    testUartLoopBack(uartPortNum, writeBytes)
    print('End   testHc12Config(), ...')     
    return

# *** Sample Tests ***
#testHc12Config(hc12Num = 0, writeBytes = 'AT')
#testHc12Config(hc12Num = 1, writeBytes = 'AT')

''' *** Sample output ***
>>> %Run -c $EDITOR_CONTENT
Begin testHc12Config(), ...
    Begin testUartLoopBack(), ...
      uartPortNum        = 0
        writeBytes       = AT
    uartAny   = 4
        readBytes        = b'OK\r\n'
    End   testUartLoopBack().
End   testHc12Config(), ...
Begin testHc12Config(), ...
    Begin testUartLoopBack(), ...
      uartPortNum        = 1
        writeBytes       = AT
    uartAny   = 4
        readBytes        = b'OK\r\n'
    End   testUartLoopBack().
End   testHc12Config(), ...
>>> 
'''

def testHc12Communication(xmitUartPortNum, recvUartPortNum, testXmitMsg):
    print('Begin testHc12Communication(), ...')

    # *** Set both HC12 communication mode, ...')
    print('  Begin set both HC12s communication mode, ...')
    setHc12CommMode(xmitUartPortNum)
    setHc12CommMode(recvUartPortNum)
    print('  End   set both HC12s communication mode.')
    
    print('  Begin transmit message, ...')
    print('    xmitBytes =', testXmitMsg)
    uartWrite(xmitUartPortNum, testXmitMsg)    
    print('  End transmit message.')        
    
    # *** uart1 recv test message ***
    print('  Begin receive message, ...')
    utime.sleep(0.1)    
    recvBytes = uartRead(recvUartPortNum)
    print('    recvBytes =', recvBytes)
    print('  End   receive msaages.')
    print('End   testHc12Communication(), ...')     
    return

# *** Sample Tests ***

#testHc12Communication(xmitUartPortNum = 0, recvUartPortNum = 1, \
#    testXmitMsg = 'Pico #1 at COM 11, uart0 transmitting, ...')

''' *** Sample Output ***
>>> %Run -c $EDITOR_CONTENT
Begin testHc12Communication(), ...
  Begin set both HC12s communication mode, ...
  End   set both HC12s communication mode.
  Begin transmit message, ...
    xmitBytes = Pico #1 at COM 11, uart0 transmitting, ...
  End transmit message.
  Begin receive message, ...
    uartAny   = 42
    recvBytes = b'Pico #1 at COM 11, uart0 transmitting, ...'
  End   receive msaages.
End   testHc12Communication(), ...
>>>
'''

# ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= =========

# *** Pico GP pin assignments ***

uart0TxDNum  = 0 # HC12 #1 TxD
uart0RdDNum  = 1 # HC12 #2 RxD

hc12Setup1   = 2 # Reserved for SPI0 Sck
hc12Setup2   = 3 # Reserved for SPI0 Tx
i2c0Sda      = 4 # Reserved for SPI0 Rx
i2c0Scl      = 5 # Reserved for SPI0 Csn  

# *** PWM Pins ***

aPwmPinNum0  = 6 #0
bPwmPinNum0  = 7 #1

uart1TxdNum  = 8 # HC12 #2 TxD
uart1RxdNum  = 9 # HC12 #2 RxD

aPwmPinNum1  = 11 #2
bPwmPinNum1  = 10 #3

# *** Inrerrupt Pins ***

intPinNum0 = 12 #4 
intPinNum1 = 13 #5 
intPinNum2 = 14 #6 
intPinNum3 = 15 #7 

# *** Motor Driver Control Pins (TB6612FNG) ***

stdByPinNum0 = 16 #8
stdByPinNum1 = 17 #9

aIn1PinNum0  = 18 #10
aIn2PinNum0  = 19 #11
bIn1PinNum0  = 20 #12
bIn2PinNum0  = 21 #13

aIn1PinNum1  = 27 #14
aIn2PinNum1  = 28 #15
bIn1PinNum1  = 22 #16
bIn2PinNum1  = 26 #17

# *** Interrupt Pin Dicts ***

intGpPinNumDict = {'0': intPinNum0, 
                   '1': intPinNum1, 
                   '2': intPinNum2, 
                   '3': intPinNum3, 
                  }

intPinNumList = [intPinNum0, intPinNum1, intPinNum2, intPinNum3]

# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========

# *** Sleep time dicts ***

secondsDict = {'OneSecond'    :   1,
               'TwoSeconds'   :   2,
               'FourSeconds'  :   4,
               'TenSeconds'   :  10,               
               'SixtySeconds' :  60,
               'ThreeMinutes' : 180,
              }

def hold(secondsName):
    utime.sleep(secondsDict[secondsName])
    return

# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========

# *** Interrupt global variables and dicts ***

global intCount0 
global intCount1
global intCount2
global intCount3

intCount0 = 0
intCount1 = 0
intCount2 = 0
intCount3 = 0

intCountDict = {
    '0': intCount0,
    '1': intCount1,
    '2': intCount2,
    '3': intCount3,
    }

# *** Interrupt Callback Functions ***   

def intCallBack0(pin):
    global intCount0
    intCount0 = intCount0 + 1    
    return

def intCallBack1(pin):
    global intCount1
    intCount1 = intCount1 + 1    
    return

def intCallBack2(pin):
    global intCount2
    intCount2 = intCount2 + 1    
    return

def intCallBack3(pin):
    global intCount3
    intCount3 = intCount3 + 1    
    return

intCallBackDict = {
    '0': intCallBack0,
    '1': intCallBack1,
    '2': intCallBack2,
    '3': intCallBack3,    
    }

def countIntPinIntPeriod(intPinNum, countPeriod):
    global intCount0
    global intCount1
    global intCount2
    global intCount3
    
    intCount0 = 0
    intCount1 = 0
    intCount2 = 0
    intCount3 = 0

    utime.sleep(countPeriod)
 
    if intPinNum  == 0:
        intCount = intCount0
    elif intPinNum == 1:    
        intCount = intCount1
    elif intPinNum == 2:    
        intCount = intCount2
    else:    
        intCount = intCount3                
    return intCount

def countIntPinNumListIntPeriod(intPinNumList, countPeriod):
    intCountList = [0] * len(intPinNumList)
    for index in range(len(intPinNumList)):
        intCountList[index] = countIntPinIntPeriod(intPinNumList[index], countPeriod)                       
    return intCountList

# *** Test functions ***

def repeatCountIntPinNumListIntPeriod(intPinNumList, countPeriod, pauseTime, repeatTimes):
    print('\n  countIntPinNumListIntPeriod()')
    intGpPinNumList = [0] * len(intPinNumList)
    for index in range(len(intGpPinNumList)):
        intGpPinNumList[index]     = intGpPinNumDict[str(index)]
    print('    intPinNumList           =', intPinNumList)
    print('    intGpPinNumList         =', intGpPinNumList)
    print('    countPeriod (seconds)   =', countPeriod)
    print('    pauseTime (seconds)     =', pauseTime)    
    print('    repeat count times      =', repeatTimes)
    print('')
    
    for count in range(repeatTimes):
        ppsList             = countIntPinNumListIntPeriod(intPinNumList, countPeriod)
        print('    countList =', ppsList, end = '')
        print(' , min ', min(ppsList), end = '')
        print(' , max ', max(ppsList), end = '')
        print(' , dif ', max(ppsList) - min(ppsList), end = '')
        print(' , avg ', int(sum(ppsList) / len(ppsList)))      
        utime.sleep(pauseTime)    
    return

# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========
# ========= ========= ========= ========= ========= ========= ========= ========= ========= =========

defaultDirection = 'Forward'
defaultSpeed     = 'VertHighSpeed'
defaultPwmFreq   = 1000
defaultDutyCycle = 90

frontLeftWheel  = 0
frontRightWheel = 1
rearLeftWheel   = 2
rearRightWheel  = 3

dutyCycleDict = {
    'VeryVerySlow' : 95,
    'VeryFast'     : 90,
    'Fast'         : 80,
    'Normal'       : 50,
    'Slow'         : 40,
    'VerySlow'     : 30,
    'VeryVerySlow' : 25,
    }

motorConfigDictDict = \
    {
        '0': {'MotorDriverNum': 0, 'ChannelNum': 0, 'position': 'Left'},
        '1': {'MotorDriverNum': 0, 'ChannelNum': 1, 'position': 'Right'},
        '2': {'MotorDriverNum': 1, 'ChannelNum': 0, 'position': 'Left'},
        '3': {'MotorDriverNum': 1, 'ChannelNum': 1, 'position': 'Right'},        
    }

motorDriverGpPinNumDict = { \
    '0': {'StdByPinNum'  : stdByPinNum0,             
          '0' : {'In1PinNum'      : aIn1PinNum0,  
                 'In2PinNum'      : aIn2PinNum0,  
                 'PwmPinNum'      : aPwmPinNum0,
                 'PwmFreq'        : defaultPwmFreq,
                 'DutyCycle'      : defaultDutyCycle,
                 'IntPinNum'      : intPinNum0,
                 'IntPinCallBack' : intCallBack0,                 
                },
          '1' : {'In1PinNum'      : bIn1PinNum0,  
                 'In2PinNum'      : bIn2PinNum0,  
                 'PwmPinNum'      : bPwmPinNum0,
                 'PwmFreq'        : defaultPwmFreq,
                 'DutyCycle'      : defaultDutyCycle,
                 'IntPinNum'      : intPinNum1,
                 'IntPinCallBack' : intCallBack1,                  
                },  
          },                   
    '1': {'StdByPinNum'  : stdByPinNum1,             
          '0' : {'In1PinNum'      : aIn1PinNum1,  
                 'In2PinNum'      : aIn2PinNum1,  
                 'PwmPinNum'      : aPwmPinNum1,
                 'PwmFreq'        : defaultPwmFreq,
                 'DutyCycle'      : defaultDutyCycle,
                 'IntPinNum'      : intPinNum2,
                 'IntPinCallBack' : intCallBack2,                  
                },
          '1' : {'In1PinNum'      : bIn1PinNum1,  
                 'In2PinNum'      : bIn2PinNum1,  
                 'PwmPinNum'      : bPwmPinNum1,
                 'PwmFreq'        : defaultPwmFreq,
                 'DutyCycle'      : defaultDutyCycle,
                 'IntPinNum'      : intPinNum3,
                 'IntPinCallBack' : intCallBack3                  
                },  
          }, 
    }

def setupMotor(motorNum):
    motorConfigDict = motorConfigDictDict[str(motorNum)]
    motorDriverNum  = motorConfigDict['MotorDriverNum']
    channelNum      = motorConfigDict['ChannelNum']
    
    stdByPinNum     = motorDriverGpPinNumDict[str(motorDriverNum)]['StdByPinNum']
    in1PinNum       = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['In1PinNum']
    in2PinNum       = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['In2PinNum']
    
    pwmPinNum       = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['PwmPinNum']
    pwmFreq         = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['PwmFreq']    
    dutyCycle       = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['DutyCycle']     
    
    intPinNum       = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['IntPinNum']
    intPinCallBack  = motorDriverGpPinNumDict[str(motorDriverNum)][str(channelNum)]['IntPinCallBack']

    # *** Motor Driver Control Pin Setup ***    
    
    stdByPin    = Pin(stdByPinNum, Pin.OUT)
    in1Pin      = Pin(in1PinNum, Pin.OUT)    
    in2Pin      = Pin(in2PinNum, Pin.OUT)
    intPin      = Pin(intPinNum, Pin.IN, Pin.PULL_DOWN)
  
    stdByPin.high()
    in1Pin.low()
    in2Pin.low()

    # *** PWM Pin Setup ***      
    
    pwmPin      = PWM(Pin(pwmPinNum))
    pwmPin.freq(pwmFreq)
    u16DutyCycle = int((dutyCycle / 100) * 65536)  
    pwmPin.duty_u16(u16DutyCycle)
    
    # *** Interrupt Pin Setup ***
    
    intPin.irq(intPinCallBack, Pin.IRQ_FALLING)
    
    # *** Motor Control Dict ***
    
    motorControlDict = {'StdByPin' : stdByPin,
                        'In1Pin'   : in1Pin,
                        'In2Pin'   : in2Pin,
                        'PwmPin'   : pwmPin,
                        'IntPin'   : intPin,
                        'MotorNum' : motorNum,
                       }
    
    motorStatusDict = {'Direction'   : defaultDirection,
                       'Speed'       : defaultSpeed,
                       'PwmFrequency': defaultPwmFreq,
                       'DutyCycle'   : defaultDutyCycle,
                      }                       

    motorDict = {'MotorConfictDict' : motorConfigDict,
                 'MotorControlDict' : motorControlDict,
                 'MotorStatusDict'  : motorStatusDict
                }    

    return motorDict

def setupMotorList(motorNumList):
    motorControlDictList = [0] * len(motorNumList)        
    for motorNum in motorNumList:
        motorControlDict = setupMotor(motorNum)
        motorControlDictList[motorNum] = motorControlDict
    return motorControlDictList

def setupMotorDirection(motorControlDict, directionName):
    if directionName =='Forward':
        if (motorControlDict['MotorNum'] % 2) == 0:
           motorControlDict['In1Pin'].low()
           motorControlDict['In2Pin'].high()
        else:
           motorControlDict['In1Pin'].high()
           motorControlDict['In2Pin'].low()      
        
    elif directionName =='Backward':
        if (motorControlDict['MotorNum'] % 2) == 0:
           motorControlDict['In1Pin'].high()
           motorControlDict['In2Pin'].low()
        else:
           motorControlDict['In1Pin'].low()
           motorControlDict['In2Pin'].high()                 
    return
    
def setupMotorSpeed(motorControlDict, speedName):
    pwmPin = motorControlDict['PwmPin']
    dutyCycle = dutyCycleDict[speedName]
    pwmPin.duty_u16(int((dutyCycle / 100) * 65536) )
    return

def moveMotorDirectionSpeed(motorControlDict, directionName, speedName):      
    moveMotorDirection(motorControlDict, directionName)
    moveMotorSpeed(motorControlDict, speedName)
    return

def moveMotorDirectionSpeedList(motorControlDictList, directionName, speedName):
    for motorControlDict in motorControlDictList:
        moveMotorDirectionSpeed(motorControlDict, directionName, speedName)
    return

def stopMotor(motorControlDict):  
    motorControlDict['In1Pin'].low()
    motorControlDict['In2Pin'].low()
    return

def moveMotorForward(motorControlDict):  
    motorControlDict['In1Pin'].high()
    motorControlDict['In2Pin'].low()
    return

def stopMotorList(motorControlDictList):
    for motorControlDict in motorControlDictList:
        stopMotor(motorControlDict)
    return

# *** Test setup/stop/move motor functions ***

def testSetupMotor(motorNum):
    print('Begin testSetupMotor(), ', 'motorNum =', motorNum)
    motorControlDict = setupMotor(motorNum)['MotorControlDict']
    stopMotor(motorControlDict)
    print('End   testSetupMotor().')
    return

def testSetupMotorList(motorNumList):
    for motorNum in motorNumList:
        testSetupMotor(motorNum)
    return

def testSetupMoveMotor(motorNum, directionName, speedName, holdSecondsName):
    print('Begiun testSetupMoveMotor(), ', 'motorNum =', motorNum)
    motorDict = setupMotor(motorNum)
    motorControlDict = motorDict['MotorControlDict']
    stopMotor(motorControlDict)
    setupMotorDirection(motorControlDict, directionName)
    setupMotorSpeed(motorControlDict, speedName)    
    hold(holdSecondsName)
    stopMotor(motorControlDict)
    print('End   testSetupMoveMotor().')    
    return

def testSetupMoveMotorListSequential(motorNumList, directionName, speedName, holdSecondsName):
    print('Begiun testSetupMoveMotorNumListSequential(), ...')
    for motorNum in motorNumList:
        motorDict = setupMotor(motorNum)
        motorControlDict = motorDict['MotorControlDict']
        stopMotor(motorControlDict)
        setupMotorDirection(motorControlDict, directionName)
        setupMotorSpeed(motorControlDict, speedName)    
        hold(holdSecondsName)
        stopMotor(motorControlDict)
    print('End   testSetupMoveMotorNumListSequential().')    
    return

def testSetupMoveMotorListConcurrent(motorNumList, directionName, speedName, holdSecondsName):
    print('Begin testSetupMoveMotorNumListConcurrent(), ...')
    for motorNum in motorNumList:
        motorDict = setupMotor(motorNum)
        motorControlDict = motorDict['MotorControlDict']
        stopMotor(motorControlDict)
        setupMotorDirection(motorControlDict, directionName)
        setupMotorSpeed(motorControlDict, speedName)       
    hold(holdSecondsName)
    for motorNum in motorNumList:
        motorDict = setupMotor(motorNum)
        motorControlDict = motorDict['MotorControlDict']
        stopMotor(motorControlDict)      
    print('End   testSetupMoveMotorNumListConcurrent().')    
    return

# ========= ========= ========= ========= ========= ========= ========= ========= 
# ========= ========= ========= ========= ========= ========= ========= =========

# *** Main ***

#testSetupMotor(motorNum = 0)
#testSetupMotorList(motorNumList = [0, 1, 2, 3])

#testSetupMoveMotor(motorNum = 0, directionName = 'Forward', speedName = 'VeryFast', holdSecondsName = 'TwoSeconds')
#testSetupMoveMotor(motorNum = 1, directionName = 'Forward', speedName = 'VeryFast', holdSecondsName = 'TwoSeconds')
#testSetupMoveMotor(motorNum = 2, directionName = 'Forward', speedName = 'VeryFast', holdSecondsName = 'TwoSeconds')
#testSetupMoveMotor(motorNum = 3, directionName = 'Forward', speedName = 'VeryFast', holdSecondsName = 'TwoSeconds')

# *** Testing TB6612FNG Driving 4 Wheels ***
testSetupMoveMotorListSequential([frontLeftWheel, frontRightWheel, rearLeftWheel, rearRightWheel], 'Forward', 'VeryFast', 'TwoSeconds')
testSetupMoveMotorListConcurrent([frontLeftWheel, frontRightWheel, rearLeftWheel, rearRightWheel], 'Backward', 'VerySlow', 'FourSeconds')

# *** Testing HC12 Config and Transive/Receive ***
#testHc12Config(hc12Num = 0, writeBytes = 'AT')
#testHc12Config(hc12Num = 1, writeBytes = 'AT')
testHc12Communication(xmitUartPortNum = 0, recvUartPortNum = 1, testXmitMsg = 'xyz msg')

# *** End ***

''' Sample output  tlfong01  2021nov20hkt1531
>>> %Run -c $EDITOR_CONTENT
Begiun testSetupMoveMotorNumListSequential(), ...
End   testSetupMoveMotorNumListSequential().
Begin testSetupMoveMotorNumListConcurrent(), ...
End   testSetupMoveMotorNumListConcurrent().
Begin testHc12Communication(), ...
  Begin set both HC12s communication mode, ...
  End   set both HC12s communication mode.
  Begin transmit message, ...
    xmitBytes = xyz msg
  End transmit message.
  Begin receive message, ...
    uartAny   = 7
    recvBytes = b'xyz msg'
  End   receive msaages.
End   testHc12Communication(), ...
>>> 
'''

Now I am watching how the ESP32 hobbyists are making Robots

The 10 Best ESP32 Robotics Projects - Cherie Tan, Muo 2021nov21

Learn Python Programming on Raspberry Pi With These Commands and Data Structures - Cherie Tan, Muo 2021sep19


Integrating modules uart, hc12, interrupt, tb6612fng

Now I have integrated the above 4 python modules and tested all functions more or less OK.The penzu journal below has the full program listing and sample output.

pico_4wd_v115 program listing

/ to continue, …

Master and slave pico 4WD 433MHz Transceiving Mirror Moving Experiment v0.1

Now that I have tested all the Smart Pico 4WD OK, I am going to do the following experiment:

  1. Using two pico 4WD, one as master, another as slave.

  2. Master 4WD moves any pattern, and sends the motor/wheel move commands, as a text string, such as “FrontLeftWheelMoveForwardFullSpeed”, through a HC12 UART RF 433MHz channel to the slave 4WD.

  3. The slave 4WD, when receiving the move wheel command, move the wheeling according, and send back the Wheel/Motor speed (from motor rotary encoder signals) back to the master 4WD.

  4. So, as an example, if the master 4WD moves in a straight line, then a square, then a circle, the the slave 4WD moves the same pattern, at the same speed.

  5. The slave 4WD, if it wants, can record/remember the move commands as a command text file, and later moves the same pattern, as many times as possible, and at same or different speed as the master 4WD.


pico_4wd_v122.py

pico_4wd_v125.py


Open-source, 3D printed, low cost vehicle, controlled over the internet programmed in Python



Open-source, 3D printed, low cost transport and reconnaissance vehicle controlled over the internet, programmed in Python

https://www.open-ats.eu/

Element14 Rpi Pico Info

https://community.element14.com/products/raspberry-pi/w/documents/5191/raspberry-pi-pico


Kitronix Pico Buggy


Kitronik Autonomous Robotics Platform for Pico [New] - £34

Kitronik Autonomous Robotics Platform for Pico`

This morning I read that Kitronix is now an official Rpi reseller and they are selling an autonomous Pico robot car. I hope pi-top will catch up soon.

The Kitronik Autonomous Robotics Platform Pico, a fun and hands-on introduction to Robotics with the Raspberry Pi Pico.

*The Kitronik Autonomous Robotics Platform for Raspberry Pi Pico is a fun and hands-on introduction to buggy robotics. *

*The easy to follow booklet provided with the kit guides you through all of the steps required for you to take control of your robot. *

The Robotics Platform has been designed to grow with you and provided additional servo and ultrasonic sensor connectors for more advanced projects. This buggy requires a Raspberry Pi Pico with pin headers attached, yo…


pi-top [4] Complete Raspberry Pi 4 Computer Kit - $250

pi-top [4] Complete Raspberry Pi 4 Computer Kit - $250

Overview

You and your children can design, code and make anything you imagine with the pi-top [4] Complete Raspberry Pi 4 computer kit.

It includes everything you need to program a Raspberry Pi inside of its rugged case.

Whatever you wish to build an alarm or a sensor, or learning about problem solving and code writing, this computer kit is a must-have.


Pico 4WD v128 Program Listing

#setupMoveCarHold('FrontLeftWheel',  'Forward', 'Normal', '2 seconds')
#setupMoveCarHold('FrontRightWheel', 'Forward', 'Normal', '2 seconds')

#setupMoveCarHold('RearLeftWheel',   'Forward', 'Normal', '2 seconds')
#setupMoveCarHold('RearRightWheel',  'Forward', 'Normal', '2 seconds')

#setupMoveCarHold('FrontWheels',     'Forward', 'Normal', '2 seconds')
#setupMoveCarHold('RearWheels',      'Forward', 'Normal', '2 seconds')
#setupMoveCarHold('AllFourWheels',   'Forward', 'Normal', '2 seconds')

setupTurnCarHold('FrontWheels',      'Forward',    'Normal', '2 seconds')
setupTurnCarHold('RearWheels',       'Backward',   'Normal', '2 seconds')

Pico 4WD v128 Program Listing


Pico 4WD Moving in a straight line, in a sqaure, in a circle functions tested OK

Now the following functions are tested OK.

setupMoveCarHold('FrontLeftWheel',  'Forward', 'Normal', '2 seconds')
setupMoveCarHold('FrontRightWheel', 'Forward', 'Normal', '2 seconds')

setupMoveCarHold('RearLeftWheel',   'Forward', 'Normal', '2 seconds')
setupMoveCarHold('RearRightWheel',  'Forward', 'Normal', '2 seconds')

setupMoveCarHold('FrontWheels',     'Forward', 'Normal', '2 seconds')
setupMoveCarHold('RearWheels',      'Forward', 'Normal', '2 seconds')
setupMoveCarHold('AllFourWheels',   'Forward', 'Normal', '2 seconds')

setupTurnCarHold('FrontWheels',      'TurnLeft',   'Normal', '2 seconds')
setupTurnCarHold('FrontWheels',      'TurnRight',  'Normal', '2 seconds')
setupTurnCarHold('RearWheels',       'TurnLeft',   'Normal', '2 seconds')
setupTurnCarHold('RearWheels',       'TurnRight',  'Normal', '2 seconds')

So I have proven the concept that it is easy to move the 4WD in any pattern I wish: straight line, square, circle, hexagon etc.

And of course it should be also easy to communicate between a group of pico 4WDs each with HC12 433MHz tranceivers to talk to each other and perform orchestraed group movements with or without a Rpi4B master/conductor

pico_4wd_v130.py


Yet another self driving car

This remote-controlled car drives itself


4WD Moving in a square functions tested OK

4WD Moving in a square program listing

def carMoveSquareV01():
    print('Begin carMoveSquareV01(), ...')
    setupMoveCarHold('AllFourWheels', 'Forward',  'Normal', '2 seconds')
    setupTurnCarHold('FrontWheels',   'TurnLeft', 'VerySlow',   '1 second')    
    setupMoveCarHold('AllFourWheels', 'Forward',  'Normal', '2 seconds')
    setupTurnCarHold('FrontWheels',   'TurnLeft', 'VerySlow',   '1 second')
    setupMoveCarHold('AllFourWheels', 'Forward',  'Normal', '2 seconds')
    print('End   carMoveSquareV01().')
    return

Pico GP pins running out problem

I am running out of GP pins for the 4WD project. One workaround is to use IO expenderes such as MCP2307/MCP23s17, or PCF8574. I am thinking of trying out PCF8574 first.

PCF8574 Remote 8-Bit I/O Expander for I2C Bus - TI



PCF8574 IO pins for TB6612FNG signals

tb6612fng_pinout_2021nov2801


Now I am reading an old Pico micropython program using PCF8574 to control a DC motor controller MX15 I need to modify this program for TB6612FNG.


 *** iox_pcf8571_v34 tlfong01 2021mar01hkt1616
# Thonny 3.3.3 MicroPython (Raspberry Pi Pico)
# Chinese Windows 10
# References - https://github.com/raspberrypi/pico-micropython-examples

#import machine
from machine import Pin, Timer
import time

# ========== ========== ========== ========== ========== ========== ========== ==========
# ========== ========== ========== ========== ========== ========== ========== ==========

# Contents
#   Part A - I2C Bus Config/Setup/Test
#   Part B - PCF8571 8-Bit I/O Extender Config/Setup/Test
#   

# *** I2C ***

# *** I2C Bus Dictionary

i2cBusDict = {
    '0' : { 'BusNum'     : 0,
            'NickName'   : 'Amy',
            'SdaPinNum'  : 0,
            'SclPinNum'  : 1,
            'Frequency'  : '100 kHz',
          },
    '1' : { 'BusNum'     : 1,
            'NickName'   : 'Betty',
            'SdaPinNum'  : 2,
            'SclPinNum'  : 3,
            'Frequency'  : '400 kHz',
          },          
    }                  

frequencyDict = {
    '100 kHz' :  100000,
    '400 kHz' :  400000,
    '1 MHz'   : 1000000,
    }

# *** Print/Debug Functions ***

def printIntListInHex(intList):
    for integer in intList:
        print(hex(integer), ' ', end = '')
    #print('')        
    return

def padString(string, paddedStringLength):
    leftOver = paddedStringLength - len(string)
    string = string + (' ' * leftOver)
    return string    

# *** I2C Functions ***

def setupI2cBus(i2cBusNum):
    sdaPinNum = i2cBusDict[str(i2cBusNum)]['SdaPinNum'] 
    sclPinNum = i2cBusDict[str(i2cBusNum)]['SclPinNum']
    sdaPin    = machine.Pin(sdaPinNum)    
    sclPin    = machine.Pin(sclPinNum)
    frequency = frequencyDict[i2cBusDict[str(i2cBusNum)]['Frequency']]     
    i2cBus = machine.I2C(i2cBusNum, sda = sdaPin, scl = sclPin, freq = frequency)
    return i2cBus

def setupI2cBusList(i2cBusNumList):
    i2cBusList = []
    for i2cBusNum in i2cBusNumList:        
       i2cBus = setupI2cBus(i2cBusNum)
       i2cBusList.append(i2cBus)   
    return i2cBusList

# *** Test Functions ***

def testSetupI2cBusList():
    print('Begin testSetupI2cBusList(), ...')
    i2cBustList = setupI2cBusList([0, 1])
    print('End   testSetupI2cBusList().\n')
    return i2cBustList

def testSetupScanI2cBusList():
    print('Begin testSetupScanI2cBusList(), ...')    
    i2cBusList = setupI2cBusList([0, 1])    
    for i2cBus in i2cBusList:
        index = i2cBusList.index(i2cBus)
        print('  I2C Bus Num =', index, ' ', end = '')
        ljustNickName = padString(i2cBusDict[str(i2cBusList.index(i2cBus))]['NickName'], 8)
        print('NickName =', ljustNickName, end ='')       
        print('Frequency =', i2cBusDict[str(i2cBusList.index(i2cBus))]['Frequency'], ' ', end ='')
        print('sdaPin =', i2cBusDict[str(i2cBusList.index(i2cBus))]['SdaPinNum'], ' ', end ='')
        print('sclPin =', i2cBusDict[str(i2cBusList.index(i2cBus))]['SclPinNum'], ' ', end ='')         
        printI2cDeviceList(i2cBus)
    print('End   testSetupI2cBusList().\n')        
    return 

# ========== ========== ========== ========== ========== ========== ========== ==========
# ========== ========== ========== ========== ========== ========== ========== ==========

# *** PCF8574 8-bit IO Extender ***

# *** IoX Config ***

i2cDevDict = {
    '0' : { 'I2cDevNum'     : 0,
            'I2cBusNum'     : 0,
            'I2cDevAddr'    : 0x23,
            'DevName'       : 'PCF8574 #1',
            'DevNickName'   : 'Connie'
          },
    '1' : { 'I2cDevNum'     : 1,
            'I2cBusNum'     : 1,
            'I2cDevAddr'    : 0x24,
            'DevName'       : 'PCF8574 #2',
            'DevNickName'   : 'Daisy'
          },
    '2' : { 'I2cDevNum'     : 2,
            'I2cBusNum'     : 0,
            'I2cDevAddr'    : 0x27,
            'DevName'       : 'LCD2004 #1',
            'DevNickName'   : 'Emily'
          },
    '3' : { 'I2cDevNum'     : 3,
            'I2cBusNum'     : 1,
            'I2cDevAddr'    : 0x22,
            'DevName'       : 'LCD1602 #1',
            'DevNickName'   : 'Fanny'
          },     
    }

def printI2cDevDict():
    dictLength = len(i2cDevDict)
    i2cDevNumList = [i2cDevNum for i2cDevNum in range(dictLength)]
    
    for i2cDevNum in i2cDevNumList:
        print('      i2cDevNum =', i2cDevNum, ' ', end = '')
        
        ljustDeviceName = padString(i2cDevDict[str(i2cDevNum)]['DevName'], 8)
        print(' DeviceName =', ljustDeviceName, end ='')        
        
        ljustNickName = padString(i2cDevDict[str(i2cDevNum)]['DevNickName'], 8)
        print('  NickName =', ljustNickName, end ='')
        
        print('  I2cBusNum =', i2cDevDict[str(i2cDevNum)]['I2cBusNum'],end ='')
        print('  I2cDevAddr =', hex(i2cDevDict[str(i2cDevNum)]['I2cDevAddr']),end ='')         
        
        print('')
    print('')        
    return

def printI2cDevInfo(i2cDevNum):
    print('      i2cDevNum =', i2cDevNum, ' ', end = '')
        
    ljustDeviceName = padString(i2cDevDict[str(i2cDevNum)]['DevName'], 8)
    print(' DeviceName =', ljustDeviceName, end ='')        
        
    ljustNickName = padString(i2cDevDict[str(i2cDevNum)]['DevNickName'], 8)
    print('  NickName =', ljustNickName, end ='')
        
    print('  I2cBusNum =', i2cDevDict[str(i2cDevNum)]['I2cBusNum'],end ='')
    print('  I2cDevAddr =', hex(i2cDevDict[str(i2cDevNum)]['I2cDevAddr']),end ='')         
        
    print('')
    return

# *** IoX Functions ***

def scanAndPrintI2cBusList(i2cBusList):
    for i2cBus in i2cBusList:
        index = i2cBusList.index(i2cBus)        
        print('      I2C Bus Num =', index, ' ', end = '')
        i2cDeviceList = i2cBus.scan()
        print('I2cDeviceList = ', end = '')
        printIntListInHex(i2cDeviceList)
        print('')
    return

# *** Iox Test Functions ***

def testIoxDevices():
    printIoxDevices()
    return

def printI2cBusListConfig(i2cBusList):
    #print('\n        I2C Bus List Config, ... ')
    for i2cBus in i2cBusList:
        index = i2cBusList.index(i2cBus)        
        print('      I2C Bus Num =', index, ' ', end = '')
        ljustNickName = padString(i2cBusDict[str(i2cBusList.index(i2cBus))]['NickName'], 8)
        print('Bus nick name =', ljustNickName, end ='')       
        print('Frequency =', i2cBusDict[str(i2cBusList.index(i2cBus))]['Frequency'], ' ', end ='')
        print('sdaPin =', i2cBusDict[str(i2cBusList.index(i2cBus))]['SdaPinNum'], ' ', end ='')
        print('sclPin =', i2cBusDict[str(i2cBusList.index(i2cBus))]['SclPinNum'], ' ', end ='')
        print('')
    return

def printIoxDevicesOnI2cBusList(i2cBusList): 
    for i2cBus in i2cBusList:
        index = i2cBusList.index(i2cBus)        
        print('      I2C Bus Num =', index, ' ', end = '')        
        printI2cDeviceList(i2cBus)
    return

# *** Basic Tests ***

def pcf8574BasicTest01():
    print('>>>>>>>>>> Begin testPcf8574(), ... >>>>>>>>>>\n')  

    print('  *** Set up I2C bus list [i2cBus0, i2cBus1] ***\n')
    i2cBusList = setupI2cBusList([0, 1])
    
    print('  *** I2C Bus List Config ***')     
    printI2cBusListConfig(i2cBusList)
    print('')     
    
    print('  *** I2c Device Dict ***')    
    printI2cDevDict()
    
    print('  *** Scan and print I2C devices I2C bus list [i2cBus0, i2cBus1] ***')
    scanAndPrintI2cBusList(i2cBusList)

    print('\n>>>>>>>>>> End   testPcf8574(). >>>>>>>>>>\n')    
    return

# *** Write/Read I2C Device ***

# *** Write Functions ***

def writeI2cBusDevAddrOneByte(i2cBus, i2cDevAddr, writeByte):
    i2cBus.writeto(i2cDevAddr, bytes([writeByte]))
    return

def writeI2cDevNumOneByte(i2cBusList, i2cDevNum, writeByte):
    i2cBusNum  = i2cDevDict[str(i2cDevNum)]['I2cBusNum']
    i2cBus     = i2cBusList[i2cBusNum]
    i2cDevAddr = i2cDevDict[str(i2cDevNum)]['I2cDevAddr']
    writeI2cBusDevAddrOneByte(i2cBus, i2cDevAddr, writeByte)
    return

def blinkI2cDevNumByte1Byte2(i2cBusList, i2cDevNum, byte1, byte2, byteTime1, \
                             byteTime2, totalCount):
    i2cBusNum  = i2cDevDict[str(i2cDevNum)]['I2cBusNum']
    i2cBus     = i2cBusList[i2cBusNum]
    i2cDevAddr = i2cDevDict[str(i2cDevNum)]['I2cDevAddr']
    
    for count in range(totalCount):
        writeI2cBusDevAddrOneByte(i2cBus, i2cDevAddr, byte1)
        time.sleep(byteTime1)
        writeI2cBusDevAddrOneByte(i2cBus, i2cDevAddr, byte2)
        time.sleep(byteTime2)
    writeI2cBusDevAddrOneByte(i2cBus, i2cDevAddr, 0x00)
    #writeI2cBusDevAddrOneByte(i2cBus, i2cDevAddr, 0xff)      
    return

def scanI2cDevNumOneByte(i2cBusList, i2cDevNum, scanByte, scanTime, totalCount):
    i2cBusNum  = i2cDevDict[str(i2cDevNum)]['I2cBusNum']
    i2cBus     = i2cBusList[i2cBusNum]
    i2cDevAddr = i2cDevDict[str(i2cDevNum)]['I2cDevAddr']
    
    maskByte = 0x01
    for byteCount in range(totalCount):
        for bitCount in range(8):
            newMaskByte = maskByte << bitCount
            newByte = scanByte & newMaskByte
            writeI2cBusDevAddrOneByte(i2cBus, i2cDevAddr, newByte)
            time.sleep(scanTime)
    writeI2cBusDevAddrOneByte(i2cBus, i2cDevAddr, 0x00)    
    return

# *** Read Functions ***

''' tested not OK 

def readI2cBusDevAddrOneByte(i2cBus, i2cDevAddr):
    readByte = i2cBus.readfrom(i2cDevAddr, 4) 
    return readByte

def readPrintI2cBusDevAddrOneByte(i2cBus, i2cDevAddr):
    readByte = readI2cBusDevAddrOneByte(i2cBus, i2cDevAddr)
    print('Read byte =', readByte)
    return

def readI2cDevNumOneByte(i2cBusList, i2cDevNum):
    i2cBusNum  = i2cDevDict[str(i2cDevNum)]['I2cBusNum']
    i2cBus     = i2cBusList[i2cBusNum]
    i2cDevAddr = i2cDevDict[str(i2cDevNum)]['I2cDevAddr']
    readPrintI2cBusDevAddrOneByte(i2cBus, i2cDevAddr)
    return
'''

# *** Write/Read Tests ***

def pcf8574WriteBlinkTest01():
    print('>>>>>>>>>> Begin   pcf8574WriteTest01(), ... >>>>>>>>>>\n')     
    
    print('  *** Set up I2C bus list [i2cBus0, i2cBus1] ***\n')
    i2cBusList = setupI2cBusList([0, 1])    
    
    print('  *** Scan and print I2C devices I2C bus list [i2cBus0, i2cBus1] ***')
    scanAndPrintI2cBusList(i2cBusList)
        
    #print('\n *** Read pcf8574 input pins - to test later ***')
    #i2cDevNum = 0    
    #readI2cDevNumOneByte(i2cBusList, i2cDevNum)
        #i2cBus = i2cBusList[0]
    #i2cDevAddr = 0x23
    #readByteArray = []
    #readByteArray = i2cBus.readfrom(i2cDevAddr, 1)
    #i2cBus.writeto_mem(76, 6, b'456')
    #i2cBus.readfrom_mem(76, 6, 4)

    print('\n  *** Toggle/Blink pcf8574 output pins/LEDs ***') 
   
    #print('dp 01')  
    i2cBusList[0].writeto(0x27, bytes([0x55]))
    i2cBusList[0].writeto(0x23, bytes([0x55]))
    i2cBusList[1].writeto(0x22, bytes([0x55]))      
    i2cBusList[1].writeto(0x24, bytes([0x55]))
    
    #print('dp 02')
    writeI2cBusDevAddrOneByte(i2cBusList[0], 0x23, 0xff)    
    writeI2cBusDevAddrOneByte(i2cBusList[1], 0x24, 0xff)
    time.sleep(1)
    writeI2cBusDevAddrOneByte(i2cBusList[0], 0x23, 0x00)    
    writeI2cBusDevAddrOneByte(i2cBusList[1], 0x24, 0x00)
    
    #print('dp03') 
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum = 0, writeByte = 0x55)
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum = 1, writeByte = 0xaa)
    
    #print('dp04 blink')
    byte1 = 0x55
    byte2 = 0xaa
    byte1Time = 0.5
    byte2Time = 0.5
    totalCount = 2
    
    i2cDevNum = 0    
    blinkI2cDevNumByte1Byte2(i2cBusList, i2cDevNum, byte1, byte2, byte1Time, \
                             byte2Time, totalCount)
    i2cDevNum = 1 
    blinkI2cDevNumByte1Byte2(i2cBusList, i2cDevNum, byte1, byte2, byte1Time, \
                             byte2Time, totalCount)

    print('\n>>>>>>>>>> End   pcf8574WriteTest01().>>>>>>>>>>') 
    return

def pcf8574WriteBitTest01():
    print('\n>>>>>>>>>> Begin pcf8574WriteBitTest01(). >>>>>>>>>>')
    
    print('  *** Set up I2C bus list [i2cBus0, i2cBus1] ***\n')
    i2cBusList = setupI2cBusList([0, 1])    
    
    print('  *** I2c Device Dict ***')    
    printI2cDevDict()
    
    print('  *** Scan and print I2C devices I2C bus list [i2cBus0, i2cBus1] ***')
    scanAndPrintI2cBusList(i2cBusList)
    
    print('\n  *** Wrtie to i2cDevNum0, i2cDevNum1 ***')
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum = 0, writeByte = 0x55)
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum = 1, writeByte = 0xaa)
    time.sleep(0.5)
    
    print('\n  *** scan bit i2cDevNum0 ***')
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum = 0, writeByte = 0x00)
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum = 1, writeByte = 0x00)
    time.sleep(0.5)

    i2cDevNum = 0   
    #oldByte = 0x00
    newByte = 0x01
    maskByte = 0x00
    
    for i in range(8):
        #maskByte = newByte
        #newByte = newByte << 1
        writeI2cDevNumOneByteOrMaskByte(i2cBusList, i2cDevNum, newByte, maskByte)
        
        #newByte = newByte | oldByte
        #writeByte = newByte
        #writeI2cDevNumOneByte(i2cBusList, i2cDevNum, writeByte)
        time.sleep(0.5)
        maskByte = newByte | maskByte
        newByte = newByte << 1        
        
    print('\n>>>>>>>>>> End   pcf8574WriteBitTest01(). >>>>>>>>>>') 
    return

def writeI2cDevNumOneByteOrMaskByte(i2cBusList, i2cDevNum, writeByte, maskByte):
    writeByte = writeByte | maskByte
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum, writeByte)
    return

def writeI2cDevNumOneByte(i2cBusList, i2cDevNum, writeByte):
    i2cBusNum  = i2cDevDict[str(i2cDevNum)]['I2cBusNum']
    i2cBus     = i2cBusList[i2cBusNum]
    i2cDevAddr = i2cDevDict[str(i2cDevNum)]['I2cDevAddr']
    writeI2cBusDevAddrOneByte(i2cBus, i2cDevAddr, writeByte)
    return

def writeI2cDevNumOneByteOrMaskByte(i2cBusList, i2cDevNum, writeByte, maskByte):
    writeByte = writeByte | maskByte
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum, writeByte)
    return

#def setBitHigh(i2cBusList, i2cDevNum, writeByte, bitPosition):

# *** Motor Driver MX1908 Functions ***

i2cMotorDict = {
    '0' : {'I2cDevNum'    : 0,
           'MotorName'    : 'Gigi',
           'Action'       : {'MoveForward'  : 0b01,
                             'MoveBackward' : 0b10,
                             'Stop'         : 0b00,
                            }
          },
    '1' : {'I2cDevNum'    : 1,
           'MotorName'    : 'Helen',
           'Action'       : {'MoveForward'  : 0b01,
                             'MoveBackward' : 0b10,
                             'Stop'         : 0b00,
                            }           
          },
    }

i2cDevNameDict = {
    'Gigi'  : {'I2cDevNum'  : 0,
               'DeviceName' : 'Motor'},
    'Helen' : {'I2cDevNum'  : 1,
               'DeviceName' : 'Motor'},   
    }

def writeI2cMotorOneByte(i2cBusList, motorName, actionName):
    i2cDevNum = i2cDevNameDict[motorName]['I2cDevNum']
    writeByte = i2cMotorDict[str(i2cDevNum)]['Action'][actionName]
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum, writeByte)
    print('dp1', 'i2cDevNum =', i2cDevNum, 'actionName =', actionName, 'writeByte =', hex(writeByte))
    return

def pcf8574MotorTest01():
    print('\n>>>>>>>>>> Begin pcf8574MotorTest01(). >>>>>>>>>>')
    
    print('  *** Set up I2C bus list [i2cBus0, i2cBus1] ***\n')
    i2cBusList = setupI2cBusList([0, 1])    
    
    print('  *** I2c Device Dict ***')    
    printI2cDevDict()
    
    print('  *** Scan and print I2C devices I2C bus list [i2cBus0, i2cBus1] ***')
    scanAndPrintI2cBusList(i2cBusList)
    
    print('\n  *** Write Motor Test ***')
    #writeI2cMotorOneByte(i2cBusList, 'Gigi', 'MoveForward')
    
    i2cDevNum = 0
    motorByte = 0b10
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum, motorByte)
    time.sleep(2)
    
    motorByte = 0b01
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum, motorByte)    
    time.sleep(2)
    
    motorByte = 0b00
    writeI2cDevNumOneByte(i2cBusList, i2cDevNum, motorByte)    
    time.sleep(2) 
    
    print('\n>>>>>>>>>> End   pcf8574MotorTest01(). >>>>>>>>>>')    
    return

# *** Main ***

# *** test PCF8457 IOX Devices ***

#pcf8574BasicTest01()
#pcf8574WriteBlinkTest01()
#pcf8574WriteBitTest01()
pcf8574MotorTest01()

# *** End ***

'''
import utime
from machine import Pin

motor1a = Pin(10, Pin.OUT)
motor1b = Pin(11, Pin.OUT)

def forward():
   motor1a.high()
   motor1b.low()

def backward():
   motor1a.low()
   motor1b.high()

def stop():
   motor1a.low()
   motor1b.low()

def test():
    print('  Begin test()')
    forward()
    utime.sleep(1)
    backward()
    utime.sleep(1)
    stop()
    print('  End   test()')

for i in range(1):
    print('Test ', i)
    test()

'''

How do we develop AI education in schools? A panel discussion - Maria Richter, Rpi Foundation monthly AI education seminar series 2021nov30



Pico I2C Evaluation Board v0.1



Drive a Raspberry Pi Car Using Hand Gestures With OpenCV - Ash Hill 2021dec04

This new Raspberry Pi-powered cluster computer is cheaper than a basic desktop - Mayank Sharma 2021dec05

Learn about electronics, coding, IoT and more as you build fun projects for just $20 - ZDNet

You don’t need to know anything except how to use a computer to acquire all kinds of new tech skills as you build the most interesting projects, like GPS tracking, smart home remote dashboards and much more.


Pico UART, SPI, I2C Evaluation Board Pinout v0.1 2021dec05hkt1314




Dual PCF8574 Module Board Testing Notes

PCF8574 Module - 8-bit I/O Expander for I2C Bus - Nettigo

PCF8574A Remote 8-bit I/O expander for I2C-bus with interrupt - NXP



PCF8574 IOX Test Function v01

PCF8574 IOX Test Function v01


PCF8574 Wiring V0.2


Everyday Robot has eyes and arms. I am so jealous, …

Alphabet’s Everyday Robots - IEEE Spectrum 2021nov23

Introducing Everyday Robots - Everyday Robots 2021nov01


PCF8574 Iox Driving TB6612FNG Python Program

pcf8574_test_v53_2021dec0801.py


The Elektor Lab Team is interesting

The Elektor Lab Team


picorobot67.py


Pico MicroPython program Driving 4WD Through PCF8514 Iox and TB6612FNG Motor Driver

picorobot79.py


Pico Wireless Pack - Piromorni £12

Me jealously read the following this morning:

Pico Wireless Pack - Piromorni £12


Then this morning I read the following:

Develop boards remotely, in real time with new hardware-as-a-service - Nitin Dahad 2021dec13


The forum editor is getting very slow. So I am writing a new reply to see the response time will improve.

/ To continue, …

Scaling up 4WD (PCF8574 x 2) to 8WD (PCF8574 x 4)

Now I am writing a new reply, to verify my guess that if a reply gets too long, the editing time gets slower and slow. So far my guess seems proved. Anyway, I am moving on to the 4WD experiements.

I have found that my Pico controlling two remote 8 bit GPIO entender chips PCF8574 seems very smooth, so greed me is thinking of scaling up two to four PCF8574 chips, so I can have 4 x 8 to 32 GPIO pins to control 2WD to up to 6WD or 8WDs.

I am also thinking of checking out if Pico I2C bus can handling as many as 8 PCF8574, or MCP23017 16 bit GPIO extenders, so I can have 16 x 8 = 128 GPIO pins. Also I don’t need to use any Pico GP pins. Also I am using microPython, and my code can be easily ported to ESP32 projects.


Quad PCF8574 Evaluation Board



picoRobotV80.py


i2cBusList = setupI2cBusList([0, 1]) 
scanI2cBusList(i2cBusList) 

# *** Sample Output ***
MicroPython v1.14 on 2021-02-02; Raspberry Pi Pico with RP2040
>>> %Run -c $EDITOR_CONTENT
      I2C Bus Num = 0   I2cDevAddr List = 0x24 0x26 
      I2C Bus Num = 1   I2cDevAddr List = 0x20 0x23 

>>>

ZDnet says 2022 might has an robotics revolution

2022: A major revolution in robotics - A world class roboticist on why everything is about to change - ZDnet


Pico Smart Vehicle 6WD/8WD setup and testing notes

picorobot85.py

So I have completed the basic tests of 8 bit Iox PCF8574 x 4 (blinking byte and blinking bit). Next step is to setup and test 4 TB6612FNG DC motor drivers and continue testing.

I have already done TB6612 testing in Part 1 of the project, together with 4 TM310 DC motors, but this time I will be using N20 motors.


I2C MCP23017 and SPI MCP23S17 16 bit/channel GPIO Extenders

I am also thinking of playing with mcp23017 and mcp23s17.

(1) Using an MCP23017 with a Pi Pico [SOLVED] - rpi.org.forum 2021jun01

(2) Raspberry Pi Pico MCP23017 Library - GitHub 2021feb01

(3) tlfong01’s mcp23017 forum posts - rpi/org/fourm search “mcp23017 tlfong01” = 178 matches


Quad TB6612FNG DC Motor Driver Board


Replacing I2C 8-bit PCF8574 Iox by 16-bit I2C MCP23017 or MCP23S17

Replacing Pico GP by PCF8574 Iox has been smooth. Below is a debugged preliminary test program:

picoRobot87.py

However, I found PCF8574 8-bit Iox newbie friendly, but not that flexible and powerful as 16-bit I2C MCP23017 or SPI MCP23S17 Iox.

So I have decided to replace PCF8574 by MCP23S17.


MCP23S17 References

(1) MCP23017/MCP23S17 16-Bit I/O Expander with SPI/I2C Interface - MicroChip

(2) CJMCU-2317 mcp23s17 bidirectional 16-bit i/o io i2c expansion board serial interface module for arduonio - AliExpress US$3.52

(3) SPI- bus length? - Asked 2015, Viewed 4k times

(4) SPI Bus Termination Issue - Asked 2015, Viewed 2k times

(5) SPI noise after extending wires. Why? - EESE 2021nov

(6) Can the length of a wire impact SPI transmission? - EESE 2020feb24



Pico Projects by Tom’s Hardware

Pico Projects by Tom’s Harware


Pico SPI Pinout


RFID and Multiple ultrasonic sensor projects

I am feeling jealous again, this time on the guys making Pico RFID and multiple ultrasonic distance sensors

(1) Raspberry Pi Pico Powered RFID Lighting - Les Pounder 2021dec19

(2) HCSR04 Ultrasonic Distance Sensor


Wiring up i2c mcp23017 x 2 and spi mcp23s17 x 2 for testing


MCP23x17 Programming Notes


Pico MicroPython SPI MCP23S17 Programming Design Pattern Notes


SPI Tutorials

Raspberry Pi Pico (RP2040) SPI Example (ADXL343) with MicroPython and C/C++ | Digi-Key Electronics

Beginners Guide to SPI on the Raspberry Pi Pico (BMP280 Example) - Learn Embedded Systems

MicroPython Doc 1.17 Class SPI – a Serial Peripheral Interface bus protocol (controller side)

Raspberry Pi Pico (RP2040) SPI Example with MicroPython and C/C++ - Digikey

Introduction to SPI Interface - Piyu Dhaker, Analog Dialogue


Raspberry Pi Pico - SPI 8x8 LED Matrix - Micropython example - 2021mar08


SPI Loopback tested OK

from machine import SPI, Pin
import utime

spiPort0  = machine.SPI(0, 
baudrate  = 100000,
polarity  = 0,
phase     = 0,
bits      = 8,
firstbit  = machine.SPI.MSB,
sck       = machine.Pin(2),
mosi      = machine.Pin(3),
miso      = machine.Pin(4))

cs0 = Pin(5, mode=Pin.OUT, value = 1) 

# *** main ***

for count in range(2):  
 
  #spiBuffer = bytearray(4)
  #spiPort0.write_readinto('test', spiBuffer)

  spiBuffer = bytearray(1)
  #spiPort0.write_readinto('x', spiBuffer)
  spiPort0.write_readinto(b'\x35', spiBuffer)   
    
  print('spiBuffer =', spiBuffer)  
  
  utime.sleep(1)
  
print('End of test')

# *** End ***

# *** Sample output ***
'''
>>> %Run -c $EDITOR_CONTENT
spiBuffer = bytearray(b'5')
spiBuffer = bytearray(b'5')
End of test
'''

# *** End of Sample Output ***

Trilobot, what is it?

A versatile, mid-level robot (with N20 motors and RGB LED underlighting) learning platform for Raspberry Pi - Piromori



Quad MCP23x17 SPI/I2C 160bit Iox Learning Board


Write Read MCP23S17 Registers

Now I have written a little Pico micropython function to do SPI loopback of three bytes. Next step is to write/read MCP23S17 registers.

# mcp23s17v03.py tlfong01  2021dec23hkt1540

from machine import SPI, Pin
import utime

# *** SPI Config ***

# *** spiPort0 ***

spiPort0  = machine.SPI(0, 
baudrate  = 100000,
polarity  = 0,
phase     = 0,
bits      = 8,
firstbit  = machine.SPI.MSB,
sck       = machine.Pin(2),
mosi      = machine.Pin(3),
miso      = machine.Pin(4))

cs00 = Pin(5, mode=Pin.OUT, value = 1)

# *** spiPort1 ***

spiPort1  = machine.SPI(1, 
baudrate  = 100000,
polarity  = 0,
phase     = 0,
bits      = 8,
firstbit  = machine.SPI.MSB,
sck       = machine.Pin(10),
mosi      = machine.Pin(11),
miso      = machine.Pin(12))

cs10 = Pin(13, mode=Pin.OUT, value = 1)

spiPortDict = {
                 '0': spiPort0,
                 '1': spiPort1,
              }                 

# *** Test Functions ***

def testSpi123ByteLoopback(): # setup notes: short MOSI and MISO pins to loop/echo input to output
    print('Begin testSpi123ByteLoopback(),...')

    spiBuffer1 = bytearray(1)                         # 1 byte buffer
    
    writeBytes = b'\x35'
    spiPort0.write_readinto(writeBytes, spiBuffer1)   # 1 byte loopback      
    print('  readBytes =', spiBuffer1)
    print('  readBytes =', hex(spiBuffer1[0]))

    writeBytes = b'\x36'
    spiPort0.write_readinto(writeBytes, spiBuffer1)   # 1 byte loopback      
    print('  readBytes =', spiBuffer1)
    print('  readBytes =', hex(spiBuffer1[0]))    
    
    spiBuffer3 = bytearray(3)                         # 3 byte buffer
    writeBytes = b'\x35\x36\x37'                      # 3 byte loopback
    print('  writeBytes =', writeBytes)
    spiPort0.write_readinto(writeBytes, spiBuffer3)    
    print('  readBytes =', spiBuffer3)
    print('  readBytes =', hex(spiBuffer3[0]), hex(spiBuffer3[1]), hex(spiBuffer3[2]))
    
    print('End   testSpi123ByteLoopback().')
    return

# *** Sample Test ***
# testSpi123ByteLoopback()

def spiWriteRead3Bytes(spiPortNum, write3Bytes):   
    spiPort = spiPortDict[str(spiPortNum)]
    buffer3Bytes = bytearray(3)    
    spiPort.write_readinto(write3Bytes, buffer3Bytes)    
    return buffer3Bytes

def testSpiWriteRead3Bytes():
    print('Begin testSpiWriteRead3Bytes(), ...')
    spiPortNum = 0
    write3Bytes = b'\x35\x36\x37'    
    buffer3Bytes = spiWriteRead3Bytes(spiPortNum, write3Bytes)
    print('  write3Bytes =', write3Bytes)
    print('  read3Bytes  =', buffer3Bytes)
    print('  read3Bytes  =', hex(buffer3Bytes[0]), hex(buffer3Bytes[1]), hex(buffer3Bytes[2]))    
    print('End   testSpiWriteRead3Bytes().') 

# *** Sample Tests ***

# testSpiWriteRead3Bytes()

# *** main ***

testSpi123ByteLoopback()
testSpiWriteRead3Bytes()

# *** End ***

'''
*** Begin Sample output ***
>>> %Run -c $EDITOR_CONTENT
Begin testSpi123ByteLoopback(),...
  readBytes = bytearray(b'5')
  readBytes = 0x35
  readBytes = bytearray(b'6')
  readBytes = 0x36
  writeBytes = b'567'
  readBytes = bytearray(b'567')
  readBytes = 0x35 0x36 0x37
End   testSpi123ByteLoopback().
Begin testSpiWriteRead3Bytes(), ...
  write3Bytes = b'567'
  read3Bytes  = bytearray(b'567')
  read3Bytes  = 0x35 0x36 0x37
End   testSpiWriteRead3Bytes().
>>> 
*** End Sample Output ***
'''

Build an Octapi


TriLoBot


Stepper Motor Control Reading Summary

This morning I read ElektroMagazine’s articles focusing on motor control. I found the stepper motor driver TMC2160 very interesting. I am thinking of experimenting with TMC21xx and TMC22xx and use TMC2209 Stepper Motor Controller to expand my 4WD.

ElektroMagazine 2022 Focus on Motor Control


TMC2160 Motor Driver Board - ElektorMagazine


Stepper Motor YouTube Tutorial

Stepper Motors with Arduino - Controlling Bipolar & Unipolar stepper motors - DroneBot Workshop, 403K subscribers 1,612,862 views, 2018feb10


/ to continue, …

Testing Stepper Motor for 4WD

Now I am thinking of using stepping motors to make a 4WD, so that I can precisely control the speed, position, and distance travelled.