0

I have this RPi3 that I am running a bunch codes on it. It is communicating with a server and also I have python supervisor running on it. Now I am trying to set up the same system on RPi4. For some reason RPi4 is not doing the functionality that I am looking for. Can someone look at the log files and let me know what is it that I am missing in RPi4. I here paste the log files from both PIs.

PI3 log file:

Port 5: 0100 power
Sent power on request
New status for hub 1-1 [0424:9514, USB 2.00, 5 ports]
  Port 2: 0100 power
  Port 3: 0100 power
  Port 4: 0100 power
  Port 5: 0100 power
Synchronizing state for rsyslog.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d rsyslog defaults
Executing /usr/sbin/update-rc.d rsyslog enable

Board model:                     2
GPIO configuration:            P1 (40 pins)
Using hardware:                PWM
Using DMA channel:              14
Idle timeout:             Disabled
Number of servos:                8
Servo cycle time:            20000us
Pulse increment step size:      10us
Minimum width value:            50 (500us)
Maximum width value:           250 (2500us)
Output levels:              Normal

Using P1 pins:               7,11,12,13,15,16,18,22

Servo mapping:
     0 on P1-7           GPIO-4
     1 on P1-11          GPIO-17
     2 on P1-12          GPIO-18
     3 on P1-13          GPIO-27
     4 on P1-15          GPIO-22
     5 on P1-16          GPIO-23
     6 on P1-18          GPIO-24
     7 on P1-22          GPIO-25

2019-09-05 23:30:55,812 Starting device runner. On version 1.16.15.
2019-09-05 23:30:55,815 Exited state off
2019-09-05 23:30:55,817 Entered state 

PI4 log file:

Sent power on request
New status for hub 2 [1d6b:0003 Linux 4.19.57-v7l+ xhci-hcd xHCI Host Controller 0000:01:00.0, USB 3.00, 4 ports]
  Port 2: 02a0 power 5gbps Rx.Detect
  Port 3: 02a0 power 5gbps Rx.Detect
  Port 4: 02a0 power 5gbps Rx.Detect
Current status for hub 1 [1d6b:0002 Linux 4.19.57-v7l+ xhci-hcd xHCI Host Controller 0000:01:00.0, USB 2.00, 1 ports]
Sent power on request
New status for hub 1 [1d6b:0002 Linux 4.19.57-v7l+ xhci-hcd xHCI Host Controller 0000:01:00.0, USB 2.00, 1 ports]
Synchronizing state of rsyslog.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable rsyslog
2019-09-05 14:53:18,748 Reseting PCA9685 MODE1 (without SLEEP) and MODE2
2019-09-05 14:53:18,749 Wrote 0x00 to register 0xFA
2019-09-05 14:53:18,750 Wrote 0x00 to register 0xFB
2019-09-05 14:53:18,751 Wrote 0x00 to register 0xFC
2019-09-05 14:53:18,751 Wrote 0x00 to register 0xFD
2019-09-05 14:53:18,752 Wrote 0x04 to register 0x01
2019-09-05 14:53:18,753 Wrote 0x01 to register 0x00
2019-09-05 14:53:18,759 Read 0x01 from register 0x00
2019-09-05 14:53:18,760 Wrote 0x01 to register 0x00
2019-09-05 14:53:18,765 Setting PWM frequency to 1600 Hz
2019-09-05 14:53:18,766 Estimated pre-scale: 2
2019-09-05 14:53:18,766 Final pre-scale: 3
2019-09-05 14:53:18,767 Read 0x01 from register 0x00
2019-09-05 14:53:18,768 Wrote 0x11 to register 0x00
2019-09-05 14:53:18,768 Wrote 0x03 to register 0xFE
2019-09-05 14:53:18,769 Wrote 0x01 to register 0x00
2019-09-05 14:53:18,775 Wrote 0x81 to register 0x00

Board model:                     2
GPIO configuration:            P1 (40 pins)
Using hardware:                PWM
Using DMA channel:              14
Idle timeout:             Disabled
Number of servos:                8
Servo cycle time:            20000us
Pulse increment step size:      10us
Minimum width value:            50 (500us)
Maximum width value:           250 (2500us)
Output levels:              Normal

Using P1 pins:               7,11,12,13,15,16,18,22

Servo mapping:
     0 on P1-7           GPIO-4
     1 on P1-11          GPIO-17
     2 on P1-12          GPIO-18
     3 on P1-13          GPIO-27
     4 on P1-15          GPIO-22
     5 on P1-16          GPIO-23
     6 on P1-18          GPIO-24
     7 on P1-22          GPIO-25

2019-09-05 14:53:18,933 Starting device runner. On version 1.16.15.
2019-09-05 14:53:28,947 Exited state off
2019-09-05 14:53:28,949 Entered state ready
2019-09-05 14:53:28,950 moving to top
  • 1
    What log files? What "bunch codes"? What doesn't work? What OS? – Milliways Sep 6 at 2:30
  • @RAS, Brainstorming questions: (1) Why do you power your 8 servos from USB2/3 hub? I remember when I tried the Rpi4V USB3 hub, some Rpi3B+ USB device drivers no long work. So I sadly went back to USB2 hub. (2) Your octo GPIO DMA driver might not port from Rpi3 to 4. That is one of the reasons I gave up using Rpi PWM pins and use PCA9685 with 16 GPIO/PWM pins. The good thing about PCA9685 is that you can set a time lag (eg 1% of 50Hz period) so when start/stop all servos together, actually each of them start with a time lag, prevent power dip and also back EMF, EMI etc. – tlfong01 Sep 6 at 3:08
  • @RAS, I noticed that you use 8 Rpi GPIO pins in Rpi3B+ but also switched to PCA9685 in Rpi4B. If that is so, of course you would expect a lot of teething or tech transfer problems. BTW, my PCA9685 functions also abstract to use higher level parameters such as: servo name (4 Rpi4B x 4 I2C bus x 2 PCA9685 x 16 PWM channels = 4 x 4 x 2 x 16 = "my-stupid-brain-calculator-overflow-so-many" servos, all "called by name") , bus name (4 I2C buses each Rpi4B), pulse width (500 ~ 2000uS), postion (extreme left, right, mid, etc), position steps (like a stepping motor! :)), for a 64+ servo robot arm, ... – tlfong01 Sep 6 at 3:19
  • @RAS, I skimmed you Rpi4B PCA9685 PWM log file again. I was a bit confused. I noticed that you said PWM frequency to 1.6 kHz. I forgot if I see the root frequency to around 50Hz for servos. Perhaps I forgot the details. I also found your setting the PCA6895 registers directory. I usually call the registers by name, to avoid any typos and easy troubleshooting. Perhaps I might show you my PCA9685 programming. BTW, I do use the scope to check out the 500uS to 2000uS pulses. Do you also use a scope or analyzer just to make sure? – tlfong01 Sep 6 at 3:57
  • @RAS, I checked my PCA9685 config function and found that I indeed base frequency. Perhaps I abstracted to often and forgot the original procedure. BTW I could no longer paste any more long code to my answer because I am hitting the Rpi Stack Exchange's 30k word limit. So I am listing my program as a Penzu entry: (1) penzu.com/p/3a36a526. You will find that in my program I call everything by name, to prevent typos, easy troubleshooting, and newbie understanding . – tlfong01 Sep 6 at 4:23
0

Question

Problem in porting Rpi3B+ python GPIO PWM servo programs to Rpi4B

Answer

Your Rpi3B+ log file shows DMA channels are use to PWM control servo. But the Rpi4B log file shows PCA9685 PWM GPIO is replacing DMA PWM. You need to show your program list to confirm this.

If PCA9685 is your project decision, I would recommend to refer to MagPi84 Circuit Python servo tutorial and use AdaFruit's open hardware/software PCA9685 PWM Controller HAT, and quad servo HAT, and their python servo libraries.

My servo project here is inspired by them. Please see Reference (2) for more details.

References

(1) Rpi4B PCA9685 PWM Controller driving 4 TowerPro MG996R Servos - tlfong01

(2) PCAS9685 PWM Controller Installation Notes - tlfong01

(3) Rpi3B+ GPIO PWM Servo Testing Notes - tlfong01

Appendices

(1) Rpi4B PCA9685 and 4 TowerPro MG996Rs

servo

(2) tlfong01's PWM Servo Program Listing

**# fservo61py  tlfong01  2019aug26hkt1446

# ********************************************************************************
# ********************************************************************************

# *** Import ***

from time          import sleep
import             fprint53    as fprint
import             ftime53     as ftime
import             fi2c53      as fi2c
import fpwm61      as fpwm

# ********************************************************************************
# ********************************************************************************

# *** Config ***

# *** Servo Type Dict ***

servoTypeDict0 = {
    'ServoModel'        : 'TowerPro MG96R',
    'ServoFunction'     : 'Ultrasound Sensor Right',
    'PwmFrequencyName'  : '50Hz',
    'PwmDutyCycleName'  : '50%',
    'PwmDutyCycleNum'   : 50,
    }

# *** Servo Device Dict ***

servoDevDict0 = {
    'ServoName'            : 'Annie',
    'ServoTypeDict'        : servoTypeDict0,
    'ServoNum'             : 0,

    'PwmDevNum'            : 0,
    'PwmChnNum'            : 0,

    'InitPwmPulseWidthNum' : 1500,
    'ServoStartDeg'        : 0,
    'ServoFinishDeg'       : 90,
    }

servoDevDict1 = {
    'ServoName'         : 'Betty',
    'ServoTypeDict'     : servoTypeDict0,
    'ServoNum'          : 1,

    'PwmDevNum'         : 0,
    'PwmChnNum'         : 1,

    'InitPwmPulseWidthNum' : 1500,
    'ServoStartDeg'     : 0,
    'ServoFinishDeg'    : 90,
    }

servoDevDict2 = {
    'ServoName'         : 'Connie',
    'ServoTypeDict'     : servoTypeDict0,
    'ServoNum'          : 2,

    'PwmDevNum'         : 1,
    'PwmChnNum'         : 0,

    'InitPwmPulseWidthNum' : 1500,
    'ServoStartDeg'     : 0,
    'ServoFinishDeg'    : 90,
    }

servoDevDict3 = {
    'ServoName'         : 'Daisy',
    'ServoTypeDict'     : servoTypeDict0,
    'ServoNum'          : 3,

    'PwmDevNum'         : 1,
    'PwmChnNum'         : 1,

    'InitPwmPulseWidthNum' : 1500,
    'ServoStartDeg'     : 0,
    'ServoFinishDeg'    : 90,
    }

# *** Servo Name Dict ***

servoNameDict = {
    'Annie'        : servoDevDict0,
    'Betty'        : servoDevDict1,
    'Connie'       : servoDevDict2,
    'Daisy'        : servoDevDict3,
    }

# *** Servo Control Byte Dict ***

servoControlByteDict = {    
    '24Hz'                        : 0xff,
    '50Hz'                        : 0x7f, # 50Hz = 20 mS period 
    '200Hz'                       : 0x1e,
    '400Hz'                       : 0x0f,
    '800Hz'                       : 0x07,      
    '1526Hz'                      : 0x03,

    '0%'                          : 0x00010fff, 
    '5%'                          : 0x00010f33, 
    '10%'                         : 0x00010e66,
    '20%'                         : 0x00010ccc,
    '50%'                         : 0x000107ff,
    '75%'                         : 0x000103ff,
    '80%'                         : 0x00010333,
    '90%'                         : 0x00010199,
    '55%'                         : 0x000100cc,
    'DutyCycle20%Delay10%'        : 0x019904cc, 

    '500uS'                       : 0x00010f99, #  0.5 mS (50Hz)
    '1000uS'                      : 0x00010f33, #  1.0 mS
    '1500uS'                      : 0x00010ecd, #  1.5 mS
    '2000uS'                      : 0x00010e66, #  2.0 mS (50Hz = 1000/50 = 20mS, (1 - 2/20) * 4096 = e66

    'Degree0'                     : 0x00010f33,
    'Degree90'                    : 0x00010ecd,
    'Degree180'                   : 0x00010e66,
    }

servoChangePositionDict = {
    '1000To2000'       : [1000, 2000],
    '2000To1000'       : [2000, 1000],

    '1000To1500'       : [1000, 1500],
    '1500To1000'       : [1500, 1000],       
    }

servoFixPositionDict = {
    '1000'             : 1000,
    '1500'             : 1500,
    '2000'             : 2000,
    }    

# *** Servo Functions ***

# *** PWM Functions ***

def setServoNamePwmFrequencyName(servoName):
    fprint.printBeginExecFunction()

    servoDevDict         = servoNameDict[servoName]
    pwmDevNum            = servoDevDict['PwmDevNum']
    pwmFrequencyName     = servoDevDict['ServoTypeDict']['PwmFrequencyName']

    fpwm.setFrequencyName(pwmDevNum, pwmFrequencyName)

    fprint.printEndExecFunction()
    return

def setServoNamePwmPulseWidthNum(servoName, pwmPulseWidthNum):
    fprint.printBeginExecFunction()

    servoDevDict    = servoNameDict[servoName]
    pwmDevNum       = servoDevDict['PwmDevNum']
    pwmChnNum       = servoDevDict['PwmChnNum']

    print('pwmChnNum =', pwmChnNum)

    fourByteNum = fpwm.convertPwmPulseWidthNumToFourByteNum(pwmPulseWidthNum)
    print('fourBytesNum =', hex(fourByteNum))

    fpwm.setDutyCycleFourByteNum(pwmDevNum, pwmChnNum, fourByteNum)

    fprint.printEndExecFunction()
    return

# *** Move Servo ***

def moveServoName(servoName, pulseWidthNum):
    fprint.printBeginExecFunction()    

    setServoNamePwmPulseWidthNum(servoName, pulseWidthNum)

    fprint.printEndExecFunction()
    return

# *** Test Functions ***

def initServoName(servoName):
    fprint.printBeginExecFunction()

    setServoNamePwmFrequencyName(servoName)

    servoDevDict    = servoNameDict[servoName]
    initPwmPulseWidthNum = servoDevDict['InitPwmPulseWidthNum']
    setServoNamePwmPulseWidthNum(servoName, initPwmPulseWidthNum)

    fprint.printEndExecFunction()
    return

def initServoNameList(servoNameList):
    fprint.printBeginExecFunction()

    for servoName in servoNameList:
        initServoName(servoName)

    fprint.printEndExecFunction()
    return

def moveToPulseWidthNum(servoName, pulseWidthNum):
    servoDevDict    = servoNameDict[servoName]
    setServoNamePwmPulseWidthNum(servoName, pulseWidthNum)
    return

def repeatSwingServo(servoName, beginPulseWidthNum, endPulseWidthNum, pauseSeconds, repeatCount):
    for count in range(repeatCount):
        moveToPulseWidthNum(servoName, beginPulseWidthNum)
        sleep(pauseSeconds)
        moveToPulseWidthNum(servoName, endPulseWidthNum)
        sleep(pauseSeconds) 
    return

def moveServo(servoName, beginPulseWidthNum, endPulseWidthNum):
    moveToPulseWidthNum(servoName, beginPulseWidthNum)
    moveToPulseWidthNum(servoName, endPulseWidthNum)    
    return

def moveServoBySteps(servoName, beginPulseWidthNum, endPulseWidthNum, stepPulseWidthNum, stepPauseSeconds):

    moveToPulseWidthNum(servoName, beginPulseWidthNum)

    tempPulseWidthNum = beginPulseWidthNum + stepPulseWidthNum
    while (tempPulseWidthNum < endPulseWidthNum):
         moveToPulseWidthNum(servoName, tempPulseWidthNum)
         sleep(stepPauseSeconds)
         tempPulseWidthNum = tempPulseWidthNum + stepPulseWidthNum

    moveToPulseWidthNum(servoName, endPulseWidthNum)    
    return

def swingServo(servoName, beginPulseWidthNum, endPulseWidthNum, pauseSeconds):
    moveServo(servoName, beginPulseWidthNum, endPulseWidthNum)
    sleep(pauseSeconds)
    moveServo(servoName, endPulseWidthNum, beginPulseWidthNum)
    sleep(pauseSeconds)
    return

def repeatSwingServo(servoName, beginPulseWidthNum, endPulseWidthNum, pauseSeconds, repeatCount):
    for count in range(repeatCount):
        swingServo(servoName, beginPulseWidthNum, endPulseWidthNum, pauseSeconds)
    return

# ********************************************************************************
# ********************************************************************************

# *** Main Tests ***

def mainTests(): 
    #fpwm.testBlinkOneLed0()
    #fpwm.testBlinkAllLedsConcurrently()

    #fpwm.setFrequencyName(devNum = 0, frequencyName = '50Hz')
    #fpwm.setDutyCycleName(devNum = 0, ledNum = 0, dutyCycleName = '50%')

    #fpwm.setFrequencyName(devNum = 0, frequencyName = '50Hz')    
    #fpwm.setDutyCycleName(devNum = 0, ledNum = 0, dutyCycleName = '20%')

    #fpwm.setFrequencyName(devNum = 0, frequencyName = '800Hz')
    #fpwm.setDutyCycleNum (devNum = 0, ledNum = 0, dutyCycleNum  = 20)

    #initServoName('Annie')

    #initServoNameList(['Annie', 'Betty', 'Cindy', 'Daisy'])

    #repeatSwingServoName('Annie', 500, 5000)

    initServoName('Annie')
    initServoName('Betty')
    initServoName('Connie')
    initServoName('Daisy')
    sleep(2)

    repeatSwingServo('Annie',  1000, 2000, 1, 2)
    sleep(1)

    repeatSwingServo('Betty',  1000, 2000, 1, 2)
    sleep(1)

    repeatSwingServo('Connie', 1000, 2000, 1, 2)
    sleep(1)

    repeatSwingServo('Daisy',  1000, 2000, 1, 2)
    sleep(1)

    #moveServoBySteps('Annie', 1000, 2000, 5, 0.1)  
    #moveServoBySteps('Connie', 1000, 2000, 5, 0.1)



# ********************************************************************************
# ********************************************************************************

# *** Init/Main Functions ***

# *** Init Function ***

def init():
    pass
    return

#*** Main Function ***

def main():
    init()
    mainTests()    
    return

# ********************************************************************************
# ********************************************************************************

# *** Main ***

if __name__ == '__main__':
    main()

# *** End of Program ***

# ********************************************************************************
# **********************************************************************************

(3) tlfong01's Rpi4B Quad I2C Bus Testing Program Listing

# fi2c62.py  tlfong01 2019aug28hkt1433

from time          import sleep
import             smbus
import             fprint53    as fprint
import             ftime53     as ftime

# I2c Bus Setup Notes
# pi@raspberrypi:~ $ date Wed 28 Aug 2019 03:26:24 PM HKT
# pi@raspberrypi:~ $ uname -a
# Linux raspberrypi 4.19.58-v7l+ #1245 SMP Fri Jul 12 17:31:45 BST 2019 armv7l GNU/Linux
# pi@raspberrypi:~ $ sudo nano /boot/config.txt
# dtoverlay=i2c1,pins_2_3  (board pins 3, 5)
# dtoverlay=i2c3,pins_4_5  (board pins 7, 29)
# dtoverlay=i2c4,pins_6_7  (board pins 31, 26)
# dtoverlay=i2c5,pins_12_13 (board pins 32, 33)
# dtoverlay=i2c6,pins_22_23 (board pins 15, 16)
# pi@raspberrypi:~ $ ls /dev/i2c*
# /dev/i2c-1  /dev/i2c-3  /dev/i2c-4  /dev/i2c-5  /dev/i2c-6

# ********************************************************************************
# ********************************************************************************

# *** I2c Bus Config ***

i2cBus1 = smbus.SMBus(1) 
i2cBus3 = smbus.SMBus(3)
i2cBus4 = smbus.SMBus(4)
i2cBus5 = smbus.SMBus(5)

i2cBusDict = {'I2cBus1': i2cBus1,
              'I2cBus3': i2cBus3,
              'I2cBus4': i2cBus4,
              'I2cBus5': i2cBus5,
             }

# *** Pca9685 I2c Slave Device Congif ***

pca9685DevAddrDict = {
                'Dev0': 0x40,
                'Dev1': 0x41,
                'Dev2': 0x42,
                'Dev3': 0x43,
                'Dev4': 0x44,
                'Dev5': 0x45,
                'Dev6': 0x46,
                'Dev7': 0x47,
              }

pca9685RegAddrDict = { 'Mode1': 0x00,
                       'Mode2': 0x01,
                     }

pca9685DataByteDict = {
                       'Mode1Reset': 0x11,

    }

# ********************************************************************************
# ********************************************************************************

# *** Read Write Print Device/Register Functions ***

def writeDevTwoBytes(i2cBus, devAddr, writeByte1, writeByte2):
    i2cBus.write_byte_data(devAddr, writeByte1, writeByte2)
    return

def writeRegOneByte(i2cBus, devAddrDict, devName, regAddrDict, regName, writeByte):
    devAddr = devAddrDict[devName]
    regAddr = regAddrDict[regName]
    writeDevTwoBytes(i2cBus, devAddr, regAddr, writeByte)
    return

def readDevOneByte(i2cBus, devAddr, readByteAddr):
    readByte = i2cBus.read_byte_data(devAddr, readByteAddr)
    return readByte

def readRegOneByte(i2cBus, devAddrDict, devName, regAddrDict, regName):
    devAddr = devAddrDict[devName]
    regAddr = regAddrDict[regName]
    readByte = i2cBus.read_byte_data(devAddr, regAddr)
    return readByte

def printRegOneByte(i2cBus, devAddrDict, devName, regAddrDict, regName):
    readByte = readRegOneByte(i2cBusName, devAddrDict, devName, regAddrDict, regName)
    print(printTitle, hex(readByte))
    return

# *** Main Test Function ***

def testWriteReadPca9685Bus1Dev0RegMode1():     
    fprint.printBeginExecFunction()

    i2cBusName = 'I2cBus1'
    devName    = 'Dev0'
    regName    = 'Mode1'

    #i2cBus      = fi2c.i2cBusDict[i2cBusName]
    i2cBus      = i2cBusDict[i2cBusName]
    devAddrDict = pca9685DevAddrDict
    regAddrDict = pca9685RegAddrDict  

    writeByte = 0x77

    writeRegOneByte(i2cBus, devAddrDict, devName, regAddrDict, regName, writeByte)
    readByte = readRegOneByte(i2cBus, devAddrDict, devName, regAddrDict, regName) 

    if readByte == writeByte:
        resultsString = 'Good'
    else:
        resultsString = 'Bad'

    devAddr = devAddrDict[devName]

    fprint.printTitleOneByteNum('PCA9685 I2C Address',             fprint.indentFormat640, devAddr)
    fprint.printTitleOneByteNum('PCA9685 MODE1 Register Written',  fprint.indentFormat640, writeByte) 
    fprint.printTitleOneByteNum('PCA9685 MODE1 Register Read',     fprint.indentFormat640, readByte)    
    fprint.printTitleString('Write/Read Results',                  fprint.indentFormat640, resultsString)

    fprint.printEndExecFunction()
    return

def testPingPca9685Bus1Dev0RegMode1():     
    fprint.printBeginExecFunction()

    i2cBusName = 'I2cBus1'
    devName    = 'Dev0'
    regName    = 'Mode1'

    #i2cBus      = fi2c.i2cBusDict[i2cBusName]
    i2cBus      = i2cBusDict[i2cBusName]
    devAddrDict = pca9685DevAddrDict
    regAddrDict = pca9685RegAddrDict

    devAddr = devAddrDict[devName]

    readByte = readRegOneByte(i2cBus, devAddrDict, devName, regAddrDict, regName)    

    compareByte = pca9685DataByteDict['Mode1Reset'] 

    if readByte == compareByte:
        resultsString = 'Good'
    else:
        resultsString = 'Bad'

    fprint.printTitleString('PCA9685 I2C Address',             fprint.indentFormat640, hex(devAddr))
    fprint.printTitleString('PCA9685 MODE1 Register Written',  fprint.indentFormat640, hex(compareByte)) 
    fprint.printTitleString('PCA9685 MODE1 Register Read',     fprint.indentFormat640, hex(readByte))    
    fprint.printTitleString('Ping Results',                    fprint.indentFormat640, resultsString)

    fprint.printEndExecFunction()
    return

# ********************************************************************************
# ********************************************************************************

# *** Main Tests ***

def mainTests():
    #ftime.testPrintDateTime()
    testPingPca9685Bus1Dev0RegMode1()
    testWriteReadPca9685Bus1Dev0RegMode1()
    return

# ********************************************************************************
# ********************************************************************************

# *** Init/Main Functions ***

# *** Init Function ***

def init():
    pass
    return

#*** Main Function ***

def main():
    init()
    mainTests()    
    return

# ********************************************************************************
# ********************************************************************************

# *** Main ***

if __name__ == '__main__':
    main()

# *** End of Program ***

# ********************************************************************************
# ********************************************************************************

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.