Diablo - Powerful motor control for your robots!
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/diablo/examples.zip
You can also view the code listings at the bottom of this page.
Diablo.py
This is the main library for Diablo, it simplifies operation from using I²C directly to simply calling simple named functions such asDIABLO.SetMotor1(0.5)
See the scripts below for examples of use, or the library function listings for usage guidance.
diabloSequence.py
A simple script which controls the motors automatically based on an example sequence.This script demonstrates how simple operating Diablo 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 ~/diablo
./diabloSequence.py
diabloJoystick.py and runDiabloJoy.sh
This script uses pygame to demonstrate how we can make a joystick / gamepad control a robotTo 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 toTrue
if the robot runs the wrong way - 35:
axisLeftRight
, the axis index used for steering control - 36:
axisLeftRightInverted
, set toTrue
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
- 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)
runDiabloJoy.sh
, is only used to hide some irritating lines displayed by pygame, it does not affect how the script works.Run using:
cd ~/diablo
./runDiabloJoy.sh
diabloStepper.py
This script demonstrates how a stepper motor (4, 5, or 6 wire) can be driven using Diablo.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 ~/diablo
./diabloStepper.py
diabloStepperSeq.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 ~/diablo
./diabloStepperSeq.py
diabloGui.py
This script shows how you can make a GUI in Python which controls Diablo, 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
DIABLO.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 ~/diablo
./diabloGui.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 Diablo DIABLO = Diablo.Diablo() DIABLO.Init() # User code here, use DIABLO to control the boardMultiple boards can be used when configured with different I²C addresses by creating multiple instances, e.g.
import Diablo DIABLO1 = Diablo.Diablo() DIABLO2 = Diablo.Diablo() DIABLO1.i2cAddress = 0x44 DIABLO2.i2cAddress = 0x45 DIABLO1.Init() DIABLO2.Init() # User code here, use DIABLO1 and DIABLO2 to control each board separatelyFor explanations of the functions available call the Help function, e.g.
import Diablo DIABLO = Diablo.Diablo() DIABLO.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 Diablo DIABLO = Diablo.Diablo() DIABLO.i2cAddress = 0x10 DIABLO.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 Diablo DIABLO = Diablo.Diablo() DIABLO.Init() if DIABLO.foundChip: print 'Yay !' else: print 'Aww...'
busNumber
I²C bus on which the Diablo is attached (Rev 1 is bus0
, 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 devicesi2cAddress
The I²C address of the Diablo chip to controlfoundChip
True
if the Diablo chip can be seen, False
otherwiseprintFunction
Function reference to call when printing text, ifNone
"print
" is usedBoard Functions
These represent the majority of the functionality of the Diablo, such as setting motor speedsCall these functions on an instance, after calling
Init()
, e.g.import Diablo DIABLO = Diablo.Diablo() DIABLO.Init() DIABLO.SetMotor1(0.4)
Print(message)
Wrapper used by the Diablo instance to print messages, will callprintFunction
if set, print
otherwiseNoPrint(message)
Does nothing, intended for disabling diagnostic printout by using:DIABLO = Diablo.Diablo() DIABLO.printFunction = DIABLO.NoPrint
Init([tryOtherBus])
Prepare the I²C driver for talking to the DiabloIf
tryOtherBus
is True
or omitted, this function will attempt to use the other bus if the Diablo 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 stoppedSetMotor2(0.75)
-> motor 2 moving forward at 75% powerSetMotor2(-0.5)
-> motor 2 moving reverse at 50% powerSetMotor2(1)
-> motor 2 moving forward at 100% powerpower = GetMotor2()
Gets the drive level for motor 2, from +1 to -1.e.g.
0
-> motor 2 is stopped0.75
-> motor 2 moving forward at 75% power-0.5
-> motor 2 moving reverse at 50% power1
-> motor 2 moving forward at 100% powerSetMotor1(power)
Sets the drive level for motor 1, from +1 to -1.e.g.
SetMotor1(0)
-> motor 1 is stoppedSetMotor1(0.75)
-> motor 1 moving forward at 75% powerSetMotor1(-0.5)
-> motor 1 moving reverse at 50% powerSetMotor1(1)
-> motor 1 moving forward at 100% powerpower = GetMotor1()
Gets the drive level for motor 1, from +1 to -1.e.g.
0
-> motor 1 is stopped0.75
-> motor 1 moving forward at 75% power-0.5
-> motor 1 moving reverse at 50% power1
-> motor 1 moving forward at 100% powerSetMotors(power)
Sets the drive level for all motors, from +1 to -1.e.g.
SetMotors(0)
-> all motors are stoppedSetMotors(0.75)
-> all motors moving forward at 75% powerSetMotors(-0.5)
-> all motors moving reverse at 50% powerSetMotors(1)
-> all motors moving forward at 100% powerMotorsOff()
Sets all motors to stopped, useful when ending a programResetEpo()
Resets the EPO latch state, use to allow movement again after the EPO has been trippedstate = 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 toFalse
if you have an EPO switch, True
if you do notstate = GetEpoIgnore()
Reads the system EPO ignore state,False
for using the EPO latch, True
for ignoring the EPO latchSetCommsFailsafe(state)
Sets the system to enable or disable the communications failsafeThe 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 failsafeThe failsafe is disabled at power on
state = GetCommsFailsafe()
Read the current system state of the communications failsafe,True
for enabled, False
for disabledThe 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 detectedFaults 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 as0V
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
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 trippedFor more details check the website at www.piborg.org/diablo and double check the wiring instructions
SetEncoderMoveMode(state)
Sets the system to enable or disable the encoder based move modeIn 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/diablo 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 32767Use negative values to move in reverse
e.g.
EncoderMoveMotor2(100)
-> motor 2 moving forward for 100 countsEncoderMoveMotor2(-50)
-> motor 2 moving reverse for 50 countsEncoderMoveMotor2(5)
-> motor 2 moving forward for 5 countsEncoderMoveMotor1(counts)
Moves motor 1 until it has seen a number of encoder counts, up to 32767Use negative values to move in reverse
e.g.
EncoderMoveMotor1(100)
-> motor 1 moving forward for 100 countsEncoderMoveMotor1(-50)
-> motor 1 moving reverse for 50 countsEncoderMoveMotor1(5)
-> motor 1 moving forward for 5 countsEncoderMoveMotors(counts)
Moves all motors until they have each seen a number of encoder counts, up to 65535Use negative values to move in reverse
e.g.
EncoderMoveMotors(100)
-> all motors moving forward for 100 countsEncoderMoveMotors(-50)
-> all motors moving reverse for 50 countsEncoderMoveMotors(5)
-> all motors moving forward for 5 countsstate = IsEncoderMoving()
Reads the current state of the encoder motion,False
for all motors have finished, True
for any motor is still movingsuccess = WaitWhileEncoderMoving([timeout])
Waits until all motors have finished performing encoder based movesIf 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 motionSetEncoderSpeed(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% powerSetEncoderSpeed(0.1)
-> motors may move at up to 10% powerSetEncoderSpeed(0.5)
-> motors may move at up to 50% powerSetEncoderSpeed(1)
-> motors may move at up to 100% powerpower = GetEncoderSpeed()
Gets the drive limit for encoder based moves, from 0 to 1.e.g.
0.01
-> motors may move at up to 1% power0.1
-> motors may move at up to 10% power0.5
-> motors may move at up to 50% power1
-> motors may move at up to 100% powerSetEnabled(state)
Sets if the system is powering the motor drive pinsIf True all of the motor pins are either low, high, or PWMed (powered)
If False all of the motor pins are tri-stated (unpowered)
state = GetEnabled()
Gets if the system is powering the motor drive pinsIf True all of the motor pins are either low, high, or PWMed (powered)
If False all of the motor pins are tri-stated (unpowered)
Help()
Displays the names and descriptions of the various functions and settings providedThis 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 Diablos to be used by a single Raspberry Pi.Call these functions directly from the module, e.g.
import Diablo Diablo.ScanForDiablo()
ScanForDiablo([busNumber])
Scans the I²C bus for a Diablo boards and returns a list of all usable addressesThe
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 Diablo and sets it to a new I²C addressIf
oldAddress
is supplied it will change the address of the board at that address rather than scanning the busThe
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
Diablo.py
#!/usr/bin/env python # coding: latin-1 """ This module is designed to communicate with the Diablo Use by creating an instance of the class, call the Init function, then command as desired, e.g. import Diablo DIABLO = Diablo.Diablo() DIABLO.Init() # User code here, use DIABLO to control the board Multiple boards can be used when configured with different I²C addresses by creating multiple instances, e.g. import Diablo DIABLO1 = Diablo.Diablo() DIABLO2 = Diablo.Diablo() DIABLO1.i2cAddress = 0x44 DIABLO2.i2cAddress = 0x45 DIABLO1.Init() DIABLO2.Init() # User code here, use DIABLO1 and DIABLO2 to control each board separately For explanations of the functions available call the Help function, e.g. import Diablo DIABLO = Diablo.Diablo() DIABLO.Help() See the website at www.piborg.org/diablo 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_DIABLO = 0x37 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_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_SET_ENABLED = 30 # Set if the motor drives are enabled COMMAND_GET_ENABLED = 31 # Get the motor drive enabled state 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 ScanForDiablo(busNumber = 1): """ ScanForDiablo([busNumber]) Scans the I²C bus for a Diablo 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_DIABLO: print 'Found Diablo at %02X' % (address) found.append(address) else: pass else: pass except KeyboardInterrupt: raise except: pass if len(found) == 0: print 'No Diablo boards found, is bus #%d correct (should be 0 for Rev 1, 1 for Rev 2)' % (busNumber) elif len(found) == 1: print '1 Diablo board found' else: print '%d Diablo 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 Diablo 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 = ScanForDiablo(busNumber) if len(found) < 1: print 'No Diablo 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_DIABLO: foundChip = True print 'Found Diablo at %02X' % (oldAddress) else: foundChip = False print 'Found a device at %02X, but it is not a Diablo (ID %02X instead of %02X)' % (oldAddress, i2cRecv[1], I2C_ID_DIABLO) else: foundChip = False print 'Missing Diablo at %02X' % (oldAddress) except KeyboardInterrupt: raise except: foundChip = False print 'Missing Diablo 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_DIABLO: foundChip = True print 'Found Diablo at %02X' % (newAddress) else: foundChip = False print 'Found a device at %02X, but it is not a Diablo (ID %02X instead of %02X)' % (newAddress, i2cRecv[1], I2C_ID_DIABLO) else: foundChip = False print 'Missing Diablo at %02X' % (newAddress) except KeyboardInterrupt: raise except: foundChip = False print 'Missing Diablo 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 Diablo class Diablo: """ This module is designed to communicate with the Diablo busNumber I²C bus on which the Diablo 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 Diablo chip to control foundChip True if the Diablo 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 = I2C_ID_DIABLO # I²C address, override for a different address bus = None foundChip = False printFunction = None def Print(self, message): """ Print(message) Wrapper used by the Diablo 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: DIABLO = Diablo.Diablo() DIABLO.printFunction = DIABLO.NoPrint """ pass def Init(self, tryOtherBus = True): """ Init([tryOtherBus]) Prepare the I2C driver for talking to the Diablo If tryOtherBus is True or omitted, this function will attempt to use the other bus if the Diablo devices can not be found on the current busNumber """ self.Print('Loading Diablo on bus %d, address %02X' % (self.busNumber, self.i2cAddress)) # Open the bus self.bus = smbus.SMBus(self.busNumber) # Check for Diablo 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_DIABLO: self.foundChip = True self.Print('Found Diablo at %02X' % (self.i2cAddress)) else: self.foundChip = False self.Print('Found a device at %02X, but it is not a Diablo (ID %02X instead of %02X)' % (self.i2cAddress, i2cRecv[1], I2C_ID_DIABLO)) else: self.foundChip = False self.Print('Missing Diablo at %02X' % (self.i2cAddress)) except KeyboardInterrupt: raise except: self.foundChip = False self.Print('Missing Diablo at %02X' % (self.i2cAddress)) # See if we are missing chips if not self.foundChip: self.Print('Diablo 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 Diablo is properly attached, the correct address is used, and the I2C drivers are running?') self.bus = None else: self.Print('Diablo 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 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 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 SetEnabled(self, state): """ SetEnabled(state) Sets if the system is powering the motor drive pins If True all of the motor pins are either low, high, or PWMed (powered) If False all of the motor pins are tri-stated (unpowered) """ if state: level = COMMAND_VALUE_ON else: level = COMMAND_VALUE_OFF try: self.bus.write_byte_data(self.i2cAddress, COMMAND_SET_ENABLED, level) except KeyboardInterrupt: raise except: self.Print('Failed sending motor drive enabled state!') def GetEnabled(self): """ state = GetEnabled() Gets if the system is powering the motor drive pins If True all of the motor pins are either low, high, or PWMed (powered) If False all of the motor pins are tri-stated (unpowered) """ try: i2cRecv = self.bus.read_i2c_block_data(self.i2cAddress, COMMAND_GET_ENABLED, I2C_MAX_LEN) except KeyboardInterrupt: raise except: self.Print('Failed reading motor drive enabled state!') return if i2cRecv[1] == COMMAND_VALUE_OFF: return False else: return True def Help(self): """ Help() Displays the names and descriptions of the various functions and settings provided """ funcList = [Diablo.__dict__.get(a) for a in dir(Diablo) if isinstance(Diablo.__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)
diabloSequence.py
#!/usr/bin/env python # coding: Latin-1 # Simple example of a motor sequence script # Import library functions we need import Diablo import time # Setup the Diablo DIABLO = Diablo.Diablo() # Create a new Diablo object DIABLO.Init() # Set the board up (checks the board is connected) DIABLO.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: DIABLO.SetMotor1(step[0]) # Set the first motor to the first value in the pair DIABLO.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 DIABLO.MotorsOff() # Turn both motors off print 'Done'
diabloJoystick.py
#!/usr/bin/env python # coding: Latin-1 # Load library functions we want import time import os import sys import pygame import Diablo # 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 Diablo DIABLO = Diablo.Diablo() #DIABLO.i2cAddress = 0x44 # Uncomment and change the value if you have changed the board address DIABLO.Init() if not DIABLO.foundChip: boards = Diablo.ScanForDiablo() if len(boards) == 0: print 'No Diablo found, check you are attached :)' else: print 'No Diablo at address %02X, but we did find boards:' % (DIABLO.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 'DIABLO.i2cAddress = 0x%02X' % (boards[0]) sys.exit() #DIABLO.SetEpoIgnore(True) # Uncomment to disable EPO latch, needed if you do not have a switch / jumper DIABLO.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) buttonFast = 8 # Joystick button number for driving fast whilst held (L2) slowFactor = 0.5 # Speed to slow to when the drive fast button is not 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): DIABLO.ResetEpo() if not joystick.get_button(buttonFast): driveLeft *= slowFactor driveRight *= slowFactor # Set the motors to the new speeds DIABLO.SetMotor1(driveLeft) DIABLO.SetMotor2(driveRight) # Wait for the interval period time.sleep(interval) # Disable all drives DIABLO.MotorsOff() except KeyboardInterrupt: # CTRL+C exit, disable all drives DIABLO.MotorsOff() print
runDiabloJoy.sh
#!/bin/bash ./diabloJoystick.py > /dev/null
diabloStepper.py
#!/usr/bin/env python # coding: latin-1 # Import library functions we need import Diablo 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 DIABLO # Setup the Diablo DIABLO = Diablo.Diablo() #DIABLO.i2cAddress = 0x44 # Uncomment and change the value if you have changed the board address DIABLO.Init() if not DIABLO.foundChip: boards = Diablo.ScanForDiablo() if len(boards) == 0: print 'No Diablo found, check you are attached :)' else: print 'No Diablo at address %02X, but we did find boards:' % (DIABLO.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 'DIABLO.i2cAddress = 0x%02X' % (boards[0]) sys.exit() #DIABLO.SetEpoIgnore(True) # Uncomment to disable EPO latch, needed if you do not have a switch / jumper DIABLO.ResetEpo() step = -1 # Function to perform a sequence of steps as fast as allowed def MoveStep(count): global step global DIABLO # 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] DIABLO.SetMotor1(drive[0]) DIABLO.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] DIABLO.SetMotor1(drive[0]) DIABLO.SetMotor2(drive[1]) time.sleep(stepDelay) count -= 1 try: # Start by turning all drives off DIABLO.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 DIABLO.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 DIABLO.MotorsOff() print 'Terminated'
diabloStepperSeq.py
#!/usr/bin/env python # coding: latin-1 # Import library functions we need import Diablo 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 DIABLO # Setup the Diablo DIABLO = Diablo.Diablo() #DIABLO.i2cAddress = 0x44 # Uncomment and change the value if you have changed the board address DIABLO.Init() if not DIABLO.foundChip: boards = Diablo.ScanForPicoBorgReverse() if len(boards) == 0: print 'No Diablo found, check you are attached :)' else: print 'No Diablo at address %02X, but we did find boards:' % (DIABLO.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 'DIABLO.i2cAddress = 0x%02X' % (boards[0]) sys.exit() #DIABLO.SetEpoIgnore(True) # Uncomment to disable EPO latch, needed if you do not have a switch / jumper DIABLO.ResetEpo() step = -1 # Function to perform a sequence of steps as fast as allowed def MoveStep(count): global step global DIABLO # 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] DIABLO.SetMotor1(drive[0]) DIABLO.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] DIABLO.SetMotor1(drive[0]) DIABLO.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 DIABLO.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 DIABLO.MotorsOff() print 'Terminated'
diabloGui.py
#!/usr/bin/env python # coding: latin-1 # Import library functions we need import Diablo import Tkinter # Setup the PicoBorg Reverse global DIABLO DIABLO = Diablo.Diablo() # Create a new Diablo object DIABLO.Init() # Set the board up (checks the board is connected) DIABLO.ResetEpo() # Reset the stop switch (EPO) state # if you do not have a switch across the two pin header then fit the jumper # Class representing the GUI dialog class Diablo_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 DIABLO self.title('Diablo 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 DIABLO.MotorsOff() # Called when the user closes the dialog def OnExit(self): global DIABLO # Turn drives off and end the program DIABLO.MotorsOff() self.quit() # Called when sld1 is moved def sld1_move(self, value): global DIABLO DIABLO.SetMotor1(float(value) / 100.0) # Called when sld2 is moved def sld2_move(self, value): global DIABLO DIABLO.SetMotor2(float(value) / 100.0) # Called when butOff is clicked def butOff_click(self): global DIABLO DIABLO.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 = Diablo_tk(None) app.mainloop()