提交工程,小程序源码已完成,单片机待完成

This commit is contained in:
2026-02-10 20:21:41 +08:00
commit fd55a14b62
3778 changed files with 1062672 additions and 0 deletions

View File

@@ -0,0 +1,322 @@
# DX-WF-24 WiFi 模块驱动使用说明
## 目录
1. [概述](#概述)
2. [文件结构](#文件结构)
3. [API 参考](#api-参考)
4. [使用示例](#使用示例)
5. [注意事项](#注意事项)
---
## 概述
本驱动用于 STM32F103 与 DX-WF-24 WiFi 模块的串口通信,基于 HAL 库实现,支持:
- **DMA 发送** - 非阻塞式数据发送
- **DMA + 空闲中断接收** - 自动帧识别接收
- **同步应答检测** - AT 指令交互
---
## 文件结构
```
BSP_WF_24/
├── dx_wf_24.h # 头文件(配置和声明)
└── dx_wf_24.c # 实现文件
```
---
## API 参考
### 1. 初始化函数
#### `WIFI_RECV_DMA_Init()`
```c
HAL_StatusTypeDef WIFI_RECV_DMA_Init(void);
```
- **功能**:初始化 WiFi DMA 接收(空闲中断方式)
- **调用时机**main() 中,在 MX_USART1_UART_Init() 之后调用
- **返回值**HAL_OK 表示成功
---
### 2. 数据发送
#### `WIFI_SEND_DMA()`
```c
HAL_StatusTypeDef WIFI_SEND_DMA(const char *data);
```
- **功能**:通过 DMA 发送字符串(非阻塞)
- **参数**`data` - 要发送的字符串
- **返回值**
- `HAL_OK` - 发送成功启动
- `HAL_BUSY` - 上一次发送未完成
- `HAL_ERROR` - 参数错误或发送失败
---
### 3. 数据接收
#### `WIFI_Get_Received_Data()`
```c
int WIFI_Get_Received_Data(uint8_t *buffer, uint16_t len);
```
- **功能**:获取接收到的数据
- **参数**
- `buffer` - 输出缓冲区
- `len` - 缓冲区大小
- **返回值**实际复制的数据长度0 表示无数据
---
### 4. 应答检测(新增)
#### `WIFI_CheckAck()`
```c
uint8_t WIFI_CheckAck(const char *cmd, const char *expect, uint32_t timeout_ms);
```
- **功能**:发送 AT 指令并检测应答
- **参数**
- `cmd` - 要发送的 AT 指令(如 "AT\r\n"
- `expect` - 期望的应答字符串(如 "OK"
- `timeout_ms` - 超时时间(毫秒)
- **返回值**
- `1` - 收到期望应答
- `0` - 超时或收到 ERROR
### 5. MQTT连接支持认证
#### `WIFI_Connect_MQTT()`
```c
uint8_t WIFI_Connect_MQTT(const char *wifi_ssid, const char *wifi_pass,
const char *mqtt_broker, uint16_t mqtt_port,
const char *client_id, const char *mqtt_user,
const char *mqtt_pass, const char *sub_topic);
```
- **功能**完整MQTT连接流程WiFi+MQTT
- **参数**
- `wifi_ssid` - WiFi名称
- `wifi_pass` - WiFi密码
- `mqtt_broker` - MQTT服务器地址
- `mqtt_port` - MQTT端口通常为1883
- `client_id` - MQTT客户端ID需唯一
- `mqtt_user` - MQTT用户名传NULL表示无需认证
- `mqtt_pass` - MQTT密码传NULL表示无需认证
- `sub_topic` - 订阅的主题
- **返回值**
- `1` - 连接成功
- `0` - 连接失败
- **连接步骤**
1. 清理MQTT环境
2. AT指令测试
3. 设置STA模式
4. 连接WiFi带重试
5. 配置MQTT参数包括认证信息
6. 连接MQTT服务器带重试
7. 订阅主题
8. 发布上线消息
---
## 使用示例
### 示例1基础初始化
```c
#include "dx_wf_24.h"
void System_Init(void)
{
// HAL 初始化...
MX_USART1_UART_Init();
// 初始化 WiFi DMA 接收
if (WIFI_RECV_DMA_Init() != HAL_OK) {
elog_e("MAIN", "WiFi init failed!");
}
}
```
### 示例2发送数据
```c
// 发送 AT 指令测试
void WiFi_Test(void)
{
HAL_StatusTypeDef status = WIFI_SEND_DMA("AT\r\n");
if (status == HAL_OK) {
elog_i("WIFI", "Command sent");
} else if (status == HAL_BUSY) {
elog_w("WIFI", "WiFi busy, try later");
}
}
```
### 示例3检测模块就绪
```c
// 初始化时检测 WiFi 模块
uint8_t WiFi_Init_Check(void)
{
// 发送 AT期望收到 OK超时 1 秒
if (WIFI_CheckAck("AT\r\n", "OK", 1000)) {
elog_i("WIFI", "Module ready");
return 1;
} else {
elog_e("WIFI", "Module not responding");
return 0;
}
}
```
### 示例4完整的 MQTT 连接(支持认证)
```c
// 连接到 MQTT 服务器(无需认证)
void Connect_MQTT_Public(void)
{
uint8_t success = WIFI_Connect_MQTT(
"MyWiFi", // WiFi名称
"12345678", // WiFi密码
"broker.emqx.io", // MQTT服务器公共测试服务器
1883, // MQTT端口
"PetFeeder-001", // 客户端ID需唯一
NULL, // MQTT用户名NULL表示无需认证
NULL, // MQTT密码NULL表示无需认证
"pet/control" // 订阅主题
);
if (success) {
elog_i("MQTT", "Connected to public MQTT server");
} else {
elog_e("MQTT", "Connection failed");
}
}
// 连接到 MQTT 服务器(需要用户名密码认证)
void Connect_MQTT_Private(void)
{
uint8_t success = WIFI_Connect_MQTT(
"MyWiFi", // WiFi名称
"12345678", // WiFi密码
"mqtt.myserver.com", // 私有MQTT服务器
1883, // MQTT端口
"PetFeeder-001", // 客户端ID
"myusername", // MQTT用户名
"mypassword", // MQTT密码
"pet/control" // 订阅主题
);
if (success) {
elog_i("MQTT", "Connected to private MQTT server");
} else {
elog_e("MQTT", "Connection failed");
}
}
### 示例5异步接收处理
```c
// 在任务循环中处理接收数据
void WiFi_Task(void)
{
uint8_t buffer[512];
int len;
while (1) {
// 检查是否有新数据
len = WIFI_Get_Received_Data(buffer, sizeof(buffer));
if (len > 0) {
// 处理接收到的数据
elog_i("WIFI", "Received: %s", buffer);
// 根据内容做不同处理...
if (strstr((char*)buffer, "+IPD")) {
// 收到网络数据
Process_Network_Data(buffer);
}
}
osDelay(10); // 10ms 轮询
}
}
```
---
## 注意事项
### 1. 中断配置
确保 `stm32f1xx_it.c` 中已添加空闲中断处理:
```c
void USART1_IRQHandler(void)
{
// 检查空闲中断
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) {
WIFI_UART_IDLE_Callback(&huart1);
}
HAL_UART_IRQHandler(&huart1);
}
```
### 2. DMA 配置
`main.c``MX_USART1_UART_Init()` 中确保启用了 DMA
- USART1_TX: DMA 模式
- USART1_RX: DMA 循环模式
### 3. 缓冲区大小
默认接收缓冲区为 512 字节,可在 `dx_wf_24.h` 中修改:
```c
#define WIFI_RX_BUF_SIZE 512
```
### 4. MQTT 用户名密码认证
- 公共MQTT服务器如 broker.emqx.io通常不需要认证`NULL` 即可
- 私有MQTT服务器需要用户名和密码传入对应字符串
- 确保 MQTT 服务器已开启用户名密码认证功能
### 5. 超时处理
`WIFI_CheckAck()` 是阻塞函数,不适合在中断或高优先级任务中调用。
### 6. 多线程安全
当前实现未加互斥锁,如果在多任务环境中同时调用发送/接收,需要额外保护。
---
## 常见问题
| 问题 | 可能原因 | 解决方法 |
|------|----------|----------|
| 收不到数据 | 空闲中断未启用 | 检查 `WIFI_RECV_DMA_Init()` 返回值 |
| 数据截断 | 缓冲区太小 | 增大 `WIFI_RX_BUF_SIZE` |
| 发送失败 | DMA 忙 | 检查返回值,稍后重试 |
| 检测超时 | 波特率不匹配 | 确认模块波特率(默认 115200|
---
## 版本历史
| 版本 | 日期 | 说明 |
|------|------|------|
| 1.0 | 2026-02-09 | 初始版本,基础 DMA 收发 |
| 1.1 | 2026-02-09 | 新增 `WIFI_CheckAck()` 同步应答检测 |
| 1.2 | 2026-02-09 | 新增 `WIFI_Connect_MQTT()` 支持用户名密码认证 |
---
## 联系方式
如有问题,请参考 DX-WF-24 模块 AT 指令手册。

View File

@@ -0,0 +1,370 @@
#include "dx_wf_24.h"
#include "elog.h"
#include <string.h>
#include "cmsis_os.h"
#include "stdio.h"
/* 发送缓冲区 */
#define WIFI_TX_BUF_SIZE 512
static uint8_t wifi_tx_buffer[WIFI_TX_BUF_SIZE];
static volatile uint8_t wifi_tx_busy = 0;
/* WiFi 全局结构体 */
WIFI_HandleTypeDef wifi = {0};
/* 日志标签 */
#define TAG "WIFI"
/**
* @brief 初始化 WiFi DMA 接收(空闲中断方式)
* @retval HAL_StatusTypeDef
* @note 在 main 中调用此函数初始化
*/
HAL_StatusTypeDef WIFI_RECV_DMA_Init(void) {
/* 清空缓冲区 */
memset(&wifi, 0, sizeof(wifi));
/* 停止可能正在进行的 DMA */
HAL_UART_DMAStop(&huart1);
/* 启动 DMA 接收(环形缓冲区模式) */
HAL_StatusTypeDef status = HAL_UART_Receive_DMA(&huart1, wifi.rx_buffer, WIFI_RX_BUF_SIZE);
if (status == HAL_OK) {
/* 启用空闲中断 - 关键! */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
elog_i(TAG, "WiFi DMA RX init OK");
} else {
elog_e(TAG, "WiFi DMA RX init failed: %d", status);
}
return status;
}
/**
* @brief 串口空闲中断回调(在 USART1_IRQHandler 中调用)
* @param huart 串口句柄
* @note 当串口空闲时触发,表示一帧数据接收完成
*/
void WIFI_UART_IDLE_Callback(UART_HandleTypeDef *huart) {
if (huart->Instance != USART1) {
return;
}
/* 清除 IDLE 标志(必须先读 SR 再读 DR */
__HAL_UART_CLEAR_IDLEFLAG(huart);
/* 停止 DMA 传输,获取已接收数据长度 */
HAL_UART_DMAStop(huart);
/* 计算本次接收的数据长度 */
uint16_t recv_len = WIFI_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx);
if (recv_len > 0 && recv_len < WIFI_RX_BUF_SIZE) {
wifi.rx_len = recv_len;
wifi.rx_buffer[recv_len] = '\0'; // 添加字符串结束符
wifi.rx_flag = 1; // 标记接收完成
elog_d(TAG, "RX[%d]: %s", recv_len, wifi.rx_buffer);
}
/* 重新启动 DMA 接收 */
HAL_UART_Receive_DMA(&huart1, wifi.rx_buffer, WIFI_RX_BUF_SIZE);
}
/**
* @brief 获取接收到的数据
* @param buffer 输出缓冲区
* @param len 缓冲区大小
* @retval int 实际复制长度0表示无数据
*/
int WIFI_Get_Received_Data(uint8_t *buffer, uint16_t len) {
if (!wifi.rx_flag || buffer == NULL || len == 0) {
return 0;
}
uint16_t copy_len = (wifi.rx_len < len) ? wifi.rx_len : (len - 1);
memcpy(buffer, wifi.rx_buffer, copy_len);
buffer[copy_len] = '\0';
wifi.rx_flag = 0; // 清除标志
return copy_len;
}
/**
* @brief 通过 DMA 发送 WiFi 数据(非阻塞)
* @param data 要发送的字符串
* @retval HAL_StatusTypeDef 发送状态
*/
HAL_StatusTypeDef WIFI_SEND_DMA(const char *data) {
if (data == NULL) {
return HAL_ERROR;
}
size_t len = strlen(data);
if (len == 0) {
return HAL_OK;
}
if (len >= WIFI_TX_BUF_SIZE) {
elog_w(TAG, "TX data too long: %d", len);
return HAL_ERROR;
}
if (wifi_tx_busy) {
return HAL_BUSY;
}
memcpy(wifi_tx_buffer, data, len);
wifi_tx_busy = 1;
HAL_StatusTypeDef status = HAL_UART_Transmit_DMA(&huart1, wifi_tx_buffer, len);
if (status != HAL_OK) {
wifi_tx_busy = 0;
elog_e(TAG, "TX DMA failed: %d", status);
}
return status;
}
/* DMA 发送完成回调 */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
wifi_tx_busy = 0;
elog_d(TAG, "TX complete");
}
}
/* DMA 接收完成回调(半传输/全传输) */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
/* 缓冲区满,标记接收完成 */
wifi.rx_len = WIFI_RX_BUF_SIZE;
wifi.rx_flag = 1;
elog_w(TAG, "RX buffer full!");
}
}
/**
* @brief 检测 WiFi 应答(同步阻塞式)
* @param cmd 要发送的 AT 指令
* @param expect 期望的应答字符串(如 "OK", "READY"
* @param timeout_ms 超时时间(毫秒)
* @retval 1-收到期望应答, 0-超时或失败
* @note 此函数会阻塞等待应答,建议在初始化阶段使用
* @warning 如果在 FreeRTOS 中使用,确保 configUSE_PREEMPTION 已启用
*
* @example
* // 检测模块是否就绪
* if (WIFI_CheckAck("AT\r\n", "OK", 1000)) {
* elog_i(TAG, "WiFi module ready");
* }
*/
uint8_t WIFI_CheckAck(const char *cmd, const char *expect, uint32_t timeout_ms)
{
char recv_buf[WIFI_RX_BUF_SIZE];
if (cmd == NULL || expect == NULL) {
elog_e(TAG, "Invalid parameters");
return 0;
}
// 清空之前的接收标志
wifi.rx_flag = 0;
// 发送指令
HAL_StatusTypeDef status = WIFI_SEND_DMA(cmd);
if (status != HAL_OK) {
elog_e(TAG, "Send failed: %d", status);
return 0;
}
elog_d(TAG, "Wait for: %s", expect);
// 等待应答(轮询方式)
uint32_t start_tick = HAL_GetTick();
while ((HAL_GetTick() - start_tick) < timeout_ms) {
// 检查是否接收到数据
if (wifi.rx_flag) {
// 获取数据并清零标志
int len = WIFI_Get_Received_Data((uint8_t *)recv_buf, sizeof(recv_buf));
if (len > 0) {
elog_d(TAG, "Recv: %s", recv_buf);
// 检查是否包含期望应答
if (strstr(recv_buf, expect) != NULL) {
elog_i(TAG, "Ack matched: %s", expect);
return 1;
}
// 检查是否包含错误应答
if (strstr(recv_buf, "ERROR") != NULL) {
elog_w(TAG, "Got ERROR response");
return 0;
}
}
}
// 小延时,避免忙等
HAL_Delay(1);
}
elog_w(TAG, "Ack timeout, expect: %s", expect);
return 0;
}
/**
* @brief 连接MQTT服务器完整流程支持用户名密码认证
* @param wifi_ssid WiFi名称
* @param wifi_pass WiFi密码
* @param mqtt_broker MQTT服务器地址
* @param mqtt_port MQTT端口
* @param client_id MQTT客户端ID
* @param mqtt_user MQTT用户名可为NULL表示不需要认证
* @param mqtt_pass MQTT密码可为NULL表示不需要认证
* @param sub_topic 订阅的主题
* @retval 1-连接成功, 0-失败
* @note 每一步都有重试机制,失败后不会卡死
*
* @example
* // 连接到MQTT服务器无需认证
* if (WIFI_Connect_MQTT("MyWiFi", "12345678",
* "broker.emqx.io", 1883,
* "PetFeeder-001", NULL, NULL, "pet/control")) {
* elog_i(TAG, "MQTT connected");
* }
*
* // 连接到MQTT服务器需要认证
* if (WIFI_Connect_MQTT("MyWiFi", "12345678",
* "broker.emqx.io", 1883,
* "PetFeeder-001", "user1", "pass123", "pet/control")) {
* elog_i(TAG, "MQTT connected");
* }
*/
uint8_t WIFI_Connect_MQTT(const char *wifi_ssid, const char *wifi_pass,
const char *mqtt_broker, uint16_t mqtt_port,
const char *client_id, const char *mqtt_user,
const char *mqtt_pass, const char *sub_topic)
{
char cmd_buf[128];
uint8_t retry_count = 0;
const uint8_t max_retry = 3;
elog_i(TAG, "=== Starting MQTT Connection ===");
/* 步骤1: 清理环境 */
elog_d(TAG, "Step 1: Clean MQTT environment");
if (!WIFI_CheckAck("AT+MQTTCLEAN\r\n", "OK", 2000)) {
elog_w(TAG, "MQTTCLEAN failed, continue...");
}
/* 步骤2: 恢复出厂设置(可选,根据需要启用) */
// elog_d(TAG, "Step 2: Restore factory settings");
// if (!WIFI_CheckAck("AT+RESTORE\r\n", "OK", 5000)) {
// elog_w(TAG, "RESTORE failed, continue...");
// }
// osDelay(3000); // 等待模块重启
/* 步骤3: AT测试 */
elog_d(TAG, "Step 3: AT test");
retry_count = 0;
while (retry_count < max_retry) {
if (WIFI_CheckAck("AT\r\n", "OK", 1000)) {
break;
}
retry_count++;
elog_w(TAG, "AT test retry %d/%d", retry_count, max_retry);
osDelay(500);
}
if (retry_count >= max_retry) {
elog_e(TAG, "AT test failed");
return 0;
}
/* 步骤4: 设置STA模式 */
elog_d(TAG, "Step 4: Set STA mode");
if (!WIFI_CheckAck("AT+CWMODE=1\r\n", "OK", 2000)) {
elog_e(TAG, "Set STA mode failed");
return 0;
}
/* 步骤5: 连接WiFi */
elog_d(TAG, "Step 5: Connect to WiFi: %s", wifi_ssid);
snprintf(cmd_buf, sizeof(cmd_buf), "AT+CWJAP=\"%s\",\"%s\"\r\n", wifi_ssid, wifi_pass);
retry_count = 0;
while (retry_count < max_retry) {
if (WIFI_CheckAck(cmd_buf, "WIFI GOT IP", 10000)) {
elog_i(TAG, "WiFi connected successfully");
break;
}
retry_count++;
elog_w(TAG, "WiFi connect retry %d/%d", retry_count, max_retry);
osDelay(2000);
}
if (retry_count >= max_retry) {
elog_e(TAG, "WiFi connect failed");
return 0;
}
/* 步骤6: 配置MQTT用户参数用户名和密码 */
if (mqtt_user != NULL && mqtt_pass != NULL) {
elog_d(TAG, "Step 6: Configure MQTT user authentication");
// 配置MQTT用户参数LinkID=0, scheme=1, client_id, username, password
snprintf(cmd_buf, sizeof(cmd_buf), "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n",
client_id, mqtt_user, mqtt_pass);
if (!WIFI_CheckAck(cmd_buf, "OK", 3000)) {
elog_e(TAG, "MQTT user configuration failed");
return 0;
}
} else {
elog_d(TAG, "Step 6: Configure MQTT without authentication");
// 无认证模式
snprintf(cmd_buf, sizeof(cmd_buf), "AT+MQTTUSERCFG=0,1,\"%s\",\"\",\"\",0,0,\"\"\r\n", client_id);
if (!WIFI_CheckAck(cmd_buf, "OK", 3000)) {
elog_e(TAG, "MQTT configuration failed");
return 0;
}
}
/* 步骤7: 连接MQTT服务器 */
elog_d(TAG, "Step 7: Connect to MQTT broker: %s:%d", mqtt_broker, mqtt_port);
snprintf(cmd_buf, sizeof(cmd_buf), "AT+MQTTCONN=0,\"%s\",%d,0\r\n", mqtt_broker, mqtt_port);
retry_count = 0;
while (retry_count < max_retry) {
if (WIFI_CheckAck(cmd_buf, "MQTTCONNECTED:", 5000)) {
elog_i(TAG, "MQTT broker connected");
break;
}
retry_count++;
elog_w(TAG, "MQTT connect retry %d/%d", retry_count, max_retry);
osDelay(1000);
}
if (retry_count >= max_retry) {
elog_e(TAG, "MQTT connect failed");
return 0;
}
/* 步骤8: 订阅主题 */
elog_d(TAG, "Step 8: Subscribe topic: %s", sub_topic);
snprintf(cmd_buf, sizeof(cmd_buf), "AT+MQTTSUB=0,\"%s\",0\r\n", sub_topic);
if (!WIFI_CheckAck(cmd_buf, "OK", 3000)) {
elog_e(TAG, "Subscribe failed");
return 0;
}
/* 步骤9: 发布上线消息 */
elog_d(TAG, "Step 9: Publish online message");
const char *online_topic = "device/online";
const char *online_msg = "{\"device\":\"PetFeeder\",\"status\":\"online\"}";
snprintf(cmd_buf, sizeof(cmd_buf), "AT+MQTTPUB=0,\"%s\",\"%s\",0,0\r\n", online_topic, online_msg);
if (!WIFI_CheckAck(cmd_buf, "OK", 2000)) {
elog_w(TAG, "Publish online message failed, continue...");
}
elog_i(TAG, "=== MQTT Connection Completed Successfully ===");
return 1;
}

View File

@@ -0,0 +1,37 @@
#ifndef __DX_WF_24_H__
#define __DX_WF_24_H__
#include "usart.h"
#include <stdint.h>
/* 默认超时时间(毫秒) */
#define WIFI_DEFAULT_TIMEOUT 1000
/* WiFi 接收缓冲区大小 */
#define WIFI_RX_BUF_SIZE 512
/* WiFi 数据结构体 */
typedef struct {
uint8_t rx_buffer[WIFI_RX_BUF_SIZE]; // 接收缓冲区
uint16_t rx_len; // 本次接收长度
uint8_t rx_flag; // 接收完成标志
} WIFI_HandleTypeDef;
extern WIFI_HandleTypeDef wifi;
/* 函数声明 */
HAL_StatusTypeDef WIFI_SEND_DMA(const char *data);
HAL_StatusTypeDef WIFI_RECV_DMA_Init(void);
void WIFI_UART_IDLE_Callback(UART_HandleTypeDef *huart);
int WIFI_Get_Received_Data(uint8_t *buffer, uint16_t len);
/* 同步应答检测函数 */
uint8_t WIFI_CheckAck(const char *cmd, const char *expect, uint32_t timeout_ms);
/* MQTT连接函数 */
uint8_t WIFI_Connect_MQTT(const char *wifi_ssid, const char *wifi_pass,
const char *mqtt_broker, uint16_t mqtt_port,
const char *client_id, const char *mqtt_user,
const char *mqtt_pass, const char *sub_topic);
#endif /* __DX_WF_24_H__ */

View File

@@ -0,0 +1,272 @@
//
// Created by wangb on 25-6-4.
//
#ifndef ELOG_H
#define ELOG_H
#include "elog_cfg.h"
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* output log's level */
#define ELOG_LVL_ASSERT 0
#define ELOG_LVL_ERROR 1
#define ELOG_LVL_WARN 2
#define ELOG_LVL_INFO 3
#define ELOG_LVL_DEBUG 4
#define ELOG_LVL_VERBOSE 5
/* the output silent level and all level for filter setting */
#define ELOG_FILTER_LVL_SILENT ELOG_LVL_ASSERT
#define ELOG_FILTER_LVL_ALL ELOG_LVL_VERBOSE
/* output log's level total number */
#define ELOG_LVL_TOTAL_NUM 6
/* EasyLogger software version number */
#define ELOG_SW_VERSION "2.2.99"
/* EasyLogger assert for developer. */
#ifdef ELOG_ASSERT_ENABLE
#define ELOG_ASSERT(EXPR) \
if (!(EXPR)) \
{ \
if (elog_assert_hook == NULL) { \
elog_a("elog", "(%s) has assert failed at %s:%ld.", #EXPR, __FUNCTION__, __LINE__); \
while (1); \
} else { \
elog_assert_hook(#EXPR, __FUNCTION__, __LINE__); \
} \
}
#else
#define ELOG_ASSERT(EXPR) ((void)0);
#endif
#ifndef ELOG_OUTPUT_ENABLE
#define elog_raw(...)
#define elog_assert(tag, ...)
#define elog_error(tag, ...)
#define elog_warn(tag, ...)
#define elog_info(tag, ...)
#define elog_debug(tag, ...)
#define elog_verbose(tag, ...)
#else /* ELOG_OUTPUT_ENABLE */
#ifdef ELOG_FMT_USING_FUNC
#define ELOG_OUTPUT_FUNC __FUNCTION__
#else
#define ELOG_OUTPUT_FUNC NULL
#endif
#ifdef ELOG_FMT_USING_DIR
#define ELOG_OUTPUT_DIR __FILE__
#else
#define ELOG_OUTPUT_DIR NULL
#endif
#ifdef ELOG_FMT_USING_LINE
#define ELOG_OUTPUT_LINE __LINE__
#else
#define ELOG_OUTPUT_LINE 0
#endif
#define elog_raw(...) elog_raw_output(__VA_ARGS__)
#if ELOG_OUTPUT_LVL >= ELOG_LVL_ASSERT
#define elog_assert(tag, ...) \
elog_output(ELOG_LVL_ASSERT, tag, ELOG_OUTPUT_DIR, ELOG_OUTPUT_FUNC, ELOG_OUTPUT_LINE, __VA_ARGS__)
#else
#define elog_assert(tag, ...)
#endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_ASSERT */
#if ELOG_OUTPUT_LVL >= ELOG_LVL_ERROR
#define elog_error(tag, ...) \
elog_output(ELOG_LVL_ERROR, tag, ELOG_OUTPUT_DIR, ELOG_OUTPUT_FUNC, ELOG_OUTPUT_LINE, __VA_ARGS__)
#else
#define elog_error(tag, ...)
#endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_ERROR */
#if ELOG_OUTPUT_LVL >= ELOG_LVL_WARN
#define elog_warn(tag, ...) \
elog_output(ELOG_LVL_WARN, tag, ELOG_OUTPUT_DIR, ELOG_OUTPUT_FUNC, ELOG_OUTPUT_LINE, __VA_ARGS__)
#else
#define elog_warn(tag, ...)
#endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_WARN */
#if ELOG_OUTPUT_LVL >= ELOG_LVL_INFO
#define elog_info(tag, ...) \
elog_output(ELOG_LVL_INFO, tag, ELOG_OUTPUT_DIR, ELOG_OUTPUT_FUNC, ELOG_OUTPUT_LINE, __VA_ARGS__)
#else
#define elog_info(tag, ...)
#endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_INFO */
#if ELOG_OUTPUT_LVL >= ELOG_LVL_DEBUG
#define elog_debug(tag, ...) \
elog_output(ELOG_LVL_DEBUG, tag, ELOG_OUTPUT_DIR, ELOG_OUTPUT_FUNC, ELOG_OUTPUT_LINE, __VA_ARGS__)
#else
#define elog_debug(tag, ...)
#endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_DEBUG */
#if ELOG_OUTPUT_LVL == ELOG_LVL_VERBOSE
#define elog_verbose(tag, ...) \
elog_output(ELOG_LVL_VERBOSE, tag, ELOG_OUTPUT_DIR, ELOG_OUTPUT_FUNC, ELOG_OUTPUT_LINE, __VA_ARGS__)
#else
#define elog_verbose(tag, ...)
#endif /* ELOG_OUTPUT_LVL == ELOG_LVL_VERBOSE */
#endif /* ELOG_OUTPUT_ENABLE */
/* all formats index */
typedef enum {
ELOG_FMT_LVL = 1 << 0, /**< level */
ELOG_FMT_TAG = 1 << 1, /**< tag */
ELOG_FMT_TIME = 1 << 2, /**< current time */
ELOG_FMT_P_INFO = 1 << 3, /**< process info */
ELOG_FMT_T_INFO = 1 << 4, /**< thread info */
ELOG_FMT_DIR = 1 << 5, /**< file directory and name */
ELOG_FMT_FUNC = 1 << 6, /**< function name */
ELOG_FMT_LINE = 1 << 7, /**< line number */
} ElogFmtIndex;
/* macro definition for all formats */
#define ELOG_FMT_ALL (ELOG_FMT_LVL|ELOG_FMT_TAG|ELOG_FMT_TIME|ELOG_FMT_P_INFO|ELOG_FMT_T_INFO| \
ELOG_FMT_DIR|ELOG_FMT_FUNC|ELOG_FMT_LINE)
/* output log's tag filter */
typedef struct {
uint8_t level;
char tag[ELOG_FILTER_TAG_MAX_LEN + 1];
bool tag_use_flag; /**< false : tag is no used true: tag is used */
} ElogTagLvlFilter, *ElogTagLvlFilter_t;
/* output log's filter */
typedef struct {
uint8_t level;
char tag[ELOG_FILTER_TAG_MAX_LEN + 1];
char keyword[ELOG_FILTER_KW_MAX_LEN + 1];
ElogTagLvlFilter tag_lvl[ELOG_FILTER_TAG_LVL_MAX_NUM];
} ElogFilter, *ElogFilter_t;
/* easy logger */
typedef struct {
ElogFilter filter;
size_t enabled_fmt_set[ELOG_LVL_TOTAL_NUM];
bool init_ok;
bool output_enabled;
bool output_lock_enabled;
bool output_is_locked_before_enable;
bool output_is_locked_before_disable;
#ifdef ELOG_COLOR_ENABLE
bool text_color_enabled;
#endif
}EasyLogger, *EasyLogger_t;
/* EasyLogger error code */
typedef enum {
ELOG_NO_ERR,
} ElogErrCode;
/* elog.c */
ElogErrCode elog_init(void);
void elog_deinit(void);
void elog_start(void);
void elog_stop(void);
void elog_set_output_enabled(bool enabled);
bool elog_get_output_enabled(void);
void elog_set_text_color_enabled(bool enabled);
bool elog_get_text_color_enabled(void);
void elog_set_fmt(uint8_t level, size_t set);
void elog_set_filter(uint8_t level, const char *tag, const char *keyword);
void elog_set_filter_lvl(uint8_t level);
void elog_set_filter_tag(const char *tag);
void elog_set_filter_kw(const char *keyword);
void elog_set_filter_tag_lvl(const char *tag, uint8_t level);
uint8_t elog_get_filter_tag_lvl(const char *tag);
void elog_raw_output(const char *format, ...);
void elog_output(uint8_t level, const char *tag, const char *file, const char *func,
const long line, const char *format, ...);
void elog_output_lock_enabled(bool enabled);
extern void (*elog_assert_hook)(const char* expr, const char* func, size_t line);
void elog_assert_set_hook(void (*hook)(const char* expr, const char* func, size_t line));
int8_t elog_find_lvl(const char *log);
const char *elog_find_tag(const char *log, uint8_t lvl, size_t *tag_len);
void elog_hexdump(const char *name, uint8_t width, const void *buf, uint16_t size);
#define elog_a(tag, ...) elog_assert(tag, __VA_ARGS__)
#define elog_e(tag, ...) elog_error(tag, __VA_ARGS__)
#define elog_w(tag, ...) elog_warn(tag, __VA_ARGS__)
#define elog_i(tag, ...) elog_info(tag, __VA_ARGS__)
#define elog_d(tag, ...) elog_debug(tag, __VA_ARGS__)
#define elog_v(tag, ...) elog_verbose(tag, __VA_ARGS__)
/**
* log API short definition
* NOTE: The `LOG_TAG` and `LOG_LVL` must defined before including the <elog.h> when you want to use log_x API.
*/
#if !defined(LOG_TAG)
#define LOG_TAG "NO_TAG"
#endif
#if !defined(LOG_LVL)
#define LOG_LVL ELOG_LVL_VERBOSE
#endif
#if LOG_LVL >= ELOG_LVL_ASSERT
#define log_a(...) elog_a(LOG_TAG, __VA_ARGS__)
#else
#define log_a(...) ((void)0);
#endif
#if LOG_LVL >= ELOG_LVL_ERROR
#define log_e(...) elog_e(LOG_TAG, __VA_ARGS__)
#else
#define log_e(...) ((void)0);
#endif
#if LOG_LVL >= ELOG_LVL_WARN
#define log_w(...) elog_w(LOG_TAG, __VA_ARGS__)
#else
#define log_w(...) ((void)0);
#endif
#if LOG_LVL >= ELOG_LVL_INFO
#define log_i(...) elog_i(LOG_TAG, __VA_ARGS__)
#else
#define log_i(...) ((void)0);
#endif
#if LOG_LVL >= ELOG_LVL_DEBUG
#define log_d(...) elog_d(LOG_TAG, __VA_ARGS__)
#else
#define log_d(...) ((void)0);
#endif
#if LOG_LVL >= ELOG_LVL_VERBOSE
#define log_v(...) elog_v(LOG_TAG, __VA_ARGS__)
#else
#define log_v(...) ((void)0);
#endif
/* assert API short definition */
#if !defined(assert)
#define assert ELOG_ASSERT
#endif
/* elog_buf.c */
void elog_buf_enabled(bool enabled);
void elog_flush(void);
/* elog_async.c */
void elog_async_enabled(bool enabled);
size_t elog_async_get_log(char *log, size_t size);
size_t elog_async_get_line_log(char *log, size_t size);
/* elog_utils.c */
size_t elog_strcpy(size_t cur_len, char *dst, const char *src);
size_t elog_cpyln(char *line, const char *log, size_t len);
void *elog_memcpy(void *dst, const void *src, size_t count);
void easylogger_init(void);
#ifdef __cplusplus
}
#endif
#endif //ELOG_H

View File

@@ -0,0 +1,59 @@
//
// Created by wangb on 25-6-4.
//
#ifndef _ELOG_CFG_H_
#define _ELOG_CFG_H_
/*---------------------------------------------------------------------------*/
/* enable log output. */
#define ELOG_OUTPUT_ENABLE
/* setting static output log level. range: from ELOG_LVL_ASSERT to ELOG_LVL_VERBOSE */
#define ELOG_OUTPUT_LVL ELOG_LVL_VERBOSE
/* enable assert check */
#define ELOG_ASSERT_ENABLE
/* buffer size for every line's log */
#define ELOG_LINE_BUF_SIZE 1024
/* output line number max length */
#define ELOG_LINE_NUM_MAX_LEN 5
/* output filter's tag max length */
#define ELOG_FILTER_TAG_MAX_LEN 30
/* output filter's keyword max length */
#define ELOG_FILTER_KW_MAX_LEN 16
/* output filter's tag level max num */
#define ELOG_FILTER_TAG_LVL_MAX_NUM 5
/* output newline sign */
#define ELOG_NEWLINE_SIGN "\r\n" //换行
/*---------------------------------------------------------------------------*/
/* enable log color */
#define ELOG_COLOR_ENABLE
/* change the some level logs to not default color if you want */
#define ELOG_COLOR_ASSERT (F_MAGENTA B_NULL S_NORMAL)
#define ELOG_COLOR_ERROR (F_RED B_NULL S_NORMAL)
#define ELOG_COLOR_WARN (F_YELLOW B_NULL S_NORMAL)
#define ELOG_COLOR_INFO (F_CYAN B_NULL S_NORMAL)
#define ELOG_COLOR_DEBUG (F_GREEN B_NULL S_NORMAL)
#define ELOG_COLOR_VERBOSE (F_BLUE B_NULL S_NORMAL)
/*---------------------------------------------------------------------------*/
/* enable log fmt */
/* comment it if you don't want to output them at all */
#define ELOG_FMT_USING_FUNC
#define ELOG_FMT_USING_DIR
#define ELOG_FMT_USING_LINE
/*---------------------------------------------------------------------------*/
/* enable asynchronous output mode */
//#define ELOG_ASYNC_OUTPUT_ENABLE
/* the highest output level for async mode, other level will sync output */
#define ELOG_ASYNC_OUTPUT_LVL ELOG_LVL_ASSERT
/* buffer size for asynchronous output mode */
#define ELOG_ASYNC_OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10)
/* each asynchronous output's log which must end with newline sign */
#define ELOG_ASYNC_LINE_OUTPUT
/* asynchronous output mode using POSIX pthread implementation */
#define ELOG_ASYNC_OUTPUT_USING_PTHREAD
/*---------------------------------------------------------------------------*/
/* enable buffered output mode */
//#define ELOG_BUF_OUTPUT_ENABLE
/* buffer size for buffered output mode */
#define ELOG_BUF_OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10)
#endif /* _ELOG_CFG_H_ */

View File

@@ -0,0 +1,127 @@
//
// Created by wangb on 25-6-4.
//
#include "elog.h"
#include <stdio.h>
#include "main.h"
/**
* EasyLogger port initialize
*
* @return result
*/
ElogErrCode elog_port_init(void) {
ElogErrCode result = ELOG_NO_ERR;
/* add your code here */
return result;
}
/**
* EasyLogger port deinitialize
*
*/
void elog_port_deinit(void) {
/* add your code here */
}
/**
* output log port interface
*
* @param log output of log
* @param size log size
*/
void elog_port_output(const char *log, size_t size) {
/* add your code here */
printf("%.*s", size, log); //elog 的输出
}
/**
* output lock
*/
void elog_port_output_lock(void) {
/* add your code here */
__disable_irq(); //关闭全局中断
}
/**
* output unlock
*/
void elog_port_output_unlock(void) {
/* add your code here */
__enable_irq(); //开启全局中断
}
#include <stdio.h>
/**
* get current time interface
*
* @return current time
*/
const char *elog_port_get_time(void) {
// static char rtc_time[20]; // 静态缓冲区,存 "YYYY-MM-DD HH:MM:SS"
// RTC_TimeTypeDef sTime = {0};
// RTC_DateTypeDef sDate = {0};
//
// // 1. 读取RTC时间先读时间再读日期HAL库要求
// HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
// HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
//
// // 2. 格式化时间字符串(直接拼接到静态数组)
// snprintf(rtc_time, sizeof(rtc_time), "20%02d-%02d-%02d %02d:%02d:%02d",
// sDate.Year, // 25 → 2025
// sDate.Month, // 1-12
// sDate.Date, // 1-31
// sTime.Hours, // 0-23
// sTime.Minutes, // 0-59
// sTime.Seconds); // 0-59
return "";
}
/**
* get current process name interface
*
* @return current process name
*/
const char *elog_port_get_p_info(void) {
/* add your code here */
return "";
}
/**
* get current thread name interface
*
* @return current thread name
*/
const char *elog_port_get_t_info(void) {
/* add your code here */
//获取STM32 的线程接口
return "";
}
void easylogger_init(void) {
/* init Easylogger */
elog_init();
/* set EasyLogger log format */
elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL & ~ELOG_FMT_P_INFO);
elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~(ELOG_FMT_FUNC | ELOG_FMT_P_INFO));
elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~(ELOG_FMT_FUNC | ELOG_FMT_P_INFO));
/* start EasyLogger */
elog_start();
}

View File

@@ -0,0 +1,912 @@
//
// Created by wangb on 25-6-4.
//
#define LOG_TAG "elog"
#include "elog.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#if !defined(ELOG_OUTPUT_LVL)
#error "Please configure static output log level (in elog_cfg.h)"
#endif
#if !defined(ELOG_LINE_NUM_MAX_LEN)
#error "Please configure output line number max length (in elog_cfg.h)"
#endif
#if !defined(ELOG_LINE_BUF_SIZE)
#error "Please configure buffer size for every line's log (in elog_cfg.h)"
#endif
#if !defined(ELOG_FILTER_TAG_MAX_LEN)
#error "Please configure output filter's tag max length (in elog_cfg.h)"
#endif
#if !defined(ELOG_FILTER_KW_MAX_LEN)
#error "Please configure output filter's keyword max length (in elog_cfg.h)"
#endif
#if !defined(ELOG_NEWLINE_SIGN)
#error "Please configure output newline sign (in elog_cfg.h)"
#endif
/* output filter's tag level max num */
#ifndef ELOG_FILTER_TAG_LVL_MAX_NUM
#define ELOG_FILTER_TAG_LVL_MAX_NUM 4
#endif
#ifdef ELOG_COLOR_ENABLE
/**
* CSI(Control Sequence Introducer/Initiator) sign
* more information on https://en.wikipedia.org/wiki/ANSI_escape_code
*/
#define CSI_START "\033["
#define CSI_END "\033[0m"
/* output log front color */
#define F_BLACK "30;"
#define F_RED "31;"
#define F_GREEN "32;"
#define F_YELLOW "33;"
#define F_BLUE "34;"
#define F_MAGENTA "35;"
#define F_CYAN "36;"
#define F_WHITE "37;"
/* output log background color */
#define B_NULL
#define B_BLACK "40;"
#define B_RED "41;"
#define B_GREEN "42;"
#define B_YELLOW "43;"
#define B_BLUE "44;"
#define B_MAGENTA "45;"
#define B_CYAN "46;"
#define B_WHITE "47;"
/* output log fonts style */
#define S_BOLD "1m"
#define S_UNDERLINE "4m"
#define S_BLINK "5m"
#define S_NORMAL "22m"
/* output log default color definition: [front color] + [background color] + [show style] */
#ifndef ELOG_COLOR_ASSERT
#define ELOG_COLOR_ASSERT (F_MAGENTA B_NULL S_NORMAL)
#endif
#ifndef ELOG_COLOR_ERROR
#define ELOG_COLOR_ERROR (F_RED B_NULL S_NORMAL)
#endif
#ifndef ELOG_COLOR_WARN
#define ELOG_COLOR_WARN (F_YELLOW B_NULL S_NORMAL)
#endif
#ifndef ELOG_COLOR_INFO
#define ELOG_COLOR_INFO (F_CYAN B_NULL S_NORMAL)
#endif
#ifndef ELOG_COLOR_DEBUG
#define ELOG_COLOR_DEBUG (F_GREEN B_NULL S_NORMAL)
#endif
#ifndef ELOG_COLOR_VERBOSE
#define ELOG_COLOR_VERBOSE (F_BLUE B_NULL S_NORMAL)
#endif
#endif /* ELOG_COLOR_ENABLE */
/* EasyLogger object */
static EasyLogger elog;
/* every line log's buffer */
static char log_buf[ELOG_LINE_BUF_SIZE] = { 0 };
/* level output info */
static const char *level_output_info[] = {
[ELOG_LVL_ASSERT] = "ASSERT/",
[ELOG_LVL_ERROR] = "ERROR/",
[ELOG_LVL_WARN] = "WARN/",
[ELOG_LVL_INFO] = "INFO/",
[ELOG_LVL_DEBUG] = "DEBUG/",
[ELOG_LVL_VERBOSE] = "VERBOS/",
};
#ifdef ELOG_COLOR_ENABLE
/* color output info */
static const char *color_output_info[] = {
[ELOG_LVL_ASSERT] = ELOG_COLOR_ASSERT,
[ELOG_LVL_ERROR] = ELOG_COLOR_ERROR,
[ELOG_LVL_WARN] = ELOG_COLOR_WARN,
[ELOG_LVL_INFO] = ELOG_COLOR_INFO,
[ELOG_LVL_DEBUG] = ELOG_COLOR_DEBUG,
[ELOG_LVL_VERBOSE] = ELOG_COLOR_VERBOSE,
};
#endif /* ELOG_COLOR_ENABLE */
static bool get_fmt_enabled(uint8_t level, size_t set);
static bool get_fmt_used_and_enabled_u32(uint8_t level, size_t set, uint32_t arg);
static bool get_fmt_used_and_enabled_ptr(uint8_t level, size_t set, const char* arg);
static void elog_set_filter_tag_lvl_default(void);
/* EasyLogger assert hook */
void (*elog_assert_hook)(const char* expr, const char* func, size_t line);
extern void elog_port_output(const char *log, size_t size);
extern void elog_port_output_lock(void);
extern void elog_port_output_unlock(void);
/**
* EasyLogger initialize.
*
* @return result
*/
ElogErrCode elog_init(void) {
extern ElogErrCode elog_port_init(void);
extern ElogErrCode elog_async_init(void);
ElogErrCode result = ELOG_NO_ERR;
if (elog.init_ok == true) {
return result;
}
/* port initialize */
result = elog_port_init();
if (result != ELOG_NO_ERR) {
return result;
}
#ifdef ELOG_ASYNC_OUTPUT_ENABLE
result = elog_async_init();
if (result != ELOG_NO_ERR) {
return result;
}
#endif
/* enable the output lock */
elog_output_lock_enabled(true);
/* output locked status initialize */
elog.output_is_locked_before_enable = false;
elog.output_is_locked_before_disable = false;
#ifdef ELOG_COLOR_ENABLE
/* enable text color by default */
elog_set_text_color_enabled(true);
#endif
/* set level is ELOG_LVL_VERBOSE */
elog_set_filter_lvl(ELOG_LVL_VERBOSE);
/* set tag_level to default val */
elog_set_filter_tag_lvl_default();
elog.init_ok = true;
return result;
}
/**
* EasyLogger deinitialize.
*
*/
void elog_deinit(void) {
extern ElogErrCode elog_port_deinit(void);
extern ElogErrCode elog_async_deinit(void);
if (!elog.init_ok) {
return ;
}
#ifdef ELOG_ASYNC_OUTPUT_ENABLE
elog_async_deinit();
#endif
/* port deinitialize */
elog_port_deinit();
elog.init_ok = false;
}
/**
* EasyLogger start after initialize.
*/
void elog_start(void) {
if (!elog.init_ok) {
return ;
}
/* enable output */
elog_set_output_enabled(true);
#if defined(ELOG_ASYNC_OUTPUT_ENABLE)
elog_async_enabled(true);
#elif defined(ELOG_BUF_OUTPUT_ENABLE)
elog_buf_enabled(true);
#endif
}
/**
* EasyLogger stop after initialize.
*/
void elog_stop(void) {
if (!elog.init_ok) {
return ;
}
/* disable output */
elog_set_output_enabled(false);
#if defined(ELOG_ASYNC_OUTPUT_ENABLE)
elog_async_enabled(false);
#elif defined(ELOG_BUF_OUTPUT_ENABLE)
elog_buf_enabled(false);
#endif
/* show version */
log_i("EasyLogger V%s is deinitialize success.", ELOG_SW_VERSION);
}
/**
* set output enable or disable
*
* @param enabled TRUE: enable FALSE: disable
*/
void elog_set_output_enabled(bool enabled) {
ELOG_ASSERT((enabled == false) || (enabled == true));
elog.output_enabled = enabled;
}
#ifdef ELOG_COLOR_ENABLE
/**
* set log text color enable or disable
*
* @param enabled TRUE: enable FALSE:disable
*/
void elog_set_text_color_enabled(bool enabled) {
ELOG_ASSERT((enabled == false) || (enabled == true));
elog.text_color_enabled = enabled;
}
/**
* get log text color enable status
*
* @return enable or disable
*/
bool elog_get_text_color_enabled(void) {
return elog.text_color_enabled;
}
#endif /* ELOG_COLOR_ENABLE */
/**
* get output is enable or disable
*
* @return enable or disable
*/
bool elog_get_output_enabled(void) {
return elog.output_enabled;
}
/**
* set log output format. only enable or disable
*
* @param level level
* @param set format set
*/
void elog_set_fmt(uint8_t level, size_t set) {
ELOG_ASSERT(level <= ELOG_LVL_VERBOSE);
elog.enabled_fmt_set[level] = set;
}
/**
* set log filter all parameter
*
* @param level level
* @param tag tag
* @param keyword keyword
*/
void elog_set_filter(uint8_t level, const char *tag, const char *keyword) {
ELOG_ASSERT(level <= ELOG_LVL_VERBOSE);
elog_set_filter_lvl(level);
elog_set_filter_tag(tag);
elog_set_filter_kw(keyword);
}
/**
* set log filter's level
*
* @param level level
*/
void elog_set_filter_lvl(uint8_t level) {
ELOG_ASSERT(level <= ELOG_LVL_VERBOSE);
elog.filter.level = level;
}
/**
* set log filter's tag
*
* @param tag tag
*/
void elog_set_filter_tag(const char *tag) {
strncpy(elog.filter.tag, tag, ELOG_FILTER_TAG_MAX_LEN);
}
/**
* set log filter's keyword
*
* @param keyword keyword
*/
void elog_set_filter_kw(const char *keyword) {
strncpy(elog.filter.keyword, keyword, ELOG_FILTER_KW_MAX_LEN);
}
/**
* lock output
*/
void elog_output_lock(void) {
if (elog.output_lock_enabled) {
elog_port_output_lock();
elog.output_is_locked_before_disable = true;
} else {
elog.output_is_locked_before_enable = true;
}
}
/**
* unlock output
*/
void elog_output_unlock(void) {
if (elog.output_lock_enabled) {
elog_port_output_unlock();
elog.output_is_locked_before_disable = false;
} else {
elog.output_is_locked_before_enable = false;
}
}
/**
* set log filter's tag level val to default
*/
static void elog_set_filter_tag_lvl_default(void)
{
uint8_t i = 0;
for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){
memset(elog.filter.tag_lvl[i].tag, '\0', ELOG_FILTER_TAG_MAX_LEN + 1);
elog.filter.tag_lvl[i].level = ELOG_FILTER_LVL_SILENT;
elog.filter.tag_lvl[i].tag_use_flag = false;
}
}
/**
* Set the filter's level by different tag.
* The log on this tag which level is less than it will stop output.
*
* example:
* // the example tag log enter silent mode
* elog_set_filter_tag_lvl("example", ELOG_FILTER_LVL_SILENT);
* // the example tag log which level is less than INFO level will stop output
* elog_set_filter_tag_lvl("example", ELOG_LVL_INFO);
* // remove example tag's level filter, all level log will resume output
* elog_set_filter_tag_lvl("example", ELOG_FILTER_LVL_ALL);
*
* @param tag log tag
* @param level The filter level. When the level is ELOG_FILTER_LVL_SILENT, the log enter silent mode.
* When the level is ELOG_FILTER_LVL_ALL, it will remove this tag's level filer.
* Then all level log will resume output.
*
*/
void elog_set_filter_tag_lvl(const char *tag, uint8_t level)
{
ELOG_ASSERT(level <= ELOG_LVL_VERBOSE);
ELOG_ASSERT(tag != ((void *)0));
uint8_t i = 0;
if (!elog.init_ok) {
return;
}
elog_output_lock();
/* find the tag in arr */
for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){
if (elog.filter.tag_lvl[i].tag_use_flag == true &&
!strncmp(tag, elog.filter.tag_lvl[i].tag,ELOG_FILTER_TAG_MAX_LEN)){
break;
}
}
if (i < ELOG_FILTER_TAG_LVL_MAX_NUM){
/* find OK */
if (level == ELOG_FILTER_LVL_ALL){
/* remove current tag's level filter when input level is the lowest level */
elog.filter.tag_lvl[i].tag_use_flag = false;
memset(elog.filter.tag_lvl[i].tag, '\0', ELOG_FILTER_TAG_MAX_LEN + 1);
elog.filter.tag_lvl[i].level = ELOG_FILTER_LVL_SILENT;
} else{
elog.filter.tag_lvl[i].level = level;
}
} else{
/* only add the new tag's level filer when level is not ELOG_FILTER_LVL_ALL */
if (level != ELOG_FILTER_LVL_ALL){
for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){
if (elog.filter.tag_lvl[i].tag_use_flag == false){
strncpy(elog.filter.tag_lvl[i].tag, tag, ELOG_FILTER_TAG_MAX_LEN);
elog.filter.tag_lvl[i].level = level;
elog.filter.tag_lvl[i].tag_use_flag = true;
break;
}
}
}
}
elog_output_unlock();
}
/**
* get the level on tag's level filer
*
* @param tag tag
*
* @return It will return the lowest level when tag was not found.
* Other level will return when tag was found.
*/
uint8_t elog_get_filter_tag_lvl(const char *tag)
{
ELOG_ASSERT(tag != ((void *)0));
uint8_t i = 0;
uint8_t level = ELOG_FILTER_LVL_ALL;
if (!elog.init_ok) {
return level;
}
elog_output_lock();
/* find the tag in arr */
for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){
if (elog.filter.tag_lvl[i].tag_use_flag == true &&
!strncmp(tag, elog.filter.tag_lvl[i].tag,ELOG_FILTER_TAG_MAX_LEN)){
level = elog.filter.tag_lvl[i].level;
break;
}
}
elog_output_unlock();
return level;
}
/**
* output RAW format log
*
* @param format output format
* @param ... args
*/
void elog_raw_output(const char *format, ...) {
va_list args;
size_t log_len = 0;
int fmt_result;
/* check output enabled */
if (!elog.output_enabled) {
return;
}
/* args point to the first variable parameter */
va_start(args, format);
/* lock output */
elog_output_lock();
/* package log data to buffer */
fmt_result = vsnprintf(log_buf, ELOG_LINE_BUF_SIZE, format, args);
/* output converted log */
if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) {
log_len = fmt_result;
} else {
log_len = ELOG_LINE_BUF_SIZE;
}
/* output log */
#if defined(ELOG_ASYNC_OUTPUT_ENABLE)
extern void elog_async_output(uint8_t level, const char *log, size_t size);
/* raw log will using assert level */
elog_async_output(ELOG_LVL_ASSERT, log_buf, log_len);
#elif defined(ELOG_BUF_OUTPUT_ENABLE)
extern void elog_buf_output(const char *log, size_t size);
elog_buf_output(log_buf, log_len);
#else
elog_port_output(log_buf, log_len);
#endif
/* unlock output */
elog_output_unlock();
va_end(args);
}
/**
* output the log
*
* @param level level
* @param tag tag
* @param file file name
* @param func function name
* @param line line number
* @param format output format
* @param ... args
*
*/
void elog_output(uint8_t level, const char *tag, const char *file, const char *func,
const long line, const char *format, ...) {
extern const char *elog_port_get_time(void);
extern const char *elog_port_get_p_info(void);
extern const char *elog_port_get_t_info(void);
size_t tag_len = strlen(tag), log_len = 0, newline_len = strlen(ELOG_NEWLINE_SIGN);
char line_num[ELOG_LINE_NUM_MAX_LEN + 1] = { 0 };
char tag_sapce[ELOG_FILTER_TAG_MAX_LEN / 2 + 1] = { 0 };
va_list args;
int fmt_result;
ELOG_ASSERT(level <= ELOG_LVL_VERBOSE);
/* check output enabled */
if (!elog.output_enabled) {
return;
}
/* level filter */
if (level > elog.filter.level || level > elog_get_filter_tag_lvl(tag)) {
return;
} else if (!strstr(tag, elog.filter.tag)) { /* tag filter */
return;
}
/* args point to the first variable parameter */
va_start(args, format);
/* lock output */
elog_output_lock();
#ifdef ELOG_COLOR_ENABLE
/* add CSI start sign and color info */
if (elog.text_color_enabled) {
log_len += elog_strcpy(log_len, log_buf + log_len, CSI_START);
log_len += elog_strcpy(log_len, log_buf + log_len, color_output_info[level]);
}
#endif
/* package level info */
if (get_fmt_enabled(level, ELOG_FMT_LVL)) {
log_len += elog_strcpy(log_len, log_buf + log_len, level_output_info[level]);
}
/* package tag info */
if (get_fmt_enabled(level, ELOG_FMT_TAG)) {
log_len += elog_strcpy(log_len, log_buf + log_len, tag);
/* if the tag length is less than 50% ELOG_FILTER_TAG_MAX_LEN, then fill space */
if (tag_len <= ELOG_FILTER_TAG_MAX_LEN / 2) {
memset(tag_sapce, ' ', ELOG_FILTER_TAG_MAX_LEN / 2 - tag_len);
log_len += elog_strcpy(log_len, log_buf + log_len, tag_sapce);
}
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
/* package time, process and thread info */
if (get_fmt_enabled(level, ELOG_FMT_TIME | ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) {
log_len += elog_strcpy(log_len, log_buf + log_len, "[");
/* package time info */
if (get_fmt_enabled(level, ELOG_FMT_TIME)) {
log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_time());
if (get_fmt_enabled(level, ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) {
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
}
/* package process info */
if (get_fmt_enabled(level, ELOG_FMT_P_INFO)) {
log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_p_info());
if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) {
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
}
/* package thread info */
if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) {
log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_t_info());
}
log_len += elog_strcpy(log_len, log_buf + log_len, "] ");
}
/* package file directory and name, function name and line number info */
if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_DIR, file) ||
get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func) ||
get_fmt_used_and_enabled_u32(level, ELOG_FMT_LINE, line)) {
log_len += elog_strcpy(log_len, log_buf + log_len, "(");
/* package file info */
if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_DIR, file)) {
log_len += elog_strcpy(log_len, log_buf + log_len, file);
if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func)) {
log_len += elog_strcpy(log_len, log_buf + log_len, ":");
} else if (get_fmt_used_and_enabled_u32(level, ELOG_FMT_LINE, line)) {
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
}
/* package line info */
if (get_fmt_used_and_enabled_u32(level, ELOG_FMT_LINE, line)) {
snprintf(line_num, ELOG_LINE_NUM_MAX_LEN, "%ld", line);
log_len += elog_strcpy(log_len, log_buf + log_len, line_num);
if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func)) {
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
}
/* package func info */
if (get_fmt_used_and_enabled_ptr(level, ELOG_FMT_FUNC, func)) {
log_len += elog_strcpy(log_len, log_buf + log_len, func);
}
log_len += elog_strcpy(log_len, log_buf + log_len, ")");
}
/* package other log data to buffer. '\0' must be added in the end by vsnprintf. */
fmt_result = vsnprintf(log_buf + log_len, ELOG_LINE_BUF_SIZE - log_len, format, args);
va_end(args);
/* calculate log length */
if ((log_len + fmt_result <= ELOG_LINE_BUF_SIZE) && (fmt_result > -1)) {
log_len += fmt_result;
} else {
/* using max length */
log_len = ELOG_LINE_BUF_SIZE;
}
/* overflow check and reserve some space for CSI end sign and newline sign */
#ifdef ELOG_COLOR_ENABLE
if (log_len + (sizeof(CSI_END) - 1) + newline_len > ELOG_LINE_BUF_SIZE) {
/* using max length */
log_len = ELOG_LINE_BUF_SIZE;
/* reserve some space for CSI end sign */
log_len -= (sizeof(CSI_END) - 1);
#else
if (log_len + newline_len > ELOG_LINE_BUF_SIZE) {
/* using max length */
log_len = ELOG_LINE_BUF_SIZE;
#endif /* ELOG_COLOR_ENABLE */
/* reserve some space for newline sign */
log_len -= newline_len;
}
/* keyword filter */
if (elog.filter.keyword[0] != '\0') {
/* add string end sign */
log_buf[log_len] = '\0';
/* find the keyword */
if (!strstr(log_buf, elog.filter.keyword)) {
/* unlock output */
elog_output_unlock();
return;
}
}
#ifdef ELOG_COLOR_ENABLE
/* add CSI end sign */
if (elog.text_color_enabled) {
log_len += elog_strcpy(log_len, log_buf + log_len, CSI_END);
}
#endif
/* package newline sign */
log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN);
/* output log */
#if defined(ELOG_ASYNC_OUTPUT_ENABLE)
extern void elog_async_output(uint8_t level, const char *log, size_t size);
elog_async_output(level, log_buf, log_len);
#elif defined(ELOG_BUF_OUTPUT_ENABLE)
extern void elog_buf_output(const char *log, size_t size);
elog_buf_output(log_buf, log_len);
#else
elog_port_output(log_buf, log_len);
#endif
/* unlock output */
elog_output_unlock();
}
/**
* get format enabled
*
* @param level level
* @param set format set
*
* @return enable or disable
*/
static bool get_fmt_enabled(uint8_t level, size_t set) {
ELOG_ASSERT(level <= ELOG_LVL_VERBOSE);
if (elog.enabled_fmt_set[level] & set) {
return true;
} else {
return false;
}
}
static bool get_fmt_used_and_enabled_u32(uint8_t level, size_t set, uint32_t arg) {
return arg && get_fmt_enabled(level, set);
}
static bool get_fmt_used_and_enabled_ptr(uint8_t level, size_t set, const char* arg) {
return arg && get_fmt_enabled(level, set);
}
/**
* enable or disable logger output lock
* @note disable this lock is not recommended except you want output system exception log
*
* @param enabled true: enable false: disable
*/
void elog_output_lock_enabled(bool enabled) {
elog.output_lock_enabled = enabled;
/* it will re-lock or re-unlock before output lock enable */
if (elog.output_lock_enabled) {
if (!elog.output_is_locked_before_disable && elog.output_is_locked_before_enable) {
/* the output lock is unlocked before disable, and the lock will unlocking after enable */
elog_port_output_lock();
} else if (elog.output_is_locked_before_disable && !elog.output_is_locked_before_enable) {
/* the output lock is locked before disable, and the lock will locking after enable */
elog_port_output_unlock();
}
}
}
/**
* Set a hook function to EasyLogger assert. It will run when the expression is false.
*
* @param hook the hook function
*/
void elog_assert_set_hook(void (*hook)(const char* expr, const char* func, size_t line)) {
elog_assert_hook = hook;
}
/**
* find the log level
* @note make sure the log level is output on each format
*
* @param log log buffer
*
* @return log level, found failed will return -1
*/
int8_t elog_find_lvl(const char *log) {
ELOG_ASSERT(log);
/* make sure the log level is output on each format */
ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_ASSERT] & ELOG_FMT_LVL);
ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_ERROR] & ELOG_FMT_LVL);
ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_WARN] & ELOG_FMT_LVL);
ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_INFO] & ELOG_FMT_LVL);
ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_DEBUG] & ELOG_FMT_LVL);
ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_VERBOSE] & ELOG_FMT_LVL);
#ifdef ELOG_COLOR_ENABLE
uint8_t i;
size_t csi_start_len = strlen(CSI_START);
for(i = 0; i < ELOG_LVL_TOTAL_NUM; i ++) {
if (!strncmp(color_output_info[i], log + csi_start_len, strlen(color_output_info[i]))) {
return i;
}
}
/* found failed */
return -1;
#else
switch (log[0]) {
case 'A': return ELOG_LVL_ASSERT;
case 'E': return ELOG_LVL_ERROR;
case 'W': return ELOG_LVL_WARN;
case 'I': return ELOG_LVL_INFO;
case 'D': return ELOG_LVL_DEBUG;
case 'V': return ELOG_LVL_VERBOSE;
default: return -1;
}
#endif
}
/**
* find the log tag
* @note make sure the log tag is output on each format
* @note the tag don't have space in it
*
* @param log log buffer
* @param lvl log level, you can get it by @see elog_find_lvl
* @param tag_len found tag length
*
* @return log tag, found failed will return NULL
*/
const char *elog_find_tag(const char *log, uint8_t lvl, size_t *tag_len) {
const char *tag = NULL, *tag_end = NULL;
ELOG_ASSERT(log);
ELOG_ASSERT(tag_len);
ELOG_ASSERT(lvl < ELOG_LVL_TOTAL_NUM);
/* make sure the log tag is output on each format */
ELOG_ASSERT(elog.enabled_fmt_set[lvl] & ELOG_FMT_TAG);
#ifdef ELOG_COLOR_ENABLE
tag = log + strlen(CSI_START) + strlen(color_output_info[lvl]) + strlen(level_output_info[lvl]);
#else
tag = log + strlen(level_output_info[lvl]);
#endif
/* find the first space after tag */
if ((tag_end = memchr(tag, ' ', ELOG_FILTER_TAG_MAX_LEN)) != NULL) {
*tag_len = tag_end - tag;
} else {
tag = NULL;
}
return tag;
}
/**
* dump the hex format data to log
*
* @param name name for hex object, it will show on log header
* @param width hex number for every line, such as: 16, 32
* @param buf hex buffer
* @param size buffer size
*/
void elog_hexdump(const char *name, uint8_t width, const void *buf, uint16_t size)
{
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
uint16_t i, j;
uint16_t log_len = 0;
const uint8_t *buf_p = buf;
char dump_string[8] = {0};
int fmt_result;
if (!elog.output_enabled) {
return;
}
/* level filter */
if (ELOG_LVL_DEBUG > elog.filter.level) {
return;
} else if (!strstr(name, elog.filter.tag)) { /* tag filter */
return;
}
/* lock output */
elog_output_lock();
for (i = 0; i < size; i += width) {
/* package header */
fmt_result = snprintf(log_buf, ELOG_LINE_BUF_SIZE, "D/HEX %s: %04X-%04X: ", name, i, i + width - 1);
/* calculate log length */
if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) {
log_len = fmt_result;
} else {
log_len = ELOG_LINE_BUF_SIZE;
}
/* dump hex */
for (j = 0; j < width; j++) {
if (i + j < size) {
snprintf(dump_string, sizeof(dump_string), "%02X ", buf_p[i + j]);
} else {
strncpy(dump_string, " ", sizeof(dump_string));
}
log_len += elog_strcpy(log_len, log_buf + log_len, dump_string);
if ((j + 1) % 8 == 0) {
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
}
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
/* dump char for hex */
for (j = 0; j < width; j++) {
if (i + j < size) {
snprintf(dump_string, sizeof(dump_string), "%c", __is_print(buf_p[i + j]) ? buf_p[i + j] : '.');
log_len += elog_strcpy(log_len, log_buf + log_len, dump_string);
}
}
/* overflow check and reserve some space for newline sign */
if (log_len + strlen(ELOG_NEWLINE_SIGN) > ELOG_LINE_BUF_SIZE) {
log_len = ELOG_LINE_BUF_SIZE - strlen(ELOG_NEWLINE_SIGN);
}
/* package newline sign */
log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN);
/* do log output */
#if defined(ELOG_ASYNC_OUTPUT_ENABLE)
extern void elog_async_output(uint8_t level, const char *log, size_t size);
elog_async_output(ELOG_LVL_DEBUG, log_buf, log_len);
#elif defined(ELOG_BUF_OUTPUT_ENABLE)
extern void elog_buf_output(const char *log, size_t size);
elog_buf_output(log_buf, log_len);
#else
elog_port_output(log_buf, log_len);
#endif
}
/* unlock output */
elog_output_unlock();
}

View File

@@ -0,0 +1,79 @@
//
// Created by wangb on 25-6-4.
//
#include "elog.h"
#include <string.h>
/**
* another copy string function
*
* @param cur_len current copied log length, max size is ELOG_LINE_BUF_SIZE
* @param dst destination
* @param src source
*
* @return copied length
*/
size_t elog_strcpy(size_t cur_len, char *dst, const char *src) {
const char *src_old = src;
assert(dst);
assert(src);
while (*src != 0) {
/* make sure destination has enough space */
if (cur_len++ < ELOG_LINE_BUF_SIZE) {
*dst++ = *src++;
} else {
break;
}
}
return src - src_old;
}
/**
* Copy line log split by newline sign. It will copy all log when the newline sign isn't find.
*
* @param line line log buffer
* @param log origin log buffer
* @param len origin log buffer length
*
* @return copy size
*/
size_t elog_cpyln(char *line, const char *log, size_t len) {
size_t newline_len = strlen(ELOG_NEWLINE_SIGN), copy_size = 0;
assert(line);
assert(log);
while (len--) {
*line++ = *log++;
copy_size++;
if (copy_size >= newline_len && !strncmp(log - newline_len, ELOG_NEWLINE_SIGN, newline_len)) {
break;
}
}
return copy_size;
}
/**
* This function will copy memory content from source address to destination
* address.
*
* @param dst the address of destination memory
* @param src the address of source memory
* @param count the copied length
*
* @return the address of destination memory
*/
void *elog_memcpy(void *dst, const void *src, size_t count) {
char *tmp = (char *) dst, *s = (char *) src;
assert(dst);
assert(src);
while (count--)
*tmp++ = *s++;
return dst;
}