diff --git a/CHANGELOG.md b/CHANGELOG.md
index edcd160f1..9ecc9f106 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
## [Unreleased] - Development
## [9.5.0.2]
+### Added
+- Initial support for Tasmota Mesh (TasMesh) providing node/broker communication using ESP-NOW (#11939)
+
+### Changed
+- ESP32 core library from v1.0.7 to v1.0.7.1
+
### Fixed
- ESP32-C3 settings layout for configuration backup and restore
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index b1cccab7d..e572f5df2 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -98,10 +98,11 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
## Changelog v9.5.0.2
### Added
- Enable UFILESYS, GUI_TRASH_FILE and GUI_EDIT_FILE for any device compiled with more than 1M flash size
+- Initial support for Tasmota Mesh (TasMesh) providing node/broker communication using ESP-NOW [#11939](https://github.com/arendst/Tasmota/issues/11939)
- Support for AM2320 Temperature and Humidity Sensor by Lars Wessels [#12485](https://github.com/arendst/Tasmota/issues/12485)
### Changed
-- ESP32 core library from v1.0.6 to v1.0.7
+- ESP32 core library from v1.0.6 to v1.0.7.1
- Force ESP32 defines USE_UFILESYS, GUI_TRASH_FILE and #define GUI_EDIT_FILE
- Speed up initial GUI console refresh
- Simplified configuration for ir-full and removal of tasmota-ircustom [#12428](https://github.com/arendst/Tasmota/issues/12428)
diff --git a/info/xdrv_57_tasmesh.md b/info/xdrv_57_tasmesh.md
new file mode 100644
index 000000000..47dc918ed
--- /dev/null
+++ b/info/xdrv_57_tasmesh.md
@@ -0,0 +1,51 @@
+# TASMESH
+
+This driver provides the ability to move TASMOTA-devices out of the WLAN by using ESP-NOW to communicate bidirectional with an internal protocol.
+
+Thus the workload for the WLAN-router is reduced and with the reduced overhead the local 2.4-GHz-band will be freed of some traffic. Power consumption of the nodes will be reduced significantly allowing better battery powered projects with TASMOTA.
+Automatic payload encryption is applied using the WiFi-password1 as the key. A maximum of 32 bytes of this password is used for the ChaCha20Poly1305 authenticated encryption as the key.
+
+As ACK/NACK messages seem to be not reliable on both ESP-platforms, the method "send-and-pray" is used.
+
+
+## Working priciple
+
+An ESP32 is needed as gateway/broker to connect the nodes (typically an ESP8266) to the WLAN. The ESP32 will receive the MQTT-topic of every node and subscribe to it as a proxy.
+If a MQTT-message in the form of 'cmnd/node_topic/...' is received, the broker will automatically send this to the referring node via ESP-NOW.
+The broker will automatically send time messages to all nodes.
+
+The nodes will send their MQTT-messages back to the broker via ESP-NOW.
+
+## Enabling the driver
+
+Add ``#define USE_TASMESH`` to your file ``user_config_override.h`` before compilation.
+
+## Commands
+
+``MeshBroker`` - starts the broker on the ESP32, printing out the MAC and used WiFi-channel to the log. Must be called after WiFi is initialized!! Example 'Rule1 on system#boot do meshbroker endon'
+
+``MeshChannel 1..13`` - changes the WiFi-channel (on the node) to n (1-13) according to the channel of the (ESP32-)broker.
+
+``MeshNode AA:BB:CC:DD:EE:FF`` - starts a node and connects the the broker with the given MAC-address, will automatically send MQTT-topic to the broker
+
+``MeshPeer AA:BB:CC:DD:EE:FF`` - usable to add a known node to another node to be able to send data via the mesh to the broker, that may be out of reach
+
+``MeshInterval 2..200`` - changes the interval between mesh messages default set to 50 ms
+
+## Rules
+
+Rules examples:
+
+- The broker must be started after wifi is up!!``rule1 on system#boot do meshbroker endon``
+- The node may be started as soon as possible. Once started wifi and webserver are disabled by design``rule1 on system#init do meshnode 98:F4:AB:6D:2D:B5 endon``
+- Add a known peer (another node in the mesh) after the node has initialized``rule3 on mesh#node=1 do meshpeer 2cf4323cdb33 endon``
+
+## Limitations
+
+The following limitations apply:
+- An ESP32 is only supported as a broker
+- An ESP8266 is only supported as a node
+- No command persistence is implemented so use rules to start a broker or a node
+- Although node send queues are implemented there is no node receive queue so MQTT commands send to the node need to be as small as possible limited to a maximum of around 160 characters including the topic
+- Although broker receive queues are implemented there is no broker send queue so MQTT commands send to the node need to be as small as possible limited to a maximum of around 160 characters including the topic
+- As there is no direct connection from the node to the MQTT broker it will signal the node as LWT Offline
diff --git a/tasmota/xdrv_57_1_tasmesh_support.ino b/tasmota/xdrv_57_1_tasmesh_support.ino
new file mode 100644
index 000000000..81949af0f
--- /dev/null
+++ b/tasmota/xdrv_57_1_tasmesh_support.ino
@@ -0,0 +1,391 @@
+/*
+ xdrv_57_1_tasmesh_support.ino - Mesh via ESP-Now support for Tasmota
+
+ Copyright (C) 2021 Christian Baars and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_TASMESH
+
+#include
+#include
+
+#ifdef ESP32
+#include
+#include
+#else
+#include
+#endif //ESP32
+
+/*********************************************************************************************\
+ * constants
+\*********************************************************************************************/
+
+#define D_CMND_MESH "MESH"
+
+#define MESH_PAYLOAD_SIZE 160 // Default 160 - with header of 20 bytes and 16 bytes tag, stays below 200 bytes, which is reported to work with ESP8266
+#define MESH_TOPICSZ 64 // Max supported topic size
+#define MESH_BUFFERS 26 // (6) Max buffers number for splitted messages
+#define MESH_MAX_PACKETS 3 // (3) Max number of packets
+#define MESH_REFRESH 50 // Number of ms
+
+// ------------------------------------------------------------------------------------------------------------
+// | MAC Header | Category Code | Organization Identifier | Random Values | Vendor Specific Content | FCS |
+// ------------------------------------------------------------------------------------------------------------
+// 24 bytes 1 byte 3 bytes 4 bytes 7~255 bytes 4 bytes
+// -------------------------------------------------------------------------------
+// | Element ID | Length | Organization Identifier | Type | Version | Body |
+// -------------------------------------------------------------------------------
+// 1 byte 1 byte 3 bytes 1 byte 1 byte 0~250 bytes
+
+struct mesh_packet_t {
+ uint8_t sender[6]; // MAC
+ uint8_t receiver[6]; // MAC
+ uint32_t counter:4; // Rolling counter to identify a packet
+ uint32_t type:6; // Command, Mqtt, ...
+ uint32_t chunks:6; // Number of chunks
+ uint32_t chunk:6; // Chunk number
+ uint32_t chunkSize:8; // Chunk size
+ uint32_t TTL:2; // Time to live, counting down
+ union {
+ uint32_t senderTime; // UTC-timestamp from every sender in the MESH
+ uint32_t peerIndex; // Only for resending in the MESH
+ };
+ uint8_t tag[16]; // Tag for de/encryption
+ uint8_t payload[MESH_PAYLOAD_SIZE];
+} __attribute__((packed));
+
+struct mesh_packet_header_t { // ToDo: Maybe, we do not need this
+ uint8_t sender[6]; // MAC
+ uint8_t receiver[6]; // MAC
+ uint32_t counter:4; // Rolling counter to identify a packet
+ uint32_t type:6; // Command, Mqtt, ...
+ uint32_t chunks:6; // Number of chunks
+ uint32_t chunk:6; // Chunk number
+ uint32_t chunkSize:8; // Chunk size
+ uint32_t TTL:2; // Time to live, counting down
+ union {
+ uint32_t senderTime; // UTC-timestamp from every sender in the MESH
+ uint32_t peerIndex; // Only for resending in the MESH
+ };
+ uint8_t tag[16]; // Tag for de/encryption
+} __attribute__((packed));
+
+struct mesh_peer_t {
+ uint8_t MAC[6];
+ uint32_t lastMessageFromPeer; // Time of last message from peer
+#ifdef ESP32
+ char topic[MESH_TOPICSZ];
+#endif //ESP32
+};
+
+struct mesh_flags_t {
+ uint8_t brokerNeedsTopic:1;
+ uint8_t nodeGotTime:1;
+ uint8_t nodeWantsTime:1;
+ uint8_t nodeWantsTimeASAP:1;
+};
+
+struct mesh_packet_combined_t {
+ mesh_packet_header_t header;
+ uint32_t receivedChunks; // Bitmask for up to 32 chunks
+ char raw[MESH_PAYLOAD_SIZE * MESH_BUFFERS];
+};
+
+struct mesh_first_header_bytes { // TODO: evaluate random 4-byte-value of pre-packet
+ uint8_t raw[15];
+} __attribute__((packed));;
+
+struct {
+ uint32_t lastMessageFromBroker; // Time of last message from broker
+ uint32_t lmfap; // Yime of last message from any peer
+ uint8_t broker[6] = { 0 };
+ uint8_t key[32];
+ uint8_t role;
+ uint8_t channel; // Wifi channel
+ uint8_t interval;
+ uint8_t currentTopicSize;
+ mesh_flags_t flags;
+ mesh_packet_t sendPacket;
+ std::vector peers;
+ std::queue packetToResend;
+ std::queue packetToConsume;
+ std::vector packetsAlreadySended;
+ std::vector packetsAlreadyReceived;
+ std::vector multiPackets;
+#ifdef ESP32
+ std::vector lastTeleMsgs;
+#endif //ESP32
+} MESH;
+
+/*********************************************************************************************\
+ * Declarations for functions with custom types
+\*********************************************************************************************/
+
+void MESHsendPacket(mesh_packet_t *_packet);
+bool MESHencryptPayload(mesh_packet_t *_packet, int _encrypt); // 1 encryption, 0 decryption
+
+/*********************************************************************************************\
+ * enumerations
+\*********************************************************************************************/
+
+enum MESH_Commands { // commands useable in console or rules
+ CMND_MESH_BROKER, // start broker on ESP32
+ CMND_MESH_NODE, // start node and connect to broker based on MAC address
+ CMND_MESH_PEER, // add node to peer list of a broker or node
+ CMND_MESH_CHANNEL}; // set wifi channel on node (the broker gets it automatically from the AP)
+
+enum MESH_Role {
+ ROLE_NONE = 0, // not initialized
+ ROLE_BROKER, // ESP32 will connect mesh to WLAN
+ ROLE_NODE_FULL, // Node will listen and resend every message for MESH functionality
+ ROLE_NODE_SMALL // Node will only talk to the broker
+};
+
+enum MESH_Packet_Type { // Type of packet
+ PACKET_TYPE_TIME = 0, //
+ PACKET_TYPE_PEERLIST, // send all kown peers, broker is always 0
+ PACKET_TYPE_COMMAND, // not used yet
+ PACKET_TYPE_REGISTER_NODE, // register a node with encrypted broker-MAC, announce mqtt topic to ESP32-proxy - broker will send time ASAP
+ PACKET_TYPE_REFRESH_NODE, // refresh node infos with encrypted broker-MAC, announce mqtt topic to ESP32-proxy - broker will send time slightly delayed
+ PACKET_TYPE_MQTT, // send regular mqtt messages, single or multipackets
+ PACKET_TYPE_WANTTOPIC // the broker has no topic for this peer/node
+};
+
+/*********************************************************************************************\
+ *
+\*********************************************************************************************/
+
+#ifdef ESP32
+
+void MESHsendTime(void) { // Only from broker to nodes
+ MESH.sendPacket.counter++;
+ MESH.sendPacket.type = PACKET_TYPE_TIME;
+ MESH.sendPacket.TTL = 1;
+ // memcpy(MESH.sendPacket.receiver,MESH.peers[_peerNumber].MAC,6);
+ MESH.sendPacket.senderTime = Rtc.utc_time;
+ MESH.sendPacket.payload[0] = 0;
+ // mesh_flags_t *_flags = (mesh_flags_t *)MESH.sendPacket.payload;
+ // if(MESH.peers[_peerNumber].topic[0]==0){
+ // AddLog(LOG_LEVEL_INFO, PSTR("MSH: Broker wants topic from peer: %u"), _peerNumber);
+ // _flags->brokerNeedsTopic = 1;
+ // }
+ MESH.sendPacket.chunkSize = 0;
+ MESH.sendPacket.chunks = 0;
+ MESHsendPacket(&MESH.sendPacket);
+}
+
+void MESHdemandTopic(uint32_t _peerNumber) {
+ MESH.sendPacket.counter++;
+ MESH.sendPacket.type = PACKET_TYPE_WANTTOPIC;
+ MESH.sendPacket.TTL = 2;
+ MESH.sendPacket.payload[0] = 0;
+ MESH.sendPacket.chunkSize = 0;
+ MESH.sendPacket.chunks = 0;
+ memcpy(MESH.sendPacket.receiver,MESH.peers[_peerNumber].MAC,6);
+ MESHsendPacket(&MESH.sendPacket);
+}
+
+#endif //ESP32
+
+void MESHsendPeerList(void) { // We send this list only to the peers, that can directly receive it
+ MESH.sendPacket.counter++;
+ MESH.sendPacket.type = PACKET_TYPE_PEERLIST;
+ MESH.sendPacket.senderTime = Rtc.utc_time;
+ uint32_t _idx = 0;
+ for (auto &_peer : MESH.peers) {
+ memcpy(MESH.sendPacket.payload + _idx, _peer.MAC, 6);
+ _idx += 6;
+ }
+ if (0 == _idx) { return; }
+
+ MESH.sendPacket.chunk = 0;
+ MESH.sendPacket.chunks = 1;
+ MESH.sendPacket.chunkSize = _idx;
+ MESH.sendPacket.TTL = 1;
+// AddLogBuffer(LOG_LEVEL_INFO, MESH.sendPacket.payload, MESH.sendPacket.chunkSize);
+ MESHsendPacket(&MESH.sendPacket);
+}
+
+bool MESHcheckPeerList(const uint8_t *MAC) {
+ bool success = false;
+ for (auto &_peer : MESH.peers) {
+ if (memcmp(_peer.MAC, MAC, 6) == 0) {
+ _peer.lastMessageFromPeer = millis();
+ return true;
+ }
+ }
+ return false;
+}
+
+uint8_t MESHcountPeers(void) {
+#ifdef ESP32
+ esp_now_peer_num_t _peernum;
+ esp_now_get_peer_num(&_peernum);
+ uint8_t _num = _peernum.total_num;
+#else
+ uint8_t _num;
+ uint8_t _numEnc; // wo don't care
+ esp_now_get_cnt_info(&_num, &_numEnc);
+#endif
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Peers %u"), _num);
+ return _num;
+}
+
+int MESHaddPeer(uint8_t *_MAC ) {
+ mesh_peer_t _newPeer;
+ memcpy(_newPeer.MAC, _MAC, 6);
+ _newPeer.lastMessageFromPeer = millis();
+#ifdef ESP32
+ _newPeer.topic[0] = 0;
+#endif
+ MESH.peers.push_back(_newPeer);
+#ifdef ESP32
+ std::string _msg = "{\"Init\":1}"; // Init with a simple JSON only while developing
+ MESH.lastTeleMsgs.push_back(_msg); // We must keep this vector in sync with the peers-struct on the broker regarding the indices
+#endif //ESP32
+ int err;
+#ifdef ESP32
+ esp_now_peer_info_t _peer;
+ _peer.channel = MESH.channel;
+ _peer.encrypt = false;
+ _peer.ifidx = (wifi_interface_t)ESP_IF_WIFI_AP;
+ memcpy(_peer.peer_addr, _MAC, 6);
+ err = esp_now_add_peer(&_peer);
+#else
+ err = esp_now_add_peer(_MAC, ESP_NOW_ROLE_COMBO, MESH.channel, NULL, 0);
+#endif
+ if (0 == err) {
+ char _peerMAC[18];
+ ToHex_P(_MAC, 6, _peerMAC, 18, ':');
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Peer %s added successful"), _peerMAC);
+ #ifdef ESP32
+ if (ROLE_BROKER == MESH.role) { MESHsendTime(); }
+ #endif //ESP32
+ Response_P(PSTR("{\"%s\":{\"Peers\":%u}}"), D_CMND_MESH, MESH.peers.size());
+ XdrvRulesProcess(0);
+ return err;
+ } else {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Failed to add peer %d"), err);
+ }
+ return err;
+}
+
+//helper functions
+void MESHstripColon(char* _string) {
+ uint32_t _length = strlen(_string);
+ uint32_t _index = 0;
+ while (_index < _length) {
+ char c = _string[_index];
+ if (c == ':') {
+ memmove(_string + _index, _string + _index +1, _length - _index);
+ }
+ _index++;
+ }
+ _string[_index] = 0;
+}
+
+void MESHHexStringToBytes(char* _string, uint8_t _MAC[]) { //uppercase
+ MESHstripColon(_string);
+ UpperCase(_string, _string);
+ uint32_t index = 0;
+ uint32_t _end = strlen(_string);
+ memset(_MAC, 0, _end / 2);
+ while (index < _end) {
+ char c = _string[index];
+ uint8_t value = 0;
+ if ((c >= '0') && (c <= '9')) {
+ value = (c - '0');
+ }
+ else if ((c >= 'A') && (c <= 'F')) {
+ value = (10 + (c - 'A'));
+ }
+ _MAC[(index / 2)] += value << (((index + 1) % 2) * 4);
+ index++;
+ }
+}
+
+void MESHsendPacket(mesh_packet_t *_packet) {
+ MESHencryptPayload(_packet, 1);
+// esp_now_send(_packet->receiver, (uint8_t *)_packet, sizeof(MESH.sendPacket) - MESH_PAYLOAD_SIZE + _packet->chunkSize);
+ esp_now_send(NULL, (uint8_t *)_packet, sizeof(MESH.sendPacket) - MESH_PAYLOAD_SIZE + _packet->chunkSize); //NULL -> broadcast
+}
+
+void MESHsetKey(uint8_t* _key) { // Must be 32 bytes!!!
+ char* _pw = SettingsText(SET_STAPWD1 + Settings->sta_active);
+ size_t _length = strlen(_pw);
+ memset(_key, 0, 32);
+ if (_length > 32) { _length = 32; }
+ memcpy(_key, _pw, _length);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: set crypto key to PASSWORD1"));
+}
+
+bool MESHencryptPayload(mesh_packet_t *_packet, int _encrypt) {
+
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: will encrypt: %u"), _encrypt);
+
+ size_t _size = _packet->chunkSize;
+ char _tag[16];
+
+// AddLog(LOG_LEVEL_DEBUG, PSTR("cc: %u, _size: %u"), _counter,_size);
+// AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)_tag,16);
+
+ br_chacha20_run bc = br_chacha20_ct_run;
+
+ br_poly1305_ctmul32_run((void*)MESH.key, (const void *)_packet,
+ (void *)_packet->payload, _size, _packet->receiver+6, 2,
+ _tag, bc, _encrypt);
+
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: encryption done "));
+
+ if (_encrypt==1) {
+ memcpy(_packet->tag, _tag, 16);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: payload encrypted"));
+ return true;
+ }
+ if (memcmp(_packet->tag, _tag, 16) == 0) {
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: payload decrypted"));
+ return true;
+ }
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: payload decryption error"));
+ return false;
+}
+
+void MESHsetSleep(void) {
+ if (MESH.role && (Settings->sleep > MESH.interval)) {
+ Settings->sleep = MESH.interval;
+ TasmotaGlobal.sleep = MESH.interval;
+ }
+}
+
+void MESHsetWifi(bool state) {
+#ifdef ESP8266 // Only ESP8266 as ESP32 is a broker and needs Wifi
+ if (state) { // Wifi On
+ Settings->flag4.network_wifi = 1; // (Re-)enable wifi as long as Mesh is not enabled
+// TasmotaGlobal.global_state.wifi_down = 0;
+ Settings->flag.global_state = 0; // (Wifi, MQTT) Control link led blinking (1)
+ } else { // Wifi Off and use ESP-NOW
+ Settings->flag4.network_wifi = 0; // The "old" wifi off command
+ TasmotaGlobal.global_state.wifi_down = 1;
+ Settings->flag.global_state = 1; // (Wifi, MQTT) Control link led blinking (1)
+ }
+#endif // ESP8266
+}
+
+uint32_t MESHmaxPayloadSize(void) {
+ return MESH_PAYLOAD_SIZE - MESH.currentTopicSize -1;
+}
+
+#endif //USE_TASMESH
diff --git a/tasmota/xdrv_57_9_tasmesh.ino b/tasmota/xdrv_57_9_tasmesh.ino
new file mode 100644
index 000000000..a56b2f4c4
--- /dev/null
+++ b/tasmota/xdrv_57_9_tasmesh.ino
@@ -0,0 +1,846 @@
+/*
+ xdrv_57_tasmesh.ino - Mesh support for Tasmota using ESP-Now
+
+ Copyright (C) 2021 Christian Baars, Federico Leoni and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*
+ --------------------------------------------------------------------------------------------
+ Version yyyymmdd Action Description
+ --------------------------------------------------------------------------------------------
+ 0.9.5.1 20210622 integrate Expand number of chunks to satisfy larger MQTT messages
+ Refactor to latest Tasmota standards
+ ---
+ 0.9.4.1 20210503 integrate Add some minor tweak for channel management by Federico Leoni
+ ---
+ 0.9.0.0 20200927 started From scratch by Christian Baars
+*/
+
+#ifdef USE_TASMESH
+
+/*********************************************************************************************\
+* Build a mesh of nodes using ESP-Now
+* Connect it through an ESP32-broker to WLAN
+\*********************************************************************************************/
+
+#define XDRV_57 57
+
+/*********************************************************************************************\
+ * Callbacks
+\*********************************************************************************************/
+
+#ifdef ESP32
+
+void CB_MESHDataSent(const uint8_t *MAC, esp_now_send_status_t sendStatus);
+void CB_MESHDataSent(const uint8_t *MAC, esp_now_send_status_t sendStatus) {
+ char _destMAC[18];
+ ToHex_P(MAC, 6, _destMAC, 18, ':');
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Sent to %s status %d"), _destMAC, sendStatus);
+}
+
+void CB_MESHDataReceived(const uint8_t *MAC, const uint8_t *packet, int len) {
+ static bool _locked = false;
+ if (_locked) { return; }
+
+ _locked = true;
+ char _srcMAC[18];
+ ToHex_P(MAC, 6, _srcMAC, 18, ':');
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Rcvd from %s"), _srcMAC);
+ mesh_packet_t *_recvPacket = (mesh_packet_t*)packet;
+ if ((_recvPacket->type == PACKET_TYPE_REGISTER_NODE) || (_recvPacket->type == PACKET_TYPE_REFRESH_NODE)) {
+ if (MESHcheckPeerList((const uint8_t *)MAC) == false) {
+ MESHencryptPayload(_recvPacket, 0); //decrypt it and check
+ if (memcmp(_recvPacket->payload, MESH.broker, 6) == 0) {
+ MESHaddPeer((uint8_t*)MAC);
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: Rcvd topic %s"), (char*)_recvPacket->payload + 6);
+// AddLogBuffer(LOG_LEVEL_INFO,(uint8_t *)&MESH.packetToConsume.front().payload,MESH.packetToConsume.front().chunkSize+5);
+ for (auto &_peer : MESH.peers) {
+ if (memcmp(_peer.MAC, _recvPacket->sender, 6) == 0) {
+ strcpy(_peer.topic, (char*)_recvPacket->payload + 6);
+ MESHsubscribe((char*)&_peer.topic);
+ _locked = false;
+ return;
+ }
+ }
+ } else {
+ char _cryptMAC[18];
+ ToHex_P(_recvPacket->payload, 6, _cryptMAC, 18, ':');
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Peer %s denied, wrong MAC %s"), _srcMAC, _cryptMAC);
+ _locked = false;
+ return;
+ }
+ } else {
+ if (_recvPacket->type == PACKET_TYPE_REGISTER_NODE) {
+ MESH.flags.nodeWantsTimeASAP = 1; //this could happen after wake from deepsleep on battery powered device
+ } else {
+ MESH.flags.nodeWantsTime = 1;
+ }
+ }
+ }
+ MESH.lmfap = millis();
+ if (MESHcheckPeerList(MAC) == true){
+ MESH.packetToConsume.push(*_recvPacket);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Packet %d from %s to queue"), MESH.packetToConsume.size(), _srcMAC);
+ }
+ _locked = false;
+}
+
+#else // ESP8266
+
+void CB_MESHDataSent(uint8_t *MAC, uint8_t sendStatus) {
+ char _destMAC[18];
+ ToHex_P(MAC, 6, _destMAC, 18, ':');
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Sent to %s status %d"), _destMAC, sendStatus);
+}
+
+void CB_MESHDataReceived(uint8_t *MAC, uint8_t *packet, uint8_t len) {
+ MESH.lmfap = millis(); //any peer
+ if (memcmp(MAC, MESH.broker, 6) == 0) {
+ MESH.lastMessageFromBroker = millis(); //directly from the broker
+ }
+ mesh_packet_t *_recvPacket = (mesh_packet_t*)packet;
+ switch (_recvPacket->type) {
+ case PACKET_TYPE_TIME:
+ Rtc.utc_time = _recvPacket->senderTime;
+ Rtc.user_time_entry = true;
+ MESH.lastMessageFromBroker = millis();
+ if (MESH.flags.nodeGotTime == 0) {
+ RtcSync();
+ TasmotaGlobal.rules_flag.system_boot = 1; // for now we consider the node booted and let trigger system#boot on RULES
+ }
+ MESH.flags.nodeGotTime = 1;
+ //Wifi.retry = 0;
+ // Response_P(PSTR("{\"%s\":{\"Time\":1}}"), D_CMND_MESH); //got the time, now we can publish some sensor data
+ // XdrvRulesProcess();
+ break;
+ case PACKET_TYPE_PEERLIST:
+ MESH.packetToConsume.push(*_recvPacket);
+ return;
+ break;
+ default:
+ // nothing for now;
+ break;
+ }
+ if (memcmp(_recvPacket->receiver, MESH.sendPacket.sender, 6) != 0) { //MESH.sendPacket.sender simply stores the MAC of the node
+ if (ROLE_NODE_SMALL == MESH.role) {
+ return; // a 'small node' does not perform mesh functions
+ }
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Packet to resend ..."));
+ MESH.packetToResend.push(*_recvPacket);
+ return;
+ } else {
+ if (_recvPacket->type == PACKET_TYPE_WANTTOPIC) {
+ MESH.flags.brokerNeedsTopic = 1;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Broker needs topic ..."));
+ return; //nothing left to be done
+ }
+ // for(auto &_message : MESH.packetsAlreadyReceived){
+ // if(memcmp(_recvPacket,_message,15==0)){
+ // AddLog(LOG_LEVEL_INFO, PSTR("MSH: Packet already received"));
+ // return;
+ // }
+ // }
+ // MESH.packetsAlreadyReceived.push_back((mesh_packet_header_t*) _recvPacket);
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Packet to consume ..."));
+ MESH.packetToConsume.push(*_recvPacket);
+ }
+}
+
+#endif // ESP32
+
+/*********************************************************************************************\
+ * init driver
+\*********************************************************************************************/
+
+void MESHInit(void) {
+ MESH.interval = MESH_REFRESH;
+ MESH.role = ROLE_NONE;
+ MESH.packetsAlreadyReceived.reserve(5);
+ MESH.peers.reserve(10);
+ MESH.multiPackets.reserve(2);
+
+ MESH.sendPacket.counter = 0;
+ MESH.sendPacket.chunks = 1;
+ MESH.sendPacket.chunk = 0;
+ MESH.sendPacket.type = PACKET_TYPE_TIME;
+ MESH.sendPacket.TTL = 2;
+
+ MESHsetWifi(1); // (Re-)enable wifi as long as Mesh is not enabled
+
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: Initialized"));
+}
+
+void MESHdeInit(void) {
+#ifdef ESP8266 // only ESP8266, ESP32 as a broker should not use deepsleep
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: Stopping"));
+ // TODO: degister from the broker, so he can stop MQTT-proxy
+ esp_now_deinit();
+#endif // ESP8266
+}
+
+/*********************************************************************************************\
+ * MQTT proxy functions
+\*********************************************************************************************/
+
+#ifdef ESP32
+
+/**
+ * @brief Subscribes as a proxy
+ *
+ * @param topic - received from the referring node
+ */
+void MESHsubscribe(char *topic) {
+ char stopic[TOPSZ];
+ GetTopic_P(stopic, CMND, topic, PSTR("#"));
+ MqttSubscribe(stopic);
+}
+
+void MESHunsubscribe(char *topic) {
+ char stopic[TOPSZ];
+ GetTopic_P(stopic, CMND, topic, PSTR("#"));
+ MqttUnsubscribe(stopic);
+}
+
+void MESHconnectMQTT(void){
+ for (auto &_peer : MESH.peers) {
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: Reconnect topic %s"), _peer.topic);
+ if (_peer.topic[0] != 0) {
+ MESHsubscribe(_peer.topic);
+ }
+ }
+}
+
+/**
+ * @brief Intercepts mqtt message, that the broker (ESP32) subscribes to as a proxy for a node.
+ * Is called from xdrv_02_mqtt.ino. Will send the message in the payload via ESP-NOW.
+ *
+ * @param _topic
+ * @param _data
+ * @param data_len
+ * @return true
+ * @return false
+ */
+bool MESHinterceptMQTTonBroker(char* _topic, uint8_t* _data, unsigned int data_len) {
+ if (MESH.role != ROLE_BROKER) { return false; }
+
+ char stopic[TOPSZ];
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Intercept topic %s"), _topic);
+ for (auto &_peer : MESH.peers) {
+ GetTopic_P(stopic, CMND, _peer.topic, PSTR("")); //cmnd/topic/
+ if (strlen(_topic) != strlen(_topic)) {
+ return false; // prevent false result when _topic is the leading substring of stopic
+ }
+ if (memcmp(_topic, stopic, strlen(stopic)) == 0) {
+ MESH.sendPacket.chunkSize = strlen(_topic) +1;
+
+ if (MESH.sendPacket.chunkSize + data_len > MESH_PAYLOAD_SIZE) {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Intercept payload oversized %d"), data_len);
+ return false;
+ }
+
+ memcpy(MESH.sendPacket.receiver, _peer.MAC, 6);
+ memcpy(MESH.sendPacket.payload, _topic, MESH.sendPacket.chunkSize);
+ memcpy(MESH.sendPacket.payload + MESH.sendPacket.chunkSize, _data, data_len);
+ MESH.sendPacket.chunkSize += data_len;
+ MESH.sendPacket.chunks = 1;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Intercept payload '%s'"), MESH.sendPacket.payload);
+ MESH.sendPacket.type = PACKET_TYPE_MQTT;
+ MESH.sendPacket.senderTime = Rtc.utc_time;
+ MESHsendPacket(&MESH.sendPacket);
+ // int result = esp_now_send(MESH.sendPacket.receiver, (uint8_t *)&MESH.sendPacket, (sizeof(MESH.sendPacket))-(MESH_PAYLOAD_SIZE-MESH.sendPacket.chunkSize));
+ //send to Node
+ return true;
+ }
+ }
+ return false;
+}
+
+#else // ESP8266
+
+void MESHreceiveMQTT(mesh_packet_t *_packet);
+void MESHreceiveMQTT(mesh_packet_t *_packet){
+ uint32_t _slength = strlen((char*)_packet->payload);
+ if (_packet->chunks == 1) { //single chunk message
+ MqttDataHandler((char*)_packet->payload, (uint8_t*)(_packet->payload)+_slength+1, (_packet->chunkSize)-_slength);
+ } else {
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: Multiple chunks %u not supported yet"), _packet->chunks);
+ // TODO: reconstruct message in buffer or only handle short messages
+ }
+}
+
+#endif // ESP32
+
+bool MESHroleNode(void) {
+ return (MESH.role > ROLE_BROKER);
+}
+
+/**
+ * @brief Redirects the mqtt message on the node just before it would have been sended to
+ * the broker via ESP-NOW
+ *
+ * @param _topic
+ * @param _data
+ * @param _retained - currently unused
+ * @return true
+ * @return false
+ */
+bool MESHrouteMQTTtoMESH(const char* _topic, char* _data, bool _retained) {
+ if (!MESHroleNode()) { return false; }
+
+ size_t _bytesLeft = strlen(_topic) + strlen(_data) +2;
+ MESH.sendPacket.counter++;
+ MESH.sendPacket.chunk = 0;
+ MESH.sendPacket.chunks = ((_bytesLeft+2) / MESH_PAYLOAD_SIZE) +1;
+ memcpy(MESH.sendPacket.receiver, MESH.broker, 6);
+ MESH.sendPacket.type = PACKET_TYPE_MQTT;
+ MESH.sendPacket.chunkSize = MESH_PAYLOAD_SIZE;
+ MESH.sendPacket.peerIndex = 0;
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Chunks %u, Counter %u"), MESH.sendPacket.chunks, MESH.sendPacket.counter);
+ size_t _topicSize = strlen(_topic) +1;
+ size_t _offsetData = 0;
+ while (_bytesLeft > 0) {
+ size_t _byteLeftInChunk = MESH_PAYLOAD_SIZE;
+ // MESH.sendPacket.chunkSize = MESH_PAYLOAD_SIZE;
+ if (MESH.sendPacket.chunk == 0) {
+ memcpy(MESH.sendPacket.payload, _topic, _topicSize);
+ MESH.sendPacket.chunkSize = _topicSize;
+
+ MESH.currentTopicSize = MESH.sendPacket.chunkSize;
+
+ _bytesLeft -= _topicSize;
+ _byteLeftInChunk -= _topicSize;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Topic in payload '%s'"), (char*)MESH.sendPacket.payload);
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: After topic -> chunk:%u, pre-size: %u"),MESH.sendPacket.chunk,MESH.sendPacket.chunkSize);
+ }
+ if (_byteLeftInChunk > 0) {
+ if (_byteLeftInChunk > _bytesLeft) {
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: only last chunk bL:%u bLiC:%u oSD:%u"),_bytesLeft,_byteLeftInChunk,_offsetData);
+ _byteLeftInChunk = _bytesLeft;
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: only last chunk after correction -> chunk:%u, pre-size: %u"),MESH.sendPacket.chunk,MESH.sendPacket.chunkSize);
+ }
+ if (MESH.sendPacket.chunk > 0) { _topicSize = 0; }
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: %u"),_topicSize);
+ memcpy(MESH.sendPacket.payload + _topicSize, _data + _offsetData, _byteLeftInChunk);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Data in payload '%s'"), (char*)MESH.sendPacket.payload + _topicSize);
+ _offsetData += _byteLeftInChunk;
+ _bytesLeft -= _byteLeftInChunk;
+ }
+ MESH.sendPacket.chunkSize += _byteLeftInChunk;
+ MESH.packetToResend.push(MESH.sendPacket);
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: chunk:%u, size: %u"),MESH.sendPacket.chunk,MESH.sendPacket.chunkSize);
+// AddLogBuffer(LOG_LEVEL_INFO, (uint8_t*)MESH.sendPacket.payload, MESH.sendPacket.chunkSize);
+ if (MESH.sendPacket.chunk == MESH.sendPacket.chunks) {
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: Too many chunks %u"), MESH.sendPacket.chunk +1);
+ }
+
+ SHOW_FREE_MEM(PSTR("MESHrouteMQTTtoMESH"));
+
+ MESH.sendPacket.chunk++;
+ MESH.sendPacket.chunkSize = 0;
+ }
+ return true;
+}
+
+/**
+ * @brief The node sends its mqtt topic to the broker
+ *
+ */
+void MESHregisterNode(uint8_t mode){
+ memcpy(MESH.sendPacket.receiver, MESH.broker, 6); // First 6 bytes -> MAC of broker
+ strcpy((char*)MESH.sendPacket.payload +6, TasmotaGlobal.mqtt_topic); // Remaining bytes -> topic of node
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Register node with topic '%s'"), (char*)MESH.sendPacket.payload +6);
+ MESH.sendPacket.TTL = 2;
+ MESH.sendPacket.chunks = 1;
+ MESH.sendPacket.chunk = 0;
+ MESH.sendPacket.chunkSize = strlen(TasmotaGlobal.mqtt_topic) + 1 + 6;
+ memcpy(MESH.sendPacket.payload, MESH.broker, 6);
+ MESH.sendPacket.type = (mode == 0) ? PACKET_TYPE_REGISTER_NODE : PACKET_TYPE_REFRESH_NODE;
+ MESHsendPacket(&MESH.sendPacket);
+}
+
+/*********************************************************************************************\
+ * Generic functions
+\*********************************************************************************************/
+
+void MESHstartNode(int32_t _channel, uint8_t _role){ //we need a running broker with a known channel at that moment
+#ifdef ESP8266 // for now only ESP8266, might be added for the ESP32 later
+ MESH.channel = _channel;
+ WiFi.mode(WIFI_STA);
+ WiFi.begin("", "", MESH.channel, nullptr, false); //fake connection attempt to set channel
+ wifi_promiscuous_enable(1);
+ wifi_set_channel(MESH.channel);
+ wifi_promiscuous_enable(0);
+ WiFi.disconnect();
+ MESHsetWifi(0);
+ if (esp_now_init() != 0) {
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: Node init failed"));
+ return;
+ }
+
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: Node initialized, channel: %u"),wifi_get_channel()); //check if we succesfully set the
+ Response_P(PSTR("{\"%s\":{\"Node\":1,\"Channel\":%u,\"Role\":%u}}"), D_CMND_MESH, wifi_get_channel(), _role);
+ XdrvRulesProcess(0);
+
+ esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
+ esp_now_register_send_cb(CB_MESHDataSent);
+ esp_now_register_recv_cb(CB_MESHDataReceived);
+
+ MESHsetKey(MESH.key);
+ memcpy(MESH.sendPacket.receiver, MESH.broker, 6);
+ WiFi.macAddress(MESH.sendPacket.sender);
+ MESHaddPeer(MESH.broker); //must always be peer 0!! -return code -7 for peer list full
+ MESHcountPeers();
+ MESH.lastMessageFromBroker = millis(); // Init
+ MESH.role = (0 == _role) ? ROLE_NODE_SMALL : ROLE_NODE_FULL;
+ MESHsetSleep();
+ MESHregisterNode(0);
+#endif // ESP8266
+}
+
+void MESHstartBroker(void) { // Must be called after WiFi is initialized!! Rule - on system#boot do meshbroker endon
+#ifdef ESP32
+ WiFi.mode(WIFI_AP_STA);
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: Broker MAC %s"), WiFi.softAPmacAddress().c_str());
+ WiFi.softAPmacAddress(MESH.broker); //set MESH.broker to the needed MAC
+ uint32_t _channel = WiFi.channel();
+
+ if (esp_now_init() != 0) {
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: Broker init failed"));
+ return;
+ }
+
+ Response_P(PSTR("{\"%s\":{\"Broker\":1,\"Channel\":%u}}"), D_CMND_MESH, _channel);
+ XdrvRulesProcess(0);
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: Broker initialized on channel %u"), _channel);
+ esp_now_register_send_cb(CB_MESHDataSent);
+ esp_now_register_recv_cb(CB_MESHDataReceived);
+ MESHsetKey(MESH.key);
+ MESHcountPeers();
+ memcpy(MESH.sendPacket.sender, MESH.broker, 6);
+ MESH.role = ROLE_BROKER;
+ MESHsetSleep();
+#endif // ESP32
+}
+
+/*********************************************************************************************\
+ * Main loops
+\*********************************************************************************************/
+
+#ifdef ESP32
+
+void MESHevery50MSecond(void) {
+ // if (MESH.packetToResend.size() > 0) {
+ // // pass the packets
+ // }
+ if (MESH.packetToConsume.size() > 0) {
+// AddLog(LOG_LEVEL_DEBUG, PSTR("_"));
+// AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t *)&MESH.packetToConsume.front(), 15);
+ for (auto &_headerBytes : MESH.packetsAlreadyReceived) {
+// AddLog(LOG_LEVEL_DEBUG, PSTR("."));
+// AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t *)_headerBytes.raw, 15);
+ if (memcmp(MESH.packetToConsume.front().sender, _headerBytes.raw, 15) == 0) {
+ MESH.packetToConsume.pop();
+ return;
+ }
+ }
+ mesh_first_header_bytes _bytes;
+ memcpy(_bytes.raw, &MESH.packetToConsume.front(), 15);
+ MESH.packetsAlreadyReceived.push_back(_bytes);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("..."));
+// AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t *)_bytes.raw, 15);
+ if (MESH.packetsAlreadyReceived.size() > MESH_MAX_PACKETS) {
+ MESH.packetsAlreadyReceived.erase(MESH.packetsAlreadyReceived.begin());
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Erase received data"));
+ }
+
+ // do something on the node
+ // AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t *)&MESH.packetToConsume.front(), 30);
+
+ MESHencryptPayload(&MESH.packetToConsume.front(), 0);
+ switch (MESH.packetToConsume.front().type) {
+ // case PACKET_TYPE_REGISTER_NODE:
+ // AddLog(LOG_LEVEL_INFO, PSTR("MSH: received topic: %s"), (char*)MESH.packetToConsume.front().payload + 6);
+ // // AddLogBuffer(LOG_LEVEL_INFO,(uint8_t *)&MESH.packetToConsume.front().payload,MESH.packetToConsume.front().chunkSize+5);
+ // for(auto &_peer : MESH.peers){
+ // if(memcmp(_peer.MAC,MESH.packetToConsume.front().sender,6)==0){
+ // strcpy(_peer.topic,(char*)MESH.packetToConsume.front().payload+6);
+ // MESHsubscribe((char*)&_peer.topic);
+ // }
+ // }
+ // break;
+ case PACKET_TYPE_PEERLIST:
+ for (uint32_t i = 0; i < MESH.packetToConsume.front().chunkSize; i += 6) {
+ if (memcmp(MESH.packetToConsume.front().payload +i, MESH.sendPacket.sender, 6) == 0) {
+ continue; // Do not add myself
+ }
+ if (MESHcheckPeerList(MESH.packetToConsume.front().payload +i) == false) {
+ MESHaddPeer(MESH.packetToConsume.front().payload +i);
+ }
+ }
+ break;
+ case PACKET_TYPE_MQTT: // Redirected MQTT from node in packet [char* _space_ char*]
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: Received node output '%s'"), (char*)MESH.packetToConsume.front().payload);
+ if (MESH.packetToConsume.front().chunks > 1) {
+ bool _foundMultiPacket = false;
+ for (auto &_packet_combined : MESH.multiPackets) {
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: Append to multipacket"));
+ if (memcmp(_packet_combined.header.sender, MESH.packetToConsume.front().sender, 12) == 0) {
+ if (_packet_combined.header.counter == MESH.packetToConsume.front().counter) {
+ memcpy(_packet_combined.raw + (MESH.packetToConsume.front().chunk * MESH_PAYLOAD_SIZE), MESH.packetToConsume.front().payload, MESH.packetToConsume.front().chunkSize);
+ bitSet(_packet_combined.receivedChunks, MESH.packetToConsume.front().chunk);
+ _foundMultiPacket = true;
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: Multipacket rcvd chunk mask 0x%08X"), _packet_combined.receivedChunks);
+ }
+ }
+ uint32_t _temp = (1 << (uint8_t)MESH.packetToConsume.front().chunks) -1; //example: 1+2+4 == (2^3)-1
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: _temp: %u = %u"),_temp,_packet_combined.receivedChunks);
+ if (_packet_combined.receivedChunks == _temp) {
+ char * _data = (char*)_packet_combined.raw + strlen((char*)_packet_combined.raw) + 1;
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Publish multipacket"));
+ MqttPublishPayload((char*)_packet_combined.raw, _data);
+ MESH.multiPackets.erase(MESH.multiPackets.begin());
+ break;
+ }
+ }
+ if (!_foundMultiPacket) {
+ mesh_packet_combined_t _packet;
+ memcpy(_packet.header.sender, MESH.packetToConsume.front().sender, sizeof(_packet.header));
+ memcpy(_packet.raw + (MESH.packetToConsume.front().chunk * MESH_PAYLOAD_SIZE), MESH.packetToConsume.front().payload, MESH.packetToConsume.front().chunkSize);
+ _packet.receivedChunks = 0;
+ bitSet(_packet.receivedChunks, MESH.packetToConsume.front().chunk);
+ MESH.multiPackets.push_back(_packet);
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: New multipacket with chunks %u"), _packet.header.chunks);
+ }
+ } else {
+// AddLog(LOG_LEVEL_INFO, PSTR("MSH: chunk: %u size: %u"), MESH.packetToConsume.front().chunk, MESH.packetToConsume.front().chunkSize);
+// if (MESH.packetToConsume.front().chunk==0) AddLogBuffer(LOG_LEVEL_INFO,(uint8_t *)&MESH.packetToConsume.front().payload,MESH.packetToConsume.front().chunkSize);
+ char * _data = (char*)MESH.packetToConsume.front().payload + strlen((char*)MESH.packetToConsume.front().payload) +1;
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Publish packet"));
+ MqttPublishPayload((char*)MESH.packetToConsume.front().payload, _data);
+
+ uint32_t idx = 0;
+ for (auto &_peer : MESH.peers){
+ if (memcmp(_peer.MAC, MESH.packetToConsume.front().sender, 6) == 0) {
+ _peer.lastMessageFromPeer = millis();
+ MESH.lastTeleMsgs[idx] = std::string(_data);
+ break;
+ }
+ idx++;
+ }
+// AddLogBuffer(LOG_LEVEL_INFO,(uint8_t *)&MESH.packetToConsume.front().payload,MESH.packetToConsume.front().chunkSize);
+ }
+ break;
+ default:
+ AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)&MESH.packetToConsume.front(), MESH.packetToConsume.front().chunkSize +5);
+ break;
+ }
+ MESH.packetToConsume.pop();
+// AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Consumed one packet %u"), (char*)MESH.packetToConsume.size());
+ }
+}
+
+void MESHEverySecond(void) {
+ static uint32_t _second = 0;
+ _second++;
+ // send a time packet every x seconds
+ if (MESH.flags.nodeWantsTimeASAP) {
+ MESHsendTime();
+ MESH.flags.nodeWantsTimeASAP = 0;
+ return;
+ }
+ if (_second % 5 == 0) {
+ if ((MESH.flags.nodeWantsTime == 1) || (_second % 30 == 0)) { //every 5 seconds on demand or every 30 seconds anyway
+ MESHsendTime();
+ MESH.flags.nodeWantsTime = 0;
+ return;
+ }
+ }
+ uint32_t _peerNumber = _second%45;
+ if (_peerNumber < MESH.peers.size()) {
+ if (MESH.peers[_peerNumber].topic[0] == 0) {
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: Broker wants topic from peer %u"), _peerNumber);
+ MESHdemandTopic(_peerNumber);
+ }
+ }
+ if (MESH.multiPackets.size() > 3) {
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: Multi packets in buffer %u"), MESH.multiPackets.size());
+ MESH.multiPackets.erase(MESH.multiPackets.begin());
+ }
+}
+
+#else // ESP8266
+
+void MESHevery50MSecond(void) {
+ if (ROLE_NONE == MESH.role) { return; }
+
+ if (MESH.packetToResend.size() > 0) {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Next packet %d to resend of type %u, TTL %u"),
+ MESH.packetToResend.size(), MESH.packetToResend.front().type, MESH.packetToResend.front().TTL);
+ if (MESH.packetToResend.front().TTL > 0) {
+ MESH.packetToResend.front().TTL--;
+ if (memcmp(MESH.packetToResend.front().sender, MESH.broker, 6) != 0) { //do not send back the packet to the broker
+ MESHsendPacket(&MESH.packetToResend.front());
+ }
+ } else {
+ MESH.packetToResend.pop();
+ }
+ // pass the packets
+ }
+
+ if (MESH.packetToConsume.size() > 0) {
+ MESHencryptPayload(&MESH.packetToConsume.front(), 0);
+ switch (MESH.packetToConsume.front().type) {
+ case PACKET_TYPE_MQTT:
+ if (memcmp(MESH.packetToConsume.front().sender, MESH.sendPacket.sender, 6) == 0) {
+ //discard echo
+ break;
+ }
+ // AddLog(LOG_LEVEL_INFO, PSTR("MSH: node received topic: %s"), (char*)MESH.packetToConsume.front().payload);
+ MESHreceiveMQTT(&MESH.packetToConsume.front());
+ break;
+ case PACKET_TYPE_PEERLIST:
+ for (uint32_t i = 0; i < MESH.packetToConsume.front().chunkSize; i += 6) {
+ if (memcmp(MESH.packetToConsume.front().payload +i, MESH.sendPacket.sender, 6) == 0) {
+ continue; //do not add myself
+ }
+ if (MESHcheckPeerList(MESH.packetToConsume.front().payload +i) == false) {
+ MESHaddPeer(MESH.packetToConsume.front().payload +i);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ MESH.packetToConsume.pop();
+ }
+}
+
+void MESHEverySecond(void) {
+ if (MESH.role > ROLE_BROKER) {
+ if (MESH.flags.brokerNeedsTopic == 1) {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Broker wants topic"));
+ MESHregisterNode(1); //refresh info
+ MESH.flags.brokerNeedsTopic = 0;
+ }
+ if (millis() - MESH.lastMessageFromBroker > 31000) {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Broker not seen for >30 secs"));
+ MESHregisterNode(1); //refresh info
+ }
+ if (millis() - MESH.lastMessageFromBroker > 70000) {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MSH: Broker not seen for 70 secs, try to re-launch wifi"));
+ MESH.role = ROLE_NONE;
+ MESHsetWifi(1);
+ WifiBegin(3, MESH.channel);
+ }
+ }
+}
+
+#endif // ESP8266
+
+/*********************************************************************************************\
+ * Presentation
+\*********************************************************************************************/
+
+void MESHshow(bool json) {
+ if (json) {
+ if (ROLE_BROKER == MESH.role) {
+ ResponseAppend_P(PSTR(",\"MESH\":{\"channel\":%u"), MESH.channel);
+ ResponseAppend_P(PSTR(",\"nodes\":%u"),MESH.peers.size());
+ if (MESH.peers.size() > 0) {
+ ResponseAppend_P(PSTR(",\"MAC\":["));
+ bool comma = false;
+ for (auto &_peer : MESH.peers) {
+ char _MAC[18];
+ ToHex_P(_peer.MAC, 6, _MAC,18, ':');
+ ResponseAppend_P(PSTR("%s\"%s\""), (comma)?",":"", _MAC);
+ comma = true;
+ }
+ ResponseAppend_P(PSTR("]"));
+ }
+ ResponseJsonEnd();
+ }
+ } else {
+#ifdef ESP32 //web UI only on the the broker = ESP32
+ if (ROLE_BROKER == MESH.role) {
+// WSContentSend_PD(PSTR("TAS-MESH:
"));
+ WSContentSend_PD(PSTR("Broker MAC %s
"), WiFi.softAPmacAddress().c_str());
+ WSContentSend_PD(PSTR("Broker Channel %u
"), WiFi.channel());
+ uint32_t idx = 0;
+ for (auto &_peer : MESH.peers) {
+ char _MAC[18];
+ ToHex_P(_peer.MAC, 6, _MAC, 18, ':');
+ WSContentSend_PD(PSTR("Node MAC %s
"), _MAC);
+ WSContentSend_PD(PSTR("Node last message %u ms
"), millis() - _peer.lastMessageFromPeer);
+ WSContentSend_PD(PSTR("Node MQTT topic %s"), _peer.topic);
+/*
+ WSContentSend_PD(PSTR("Node MQTT topic: %s
"), _peer.topic);
+ if (MESH.lastTeleMsgs.size() > idx) {
+ char json_buffer[MESH.lastTeleMsgs[idx].length() +1];
+ strcpy(json_buffer, (char*)MESH.lastTeleMsgs[idx].c_str());
+ JsonParser parser(json_buffer);
+ JsonParserObject root = parser.getRootObject();
+ for (auto key : root) {
+ JsonParserObject subObj = key.getValue().getObject();
+ if (subObj) {
+ WSContentSend_PD(PSTR("%s:"), key.getStr());
+ for (auto subkey : subObj) {
+ WSContentSend_PD(PSTR(""), subkey.getStr(), subkey.getValue().getStr());
+ }
+ WSContentSend_PD(PSTR("
"));
+ } else {
+ WSContentSend_PD(PSTR(""), key.getStr(), key.getValue().getStr());
+ }
+ }
+// AddLog(LOG_LEVEL_INFO,PSTR("MSH: teleJSON %s"), (char*)MESH.lastTeleMsgs[idx].c_str());
+// AddLog(LOG_LEVEL_INFO,PSTR("MSH: stringsize: %u"),MESH.lastTeleMsgs[idx].length());
+ } else {
+// AddLog(LOG_LEVEL_INFO,PSTR("MSH: telemsgSize: %u"),MESH.lastTeleMsgs.size());
+ }
+*/
+ WSContentSend_PD(PSTR("
"));
+ idx++;
+ }
+ }
+#endif // ESP32
+ }
+}
+
+/*********************************************************************************************\
+ * Commands
+\*********************************************************************************************/
+
+const char kMeshCommands[] PROGMEM = "Mesh|" // Prefix
+ "Broker|Node|Peer|Channel|Interval";
+
+void (* const MeshCommand[])(void) PROGMEM = {
+ &CmndMeshBroker, &CmndMeshNode, &CmndMeshPeer, &CmndMeshChannel, &CmndMeshInterval };
+
+void CmndMeshBroker(void) {
+ MESH.channel = WiFi.channel(); // The Broker gets the channel from the router, no need to declare it with MESHCHANNEL (will be mandatory set it when ETH will be implemented)
+ MESHstartBroker();
+ ResponseCmndNumber(MESH.channel);
+}
+
+void CmndMeshNode(void) {
+ if (XdrvMailbox.data_len > 0) {
+ MESHHexStringToBytes(XdrvMailbox.data, MESH.broker);
+ if (XdrvMailbox.index != 0) { XdrvMailbox.index = 1; } // Everything not 0 is a full node
+ // meshnode FA:KE:AD:DR:ES:S1
+ bool broker = false;
+ char EspSsid[11];
+ String mac_address = XdrvMailbox.data;
+ snprintf_P(EspSsid, sizeof(EspSsid), PSTR("ESP_%s"), mac_address.substring(6).c_str());
+ int32_t getWiFiChannel(const char *EspSsid);
+ if (int32_t ch = WiFi.scanNetworks()) {
+ for (uint8_t i = 0; i < ch; i++) {
+ if (!strcmp(EspSsid, WiFi.SSID(i).c_str())) {
+ MESH.channel = WiFi.channel(i);
+ broker = true;
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: Successfully connected to Mesh Broker using MAC %s as %s on channel %d"),
+ XdrvMailbox.data, EspSsid, MESH.channel);
+ MESHstartNode(MESH.channel, XdrvMailbox.index);
+ ResponseCmndNumber(MESH.channel);
+ }
+ }
+ }
+ if (!broker) {
+ AddLog(LOG_LEVEL_INFO, PSTR("MSH: No Mesh Broker found using MAC %s"), XdrvMailbox.data);
+ }
+ }
+}
+
+void CmndMeshPeer(void) {
+ if (XdrvMailbox.data_len > 0) {
+ uint8_t _MAC[6];
+ MESHHexStringToBytes(XdrvMailbox.data, _MAC);
+ char _peerMAC[18];
+ ToHex_P(_MAC, 6, _peerMAC, 18, ':');
+ AddLog(LOG_LEVEL_DEBUG,PSTR("MSH: MAC-string %s (%s)"), XdrvMailbox.data, _peerMAC);
+ MESHaddPeer(_MAC);
+ MESHcountPeers();
+ ResponseCmndChar(_peerMAC);
+ }
+}
+
+void CmndMeshChannel(void) {
+ if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 14)) {
+ MESH.channel = XdrvMailbox.payload;
+ }
+ ResponseCmndNumber(MESH.channel);
+}
+
+void CmndMeshInterval(void) {
+ if ((XdrvMailbox.payload > 1) && (XdrvMailbox.payload < 201)) {
+ MESH.interval = XdrvMailbox.payload; // 2 to 200 ms
+ MESHsetSleep();
+ }
+ ResponseCmndNumber(MESH.interval);
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xdrv57(uint8_t function) {
+ bool result = false;
+
+ switch (function) {
+ case FUNC_COMMAND:
+ result = DecodeCommand(kMeshCommands, MeshCommand);
+ break;
+ case FUNC_PRE_INIT:
+ MESHInit(); // TODO: save state
+ break;
+ }
+ if (MESH.role) {
+ switch (function) {
+ case FUNC_LOOP:
+ static uint32_t mesh_transceive_msecond = 0; // State 50msecond timer
+ if (TimeReached(mesh_transceive_msecond)) {
+ SetNextTimeInterval(mesh_transceive_msecond, MESH.interval);
+ MESHevery50MSecond();
+ }
+ break;
+ case FUNC_EVERY_SECOND:
+ MESHEverySecond();
+ break;
+#ifdef USE_WEBSERVER
+ case FUNC_WEB_SENSOR:
+ MESHshow(0);
+ break;
+#endif
+ case FUNC_JSON_APPEND:
+ MESHshow(1);
+ break;
+#ifdef ESP32
+ case FUNC_MQTT_SUBSCRIBE:
+ MESHconnectMQTT();
+ break;
+#endif // ESP32
+ case FUNC_SHOW_SENSOR:
+ MESHsendPeerList(); // Sync this to the Teleperiod with a delay
+ break;
+#ifdef USE_DEEPSLEEP
+ case FUNC_SAVE_BEFORE_RESTART:
+ MESHdeInit();
+ break;
+#endif // USE_DEEPSLEEP
+ }
+ }
+ return result;
+}
+
+#endif // USE_TASMESH
diff --git a/tools/mqtt-file/Config_demo_9.5.0.1.dmp b/tools/mqtt-file/Config_demo_9.5.0.1.dmp
deleted file mode 100644
index b265d2029..000000000
Binary files a/tools/mqtt-file/Config_demo_9.5.0.1.dmp and /dev/null differ
diff --git a/tools/mqtt-file/download-settings.py b/tools/mqtt-file/download-settings.py
index 6953f8737..dac66c384 100644
--- a/tools/mqtt-file/download-settings.py
+++ b/tools/mqtt-file/download-settings.py
@@ -94,7 +94,12 @@ def on_message(client, userdata, msg):
if "Started" in rcv_code:
return
if "Error" in rcv_code:
- print("Error: "+rcv_code)
+ if "1" in rcv_code: print("Error: Wrong password")
+ else:
+ if "2" in rcv_code: print("Error: Bad chunk size")
+ else:
+ if "3" in rcv_code: print("Error: Invalid file type")
+ else: print("Error: "+rcv_code)
Err_flag = True
return
if "Command" in root:
diff --git a/tools/mqtt-file/upload-ota.py b/tools/mqtt-file/upload-ota.py
index ed9f962e1..e7cd22e77 100644
--- a/tools/mqtt-file/upload-ota.py
+++ b/tools/mqtt-file/upload-ota.py
@@ -87,7 +87,12 @@ def on_message(client, userdata, msg):
if "Started" in rcv_code:
return
if "Error" in rcv_code:
- print("Error: "+rcv_code)
+ if "1" in rcv_code: print("Error: Wrong password")
+ else:
+ if "2" in rcv_code: print("Error: Bad chunk size")
+ else:
+ if "3" in rcv_code: print("Error: Invalid file type")
+ else: print("Error: "+rcv_code)
Err_flag = True
return
if "Command" in root:
diff --git a/tools/mqtt-file/upload-settings.py b/tools/mqtt-file/upload-settings.py
index fe782f04c..fd8216d15 100644
--- a/tools/mqtt-file/upload-settings.py
+++ b/tools/mqtt-file/upload-settings.py
@@ -44,7 +44,7 @@ broker_port = 1883 # MQTT broker port
mypassword = "" # Tasmota MQTT password
mytopic = "demo" # Tasmota MQTT topic
-myfile = "Config_demo_9.5.0.1.dmp" # Tasmota Settings file name
+myfile = "Config_demo_9.5.0.2.dmp" # Tasmota Settings file name
myfiletype = 2 # Tasmota Settings file type
# **** End of User Configuration Section
@@ -86,7 +86,12 @@ def on_message(client, userdata, msg):
if "Started" in rcv_code:
return
if "Error" in rcv_code:
- print("Error: "+rcv_code)
+ if "1" in rcv_code: print("Error: Wrong password")
+ else:
+ if "2" in rcv_code: print("Error: Bad chunk size")
+ else:
+ if "3" in rcv_code: print("Error: Invalid file type")
+ else: print("Error: "+rcv_code)
Err_flag = True
return
if "Command" in root: