SerialPort.cpp
9.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
//!
//! @file SerialPort.cpp
//! @author Geoffrey Hunter <gbmhunter@gmail.com> ()
//! @created 2014/01/07
//! @last-modified 2014/05/15
//! @brief The main serial port class.
//! @details
//! See README.rst in repo root dir for more info.
#include <iostream>
#include <sstream>
#include <stdio.h> // Standard input/output definitions
#include <string.h> // String function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitions (struct termios)
#include <system_error> // For throwing std::system_error
#include "../include/Config.hpp"
#include "../include/SerialPort.hpp"
#include "lib/SmartPrint/api/SmartPrint.hpp"
namespace SerialPort
{
SerialPort::SerialPort() :
filePath(std::string()),
baudRate(BaudRates::none),
fileDesc(0),
sp(
new SmartPrint::Sp(
"Port",
&std::cout,
&SmartPrint::Colours::yellow,
&std::cout,
&SmartPrint::Colours::yellow,
&std::cerr,
&SmartPrint::Colours::red))
{
// Everything setup in initialiser list
}
SerialPort::~SerialPort()
{
}
void SerialPort::SetFilePath(std::string filePath)
{
// Save a pointer to the file path
this->filePath = filePath;
}
void SerialPort::SetBaudRate(BaudRates baudRate)
{
// Get current termios struct
termios myTermios = this->GetTermios();
switch(baudRate)
{
case BaudRates::none:
// Error, baud rate has not been set yet
throw std::runtime_error("Baud rate for '" + this->filePath + "' cannot be set to none.");
break;
case BaudRates::b9600:
cfsetispeed(&myTermios, B9600);
cfsetospeed(&myTermios, B9600);
break;
case BaudRates::b57600:
cfsetispeed(&myTermios, B57600);
cfsetospeed(&myTermios, B57600);
break;
}
// Save back to file
this->SetTermios(myTermios);
// Setting the baudrate must of been successful, so we can now store this
// new value internally. This must be done last!
this->baudRate = baudRate;
}
void SerialPort::Open()
{
this->sp->PrintDebug(SmartPrint::Ss() << "Attempting to open COM port \"" << this->filePath << "\".");
if(this->filePath.size() == 0)
{
//this->sp->PrintError(SmartPrint::Ss() << "Attempted to open file when file path has not been assigned to.");
//return false;
throw std::runtime_error("Attempted to open file when file path has not been assigned to.");
}
// Attempt to open file
//this->fileDesc = open(this->filePath, O_RDWR | O_NOCTTY | O_NDELAY);
// O_RDONLY for read-only, O_WRONLY for write only, O_RDWR for both read/write access
// 3rd, optional parameter is mode_t mode
this->fileDesc = open(this->filePath.c_str(), O_RDWR);
// Check status
if (this->fileDesc == -1)
{
// Could not open COM port
//this->sp->PrintError(SmartPrint::Ss() << "Unable to open " << this->filePath << " - " << strerror(errno));
//return false;
throw std::system_error(EFAULT, std::system_category());
}
this->sp->PrintDebug(SmartPrint::Ss() << "COM port opened successfully.");
// If code reaches here, open and config must of been successful
}
void SerialPort::SetEverythingToCommonDefaults()
{
this->sp->PrintDebug(SmartPrint::Ss() << "Configuring COM port \"" << this->filePath << "\".");
//================== CONFIGURE ==================//
termios tty = this->GetTermios();
/*struct termios tty;
memset(&tty, 0, sizeof(tty));
// Get current settings (will be stored in termios structure)
if(tcgetattr(this->fileDesc, &tty) != 0)
{
// Error occurred
this->sp->PrintError(SmartPrint::Ss() << "Could not get terminal attributes for \"" << this->filePath << "\" - " << strerror(errno));
//return false;
return;
}*/
//========================= SET UP BAUD RATES =========================//
this->SetBaudRate(BaudRates::b57600);
/*
switch(this->baudRate)
{
case BaudRates::none:
// Error, baud rate has not been set yet
this->sp->PrintError(SmartPrint::Ss() << "Baud rate for \"" << this->filePath << "\" has not been set.");
return;
case BaudRates::b9600:
cfsetispeed(&tty, B9600);
cfsetospeed(&tty, B9600);
break;
case BaudRates::b57600:
cfsetispeed(&tty, B57600);
cfsetospeed(&tty, B57600);
break;
}*/
//================= (.c_cflag) ===============//
tty.c_cflag &= ~PARENB; // No parity bit is added to the output characters
tty.c_cflag &= ~CSTOPB; // Only one stop-bit is used
tty.c_cflag &= ~CSIZE; // CSIZE is a mask for the number of bits per character
tty.c_cflag |= CS8; // Set to 8 bits per character
tty.c_cflag &= ~CRTSCTS; // Disable hadrware flow control (RTS/CTS)
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
//===================== (.c_oflag) =================//
tty.c_oflag = 0; // No remapping, no delays
tty.c_oflag &= ~OPOST; // Make raw
//================= CONTROL CHARACTERS (.c_cc[]) ==================//
// c_cc[WMIN] sets the number of characters to block (wait) for when read() is called.
// Set to 0 if you don't want read to block. Only meaningful when port set to non-canonical mode
//tty.c_cc[VMIN] = 1;
this->SetNumCharsToWait(1);
// c_cc[VTIME] sets the inter-character timer, in units of 0.1s.
// Only meaningful when port is set to non-canonical mode
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
//======================== (.c_iflag) ====================//
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 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 &= ~ECHO; // Turn off 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
// Try and use raw function call
//cfmakeraw(&tty);
this->SetTermios(tty);
/*
// Flush port, then apply attributes
tcflush(this->fileDesc, TCIFLUSH);
if(tcsetattr(this->fileDesc, TCSANOW, &tty) != 0)
{
// Error occurred
this->sp->PrintError(SmartPrint::Ss() << "Could not apply terminal attributes for \"" << this->filePath << "\" - " << strerror(errno));
return;
}*/
}
void SerialPort::SetNumCharsToWait(uint32_t numCharsToWait)
{
// Get current termios struct
termios myTermios = this->GetTermios();
// Save the number of characters to wait for
// to the control register
myTermios.c_cc[VMIN] = numCharsToWait;
// Save termios back
this->SetTermios(myTermios);
}
void SerialPort::Write(std::string* str)
{
if(this->fileDesc == 0)
{
//this->sp->PrintError(SmartPrint::Ss() << );
//return false;
throw std::runtime_error("SendMsg called but file descriptor (fileDesc) was 0, indicating file has not been opened.");
}
int writeResult = write(this->fileDesc, str->c_str(), str->size());
// Check status
if (writeResult == -1)
{
// Could not open COM port
//this->sp->PrintError(SmartPrint::Ss() << "Unable to write to \"" << this->filePath << "\" - " << strerror(errno));
//return false;
throw std::system_error(EFAULT, std::system_category());
}
// If code reaches here than write must of been successful
}
void SerialPort::Read(std::string* str)
{
if(this->fileDesc == 0)
{
//this->sp->PrintError(SmartPrint::Ss() << "Read() was called but file descriptor (fileDesc) was 0, indicating file has not been opened.");
//return false;
throw std::runtime_error("Read() was called but file descriptor (fileDesc) was 0, indicating file has not been opened.");
}
// Allocate memory for read buffer
char buf [256];
memset (&buf, '\0', sizeof buf);
// Read from file
int n = read(this->fileDesc, &buf, sizeof(buf));
// Error Handling
if(n < 0)
{
// Could not open COM port
//this->sp->PrintError(SmartPrint::Ss() << "Unable to read from \"" << this->filePath << "\" - " << strerror(errno));
//return false;
throw std::system_error(EFAULT, std::system_category());
}
if(n > 0)
{
//this->sp->PrintDebug(SmartPrint::Ss() << "\"" << n << "\" characters have been read from \"" << this->filePath << "\"");
// Characters have been read
buf[n] = '\0';
//printf("%s\r\n", buf);
str->append(buf);
//std::cout << *str << " and size of string =" << str->size() << "\r\n";
}
// If code reaches here, read must of been successful
}
termios SerialPort::GetTermios()
{
struct termios tty;
memset(&tty, 0, sizeof(tty));
// Get current settings (will be stored in termios structure)
if(tcgetattr(this->fileDesc, &tty) != 0)
{
// Error occurred
this->sp->PrintError(SmartPrint::Ss() << "Could not get terminal attributes for \"" << this->filePath << "\" - " << strerror(errno));
throw std::system_error(EFAULT, std::system_category());
//return false;
}
return tty;
}
void SerialPort::SetTermios(termios myTermios)
{
// Flush port, then apply attributes
tcflush(this->fileDesc, TCIFLUSH);
if(tcsetattr(this->fileDesc, TCSANOW, &myTermios) != 0)
{
// Error occurred
this->sp->PrintError(SmartPrint::Ss() << "Could not apply terminal attributes for \"" << this->filePath << "\" - " << strerror(errno));
throw std::system_error(EFAULT, std::system_category());
}
// Successful!
}
} // namespace ComPort