Skip to content

Commit

Permalink
Merge pull request #35 from gbmhunter/develop
Browse files Browse the repository at this point in the history
Reads no longer clear containers. Local testing improvements.
  • Loading branch information
gbmhunter authored Feb 12, 2023
2 parents 71e8887 + ae0633d commit 3c929be
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 57 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ cmake-build-debug/
docs/_build/

# Build artifacts
a.out
a.out
test/arduino/test
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.7.0] - 2023-02-13

- `Read()` and `ReadBinary()` now append to the provided data containers (string or vector) rather than erase and write.
- Added `run.sh` bash script for running local serial port tests with connected Arduino Uno (see README). Updated local tests to write and read back data in both string and binary forms.

## [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).
Expand Down
39 changes: 24 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,19 @@ int main() {
// and no flow control
SerialPort serialPort("/dev/ttyUSB0", BaudRate::B_57600, NumDataBits::EIGHT, Parity::NONE, NumStopBits::ONE);
// Use SerialPort serialPort("/dev/ttyACM0", 13000); instead if you want to provide a custom baud rate
serialPort.SetTimeout(-1); // Block when reading until any data is received
serialPort.SetTimeout(100); // Block for up to 100ms to receive data
serialPort.Open();

// WARNING: If using the Arduino Uno or similar, you may want to delay here, as opening the serial port causes
// the micro to reset!

// Write some ASCII data
serialPort.Write("Hello");

// Read some data back (will block until at least 1 byte is received due to the SetTimeout(-1) call above)
// Read some data back (will block for up to 100ms due to the SetTimeout(100) call above)
std::string readData;
serialPort.Read(readData);
std::cout << "Read data = \"" << readData << "\"" << std::endl;

// Close the serial port
serialPort.Close();
Expand Down Expand Up @@ -153,37 +157,42 @@ sudo chmod 666 /dev/ttyACM0

## Tests

Serial port testing cannot really be done easily on cloud-based CICD platforms, as serial ports and devices connected to these ports are not readibly available (nor configurable). `CppLinuxSerial` relies on running tests manually on your local Linux OS, alongside a connected Arduino Uno configured to echo serial data back (at a later data this could be reconfigured to cycle through tests at different baud rates, parity settings, e.t.c).

### Prerequisties

Install arduino:avr platform:
You will need:

* Arduino Uno (or equivalent) dev kit.
* Linux OS.

Install the arduino-cli as per https://arduino.github.io/arduino-cli/0.21/installation/ on your local Linux machine.

Install the `arduino:avr` platform:

```
$ arduino-cli core install arduino:avr
```

Detect attached Arduino boards with:
Make sure Arduino board is detected with:

```
$ arduino-cli board list
```

Compile:

```
arduino-cli compile --fqbn arduino:avr:uno Basic/
```
### Running

Upload:
Run the following bash script:

```
arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:avr:uno Basic/
./test/arduino/run.sh
```

### Running
This script will:

```
g++ main.cpp -lCppLinuxSerial
```
1. Build and install `CppLinuxSerial` onto your local Linux OS.
1. Build and upload the test Arduino firmware to the connected Arduino Uno (it assumes it's connected to `/dev/ttyACM0`).
1. Build and run the test C++ application. This sends serial data to the Uno via CppLinuxSerial and expects the data to be echoed back.

## Changelog

Expand Down
14 changes: 5 additions & 9 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 2022-11-12
/// \last-modified 2023-02-13
/// \brief The main serial port class.
/// \details
/// See README.rst in repo root dir for more info.
Expand Down Expand Up @@ -171,20 +171,16 @@ namespace mn {
/// \throws CppLinuxSerial::Exception if state != OPEN.
void WriteBinary(const std::vector<uint8_t>& data);

/// \brief Use to read text from the COM port.
/// \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.
/// \brief Use to read text from the COM port. Blocking nature depends on SetTimeout().
/// \param data The read characters from the COM port will be appended to this string.
/// \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.
/// \brief Use to read binary data from the COM port. Blocking nature depends on SetTimeout().
/// \param data The read bytes from the COM port will be appended to this vector.
/// \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.
Expand Down
22 changes: 7 additions & 15 deletions src/SerialPort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! @file SerialPort.cpp
//! @author Geoffrey Hunter <[email protected]> (www.mbedded.ninja)
//! @created 2014-01-07
//! @last-modified 2022-11-11
//! @last-modified 2023-02-13
//! @brief The main serial port class.
//! @details
//! See README.rst in repo root dir for more info.
Expand Down Expand Up @@ -517,10 +517,7 @@ namespace CppLinuxSerial {
}
}

void SerialPort::Read(std::string& data)
{
data.clear();

void SerialPort::Read(std::string& data) {
if(fileDesc_ == 0) {
//this->sp->PrintError(SmartPrint::Ss() << "Read() was called but file descriptor (fileDesc) was 0, indicating file has not been opened.");
//return false;
Expand Down Expand Up @@ -548,16 +545,13 @@ namespace CppLinuxSerial {
}
}
else if(n > 0) {
data = std::string(&readBuffer_[0], n);
data += std::string(&readBuffer_[0], n);
}

// If code reaches here, read must of been successful
}

void SerialPort::ReadBinary(std::vector<uint8_t>& data)
{
data.clear();

void SerialPort::ReadBinary(std::vector<uint8_t>& data) {
if(fileDesc_ == 0) {
//this->sp->PrintError(SmartPrint::Ss() << "Read() was called but file descriptor (fileDesc) was 0, indicating file has not been opened.");
//return false;
Expand All @@ -574,18 +568,16 @@ namespace CppLinuxSerial {
if(n < 0) {
// Read was unsuccessful
throw std::system_error(EFAULT, std::system_category());
}
else if(n == 0) {
} 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());
}
}
else if(n > 0) {
copy(readBuffer_.begin(), readBuffer_.begin() + n, back_inserter(data));
} else if(n > 0) {
std::copy(readBuffer_.begin(), readBuffer_.begin() + n, back_inserter(data));
}

// If code reaches here, read must of been successful
Expand Down
10 changes: 6 additions & 4 deletions test/arduino/Basic/Basic.ino
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
// Serial.begin(9600);
Serial.begin(9600);
// Serial.begin(9600, SERIAL_5N1); // 5 data bits, no parity, 1 stop bit
Serial.begin(9600, SERIAL_8E2); // 8 data bits, even parity, 2 stop bits
// Serial.begin(9600, SERIAL_8E2); // 8 data bits, even parity, 2 stop bits
// Serial.begin(81234); // Used to test custom baud rates
}

// the loop routine runs over and over again forever:
void loop() {
Serial.println("Hello");
delay(100); // delay in between reads for stability
if (Serial.available() > 0) {
char c = Serial.read();
Serial.print(c);
}
}
75 changes: 62 additions & 13 deletions test/arduino/main.cpp
Original file line number Diff line number Diff line change
@@ -1,40 +1,89 @@
#include <chrono>
#include <thread>

#include <CppLinuxSerial/SerialPort.hpp>

using namespace mn::CppLinuxSerial;

// Overload to be able to print vectors to std::cout
template <typename T>
std::ostream& operator<<( std::ostream& ostrm, const std::vector<T>& vec ){
if (vec.size() == 0) {
return ostrm << "[]";
}
for( int j = 0, n = vec.size(); j < n; ++j ){
ostrm << ",["[ !j ] << " " << vec[ j ];
}
return ostrm << " ]";
}

const double RX_TIMEOUT_MS = 2000;

int main() {
// Create serial port object and open serial port
SerialPort serialPort("/dev/ttyACM0", BaudRate::B_9600, NumDataBits::EIGHT, Parity::NONE, NumStopBits::ONE);
// SerialPort serialPort("/dev/ttyACM0", 13000);
serialPort.SetTimeout(-1); // Block when reading until any data is received

// Block for at most 100ms when receiving data
// NOTE: I haven't had luck setting this to -1, Read() seems to never return
// even though serial data has been sent to Linux
serialPort.SetTimeout(100);

std::cout << "Opening /dev/ttyACM0 at 9600 baud, 8n1..." << std::flush;
serialPort.Open();
std::cout << "OK." << std::endl;

// Write some ASCII datae
// serialPort0.Write("Hello");
std::cout << "Sleeping to allow Arduino to restart (happens after opening serial port)..." << std::flush;
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "OK." << std::endl;

// Read some data back
// while(1) {
// std::string readData;
// serialPort.Read(readData);
// std::cout << "Received data: " << readData;
// }
// Write some ASCII data and read it back
std::cout << "Writing string data..." << std::flush;
std::string txDataString = "Hello";
serialPort.Write(txDataString);
std:: cout << "OK." << std::endl;

std::cout << "Reading string data..." << std::flush;
std::string rxDataString;
auto t_start = std::chrono::high_resolution_clock::now();
while(1) {
serialPort.Read(rxDataString);
if (rxDataString == txDataString) {
std::cout << "OK." << std::endl;
break;
}
auto t_now = std::chrono::high_resolution_clock::now();
double elapsed_time_ms = std::chrono::duration<double, std::milli>(t_now - t_start).count();
if (elapsed_time_ms >= RX_TIMEOUT_MS) {
std::cout << "ERROR: Did not receive the string data \"Hello\" from Arduino." << std::endl;
return -1;
}
}

// Read some data back (raw)
// Write some binary data and read it back
std::cout << "Writing binary data..." << std::flush;
std::vector<uint8_t> txDataBinary{ 1, 2, 3, 4, 5 };
serialPort.WriteBinary(txDataBinary);
std:: cout << "OK." << std::endl;

std::cout << "Reading binary data..." << std::flush;
std::vector<uint8_t> rxDataBinary;
t_start = std::chrono::high_resolution_clock::now();
while(1) {
std::vector<unsigned char> readData;
serialPort.ReadBinary(readData);
std::cout << "Received data: " << readData;
serialPort.ReadBinary(rxDataBinary);
if (rxDataBinary == txDataBinary) {
std::cout << "OK." << std::endl;
break;
}
auto t_now = std::chrono::high_resolution_clock::now();
double elapsed_time_ms = std::chrono::duration<double, std::milli>(t_now - t_start).count();
if (elapsed_time_ms >= RX_TIMEOUT_MS) {
std::cout << "ERROR: Did not receive the binary data from Arduino." << std::endl;
return -1;
}
}

// Close the serial port
serialPort.Close();
return 0;
}
20 changes: 20 additions & 0 deletions test/arduino/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# exit when any command fails
set -e

echo "Building/installing CppLinuxSerial..."
cd build/
cmake ..
sudo make install

echo "Building test application..."
cd ../test/arduino
g++ main.cpp -lCppLinuxSerial -o test

echo "Compiling Arduino firmware..."
arduino-cli compile --fqbn arduino:avr:uno ./Basic/

echo "Uploading Arduino firmware..."
arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:avr:uno ./Basic/

echo "Running test application..."
./test

0 comments on commit 3c929be

Please sign in to comment.