diff --git a/info/xdrv_57_tasmesh.md b/info/xdrv_57_tasmesh.md index 47dc918ed..2da2fac19 100644 --- a/info/xdrv_57_tasmesh.md +++ b/info/xdrv_57_tasmesh.md @@ -22,6 +22,10 @@ Add ``#define USE_TASMESH`` to your file ``user_config_override.h`` before compi ## Commands +**WARNING: The MAC address used for ESP-NOW on the broker is the *Soft AP MAC*, not the WiFi MAC.** + +*NOTE: The colons in the mac addresses of the commands are optional.* + ``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. @@ -36,16 +40,24 @@ Add ``#define USE_TASMESH`` to your file ``user_config_override.h`` before compi 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`` +- The broker must be started after wifi is up!! + - To start as ESP32 as broker after wifi and mqtt connection, use
``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. + - To start the node immediately use
``rule1 on system#init do meshnode FA:KE:AD:DR:ES:S1 endon`` + - To use mesh in combination with deep sleep, you must set a rule to re-initialize the mesh on wake-up. +The mesh status and parameters are **NOT** (yet) saved to flash and the mesh is not restarted automatically. + - **WARNING**: In case of a system-wide power outage, nodes will be unable to reconnect until after the broker is ready! +If all devices power up at the same time, a broker starting after `system#boot` will likely not be ready until *after* a node attempting to join at `system#init`. +This will cause the node to fail to mesh and *no retrying is implemented at this time*. +To account for this, instead of (or in addition to) using a rule on the nodes, assign all nodes to a common group topic (`GroupTopic2 tasnodes`) and have the broker send a command on that topic after it is ready:
`rule2 on mesh#broker=1 do publish cmnd/tasnodes/meshnode FA:KE:AD:DR:ES:S1` +- Add a known peer (another node in the mesh) after the node has initialized
``rule3 on mesh#node=1 do meshpeer FA:KE:AD:DR:ES:S1 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 +- No command persistence is implemented so use rules to start a broker or a node after start up or deep sleep - 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/support_command.ino b/tasmota/support_command.ino index 2ef127935..3c72125c1 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -649,6 +649,9 @@ void CmndStatus(void) (uint32_t)WiFi.localIP(), Settings->ipv4_address[1], Settings->ipv4_address[2], Settings->ipv4_address[3], Settings->ipv4_address[4], WiFi.macAddress().c_str()); +#ifdef USE_TASMESH + ResponseAppend_P(PSTR(",\"SoftAPMac\":\"%s\""), WiFi.softAPmacAddress().c_str()); +#endif // USE_TASMESH #if defined(ESP32) && CONFIG_IDF_TARGET_ESP32 && defined(USE_ETHERNET) ResponseAppend_P(PSTR(",\"Ethernet\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%_I\",\"" D_JSON_GATEWAY "\":\"%_I\",\"" D_JSON_SUBNETMASK "\":\"%_I\",\"" diff --git a/tasmota/xdrv_57_1_tasmesh_support.ino b/tasmota/xdrv_57_1_tasmesh_support.ino index 81949af0f..5c3718adc 100644 --- a/tasmota/xdrv_57_1_tasmesh_support.ino +++ b/tasmota/xdrv_57_1_tasmesh_support.ino @@ -41,10 +41,13 @@ #define MESH_MAX_PACKETS 3 // (3) Max number of packets #define MESH_REFRESH 50 // Number of ms +// The format of the vendor-specific action frame is as follows: // ------------------------------------------------------------------------------------------------------------ // | 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 +// +// The Vendor Specific Content contains vendor-specific fields as follows: // ------------------------------------------------------------------------------- // | Element ID | Length | Organization Identifier | Type | Version | Body | // ------------------------------------------------------------------------------- diff --git a/tasmota/xdrv_57_9_tasmesh.ino b/tasmota/xdrv_57_9_tasmesh.ino index f0b18f765..d309b4d59 100644 --- a/tasmota/xdrv_57_9_tasmesh.ino +++ b/tasmota/xdrv_57_9_tasmesh.ino @@ -288,7 +288,7 @@ bool MESHroleNode(void) { } /** - * @brief Redirects the mqtt message on the node just before it would have been sended to + * @brief Redirects the outgoing mqtt message on the node just before it would have been sent to * the broker via ESP-NOW * * @param _topic @@ -385,8 +385,10 @@ void MESHstartNode(int32_t _channel, uint8_t _role){ //we need a running broker wifi_promiscuous_enable(0); WiFi.disconnect(); MESHsetWifi(0); - if (esp_now_init() != 0) { - AddLog(LOG_LEVEL_INFO, PSTR("MSH: Node init failed")); + esp_now_deinit(); // in case it was already initialized but disconnected + int init_result = esp_now_init(); + if (init_result != 0) { + AddLog(LOG_LEVEL_INFO, PSTR("MSH: Node init failed with error: %d"), init_result); // try to re-launch wifi MESH.role = ROLE_NONE; MESHsetWifi(1); @@ -421,8 +423,10 @@ void MESHstartBroker(void) { // Must be called after WiFi is initialized!! 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")); + esp_now_deinit(); // in case it was already initialized by disconnected + esp_err_t init_result = esp_now_init(); + if (esp_err_t() != ESP_OK) { + AddLog(LOG_LEVEL_INFO, PSTR("MSH: Broker init failed with error: %s"), init_result); return; } @@ -646,6 +650,7 @@ void MESHEverySecond(void) { 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; + MESHdeInit(); // if we don't deinit after losing connection, we will get an error trying to reinit later MESHsetWifi(1); WifiBegin(3, MESH.channel); } @@ -733,12 +738,15 @@ void (* const MeshCommand[])(void) PROGMEM = { &CmndMeshBroker, &CmndMeshNode, &CmndMeshPeer, &CmndMeshChannel, &CmndMeshInterval }; void CmndMeshBroker(void) { +#ifdef ESP32 // only ESP32 currently supported as broker 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); +#endif // ESP32 } void CmndMeshNode(void) { +#ifndef ESP32 // only ESP8266 current supported as node if (XdrvMailbox.data_len > 0) { MESHHexStringToBytes(XdrvMailbox.data, MESH.broker); if (XdrvMailbox.index != 0) { XdrvMailbox.index = 1; } // Everything not 0 is a full node @@ -764,6 +772,7 @@ void CmndMeshNode(void) { AddLog(LOG_LEVEL_INFO, PSTR("MSH: No Mesh Broker found using MAC %s"), XdrvMailbox.data); } } +#endif // ESP32 } void CmndMeshPeer(void) { @@ -773,9 +782,16 @@ void CmndMeshPeer(void) { 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); + if (MESHcheckPeerList((const uint8_t *)_MAC) == false) { + MESHaddPeer(_MAC); + MESHcountPeers(); + ResponseCmndChar(_peerMAC); + } else if (WiFi.macAddress() == String(_peerMAC) || WiFi.softAPmacAddress() == String(_peerMAC)){ + // a device can be added as its own peer, but every send will result in a ESP_NOW_SEND_FAIL + AddLog(LOG_LEVEL_DEBUG,PSTR("MSH: device %s cannot be a peer of itself"), XdrvMailbox.data, _peerMAC); + } else { + AddLog(LOG_LEVEL_DEBUG,PSTR("MSH: %s is already on peer list, will not add"), XdrvMailbox.data, _peerMAC); + } } } diff --git a/tasmota/xsns_06_dht.ino b/tasmota/xsns_06_dht.ino index c8b209100..7da466f53 100644 --- a/tasmota/xsns_06_dht.ino +++ b/tasmota/xsns_06_dht.ino @@ -45,12 +45,13 @@ bool dht_active = true; // DHT configured bool dht_dual_mode = false; // Single pin mode struct DHTSTRUCT { + float t = NAN; + float h = NAN; + int16_t raw; + char stype[12]; int8_t pin; uint8_t type; uint8_t lastresult; - char stype[12]; - float t = NAN; - float h = NAN; } Dht[DHT_MAX_SENSORS]; bool DhtWaitState(uint32_t sensor, uint32_t level) { @@ -167,7 +168,7 @@ bool DhtRead(uint32_t sensor) { break; } case GPIO_MS01: { // Sonoff MS01 - int32_t voltage = ((dht_data[0] << 8) | dht_data[1]); + int16_t voltage = ((dht_data[0] << 8) | dht_data[1]); // AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: MS01 %d"), voltage); @@ -187,6 +188,7 @@ bool DhtRead(uint32_t sensor) { humidity = - FastPrecisePowf(0.00046 * x, 3) - 0.0004 * x + 15; } temperature = 0; + Dht[sensor].raw = voltage; break; } } @@ -264,7 +266,8 @@ void DhtShow(bool json) { for (uint32_t i = 0; i < dht_sensors; i++) { if (GPIO_MS01 == Dht[i].type) { if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_HUMIDITY "\":%*_f}"), Dht[i].stype, Settings->flag2.humidity_resolution, &Dht[i].h); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_HUMIDITY "\":%*_f,\"Raw\":%d}"), + Dht[i].stype, Settings->flag2.humidity_resolution, &Dht[i].h, Dht[i].raw); #ifdef USE_WEBSERVER } else { char parameter[FLOATSZ];