Fundamentals 8 min read

How to Build an AT Command Parsing Module for Modem, Wi‑Fi, and Bluetooth

This guide explains the design, architecture, and step‑by‑step usage of an AT command parsing library for bare‑metal and OS‑based systems, covering job queue management, core APIs, example code, and integration with modem, Wi‑Fi, and Bluetooth modules.

Liangxu Linux
Liangxu Linux
Liangxu Linux
How to Build an AT Command Parsing Module for Modem, Wi‑Fi, and Bluetooth

Overview

The AT command communication module provides two implementations: a bare‑metal version ( at_chat) and an OS‑based version ( at). Both are suitable for modem, Wi‑Fi, and Bluetooth modules.

Software Architecture

at_chat.c, at_chat.h, list.h – no‑OS implementation. Uses chained queues and asynchronous callbacks to transmit and receive AT commands, supports URC handling and custom command jobs.

at.c, at.h, at_util.h, comdef.h – OS implementation. Requires porting of OS primitives (semaphores, task delays, etc.) defined in at_util.h.

at_chat (no‑OS) API

Job Item Structure

typedef struct {
    unsigned int state : 3;
    unsigned int type  : 3;   /* job type */
    unsigned int abort : 1;
    void *param;              /* generic parameter */
    void *info;               /* generic info pointer */
    struct list_head node;   /* list node */
} at_item_t;

Ten job items are statically allocated; no dynamic memory allocation is required.

Core Functions

at_send_singlline(at_obj_t *obj, at_response_cb cb, const char *cmd)

– send a single‑line command, wait for “OK”, timeout 3000 ms.

at_send_multiline(at_obj_t *obj, at_response_cb cb, const char *cmd)

– send a multi‑line command, same timeout.

at_do_cmd(at_obj_t *obj, at_respond_t *resp, const char *fmt, ...)

– custom format and response matching. at_do_work(at_obj_t *obj, at_work work, void *ctx) – custom send/receive parsing for composite commands.

Typical Initialization

Define an at_obj_t controller and an at_adapter_t that implements write and read for the UART (or other transport).

static at_obj_t at;

const at_adapter_t adap = {
    .write = uart_write,
    .read  = uart_read,
    /* other fields as needed */
};

Initialize the controller and start a periodic poll (recommended interval ≤20 ms).

void wifi_init(void) {
    at_obj_init(&at, &adap);
    /* additional hardware init */
}
driver_init("wifi", wifi_init);

void wifi_task(void) {
    at_poll_task(&at);
}
task_register("wifi", wifi_task, 10);   /* 10 ms interval */

Example: Single‑Line Command

// Send AT+GPIO_TEST_EN=1 and handle result
static void gpio_cb(at_response_t *r) {
    if (r->ret == AT_RET_OK) {
        printf("Execute successfully
");
    } else {
        printf("Execute failure
");
    }
}
at_send_singlline(&at, gpio_cb, "AT+GPIO_TEST_EN=1");

at (OS) API

Key Functions

at_do_cmd

– execute an AT command; can be wrapped for common single‑line or multi‑line usage. at_split_respond_lines – split a raw response into separate lines. at_do_work – state‑machine helper for commands that require intermediate prompts such as “CONNECT”.

Initialization for an RTOS

Define controller, URC buffer, and URC table.

static at_obj_t at;
static char urc_buf[128];

utc_item_t utc_tbl[] = {
    { "+CSQ: ", csq_updated_handler },
    /* add more URC patterns as needed */
};

const at_adapter_t adap = {
    .urc_buf        = urc_buf,
    .urc_bufsize    = sizeof(urc_buf),
    .utc_tbl        = utc_tbl,
    .urc_tbl_count  = sizeof(utc_tbl) / sizeof(utc_item_t),
    .debug          = at_debug,
    .write          = uart_write,
    .read           = uart_read,
};

Create the controller and run a dedicated polling thread.

void at_thread(void) {
    at_obj_create(&at, &adap);
    while (1) {
        at_process(&at);
    }
}

Example 1 – Query Signal Quality

bool read_csq_value(at_obj_t *at, int *rssi, int *error_rate) {
    unsigned char recvbuf[32];
    at_respond_t resp = { "OK", recvbuf, sizeof(recvbuf), 3000 };
    if (at_do_cmd(at, &resp, "AT+CSQ") != AT_RET_OK)
        return false;
    return (sscanf((char *)recvbuf, "%*[^+]+CSQ: %d,%d", rssi, error_rate) == 2);
}

Example 2 – Send TCP/UDP Data (HL8518 socket)

static bool socket_send_handler(at_work_ctx_t *e) {
    struct socket_info *i = (struct socket_info *)e->params;
    struct ril_sock *s = i->s;

    if (s->type == SOCK_TYPE_TCP)
        e->printf(e, "AT+KTCPSND=%d,%d", s->session, i->bufsize);
    else
        e->printf(e, "AT+KUDPSND=%d,%s,%d,%d",
                  s->session, s->host, s->port, i->bufsize);

    if (e->wait_resp(e, "CONNECT", 5000) != AT_RET_OK)
        goto error;

    e->write(i->buf, i->bufsize);
    e->write("--EOF--Pattern--", strlen("--EOF--Pattern--"));

    return (e->wait_resp(e, "OK", 5000) == AT_RET_OK);
error:
    e->write("--EOF--Pattern--", strlen("--EOF--Pattern--"));
    return false;
}

static bool hl8518_sock_send(ril_obj_t *r, struct ril_sock *s,
                             const void *buf, unsigned int len) {
    struct socket_info info = { s, (unsigned char *)buf, len, 0 };
    if (len == 0) return false;
    return at_do_work(&r->at, (at_work)socket_send_handler, &info);
}

Repository

Source code: https://toscode.gitee.com/smtian/AT-Command

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

WiFiBluetoothC programmingUARTAT commandscommunication moduleembedded firmware
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.