我创建了一个在 Arduino MKR 1010 上运行的 Arduino 应用程序。连接到 WiFi 后,设备最初订阅为自己指定的 MQTT 主题,并通过该主题通知它应该订阅的其他主题。这些后续 JSON 消息的负载大小约为 2kb,我测试的最后一个是 1.91kb。信息以彩色灯光的形式处理和表示。随着新主题上线或下线,它会收到此信息的通知并相应地调整其订阅。
预期行为
应用程序应该正确运行 - 在收到通知时订阅和取消订阅主题 - 并以彩色 LED 的形式处理主题的有效负载。这些 JSON 有效负载应在处理后清除,为任何后续工作负载释放内存。
实际行为
工作一段时间后(这可能会有所不同),应用程序要么停止完全处理有效负载,要么完全停止工作,并需要硬重置才能再次与设备通信。
下面是代码 - 我希望这能有所帮助 - 我已经对代码进行了注释,以便更清楚地说明它在哪里以及为什么执行给定的功能。我已经尝试将动态 Json 文档更改为各种大小 - 对于有效负载的大小来说,这似乎是最佳的 - 尽管我很乐意接受其他建议。
#include <SPI.h>
#include <WiFiNINA.h>
#include <ArduinoMqttClient.h>
#include <Adafruit_NeoPixel.h>
#include <ArduinoJson.h>
#include <MemoryFree.h>
#include <pgmStrToRAM.h>
// Networking Values
WiFiClient wlan;
int status = WL_IDLE_STATUS;
char ssid[] = ""; // network SSID (name)
char pass[] = ""; // network password (use for WPA, or use as key for WEP)
int keyIndex = 0;
// Subscription list for MQTT Subscriptions
MqttClient mqttClient(wlan);
String subscriptions[10] ;
// Lighting Values
int dataPin = 5;
#define NUMPIXELS 30
Adafruit_NeoPixel pixel = Adafruit_NeoPixel(NUMPIXELS, dataPin, NEO_GRB + NEO_KHZ800);
int lights[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29};
typedef struct {
uint8_t red;
uint8_t green;
uint8_t blue;
} pixel_config_t;
pixel_config_t currentLights[NUMPIXELS];
// Setup - Initialises the light array to give non-sequential illumination
void setup() {
Serial.begin(57600);
pixel.begin();
const size_t n = sizeof(lights) / sizeof(lights[0]);
for (size_t i = 0; i < n - 1; i++) {
size_t j = random(0, n - i);
int t = lights[i];
lights[i] = lights[j];
lights[j] = t;
}
// Initialise lights with white light
for(int counter = 0; counter < NUMPIXELS; counter++) {
changePixelColor(lights[counter], 255, 255, 255);
}
mqttClient.onMessage(onMqttMessage);
}
// Loop - checks for the connectivity - and polls the MQTT server for new messages every 3 seconds
void loop() {
if (WiFi.status() != WL_CONNECTED) {
connectWiFi();
}
if (!mqttClient.connected()) {
// MQTT client is disconnected, connect
connectMQTT();
}
mqttClient.poll();
delay(3000);
}
// On receipt of MQTT message - process either a notification of a new Topic to subscribe to - or process a publication to a subscribed Topic
void onMqttMessage(int messageSize) {
Serial.println(F("Received Mqtt Message"));
DynamicJsonDocument doc(12000);
deserializeJson(doc, mqttClient);
String type = doc["type"];
// if this is a message notifying of a new Topics that has come online - unsubscribe to those in our list that are no longer on this payload - and subscribe to the new ones
if(type == "subscription_notification") {
JsonArray query_uuids = doc["query_uuids"].as<JsonArray>();
for(JsonVariant currentUuid: query_uuids) {
String currentUuidAsStr = currentUuid.as<String>();
if(!isInStringArray(currentUuidAsStr, subscriptions)) {
subscribeToNewTopic(currentUuidAsStr);
}
}
for(String existingSubscription: subscriptions) {
if(existingSubscription && existingSubscription != "" && !isInJsonVariantArray(existingSubscription, query_uuids)) {
unsubscribeToTopic(existingSubscription);
}
}
}
// Else this is a notification of new colourings of lights - iterate through the list of lights changing the LED to the corresponding number in the payload
else {
JsonArray spreadArray = doc["spreadProportions"].as<JsonArray>();
int lightNumber = 0;
for(JsonObject currentProportion : spreadArray) {
uint8_t red = currentProportion["red"].as<int>();
uint8_t green = currentProportion["green"].as<int>();
uint8_t blue = currentProportion["blue"].as<int>();
changePixelColor(lights[lightNumber], red, green, blue);
lightNumber++;
}
Serial.println(F("Completed Update."));
}
// Clear JSON Document
doc.clear();
}
// Check if the UUID is in our current List of topics
bool isInStringArray(String uuidToQuery, String arrayToQuery[]) {
for(int counter = 0; counter < sizeof(arrayToQuery) ; counter++) {
if(arrayToQuery[counter] == uuidToQuery) {
return true;
}
}
return false;
}
// Check if a UUID is included in the incoming Json Array
bool isInJsonVariantArray(String uuidToQuery, JsonArray arrayToQuery) {
for(JsonVariant currentUuid: arrayToQuery) {
String currentUuidAsStr = currentUuid.as<String>();
if(currentUuidAsStr == uuidToQuery) {
return true;
}
}
return false;
}
// Subscribe to this new Topic and add it to our list
void subscribeToNewTopic(String topicname) {
if((!topicname) || topicname == "null") {
return;
}
Serial.println(F("Subscribing to new topic"));
mqttClient.subscribe("query/" + topicname, 2);
// add to the first element that has no value
int counter = 0;
for(String subscription : subscriptions) {
if(subscription == "") {
break;
}
counter++;
}
subscriptions[counter] = topicname;
}
// Unsubscribe to a Topic
void unsubscribeToTopic(String topicname) {
Serial.println(F("Unsubscribing to old topic"));
mqttClient.unsubscribe("query/" + topicname);
for(int counter = 0; counter < sizeof(subscriptions) ; counter++) {
if(subscriptions[counter] == topicname) {
subscriptions[counter] = "";
break;
}
}
}
// Change the pixel color gradually from the current color
void changePixelColor(int pixelNum, uint8_t r, uint8_t g, uint8_t b ) {
//int pixelNum = *(int*) pvParameters;
uint8_t startR = currentLights[pixelNum].red;
uint8_t startG = currentLights[pixelNum].green;
uint8_t startB = currentLights[pixelNum].blue;
while ((startR != r) || (startG != g) || (startB != b)){ // while the curr color is not yet the target color
if (startR < r) startR++; else if (startR > r) startR--; // increment or decrement the old color values
if (startG < g) startG++; else if (startG > g) startG--;
if (startB < b) startB++; else if (startB > b) startB--;
uint32_t color = pixel.gamma32(pixel.Color(startR, startG, startB));
pixel.setPixelColor(pixelNum, color);
pixel.show();
currentLights[pixelNum].red = startR;
currentLights[pixelNum].green = startG;
currentLights[pixelNum].blue = startB;
delay(3); // add a delay if its too fast
}
}
// Connect to the Wifi
void connectWiFi() {
Serial.print("Attempting to connect to WiFi");
while (WiFi.begin("", "") != WL_CONNECTED) {
// failed, retry
delay(5000);
}
Serial.println("Connected.");
}
// Connect to the MQTT Server - subscribing to the base Topic associated with this device
void connectMQTT() {
mqttClient.setId("divnr-29715938");
mqttClient.setUsernamePassword("", "");
mqttClient.setKeepAliveInterval(5000);
while (!mqttClient.connect("", 1883)) {
// failed, retry
Serial.print(".");
delay(2000);
}
Serial.println(F("Reconnected"));
// subscribe to a topic
mqttClient.subscribe("device/7e887232-9cf1-453c-b352-647e9ccce11a", 2);
}