实现树莓派与上位机进行mqtt转tcp通信

本文详细介绍了如何在树莓派上通过paho库建立MQTT客户端,并编写mqtt转tcp的网关程序,连接到Linux系统。同时,利用Windows下的网络调试助手模拟TCP上位机,进行通信测试,确保数据在MQTT与TCP之间顺利转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.连接树莓派

配置新的树莓派请参考

刚拿到树莓派,该怎么使用(折腾)呢?_树莓派拿到后做什么-优快云博客

本人连接树莓派用的NVC可以显示图形化界面,下载VNC

链接: https://pan.baidu.com/s/1IILrcjdCIErBwDWdcv3lPg?pwd=tb63 提取码: tb63

跟着指导一步一步next即可

安装好后点击File--》new connect

(若遇到登录,点击取消即可)

点击它,输入账号密码

连接成功后即可操作树莓派可视化界面

2.在树莓派安装paho的库

因为mqtt需要用paho的库,所以要安装库

在windows下下载该文件并解压

链接: https://pan.baidu.com/s/1sTmDhjWqQrSN2A3DdgOtnw?pwd=z123 提取码: z123

因为windows不可以直接拖动文件到树莓派中,要下载WINscp软件

链接: https://pan.baidu.com/s/1EEkpGfmXh24MYEAp5flHwg?pwd=xegm 提取码: xegm

按步骤安装后,连接树莓派

连接成功后可以将解压的paho.mqtt.c-1.3.0文件复制或直接拖到树莓派桌面上

在桌面创建mqtt文件夹

在该文件夹里创建文件mqtt.c以及makefile文件

将复制过来的paho.mqtt.c-1.3.0文件放到mqtt文件夹中

进入到该文件里,右击打开终端

输入以下命令并回车进行编译

make

编译paho.mqtt.c-1.3.0若提示 openssl 错误解决方法

树莓派(Raspberry Pi)安装使用 Eclipse paho C库_树莓派 安装 matt paho c-优快云博客

之后进行安装

sudo make uninstall(去掉前面有错的安装,之前未安装可不加)

sudo make install(重新安装)

3.在树莓派编写mqtt客户端代码

mqtt.c中代码如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
//以下为库头文件的引入注意路径别写错
#include "./paho.mqtt.c-1.3.0/paho.mqtt.c-1.3.0/src/MQTTClient.h"

#define ALIYUN_MQTT_HOST      "tcp://xxx.xxx.xxx.xxx:1883"//你的mqtt代理服务器的ip
#define ALIYUN_MQTT_CLIENT_ID ""
#define ALIYUN_MQTT_USERNAME  ""
#define ALIYUN_MQTT_PASSWORD  ""
#define TOPIC_S               "up"//订阅话题
#define TOPIC_P               "down"//发布话题

#define QOS         1
char buf[1024]={0};

void connection_lost(void *context, char *cause) {
    printf("\nConnection lost\n");
    printf("     cause: %s\n", cause);
}
 
int messageArrived(void *context, char *topicName, int topicLen, MQTTClient_message *message) {
    printf("Message arrived\n");
    strcpy(buf,message->payload);
    printf("%s\n",buf);
    return 1;
}


int main(int argc, char *argv[]) {
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;
    int rc;
 
    MQTTClient_create(&client, ALIYUN_MQTT_HOST, ALIYUN_MQTT_CLIENT_ID,
        MQTTCLIENT_PERSISTENCE_NONE, NULL);
 
    MQTTClient_setCallbacks(client, NULL, connection_lost, messageArrived, NULL);
 
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    conn_opts.username = ALIYUN_MQTT_USERNAME;
    conn_opts.password = ALIYUN_MQTT_PASSWORD;
 
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
    MQTTClient_subscribe(client, TOPIC_S, QOS);

    while (1)
    {
        scanf("%s",buf);
        pubmsg.payload = buf;
        pubmsg.payloadlen = strlen((char *)pubmsg.payload);
        pubmsg.qos = QOS;
        pubmsg.retained = 0;
        MQTTClient_publishMessage(client, TOPIC_P, &pubmsg, &token);
        MQTTClient_waitForCompletion(client, token, 10000L);
        printf("Message sent\n"); 
    }
    MQTTClient_unsubscribe(client, TOPIC_S);
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);

    return rc;
}

makefile文件代码

#指定生成的文件名
OJB_OUT = mqtt.out

#指定每一个c文件对应的.o文件
OBJS =mqtt.o

#指定编译器
CC = gcc

#指定需要的库
ULDFLAGS = -l m -l paho-mqtt3c

###########################################
#以下的内容不需要修改
###########################################
all:$(OJB_OUT)

$(OJB_OUT):$(OBJS)
	$(CC) -o $@ $^ $(ULDFLAGS)

dep_files := $(foreach f,$(OBJS),.$(f).d)
dep_files := $(wildcard $(dep_files))

ifneq ($(dep_files),)
  include $(dep_files)
endif

%.o:%.c
	$(CC) -Wp,-MD,.$@.d -c $< -o $@
    
clean:
	rm -rf .*.o.d *.o $(OJB_OUT)

之后右击mqtt文件在终端打开

输入以下命令并回车进行编译

make

之后会生成这几个文件,其中的mqtt.out即为生成的可执行文件

然后运行可执行文件即可启动mqtt客户端

./mqtt.out

4.在linux下编写mqtt转tcp的网关程序

mqtt_change.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include "MQTTClient.h"
 
//阿里云
#define ALIYUN_MQTT_HOST      "tcp://xxx.xxx.xxx.xxx:1883"//你的mqtt代理服务器的ip
#define ALIYUN_MQTT_CLIENT_ID ""//设备id
#define ALIYUN_MQTT_USERNAME  ""//用户名
#define ALIYUN_MQTT_PASSWORD  ""//密码
#define TOPIC_S               "down"
#define TOPIC_P               "up"

#define QOS         1
char buf[1024]={0};//数据包
int flagA = 0;//标志位:MQTT收到消息为1,TCP发送后为0
int flagB = 0;//标志位:TCP收到消息为1,MQTT发送后为0
int flagC = 0;//标志位:客户端连接为0,退出为1
int acceptfd;
void connection_lost(void *context, char *cause) {
    printf("\nConnection lost\n");
    printf("     cause: %s\n", cause);
}
 
int messageArrived(void *context, char *topicName, int topicLen, MQTTClient_message *message) {
    printf("Message arrived\n");
    strcpy(buf,message->payload);
    flagA = 1;
    return 1;
}
//线程函数--》处理tcp的发
void * fun_tcp_send(void *arg)
{
    while (1)
    {
        if(flagA == 1)
        {
            send(acceptfd, buf, strlen(buf), 0);
            flagA = 0;
            printf("tcp send is ok\n");
        }
        if(flagC == 1)
        {
            break;
        }
    }
     
}
//线程函数--》处理tcp的收发
void * fun_tcp(void *arg)
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        //return;
    }
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(8887);
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    socklen_t len = sizeof(saddr);
    if (bind(sockfd, (struct sockaddr *)&saddr, len))
    {
        perror("bind err");
        //return;
    }
    if (listen(sockfd, 6) < 0)
    {
        perror("listen err");
        //return;
    }
    printf("listen success\n");
    struct sockaddr_in aaddr;
    while (1)
    {
        acceptfd = accept(sockfd, (struct sockaddr *)&aaddr, &len);
        if (acceptfd < 0)
        {
            perror("accept err");
            //return -1;
        }
        flagC = 0;
        printf("accept success\n");
        //来电显示:打印客户端的ip和port
        printf("ip:%s port:%d\n",inet_ntoa(aaddr.sin_addr),ntohs(aaddr.sin_port));
        
        //创建线程,发tcp的消息
        pthread_t tid2;
        if(pthread_create(&tid2,NULL,fun_tcp_send,NULL))
        {
            perror("create err");
            //return;
        }
        //收消息
        while (1)
        {
            //接收数据
            ssize_t ret = recv(acceptfd, buf, sizeof(buf), 0);
            if (ret < 0)
            {
                perror("recv err");
                //return;
            }
            else if (ret == 0)
            {
                printf("client exit\n");
                flagC = 1;
                close(acceptfd);
                break;
            }
            else
            {
                flagB = 1;//tcp收到消息
                printf("%s\n",buf);
                printf("%d\n",buf[0]);
                printf("tcp recv is ok\n");
            }
        }
        pthread_join(tid2,NULL);
    }
    close(sockfd);
}

int main(int argc, char *argv[]) {
    //创建线程,收发tcp的消息
    pthread_t tid;
	if(pthread_create(&tid,NULL,fun_tcp,NULL))
	{
		perror("create err");
		return -1;
	}

    //mqtt
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;//分发标记
    int rc;
 
    MQTTClient_create(&client, ALIYUN_MQTT_HOST, ALIYUN_MQTT_CLIENT_ID,
        MQTTCLIENT_PERSISTENCE_NONE, NULL);
 
    MQTTClient_setCallbacks(client, NULL, connection_lost, messageArrived, NULL);
 
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    conn_opts.username = ALIYUN_MQTT_USERNAME;
    conn_opts.password = ALIYUN_MQTT_PASSWORD;
 
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
     //订阅主题
    MQTTClient_subscribe(client, TOPIC_S, QOS);

    while (1)
    {
        if(flagB == 1)
        {
            pubmsg.payload = buf;
            pubmsg.payloadlen = strlen((char *)pubmsg.payload);
            pubmsg.qos = QOS;
            pubmsg.retained = 0;
            //发布主题
            MQTTClient_publishMessage(client, TOPIC_P, &pubmsg, &token);

            MQTTClient_waitForCompletion(client, token, 10000L);
            printf("Message sent\n"); 
            flagB = 0;
        }  
    }
    MQTTClient_unsubscribe(client, TOPIC_S);
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    pthread_join(tid,NULL);
    return rc;
}

makefile

#指定生成的文件名
OJB_OUT = mqtt_change.out

#指定每一个c文件对应的.o文件
OBJS = cJSON.o mqtt_change.o

#指定编译器
CC = gcc

#指定需要的库
ULDFLAGS = -l m -l paho-mqtt3c -pthread

###########################################
#以下的内容不需要修改
###########################################
all:$(OJB_OUT)

$(OJB_OUT):$(OBJS)
	$(CC) -o $@ $^ $(ULDFLAGS)

dep_files := $(foreach f,$(OBJS),.$(f).d)
dep_files := $(wildcard $(dep_files))

ifneq ($(dep_files),)
  include $(dep_files)
endif

%.o:%.c
	$(CC) -Wp,-MD,.$@.d -c $< -o $@
    
clean:
	rm -rf .*.o.d *.o $(OJB_OUT)

5.用windows下的网络调试助手模拟只能接收TCP消息的上位机

链接: https://pan.baidu.com/s/1mcjA6CK_2s67cOgv7TFZpQ?pwd=2tih 提取码: 2tih

地址你的linuxip--可用ifconfig查看

端口号自己在代码中设定的8887可以自己

6.通信测试

运行linux下网关程序

运行树莓派下mqtt客户端

打开网络调试助手进行tcp连接

收发消息进行测试

可以看到树莓派一直在上位机“hello”上位机也可以树莓发送1111”

注意:本文中使用的mqtt代理服务器为阿里云服务器中配置的,具体操作请看本人的第一个文章

<think>好的,我现在需要帮助用户获取关于单片机树莓派之间串口通信,以及树莓派上位机(电脑)通过网络通信的详细步骤和Python代码示例。首先,我需要理清用户的需求,明确他们需要的是具体的步骤和代码,可能还需要解释每个步骤的作用和配置方法。 首先,关于单片机树莓派的串口通信。根据引用[2]和[3],树莓派可以通过串口外部设备如单片机(如STM32通信。用户可能需要了解如何配置树莓派的串口,以及如何编写Python代码进行数据收发。同时,引用[3]提到类似OpenMVSTM32通信方式,可能需要参考已有的方法,但用户需要更详细的步骤。 接下来是树莓派上位机通过网络通信。引用[1]提到Python可以使用socket和pyserial库进行网络通信,这里可能需要使用socket库建立TCP或UDP连接,实现树莓派和电脑之间的数据传输。用户需要具体的代码示例,比如服务器端和客户端的代码,以及如何整合这两个部分。 接下来,我需要考虑用户可能的背景知识。他们可能已经具备一定的单片机开发经验,但对树莓派的串口配置和Python编程不太熟悉。因此,步骤需要详细,包括硬件连接、树莓派串口配置、代码示例,并解释关键点。 对于串口通信部分,树莓派默认的串口可能被蓝牙占用,需要禁用蓝牙并启用UART。这可能涉及到修改config.txt文件和使用raspi-config工具。然后,使用Python的pyserial库编写代码,打开串口,发送和接收数据。需要确保波特率等参数单片机一致。 对于网络通信部分,可能需要使用socket库,树莓派作为服务器或客户端,电脑作为另一端。例如,树莓派运行TCP服务器代码,电脑运行客户端代码进行连接和数据传输。需要示例代码展示如何建立连接、发送和接收数据。 另外,需要考虑数据的中过程,即树莓派如何将从单片机接收到的数据通过网络发送给上位机,或者反过来。可能需要整合串口和网络通信的代码,使用多线程或异步处理来同时处理串口和网络的数据流。 可能的注意事项包括权限问题(如树莓派串口需要用户加入dialout组),波特率匹配,正确的引脚连接,以及网络通信中的IP地址和端口设置是否正确。还需要处理可能的错误,如连接失败、数据格式错误等。 最后,需要确保提供的代码示例正确,并且有详细的注释,方便用户理解和修改。同时,引用相关的参考资料,如用户提供的引用[1]、[2]、[3],确保信息的准确性。</think>### 单片机树莓派串口通信 + 树莓派上位机网络通信指南 #### 一、硬件连接配置 1. **硬件连接** - 树莓派单片机(如STM32)通过**UART引脚**连接: - 树莓派引脚:`TXD (GPIO14)` -> 单片机`RX` - 树莓派引脚:`RXD (GPIO15)` -> 单片机`TX` - 共地连接:树莓派`GND` -> 单片机`GND`[^2] 2. **树莓派串口配置** ```bash # 禁用蓝牙占用串口 sudo nano /boot/config.txt # 添加以下两行 dtoverlay=disable-bt enable_uart=1 # 重启生效 sudo reboot # 验证串口设备是否存在 ls /dev/ttyAMA0 # 安装串口工具 sudo apt-get install python3-serial ``` --- #### 二、Python代码实现 ##### 1. 树莓派单片机串口通信 ```python # 文件名:raspi_serial.py import serial import time # 配置串口参数(单片机一致) ser = serial.Serial( port='/dev/ttyAMA0', # 树莓派串口设备 baudrate=115200, # 波特率需单片机匹配 timeout=1 # 读取超时时间 ) try: while True: # 发送数据给单片机 ser.write(b'Hello STM32\n') # 接收单片机数据 if ser.in_waiting > 0: data = ser.readline().decode('utf-8').strip() print(f"Received from STM32: {data}") time.sleep(1) except KeyboardInterrupt: ser.close() ``` ##### 2. 树莓派上位机网络通信TCP协议) **树莓派端(TCP服务器)** ```python # 文件名:raspi_tcp_server.py import socket HOST = '0.0.0.0' # 监听所有网络接口 PORT = 65432 # 端口号 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((HOST, PORT)) s.listen() conn, addr = s.accept() with conn: print(f"Connected by {addr}") while True: data = conn.recv(1024) if not data: break print(f"Received from PC: {data.decode()}") # 发给单片机(需集成串口代码) # ser.write(data) ``` **上位机端(TCP客户端)** ```python # 文件名:pc_tcp_client.py import socket HOST = '树莓派IP地址' # 替换为实际IP PORT = 65432 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((HOST, PORT)) s.sendall(b'Hello Raspberry Pi') ``` --- #### 三、数据中整合 将串口网络通信结合,实现树莓派作为**数据中站**: ```python # 文件名:raspi_bridge.py import serial import socket from threading import Thread # 串口配置 ser = serial.Serial('/dev/ttyAMA0', 115200) # TCP服务器配置 HOST, PORT = '0.0.0.0', 65432 def handle_tcp(client_socket): while True: data = client_socket.recv(1024) if data: ser.write(data) # 网络数据发给单片机 def handle_serial(): while True: if ser.in_waiting > 0: data = ser.readline() # 此处可添加网络广播逻辑 if __name__ == "__main__": Thread(target=handle_serial).start() with socket.socket() as s: s.bind((HOST, PORT)) s.listen() while True: conn, addr = s.accept() Thread(target=handle_tcp, args=(conn,)).start() ``` --- #### 四、关键注意事项 1. **权限问题** ```bash sudo usermod -aG dialout $USER # 将用户加入串口组 ``` 2. **波特率一致性**:单片机树莓派代码需设置相同波特率(如115200) 3. **网络配置**:确保树莓派上位机在同一局域网,关闭防火墙干扰[^1] 4. **数据格式**:建议定义协议头尾(如`<START>数据<END>`)避免粘包 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值