diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep
index fd399515e629e7bb92905734624d1d2417a6936a..1621e5ec5dd804a3802a10d50de89efc80782364 100644
--- a/drivers/Makefile.dep
+++ b/drivers/Makefile.dep
@@ -22,8 +22,10 @@ ifneq (,$(filter apa102,$(USEMODULE)))
 endif
 
 ifneq (,$(filter at,$(USEMODULE)))
-    USEMODULE += fmt
-    USEMODULE += xtimer
+  FEATURES_REQUIRED += periph_uart
+  USEMODULE += fmt
+  USEMODULE += xtimer
+  USEMODULE += isrpipe
 endif
 
 ifneq (,$(filter at30tse75x,$(USEMODULE)))
diff --git a/drivers/at/at.c b/drivers/at/at.c
index aff5757dca3c3eaacb1997b4e58bd56a536d1921..ec37d8e8bc407ee3cbc7b53563ac89102b226610 100644
--- a/drivers/at/at.c
+++ b/drivers/at/at.c
@@ -32,19 +32,18 @@ int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t
     return 0;
 }
 
-int at_expect_bytes(at_dev_t *dev, const char *bytes, size_t len, uint32_t timeout)
+int at_expect_bytes(at_dev_t *dev, const char *bytes, uint32_t timeout)
 {
-    while (len) {
+    while (*bytes) {
         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++)) {
+            if (c != *bytes++) {
                 return -1;
             }
-            len--;
         }
         else {
             return res;
@@ -61,17 +60,19 @@ void at_send_bytes(at_dev_t *dev, const char *bytes, size_t len)
 
 int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout)
 {
-    unsigned cmdlen = strlen(command);
+    size_t cmdlen = strlen(command);
 
     uart_write(dev->uart, (const uint8_t *)command, cmdlen);
-    uart_write(dev->uart, (const uint8_t *)AT_END_OF_LINE, sizeof(AT_END_OF_LINE) - 1);
+    uart_write(dev->uart, (const uint8_t *)AT_SEND_EOL, AT_SEND_EOL_LEN);
 
-    if (at_expect_bytes(dev, command, cmdlen, timeout)) {
-        return -1;
-    }
+    if (AT_SEND_ECHO) {
+        if (at_expect_bytes(dev, command, timeout)) {
+            return -1;
+        }
 
-    if (at_expect_bytes(dev, AT_END_OF_LINE "\r\n", sizeof(AT_END_OF_LINE) + 1, timeout)) {
-        return -2;
+        if (at_expect_bytes(dev, AT_SEND_EOL "\r\n", timeout)) {
+            return -2;
+        }
     }
 
     return 0;
@@ -83,13 +84,15 @@ void at_drain(at_dev_t *dev)
     int res;
 
     do {
+        /* consider no character within 10ms "drained" */
         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 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;
+    ssize_t res;
 
     at_drain(dev);
 
@@ -108,9 +111,10 @@ 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 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;
+    ssize_t res;
     size_t bytes_left = len - 1;
     char *pos = resp_buf;
 
@@ -123,7 +127,7 @@ ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, char *resp_buf
         goto out;
     }
 
-    while(1) {
+    while (1) {
         res = at_readline(dev, pos, bytes_left, timeout);
         if (res == 0) {
             continue;
@@ -140,6 +144,9 @@ ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, char *resp_buf
             else if (strncmp(pos, "+CME ERROR:", 11) == 0) {
                 return -1;
             }
+            else if (strncmp(pos, "+CMS ERROR:", 11) == 0) {
+                return -1;
+            }
             else {
                 pos += res;
                 if (bytes_left) {
@@ -167,17 +174,17 @@ int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout
     at_drain(dev);
 
     uart_write(dev->uart, (const uint8_t *)command, cmdlen);
-    uart_write(dev->uart, (const uint8_t *)AT_END_OF_LINE, sizeof(AT_END_OF_LINE) - 1);
+    uart_write(dev->uart, (const uint8_t *)AT_SEND_EOL, AT_SEND_EOL_LEN);
 
-    if (at_expect_bytes(dev, command, cmdlen, timeout)) {
+    if (at_expect_bytes(dev, command, timeout)) {
         return -1;
     }
 
-    if (at_expect_bytes(dev, AT_END_OF_LINE "\n", sizeof(AT_END_OF_LINE), timeout)) {
+    if (at_expect_bytes(dev, AT_SEND_EOL "\n", timeout)) {
         return -2;
     }
 
-    if (at_expect_bytes(dev, ">", 1, timeout)) {
+    if (at_expect_bytes(dev, ">", timeout)) {
         return -3;
     }
 
@@ -187,7 +194,7 @@ int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout
 int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout)
 {
     int res;
-    char resp_buf[32];
+    char resp_buf[64];
 
     res = at_send_cmd_get_resp(dev, command, resp_buf, sizeof(resp_buf), timeout);
     if (res > 0) {
@@ -234,5 +241,8 @@ ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, uint32_t timeout)
     }
 
 out:
+    if (res < 0) {
+        *resp_buf = '\0';
+    }
     return res;
 }
diff --git a/drivers/include/at.h b/drivers/include/at.h
index 37608bee9d428db62b8bd0c8969dd08c46b00a67..53e2991ab381b4eb143f61cc7a25406b860c47dd 100644
--- a/drivers/include/at.h
+++ b/drivers/include/at.h
@@ -16,7 +16,7 @@
  * 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
+ * Furthermore, the library tries to cope with difficulties regarding different
  * line endings. It usually sends "<command><CR>", but expects
  * "<command>\LF\CR" as echo.
  *
@@ -34,6 +34,7 @@
 #define AT_H
 
 #include <stdint.h>
+#include <unistd.h>
 
 #include "isrpipe.h"
 #include "periph/uart.h"
@@ -42,11 +43,19 @@
 extern "C" {
 #endif
 
-#ifndef AT_END_OF_LINE
+#ifndef AT_SEND_EOL
 /** End of line character to send after the AT command */
-#define AT_END_OF_LINE "\r"
+#define AT_SEND_EOL "\r"
 #endif
 
+#ifndef AT_SEND_ECHO
+/** Enable/disable the expected echo after an AT command is sent */
+#define AT_SEND_ECHO 1
+#endif
+
+/** Shortcut for getting send end of line length */
+#define AT_SEND_EOL_LEN  (sizeof(AT_SEND_EOL) - 1)
+
 /**
  * @brief AT device structure
  */
@@ -55,7 +64,6 @@ typedef struct {
     uart_t uart;            /**< UART device where the AT device is attached */
 } at_dev_t;
 
-
 /**
  * @brief   Initialize AT device struct
  *
@@ -71,7 +79,7 @@ typedef struct {
 int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t bufsize);
 
 /**
- * @brief   simple command helper
+ * @brief   Simple command helper
  *
  * This function sends an AT command to the device and waits for "OK".
  *
@@ -85,10 +93,10 @@ int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t
 int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout);
 
 /**
- * @brief   send AT command, wait for a prompt
+ * @brief   Send AT command, wait for a prompt
  *
- * This function will send the supplied @p command, then wait for the prompt (>)
- * character and return
+ * This function sends the supplied @p command, then waits for the prompt (>)
+ * character and returns
  *
  * @param[in]   dev     device to operate on
  * @param[in]   command command string to send
@@ -100,9 +108,9 @@ int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout);
 int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout);
 
 /**
- * @brief   send AT command, wait for response
+ * @brief   Send AT command, wait for response
  *
- * This function will send the supplied @p command, then wait and return one
+ * This function sends the supplied @p command, then waits and returns one
  * line of response.
  *
  * A possible empty line will be skipped.
@@ -113,15 +121,15 @@ int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout
  * @param[in]   len         len of @p buffer
  * @param[in]   timeout     timeout (in usec)
  *
- * @returns     lenght of response on success
+ * @returns     length 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
+ * @brief   Send AT command, wait for multiline response
  *
- * This function will send the supplied @p command, then return all response
+ * This function sends the supplied @p command, then returns all response
  * lines until the device sends "OK".
  *
  * If a line starts with "ERROR" or "+CME ERROR:", or the buffer is full, the
@@ -133,7 +141,7 @@ ssize_t at_send_cmd_get_resp(at_dev_t *dev, const char *command, char *resp_buf,
  * @param[in]   len         len of @p buffer
  * @param[in]   timeout     timeout (in usec)
  *
- * @returns     lenght of response on success
+ * @returns     length 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);
@@ -142,17 +150,16 @@ ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, char *resp_buf
  * @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]   bytes   buffer containing bytes to expect (NULL-terminated)
  * @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);
+int at_expect_bytes(at_dev_t *dev, const char *bytes, uint32_t timeout);
 
 /**
- * @brief  Send raw bytes to a device
+ * @brief   Send raw bytes to a device
  *
  * @param[in]   dev     device to operate on
  * @param[in]   bytes   buffer containing bytes to send
@@ -161,7 +168,7 @@ int at_expect_bytes(at_dev_t *dev, const char *bytes, size_t len, uint32_t timeo
 void at_send_bytes(at_dev_t *dev, const char *bytes, size_t len);
 
 /**
- * @brief   send command to device
+ * @brief   Send command to device
  *
  * @param[in]   dev     device to operate on
  * @param[in]   command command to send
@@ -173,7 +180,7 @@ void at_send_bytes(at_dev_t *dev, const char *bytes, size_t len);
 int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout);
 
 /**
- * @brief   read a line from device
+ * @brief   Read a line from device
  *
  * @param[in]   dev         device to operate on
  * @param[in]   resp_buf    buffer to store line
@@ -186,7 +193,7 @@ int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout);
 ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, uint32_t timeout);
 
 /**
- * @brief   drain device input buffer
+ * @brief   Drain device input buffer
  *
  * This function drains any possible bytes waiting in the device's input
  * buffer.
@@ -200,3 +207,4 @@ void at_drain(at_dev_t *dev);
 #endif
 
 #endif /* AT_H */
+/** @} */