Skip to content

Commit dc8ce15

Browse files
palto42pvizeli
authored andcommitted
New Buffer and Callback functions (#12)
* gitignore * added non-blocking serial read and echo option * re-use readSerialChar for ReadFromLine * README update & new example * gitignore VS Code * fix warnings in CmdBuffer * clang-format * removed useless if-clause * removed useless if-clause
1 parent 6a497bb commit dc8ce15

File tree

7 files changed

+243
-29
lines changed

7 files changed

+243
-29
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
11
.clang_complete
22
.gcc-flags.json
3+
*.gch
4+
5+
# VS Code
6+
.vscode
7+
!.vscode/settings.json
8+
!.vscode/tasks.json
9+
!.vscode/launch.json
10+
!.vscode/extensions.json

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[![Build Status](https://dev.azure.com/pascalvizeli/CmdParser/_apis/build/status/CI?branchName=master)](https://dev.azure.com/pascalvizeli/CmdParser/_build/latest?definitionId=3&branchName=master)
22

33
# CmdParser
4+
45
A simple and most powerfull cmd parser with small memory footprint and realy
56
fast algorithm.
67

@@ -34,11 +35,11 @@ myParser.parseCmd(cString); // C string buffer
3435
```
3536

3637
### Options
38+
3739
- ```setOptIgnoreQuote``` (default off) support string with "my value"
3840
- ```setOptSeperator``` (default ' ') use this character for seperate cmd
3941
- ```setOptKeyValue``` (default off) Support dynamic key=value feature
4042

41-
4243
## Buffer Object
4344

4445
```c++
@@ -48,10 +49,16 @@ CmdBuffer<32> myBuffer;
4849

4950
// Reading Data
5051
myBuffer.readFromSerial(&Serial, numTimeout);
52+
53+
// Read single character
54+
myBuffer.readSerialChar(&Serial);
5155
```
5256

5357
### Options
58+
5459
- ```setEndChar``` (default '\n') set character for stop reading. Normal is a line end.
60+
- `setBackChar` (default '\h') set the character for backspace.
61+
- `setEcho` (default 'false') enable local echo.
5562

5663
## Callback Object
5764

@@ -69,4 +76,12 @@ myCallbackP.loopCmdProcessing(&myParser, &myBuffer, &Serial);
6976

7077
// Manual
7178
myCallbackP.processCmd(cstrCmd);
79+
80+
// Check for new chars and check if complete command was entered
81+
// This function is non-blocking to be used in a loop.
82+
myCallbackP.updateCmdProcessing(&myParser, &myBuffer, &Serial);
83+
84+
// Search command in the buffer, returns true if such command was defined.
85+
myCallbackP.hasCmd(cstrCmd);
86+
7287
```
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* Copyright 2016 Pascal Vizeli <pvizeli@syshack.ch>
2+
* Copyright 2019 Matthias Homann
3+
* BSD License
4+
*
5+
* https://github.com/pvizeli/CmdParser
6+
*/
7+
8+
////
9+
// This example is a demo for non-blocking CmdCallback.
10+
// It regularly checks for new characters on the serial interface
11+
// and parses the read string if the 'endchar' (\n) was entered.
12+
////
13+
14+
#include <CmdBuffer.hpp>
15+
#include <CmdCallback.hpp>
16+
#include <CmdParser.hpp>
17+
18+
CmdCallback<3> cmdCallback;
19+
20+
CmdBuffer<32> myBuffer;
21+
CmdParser myParser;
22+
23+
char strHallo[] = "HALLO";
24+
char strQuit[] = "QUIT";
25+
char strSet[] = "SET";
26+
27+
void functHallo(CmdParser *myParser) { Serial.println("Receive Hallo"); }
28+
29+
void functSet(CmdParser *myParser)
30+
{
31+
Serial.println("Receive Set");
32+
33+
// Alarm
34+
if (myParser->equalCmdParam(1, "ALARM")) {
35+
Serial.println(myParser->getCmdParam(2));
36+
}
37+
// Command Unknwon
38+
else {
39+
Serial.println("Only Alarm is allowed!");
40+
}
41+
}
42+
43+
void functQuit(CmdParser *myParser) { Serial.println("Receive Quit"); }
44+
45+
void setup()
46+
{
47+
Serial.begin(115200);
48+
49+
cmdCallback.addCmd(strHallo, &functHallo);
50+
cmdCallback.addCmd(strQuit, &functQuit);
51+
cmdCallback.addCmd(strSet, &functSet);
52+
53+
// Set parser options:
54+
// enable local echo
55+
myBuffer.setEcho(true);
56+
// ...
57+
58+
Serial.println("Type you commands. Supported: ");
59+
Serial.println("1: hallo");
60+
Serial.println("2: set alarm on/off");
61+
Serial.println("3: quit");
62+
}
63+
64+
void loop()
65+
{
66+
67+
// Check for new char on serial and call function if command was entered
68+
cmdCallback.updateCmdProcessing(&myParser, &myBuffer, &Serial);
69+
70+
// do some other stuff...
71+
delay(100);
72+
}

src/CmdBuffer.cpp

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ bool CmdBufferObject::readFromSerial(Stream *serial, uint32_t timeOut)
1010
{
1111
uint32_t isTimeOut;
1212
uint32_t startTime;
13-
uint8_t readChar;
14-
uint8_t *buffer = this->getBuffer();
15-
bool over = false;
13+
bool over = false;
1614

1715
// UART initialize?
1816
if (serial == NULL) {
@@ -40,26 +38,9 @@ bool CmdBufferObject::readFromSerial(Stream *serial, uint32_t timeOut)
4038
// if data in serial input buffer
4139
while (serial->available()) {
4240

43-
// is buffer full?
44-
if (m_dataOffset >= this->getBufferSize()) {
45-
m_dataOffset = 0;
46-
return false;
47-
}
48-
49-
// read into buffer
50-
readChar = serial->read();
51-
52-
// is that the end of command
53-
if (readChar == m_endChar) {
54-
buffer[m_dataOffset] = '\0';
55-
m_dataOffset = 0;
41+
if (this->readSerialChar(serial)) {
5642
return true;
5743
}
58-
59-
// is a printable character
60-
if (readChar > CMDBUFFER_CHAR_PRINTABLE) {
61-
buffer[m_dataOffset++] = readChar;
62-
}
6344
}
6445

6546
// Timeout is active?
@@ -81,3 +62,53 @@ bool CmdBufferObject::readFromSerial(Stream *serial, uint32_t timeOut)
8162

8263
return false;
8364
}
65+
66+
bool CmdBufferObject::readSerialChar(Stream *serial)
67+
{
68+
uint8_t readChar;
69+
uint8_t *buffer = this->getBuffer();
70+
71+
// UART initialize?
72+
if (serial == NULL) {
73+
return false;
74+
}
75+
76+
if (serial->available()) {
77+
// is buffer full?
78+
if (m_dataOffset >= this->getBufferSize()) {
79+
m_dataOffset = 0;
80+
return false;
81+
}
82+
83+
// read into buffer
84+
readChar = serial->read();
85+
86+
if (m_echo) {
87+
serial->write(readChar);
88+
}
89+
90+
// is that the end of command
91+
if (readChar == m_endChar) {
92+
buffer[m_dataOffset] = '\0';
93+
m_dataOffset = 0;
94+
return true;
95+
}
96+
97+
// is that a backspace char?
98+
if ((readChar == m_bsChar) && (m_dataOffset > 0)) {
99+
// buffer[--m_dataOffset] = 0;
100+
--m_dataOffset;
101+
if (m_echo) {
102+
serial->write(' ');
103+
serial->write(readChar);
104+
}
105+
return false;
106+
}
107+
108+
// is a printable character
109+
if (readChar > CMDBUFFER_CHAR_PRINTABLE) {
110+
buffer[m_dataOffset++] = readChar;
111+
}
112+
}
113+
return false;
114+
}

src/CmdBuffer.hpp

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
const uint8_t CMDBUFFER_CHAR_PRINTABLE = 0x1F;
1616
const uint8_t CMDBUFFER_CHAR_LF = 0x0A;
1717
const uint8_t CMDBUFFER_CHAR_CR = 0x0D;
18+
const uint8_t CMDBUFFER_CHAR_BS = 0x08;
19+
const uint8_t CMDBUFFER_CHAR_DEL = 0x7F;
1820

1921
/**
2022
*
@@ -26,7 +28,13 @@ class CmdBufferObject
2628
/**
2729
* Clear buffer and set defaults.
2830
*/
29-
CmdBufferObject() : m_endChar(CMDBUFFER_CHAR_LF), m_dataOffset(0) {}
31+
CmdBufferObject()
32+
: m_endChar(CMDBUFFER_CHAR_LF),
33+
m_bsChar(CMDBUFFER_CHAR_BS),
34+
m_dataOffset(0),
35+
m_echo(false)
36+
{
37+
}
3038

3139
/**
3240
* Read data from serial communication to buffer. It read only printable
@@ -39,9 +47,21 @@ class CmdBufferObject
3947
*/
4048
bool readFromSerial(Stream *serial, uint32_t timeOut = 0);
4149

50+
/**
51+
* Read one char from serial communication to buffer if available.
52+
* It read only printable ASCII character from serial.
53+
* All other will ignore for buffer. This function only ready currently
54+
* available data and doesn't wait for a full command (= end character)
55+
*
56+
* @param serial Arduino Serial object from read commands
57+
* @return TRUE if data readed until end character or
58+
* FALSE if not.
59+
*/
60+
bool readSerialChar(Stream *serial);
61+
4262
/**
4363
* Set a ASCII character for serial cmd end.
44-
" Default value is LF.
64+
* Default value is LF.
4565
*
4666
* Macros for helping are:
4767
* - CMDBUFFER_CHAR_LF
@@ -51,6 +71,25 @@ class CmdBufferObject
5171
*/
5272
void setEndChar(uint8_t end) { m_endChar = end; }
5373

74+
/**
75+
* Set a ASCII character for serial cmd backspace.
76+
* Default value is BS.
77+
*
78+
* Macros for helping are:
79+
* - CMDBUFFER_CHAR_BS
80+
* - CMDBUFFER_CHAR_DEL
81+
*
82+
* @param backspace ASCII character
83+
*/
84+
void setBackChar(uint8_t backspace) { m_bsChar = backspace; }
85+
86+
/**
87+
* Set echo serial on (true) or off (false)
88+
*
89+
* @param echo bool
90+
*/
91+
void setEcho(bool echo) { m_echo = echo; }
92+
5493
/**
5594
* Cast Buffer to c string.
5695
*
@@ -83,7 +122,9 @@ class CmdBufferObject
83122
private:
84123
/** Character for handling the end of serial data communication */
85124
uint8_t m_endChar;
125+
uint8_t m_bsChar;
86126
size_t m_dataOffset;
127+
bool m_echo;
87128
};
88129

89130
/**

src/CmdCallback.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
void CmdCallbackObject::loopCmdProcessing(CmdParser * cmdParser,
1010
CmdBufferObject *cmdBuffer,
11-
Stream * serial)
11+
Stream * serial)
1212
{
1313
do {
1414
// read data
@@ -17,10 +17,9 @@ void CmdCallbackObject::loopCmdProcessing(CmdParser * cmdParser,
1717
// parse command line
1818
if (cmdParser->parseCmd(cmdBuffer) != CMDPARSER_ERROR) {
1919
// search command in store and call function
20-
if (this->processCmd(cmdParser)) {
21-
// FIXME: handling cmd not found
22-
}
23-
cmdBuffer->clear();
20+
// ignore return value "false" if command was not found
21+
this->processCmd(cmdParser);
22+
cmdBuffer->clear();
2423
}
2524
}
2625
} while (true);
@@ -47,3 +46,33 @@ bool CmdCallbackObject::processCmd(CmdParser *cmdParser)
4746

4847
return false;
4948
}
49+
50+
void CmdCallbackObject::updateCmdProcessing(CmdParser * cmdParser,
51+
CmdBufferObject *cmdBuffer,
52+
Stream * serial)
53+
{
54+
// read data and check if command was entered
55+
if (cmdBuffer->readSerialChar(serial)) {
56+
// parse command line
57+
if (cmdParser->parseCmd(cmdBuffer) != CMDPARSER_ERROR) {
58+
// search command in store and call function
59+
// ignore return value "false" if command was not found
60+
this->processCmd(cmdParser);
61+
cmdBuffer->clear();
62+
}
63+
}
64+
}
65+
66+
bool CmdCallbackObject::hasCmd(char *cmdStr)
67+
{
68+
// search cmd in store
69+
for (size_t i = 0; this->checkStorePos(i); i++) {
70+
71+
// compare command with string
72+
if (this->equalStoreCmd(i, cmdStr)) {
73+
return true;
74+
}
75+
}
76+
77+
return false;
78+
}

src/CmdCallback.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,24 @@ class CmdCallbackObject
4848
*/
4949
virtual bool processCmd(CmdParser *cmdParser);
5050

51+
/**
52+
* Check for single new char on serial and if it was the endChar
53+
*
54+
* @param cmdParser Parser object with options set
55+
* @param cmdBuffer Buffer object for data handling
56+
* @param serial Arduino serial interface from comming data
57+
*/
58+
void updateCmdProcessing(CmdParser *cmdParser, CmdBufferObject *cmdBuffer,
59+
Stream *serial);
60+
61+
/**
62+
* Search command in the buffer.
63+
*
64+
* @param cmdStr Cmd string to search
65+
* @return TRUE if found the command in the buffer
66+
*/
67+
virtual bool hasCmd(char *cmdStr);
68+
5169
/**
5270
* Give the size of callback store.
5371
*

0 commit comments

Comments
 (0)