使用虚拟机Ubuntu搭建mosquito服务器 使esp32、上位机通信

一、安装Ubuntu系统

安装的是服务器版本,占用资源较少。

下载地址https://pan.baidu.com/s/1ooi2Aj0Q_R3rVWyvgnz1Tw?pwd=5278我们需要使用FinalShell,一个SSH客户端,可以通过SSH连接我们的Linux系统。

不过记得,要在系统初始化勾选使用SSH功能。

二、网络设置

本文使用 NAT + 端口转发,这样设置的好处就是我们无需知道虚拟机的ip地址,只需要知道主机的ip地址就可以,ip地址是固定的,不需要频繁更改。

这里有一个比较大的坑,因为我在学校使用校园网,主机是一个校园网公有地址。因为我们使用NAT+端口转发的模式,所以消息会率先到达这个地址,但是校园网一般会有防火墙,也就是对陌生的连接请求,会拒绝掉。也就是说我们无法在主机上,使用校园网,

我使用的是手机热点,主机连接到手机热点。这时候主机会被分配一个私有ip,这时候又有一个问题了。如果服务器搭建在私有地址上,那么只有跟他位于一个网络下的设备才可以访问到,也就是说我们需要将esp32连接到手机热点上,才可以访问这个服务器。

三、搭建mosquito服务器

之前勾选系统初始化的mosquito下载,是snap下载的,与传统的管理工具apt有些差别,不太好用,所以下面的指令,都是基于apt下载mosquito的。

1、下载mosquito

bash 复制代码
sudo apt update && sudo apt install mosquitto mosquitto-clients
  • mosquitto:包含 MQTT 服务器(broker)本体。
  • mosquitto-clients:包含用于测试的客户端工具 mosquitto_pubmosquitto_sub

2、配置mosquito

bash 复制代码
sudo vim /etc/mosquitto/conf.d/default.conf

在配置文件中,确保有以下内容以实现您的需求(允许外部连接并设置认证):

bash 复制代码
listener 1883 0.0.0.0
allow_anonymous true
password_file /etc/mosquitto/passwd

其中listener非常重要,因为默认情况下,mosquito只会监听本地环回地址下的1883端口。而我们使用NAT+端口转发,也就是消息首先发送到我们的主机ip,再转发到虚拟机ip,这个消息来自于我们主机的ip地址,所以mosquito不会接受。我们需要设置,mosquito监听所有地址的1883端口,这也是第一句代码的意思。

3、创建用户和密码文件

bash 复制代码
sudo mosquitto_passwd -c /etc/mosquitto/passwd your_username

(将 your_username 替换为您想要的用户名,系统会提示您输入密码)

4、重启服务并验证

bash 复制代码
sudo systemctl restart mosquitto
sudo systemctl status mosquitto

5、验证安装成功

bash 复制代码
mosquitto -v

如果安装成功,这次将会显示 Mosquitto 的版本信息和启动日志,而不是 command not found

执行这句代码会有光标一直闪烁,代表这mosquito正在占用命令行,可以尝试用MQTT.fx连接它,会出来相应的连接信息。

输入ctrl + c 会强行中断mosquito程序,我们可以让mosquito作为守护进程在后台运行。

复制代码
mosquitto -d

四、连接esp32和上位机

1、esp32

下面是我的下位机代码,功能是连接WiFi、服务器后,向test/esp32这个主题每三秒发送一个"1111"数据。

cpp 复制代码
#include <WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>
#include <ArduinoJson.h>

void connectWifi();
void subscribeTopic();
void pubMQTTmsg();
void receiveCallback(char *topic, byte *payload, unsigned int length);
void connectMQTTserver();
void tickerCount();
String createJson();

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char *ssid = "zzzz";
const char *password = "12345678";
const char *mqttServer = "192.168.136.55";
// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
Ticker ticker;
int count = 0; // Ticker计数用变量

// ****************************************************
// 注意!以下需要用户根据然也物联平台信息进行修改!否则无法工作!
// ****************************************************
const char *mqttUserName = "han";                                                                                                                     // 服务端连接用户名(需要修改)
const char *mqttPassword = "123456"; // 服务端连接密码(需要修改)
const char *clientId = "ESP32";                                                                                                                           // 客户端id (需要修改)
const char *subTopic = "test/mqtt_fx";                                                                                        // 订阅主题(需要修改)
const char *pubTopic = "test/esp32";                                                                                       // 订阅主题(需要修改)
const char *willTopic = "/broadcast/h9sj0dFIZzO/test1";                                                                                                      // 遗嘱主题名称(需要修改)
// ****************************************************

// 遗嘱相关信息
const char *willMsg = "esp8266 offline"; // 遗嘱主题信息
const int willQos = 0;                   // 遗嘱QoS
const int willRetain = false;            // 遗嘱保留

const int subQoS = 1;            // 客户端订阅主题时使用的QoS级别(截止2020-10-07,仅支持QoS = 1,不支持QoS = 2)
const bool cleanSession = false; // 清除会话(如QoS>0必须要设为false)

bool ledStatus = HIGH;

void setup()
{

  Serial.begin(115200);          // 启动串口通讯
  ticker.attach(1, tickerCount); // Ticker定时对象

  // 设置ESP8266工作模式为无线终端模式
  WiFi.mode(WIFI_STA);

  // 连接WiFi
  connectWifi();

  Serial.println(ESP.getChipModel());

  // 设置MQTT服务器和端口号
  mqttClient.setServer(mqttServer, 1883);
  mqttClient.setCallback(receiveCallback);

  // 连接MQTT服务器
  connectMQTTserver();
}

void loop()
{
  // 如果开发板未能成功连接服务器,则尝试连接服务器
  if (!mqttClient.connected())
  {
    connectMQTTserver();
    Serial.println("正在重连");
  }
  if (count >= 3)
  {
    pubMQTTmsg(); // 每隔3秒钟发布一次信息
    count = 0;
  }
  // 定期发送心跳 检查服务器回复的心跳
  //如果有订阅主题的消息到达 触发回调函数
  mqttClient.loop();
  delay(10);
}
// 计时器
void tickerCount()
{
  count++;
}
// 连接MQTT服务器并订阅信息
void connectMQTTserver()
{
  if (mqttClient.connect(clientId, mqttUserName,
                         mqttPassword))
  {
    Serial.print("MQTT Server Connected. ClientId: ");
    Serial.println(clientId);
    Serial.print("MQTT Server: ");
    Serial.println(mqttServer);

    subscribeTopic(); // 订阅指定主题
  }
  else
  {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state());
    delay(5000);
  }
}

// 收到信息后的回调函数
void receiveCallback(char *topic, byte *payload, unsigned int length)
{
  Serial.print("Message Received [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++)
  {
    Serial.print((char)payload[i]);
  }
  Serial.println("");
  Serial.print("Message Length(Bytes) ");
  Serial.println(length);

  if ((char)payload[0] == '1')
  { // 如果收到的信息以"1"为开始
    ledStatus = LOW;
  }
  else
  {
    ledStatus = HIGH;
  }

  pubMQTTmsg();
}

// 订阅指定主题
void subscribeTopic()
{

  // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
  // 请注意subscribe函数第二个参数数字为QoS级别。这里为QoS = 1
  if (mqttClient.subscribe(subTopic, subQoS))
  {
    Serial.print("Subscribed Topic: ");
    Serial.println(subTopic);
  }
  else
  {
    Serial.print("Subscribe Fail...");
  }
}

// 发布信息
void pubMQTTmsg()
{
  char *pubMessage1 = "{\"id\":\"123\",\"version\":\"1.0\",\"params\":{\"number\":{\"value\":2}}}";

  String jsonStr = createJson(); // 保持String对象存活
  const char *pubMessage2 = jsonStr.c_str();

  const char * pubMessage = "1111";

  // 实现ESP8266向主题发布信息
  if (mqttClient.publish(pubTopic, pubMessage))
  {
    Serial.println("Publish Topic:");
    Serial.println(pubTopic);
    Serial.println(pubMessage);
  }
  else
  {
    Serial.println("Message Publish Failed.");
  }
}

// 根据时间动态创建Json
// 1~100
String createJson()
{
  DynamicJsonDocument doc(1024);

  doc["id"] = "123";
  doc["version"] = "1.0";

  JsonObject params = doc.createNestedObject("params");
  JsonObject number = params.createNestedObject("number");

  static int num = 0;

  if (count >= 3)
  {
    num++;
    num %= 100;
  }

  number["value"] = num;

  String result;

  serializeJson(doc, result);

  return result;
}

// ESP8266连接wifi
void connectWifi()
{

  WiFi.begin(ssid, password);

  // 等待WiFi连接,成功连接后输出成功信息
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected!");
  Serial.println("");
}

这是程序运行起来的样子,可以发现,连接成功,发送也成功了。

2、上位机

这时候没有编写上位机程序,用MQTT.fx,来充当一个上位机。

MQTT.fx出现了些问题,就先不展示连接参数了。

因为我们设置的可以匿名连接,所以只要服务器ip地址和端口没有输错就可以。

这是MQTT.fx收到esp32发来消息的截图。

这是MQTT.fx发送给esp32消息的截图。

可以发现esp32收到了来自上位机发来的消息。

至此,我们打通了下位机 MQTT 上位机 数据传输 的通路。

相关推荐
虎头金猫3 小时前
如何在Linux上使用Docker在本地部署开源PDF工具Stirling PDF:StirlingPDF+cpolar让专业操作像在线文档一样简单
linux·运维·ubuntu·docker·pdf·开源·centos
空灵之海4 小时前
Ubuntu系统安全合规配置
linux·ubuntu·系统安全·1024程序员节
笨鸟贤妃8 小时前
Ubuntu 22.04 安装 Docker & Compose 最新最简单完整指南
ubuntu·docker·compose
leo__5208 小时前
在Ubuntu 22.04系统中无需重启设置静态IP地址
tcp/ip·ubuntu·php
对你无可奈何10 小时前
关于Ubuntu的 update造成的内核升级
运维·服务器·ubuntu
夜无霄12 小时前
安卓逆向(一)Ubuntu环境配置
linux·运维·爬虫·ubuntu
抠脚学代码1 天前
Ubuntu Qt x64平台搭建 arm64 编译套件
数据库·qt·ubuntu
Ribou1 天前
Ubuntu 24.04.2安装k8s 1.33.4 配置cilium
linux·ubuntu·kubernetes
Mr. Cao code1 天前
Docker:颠覆传统虚拟化的轻量级革命
linux·运维·ubuntu·docker·容器