diff --git a/ev3/ev3.py b/ev3/ev3.py index 8e21840..ae73285 100755 --- a/ev3/ev3.py +++ b/ev3/ev3.py @@ -2,47 +2,50 @@ Prerequisites: - Make sure your sdp includes an SP (serial port): - % sdptool browse local - ... - Service Name: Serial Port - Service Description: COM Port + Make sure your sdp includes an SP (serial port): + % sdptool browse local + ... + Service Name: Serial Port + Service Description: COM Port - If not: - % sdptool add SP + If not: + % sdptool add SP - Then: - % hcitool scan - ... - XX:XX:XX:XX:XX:XX EV3 + Then: + % hcitool scan + ... + XX:XX:XX:XX:XX:XX EV3 - % sudo rfcomm bind /dev/rfcomm0 XX:XX:XX:XX:XX:XX + % sudo rfcomm bind /dev/rfcomm0 XX:XX:XX:XX:XX:XX - Now /dev/rfcomm0 can be opened and closed like a normal serial port. - The opposite action is: - % sudo rfcomm release /dev/rfcomm0 + Now /dev/rfcomm0 can be opened and closed like a normal serial port. + The opposite action is: + % sudo rfcomm release /dev/rfcomm0 - EXAMPLE USAGE: - from ev3 import * + EXAMPLE USAGE: + from ev3 import * - with ev3.EV3() as brick: - # Create DirectCommand objects and add commands to them. - cmd = direct_command.DirectCommand() - cmd.add_ui_draw_update() - cmd.send(brick) + with ev3.EV3() as brick: + # Create DirectCommand objects and add commands to them. + cmd = direct_command.DirectCommand() + cmd.add_ui_draw_update() + cmd.send(brick) - # Call single DirectCommand functions without creating DirectCommand - # objects each time. - brick.output_stop(direct_command.OutputPort.PORT_C, - direct_command.StopType.BRAKE) + # Call single DirectCommand functions without creating DirectCommand + # objects each time. + brick.output_stop(direct_command.OutputPort.PORT_C, + direct_command.StopType.BRAKE) - # Call single system_command functions. - brick.write_mailbox('foo', (0,1,2,3,4,5,6,7,8,9,0)) + # Call single system_command functions. + brick.write_mailbox('foo', (0,1,2,3,4,5,6,7,8,9,0)) """ +import sys import serial +import socket +import array import message import system_command @@ -50,139 +53,176 @@ import direct_command class KnownPaths(object): - """These are the default directories on the brick. All paths are - relative to 'lms2012/sys' by default. + """These are the default directories on the brick. All paths are + relative to 'lms2012/sys' by default. - """ - DEFAULT_PATH = "." # lms2012/sys - PROJECTS_PATH = "../prjs" # lms2012/prjs - APPS_PATH = "../apps" # lms2012/apps - TOOLS_PATH = "../tools" # lms2012/tools - SOURCE_PATH = "../source" # lms2012/source + """ + DEFAULT_PATH = "." # lms2012/sys + PROJECTS_PATH = "../prjs" # lms2012/prjs + APPS_PATH = "../apps" # lms2012/apps + TOOLS_PATH = "../tools" # lms2012/tools + SOURCE_PATH = "../source" # lms2012/source class EV3Error(Exception): - """Subclass for reporting errrors.""" - pass + """Subclass for reporting errrors.""" + pass +class Port(object): + RFCOMM_BAUDRATE = 115200 + + def __init__(self, port_str): + self._port_str = port_str + self.sock = None + self.mode = 'Unknown' + if (self.sock is None): + try: + addr = self._port_str.split(':') + _ = socket.inet_aton(addr[0]) + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + self.sock.connect((addr[0], int(addr[1]))) + self.mode = "TCP" + + except Exception as e: + self.sock = serial.Serial(port=self._port_str, + baudrate=self.RFCOMM_BAUDRATE, + bytesize=serial.EIGHTBITS, + parity=serial.PARITY_NONE, + stopbits=serial.STOPBITS_ONE, + timeout=None, + xonxoff=False, + rtscts=False, + writeTimeout=None, + dsrdtr=False, + interCharTimeout=None) + + self.mode = 'Serial' + + def read(self, size, timeout=1.0): + if self.mode == 'Serial': + return self.sock.read(size) + elif self.mode == 'TCP': + return self.sock.recv(size) + + def write(self, data): + if self.mode == 'Serial': + return self.sock.write(data) + elif self.mode == 'TCP': + byte = array.array('B', data).tostring() + return self.sock.send(byte) + + def __del__(self): + if (self.sock is not None): + self.sock.close() + self.sock = None + class EV3(object): - """""" - DEFAULT_RFCOMM_PORT = '/dev/rfcomm0' - RFCOMM_BAUDRATE = 115200 + """""" + DEFAULT_RFCOMM_PORT = '/dev/rfcomm0' + + def __init__(self, port_str=DEFAULT_RFCOMM_PORT): + """Creates a new object but doesn't open the port.""" + self._port_str = port_str + self._port = None - def __init__(self, port_str=DEFAULT_RFCOMM_PORT): - """Creates a new object but doesn't open the port.""" - self._port_str = port_str - self._port = None + def open(self): + """Opens the object's serial port.""" + if (self._port is None): + self._port = Port(self._port_str) + + def close(self): + """Closes the object's serial port.""" + if (self._port is not None): + del self._port + self._port = None - def open(self): - """Opens the object's serial port.""" - if (self._port is None): - self._port = serial.Serial(port=self._port_str, - baudrate=self.RFCOMM_BAUDRATE, - bytesize=serial.EIGHTBITS, - parity=serial.PARITY_NONE, - stopbits=serial.STOPBITS_ONE, - timeout=None, - xonxoff=False, - rtscts=False, - writeTimeout=None, - dsrdtr=False, - interCharTimeout=None) + def send_message(self, msg, message_counter=0x1234): + """Allows for sending raw messages to the EV3. The msg parameter should + be an array of byte values. The msg parameter should not include the + length/message_counter header. Raises an EV3Error if the specified + message is a type that expects a reply. - def close(self): - """Closes the object's serial port.""" - if (self._port is not None): - self._port.close() - self._port = None + """ + try: + message.send_message_no_reply(self._port, msg, message_counter) + except message.MessageError as ex: + raise EV3Error(ex.message) - def send_message(self, msg, message_counter=0x1234): - """Allows for sending raw messages to the EV3. The msg parameter should - be an array of byte values. The msg parameter should not include the - length/message_counter header. Raises an EV3Error if the specified - message is a type that expects a reply. + def send_message_for_reply(self, msg, message_counter=0x1234): + """Allows for sending raw messages to the EV3. The msg parameter should + be an array of byte values. The msg parameter should not include the + length/message_counter header. Raises an EV3Error if the specified + message is a type that doesn't expect a reply. - """ - try: - message.send_message_no_reply(self._port, msg, message_counter) - except message.MessageError as ex: - raise EV3Error(ex.message) + """ + try: + return message.send_message_for_reply(self._port, + msg, + message_counter) + except message.MessageError as ex: + raise EV3Error(ex.message) - def send_message_for_reply(self, msg, message_counter=0x1234): - """Allows for sending raw messages to the EV3. The msg parameter should - be an array of byte values. The msg parameter should not include the - length/message_counter header. Raises an EV3Error if the specified - message is a type that doesn't expect a reply. + def __dir__(self): + """Add in functions from the system_command module as well as methods + from the DirectCommand class because they can be called directly on an + EV3 object. - """ - try: - return message.send_message_for_reply(self._port, - msg, - message_counter) - except message.MessageError as ex: - raise EV3Error(ex.message) + """ + result = dir(type(self)) + result += list(self.__dict__) + result += [s for s in list(system_command.__dict__) + if not s.startswith('_')] + result += [s[4:] for s in list(direct_command.DirectCommand.__dict__) + if s.startswith('add_')] + return sorted(set(result)) - def __dir__(self): - """Add in functions from the system_command module as well as methods - from the DirectCommand class because they can be called directly on an - EV3 object. + def __getattr__(self, name): + """A little bit of magic is used in order to make it easier to work with + EV3 objects. - """ - result = dir(type(self)) - result += list(self.__dict__) - result += [s for s in list(system_command.__dict__) - if not s.startswith('_')] - result += [s[4:] for s in list(direct_command.DirectCommand.__dict__) - if s.startswith('add_')] - return sorted(set(result)) + """ + if (hasattr(system_command, name)): + # This allows functions from the system_command module to be called + # from an EV3 object i.e. ev3.list_files(KnownPaths.PROJECTS_PATH). + def execute_sc(*args): + """This is just a wrapper around an individual function from the + system_command module. See the system_command module for more + information. + + """ + getattr(system_command, name)(self, *args) + + return execute_sc + + # This allows single functions from the DirectCommand class to be called + # from an EV3 object i.e. ev3.ui_draw_update(). + dc_name = ('add_' + name) + if (hasattr(direct_command.DirectCommand, dc_name)): + def execute_dc(*args): + """This is just a wrapper around an individual DirectCommand + method. See the DirectCommand class for more information. + + """ + dc = direct_command.DirectCommand() + getattr(dc, dc_name)(*args) + return dc.send(self) + + return execute_dc + + return Object.__getattr__(self, name) - def __getattr__(self, name): - """A little bit of magic is used in order to make it easier to work with - EV3 objects. - - """ - if (hasattr(system_command, name)): - # This allows functions from the system_command module to be called - # from an EV3 object i.e. ev3.list_files(KnownPaths.PROJECTS_PATH). - def execute_sc(*args): - """This is just a wrapper around an individual function from the - system_command module. See the system_command module for more - information. - - """ - getattr(system_command, name)(self, *args) - - return execute_sc - - # This allows single functions from the DirectCommand class to be called - # from an EV3 object i.e. ev3.ui_draw_update(). - dc_name = ('add_' + name) - if (hasattr(direct_command.DirectCommand, dc_name)): - def execute_dc(*args): - """This is just a wrapper around an individual DirectCommand - method. See the DirectCommand class for more information. - - """ - dc = direct_command.DirectCommand() - getattr(dc, dc_name)(*args) - return dc.send(self) - - return execute_dc - - return Object.__getattr__(self, name) + def __enter__(self): + self.open() + return self - def __enter__(self): - self.open() - return self - - - def __exit__(self, type, value, traceback): - self.close() + def __exit__(self, type, value, traceback): + self.close() diff --git a/ev3/message.py b/ev3/message.py index bf58892..3a633b7 100755 --- a/ev3/message.py +++ b/ev3/message.py @@ -159,7 +159,7 @@ def append_str(byte_list, str_value): def _read_bytes(port, num_bytes): - return [ord(i) for i in port.read(num_bytes)] + return port.read(num_bytes) def _write_bytes(port, byte_seq):