diff --git a/tasmota/support_tasmesh.ino b/tasmota/support_tasmesh.ino
new file mode 100644
index 000000000..5b6336068
--- /dev/null
+++ b/tasmota/support_tasmesh.ino
@@ -0,0 +1,283 @@
+/*
+ support_tasmesh.ino - mesh via ESP-Now support for Sonoff-Tasmota
+
+ Copyright (C) 2020 Theo Arends & Christian Baars
+
+ 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 Date Action Description
+ --------------------------------------------------------------------------------------------
+*/
+
+#define USE_TASMESH
+#ifdef USE_TASMESH
+
+#include
+#include
+
+#ifdef ESP32
+#include
+#include
+#else
+#include //ESP8266 ... why Espressif, why??
+#endif //ESP32
+
+#define MESH_PAYLOAD_SIZE 160 // default 180 - with header of 20 bytes, stays at 200 bytes, which is reported to work with ESP8266
+#define MESH_TOPICSZ 64 // max supported topic size
+#define MESH_BUFFERS 4 // max buffers number for splitted messages
+
+struct mesh_packet_t{
+ uint8_t sender[6]; //MAC
+ uint8_t receiver[6]; //MAC
+ uint32_t counter:4; //endless 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{
+ uint8_t sender[6]; //MAC
+ uint8_t receiver[6]; //MAC
+ uint32_t counter:4; //endless 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 lmfp; //time of last message from peer
+#ifdef ESP32
+ char topic[MESH_TOPICSZ];
+#endif //ESP32
+};
+
+struct mesh_broker_flags_t{
+ uint8_t brokerNeedsTopic:1;
+};
+
+struct mesh_packet_combined_t{
+ mesh_packet_header_t header;
+ uint8_t receivedChunks;
+ char raw[MESH_PAYLOAD_SIZE*6];
+};
+
+struct{
+ uint8_t broker[6];
+ uint8_t role;
+ uint8_t channel; //Wifi channel
+ uint8_t counter; //for every message
+ uint32_t lmfb; //time of last message from broker
+ uint32_t lmfap; //time of last message from any peer
+ uint8_t pmk[32];
+ mesh_broker_flags_t flags;
+ mesh_packet_t sendPacket;
+ std::vector peers;
+ std::queue packetToResend;
+ std::queue packetToConsume;
+ std::vector packetsAlreadySended;
+ std::vector multiPackets;
+}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_Role {
+ ROLE_NONE = 0, // not initialized
+ ROLE_BROKER, // ESP32 will connect mesh to WLAN
+ ROLE_NODE_SMALL, // Node will only talk to the broker
+ ROLE_NODE_FULL // Node will listen and resend every message for MESH functionality
+};
+
+enum MESH_Packet_Type { // Type of packet
+ PACKET_TYPE_TIME = 0, //
+ PACKET_TYPE_SENSOR, //
+ PACKET_TYPE_COMMAND, //
+ PACKET_TYPE_TOPIC, // announce mqtt topic to ESP32-proxy
+ PACKET_TYPE_MQTT //
+};
+
+/*********************************************************************************************\
+ *
+\*********************************************************************************************/
+#ifdef ESP32
+void MESHsendTime(uint32_t _peerNumber){ //only from broker to nodes
+ MESH.sendPacket.counter++;
+ MESH.sendPacket.type = PACKET_TYPE_TIME;
+ MESH.sendPacket.TTL = 3;
+ memcpy(MESH.sendPacket.receiver,MESH.peers[_peerNumber].MAC,6);
+ MESH.sendPacket.senderTime = Rtc.utc_time;
+ MESH.sendPacket.payload[0] = 0;
+ mesh_broker_flags_t *_flags = (mesh_broker_flags_t *)MESH.sendPacket.payload;
+ if(MESH.peers[_peerNumber].topic[0]==0){
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: broker wants topic from peer: %u"), _peerNumber);
+ _flags->brokerNeedsTopic = 1;
+ }
+ MESH.sendPacket.chunkSize = 0;
+ MESHsendPacket(&MESH.sendPacket);
+ // esp_now_send(MESH.sendPacket.receiver, (uint8_t *)&MESH.sendPacket, sizeof(MESH.sendPacket)-MESH_PAYLOAD_SIZE+1);
+}
+#endif //ESP32
+
+void MESHcheckPeerList(const uint8_t *MAC){
+ for(auto &_peer : MESH.peers){
+ if(memcmp(_peer.MAC,MAC,6)==0){
+ _peer.lmfp = millis();
+ return;
+ }
+ }
+ MESHaddPeer((uint8_t *)MAC);
+}
+
+
+void MESHcountPeers(void){
+ #ifdef ESP32
+ esp_now_peer_num_t _num;
+ esp_now_get_peer_num(&_num);
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("TAS-MESH peers: %u"),_num.total_num);
+#else
+ uint8_t _num;
+ uint8_t _numEnc;
+ esp_now_get_cnt_info(&_num,&_numEnc);
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("TAS-MESH peers: %u"),_num);
+#endif
+}
+
+
+void MESHaddPeer(uint8_t *_MAC ){
+ mesh_peer_t _newPeer;
+ memcpy(_newPeer.MAC,_MAC,6);
+ _newPeer.lmfp = millis();
+#ifdef ESP32
+ _newPeer.topic[0] = 0;
+#endif
+ MESH.peers.push_back(_newPeer);
+ int err;
+#ifdef ESP32
+ esp_now_peer_info_t _peer;
+ _peer.channel = WiFi.channel();
+ _peer.encrypt = false;
+ _peer.ifidx = 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
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MESH: peer added, err: %d"), err);
+ AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t *)_MAC,6);
+#ifdef ESP32
+ if(MESH.role == ROLE_BROKER) MESHsendTime(MESH.peers.size()-1);
+#endif //ESP32
+}
+
+//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 MESHMACStringToBytes(char* _string,uint8_t _MAC[]) { //uppercase
+ uint32_t index = 0;
+ uint32_t _end = 12;
+ 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);
+}
+
+void MESHsetPMK(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_P2(LOG_LEVEL_DEBUG, PSTR("MESH: set crypto key to PASSWORD1"));
+}
+
+
+bool MESHencryptPayload(mesh_packet_t *_packet, int _encrypt){
+
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MESH: will encrypt: %u"), _encrypt);
+
+size_t _size = _packet->chunkSize;
+char _tag[16];
+
+// AddLog_P2(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.pmk, (const void *)_packet,
+(void *)_packet->payload, _size, (void*)&_packet->senderTime, 4,
+_tag, bc, _encrypt);
+
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MESH: encryption done "));
+
+if(_encrypt==1){
+ memcpy(_packet->tag,_tag,16);
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MESH: payload encrypted"));
+ return true;
+}
+if(memcmp(_packet->tag,_tag,16)==0){
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MESH: payload decrypted"));
+ return true;
+}
+AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MESH: payload decryption error"));
+return false;
+}
+
+#endif //USE_TASMESH
diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino
index 58fd04c2c..70d83df77 100644
--- a/tasmota/xdrv_02_mqtt.ino
+++ b/tasmota/xdrv_02_mqtt.ino
@@ -214,6 +214,13 @@ bool MqttPublishLib(const char* topic, bool retained)
mqtt_cmnd_blocked++;
}
}
+#ifdef USE_TASMESH
+if(MESH.role == ROLE_NODE_SMALL){
+ MESHredirectMQTT(topic, mqtt_data, retained);
+ yield();
+ return true;
+}
+#endif //USE_TASMESH
bool result = MqttClient.publish(topic, mqtt_data, retained);
yield(); // #3313
@@ -245,6 +252,14 @@ void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len
char data[data_len +1];
memcpy(data, mqtt_data, sizeof(data));
+#ifdef USE_TASMESH
+#ifdef ESP32
+if(MESH.role == ROLE_BROKER){
+ if (MESHinterceptMQTT(topic, (uint8_t*)data, data_len+1)) return;
+}
+#endif //ESP32
+#endif //USE_TASMESH
+
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_MQTT D_RECEIVED_TOPIC " \"%s\", " D_DATA_SIZE " %d, " D_DATA " \"%s\""), topic, data_len, data);
// if (LOG_LEVEL_DEBUG_MORE <= seriallog_level) { Serial.println(data); }
diff --git a/tasmota/xdrv_44_tasmesh.ino b/tasmota/xdrv_44_tasmesh.ino
new file mode 100644
index 000000000..c88cfce31
--- /dev/null
+++ b/tasmota/xdrv_44_tasmesh.ino
@@ -0,0 +1,613 @@
+/*
+ xdrv_44_tasmesh.ino - Mesh support for Tasmota using ESP-Now
+
+ Copyright (C) 2020 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 .
+
+ --------------------------------------------------------------------------------------------
+ Version yyyymmdd Action Description
+ --------------------------------------------------------------------------------------------
+ 0.9.0.0 20200927 started - from scratch
+
+*/
+
+
+#ifdef USE_TASMESH
+
+/*********************************************************************************************\
+* Build a mesh of nodes using ESP-Now
+* Connect it through an ESP32-broker to WLAN
+\*********************************************************************************************/
+
+#define XDRV_44 44
+
+/*********************************************************************************************\
+ * constants
+\*********************************************************************************************/
+
+#define D_CMND_MESH "MESH"
+
+const char S_JSON_MESH_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MESH "%s\":%d}";
+const char S_JSON_MESH_COMMAND[] PROGMEM = "{\"" D_CMND_MESH "%s\"}";
+const char kMESH_Commands[] PROGMEM = "Broker|Node|Peer|Channel";
+
+/*********************************************************************************************\
+ * 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
+
+/*********************************************************************************************\
+ * 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) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BROKER sended packet"));
+}
+
+void CB_MESHDataReceived(const uint8_t *MAC, const uint8_t *packet, int len) {
+ MESH.lmfap = millis();
+ MESHcheckPeerList((const uint8_t *)MAC);
+ mesh_packet_t *_recvPacket = (mesh_packet_t*)packet;
+ MESH.packetToConsume.push(*_recvPacket);
+}
+#else //ESP8266
+void CB_MESHDataSent( uint8_t *MAC, uint8_t 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.lmfb = millis(); //directly from the broker
+ mesh_packet_t *_recvPacket = (mesh_packet_t*)packet;
+ if(memcmp(_recvPacket->receiver,MESH.sendPacket.sender,6)!=0){ //MESH.sendPacket.sender simply stores the MAC of the node
+ //pass packet back to the MESH
+ _recvPacket->peerIndex = 0;
+ MESH.packetToResend.push(*_recvPacket);
+ return;
+ }
+ switch(_recvPacket->type){
+ case PACKET_TYPE_TIME:
+ Rtc.utc_time = _recvPacket->senderTime;
+ Rtc.user_time_entry = true;
+ memcpy((uint8_t*)&MESH.flags,_recvPacket->payload,1);
+ break;
+ default:
+ MESH.packetToConsume.push(*_recvPacket);
+ break;
+ }
+}
+#endif //ESP32
+
+/*********************************************************************************************\
+ * init driver
+\*********************************************************************************************/
+
+void MESHInit(void) {
+ MESH.role == ROLE_NONE;
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("TAS-MESH initialized: %u"),Settings.tele_period);
+
+ MESH.sendPacket.counter = 0;
+ MESH.sendPacket.chunks = 1;
+ MESH.sendPacket.chunk = 0;
+ MESH.sendPacket.type = PACKET_TYPE_TIME;
+ MESH.sendPacket.TTL = 2;
+}
+
+/*********************************************************************************************\
+ * 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_P2(LOG_LEVEL_INFO, PSTR("MESH: 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 MESHinterceptMQTT(char* _topic, uint8_t* _data, unsigned int data_len){
+ char stopic[TOPSZ];
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MESH: 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;
+ memcpy(MESH.sendPacket.payload,_topic,MESH.sendPacket.chunkSize);
+ memcpy(MESH.sendPacket.payload+MESH.sendPacket.chunkSize,_data,data_len);
+ MESH.sendPacket.chunkSize += data_len;
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MESH: 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_P2(LOG_LEVEL_INFO, PSTR("MESH: multiple chunks: %u"),_packet->chunks);
+ // TODO: reconstruct message in buffer
+ }
+}
+#endif //ESP32
+
+/**
+ * @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 MESHredirectMQTT(const char* _topic, char* _data, bool _retained){
+ 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_P2(LOG_LEVEL_INFO, PSTR("MESH: 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;
+ _bytesLeft -= _topicSize;
+ _byteLeftInChunk -= _topicSize;
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("topic in payload %s"),(char*)MESH.sendPacket.payload);
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: after topic -> chunk:%u, pre-size: %u"),MESH.sendPacket.chunk,MESH.sendPacket.chunkSize);
+ }
+ if(_byteLeftInChunk>0){
+ if(_byteLeftInChunk>_bytesLeft){
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: only last chunk bL:%u bLiC:%u oSD:%u"),_bytesLeft,_byteLeftInChunk,_offsetData);
+ _byteLeftInChunk = _bytesLeft;
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: only last chunk after correction -> chunk:%u, pre-size: %u"),MESH.sendPacket.chunk,MESH.sendPacket.chunkSize);
+ }
+ if(MESH.sendPacket.chunk>0) _topicSize = 0;
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: %u"),_offsetPayload);
+ memcpy(MESH.sendPacket.payload + _topicSize, _data + _offsetData,_byteLeftInChunk);
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("data in payload %s"),(char*)MESH.sendPacket.payload + _offsetPayload);
+ _offsetData += _byteLeftInChunk;
+ _bytesLeft -= _byteLeftInChunk;
+ }
+ MESH.sendPacket.chunkSize += _byteLeftInChunk;
+ MESH.packetToResend.push(MESH.sendPacket);
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: 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_P2(LOG_LEVEL_INFO, PSTR("MESH: too many chunks: %u"),MESH.sendPacket.chunk+1);
+ }
+ MESH.sendPacket.chunk++;
+ MESH.sendPacket.chunkSize = 0;
+ }
+ //send to pipeline
+ return true;
+}
+
+/**
+ * @brief The node sends its mqtt topic to the broker
+ *
+ */
+void MESHanounceTopic(){
+ memset(MESH.sendPacket.payload,0,MESH_PAYLOAD_SIZE);
+ strcpy((char*)MESH.sendPacket.payload,mqtt_topic);
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: topic: %s"),(char*)MESH.sendPacket.payload);
+ MESH.sendPacket.chunkSize = strlen((char*)MESH.sendPacket.payload) + 1;
+ MESH.sendPacket.type = PACKET_TYPE_TOPIC;
+ MESHsendPacket(&MESH.sendPacket);
+ // int result = esp_now_send(MESH.sendPacket.receiver, (uint8_t *)&MESH.sendPacket, (sizeof(MESH.sendPacket))-(MESH_PAYLOAD_SIZE-MESH.sendPacket.chunkSize-1));
+}
+
+/*********************************************************************************************\
+ * generic functions
+\*********************************************************************************************/
+
+void MESHstartNode(int32_t _channel){
+ MESH.channel = _channel;
+ WiFi.mode(WIFI_STA);
+ WiFi.begin("","",MESH.channel, nullptr, false); //fake connection attempt to set channel
+ WiFi.disconnect();
+ if (esp_now_init() != 0) {
+ return;
+ }
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: Node initialized, channel: %u"),MESH.channel);
+#ifdef ESP8266
+ esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
+#endif //ESP8266
+
+ esp_now_register_send_cb(CB_MESHDataSent);
+ esp_now_register_recv_cb(CB_MESHDataReceived);
+ MESHsetPMK(MESH.pmk);
+ memcpy(MESH.sendPacket.receiver,MESH.broker,6);
+ WiFi.macAddress(MESH.sendPacket.sender);
+ MESHaddPeer(MESH.broker); //must always be peer 0!!
+ MESHcountPeers();
+ MESH.role = ROLE_NODE_SMALL;
+ MESHanounceTopic();
+}
+
+void MESHstartBroker(){
+#ifdef ESP32
+ WiFi.mode(WIFI_AP_STA);
+ // WiFi.softAP("SSID_NOW","PASSWORD_NOW",9,1);
+ // AddLogBuffer(LOG_LEVEL_INFO,(uint8_t *)WiFi.softAPmacAddress(),6);
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: Broker MAC: %s"),WiFi.softAPmacAddress().c_str());
+ WiFi.softAPmacAddress(MESH.broker);
+
+ uint32_t _channel = WiFi.channel();
+ esp_wifi_set_promiscuous(true);
+ esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE);
+ esp_wifi_set_promiscuous(false);
+
+ if (esp_now_init() != 0) {
+ return;
+ }
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: Broker initialized on channel: %u"), _channel);
+ esp_now_register_send_cb(CB_MESHDataSent);
+ esp_now_register_recv_cb(CB_MESHDataReceived);
+ MESHsetPMK(MESH.pmk);
+ MESHcountPeers();
+ memcpy(MESH.sendPacket.sender,MESH.broker,6);
+ MESH.role = ROLE_BROKER;
+#endif //ESP32
+}
+
+/*********************************************************************************************\
+ * main loops
+\*********************************************************************************************/
+#ifdef ESP32
+
+void MESHevery50MSecond(){
+ // if(MESH.packetToResend.size()>0){
+ // // pass the packets
+ // }
+ if(MESH.packetToConsume.size()>0){
+ // do something on the node
+ MESHencryptPayload(&MESH.packetToConsume.front(),0);
+ switch(MESH.packetToConsume.front().type){
+ case PACKET_TYPE_TOPIC:
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: received topic: %s"), (char*)MESH.packetToConsume.front().payload);
+ 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);
+ MESHsubscribe((char*)&_peer.topic);
+ }
+ }
+ break;
+ // case PACKET_TYPE_SENSOR:
+ // for(auto &_peer : MESH.peers){
+ // if(memcmp(_peer.MAC,MESH.packetToConsume.front().sender,6)==0){
+ // // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: received sensor output: %s"), (char*)MESH.packetToConsume.front().payload);
+ // char stopic[MESH_TOPICSZ];
+ // GetTopic_P(stopic, TELE, _peer.topic, PSTR("SENSOR"));
+ // MqttClient.publish(stopic, (char*)MESH.packetToConsume.front().payload);
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: topic: %s output: %s"), stopic, (char*)MESH.packetToConsume.front().payload);
+ // yield(); // #3313
+ // break;
+ // }
+ // }
+ case PACKET_TYPE_MQTT: // redirected MQTT from node in packet [char* _space_ char*]
+ {
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: 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_P2(LOG_LEVEL_INFO, PSTR("MESH: 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_P2(LOG_LEVEL_INFO, PSTR("MESH: recChunks= %u"),_packet_combined.receivedChunks);
+ }
+ }
+ uint32_t _temp = (1 << (uint8_t)MESH.packetToConsume.front().chunks)-1 ; //example: 1+2+4 == (2^3)-1
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: _temp: %u = %u"),_temp,_packet_combined.receivedChunks);
+ if(_packet_combined.receivedChunks==_temp){
+ char * _data = (char*)_packet_combined.raw + strlen((char*)_packet_combined.raw) + 1;
+ MqttClient.publish((char*)_packet_combined.raw, _data);
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: combined done: %s = %s"),(char*)_packet_combined.raw,_data);
+ // AddLogBuffer(LOG_LEVEL_INFO,(uint8_t*)_packet_combined.raw,50);
+ }
+ }
+ 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_P2(LOG_LEVEL_INFO, PSTR("new multipacket with chunks: %u"),_packet.header.chunks);
+ }
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: no support yet for multiple chunks: %u"),MESH.packetToConsume.front().chunks);
+ break;
+ }
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: 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;
+ MqttClient.publish((char*)MESH.packetToConsume.front().payload, _data);
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: topic: %s output: %s"), (char*)MESH.packetToConsume.front().payload, _data);
+ // AddLogBuffer(LOG_LEVEL_INFO,(uint8_t *)&MESH.packetToConsume.front().payload,MESH.packetToConsume.front().chunkSize);
+ yield(); // #3313
+ }
+ break;
+ default:
+ AddLogBuffer(LOG_LEVEL_INFO,(uint8_t *)&MESH.packetToConsume.front(),MESH.packetToConsume.front().chunkSize+5);
+ break;
+ }
+ MESH.packetToConsume.pop();
+ }
+}
+
+void MESHEverySecond(){
+ static uint32_t _second = 0;
+ _second++;
+ // send a time packet every x seconds
+ uint32_t _peerNumber = _second%60;
+ if(_peerNumber3){
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("MESH: multi packets in buffer: %u"),MESH.multiPackets.size());
+ MESH.multiPackets.erase(MESH.multiPackets.begin());
+ }
+}
+
+#else //ESP8266
+void MESHevery50MSecond(){
+ if(MESH.packetToResend.size()>0){
+ uint32_t _tempIndex = MESH.packetToResend.front().peerIndex;
+ if(MESH.peers.size()>_tempIndex){
+ MESH.packetToResend.front().senderTime = Rtc.utc_time;
+ 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 TODO: guarantee that peer[0] is always the broker
+ MESHsendPacket(&MESH.packetToResend.front());
+ // int result = esp_now_send(MESH.packetToResend.front().receiver, (uint8_t *)&MESH.packetToResend.front(), (sizeof(MESH.sendPacket))-(MESH_PAYLOAD_SIZE-MESH.packetToResend.front().chunkSize));
+ }
+ }
+ MESH.packetToResend.front().peerIndex = _tempIndex + 1;
+ }
+ 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_P2(LOG_LEVEL_INFO, PSTR("MESH: node received topic: %s"), (char*)MESH.packetToConsume.front().payload);
+ MESHreceiveMQTT(&MESH.packetToConsume.front());
+ break;
+ default:
+ break;
+ }
+ MESH.packetToConsume.pop();
+ }
+}
+
+
+void MESHEverySecond(){
+ static uint32_t _second = 0;
+ static uint32_t _tele_period = Settings.tele_period;
+ if (MESH.role == ROLE_NODE_SMALL){
+ _tele_period--;
+ if(_tele_period == 0){
+ // uint8_t broadcastAddress[6] = {0x30,0xAE,0xA4,0x26,0xE7,0x29};
+ // memcpy(MESH.sendPacket.receiver,MESH.broker,6);
+ // _tele_period = Settings.tele_period;
+ // mqtt_data[0] = 0;
+
+ // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("SENSOR: %s %u"), mqtt_data, strlen(mqtt_data));
+ // MESH.sendPacket.chunkSize = strlen(mqtt_data);
+ // memcpy(MESH.sendPacket.payload,mqtt_data,MESH.sendPacket.chunkSize);
+ // MESH.sendPacket.type = PACKET_TYPE_SENSOR;
+ // MESH.sendPacket.senderTime = Rtc.utc_time;
+ // int result = esp_now_send(MESH.sendPacket.receiver, (uint8_t *)&MESH.sendPacket, (sizeof(MESH.sendPacket))-(MESH_PAYLOAD_SIZE-MESH.sendPacket.chunkSize));
+ // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("send error: %d, tele: %u"), result, tele_period);
+ }
+ if(MESH.flags.brokerNeedsTopic == 1){
+ MESHanounceTopic();
+ MESH.flags.brokerNeedsTopic = 0;
+ }
+ }
+}
+#endif //ESP8266
+
+/*********************************************************************************************\
+ * presentation
+\*********************************************************************************************/
+void MESHshow(bool json){
+ if (json) {
+ if(MESH.role != ROLE_NONE){
+ if(MESH.role != ROLE_BROKER) ResponseAppend_P(PSTR(",\"MESH\":{\"broker\":%u"),MESH.channel);
+ else ResponseAppend_P(PSTR(",\"MESH\":{\"node\":%u"),MESH.channel);
+ ResponseJsonEnd();
+ }
+ } else {
+#ifdef ESP32
+ if(MESH.role == ROLE_BROKER){
+ WSContentSend_PD(PSTR("TAS-MESH:
"));
+ WSContentSend_PD(PSTR("Broker MAC: %s
"),WiFi.softAPmacAddress().c_str());
+ WSContentSend_PD(PSTR("Broker Channel: %u
"),WiFi.channel());
+ 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
"),_peer.lmfp);
+ WSContentSend_PD(PSTR("Node MQTT topic: %s
"),_peer.topic);
+ }
+ }
+#endif //ESP32
+ }
+}
+
+
+/*********************************************************************************************\
+ * check the MESH commands
+\*********************************************************************************************/
+
+bool MESHCmd(void) {
+ char command[CMDSZ];
+ bool serviced = true;
+ uint8_t disp_len = strlen(D_CMND_MESH);
+
+ if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_MESH), disp_len)) { // prefix
+ int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kMESH_Commands);
+
+ switch (command_code) {
+ case CMND_MESH_BROKER:
+ MESHstartBroker();
+ Response_P(S_JSON_MESH_COMMAND_NVALUE, command, MESH.channel);
+ break;
+ case CMND_MESH_NODE:
+ if (XdrvMailbox.data_len > 0) {
+ MESHstripColon(XdrvMailbox.data);
+ MESHMACStringToBytes(XdrvMailbox.data,MESH.broker);
+ MESHstartNode(MESH.channel);
+ Response_P(S_JSON_MESH_COMMAND_NVALUE, command, MESH.channel);
+ }
+ break;
+ case CMND_MESH_CHANNEL:
+ if (XdrvMailbox.data_len > 0) {
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("channel: %u"), XdrvMailbox.payload);
+ MESH.channel = XdrvMailbox.payload;
+ }
+ break;
+ case CMND_MESH_PEER:
+ if (XdrvMailbox.data_len > 0) {
+ uint8_t _MAC[6] = {0};
+ MESHstripColon(XdrvMailbox.data);
+ MESHMACStringToBytes(XdrvMailbox.data,_MAC);
+ // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MAC-string: %s"), XdrvMailbox.data);
+ // AddLogBuffer(LOG_LEVEL_INFO,(uint8_t *)_MAC,6);
+ MESHaddPeer(_MAC);
+ MESHcountPeers();
+ }
+ break;
+ default:
+ // else for Unknown command
+ serviced = false;
+ break;
+ }
+ } else {
+ return false;
+ }
+ return serviced;
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xdrv44(uint8_t function)
+{
+ bool result = false;
+
+ switch (function) {
+ case FUNC_PRE_INIT:
+ MESHInit(); // TODO: save state
+ break;
+ case FUNC_EVERY_50_MSECOND:
+ MESHevery50MSecond();
+ break;
+ case FUNC_EVERY_SECOND:
+ MESHEverySecond();
+ break;
+ case FUNC_COMMAND:
+ result = MESHCmd();
+ break;
+ case FUNC_WEB_SENSOR:
+#ifdef USE_WEBSERVER
+ MESHshow(0);
+#endif
+ break;
+ case FUNC_JSON_APPEND:
+ MESHshow(1);
+ break;
+#ifdef ESP32
+ case FUNC_MQTT_SUBSCRIBE:
+ MESHconnectMQTT();
+ break;
+#endif //ESP32
+ // case FUNC_SHOW_SENSOR:
+ // if(MESH.role == ROLE_NODE_SMALL) MESHsendTeleSensor();
+ // break;
+ }
+return result;
+}
+
+#endif // USE_TASMESH