STM32 FreeRTOS + LwIP 集成实践:基于 MQTT 的通信示例 - 实践

news/2025/9/22 9:57:43/文章来源:https://www.cnblogs.com/yfceshi/p/19104621

1. LWIP简介

一个 轻量级 TCP/IP 协议栈搭建,最初由 Adam Dunkels 在瑞典计算机科学研究所(CNA 实验室)编写,目前由 Simon Goldschmidt 和 Dirk Ziegelmeier 领导的全球编写团队持续维护就是LWIP(Lightweight IP)

1.1 重要特点

  • 轻量级设计:优化 RAM 占用(可在几十 KB 内存中运行),代码占用约 40 KB ROM。
  • 开源免费:采用 BSD 风格许可,C 源码形式提供。
  • 多平台承受:可移植到多种 MCU、嵌入式平台,也可在有或无操作系统的环境下运行。
  • 协议完整:支持 IP、ICMP、IGMP、UDP(含 UDP-lite)、TCP(含拥塞控制、RTT 估算、快速恢复/快速重传)、ARP、PPP 等。
  • 网络服务帮助:可选 DNS、DHCP、AUTOIP、SNMP 等功能。
  • 接口灵活:供应原生 Raw API、可选 Berkeley-style Socket API,兼顾性能和易用性。

1.2 应用场景

  • IoT 设备(智能家居、传感器网关)
  • 工业控制系统
  • 网络摄像头、音视频传输设备
  • 车载信息娱乐系统

2. 在 FreeRTOS 中集成 LwIP 并完成 MQTT 通信

2.1 工具准备

  • MQTTX:桌面版 MQTT 客户端,操作方便,适合测试。
  • MQTT Broker:使用 broker.emqx.io (EMQX 团队 提供的一个 公共 MQTT Broker 服务,主要用于学习、测试和验证 MQTT 协议的功能)。

2.2 目标

经过 STM32 开发板发送 MQTT 消息到 Broker,并使用桌面客户端接收消息。

2.3 工程配置

1. 开始参考这篇博文创建一个 FreeRTOS 工程:STM32 FreeRTOS工程创建

在此基础上进行以下修改:

  • 接口选择

    • 原工程接口为 CMSIS_V2,这里改为 CMSIS_V1。
    • 测试发现,在 STM32F746 板子上选择 CMSIS_V1 时,LwIP 初始化才能正常运行。
  • 启用 Counting Semaphore

    • 打开 USE_COUNTING_SEMAPHORES,后续在代码中配置信号量时应该用到。

2.启用LWIP。

  • 网络配置

    • DHCP 默认可保持启用,这里为了测试网络连接,改用静态地址。
    • 启用DNS模块。
    • 配置 Ethernet PHY 驱动。

3. 配置 Ethernet 接口(参考开发板手册)。

4. 生成工程,并在 STM32CubeIDE 中打开。

2.4 添加MQTT任务

1. 在 main() 中删除自动生成的任务,改为调用 mqtt_setup()。

#include "lwip.h"
#include "mqtt.h"
#define HOST  "broker.emqx.io"              // MQTT 服务器域名
#define PORT  1883                          // MQTT 服务器端口
#define TOPIC "stm32f746g-disco/mqtt/test"  // 发布的主题
#define SIGNAL_CONNECTED 0x01               // MQTT 连接成功信号
#define SIGNAL_HOST_RESOLVED 0x02           // DNS 解析完成信号
// 定义并声明一个信号量,用于发布消息控制
osSemaphoreDef (publish_semaphore);
osSemaphoreId  (publish_semaphore_id);
static osThreadId mqtt_thread_id;
static ip_addr_t host_ip;
// MQTT 任务入口函数
void start_mqtt_task(void *argument);
// DNS 解析完成后的回调函数
void dns_found_callback(const char *name, ip_addr_t *ipaddr, void *arg)
{
if (ipaddr)
{
// 保存解析到的 IP 地址
host_ip = *ipaddr;
// 发送 DNS 解析完成信号,唤醒 MQTT 线程
osSignalSet(mqtt_thread_id, SIGNAL_HOST_RESOLVED);
}
}
// 创建 MQTT 任务
void mqtt_setup(){
osThreadDef(mqtt_task, start_mqtt_task, osPriorityNormal, 0, 1025);
mqtt_thread_id = osThreadCreate(osThread(mqtt_task), NULL);
}
// MQTT 连接回调函数
static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
if(status == MQTT_CONNECT_ACCEPTED) {
// 连接成功,发送信号通知任务
osSignalSet(mqtt_thread_id, SIGNAL_CONNECTED);
}
}
/* 发布消息完成后的回调(成功或失败都会调用) */
static void mqtt_pub_request_cb(void *arg, err_t result)
{
// 释放信号量,允许下一条消息发布
osSemaphoreRelease(publish_semaphore_id);
}
// MQTT 任务主体函数
void start_mqtt_task(void *argument)
{
/* 初始化 LwIP 网络协议栈 */
MX_LWIP_Init();
ip_addr_t dnsserver;
// 设置 DNS 服务器为 8.8.8.8
IP4_ADDR(&dnsserver,8,8,8,8);
dns_setserver(0, &dnsserver);
// 异步解析 MQTT 服务器域名
err_t result = dns_gethostbyname(HOST, &host_ip, dns_found_callback, NULL);
if(result == ERR_OK){
// 主机名已存在于本地缓存,解析立即成功
} else if (result == ERR_INPROGRESS){
// 向 DNS 服务器发送请求,等待解析完成信号
osSignalWait(SIGNAL_HOST_RESOLVED, osWaitForever);
} else{
// 解析失败,直接返回
return;
}
// 创建 MQTT 客户端对象
mqtt_client_t *client = mqtt_client_new();
// 配置 MQTT 客户端连接信息
struct mqtt_connect_client_info_t ci;
memset(&ci, 0, sizeof(ci));
ci.client_id = "lwip_mqtt_test"; // 客户端 ID
// 创建发布信号量,控制同时发布的消息数
publish_semaphore_id = osSemaphoreCreate(osSemaphore(publish_semaphore), MQTT_REQ_MAX_IN_FLIGHT);
// 建立与 MQTT 服务器的连接
mqtt_client_connect(client, &host_ip, PORT, mqtt_connection_cb, NULL, &ci);
char payload[100];
u8_t qos = 1; /* 0 1 or 2, see MQTT specification */
u8_t retain = 0; /* No don't retain such crappy payload... */
// 等待mqtt连接建立
osSignalWait(SIGNAL_CONNECTED, osWaitForever);
// 无限循环,持续发布消息
for(int i = 0; ;i++)
{
osSemaphoreWait(publish_semaphore_id, osWaitForever);
sprintf(payload, "Hello Woirld %d", i);
mqtt_publish(client, TOPIC, payload, strlen(payload), qos, retain, mqtt_pub_request_cb, NULL);
}
}

2. 整体逻辑概括

  • 初始化网络栈 (LWIP)

调用 MX_LWIP_Init(),让 STM32 的网络功能(以太网 + TCP/IP 协议栈)启动。

  • DNS 域名解析

    • 使用 dns_gethostbyname() 异步解析 broker.emqx.io 域名。
    • 假如立即解析成功 (ERR_OK),直接得到 IP 地址。
    • 如果需要等待 (ERR_INPROGRESS),就阻塞在 osSignalWait(SIGNAL_HOST_RESOLVED, osWaitForever),等回调函数 - dns_found_callback 通知完毕。
  • MQTT 客户端连接

    • 创建 MQTT 客户端 mqtt_client_new()。
    • 配置客户端信息(如 client_id)。
    • 调用 mqtt_client_connect() 发起连接,成功后由 mqtt_connection_cb() 回调函数通知。
    • 任务里会等待 SIGNAL_CONNECTED 信号,确保连接成功后才进入发布循环。
  • 发布消息循环

    • 创建信号量 publish_semaphore,用于控制消息发布并发数量。
    • 每次发布消息前,先 osSemaphoreWait()(等待可用 slot)。
    • 调用 mqtt_publish() 发送消息。
    • 消息发布结束后,回调函数 mqtt_pub_request_cb() 释放信号量 (osSemaphoreRelease()),允许下一条消息发送。
    • 循环构造 "Hello World X" 消息并持续发布。

3. 关键同步机制

  • 信号 (osSignal)

    • SIGNAL_HOST_RESOLVED:DNS 解析做完后由回调设置 → 唤醒 MQTT 线程继续执行。
    • SIGNAL_CONNECTED:MQTT 连接建立成功时设置 → 唤醒 MQTT 线程开始发布消息。
  • 信号量 (osSemaphore)

    • 控制并发的 MQTT 消息发布数(受 MQTT_REQ_MAX_IN_FLIGHT 限制)。
    • 每次发布需要先 wait,完成后在回调里 release。
    • 避免发送过快、超过协议栈处理能力。

2.5 采用MQTTX测试

  1. 连接到 Broker。

  2. 创建订阅 

  3. 设置订阅主题和 QoS.

  4. 将工程下载到开发板运行后,可在 MQTTX 中看到发布的消息:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/909236.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数分3

回忆: \[\]

基于模拟退火算法解决带容量限制车辆路径问题(CVRP)的MATLAB实现

一、问题建模 1.1 数学描述 目标函数:其中:\(R_k\):第k条路径的边集合 \(dij\):节点i到j的距离 \(K\):使用车辆数 \(λ\):车辆使用惩罚系数约束条件:每个客户仅被访问一次 车辆从仓库出发并返回 路径载重不超过…

完整教程:分片后的聚合分页处理

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

深入解析:HDR 动态元数据生成:场景自适应与质检脚本

深入解析:HDR 动态元数据生成:场景自适应与质检脚本pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&qu…

CSS-渐变

CSS渐变:<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> *{margin:…

H3C交换机取消分页,H3C交换机关闭分页功能

H3C交换机取消分页,H3C交换机关闭分页功能默认H3C交换机有分页显示功能,就是下面的MORE,如下图 关闭分页:system-view //进入全局模式user-interface vty 0 4 //进入用户界面,vty ,全称为Virtual Teletype Termi…

Codeforces Round 1052 (Div. 2) E. Yet Another MEX Problem

Codeforces Round 1052 (Div. 2) E. Yet Another MEX Problem https://codeforces.com/contest/2146/problem/E 场上秒了却没时间做的题目。 题意 \(\huge w(l,r) = \sum_{i \in [l,r]} [b_i > mex(l,r)]\)。 \(\la…

基于Python+Vue开发的美容预约管理系统源码+运行步骤

项目简介该项目是基于Python+Vue开发的美容预约管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Python编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于…

马大姐携手纷享销客启动CRM,打造快消行业数字化新标杆

作为休闲食品行业的持续领跑者,马大姐凭借传统酥糖、酒心巧克力等产品承载着中国传世技艺文化,始终以缔造品质上乘、健康美味为理念。近日,其 CRM 项目启动会成功举行。马大姐集团执行总裁苗盈晖、副总裁马立永、信…

9.21 判断推理 6/10

9.21 判断推理 6/10图形推理这个算是组成相同,但是没有位置规律,所以考虑数量, 算直线占的格子数,都是8个,所以只有c是8个图形推理这算相似,考虑加减和黑白运算,这是黑白运算 黑+黑=黑 白+白=黑 黑+白=白 所以…

利用MCMC方法产生平稳的马尔科夫链

马尔科夫链蒙特卡洛(MCMC)方法是一种通过构建平稳马尔科夫链来从复杂概率分布中采样的强大技术。 % 利用MCMC方法产生平稳的马尔科夫链 clear; clc; close all;%% 1. 参数设置 fprintf(设置MCMC参数...\n);% MCMC参数 …

FDS-400 土壤温湿电导率盐分传感器 四合一款 频域法测量

FDS-400 土壤温湿电导率盐分传感器 四合一款 频域法测量产品概述 土壤温度部分是由精密铂电阻和高精度变送器两部分组成。变送器部分由电源模块、温度传感模块、变送模块、温度补偿模块及数据处理模块等组成,彻底解…

接口压测方案

项目名称: 版本号: 编写人: 审核人: 日期:测试目标验证系统在预期用户并发量下的响应时间是否达标 评估系统在峰值负载下的稳定性 识别系统性能瓶颈(CPU、内存、数据库、网络等) 为系统优化提供数据支持 吞吐量…

pc.vivo.com vivo办公套件网页,拼图验证失败的原因

chrome 扩展程序里,找到 Phantom 禁用掉,就能正常登录了!本文来自博客园,作者:imzhi,转载请注明原文链接:https://www.cnblogs.com/imzhi/p/19104572

产业投资集团如何科学选择HR系统?一文详解5大选型维度与主流产品对比

【精选摘要】在多元控参、高度分层的产业投资集团中,如何科学选型HR系统,成为数字化转型的核心议题。本文聚焦集团化管理痛点,系统梳理选型关键维度,详解SAP、Workday、红海云、金蝶、用友五大主流产品在实际业务场…

J-link RTT 助手,串口助手,数据可视化,波形图显示,多多盒子

非常好用的一个软件 J-link RTT 助手:串口助手:下载地址: https://gitee.com/momingchuan/duo-duo-box

No.72 阿里图标库的使用

一、打开官网 https://www.iconfont.cn/ 二. 添加到购物车 三.下载代码,解压四.引用 打开demo_index.html文件

python处理Excel的单机小工具:自动合并相同数据的行, 并同时计算其他列的加和

2025-09-22 场景: 一个表格, 比如有 序号, 姓名, 班级, 考分 几列, 要求: 1. 要按照班级合并, 相同的班级的行合并在一起 2. 序号这一列也同时合并 3. 合并后, 计算每个班级的总考分 原始表:处理后的表:软件交互: 1: 选…

297、瑶瑟怨

297、瑶瑟怨 297、瑶瑟怨 唐●温庭筠 冰簟银床梦不成,碧天如水夜云轻。 雁声远过潇湘去,十二楼中月自明。【现代诗意译】 月光下 铺着清凉竹席的床上 我难以入眠 团圆美梦也做不成了 天空碧蓝如水 飘着几片薄薄夜云一…

极飞科技携手纷享销客CRM实现业务全链条数字化

极飞科技股份有限公司(以下简称“极飞科技”)是全球领先的农业机器人公司,致力于用机器人、人工智能和新能源技术赋能农业。随着业务持续拓展与战略升级,极飞客户面临线索管理分散、多系统数据孤岛等挑战。为进一步…