diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 9d1fc704e..2084f89a1 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -6,6 +6,12 @@ ## Added +- rssi in Network Status Page + ## Fixed +- issue in espMqttClient on low mem + ## Changed + +- mqtt free mem check 60k diff --git a/interface/package.json b/interface/package.json index ed55b6aed..44f6bd8b0 100644 --- a/interface/package.json +++ b/interface/package.json @@ -27,7 +27,7 @@ "@preact/compat": "^17.1.2", "@prefresh/vite": "^2.4.1", "@table-library/react-table-library": "4.1.7", - "@types/lodash-es": "^4.17.8", + "@types/lodash-es": "^4.17.9", "@types/node": "^20.5.7", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", diff --git a/interface/src/framework/network/NetworkStatusForm.tsx b/interface/src/framework/network/NetworkStatusForm.tsx index 5994237c1..6d4189327 100644 --- a/interface/src/framework/network/NetworkStatusForm.tsx +++ b/interface/src/framework/network/NetworkStatusForm.tsx @@ -114,7 +114,7 @@ const NetworkStatusForm: FC = () => { - + diff --git a/interface/yarn.lock b/interface/yarn.lock index 658d96427..55a30cff6 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -1339,12 +1339,12 @@ __metadata: languageName: node linkType: hard -"@types/lodash-es@npm:^4.17.8": - version: 4.17.8 - resolution: "@types/lodash-es@npm:4.17.8" +"@types/lodash-es@npm:^4.17.9": + version: 4.17.9 + resolution: "@types/lodash-es@npm:4.17.9" dependencies: "@types/lodash": "*" - checksum: 950771d406c842814dd22217adba5e01bd06b3c21e97900d3c3816f38580e132894400b5d83a7962645fa284d8478614bdcc50755255ad15024311b7b8ed8520 + checksum: 9fe82df0ec14e2aad50a1bf6488c4457e3378fcc77f5806fbc8035904ef0848b70e50037b13d9bddb66d3a30b425d2998a4a438a5024efe7431b63fde0920378 languageName: node linkType: hard @@ -1586,7 +1586,7 @@ __metadata: "@prefresh/vite": ^2.4.1 "@table-library/react-table-library": 4.1.7 "@types/babel__core": ^7 - "@types/lodash-es": ^4.17.8 + "@types/lodash-es": ^4.17.9 "@types/node": ^20.5.7 "@types/react": ^18.2.21 "@types/react-dom": ^18.2.7 diff --git a/lib/espMqttClient/src/MqttClient.cpp b/lib/espMqttClient/src/MqttClient.cpp index a428158ed..97db0dcce 100644 --- a/lib/espMqttClient/src/MqttClient.cpp +++ b/lib/espMqttClient/src/MqttClient.cpp @@ -103,7 +103,7 @@ bool MqttClient::disconnected() const { } bool MqttClient::connect() { - bool result = true; + bool result = false; if (_state == State::disconnected) { EMC_SEMAPHORE_TAKE(); if (_addPacketFront(_cleanSession, @@ -116,19 +116,21 @@ bool MqttClient::connect() { _willPayloadLength, (uint16_t)(_keepAlive / 1000), // 32b to 16b doesn't overflow because it comes from 16b orignally _clientId)) { + result = true; + _state = State::connectingTcp1; #if defined(ARDUINO_ARCH_ESP32) if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { vTaskResume(_taskHandle); } #endif - _state = State::connectingTcp1; } else { EMC_SEMAPHORE_GIVE(); emc_log_e("Could not create CONNECT packet"); _onError(0, Error::OUT_OF_MEMORY); - result = false; } EMC_SEMAPHORE_GIVE(); + } else if (_state <= State::connected) { // already connected or connecting + result = true; } return result; } @@ -196,6 +198,14 @@ const char * MqttClient::getClientId() const { return _clientId; } +size_t MqttClient::queueSize() { + size_t ret = 0; + EMC_SEMAPHORE_TAKE(); + ret = _outbox.size(); + EMC_SEMAPHORE_GIVE(); + return ret; +} + void MqttClient::loop() { switch ((State)_state) { // modified by proddy for EMS-ESP compiling standalone case State::disconnected: @@ -335,7 +345,6 @@ int MqttClient::_sendPacket() { size_t wantToWrite = 0; size_t written = 0; if (packet && (wantToWrite == written)) { - // mixing signed with unsigned here but safe because of MQTT packet size limits wantToWrite = packet->packet.available(_bytesSent); if (wantToWrite == 0) { EMC_SEMAPHORE_GIVE(); @@ -630,9 +639,6 @@ void MqttClient::_onPubcomp() { // if it doesn't match the ID, return if ((it.get()->packet.packetType()) == PacketType.PUBREL) { if (it.get()->packet.packetId() == idToMatch) { - // if (!_addPacket(PacketType.PUBCOMP, idToMatch)) { - // emc_log_e("Could not create PUBCOMP packet"); - // } callback = true; _outbox.remove(it); break; @@ -697,20 +703,6 @@ void MqttClient::_onUnsuback() { } } -uint16_t MqttClient::getQueue() const { - EMC_SEMAPHORE_TAKE(); - espMqttClientInternals::Outbox::Iterator it = _outbox.front(); - uint16_t count = 0; - while (it) { - // if (it.get()->packet.packetType() == PacketType.PUBLISH) { - ++count; - // } - ++it; - } - EMC_SEMAPHORE_GIVE(); - return count; -} - void MqttClient::_clearQueue(int clearData) { emc_log_i("clearing queue (clear session: %d)", clearData); EMC_SEMAPHORE_TAKE(); diff --git a/lib/espMqttClient/src/MqttClient.h b/lib/espMqttClient/src/MqttClient.h index d09db68c5..dba4bf245 100644 --- a/lib/espMqttClient/src/MqttClient.h +++ b/lib/espMqttClient/src/MqttClient.h @@ -65,7 +65,7 @@ class MqttClient { uint16_t publish(const char * topic, uint8_t qos, bool retain, espMqttClientTypes::PayloadCallback callback, size_t length); void clearQueue(bool deleteSessionData = false); // Not MQTT compliant and may cause unpredictable results when `deleteSessionData` = true! const char * getClientId() const; - uint16_t getQueue() const; + size_t queueSize(); // No const because of mutex void loop(); protected: @@ -131,8 +131,9 @@ class MqttClient { uint32_t timeSent; espMqttClientInternals::Packet packet; template - OutgoingPacket(uint32_t t, espMqttClientTypes::Error error, Args &&... args) - : timeSent(t) + OutgoingPacket(uint32_t t, espMqttClientTypes::Error & error, Args &&... args) + : // NOLINT(runtime/references) + timeSent(t) , packet(error, std::forward(args)...) { } }; @@ -150,18 +151,26 @@ class MqttClient { bool _addPacket(Args &&... args) { espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS); espMqttClientInternals::Outbox::Iterator it = _outbox.emplace(0, error, std::forward(args)...); - if (it && error == espMqttClientTypes::Error::SUCCESS) + if (it && error == espMqttClientTypes::Error::SUCCESS) { return true; - return false; + } else { + if (it) + _outbox.remove(it); + return false; + } } template bool _addPacketFront(Args &&... args) { espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS); espMqttClientInternals::Outbox::Iterator it = _outbox.emplaceFront(0, error, std::forward(args)...); - if (it && error == espMqttClientTypes::Error::SUCCESS) + if (it && error == espMqttClientTypes::Error::SUCCESS) { return true; - return false; + } else { + if (it) + _outbox.remove(it); + return false; + } } void _checkOutbox(); diff --git a/lib/espMqttClient/src/Outbox.h b/lib/espMqttClient/src/Outbox.h index dfbbd13c0..cfb9f244d 100644 --- a/lib/espMqttClient/src/Outbox.h +++ b/lib/espMqttClient/src/Outbox.h @@ -163,6 +163,16 @@ class Outbox { return false; } + size_t size() const { + Node* n = _first; + size_t count = 0; + while (n) { + n = n->next; + ++count; + } + return count; + } + private: Node* _first; Node* _last; diff --git a/lib/espMqttClient/src/Packets/Packet.cpp b/lib/espMqttClient/src/Packets/Packet.cpp index df463ef7b..2f84b503a 100644 --- a/lib/espMqttClient/src/Packets/Packet.cpp +++ b/lib/espMqttClient/src/Packets/Packet.cpp @@ -100,7 +100,7 @@ Packet::Packet(espMqttClientTypes::Error& error, (password ? 2 + strlen(password) : 0); // allocate memory - if (!_allocate(remainingLength)) { + if (!_allocate(remainingLength, false)) { error = espMqttClientTypes::Error::OUT_OF_MEMORY; return; } @@ -300,8 +300,8 @@ Packet::Packet(espMqttClientTypes::Error& error, MQTTPacketType type) } -bool Packet::_allocate(size_t remainingLength) { - if (EMC_GET_FREE_MEMORY() < EMC_MIN_FREE_MEMORY) { +bool Packet::_allocate(size_t remainingLength, bool check) { + if (check && EMC_GET_FREE_MEMORY() < EMC_MIN_FREE_MEMORY) { emc_log_w("Packet buffer not allocated: low memory"); return false; } diff --git a/lib/espMqttClient/src/Packets/Packet.h b/lib/espMqttClient/src/Packets/Packet.h index 1af2f06af..f2b290293 100644 --- a/lib/espMqttClient/src/Packets/Packet.h +++ b/lib/espMqttClient/src/Packets/Packet.h @@ -133,7 +133,7 @@ class Packet { private: // pass remainingLength = total size - header - remainingLengthLength! - bool _allocate(size_t remainingLength); + bool _allocate(size_t remainingLength, bool check = true); // fills header and returns index of next available byte in buffer size_t _fillPublishHeader(uint16_t packetId, diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 56c5165cd..d62367503 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -124,6 +124,8 @@ void Mqtt::resubscribe() { // Main MQTT loop - sends out top item on publish queue void Mqtt::loop() { + queuecount_ = mqttClient_->queueSize(); + // exit if MQTT is not enabled or if there is no network connection if (!connected()) { return; @@ -142,7 +144,7 @@ void Mqtt::loop() { EMSESP::publish_sensor_values(false); } - queuecount_ = mqttClient_->getQueue(); + // wait for empty queue before sending scheduled device messages if (queuecount_ > 0) { return; } @@ -482,7 +484,7 @@ void Mqtt::on_connect() { connecting_ = true; connectcount_++; // count # reconnects. not currently used. - queuecount_ = 0; + queuecount_ = mqttClient_->queueSize(); load_settings(); // reload MQTT settings - in case they have changes @@ -510,7 +512,7 @@ void Mqtt::on_connect() { // publish to the last will topic (see Mqtt::start() function) to say we're alive queue_publish_retain("status", "online", true); // with retain on - mqtt_publish_fails_ = 0; // reset fail count to 0 + // mqtt_publish_fails_ = 0; // reset fail count to 0 } // Home Assistant Discovery - the main master Device called EMS-ESP @@ -590,14 +592,23 @@ bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, con if (!mqtt_enabled_ || topic.empty()) { return false; // quit, not using MQTT } + // check free mem + if (ESP.getFreeHeap() < 60 * 1204) { + if (operation == Operation::PUBLISH) { + mqtt_message_id_++; + mqtt_publish_fails_++; + } + LOG_DEBUG("%s failed: low memory", operation == Operation::PUBLISH ? "Publish" : operation == Operation::SUBSCRIBE ? "Subscribe" : "Unsubscribe"); + return false; // quit + } uint16_t packet_id = 0; char fulltopic[MQTT_TOPIC_MAX_SIZE]; if (topic.find(discovery_prefix_) == 0) { - strlcpy(fulltopic, topic.c_str(), sizeof(fulltopic)); // leave topic as it is + strlcpy(fulltopic, topic.c_str(), sizeof(fulltopic)); // leave discovery topic as it is } else { - // it's a discovery topic, added the mqtt base to the topic path + // it's not a discovery topic, added the mqtt base to the topic path snprintf(fulltopic, sizeof(fulltopic), "%s/%s", mqtt_base_.c_str(), topic.c_str()); // uses base } diff --git a/src/version.h b/src/version.h index b6cc182cf..54c0226cd 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.7.0-dev.1" +#define EMSESP_APP_VERSION "3.7.0-dev.2" diff --git a/src/web/WebEntityService.cpp b/src/web/WebEntityService.cpp index 2de13c4a3..15708c8a1 100644 --- a/src/web/WebEntityService.cpp +++ b/src/web/WebEntityService.cpp @@ -31,6 +31,7 @@ WebEntityService::WebEntityService(AsyncWebServer * server, FS * fs, SecurityMan void WebEntityService::begin() { _fsPersistence.readFromFS(); EMSESP::logger().info("Starting Custom entity service"); + Mqtt::subscribe(EMSdevice::DeviceType::CUSTOM, "custom/#", nullptr); // use empty function callback } // this creates the entity file, saving it to the FS diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index b794e0335..c6a3ad6cf 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -31,6 +31,7 @@ WebSchedulerService::WebSchedulerService(AsyncWebServer * server, FS * fs, Secur void WebSchedulerService::begin() { _fsPersistence.readFromFS(); EMSESP::logger().info("Starting Scheduler service"); + Mqtt::subscribe(EMSdevice::DeviceType::SCHEDULER, "scheduler/#", nullptr); // use empty function callback } // this creates the scheduler file, saving it to the FS diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index 86ec82683..36ee1084f 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -166,9 +166,9 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) { if (Mqtt::enabled()) { statJson = statsJson.createNestedObject(); statJson["id"] = 5; - statJson["s"] = Mqtt::publish_count(); + statJson["s"] = Mqtt::publish_count() - Mqtt::publish_fails(); statJson["f"] = Mqtt::publish_fails(); - statJson["q"] = Mqtt::publish_count() == 0 ? 100 : 100 - (uint8_t)((100 * Mqtt::publish_fails()) / (Mqtt::publish_count() + Mqtt::publish_fails())); + statJson["q"] = Mqtt::publish_count() == 0 ? 100 : 100 - (uint8_t)((100 * Mqtt::publish_fails()) / Mqtt::publish_count()); } statJson = statsJson.createNestedObject();