Build your own Utils Library in embedded Projects – enum to string

Time:2021-11-26

preface

I wonder if you often need to print the names of some type variables when debugging. A unified method is provided here. When used, the variable names of macro definitions and enumerations can be printed out with only a small amount of code.
Let’s start with the code.

realization

utils.h

typedef struct {
    int32_t val;
    char* name;
} Enumerate;

char* enumerate_to_string(Enumerate* enumerate, uint8_t total, int32_t val);

#define ENUMERATE_DEF(object) static Enumerate _ENUM_##object[]
#define ENUMERATE_ITEM(val) {val, #val}

#define ENUM_TO_STRING(object, val) enumerate_to_string(_ENUM_##object, ARRAY_LENGTH(_ENUM_##object), val)

#define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0]))

Because the code needs to traverse the array, it can no longer be fully implemented with a simple macro definition.
utils.c

char* enumerate_to_string(Enumerate* enumerate, uint8_t total, int32_t val)
{
    for(uint8_t i=0; i<total; i++) {
        if(enumerate[i].val == val) {
            return enumerate[i].name;
        }
    }
    return "EnumUnknow";
}

Old rules, example:

enum {
    UNDEFINED = 0,
    OBJECT,
    ARRAY,
    STRING,
    NUMBER,
    FLOAT,
    BOOL,
};

ENUMERATE_DEF(JsonTypeStr) = {
    ENUMERATE_ITEM(UNDEFINED),
    ENUMERATE_ITEM(OBJECT),
    ENUMERATE_ITEM(ARRAY),
    ENUMERATE_ITEM(STRING),
    ENUMERATE_ITEM(NUMBER),
    ENUMERATE_ITEM(FLOAT),
    ENUMERATE_ITEM(BOOL),
};

uint8_t json_type = ARRAY;
printf("json_type:%s(%d)\n", ENUM_TO_STRING(JsonTypeStr, json_type), json_type);

result:

json_type:ARRAY(2)

code analysis

The idea here is to redefine a structure array and save the enumeration value and value name in this structure. When in use, traverse the entire array to find the name of the corresponding value.
At the same time, in order to reduce the number of handwritten codes, the text name of the value is directly converted into a string by using the characteristics of macro definition, so there is no need to write the name string again manually.
The syntax of enumerations defined in the original C language is retained in usexxx={a1,a2,a3};The way to make code reading more in line with habits.
In terms of calculating array length, C language is no better than other languages, Javaarray.length(), Pythonlen(list), you can get the array length quickly and accurately. In C language, it needs to be calculated as follows:

size_t array_len = sizeof(array) / sizeof(item);

Divide the memory size occupied by the array by the memory size occupied by each item to get the total number of items, which is the length of the array. On the premise that all the parameters passed are arrays, the code can be simplified by using the first element:

size_t array_len = sizeof(array) / sizeof(array[0]);

There are some hidden dangers in this usage. The incoming parameters are not checked, so it is also the reason why it is not widely popularized.
At the same time, note that sizeof is an operator, which is the same as =, + and is not a function. If you want to calculate the size of an array, you need toPass in the correct name, it is not possible to pass in a pointer to an array.

Tips:

In the declaration, a specific prefix is added after the macro definition is expanded, which can effectively avoid naming repetition with other variables, make this variable closer to the business and easier to enter.

application

Take the reason why Bluetooth is disconnected as an example. It runs on the esp32 platform.
The reason for Bluetooth disconnection is IDF \ components \ BT \ host \ bluedroid \ API \ include \ API \ esp_ gatt_ Defs. H defines:

/**
 * @brief Gatt Connection reason enum
 */
typedef enum {
    ESP_GATT_CONN_UNKNOWN = 0,                      /*!< Gatt connection unknown */               /* relate to BTA_GATT_CONN_UNKNOWN in bta/bta_gatt_api.h */
    ESP_GATT_CONN_L2C_FAILURE = 1,                  /*!< General L2cap failure  */                /* relate to BTA_GATT_CONN_L2C_FAILURE in bta/bta_gatt_api.h */
    ESP_GATT_CONN_TIMEOUT = 0x08,                   /*!< Connection timeout  */                   /* relate to BTA_GATT_CONN_TIMEOUT in bta/bta_gatt_api.h */
    ESP_GATT_CONN_TERMINATE_PEER_USER = 0x13,       /*!< Connection terminate by peer user  */    /* relate to BTA_GATT_CONN_TERMINATE_PEER_USER in bta/bta_gatt_api.h */
    ESP_GATT_CONN_TERMINATE_LOCAL_HOST = 0x16,      /*!< Connection terminated by local host */    /* relate to BTA_GATT_CONN_TERMINATE_LOCAL_HOST in bta/bta_gatt_api.h */
    ESP_GATT_CONN_FAIL_ESTABLISH = 0x3e,            /*!< Connection fail to establish  */         /* relate to BTA_GATT_CONN_FAIL_ESTABLISH in bta/bta_gatt_api.h */
    ESP_GATT_CONN_LMP_TIMEOUT = 0x22,               /*!< Connection fail for LMP response tout */ /* relate to BTA_GATT_CONN_LMP_TIMEOUT in bta/bta_gatt_api.h */
    ESP_GATT_CONN_CONN_CANCEL = 0x0100,             /*!< L2CAP connection cancelled  */           /* relate to BTA_GATT_CONN_CONN_CANCEL in bta/bta_gatt_api.h */
    ESP_GATT_CONN_NONE = 0x0101                     /*!< No connection to cancel  */              /* relate to BTA_GATT_CONN_NONE in bta/bta_gatt_api.h */
} esp_gatt_conn_reason_t;

Let’s add a string for it: (copy the above definition as it is, and then make a little replacement and deletion)

ENUMERATE_DEF(BleConnReasonStr) = {
    ENUMERATE_ITEM(ESP_GATT_CONN_UNKNOWN),
    ENUMERATE_ITEM(ESP_GATT_CONN_L2C_FAILURE),
    ENUMERATE_ITEM(ESP_GATT_CONN_TIMEOUT),
    ENUMERATE_ITEM(ESP_GATT_CONN_TERMINATE_PEER_USER),
    ENUMERATE_ITEM(ESP_GATT_CONN_TERMINATE_LOCAL_HOST),
    ENUMERATE_ITEM(ESP_GATT_CONN_FAIL_ESTABLISH),
    ENUMERATE_ITEM(ESP_GATT_CONN_LMP_TIMEOUT),
    ENUMERATE_ITEM(ESP_GATT_CONN_CONN_CANCEL),
    ENUMERATE_ITEM(ESP_GATT_CONN_NONE),
};

Application in Engineering:

//Bluetooth event callback function
static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
{
    esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;

    switch (event) {
    case ESP_ GATTC_ DISCONNECT_ EVT: // disconnection event
        //Print event reason
        ESP_LOGI(TAG, "ESP_GATTC_DISCONNECT_EVT, reason = %s(%x)", ENUM_TO_STRING(BleConnReasonStr, p_data->disconnect.reason), p_data->disconnect.reason);
        break;
    default:
        break;
    }
}

The actual print is as follows:

I (1126378) BLE: ESP_GATTC_DISCONNECT_EVT, reason = ESP_GATT_CONN_TIMEOUT(8)

Is it convenient? You can see the log at a glance. You don’t have to compare and define the error code. When in use, there is no need to write additional functions to convert numbers to strings, which can be added in many places.

End

Do you still like this article? I’ll see you next time.

Recommended Today

Vue、Three. JS implementation panorama

1、 First, we need to create a Vue project This paper mainly records the process of building panorama in detail, so building Vue project is not described too much. 2、 Install three js npm install three –save npm install three-trackballcontrols –save npm install three-orbit-controls –save npm i three-obj-mtl-loader –save npm i three-fbx-loader –save npm i […]