-
Notifications
You must be signed in to change notification settings - Fork 87
/
CmdMessenger.h
301 lines (249 loc) · 8.74 KB
/
CmdMessenger.h
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
/*
CmdMessenger - library that provides command based messaging
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef CmdMessenger_h
#define CmdMessenger_h
#include <inttypes.h>
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
//#include "Stream.h"
extern "C"
{
// callback functions always follow the signature: void cmd(void);
typedef void(*messengerCallbackFunction) (void);
}
#define MAXCALLBACKS 50 // The maximum number of commands (default: 50)
#define MESSENGERBUFFERSIZE 64 // The length of the commandbuffer (default: 64)
#define MAXSTREAMBUFFERSIZE 512 // The length of the streambuffer (default: 64)
#define DEFAULT_TIMEOUT 5000 // Time out on unanswered messages. (default: 5s)
// Message States
enum
{
kProccesingMessage, // Message is being received, not reached command separator
kEndOfMessage, // Message is fully received, reached command separator
kProcessingArguments, // Message is received, arguments are being read parsed
};
#define white_space(c) ((c) == ' ' || (c) == '\t')
#define valid_digit(c) ((c) >= '0' && (c) <= '9')
class CmdMessenger
{
private:
// **** Private variables ***
bool startCommand; // Indicates if sending of a command is underway
uint8_t lastCommandId; // ID of last received command
uint8_t bufferIndex; // Index where to write data in buffer
uint8_t bufferLength; // Is set to MESSENGERBUFFERSIZE
uint8_t bufferLastIndex; // The last index of the buffer
char ArglastChar; // Bookkeeping of argument escape char
char CmdlastChar; // Bookkeeping of command escape char
bool pauseProcessing; // pauses processing of new commands, during sending
bool print_newlines; // Indicates if \r\n should be added after send command
char commandBuffer[MESSENGERBUFFERSIZE]; // Buffer that holds the data
char streamBuffer[MAXSTREAMBUFFERSIZE]; // Buffer that holds the data
uint8_t messageState; // Current state of message processing
bool dumped; // Indicates if last argument has been externally read
bool ArgOk; // Indicated if last fetched argument could be read
char *current; // Pointer to current buffer position
char *last; // Pointer to previous buffer position
char prevChar; // Previous char (needed for unescaping)
Stream *comms; // Serial data stream
char command_separator; // Character indicating end of command (default: ';')
char field_separator; // Character indicating end of argument (default: ',')
char escape_character; // Character indicating escaping of special chars
messengerCallbackFunction default_callback; // default callback function
messengerCallbackFunction callbackList[MAXCALLBACKS]; // list of attached callback functions
// **** Initialize ****
void init(Stream & comms, const char fld_separator, const char cmd_separator, const char esc_character);
void reset();
// **** Command processing ****
inline uint8_t processLine(char serialChar) __attribute__((always_inline));
inline void handleMessage() __attribute__((always_inline));
inline bool blockedTillReply(unsigned int timeout = DEFAULT_TIMEOUT, byte ackCmdId = 1) __attribute__((always_inline));
inline bool checkForAck(byte AckCommand) __attribute__((always_inline));
// **** Command sending ****
/**
* Print variable of type T binary in binary format
*/
template < class T >
void writeBin(const T & value)
{
const byte *bytePointer = (const byte *)(const void *)&value;
for (unsigned int i = 0; i < sizeof(value); i++)
{
printEsc(*bytePointer);
bytePointer++;
}
}
// **** Command receiving ****
int findNext(char *str, char delim);
/**
* Read a variable of any type in binary format
*/
template < class T >
T readBin(char *str)
{
T value;
unescape(str);
byte *bytePointer = (byte *)(const void *)&value;
for (unsigned int i = 0; i < sizeof(value); i++)
{
*bytePointer = str[i];
bytePointer++;
}
return value;
}
template < class T >
T empty()
{
T value;
byte *bytePointer = (byte *)(const void *)&value;
for (unsigned int i = 0; i < sizeof(value); i++)
{
*bytePointer = '\0';
bytePointer++;
}
return value;
}
// **** Escaping tools ****
char *split_r(char *str, const char delim, char **nextp);
bool isEscaped(char *currChar, const char escapeChar, char *lastChar);
void printEsc(char *str);
void printEsc(char str);
public:
// ****** Public functions ******
// **** Initialization ****
CmdMessenger(Stream & comms, const char fld_separator = ',',
const char cmd_separator = ';',
const char esc_character = '/');
void printLfCr(bool addNewLine = true);
void attach(messengerCallbackFunction newFunction);
void attach(byte msgId, messengerCallbackFunction newFunction);
// **** Command processing ****
void feedinSerialData();
bool next();
bool available();
bool isArgOk();
uint8_t commandID();
// **** Command sending ****
/**
* Send a command with a single argument of any type
* Note that the argument is sent as string
*/
template < class T >
bool sendCmd(byte cmdId, T arg, bool reqAc = false, byte ackCmdId = 1,
unsigned int timeout = DEFAULT_TIMEOUT)
{
if (!startCommand) {
sendCmdStart(cmdId);
sendCmdArg(arg);
return sendCmdEnd(reqAc, ackCmdId, timeout);
}
return false;
}
/**
* Send a command with a single argument of any type
* Note that the argument is sent in binary format
*/
template < class T >
bool sendBinCmd(byte cmdId, T arg, bool reqAc = false, byte ackCmdId = 1,
unsigned int timeout = DEFAULT_TIMEOUT)
{
if (!startCommand) {
sendCmdStart(cmdId);
sendCmdBinArg(arg);
return sendCmdEnd(reqAc, ackCmdId, timeout);
}
return false;
}
bool sendCmd(byte cmdId);
bool sendCmd(byte cmdId, bool reqAc, byte ackCmdId);
// **** Command sending with multiple arguments ****
void sendCmdStart(byte cmdId);
void sendCmdEscArg(char *arg);
void sendCmdfArg(char *fmt, ...);
bool sendCmdEnd(bool reqAc = false, byte ackCmdId = 1, unsigned int timeout = DEFAULT_TIMEOUT);
/**
* Send a single argument as string
* Note that this will only succeed if a sendCmdStart has been issued first
*/
template < class T > void sendCmdArg(T arg)
{
if (startCommand) {
comms->print(field_separator);
comms->print(arg);
}
}
/**
* Send a single argument as string with custom accuracy
* Note that this will only succeed if a sendCmdStart has been issued first
*/
template < class T > void sendCmdArg(T arg, unsigned int n)
{
if (startCommand) {
comms->print(field_separator);
comms->print(arg, n);
}
}
/**
* Send double argument in scientific format.
* This will overcome the boundary of normal d sending which is limited to abs(f) <= MAXLONG
*/
void sendCmdSciArg(double arg, unsigned int n = 6);
/**
* Send a single argument in binary format
* Note that this will only succeed if a sendCmdStart has been issued first
*/
template < class T > void sendCmdBinArg(T arg)
{
if (startCommand) {
comms->print(field_separator);
writeBin(arg);
}
}
// **** Command receiving ****
bool readBoolArg();
int16_t readInt16Arg();
int32_t readInt32Arg();
char readCharArg();
float readFloatArg();
double readDoubleArg();
char *readStringArg();
void copyStringArg(char *string, uint8_t size);
uint8_t compareStringArg(char *string);
/**
* Read an argument of any type in binary format
*/
template < class T > T readBinArg()
{
if (next()) {
dumped = true;
return readBin < T >(current);
}
else {
return empty < T >();
}
}
// **** Escaping tools ****
void unescape(char *fromChar);
void printSci(double f, unsigned int digits);
};
#endif