-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSerialCommandR2.cpp
More file actions
281 lines (247 loc) · 10.1 KB
/
SerialCommandR2.cpp
File metadata and controls
281 lines (247 loc) · 10.1 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
/*******************************************************************************
SerialCommandR - Modified from Steven Cogswell Llibrary noted below.
Bob Larkin 21 Jan 2017 - See .h file for notes.
*
SerialCommand - An Arduino library to tokenize and parse commands received over
a serial port.
Copyright (C) 2011-2013 Steven Cogswell <steven.cogswell@gmail.com>
http://awtfy.com
See SerialCommand.h for version history.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
***********************************************************************************/
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "SerialCommandR2.h"
#include <string.h>
#ifndef SERIALCOMMAND_HARDWAREONLY
#include <SoftwareSerial.h>
#endif
// #define SERIALCOMMANDDEBUG 1
// Constructor makes sure some things are set. Untested by RSL
SerialCommand::SerialCommand()
{
usingSoftwareSerial=false;
// term was '\r' but that was not received from Serial Monitor---Why?
term = DEFAULT_TERM; // '#' return character, default terminator for command, programmable
delim[0]= DEFAULT_TERM; // for finding tokens
delim[1] = ' '; // Space always available
delim[2] = ','; // Comma
delim[3] = '\n'; // newline doesn't appear for me, but here for safety
delim[4] = '\r'; // same for CR
response = NO_RESPONSE;
numCommand=0; // Number of callback handlers installed
clearBuffer();
}
#ifndef SERIALCOMMAND_HARDWAREONLY
// Constructor to use a SoftwareSerial object
SerialCommand::SerialCommand(SoftwareSerial &_SoftSer)
{
usingSoftwareSerial=true;
SoftSerial = &_SoftSer;
strncpy(delim," ",MAXDELIMETER); // strtok_r needs a null-terminated string
term = DEFAULT_TERM; // '#' return character, default terminator for command, programmable
delim[0]= DEFAULT_TERM; // for finding tokens
delim[1] = ' '; // Space always available
delim[2] = ','; // Comma
delim[3] = '\n'; // newline doesn't appear for me, but here for safety
delim[4] = '\r'; // same for CR
numCommand=0; // Number of callback handlers installed
clearBuffer();
}
#endif
// Set command termination character, term, to over-ride DEFAULT_TERM
void SerialCommand::setTerm(char t)
{
term = t; // Change terminator character
delim[0] = t; // All endings should also be deliminators
}
// Set command response, response, to over-ride NO_RESPONSE
void SerialCommand::setResponse(uint16_t r)
{
response = r; // Change serial response to commands
}
//
// Initialize the command buffer being processed to all null characters
//
void SerialCommand::clearBuffer()
{
for (int i=0; i<SERIALCOMMANDBUFFER; i++)
buffer[i]='\0';
bufPos=0;
}
// Retrieve the next token ("word" or "argument") from the Command buffer.
// returns a NULL if no more tokens exist.
char *SerialCommand::next()
{
char *nextToken;
nextToken = strtok_r(NULL, delim, &last);
return nextToken;
}
//list the commands
void SerialCommand::listCommands() //mdr20200211 from my mods in old file on mdr20181123
{
uint16_t i;
for (i=0; i<numCommand; i++){
Serial.println(CommandList[i].command);
}
}
// This checks the Serial stream for characters, and assembles them into a buffer.
// When the terminator character (default '\r') is seen, it starts parsing the
// buffer for a prefix command, and calls handlers setup by addCommand() member
void SerialCommand::readSerial()
{
// If we're using the Hardware port, check it. Otherwise check the user-created SoftwareSerial Port
#ifdef SERIALCOMMAND_HARDWAREONLY
while (Serial.available() > 0)
#else
while ((usingSoftwareSerial && Serial.available() > 0) || (usingSoftwareSerial && SoftSerial->available() > 0) )
#endif
{
if (!usingSoftwareSerial)
{
// Hardware serial port
inChar=Serial.read(); // Read single available character, there may be more waiting
}
else
{
#ifndef SERIALCOMMAND_HARDWAREONLY
// SoftwareSerial port
inChar = SoftSerial->read(); // Read single available character, there may be more waiting
#endif
}
processCh(inChar);
}
}
// Alternate for readSerial() whith character already read Rev Jan 2020
// This checks the Serial stream for characters, and assembles them into a buffer.
// When the terminator character (default '\r') is seen, it starts parsing the
// buffer for a prefix command, and calls handlers setup by addCommand() member.
// Returns 0=still filling buffer, 1=invalid command found 2=valid command found
int16_t SerialCommand::processCh(char ccc)
{
char *c;
int16_t returnValue;
uint16_t i;
boolean matched;
inChar = ccc; // <<<WOULD SEEM THAT inChar could be local
returnValue = 0;
#ifdef SERIALCOMMANDDEBUG
// Echo back to serial stream
Serial.print("0x");
if ((uint8_t)inChar<0x10) {Serial.print("0");}
Serial.print((uint8_t)inChar, HEX);
Serial.print(" term=");
Serial.print("0x");
Serial.print((uint8_t)term, HEX);
Serial.println("");
#endif
if (inChar==term)
{ // Check for the terminator, meaning end of command
#ifdef SERIALCOMMANDDEBUG
Serial.print("Received: ");
Serial.println(buffer);
#endif
if (response==ECHO_FULL_COMMAND)
Serial.println(buffer);
// Look for any leading spaces or blank commands
c = buffer;
bufPos=0; // Reset to start of buffer
while (*c==' ' || *c==term || *c=='\n' || *c=='\r')
{
c++;
bufPos++;
}
token = strtok_r(buffer,delim,&last); // Search for command at start of buffer
if (token == NULL) return 0; // End of command delimiter not found yet
matched=false;
for (i=0; i<numCommand; i++) // Try all commands
{
#ifdef SERIALCOMMANDDEBUG
Serial.print("Comparing [");
Serial.print(token);
Serial.print("] to [");
Serial.print(CommandList[i].command);
Serial.println("]");
#endif
// Compare the found command against the list of known commands for a match
if (strncmp(token,CommandList[i].command,SERIALCOMMANDBUFFER) == 0)
{
#ifdef SERIALCOMMANDDEBUG
Serial.print("Matched Command: ");
Serial.println(token);
#endif
returnValue = 2;
if(response==ECHO_COMMAND)
Serial.println(token);
else if (response==ECHO_OK)
Serial.println("OK");
else if (response==ECHO_ONE)
Serial.print(1);
// Execute the stored handler function for the command
(*CommandList[i].function)();
clearBuffer();
matched=true;
break;
}
}
if (matched==false)
{
returnValue = 1;
(*defaultHandler)();
clearBuffer();
if (response==ECHO_ONE)
Serial.print(0);
}
}
// printable includes space, tab, \n, \r, etc. Excludes control ch.
if (isprint(inChar)) // Only "printable" characters into the buffer
{
buffer[bufPos++]=inChar; // Put character into buffer
buffer[bufPos]='\0'; // Null terminate
if (bufPos > SERIALCOMMANDBUFFER-1) bufPos=0; // wrap buffer around if full
}
return returnValue;
}
// Adds a "command" and a handler function to the list of available commands.
// This is used for matching a found token in the buffer, and gives the pointer
// to the handler function to deal with it.
void SerialCommand::addCommand(const char *command, void (*function)())
{
if (numCommand < MAXSERIALCOMMANDS) {
#ifdef SERIALCOMMANDDEBUG
Serial.print(numCommand);
Serial.print("-");
Serial.print("Adding command for ");
Serial.println(command);
#endif
strncpy(CommandList[numCommand].command,command,SERIALCOMMANDBUFFER);
CommandList[numCommand].function = function;
numCommand++;
} else {
// In this case, you tried to push more commands into the buffer than it is compiled to hold.
// Not much we can do since there is no real visible error assertion, we just ignore adding
// the command
#ifdef SERIALCOMMANDDEBUG
Serial.println("Too many handlers - recompile changing MAXSERIALCOMMANDS");
#endif
}
}
// This sets up a handler to be called in the event that the receveived command string
// isn't in the list of things with handlers.
void SerialCommand::addDefaultHandler(void (*function)())
{
defaultHandler = function;
}