首页 > 解决方案 > 在自定义类中使用 PubSubClient

问题描述

我正在构建一个库,我可以在多个设备上重复使用它,以节省我在代码中一遍又一遍地设置相同的功能。

我在尝试发布消息时遇到问题,希望您能提供帮助!

该代码针对启用了 BLE 和 WiFi 的 ESP32(它是Timo 的 Heart Rate Monitor的一个端口,并且消息有效负载是使用 ArduinoJSON 6.x 创建的(最初使用的是 ArduinoJSON 5.x,但这不是代码的问题)尝试打印纯文本时失败并出现相同的错误)

代码通过 Platformio.org 管理,而不是 Arduino IDE,我的 platformio.ini 文件如下所示:

[env:esp32dev]
platform = espressif32
board = ttgo-t-beam
framework = arduino
lib_extra_dirs = 
    ../common_libraries
build_flags = !../bin/build_flags.sh
board_build.partitions = min_spiffs.csv
lib_deps = 
    nkolban/ESP32 BLE Arduino@^1.0.1
    bblanchon/ArduinoJson@^6.17.2

build_flags.sh脚本只是为 WiFi 和 MQTT 客户端设置环境变量,稍后您会看到。

库中的代码如下,我看到的问题是,虽然总是发送课程设置中的“注册消息”,但心率监测器的数据是零星发送的,总是会导致 Guru Meditation .

令人沮丧的是,这只会在类外部的 MQTT 发布上失败,无论客户端在哪里初始化,订阅消息都会通过。

编码

我的图书馆”

/*
 * mymqtt32.h
 *
 */

#ifndef mymqtt32_h
#define mymqtt32_h

#include "Arduino.h"

#include <WiFi.h>
#include <PubSubClient.h>


class mymqtt32 {
    public:
        mymqtt32(
                char* device_name,
                char* device_type,
                char* wifi_ssid,
                char* wifi_pw,
                char* mqtt_server_ip,
                int mqtt_server_port
                );
        void wifi_setup();
        PubSubClient mqtt_setup(String client_name);
    private:
        char* _device_name;
        char* _device_type;
        char* _wifi_ssid;
        char* _wifi_pw;
        char* _mqtt_server_ip;
        int _mqtt_server_port;
        String _getMAC();
        WiFiClient _espClient;
        PubSubClient _mqtt_client;
};

#endif
#include "mymqtt32.h"

#include <ArduinoJson.h>

mymqtt32::MYMQTT(
        char* device_name,
        char* device_type,
        char* wifi_ssid, 
        char* wifi_pw,
        char* mqtt_server_ip,
        int mqtt_server_port
) {
    _device_name = device_name;
    _device_type = device_type;
    _wifi_ssid = wifi_ssid;
    _wifi_pw = wifi_pw;
    _mqtt_server_ip = mqtt_server_ip;
    _mqtt_server_port = mqtt_server_port;
}

void mymqtt32::wifi_setup() {
    delay(10);
    // We start by connecting to a WiFi network
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(_wifi_ssid);
    WiFi.mode(WIFI_STA);
    WiFi.begin(_wifi_ssid, _wifi_pw);

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

    randomSeed(micros());

    Serial.print("My mac address is: ");
    Serial.println(_getMAC());
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
}

PubSubClient mymqtt32::mqtt_setup(String client_name) {
    _mqtt_client.setClient(_espClient);
    _mqtt_client.setServer(_mqtt_server_ip,
                           _mqtt_server_port);
    String _client_name = client_name;
    String _dev_type_str = _device_type;
    while (!_mqtt_client.connected()) {
        Serial.print("Attempting MQTT connection...");
        // Attempt to connect
        if (
            _mqtt_client.connect(_client_name.c_str())
            ) {
            Serial.println("connected");
            const size_t capacity = JSON_OBJECT_SIZE(3);
            DynamicJsonDocument systemDescription(capacity);

            systemDescription["device_name"] = _client_name;
            systemDescription["device_type"] = _dev_type_str;
            systemDescription["device_mac"]  = _getMAC();

            char bodyString[500];
            serializeJson(systemDescription, bodyString, sizeof(bodyString));
            serializeJson(systemDescription, Serial);
            Serial.println();
            Serial.println(bodyString);
            String pub_topic = "/register";
            char pub_topic_name[50];
            pub_topic.toCharArray(pub_topic_name, 50);
            Serial.print("Publishing to: ");
            Serial.println(pub_topic_name);
            // Once connected, publish an announcement...
            _mqtt_client.publish(pub_topic_name, bodyString);
            // Now subscribe to the controller topic
            String sub_topic = "devices/control/";
            sub_topic.concat(_dev_type_str);
            sub_topic.concat("/");
            sub_topic.concat(_getMAC());
            char sub_topic_name[50];
            sub_topic.toCharArray(sub_topic_name, 50);
            Serial.print("Subscribing to: ");
            Serial.println(sub_topic_name);
            _mqtt_client.subscribe(sub_topic_name);
        } else {
            Serial.print("Trying to connect to ");
            Serial.print(_mqtt_server_ip);
            Serial.print(" on port ");
            Serial.println(_mqtt_server_port);
            Serial.print("failed, rc=");
            Serial.print(_mqtt_client.state());
            Serial.println(" try again in 5 seconds");
            // Wait 5 seconds before retrying
            delay(5000);
        }
    }
    return _mqtt_client;
}

String mymqtt32::_getMAC()
{
  String macaddr = String(WiFi.macAddress());
  macaddr.replace(":", "");
  macaddr.toLowerCase();
  return String( macaddr );
}

我如何调用代码

#include <MYMQTT32.h>
#define ST(A) #A
#define STR(A) ST(A)

// Setup the WiFi and MQTT Client based on the build flags
MYMQTT mymqtt(STR(DEVICE_NAME), device_type, STR(WIFI_SSID), STR(WIFI_PASS), STR(MQTT_SERVER), 1883);
PubSubClient mqctrl;

...
void setup() {
    // put your setup code here, to run once:
    Serial.begin(9600);
    hbat.wifi_setup();
    mqctrl = hbat.mqtt_setup(device_type);
    mqctrl.setCallback(control);

...

}

void loop() {
...

  mqctrl.loop()
}

旨在发布但导致上师冥想的功能

//--------------------------------------------------------------------------------------------
  // Send HRM stats to MQTT
  //--------------------------------------------------------------------------------------------
  void sendHRMData() {
    Serial.println("Message received from chest strap, sending to MQTT");
    DynamicJsonDocument  dataJson(20);
  
    dataJson[F("HRM")]             = hrm.HRM;
    char buffer[20];
    serializeJson(dataJson, buffer);
~   Serial.print("Buffer Content: ");
~   Serial.println(buffer);
     mqctrl.publish("/devices/status/heartrate", buffer); // <- JSON BUFFER CAUSES GURU MEDITIATION
~   // mqctrl.publish("/devices/status/heartrate", "test message"); <- STATIC MESSAGE CAUSES THE SAME ISSUE
  }

“冥想”

返回的冥想如下:

Guru Meditation Error: Core  0 panic'ed (InstrFetchProhibited). Exception was unhandled.
Core 0 register dump:
PC      : 0x2f736563  PS      : 0x00060031  A0      : 0x8001a060  A1      : 0x3ffbe290  
A2      : 0x00000000  A3      : 0x00000000  A4      : 0x00000000  A5      : 0x00000001  
A6      : 0x00000000  A7      : 0x3ffb8360  A8      : 0x8008ebac  A9      : 0x3ffbc88c  
A10     : 0x00000002  A11     : 0x3ffbc230  A12     : 0x800922e0  A13     : 0x3ffbc230  
A14     : 0x00000008  A15     : 0x00000000  SAR     : 0x0000001d  EXCCAUSE: 0x00000014  
EXCVADDR: 0x2f736560  LBEG    : 0x00000000  LEND    : 0x00000000  LCOUNT  : 0x00000000  
Core 0 was running in ISR context:
EPC1    : 0x2f736563  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x00000000

Backtrace: 0x2f736563:0x3ffbe290 0x4001a05d:0x3ffbe2b0 0x4008cd3e:0x3ffbe2e0 0x4008a591:0x3ffbe320 0x4008df8d:0x3ffbe340 0x4008e97f:0x3ffbe360 0x40086f41:0x3ffbe380 0x401e7d03:0x3ffbc210 0x400df6ce:0x3ffbc230 0x400922dd:0x3ffbc250 0x40090af9:0x3ffbc270

这解码为

PC: 0x2f736563
EXCVADDR: 0x2f736560

Decoding stack results
0x4008df8d: xRingbufferCreate at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_ringbuf/ringbuf.c line 704
0x4008e97f: xQueueGenericSendFromISR at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/queue.c line 1207
0x40086f41: spi_flash_mmap_pages at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/spi_flash/flash_mmap.c line 201
0x400df6ce: esp_fill_random at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/hw_random.c line 61
0x400922dd: verify_allocated_region at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/heap/multi_heap_poisoning.c line 109
0x40090af9: vTaskPlaceOnEventListRestricted at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/include/freertos/portable.h line 208

并且根据文件表明我错误地调用了发布命令:

InstrFetchProhibited

This CPU exception indicates that CPU could not load an instruction because the the address of the instruction did not belong to a valid region in instruction RAM or ROM.

Usually this means an attempt to call a function pointer, which does not point to valid code. PC (Program Counter) register can be used as an indicator: it will be zero or will contain garbage value (not 0x4xxxxxxx).

(来自ESP32 致命错误页面)

我确定这是一个明显的问题,这只是我对如何传递 PubSubClient 的误解,但我完全不知道该怎么做!

标签: c++arduinomqtt

解决方案


推荐阅读