Giter Club home page Giter Club logo

cat's Introduction

Build Status

libcat (cAT)

Plain C library for parsing AT commands for use in host devices.

Features

  • blazing fast, non-blocking, robust implementation
  • 100% static implementation (without any dynamic memory allocation)
  • very small footprint (both RAM and ROM)
  • support for READ, WRITE, TEST and RUN type commands
  • commands shortcuts (auto select best command candidate)
  • single request - multiple responses
  • unsolicited read/test command support
  • hold state for delayed responses for time-consuming tasks
  • high-level memory variables mapping arguments parsing
  • variables accessors (read and write, read only, write only)
  • automatic arguments types validating
  • automatic format test responses for commands with variables
  • CRLF and LF compatible
  • case-insensitive
  • dedicated for embedded systems
  • object-oriented architecture
  • separated interface for low-level layer
  • fully asynchronous input/output operations
  • multiplatform and portable
  • asynchronous api with event callbacks
  • print registered commands list feature
  • only two source files
  • wide unit tests

Build

Build and install:

cmake .
make
make test
sudo make install

Example basic demo posibilities

AT+PRINT=?                                              # TEST command
+PRINT=<X:UINT8[RW]>,<Y:UINT8[RW]>,<MESSAGE:STRING[RW]> # Automatic response
Printing something special at (X,Y).                    # Automatic response
OK                                                      # Automatic acknowledge

AT+PRINT?                                               # READ command
+PRINT=0,0,""                                           # Automatic response
OK                                                      # Automatic acknowledge

AT+PRINT=xyz,-2                                         # WRITE command
ERROR                                                   # Automatic acknowledge

AT+PRINT=1,2,"test"                                     # WRITE command
OK                                                      # Automatic acknowledge

AT+PRINT                                                # RUN command
some printing at (1,2) with text "test"                 # Manual response
OK                                                      # Automatic acknowledge

Example unsolicited demo posibilities

AT+START=?                                              # TEST command
+START=<MODE:UINT32[WO]>                                # Automatic response
Start scanning after write (0 - wifi, 1 - bluetooth).   # Automatic response
OK                                                      # Automatic acknowledge

AT+START=0                                              # WRITE command
+SCAN=-10,"wifi1"                                       # Unsolicited read response
+SCAN=-50,"wifi2"                                       # Unsolicited read response
+SCAN=-20,"wifi3"                                       # Unsolicited read response
OK                                                      # Unsolicited acknowledge

AT+START=1                                              # WRITE command
+SCAN=-20,"bluetooth1"                                  # Unsolicited read response
OK                                                      # Unsolicited acknowledge

AT+SCAN=?                                               # TEST command
+SCAN=<RSSI:INT32[RO]>,<SSID:STRING[RO]>                # Automatic response
Scan result record.                                     # Automatic response
OK                                                      # Automatic acknowledge

Usage

Define High-Level variables:

static uint8_t x;
static uint8_t y;
static char msg[32];

static struct cat_variable go_vars[] = {
        {
                .type = CAT_VAR_UINT_DEC, /* unsigned int variable */
                .data = &x,
                .data_size = sizeof(x),
                .write = x_write,
                .name = "X",
                .access = CAT_VAR_ACCESS_READ_WRITE,
        },
        {
                .type = CAT_VAR_UINT_DEC, /* unsigned int variable */
                .data = &y,
                .data_size = sizeof(y),
                .write = y_write,
                .access = CAT_VAR_ACCESS_READ_WRITE,
        },
        {
                .type = CAT_VAR_BUF_STRING, /* string variable */
                .data = msg,
                .data_size = sizeof(msg),
                .write = msg_write,
                .access = CAT_VAR_ACCESS_READ_WRITE,
        }
};

Define AT commands descriptor:

static struct cat_command cmds[] = {
        {
                .name = "TEST",
                .read = test_read, /* read handler for ATTEST? command */
                .write = test_write, /* write handler for ATTEST={val} command */
                .run = test_run /* run handler for ATTEST command */
        },
        {
                .name = "+NUM",
                .write = num_write, /* write handler for AT+NUM={val} command */
                .read = num_read /* read handler for AT+NUM? command */
        },
        {
                .name = "+GO",
                .write = go_write, /* write handler for AT+GO={x},{y},{msg} command */
                .var = go_vars, /* attach variables to command */
                .var_num = sizeof(go_vars) / sizeof(go_vars[0]),
                .need_all_vars = true
        },
        {
                .name = "RESTART",
                .run = restart_run /* run handler for ATRESTART command */
        }
};

Define AT command parser descriptor:

static char working_buf[128]; /* working buffer, must be declared manually */

static struct cat_command_group cmd_group = {
        .cmd = cmds,
        .cmd_num = sizeof(cmds) / sizeof(cmds[0]),
};

static struct cat_command_group *cmd_desc[] = {
        &cmd_group
};

static struct cat_descriptor desc = {
        .cmd_group = cmd_desc,
        .cmd_group_num = sizeof(cmd_desc) / sizeof(cmd_desc[0]),

        .buf = working_buf,
        .buf_size = sizeof(working_buf),
};

Define IO low-level layer interface:

static int write_char(char ch)
{
        putc(ch, stdout);
        return 1;
}

static int read_char(char *ch)
{
        *ch = getch();
        return 1;
}

static struct cat_io_interface iface = {
        .read = read_char,
        .write = write_char
};

Initialize AT command parser and run:

struct cat_object at; /* at command parser object */

cat_init(&at, &desc, &iface, NULL); /* initialize at command parser object */

while (1) {
        cat_service(&at) /* periodically call at command parser service */

        ... /* other stuff, running in main loop */
}

cat's People

Contributors

barrowsys avatar junbo-zheng avatar m-wichmann avatar marcinbor85 avatar mdevaev avatar parsley72 avatar xeenych avatar xiaoxiang781216 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cat's Issues

Printing HELP with command descriptioin?

Hello,

nice work, i have a question. Is it possible to configure cAT to print the command list also with description id AT+HELP is called?

Something like:

AT+BMSERRCLR       BMS error clerar

AT#HELP            Print all available commands with a description

AT#RST             Restart the BMS

Or could you please outline the solution and I will implement it?

Nice work by the way,
Best regards,
Jiri Ctibor

Add build

Use Github actions to do a build.

Demo.c Write then Read Storing Only Some Data?

Hi,

Awesome library first off. I worked on building out the 'demo.c' example in a PIC24 environment testing out setting and reading the 'x', 'y', and 'msg' variables. I have pretty much all of it working but am seeing a weird response when I use the following AT commands:

  1. AT+GO? ------> Response: +GO=0,0,""
  2. AT+GO=1,2,"test" ------> Response: x variable updated internally to: 1
    y variable updated internally to: 2
    msg variable updated 264918 bytes internally to: <>
    <+GO>: x=1 y=2 msg=test @ speed=0
    : 00000000
  3. AT+GO? ------> Response: +GO=0,0,"test"

So, what I can't figure out is why the 'msg' variable is the only one retaining properly. No matter what I try, 'x' and 'y' just stay at 0.

The code is identical to the demo.c example. Would there be anything simple I might be missing?

Thanks!

Tests fail

      Start  1: test_parse
 1/29 Test  #1: test_parse .......................Child aborted***Exception:   0.15 sec
      Start  2: test_run
 2/29 Test  #2: test_run .........................Child aborted***Exception:   0.14 sec
      Start  3: test_read
 3/29 Test  #3: test_read ........................Child aborted***Exception:   0.16 sec
      Start  4: test_write
 4/29 Test  #4: test_write .......................Child aborted***Exception:   0.17 sec
      Start  5: test_write_parse
 5/29 Test  #5: test_write_parse .................   Passed    0.00 sec
      Start  6: test_write_int_range
 6/29 Test  #6: test_write_int_range .............   Passed    0.00 sec
      Start  7: test_write_uint_range
 7/29 Test  #7: test_write_uint_range ............   Passed    0.00 sec
      Start  8: test_write_hex_range
 8/29 Test  #8: test_write_hex_range .............   Passed    0.00 sec
      Start  9: test_write_hex_buffer
 9/29 Test  #9: test_write_hex_buffer ............   Passed    0.00 sec
      Start 10: test_write_string_buffer
10/29 Test #10: test_write_string_buffer .........   Passed    0.00 sec
      Start 11: test_read_args
11/29 Test #11: test_read_args ...................Child aborted***Exception:   0.16 sec
      Start 12: test_test
12/29 Test #12: test_test ........................   Passed    0.00 sec
      Start 13: test_test_args
13/29 Test #13: test_test_args ...................Child aborted***Exception:   0.16 sec
      Start 14: test_mutex
14/29 Test #14: test_mutex .......................   Passed    0.00 sec
      Start 15: test_unsolicited_read
15/29 Test #15: test_unsolicited_read ............Child aborted***Exception:   0.18 sec
      Start 16: test_unsolicited_read_stress
16/29 Test #16: test_unsolicited_read_stress .....Child aborted***Exception:   0.16 sec
      Start 17: test_unsolicited_read_buffer
17/29 Test #17: test_unsolicited_read_buffer .....Child aborted***Exception:   0.17 sec
      Start 18: test_hold_state
18/29 Test #18: test_hold_state ..................Child aborted***Exception:   0.17 sec
      Start 19: test_return_read
19/29 Test #19: test_return_read .................Child aborted***Exception:   0.15 sec
      Start 20: test_return_write
20/29 Test #20: test_return_write ................Child aborted***Exception:   0.17 sec
      Start 21: test_unsolicited_test
21/29 Test #21: test_unsolicited_test ............Child aborted***Exception:   0.17 sec
      Start 22: test_return_test
22/29 Test #22: test_return_test .................Child aborted***Exception:   0.17 sec
      Start 23: test_return_run
23/29 Test #23: test_return_run ..................Child aborted***Exception:   0.17 sec
      Start 24: test_test_only
24/29 Test #24: test_test_only ...................   Passed    0.00 sec
      Start 25: test_search_cmd
25/29 Test #25: test_search_cmd ..................   Passed    0.00 sec
      Start 26: test_order
26/29 Test #26: test_order .......................   Passed    0.00 sec
      Start 27: test_cmd_list
27/29 Test #27: test_cmd_list ....................   Passed    0.00 sec
      Start 28: test_var_access
28/29 Test #28: test_var_access ..................Child aborted***Exception:   0.18 sec
      Start 29: test_shortcuts
29/29 Test #29: test_shortcuts ...................Child aborted***Exception:   0.22 sec

41% tests passed, 17 tests failed out of 29

Total Test time (real) =   2.86 sec

The following tests FAILED:
          1 - test_parse (Child aborted)
          2 - test_run (Child aborted)
          3 - test_read (Child aborted)
          4 - test_write (Child aborted)
         11 - test_read_args (Child aborted)
         13 - test_test_args (Child aborted)
         15 - test_unsolicited_read (Child aborted)
         16 - test_unsolicited_read_stress (Child aborted)
         17 - test_unsolicited_read_buffer (Child aborted)
         18 - test_hold_state (Child aborted)
         19 - test_return_read (Child aborted)
         20 - test_return_write (Child aborted)
         21 - test_unsolicited_test (Child aborted)
         22 - test_return_test (Child aborted)
         23 - test_return_run (Child aborted)
         28 - test_var_access (Child aborted)
         29 - test_shortcuts (Child aborted)
Errors while running CTest

run, option to return without OK

wishlist: add an option for run to return without OK.

or does it misses something at:

    case CAT_RETURN_STATE_DATA_NEXT:
    case CAT_RETURN_STATE_NEXT:
            break;

?

Question, is there an easy way to use and update a 'double' or 'float' variable type?

Hello,

I've been digging into this library and trying to write a new function to handle 'double' or 'float' data types. Right now the best I have is writing and reading the float variable as a char buffer. I have this reading and writing out, however, this doesn't seem to allow me to modify the actual variable when mapped through 'static struct cat_variable'. I set up a new type CAT_VAR_NUM_DOUBLE to handle it.

static struct cat_variable go_vars[] = { { .type = CAT_VAR_NUM_DOUBLE, .data = &global.x, .data_size = sizeof(global.x), .write = x_write, .name = "x", .access = CAT_VAR_ACCESS_READ_WRITE }, }

I have global.x defined as the following in order to not truncate the data I send.

typedef struct { double x[16]; } GlobalControl;

My parsing of the write arguments is:

`static int parse_num_double(struct cat_object *self)
{
assert(self != NULL);
char ch;
int state = 0;
size_t size = 0;

    while (1) {
            ch = get_atcmd_buf(self)[self->position++];

            switch (state) {
                case 0:
                        if (ch == '.'){
                                return -1;
                        }
                        if ((ch == 0) || (ch == ',')) {
                                if (size > self->var->data_size){
                                        return -1;                                        
                                }
                                if (self->var->access == CAT_VAR_ACCESS_READ_ONLY) {
                                        self->write_size = 0;
                                } else {
                                        ((int8_t *)(self->var->data))[size] = 0;
                                        self->write_size = size;
                                }
                                return (ch == ',') ? 1 : 0;
                        } else {
                                ((int8_t *)(self->var->data))[size++] = ch;
                        }
                        state = 1;
                        break;
                case 1:
                        if ((ch == ',')) {
                                if (size > self->var->data_size){
                                        return -1;                                        
                                }
                                return (ch == ',') ? 1 : 0;
                        } 
                        if (size > self->var->data_size){
                                return -1;
                        }       
                        if (self->var->access == CAT_VAR_ACCESS_READ_ONLY) {
                                size++;
                        } else {
                                ((int8_t *)(self->var->data))[size++] = ch;
                        }
                        break;
                default:
                    break;
            }            
    } 
    return -1; 

}`

My question is when storing the float variable as a char buffer, is there a method I can call to update the float variable directly as a char buffer? Or if my current approach does not make sense, maybe you can point me in a new direction?

Thanks!

CAT_VAR_BUF_HEX Doesn't allow for uneven hex values.

Hello,

While using your library, i've been trying to send hex values into a buffer using CAT_VAR_BUF_HEX. Every hex value i send over has to be even, for example if i send 0x123 it will ERROR, this needs to be changed to 0x1230.

CAT_RETURN_STATE_ERROR in cat_service loop

Hi,

I'm running cAT on a host nRF52811 MCU attempting to communicate with a Quectel BC660 AT module. I've established UART communication prior to this and now I want to add cAT to easily send and handle AT commands.

This is my code, part of a larger codebase:
cAT_handler.zip

I keep getting CAT_RETURN_STATE_ERROR (-1) in the main loop and as far as I can tell, none of my write, read or run functions are called. For now, I'm just trying to send and receive responses for a simple AT+CPIN? command to check for a SIM card. Can you tell me what I'm doing wrong here?

How to model ATE?

I am having trouble modeling the ATE command.

Do I have to register ATE, ATE0 and ATE1 as seperate commands
or is it possible to register ATE<boolean selector char here>
and have the parser do a run if it sees ATE and a write if it sees ATE0 or ATE1?

I see how it'd be possible to do ATE=<value>, but it looks a bit non-Hayes to me.

using cAT Library in a multi core CPU environment

Hello,

I noticed that the cat_init() function supports an argument for a mutex.
Is the mutex intended to be used to protect in environments with multiple core CPU?

One of the project I am working on uses the cAT library for unit test and target deployment.

In the unit-test (running on a multi core system) the AT-commands are written to a circular buffer
and the cAT library writes out its responses back to a file in the filesystem that is then compared
against an expected response.

If I run this in a multi CPU core environment, some test fails with responses that does not match
the expected response. However, if I restrict the test to a single core
(e.g. using taskset -c 1 in linux), all the test will pass.

If I am missing something, what are the recommended
practices for using the library in a multi core CPU environment?

Thank you for everyone's time!

unsolicited: "AT+START=?" doesn't work

I tried the unsolicited demo but when I enter "AT+START=?" I just get "ERROR". The example shows:

AT+START=?                                              # TEST command
+START=<MODE:UINT32[WO]>                                # Automatic response
Start scanning after write (0 - wifi, 1 - bluetooth).   # Automatic response
OK                                                      # Automatic acknowledge

cAT returning ERROR when running embedded.

I have been testing cAT locally using a Linux machine and compiling with GCC and all is working fine.
This is how I have the comms interface set.
static int write_char(char ch)
{
putc(ch, stdout);
return 1;
}

static int read_char(char *ch)
{
*ch = getc(stdin);
return 1;
}

I now want to run the code embedded in a SiLabs Microcontroller via a UART and TeraTerm.
Serial output from the application is working fine. But when I send AT#HELP (or any other command) I get the "ERROR" response.

The main loop is calling handleATProcessing() every iteration.

struct cat_object at; is declared as a global.

int handleATProcessing(void)
{

    while ((cat_service(&at) != 0) && (quit_flag == 0))
    {
        sl_sleeptimer_delay_millisecond(1);
    };


    printf("Bye!\n");

    return 0;

}

My gut feeling is that a command is not being built as characters are received, so command buffer is empty when CR is received.
Can you see where I am going wrong or advise how to debug this?

Question, Are there any video to use this library?

Hello,

I looked at the examples for usage for this library. In readme, It says this library for host devices. I have some questions.

1-For example, I have a device and i want to handle the responses. Can I use this library for handling responses?

Could you provide an example video?

Timeout?

Does it make sense to add a timeout capabilities ?

Or is possible to implement some flush/reset of the internal state?

Support of URC events

Hi @marcinbor85,
Thanks for your very useful repository. I was digging into your library to find out if it can support URC events that are not AT-based commands (such as RING in GSM modems). As far as I understood it just can interpret unsolicited AT commands. Is that true? and which parts should I change to add support for these events (if it is possible at all)?
Thank you

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.