Skip to content

Commit

Permalink
Split the hardware and software flow control settings. Added an example.
Browse files Browse the repository at this point in the history
  • Loading branch information
gbmhunter committed Nov 11, 2022
1 parent 7186684 commit d7833f5
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 39 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [vX.X.X]

- Replaced all tabs in code with spaces, which should fix the ugly code rendering in GitHub.
- Added ability to set/change the flow control (hardware, software or none).
- Added ability to set/change both hardware and software flow control.
- Added some ready-to-run examples in the new `example/` directory.
- Added a section in the README on WSL usage.

## [v2.4.0] - 2022-02-12

Expand Down
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,11 @@ If the above code was in a file called `main.cpp` and you had installed `CppLinu
g++ main.cpp -lCppLinuxSerial
```

If you wanted to also set flow control (e.g. hardware), you can add it onto the end of the constructor as shown below. If you don't set it, it defaults to no flow control.
If you wanted to enable flow control (hardware or software flow control), you can add it onto the end of the constructor as shown below. If you don't set them, they both default to OFF (the most common setting).

```c++
SerialPort serialPort("/dev/ttyUSB0", BaudRate::B_57600, NumDataBits::EIGHT, Parity::NONE, NumStopBits::ONE, FlowControl::HARDWARE);
// Enabling hardware flow control
SerialPort serialPort("/dev/ttyUSB0", BaudRate::B_57600, NumDataBits::EIGHT, Parity::NONE, NumStopBits::ONE, HardwareFlowControl::ON, SoftwareFlowControl::OFF);
```
If you want to read and write binary rather than strings, you can use `WriteBinary()` and `ReadBinary()` which take vectors of bytes rather than `std::string`:
Expand All @@ -113,8 +114,37 @@ See GitHub Issues.

## FAQ

1. I get the error `Could not open device "/dev/ttyACM0". Is the device name correct and do you have read/write permissions?`, but the device is definitely there. You typically have to add your user to the `dialout` group before you can access `tty` devices.

1. My code stalls when calling functions like `SerialPort::Read()`. This is probably because the library is set up to do a blocking read, and not enough characters have been received to allow `SerialPort::Read()` to return. Call `SerialPort::SetTimeout(0)` before the serial port is open to set a non-blocking mode.

## WSL

If you want to use this library in WSL, you'll have to use usbipd to pass-through the USB device.

```
usbipd wsl list
```

```powershell
$ usbipd wsl list
BUSID VID:PID DEVICE STATE
1-1 046d:c332 USB Input Device Not attached
1-4 13d3:5666 USB2.0 HD UVC WebCam Not attached
1-5 2341:0043 Arduino Uno (COM4) Not attached
1-6 046d:0a9c Logitech G432 Gaming Headset, USB Input Device Not attached
1-8 0b05:1837 USB Input Device Not attached
1-9 8087:0a2a Intel(R) Wireless Bluetooth(R) Not attached
```

Attaching the Arduino Uno (need to be done with Admin priviliges the first time around):

```
usbipd wsl attach --busid=1-5
```

`/dev/ttyACM0` now appears inside WSL, and you can use CppLinuxSerial with this device like usual.

## Changelog

See CHANGELOG.md.
42 changes: 36 additions & 6 deletions examples/FlowControl.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,45 @@
#include <chrono>
#include <thread>

#include "CppLinuxSerial/SerialPort.hpp"

using namespace std::chrono_literals;
using namespace mn::CppLinuxSerial;

int main() {
SerialPort serialPort("/dev/ttyUSB0", BaudRate::B_9600, NumDataBits::EIGHT, Parity::NONE, NumStopBits::ONE, FlowControl::NONE);
serialPort.SetTimeout(-1); // Block when reading until any data is received
// This example relies on a serial device which echos serial data at 9600 baud, 8n1.
std::cout << "FlowControll.cpp::main() called." << std::endl;
SerialPort serialPort("/dev/ttyACM0", BaudRate::B_9600, NumDataBits::EIGHT, Parity::NONE, NumStopBits::ONE, HardwareFlowControl::ON, SoftwareFlowControl::OFF);
serialPort.SetTimeout(1000); // Block when reading for 1000ms

serialPort.Open();
serialPort.Write("Hello");
std::string readData;
serialPort.Read(readData);
std::cout << "readData: " << readData << std::endl;

std::this_thread::sleep_for(100ms);

std::thread t1([&]() {
// Do Something
for (int x = 0; x < 10; x++) {
// std::this_thread::sleep_for(100ms);
std::cout << "Reading" << std::endl;
std::string readData;
serialPort.Read(readData);
std::cout << "readData: " << readData << std::endl;
}
});

std::thread t2([&]() {
// Do Something
std::this_thread::sleep_for(100ms);
for (int x = 0; x < 10; x++) {
std::this_thread::sleep_for(100ms);
std::cout << "Writing \"Hello\"" << std::endl;
serialPort.Write("Hello");
}
});

t1.join();
t2.join();

serialPort.Close();
return 0;
}
28 changes: 18 additions & 10 deletions include/CppLinuxSerial/SerialPort.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/// \file SerialPort.hpp
/// \author Geoffrey Hunter <[email protected]> (www.mbedded.ninja)
/// \created 2014-01-07
/// \last-modified 2019-05-30
/// \last-modified 2022-11-12
/// \brief The main serial port class.
/// \details
/// See README.rst in repo root dir for more info.
Expand Down Expand Up @@ -81,11 +81,16 @@ namespace mn {
TWO,
};

/// \brief All the possible options for setting the flow control.
enum class FlowControl {
NONE,
HARDWARE,
SOFTWARE,
/// \brief All the possible options for setting the hardware flow control.
enum class HardwareFlowControl {
OFF,
ON,
};

/// \brief All the possible options for setting the software flow control.
enum class SoftwareFlowControl {
OFF,
ON,
};

/// \brief Represents the state of the serial port.
Expand All @@ -108,7 +113,8 @@ namespace mn {
SerialPort(const std::string &device, BaudRate baudRate, NumDataBits numDataBits, Parity parity, NumStopBits numStopBits);

/// \brief Constructor that sets up serial port and allows the user to specify all the common parameters and flow control.
SerialPort(const std::string &device, BaudRate baudRate, NumDataBits numDataBits, Parity parity, NumStopBits numStopBits, FlowControl flow);
SerialPort(const std::string &device, BaudRate baudRate, NumDataBits numDataBits, Parity parity,
NumStopBits numStopBits, HardwareFlowControl hardwareFlowControl, SoftwareFlowControl softwareFlowControl);

/// \brief Constructor that sets up serial port with the basic parameters, and a custom baud rate.
SerialPort(const std::string &device, speed_t baudRate);
Expand Down Expand Up @@ -226,8 +232,11 @@ namespace mn {
/// \brief The num. of stop bits. Defaults to 1 (most common).
NumStopBits numStopBits_ = NumStopBits::ONE;

/// \brief The flow control of the system. Defaults to None (most common).
FlowControl flowControl_ = FlowControl::NONE;
/// \brief The hardware flow control setting. Defaults to OFF (most common).
HardwareFlowControl hardwareFlowControl_ = HardwareFlowControl::OFF;

/// \brief The software flow control setting. Defaults to OFF (most common).
SoftwareFlowControl softwareFlowControl_ = SoftwareFlowControl::OFF;

/// \brief The file descriptor for the open file. This gets written to when Open() is called.
int fileDesc_;
Expand All @@ -243,7 +252,6 @@ namespace mn {
static constexpr int32_t defaultTimeout_ms_ = -1;
static constexpr unsigned char defaultReadBufferSize_B_ = 255;


};

} // namespace CppLinuxSerial
Expand Down
43 changes: 23 additions & 20 deletions src/SerialPort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,17 @@ namespace CppLinuxSerial {
numStopBits_ = numStopBits;
}

SerialPort::SerialPort(const std::string &device, BaudRate baudRate, NumDataBits numDataBits, Parity parity, NumStopBits numStopBits, FlowControl flowControl):
SerialPort::SerialPort(const std::string &device, BaudRate baudRate, NumDataBits numDataBits, Parity parity, NumStopBits numStopBits,
HardwareFlowControl hardwareFlowControl, SoftwareFlowControl softwareFlowControl):
SerialPort() {
device_ = device;
baudRateType_ = BaudRateType::STANDARD;
baudRateStandard_ = baudRate;
numDataBits_ = numDataBits;
parity_ = parity;
numStopBits_ = numStopBits;
flowControl_ = flowControl;
hardwareFlowControl_ = hardwareFlowControl;
softwareFlowControl_ = softwareFlowControl;
}

SerialPort::~SerialPort() {
Expand Down Expand Up @@ -216,18 +218,19 @@ namespace CppLinuxSerial {
}

// Configure flow control
switch(flowControl_){
case FlowControl::NONE:
tty.c_cflag &= ~CRTSCTS;
break;
switch(hardwareFlowControl_){
case HardwareFlowControl::OFF:
tty.c_cflag &= ~CRTSCTS;
break;

case FlowControl::HARDWARE: // Hardware flow control (RTS/CTS)
tty.c_cflag |= CRTSCTS;
break;
case HardwareFlowControl::ON: // Hardware flow control (RTS/CTS)
tty.c_cflag |= CRTSCTS;
break;

default:
// We should never get here.
THROW_EXCEPT("flowControl_ value not supported!");
break;
break;
}

tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
Expand Down Expand Up @@ -434,32 +437,32 @@ namespace CppLinuxSerial {

//======================== (.c_iflag) ====================//

switch(flowControl_){
case FlowControl::NONE:
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
switch(softwareFlowControl_){
case SoftwareFlowControl::OFF:
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
break;

case FlowControl::SOFTWARE:
tty.c_iflag |= (IXON | IXOFF | IXANY);
case SoftwareFlowControl::ON:
tty.c_iflag |= (IXON | IXOFF | IXANY);
break;
}
// Turn off s/w flow ctrl

tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);

//=========================== LOCAL MODES (c_lflag) =======================//

// Canonical input is when read waits for EOL or EOF characters before returning. In non-canonical mode, the rate at which
// read() returns is instead controlled by c_cc[VMIN] and c_cc[VTIME]
tty.c_lflag &= ~ICANON; // Turn off canonical input, which is suitable for pass-through
tty.c_lflag &= ~ICANON; // Turn off canonical input, which is suitable for pass-through
// Configure echo depending on echo_ boolean
if(echo_) {
tty.c_lflag |= ECHO;
} else {
tty.c_lflag &= ~(ECHO);
}
tty.c_lflag &= ~ECHOE; // Turn off echo erase (echo erase only relevant if canonical input is active)
tty.c_lflag &= ~ECHONL; //
tty.c_lflag &= ~ISIG; // Disables recognition of INTR (interrupt), QUIT and SUSP (suspend) characters
tty.c_lflag &= ~ECHOE; // Turn off echo erase (echo erase only relevant if canonical input is active)
tty.c_lflag &= ~ECHONL; //
tty.c_lflag &= ~ISIG; // Disables recognition of INTR (interrupt), QUIT and SUSP (suspend) characters


// Try and use raw function call
Expand Down

0 comments on commit d7833f5

Please sign in to comment.