PicoBorg Reverse - Advanced motor control for your Raspberry Pi

Main Examples

Download and installation instructions can be found on the getting started tab, as well as wiring instructions for the motor(s) and power.
The package of examples can be downloaded as a zip file directly: http://www.piborg.org/downloads/picoborgrev/examples.zip
You can also view the code listings at the bottom of this page.

PicoBorgRev.py

This is the main library for PicoBorg Reverse.
It makes life easier by providing simple named functions instead of having to call complicated I²C commands directly, for example:
PBR.SetMotor1(0.5)
See the scripts below for examples of use, or the library function listings for usage guidance.

pbrSequence.py

A simple script which controls the motors automatically based on an example sequence.
This script demonstrates how simple operating PicoBorg Reverse can be ^_^
Lines 18 to 36 determine the sequence the motors will run, line 38 determines how fast the sequence runs through.
Run using:
cd ~/picoborgrev
./pbrSequence.py

pbrJoystick.py and runPbrJoy.sh

This script uses pygame to demonstrate how we can make a joystick / gamepad control a robot
To run this example you will need to get a joystick / gamepad installed and working with the Raspberry Pi first.
If you are using a PS3 controller our PS3 help page here, otherwise see the JoyBorg, Connecting the joystick to the Raspberry Pi section for guidance.
The script will need to be setup for the controller you are using.
The lines which may need changing are:
  • 33: axisUpDown, the axis index used for speed control
  • 34: axisUpDownInverted, set to True if the robot runs the wrong way
  • 35: axisLeftRight, the axis index used for steering control
  • 36: axisLeftRightInverted, set to True if the robot turns the wrong way
  • 37: buttonResetEpo, the button index used for resetting the safety stop, generally should not be needed
  • 38: buttonSlow, the button index used for hold to drive slowly
  • 39: slowFactor, the maximum drive speed when the hold to drive slowly button is held
  • 40: buttonFastTurn, the button index used for hold to turn fast, allows turning on the spot whilst held
The default values when using a PS3 controller correspond to:
  • Left stick up / down: Speed control
  • Right stick left / right: Steering control
  • L2: Drive slowly whilst held (limits to 50% speed)
  • R2: Turn fast whilst held (allows on the spot turning)
  • Start button: Resets the safety stop if tripped (indicated by script using the LED)
The bash script, runPbrJoy.sh, is only used to hide some irritating lines displayed by pygame, it does not affect how the script works.
Run using:
cd ~/picoborgrev
./runPbrJoy.sh

pbrStepper.py

This script demonstrates how a stepper motor (4, 5, or 6 wire) can be driven using PicoBorg Reverse.
The script will ask you how many steps to move, it will then move that many steps as fast as it can.
Line 10 sets how long the script must remain at a given step before moving to the next position, if the motor is not operating correctly you may need to make this value larger.
Run using:
cd ~/picoborgrev
./pbrStepper.py

pbrStepperSeq.py

A simple script which controls a steeper motor automatically based on an example sequence.
Lines 83 to 92 determine the sequence the motor will run as a coded operation, this is then looped.
Line 10 sets how long the script must remain at a given step before moving to the next position, if the motor is not operating correctly you may need to make this value larger.
Line 11 tells the script how many degrees are represented by a single step, this will need to be set based on the stepper motor used.
The number of steps per rotation can be used to calculate the degPerStep value as follows:
degPerStep = stepsPerRotation / 360.0
Run using:
cd ~/picoborgrev
./pbrStepperSeq.py

pbrGui.py

This script shows how you can make a GUI in Python which controls PicoBorg Reverse, as well as providing an easy to use interactive demonstration of the board without needing any additional controller.

Drag the sliders up to make the motor move forwards, down to make the motor move backwards.
To get the same speed as the GUI in a script divide the value shown by 100, e.g. if the GUI shows motor 1 as -35 and you want the same speed run
PBR.SetMotor1(-0.35) in your script.
The "All Off" button will set both motors back to 0, the GUI will also do this when closed.
Run using:
cd ~/picoborgrev
./pbrGui.py
or double-clicking the icon on the desktop.

User Examples

Please post any examples you would like to share with others on the forum here.

Library Functions

Use by creating an instance of the class, call the Init function, then command as desired, e.g.
import PicoBorgRev
PBR = PicoBorgRev.PicoBorgRev()
PBR.Init()
# User code here, use PBR to control the board
Multiple boards can be used when configured with different I²C addresses by creating multiple instances, e.g.
import PicoBorgRev
PBR1 = PicoBorgRev.PicoBorgRev()
PBR2 = PicoBorgRev.PicoBorgRev()
PBR1.i2cAddress = 0x44
PBR2.i2cAddress = 0x45
PBR1.Init()
PBR2.Init()
# User code here, use PBR1 and PBR2 to control each board separately
For explanations of the functions available call the Help function, e.g.
import PicoBorgRev
PBR = PicoBorgRev.PicoBorgRev()
PBR.Help()
The same help text is also found below
In the below listing any function parameters surrounded by [ ] are optional and may be omitted.

Board Settings

These values should be accessed from a particular instance of the library, and may be set / read differently if more then one is created.
Set these after creating an instance but before calling Init(), for example to set the I²C address:
import PicoBorgRev
PBR = PicoBorgRev.PicoBorgRev()
PBR.i2cAddress = 0x10
PBR.Init()
When reading these values to see if things have worked read after calling Init(), for example to see if the board was found:
import PicoBorgRev
PBR = PicoBorgRev.PicoBorgRev()
PBR.Init()
if PBR.foundChip:
    print 'Yay !'
else:
    print 'Aww...'

busNumber

I²C bus on which the PicoBorg Reverse is attached (Rev 1 is bus 0, Rev 2 is bus 1)

bus

The smbus object used to talk to the I²C bus, only use this to talk with other I²C devices

i2cAddress

The I²C address of the PicoBorg Reverse chip to control

foundChip

True if the PicoBorg Reverse chip can be seen, False otherwise

printFunction

Function reference to call when printing text, if None "print" is used

Board Functions

These represent the majority of the functionality of the PicoBorg Reverse, such as setting motor speeds
Call these functions on an instance, after calling Init(), e.g.
import PicoBorgRev
PBR = PicoBorgRev.PicoBorgRev()
PBR.Init()
PBR.SetMotor1(0.4)

Print(message)

Wrapper used by the PicoBorgRev instance to print messages, will call printFunction if set, print otherwise

NoPrint(message)

Does nothing, intended for disabling diagnostic printout by using:
PBR = PicoBorgRev.PicoBorgRev()
PBR.printFunction = PBR.NoPrint

Init([tryOtherBus])

Prepare the I²C driver for talking to the PicoBorg Reverse
If tryOtherBus is True or omitted, this function will attempt to use the other bus if the PicoBorg Reverse devices can not be found on the current busNumber

SetMotor2(power)

Sets the drive level for motor 2, from +1 to -1.
e.g.
SetMotor2(0) -> motor 2 is stopped
SetMotor2(0.75) -> motor 2 moving forward at 75% power
SetMotor2(-0.5) -> motor 2 moving reverse at 50% power
SetMotor2(1) -> motor 2 moving forward at 100% power

power = GetMotor2()

Gets the drive level for motor 2, from +1 to -1.
e.g.
0 -> motor 2 is stopped
0.75 -> motor 2 moving forward at 75% power
-0.5 -> motor 2 moving reverse at 50% power
1 -> motor 2 moving forward at 100% power

SetMotor1(power)

Sets the drive level for motor 1, from +1 to -1.
e.g.
SetMotor1(0) -> motor 1 is stopped
SetMotor1(0.75) -> motor 1 moving forward at 75% power
SetMotor1(-0.5) -> motor 1 moving reverse at 50% power
SetMotor1(1) -> motor 1 moving forward at 100% power

power = GetMotor1()

Gets the drive level for motor 1, from +1 to -1.
e.g.
0 -> motor 1 is stopped
0.75 -> motor 1 moving forward at 75% power
-0.5 -> motor 1 moving reverse at 50% power
1 -> motor 1 moving forward at 100% power

SetMotors(power)

Sets the drive level for all motors, from +1 to -1.
e.g.
SetMotors(0) -> all motors are stopped
SetMotors(0.75) -> all motors moving forward at 75% power
SetMotors(-0.5) -> all motors moving reverse at 50% power
SetMotors(1) -> all motors moving forward at 100% power

MotorsOff()

Sets all motors to stopped, useful when ending a program

SetLed(state)

Sets the current state of the LED, False for off, True for on

state = GetLed()

Reads the current state of the LED, False for off, True for on

ResetEpo()

Resets the EPO latch state, use to allow movement again after the EPO has been tripped

state = GetEpo()

Reads the system EPO latch state.
If False the EPO has not been tripped, and movement is allowed.
If True the EPO has been tripped, movement is disabled if the EPO is not ignored (see SetEpoIgnore)
    Movement can be re-enabled by calling ResetEpo.

SetEpoIgnore(state)

Sets the system to ignore or use the EPO latch, set to False if you have an EPO switch, True if you do not

state = GetEpoIgnore()

Reads the system EPO ignore state, False for using the EPO latch, True for ignoring the EPO latch

SetCommsFailsafe(state)

Sets the system to enable or disable the communications failsafe
The failsafe will turn the motors off unless it is commanded at least once every 1/4 of a second
Set to True to enable this failsafe, set to False to disable this failsafe
The failsafe is disabled at power on

state = GetCommsFailsafe()

Read the current system state of the communications failsafe, True for enabled, False for disabled
The failsafe will turn the motors off unless it is commanded at least once every 1/4 of a second

state = GetDriveFault()

Reads the system drive fault state, False for no problems, True for a fault has been detected
Faults may indicate power problems, such as under-voltage (not enough power), and may be cleared by setting a lower drive power
If a fault is persistent, it repeatably occurs when trying to control the board, this may indicate a wiring problem such as:
  • The supply is not powerful enough for the motors
    The board has a bare minimum requirement of 6V to operate correctly
    A recommended minimum supply of 7.2V should be sufficient for smaller motors
  • The + and - connections for either motor are connected to each other
  • Either + or - is connected to ground (GND, also known as 0V or earth)
  • Either + or - is connected to the power supply (V+, directly to the battery or power pack)
  • One of the motors may be damaged
Faults will self-clear, they do not need to be reset, however some faults require both motors to be moving at less than 100% to clear
The easiest way to check is to put both motors at a low power setting which is high enough for them to rotate easily, such as 30%
Note that the fault state may be true at power up, this is normal and should clear when both motors have been driven
If there are no faults but you cannot make your motors move check GetEpo to see if the safety switch has been tripped
For more details check the website at www.piborg.org/picoborgrev and double check the wiring instructions

SetEncoderMoveMode(state)

Sets the system to enable or disable the encoder based move mode
In encoder move mode (enabled) the EncoderMoveMotor* commands are available to move fixed distances
In non-encoder move mode (disabled) the SetMotor* commands should be used to set drive levels
The encoder move mode requires that the encoder feedback is attached to an encoder signal, see the website at www.piborg.org/picoborgrev for wiring instructions
The encoder based move mode is disabled at power on

state = GetEncoderMoveMode()

Read the current system state of the encoder based move mode, True for enabled (encoder moves), False for disabled (power level moves)

EncoderMoveMotor2(counts)

Moves motor 2 until it has seen a number of encoder counts, up to 32767
Use negative values to move in reverse
e.g.
EncoderMoveMotor2(100) -> motor 2 moving forward for 100 counts
EncoderMoveMotor2(-50) -> motor 2 moving reverse for 50 counts
EncoderMoveMotor2(5) -> motor 2 moving forward for 5 counts

EncoderMoveMotor1(counts)

Moves motor 1 until it has seen a number of encoder counts, up to 32767
Use negative values to move in reverse
e.g.
EncoderMoveMotor1(100) -> motor 1 moving forward for 100 counts
EncoderMoveMotor1(-50) -> motor 1 moving reverse for 50 counts
EncoderMoveMotor1(5) -> motor 1 moving forward for 5 counts

EncoderMoveMotors(counts)

Moves all motors until they have each seen a number of encoder counts, up to 65535
Use negative values to move in reverse
e.g.
EncoderMoveMotors(100) -> all motors moving forward for 100 counts
EncoderMoveMotors(-50) -> all motors moving reverse for 50 counts
EncoderMoveMotors(5) -> all motors moving forward for 5 counts

state = IsEncoderMoving()

Reads the current state of the encoder motion, False for all motors have finished, True for any motor is still moving

success = WaitWhileEncoderMoving([timeout])

Waits until all motors have finished performing encoder based moves
If the motors stop moving the function will return True
If a timeout is provided the function will return False after timeout seconds if the motors are still in motion

SetEncoderSpeed(power)

Sets the drive limit for encoder based moves, from 0 to 1.
e.g.
SetEncoderSpeed(0.01) -> motors may move at up to 1% power
SetEncoderSpeed(0.1) -> motors may move at up to 10% power
SetEncoderSpeed(0.5) -> motors may move at up to 50% power
SetEncoderSpeed(1) -> motors may move at up to 100% power

power = GetEncoderSpeed()

Gets the drive limit for encoder based moves, from 0 to 1.
e.g.
0.01 -> motors may move at up to 1% power
0.1 -> motors may move at up to 10% power
0.5 -> motors may move at up to 50% power
1 -> motors may move at up to 100% power

Help()

Displays the names and descriptions of the various functions and settings provided
This will be the same text as above

Utility Functions

These functions are mostly used for setting up boards, they can be used to change I²C addresses to new values to either avoid conflicts with other devices or allow multiple PicoBorg Reverses to be used by a single Raspberry Pi.
Call these functions directly from the module, e.g.
import PicoBorgRev
PicoBorgRev.ScanForPicoBorgReverse()

ScanForPicoBorgReverse([busNumber])

Scans the I²C bus for a PicoBorg Reverse boards and returns a list of all usable addresses
The busNumber if supplied is which I²C bus to scan, 0 for Rev 1 boards, 1 for Rev 2 boards, if not supplied the default is 1

SetNewAddress(newAddress, [oldAddress], [busNumber])

Scans the I²C bus for the first PicoBorg Reverse and sets it to a new I²C address
If oldAddress is supplied it will change the address of the board at that address rather than scanning the bus
The busNumber if supplied is which I²C bus to scan, 0 for Rev 1 boards, 1 for Rev 2 boards, if not supplied the default is 1
Warning, this new I²C address will still be used after resetting the power on the device

Code Listings

PicoBorgRev.py

#!/usr/bin/env python
# coding: latin-1
"""
This module is designed to communicate with the PicoBorg Reverse

Use by creating an instance of the class, call the Init function, then command as desired, e.g.
import PicoBorgRev
PBR = PicoBorgRev.PicoBorgRev()
PBR.Init()
# User code here, use PBR to control the board

Multiple boards can be used when configured with different I²C addresses by creating multiple instances, e.g.
import PicoBorgRev
PBR1 = PicoBorgRev.PicoBorgRev()
PBR2 = PicoBorgRev.PicoBorgRev()
PBR1.i2cAddress = 0x44
PBR2.i2cAddress = 0x45
PBR1.Init()
PBR2.Init()
# User code here, use PBR1 and PBR2 to control each board separately

For explanations of the functions available call the Help function, e.g.
import PicoBorgRev
PBR = PicoBorgRev.PicoBorgRev()
PBR.Help()
See the website at www.piborg.org/picoborgreverse for more details
"""

# Import the libraries we need
import smbus
import types
import time

# Constant values
PWM_MAX                 = 255
I2C_MAX_LEN             = 4

I2C_ID_PICOBORG_REV     = 0x15

COMMAND_SET_LED         = 1     # Set the LED status
COMMAND_GET_LED         = 2     # Get the LED status
COMMAND_SET_A_FWD       = 3     # Set motor 2 PWM rate in a forwards direction
COMMAND_SET_A_REV       = 4     # Set motor 2 PWM rate in a reverse direction
COMMAND_GET_A           = 5     # Get motor 2 direction and PWM rate
COMMAND_SET_B_FWD       = 6     # Set motor 1 PWM rate in a forwards direction
COMMAND_SET_B_REV       = 7     # Set motor 1 PWM rate in a reverse direction
COMMAND_GET_B           = 8     # Get motor 1 direction and PWM rate
COMMAND_ALL_OFF         = 9     # Switch everything off
COMMAND_RESET_EPO       = 10    # Resets the EPO flag, use after EPO has been tripped and switch is now clear
COMMAND_GET_EPO         = 11    # Get the EPO latched flag
COMMAND_SET_EPO_IGNORE  = 12    # Set the EPO ignored flag, allows the system to run without an EPO
COMMAND_GET_EPO_IGNORE  = 13    # Get the EPO ignored flag
COMMAND_GET_DRIVE_FAULT = 14    # Get the drive fault flag, indicates faults such as short-circuits and under voltage
COMMAND_SET_ALL_FWD     = 15    # Set all motors PWM rate in a forwards direction
COMMAND_SET_ALL_REV     = 16    # Set all motors PWM rate in a reverse direction
COMMAND_SET_FAILSAFE    = 17    # Set the failsafe flag, turns the motors off if communication is interrupted
COMMAND_GET_FAILSAFE    = 18    # Get the failsafe flag
COMMAND_SET_ENC_MODE    = 19    # Set the board into encoder or speed mode
COMMAND_GET_ENC_MODE    = 20    # Get the boards current mode, encoder or speed
COMMAND_MOVE_A_FWD      = 21    # Move motor 2 forward by n encoder ticks
COMMAND_MOVE_A_REV      = 22    # Move motor 2 reverse by n encoder ticks
COMMAND_MOVE_B_FWD      = 23    # Move motor 1 forward by n encoder ticks
COMMAND_MOVE_B_REV      = 24    # Move motor 1 reverse by n encoder ticks
COMMAND_MOVE_ALL_FWD    = 25    # Move all motors forward by n encoder ticks
COMMAND_MOVE_ALL_REV    = 26    # Move all motors reverse by n encoder ticks
COMMAND_GET_ENC_MOVING  = 27    # Get the status of encoders moving
COMMAND_SET_ENC_SPEED   = 28    # Set the maximum PWM rate in encoder mode
COMMAND_GET_ENC_SPEED   = 29    # Get the maximum PWM rate in encoder mode
COMMAND_GET_ID          = 0x99  # Get the board identifier
COMMAND_SET_I2C_ADD     = 0xAA  # Set a new I2C address

COMMAND_VALUE_FWD       = 1     # I2C value representing forward
COMMAND_VALUE_REV       = 2     # I2C value representing reverse

COMMAND_VALUE_ON        = 1     # I2C value representing on
COMMAND_VALUE_OFF       = 0     # I2C value representing off


def ScanForPicoBorgReverse(busNumber = 1):
    """
ScanForPicoBorgReverse([busNumber])

Scans the I²C bus for a PicoBorg Reverse boards and returns a list of all usable addresses
The busNumber if supplied is which I²C bus to scan, 0 for Rev 1 boards, 1 for Rev 2 boards, if not supplied the default is 1
    """
    found = []
    print 'Scanning I²C bus #%d' % (busNumber)
    bus = smbus.SMBus(busNumber)
    for address in range(0x03, 0x78, 1):
        try:
            i2cRecv = bus.read_i2c_block_data(address, COMMAND_GET_ID, I2C_MAX_LEN)
            if len(i2cRecv) == I2C_MAX_LEN:
                if i2cRecv[1] == I2C_ID_PICOBORG_REV:
                    print 'Found PicoBorg Reverse at %02X' % (address)
                    found.append(address)
                else:
                    pass
            else:
                pass
        except KeyboardInterrupt:
            raise
        except:
            pass
    if len(found) == 0:
        print 'No PicoBorg Reverse boards found, is bus #%d correct (should be 0 for Rev 1, 1 for Rev 2)' % (busNumber)
    elif len(found) == 1:
        print '1 PicoBorg Reverse board found'
    else:
        print '%d PicoBorg Reverse boards found' % (len(found))
    return found


def SetNewAddress(newAddress, oldAddress = -1, busNumber = 1):
    """
SetNewAddress(newAddress, [oldAddress], [busNumber])

Scans the I²C bus for the first PicoBorg Reverse and sets it to a new I2C address
If oldAddress is supplied it will change the address of the board at that address rather than scanning the bus
The busNumber if supplied is which I²C bus to scan, 0 for Rev 1 boards, 1 for Rev 2 boards, if not supplied the default is 1
Warning, this new I²C address will still be used after resetting the power on the device
    """
    if newAddress < 0x03:
        print 'Error, I²C addresses below 3 (0x03) are reserved, use an address between 3 (0x03) and 119 (0x77)'
        return
    elif newAddress > 0x77:
        print 'Error, I²C addresses above 119 (0x77) are reserved, use an address between 3 (0x03) and 119 (0x77)'
        return
    if oldAddress < 0x0:
        found = ScanForPicoBorgReverse(busNumber)
        if len(found) < 1:
            print 'No PicoBorg Reverse boards found, cannot set a new I²C address!'
            return
        else:
            oldAddress = found[0]
    print 'Changing I²C address from %02X to %02X (bus #%d)' % (oldAddress, newAddress, busNumber)
    bus = smbus.SMBus(busNumber)
    try:
        i2cRecv = bus.read_i2c_block_data(oldAddress, COMMAND_GET_ID, I2C_MAX_LEN)
        if len(i2cRecv) == I2C_MAX_LEN:
            if i2cRecv[1] == I2C_ID_PICOBORG_REV:
                foundChip = True
                print 'Found PicoBorg Reverse at %02X' % (oldAddress)
            else:
                foundChip = False
                print 'Found a device at %02X, but it is not a PicoBorg Reverse (ID %02X instead of %02X)' % (oldAddress, i2cRecv[1], I2C_ID_PICOBORG_REV)
        else:
            foundChip = False
            print 'Missing PicoBorg Reverse at %02X' % (oldAddress)
    except KeyboardInterrupt:
        raise
    except:
        foundChip = False
        print 'Missing PicoBorg Reverse at %02X' % (oldAddress)
    if foundChip:
        bus.write_byte_data(oldAddress, COMMAND_SET_I2C_ADD, newAddress)
        time.sleep(0.1)
        print 'Address changed to %02X, attempting to talk with the new address' % (newAddress)
        try:
            i2cRecv = bus.read_i2c_block_data(newAddress, COMMAND_GET_ID, I2C_MAX_LEN)
            if len(i2cRecv) == I2C_MAX_LEN:
                if i2cRecv[1] == I2C_ID_PICOBORG_REV:
                    foundChip = True
                    print 'Found PicoBorg Reverse at %02X' % (newAddress)
                else:
                    foundChip = False
                    print 'Found a device at %02X, but it is not a PicoBorg Reverse (ID %02X instead of %02X)' % (newAddress, i2cRecv[1], I2C_ID_PICOBORG_REV)
            else:
                foundChip = False
                print 'Missing PicoBorg Reverse at %02X' % (newAddress)
        except KeyboardInterrupt:
            raise
        except:
            foundChip = False
            print 'Missing PicoBorg Reverse at %02X' % (newAddress)
    if foundChip:
        print 'New I²C address of %02X set successfully' % (newAddress)
    else:
        print 'Failed to set new I²C address...'


# Class used to control PicoBorg Reverse
class PicoBorgRev:
    """
This module is designed to communicate with the PicoBorg Reverse

busNumber               I²C bus on which the PicoBorg Reverse is attached (Rev 1 is bus 0, Rev 2 is bus 1)
bus                     the smbus object used to talk to the I²C bus
i2cAddress              The I²C address of the PicoBorg Reverse chip to control
foundChip               True if the PicoBorg Reverse chip can be seen, False otherwise
printFunction           Function reference to call when printing text, if None "print" is used
    """

    # Shared values used by this class
    busNumber               = 1     # Check here for Rev 1 vs Rev 2 and select the correct bus
    i2cAddress              = 0x44  # I²C address, override for a different address
    bus                     = None
    foundChip               = False
    printFunction           = None


    def Print(self, message):
        """
Print(message)

Wrapper used by the PicoBorgRev instance to print messages, will call printFunction if set, print otherwise
        """
        if self.printFunction == None:
            print message
        else:
            self.printFunction(message)


    def NoPrint(self, message):
        """
NoPrint(message)

Does nothing, intended for disabling diagnostic printout by using:
PBR = PicoBorgRev.PicoBorgRev()
PBR.printFunction = PBR.NoPrint
        """
        pass


    def Init(self, tryOtherBus = True):
        """
Init([tryOtherBus])

Prepare the I2C driver for talking to the PicoBorg Reverse
If tryOtherBus is True or omitted, this function will attempt to use the other bus if the PicoBorg Reverse devices can not be found on the current busNumber
        """
        self.Print('Loading PicoBorg Reverse on bus %d, address %02X' % (self.busNumber, self.i2cAddress))

        # Open the bus
        self.bus = smbus.SMBus(self.busNumber)

        # Check for PicoBorg Reverse
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_ID, I2C_MAX_LEN)
            if len(i2cRecv) == I2C_MAX_LEN:
                if i2cRecv[1] == I2C_ID_PICOBORG_REV:
                    self.foundChip = True
                    self.Print('Found PicoBorg Reverse at %02X' % (self.i2cAddress))
                else:
                    self.foundChip = False
                    self.Print('Found a device at %02X, but it is not a PicoBorg Reverse (ID %02X instead of %02X)' % (self.i2cAddress, i2cRecv[1], I2C_ID_PICOBORG_REV))
            else:
                self.foundChip = False
                self.Print('Missing PicoBorg Reverse at %02X' % (self.i2cAddress))
        except KeyboardInterrupt:
            raise
        except:
            self.foundChip = False
            self.Print('Missing PicoBorg Reverse at %02X' % (self.i2cAddress))

        # See if we are missing chips
        if not self.foundChip:
            self.Print('PicoBorg Reverse was not found')
            if tryOtherBus:
                if self.busNumber == 1:
                    self.busNumber = 0
                else:
                    self.busNumber = 1
                self.Print('Trying bus %d instead' % (self.busNumber))
                self.Init(False)
            else:
                self.Print('Are you sure your PicoBorg Reverse is properly attached, the correct address is used, and the I2C drivers are running?')
                self.bus = None
        else:
            self.Print('PicoBorg Reverse loaded on bus %d' % (self.busNumber))


    def SetMotor2(self, power):
        """
SetMotor2(power)

Sets the drive level for motor 2, from +1 to -1.
e.g.
SetMotor2(0)     -> motor 2 is stopped
SetMotor2(0.75)  -> motor 2 moving forward at 75% power
SetMotor2(-0.5)  -> motor 2 moving reverse at 50% power
SetMotor2(1)     -> motor 2 moving forward at 100% power
        """
        if power < 0:
            # Reverse
            command = COMMAND_SET_A_REV
            pwm = -int(PWM_MAX * power)
            if pwm > PWM_MAX:
                pwm = PWM_MAX
        else:
            # Forward / stopped
            command = COMMAND_SET_A_FWD
            pwm = int(PWM_MAX * power)
            if pwm > PWM_MAX:
                pwm = PWM_MAX

        try:
            self.bus.write_byte_data(self.i2cAddress, command, pwm)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending motor 2 drive level!')


    def GetMotor2(self):
        """
power = GetMotor2()

Gets the drive level for motor 2, from +1 to -1.
e.g.
0     -> motor 2 is stopped
0.75  -> motor 2 moving forward at 75% power
-0.5  -> motor 2 moving reverse at 50% power
1     -> motor 2 moving forward at 100% power
        """
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_A, I2C_MAX_LEN)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed reading motor 2 drive level!')
            return

        power = float(i2cRecv[2]) / float(PWM_MAX)

        if i2cRecv[1] == COMMAND_VALUE_FWD:
            return power
        elif i2cRecv[1] == COMMAND_VALUE_REV:
            return -power
        else:
            return


    def SetMotor1(self, power):
        """
SetMotor1(power)

Sets the drive level for motor 1, from +1 to -1.
e.g.
SetMotor1(0)     -> motor 1 is stopped
SetMotor1(0.75)  -> motor 1 moving forward at 75% power
SetMotor1(-0.5)  -> motor 1 moving reverse at 50% power
SetMotor1(1)     -> motor 1 moving forward at 100% power
        """
        if power < 0:
            # Reverse
            command = COMMAND_SET_B_REV
            pwm = -int(PWM_MAX * power)
            if pwm > PWM_MAX:
                pwm = PWM_MAX
        else:
            # Forward / stopped
            command = COMMAND_SET_B_FWD
            pwm = int(PWM_MAX * power)
            if pwm > PWM_MAX:
                pwm = PWM_MAX

        try:
            self.bus.write_byte_data(self.i2cAddress, command, pwm)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending motor 1 drive level!')


    def GetMotor1(self):
        """
power = GetMotor1()

Gets the drive level for motor 1, from +1 to -1.
e.g.
0     -> motor 1 is stopped
0.75  -> motor 1 moving forward at 75% power
-0.5  -> motor 1 moving reverse at 50% power
1     -> motor 1 moving forward at 100% power
        """
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_B, I2C_MAX_LEN)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed reading motor 1 drive level!')
            return

        power = float(i2cRecv[2]) / float(PWM_MAX)

        if i2cRecv[1] == COMMAND_VALUE_FWD:
            return power
        elif i2cRecv[1] == COMMAND_VALUE_REV:
            return -power
        else:
            return


    def SetMotors(self, power):
        """
SetMotors(power)

Sets the drive level for all motors, from +1 to -1.
e.g.
SetMotors(0)     -> all motors are stopped
SetMotors(0.75)  -> all motors are moving forward at 75% power
SetMotors(-0.5)  -> all motors are moving reverse at 50% power
SetMotors(1)     -> all motors are moving forward at 100% power
        """
        if power < 0:
            # Reverse
            command = COMMAND_SET_ALL_REV
            pwm = -int(PWM_MAX * power)
            if pwm > PWM_MAX:
                pwm = PWM_MAX
        else:
            # Forward / stopped
            command = COMMAND_SET_ALL_FWD
            pwm = int(PWM_MAX * power)
            if pwm > PWM_MAX:
                pwm = PWM_MAX

        try:
            self.bus.write_byte_data(self.i2cAddress, command, pwm)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending all motors drive level!')


    def MotorsOff(self):
        """
MotorsOff()

Sets all motors to stopped, useful when ending a program
        """
        try:
            self.bus.write_byte_data(self.i2cAddress, COMMAND_ALL_OFF, 0)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending motors off command!')


    def SetLed(self, state):
        """
SetLed(state)

Sets the current state of the LED, False for off, True for on
        """
        if state:
            level = COMMAND_VALUE_ON
        else:
            level = COMMAND_VALUE_OFF

        try:
            self.bus.write_byte_data(self.i2cAddress, COMMAND_SET_LED, level)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending LED state!')


    def GetLed(self):
        """
state = GetLed()

Reads the current state of the LED, False for off, True for on
        """ 
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_LED, I2C_MAX_LEN)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed reading LED state!')
            return

        if i2cRecv[1] == COMMAND_VALUE_OFF:
            return False
        else:
            return True


    def ResetEpo(self):
        """
ResetEpo()

Resets the EPO latch state, use to allow movement again after the EPO has been tripped
        """
        try:
            self.bus.write_byte_data(self.i2cAddress, COMMAND_RESET_EPO, 0)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed resetting EPO!')


    def GetEpo(self):
        """
state = GetEpo()

Reads the system EPO latch state.
If False the EPO has not been tripped, and movement is allowed.
If True the EPO has been tripped, movement is disabled if the EPO is not ignored (see SetEpoIgnore)
    Movement can be re-enabled by calling ResetEpo.
        """ 
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_EPO, I2C_MAX_LEN)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed reading EPO ignore state!')
            return

        if i2cRecv[1] == COMMAND_VALUE_OFF:
            return False
        else:
            return True


    def SetEpoIgnore(self, state):
        """
SetEpoIgnore(state)

Sets the system to ignore or use the EPO latch, set to False if you have an EPO switch, True if you do not
        """
        if state:
            level = COMMAND_VALUE_ON
        else:
            level = COMMAND_VALUE_OFF

        try:
            self.bus.write_byte_data(self.i2cAddress, COMMAND_SET_EPO_IGNORE, level)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending EPO ignore state!')


    def GetEpoIgnore(self):
        """
state = GetEpoIgnore()

Reads the system EPO ignore state, False for using the EPO latch, True for ignoring the EPO latch
        """ 
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_EPO_IGNORE, I2C_MAX_LEN)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed reading EPO ignore state!')
            return

        if i2cRecv[1] == COMMAND_VALUE_OFF:
            return False
        else:
            return True


    def SetCommsFailsafe(self, state):
        """
SetCommsFailsafe(state)

Sets the system to enable or disable the communications failsafe
The failsafe will turn the motors off unless it is commanded at least once every 1/4 of a second
Set to True to enable this failsafe, set to False to disable this failsafe
The failsafe is disabled at power on
        """
        if state:
            level = COMMAND_VALUE_ON
        else:
            level = COMMAND_VALUE_OFF

        try:
            self.bus.write_byte_data(self.i2cAddress, COMMAND_SET_FAILSAFE, level)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending communications failsafe state!')


    def GetCommsFailsafe(self):
        """
state = GetCommsFailsafe()

Read the current system state of the communications failsafe, True for enabled, False for disabled
The failsafe will turn the motors off unless it is commanded at least once every 1/4 of a second
        """ 
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_FAILSAFE, I2C_MAX_LEN)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed reading communications failsafe state!')
            return

        if i2cRecv[1] == COMMAND_VALUE_OFF:
            return False
        else:
            return True


    def GetDriveFault(self):
        """
state = GetDriveFault()

Reads the system drive fault state, False for no problems, True for a fault has been detected
Faults may indicate power problems, such as under-voltage (not enough power), and may be cleared by setting a lower drive power
If a fault is persistent, it repeatably occurs when trying to control the board, this may indicate a wiring problem such as:
    * The supply is not powerful enough for the motors
        The board has a bare minimum requirement of 6V to operate correctly
        A recommended minimum supply of 7.2V should be sufficient for smaller motors
    * The + and - connections for either motor are connected to each other
    * Either + or - is connected to ground (GND, also known as 0V or earth)
    * Either + or - is connected to the power supply (V+, directly to the battery or power pack)
    * One of the motors may be damaged
Faults will self-clear, they do not need to be reset, however some faults require both motors to be moving at less than 100% to clear
The easiest way to check is to put both motors at a low power setting which is high enough for them to rotate easily, such as 30%
Note that the fault state may be true at power up, this is normal and should clear when both motors have been driven
If there are no faults but you cannot make your motors move check GetEpo to see if the safety switch has been tripped
For more details check the website at www.piborg.org/picoborgrev and double check the wiring instructions
        """ 
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_DRIVE_FAULT, I2C_MAX_LEN)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed reading the drive fault state!')
            return

        if i2cRecv[1] == COMMAND_VALUE_OFF:
            return False
        else:
            return True


    def SetEncoderMoveMode(self, state):
        """
SetEncoderMoveMode(state)

Sets the system to enable or disable the encoder based move mode
In encoder move mode (enabled) the EncoderMoveMotor* commands are available to move fixed distances
In non-encoder move mode (disabled) the SetMotor* commands should be used to set drive levels
The encoder move mode requires that the encoder feedback is attached to an encoder signal, see the website at www.piborg.org/picoborgrev for wiring instructions
The encoder based move mode is disabled at power on
        """
        if state:
            level = COMMAND_VALUE_ON
        else:
            level = COMMAND_VALUE_OFF

        try:
            self.bus.write_byte_data(self.i2cAddress, COMMAND_SET_ENC_MODE, level)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending the encoder move mode!')


    def GetEncoderMoveMode(self):
        """
state = GetEncoderMoveMode()

Read the current system state of the encoder based move mode, True for enabled (encoder moves), False for disabled (power level moves)
        """ 
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_ENC_MODE, I2C_MAX_LEN)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed reading the encoder move mode!')
            return

        if i2cRecv[1] == COMMAND_VALUE_OFF:
            return False
        else:
            return True


    def EncoderMoveMotor2(self, counts):
        """
EncoderMoveMotor2(counts)

Moves motor 2 until it has seen a number of encoder counts, up to 32767
Use negative values to move in reverse
e.g.
EncoderMoveMotor2(100)   -> motor 2 moving forward for 100 counts
EncoderMoveMotor2(-50)   -> motor 2 moving reverse for 50 counts
EncoderMoveMotor2(5)     -> motor 2 moving forward for 5 counts
        """
        counts = int(counts)
        if counts < 0:
            # Reverse
            command = COMMAND_MOVE_A_REV
            counts = -counts
        else:
            # Forward
            command = COMMAND_MOVE_A_FWD

        if counts > 32767:
            self.Print('Cannot move %d counts in one go, moving 32767 counts instead' % (counts))
            counts = 32767
        countsLow = counts & 0xFF
        countsHigh = (counts >> 8) & 0xFF

        try:
            self.bus.write_i2c_block_data(self.i2cAddress, command, [countsHigh, countsLow])
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending motor 2 move request!')


    def EncoderMoveMotor1(self, counts):
        """
EncoderMoveMotor1(counts)

Moves motor 1 until it has seen a number of encoder counts, up to 32767
Use negative values to move in reverse
e.g.
EncoderMoveMotor1(100)   -> motor 1 moving forward for 100 counts
EncoderMoveMotor1(-50)   -> motor 1 moving reverse for 50 counts
EncoderMoveMotor1(5)     -> motor 1 moving forward for 5 counts
        """
        counts = int(counts)
        if counts < 0:
            # Reverse
            command = COMMAND_MOVE_B_REV
            counts = -counts
        else:
            # Forward
            command = COMMAND_MOVE_B_FWD

        if counts > 32767:
            self.Print('Cannot move %d counts in one go, moving 32767 counts instead' % (counts))
            counts = 32767
        countsLow = counts & 0xFF
        countsHigh = (counts >> 8) & 0xFF

        try:
            self.bus.write_i2c_block_data(self.i2cAddress, command, [countsHigh, countsLow])
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending motor 1 move request!')


    def EncoderMoveMotors(self, counts):
        """
EncoderMoveMotors(counts)

Moves all motors until they have each seen a number of encoder counts, up to 65535
Use negative values to move in reverse
e.g.
EncoderMoveMotors(100)   -> all motors moving forward for 100 counts
EncoderMoveMotors(-50)   -> all motors moving reverse for 50 counts
EncoderMoveMotors(5)     -> all motors moving forward for 5 counts
        """
        counts = int(counts)
        if counts < 0:
            # Reverse
            command = COMMAND_MOVE_ALL_REV
            counts = -counts
        else:
            # Forward
            command = COMMAND_MOVE_ALL_FWD
        countsLow = counts & 0xFF
        countsHigh = (counts >> 8) & 0xFF

        if counts > 32767:
            self.Print('Cannot move %d counts in one go, moving 32767 counts instead' % (counts))
            counts = 32767


        try:
            self.bus.write_i2c_block_data(self.i2cAddress, command, [countsHigh, countsLow])
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending motors move request!')


    def IsEncoderMoving(self):
        """
state = IsEncoderMoving()

Reads the current state of the encoder motion, False for all motors have finished, True for any motor is still moving
        """ 
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_ENC_MOVING, I2C_MAX_LEN)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed reading motor encoder moving state!')
            return

        if i2cRecv[1] == COMMAND_VALUE_OFF:
            return False
        else:
            return True


    def WaitWhileEncoderMoving(self, timeout = -1):
        """
success = WaitWhileEncoderMoving([timeout])

Waits until all motors have finished performing encoder based moves
If the motors stop moving the function will return True
If a timeout is provided the function will return False after timeout seconds if the motors are still in motion
        """
        startTime = time.time()
        while self.IsEncoderMoving():
            if timeout >= 0:
                if (time.time() - startTime) >= timeout:
                    self.Print('Timed out after %d seconds waiting for encoder moves to complete' % (timeout))
                    return False
            time.sleep(0.1)
        return True


    def SetEncoderSpeed(self, power):
        """
SetEncoderSpeed(power)

Sets the drive limit for encoder based moves, from 0 to 1.
e.g.
SetEncoderSpeed(0.01)  -> motors may move at up to 1% power
SetEncoderSpeed(0.1)   -> motors may move at up to 10% power
SetEncoderSpeed(0.5)   -> motors may move at up to 50% power
SetEncoderSpeed(1)     -> motors may move at up to 100% power
        """
        pwm = int(PWM_MAX * power)
        if pwm > PWM_MAX:
            pwm = PWM_MAX

        try:
            self.bus.write_byte_data(self.i2cAddress, COMMAND_SET_ENC_SPEED, pwm)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed sending motor encoder move speed limit!')


    def GetEncoderSpeed(self):
        """
power = GetEncoderSpeed()

Gets the drive limit for encoder based moves, from 0 to 1.
e.g.
0.01  -> motors may move at up to 1% power
0.1   -> motors may move at up to 10% power
0.5   -> motors may move at up to 50% power
1     -> motors may move at up to 100% power
        """
        try:
            i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_ENC_SPEED, I2C_MAX_LEN)
        except KeyboardInterrupt:
            raise
        except:
            self.Print('Failed reading motor encoder move speed limit!')
            return

        power = float(i2cRecv[1]) / float(PWM_MAX)
        return power


    def Help(self):
        """
Help()

Displays the names and descriptions of the various functions and settings provided
        """
        funcList = [PicoBorgRev.__dict__.get(a) for a in dir(PicoBorgRev) if isinstance(PicoBorgRev.__dict__.get(a), types.FunctionType)]
        funcListSorted = sorted(funcList, key = lambda x: x.func_code.co_firstlineno)

        print self.__doc__
        print
        for func in funcListSorted:
            print '=== %s === %s' % (func.func_name, func.func_doc)

pbrSequence.py

#!/usr/bin/env python
# coding: Latin-1

# Simple example of a motor sequence script

# Import library functions we need
import PicoBorgRev
import time

# Setup the PicoBorg Reverse
PBR = PicoBorgRev.PicoBorgRev()     # Create a new PicoBorg Reverse object
PBR.Init()                          # Set the board up (checks the board is connected)
PBR.ResetEpo()                      # Reset the stop switch (EPO) state
                                    # if you do not have a switch across the two pin header then fit the jumper

# Set our sequence, pairs of motor 1 and motor 2 drive levels
sequence = [
            [+0.2, +0.2],
            [+0.4, +0.4],
            [+0.6, +0.6],
            [+0.8, +0.8],
            [+1.0, +1.0],
            [+0.6, +1.0],
            [+0.2, +1.0],
            [-0.2, +1.0],
            [-0.6, +1.0],
            [-1.0, +1.0],
            [-0.6, +0.6],
            [-0.2, +0.2],
            [+0.2, -0.2],
            [+0.6, -0.6],
            [+1.0, -1.0],
            [+0.6, -0.6],
            [+0.3, -0.3],
            [+0.1, -0.1],
            [+0.0, +0.0],
           ]
stepDelay = 1.0                     # Number of seconds between each sequence step

# Loop over the sequence until the user presses CTRL+C
print 'Press CTRL+C to finish'
try:
    while True:
        # Go through each entry in the sequence in order
        for step in sequence:
            PBR.SetMotor1(step[0])                  # Set the first motor to the first value in the pair
            PBR.SetMotor2(step[1])                  # Set the second motor to the second value in the pair
            print '%+.1f %+.1f' % (step[0], step[1])
            time.sleep(stepDelay)                   # Wait between steps
except KeyboardInterrupt:
    # User has pressed CTRL+C
    PBR.MotorsOff()                 # Turn both motors off
    print 'Done'

pbrJoystick.py

#!/usr/bin/env python
# coding: Latin-1

# Load library functions we want
import time
import os
import sys
import pygame
import PicoBorgRev

# Re-direct our output to standard error, we need to ignore standard out to hide some nasty print statements from pygame
sys.stdout = sys.stderr

# Setup the PicoBorg Reverse
PBR = PicoBorgRev.PicoBorgRev()
#PBR.i2cAddress = 0x44                  # Uncomment and change the value if you have changed the board address
PBR.Init()
if not PBR.foundChip:
    boards = PicoBorgRev.ScanForPicoBorgReverse()
    if len(boards) == 0:
        print 'No PicoBorg Reverse found, check you are attached :)'
    else:
        print 'No PicoBorg Reverse at address %02X, but we did find boards:' % (PBR.i2cAddress)
        for board in boards:
            print '    %02X (%d)' % (board, board)
        print 'If you need to change the I²C address change the setup line so it is correct, e.g.'
        print 'PBR.i2cAddress = 0x%02X' % (boards[0])
    sys.exit()
#PBR.SetEpoIgnore(True)                 # Uncomment to disable EPO latch, needed if you do not have a switch / jumper
PBR.ResetEpo()

# Settings for the joystick
axisUpDown = 1                          # Joystick axis to read for up / down position
axisUpDownInverted = False              # Set this to True if up and down appear to be swapped
axisLeftRight = 2                       # Joystick axis to read for left / right position
axisLeftRightInverted = False           # Set this to True if left and right appear to be swapped
buttonResetEpo = 3                      # Joystick button number to perform an EPO reset (Start)
buttonSlow = 8                          # Joystick button number for driving slowly whilst held (L2)
slowFactor = 0.5                        # Speed to slow to when the drive slowly button is held, e.g. 0.5 would be half speed
buttonFastTurn = 9                      # Joystick button number for turning fast (R2)
interval = 0.00                         # Time between updates in seconds, smaller responds faster but uses more processor time

# Setup pygame
os.environ["SDL_VIDEODRIVER"] = "dummy" # Removes the need to have a GUI window
pygame.init()
pygame.joystick.init()
joystick = pygame.joystick.Joystick(0)
joystick.init()

try:
    print 'Press CTRL+C to quit'
    driveLeft = 0.0
    driveRight = 0.0
    running = True
    hadEvent = False
    upDown = 0.0
    leftRight = 0.0
    # Loop indefinitely
    while running:
        # Get the latest events from the system
        hadEvent = False
        events = pygame.event.get()
        # Handle each event individually
        for event in events:
            if event.type == pygame.QUIT:
                # User exit
                running = False
            elif event.type == pygame.JOYBUTTONDOWN:
                # A button on the joystick just got pushed down
                hadEvent = True                    
            elif event.type == pygame.JOYAXISMOTION:
                # A joystick has been moved
                hadEvent = True
            if hadEvent:
                # Read axis positions (-1 to +1)
                if axisUpDownInverted:
                    upDown = -joystick.get_axis(axisUpDown)
                else:
                    upDown = joystick.get_axis(axisUpDown)
                if axisLeftRightInverted:
                    leftRight = -joystick.get_axis(axisLeftRight)
                else:
                    leftRight = joystick.get_axis(axisLeftRight)
                # Apply steering speeds
                if not joystick.get_button(buttonFastTurn):
                    leftRight *= 0.5
                # Determine the drive power levels
                driveLeft = -upDown
                driveRight = -upDown
                if leftRight < -0.05:
                    # Turning left
                    driveLeft *= 1.0 + (2.0 * leftRight)
                elif leftRight > 0.05:
                    # Turning right
                    driveRight *= 1.0 - (2.0 * leftRight)
                # Check for button presses
                if joystick.get_button(buttonResetEpo):
                    PBR.ResetEpo()
                if joystick.get_button(buttonSlow):
                    driveLeft *= slowFactor
                    driveRight *= slowFactor
                # Set the motors to the new speeds
                PBR.SetMotor1(driveLeft)
                PBR.SetMotor2(driveRight)
        # Change the LED to reflect the status of the EPO latch
        PBR.SetLed(PBR.GetEpo())
        # Wait for the interval period
        time.sleep(interval)
    # Disable all drives
    PBR.MotorsOff()
except KeyboardInterrupt:
    # CTRL+C exit, disable all drives
    PBR.MotorsOff()
print

runPbrJoy.sh

#!/bin/bash
./pbrJoystick.py > /dev/null

pbrStepper.py

#!/usr/bin/env python
# coding: latin-1

# Import library functions we need
import PicoBorgRev
import time

# Tell the system how to drive the stepper
sequence = [[1.0, 1.0], [1.0, -1.0], [-1.0, -1.0], [-1.0, 1.0]] # Order for stepping 
stepDelay = 0.002                                               # Delay between steps

# Name the global variables
global step
global PBR

# Setup the PicoBorg Reverse
PBR = PicoBorgRev.PicoBorgRev()
#PBR.i2cAddress = 0x44                   # Uncomment and change the value if you have changed the board address
PBR.Init()
if not PBR.foundChip:
    boards = PicoBorgRev.ScanForPicoBorgReverse()
    if len(boards) == 0:
        print 'No PicoBorg Reverse found, check you are attached :)'
    else:
        print 'No PicoBorg Reverse at address %02X, but we did find boards:' % (PBR.i2cAddress)
        for board in boards:
            print '    %02X (%d)' % (board, board)
        print 'If you need to change the I²C address change the setup line so it is correct, e.g.'
        print 'PBR.i2cAddress = 0x%02X' % (boards[0])
    sys.exit()
#PBR.SetEpoIgnore(True)                 # Uncomment to disable EPO latch, needed if you do not have a switch / jumper
PBR.ResetEpo()
step = -1

# Function to perform a sequence of steps as fast as allowed
def MoveStep(count):
    global step
    global PBR

    # Choose direction based on sign (+/-)
    if count < 0:
        dir = -1
        count *= -1
    else:
        dir = 1

    # Loop through the steps
    while count > 0:
        # Set a starting position if this is the first move
        if step == -1:
            drive = sequence[-1]
            PBR.SetMotor1(drive[0])
            PBR.SetMotor2(drive[1])
            step = 0
        else:
            step += dir

        # Wrap step when we reach the end of the sequence
        if step < 0:
            step = len(sequence) - 1
        elif step >= len(sequence):
            step = 0

        # For this step set the required drive values
        if step < len(sequence):
            drive = sequence[step]
            PBR.SetMotor1(drive[0])
            PBR.SetMotor2(drive[1])
        time.sleep(stepDelay)
        count -= 1

try:
    # Start by turning all drives off
    PBR.MotorsOff()
    # Loop forever
    while True:
        # Ask the user how many steps to move
        steps = input("Steps to move (-ve for reverse, 0 to quit): ")
        if steps == 0:
            # Turn off the drives and release the GPIO pins
            PBR.MotorsOff()
            print 'Goodbye'
            break
        else:
            # Move the specified amount of steps
            MoveStep(steps)
except KeyboardInterrupt:
    # CTRL+C exit, turn off the drives and release the GPIO pins
    PBR.MotorsOff()
    print 'Terminated'

pbrStepperSeq.py

#!/usr/bin/env python
# coding: latin-1

# Import libary functions we need
import PicoBorgRev
import time

# Tell the system how to drive the stepper
sequence = [[1.0, 1.0], [1.0, -1.0], [-1.0, -1.0], [-1.0, 1.0]] # Order for stepping 
stepDelay = 0.002                                               # Delay between steps
degPerStep= 1.8                                                 # Number of degrees moved per step

# Name the global variables
global step
global PBR

# Setup the PicoBorg Reverse
PBR = PicoBorgRev.PicoBorgRev()
#PBR.i2cAddress = 0x44                   # Uncomment and change the value if you have changed the board address
PBR.Init()
if not PBR.foundChip:
    boards = PicoBorgRev.ScanForPicoBorgReverse()
    if len(boards) == 0:
        print 'No PicoBorg Reverse found, check you are attached :)'
    else:
        print 'No PicoBorg Reverse at address %02X, but we did find boards:' % (PBR.i2cAddress)
        for board in boards:
            print '    %02X (%d)' % (board, board)
        print 'If you need to change the I²C address change the setup line so it is correct, e.g.'
        print 'PBR.i2cAddress = 0x%02X' % (boards[0])
    sys.exit()
#PBR.SetEpoIgnore(True)                 # Uncomment to disable EPO latch, needed if you do not have a switch / jumper
PBR.ResetEpo()
step = -1

# Function to perform a sequence of steps as fast as allowed
def MoveStep(count):
    global step
    global PBR

    # Choose direction based on sign (+/-)
    if count < 0:
        dir = -1
        count *= -1
    else:
        dir = 1

    # Loop through the steps
    while count > 0:
        # Set a starting position if this is the first move
        if step == -1:
            drive = sequence[-1]
            PBR.SetMotor1(drive[0])
            PBR.SetMotor2(drive[1])
            step = 0
        else:
            step += dir

        # Wrap step when we reach the end of the sequence
        if step < 0:
            step = len(sequence) - 1
        elif step >= len(sequence):
            step = 0

        # For this step set the required drive values
        if step < len(sequence):
            drive = sequence[step]
            PBR.SetMotor1(drive[0])
            PBR.SetMotor2(drive[1])
        time.sleep(stepDelay)
        count -= 1

# Function to move based on an angular distance
def MoveDeg(angle):
    count = int(angle / float(degPerStep))
    MoveStep(count)

try:
    # Start by turning all drives off
    PBR.MotorsOff()
    # Loop forever
    while True:
        # Rotate forward 10 turns then wait
        MoveDeg(360 * 10)
        time.sleep(1)
        # Rotate reverse 10 turns then wait
        MoveDeg(-360 * 10)
        time.sleep(1)
        # Move forward 1/4 turn 16 times with waits between
        for i in range(16):
            MoveDeg(90)
            time.sleep(1)
except KeyboardInterrupt:
    # CTRL+C exit, turn off the drives and release the GPIO pins
    PBR.MotorsOff()
    print 'Terminated'

pbrGui.py

#!/usr/bin/env python
# coding: latin-1

# Import library functions we need 
import PicoBorgRev
import Tkinter

# Setup the PicoBorg Reverse
global PBR
PBR = PicoBorgRev.PicoBorgRev()     # Create a new PicoBorg Reverse object
PBR.Init()                          # Set the board up (checks the board is connected)
PBR.ResetEpo()                      # Reset the stop switch (EPO) state
                                    # if you do not have a switch across the two pin header then fit the jumper

# Class respresenting the GUI dialog
class PicoBorgRev_tk(Tkinter.Tk):
    # Constructor (called when the object is first created)
    def __init__(self, parent):
        Tkinter.Tk.__init__(self, parent)
        self.parent = parent
        self.protocol("WM_DELETE_WINDOW", self.OnExit) # Call the OnExit function when user closes the dialog
        self.Initialise()

    # Initialise the dialog
    def Initialise(self):
        global PBR
        self.title('PicoBorg Reverse Example GUI')
        # Setup a grid of 2 sliders which command each motor output, plus a stop button for both motors
        self.grid()
        self.sld1 = Tkinter.Scale(self, from_ = +100, to = -100, orient = Tkinter.VERTICAL, command = self.sld1_move)
        self.sld1.set(0)
        self.sld1.grid(column = 0, row = 0, rowspan = 1, columnspan = 1, sticky = 'NSEW')
        self.sld2 = Tkinter.Scale(self, from_ = +100, to = -100, orient = Tkinter.VERTICAL, command = self.sld2_move)
        self.sld2.set(0)
        self.sld2.grid(column = 1, row = 0, rowspan = 1, columnspan = 1, sticky = 'NSEW')
        self.butOff = Tkinter.Button(self, text = 'All Off', command = self.butOff_click)
        self.butOff['font'] = ("Arial", 20, "bold")
        self.butOff.grid(column = 0, row = 1, rowspan = 1, columnspan = 2, sticky = 'NSEW')
        self.grid_columnconfigure(0, weight = 1)
        self.grid_columnconfigure(1, weight = 1)
        self.grid_rowconfigure(0, weight = 4)
        self.grid_rowconfigure(1, weight = 1)
        # Set the size of the dialog
        self.resizable(True, True)
        self.geometry('200x600')
        # Setup the initial motor state
        PBR.MotorsOff()

    # Called when the user closes the dialog
    def OnExit(self):
        global PBR
        # Turn drives off and end the program
        PBR.MotorsOff()
        self.quit()
  
    # Called when sld1 is moved
    def sld1_move(self, value):
        global PBR
        PBR.SetMotor1(float(value) / 100.0)

    # Called when sld2 is moved
    def sld2_move(self, value):
        global PBR
        PBR.SetMotor2(float(value) / 100.0)

    # Called when butOff is clicked
    def butOff_click(self):
        global PBR
        PBR.MotorsOff()
        self.sld1.set(0)
        self.sld2.set(0)

# if we are the main program (python was passed a script) load the dialog automatically
if __name__ == "__main__":
    app = PicoBorgRev_tk(None)
    app.mainloop()
Subscribe to Comments for &quot;PicoBorg Reverse - Advanced motor control for your Raspberry Pi&quot;