User | Revision | Line number | New contents of line |
geky |
0:c741e144517c
|
1
|
/* Copyright (c) 2015 ARM Limited
|
geky |
0:c741e144517c
|
2
|
*
|
geky |
0:c741e144517c
|
3
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
geky |
0:c741e144517c
|
4
|
* you may not use this file except in compliance with the License.
|
geky |
0:c741e144517c
|
5
|
* You may obtain a copy of the License at
|
geky |
0:c741e144517c
|
6
|
*
|
geky |
0:c741e144517c
|
7
|
* http://www.apache.org/licenses/LICENSE-2.0
|
geky |
0:c741e144517c
|
8
|
*
|
geky |
0:c741e144517c
|
9
|
* Unless required by applicable law or agreed to in writing, software
|
geky |
0:c741e144517c
|
10
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
geky |
0:c741e144517c
|
11
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
geky |
0:c741e144517c
|
12
|
* See the License for the specific language governing permissions and
|
geky |
0:c741e144517c
|
13
|
* limitations under the License.
|
geky |
0:c741e144517c
|
14
|
*
|
geky |
0:c741e144517c
|
15
|
* @section DESCRIPTION
|
geky |
0:c741e144517c
|
16
|
*
|
geky |
0:c741e144517c
|
17
|
* Parser for the AT command syntax
|
geky |
0:c741e144517c
|
18
|
*
|
geky |
0:c741e144517c
|
19
|
*/
|
geky |
0:c741e144517c
|
20
|
|
geky |
0:c741e144517c
|
21
|
#include "ATParser.h"
|
geky |
0:c741e144517c
|
22
|
#include <cstdarg>
|
geky |
0:c741e144517c
|
23
|
|
geky |
0:c741e144517c
|
24
|
// This can be defined to assist in debugging
|
geky |
2:4d68f546861c
|
25
|
#define AT_ECHO 1
|
geky |
0:c741e144517c
|
26
|
|
geky |
0:c741e144517c
|
27
|
|
geky |
0:c741e144517c
|
28
|
// getc/putc handling with timeouts
|
geky |
0:c741e144517c
|
29
|
int ATParser::_putc(char c) {
|
geky |
0:c741e144517c
|
30
|
Timer timer;
|
geky |
0:c741e144517c
|
31
|
timer.start();
|
geky |
0:c741e144517c
|
32
|
|
geky |
0:c741e144517c
|
33
|
while (true) {
|
geky |
0:c741e144517c
|
34
|
if (_serial->writeable())
|
geky |
0:c741e144517c
|
35
|
return _serial->putc(c);
|
geky |
0:c741e144517c
|
36
|
|
geky |
0:c741e144517c
|
37
|
if (timer.read_ms() > _timeout)
|
geky |
0:c741e144517c
|
38
|
return -1;
|
geky |
0:c741e144517c
|
39
|
}
|
geky |
0:c741e144517c
|
40
|
}
|
geky |
0:c741e144517c
|
41
|
|
geky |
0:c741e144517c
|
42
|
int ATParser::_getc() {
|
geky |
0:c741e144517c
|
43
|
Timer timer;
|
geky |
0:c741e144517c
|
44
|
timer.start();
|
geky |
0:c741e144517c
|
45
|
|
geky |
0:c741e144517c
|
46
|
while (true) {
|
geky |
0:c741e144517c
|
47
|
if (_serial->readable())
|
geky |
0:c741e144517c
|
48
|
return _serial->getc();
|
geky |
0:c741e144517c
|
49
|
|
geky |
0:c741e144517c
|
50
|
if (timer.read_ms() > _timeout)
|
geky |
0:c741e144517c
|
51
|
return -1;
|
geky |
0:c741e144517c
|
52
|
}
|
geky |
0:c741e144517c
|
53
|
}
|
geky |
0:c741e144517c
|
54
|
|
geky |
0:c741e144517c
|
55
|
void ATParser::_flush() {
|
geky |
0:c741e144517c
|
56
|
while (_serial->readable())
|
geky |
0:c741e144517c
|
57
|
_serial->getc();
|
geky |
0:c741e144517c
|
58
|
}
|
geky |
0:c741e144517c
|
59
|
|
geky |
1:66a14afe650a
|
60
|
|
geky |
0:c741e144517c
|
61
|
// getline/putline handling with timeouts/bounds checking
|
geky |
1:66a14afe650a
|
62
|
bool ATParser::_putline(const char *line) {
|
geky |
0:c741e144517c
|
63
|
for (int i = 0; line[i]; i++) {
|
geky |
0:c741e144517c
|
64
|
if (_putc(line[i]) < 0)
|
geky |
0:c741e144517c
|
65
|
return false;
|
geky |
0:c741e144517c
|
66
|
}
|
geky |
0:c741e144517c
|
67
|
|
geky |
0:c741e144517c
|
68
|
// Finish with newline
|
geky |
1:66a14afe650a
|
69
|
if (_putc('\r') < 0 || _putc('\n') < 0)
|
geky |
0:c741e144517c
|
70
|
return false;
|
geky |
0:c741e144517c
|
71
|
|
geky |
0:c741e144517c
|
72
|
#ifdef AT_ECHO
|
geky |
0:c741e144517c
|
73
|
printf("AT> %s\r\n", line);
|
geky |
0:c741e144517c
|
74
|
#endif
|
geky |
1:66a14afe650a
|
75
|
|
geky |
0:c741e144517c
|
76
|
return true;
|
geky |
0:c741e144517c
|
77
|
}
|
geky |
0:c741e144517c
|
78
|
|
geky |
1:66a14afe650a
|
79
|
bool ATParser::_getline(char *line, int size) {
|
geky |
1:66a14afe650a
|
80
|
int i = 0;
|
geky |
1:66a14afe650a
|
81
|
|
geky |
1:66a14afe650a
|
82
|
while (i < size) {
|
geky |
0:c741e144517c
|
83
|
int c = _getc();
|
geky |
0:c741e144517c
|
84
|
|
geky |
1:66a14afe650a
|
85
|
if (c < 0)
|
geky |
1:66a14afe650a
|
86
|
return false;
|
geky |
0:c741e144517c
|
87
|
|
geky |
1:66a14afe650a
|
88
|
// Finish when we hit a newline
|
geky |
1:66a14afe650a
|
89
|
if (c == '\r' || c == '\n') {
|
geky |
1:66a14afe650a
|
90
|
// Only handle newlines on \n
|
geky |
1:66a14afe650a
|
91
|
if (c != '\n')
|
geky |
1:66a14afe650a
|
92
|
continue;
|
geky |
0:c741e144517c
|
93
|
|
geky |
1:66a14afe650a
|
94
|
line[i++] = 0;
|
geky |
0:c741e144517c
|
95
|
#ifdef AT_ECHO
|
geky |
0:c741e144517c
|
96
|
printf("AT< %s\r\n", line);
|
geky |
0:c741e144517c
|
97
|
#endif
|
geky |
0:c741e144517c
|
98
|
return true;
|
geky |
0:c741e144517c
|
99
|
}
|
geky |
0:c741e144517c
|
100
|
|
geky |
1:66a14afe650a
|
101
|
line[i++] = c;
|
geky |
0:c741e144517c
|
102
|
}
|
geky |
0:c741e144517c
|
103
|
|
geky |
0:c741e144517c
|
104
|
// Ran out of space
|
geky |
0:c741e144517c
|
105
|
return false;
|
geky |
1:66a14afe650a
|
106
|
}
|
geky |
0:c741e144517c
|
107
|
|
geky |
0:c741e144517c
|
108
|
|
geky |
0:c741e144517c
|
109
|
bool ATParser::command(const char *command, const char *response, ...) {
|
geky |
0:c741e144517c
|
110
|
va_list args;
|
geky |
0:c741e144517c
|
111
|
va_start(args, response);
|
geky |
0:c741e144517c
|
112
|
|
geky |
0:c741e144517c
|
113
|
_flush();
|
geky |
0:c741e144517c
|
114
|
|
geky |
0:c741e144517c
|
115
|
// Create and send command
|
geky |
0:c741e144517c
|
116
|
if (vsprintf(_buffer, command, args) < 0 ||
|
geky |
0:c741e144517c
|
117
|
!_putline(_buffer)) {
|
geky |
0:c741e144517c
|
118
|
va_end(args);
|
geky |
0:c741e144517c
|
119
|
return false;
|
geky |
0:c741e144517c
|
120
|
}
|
geky |
0:c741e144517c
|
121
|
|
geky |
1:66a14afe650a
|
122
|
// Iterate through each line in the expected response
|
geky |
1:66a14afe650a
|
123
|
while (response && response[0]) {
|
geky |
1:66a14afe650a
|
124
|
// Since response is const, we need to copy it into our buffer to
|
geky |
1:66a14afe650a
|
125
|
// add the line's null terminator and clobber value matches with asterisks.
|
geky |
1:66a14afe650a
|
126
|
//
|
geky |
1:66a14afe650a
|
127
|
// We just use the beginning of the buffer to avoid unecessary allocations.
|
geky |
1:66a14afe650a
|
128
|
int i = 0;
|
geky |
1:66a14afe650a
|
129
|
int offset = 0;
|
geky |
1:66a14afe650a
|
130
|
|
geky |
1:66a14afe650a
|
131
|
while (response[i]) {
|
geky |
1:66a14afe650a
|
132
|
// Only handle newlines on \n
|
geky |
1:66a14afe650a
|
133
|
if (response[i] == '\n') {
|
geky |
1:66a14afe650a
|
134
|
i++;
|
geky |
1:66a14afe650a
|
135
|
break;
|
geky |
1:66a14afe650a
|
136
|
} else if (response[i] == '\r') {
|
geky |
1:66a14afe650a
|
137
|
i++;
|
geky |
1:66a14afe650a
|
138
|
} else if (response[i] == '%' &&
|
geky |
1:66a14afe650a
|
139
|
response[i+1] != '%' &&
|
geky |
1:66a14afe650a
|
140
|
response[i+1] != '*') {
|
geky |
1:66a14afe650a
|
141
|
i++;
|
geky |
1:66a14afe650a
|
142
|
|
geky |
1:66a14afe650a
|
143
|
_buffer[offset++] = '%';
|
geky |
1:66a14afe650a
|
144
|
_buffer[offset++] = '*';
|
geky |
1:66a14afe650a
|
145
|
} else {
|
geky |
1:66a14afe650a
|
146
|
_buffer[offset++] = response[i++];
|
geky |
1:66a14afe650a
|
147
|
}
|
geky |
1:66a14afe650a
|
148
|
}
|
geky |
1:66a14afe650a
|
149
|
|
geky |
1:66a14afe650a
|
150
|
// Scanf has very poor support for catching errors
|
geky |
1:66a14afe650a
|
151
|
// fortunately, we can abuse the %n specifier to determine
|
geky |
1:66a14afe650a
|
152
|
// if the entire string was matched.
|
geky |
1:66a14afe650a
|
153
|
_buffer[offset++] = '%';
|
geky |
1:66a14afe650a
|
154
|
_buffer[offset++] = 'n';
|
geky |
1:66a14afe650a
|
155
|
_buffer[offset++] = 0;
|
geky |
1:66a14afe650a
|
156
|
|
geky |
1:66a14afe650a
|
157
|
// To workaround scanf's lack of error reporting, we actually
|
geky |
1:66a14afe650a
|
158
|
// make two passes. One checks the validity with the modified
|
geky |
1:66a14afe650a
|
159
|
// format string that only stores the matched characters (%n).
|
geky |
1:66a14afe650a
|
160
|
// The other reads in the actual matched values.
|
geky |
1:66a14afe650a
|
161
|
//
|
geky |
1:66a14afe650a
|
162
|
// We keep trying the match until we succeed or some other error
|
geky |
1:66a14afe650a
|
163
|
// derails us.
|
geky |
1:66a14afe650a
|
164
|
while (true) {
|
geky |
1:66a14afe650a
|
165
|
// Recieve response
|
geky |
1:66a14afe650a
|
166
|
if (!_getline(_buffer+offset, _buffer_size-offset)) {
|
geky |
1:66a14afe650a
|
167
|
va_end(args);
|
geky |
1:66a14afe650a
|
168
|
return false;
|
geky |
1:66a14afe650a
|
169
|
}
|
geky |
1:66a14afe650a
|
170
|
|
geky |
2:4d68f546861c
|
171
|
int count = -1;
|
geky |
1:66a14afe650a
|
172
|
sscanf(_buffer+offset, _buffer, &count);
|
geky |
1:66a14afe650a
|
173
|
|
geky |
1:66a14afe650a
|
174
|
// We only succeed if all characters in the response is matched
|
geky |
2:4d68f546861c
|
175
|
if (count >= 0 && (_buffer+offset)[count] == 0) {
|
geky |
1:66a14afe650a
|
176
|
// Reuse the front end of the buffer
|
geky |
1:66a14afe650a
|
177
|
int j;
|
geky |
1:66a14afe650a
|
178
|
for (j = 0; j < i; j++) {
|
geky |
1:66a14afe650a
|
179
|
_buffer[j] = response[j];
|
geky |
1:66a14afe650a
|
180
|
}
|
geky |
1:66a14afe650a
|
181
|
_buffer[j] = 0;
|
geky |
1:66a14afe650a
|
182
|
|
geky |
1:66a14afe650a
|
183
|
// Store the found results
|
geky |
1:66a14afe650a
|
184
|
vsscanf(_buffer+offset, _buffer, args);
|
geky |
1:66a14afe650a
|
185
|
|
geky |
1:66a14afe650a
|
186
|
// Jump to next line and continue parsing
|
geky |
1:66a14afe650a
|
187
|
response += i;
|
geky |
1:66a14afe650a
|
188
|
break;
|
geky |
1:66a14afe650a
|
189
|
}
|
geky |
1:66a14afe650a
|
190
|
}
|
geky |
0:c741e144517c
|
191
|
}
|
geky |
0:c741e144517c
|
192
|
|
geky |
0:c741e144517c
|
193
|
va_end(args);
|
geky |
0:c741e144517c
|
194
|
return true;
|
geky |
0:c741e144517c
|
195
|
}
|