This text is documented in AsciiDoc format

Suggested viewer FireFox plugin Asciidoctor.js Live Preview

ssct logo

Scilab Serial Communication Toolbox (SSCT)

Abstract:

Scilab Serial Communication Toolbox (SSCT) is inspired by a toolbox with a similar name originally developed by Aditya Sengupta and Enrico Segre. However, this new development line is an attempt to comply / resemble MATLAB serial communication functionalities and syntax as much as possible. The toolbox adds serial port (e.g., RS-232) protocol communication to Scilab / Scicoslab platforms, which are Free, Libre, and Open Source Software (FLOSS) alternatives of MATLAB. The toolbox can be used "for sending commands to control robots, motors, or reading sensors such as GPS, laser scanners, compasses, etc." [Esposito2009]. For the moment, the toolbox only works on Windows OS, but development for Linux and macOS are under progress and hopefully will be added in the future. This toolbox is still in the early-stage prototyping phase, and functions are not compiled. To use it, just download this Gist and run the ssctmain.sci in Scilab. For ScicosLab, due to the fact that multiline commenting /* …​ */ is yet to be implemented, the AsciiDoc documentations in those tags need to be removed in advance.

A serial object structure with similar properties / attributes and functions of the one in MATLAB has been developed. There are of course, some syntax differences due to Scilab intrinsic limitations, not-implemented features, as well as extra features considered to improve upon what MathWorks has already done. Considering that Scilab doesn’t have built-in Serial Port communication, external languages, including Tcl, PowerShell, CMD / batch, bash, C, C++, have been used. Moreover, due to the lack of object-oriented programing (OOP) capability in Scilab there are major differences between this toolbox and MATLAB. Mainly the serial object has no routine / subroutine / procedure accessible through the dot . operator, as seen in the OOP languages. Furthermore, all the MATLAB functions starting with an f are changed to s to avoid confusion and conflicts with other Scilab functions, as well as possible copyright issues. For example, MATLAB’s fopen is sopen in this toolbox. The syntax has been changed in a way to be more consistent and concise.

Although there are other FOSS options like Python, Octave, Julia, Sage, Maxima, R and FreeMat, …​, Scilab stands out among these options for having the great xcos / scicos environment, which is a replica of SIMULINK visual programing. xcos / scicos can also include code blocks from other languages such as C/C++, Fortran, Scilab, and Modelica language. In addition, Scilab / ScicosLab also have a nice GUI building functionality that resembles MATLAB syntax. In general for users coming from a MATLAB background, it is one of the best alternatives.

The codes / texts copied from other sources have the original licensing if not specified. The text of this author is under Creative Commons License BY-NC-SA, and the codes are GPLv3. The beautiful logo is designed by Alex Potterson. If you are a FLOSS developer and need a logo, you may contact him.

Cc by nc sa icon
GPLv3 Logo

Introduction:

A typical serial port communication session has five steps:

  1. Find the valid, available, open, defined serial ports using the slist function

  2. Create a serial port object using the sdefine function, specifying the required properties.

  3. Opening the port using the sopen function and adjusting the properties using sconfig and sinfo function

  4. Sending or receiving data using

    1. sput and sputl for putting data to the serial port buffer and sflush for sending them out, or alternatively ssend/ssendl

    2. sget and sgetl function for taking data out of the serial port buffer

  5. Closing the port using the sclose function and cleaning up the DefinedPorts list using the sdelete function.

Alternatively one can use the sprint/sprintl and sread/sreadl functions to send and read data in one step.

Example:

s1 = sdefine(Port = "COM1", BaudRate = 4800);
sopen(s1);
sput(s1, "*IDN?");
out = sget(s1);
sclose(s1);
sdelete(s1);
clear s1;

or alternatively using the simpler syntax:

sprint(Port = "COM1", Message = "*IDN?", BaudRate = 4800);
sread("COM1"); // to be implemented

Serial Object Properties:

This code is documented in AsciiDoc format

SSCT serial object has been developed to be mostly compatible with MATLAB’s serial object properties, but when possible/needed go beyond that. and their accepted and default values. Default values have been specified with bold font.

BaudRate

Accepted values are 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000 and 256000 bits per second. This property is also limited by the MaximumBaudRate (PowerShell syntax) provided by the operating system and the underlying hardware.

*/
StandardBaudRates = [110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000, 256000]; //bits per second. 9600 is the default
/*

DataBits

Accepted values are 5, 6, 7 and 8

*/
StandardDataBits = [5, 6, 7, 8]; // 8 is the default
/*

Parity

"none", "odd", "even", "mark" and "space"

*/
StandardParities = ["none", "odd", "even", "mark" "space"]; // none is the default
/*

StopBits

1 and 2 . Please consider that MATLAB also accepts 1.5 which is not compatible with Tcl and this toolbox.

*/
StandardStopBits = [1, 2]; //1 is the default. MATLAB also accepts 1.5
/*

Terminator

Accepted values are "auto", "binary", "cr", "crlf", "lf". Please consider that this is Tcl syntax not MATLAB’s.

*/
Terminators = ["auto", "binary", "cr", "crlf", "lf", "platform"]; // this is Tcl syntax. binary is the default. MATLAB accepts 0 to 127 ASCII or  CR (carriage return), LF (linefeed),  CR/LF or LF/CR
/*

FlowControl

*/
FlowControls = ["none", "hardware", "software"]; // default is none. Tcl syntax:  rtscts (dtrdsr for Windows)  for hardware and xonxoff for software
/*

Buffering

*/
Bufferings = ["full", "line", "none"]; // this is Tcl syntax. full is default
/*

ReadAsyncMode

*/
ReadAsyncModes = ["manual", "continuous"]; //MATLAB syntax, probably the blocking option in Tcl
/*

ByteOrder

*/
ByteOrders = ["bigEndian", "littleEndian"];
/*

Functions:

This code is documented in AsciiDoc format

slist()

Lists the valid, available, open or defined serial ports / objects (similar to the seriallist of MATLAB)

Parameters:

  • Verbose: Boolean

  • PortList: One of the values (default "available")

*/
PortLists = ["all", "available", "open", "defined"];
/*

Syntax:

*/
function SerialList = slist(Verbose, PortList)
/*

Description:

The Verbose input is set to %f by default returning a vector of strings. If set to %t it will export a matrix including more elaborate details about the specified PortList. The PortList option can be "all" for showing the valid ports available or open, "available" for showing the ports are free to be opened, "open" for all the already opened ports and "defined" for showing all the ports which are already defined.

Example:

--> slist
 ans  =

!COM6  COM7  !

Source Code:

Click to see the source code
*/
WindowsPSProperties = ["DeviceID", "Description", "MaxBaudRate", "ProviderType"];
WindowsComExp = "/(COM[1-9]\d*)[^.]+?Baud.+?([1-9]\d*)[^.]+?Parity.+?(\w+)[^.]+?Data Bits.+?(\d)[^.]+?Stop Bits.+?(1|1\.5|2)[^.]+?Timeout.+?(ON|OFF)[^.]+?XON\/XOFF.+?(ON|OFF)[^.]+?CTS handshaking.+?(ON|OFF)[^.]+?DSR handshaking.+?(ON|OFF)[^.]+?DSR sensitivity.+?(ON|OFF)[^.]+?DTR circuit.+?(ON|OFF)[^.]+?RTS circuit.+?(ON|OFF)/"; // this is to extract information from cmd mode command

    global DefinedPorts;
    global OpenPorts;
    global AvailablePorts;

    if ~exists("Verbose","local") then
        Verbose = %f;
    end

    if type(Verbose) ~= 4 then
        error("The Verbose must be Boolean type %t or %f");
    end

    if ~exists("PortList","local") then
        PortList = "available";
    end

    if isempty(find(PortLists == PortList)) then
        error("Supported options for PortList are: " + strcat(PortLists, ", "));
    end

    if PortList == "defined" then
        SerialList = return(DefinedPorts(2:$)');
    end

    if PortList == "open" then
        SerialList = return(OpenPorts(2:$, 1)');
    end

    [OS, Version] = getos();

    if OS == "Windows" then
        if PortList == "all" then
            if Verbose == %f then
                //                SerialList = powershell("[System.IO.Ports.SerialPort]::getportnames()")'; //--> This solution considers the ports opned by powershell as available while there are in fact opened internally as explained [here](https://superuser.com/questions/1412117/list-all-the-serial-ports-available-and-busy-on-any-of-windows-terminals)
                if getinterpreter() == "scilab" then
                    [output, bOK] = powershell("Get-WMIObject Win32_SerialPort | Select-Object DeviceID");
                    [start, final, match, foundString] = regexp(strcat(output), '/(COM\d+)/');
                    SerialList = foundString';
                else
                    outputString = unix_g("cmd /V:ON /C '"set '"var='" & (For /F '"tokens=1 Delims= '" %s In (''WMIC Path Win32_SerialPort Get DeviceID^|FindStr '"COM[0-9]*'"'') do @IF DEFINED var (@set '"var=!var!,%s'") else (set '"var=%s'")) & @echo !var!'""); //"
                    SerialList = tokens(outputString, [",", " "])';
                    return;
                end
            else
                if getinterpreter() == "scilab" then
                    SerialList = powershell(" Get-WMIObject Win32_SerialPort | Select-Object DeviceID, Description, MaxBaudRate, ProviderType")(2:$-2);
                else
                    SerialList = unix_g("WMIC Path Win32_SerialPort Get DeviceID, Description, MaxBaudRate, ProviderType"); // cmd / batch alternative
                    return;
                end
            end
        elseif PortList == "available" then
            if Verbose == %f then
                if getinterpreter() == "scilab" then
                    [rep, stat] = dos("mode");
                    //[rep, stat] = unix_g("mode"); //unix_g, unix, host are scicoslab compatible
                    [start, final, match, foundString] = regexp(strcat(rep), '/(COM\d+)/'); // using strindex instead of regexp to make it ScosLab compatible
                    SerialList = foundString';
                else
                    outputString = unix_g("cmd /V:ON /C '"set '"var='" & (For /F '"tokens=4 Delims=: '" %s In (''Mode^|FindStr '"COM[0-9]*'"'') do @IF DEFINED var (@set '"var=!var!,%s'") else (set '"var=%s'")) & @echo !var!'""); //"
                    SerialList = tokens(outputString, [",", " "])';
                    return;
                end
            else
                if getinterpreter() == "scilab" then
                    [rep, stat] = dos("mode");
                    [start, final, match, foundString] = regexp(strcat(rep), WindowsComExp);
                    SerialList = cat(1, WindowsComProperties, foundString);
                    return;
                else
                    [rep, stat] = unix_g("mode");
                    [start, final, match, foundString] = regexp(strcat(rep), WindowsComExp);
                    SerialList = cat(1, WindowsComProperties, foundString);
                    return;
                end
            end
        end
    elseif OS == "Darwin" then
        if PortList == "all" then
            if Verbose == %f then
                SerialList = unix_g("ls /dev/{tty,cu}.* | grep tty | sed -n -E "'s/\/dev\/tty\.(.+)/\1/p"'")' //"
            end
        end
        // error("this feature has not been implemented for this operating system yet");
    else
        error("this feature has not been implemented for this operating system yet");
        //    elseif getos() == "Linux" then
        //    elseif  then
        //        [rep, stat] = unix_g("dmesg | grep tty");
        //        [rep, stat] = unix_g("ls /dev/ttyACM*"); // alternatively
        //        setserial -bg /dev/ttyS*
        //        stty
    end
endfunction
/*

This code is documented in AsciiDoc format

sdefine()

Creates a serial port object (i.e. Scilab structure) given the required properties

Syntax:

*/
function [SerialObject, Result] = sdefine(Port, BaudRate, DataBits, Parity, StopBits, FlowControl, Terminator, TimeOut, InputBufferSize, OutputBufferSize, ReadAsyncMode, ByteOrder, Tag, OverWrite)
/*

Description:

The sdefine function is an implementation of MATLAB’s serial function which creates a serial port object. At the moment the serial object is actually just a Scilab struct with no methods. The Port property (a Scilab string) must be specified but the rest of the properties are optional and the function can set them to their default values if not specified.

While creating the object it will warn the user if the object is not a valid serial object. Valid serial objects are the one recognized by the operating system, including the available and open ones. It will check if the port with the given name is already defined, returning %f if that’s the case, Unless the OverWrite input has been set to %t.

On Windows the Port input is of the form COMx where x is a positive integer.

Example:

s1 = sdefine("COM1")
s2 = sdefine(Port = 'COM2', BaudRate = 4800, Terminator = "cr")

Although it is possible to change the properties of the defined serial objects using the dot . operator

s2.BaudRate = 9600;

it is not recommended at all, and should be avoided at all costs. you may always redefine the objects setting the OverWrite input to %t with the new properties. Otherwise, reading the information through the . operator is fine.

Source Code:

Click to see the source code
*/

    if argn(1) < 1 then
        warning("The SerialObject output argument must be present otherwise it can not be opned");
        Result = %f;
        return;
    end

    SerialObject = "UndefinedSerialObject";

    global DefinedPorts;
    global AvailablePorts;

    if ~exists("Port","local") then
        error("The Port must be specified");
    end

    if typeof(Port) ~= "string" then
        warning("Port must be a string. Available ports at the moment are: " + strcat(slist(slist = "all"), ", "));
        Result = %f;
        Return;
    end



    //    AvailablePorts = slist();

    //    if isempty(find(AvailablePorts == Port)) then
    if ~isportavailable(Port) then
        warning("The port " + string(Port) + " is not available at the moment. Available ports are: " + strcat(slist(), ", "));
    end

    // scilabcos compatible
    //    if size(strindex(strcat(unix_g("mode")), Port)) ~= [1, 1] then
    //        warning("The port " + Port + " is not available at the moment.");
    //        Result = %f;
    //        return;
    //    end

    if ~exists("OverWrite", "local") then
        OverWrite = %f;
    end

    if isportdefined(Port) then
        if OverWrite == %f then
            Result = %f;
            SerialObject = "UndefinedSerialObject";
            warning("The port is already defined. To over write it set OverWrite = %t ");
            return;
        else
            sdelete(Port);
        end
    end

    //    try
    //        if ~int(Port) == Port | Port < 0 then
    //            error("Port must be a positive integer value");
    //        end
    //    catch
    //        error("Port must be a positive integer (numeric) value");
    //    end



    //    if getos() == "Windows" then
    //        Port = "COM" + string(Port);
    //    else
    //        error("this feature has not been implemented for this operating system yet");
    //    end



    if ~exists("BaudRate","local") then
        BaudRate = 9600;
    end

    if isempty(find(StandardBaudRates == BaudRate)) then
        error(string(BaudRate) + " is not a standard baud rate. Available values are: " + strcat(string(StandardBaudRates), ", "));
    end

    if ~exists("Parity","local") then
        Parity = "none";
    end

    if isempty(find(StandardParities == Parity)) then
        error(string(Parity) + " is not a standard parity. Available values are: " + strcat(StandardParities, ", "));
    end

    if ~exists("DataBits","local") then
        DataBits = 8;
    end

    if isempty(find(StandardDataBits == DataBits)) then
        error(string(DataBits) + " is not a standard data bit. Available values are: " + strcat(string(StandardDataBits), ", "));
    end

    if ~exists("StopBits","local") then
        StopBits = 1;
    end

    if isempty(find(StandardStopBits == StopBits)) then
        error(string(StopBits) + " is not a standard StopBits. Available values are: " + strcat(string(StandardStopBits), ", "));
    end

    if ~exists("FlowControl","local") then
        FlowControl = "none";
    end

    if isempty(find(FlowControls == FlowControl)) then
        error(string(FlowControl) + " is not a standard FlowControl. Available values are: " + strcat(string(FlowControls), ", "));
    end

    if ~exists("Terminator","local") then
        Terminator = "binary";
    end

    if isempty(find(Terminators == Terminator)) then
        error(string(Terminator) + " is not a standard Terminator. Available values are: " + strcat(Terminators, ", "));
    end

    if ~exists("TimeOut","local") then
        TimeOut = 10;
    end

    // check integers

    //    try
    //        if ~int(TimeOut) == TimeOut | TimeOut < 0 then
    // if ~isscalar(TimeOut) | typeof(TimeOut) ~= "constant" | TimeOut < 0 then
    if typeof(TimeOut) ~= "constant" | TimeOut < 0 then
        error("TimeOut must be a positive integer value");
    end
    //    catch
    //        error("TimeOut must be a positive integer (numeric) value");
    //    end

    if ~exists("InputBufferSize","local") then
        InputBufferSize = 512;
    end

    if modulo(InputBufferSize, 1) | InputBufferSize < 0 then
        error("InputBufferSize must be a positive integer value");
    end

    if ~exists("OutputBufferSize","local") then
        OutputBufferSize = 512;
    elseif getos() ~= "Windows" then
        error("OutputBufferSize feature is only for Wondows");
    end

    if modulo(OutputBufferSize, 1) | OutputBufferSize < 0 then
        error("OutputBufferSize must be a positive integer value");
    end


    if ~exists("ReadAsyncMode","local") then
        ReadAsyncMode = "continuous";
    end

    if isempty(find(ReadAsyncModes == ReadAsyncMode)) then
        error(string(ReadAsyncMode) + " is not a standard ReadAsyncMode. Available values are: " + strcat(ReadAsyncModes, ", "));
    end

    if ~exists("ByteOrder","local") then
        ByteOrder = "littleEndian";
    end

    if isempty(find(ByteOrders == ByteOrder)) then
        error(string(ByteOrder) + " is not a standard ByteOrder. Available values are: " + strcat(ByteOrders, ", "));
    end

    //    SerialObject = 0;

    SerialObject = struct("Port", Port, "BaudRate", BaudRate, "DataBits", DataBits, "Parity", Parity, "StopBits", StopBits, "FlowControl", FlowControl, "Terminator", Terminator, "TimeOut", TimeOut, "InputBufferSize", InputBufferSize, "OutputBufferSize", OutputBufferSize, "Type", "serial", "Name", "Serial-" + Port, "SerialName", "");
    //    SerialObject = struct("Port", Port, "BaudRate", BaudRate, "DataBits", DataBits, "Parity", Parity, "StopBits", StopBits, "FlowControl", FlowControl, "Terminator", Terminator, "Type", "serial", "Name", "Serial-" + Port);
    DefinedPorts = cat(1, DefinedPorts, Port);
endfunction
/*

This code is documented in AsciiDoc format

sopen()

Opens the serial port object to get ready for send and receive (same as MATLAB fopen)

Syntax:

*/
function [Result, ResultSerialPort, SerialName] = sopen(SerialPort, Access, Reopen)
/*

Parameters

*/
AccessArguments = ["r", "r+", "w", "w+", "a", "a+"];
/*

Description:

The SerialPort input must be a defined serial port object defined by serial function otherwise it will return %f for Result. The list of defined serial objects can be found using the slist(PortList = "defined") command. The Access input can be set to "r", "r+", "w", "w+", "a", "a+". The function will make sure the specified port is a valid, defined and available serial port. If the Reopen input has been set to %t the function will close it and reestablish the connection.

Source Code:

Click to see the source code
*/
    Result = isvalidserialobject(SerialPort, Behaviour = "warning");
    SerialName = "";
    ResultSerialPort = SerialPort;
    if Result == %f then
        return;
    end

    //    if typeof(SerialPort) ~= "st" then
    //        error("The SerialPort input must be a SerialObject")
    //    end

    global OpenPorts;

    if ~exists("Access","local") then
        Access = "r+";
    end

    if isempty(find(AccessArguments == Access)) then
        error(string(Access) + " is not a valid access argument. Available values are: " + strcat(AccessArguments, ", "));
    end

    if typeof(SerialPort) == "st" then
        Port = SerialPort.Port;
        //    elseif typeof(SerialPort) == "string" then
    else
        error("This feature is not implemented yet. The sopen SerialPort must be a serial port structure made through serial function.");
        //        Port = SerialPort;
    end

    //    try

    if ~exists("Reopen","local") then
        Reopen = %f;
    end

    if isportopen(SerialPort)  then
        if Reopen == %f then
            warning("The port " + Port + " is already open");
            SerialName = getserialname(Port);
            ResultSerialPort.SerialName = SerialName;
            Result = %f;
            return;
        else
            sclose(SerialPort);
            [Result, SerialName, ResultSerialPort]= sopen(SerialPort, Access);
            return;
        end
    end



    if ~isportdefined(SerialPort) then
        //            warning("The port is not defined. I will attemt to make one with the default properties");
        //            SerialPort = serial(Port);
        warning("The port " + Port + " is not defined. Defined Ports are: " + strcat(DefinedPorts, ", "));
        Result = %f;
        return;
    end



    if ~isportavailable(SerialPort) then
        warning("The port " + string(Port) + " is not available. Available ports are: " + strcat(slist(), ", "));
        Result = %f;
        return;
    end

    Digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];

    if getos() == "Windows" then
        // if strtod(part(Port, $)) < 10 then
        // if size(str2code(part(Port, 4:length(Port)))) == [1, 1] then
        if ~isempty(find(part(Port, 4:length(Port)) == Digits)) then
            SerialName = TCL_EvalStr("set " + "Serial_" + Port +" [open " + Port + " " + Access + "]");
        else
            SerialName = TCL_EvalStr("set " + "Serial_" + Port +" [open " + "\\\\.\\" + Port + " " + Access + "]");
        end
    else
        error("this feature has not been implemented for this operating system yet");
    end



    //        SerialName = TCL_GetVar("Serial-" + Port);
    //        SerialPort.SerialName = SerialName;
    ResultSerialPort.SerialName = SerialName;
    OpenPorts = cat(1, OpenPorts, [Port, SerialName]);
    ////        SerialPort.SerialName = SerialName;
    Result(1) = TCL_EvalStr("fconfigure " + SerialName + " -mode " + string(SerialPort.BaudRate) + "," + part(SerialPort.Parity, 1) + "," + string(SerialPort.DataBits) + "," + string(SerialPort.StopBits)) == "";


    Result(2) = TCL_EvalStr("fconfigure " + SerialName + " -translation " + SerialPort.Terminator) == "";
    Result(3) = TCL_EvalStr("fconfigure " + SerialName + " -blocking 0") == ""; // this one has to do with sync and async I think
    Result(4) = TCL_EvalStr("fconfigure " + SerialName + " -timeout " + string(SerialPort.TimeOut * 100)) == "";

    if getos() == "Windows" then
        Result(5) = TCL_EvalStr("fconfigure " + SerialName + " -sysbuffer " + "{" + string(SerialPort.InputBufferSize) + " " + string(SerialPort.OutputBufferSize) + "}") == "";
    else
        Result(5) = TCL_EvalStr("fconfigure " + SerialName + " -sysbuffer " + string(SerialPort.InputBufferSize)) == "";
    end




    select SerialPort.FlowControl
    case "hardware" then
        if getos() == "Windows" then
            handshake = "dtrdsr";
        else
            handshake = "rtscts";
        end
    case "software" then
        handshake = "xonxoff";
    else
        handshake = "none";
    end
    Result(6) = TCL_EvalStr("fconfigure " + SerialName + " -handshake " + handshake) == "";

    //    catch
    //        error("Failed opening port. The SerialPort input must be a SerialObject");
    ////        sclose(SerialPort);
    //        TCL_EvalStr("set closeresult [catch {close " + SerialName + "}]");
    //    end
    Result = Result(1) & Result(2) & Result(3) & Result(4) & Result(5) & Result(6);
    if Result == %f then
        sclose(Port);
        wanting("Opening was not sucessful!")
        return;
    end
endfunction
/*

sprint()

writes text to the serial devoice (similar to the MATLAB fprintf)

Syntax:

<Result> = sprint(<SerialPort => SerialPort, <Message => Message, <Mode = "syn"/"async">)

Description:

The SerialPort is a serial port object defined by the serial function or a string of a valid serial port such as COM1. The Message is a Scilab string which can be a formatted text created by Scilab’s msprintf function or a SCPI / SCI command known to the serial device.

Example:

sprint(s1, 'RS232?');
sprint(s1,'*IDN?');
sprint(s1, msprintf('%s','RS232?'));
sprint(s1, msprintf('ch:%d scale:%d', 1, 20e-3), 'sync');

sinfo()

Check the input and output buffer for available bytes and the status of the port

Syntax:

[InutBytesAvailable, <OutputBytesAvailable>, <Status>, <Result>] = sinfo(SerialPort)

Tutorials:

Example 01:

In this example (inspired by this tutorial) you will learn how to communicate with an Arduino microcontroller (MCU) expansion board and a Windows PC, through serial port. By the end of this example you will be able to read the value of a potentiometer, while this can be also expanded to reading any other sensors as well. To follow along the example you require the hardware including an Arduino Uno MCU development board, a potentiometer, wiring and breadboard.

ex01 pic01
Figure 1. An Ardunio Uno board with Potentiometer connected to port A0. (Image courtesy of arduino.cc)

Alternatively you can use SimulIDE to simulate the hardware and the Null-modem emulator (com0com) one Windows, to stablish a pair of virtual serial ports.

The Arduino code is:

int potPin = A0;

long val = 0;
long val_ = 0;

void setup() {
  Serial.begin(9600);
}

void loop() {

    val = ((long) analogRead(potPin));

    if (val != val_) {
      val_ = val;
      Serial.println(val);
    }

}

find the port connected to the Arduino board by running the command:

slist(Verbose = %t, PortList = "all")

This will show you are the serial ports recognized by the operating system, including the "available" and "open"`s. Under `Description you should be able to find the Arduino serial port.

Scilab code

Click to see the source code
*/
s7 = sdefine(Port = "COM7", BaudRate = 9600, Parity = 'none', DataBits = 8, StopBits = 1, OverWrite = %t); // Assuming COM7 is the port you got from above command
sopen(SerialPort = s7, Access = 'r');



pData = [];
pTime = [];
nData = csvTextScan(part(sget(s7), 1:$-1), ',');
pData = [pData, nData(2)];
pTime = [pTime, nData(1)];
plot(pTime, pData);

while %t
    drawlater();
    nData = csvTextScan(part(sget(s7), 1:$-1), ',');
    pData = [pData, nData(2)];
    pTime = [pTime, nData(1)];
    clf();
    plot(pTime, pData);
    drawnow();
    sleep(1000);
end



sclose(s7);
sdelete(s7);

/*

SimulIDE model

Click to see the source code
<circuit type="simulide_0.1">

Arduino Uno-17:
<item itemtype="Arduino" hflip="1" rotation="0" x="-252" valLabRot="0" Mhz="16" valLabely="0" id="Arduino Uno" valLabelx="0" labelrot="0" vflip="1" labely="-21" Ser_Port="true" labelx="54" objectName="Arduino Uno-17" Program="sketch_noDelay_20190228_1/sketch_noDelay_20190228_1.ino.standard.hex" Ser_Monitor="true" Show_id="true" y="-244"/>

Potentiometer-15:
<item itemtype="Potentiometer" hflip="1" rotation="0" x="4" valLabRot="0" Show_res="true" valLabely="-42" id="Potentiometer-15" valLabelx="-17" labelrot="0" vflip="1" labely="-24" Resistance="10" labelx="0" objectName="Potentiometer-15" Value_Ohm="8380" Show_id="false" y="-252" Unit=" kO"/>

Connector-24:
<item pointList="4,-236,4,-188,-100,-188" itemtype="Connector" hflip="1" rotation="0" endpinid="Arduino Uno-17-PC0" x="4" valLabRot="0" valLabely="0" id="Connector-24" valLabelx="0" labelrot="0" vflip="1" labely="-24" labelx="0" objectName="Connector-24" startpinid="Potentiometer-15-PinM" Show_id="false" y="-236" enodeid="Circ_eNode-25"/>

Connector-26:
<item pointList="-12,-252,-12,-164,-100,-164" itemtype="Connector" hflip="1" rotation="0" endpinid="Arduino Uno-17-GND1" x="-12" valLabRot="0" valLabely="0" id="Connector-26" valLabelx="0" labelrot="0" vflip="1" labely="-24" labelx="-16" objectName="Connector-26" startpinid="Potentiometer-15-PinA" Show_id="false" y="-252" enodeid="enode-26"/>

Connector-28:
<item pointList="20,-252,20,-148,-100,-148" itemtype="Connector" hflip="1" rotation="0" endpinid="Arduino Uno-17-V5V" x="20" valLabRot="0" valLabely="0" id="Connector-28" valLabelx="0" labelrot="0" vflip="1" labely="-24" labelx="-16" objectName="Connector-28" startpinid="Potentiometer-15-PinB" Show_id="false" y="-252" enodeid="enode-28"/>

PlotterWidget-56:
<item focus="false" frameGeometry="" maximumHeight="200" childrenRegion="" windowFilePath="" autoFillBackground="false" sizePolicy="" palette="" toolTip="" accessibleName="" styleSheet="" layoutDirection="0" minimumSize="" windowIconText="" windowModified="false" rect="" statusTip="" windowOpacity="1" toolTipDuration="-1" acceptDrops="false" updatesEnabled="true" visible="false" isActiveWindow="true" objectName="PlotterWidget-56" itemtype="Plotter" maximumSize="" contextMenuPolicy="1" pos="" cursor="" font="MS Shell Dlg 2,7.8,-1,5,50,0,0,0,0,0" height="200" locale="" geometry="" minimumSizeHint="" whatsThis="" y="791" accessibleDescription="" MaxVolt="500" minimumWidth="200" inputMethodHints="0" x="0" baseSize="" maximized="false" windowIcon="" width="265" childrenRect="" normalGeometry="" MinVolt="0" enabled="true" mouseTracking="false" minimized="false" frameSize="" sizeHint="" fullScreen="false" sizeIncrement="" size="" windowTitle="" maximumWidth="1000" modal="false" focusPolicy="0" windowModality="0" minimumHeight="200"/>

SerialPortWidget-57:
<item focus="false" frameGeometry="" maximumHeight="170" childrenRegion="" windowFilePath="" autoFillBackground="false" sizePolicy="" palette="" toolTip="" accessibleName="" SettingsProp="COM6,0,3,0,0,0" styleSheet="" layoutDirection="0" minimumSize="" windowIconText="" windowModified="false" rect="" statusTip="" windowOpacity="1" toolTipDuration="-1" acceptDrops="false" updatesEnabled="true" visible="true" isActiveWindow="true" objectName="SerialPortWidget-57" itemtype="SerialPort" maximumSize="" contextMenuPolicy="1" pos="" cursor="" font="MS Shell Dlg 2,7.8,-1,5,50,0,0,0,0,0" height="170" locale="" geometry="" minimumSizeHint="" whatsThis="" y="806" accessibleDescription="" minimumWidth="0" inputMethodHints="0" x="528" baseSize="" maximized="false" windowIcon="" width="450" childrenRect="" normalGeometry="" enabled="true" mouseTracking="false" minimized="false" frameSize="" sizeHint="" fullScreen="false" sizeIncrement="" size="" windowTitle="Settings" maximumWidth="450" modal="false" focusPolicy="0" windowModality="0" minimumHeight="0"/>

</circuit>

Result:

ex01 pic02
Figure 2. Live plotting of a potentiometer from Arduino

Nomenclature / Glossary:

For those who are not familiar with serial communication, some terms used in the practice can be difficult to grasp. In this section we will try to explain those terms.

  • message: a serial message is 1 byte or 8 bits, which is able to represent between 0-255.

  • terminator: indicates how a message should end. The common terminator is a carriage return indicated as "cr" in this toolbox.

  • buffer: is like a container on the on both sides of the communication ports. The sender stores the data there before flushing them through the port and the receiver stores them there before dumping them over to the users space. The buffers have limited size which if exceeds the oldest data will be discarded. The size of buffer can be set through InputBufferSize and OutputBufferSize serial object property, which can not exceed the maximum provided by the hardware MaxInputBufferSize and MaxOutputBufferSize. If you do not flush/read the data on the sender/receiver side in time it can lead to irreversible data loss.

  • checksum: in simple terms is basically a signature attached to each message, known by the receiver, which can be used to verify that the data is not garbled.

  • polling and streaming: when requesting one single measurements from a sensor, it is called polling. If we read data from the sensor given a certain frequency it is called streaming.

  • Serial Command Interface (SCI): firstly used by Motorola in the 1970s, is basically a language known by the serial device (e.g. MCU) to do certain things upon receiving certain messages.

  • serial peripheral interface ( SPI )

  • universal asynchronous receiver/transmitter (UART)

  • USART

  • Standard Commands for Programmable Instruments (SCPI)

  • Transistor–transistor logic (TTL)

  • TTY: teletypewriter, teletype, terminal

  • Serial Port Profile (SPP)

Abbreviations:

  • EIA Electronic Industries Alliance

  • ANSI American National Standards Institute

  • TIA Telecommunications Industry Association

  • DIN Deutsches Institut für Normung

  • RS Recommended Standard

    • 232

      • DTE Data Terminal Equipment eg. IBM computer, printer, plotter etc

      • DCE Data Communication/circuit-terminating Equipment eg. modem, multiplexors, etc

    • 422, 449, 485, 530

  • ASCII: American Standard Code for Information Interchange

Pinout and Signals

Connectors used to be in the form of D-sub series or Mini-DIN

Serial and Parallel ports were found in two different forms, DB-25 and DE-9 (which is mistakenly also referred to as DB-9).

port (e.g. , DB-25, DE-9 …​),voltage (TTL 5V, True RS-232 12V), communication standards (RS-232, USB), data transmission method (parallel or serial)

DE-9 DB-25 acronym / abbreviation Name typical purpose DTE DCE

1

8

DCD

Data Carrier Detect

DCE is receiving a carrier from a remote DCE

in

out

2

3

RxD

Received Data

Carries data from DCE to DTE

in

out

3

2

TxD

Transmit Data

Carries data from DTE to DCE

out

in

4

20

DTR

Data Terminal Ready

DTE is ready to receive, initiate, or continue a call

out

in

5

7

GND

Common Ground

Zero voltage reference (signal ground)

-

-

6

6

DSR

Data Set Ready

DCE is ready to receive and send data

in

out

7

4

RTR

Ready To Receive

DTE is ready to receive data from DCE

out

in

7

4

RTS

Request To Send

flow control, DTE requests the DCE prepare to transmit data

out

in

8

5

CTS

Clear To Send

DCE is ready to accept data from the DTE

in

out

9

22

RI

Ring Indicator

DCE has detected an incoming ring signal on the telephone line

in

out

1

PG

Protective Ground

Frame / Chassis Ground

-

-

9

10

11

12

SDCD

Secondary Carrier Detect

Tone from a modem

in

out

13

SCTS

Secondary Clear To Send

in

out

14

STD

Secondary Transmitted Data

out

in

15

ST

Send Timing

16

SRD

Secondary Received Data

in

out

17

RT

receive timing

18

Loopback

19

SRTS

Secondary Request To Send

out

in

21

Loopback

23

Signal rate selection

24

TT

Transmitter Timing

25

References

  • [Esposito2009] Joel M. Esposito, Tutorial: Serial Communication in Matlab, 2009, URL