【MQTT协议使用总结】基于-FreeRTOS平台-移植MQTT协议栈

news/2024/9/18 23:05:59 标签: 物联网

文章目录

  • 仓库地址
  • 关键接口适配
    • FreeRTOS_read
    • FreeRTOS_write
    • NetworkInit && NetworkConnect && NetworkDisconnect
  • 总结

仓库地址

https://github.com/eclipse/paho.mqtt.embedded-c

在这里插入图片描述
这里官方给了一些平台适配案例,这里参考FreeRTOS的

关键接口适配

使用使用了lwip的一些接口

FreeRTOS_read

int FreeRTOS_read(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
    // 将超时时间转换为 timeval 结构体表示的时间间隔
    struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};
    // 如果超时时间小于等于 0,将超时时间设置为 0.1 秒(100 微秒)
    if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 0)) {
        interval.tv_sec = 0;
        interval.tv_usec = 100;
    }

    fd_set read_fds;
    // 初始化文件描述符集合,清空 read_fds
    FD_ZERO(&read_fds);
    // 将网络结构体中的套接字加入到文件描述符集合中,表示关注该套接字的可读事件
    FD_SET(n->my_socket, &read_fds);

    int bytes = 0;
    while(bytes < len) {
        // 使用 lwip_select 等待套接字可读事件,设置超时时间为 interval
        int result = lwip_select(n->my_socket + 1, &read_fds, NULL, NULL, &interval);
        // 如果返回值小于等于 0,表示没有可读事件发生或者发生错误,跳出循环
        if (result <= 0)
            break;
        // 套接字可读
        int rc = lwip_recv(n->my_socket, &buffer[bytes], (len - bytes), 0);
        // 如果接收返回值为 -1,表示接收出错
        if (rc == -1) {
            // 如果错误不是 EAGAIN(资源暂时不可用)或 EWOULDBLOCK(操作会阻塞)
            if (errno!= EAGAIN && errno!= EWOULDBLOCK)
                bytes = -1;
            break;
        }
        // 如果接收返回值为 0,表示连接已关闭
        else if (rc == 0) {
            bytes = 0;
            break;
        }
        // 如果接收成功,更新已接收的字节数
        else
            bytes += rc;
    }

    return bytes;
}

FreeRTOS_write

int FreeRTOS_write(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
	TickType_t xTicksToWait = timeout_ms / portTICK_PERIOD_MS; /* convert milliseconds to ticks */
	TimeOut_t xTimeOut;
	int sentLen = 0;
	int flags = 0;

	vTaskSetTimeOutState(&xTimeOut); /* Record the time at which this function was entered. */
	do {
		int rc = 0;

		// 设置 lwip_send 为非阻塞方式
		flags = MSG_DONTWAIT;
		rc = lwip_send(n->my_socket, buffer + sentLen, len - sentLen, flags);
		if (rc > 0)
			sentLen += rc;
		else if (rc == 0 || (rc < 0 && errno == EAGAIN)) {
			// 缓冲区已满,等待一段时间再尝试发送
			vTaskDelay(pdMS_TO_TICKS(10)); // 延时10ms再尝试发送
		} else if (rc < 0) {
			sentLen = rc;
			break;
		}
	} while (sentLen < len && xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE);

	return sentLen;
}

NetworkInit && NetworkConnect && NetworkDisconnect

// 初始化网络结构体
void NetworkInit(Network* n)
{
    // 将套接字初始化为无效值
    n->my_socket = -1;

    // 设置网络结构体的读取函数指针为 FreeRTOS_read
    n->mqttread = FreeRTOS_read;
    // 设置网络结构体的写入函数指针为 FreeRTOS_write
    n->mqttwrite = FreeRTOS_write;
}

// 连接到指定地址和端口
int NetworkConnect(Network* n, char* addr, int port)
{
    // 打开连接并获取套接字描述符
    n->my_socket = transport_open((int8_t*)addr, port);

    // 如果套接字描述符有效(不等于 -1),表示连接成功
    if (-1!= n->my_socket) {
        return 0;
    }

    // 连接失败,返回 -1
    return -1;
}

// 断开网络连接
void NetworkDisconnect(Network* n)
{
    // 如果套接字描述符有效(大于 0)
    if (n->my_socket > 0 ) {
        // 关闭套接字
        closesocket(n->my_socket);
        // 将套接字描述符重置为无效值
        n->my_socket = -1;
    }
}
int32_t transport_open(int8_t* servip, int32_t port)
{
    int32_t *sock = &mysock;
    int32_t ret;
    int32_t opt;
    struct sockaddr_in addr;

    // 初始化服务器信息
    memset(&addr, 0, sizeof(addr));
    addr.sin_len = sizeof(addr);
    addr.sin_family = AF_INET;
    // 填写服务器端口号
    addr.sin_port = PP_HTONS(port);
    // 填写服务器 IP 地址
    addr.sin_addr.s_addr = inet_addr((const char*)servip);

    // 创建套接字
    *sock = socket(AF_INET, SOCK_STREAM, 0);
    // 连接服务器
    ret = connect(*sock, (struct sockaddr*)&addr, sizeof(addr));
    if(ret!= 0)
    {
        // 连接失败,关闭链接
        close(*sock);
        // 返回 -1 表示连接失败
        return -1;
    }
    // 连接成功,设置 TCP_NODELAY 选项(禁用 Nagle 算法)
    opt = 1;
    setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(int));

    // 返回套接字描述符
    return *sock;
}

总结

实际使用过程中,遇到了lwip阻塞的阻塞情况,注意采用非阻塞方式进行读取。


http://www.niftyadmin.cn/n/5664668.html

相关文章

1. 如何在Java中连接MySQL数据库?请解释使用JDBC连接的步骤。

要在Java中连接MySQL数据库&#xff0c;通常使用JDBC&#xff08;Java Database Connectivity&#xff09;API。这是一个用于执行SQL语句的Java API&#xff0c;可以用来访问关系型数据库。下面是使用JDBC连接MySQL数据库的详细步骤&#xff1a; 1. 添加MySQL JDBC驱动 首先&a…

python函数一:函数的概念、函数定义与调用、函数的参数、函数的返回值、说明文档以及函数的嵌套调用

文章目录 1. 函数介绍1.1 函数的概念1.2 函数定义与调用1.2 函数的参数1.3 函数的返回值1.4 说明文档 2. 函数的嵌套调用2.1 嵌套调用及执行流程2.2 嵌套调用的应用 1. 函数介绍 1.1 函数的概念 什么是函数&#xff1f; 函数:是一个被命名的、独立的、完成特定功能的代码段&am…

设计模式学习[6]---代理模式

文章目录 前言1.原理阐述2.举例2.1 例子与类图2.2 代码 总结 前言 代理这个词&#xff0c;从小到大听过不少。比如什么代理服务器&#xff0c;代理商&#xff0c;代理人之类的。通俗来说&#xff0c;代理无非无非就是我代表你处理事务的意思。 那么在设计模式中&#xff0c;针…

Java 中的 FileWriter:用法详解与实战

FileWriter 是专门用于将字符写入文件的便捷类。它继承自 OutputStreamWriter&#xff0c;属于字符流类的一个部分&#xff0c;适合用于处理文本文件的写入操作。 1. FileWriter 概述 FileWriter 主要用于将字符数据写入到文件中。如果文件不存在&#xff0c;它会自动创建&…

四、JVM原理-4.1、JVM介绍

4.1、JVM介绍 4.1.1、如何理解Java虚拟机&#xff0c;它的结构是如何设计的&#xff1f; 答&#xff1a; Java虚拟机&#xff08;Java Virtual Machine&#xff0c;JVM&#xff09;是Java语言编写的程序在运行时的执行环境。它是Java的核心组成部分&#xff0c;负责解释和执行…

沉浸式体验Stability AI最新超强AI图片生成模型Ultra

2024年9月4日&#xff0c;亚马逊云科技在Amazon Bedrock上新了Stability AI最新的的三款文本图像生成模型&#xff1a;他们分别是Stable Image Ultra、Stable Diffusion 3 Large 和 Stable Image Core。全新的模型在处理多主题提示词、图像质量和图片排版上较上一代模型有显著提…

一个基于 laravel 和 amis 开发的后台框架, 友好的组件使用体验,可轻松实现复杂页面(附源码)

前言 随着互联网应用的发展&#xff0c;后台管理系统的复杂度不断增加&#xff0c;对于开发者而言&#xff0c;既要系统的功能完备&#xff0c;又要追求开发效率的提升。然而&#xff0c;传统的开发方式往往会导致大量的重复劳动&#xff0c;尤其是在构建复杂的管理页面时。有…

多目标优化算法(Multi-Objective Optimization Algorithms, MOOA)介绍

在现实世界中&#xff0c;许多问题都涉及到多个目标的权衡和优化。例如&#xff0c;在工程设计中&#xff0c;可能需要同时考虑成本、效率和可靠性&#xff1b;在资源管理中&#xff0c;可能需要平衡环境保护和经济效益。多目标优化算法&#xff08;Multi-Objective Optimizati…