示例#1
0
    def __init__(self,
                 port,
                 baudrate=None,
                 bytesize=None,
                 parity=None,
                 stopbits=None,
                 timeout=None,
                 xonxoff=None,
                 rtscts=None,
                 dsrdtr=None,
                 input_history=None,
                 os_buffer_size=None,
                 clock=None):
        """Create a serial port input and output.

        The port argument will accept the number of the port (e.g. 0 for COM1)
        as well as a string describing the full port location ("COM1" or
        "/dev/ttyS0").

        Notes
        -----
        An input_history can be used to overcome the size limitation of the
        receive buffer of the operating system. An input_history consists of a
        misc.ByteBuffer instance.
        In order to not miss any input, the serial port has to be updated
        regularly (i.e. calling read_input() or clear() before the receive
        buffer will be full). If the receive buffer size is set correctly, a
        warning will be given, when the input_history was not updated fast
        enough.
        Importantly, the fuller the receive buffer is, the longer clearing
        and polling will take (this can be more than 1 ms!), since all the
        bytes have to be transfered to the input_history.

        Parameters
        ----------
        port : int or str
            port to use
        baudrate : int, optional
        bytesize : int, optional
        parity : str, optional
            parity:'E'=even, 'O'=odd, 'N'=none
        stopbits : int, optional
        timeout : int, optional
            the timeout for read(): -1=block
        xonxoff : int, optional
        rtscts : int, optional
        dsrdtr : int, optional
        input_history : bool, optional
            True if an input_history should be used
        os_buffer_size : int, optional
            the size of the receive input_history provided by the operating
            system in bytes
        clock : misc.Clock, optional
            an experimental clock
                            (optional)

        """

        import types
        if type(serial) is not types.ModuleType:
            message = """SerialPort can not be initialized.
The Python package 'pySerial' is not installed."""
            raise ImportError(message)

        if float(serial.VERSION) < 2.5:
            raise ImportError(
                "Expyriment {0} ".format(__version__) +
                "is not compatible with PySerial {0}.".format(serial.VERSION) +
                "\nPlease install PySerial 2.5 or higher.")

        Input.__init__(self)
        Output.__init__(self)
        if baudrate is None:
            baudrate = defaults.serialport_baudrate
        if bytesize is None:
            bytesize = defaults.serialport_bytesize
        if parity is None:
            parity = defaults.serialport_parity
        if stopbits is None:
            stopbits = defaults.serialport_stopbits
        if timeout is None:
            timeout = defaults.serialport_timeout
        if timeout == -1:
            timeout = None
        if xonxoff is None:
            xonxoff = defaults.serialport_xonxoff
        if rtscts is None:
            rtscts = defaults.serialport_rtscts
        if dsrdtr is None:
            dsrdtr = defaults.serialport_dsrdtr
        if clock is not None:
            self._clock = clock
        else:
            if expyriment._active_exp.is_initialized:
                self._clock = expyriment._active_exp.clock
            else:
                self._clock = Clock()
        if input_history is None:
            input_history = defaults.serialport_input_history
        if input_history is True:
            self._input_history = ByteBuffer(
                name="SerialPortBuffer (Port {0})".format(repr(port)),
                clock=self._clock)
        else:
            self._input_history = False
        if os_buffer_size is None:
            os_buffer_size = defaults.serialport_os_buffer_size
        self._os_buffer_size = os_buffer_size

        self._serial = serial.Serial(port, baudrate, bytesize, parity,
                                     stopbits, timeout, xonxoff, rtscts,
                                     dsrdtr)
        if not self._serial.isOpen():
            raise IOError("Could not open serial port")

        atexit.register(self.close)
示例#2
0
    def __init__(self, port, baudrate=None, bytesize=None, parity=None,
                 stopbits=None, timeout=None, xonxoff=None, rtscts=None,
                 dsrdtr=None, input_history=None, os_buffer_size=None,
                 clock=None):
        """Create a serial port input and output.

        The port argument will accept the number of the port (e.g. 0 for COM1)
        as well as a string describing the full port location ("COM1" or
        "/dev/ttyS0").

        Notes
        -----
        An input_history can be used to overcome the size limitation of the
        receive buffer of the operating system. An input_history consists of a
        misc.ByteBuffer instance.
        In order to not miss any input, the serial port has to be updated
        regularly (i.e. calling read_input() or clear() before the receive
        buffer will be full). If the receive buffer size is set correctly, a
        warning will be given, when the input_history was not updated fast
        enough.
        Importantly, the fuller the receive buffer is, the longer clearing
        and polling will take (this can be more than 1 ms!), since all the
        bytes have to be transfered to the input_history.

        Parameters
        ----------
        port : int or str
            port to use
        baudrate : int, optional
        bytesize : int, optional
        parity : str, optional
            parity:'E'=even, 'O'=odd, 'N'=none
        stopbits : int, optional
        timeout : int, optional
            the timeout for read(): -1=block
        xonxoff : int, optional
        rtscts : int, optional
        dsrdtr : int, optional
        input_history : bool, optional
            True if an input_history should be used
        os_buffer_size : int, optional
            the size of the receive input_history provided by the operating
            system in bytes
        clock : misc.Clock, optional
            an experimental clock
                            (optional)

        """

        import types
        if type(serial) is not types.ModuleType:
            message = """SerialPort can not be initialized.
The Python package 'pySerial' is not installed."""
            raise ImportError(message)

        if float(serial.VERSION) < 2.5:
            raise ImportError("Expyriment {0} ".format(__version__) +
                    "is not compatible with PySerial {0}.".format(
                        serial.VERSION) +
                      "\nPlease install PySerial 2.5 or higher.")

        Input.__init__(self)
        Output.__init__(self)
        if baudrate is None:
            baudrate = defaults.serialport_baudrate
        if bytesize is None:
            bytesize = defaults.serialport_bytesize
        if parity is None:
            parity = defaults.serialport_parity
        if stopbits is None:
            stopbits = defaults.serialport_stopbits
        if timeout is None:
            timeout = defaults.serialport_timeout
        if timeout == -1:
            timeout = None
        if xonxoff is None:
            xonxoff = defaults.serialport_xonxoff
        if rtscts is None:
            rtscts = defaults.serialport_rtscts
        if dsrdtr is None:
            dsrdtr = defaults.serialport_dsrdtr
        if clock is not None:
            self._clock = clock
        else:
            if expyriment._active_exp.is_initialized:
                self._clock = expyriment._active_exp.clock
            else:
                self._clock = Clock()
        if input_history is None:
            input_history = defaults.serialport_input_history
        if input_history is True:
            self._input_history = ByteBuffer(
                name="SerialPortBuffer (Port {0})".format(repr(port)),
                clock=self._clock)
        else:
            self._input_history = False
        if os_buffer_size is None:
            os_buffer_size = defaults.serialport_os_buffer_size
        self._os_buffer_size = os_buffer_size

        self._serial = serial.Serial(port, baudrate, bytesize, parity,
                                    stopbits, timeout, xonxoff, rtscts,
                                    dsrdtr)
        if not self._serial.isOpen():
            raise IOError("Could not open serial port")

        atexit.register(self.close)
示例#3
0
class SerialPort(Input, Output):
    """A class implementing a serial port input and output."""
    def __init__(self,
                 port,
                 baudrate=None,
                 bytesize=None,
                 parity=None,
                 stopbits=None,
                 timeout=None,
                 xonxoff=None,
                 rtscts=None,
                 dsrdtr=None,
                 input_history=None,
                 os_buffer_size=None,
                 clock=None):
        """Create a serial port input and output.

        The port argument will accept the number of the port (e.g. 0 for COM1)
        as well as a string describing the full port location ("COM1" or
        "/dev/ttyS0").

        Notes
        -----
        An input_history can be used to overcome the size limitation of the
        receive buffer of the operating system. An input_history consists of a
        misc.ByteBuffer instance.
        In order to not miss any input, the serial port has to be updated
        regularly (i.e. calling read_input() or clear() before the receive
        buffer will be full). If the receive buffer size is set correctly, a
        warning will be given, when the input_history was not updated fast
        enough.
        Importantly, the fuller the receive buffer is, the longer clearing
        and polling will take (this can be more than 1 ms!), since all the
        bytes have to be transfered to the input_history.

        Parameters
        ----------
        port : int or str
            port to use
        baudrate : int, optional
        bytesize : int, optional
        parity : str, optional
            parity:'E'=even, 'O'=odd, 'N'=none
        stopbits : int, optional
        timeout : int, optional
            the timeout for read(): -1=block
        xonxoff : int, optional
        rtscts : int, optional
        dsrdtr : int, optional
        input_history : bool, optional
            True if an input_history should be used
        os_buffer_size : int, optional
            the size of the receive input_history provided by the operating
            system in bytes
        clock : misc.Clock, optional
            an experimental clock
                            (optional)

        """

        import types
        if type(serial) is not types.ModuleType:
            message = """SerialPort can not be initialized.
The Python package 'pySerial' is not installed."""
            raise ImportError(message)

        if float(serial.VERSION) < 2.5:
            raise ImportError(
                "Expyriment {0} ".format(__version__) +
                "is not compatible with PySerial {0}.".format(serial.VERSION) +
                "\nPlease install PySerial 2.5 or higher.")

        Input.__init__(self)
        Output.__init__(self)
        if baudrate is None:
            baudrate = defaults.serialport_baudrate
        if bytesize is None:
            bytesize = defaults.serialport_bytesize
        if parity is None:
            parity = defaults.serialport_parity
        if stopbits is None:
            stopbits = defaults.serialport_stopbits
        if timeout is None:
            timeout = defaults.serialport_timeout
        if timeout == -1:
            timeout = None
        if xonxoff is None:
            xonxoff = defaults.serialport_xonxoff
        if rtscts is None:
            rtscts = defaults.serialport_rtscts
        if dsrdtr is None:
            dsrdtr = defaults.serialport_dsrdtr
        if clock is not None:
            self._clock = clock
        else:
            if expyriment._active_exp.is_initialized:
                self._clock = expyriment._active_exp.clock
            else:
                self._clock = Clock()
        if input_history is None:
            input_history = defaults.serialport_input_history
        if input_history is True:
            self._input_history = ByteBuffer(
                name="SerialPortBuffer (Port {0})".format(repr(port)),
                clock=self._clock)
        else:
            self._input_history = False
        if os_buffer_size is None:
            os_buffer_size = defaults.serialport_os_buffer_size
        self._os_buffer_size = os_buffer_size

        self._serial = serial.Serial(port, baudrate, bytesize, parity,
                                     stopbits, timeout, xonxoff, rtscts,
                                     dsrdtr)
        if not self._serial.isOpen():
            raise IOError("Could not open serial port")

        atexit.register(self.close)

    @property
    def input_history(self):
        """Getter for input_history."""

        return self._input_history

    @property
    def clock(self):
        """Getter for clock."""
        return self._clock

    @property
    def serial(self):
        """Getter for serial."""
        return self._serial

    @property
    def os_buffer_size(self):
        """Getter for os_buffer_size."""
        return self._os_buffer_size

    @property
    def baudrate(self):
        """Getter for baudrate."""

        return self._serial.baudrate

    @baudrate.setter
    def baudrate(self, value):
        """Setter for baudrate."""

        self._serial.baudrate = value

    @property
    def bytesize(self):
        """Getter for bytesize."""

        return self._serial.bytesize

    @bytesize.setter
    def bytesize(self, value):
        """Setter for bytesize."""

        self._serial.bytesize = value

    @property
    def parity(self):
        """Getter for parity."""

        return self._serial.parity

    @parity.setter
    def parity(self, value):
        """Setter for parity."""

        self._serial.parity = value

    @property
    def stopbits(self):
        """Getter for stopbits."""

        return self._serial.stopbits

    @stopbits.setter
    def stopbits(self, value):
        """Setter for stopbits."""

        self._serial.stopbits = value

    @property
    def timeout(self):
        """Getter for timeout."""

        return self._serial.timeout

    @timeout.setter
    def timeout(self, value):
        """Setter for timeout."""

        self._serial.timeout = value

    @property
    def xonxoff(self):
        """Getter for xonxoff."""

        return self._serial.xonxoff

    @xonxoff.setter
    def xonxoff(self, value):
        """Setter for xonxoff."""

        self._serial.xonxoff = value

    @property
    def rtscts(self):
        """Getter for rtscts."""

        return self._serial.rtscts

    @rtscts.setter
    def rtscts(self, value):
        """Setter for rtscts."""

        self._serial.rtscts = value

    @property
    def dsrdtr(self):
        """Getter for dsrdtr."""

        return self._serial.dsrdtr

    @dsrdtr.setter
    def dsrdtr(self, value):
        """Setter for dsrdtr."""

        self._serial.dsrdtr = value

    def close(self):
        """Close the serial port."""

        try:
            self._serial.close()
        except:
            pass

    @property
    def has_input_history(self):
        """Returns if a input_history exists or not (True / False)."""

        return (type(self._input_history) == ByteBuffer)

    def clear(self, skip_input_history=False):
        """Clear the serial port.

        Notes
        -----
        If an input_history is used, all data in the receive buffer, will be
        added to the history before clearing (via read_input()).
        Note: The copy process might take a few milliseconds. If you need a
        very fast clearing of the device buffer, you should skip copying the
        data into the input_history using the skip_input_history parameter.

        Parameters
        ----------
        skip_input_history : bool, optional
            if True available data will not be copied to the input_history.
            (default = False)

        """

        if self.has_input_history and not skip_input_history:
            self.read_input()
        else:
            self._serial.flushInput()
        if self._logging:
            expyriment._active_exp._event_file_log("SerialPort {0},cleared".\
                           format(repr(self._serial.port)), 2)

    def read_input(self):
        """Read all input from serial port.

        If a input_history is used, all received data will be added.

        Returns
        -------
        out : list of bytes

        """

        read_time = self._clock.time
        read = self._serial.read(self._serial.inWaiting())
        if len(read) > 0:
            read = map(ord, list(read))
            if self.has_input_history:
                if len(self._input_history.memory):
                    last_time = self._input_history.memory[-1][1]
                else:
                    last_time = 0  #first time
                self._input_history.add_events(read)
                if len(read) >= (self._os_buffer_size - 1) and last_time:
                    warn_message = "{0} not updated for {1} ms!".format(
                        self._input_history.name, read_time - last_time)
                    print "Warning: " + warn_message
                    expyriment._active_exp._event_file_warn(warn_message)
            if self._logging:
                expyriment._active_exp._event_file_log(
                    "SerialPort {0}, read input, {1} bytes".format(
                        repr(self._serial.port), len(read)), 2)
            return read
        return []

    def poll(self):
        """Poll the serial port.

        If a input_history is used, it will be added.

        Returns
        -------
        out : byte
             one byte only will be returned
        """

        poll_time = self._clock.time
        read = self._serial.read()
        if read != "":
            if self.has_input_history:
                last = self._input_history.get_last_event()
                self._input_history.add_event(ord(read))
                if last[1] > 0:  # if input_history is empty
                    if self._serial.inWaiting() >= (self._os_buffer_size - 1):
                        warn_message = "{0} not updated for {1} ms!".format(
                            self._input_history.name, poll_time - last[1])
                        print "Warning: " + warn_message
                        expyriment._active_exp._event_file_warn(warn_message)
            if self._logging:
                expyriment._active_exp._event_file_log(
                    "SerialPort {0},received,{1},poll".format(
                        repr(self._serial.port), ord(read)), 2)
            return ord(read)
        return None

    def read_line(self, duration=None):
        """Read a line from serial port (until newline) and return string.

        Notes
        -----
        The function is waiting for input. Use the duration parameter
        to avoid too long program blocking.

        Parameters
        ----------
        duration : int, optional
            try to read for given amount of time (default=None)

        Returns
        -------
        line : str

        See Also
        --------
        design.experiment.register_wait_callback_function

        """

        rtn_string = ""
        if duration is not None:
            timeout_time = self._clock.time + duration

        if self._logging:
            expyriment._active_exp._event_file_log(
                "SerialPort {0}, read line, start".format(
                    repr(self._serial.port)), 2)

        while True:
            rtn_callback = expyriment._active_exp._execute_wait_callback()
            if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent):
                rtn_string = rtn_callback
                break
            byte = self.poll()
            if byte:
                byte = chr(byte)
                if byte != '\n' and byte != '\r':
                    rtn_string = rtn_string + byte
                elif byte == '\n':
                    break
                elif byte == '\r':
                    pass
            elif duration is not None and self._clock.time >= timeout_time:
                break
        if self._logging:
            expyriment._active_exp._event_file_log("SerialPort {0}, read line, end"\
                                .format(repr(self._serial.port)), 2)
        return rtn_string

    @staticmethod
    def get_available_ports():
        """Return an list of strings representing the available serial ports.

        Notes
        -----
        If pyserial is not installed, 'None' will be returned.

        Returns
        -------
        arr : list
            list of strings representing the available serial ports

        """

        import types
        if type(serial) is not types.ModuleType:
            return None
        ports = []
        if platform.startswith("linux"):  #for operation systems
            dev = sorted(listdir('/dev'))
            max_length = 0
            for d in dev:
                if d.startswith("tty") and len(d) > max_length:
                    max_length = len(d)
            for length in range(7, max_length + 1):
                for p in dev:
                    if p.startswith("ttyUSB") and len(p) == length:
                        ports.append("/dev/" + p)
            for length in range(5, max_length + 1):
                for p in dev:
                    if p.startswith("ttyS") and len(p) == length:
                        ports.append("/dev/" + p)
        elif platform == "darwin":  #for MacOS
            dev = sorted(listdir('/dev'))
            max_length = 0
            for d in dev:
                if d.startswith("cu") and len(d) > max_length:
                    max_length = len(d)
            for length in range(13, max_length + 1):
                for p in dev:
                    if p.startswith("cu.usbserial") and len(p) == length:
                        ports.append("/dev/" + p)
            for length in range(5, max_length + 1):
                for p in dev:
                    if p.startswith("cuad") and len(p) == length:
                        ports.append("/dev/" + p)
        else:  #for windows, os2
            for p in range(256):
                try:
                    s = serial.Serial(p)
                    if s.isOpen():
                        ports.append(s.portstr)
                        s.close()
                except serial.SerialException:
                    pass
        return ports

    def send(self, data):
        """Send data via the serial port.

        Parameters
        ----------
        data : int
            data to be send (int)

        """

        self._serial.write(chr(data))
        if self._logging:
            expyriment._active_exp._event_file_log("SerialPort {0},sent,{1}"\
                                .format(repr(self._serial.port), data), 2)

    @staticmethod
    def _self_test(exp):
        """Test the serial port"""
        def int2bin(n, count=8):
            return "".join(
                [str((n >> y) & 1) for y in range(count - 1, -1, -1)])

        result = {}
        result["testsuite_serial_port"] = ""
        result["testsuite_serial_baudrate"] = ""
        result["testsuite_serial_parity"] = ""
        result["testsuite_serial_stopbits"] = ""
        result["testsuite_serial_success"] = "No"

        ports = expyriment.io.SerialPort.get_available_ports()
        if ports is None:
            expyriment.stimuli.TextScreen(
                "The Python package 'pySerial' is not installed!",
                "[Press RETURN to continue]").present()
            exp.keyboard.wait(expyriment.misc.constants.K_RETURN)
            return result

        elif ports == []:
            expyriment.stimuli.TextScreen(
                "No serial ports found!",
                "[Press RETURN to continue]").present()
            exp.keyboard.wait(expyriment.misc.constants.K_RETURN)
            return result

        else:
            import serial

            idx = expyriment.io.TextMenu("Select Serial Port",
                                         ports,
                                         width=200,
                                         justification=1,
                                         scroll_menu=5).get()
            comport = ports[idx]

            rates = [
                0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
                4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800,
                500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
                2500000, 3000000, 3500000, 4000000
            ]
            idx = expyriment.io.TextMenu(
                "Select Baudrate", [repr(x) for x in rates],
                width=200,
                justification=1,
                scroll_menu=2).get(preselected_item=14)
            baudrate = rates[idx]

            parities = ["N", "E", "O"]
            idx = expyriment.io.TextMenu("Select Parity",
                                         parities,
                                         width=200,
                                         justification=1,
                                         scroll_menu=2).get(preselected_item=0)
            parity = parities[idx]

            stopbits = [0, 1, 1.5, 2]
            idx = expyriment.io.TextMenu("Select Stopbits",
                                         [repr(x) for x in stopbits],
                                         width=200,
                                         justification=1,
                                         scroll_menu=2).get(preselected_item=1)
            stopbit = stopbits[idx]

            expyriment.stimuli.TextScreen("Serial Port {0}".format(comport),
                                          "").present()
            try:
                ser = expyriment.io.SerialPort(port=comport,
                                               baudrate=baudrate,
                                               parity=parity,
                                               stopbits=stopbit,
                                               input_history=True)
            except serial.SerialException:
                expyriment.stimuli.TextScreen(
                    "Could not open {0}!".format(comport),
                    "[Press RETURN to continue]").present()
                exp.keyboard.wait(expyriment.misc.constants.K_RETURN)
                result["testsuite_serial_port"] = comport
                result["testsuite_serial_baudrate"] = baudrate
                result["testsuite_serial_parity"] = parity
                result["testsuite_serial_stopbits"] = stopbit
                result["testsuite_serial_success"] = "No"
                return result
            cnt = 0
            while True:
                read = ser.read_input()
                if len(read) > 0:
                    byte = read[-1]
                    cnt = ser.input_history.get_size()
                    if byte is not None:
                        expyriment.stimuli.TextScreen(
                            "Serial Port {0}".format(comport),
                            "{0}\n {1} - {2}".format(cnt, byte,
                                                     int2bin(byte))).present()
                key = exp.keyboard.check()
                if key:
                    break

            result["testsuite_serial_port"] = comport
            result["testsuite_serial_baudrate"] = baudrate
            result["testsuite_serial_parity"] = parity
            result["testsuite_serial_stopbits"] = stopbit
            result["testsuite_serial_success"] = "Yes"
            return result
示例#4
0
class SerialPort(Input, Output):
    """A class implementing a serial port input and output."""

    def __init__(self, port, baudrate=None, bytesize=None, parity=None,
                 stopbits=None, timeout=None, xonxoff=None, rtscts=None,
                 dsrdtr=None, input_history=None, os_buffer_size=None,
                 clock=None):
        """Create a serial port input and output.

        The port argument will accept the number of the port (e.g. 0 for COM1)
        as well as a string describing the full port location ("COM1" or
        "/dev/ttyS0").

        Notes
        -----
        An input_history can be used to overcome the size limitation of the
        receive buffer of the operating system. An input_history consists of a
        misc.ByteBuffer instance.
        In order to not miss any input, the serial port has to be updated
        regularly (i.e. calling read_input() or clear() before the receive
        buffer will be full). If the receive buffer size is set correctly, a
        warning will be given, when the input_history was not updated fast
        enough.
        Importantly, the fuller the receive buffer is, the longer clearing
        and polling will take (this can be more than 1 ms!), since all the
        bytes have to be transfered to the input_history.

        Parameters
        ----------
        port : int or str
            port to use
        baudrate : int, optional
        bytesize : int, optional
        parity : str, optional
            parity:'E'=even, 'O'=odd, 'N'=none
        stopbits : int, optional
        timeout : int, optional
            the timeout for read(): -1=block
        xonxoff : int, optional
        rtscts : int, optional
        dsrdtr : int, optional
        input_history : bool, optional
            True if an input_history should be used
        os_buffer_size : int, optional
            the size of the receive input_history provided by the operating
            system in bytes
        clock : misc.Clock, optional
            an experimental clock
                            (optional)

        """

        import types
        if type(serial) is not types.ModuleType:
            message = """SerialPort can not be initialized.
The Python package 'pySerial' is not installed."""
            raise ImportError(message)

        if float(serial.VERSION) < 2.5:
            raise ImportError("Expyriment {0} ".format(__version__) +
                    "is not compatible with PySerial {0}.".format(
                        serial.VERSION) +
                      "\nPlease install PySerial 2.5 or higher.")

        Input.__init__(self)
        Output.__init__(self)
        if baudrate is None:
            baudrate = defaults.serialport_baudrate
        if bytesize is None:
            bytesize = defaults.serialport_bytesize
        if parity is None:
            parity = defaults.serialport_parity
        if stopbits is None:
            stopbits = defaults.serialport_stopbits
        if timeout is None:
            timeout = defaults.serialport_timeout
        if timeout == -1:
            timeout = None
        if xonxoff is None:
            xonxoff = defaults.serialport_xonxoff
        if rtscts is None:
            rtscts = defaults.serialport_rtscts
        if dsrdtr is None:
            dsrdtr = defaults.serialport_dsrdtr
        if clock is not None:
            self._clock = clock
        else:
            if expyriment._active_exp.is_initialized:
                self._clock = expyriment._active_exp.clock
            else:
                self._clock = Clock()
        if input_history is None:
            input_history = defaults.serialport_input_history
        if input_history is True:
            self._input_history = ByteBuffer(
                name="SerialPortBuffer (Port {0})".format(repr(port)),
                clock=self._clock)
        else:
            self._input_history = False
        if os_buffer_size is None:
            os_buffer_size = defaults.serialport_os_buffer_size
        self._os_buffer_size = os_buffer_size

        self._serial = serial.Serial(port, baudrate, bytesize, parity,
                                    stopbits, timeout, xonxoff, rtscts,
                                    dsrdtr)
        if not self._serial.isOpen():
            raise IOError("Could not open serial port")

        atexit.register(self.close)

    @property
    def input_history(self):
        """Getter for input_history."""

        return self._input_history

    @property
    def clock(self):
        """Getter for clock."""
        return self._clock

    @property
    def serial(self):
        """Getter for serial."""
        return self._serial

    @property
    def os_buffer_size(self):
        """Getter for os_buffer_size."""
        return self._os_buffer_size

    @property
    def baudrate(self):
        """Getter for baudrate."""

        return self._serial.baudrate

    @baudrate.setter
    def baudrate(self, value):
        """Setter for baudrate."""

        self._serial.baudrate = value

    @property
    def bytesize(self):
        """Getter for bytesize."""

        return self._serial.bytesize

    @bytesize.setter
    def bytesize(self, value):
        """Setter for bytesize."""

        self._serial.bytesize = value

    @property
    def parity(self):
        """Getter for parity."""

        return self._serial.parity

    @parity.setter
    def parity(self, value):
        """Setter for parity."""

        self._serial.parity = value

    @property
    def stopbits(self):
        """Getter for stopbits."""

        return self._serial.stopbits

    @stopbits.setter
    def stopbits(self, value):
        """Setter for stopbits."""

        self._serial.stopbits = value

    @property
    def timeout(self):
        """Getter for timeout."""

        return self._serial.timeout

    @timeout.setter
    def timeout(self, value):
        """Setter for timeout."""

        self._serial.timeout = value

    @property
    def xonxoff(self):
        """Getter for xonxoff."""

        return self._serial.xonxoff

    @xonxoff.setter
    def xonxoff(self, value):
        """Setter for xonxoff."""

        self._serial.xonxoff = value


    @property
    def rtscts(self):
        """Getter for rtscts."""

        return self._serial.rtscts

    @rtscts.setter
    def rtscts(self, value):
        """Setter for rtscts."""

        self._serial.rtscts = value

    @property
    def dsrdtr(self):
        """Getter for dsrdtr."""

        return self._serial.dsrdtr

    @dsrdtr.setter
    def dsrdtr(self, value):
        """Setter for dsrdtr."""

        self._serial.dsrdtr = value

    def close(self):
        """Close the serial port."""

        try:
            self._serial.close()
        except:
            pass

    @property
    def has_input_history(self):
        """Returns if a input_history exists or not (True / False)."""

        return (type(self._input_history) == ByteBuffer)

    def clear(self, skip_input_history=False):
        """Clear the serial port.

        Notes
        -----
        If an input_history is used, all data in the receive buffer, will be
        added to the history before clearing (via read_input()).
        Note: The copy process might take a few milliseconds. If you need a
        very fast clearing of the device buffer, you should skip copying the
        data into the input_history using the skip_input_history parameter.

        Parameters
        ----------
        skip_input_history : bool, optional
            if True available data will not be copied to the input_history.
            (default = False)

        """

        if self.has_input_history and not skip_input_history:
            self.read_input()
        else:
            self._serial.flushInput()
        if self._logging:
            expyriment._active_exp._event_file_log("SerialPort {0},cleared".\
                           format(repr(self._serial.port)), 2)

    def read_input(self):
        """Read all input from serial port.

        If a input_history is used, all received data will be added.

        Returns
        -------
        out : list of bytes

        """

        read_time = self._clock.time
        read = self._serial.read(self._serial.inWaiting())
        if len(read) > 0:
            read = map(ord, list(read))
            if self.has_input_history:
                if len(self._input_history.memory):
                    last_time = self._input_history.memory[-1][1]
                else:
                    last_time = 0 #first time
                self._input_history.add_events(read)
                if len(read) >= (self._os_buffer_size - 1) and last_time:
                    warn_message = "{0} not updated for {1} ms!".format(
                                        self._input_history.name,
                                        read_time - last_time)
                    print "Warning: " + warn_message
                    expyriment._active_exp._event_file_warn(warn_message)
            if self._logging:
                expyriment._active_exp._event_file_log(
                        "SerialPort {0}, read input, {1} bytes".format(
                        repr(self._serial.port), len(read)), 2)
            return read
        return []

    def poll(self):
        """Poll the serial port.

        If a input_history is used, it will be added.

        Returns
        -------
        out : byte
             one byte only will be returned
        """

        poll_time = self._clock.time
        read = self._serial.read()
        if read != "":
            if self.has_input_history:
                last = self._input_history.get_last_event()
                self._input_history.add_event(ord(read))
                if last[1] > 0: # if input_history is empty
                    if self._serial.inWaiting() >= (self._os_buffer_size - 1):
                        warn_message = "{0} not updated for {1} ms!".format(
                                            self._input_history.name,
                                            poll_time - last[1])
                        print "Warning: " + warn_message
                        expyriment._active_exp._event_file_warn(warn_message)
            if self._logging:
                expyriment._active_exp._event_file_log(
                        "SerialPort {0},received,{1},poll".format(
                        repr(self._serial.port), ord(read)), 2)
            return ord(read)
        return None

    def read_line(self, duration=None):
        """Read a line from serial port (until newline) and return string.

        Notes
        -----
        The function is waiting for input. Use the duration parameter
        to avoid too long program blocking.

        Parameters
        ----------
        duration : int, optional
            try to read for given amount of time (default=None)

        Returns
        -------
        line : str

        See Also
        --------
        design.experiment.register_wait_callback_function

        """

        rtn_string = ""
        if duration is not None:
            timeout_time = self._clock.time + duration

        if self._logging:
            expyriment._active_exp._event_file_log(
                    "SerialPort {0}, read line, start".format(
                    repr(self._serial.port)), 2)

        while True:
            rtn_callback = expyriment._active_exp._execute_wait_callback()
            if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent):
                rtn_string = rtn_callback
                break
            byte = self.poll()
            if byte:
                byte = chr(byte)
                if byte != '\n' and byte != '\r':
                    rtn_string = rtn_string + byte
                elif byte == '\n':
                    break
                elif byte == '\r':
                    pass
            elif duration is not None and self._clock.time >= timeout_time:
                break
        if self._logging:
            expyriment._active_exp._event_file_log("SerialPort {0}, read line, end"\
                                .format(repr(self._serial.port)), 2)
        return rtn_string

    @staticmethod
    def get_available_ports():
        """Return an list of strings representing the available serial ports.

        Notes
        -----
        If pyserial is not installed, 'None' will be returned.

        Returns
        -------
        arr : list
            list of strings representing the available serial ports

        """

        import types
        if type(serial) is not types.ModuleType:
            return None
        ports = []
        if platform.startswith("linux"): #for operation systems
            dev = sorted(listdir('/dev'))
            max_length = 0
            for d in dev:
                if d.startswith("tty") and len(d) > max_length:
                    max_length = len(d)
            for length in range(7, max_length + 1):
                for p in dev:
                    if p.startswith("ttyUSB") and len(p) == length:
                        ports.append("/dev/" + p)
            for length in range(5, max_length + 1):
                for p in dev:
                    if p.startswith("ttyS") and len(p) == length:
                        ports.append("/dev/" + p)
        elif platform == "darwin": #for MacOS
            dev = sorted(listdir('/dev'))
            max_length = 0
            for d in dev:
                if d.startswith("cu") and len(d) > max_length:
                    max_length = len(d)
            for length in range(13, max_length + 1):
                for p in dev:
                    if p.startswith("cu.usbserial") and len(p) == length:
                        ports.append("/dev/" + p)
            for length in range(5, max_length + 1):
                for p in dev:
                    if p.startswith("cuad") and len(p) == length:
                        ports.append("/dev/" + p)
        else: #for windows, os2
            for p in range(256):
                try:
                    s = serial.Serial(p)
                    if s.isOpen():
                        ports.append(s.portstr)
                        s.close()
                except serial.SerialException:
                    pass
        return ports

    def send(self, data):
        """Send data via the serial port.

        Parameters
        ----------
        data : int
            data to be send (int)

        """

        self._serial.write(chr(data))
        if self._logging:
            expyriment._active_exp._event_file_log("SerialPort {0},sent,{1}"\
                                .format(repr(self._serial.port), data), 2)


    @staticmethod
    def _self_test(exp):
        """Test the serial port"""
        def int2bin(n, count=8):
            return "".join([str((n >> y) & 1) for y in range(count - 1, -1, -1)])

        result = {}
        result["testsuite_serial_port"] = ""
        result["testsuite_serial_baudrate"] = ""
        result["testsuite_serial_parity"] = ""
        result["testsuite_serial_stopbits"] = ""
        result["testsuite_serial_success"] = "No"

        ports = expyriment.io.SerialPort.get_available_ports()
        if ports is None:
            expyriment.stimuli.TextScreen(
                            "The Python package 'pySerial' is not installed!",
                               "[Press RETURN to continue]").present()
            exp.keyboard.wait(expyriment.misc.constants.K_RETURN)
            return result

        elif ports == []:
            expyriment.stimuli.TextScreen("No serial ports found!",
                               "[Press RETURN to continue]").present()
            exp.keyboard.wait(expyriment.misc.constants.K_RETURN)
            return result

        else:
            import serial

            idx = expyriment.io.TextMenu("Select Serial Port", ports, width=200,
                                         justification=1, scroll_menu=5).get()
            comport = ports[idx]

            rates = [0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
                     4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800,
                     500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
                     2500000, 3000000, 3500000, 4000000]
            idx = expyriment.io.TextMenu("Select Baudrate", [repr(x) for x in rates],
                                         width=200, justification=1,
                                         scroll_menu=2).get(preselected_item=14)
            baudrate = rates[idx]

            parities = ["N", "E", "O"]
            idx = expyriment.io.TextMenu("Select Parity", parities, width=200,
                                         justification=1, scroll_menu=2).get(
                                             preselected_item=0)
            parity = parities[idx]

            stopbits = [0, 1, 1.5, 2]
            idx = expyriment.io.TextMenu("Select Stopbits", [repr(x) for x in stopbits],
                                         width=200, justification=1,
                                         scroll_menu=2).get(preselected_item=1)
            stopbit = stopbits[idx]

            expyriment.stimuli.TextScreen("Serial Port {0}".format(comport),
                                                                "").present()
            try:
                ser = expyriment.io.SerialPort(port=comport, baudrate=baudrate,
                                               parity=parity, stopbits=stopbit,
                                               input_history=True)
            except serial.SerialException:
                expyriment.stimuli.TextScreen("Could not open {0}!".format(comport),
                                   "[Press RETURN to continue]").present()
                exp.keyboard.wait(expyriment.misc.constants.K_RETURN)
                result["testsuite_serial_port"] = comport
                result["testsuite_serial_baudrate"] = baudrate
                result["testsuite_serial_parity"] = parity
                result["testsuite_serial_stopbits"] = stopbit
                result["testsuite_serial_success"] = "No"
                return result
            cnt = 0
            while True:
                read = ser.read_input()
                if len(read) > 0:
                    byte = read[-1]
                    cnt = ser.input_history.get_size()
                    if byte is not None:
                        expyriment.stimuli.TextScreen("Serial Port {0}".format(comport),
                                      "{0}\n {1} - {2}".format(cnt, byte,
                                       int2bin(byte))).present()
                key = exp.keyboard.check()
                if key:
                    break

            result["testsuite_serial_port"] = comport
            result["testsuite_serial_baudrate"] = baudrate
            result["testsuite_serial_parity"] = parity
            result["testsuite_serial_stopbits"] = stopbit
            result["testsuite_serial_success"] = "Yes"
            return result