drivers: initial commit of generic AT parser module

This commit is contained in:
Kaspar Schleiser 2016-12-13 14:34:13 +01:00
parent 3d2d3be267
commit 9c9fad6da5
4 changed files with 388 additions and 0 deletions

View File

@ -21,6 +21,11 @@ ifneq (,$(filter apa102,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio FEATURES_REQUIRED += periph_gpio
endif endif
ifneq (,$(filter at,$(USEMODULE)))
USEMODULE += fmt
USEMODULE += xtimer
endif
ifneq (,$(filter at30tse75x,$(USEMODULE))) ifneq (,$(filter at30tse75x,$(USEMODULE)))
USEMODULE += xtimer USEMODULE += xtimer
FEATURES_REQUIRED += periph_i2c FEATURES_REQUIRED += periph_i2c

1
drivers/at/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

209
drivers/at/at.c Normal file
View File

@ -0,0 +1,209 @@
/*
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <errno.h>
#include <string.h>
#include "at.h"
#include "fmt.h"
#include "isrpipe.h"
#include "periph/uart.h"
#include "xtimer.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#ifndef AT_PRINT_INCOMING
#define AT_PRINT_INCOMING (0)
#endif
int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t bufsize)
{
dev->uart = uart;
isrpipe_init(&dev->isrpipe, buf, bufsize);
uart_init(uart, baudrate, (uart_rx_cb_t) isrpipe_write_one,
&dev->isrpipe);
return 0;
}
int at_expect_bytes(at_dev_t *dev, const char *bytes, size_t len, uint32_t timeout)
{
while (len) {
char c;
int res;
if ((res = isrpipe_read_timeout(&dev->isrpipe, &c, 1, timeout)) == 1) {
if (AT_PRINT_INCOMING) {
print(&c, 1);
}
if (!(c == *bytes++)) {
return -1;
}
len--;
}
else {
return res;
}
}
return 0;
}
int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout)
{
unsigned cmdlen = strlen(command);
uart_write(dev->uart, (const uint8_t *)command, cmdlen);
uart_write(dev->uart, (const uint8_t *)"\n", 1);
if (at_expect_bytes(dev, command, cmdlen, timeout)) {
return -1;
}
if (at_expect_bytes(dev, "\r\n", 2, timeout)) {
return -2;
}
return 0;
}
void at_drain(at_dev_t *dev)
{
char _tmp[16];
int res;
do {
res = isrpipe_read_timeout(&dev->isrpipe, _tmp, sizeof(_tmp), 10000U);
} while (res > 0);
}
ssize_t at_send_cmd_get_resp(at_dev_t *dev, const char *command, char *resp_buf, size_t len, uint32_t timeout)
{
ssize_t res = -1;
at_drain(dev);
res = at_send_cmd(dev, command, timeout);
if (res) {
goto out;
}
res = at_readline(dev, resp_buf, len, timeout);
if (res == 0) {
/* skip possible empty line */
res = at_readline(dev, resp_buf, len, timeout);
}
out:
return res;
}
ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, char *resp_buf, size_t len, uint32_t timeout)
{
ssize_t res = -1;
size_t bytes_left = len - 1;
char *pos = resp_buf;
memset(resp_buf, '\0', len);
at_drain(dev);
res = at_send_cmd(dev, command, timeout);
if (res) {
goto out;
}
while(1) {
res = at_readline(dev, pos, bytes_left, timeout);
if (res == 0) {
continue;
}
else if (res > 0) {
bytes_left -= res;
if ((res == 2) && (strncmp(pos, "OK", 2) == 0)) {
res = len - bytes_left;
break;
}
else if ((res == 5) && (strncmp(pos, "ERROR", 5) == 0)) {
return -1;
}
else if (strncmp(pos, "+CME ERROR:", 11) == 0) {
return -1;
}
else {
pos += res;
if (bytes_left) {
*pos++ = '\n';
bytes_left--;
}
else {
return -1;
}
}
}
else {
break;
}
}
out:
return res;
}
int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout)
{
int res;
char resp_buf[32];
res = at_send_cmd_get_resp(dev, command, resp_buf, sizeof(resp_buf), timeout);
if (res > 0) {
if (strcmp(resp_buf, "OK") == 0) {
res = 0;
}
else {
res = -1;
}
}
return res;
}
ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, uint32_t timeout)
{
ssize_t res = -1;
char *resp_pos = resp_buf;
memset(resp_buf, 0, len);
while (len) {
int read_res;
if ((read_res = isrpipe_read_timeout(&dev->isrpipe, resp_pos, 1, timeout)) == 1) {
if (AT_PRINT_INCOMING) {
print(resp_pos, read_res);
}
if (*resp_pos == '\r') {
continue;
}
if (*resp_pos == '\n') {
*resp_pos = '\0';
res = resp_pos - resp_buf;
goto out;
}
resp_pos += read_res;
len -= read_res;
}
else if (read_res == -ETIMEDOUT) {
res = -ETIMEDOUT;
break;
}
}
out:
return res;
}

173
drivers/include/at.h Normal file
View File

@ -0,0 +1,173 @@
/*
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup drivers_at AT (Hayes) command set library
* @ingroup drivers
* @brief AT (Hayes) command set library
*
* This module provides functions to interact with devices using AT commands.
*
* Most functions compare the bytes echoed by the device with what they
* intended to send, and bail out if there's no match.
*
* Furthermore, the library tries to copy with difficulties regarding different
* line endings. It usually sends "<command><CR>", but expects
* "<command>\LF\CR" as echo.
*
* As a debugging aid, when compiled with "-DAT_PRINT_INCOMING=1", every input
* byte gets printed.
* @{
*
* @file
*
* @brief AT (Hayes) library interface
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef AT_H
#define AT_H
#include <stdint.h>
#include "isrpipe.h"
#include "periph/uart.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief AT device structure
*/
typedef struct {
isrpipe_t isrpipe; /**< isrpipe used for getting data from uart */
uart_t uart; /**< UART device where the AT device is attached */
} at_dev_t;
/**
* @brief Initialize AT device struct
*
* @param[in] dev struct to initialize
* @param[in] uart UART the device is connected to
* @param[in] baudrate baudrate of the device
* @param[in] buf input buffer
* @param[in] bufsize size of @p buf
*
* @returns 0 on success
* @returns <0 otherwise
*/
int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t bufsize);
/**
* @brief simple command helper
*
* This function sends an AT command to the device and waits for "OK".
*
* @param[in] dev device to operate on
* @param[in] command command string to send
* @param[in] timeout timeout (in usec)
*
* @returns 0 when device answers "OK"
* @returns <0 otherwise
*/
int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout);
/**
* @brief send AT command, wait for response
*
* This function will send the supplied @p command, then wait and return one
* line of response.
*
* A possible empty line will be skipped.
*
* @param[in] dev device to operate on
* @param[in] command command to send
* @param[out] resp_buf buffer for storing response
* @param[in] len len of @p buffer
* @param[in] timeout timeout (in usec)
*
* @returns lenght of response on success
* @returns <0 on error
*/
ssize_t at_send_cmd_get_resp(at_dev_t *dev, const char *command, char *resp_buf, size_t len, uint32_t timeout);
/**
* @brief send AT command, wait for multiline response
*
* This function will send the supplied @p command, then return all response
* lines until the device sends "OK".
*
* If a line starts with "ERROR" or "+CME ERROR:", or the buffer is full, the
* function returns -1.
*
* @param[in] dev device to operate on
* @param[in] command command to send
* @param[out] resp_buf buffer for storing response
* @param[in] len len of @p buffer
* @param[in] timeout timeout (in usec)
*
* @returns lenght of response on success
* @returns <0 on error
*/
ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, char *resp_buf, size_t len, uint32_t timeout);
/**
* @brief Expect bytes from device
*
* @param[in] dev device to operate on
* @param[in] bytes buffer containing bytes to expect
* @param[in] len number of bytes to expect
* @param[in] timeout timeout (in usec)
*
* @returns 0 on success
* @returns <0 otherwise
*/
int at_expect_bytes(at_dev_t *dev, const char *bytes, size_t len, uint32_t timeout);
/**
* @brief send command to device
*
* @param[in] dev device to operate on
* @param[in] command command to send
* @param[in] timeout timeout (in usec)
*
* @returns 0 on success
* @returns <0 otherwise
*/
int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout);
/**
* @brief read a line from device
*
* @param[in] dev device to operate on
* @param[in] resp_buf buffer to store line
* @param[in] len size of @p buffer
* @param[in] timeout timeout (in usec)
*
* @returns line length on success
* @returns <0 on error
*/
ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, uint32_t timeout);
/**
* @brief drain device input buffer
*
* This function drains any possible bytes waiting in the device's input
* buffer.
*
* @param[in] dev device to operate on
*/
void at_drain(at_dev_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* AT_H */