一个开源的AT命令解析模块

概述

        一种AT命令通信解析模块,支持裸机(at_chat)和OS版本(at)。适用于modem、WIFI模块、蓝牙通信。

软件架构

  • at_chat.c at_chat.h list.h

用于无OS版本,使用链式队列及异步回调方式处理AT命令收发,支持URC处理、自定义命令发送与解析作业。

  • at.c at.h at_util.h comdef.h

用于OS版本, 使用前需要根据at_util.h规定的操作系统相关的接口进行移植,如提供信号量操作、任务延时等操作。

使用说明

at_chat 模块(无OS)

基本概念

at_chat 模块使用链式队列进行管理,包含2条链表,空闲链表和就绪链表。它们的每一个基本工作单元称为一个作业项,对于将要执行的命令都会放到就绪链表中,命令执行完成之后由空闲链表来进行回收,作业项的定义如下:

/*AT作业项*/
typedef struct {
    unsigned int  state : 3;
    unsigned int  type  : 3;                                 /* 作业类型*/
    unsigned int  abort : 1; 
    void          *param;                                    /* 通用参数*/
	void          *info;                                     /* 通用信息指针*/
    struct list_head node;                                   /* 链表结点*/
}at_item_t;

作业是AT控制器定义时固定分配的,没有使用动态内存,默认支持10个作业项,即同时可以允许10个AT命令排队等待处理。

/...未完,待续/

基本接口与描述
  • at_send_singlline, 发送单行命令,默认等待OK响应,超时3S
  • at_send_multiline, 多行命令,默认等待OK响应,超时3S
  • at_do_cmd,支持自定义发送格式与接收匹配串
  • at_do_work,支持自定义发送与接收解析
效果演示

详细使用可以参考Demo程序wifi_task.c模块

使用步骤

1.定义AT控制器及通信适配器接口

/* 
 * @brief   定义AT控制器
 */
static at_obj_t at;

const at_adapter_t adap = {  //AT适配器接口
	//适配GPRS模块的串口读写接口
	.write       = uart_write,
	.read        = uart_read
	...
};
  1. 初始化AT控制器并放入任务中轮询(考虑到处理实时性,建议20ms以下)
/* 
 * @brief    wifi初始化
 */
void wifi_init(void)
{
	at_obj_init(&at, &adap);
	/*...*/
}driver_init("wifi", wifi_init); 

/* 
 * @brief    wifi任务(10ms 轮询1次)
 */
void wifi_task(void)
{
    at_poll_task(&at);
}task_register("wifi", wifi_task, 10);
例子演示
//WIFI IO配置命令
=> AT+GPIO_TEST_EN=1\r\n

<= OK\r\n
/**
 * @brief AT执行回调处理程序
 */
static void test_gpio_callback(at_response_t *r)
{
	if (r->ret == AT_RET_OK ) {
	    printf("Execute successfully\r\n");
	} else {
	    printf("Execute failure\r\n");
	}
}
at_send_singlline(&at, test_gpio_callback, "AT+GPIO_TEST_EN=1");

at 模块(OS版本)

由于AT命令通信是一个比较复杂的过程,对于没有OS的环境下处理难度比较大,也很绕,对于不允许阻塞程序,除了使用状态与+回调没有其它更好的办法,所以推荐使用这个模块

基本接口与描述
  • at_do_cmd,执行AT命令,可以通过这个接口进一步封装出一常用的单行命令、多行命令。
  • at_split_respond_lines,命令响应分割器。
  • at_do_work,适用于发送组合命令,如GPRS模组发送短信或者发送socket数据需要等待"<"或者"CONNECT"提示符,可以通过这个接口自定义收发。
案例演示

参考我的另外一个项目RIL(Radio Interface Layer)

使用步骤

1.定义AT控制器、通信适配器接口(包含URC回调函数表,接口缓冲区URC)

static at_obj_t at;          //定义AT控制器对象

static char urc_buf[128];    //URC主动上报缓冲区

utc_item_t utc_tbl[] = {     //定义URC表
	"+CSQ: ", csq_updated_handler
}

const at_adapter_t adap = {  //AT适配器接口
	.urc_buf     = urc_buf,
	.urc_bufsize = sizeof(urc_buf),
	.utc_tbl     = utc_tbl,
	.urc_tbl_count = sizeof(utc_tbl) / sizeof(utc_item_t),	
	//debug调试接口
	.debug       = at_debug, 
	//适配GPRS模块的串口读写接口
	.write       = uart_write,
	.read        = uart_read
};

2.创建AT控制器并创建轮询处理线程

void at_thread(void)
{
	at_obj_create(&at, &adap);
    while (1) {        
        at_process(&at);
    }
}
例子演示
例子1(查询无线模组信号质量)
/** at_do_cmd 接口使用演示
    查询GPRS模组信号质量命令
	=> AT+CSQ
	
	<= +CSQ: 24, 0
	<= OK
*/

/* 
 * @brief    获取csq值
 */ 
bool read_csq_value(at_obj_t *at, int *rssi, int *error_rate)
{
	//接收缓冲区
	unsigned char recvbuf[32];
	//AT应答
	at_respond_t r = {"OK", recvbuf, sizeof(recvbuf), 3000};
	//
	if (at_do_cmd(at, &r, "AT+CSQ") != AT_RET_OK)
		return false;
	//提取出响应数据
	return (sscanf(recv, "%*[^+]+CSQ: %d,%d", rssi, error_rate) == 2);

}
例子2(发送TCP数据)
/** at_do_work 接口使用演示
    参考自hl8518模组Socket 数据发送命令
	=> AT+KTCPSND=<session_id>,<ndata>
	
	<= CONNECT
	
	=> <data>
	
	<= OK
*/

/*
 * @brief       数据发送处理
 * @retval      none
 */
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--")); //发送结束符

    if (e->wait_resp(e, "OK", 5000) == AT_RET_OK)
        return true;
    else {   
Error:
        e->write("--EOF--Pattern--", strlen("--EOF--Pattern--"));
        return false;
    }
}

/**
 * @brief       socket 数据发送
 * @param[in]   s   - socket
 * @param[in]   buf - 数据缓冲区
 * @param[in]   len - 缓冲区长度
 */
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);
}

源码链接:AT Command: 一种AT命令通信解析模块,支持裸机(at_chat)和OS版本(at)。适用于modem、WIFI模块、蓝牙通信。

  • 29
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 以下是一个使用OpenCV DNN模块的C语言示例: ```c #include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> #include <iostream> using namespace cv; using namespace dnn; using namespace std; int main(int argc, char** argv) { // 加载模型和权重 String modelTxt = "path/to/model.txt"; String modelBin = "path/to/model.bin"; Net net = readNetFromCaffe(modelTxt, modelBin); // 加载图像 String imagePath = "path/to/image.jpg"; Mat image = imread(imagePath); // 预处理图像 Mat inputBlob = blobFromImage(image, 1.0, Size(300, 300), Scalar(104, 177, 123)); // 输入图像到网络中 net.setInput(inputBlob, "data"); // 运行前向传递 Mat detection = net.forward("detection_out"); // 解析输出 Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>()); for(int i = 0; i < detectionMat.rows; i++) { float confidence = detectionMat.at<float>(i, 2); if(confidence > 0.5) { int xLeftBottom = static_cast<int>(detectionMat.at<float>(i, 3) * image.cols); int yLeftBottom = static_cast<int>(detectionMat.at<float>(i, 4) * image.rows); int xRightTop = static_cast<int>(detectionMat.at<float>(i, 5) * image.cols); int yRightTop = static_cast<int>(detectionMat.at<float>(i, 6) * image.rows); // 绘制矩形框 rectangle(image, Point(xLeftBottom, yLeftBottom), Point(xRightTop, yRightTop), Scalar(0, 255, 0), 2); } } // 显示结果 imshow("detection result", image); waitKey(0); return 0; } ``` 此示例使用OpenCV DNN模块加载Caffe模型,对输入的图像进行检测,并将结果绘制在图像上。首先,我们需要加载模型和权重文件,然后加载要检测的图像。接下来,我们使用blobFromImage函数对图像进行预处理,然后将其输入到网络中进行前向传递。最后,我们解析输出结果并绘制检测结果的矩形框。 ### 回答2: OpenCV是一个开源的计算机视觉库,而OpenCV dnn模块则是其中的一个重要模块,用于深度神经网络的相关操作。下面我将给出一个使用OpenCV dnn模块进行目标检测的C语言示例。 ``` #include <opencv2/dnn.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <iostream> using namespace cv; using namespace cv::dnn; using namespace std; int main() { // 加载模型与相关参数 Net net = readNetFromDarknet("yolov3.cfg", "yolov3.weights"); vector<string> classNames; ifstream classNamesFile("coco.names"); string className; while (getline(classNamesFile, className)) { classNames.push_back(className); } // 加载图像 Mat image = imread("test.jpg"); if (image.empty()) { cerr << "无法读取图像文件!" << endl; return -1; } // 对图像进行目标检测 Mat blob = blobFromImage(image, 1 / 255.0, Size(416, 416), Scalar(0, 0, 0), true, false); net.setInput(blob); vector<Mat> outs; net.forward(outs); // 解析检测结果 float confidenceThreshold = 0.5; float nmsThreshold = 0.4; vector<int> classIds; vector<float> confidences; vector<Rect> boxes; for (size_t i = 0; i < outs.size(); ++i) { Mat detection = outs[i]; for (int j = 0; j < detection.rows; ++j) { Mat scores = detection.row(j).colRange(5, detection.cols); Point classIdPoint; double confidence; minMaxLoc(scores, 0, &confidence, 0, &classIdPoint); if (confidence > confidenceThreshold) { int centerX = (int)(detection.at<float>(j, 0) * image.cols); int centerY = (int)(detection.at<float>(j, 1) * image.rows); int width = (int)(detection.at<float>(j, 2) * image.cols); int height = (int)(detection.at<float>(j, 3) * image.rows); int left = centerX - width / 2; int top = centerY - height / 2; classIds.push_back(classIdPoint.x); confidences.push_back((float)confidence); boxes.push_back(Rect(left, top, width, height)); } } } // 对检测结果进行非最大值抑制 vector<int> indices; NMSBoxes(boxes, confidences, confidenceThreshold, nmsThreshold, indices); // 绘制检测框及类别标签 for (size_t i = 0; i < indices.size(); ++i) { int idx = indices[i]; Rect box = boxes[idx]; String label = classNames[classIds[idx]] + ": " + to_string(confidences[idx]); rectangle(image, box, Scalar(0, 0, 255), 2); putText(image, label, Point(box.x, box.y - 10), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 1); } // 显示结果 namedWindow("Object Detection", WINDOW_NORMAL); imshow("Object Detection", image); waitKey(0); return 0; } ``` 以上是一个使用OpenCV dnn模块进行目标检测的C语言例子。该例子首先加载了预训练的模型和相关参数,然后读取待检测的图像。接下来,通过设置输入并调用forward函数,使用模型对图像进行目标检测并得到检测结果。最后,根据检测结果,利用OpenCV提供的函数将检测框和类别标签绘制在图像上,并显示结果。 ### 回答3: 以下是一个使用OpenCV DNN模块的C语言示例: ```c #include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> #include <vector> using namespace cv; using namespace dnn; int main() { // 加载预训练的模型 String modelTxt = "path/to/model.txt"; String modelBin = "path/to/model.bin"; Net net = readNetFromDarknet(modelTxt, modelBin); // 加载输入图片 Mat image = imread("path/to/image.jpg"); // 图片预处理 Mat blob; blobFromImage(image, blob, 1.0, Size(416, 416), Scalar(), true, false); // 将blob输入到网络中 net.setInput(blob); // 进行目标检测 std::vector<Mat> outputs; std::vector<String> outputNames = net.getUnconnectedOutLayersNames(); net.forward(outputs, outputNames); // 处理输出结果 for (size_t i = 0; i < outputs.size(); ++i) { Mat output = outputs[i]; for (int j = 0; j < output.rows; ++j) { float* data = output.ptr<float>(j); for (int k = 0; k < output.cols; ++k) { // 处理输出数据,如绘制边界框等 // 示例:绘制边界框 if (data[k] > 0.5) { // 获取边界框的坐标 int x = data[k+1] * image.cols; int y = data[k+2] * image.rows; int width = data[k+3] * image.cols; int height = data[k+4] * image.rows; // 绘制边界框 rectangle(image, Point(x-width/2, y-height/2), Point(x+width/2, y+height/2), Scalar(0, 255, 0), 2); } } } } // 显示处理后的图片 imshow("Output", image); waitKey(0); return 0; } ``` 这个例子中,首先你需要使用`readNetFromDarknet`函数加载预训练的Darknet模型。然后,你使用`imread`函数加载待处理的图片。接下来,你需要对输入图片进行预处理,这里使用了`blobFromImage`函数。然后,你将预处理后的图片输入到网络中,使用`setInput`函数。之后,你可以通过调用`forward`函数进行目标检测,并将输出结果存储在一个`std::vector`中。最后,你可以根据输出结果对图片进行后处理,例如绘制边界框。最后,你使用`imshow`函数显示处理后的图片,在窗口中等待用户按下键盘。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ch_champion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值