/*---------------------------------------------------------*\ | Cross Platform Serial COM Library for Windows and Linux | | This library provides access to serial ports with a | | common API for both Windows and Linux systems. It | | features read and write as well as tx/rx buffer flush. | | | | Adam Honse (calcprogrammer1@gmail.com), 1/21/2013 | \*---------------------------------------------------------*/ #include "serial_port.h" /*---------------------------------------------------------*\ | serial_port (constructor) | | The default constructor does not initialize the serial | | port | \*---------------------------------------------------------*/ serial_port::serial_port() { /*-----------------------------------------------------*\ | Set a default baud rate | \*-----------------------------------------------------*/ baud_rate = 9600; } /*---------------------------------------------------------*\ | serial_port (constructor) | | When created with port information, the constructor | | will automatically open port at baud rate | \*---------------------------------------------------------*/ serial_port::serial_port(const char * name, unsigned int baud) { serial_open(name, baud); } /*---------------------------------------------------------*\ | ~serial_port (destructor) | | Closes the port before destroying the object | \*---------------------------------------------------------*/ serial_port::~serial_port() { serial_close(); } /*---------------------------------------------------------*\ | serial_open | | Opens the serial port using stored information | | Sets the baud rate to the stored baud rate | | 8 data bits, no parity, one stop bit | \*---------------------------------------------------------*/ bool serial_port::serial_open() { /*-----------------------------------------------------*\ | Windows-specific code path for serial port opening | \*-----------------------------------------------------*/ #ifdef _WIN32 // On Windows, ports above 9 need to have "\\.\" prepended to their path. For ports below 9, this is optional. // https://support.microsoft.com/en-us/topic/howto-specify-serial-ports-larger-than-com9-db9078a5-b7b6-bf00-240f-f749ebfd913e char full_path[100]; snprintf(full_path, sizeof(full_path), "\\\\.\\%s", port_name); file_descriptor = CreateFile(full_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if((int)file_descriptor < 0) { return false; } SetupComm(file_descriptor, 1, 128); GetCommState(file_descriptor, &dcb); dcb.BaudRate = baud_rate; //Set baud rate dcb.ByteSize = 8; //8 data bits dcb.Parity = NOPARITY; //Parity = none dcb.StopBits = ONESTOPBIT; //One stop bit dcb.fAbortOnError = TRUE; //Abort on error dcb.fOutX = FALSE; //XON/XOFF off for transmit dcb.fInX = FALSE; //XON/XOFF off for receive dcb.fOutxCtsFlow = FALSE; //Turn off CTS flow control dcb.fRtsControl = RTS_CONTROL_DISABLE; //Options DISABLE, ENABLE, HANDSHAKE dcb.fOutxDsrFlow = FALSE; //Turn off DSR flow control dcb.fDtrControl = DTR_CONTROL_DISABLE; //Disable DTR control SetCommState(file_descriptor, &dcb); COMMTIMEOUTS timeouts = {0}; timeouts.ReadIntervalTimeout = 50; timeouts.ReadTotalTimeoutConstant=50; timeouts.ReadTotalTimeoutMultiplier=10; timeouts.WriteTotalTimeoutConstant=50; timeouts.WriteTotalTimeoutMultiplier=10; SetCommTimeouts(file_descriptor, &timeouts); #endif /*-----------------------------------------------------*\ | Linux-specific code path for serial port opening | \*-----------------------------------------------------*/ #ifdef __linux__ file_descriptor = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY); if(file_descriptor < 0) { return false; } struct termios2 options; ioctl(file_descriptor, TCGETS2, &options); options.c_cflag &= ~CBAUD; options.c_cflag |= BOTHER; options.c_lflag &= ~ICANON; options.c_lflag &= ~ECHO; // Disable echo options.c_lflag &= ~ECHOE; // Disable erasure options.c_lflag &= ~ECHONL; // Disable new-line echo options.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP options.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl options.c_ispeed = baud_rate; options.c_ospeed = baud_rate; options.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes ioctl(file_descriptor, TCSETS2, &options); #endif /*-----------------------------------------------------*\ | MacOS-specific code path for serial port opening | \*-----------------------------------------------------*/ #ifdef __APPLE__ file_descriptor = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY); if(file_descriptor < 0) { return false; } struct termios options; tcgetattr(file_descriptor, &options); switch(baud_rate) { case 9600: cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); break; case 19200: cfsetispeed(&options, B19200); cfsetospeed(&options, B19200); break; case 115200: cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); break; case 38400: cfsetispeed(&options, B38400); cfsetospeed(&options, B38400); break; case 57600: cfsetispeed(&options, B57600); cfsetospeed(&options, B57600); break; default: cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); break; } /*-----------------------------------------------------*\ | Configure other settings | \*-----------------------------------------------------*/ options.c_iflag &= ~(INLCR | ICRNL); options.c_iflag |= IGNPAR | IGNBRK; options.c_oflag &= ~(OPOST | ONLCR | OCRNL); options.c_cflag &= ~(PARENB | PARODD | CSTOPB | CSIZE | CRTSCTS); options.c_cflag |= CLOCAL | CREAD | CS8; options.c_lflag &= ~(ICANON | ISIG | ECHO); options.c_cc[VTIME] = 1; options.c_cc[VMIN] = 0; if(tcsetattr(file_descriptor, TCSANOW, &options) < 0) { close(file_descriptor); return false; } #endif /*-----------------------------------------------------*\ | Return true if successful | \*-----------------------------------------------------*/ return true; } /*---------------------------------------------------------*\ | serial_open | | Opens the serial port without changing stored | | baud rate | \*---------------------------------------------------------*/ bool serial_port::serial_open(const char * name) { return serial_open(name, baud_rate); } /*---------------------------------------------------------*\ | serial_open | | Opens the serial port at baud rate | \*---------------------------------------------------------*/ bool serial_port::serial_open(const char* name, unsigned int baud) { snprintf(port_name,sizeof(port_name),"%s",name); baud_rate = baud; return serial_open(); } /*---------------------------------------------------------*\ | serial_close | | Closes the serial port | \*---------------------------------------------------------*/ void serial_port::serial_close() { /*-----------------------------------------------------*\ | Windows-specific code path for serial close | \*-----------------------------------------------------*/ #ifdef _WIN32 CloseHandle(file_descriptor); #endif /*-----------------------------------------------------*\ | Linux-specific code path for serial close | \*-----------------------------------------------------*/ #ifdef __linux__ close(file_descriptor); #endif /*-----------------------------------------------------*\ | MacOS-specific code path for serial close | \*-----------------------------------------------------*/ #ifdef __APPLE__ close(file_descriptor); #endif } /*---------------------------------------------------------*\ | serial_read | | Reads bytes from the serial port into | | Returns the number of bytes actually read | | If less than bytes are available, it will read| | all available bytes | \*---------------------------------------------------------*/ int serial_port::serial_read(char * buffer, int length) { /*-----------------------------------------------------*\ | Windows-specific code path for serial read | \*-----------------------------------------------------*/ #ifdef _WIN32 DWORD bytesread; ReadFile(file_descriptor, buffer, length, &bytesread, NULL); return bytesread; #endif /*-----------------------------------------------------*\ | Linux-specific code path for serial read | \*-----------------------------------------------------*/ #ifdef __linux__ int bytesread; bytesread = read(file_descriptor, buffer, length); return bytesread; #endif /*-----------------------------------------------------*\ | MacOS-specific code path for serial read | \*-----------------------------------------------------*/ #ifdef __APPLE__ int bytesread; bytesread = read(file_descriptor, buffer, length); return bytesread; #endif /*-----------------------------------------------------*\ | Return 0 on unsupported platforms | \*-----------------------------------------------------*/ return 0; } /*---------------------------------------------------------*\ | serial_write | | Writes bytes to the serial port from | | Returns the number of bytes actually written | | Does not check for null-termination, so if is | | greater than the number of bytes in , it will | | read past and may cause a segfault | \*---------------------------------------------------------*/ int serial_port::serial_write(char * buffer, int length) { /*-----------------------------------------------------*\ | Windows-specific code path for serial write | \*-----------------------------------------------------*/ #ifdef _WIN32 DWORD byteswritten; WriteFile(file_descriptor, buffer, length, &byteswritten, NULL); return byteswritten; #endif /*-----------------------------------------------------*\ | Linux-specific code path for serial write | \*-----------------------------------------------------*/ #ifdef __linux__ int byteswritten; tcdrain(file_descriptor); byteswritten = write(file_descriptor, buffer, length); tcdrain(file_descriptor); return byteswritten; #endif /*-----------------------------------------------------*\ | MacOS-specific code path for serial write | \*-----------------------------------------------------*/ #ifdef __APPLE__ int byteswritten; tcdrain(file_descriptor); byteswritten = write(file_descriptor, buffer, length); tcdrain(file_descriptor); return byteswritten; #endif /*-----------------------------------------------------*\ | Return 0 on unsupported platforms | \*-----------------------------------------------------*/ return 0; } /*---------------------------------------------------------*\ | serial_flush | \*---------------------------------------------------------*/ void serial_port::serial_flush_rx() { #ifdef _WIN32 PurgeComm(file_descriptor, PURGE_RXABORT | PURGE_RXCLEAR); #endif #ifdef __linux__ tcflush(file_descriptor, TCIFLUSH); #endif #ifdef __APPLE__ tcflush(file_descriptor, TCIFLUSH); #endif } /*---------------------------------------------------------*\ | serial_flush_tx | \*---------------------------------------------------------*/ void serial_port::serial_flush_tx() { #ifdef _WIN32 PurgeComm(file_descriptor, PURGE_TXABORT | PURGE_TXCLEAR); #endif #ifdef __linux__ tcflush(file_descriptor, TCOFLUSH); #endif #ifdef __APPLE__ tcflush(file_descriptor, TCOFLUSH); #endif }