├── .gitignore ├── examples ├── file_read_write.h ├── build.sh ├── serial_reader.h ├── command_parser.h ├── CMakeLists.txt ├── serial_reader.cpp ├── file_read_write.cpp └── command_parser.cpp ├── src ├── order.h ├── robust_serial.hpp └── robust_serial.cpp ├── .github └── workflows │ └── ci.yml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | bin/ 3 | *.x 4 | *.log 5 | -------------------------------------------------------------------------------- /examples/file_read_write.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_READ_WRITE_H 2 | #define FILE_READ_WRITE_H 3 | #include 4 | 5 | #endif 6 | -------------------------------------------------------------------------------- /examples/build.sh: -------------------------------------------------------------------------------- 1 | # Build and copy executables to bin/ dir 2 | mkdir -p build 3 | mkdir -p bin 4 | cd build 5 | cmake .. 6 | make 7 | cp *.x ../bin 8 | -------------------------------------------------------------------------------- /examples/serial_reader.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIAL_READER_H 2 | #define SERIAL_READER_H 3 | #include 4 | 5 | #define DEFAULT_PORT "/dev/ttyACM0" 6 | #define MAX_N_ORDER 1000 // Max number of orders that can be received 7 | #define TIMEOUT_MS 30000 // Timeout in milliseconds -> 30s 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/order.h: -------------------------------------------------------------------------------- 1 | #ifndef ORDER_H 2 | #define ORDER_H 3 | 4 | // Define the orders that can be sent and received 5 | enum Order { 6 | HELLO = 0, 7 | SERVO = 1, 8 | MOTOR = 2, 9 | ALREADY_CONNECTED = 3, 10 | ERROR = 4, 11 | RECEIVED = 5, 12 | STOP = 6, 13 | }; 14 | 15 | typedef enum Order Order; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /examples/command_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMAND_PARSER_H 2 | #define COMMAND_PARSER_H 3 | #include 4 | 5 | #define PORT "/dev/ttyACM0" 6 | 7 | int getIntFromUserInput(std::string infoMessage); 8 | unsigned int getUnsignedIntFromUserInput(std::string infoMessage); 9 | long getLongIntFromUserInput(std::string infoMessage); 10 | 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.25 FATAL_ERROR) 2 | set(CMAKE_CXX_STANDARD 11) 3 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic") 4 | project(RobustSerialExamples) 5 | 6 | 7 | # TODO: add the require for serial 8 | # TODO: transform it into a shared library 9 | 10 | include_directories(../src) 11 | 12 | # Command Parser 13 | add_executable(command_parser.x command_parser.cpp ../src/robust_serial.cpp) 14 | target_link_libraries(command_parser.x serial pthread) 15 | 16 | # Serial Reader (require serial lib) 17 | add_executable(serial_reader.x serial_reader.cpp ../src/robust_serial.cpp) 18 | target_link_libraries(serial_reader.x serial pthread) 19 | 20 | # File read write example 21 | add_executable(file_read_write.x file_read_write.cpp ../src/robust_serial.cpp) 22 | target_link_libraries(file_read_write.x serial pthread) 23 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: CI 5 | 6 | on: 7 | push: 8 | branches: [master] 9 | pull_request: 10 | branches: [master] 11 | 12 | jobs: 13 | build: 14 | env: 15 | TERM: xterm-256color 16 | FORCE_COLOR: 1 17 | 18 | # Skip CI if [ci skip] in the commit message 19 | if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Install dependencies 24 | run: | 25 | sudo apt-get install libserial-dev 26 | 27 | - name: Build and run examples 28 | run: | 29 | cd examples/ 30 | ./build.sh 31 | ./bin/file_read_write.x -y 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Antonin RAFFIN 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Robust Arduino Serial Protocol in C++ 2 | 3 | [![CI](https://github.com/araffin/cpp-arduino-serial/workflows/CI/badge.svg)](https://github.com/araffin/cpp-arduino-serial/actions/workflows/ci.yml) 4 | 5 | 6 | **Robust Arduino Serial** is a simple and robust serial communication protocol. It was designed to make two Arduinos communicate, but can also be useful when you want a computer (e.g. a Raspberry Pi) to communicate with an Arduino. 7 | 8 | This repository is part of the Robust Arduino Serial project, main repository: [https://github.com/araffin/arduino-robust-serial](https://github.com/araffin/arduino-robust-serial) 9 | 10 | **Please read the [Medium Article](https://medium.com/@araffin/simple-and-robust-computer-arduino-serial-communication-f91b95596788) to have an overview of this protocol.** 11 | 12 | Implementations are available in various programming languages: 13 | 14 | - [Arduino](https://github.com/araffin/arduino-robust-serial) 15 | - [Python](https://github.com/araffin/python-arduino-serial) 16 | - [C++](https://github.com/araffin/cpp-arduino-serial) 17 | - [Rust](https://github.com/araffin/rust-arduino-serial) 18 | 19 | ## Dependency 20 | 21 | Dependency (libserial): 22 | ```bash 23 | sudo apt-get install libserial-dev 24 | ``` 25 | 26 | ## Examples 27 | 28 | To build the two examples, run the `build.sh` inside the `examples/` directory, the binaries will be located in the `bin/` folder. 29 | 30 | Read write in a file 31 | ```bash 32 | ./file_read_write.x 33 | ``` 34 | 35 | If you want to use `command_parser` and `serial_reader` with a real Arduino, you need to flash it with this code: 36 | [Arduino Source Code](https://github.com/araffin/arduino-robust-serial/tree/master/arduino-board/) 37 | 38 | Run the command parser to send order to the Arduino (optional: you can run it using rlwrap): 39 | ```bash 40 | ./command_parser.x 41 | ``` 42 | 43 | Idem for listening to the serial port (for now during 30 seconds, it can be changed in the serial_reader.h) 44 | ```bash 45 | ./serial_reader.x 46 | ``` 47 | -------------------------------------------------------------------------------- /src/robust_serial.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ROBUST_SERIAL_H 2 | #define ROBUST_SERIAL_H 3 | #include 4 | #include // int8_t, int16_t, ... 5 | // libserial-dev 6 | #include 7 | #include 8 | #include 9 | #include "order.h" 10 | 11 | using namespace LibSerial; 12 | 13 | /** 14 | * Send one order (one byte) 15 | * @param order the order to send 16 | */ 17 | void write_order(std::fstream &file, enum Order order); 18 | 19 | /** 20 | * Write one byte int to a file/serial port 21 | * @param file fstream object 22 | * @param num 23 | */ 24 | void write_i8(std::fstream &file, int8_t num); 25 | 26 | /** 27 | * Write two bytes int to a file/serial port. 28 | * @param file fstream object 29 | * @param num the number to send 30 | */ 31 | void write_i16(std::fstream &file, int16_t num); 32 | 33 | /** 34 | * Write four bytes int to a file/serial port. 35 | * @param num the number to send (−2,147,483,647, +2,147,483,647) 36 | */ 37 | void write_i32(std::fstream &file, int32_t num); 38 | 39 | /** 40 | * Send a two bytes unsigned (max 2**16 -1) int via the serial 41 | * @param num the number to send 42 | */ 43 | void write_u16(std::fstream &file, uint16_t num); 44 | 45 | /** 46 | * Read one byte from the serial and cast it to an Order 47 | * @param serial_port SerialPort object 48 | * @param msTimeout Read Timeout in ms 49 | * @return the order received 50 | */ 51 | Order read_order(SerialPort &serial_port, const unsigned int timeout_ms); 52 | Order read_order(std::fstream &file); 53 | 54 | /** 55 | * Read one byte from a serial port and convert it to a 8 bits int 56 | * @param serial_port SerialPort object 57 | * @return the decoded number 58 | */ 59 | int8_t read_i8(SerialPort &serial_port); 60 | // Variant for reading file 61 | int8_t read_i8(std::fstream &file); 62 | 63 | /** 64 | * Read two bytes from a serial port and convert it to a 16 bits int 65 | * @param serial_port SerialPort object 66 | * @return the decoded number 67 | */ 68 | int16_t read_i16(SerialPort &serial_port); 69 | // Variant for reading file 70 | int16_t read_i16(std::fstream &file); 71 | /** 72 | * Read two bytes from a serial port and convert it to a 16 bits unsigned int 73 | * @param serial_port SerialPort object 74 | * @return the decoded number 75 | */ 76 | uint16_t read_u16(SerialPort &serial_port); 77 | 78 | /** 79 | * Read four bytes from a serial port and convert it to a 32 bits int 80 | * @param serial_port SerialPort object 81 | * @return the decoded number 82 | */ 83 | int32_t read_i32(SerialPort &serial_port); 84 | // Variant for reading file 85 | int32_t read_i32(std::fstream &file); 86 | 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/robust_serial.cpp: -------------------------------------------------------------------------------- 1 | #include "robust_serial.hpp" 2 | 3 | using namespace std; 4 | 5 | 6 | void write_order(fstream &file, enum Order order) 7 | { 8 | write_i8(file, (int8_t) order); 9 | } 10 | 11 | void write_i8(fstream &file, int8_t num) 12 | { 13 | file.write((char *)&num, sizeof(int8_t)); 14 | } 15 | 16 | void write_i16(fstream &file, int16_t num) 17 | { 18 | int8_t buffer[2] = {(int8_t) (num & 0xff), (int8_t) (num >> 8)}; 19 | file.write((char *)buffer, 2*sizeof(int8_t)); 20 | } 21 | 22 | void write_i32(fstream &file, int32_t num) 23 | { 24 | int8_t buffer[4] = {(int8_t) (num & 0xff), (int8_t) (num >> 8 & 0xff), (int8_t) (num >> 16 & 0xff), (int8_t) (num >> 24 & 0xff)}; 25 | file.write((char *)buffer, 4*sizeof(int8_t)); 26 | } 27 | 28 | void write_u16(fstream &file, uint16_t num) 29 | { 30 | uint8_t buffer[2] = {(uint8_t) (num & 0xff), (uint8_t) (num >> 8)}; 31 | file.write((char *)buffer, 2*sizeof(uint8_t)); 32 | } 33 | 34 | 35 | Order read_order(SerialPort &serial_port, const unsigned int timeout_ms) 36 | { 37 | DataBuffer buffer; 38 | serial_port.Read(buffer, 1, timeout_ms); 39 | return (Order) buffer[0]; 40 | } 41 | 42 | Order read_order(fstream &serial_file) 43 | { 44 | return (Order) read_i8(serial_file); 45 | } 46 | 47 | int8_t read_i8(SerialPort &serial_port) 48 | { 49 | DataBuffer buffer; 50 | serial_port.Read(buffer, 1); 51 | // We have to cast it to keep the sign 52 | return (int8_t) static_cast(buffer[0]); 53 | } 54 | 55 | int8_t read_i8(fstream &file) 56 | { 57 | char buffer[1]; 58 | file.read(buffer, 1); 59 | return (int8_t) buffer[0]; 60 | } 61 | 62 | int16_t read_i16(SerialPort &serial_port) 63 | { 64 | DataBuffer buffer; 65 | serial_port.Read(buffer, 2); 66 | return (((int16_t) buffer[0]) & 0xff) | (((int16_t) buffer[1]) << 8 & 0xff00); 67 | } 68 | 69 | int16_t read_i16(fstream &file) 70 | { 71 | char buffer[2]; 72 | file.read(buffer, 2); 73 | return (((int16_t) buffer[0]) & 0xff) | (((int16_t) buffer[1]) << 8 & 0xff00); 74 | } 75 | 76 | uint16_t read_u16(SerialPort &serial_port) 77 | { 78 | DataBuffer buffer; 79 | serial_port.Read(buffer, 2); 80 | return (((uint16_t) buffer[0]) & 0xff) | (((uint16_t) buffer[1]) << 8); 81 | } 82 | 83 | int32_t read_i32(SerialPort &serial_port) 84 | { 85 | DataBuffer buffer; 86 | serial_port.Read(buffer, 4); 87 | return (((int32_t) buffer[0]) & 0xff) | (((int32_t) buffer[1]) << 8 & 0xff00) | (((int32_t) buffer[2]) << 16 & 0xff0000) | (((int32_t) buffer[3]) << 24 & 0xff000000); 88 | } 89 | 90 | int32_t read_i32(fstream &file) 91 | { 92 | char buffer[4]; 93 | file.read(buffer, 4); 94 | return (((int32_t) buffer[0]) & 0xff) | (((int32_t) buffer[1]) << 8 & 0xff00) | (((int32_t) buffer[2]) << 16 & 0xff0000) | (((int32_t) buffer[3]) << 24 & 0xff000000); 95 | } 96 | -------------------------------------------------------------------------------- /examples/serial_reader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include //Binary representation 5 | #include "serial_reader.h" 6 | 7 | using namespace std; 8 | 9 | 10 | int main(int argc, char const *argv[]) 11 | { 12 | string default_filename = DEFAULT_PORT; 13 | string serial_filename = ""; 14 | 15 | cout << "Enter the name of the serial file (default: " << default_filename << " )" << endl; 16 | getline(cin, serial_filename); 17 | 18 | if(serial_filename.empty()) 19 | { 20 | cout << "Using default serial file : " << default_filename << endl; 21 | serial_filename = default_filename; 22 | } 23 | 24 | SerialPort serial_port; 25 | 26 | const unsigned int timeout_ms = TIMEOUT_MS; 27 | Order current_order; 28 | serial_port.Open(serial_filename); 29 | // Set serial port parameters 30 | serial_port.SetBaudRate(BaudRate::BAUD_115200); 31 | serial_port.SetCharacterSize(CharacterSize::CHAR_SIZE_8); 32 | serial_port.SetFlowControl(FlowControl::FLOW_CONTROL_NONE); 33 | serial_port.SetParity(Parity::PARITY_NONE); 34 | serial_port.SetStopBits(StopBits::STOP_BITS_1); 35 | 36 | 37 | for (size_t i = 0; i <= MAX_N_ORDER; i++) 38 | { 39 | // while (!serial_port.IsDataAvailable()){} 40 | try 41 | { 42 | current_order = read_order(serial_port, timeout_ms); 43 | // ReadTimeout 44 | } 45 | catch(exception& e) 46 | { 47 | std::cout << "Timeout!" << endl; 48 | serial_port.Close(); 49 | return 0; 50 | } 51 | 52 | switch (current_order) 53 | { 54 | case HELLO: 55 | { 56 | cout << "HELLO" << endl; 57 | break; 58 | } 59 | case SERVO: 60 | { 61 | int16_t angle = read_i16(serial_port); 62 | cout << "SERVO: angle=" << angle << endl; 63 | break; 64 | } 65 | case MOTOR: 66 | { 67 | int speed = read_i8(serial_port); 68 | cout << "MOTOR: speed=" << speed << endl; 69 | break; 70 | } 71 | case ALREADY_CONNECTED: 72 | { 73 | cout << "ALREADY_CONNECTED" << endl; 74 | break; 75 | } 76 | case ERROR: 77 | { 78 | int16_t errorCode = read_i16(serial_port); 79 | cout << "ERROR " << errorCode << endl; 80 | break; 81 | } 82 | case RECEIVED: 83 | { 84 | cout << "RECEIVED" << endl; 85 | break; 86 | } 87 | case STOP: 88 | { 89 | cout << "STOP!" << endl; 90 | break; 91 | } 92 | default: 93 | { 94 | bitset<8> b(current_order); 95 | cout << "Unknown command:" << b << endl; 96 | } 97 | } 98 | } 99 | 100 | serial_port.Close(); 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /examples/file_read_write.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include //Binary representation 5 | #include "file_read_write.h" 6 | 7 | using namespace std; 8 | 9 | fstream serial_file; 10 | 11 | int main(int argc, char const *argv[]) 12 | { 13 | string default_filename = "test.log"; 14 | string serial_filename = ""; 15 | 16 | // Skip the prompt if an argument is passed 17 | if (argc < 1) 18 | { 19 | cout << "Enter the name of the serial file (default: " << default_filename << " )" << endl; 20 | getline(cin, serial_filename); 21 | } 22 | 23 | if(serial_filename.empty()) 24 | { 25 | cout << "Using default serial file : " << default_filename << endl; 26 | serial_filename = default_filename; 27 | } 28 | // Open the serial file (read/write mode) 29 | // ios::app Append mode. All output to that file to be appended to the end. 30 | serial_file.open(serial_filename, ios::in|ios::out|ios::app|ios::binary); 31 | // If we couldn't open the output file stream for writing 32 | if (!serial_file) 33 | { 34 | // Print an error and exit 35 | cerr << "Uh oh, serial_file could not be opened for writing!" << endl; 36 | exit(1); 37 | } 38 | 39 | // Write some orders 40 | write_order(serial_file, HELLO); 41 | 42 | write_order(serial_file, MOTOR); 43 | write_i8(serial_file, -59); 44 | write_i32(serial_file, 131072); 45 | 46 | write_order(serial_file, SERVO); 47 | write_i16(serial_file, 3684); 48 | 49 | write_order(serial_file, ERROR); 50 | write_i16(serial_file, -2541); 51 | 52 | serial_file.seekg(0); //Return to the beginning 53 | 54 | size_t num_orders = 4; 55 | 56 | Order current_order; 57 | 58 | for (size_t i = 0; i < num_orders; i++) 59 | { 60 | current_order = read_order(serial_file); 61 | 62 | switch (current_order) 63 | { 64 | case HELLO: 65 | { 66 | cout << "HELLO" << endl; 67 | break; 68 | } 69 | case SERVO: 70 | { 71 | int16_t angle = read_i16(serial_file); 72 | cout << "SERVO: angle=" << angle << endl; 73 | break; 74 | } 75 | case MOTOR: 76 | { 77 | int speed = read_i8(serial_file); 78 | int32_t test = read_i32(serial_file); 79 | cout << "MOTOR: speed=" << speed << endl; 80 | cout << "test:"<< test << endl; 81 | break; 82 | } 83 | case ALREADY_CONNECTED: 84 | { 85 | cout << "ALREADY_CONNECTED" << endl; 86 | break; 87 | } 88 | case ERROR: 89 | { 90 | int16_t errorCode = read_i16(serial_file); 91 | cout << "ERROR " << errorCode << endl; 92 | break; 93 | } 94 | case RECEIVED: 95 | { 96 | cout << "RECEIVED" << endl; 97 | break; 98 | } 99 | case STOP: 100 | { 101 | cout << "STOP!" << endl; 102 | break; 103 | } 104 | default: 105 | { 106 | bitset<8> b(current_order); 107 | cout << "Unknown command:" << b << endl; 108 | } 109 | } 110 | } 111 | 112 | serial_file.close(); 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /examples/command_parser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // For strtolower 4 | #include 5 | #include "command_parser.h" 6 | 7 | using namespace std; 8 | 9 | fstream serialFile; 10 | 11 | int main(int argc, char const *argv[]) 12 | { 13 | string defaultFileName = "/dev/ttyACM0"; 14 | string serialFileName = ""; 15 | string cmd;// the command name 16 | bool exitPrompt = false; 17 | bool validCommand = true; 18 | cout << "Enter the name of the serial file (default: /dev/ttyACM0 )" << endl; 19 | getline(cin, serialFileName); 20 | 21 | if(serialFileName.empty()) 22 | { 23 | cout << "Using default serial file : " << defaultFileName << endl; 24 | serialFileName = defaultFileName; 25 | } 26 | // Open the serial file (read/write mode) 27 | // ios::app Append mode. All output to that file to be appended to the end. 28 | serialFile.open(serialFileName, ios::in|ios::out|ios::app|ios::binary); 29 | // If we couldn't open the output file stream for writing 30 | if (!serialFile) 31 | { 32 | // Print an error and exit 33 | cerr << "Uh oh, serialFile could not be opened for writing!" << endl; 34 | exit(1); 35 | } 36 | 37 | while (!exitPrompt) 38 | { 39 | cout << "=========" << endl; 40 | cout << "Commands: hello | motor | servo | stop | exit " << endl; 41 | cout << "=========" << endl; 42 | if(cmd != "") 43 | { 44 | if(!validCommand) 45 | { 46 | cmd += " Invalid command!"; 47 | validCommand = true; 48 | } 49 | cout << "Last command: " << cmd << endl; 50 | } 51 | cout << "Please enter a command" << endl; 52 | cin >> cmd; 53 | // To lower case 54 | transform(cmd.begin(), cmd.end(), cmd.begin(), ::tolower); 55 | if (cmd == "motor") 56 | { 57 | int speed = getIntFromUserInput("speed (between -100 and 100) ?"); 58 | write_order(serialFile, MOTOR); 59 | write_i8(serialFile, speed); 60 | cmd += " " + to_string(speed); 61 | } 62 | else if (cmd == "servo") 63 | { 64 | int angle = getIntFromUserInput("angle (between 0 and 180) ?"); 65 | write_order(serialFile, SERVO); 66 | write_i16(serialFile, angle); 67 | cmd += " " + to_string(angle); 68 | } 69 | else if (cmd == "hello") 70 | { 71 | write_order(serialFile, HELLO); 72 | } 73 | else if (cmd == "stop") 74 | { 75 | write_order(serialFile, STOP); 76 | } 77 | else if (cmd == "exit") 78 | { 79 | exitPrompt = true; 80 | } 81 | else 82 | { 83 | validCommand = false; 84 | cout << endl << "Unknown command ! " << endl << endl; 85 | } 86 | flush(serialFile);// Write data in the buffer to the output file 87 | cout << "\033[2J\033[1;1H";//Clear the terminal 88 | } 89 | serialFile.close(); 90 | return 0; 91 | } 92 | 93 | /** 94 | * Ask the user to enter an integer 95 | * Prompt until the input is valid 96 | * @param infoMessage The message displayed to the user 97 | * @return the integer entered by the user 98 | */ 99 | int getIntFromUserInput(std::string infoMessage) 100 | { 101 | bool isValid = false; 102 | int intParam; 103 | string param; 104 | while(!isValid) 105 | { 106 | cout << infoMessage << endl;// Ask the user for input 107 | cin >> param;// Store the string entered 108 | try 109 | { 110 | // Convert String to int 111 | intParam = stoi(param); 112 | isValid = true; 113 | } 114 | catch(exception& e) 115 | { 116 | isValid = false; 117 | cout << "Please enter a valid integer "<< endl << endl; 118 | } 119 | } 120 | return intParam; 121 | } 122 | 123 | /** 124 | * Ask the user to enter a float 125 | * Prompt until the input is valid 126 | * @param infoMessage The message displayed to the user 127 | * @return the integer entered by the user 128 | */ 129 | float getFloatFromUserInput(std::string infoMessage) 130 | { 131 | bool isValid = false; 132 | float floatParam; 133 | string param; 134 | while(!isValid) 135 | { 136 | cout << infoMessage << endl;// Ask the user for input 137 | cin >> param;// Store the string entered 138 | try 139 | { 140 | // Convert String to float 141 | floatParam = stof(param); 142 | isValid = true; 143 | } 144 | catch(exception& e) 145 | { 146 | isValid = false; 147 | cout << "Please enter a valid float "<< endl << endl; 148 | } 149 | } 150 | return floatParam; 151 | } 152 | 153 | unsigned int getUnsignedIntFromUserInput(std::string infoMessage) 154 | { 155 | bool isValid = false; 156 | unsigned int intParam; 157 | string param; 158 | while(!isValid) 159 | { 160 | cout << infoMessage << endl;// Ask the user for input 161 | cin >> param;// Store the string entered 162 | try 163 | { 164 | // Convert String to int 165 | intParam = stoul(param); 166 | isValid = true; 167 | } 168 | catch(exception& e) 169 | { 170 | isValid = false; 171 | cout << "Please enter a valid unsigned integer "<< endl << endl; 172 | } 173 | } 174 | return intParam; 175 | } 176 | 177 | /** 178 | * Ask the user to enter an long 179 | * Prompt until the input is valid 180 | * @param infoMessage The message displayed to the user 181 | * @return the integer entered by the user 182 | */ 183 | long getLongIntFromUserInput(std::string infoMessage) 184 | { 185 | bool isValid = false; 186 | long intParam; 187 | string param; 188 | while(!isValid) 189 | { 190 | cout << infoMessage << endl;// Ask the user for input 191 | cin >> param;// Store the string entered 192 | try 193 | { 194 | // Convert String to long int 195 | intParam = stol(param); 196 | isValid = true; 197 | } 198 | catch(exception& e) 199 | { 200 | isValid = false; 201 | cout << "Please enter a valid long integer "<< endl << endl; 202 | } 203 | } 204 | return intParam; 205 | } 206 | --------------------------------------------------------------------------------