forked from donaloconnor/automon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserialhelper.cpp
356 lines (275 loc) · 11 KB
/
serialhelper.cpp
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
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/*
==================================================================================================
| serialhelper.cpp |
| Part of the Automon application. |
| |
| Final Year Project - "An Embedded Automotive Monitoring Device" |
| |
| By Donal O' Connor for completion of B.Sc (Hons) Software Development and Computer Networking |
| Email: [email protected] |
| Website/Blog: http://automon.killarneyonline.eu |
| |
| Cork Institute of Technology, Cork, Ireland - http://www.cit.ie/ |
| |
| Copyright © 2009 Donal O'Connor <[email protected]> |
==================================================================================================
This is the implementation file of the SerialHelper class. More details are below.
*/
#include <QMutex>
#include "automon.h"
#ifndef Q_OS_ANDROID // [LA] Cross-compile to get this up and running
#include <QSerialPortInfo>
#endif
using namespace AutomonKernel;
SerialHelper::SerialHelper(QString port)
{
/* Create a QSerialPort object passing in the parameters that the ELM327 communicates with */
m_connection = new QSerialPort(port);
// Override the default port, if it finds something connected to a different port [LA]
qDebug() << "**********Populating Serial Port(s)*************";
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
qDebug() << "Name : " << info.portName();
qDebug() << "Description : " << info.description();
qDebug() << "Product ID : " << info.productIdentifier();
qDebug() << "Manufacturer: " << info.manufacturer();
if( info.manufacturer() == "FTDI")
m_connection->setPort(info);
}
int result = m_connection->open(QIODevice::ReadWrite);
if(result)
{
m_connection->setBaudRate(QSerialPort::Baud57600);
m_connection->setBaudRate(QSerialPort::Baud38400);
m_connection->setFlowControl(QSerialPort::NoFlowControl);
m_connection->setParity(QSerialPort::NoParity);
m_connection->setDataBits(QSerialPort::Data8);
m_connection->setStopBits(QSerialPort::OneStop);
// m_connection->clear();
// connect(m_connection, SIGNAL(readyRead()), this, SLOT(onDataReceived()));
// m_connection->setTimeout(0,10);
}
/* m_stop is used to stop the monitoring thread running */
m_stop = true;
m_isMonitoring = false;
/* Open a connection to the ELM327 */
// int result = m_connection->open(QSerialPort::ReadWrite);
/* If could not connect, major problem! Throw exception */
if (!result)
throw serialio_exception();
#ifdef DEBUGAUTOMON
if (result)
qDebug("Successfully Connected to Serial Port");
else
qWarning("Serial Could not be opened");
#endif
}
void SerialHelper::setMonitoring(bool monitoring)
{
/* Set the monitoring variable so we know the Serial thread running */
m_isMonitoring = monitoring;
}
SerialHelper::~SerialHelper()
{
/* SerialHelper destructor. We have to clean up the serial connection object */
if(m_connection->isOpen())
m_connection->close();
/* Delete memory from heap */
delete m_connection;
#ifdef DEBUGAUTOMON
qDebug("Closed SerialHelper");
#endif
}
void SerialHelper::clearReadBuffer()
{
/* Read any trash that's currently in the serial input buffer */
QString rubbish = m_connection->readAll(); /* Read Trash */
}
void SerialHelper::run()
{
/* This is the code that runs when the Serial thread started */
QTime t; /* Used for timeout purposes */
int i = 0;
QString command;
int bytes = 0;
int size = 0;
/* Temporary buffers */
char buffer[1024];
char tmpBuf[1024];
m_stop = false;
m_isMonitoring = true;
#ifdef DEBUGAUTOMON
qDebug("Started serial thread");
#endif
while (!m_stop && m_activeSensors.size() > 0)
{
/* Iterate through each sensors, while there are sensors to monitor and we are not stopped */
t.start(); /* Start the clock */
if(m_activeSensors[i]->isTurn())
{
/* Only process sensor if it is it's turn. (Frequency) */
/*
Each sensor can have an expected bytes interger assigned that gives the ELM327 a hint of how many bytes
to receive. This speeds up the waiting time from the ELM327
*/
int expectedBytes = m_activeSensors[i]->getExpectedBytes();
/* Create the command to send to the ELM327 */
command = m_activeSensors[i]->getCommand() + " " + (!expectedBytes ? "" : QString::number(expectedBytes)) + "\x0D";
/* Send command to ELM327 */
m_connection->write(command.toLatin1(), command.length());
/* Get the amount of bytes in the serial input buffer */
bytes = m_connection->bytesAvailable();
size = 0;
buffer[0] = '\0';
tmpBuf[0] = '\0';
while(strchr(buffer, '>') == NULL)
{
/* Keep gathering bytes until we hit the prompt character at which point we know we're at end of response */
bytes = m_connection->bytesAvailable();
if (bytes > 0)
{
/* Bytes available in input buffer */
m_connection->read(tmpBuf, bytes);
buffer[size] = '\0';
tmpBuf[bytes] = '\0';
/* Concatenate response to previous response to build up full final response */
strcat(buffer,tmpBuf);
size += bytes;
}
if (t.elapsed() > 2500)
{
/*
If we still receiving responses after 2.5 secondsor waiting on the prompt character, then
something wrong. Exit out of the loop to prevent hang
*/
#ifdef DEBUGAUTOMON
qDebug("Timeout!");
#endif
/* Clear out all rubbish in the input buffer */
QString rubbish = m_connection->readAll(); /* Read Trash */
/* Skip out of this loop */
goto timeout;
}
msleep(1); /* Insert a little sleep to prevent CPU utilization going up, (even tho Linux is pre emptive */
}
timeout:
buffer[size] = '\0';
/* Set the returned response from ELM into the sensor's buffer. The sensor will look after rest such as
sending signal updates etc.
*/
m_activeSensors[i]->setBuffer(QString(buffer));
#ifdef DEBUGAUTOMON
qDebug() << "Received " << QString::number(m_activeSensors[i]->getBuffer().size()) << " Bytes";
#endif
msleep(1);
#ifdef DEBUGAUTOMON
qDebug("Time Elapsed :%d", t.elapsed());
#endif
}
/* Move on to next sensor */
if (i == m_activeSensors.size() - 1)
i = 0;
else
i++;
}
}
void SerialHelper::removeAllActiveSensors()
{
/* Clear all sensors from list */
m_activeSensors.clear();
}
bool SerialHelper::addActiveSensor(Sensor * sensor)
{
/*
This method is reponsible for accepting a sensor pointer and adding it to the list of sensors
to monitor
*/
#ifdef DEBUGAUTOMON
qDebug("Adding Realtime Sensor to Active Sensors");
#endif
m_activeSensors << sensor;
return true;
}
bool SerialHelper::removeActiveSensorByCommand(QString command)
{
/* This method allows one to remove a sensor from the monitoring list by specifying it's command/pid */
for (int i = 0; i < m_activeSensors.size(); i++)
{
if (command.compare(m_activeSensors.at(i)->getCommand()) == 0)
{
/* Found the sensor we looking for so remove it from list and exit */
m_activeSensors.removeAt(i);
return true;
}
}
return false;
}
bool SerialHelper::isMonitoring()
{
/* Return a bool to suggest if we monitoring or not */
return m_isMonitoring;
}
bool SerialHelper::sendCommand(Command & command, int timeout)
{
/*
This method allows one to send commands to the ELM327. It looks after updating the command object's buffer
to the response as well. I've included mutex's so incase multiple threads use this, they get blocked as only
one thread can send a command at a time
*/
/* Create a lock for current thread */
m_mutexLock.lock();
if (m_isMonitoring)
{
/* Cannot send a command if monitoring is active! */
monitoring_exception e;
throw e;
}
QTime t;
QByteArray rubbish = m_connection->readAll();
/* Create command to send to ELM327 */
QString message = command.getCommand()+"\x0D";
/* Send command */
m_connection->write(message.toLatin1(),message.length());
/* Sleeep for a few ms to give chance for buffer to fill */
msleep(10);
char buffer[1024];
char tmpBuf[1024];
int size = 0;
int bytes = 0;
buffer[0] = '\0';
tmpBuf[0] = '\0';
t.start(); /* Start a clock so we can check if time out */
while(strchr(buffer, '>') == NULL)
{
/* Keep looping until we hit the > character in which case we are at end of response */
bytes = m_connection->bytesAvailable();
if (bytes > 0)
{
/* Bytes available in input buffer of serial device so read them */
m_connection->read(tmpBuf, bytes);
buffer[size] = '\0';
tmpBuf[bytes] = '\0';
/* Add this current buffer to previous to construct over all response */
strcat(buffer,tmpBuf);
size += bytes;
}
else
msleep(1);
if (t.elapsed() > timeout)
{
/* If our clock has gone above the time out , then something wrong! Exit out of loop by doing jump */
#ifdef DEBUGAUTOMON
qDebug() << "Timeout";
#endif
goto timeout;
}
}
timeout:
/* Either we finished successfully or we timed out */
buffer[size] = '\0';
/* Set the command's buffer so the calling method can now read the response from ELM327 */
command.setBuffer(QString(buffer));
/* Unlock the mutex again so another thread can use this method */
m_mutexLock.unlock();
return true;
}