使用虚拟机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 上位机 数据传输 的通路。

相关推荐
starvapour8 小时前
Ubuntu硬盘的创建分区、格式化与挂载
linux·ubuntu
Better Bench14 小时前
ThinkStation PGX Ubuntu aarch64架构上使用Docker 配置amd64架构镜像的深度学习环境并使用Vscode开发
ubuntu·docker·架构
weixin_4492900115 小时前
Ubuntu 系统上安装和配置 Go 语言运行环境
linux·ubuntu·golang
james bid17 小时前
MacBook Pro 2015 上 XUbuntu 24.04 启用 eGPU (GeForce GTX 1080 Ti) 和核显黑屏问题解决
linux·ubuntu·macos·cuda·egpu
龙吟游戏17 小时前
Ubuntu Server 25.10安装
linux·运维·ubuntu
zzxxlty17 小时前
windows 迁移ubuntu wsl从C盘到D盘
linux·运维·ubuntu
robator17 小时前
ubuntu 22.04 升级nvidia显卡驱动、cuda 和cudnn
linux·服务器·ubuntu
肖恭伟18 小时前
Pycharm历史community版本下载
linux·ubuntu·pycharm·下载·community
xiaohai@Linux18 小时前
ESP32在IDF v5.3.1版本下实现驱动摄像头(OV2640为例)
esp32·摄像头·ov2640·esp32-camera
shandianchengzi18 小时前
【记录】ARM|Ubuntu 24 快速安装 arm-none-eabi-gdb 及 QEMU 调试实战
linux·arm开发·ubuntu·arm·qemu