class:: SerialPort
summary:: serial port interface
categories:: External Control

This class provides basic support for serial port communication. Ports are opened with link::#*new:: and closed with link::#-close::.

Each SerialPort object uses an 8KB internal buffer and reads data as soon as it is available. If the data is not read out of the buffer and the buffer fills up, incoming bytes will be dropped. Use link::#-rxErrors:: to get a count of the number of bytes dropped.

Since it is constantly polling the port for available data, a SerialPort object knows almost immediately when the port has been lost. When this happens, it will call the link::#-doneAction:: callback and mark itself as closed.

ClassMethods::

private::initClass

method::new
Creates and opens the port. Throws if creation fails; this may be because the port does not exist,
the port could not be opened, or the settings were invalid.

argument::port
A link::Classes/String:: representing the port to be opened. (An link::Classes/Integer:: index into link::#*devices:: is allowed, but this is deprecated.)

argument::baudrate
Integer baud rate, typically in the range code::[4800..230400]::.

argument::databits
Bits per character. Typically 8, but can be any integer.

argument::stopbit
A link::Classes/Boolean:: indicating whether to use two stop bits (code::true::) or one stop bit
(code::false::).

argument::parity
Whether the port uses even, odd, or no parity. Pass code::'even'::, code::'odd'::, or code::nil::
 (for none).

argument::crtscts
A link::Classes/Boolean:: indicating whether to use hardware flow control (RTS/CTS signals).

argument::xonxoff
A link::Classes/Boolean:: indicating whether to use software flow control (XON/XOFF signals).

argument::exclusive
A link::Classes/Boolean:: indicating whether to open the device exclusively. This option is not
implemented on Windows.

discussion::
code::crtscts:: and code::xonxoff:: cannot both be true; code::*new:: will throw an error if both
are set.

method::devices
Retrieve an array of available devices represented as link::Classes/String##Strings::. On macOS and Linux, this list is obtained using a number of regular expression rules on files in the code::/dev/:: directory. On Windows, this is obtained using a registry key. The matching rules are designed to be identical to that of the Arduino IDE.

For backward compatibility, if code::SerialPort.devicePattern:: is not nil, code::SerialPort.devicePattern.pathMatch:: is returned instead of the default behavior.

code::
SerialPort.devices;
::

method::listDevices
Prints the list of available devices, one per line. Shorthand for
code::SerialPort.devices.do(_.postln)::.
code::
SerialPort.listDevices;
::

method::devicePattern
If set to a non-nil value, code::SerialPort.devicePattern:: instead returns code::SerialPort.devicePattern.patchMatch::. That is, the value of this class variable is used as a file glob.

This is a legacy feature and no longer recommended. File globbing alone is not powerful enough to capture a general set of possible serial port paths, and this level of customization was not necessary for code::SerialPort.devices::. If you need to refine the results returned by code::SerialPort.devices::, it is better to do your own matching or filtering.

method::closeAll
Calls link::#-close:: on all ports.

method::cleanupAll
Deprecated; use link::#*closeAll:: instead.

InstanceMethods::

private::prInit, prOpen, prClose, primCleanup, prCleanup, prPut, prDataAvailable, prDoneAction

method::isOpen
Whether this object represents a valid serial port connection.

method::next
Read a byte from the device. Non-blocking read.

method::read
Read a byte from the device. Blocking read.

method::rxErrors
RX (receive) errors since last query. An RX error occurs when the internal buffer is completely
full. This count is reset to 0 every time this method is called.

method::put
Write a byte to the device. Always blocks.

argument::byte
An link::Classes/Integer:: or link::Classes/Char::. Only values in the range from 0 to
code::2**databits - 1:: may be written. If a value out of that range is passed to code::put::, only
the lowest bits will be used.

argument::timeout
Unused, deprecated.

returns::
A link::Classes/Boolean:: indicating whether the write was successful.

method::putAll
Write multiple bytes to the device.

argument::bytes
Collection may be link::Classes/Int8Array:: or link::Classes/String::.

argument::timeout
Unused, deprecated.

method::doneAction
A link::Classes/Function:: which will be evaluated if the port gets closed (maybe unexpectedly so,
due to hardware failure or accidental disconnection). This allows you to for example to make a
backup solution and activate it (like using fake input data for your algorithm, or trying to reopen
the device). By default it will post a message reading "SerialPort X was closed".

method::close
Closes the port.

Examples::

code::
(
p = SerialPort(
	"/dev/tty.usbserial-181",
	baudrate: 9600,
	crtscts: true);
)

// read a byte from the device

p.next; // doesn't block
fork { p.read.postln }; // may suspend thisThread - should be called within a routine

// write a byte to the device

fork {p.put(42) }; // may suspend thisThread - should be called within a routine

// write multiple bytes to the device

fork { p.putAll("whaddayawant") };
fork { p.putAll(Int8Array[13, 10]) };

p.doneAction = { "my serial port got closed".postln; }

p.close; // close the port

SerialPort.closeAll; // close all ports
::

subsection::Arduino write example

First load the sketch Examples/Communication/Dimmer. See
link::http://www.arduino.cc/en/Tutorial/Dimmer::.

note::
Always make sure the serial monitor is closed in the Arduino application before opening the port in SuperCollider.
::

code::
(
p = SerialPort(
	"/dev/tty.usbserial-A800crTT",	//edit to match your port. SerialPort.listDevices
	baudrate: 9600,	//check that baudrate is the same as in arduino sketch
	crtscts: true);
)

//send serial data - slow pulsating
(
r = Routine({
	inf.do{|i|
		p.put(i.fold(0, 100).linexp(0, 100, 1, 255).asInteger.postln);
		0.02.wait;
	};
}).play;
)

r.stop;
p.close;
::

subsection::Arduino read example

First load the sketch Examples/Communication/Graph. See
link::http://www.arduino.cc/en/Tutorial/Graph::.

note::
Always make sure the serial monitor is closed in the Arduino application before opening the port in SuperCollider.
::

code::
(
p = SerialPort(
	"/dev/tty.usbserial-A800crTT",	//edit to match your port. SerialPort.listDevices
	baudrate: 9600,	//check that baudrate is the same as in arduino sketch
	crtscts: true);
)

//read 10bit serial data sent from Arduino's Serial.println
(
r= Routine({
	var byte, str, res;
	99999.do{|i|
		if(p.read==10, {
			str = "";
			while({byte = p.read; byte !=13 }, {
				str= str++byte.asAscii;
			});
			res= str.asInteger;
			("read value:"+res).postln;
		});
	};
}).play;
)

r.stop;
p.close;
::
