ScilabSerialLib (SiSeLi) Mini Documentation, V0.7 =================================================== Serial Port library for Scilab (Windoze). (c)ASkr, 10/2010 .. 04/2014 www.askrprojects.net 1.0 INTRODUCTION A serial port library for Scilab and Windoze. FEATURES: - no installation required; Just copy SiSeLi into your Scilab project directory. - Supports all kinds of serial ports, including USB, Bluetooth or virtual connections. - baud rates up to 3MBit/s - unlimited amount of simultaneous connections - supports DLE sequenced packet mechanism - it rocks - ... 1.1 FEATURES See Chapter 3 for a complete function reference. Scroll down to the end to see a list of changes or new features. 1.2 REQUIREMENTS/NOTES SiSeLi requires at least Scilab version 5.3+ and comes in 32 and 64 bit variants. Beside Windows and a running Scilab installation, nothing else is required. Is it stable? Yes. Usually, Scilab crashes before SiSeli does ;) NOTICE: All data transfer Scilab <-> SiSeLi is done with pointer arithmetic. The only way of securing memory integrity, would be adding more stuff to the Scilab wrapper functions (Scilab's own function definitions), at the cost of speed, so I skipped this. Especially the "slSendArray(<handle>, <array>, <size>)" function (and maybe more in the future, see "Packet Mode"), relies on what you told it to do. If you assign a <size>, exceeding the limit of <array>, a memory exception will occur... Be careful !-) Can it be used with USB or Bluetooth virtual COM ports? Yes, although some extra care may be required. Sometimes, Windows "reserves" a COM port, although the device is not attached. In this case, you'll send all the data to nowhere and receive from anywhere. Basically this is not a problem until you use a function or mechanism that blocks until a byte is received. At this moment, Scilab will hang... But all the other time (you attached the device, the driver is loaded and you managed to get the right COM port number...) it will cause no (known) problems... I tested it with several FTDIs, several off-the-shelf USB <-> RS-232 converters, a few mobile phones and a pbLualized Lego NXT. 1.3 DEVELOPMENT ENVIRONMENT The source code and additional information about how to build your own SiSeLi DLL, can be found at Github: https://github.com/FMMT666/SiSeLi 1.4 FUTURE DEVELOPMENT, TODO LIST: - docs in Markdown notation - ... 1.5 FILES IN THE DISTRIBUTION DOC . slReadme.txt -> what you're reading, right now . ... SISELI . siseli_x86.dll -> ScilabSerialLib, 32 bit version . siseli_x64.dll -> ScilabSerialLib, 64 bit version . LoopDemo.sce -> example usage . PacketTest.sce -> example usage for sending/receiving packets . slLoadLib.sci -> a script to load the library and all necessary wrapper functions README.md -> coarse overview and release notes README.html -> same in HTML format copyright.txt -> copyright notice 2.0 EXAMPLE USAGE For an in-depth explanation of (all) the SiSeLi Scilab-wrapper functions, see chapter 3. You don't need to install anything. Just copy the two DLLs and "slLoadLib.sci" to your Scilab working directory and change Scilab's working path to there. That's all... - Load the libary: exec("slLoadLib.sci") If any error occurs, Scilab script execution is aborted via "abort". There is no return variable available, but success or failure can be determined by the message that gets print out on the console. - Create a handle by mounting a port. A handle can be regarded as a reference number of an interface. SiSeLi supports an unlimited amount of interfaces. port1 = slMount() NOTICE: As of V0.7 (04/2014), slMount() does not require an argument (handle) any more. Just call it and it will return the handle. For compatibility reasons you still can call it with a parameter, but this is not recommended. Remember that number in "port1". From now on, every function call of the SiSeLi DLL wrapper functions require this number as an argument. Calling "slMount()" is once required before doing anything else. It internally creates all necessary objects an dependencies. If everything went fine, a number >0 is returned. In case of an error, 0 is returned. - Check if a serial port is available (COM x): ret = slCheck(port1,5) The first number is the handle, the second one specifies the number of the COM port. If COM5 is availabe and free, the function returns "1", otherwise "0" is returned. - Set COMM parameters: ret = slConfig(port1,19200,8,0,1) On handle "port1" (first arg), use "19200" baud, "8" data bits, "0" no parity and "1" stop bit. If everything went right, "1" is returned, "0" otherwise. Notice that there is no COM port argument! - Open port: ret = slOpen(port1,5) On handle "port1", open "COM5". If successful, guess what, a "1" is returned ("0" on error). From now on, your serial interface is ready to send and receive data. - Sending a byte/character: ret = slSendByte(port1,65) ret = slSendByte(port1,ascii('A')) On handle "port1", both functions send an "A" out to the port ("ascii('A')" returns 65 too). Notice that this function is not limited to ASCII characters. Every value 0..255 is supported. As from here on, I skip commenting the value of the return arguments... I guess you got the clue ;) - Sending an array (1D/2D): ret = slSendArray(port1,a,length(a)); ret = slSendArray(port1,a',length(a)); ret = slSendArray(port1,a(1,:),size(a,2)); ret = slSendArray(port1,a(:,1),size(a,1)); Assuming that "a" is an integer array of size(n,m), it can be sent as written above. First argument "port1" is the handle, the second "a" an array (even an array of size [1,1]), the third and last one is the size of the array. NOTICE: It is extremely important that the given size DOES NOT EXCEED the length of the array (smaller values are OK). Otherwise memory exceptions will occur. - Checking bytes in the (receiving) buffer: bytes = slCount(port1) This function returns the number of bytes available in the receive buffer. Internally, a thread receives and stores all incoming data bytes. If you (or Scilab) do not poll them fast enough and empty the buffer (see next function(s)), an overflow might occur. On overflow, slCount(<handle>) reports a "-1". The overflow can only be reset by calling slFlush(<handle>). - Receiving a byte (Actually: reading it from the internal buffer): databyte = slReadByte(port1) // NON-BLOCKING (returns immediately) databyte = slReadByte(port1,0) // NON-BLOCKING (returns immediately, same as above) databyte = slReadByte(port1,1) // BLOCKING (waits until a byte was received) If anything is in the receiving buffer, slReadbyte() returns one byte of that data, here on handle "port1". If the second argument is "0" or skipped, the function returns immediately, even if the buffer is empty. A "1" as a second argument waits until at least one byte is received. It "blocks". slReadByte returns either a valid data byte (value 0..255) or "-1" if the receive buffer is empty, an overflow occurs or the port is not opened. Notice that "BLOCKING" really blocks. Scilab will do absolutely nothing until a byte arrives (*subject to change in future versions*). - Closing the COM port: ret = slClose(port1); After this call, the COM port is closed. You can not send or receive data anymore, but the handle is still valid for, e.g. slConfig(), which still allows changing communication parameters, slOpen(), to re-open the port, etc... - Unmount handle: ret = slUMount(port1) slUMount() frees the internal objects and dependecies. From now on, any access to this former handle is invalid, but the library is still loaded and ready to run. You still remember the return stuff, right? "0" is bad, "1" means good... - Unload library: ret = slUnload() Calling slUnload removes the library from memory. No go, until you execute exec("slLoadLib.sci") again. Do NOT call unload until the port is closed and unmounted! 3.0 WRAPPER FUNCTIONS AND VARIABLES 3.1 LIST OF AVAILABLE FUNCTIONS In no particular order: exec("slLoadLib.sci") Not a function, but required to load the library. slLoad *SEE REF* Loads the library and links functions. Also in "slLoadLib.sci"! slInit *SEE REF* Initializes some internal objects. Also in "slLoadLib.sci"! slVersion Returns the version of the library. slUnload Unload the library. slMount Returns a handle to a COM port and initializes internal objects. slUMount Deletes the handle and frees internal objects. slCheck Check availability of a COM port. slConfig Configure a COM port (baudrate, databits, parity and stop bits). slOpen Opens a COM port. slClose Closes a COM port. slSendByte Sends a byte to the COM port. slSendArray Sends an array to the COM port. slCount Counts the available bytes in the receive buffer. slReadByte Reads a byte from the receive buffer. slFlush Flushes the receive buffer and resets the overflow flag (if set) slReadArray Reads up to 32 bytes of data from the buffer. slReadArrayN Reads an arbitrary amount of data from the buffer. slSendPacket Sends a <DLE><STX><DATA/DLE><DLE><ETX> packet. slReadPacket Reads a <DLE><STX><DATA/DLE><DLE><ETX> packet. slCountPacket Counts the number of packets in the receive buffer. slSetPacketStart Allows configuration of the 2nd "packet start" bytes <STX>; e.g. <DLE><STX> slSetPacketEnd Allows configuration of the 2nd "packet end" bytes <ETX>; e.g. <DLE><ETX> slSetPacketChar Allows configuration of the "special character" byte <DLE>; e.g. <DLE> 3.2 LIST OF AVAILABLE VARIABLES slFuncs An array that keeps track of linked functions. Must not be deleted! 3.3 QUICK REFERENCE NULL = exec("slLoadLib.sci") NULL = slLoad() <ret> = slInit() <version> = slVersion(<handle>) <ret> = slUnload() <handle> = slMount() <handle> = slMount(<handle>) *** DEPRECATED *** <ret> = slUMount(<handle>) <ret> = slCheck(<handle>, <port>) <ret> = slConfig(<handle>, <baud>, <databits>, <parity>, <stopbits>) <ret> = slOpen(<handle>, <port>) <ret> = slClose(<handle>) <ret> = slSendByte(<handle>, <byte>) <ret> = slSendArray(<handle>, <array>, <size>) <count> = slCount(<handle>) <byte> = slReadByte(<handle>) <byte> = slReadByte(<handle>,<blocking>) <ret> = slFlush(<handle>) <array> = slReadArray(<handle>) <array> = slReadArrayN(<handle>, <length>) <ret> = slSendPacket(<handle>, <array>, <size>) <array> = slReadPacket(<handle>, <size>) <count> = slCountPacket(<handle>) <ret> = slSetPacketStart(<handle>, <byte>) <ret> = slSetPacketEnd(<handle>, <byte>) <ret> = slSetPacketChar(<handle>, <byte>) 3.4 REFERENCE 3.4.1 slInit <ret> = slInit() Notice: Not required if you use "exec('slLoadLib.sci')"! Initializes some internal objects. Needs to be called once before you do anything else. RETURNS: 1 = OK 3.4.2 slLoad NULL = slLoad() Notice: Not required if you use "exec('slLoadLib.sci')"! Opens "siseli.dll", which has to be on your path (usually right inside the directory Scilab is currently operating in), and links the DLL functions. If any error occurs, execution will be aborted via "abort" (Scilab stops) and an error message is print out on the console. RETURNS: nothing 3.4.3 slVersion <version> = slVersion(<handle>) Returns a floating point representation of the libraries' version. E.g. V1.2 will return "1.2". Requires a valid <handle> argument. RETURNS: <version> = floating point number of library version 3.4.4 slUnload() <ret> = slUnload() Unloads the library from memory by removing all DLL links. Make sure all ports are closed ("slClose(<handle>)") and unmounted ("slUMount(<handle>)") before calling this. Otherwise strange things may happen! Example: // 2 COM ports are referenced by handle 2 and 5 and opened (communicating). // The right way to shut down everything is: slClose(2) slClose(5) slUMount(2) slUMount(5) slUnload() RETURNS: 1 = OK; Even returns "1" if library was previously unloaded (nothing dramatic). 0 = Something went wrong. may be someone deleted "slFuncs", the link list? 3.4.5 slMount <handle> = slMount() <handle> = slMount(<handle>) *** DEPRECATED *** Creates internal objects and dependencies. Needs to be called once, before anything else. The returned value <handle> is an integer number and references the selected COMM port. This way, different COM ports, that are opened the same time, can be easily distinguished. As of V0.7, slMount() does not need to get called with an argument. To remain compatibility, the old function is still available, but its usage is not recommended. Example: // You already executed "exec('slLoadLib.sci')" to load the library. // Execute or type handle = slMount() // From now on, all the internal objects, necessary for operation, are available // and you can start to use the library functions. // Remember the "handle", as this is the reference to the comm port(s). RETURNS: >0 = a number (handle) to identify the selected COMM port 0 = ERROR 3.4.6 slUMount <ret> = slUMount(<handle>) Deletes internal objects and dependencies. After this is called, the <handle> is invalid and can not be used any more, but the library is still loaded. Notice that the COM port, reference by <handle>, needs to be closed via slClose(<handle>), prior to calling this! Example: // Close COM port, referenced by handle 3 and destroy object: slClose(3) slUMount(3) RETURNS: 1 = OK, everything deleted 0 = ERROR, something very strange happened (you better restart Scilab ;-) 3.4.7 slCheck <ret> = slCheck(<handle>, <port>) Checks if COM port <port> is available and free. Example: // get a handle and check if COM4 is present and free: handle = slMount() if slCheck(handle,4) then printf("Port 4 available\n") else printf("Port over board!\n") end RETURNS: 1 = port available, can be opened 0 = port does not exist or is already opened by an other application 3.4.8 slConfig <ret> = slConfig(<handle>, <baud>, <databits>, <parity>, <stopbits>) Configures a not-yet opened port: <baud> = baud rate; 300..3000000 bits/sec <databits> = number of data bits; 5..8 <parity> = 0=NONE, 1=ODD, 2=EVEN <stopbits> = 1=one stop bit, 2=two stop bits, 15=1.5 stop bits Example: // Configure handle "port1" to 4800 bits/s, 8 databits, no parity, one stop bit: slConfig(port1, 4800, 8, 0, 1) Supported baudrates: 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 256000, 921600, 1000000, 2000000, 3000000 RETURNS: 1 = OK 0 = you entered something stupid... 3.4.9 slOpen <ret> = slOpen(<handle>, <port>) Opens COM port <port> on handle <handle>. After the call, the COM port is ready for usage. Example: // example sequence, e.g. after calling "exec('slloadlib.sci')". // Notice: No checks done! // get a handle handle = slMount() // configure it (115k2, 7O1) slConfig(handle, 115200, 7, 1, 1) // open COM4 on this port slOpen(handle,4) RETURNS: 1 = OK 0 = ERROR 3.4.10 slClose <ret> = slClose(<handle>) Closes the COM port, referenced by <handle>. Note that the handle is still valid and the library still loaded! You may still use the handle for everything except for sending bytes or arrays. A read from the buffer is still possible, but probably does not make sense ;) After the call, the COM port is availabe for other applications. RETURNS: 1 = OK 0 = ERROR 3.4.11 slSendByte <ret> = slSendByte(<handle>, <byte>) Sends a single byte <byte> 0..255, to the COM port, referenced by <handle>. The COM port needs to be configured ("slConfig()"), and open ("slOpen()"). Notice that it might take "some time" (...) until the data is sent out. Windows ... ;-) Example: // send an "A" to the port, referenced by handle "port1": slSendByte(port1,ascii("A")) RETURNS: 1 = OK 0 = ERROR 3.4.12 slSendArray <ret> = slSendArray(<handle>, <array>, <size>) Send an array of integers (although you may *carefully* try something else) <array>, of the size <size> (or lesser) to the COM port references by <handle>. If the given size argument exceeds the length of the array, a memory exception will occur and you have to restart Scilab. Notice that the complete Scilab <-> SiSeLi "communication" is pointer arithmetic. Unfortunately Scilab does not provide any (fast and failsafe) checks for this, so you really should take care of that. Scilab Tip: If you need to send a string (e.g.: "Hello"), you can easily convert it to an array by using: array = ascii("Hello") slSendArray(<handle>, array, length(array) ) Example: // send "BLOED" to the port, referenced by handle "port1": slSendArray(port1, ascii("BLOED"), 5) // send an array of integers to the port, referenced by handle "port1": a=[68 79 79 70] slSendArray(port1, a, length(a)) RETURNS: 1 = OK 0 = ERROR 3.4.13 slCount <count> = slCount(<handle>) Counts the number of bytes available in the receive buffer, referenced by <handle>. RETURNS: <count> = number of bytes in receive buffer <-1> = A buffer overflow occurred. You have to call slFlush() to reset it. Also returned if called with an invalid handle. 3.4.14 slReadByte <byte> = slReadByte(<handle>) <byte> = slReadByte(<handle>,<blocking>) Reads (and removes) a byte from the receive buffer, referenced by <handle>. The <blocking> parameter, if given, specifies its behaviour: empty -> the function always returns immediately <blocking=0> -> the function always returns immediately <blocking=1> -> the functions waits (blocks all) until at least one byte is available RETURNS: <byte> = a received byte <-1> = ERROR: nothing in buffer, overflow or port not opened or available 3.4.15 slFlush <ret> = slFlush(<handle>) Flushes (empties) the receive buffer, referenced by <handle>. If an overflow flag is set, it is reset. RETURNS 1 = OK 0 = ERROR 3.4.16 slReadArray <array> = slReadArray(<handle>) To speed up reading, more than a single byte may be read by slReadArray(), on the port referenced by <handle>. Size of <array> is fixed to 33 bytes. The first byte determines the number of valid bytes following. The rest of the array is filled up with "-1" E.g.: If the buffer contains 5 received bytes 65, 66, 67, 68, 69 <array> will be: index: 1 2 3 4 5 6 7 8 33 content: 5, 65, 66, 67, 68, 69, -1, -1, ... -1 WARNING: Calling slReadArray() with an invalid handle may return garbage at index 2..33. The first byte will contain a valid "0" though... Example usage in Scilab: tmp=[] data=[] while 1 then tmp=slReadArray(1) data=[data tmp(1,2:1+tmp(1))] if tmp(1) < 32 then break end end Scilab Tip: If <array> contains a string (ascii characters), it can easily be converted via: str = ascii(<array>) RETURNS <array> = [ <N> <data1> <data2> ... <dataN> ] 3.4.17 slReadArrayN <array> = slReadArray(<handle>, <length>) By popular demand: This function behaves exactly like "slReadArray" above, except it takes a second argument which specifies the length of data to read. <length> must be >1 and <31000. Size of <array> is at least <length+1>. The first byte determines the number of valid bytes following. The rest of the array is filled up with "-1" E.g.: If the buffer contains 5 received bytes 65, 66, 67, 68, 69 executing tmp = slReadArrayN(1, 1000) will return an <array> like this: index: 1 2 3 4 5 6 7 8 1001 content: 5, 65, 66, 67, 68, 69, -1, -1, ... -1 WARNING: Calling slReadArray() with an invalid handle may return garbage after index 1. The first byte will contain a valid "0" though... RETURNS <array> = [ <N> <data1> <data2> ... <dataN> ] 3.4.18 slSendPacket <ret> = slSendPacket(<handle>, <array>, <size>) By default, slSendPacket() creates a < PSTART >< DATA >< PEND > packet. With <PSTART> = <DLE><STX> <PEND> = <DLE><ETX> this results in a <DLE><STX><array+DLE><DLE><ETX> packet, which is then sent out to the com PORT, referenced by <handle>. If the given <size> argument exceeds the length of <array>, a memory exception will occur and you have to restart Scilab. Packet start <PSTART>, packet end <PEND> and the special <DLE> character can be configured by: slSetPacketStart() // set <STX>; range from 0..255 slSetPacketEnd() // set <ETX>; range from 0..255 slSetPacketChar() // set <DLE>; range 0..255 Example: // our array, that needs to be sent a = [14 15 16 17 18] // send it to handle "port1" slSendPacket(port1, a, length(a)) Assuming that <PSTART> = <0x10><0x02>, <PEND> = <0x10><0x03> and <DLE> = <0x10>, the sent packet looks like this: <0x10> <0x02> < 14 > < 15 > < 16 > < 16 > < 17 > < 18 > <0x10> <0x03> In hexadecimal notation (notice that 16==0x10 ;-): <0x10> <0x02> <0x0E> <0x0F> <0x10> <0x10> <0x11> <0x12> <0x10> <0x03> In <array>, every character, that equals <DLE> ("packet character" or data link escape "DLE"), is doubled. It can be set via slSetPacketChar(). RETURNS: 1 = OK 0 = ERROR 3.4.19 slReadPacket <array> = slSendPacket(<handle>, <size>) Reads a packet from the receive buffer and returns it as <array>. All packet start and end markers, as well as the "packet character" <DLE> are removed. The first byte of <array> determines the number of valid bytes that are following, the rest of <array>, up to <size+1> is filled up with "-1". If <size> is smaller than the packet in the receive buffer, the rest of the packet is discarded. Example: Assuming that <PSTART> = <0x10><0x02>, <PEND> = <0x10><0x03> and <DLE> = <0x10> and the receive buffer contains <0x34> <0x65> <0x10> <0x02> <0x01> <0x10> <0x10> <0x03> <0x04> <0x10> <0x03> <0x65> |...ANYDATA...| PACKETSTART | DAT1 | DLE DAT 2 | DAT3 | DAT4 | PACKET END |...ANY slReadPacket(1, 30) will return: <0x04> <0x01> <0x10> <0x03> <0x04> |AMOUNT| DAT1 | DAT2 | DAT3 | DAT4 3.4.13 slCountPacket <count> = slCountPacket(<handle>) Counts the number of packets available in the receive buffer, referenced by <handle>. RETURNS: <count> = number of packets in receive buffer <-1> = a buffer overflow occured; you have to call slFlush() to reset it 3.4.21 slSetPacketStart <ret> = slSetPacketStart(<handle>, <byte>) See slSendPacket() for a description of the packet sending mechanism. Sets the 2nd byte of "packet start" string on the COM port referenced by <handle>, to the content of <byte>. Examples: // Set packet start to <0x10><0x02> (the classic <DLE><STX>), on handle "port1". // The raw data array [ 14 15 16 17 18 19 ], 6 bytes, // will get sent as [ 16 02 14 15 16 17 18 19 ], 8 bytes. slSetPacketStart(port1, 2) RETURNS 1 = OK 3.4.22 slSetPacketEnd <ret> = slSetPacketEnd(<handle>, <byte>) See slSendPacket() for a description of the packet sending mechanism. Sets the 2nd byte of "packet end" string on the COM port referenced by <handle>, to the content of <byte>. Examples: // Set packet end to <0x10><0x03> (the classic <DLE><ETX>), on handle "port1". // The raw data array [ 14 15 16 17 18 19 ], 6 bytes, // will get sent as [ 14 15 16 17 18 19 16 03 ], 8 bytes. slSetPacketEnd(port1, 3) RETURNS 1 = OK 3.4.23 slSetPacketChar <ret> = slSetPacketChar(<handle>, <byte>) Sets the "special character", also known as the classic <DLE> (data link escape), for the communication with the COM port referenced by <handle>. If <byte> is in the range 0..255, any data byte of the same value is doubled. As of SiSeLi V0.6, the special character is automatically used for packet start and packet end (<DLE>). Examples: // Use classic <DLE> (0x10) during transmission on handle "port1": // After the call, every data byte, with a value of "16" will be doubled: // The raw data array [ 14 15 16 17 18 16 ], 6 bytes, // will get sent as [ 14 15 16 16 17 18 16 16 ], 8 bytes. slSetPacketChar(port1,16) RETURNS: 1 = OK 9.0 CHANGES V0.1, initial release V0.1a, mini-fix - *NEW* added slInit() function V0.2, next steps - *FIX* removed some debug lines - *CHG* increased default RX buffer size to 30kB V0.3, never released - *NOP* bummer V0.4, tiny add-on - *NEW* added slReadArrayN() function V0.5, baudrate upgrade, fix - *NEW* added baudrates 256000, up to 3MBit/s - *FIX* error handling did not work for some functions; always reported "OK" V0.6, implemented packet read stuff - *NEW* added slReadPacket() - *NEW* added slCountPacket() - *CHG* reworked complete packet send/write algorithm - *CHG* increased simultaneously usable serial ports to 16 V0.7, refactoring - *NEW* switched over to TDM-GCC and Code::Blocks - *NEW* slLoadLib.sci now auto-selects the right DLL (x86/x64) - *NEW* started to remove old C-style stuff - *NEW* unlimited amount of serial ports (previously limited to 16) - *CHG* slMount() does not require the handle argument any longer. - *CHG* slReadByte() does not require the 2nd argument if NONBLOCKING