将遥测数据发布到本地 Thingsboard 服务器适用于 mqtt.js 但不适用于 ESP arduino 示例

Publishing telemtry data to local Thingsboard server works with mqtt.js but not ESP arduino sample

我为我正在处理的 IoT 项目设置了一个虚拟机 运行 一个 ThingsBoard 代理,运行 遇到了一些奇怪的问题。我正在使用连接了几个传感器(温度、湿度、压力等)的 ESP32,并希望使用 MQTT 将这些值简单地推送到 ThingsBoard 代理。我根据 ThingsBoard 提供的代码示例创建了一个测试程序,但在连接到服务器时遇到问题。下面是我的代码:

#include <WiFi.h>
#include <Wire.h>
#include <OneWire.h>
#include <PubSubClient.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <DallasTemperature.h>

// Constants
#define ONE_WIRE_PIN  14  // One-Wire pin for water temperature sensor
#define TURBIDITY_PIN 36  // Analog pin for turbidity sensor
#define SEALEVELPRESSURE_HPA (1013.25)

// WiFi Login Info
#define ssid              "WiFiNetwork"
#define password          "password"

// MQTT Broker IP address:
#define mqtt_server       "192.168.0.10"
#define mqtt_server_port  1883

// MQTT Client Info
#define MQTT_CLIENT_NAME "ESP32"
#define ACCESS_TOKEN "ESP32_DEMO_TOKEN"

// Sensor Variables
float airTemperature = 0;
float waterTemperature = 0;
float humidity = 0;
float pressure = 0;
float altitude = 0;
float turbidity = 0;

// Control Variables
long lastMsg = 0;   // keeps track of timestamp since the last message was sent

// Objects
Adafruit_BME280 BME280; // I2C
OneWire oneWire_in(ONE_WIRE_PIN);
DallasTemperature temperature_sensor(&oneWire_in);

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  int status = 0;

  Serial.begin(115200);

  // Initialize BME280
  status = BME280.begin();  
  if (!status) {
      while(true) {
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
        delay(5000);
      }
  }

  // Initialize the One-Wire Temperature Sensor
  temperature_sensor.begin();

  // Initialize the WiFi and MQTT connections
  setup_wifi();
  client.setServer(mqtt_server, mqtt_server_port);
  client.setCallback(mqtt_callback);
}

void loop() {
  // Update/refresh the Wifi/MQTT connection
  updateWirelessConnection();

  // Read and transmit the sensor values every 5 seconds
  long now = millis();
  if (now - lastMsg > 5000) {
    lastMsg = now;

    // Read all the sensor values
    collectMeasurements();

    // Publish updated sensor values
    publishMeasurements();

    // Print all sensor values
    printMeasurements();
  }
}

void updateWirelessConnection()
{
  if (!client.connected()) {
    reconnect();
  }

  client.loop();
}

void printMeasurements()
{
  Serial.print("Air Temperature: ");
  Serial.print(airTemperature);
  Serial.println( " *C");

  Serial.print("Air Pressure: ");
  Serial.print(pressure);
  Serial.println( " hPa");

  Serial.print("Humidity: ");
  Serial.print(humidity);
  Serial.println( " %RH");

  Serial.print("Altitude: ");
  Serial.print(altitude);
  Serial.println( " m");

  Serial.print("Water Temperature: ");
  Serial.print(waterTemperature);
  Serial.println( " *C");

  Serial.print("Water Turbidity: ");
  Serial.print(turbidity);
  Serial.println( " NTUs");

  Serial.println();
}

void collectMeasurements()
{
  // Read the measurements from the BME280 sensor
  airTemperature = BME280.readTemperature();
  pressure = BME280.readPressure() / 100.0F;
  altitude = BME280.readAltitude(SEALEVELPRESSURE_HPA);
  humidity = BME280.readHumidity();

  // Read the water temperature sensor
  temperature_sensor.requestTemperatures();
  waterTemperature = temperature_sensor.getTempCByIndex(0);

  // Read the turbidity sensor
  turbidity = calculateTurbidityValue(analogRead(TURBIDITY_PIN), waterTemperature);
}

// Calculates the temperature-compensated Turbidity value based on the raw ADC voltage and water temperature in celsius
float calculateTurbidityValue(int adcValue, float waterTemp)
{
  // Convert ADC value to original voltage reading
  float voltage = adcValue * (5.0 / 4096.0);

  // Correct the voltage value for temperature
  // TODO: TBD later

  // Convert the voltage to NTUs
  float NTUs = -1120.4 * pow(voltage, 2.0) + 5742.3 * voltage - 4352.9;

  if (NTUs < 0) {
    return 0;
  } else {
    return NTUs;
  }
}

// Publishes all measurements to the MQTT broker
void publishMeasurements()
{
  // Create Payload string
  // Paylod String format: {"key1":"value1", "key2":"value2"}
  String payload1 = "{";
  payload1 += "\"airTemperature\":";   payload1 += airTemperature;    payload1 += ",";
  payload1 += "\"pressure\":";         payload1 += pressure;          payload1 += ",";
  payload1 += "\"humidity\":";         payload1 += humidity;
  payload1 += "}";

  String payload2 = "{";
  payload2 += "\"altitude\":";         payload2 += altitude;          payload2 += ",";
  payload2 += "\"waterTemperature\":"; payload2 += waterTemperature;  payload2 += ",";
  payload2 += "\"turbidity\":";        payload2 += turbidity;
  payload2 += "}";

  transmitPayload(payload1);
  transmitPayload(payload2);
}

void transmitPayload(String payload)
{
  // Convert Payload string to c-string and transmit
  char attributes[500];
  payload.toCharArray(attributes, 500);
  client.publish("v1/devices/me/telemetry", attributes);
}

void setup_wifi() {
  delay(10);

  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void mqtt_callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;

  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // Feel free to add more if statements to control more GPIOs with MQTT

  // If a message is received on the topic esp32/output, you check if the message is either "on" or "off". 
  // Changes the output state according to the message
  /*if (String(topic) == "esp32/output") {
    Serial.print("Changing output to ");
    if(messageTemp == "on"){
      Serial.println("on");
      digitalWrite(ledPin, HIGH);
    }
    else if(messageTemp == "off"){
      Serial.println("off");
      digitalWrite(ledPin, LOW);
    }
  }*/
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected())
  {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(MQTT_CLIENT_NAME, ACCESS_TOKEN, NULL)) {
      Serial.println("connected");
      // Subscribe
      //client.subscribe("esp32/output");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

对于那些感兴趣的人,实际读取传感器值并将其打印出来的代码工作正常,问题是连接到 ThingsBoard 服务器的代码。当我 运行 这段代码时,这是我在串行终端中看到的:

ets Jun 8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:1 load:0x3fff0018,len:4 load:0x3fff001c,len:928 ho 0 tail 12 room 4 load:0x40078000,len:9280 load:0x40080400,len:5848 entry 0x40080698

Connecting to WiFiNetwork .... WiFi connected IP address: 192.168.0.15 Attempting MQTT connection...failed, rc=-1 try again in 5 seconds Attempting MQTT connection...failed, rc=-1 try again in 5 seconds

PubSubClient library documentation 来看,-1 的状态代码表示 "the client is disconnected cleanly",这实际上没有意义...

所以假设我的 ThingsBoard 服务器可能有问题,我使用了 ThingsBoard Getting Started Guide 中的 MQTT.js 客户端示例文件并将 .bat 和 .json 文件修改为按如下方式匹配我的输入:

mqtt-js.bat

@echo off

REM Set ThingsBoard host to "demo.thingsboard.io" or "localhost"
set THINGSBOARD_HOST=192.168.0.10

REM Replace YOUR_ACCESS_TOKEN with one from Device details panel.
set ACCESS_TOKEN=ESP32_DEMO_TOKEN

REM Read serial number and firmware version attributes
set /p ATTRIBUTES=<attributes-data.json

REM Read timeseries data as an object without timestamp (server-side timestamp will be used)
set /p TELEMETRY=<telemetry-data.json

REM publish attributes and telemetry data via mqtt client
node publish.js

遥测-data.json

{"airTemperature":21, "humidity":55.0, "pressure": 101.6}

当我执行 .bat 文件时,数据毫无问题地发布到我的服务器!

奇怪的是,这个 MQTT.js 文件似乎不需要像我的 Arduino 代码那样指定端口 8080。很明显,问题出在 Arduino 代码上,而不是服务器本身,但我完全被难住了……有什么想法吗?

编辑: 更正错误的 MQQT 端口号问题后,服务器能够正确连接。然后经过一些测试,我注意到一旦包含所有变量,有效负载字符串就根本没有在仪表板上更新。结果发现字符串太长无法正确传输,所以我更新了代码将字符串拆分为两次传输,最终解决了所有问题。

您说过您是 运行 虚拟机中的 ThingsBoard。

除非您以其他方式明确配置它,否则只能通过与 VM 位于同一计算机上的软件 运行 访问该 VM。这就是 .bat 文件起作用的原因。

VM 通常会在它们 运行 所在的计算机内部设置一个私有虚拟网络,只有它们自己和网络上的那台计算机。

您需要查看 VM 软件的文档,了解如何使您的 Windows 计算机所连接的网络上的其他计算机可以访问 VM。

也有可能您使用了错误的端口号。 MQTT 通常使用 1883,而不是您的 ESP32 代码定义的 8080。在深入研究 VM 的配置之前,我会先验证这一点。