├── .travis.yml ├── ATParser.cpp ├── ATParser.h ├── BufferedSerial ├── Buffer │ ├── MyBuffer.cpp │ └── MyBuffer.h ├── BufferedPrint.c ├── BufferedSerial.cpp └── BufferedSerial.h └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | script: 2 | # Compile any tests 3 | - PYTHONPATH=mbed-os python mbed-os/tools/test.py 4 | -t GCC_ARM -m K64F --source=. --build=BUILD/TESTS/K64F/GCC_ARM -j0 5 | -n tests* 6 | 7 | python: 8 | - "2.7" 9 | 10 | install: 11 | # Get arm-none-eabi-gcc 12 | - sudo add-apt-repository -y ppa:terry.guo/gcc-arm-embedded 13 | - sudo apt-get update -qq 14 | - sudo apt-get install -qq gcc-arm-none-eabi --force-yes 15 | # Get dependencies 16 | - git clone https://github.com/armmbed/mbed-os.git 17 | # Install python dependencies 18 | - pip install --user -r mbed-os/requirements.txt 19 | 20 | -------------------------------------------------------------------------------- /ATParser.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015 ARM Limited 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | * 15 | * @section DESCRIPTION 16 | * 17 | * Parser for the AT command syntax 18 | * 19 | */ 20 | 21 | #include "ATParser.h" 22 | #include "mbed_debug.h" 23 | 24 | 25 | // getc/putc handling with timeouts 26 | int ATParser::putc(char c) 27 | { 28 | Timer timer; 29 | timer.start(); 30 | 31 | while (true) { 32 | if (_serial->writeable()) { 33 | return _serial->putc(c); 34 | } 35 | if (timer.read_ms() > _timeout) { 36 | return -1; 37 | } 38 | } 39 | } 40 | 41 | int ATParser::getc() 42 | { 43 | Timer timer; 44 | timer.start(); 45 | 46 | while (true) { 47 | if (_serial->readable()) { 48 | return _serial->getc(); 49 | } 50 | if (timer.read_ms() > _timeout) { 51 | return -1; 52 | } 53 | } 54 | } 55 | 56 | void ATParser::flush() 57 | { 58 | while (_serial->readable()) { 59 | _serial->getc(); 60 | } 61 | } 62 | 63 | 64 | // read/write handling with timeouts 65 | int ATParser::write(const char *data, int size) 66 | { 67 | int i = 0; 68 | for ( ; i < size; i++) { 69 | if (putc(data[i]) < 0) { 70 | return -1; 71 | } 72 | } 73 | return i; 74 | } 75 | 76 | int ATParser::read(char *data, int size) 77 | { 78 | int i = 0; 79 | for ( ; i < size; i++) { 80 | int c = getc(); 81 | if (c < 0) { 82 | return -1; 83 | } 84 | data[i] = c; 85 | } 86 | return i; 87 | } 88 | 89 | 90 | // printf/scanf handling 91 | int ATParser::vprintf(const char *format, va_list args) 92 | { 93 | if (vsprintf(_buffer, format, args) < 0) { 94 | return false; 95 | } 96 | int i = 0; 97 | for ( ; _buffer[i]; i++) { 98 | if (putc(_buffer[i]) < 0) { 99 | return -1; 100 | } 101 | } 102 | return i; 103 | } 104 | 105 | int ATParser::vscanf(const char *format, va_list args) 106 | { 107 | // Since format is const, we need to copy it into our buffer to 108 | // add the line's null terminator and clobber value-matches with asterisks. 109 | // 110 | // We just use the beginning of the buffer to avoid unnecessary allocations. 111 | int i = 0; 112 | int offset = 0; 113 | 114 | while (format[i]) { 115 | if (format[i] == '%' && format[i+1] != '%' && format[i+1] != '*') { 116 | _buffer[offset++] = '%'; 117 | _buffer[offset++] = '*'; 118 | i++; 119 | } else { 120 | _buffer[offset++] = format[i++]; 121 | } 122 | } 123 | 124 | // Scanf has very poor support for catching errors 125 | // fortunately, we can abuse the %n specifier to determine 126 | // if the entire string was matched. 127 | _buffer[offset++] = '%'; 128 | _buffer[offset++] = 'n'; 129 | _buffer[offset++] = 0; 130 | 131 | // To workaround scanf's lack of error reporting, we actually 132 | // make two passes. One checks the validity with the modified 133 | // format string that only stores the matched characters (%n). 134 | // The other reads in the actual matched values. 135 | // 136 | // We keep trying the match until we succeed or some other error 137 | // derails us. 138 | int j = 0; 139 | 140 | while (true) { 141 | // Ran out of space 142 | if (j+1 >= _buffer_size - offset) { 143 | return false; 144 | } 145 | // Receive next character 146 | int c = getc(); 147 | if (c < 0) { 148 | return -1; 149 | } 150 | _buffer[offset + j++] = c; 151 | _buffer[offset + j] = 0; 152 | 153 | // Check for match 154 | int count = -1; 155 | sscanf(_buffer+offset, _buffer, &count); 156 | 157 | // We only succeed if all characters in the response are matched 158 | if (count == j) { 159 | // Store the found results 160 | vsscanf(_buffer+offset, format, args); 161 | return j; 162 | } 163 | } 164 | } 165 | 166 | 167 | // Command parsing with line handling 168 | bool ATParser::vsend(const char *command, va_list args) 169 | { 170 | // Create and send command 171 | if (vsprintf(_buffer, command, args) < 0) { 172 | return false; 173 | } 174 | for (int i = 0; _buffer[i]; i++) { 175 | if (putc(_buffer[i]) < 0) { 176 | return false; 177 | } 178 | } 179 | 180 | // Finish with newline 181 | for (int i = 0; _send_delimiter[i]; i++) { 182 | if (putc(_send_delimiter[i]) < 0) { 183 | return false; 184 | } 185 | } 186 | 187 | debug_if(dbg_on, "AT> %s\r\n", _buffer); 188 | return true; 189 | } 190 | 191 | bool ATParser::vrecv(const char *response, va_list args) 192 | { 193 | vrecv_start: 194 | // Iterate through each line in the expected response 195 | while (response[0]) { 196 | // Since response is const, we need to copy it into our buffer to 197 | // add the line's null terminator and clobber value-matches with asterisks. 198 | // 199 | // We just use the beginning of the buffer to avoid unnecessary allocations. 200 | int i = 0; 201 | int offset = 0; 202 | 203 | while (response[i]) { 204 | if (memcmp(&response[i+1-_recv_delim_size], _recv_delimiter, _recv_delim_size) == 0) { 205 | i++; 206 | break; 207 | } else if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') { 208 | _buffer[offset++] = '%'; 209 | _buffer[offset++] = '*'; 210 | i++; 211 | } else { 212 | _buffer[offset++] = response[i++]; 213 | } 214 | } 215 | 216 | // Scanf has very poor support for catching errors 217 | // fortunately, we can abuse the %n specifier to determine 218 | // if the entire string was matched. 219 | _buffer[offset++] = '%'; 220 | _buffer[offset++] = 'n'; 221 | _buffer[offset++] = 0; 222 | 223 | // To workaround scanf's lack of error reporting, we actually 224 | // make two passes. One checks the validity with the modified 225 | // format string that only stores the matched characters (%n). 226 | // The other reads in the actual matched values. 227 | // 228 | // We keep trying the match until we succeed or some other error 229 | // derails us. 230 | int j = 0; 231 | 232 | while (true) { 233 | // Receive next character 234 | int c = getc(); 235 | if (c < 0) { 236 | return false; 237 | } 238 | _buffer[offset + j++] = c; 239 | _buffer[offset + j] = 0; 240 | 241 | // Check for oob data 242 | for (unsigned int k = 0; k < _oobs.size(); k++) { 243 | if (j == (int)_oobs[k].len && memcmp( 244 | _oobs[k].prefix, _buffer+offset, _oobs[k].len) == 0) { 245 | debug_if(dbg_on, "AT! %s\r\n", _oobs[k].prefix); 246 | _oobs[k].cb(); 247 | 248 | // oob may have corrupted non-reentrant buffer, 249 | // so we need to set it up again. 250 | // Use goto to save stack usage rather than a 251 | // recursive approach. 252 | goto vrecv_start; 253 | } 254 | } 255 | 256 | // Check for match 257 | int count = -1; 258 | sscanf(_buffer+offset, _buffer, &count); 259 | 260 | // We only succeed if all characters in the response are matched 261 | if (count == j) { 262 | debug_if(dbg_on, "AT= %s\r\n", _buffer+offset); 263 | // Reuse the front end of the buffer 264 | memcpy(_buffer, response, i); 265 | _buffer[i] = 0; 266 | 267 | // Store the found results 268 | vsscanf(_buffer+offset, _buffer, args); 269 | 270 | // Jump to next line and continue parsing 271 | response += i; 272 | break; 273 | } 274 | 275 | // Clear the buffer when we hit a newline or ran out of space 276 | // running out of space usually means we ran into binary data 277 | if (j+1 >= _buffer_size - offset || 278 | strcmp(&_buffer[offset + j-_recv_delim_size], _recv_delimiter) == 0) { 279 | 280 | debug_if(dbg_on, "AT< %s", _buffer+offset); 281 | j = 0; 282 | } 283 | } 284 | } 285 | 286 | return true; 287 | } 288 | 289 | 290 | // Mapping to vararg functions 291 | int ATParser::printf(const char *format, ...) 292 | { 293 | va_list args; 294 | va_start(args, format); 295 | int res = vprintf(format, args); 296 | va_end(args); 297 | return res; 298 | } 299 | 300 | int ATParser::scanf(const char *format, ...) 301 | { 302 | va_list args; 303 | va_start(args, format); 304 | int res = vscanf(format, args); 305 | va_end(args); 306 | return res; 307 | } 308 | 309 | bool ATParser::send(const char *command, ...) 310 | { 311 | va_list args; 312 | va_start(args, command); 313 | bool res = vsend(command, args); 314 | va_end(args); 315 | return res; 316 | } 317 | 318 | bool ATParser::recv(const char *response, ...) 319 | { 320 | va_list args; 321 | va_start(args, response); 322 | bool res = vrecv(response, args); 323 | va_end(args); 324 | return res; 325 | } 326 | 327 | 328 | // oob registration 329 | void ATParser::oob(const char *prefix, Callback cb) 330 | { 331 | struct oob oob; 332 | oob.len = strlen(prefix); 333 | oob.prefix = prefix; 334 | oob.cb = cb; 335 | _oobs.push_back(oob); 336 | } 337 | 338 | bool ATParser::process_oob() 339 | { 340 | if (!_serial->readable()) { 341 | return false; 342 | } 343 | 344 | int i = 0; 345 | while (true) { 346 | // Receive next character 347 | int c = getc(); 348 | if (c < 0) { 349 | return false; 350 | } 351 | _buffer[i++] = c; 352 | _buffer[i] = 0; 353 | 354 | // Check for oob data 355 | for (unsigned int j = 0; j < _oobs.size(); j++) { 356 | if (i == (int)_oobs[j].len && memcmp( 357 | _oobs[j].prefix, _buffer, _oobs[j].len) == 0) { 358 | debug_if(dbg_on, "AT! %s\r\n", _oobs[j].prefix); 359 | _oobs[j].cb(); 360 | return true; 361 | } 362 | } 363 | 364 | // Clear the buffer when we hit a newline or ran out of space 365 | // running out of space usually means we ran into binary data 366 | if (i+1 >= _buffer_size || 367 | strcmp(&_buffer[i-_recv_delim_size], _recv_delimiter) == 0) { 368 | 369 | debug_if(dbg_on, "AT< %s", _buffer); 370 | i = 0; 371 | } 372 | } 373 | 374 | } 375 | -------------------------------------------------------------------------------- /ATParser.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015 ARM Limited 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | * 15 | * @section DESCRIPTION 16 | * 17 | * Parser for the AT command syntax 18 | * 19 | */ 20 | #ifndef AT_PARSER_H 21 | #define AT_PARSER_H 22 | 23 | #include "mbed.h" 24 | #include 25 | #include 26 | #include "BufferedSerial.h" 27 | #include "Callback.h" 28 | 29 | 30 | /** 31 | * Parser class for parsing AT commands 32 | * 33 | * Here are some examples: 34 | * @code 35 | * ATParser at = ATParser(serial, "\r\n"); 36 | * int value; 37 | * char buffer[100]; 38 | * 39 | * at.send("AT") && at.recv("OK"); 40 | * at.send("AT+CWMODE=%d", 3) && at.recv("OK"); 41 | * at.send("AT+CWMODE?") && at.recv("+CWMODE:%d\r\nOK", &value); 42 | * at.recv("+IPD,%d:", &value); 43 | * at.read(buffer, value); 44 | * at.recv("OK"); 45 | * @endcode 46 | */ 47 | class ATParser 48 | { 49 | private: 50 | // Serial information 51 | BufferedSerial *_serial; 52 | int _buffer_size; 53 | char *_buffer; 54 | int _timeout; 55 | 56 | // Parsing information 57 | const char *_recv_delimiter; 58 | const char *_send_delimiter; 59 | int _recv_delim_size; 60 | int _send_delim_size; 61 | bool dbg_on; 62 | 63 | struct oob { 64 | unsigned len; 65 | const char *prefix; 66 | mbed::Callback cb; 67 | }; 68 | std::vector _oobs; 69 | 70 | public: 71 | /** 72 | * Constructor 73 | * 74 | * @param serial serial interface to use for AT commands 75 | * @param buffer_size size of internal buffer for transaction 76 | * @param timeout timeout of the connection 77 | * @param send_delimiter string of characters to use as line delimiters for sending 78 | * @param recv_delimiter string of characters to use as line delimiters for receiving 79 | */ 80 | ATParser(BufferedSerial &serial, const char *recv_delimiter, const char *send_delimiter, int buffer_size = 256, int timeout = 8000, bool debug = false) : 81 | _serial(&serial), 82 | _buffer_size(buffer_size) { 83 | _buffer = new char[buffer_size]; 84 | setTimeout(timeout); 85 | setRecvDelimiter(recv_delimiter); 86 | setSendDelimiter(send_delimiter); 87 | debugOn(debug); 88 | } 89 | 90 | /** 91 | * Constructor 92 | * 93 | * @param serial serial interface to use for AT commands 94 | * @param buffer_size size of internal buffer for transaction 95 | * @param timeout timeout of the connection 96 | * @param delimiter string of characters to use as line delimiters 97 | */ 98 | ATParser(BufferedSerial &serial, const char *delimiter = "\r\n", int buffer_size = 256, int timeout = 8000, bool debug = false) : 99 | _serial(&serial), 100 | _buffer_size(buffer_size) { 101 | _buffer = new char[buffer_size]; 102 | setTimeout(timeout); 103 | setDelimiter(delimiter); 104 | debugOn(debug); 105 | } 106 | 107 | /** 108 | * Destructor 109 | */ 110 | ~ATParser() { 111 | delete [] _buffer; 112 | } 113 | 114 | /** 115 | * Allows timeout to be changed between commands 116 | * 117 | * @param timeout timeout of the connection 118 | */ 119 | void setTimeout(int timeout) { 120 | _timeout = timeout; 121 | } 122 | 123 | /** 124 | * Sets string of characters to use as line delimiters 125 | * 126 | * @param delimiter string of characters to use as line delimiters 127 | */ 128 | void setDelimiter(const char *delimiter) { 129 | _recv_delimiter = delimiter; 130 | _send_delimiter = delimiter; 131 | _recv_delim_size = strlen(delimiter); 132 | _send_delim_size = strlen(delimiter); 133 | } 134 | 135 | /** 136 | * Sets string of characters to use as line delimiters for receiving 137 | * 138 | * @param delimiter string of characters to use as line delimiters 139 | */ 140 | void setRecvDelimiter(const char *delimiter) { 141 | _recv_delimiter = delimiter; 142 | _recv_delim_size = strlen(delimiter); 143 | } 144 | 145 | /** 146 | * Sets string of characters to use as line delimiters for sending 147 | * 148 | * @param delimiter string of characters to use as line delimiters 149 | */ 150 | void setSendDelimiter(const char *delimiter) { 151 | _send_delimiter = delimiter; 152 | _send_delim_size = strlen(delimiter); 153 | } 154 | 155 | /** 156 | * Allows echo to be on or off 157 | * 158 | * @param echo 1 for echo and 0 turns it off 159 | */ 160 | void debugOn(uint8_t on) { 161 | dbg_on = (on) ? 1 : 0; 162 | } 163 | 164 | /** 165 | * Sends an AT command 166 | * 167 | * Sends a formatted command using printf style formatting 168 | * @see ::printf 169 | * 170 | * @param command printf-like format string of command to send which 171 | * is appended with the specified delimiter 172 | * @param ... all printf-like arguments to insert into command 173 | * @return true only if command is successfully sent 174 | */ 175 | bool send(const char *command, ...); 176 | bool vsend(const char *command, va_list args); 177 | 178 | /** 179 | * Recieve an AT response 180 | * 181 | * Recieves a formatted response using scanf style formatting 182 | * @see ::scanf 183 | * 184 | * Responses are parsed line at a time using the specified delimiter. 185 | * Any recieved data that does not match the response is ignored until 186 | * a timeout occurs. 187 | * 188 | * @param response scanf-like format string of response to expect 189 | * @param ... all scanf-like arguments to extract from response 190 | * @return true only if response is successfully matched 191 | */ 192 | bool recv(const char *response, ...); 193 | bool vrecv(const char *response, va_list args); 194 | 195 | /** 196 | * Write a single byte to the underlying stream 197 | * 198 | * @param c The byte to write 199 | * @return The byte that was written or -1 during a timeout 200 | */ 201 | int putc(char c); 202 | 203 | /** 204 | * Get a single byte from the underlying stream 205 | * 206 | * @return The byte that was read or -1 during a timeout 207 | */ 208 | int getc(); 209 | 210 | /** 211 | * Write an array of bytes to the underlying stream 212 | * 213 | * @param data the array of bytes to write 214 | * @param size number of bytes to write 215 | * @return number of bytes written or -1 on failure 216 | */ 217 | int write(const char *data, int size); 218 | 219 | /** 220 | * Read an array of bytes from the underlying stream 221 | * 222 | * @param data the destination for the read bytes 223 | * @param size number of bytes to read 224 | * @return number of bytes read or -1 on failure 225 | */ 226 | int read(char *data, int size); 227 | 228 | /** 229 | * Direct printf to underlying stream 230 | * @see ::printf 231 | * 232 | * @param format format string to pass to printf 233 | * @param ... arguments to printf 234 | * @return number of bytes written or -1 on failure 235 | */ 236 | int printf(const char *format, ...); 237 | int vprintf(const char *format, va_list args); 238 | 239 | /** 240 | * Direct scanf on underlying stream 241 | * @see ::scanf 242 | * 243 | * @param format format string to pass to scanf 244 | * @param ... arguments to scanf 245 | * @return number of bytes read or -1 on failure 246 | */ 247 | int scanf(const char *format, ...); 248 | int vscanf(const char *format, va_list args); 249 | 250 | /** 251 | * Attach a callback for out-of-band data 252 | * 253 | * @param prefix string on when to initiate callback 254 | * @param func callback to call when string is read 255 | * @note out-of-band data is only processed during a scanf call 256 | */ 257 | void oob(const char *prefix, mbed::Callback func); 258 | 259 | /** 260 | * Process out-of-band data 261 | * 262 | * Process out-of-band data in the receive buffer. This function 263 | * returns immediately if there is no data to process. 264 | * 265 | * @return true if oob data processed, false otherwise 266 | */ 267 | bool process_oob(void); 268 | 269 | /** 270 | * Attach a callback for out-of-band data 271 | * 272 | * @param prefix string on when to initiate callback 273 | * @param obj pointer to object to call member function on 274 | * @param method callback to call when string is read 275 | * @note out-of-band data is only processed during a scanf call 276 | */ 277 | template 278 | void oob(const char *prefix, T *obj, M method) { 279 | return oob(prefix, mbed::Callback(obj, method)); 280 | } 281 | 282 | /** 283 | * Flushes the underlying stream 284 | */ 285 | void flush(); 286 | }; 287 | #endif 288 | -------------------------------------------------------------------------------- /BufferedSerial/Buffer/MyBuffer.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file Buffer.cpp 4 | * @brief Software Buffer - Templated Ring Buffer for most data types 5 | * @author sam grove 6 | * @version 1.0 7 | * @see 8 | * 9 | * Copyright (c) 2013 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | 24 | #include "MyBuffer.h" 25 | 26 | template 27 | MyBuffer::MyBuffer(uint32_t size) 28 | { 29 | _buf = new T [size]; 30 | _size = size; 31 | clear(); 32 | 33 | return; 34 | } 35 | 36 | template 37 | MyBuffer::~MyBuffer() 38 | { 39 | delete [] _buf; 40 | 41 | return; 42 | } 43 | 44 | template 45 | uint32_t MyBuffer::getSize() 46 | { 47 | return this->_size; 48 | } 49 | 50 | template 51 | void MyBuffer::clear(void) 52 | { 53 | _wloc = 0; 54 | _rloc = 0; 55 | memset(_buf, 0, _size); 56 | 57 | return; 58 | } 59 | 60 | template 61 | uint32_t MyBuffer::peek(char c) 62 | { 63 | return 1; 64 | } 65 | 66 | // make the linker aware of some possible types 67 | template class MyBuffer; 68 | template class MyBuffer; 69 | template class MyBuffer; 70 | template class MyBuffer; 71 | template class MyBuffer; 72 | template class MyBuffer; 73 | template class MyBuffer; 74 | template class MyBuffer; 75 | template class MyBuffer; 76 | template class MyBuffer; 77 | -------------------------------------------------------------------------------- /BufferedSerial/Buffer/MyBuffer.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file Buffer.h 4 | * @brief Software Buffer - Templated Ring Buffer for most data types 5 | * @author sam grove 6 | * @version 1.0 7 | * @see 8 | * 9 | * Copyright (c) 2013 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | 24 | #ifndef MYBUFFER_H 25 | #define MYBUFFER_H 26 | 27 | #include 28 | #include 29 | 30 | #include "cmsis.h" 31 | 32 | /** A templated software ring buffer 33 | * 34 | * Example: 35 | * @code 36 | * #include "mbed.h" 37 | * #include "MyBuffer.h" 38 | * 39 | * MyBuffer buf; 40 | * 41 | * int main() 42 | * { 43 | * buf = 'a'; 44 | * buf.put('b'); 45 | * char *head = buf.head(); 46 | * puts(head); 47 | * 48 | * char whats_in_there[2] = {0}; 49 | * int pos = 0; 50 | * 51 | * while(buf.available()) 52 | * { 53 | * whats_in_there[pos++] = buf; 54 | * } 55 | * printf("%c %c\n", whats_in_there[0], whats_in_there[1]); 56 | * buf.clear(); 57 | * error("done\n\n\n"); 58 | * } 59 | * @endcode 60 | */ 61 | 62 | template 63 | class MyBuffer 64 | { 65 | private: 66 | T *_buf; 67 | volatile uint32_t _wloc; 68 | volatile uint32_t _rloc; 69 | uint32_t _size; 70 | 71 | public: 72 | /** Create a Buffer and allocate memory for it 73 | * @param size The size of the buffer 74 | */ 75 | MyBuffer(uint32_t size = 0x100); 76 | 77 | /** Get the size of the ring buffer 78 | * @return the size of the ring buffer 79 | */ 80 | uint32_t getSize(); 81 | 82 | /** Destry a Buffer and release it's allocated memory 83 | */ 84 | ~MyBuffer(); 85 | 86 | /** Add a data element into the buffer 87 | * @param data Something to add to the buffer 88 | */ 89 | void put(T data); 90 | 91 | /** Remove a data element from the buffer 92 | * @return Pull the oldest element from the buffer 93 | */ 94 | T get(void); 95 | 96 | /** Get the address to the head of the buffer 97 | * @return The address of element 0 in the buffer 98 | */ 99 | T *head(void); 100 | 101 | /** Reset the buffer to 0. Useful if using head() to parse packeted data 102 | */ 103 | void clear(void); 104 | 105 | /** Determine if anything is readable in the buffer 106 | * @return 1 if something can be read, 0 otherwise 107 | */ 108 | uint32_t available(void); 109 | 110 | /** Overloaded operator for writing to the buffer 111 | * @param data Something to put in the buffer 112 | * @return 113 | */ 114 | MyBuffer &operator= (T data) 115 | { 116 | put(data); 117 | return *this; 118 | } 119 | 120 | /** Overloaded operator for reading from the buffer 121 | * @return Pull the oldest element from the buffer 122 | */ 123 | operator int(void) 124 | { 125 | return get(); 126 | } 127 | 128 | uint32_t peek(char c); 129 | 130 | }; 131 | 132 | template 133 | inline void MyBuffer::put(T data) 134 | { 135 | _buf[_wloc] = data; 136 | __DSB(); 137 | _wloc = (_wloc + 1) % (_size-1); 138 | 139 | return; 140 | } 141 | 142 | template 143 | inline T MyBuffer::get(void) 144 | { 145 | T data_pos = _buf[_rloc]; 146 | __DSB(); 147 | _rloc = (_rloc + 1) % (_size-1); 148 | 149 | return data_pos; 150 | } 151 | 152 | template 153 | inline T *MyBuffer::head(void) 154 | { 155 | T *data_pos = &_buf[0]; 156 | 157 | return data_pos; 158 | } 159 | 160 | template 161 | inline uint32_t MyBuffer::available(void) 162 | { 163 | return (_wloc == _rloc) ? 0 : 1; 164 | } 165 | 166 | #endif 167 | 168 | -------------------------------------------------------------------------------- /BufferedSerial/BufferedPrint.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ARM Limited. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * Licensed under the Apache License, Version 2.0 (the License); you may 5 | * not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "mbed_error.h" 23 | 24 | size_t BufferedSerialThunk(void *buf_serial, const void *s, size_t length); 25 | 26 | int BufferedPrintfC(void *stream, int size, const char* format, va_list arg) 27 | { 28 | int r; 29 | char buffer[512]; 30 | if (size >= 512) { 31 | return -1; 32 | } 33 | memset(buffer, 0, size); 34 | r = vsprintf(buffer, format, arg); 35 | // this may not hit the heap but should alert the user anyways 36 | if(r > (int32_t) size) { 37 | error("%s %d buffer overwrite (max_buf_size: %d exceeded: %d)!\r\n", __FILE__, __LINE__, size, r); 38 | return 0; 39 | } 40 | if ( r > 0 ) { 41 | BufferedSerialThunk(stream, buffer, r); 42 | } 43 | return r; 44 | } 45 | -------------------------------------------------------------------------------- /BufferedSerial/BufferedSerial.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file BufferedSerial.cpp 3 | * @brief Software Buffer - Extends mbed Serial functionallity adding irq driven TX and RX 4 | * @author sam grove 5 | * @version 1.0 6 | * @see 7 | * 8 | * Copyright (c) 2013 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | */ 22 | 23 | #include "BufferedSerial.h" 24 | #include 25 | 26 | extern "C" int BufferedPrintfC(void *stream, int size, const char* format, va_list arg); 27 | 28 | BufferedSerial::BufferedSerial(PinName tx, PinName rx, uint32_t buf_size, uint32_t tx_multiple, const char* name) 29 | : RawSerial(tx, rx) , _rxbuf(buf_size), _txbuf((uint32_t)(tx_multiple*buf_size)) 30 | { 31 | RawSerial::attach(callback(this, &BufferedSerial::rxIrq), Serial::RxIrq); 32 | this->_buf_size = buf_size; 33 | this->_tx_multiple = tx_multiple; 34 | return; 35 | } 36 | 37 | BufferedSerial::~BufferedSerial(void) 38 | { 39 | RawSerial::attach(NULL, RawSerial::RxIrq); 40 | RawSerial::attach(NULL, RawSerial::TxIrq); 41 | 42 | return; 43 | } 44 | 45 | int BufferedSerial::readable(void) 46 | { 47 | return _rxbuf.available(); // note: look if things are in the buffer 48 | } 49 | 50 | int BufferedSerial::writeable(void) 51 | { 52 | return 1; // buffer allows overwriting by design, always true 53 | } 54 | 55 | int BufferedSerial::getc(void) 56 | { 57 | return _rxbuf; 58 | } 59 | 60 | int BufferedSerial::putc(int c) 61 | { 62 | _txbuf = (char)c; 63 | BufferedSerial::prime(); 64 | 65 | return c; 66 | } 67 | 68 | int BufferedSerial::puts(const char *s) 69 | { 70 | if (s != NULL) { 71 | const char* ptr = s; 72 | 73 | while(*(ptr) != 0) { 74 | _txbuf = *(ptr++); 75 | } 76 | _txbuf = '\n'; // done per puts definition 77 | BufferedSerial::prime(); 78 | 79 | return (ptr - s) + 1; 80 | } 81 | return 0; 82 | } 83 | 84 | extern "C" size_t BufferedSerialThunk(void *buf_serial, const void *s, size_t length) 85 | { 86 | BufferedSerial *buffered_serial = (BufferedSerial *)buf_serial; 87 | return buffered_serial->write(s, length); 88 | } 89 | 90 | int BufferedSerial::printf(const char* format, ...) 91 | { 92 | va_list arg; 93 | va_start(arg, format); 94 | int r = BufferedPrintfC((void*)this, this->_buf_size, format, arg); 95 | va_end(arg); 96 | return r; 97 | } 98 | 99 | ssize_t BufferedSerial::write(const void *s, size_t length) 100 | { 101 | if (s != NULL && length > 0) { 102 | const char* ptr = (const char*)s; 103 | const char* end = ptr + length; 104 | 105 | while (ptr != end) { 106 | _txbuf = *(ptr++); 107 | } 108 | BufferedSerial::prime(); 109 | 110 | return ptr - (const char*)s; 111 | } 112 | return 0; 113 | } 114 | 115 | 116 | void BufferedSerial::rxIrq(void) 117 | { 118 | // read from the peripheral and make sure something is available 119 | if(serial_readable(&_serial)) { 120 | _rxbuf = serial_getc(&_serial); // if so load them into a buffer 121 | // trigger callback if necessary 122 | if (_cbs[RxIrq]) { 123 | _cbs[RxIrq](); 124 | } 125 | } 126 | 127 | return; 128 | } 129 | 130 | void BufferedSerial::txIrq(void) 131 | { 132 | // see if there is room in the hardware fifo and if something is in the software fifo 133 | while(serial_writable(&_serial)) { 134 | if(_txbuf.available()) { 135 | serial_putc(&_serial, (int)_txbuf.get()); 136 | } else { 137 | // disable the TX interrupt when there is nothing left to send 138 | RawSerial::attach(NULL, RawSerial::TxIrq); 139 | // trigger callback if necessary 140 | if (_cbs[TxIrq]) { 141 | _cbs[TxIrq](); 142 | } 143 | break; 144 | } 145 | } 146 | 147 | return; 148 | } 149 | 150 | void BufferedSerial::prime(void) 151 | { 152 | // if already busy then the irq will pick this up 153 | if(serial_writable(&_serial)) { 154 | RawSerial::attach(NULL, RawSerial::TxIrq); // make sure not to cause contention in the irq 155 | BufferedSerial::txIrq(); // only write to hardware in one place 156 | RawSerial::attach(callback(this, &BufferedSerial::txIrq), RawSerial::TxIrq); 157 | } 158 | 159 | return; 160 | } 161 | 162 | void BufferedSerial::attach(Callback func, IrqType type) 163 | { 164 | _cbs[type] = func; 165 | } 166 | 167 | -------------------------------------------------------------------------------- /BufferedSerial/BufferedSerial.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file BufferedSerial.h 4 | * @brief Software Buffer - Extends mbed Serial functionallity adding irq driven TX and RX 5 | * @author sam grove 6 | * @version 1.0 7 | * @see 8 | * 9 | * Copyright (c) 2013 10 | * 11 | * Licensed under the Apache License, Version 2.0 (the "License"); 12 | * you may not use this file except in compliance with the License. 13 | * You may obtain a copy of the License at 14 | * 15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | * 17 | * Unless required by applicable law or agreed to in writing, software 18 | * distributed under the License is distributed on an "AS IS" BASIS, 19 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | * See the License for the specific language governing permissions and 21 | * limitations under the License. 22 | */ 23 | 24 | #ifndef BUFFEREDSERIAL_H 25 | #define BUFFEREDSERIAL_H 26 | 27 | #include "mbed.h" 28 | #include "MyBuffer.h" 29 | 30 | /** A serial port (UART) for communication with other serial devices 31 | * 32 | * Can be used for Full Duplex communication, or Simplex by specifying 33 | * one pin as NC (Not Connected) 34 | * 35 | * Example: 36 | * @code 37 | * #include "mbed.h" 38 | * #include "BufferedSerial.h" 39 | * 40 | * BufferedSerial pc(USBTX, USBRX); 41 | * 42 | * int main() 43 | * { 44 | * while(1) 45 | * { 46 | * Timer s; 47 | * 48 | * s.start(); 49 | * pc.printf("Hello World - buffered\n"); 50 | * int buffered_time = s.read_us(); 51 | * wait(0.1f); // give time for the buffer to empty 52 | * 53 | * s.reset(); 54 | * printf("Hello World - blocking\n"); 55 | * int polled_time = s.read_us(); 56 | * s.stop(); 57 | * wait(0.1f); // give time for the buffer to empty 58 | * 59 | * pc.printf("printf buffered took %d us\n", buffered_time); 60 | * pc.printf("printf blocking took %d us\n", polled_time); 61 | * wait(0.5f); 62 | * } 63 | * } 64 | * @endcode 65 | */ 66 | 67 | /** 68 | * @class BufferedSerial 69 | * @brief Software buffers and interrupt driven tx and rx for Serial 70 | */ 71 | class BufferedSerial : public RawSerial 72 | { 73 | private: 74 | MyBuffer _rxbuf; 75 | MyBuffer _txbuf; 76 | uint32_t _buf_size; 77 | uint32_t _tx_multiple; 78 | 79 | void rxIrq(void); 80 | void txIrq(void); 81 | void prime(void); 82 | 83 | Callback _cbs[2]; 84 | 85 | public: 86 | /** Create a BufferedSerial port, connected to the specified transmit and receive pins 87 | * @param tx Transmit pin 88 | * @param rx Receive pin 89 | * @param buf_size printf() buffer size 90 | * @param tx_multiple amount of max printf() present in the internal ring buffer at one time 91 | * @param name optional name 92 | * @note Either tx or rx may be specified as NC if unused 93 | */ 94 | BufferedSerial(PinName tx, PinName rx, uint32_t buf_size = 256, uint32_t tx_multiple = 4,const char* name=NULL); 95 | 96 | /** Destroy a BufferedSerial port 97 | */ 98 | virtual ~BufferedSerial(void); 99 | 100 | /** Check on how many bytes are in the rx buffer 101 | * @return 1 if something exists, 0 otherwise 102 | */ 103 | virtual int readable(void); 104 | 105 | /** Check to see if the tx buffer has room 106 | * @return 1 always has room and can overwrite previous content if too small / slow 107 | */ 108 | virtual int writeable(void); 109 | 110 | /** Get a single byte from the BufferedSerial Port. 111 | * Should check readable() before calling this. 112 | * @return A byte that came in on the Serial Port 113 | */ 114 | virtual int getc(void); 115 | 116 | /** Write a single byte to the BufferedSerial Port. 117 | * @param c The byte to write to the Serial Port 118 | * @return The byte that was written to the Serial Port Buffer 119 | */ 120 | virtual int putc(int c); 121 | 122 | /** Write a string to the BufferedSerial Port. Must be NULL terminated 123 | * @param s The string to write to the Serial Port 124 | * @return The number of bytes written to the Serial Port Buffer 125 | */ 126 | virtual int puts(const char *s); 127 | 128 | /** Write a formatted string to the BufferedSerial Port. 129 | * @param format The string + format specifiers to write to the Serial Port 130 | * @return The number of bytes written to the Serial Port Buffer 131 | */ 132 | virtual int printf(const char* format, ...); 133 | 134 | /** Write data to the Buffered Serial Port 135 | * @param s A pointer to data to send 136 | * @param length The amount of data being pointed to 137 | * @return The number of bytes written to the Serial Port Buffer 138 | */ 139 | virtual ssize_t write(const void *s, std::size_t length); 140 | 141 | /** Attach a function to call whenever a serial interrupt is generated 142 | * @param func A pointer to a void function, or 0 to set as none 143 | * @param type Which serial interrupt to attach the member function to (Serial::RxIrq for receive, TxIrq for transmit buffer empty) 144 | */ 145 | virtual void attach(Callback func, IrqType type=RxIrq); 146 | 147 | /** Attach a member function to call whenever a serial interrupt is generated 148 | * @param obj pointer to the object to call the member function on 149 | * @param method pointer to the member function to call 150 | * @param type Which serial interrupt to attach the member function to (Serial::RxIrq for receive, TxIrq for transmit buffer empty) 151 | */ 152 | template 153 | void attach(T *obj, void (T::*method)(), IrqType type=RxIrq) { 154 | attach(Callback(obj, method), type); 155 | } 156 | 157 | /** Attach a member function to call whenever a serial interrupt is generated 158 | * @param obj pointer to the object to call the member function on 159 | * @param method pointer to the member function to call 160 | * @param type Which serial interrupt to attach the member function to (Serial::RxIrq for receive, TxIrq for transmit buffer empty) 161 | */ 162 | template 163 | void attach(T *obj, void (*method)(T*), IrqType type=RxIrq) { 164 | attach(Callback(obj, method), type); 165 | } 166 | }; 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AT Command Parser 2 | 3 | An mbed-os compatible AT command parser. 4 | --------------------------------------------------------------------------------