1135 lines
32 KiB
C++
1135 lines
32 KiB
C++
/*!
|
|
\file serialib.cpp
|
|
\brief Source file of the class serialib. This class is used for communication over a serial device.
|
|
\author Philippe Lucidarme (University of Angers)
|
|
\version 2.0
|
|
\date december the 27th of 2019
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM,
|
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
This is a licence-free software, it can be used by anyone who try to build a better world.
|
|
*/
|
|
|
|
#include "serialib.h"
|
|
|
|
|
|
|
|
//_____________________________________
|
|
// ::: Constructors and destructors :::
|
|
|
|
|
|
/*!
|
|
\brief Constructor of the class serialib.
|
|
*/
|
|
serialib::serialib()
|
|
{
|
|
#if defined (_WIN32) || defined( _WIN64)
|
|
// Set default value for RTS and DTR (Windows only)
|
|
currentStateRTS=true;
|
|
currentStateDTR=true;
|
|
hSerial = INVALID_HANDLE_VALUE;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
fd = -1;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Destructor of the class serialib. It close the connection
|
|
*/
|
|
// Class desctructor
|
|
serialib::~serialib()
|
|
{
|
|
closeDevice();
|
|
}
|
|
|
|
|
|
|
|
//_________________________________________
|
|
// ::: Configuration and initialization :::
|
|
|
|
|
|
|
|
/*!
|
|
\brief Open the serial port
|
|
\param Device : Port name (COM1, COM2, ... for Windows ) or (/dev/ttyS0, /dev/ttyACM0, /dev/ttyUSB0 ... for linux)
|
|
\param Bauds : Baud rate of the serial port.
|
|
|
|
\n Supported baud rate for Windows :
|
|
- 110
|
|
- 300
|
|
- 600
|
|
- 1200
|
|
- 2400
|
|
- 4800
|
|
- 9600
|
|
- 14400
|
|
- 19200
|
|
- 38400
|
|
- 56000
|
|
- 57600
|
|
- 115200
|
|
- 128000
|
|
- 256000
|
|
|
|
\n Supported baud rate for Linux :\n
|
|
- 110
|
|
- 300
|
|
- 600
|
|
- 1200
|
|
- 2400
|
|
- 4800
|
|
- 9600
|
|
- 19200
|
|
- 38400
|
|
- 57600
|
|
- 115200
|
|
|
|
\n Optionally supported baud rates, depending on Linux kernel:\n
|
|
- 230400
|
|
- 460800
|
|
- 500000
|
|
- 576000
|
|
- 921600
|
|
- 1000000
|
|
- 1152000
|
|
- 1500000
|
|
- 2000000
|
|
- 2500000
|
|
- 3000000
|
|
- 3500000
|
|
- 4000000
|
|
|
|
\param Databits : Number of data bits in one UART transmission.
|
|
|
|
\n Supported values: \n
|
|
- SERIAL_DATABITS_5 (5)
|
|
- SERIAL_DATABITS_6 (6)
|
|
- SERIAL_DATABITS_7 (7)
|
|
- SERIAL_DATABITS_8 (8)
|
|
- SERIAL_DATABITS_16 (16) (not supported on Unix)
|
|
|
|
\param Parity: Parity type
|
|
|
|
\n Supported values: \n
|
|
- SERIAL_PARITY_NONE (N)
|
|
- SERIAL_PARITY_EVEN (E)
|
|
- SERIAL_PARITY_ODD (O)
|
|
- SERIAL_PARITY_MARK (MARK) (not supported on Unix)
|
|
- SERIAL_PARITY_SPACE (SPACE) (not supported on Unix)
|
|
\param Stopbit: Number of stop bits
|
|
|
|
\n Supported values:
|
|
- SERIAL_STOPBITS_1 (1)
|
|
- SERIAL_STOPBITS_1_5 (1.5) (not supported on Unix)
|
|
- SERIAL_STOPBITS_2 (2)
|
|
|
|
\return 1 success
|
|
\return -1 device not found
|
|
\return -2 error while opening the device
|
|
\return -3 error while getting port parameters
|
|
\return -4 Speed (Bauds) not recognized
|
|
\return -5 error while writing port parameters
|
|
\return -6 error while writing timeout parameters
|
|
\return -7 Databits not recognized
|
|
\return -8 Stopbits not recognized
|
|
\return -9 Parity not recognized
|
|
*/
|
|
char serialib::openDevice(const char *Device, const unsigned int Bauds,
|
|
SerialDataBits Databits,
|
|
SerialParity Parity,
|
|
SerialStopBits Stopbits) {
|
|
#if defined (_WIN32) || defined( _WIN64)
|
|
// Open serial port
|
|
hSerial = CreateFileA(Device,GENERIC_READ | GENERIC_WRITE,0,0,OPEN_EXISTING,/*FILE_ATTRIBUTE_NORMAL*/0,0);
|
|
if(hSerial==INVALID_HANDLE_VALUE) {
|
|
if(GetLastError()==ERROR_FILE_NOT_FOUND)
|
|
return -1; // Device not found
|
|
|
|
// Error while opening the device
|
|
return -2;
|
|
}
|
|
|
|
// Set parameters
|
|
|
|
// Structure for the port parameters
|
|
DCB dcbSerialParams;
|
|
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
|
|
|
|
// Get the port parameters
|
|
if (!GetCommState(hSerial, &dcbSerialParams)) return -3;
|
|
|
|
// Set the speed (Bauds)
|
|
switch (Bauds)
|
|
{
|
|
case 110 : dcbSerialParams.BaudRate=CBR_110; break;
|
|
case 300 : dcbSerialParams.BaudRate=CBR_300; break;
|
|
case 600 : dcbSerialParams.BaudRate=CBR_600; break;
|
|
case 1200 : dcbSerialParams.BaudRate=CBR_1200; break;
|
|
case 2400 : dcbSerialParams.BaudRate=CBR_2400; break;
|
|
case 4800 : dcbSerialParams.BaudRate=CBR_4800; break;
|
|
case 9600 : dcbSerialParams.BaudRate=CBR_9600; break;
|
|
case 14400 : dcbSerialParams.BaudRate=CBR_14400; break;
|
|
case 19200 : dcbSerialParams.BaudRate=CBR_19200; break;
|
|
case 38400 : dcbSerialParams.BaudRate=CBR_38400; break;
|
|
case 56000 : dcbSerialParams.BaudRate=CBR_56000; break;
|
|
case 57600 : dcbSerialParams.BaudRate=CBR_57600; break;
|
|
case 115200 : dcbSerialParams.BaudRate=CBR_115200; break;
|
|
case 128000 : dcbSerialParams.BaudRate=CBR_128000; break;
|
|
case 256000 : dcbSerialParams.BaudRate=CBR_256000; break;
|
|
default : return -4;
|
|
}
|
|
//select data size
|
|
BYTE bytesize = 0;
|
|
switch(Databits) {
|
|
case SERIAL_DATABITS_5: bytesize = 5; break;
|
|
case SERIAL_DATABITS_6: bytesize = 6; break;
|
|
case SERIAL_DATABITS_7: bytesize = 7; break;
|
|
case SERIAL_DATABITS_8: bytesize = 8; break;
|
|
case SERIAL_DATABITS_16: bytesize = 16; break;
|
|
default: return -7;
|
|
}
|
|
BYTE stopBits = 0;
|
|
switch(Stopbits) {
|
|
case SERIAL_STOPBITS_1: stopBits = ONESTOPBIT; break;
|
|
case SERIAL_STOPBITS_1_5: stopBits = ONE5STOPBITS; break;
|
|
case SERIAL_STOPBITS_2: stopBits = TWOSTOPBITS; break;
|
|
default: return -8;
|
|
}
|
|
BYTE parity = 0;
|
|
switch(Parity) {
|
|
case SERIAL_PARITY_NONE: parity = NOPARITY; break;
|
|
case SERIAL_PARITY_EVEN: parity = EVENPARITY; break;
|
|
case SERIAL_PARITY_ODD: parity = ODDPARITY; break;
|
|
case SERIAL_PARITY_MARK: parity = MARKPARITY; break;
|
|
case SERIAL_PARITY_SPACE: parity = SPACEPARITY; break;
|
|
default: return -9;
|
|
}
|
|
// configure byte size
|
|
dcbSerialParams.ByteSize = bytesize;
|
|
// configure stop bits
|
|
dcbSerialParams.StopBits = stopBits;
|
|
// configure parity
|
|
dcbSerialParams.Parity = parity;
|
|
|
|
// Write the parameters
|
|
if(!SetCommState(hSerial, &dcbSerialParams)) return -5;
|
|
|
|
// Set TimeOut
|
|
|
|
// Set the Timeout parameters
|
|
timeouts.ReadIntervalTimeout=0;
|
|
// No TimeOut
|
|
timeouts.ReadTotalTimeoutConstant=MAXDWORD;
|
|
timeouts.ReadTotalTimeoutMultiplier=0;
|
|
timeouts.WriteTotalTimeoutConstant=MAXDWORD;
|
|
timeouts.WriteTotalTimeoutMultiplier=0;
|
|
|
|
// Write the parameters
|
|
if(!SetCommTimeouts(hSerial, &timeouts)) return -6;
|
|
|
|
// Opening successfull
|
|
return 1;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Structure with the device's options
|
|
struct termios options;
|
|
|
|
|
|
// Open device
|
|
fd = open(Device, O_RDWR | O_NOCTTY | O_NDELAY);
|
|
// If the device is not open, return -2
|
|
if (fd == -1) return -2;
|
|
// Open the device in nonblocking mode
|
|
fcntl(fd, F_SETFL, FNDELAY);
|
|
|
|
|
|
// Get the current options of the port
|
|
tcgetattr(fd, &options);
|
|
// Clear all the options
|
|
bzero(&options, sizeof(options));
|
|
|
|
// Prepare speed (Bauds)
|
|
speed_t Speed;
|
|
switch (Bauds)
|
|
{
|
|
case 110 : Speed=B110; break;
|
|
case 300 : Speed=B300; break;
|
|
case 600 : Speed=B600; break;
|
|
case 1200 : Speed=B1200; break;
|
|
case 2400 : Speed=B2400; break;
|
|
case 4800 : Speed=B4800; break;
|
|
case 9600 : Speed=B9600; break;
|
|
case 19200 : Speed=B19200; break;
|
|
case 38400 : Speed=B38400; break;
|
|
case 57600 : Speed=B57600; break;
|
|
case 115200 : Speed=B115200; break;
|
|
#if defined (B230400)
|
|
case 230400 : Speed=B230400; break;
|
|
#endif
|
|
#if defined (B460800)
|
|
case 460800 : Speed=B460800; break;
|
|
#endif
|
|
#if defined (B500000)
|
|
case 500000 : Speed=B500000; break;
|
|
#endif
|
|
#if defined (B576000)
|
|
case 576000 : Speed=B576000; break;
|
|
#endif
|
|
#if defined (B921600)
|
|
case 921600 : Speed=B921600; break;
|
|
#endif
|
|
#if defined (B1000000)
|
|
case 1000000 : Speed=B1000000; break;
|
|
#endif
|
|
#if defined (B1152000)
|
|
case 1152000 : Speed=B1152000; break;
|
|
#endif
|
|
#if defined (B1500000)
|
|
case 1500000 : Speed=B1500000; break;
|
|
#endif
|
|
#if defined (B2000000)
|
|
case 2000000 : Speed=B2000000; break;
|
|
#endif
|
|
#if defined (B2500000)
|
|
case 2500000 : Speed=B2500000; break;
|
|
#endif
|
|
#if defined (B3000000)
|
|
case 3000000 : Speed=B3000000; break;
|
|
#endif
|
|
#if defined (B3500000)
|
|
case 3500000 : Speed=B3500000; break;
|
|
#endif
|
|
#if defined (B4000000)
|
|
case 4000000 : Speed=B4000000; break;
|
|
#endif
|
|
default : return -4;
|
|
}
|
|
int databits_flag = 0;
|
|
switch(Databits) {
|
|
case SERIAL_DATABITS_5: databits_flag = CS5; break;
|
|
case SERIAL_DATABITS_6: databits_flag = CS6; break;
|
|
case SERIAL_DATABITS_7: databits_flag = CS7; break;
|
|
case SERIAL_DATABITS_8: databits_flag = CS8; break;
|
|
//16 bits and everything else not supported
|
|
default: return -7;
|
|
}
|
|
int stopbits_flag = 0;
|
|
switch(Stopbits) {
|
|
case SERIAL_STOPBITS_1: stopbits_flag = 0; break;
|
|
case SERIAL_STOPBITS_2: stopbits_flag = CSTOPB; break;
|
|
//1.5 stopbits and everything else not supported
|
|
default: return -8;
|
|
}
|
|
int parity_flag = 0;
|
|
switch(Parity) {
|
|
case SERIAL_PARITY_NONE: parity_flag = 0; break;
|
|
case SERIAL_PARITY_EVEN: parity_flag = PARENB; break;
|
|
case SERIAL_PARITY_ODD: parity_flag = (PARENB | PARODD); break;
|
|
//mark and space parity not supported
|
|
default: return -9;
|
|
}
|
|
|
|
// Set the baud rate
|
|
cfsetispeed(&options, Speed);
|
|
cfsetospeed(&options, Speed);
|
|
// Configure the device : data bits, stop bits, parity, no control flow
|
|
// Ignore modem control lines (CLOCAL) and Enable receiver (CREAD)
|
|
options.c_cflag |= ( CLOCAL | CREAD | databits_flag | parity_flag | stopbits_flag);
|
|
options.c_iflag |= ( IGNPAR | IGNBRK );
|
|
// Timer unused
|
|
options.c_cc[VTIME]=0;
|
|
// At least on character before satisfy reading
|
|
options.c_cc[VMIN]=0;
|
|
// Activate the settings
|
|
tcsetattr(fd, TCSANOW, &options);
|
|
// Success
|
|
return (1);
|
|
#endif
|
|
|
|
}
|
|
|
|
bool serialib::isDeviceOpen()
|
|
{
|
|
#if defined (_WIN32) || defined( _WIN64)
|
|
return hSerial != INVALID_HANDLE_VALUE;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
return fd >= 0;
|
|
#endif
|
|
}
|
|
|
|
/*!
|
|
\brief Close the connection with the current device
|
|
*/
|
|
void serialib::closeDevice()
|
|
{
|
|
#if defined (_WIN32) || defined( _WIN64)
|
|
CloseHandle(hSerial);
|
|
hSerial = INVALID_HANDLE_VALUE;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
close (fd);
|
|
fd = -1;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
//___________________________________________
|
|
// ::: Read/Write operation on characters :::
|
|
|
|
|
|
|
|
/*!
|
|
\brief Write a char on the current serial port
|
|
\param Byte : char to send on the port (must be terminated by '\0')
|
|
\return 1 success
|
|
\return -1 error while writting data
|
|
*/
|
|
int serialib::writeChar(const char Byte)
|
|
{
|
|
#if defined (_WIN32) || defined( _WIN64)
|
|
// Number of bytes written
|
|
DWORD dwBytesWritten;
|
|
// Write the char to the serial device
|
|
// Return -1 if an error occured
|
|
if(!WriteFile(hSerial,&Byte,1,&dwBytesWritten,NULL)) return -1;
|
|
// Write operation successfull
|
|
return 1;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Write the char
|
|
if (write(fd,&Byte,1)!=1) return -1;
|
|
|
|
// Write operation successfull
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//________________________________________
|
|
// ::: Read/Write operation on strings :::
|
|
|
|
|
|
/*!
|
|
\brief Write a string on the current serial port
|
|
\param receivedString : string to send on the port (must be terminated by '\0')
|
|
\return 1 success
|
|
\return -1 error while writting data
|
|
*/
|
|
int serialib::writeString(const char *receivedString)
|
|
{
|
|
#if defined (_WIN32) || defined( _WIN64)
|
|
// Number of bytes written
|
|
DWORD dwBytesWritten;
|
|
// Write the string
|
|
if(!WriteFile(hSerial,receivedString,strlen(receivedString),&dwBytesWritten,NULL))
|
|
// Error while writing, return -1
|
|
return -1;
|
|
// Write operation successfull
|
|
return 1;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Lenght of the string
|
|
int Lenght=strlen(receivedString);
|
|
// Write the string
|
|
if (write(fd,receivedString,Lenght)!=Lenght) return -1;
|
|
// Write operation successfull
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
// _____________________________________
|
|
// ::: Read/Write operation on bytes :::
|
|
|
|
|
|
|
|
/*!
|
|
\brief Write an array of data on the current serial port
|
|
\param Buffer : array of bytes to send on the port
|
|
\param NbBytes : number of byte to send
|
|
\return 1 success
|
|
\return -1 error while writting data
|
|
*/
|
|
int serialib::writeBytes(const void *Buffer, const unsigned int NbBytes)
|
|
{
|
|
#if defined (_WIN32) || defined( _WIN64)
|
|
// Number of bytes written
|
|
DWORD dwBytesWritten;
|
|
// Write data
|
|
if(!WriteFile(hSerial, Buffer, NbBytes, &dwBytesWritten, NULL))
|
|
// Error while writing, return -1
|
|
return -1;
|
|
// Write operation successfull
|
|
return 1;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Write data
|
|
if (write (fd,Buffer,NbBytes)!=(ssize_t)NbBytes) return -1;
|
|
// Write operation successfull
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
\brief Wait for a byte from the serial device and return the data read
|
|
\param pByte : data read on the serial device
|
|
\param timeOut_ms : delay of timeout before giving up the reading
|
|
If set to zero, timeout is disable (Optional)
|
|
\return 1 success
|
|
\return 0 Timeout reached
|
|
\return -1 error while setting the Timeout
|
|
\return -2 error while reading the byte
|
|
*/
|
|
int serialib::readChar(char *pByte,unsigned int timeOut_ms)
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
// Number of bytes read
|
|
DWORD dwBytesRead = 0;
|
|
|
|
// Set the TimeOut
|
|
timeouts.ReadTotalTimeoutConstant=timeOut_ms;
|
|
|
|
// Write the parameters, return -1 if an error occured
|
|
if(!SetCommTimeouts(hSerial, &timeouts)) return -1;
|
|
|
|
// Read the byte, return -2 if an error occured
|
|
if(!ReadFile(hSerial,pByte, 1, &dwBytesRead, NULL)) return -2;
|
|
|
|
// Return 0 if the timeout is reached
|
|
if (dwBytesRead==0) return 0;
|
|
|
|
// The byte is read
|
|
return 1;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Timer used for timeout
|
|
timeOut timer;
|
|
// Initialise the timer
|
|
timer.initTimer();
|
|
// While Timeout is not reached
|
|
while (timer.elapsedTime_ms()<timeOut_ms || timeOut_ms==0)
|
|
{
|
|
// Try to read a byte on the device
|
|
switch (read(fd,pByte,1)) {
|
|
case 1 : return 1; // Read successfull
|
|
case -1 : return -2; // Error while reading
|
|
}
|
|
}
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
\brief Read a string from the serial device (without TimeOut)
|
|
\param receivedString : string read on the serial device
|
|
\param FinalChar : final char of the string
|
|
\param MaxNbBytes : maximum allowed number of bytes read
|
|
\return >0 success, return the number of bytes read
|
|
\return -1 error while setting the Timeout
|
|
\return -2 error while reading the byte
|
|
\return -3 MaxNbBytes is reached
|
|
*/
|
|
int serialib::readStringNoTimeOut(char *receivedString,char finalChar,unsigned int maxNbBytes)
|
|
{
|
|
// Number of characters read
|
|
unsigned int NbBytes=0;
|
|
// Returned value from Read
|
|
char charRead;
|
|
|
|
// While the buffer is not full
|
|
while (NbBytes<maxNbBytes)
|
|
{
|
|
// Read a character with the restant time
|
|
charRead=readChar(&receivedString[NbBytes]);
|
|
|
|
// Check a character has been read
|
|
if (charRead==1)
|
|
{
|
|
// Check if this is the final char
|
|
if (receivedString[NbBytes]==finalChar)
|
|
{
|
|
// This is the final char, add zero (end of string)
|
|
receivedString [++NbBytes]=0;
|
|
// Return the number of bytes read
|
|
return NbBytes;
|
|
}
|
|
|
|
// The character is not the final char, increase the number of bytes read
|
|
NbBytes++;
|
|
}
|
|
|
|
// An error occured while reading, return the error number
|
|
if (charRead<0) return charRead;
|
|
}
|
|
// Buffer is full : return -3
|
|
return -3;
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Read a string from the serial device (with timeout)
|
|
\param receivedString : string read on the serial device
|
|
\param finalChar : final char of the string
|
|
\param maxNbBytes : maximum allowed number of characters read
|
|
\param timeOut_ms : delay of timeout before giving up the reading (optional)
|
|
\return >0 success, return the number of bytes read (including the null character)
|
|
\return 0 timeout is reached
|
|
\return -1 error while setting the Timeout
|
|
\return -2 error while reading the character
|
|
\return -3 MaxNbBytes is reached
|
|
*/
|
|
int serialib::readString(char *receivedString,char finalChar,unsigned int maxNbBytes,unsigned int timeOut_ms)
|
|
{
|
|
// Check if timeout is requested
|
|
if (timeOut_ms==0) return readStringNoTimeOut(receivedString,finalChar,maxNbBytes);
|
|
|
|
// Number of bytes read
|
|
unsigned int nbBytes=0;
|
|
// Character read on serial device
|
|
char charRead;
|
|
// Timer used for timeout
|
|
timeOut timer;
|
|
long int timeOutParam;
|
|
|
|
// Initialize the timer (for timeout)
|
|
timer.initTimer();
|
|
|
|
// While the buffer is not full
|
|
while (nbBytes<maxNbBytes)
|
|
{
|
|
// Compute the TimeOut for the next call of ReadChar
|
|
timeOutParam = timeOut_ms-timer.elapsedTime_ms();
|
|
|
|
// If there is time remaining
|
|
if (timeOutParam>0)
|
|
{
|
|
// Wait for a byte on the serial link with the remaining time as timeout
|
|
charRead=readChar(&receivedString[nbBytes],timeOutParam);
|
|
|
|
// If a byte has been received
|
|
if (charRead==1)
|
|
{
|
|
// Check if the character received is the final one
|
|
if (receivedString[nbBytes]==finalChar)
|
|
{
|
|
// Final character: add the end character 0
|
|
receivedString [++nbBytes]=0;
|
|
// Return the number of bytes read
|
|
return nbBytes;
|
|
}
|
|
// This is not the final character, just increase the number of bytes read
|
|
nbBytes++;
|
|
}
|
|
// Check if an error occured during reading char
|
|
// If an error occurend, return the error number
|
|
if (charRead<0) return charRead;
|
|
}
|
|
// Check if timeout is reached
|
|
if (timer.elapsedTime_ms()>timeOut_ms)
|
|
{
|
|
// Add the end caracter
|
|
receivedString[nbBytes]=0;
|
|
// Return 0 (timeout reached)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Buffer is full : return -3
|
|
return -3;
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Read an array of bytes from the serial device (with timeout)
|
|
\param buffer : array of bytes read from the serial device
|
|
\param maxNbBytes : maximum allowed number of bytes read
|
|
\param timeOut_ms : delay of timeout before giving up the reading
|
|
\param sleepDuration_us : delay of CPU relaxing in microseconds (Linux only)
|
|
In the reading loop, a sleep can be performed after each reading
|
|
This allows CPU to perform other tasks
|
|
\return >=0 return the number of bytes read before timeout or
|
|
requested data is completed
|
|
\return -1 error while setting the Timeout
|
|
\return -2 error while reading the byte
|
|
*/
|
|
int serialib::readBytes (void *buffer,unsigned int maxNbBytes,unsigned int timeOut_ms, unsigned int sleepDuration_us)
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
// Avoid warning while compiling
|
|
UNUSED(sleepDuration_us);
|
|
|
|
// Number of bytes read
|
|
DWORD dwBytesRead = 0;
|
|
|
|
// Set the TimeOut
|
|
timeouts.ReadTotalTimeoutConstant=(DWORD)timeOut_ms;
|
|
|
|
// Write the parameters and return -1 if an error occrured
|
|
if(!SetCommTimeouts(hSerial, &timeouts)) return -1;
|
|
|
|
|
|
// Read the bytes from the serial device, return -2 if an error occured
|
|
if(!ReadFile(hSerial,buffer,(DWORD)maxNbBytes,&dwBytesRead, NULL)) return -2;
|
|
|
|
// Return the byte read
|
|
return dwBytesRead;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Timer used for timeout
|
|
timeOut timer;
|
|
// Initialise the timer
|
|
timer.initTimer();
|
|
unsigned int NbByteRead=0;
|
|
// While Timeout is not reached
|
|
while (timer.elapsedTime_ms()<timeOut_ms || timeOut_ms==0)
|
|
{
|
|
// Compute the position of the current byte
|
|
unsigned char* Ptr=(unsigned char*)buffer+NbByteRead;
|
|
// Try to read a byte on the device
|
|
int Ret=read(fd,(void*)Ptr,maxNbBytes-NbByteRead);
|
|
// Error while reading
|
|
if (Ret==-1) return -2;
|
|
|
|
// One or several byte(s) has been read on the device
|
|
if (Ret>0)
|
|
{
|
|
// Increase the number of read bytes
|
|
NbByteRead+=Ret;
|
|
// Success : bytes has been read
|
|
if (NbByteRead>=maxNbBytes)
|
|
return NbByteRead;
|
|
}
|
|
// Suspend the loop to avoid charging the CPU
|
|
usleep (sleepDuration_us);
|
|
}
|
|
// Timeout reached, return the number of bytes read
|
|
return NbByteRead;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
// _________________________
|
|
// ::: Special operation :::
|
|
|
|
|
|
|
|
/*!
|
|
\brief Empty receiver buffer
|
|
Note that when using serial over USB on Unix systems, a delay of 20ms may be necessary before calling the flushReceiver function
|
|
\return If the function succeeds, the return value is nonzero.
|
|
If the function fails, the return value is zero.
|
|
*/
|
|
char serialib::flushReceiver()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
// Purge receiver
|
|
return PurgeComm (hSerial, PURGE_RXCLEAR);
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Purge receiver
|
|
tcflush(fd,TCIFLUSH);
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
\brief Return the number of bytes in the received buffer (UNIX only)
|
|
\return The number of bytes received by the serial provider but not yet read.
|
|
*/
|
|
int serialib::available()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
// Device errors
|
|
DWORD commErrors;
|
|
// Device status
|
|
COMSTAT commStatus;
|
|
// Read status
|
|
ClearCommError(hSerial, &commErrors, &commStatus);
|
|
// Return the number of pending bytes
|
|
return commStatus.cbInQue;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
int nBytes=0;
|
|
// Return number of pending bytes in the receiver
|
|
ioctl(fd, FIONREAD, &nBytes);
|
|
return nBytes;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// __________________
|
|
// ::: I/O Access :::
|
|
|
|
/*!
|
|
\brief Set or unset the bit DTR (pin 4)
|
|
DTR stands for Data Terminal Ready
|
|
Convenience method :This method calls setDTR and clearDTR
|
|
\param status = true set DTR
|
|
status = false unset DTR
|
|
\return If the function fails, the return value is false
|
|
If the function succeeds, the return value is true.
|
|
*/
|
|
bool serialib::DTR(bool status)
|
|
{
|
|
if (status)
|
|
// Set DTR
|
|
return this->setDTR();
|
|
else
|
|
// Unset DTR
|
|
return this->clearDTR();
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Set the bit DTR (pin 4)
|
|
DTR stands for Data Terminal Ready
|
|
\return If the function fails, the return value is false
|
|
If the function succeeds, the return value is true.
|
|
*/
|
|
bool serialib::setDTR()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
// Set DTR
|
|
currentStateDTR=true;
|
|
return EscapeCommFunction(hSerial,SETDTR);
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Set DTR
|
|
int status_DTR=0;
|
|
ioctl(fd, TIOCMGET, &status_DTR);
|
|
status_DTR |= TIOCM_DTR;
|
|
ioctl(fd, TIOCMSET, &status_DTR);
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
/*!
|
|
\brief Clear the bit DTR (pin 4)
|
|
DTR stands for Data Terminal Ready
|
|
\return If the function fails, the return value is false
|
|
If the function succeeds, the return value is true.
|
|
*/
|
|
bool serialib::clearDTR()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
// Clear DTR
|
|
currentStateDTR=false;
|
|
return EscapeCommFunction(hSerial,CLRDTR);
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Clear DTR
|
|
int status_DTR=0;
|
|
ioctl(fd, TIOCMGET, &status_DTR);
|
|
status_DTR &= ~TIOCM_DTR;
|
|
ioctl(fd, TIOCMSET, &status_DTR);
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
\brief Set or unset the bit RTS (pin 7)
|
|
RTS stands for Data Termina Ready
|
|
Convenience method :This method calls setDTR and clearDTR
|
|
\param status = true set DTR
|
|
status = false unset DTR
|
|
\return false if the function fails
|
|
\return true if the function succeeds
|
|
*/
|
|
bool serialib::RTS(bool status)
|
|
{
|
|
if (status)
|
|
// Set RTS
|
|
return this->setRTS();
|
|
else
|
|
// Unset RTS
|
|
return this->clearRTS();
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Set the bit RTS (pin 7)
|
|
RTS stands for Data Terminal Ready
|
|
\return If the function fails, the return value is false
|
|
If the function succeeds, the return value is true.
|
|
*/
|
|
bool serialib::setRTS()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
// Set RTS
|
|
currentStateRTS=true;
|
|
return EscapeCommFunction(hSerial,SETRTS);
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Set RTS
|
|
int status_RTS=0;
|
|
ioctl(fd, TIOCMGET, &status_RTS);
|
|
status_RTS |= TIOCM_RTS;
|
|
ioctl(fd, TIOCMSET, &status_RTS);
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
\brief Clear the bit RTS (pin 7)
|
|
RTS stands for Data Terminal Ready
|
|
\return If the function fails, the return value is false
|
|
If the function succeeds, the return value is true.
|
|
*/
|
|
bool serialib::clearRTS()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
// Clear RTS
|
|
currentStateRTS=false;
|
|
return EscapeCommFunction(hSerial,CLRRTS);
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
// Clear RTS
|
|
int status_RTS=0;
|
|
ioctl(fd, TIOCMGET, &status_RTS);
|
|
status_RTS &= ~TIOCM_RTS;
|
|
ioctl(fd, TIOCMSET, &status_RTS);
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
\brief Get the CTS's status (pin 8)
|
|
CTS stands for Clear To Send
|
|
\return Return true if CTS is set otherwise false
|
|
*/
|
|
bool serialib::isCTS()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
DWORD modemStat;
|
|
GetCommModemStatus(hSerial, &modemStat);
|
|
return modemStat & MS_CTS_ON;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
int status=0;
|
|
//Get the current status of the CTS bit
|
|
ioctl(fd, TIOCMGET, &status);
|
|
return status & TIOCM_CTS;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
\brief Get the DSR's status (pin 6)
|
|
DSR stands for Data Set Ready
|
|
\return Return true if DTR is set otherwise false
|
|
*/
|
|
bool serialib::isDSR()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
DWORD modemStat;
|
|
GetCommModemStatus(hSerial, &modemStat);
|
|
return modemStat & MS_DSR_ON;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
int status=0;
|
|
//Get the current status of the DSR bit
|
|
ioctl(fd, TIOCMGET, &status);
|
|
return status & TIOCM_DSR;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
\brief Get the DCD's status (pin 1)
|
|
CDC stands for Data Carrier Detect
|
|
\return true if DCD is set
|
|
\return false otherwise
|
|
*/
|
|
bool serialib::isDCD()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
DWORD modemStat;
|
|
GetCommModemStatus(hSerial, &modemStat);
|
|
return modemStat & MS_RLSD_ON;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
int status=0;
|
|
//Get the current status of the DCD bit
|
|
ioctl(fd, TIOCMGET, &status);
|
|
return status & TIOCM_CAR;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Get the RING's status (pin 9)
|
|
Ring Indicator
|
|
\return Return true if RING is set otherwise false
|
|
*/
|
|
bool serialib::isRI()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
DWORD modemStat;
|
|
GetCommModemStatus(hSerial, &modemStat);
|
|
return modemStat & MS_RING_ON;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
int status=0;
|
|
//Get the current status of the RING bit
|
|
ioctl(fd, TIOCMGET, &status);
|
|
return status & TIOCM_RNG;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Get the DTR's status (pin 4)
|
|
DTR stands for Data Terminal Ready
|
|
May behave abnormally on Windows
|
|
\return Return true if CTS is set otherwise false
|
|
*/
|
|
bool serialib::isDTR()
|
|
{
|
|
#if defined (_WIN32) || defined( _WIN64)
|
|
return currentStateDTR;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
int status=0;
|
|
//Get the current status of the DTR bit
|
|
ioctl(fd, TIOCMGET, &status);
|
|
return status & TIOCM_DTR ;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
\brief Get the RTS's status (pin 7)
|
|
RTS stands for Request To Send
|
|
May behave abnormally on Windows
|
|
\return Return true if RTS is set otherwise false
|
|
*/
|
|
bool serialib::isRTS()
|
|
{
|
|
#if defined (_WIN32) || defined(_WIN64)
|
|
return currentStateRTS;
|
|
#endif
|
|
#if defined (__linux__) || defined(__APPLE__)
|
|
int status=0;
|
|
//Get the current status of the CTS bit
|
|
ioctl(fd, TIOCMGET, &status);
|
|
return status & TIOCM_RTS;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ******************************************
|
|
// Class timeOut
|
|
// ******************************************
|
|
|
|
|
|
/*!
|
|
\brief Constructor of the class timeOut.
|
|
*/
|
|
// Constructor
|
|
timeOut::timeOut()
|
|
{}
|
|
|
|
|
|
/*!
|
|
\brief Initialise the timer. It writes the current time of the day in the structure PreviousTime.
|
|
*/
|
|
//Initialize the timer
|
|
void timeOut::initTimer()
|
|
{
|
|
#if defined (NO_POSIX_TIME)
|
|
LARGE_INTEGER tmp;
|
|
QueryPerformanceFrequency(&tmp);
|
|
counterFrequency = tmp.QuadPart;
|
|
// Used to store the previous time (for computing timeout)
|
|
QueryPerformanceCounter(&tmp);
|
|
previousTime = tmp.QuadPart;
|
|
#else
|
|
gettimeofday(&previousTime, NULL);
|
|
#endif
|
|
}
|
|
|
|
/*!
|
|
\brief Returns the time elapsed since initialization. It write the current time of the day in the structure CurrentTime.
|
|
Then it returns the difference between CurrentTime and PreviousTime.
|
|
\return The number of microseconds elapsed since the functions InitTimer was called.
|
|
*/
|
|
//Return the elapsed time since initialization
|
|
unsigned long int timeOut::elapsedTime_ms()
|
|
{
|
|
#if defined (NO_POSIX_TIME)
|
|
// Current time
|
|
LARGE_INTEGER CurrentTime;
|
|
// Number of ticks since last call
|
|
int sec;
|
|
|
|
// Get current time
|
|
QueryPerformanceCounter(&CurrentTime);
|
|
|
|
// Compute the number of ticks elapsed since last call
|
|
sec=CurrentTime.QuadPart-previousTime;
|
|
|
|
// Return the elapsed time in milliseconds
|
|
return sec/(counterFrequency/1000);
|
|
#else
|
|
// Current time
|
|
struct timeval CurrentTime;
|
|
// Number of seconds and microseconds since last call
|
|
int sec,usec;
|
|
|
|
// Get current time
|
|
gettimeofday(&CurrentTime, NULL);
|
|
|
|
// Compute the number of seconds and microseconds elapsed since last call
|
|
sec=CurrentTime.tv_sec-previousTime.tv_sec;
|
|
usec=CurrentTime.tv_usec-previousTime.tv_usec;
|
|
|
|
// If the previous usec is higher than the current one
|
|
if (usec<0)
|
|
{
|
|
// Recompute the microseonds and substract one second
|
|
usec=1000000-previousTime.tv_usec+CurrentTime.tv_usec;
|
|
sec--;
|
|
}
|
|
|
|
// Return the elapsed time in milliseconds
|
|
return sec*1000+usec/1000;
|
|
#endif
|
|
}
|