I'm writing the driver for HiTechnic IRLink (translating from the leJOS driver which I know it works well).
However I get the above error when sending the command.
I verified that the i2c message is identical at the byte level to the message produced by the lejos driver, so I don't know what's going on...
File "/Users/user/nxt-python-svn/nxt/sensor/hitechnic.py", line 241, in _complete_send
self.write_value('tx', pfCommand)
File "/Users/user/nxt-python-svn/nxt/sensor/digital.py", line 152, in write_value
self._i2c_command(address, value, fmt)
File "/Users/user/nxt-python-svn/nxt/sensor/digital.py", line 104, in _i2c_command
self.brick.ls_write(self.port, msg, 0)
File "/Users/user/nxt-python-svn/nxt/brick.py", line 31, in poll
return parse_func(igram)
File "/Users/user/nxt-python-svn/nxt/direct.py", line 28, in _parse_simple
tgram.check_status()
File "/Users/user/nxt-python-svn/nxt/telegram.py", line 114, in check_status
nxt.error.check_status(self.parse_u8())
File "/Users/user/nxt-python-svn/nxt/error.py", line 85, in check_status
raise ex
nxt.error.DirProtError: Illegal size specified
class IRLink(BaseDigitalSensor):
"""Object for HiTechnic IRLink sensor for use with LEGO Power Functions IR
Receivers."""
I2C_ADDRESS = BaseDigitalSensor.I2C_ADDRESS.copy()
I2C_ADDRESS.update({
'tx_buf': (0x45, '10B'),
'tx_len': (0x4D, 'B'),
'tx_mode': (0x4E, 'B'),
'tx_trigger': (0x4F, 'B'),
'tx': (0x40, '16B'),
})
class TXMode(object):
def __init__(self, code):
self.code = code
TXMODE_PF = TXMode(0x02) # used for LEGO PowerFunctions
TXMODE_UART = TXMode(0x00) # will transmit 8 bytes at time at 2400 baud using a 38kHz carrier frequency (also used for RCX)
# PF Receiver channel selection
PF_CHANNEL_1 = 0
PF_CHANNEL_2 = 1
PF_CHANNEL_3 = 2
PF_CHANNEL_4 = 3
# PF Modes
ESC_MODE_SELECT = 0
ESC_PWM = 1
PF_MODE_EXTENDED = 0
PF_MODECOMBO_DIRECT = 1
# PF PWM motor operations for various modes
PF_PWM_FLOAT = 0
PF_PWM_FWD_1 = 1
PF_PWM_FWD_2 = 2
PF_PWM_FWD_3 = 3
PF_PWM_FWD_4 = 4
PF_PWM_FWD_5 = 5
PF_PWM_FWD_6 = 6
PF_PWM_FWD_7 = 7
PF_PWM_BRK_FLT = 8
PF_PWM_REV_7 = 9
PF_PWM_REV_6 = 10
PF_PWM_REV_5 = 11
PF_PWM_REV_4 = 12
PF_PWM_REV_3 = 13
PF_PWM_REV_2 = 14
PF_PWM_REV_1 = 15
# PF Combo Direct motor operations
PF_COMBO_DIRECT_FLOAT = 0
PF_COMBO_DIRECT_FORWARD = 1
PF_COMBO_DIRECT_BACKWARD = 2
PF_COMBO_DIRECT_BRAKE_FLOAT = 3
# PF Extended mode motor operations
PF_EXTENDED_BRAKE_FLOAT = 0
PF_EXTENDED_INC_SPEED_A = 1
PF_EXTENDED_DEC_SPEED_A = 2
PF_EXTENDED_TOGGLE_FWD_FLT_B = 4
# PF Single Mode PWM & CST motor selection
PF_SINGLE_MODE_RED_PORT = 0
PF_SINGLE_MODE_BLUE_PORT = 1
# PF PWM motor operations for various modes
PF_CST_TOGGLE_FULL_FW = 0
PF_CST_TOGGLE_DIR = 1
PF_CST_INC_PWM_NUM = 2
PF_CST_DEC_PWM_NUM = 3
PF_CST_INC_PWM = 4
PF_CST_DEC_PWM = 5
PF_CST_FULL_FW = 6
PF_CST_FULL_BW = 7
PF_CST_TOGGLE_FULL_FW_BW = 8
PF_CST_CLR_C1 = 9
PF_CST_SET_C1 = 10
PF_CST_TOGGLE_C1 = 11
PF_CST_CLR_C2 = 12
PF_CST_SET_C2 = 13
PF_CST_TOGGLE_C2 = 14
PF_CST_TOGGLE_FL_BW = 15
def __init__(self, brick, port, check_compatible=True):
super(IRLink, self).__init__(brick, port, check_compatible)
self._toggle = 0
def _uart_send(self, data):
if len(data) != 8 or any(x < 0 or x > 255 for x in data):
raise ValueError, 'data must be exactly 8 unsigned bytes'
self.write_value('tx_buf', data + [len(data), self.TXMODE_UART.code])
def _complete_send(self, pfData):
bits = []
# start bit:
bits += [True] + [False] * 7
for i in reversed(range(16)):
if ((pfData >> i) & 1) == 0:
# 0 is encoded as 100
bits += [True, False, False]
else:
# 1 is encoded as 10000
bits += [True, False, False, False, False]
# stop bit:
bits += [True]
self._toggle ^= 1
byteIndex = 0
pfCommand = [0] * 16
while bits:
for i, bitVal in enumerate(bits[0:8]):
pfCommand[byteIndex] |= int(bitVal) << (7 - i)
bits = bits[8:]
byteIndex += 1
pfCommand[13] = 13
pfCommand[14] = self.TXMODE_PF.code
pfCommand[15] = 1
print('about to send command %s' % pfCommand)
self.write_value('tx', pfCommand)
def send_combo_pwm(self, channel, opA, opB):
"""Send commands to both motors. Uses PF Combo PWM mode.
Params:
channel: the channel number (0-3)
opA: Motor A operation RED
opB: Motor B operation BLUE
"""
nibble1 = ((0 << 3) | (self.ESC_PWM << 2) | channel) & 0xF # 0 is a place holder for a (address) for future use
nibble2 = opB & 0xF
nibble3 = opA & 0xF
lrc = (0xF ^ nibble1 ^ nibble2 ^ nibble3) & 0xF
pfData = (nibble1 << 12) | (nibble2 << 8) | (nibble3 << 4) | lrc;
self._complete_send(pfData)
def send_combo_direct(self, channel, opA, opB):
"""Send commands to both motors. Uses PF Combo DIRECT mode.
Params:
channel: the channel number (0-3)
opA: Motor A operation RED
opB: Motor B operation BLUE
"""
nibble1 = ((self._toggle << 3) | (self.ESC_MODE_SELECT << 2) | channel) & 0xF
nibble2 = ((0 << 3) | self.PF_MODECOMBO_DIRECT) & 0xF # 0 is a place holder for a (address) for future use
nibble3 = (opB << 2 | opA) & 0xF
lrc = (0xF ^ nibble1 ^ nibble2 ^ nibble3) & 0xF
pfData = (nibble1 << 12) | (nibble2 << 8) | (nibble3 << 4) | lrc
self._complete_send(pfData)
def send_extended(self, channel, opA):
"""Send commands to both motors. Uses PF Extended mode.
Params:
channel: the channel number (0-3)
opA: Refer to action list
"""
nibble1 = ((self._toggle << 3) | (self.ESC_MODE_SELECT << 2) | channel) & 0xF
nibble2 = ((0 << 3) | self.PF_MODE_EXTENDED) & 0xF # 0 is a place holder for a (address) for future use
nibble3 = opA & 0xF
lrc = (0xF ^ nibble1 ^ nibble2 ^ nibble3) & 0xF
pfData = (nibble1 << 12) | (nibble2 << 8) | (nibble3 << 4) | lrc;
self._complete_send(pfData)
def send_single_mode_pwm(self, channel, opA, opB):
"""Send commands to a single motors. Uses PF Single mode PWM.
Params:
channel: the channel number (0-3)
opA: Motor selection Red/Blue port
opB: Use PF_PWM operation codes
"""
# opA = output RED or BlUE, opB command code
nibble1 = ((self._toggle << 3) | (self.ESC_MODE_SELECT << 2) | channel) & 0xF
nibble2 = ((0 << 3) | (1 << 2) | (0 << 1) | opA) & 0xF # 0 is a place holder for a (address) for future use
nibble3 = opB & 0xF
lrc = (0xF ^ nibble1 ^ nibble2 ^ nibble3) & 0xF
pfData = (nibble1 << 12) | (nibble2 << 8) | (nibble3 << 4) | lrc
self._complete_send(pfData)
def send_single_mode_cst(self, channel, opA, opB):
"""Send commands to a single port. Uses PF Single mode Clear/Set/Toggle
control PINS.
Params:
channel: the channel number (0-3)
opA: Motor selection Red/Blue port
opB: Refer to action list
"""
# opA = output RED or BlUE, opB command code
nibble1 = ((self._toggle << 3) | (self.ESC_MODE_SELECT << 2) | channel) & 0xF
nibble2 = ((0 << 3) | (1 << 2) | (1 << 1) | opA) & 0xF # 0 is a place holder for a (address) for future use
nibble3 = opB & 0xF
lrc = (0xF ^ nibble1 ^ nibble2 ^ nibble3) & 0xF
pfData = (nibble1 << 12) | (nibble2 << 8) | (nibble3 << 4) | lrc
self._complete_send(pfData)
IRLink.add_compatible_sensor(None, 'HiTechnc', 'IRLink ')