From 79c0c9120eb4b1e85059bb7135d50654c11514a0 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sun, 29 Jan 2023 01:30:37 +0700 Subject: [PATCH 1/3] Distinguish device disconnection and tiemout if read returns 0 --- src/SerialPort.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/SerialPort.cpp b/src/SerialPort.cpp index 201d0f0..51b213d 100644 --- a/src/SerialPort.cpp +++ b/src/SerialPort.cpp @@ -533,6 +533,17 @@ namespace CppLinuxSerial { // is called ssize_t n = read(fileDesc_, &readBuffer_[0], readBufferSize_B_); + if(n == 0) + { + struct termios2 term2; + int rv = ioctl(fileDesc_, TCGETS2, &term2); + + if(rv != 0) + { + throw std::system_error(EFAULT, std::system_category()); + } + } + // Error Handling if(n < 0) { // Read was unsuccessful From 5a2249e838e16943a9c27bd2ed6de56775811bec Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 2 Feb 2023 02:14:07 +0700 Subject: [PATCH 2/3] Rearrange ifs in read routine --- src/SerialPort.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/SerialPort.cpp b/src/SerialPort.cpp index 51b213d..3c3af99 100644 --- a/src/SerialPort.cpp +++ b/src/SerialPort.cpp @@ -533,26 +533,23 @@ namespace CppLinuxSerial { // is called ssize_t n = read(fileDesc_, &readBuffer_[0], readBufferSize_B_); - if(n == 0) - { - struct termios2 term2; - int rv = ioctl(fileDesc_, TCGETS2, &term2); - - if(rv != 0) - { - throw std::system_error(EFAULT, std::system_category()); - } - } - // Error Handling if(n < 0) { // Read was unsuccessful throw std::system_error(EFAULT, std::system_category()); } - - if(n > 0) { + else if(n > 0) { data = std::string(&readBuffer_[0], n); } + else if(n == 0) { + // n == 0 means EOS, but also returned on device disconnection. We try to get termios2 to distinguish two these two states + struct termios2 term2; + int rv = ioctl(fileDesc_, TCGETS2, &term2); + + if(rv != 0) { + throw std::system_error(EFAULT, std::system_category()); + } + } // If code reaches here, read must of been successful } From a8dea5a5df55ba9f7dd11d88fbf5816986ab1668 Mon Sep 17 00:00:00 2001 From: Geoffrey Hunter Date: Thu, 2 Feb 2023 17:05:21 +1300 Subject: [PATCH 3/3] Added the disconnection check to ReadBinary(). Updated the README with some Arduino testing info. --- CHANGELOG.md | 5 ++++ README.md | 42 ++++++++++++++++++++++++++- include/CppLinuxSerial/SerialPort.hpp | 7 ++++- src/SerialPort.cpp | 16 +++++++--- test/arduino/main.cpp | 20 +++++++++++-- 5 files changed, 82 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d63fc59..03a6388 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [v2.6.0] - 2023-02-02 + +- `Read()` and `ReadBinary()` now throw exceptions if they detect that the serial device has been disconnected (thanks to [aldoshkind](https://github.com/aldoshkind) for helping with this one). +- Added Arduino testing instructions to the README. + ## [v2.5.0] - 2022-11-12 - Replaced all tabs in code with spaces, which should fix the ugly code rendering in GitHub. diff --git a/README.md b/README.md index 94bea0e..8e31f60 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,47 @@ Attaching the Arduino Uno (need to be done with Admin priviliges the first time usbipd wsl attach --busid=1-5 ``` -`/dev/ttyACM0` now appears inside WSL, and you can use CppLinuxSerial with this device like usual. +`/dev/ttyACM0` now appears inside WSL, and you can use `CppLinuxSerial` with this device like usual. + +NOTE: Sometimes `/dev/ttyACM0` is not part of the dialout group, so even with your user being part of that group, you will get permission denied errors when trying to access the serial port. Sometimes using `chmod` to change the permissions works: + +``` +sudo chmod 666 /dev/ttyACM0 +``` + +## Tests + +### Prerequisties + +Install arduino:avr platform: + +``` +$ arduino-cli core install arduino:avr +``` + +Detect attached Arduino boards with: + +``` +$ arduino-cli board list +``` + +Compile: + +``` +arduino-cli compile --fqbn arduino:avr:uno Basic/ +``` + +Upload: + +``` +arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:avr:uno Basic/ +``` + +### Running + +``` +g++ main.cpp -lCppLinuxSerial +``` ## Changelog diff --git a/include/CppLinuxSerial/SerialPort.hpp b/include/CppLinuxSerial/SerialPort.hpp index bb4ca30..b2ce252 100644 --- a/include/CppLinuxSerial/SerialPort.hpp +++ b/include/CppLinuxSerial/SerialPort.hpp @@ -175,14 +175,19 @@ namespace mn { /// \param data The object the read characters from the COM port will be saved to. /// \param wait_ms The amount of time to wait for data. Set to 0 for non-blocking mode. Set to -1 /// to wait indefinitely for new data. - /// \throws CppLinuxSerial::Exception if state != OPEN. + /// \note Use ReadBinary() if you want to interpret received data as binary. + /// \throws + /// CppLinuxSerial::Exception if state != OPEN. + /// std::system_error() if device has been disconnected. void Read(std::string& data); /// \brief Use to read binary data from the COM port. /// \param data The object the read uint8_t bytes from the COM port will be saved to. /// \param wait_ms The amount of time to wait for data. Set to 0 for non-blocking mode. Set to -1 /// to wait indefinitely for new data. + /// \note Use Read() if you want to interpret received data as a string. /// \throws CppLinuxSerial::Exception if state != OPEN. + /// std::system_error() if device has been disconnected. void ReadBinary(std::vector& data); /// \brief Use to get number of bytes available in receive buffer. diff --git a/src/SerialPort.cpp b/src/SerialPort.cpp index 3c3af99..f8aa2b7 100644 --- a/src/SerialPort.cpp +++ b/src/SerialPort.cpp @@ -538,9 +538,6 @@ namespace CppLinuxSerial { // Read was unsuccessful throw std::system_error(EFAULT, std::system_category()); } - else if(n > 0) { - data = std::string(&readBuffer_[0], n); - } else if(n == 0) { // n == 0 means EOS, but also returned on device disconnection. We try to get termios2 to distinguish two these two states struct termios2 term2; @@ -550,6 +547,9 @@ namespace CppLinuxSerial { throw std::system_error(EFAULT, std::system_category()); } } + else if(n > 0) { + data = std::string(&readBuffer_[0], n); + } // If code reaches here, read must of been successful } @@ -575,8 +575,16 @@ namespace CppLinuxSerial { // Read was unsuccessful throw std::system_error(EFAULT, std::system_category()); } + else if(n == 0) { + // n == 0 means EOS, but also returned on device disconnection. We try to get termios2 to distinguish two these two states + struct termios2 term2; + int rv = ioctl(fileDesc_, TCGETS2, &term2); - if(n > 0) { + if(rv != 0) { + throw std::system_error(EFAULT, std::system_category()); + } + } + else if(n > 0) { copy(readBuffer_.begin(), readBuffer_.begin() + n, back_inserter(data)); } diff --git a/test/arduino/main.cpp b/test/arduino/main.cpp index b577d96..3a3352c 100644 --- a/test/arduino/main.cpp +++ b/test/arduino/main.cpp @@ -2,6 +2,15 @@ using namespace mn::CppLinuxSerial; +// Overload to be able to print vectors to std::cout +template +std::ostream& operator<<( std::ostream& ostrm, const std::vector& vec ){ + for( int j = 0, n = vec.size(); j < n; ++j ){ + ostrm << ",["[ !j ] << " " << vec[ j ]; + } + return ostrm << " ]"; +} + int main() { // Create serial port object and open serial port SerialPort serialPort("/dev/ttyACM0", BaudRate::B_9600, NumDataBits::EIGHT, Parity::NONE, NumStopBits::ONE); @@ -13,9 +22,16 @@ int main() { // serialPort0.Write("Hello"); // Read some data back + // while(1) { + // std::string readData; + // serialPort.Read(readData); + // std::cout << "Received data: " << readData; + // } + + // Read some data back (raw) while(1) { - std::string readData; - serialPort.Read(readData); + std::vector readData; + serialPort.ReadBinary(readData); std::cout << "Received data: " << readData; }