diff --git a/src/ESPNetwork.cpp b/src/ESPNetwork.cpp index 7ae33b0..d25abde 100644 --- a/src/ESPNetwork.cpp +++ b/src/ESPNetwork.cpp @@ -180,8 +180,8 @@ bool ESPNetwork::changeAP(const uint8_t *bssid, const int32_t channel) { void ESPNetwork::emitSockets() { this->emitHeap(); if(this->needsBroadcast || - (this->connType == conn_types_t::wifi && rssiNeedsEmit(WiFi.RSSI(), this->lastRSSI, WiFi.channel(), this->lastChannel, - millis() - this->lastRSSIEmit, RSSI_EMIT_MIN_DBM, RSSI_EMIT_MIN_MS))) { + (this->connType == conn_types_t::wifi && + this->rssiBroadcast.due(WiFi.RSSI(), WiFi.channel(), millis(), RSSI_EMIT_MIN_DBM, RSSI_EMIT_MIN_MS))) { this->emitSockets(255); sockEmit.loop(); this->needsBroadcast = false; @@ -209,9 +209,7 @@ void ESPNetwork::emitSockets(uint8_t num) { json->addElem("channel", (int32_t)this->channel); json->endObject(); sockEmit.endEmit(num); - this->lastRSSI = WiFi.RSSI(); - this->lastChannel = WiFi.channel(); - this->lastRSSIEmit = millis(); + this->rssiBroadcast.sent(num == 255, WiFi.RSSI(), WiFi.channel(), millis()); } else { JsonSockEvent *json = sockEmit.beginEmit("wifiStrength"); @@ -229,9 +227,7 @@ void ESPNetwork::emitSockets(uint8_t num) { json->addElem("fullduplex", false); json->endObject(); sockEmit.endEmit(num); - this->lastRSSI = -100; - this->lastChannel = -1; - this->lastRSSIEmit = millis(); + this->rssiBroadcast.sent(num == 255, -100, -1, millis()); } } // A broadcast comes from emitSockets(), which has already sent memStatus. diff --git a/src/ESPNetwork.h b/src/ESPNetwork.h index bb0bdc6..e90d0ed 100644 --- a/src/ESPNetwork.h +++ b/src/ESPNetwork.h @@ -1,5 +1,6 @@ #include #include +#include "Utils.h" #ifndef ESPNetwork_h #define ESPNetwork_h @@ -21,9 +22,7 @@ class ESPNetwork { protected: unsigned long lastEmit = 0; unsigned long lastMDNS = 0; - unsigned long lastRSSIEmit = 0; - int lastRSSI = 0; - int lastChannel = 0; + RssiBroadcast rssiBroadcast; int linkSpeed = 0; bool _connecting = false; public: diff --git a/src/Utils.cpp b/src/Utils.cpp index 68a9043..becb358 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -135,3 +135,12 @@ bool rssiNeedsEmit(int rssi, int lastRssi, int channel, int lastChannel, uint32_ int delta = rssi > lastRssi ? rssi - lastRssi : lastRssi - rssi; return delta >= minDelta && sinceLastMs >= minMs; } +bool RssiBroadcast::due(int rssi, int channel, uint32_t now, int minDelta, uint32_t minMs) const { + return rssiNeedsEmit(rssi, this->rssi, channel, this->channel, now - this->at, minDelta, minMs); +} +void RssiBroadcast::sent(bool broadcast, int rssi, int channel, uint32_t now) { + if(!broadcast) return; + this->rssi = rssi; + this->channel = channel; + this->at = now; +} diff --git a/src/Utils.h b/src/Utils.h index 5088bbc..ebe5597 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -60,6 +60,15 @@ struct rebootDelay_t { // otherwise only for an RSSI change of at least minDelta dBm and not more often // than every minMs. bool rssiNeedsEmit(int rssi, int lastRssi, int channel, int lastChannel, uint32_t sinceLastMs, int minDelta, uint32_t minMs); +// What the last wifiStrength broadcast told every client. Only broadcasts +// move it: an emit to one new client tells the others nothing. +struct RssiBroadcast { + int rssi = 0; + int channel = 0; + uint32_t at = 0; // millis() of the last broadcast. + bool due(int rssi, int channel, uint32_t now, int minDelta, uint32_t minMs) const; + void sent(bool broadcast, int rssi, int channel, uint32_t now); +}; class Timestamp { char _timeBuffer[128]; diff --git a/test/test_utils/test_utils.cpp b/test/test_utils/test_utils.cpp index 5d17801..7d483bd 100644 --- a/test/test_utils/test_utils.cpp +++ b/test/test_utils/test_utils.cpp @@ -197,6 +197,49 @@ static void test_rssiNeedsEmit_either_direction() { TEST_ASSERT_TRUE(rssiNeedsEmit(-73, -70, 6, 6, 10000, 3, 10000)); } +/********************************************************************* + * RssiBroadcast + ********************************************************************/ +static RssiBroadcast broadcastAt(int rssi, int channel, uint32_t at) { + RssiBroadcast b; + b.sent(true, rssi, channel, at); + return b; +} +static void test_rssiBroadcast_client_emit_keeps_baseline() { + // A new client gets the current RSSI right as it drops 5 dBm. The clients + // already connected must still get the change. + RssiBroadcast b = broadcastAt(-60, 6, 0); + b.sent(false, -65, 6, 20000); + TEST_ASSERT_EQUAL_INT(-60, b.rssi); + TEST_ASSERT_EQUAL_UINT32(0, b.at); + TEST_ASSERT_TRUE(b.due(-65, 6, 20000, 3, 10000)); +} +static void test_rssiBroadcast_broadcast_moves_baseline() { + RssiBroadcast b = broadcastAt(-60, 6, 0); + b.sent(true, -65, 6, 20000); + TEST_ASSERT_EQUAL_INT(-65, b.rssi); + TEST_ASSERT_EQUAL_UINT32(20000, b.at); + // Measured from the new baseline and too soon after it. + TEST_ASSERT_FALSE(b.due(-66, 6, 40000, 3, 10000)); + TEST_ASSERT_FALSE(b.due(-70, 6, 25000, 3, 10000)); + TEST_ASSERT_TRUE(b.due(-70, 6, 30000, 3, 10000)); +} +static void test_rssiBroadcast_channel_change_is_due() { + RssiBroadcast b = broadcastAt(-60, 6, 1000); + TEST_ASSERT_TRUE(b.due(-60, 11, 1000, 3, 10000)); +} +static void test_rssiBroadcast_client_emit_on_disconnect_keeps_baseline() { + RssiBroadcast b = broadcastAt(-60, 6, 0); + b.sent(false, -100, -1, 5000); + TEST_ASSERT_EQUAL_INT(6, b.channel); + TEST_ASSERT_FALSE(b.due(-60, 6, 5000, 3, 10000)); +} +static void test_rssiBroadcast_hold_off_across_millis_wrap() { + RssiBroadcast b = broadcastAt(-60, 6, 0xFFFFF000u); + TEST_ASSERT_FALSE(b.due(-70, 6, 0x00001000u, 3, 10000)); // 8192ms later + TEST_ASSERT_TRUE(b.due(-70, 6, 0x00002000u, 3, 10000)); // 12288ms later +} + int main(int argc, char **argv) { (void)argc; (void)argv; @@ -228,5 +271,10 @@ int main(int argc, char **argv) { RUN_TEST(test_rssiNeedsEmit_real_change_after_interval_emits); RUN_TEST(test_rssiNeedsEmit_real_change_too_soon_waits); RUN_TEST(test_rssiNeedsEmit_either_direction); + RUN_TEST(test_rssiBroadcast_client_emit_keeps_baseline); + RUN_TEST(test_rssiBroadcast_broadcast_moves_baseline); + RUN_TEST(test_rssiBroadcast_channel_change_is_due); + RUN_TEST(test_rssiBroadcast_client_emit_on_disconnect_keeps_baseline); + RUN_TEST(test_rssiBroadcast_hold_off_across_millis_wrap); return UNITY_END(); }