#!/usr/bin/env python # coding: latin-1 """ This module is designed to communicate with the ZeroBorg Use by creating an instance of the class, call the Init function, then command as desired, e.g. import ZeroBorg3 as ZeroBorg ZB = ZeroBorg.ZeroBorg() ZB.Init() # User code here, use ZB to control the board Multiple boards can be used when configured with different I²C addresses by creating multiple instances, e.g. import ZeroBorg as ZeroBorg ZB1 = ZeroBorg.ZeroBorg() ZB2 = ZeroBorg.ZeroBorg() ZB1.i2cAddress = 0x44 ZB2.i2cAddress = 0x45 ZB1.Init() ZB2.Init() # User code here, use ZB1 and ZB2 to control each board separately For explanations of the functions available call the Help function, e.g. import ZeroBorg ZB = ZeroBorg.ZeroBorg() ZB.Help() See the website at www.piborg.org/zeroborg for more details """ # Import the libraries we need import io import fcntl import types import time # Constant values I2C_SLAVE = 0x0703 PWM_MAX = 255 I2C_NORM_LEN = 4 I2C_LONG_LEN = 24 I2C_ID_ZEROBORG = 0x40 COMMAND_SET_LED = 1 # Set the LED status COMMAND_GET_LED = 2 # Get the LED status COMMAND_SET_A_FWD = 3 # Set motor 1 PWM rate in a forwards direction COMMAND_SET_A_REV = 4 # Set motor 1 PWM rate in a reverse direction COMMAND_GET_A = 5 # Get motor 1 direction and PWM rate COMMAND_SET_B_FWD = 6 # Set motor 2 PWM rate in a forwards direction COMMAND_SET_B_REV = 7 # Set motor 2 PWM rate in a reverse direction COMMAND_GET_B = 8 # Get motor 2 direction and PWM rate COMMAND_SET_C_FWD = 9 # Set motor 3 PWM rate in a forwards direction COMMAND_SET_C_REV = 10 # Set motor 3 PWM rate in a reverse direction COMMAND_GET_C = 11 # Get motor 3 direction and PWM rate COMMAND_SET_D_FWD = 12 # Set motor 4 PWM rate in a forwards direction COMMAND_SET_D_REV = 13 # Set motor 4 PWM rate in a reverse direction COMMAND_GET_D = 14 # Get motor 4 direction and PWM rate COMMAND_ALL_OFF = 15 # Switch everything off COMMAND_SET_ALL_FWD = 16 # Set all motors PWM rate in a forwards direction COMMAND_SET_ALL_REV = 17 # Set all motors PWM rate in a reverse direction COMMAND_SET_FAILSAFE = 18 # Set the failsafe flag, turns the motors off if communication is interrupted COMMAND_GET_FAILSAFE = 19 # Get the failsafe flag COMMAND_RESET_EPO = 20 # Resets the EPO flag, use after EPO has been tripped and switch is now clear COMMAND_GET_EPO = 21 # Get the EPO latched flag COMMAND_SET_EPO_IGNORE = 22 # Set the EPO ignored flag, allows the system to run without an EPO COMMAND_GET_EPO_IGNORE = 23 # Get the EPO ignored flag COMMAND_GET_NEW_IR = 24 # Get the new IR message received flag COMMAND_GET_LAST_IR = 25 # Get the last IR message received (long message, resets new IR flag) COMMAND_SET_LED_IR = 26 # Set the LED for indicating IR messages COMMAND_GET_LED_IR = 27 # Get if the LED is being used to indicate IR messages COMMAND_GET_ANALOG_1 = 28 # Get the analog reading from port #1, pin 2 COMMAND_GET_ANALOG_2 = 29 # Get the analog reading from port #2, pin 4 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 COMMAND_ANALOG_MAX = 0x3FF # Maximum value for analog readings IR_MAX_BYTES = I2C_LONG_LEN - 2 def ScanForZeroBorg(busNumber = 1): """ ScanForZeroBorg([busNumber]) Scans the I²C bus for a ZeroBorg 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 = ZeroBorg() for address in range(0x03, 0x78, 1): try: bus.InitBusOnly(busNumber, address) i2cRecv = bus.RawRead(COMMAND_GET_ID, I2C_NORM_LEN) if len(i2cRecv) == I2C_NORM_LEN: if i2cRecv[1] == I2C_ID_ZEROBORG: print('Found ZeroBorg at %02X' % (address)) found.append(address) else: pass else: pass except KeyboardInterrupt: raise except: pass if len(found) == 0: print('No ZeroBorg boards found, is bus #%d correct (should be 0 for Rev 1, 1 for Rev 2)' % (busNumber)) elif len(found) == 1: print('1 ZeroBorg board found') else: print('%d ZeroBorg 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 ZeroBorg 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 = ScanForZeroBorg(busNumber) if len(found) < 1: print('No ZeroBorg 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 = ZeroBorg() bus.InitBusOnly(busNumber, oldAddress) try: i2cRecv = bus.RawRead(COMMAND_GET_ID, I2C_NORM_LEN) if len(i2cRecv) == I2C_NORM_LEN: if i2cRecv[1] == I2C_ID_ZEROBORG: foundChip = True print('Found ZeroBorg at %02X' % (oldAddress)) else: foundChip = False print('Found a device at %02X, but it is not a ZeroBorg (ID %02X instead of %02X)' % (oldAddress, i2cRecv[1], I2C_ID_ZEROBORG)) else: foundChip = False print('Missing ZeroBorg at %02X' % (oldAddress)) except KeyboardInterrupt: raise except: foundChip = False print('Missing ZeroBorg at %02X' % (oldAddress)) if foundChip: bus.RawWrite(COMMAND_SET_I2C_ADD, [newAddress]) time.sleep(0.1) print('Address changed to %02X, attempting to talk with the new address' % (newAddress)) try: bus.InitBusOnly(busNumber, newAddress) i2cRecv = bus.RawRead(COMMAND_GET_ID, I2C_NORM_LEN) if len(i2cRecv) == I2C_NORM_LEN: if i2cRecv[1] == I2C_ID_ZEROBORG: foundChip = True print('Found ZeroBorg at %02X' % (newAddress)) else: foundChip = False print('Found a device at %02X, but it is not a ZeroBorg (ID %02X instead of %02X)' % (newAddress, i2cRecv[1], I2C_ID_ZEROBORG)) else: foundChip = False print('Missing ZeroBorg at %02X' % (newAddress)) except KeyboardInterrupt: raise except: foundChip = False print('Missing ZeroBorg 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 ZeroBorg class ZeroBorg: """ This module is designed to communicate with the ZeroBorg busNumber I²C bus on which the ZeroBorg 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 ZeroBorg chip to control foundChip True if the ZeroBorg 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_ZEROBORG # I²C address, override for a different address foundChip = False printFunction = None i2cWrite = None i2cRead = None def RawWrite(self, command, data): """ RawWrite(command, data) Sends a raw command on the I2C bus to the ZeroBorg Command codes can be found at the top of ZeroBorg.py, data is a list of 0 or more byte values Under most circumstances you should use the appropriate function instead of RawWrite """ rawOutput = [command] rawOutput.extend(data) rawOutput = bytes(rawOutput) self.i2cWrite.write(rawOutput) def RawRead(self, command, length, retryCount = 3): """ RawRead(command, length, [retryCount]) Reads data back from the ZeroBorg after sending a GET command Command codes can be found at the top of ZeroBorg.py, length is the number of bytes to read back The function checks that the first byte read back matches the requested command If it does not it will retry the request until retryCount is exhausted (default is 3 times) Under most circumstances you should use the appropriate function instead of RawRead """ while retryCount > 0: self.RawWrite(command, []) rawReply = self.i2cRead.read(length) reply = [] for singleByte in rawReply: reply.append(singleByte) if command == reply[0]: break else: retryCount -= 1 if retryCount > 0: return reply else: raise IOError('I2C read for command %d failed' % (command)) def InitBusOnly(self, busNumber, address): """ InitBusOnly(busNumber, address) Prepare the I2C driver for talking to a ZeroBorg on the specified bus and I2C address This call does not check the board is present or working, under most circumstances use Init() instead """ self.busNumber = busNumber self.i2cAddress = address self.i2cRead = io.open("/dev/i2c-" + str(self.busNumber), "rb", buffering = 0) fcntl.ioctl(self.i2cRead, I2C_SLAVE, self.i2cAddress) self.i2cWrite = io.open("/dev/i2c-" + str(self.busNumber), "wb", buffering = 0) fcntl.ioctl(self.i2cWrite, I2C_SLAVE, self.i2cAddress) def Print(self, message): """ Print(message) Wrapper used by the ZeroBorg 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: ZB = ZeroBorg.ZeroBorg() ZB.printFunction = ZB.NoPrint """ pass def Init(self, tryOtherBus = False): """ Init([tryOtherBus]) Prepare the I2C driver for talking to the ZeroBorg If tryOtherBus is True, this function will attempt to use the other bus if the ThunderBorg devices can not be found on the current busNumber This is only really useful for early Raspberry Pi models! """ self.Print('Loading ZeroBorg on bus %d, address %02X' % (self.busNumber, self.i2cAddress)) # Open the bus self.i2cRead = io.open("/dev/i2c-" + str(self.busNumber), "rb", buffering = 0) fcntl.ioctl(self.i2cRead, I2C_SLAVE, self.i2cAddress) self.i2cWrite = io.open("/dev/i2c-" + str(self.busNumber), "wb", buffering = 0) fcntl.ioctl(self.i2cWrite, I2C_SLAVE, self.i2cAddress) # Check for ZeroBorg try: i2cRecv = self.RawRead(COMMAND_GET_ID, I2C_NORM_LEN) if len(i2cRecv) == I2C_NORM_LEN: if i2cRecv[1] == I2C_ID_ZEROBORG: self.foundChip = True self.Print('Found ZeroBorg at %02X' % (self.i2cAddress)) else: self.foundChip = False self.Print('Found a device at %02X, but it is not a ZeroBorg (ID %02X instead of %02X)' % (self.i2cAddress, i2cRecv[1], I2C_ID_ZEROBORG)) else: self.foundChip = False self.Print('Missing ZeroBorg at %02X' % (self.i2cAddress)) except KeyboardInterrupt: raise except: self.foundChip = False self.Print('Missing ZeroBorg at %02X' % (self.i2cAddress)) # See if we are missing chips if not self.foundChip: self.Print('ZeroBorg 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 ZeroBorg is properly attached, the correct address is used, and the I2C drivers are running?') self.bus = None else: self.Print('ZeroBorg loaded on bus %d' % (self.busNumber)) 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_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.RawWrite(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.RawRead(COMMAND_GET_A, I2C_NORM_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 SetMotor2(self, power): """ SetMotor1(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_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.RawWrite(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.RawRead(COMMAND_GET_B, I2C_NORM_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 SetMotor3(self, power): """ SetMotor3(power) Sets the drive level for motor 3, from +1 to -1. e.g. SetMotor3(0) -> motor 3 is stopped SetMotor3(0.75) -> motor 3 moving forward at 75% power SetMotor3(-0.5) -> motor 3 moving reverse at 50% power SetMotor3(1) -> motor 3 moving forward at 100% power """ if power < 0: # Reverse command = COMMAND_SET_C_REV pwm = -int(PWM_MAX * power) if pwm > PWM_MAX: pwm = PWM_MAX else: # Forward / stopped command = COMMAND_SET_C_FWD pwm = int(PWM_MAX * power) if pwm > PWM_MAX: pwm = PWM_MAX try: self.RawWrite(command, [pwm]) except KeyboardInterrupt: raise except: self.Print('Failed sending motor 3 drive level!') def GetMotor3(self): """ power = GetMotor3() Gets the drive level for motor 3, from +1 to -1. e.g. 0 -> motor 3 is stopped 0.75 -> motor 3 moving forward at 75% power -0.5 -> motor 3 moving reverse at 50% power 1 -> motor 3 moving forward at 100% power """ try: i2cRecv = self.RawRead(COMMAND_GET_C, I2C_NORM_LEN) except KeyboardInterrupt: raise except: self.Print('Failed reading motor 3 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 SetMotor4(self, power): """ SetMotor4(power) Sets the drive level for motor 4, from +1 to -1. e.g. SetMotor4(0) -> motor 4 is stopped SetMotor4(0.75) -> motor 4 moving forward at 75% power SetMotor4(-0.5) -> motor 4 moving reverse at 50% power SetMotor4(1) -> motor 4 moving forward at 100% power """ if power < 0: # Reverse command = COMMAND_SET_D_REV pwm = -int(PWM_MAX * power) if pwm > PWM_MAX: pwm = PWM_MAX else: # Forward / stopped command = COMMAND_SET_D_FWD pwm = int(PWM_MAX * power) if pwm > PWM_MAX: pwm = PWM_MAX try: self.RawWrite(command, [pwm]) except KeyboardInterrupt: raise except: self.Print('Failed sending motor 4 drive level!') def GetMotor4(self): """ power = GetMotor4() Gets the drive level for motor 4, from +1 to -1. e.g. 0 -> motor 4 is stopped 0.75 -> motor 4 moving forward at 75% power -0.5 -> motor 4 moving reverse at 50% power 1 -> motor 4 moving forward at 100% power """ try: i2cRecv = self.RawRead(COMMAND_GET_D, I2C_NORM_LEN) except KeyboardInterrupt: raise except: self.Print('Failed reading motor 4 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.RawWrite(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.RawWrite(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.RawWrite(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.RawRead(COMMAND_GET_LED, I2C_NORM_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.RawWrite(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.RawRead(COMMAND_GET_EPO, I2C_NORM_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.RawWrite(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.RawRead(COMMAND_GET_EPO_IGNORE, I2C_NORM_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 HasNewIrMessage(self): """ state = HasNewIrMessage() Reads the new IR message received flag. If False there has been no messages to the IR sensor since the last read. If True there has been a new IR message which can be read using GetIrMessage(). """ try: i2cRecv = self.RawRead(COMMAND_GET_NEW_IR, I2C_NORM_LEN) except KeyboardInterrupt: raise except: self.Print('Failed reading new IR message received flag!') return if i2cRecv[1] == COMMAND_VALUE_OFF: return False else: return True def GetIrMessage(self): """ message = GetIrMessage() Reads the last IR message which has been received and clears the new IR message received flag. Returns the bytes from the remote control as a hexadecimal string, e.g. 'F75AD5AA8000' Use HasNewIrMessage() to see if there has been a new IR message since the last call. """ try: i2cRecv = self.RawRead(COMMAND_GET_LAST_IR, I2C_LONG_LEN) except KeyboardInterrupt: raise except: self.Print('Failed reading IR message!') return message = '' for i in range(IR_MAX_BYTES): message += '%02X' % (i2cRecv[1+i]) return message.rstrip('0') def SetLedIr(self, state): """ SetLedIr(state) Sets if IR messages control the state of the LED, False for no effect, True for incoming messages blink the LED """ if state: level = COMMAND_VALUE_ON else: level = COMMAND_VALUE_OFF try: self.RawWrite(COMMAND_SET_LED_IR, [level]) except KeyboardInterrupt: raise except: self.Print('Failed sending LED state!') def GetLedIr(self): """ state = GetLedIr() Reads if IR messages control the state of the LED, False for no effect, True for incoming messages blink the LED """ try: i2cRecv = self.RawRead(COMMAND_GET_LED_IR, I2C_NORM_LEN) except KeyboardInterrupt: raise except: self.Print('Failed reading LED state!') return if i2cRecv[1] == COMMAND_VALUE_OFF: return False else: return True def GetAnalog1(self): """ voltage = GetAnalog1() Reads the current analog level from port #1 (pin 2). Returns the value as a voltage based on the 3.3 V reference pin (pin 1). """ try: i2cRecv = self.RawRead(COMMAND_GET_ANALOG_1, I2C_NORM_LEN) except KeyboardInterrupt: raise except: self.Print('Failed reading analog level #1!') return raw = (i2cRecv[1] << 8) + i2cRecv[2] level = float(raw) / float(COMMAND_ANALOG_MAX) return level * 3.3 def GetAnalog2(self): """ voltage = GetAnalog2() Reads the current analog level from port #2 (pin 4). Returns the value as a voltage based on the 3.3 V reference pin (pin 1). """ try: i2cRecv = self.RawRead(COMMAND_GET_ANALOG_2, I2C_NORM_LEN) except KeyboardInterrupt: raise except: self.Print('Failed reading analog level #2!') return raw = (i2cRecv[1] << 8) + i2cRecv[2] level = float(raw) / float(COMMAND_ANALOG_MAX) return level * 3.3 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.RawWrite(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.RawRead(COMMAND_GET_FAILSAFE, I2C_NORM_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 Help(self): """ Help() Displays the names and descriptions of the various functions and settings provided """ funcList = [ZeroBorg.__dict__.get(a) for a in dir(ZeroBorg) if isinstance(ZeroBorg.__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))