Arduino Uno WiFi Rev2 gradually fading signal strength

The sketch below is for a controller intended to switch a pump on/off via a relay. It works with a few minor touchups I need to do. However, I’ve found an odd behavior that is going to take very long / be very difficult to track down, so I want to ask the forum if anyone else has seen similar behavior.

The software is implemented with Blynk, but because this seems to be a WiFi problem, I’m posting here first.

What I’ve found is that after being up for days, the signal strength reported gets gradually worse, until at about two weeks continuous operation the board could no longer connect to the net and it reset (as it’s programmed to do). I happened to be watching at the time, and the signal immediately came up at full strength on reboot. It feels to me like a problem in the WiFi code I call via
** **[u]WiFi.RSSI()[/u]** **
(which as far as I understand is an independent HW/SW module on the board), and so I don’t have any models for what kind of bugs I could have created in my code that would cause this.

Thanks for looking. Here’s the code. I’ve removed a lot of formatting and display code, and some relay control routines, to keep within the character limit; I don’t think there’s anything in there relevant to this issue:

#define BLYNK_PRINT Serial 
#define RETRY_LIMIT 20
#define MIN_RSSI -80      
#define MAX_RSSI -50
#define NUM_BARS 7

#include <SPI.h>
#include <WiFiNINA.h>
#include <BlynkSimpleWiFiNINA.h>
#include <Adafruit_RGBLCDShield.h>
#include <time.h>
#include <util/usa_dst.h>
#include <math.h>
#include "arduino_secrets.h"

Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
WidgetLCD vlcd(V2);
BlynkTimer timer = BlynkTimer();

bool ledState = false;
int poolState = RELAY_OFF;
bool tickState = false;
bool secondState = false;
int pumpDuration = 0;
int remainingPumpInterval = 0;
bool showingUptime = false;
time_t y2kSeconds = 0;
unsigned long startMillis = millis();
time_t rise = 0;
time_t set = 0;
static bool priorDay = false;

void clockTick5() {
  showingUptime = !showingUptime;
}

void clockTick60() {
  readTime();
}

void oneSecondTick() {
  system_tick();
  clockTick();
  set_dst(usa_dst);
}

BLYNK_CONNECTED() {
  writeSerialStatus("Blynk_connected");

  Blynk.syncAll();
  poolState = digitalRead(poolPin);
  updatePool(poolState);
  Blynk.virtualWrite(V0, ledState ? LOW : HIGH);
  Blynk.virtualWrite(V1, poolState == RELAY_OFF ? 0 : 255);
}

bool checkForce(uint8_t button, int state, int* localState) {
  int localPoolState = digitalRead(poolPin);
  bool wantOn = state == RELAY_ON;
  bool force = haveButton(button);
  bool execute = force && localPoolState != state;
  if (execute) {
    if (wantOn) {
      turnOnPump();
    } else {
      turnOffPump();
    }
    writeSerialStatus(wantOn ? "force on" : "force off");
  }
  return execute;
}

const char* formatLine1() {
  const char* trail;
  if (showingUptime) {
    trail = formatUptime(false);
  } else if (remainingPumpInterval > 0) {
    trail = formatDuration(remainingPumpInterval, false);
  } else {
    trail = formatTime(false, true);
  }

  int trailLen = strlen(trail);
  int rssiMax = max(1, LCDwidth - trailLen);
  const char* rssi = formatRSSI(rssiMax);
  int rssilen = strlen(rssi);

  static char blanks[LCDwidth + 1];
  memset(blanks, ' ', sizeof(blanks));
  int spacer = min(LCDwidth, max(1, LCDwidth - rssilen - trailLen));
  blanks[spacer] = EOS;

  static char line[LCDwidth + 1];
  snprintf(line, sizeof(line), "%s%s%s", rssi, blanks, trail);
  line[LCDwidth] = EOS;

  return line;
}

const char* formatRSSI(int maxlen) {
  if (maxlen < NUM_BARS) {
    return formatNarrowRSSI();
  } else {
    return formatWideRSSI();
  }
}

const char* formatNarrowRSSI() {
  byte levels[8];
  const int MAXLEVEL = min(sizeof(levels), NUM_BARS); 
  int barson = max(0, min(MAXLEVEL, rssiBars()));
  int barsoff = sizeof(levels) - barson;

  memset(levels,           0b00010001, barsoff);
  memset(levels + barsoff, 0b00011111, barson);
  lcd.createChar(1, levels);

  const static char rssi[] = "\1";
  return rssi;
}

const char* formatWideRSSI() {
  static char buf[NUM_BARS + 1];
  int bars = rssiBars();
  memset(buf, ' ', sizeof(buf));
  for (int idx = 0; idx < NUM_BARS; ++idx) {
    buf[idx] = (bars > 0 && idx < bars) ? (char)(min(7, idx + 1)) : ' ';
  }
  buf[NUM_BARS] = EOS;

  setupRSSICharacters();
  return buf;
}

bool haveButton(uint8_t mask) {
  uint8_t buttons = lcd.readButtons();
  bool result = (buttons & mask) == mask;
  return result;
}

void applicationSetup() {
  set_zone(-8 * ONE_HOUR);
  set_position(33.7764 * ONE_DEGREE, -118.3122 * ONE_DEGREE);
  readTime();

  time_t now = time(NULL); auto nowyday = localtime(&now)->tm_yday;
  time_t newrise = sun_rise(&now);
  time_t newset = sun_set(&now); auto newsetyday = localtime(&newset)->tm_yday;
  time_t bias = (nowyday == newsetyday) ? 0 : 86400;
  rise = newrise - bias;
  set = newset - bias;
  priorDay = daytime();
  isDaytimeReport(0, priorDay);

  localSetup();
}

void localSetup() {
  banner("Begin localSetup");

  digitalWrite(secondPin, HIGH);
  pinMode(secondPin, OUTPUT);

  digitalWrite(ledPin, HIGH);
  pinMode(ledPin, OUTPUT);

  digitalWrite(tickPin, HIGH);
  pinMode(tickPin, OUTPUT);

  digitalWrite(busyPin, HIGH);
  pinMode(busyPin, OUTPUT);

  poolState = RELAY_OFF;
  digitalWrite(poolPin, RELAY_OFF);
  pinMode(poolPin, OUTPUT);
  banner("Finished pin setup");

  lcd.begin(LCDwidth, LCDlines);

  setupRSSICharacters();

  banner("Finished LCD setup");
  writeLCDStatus();
  writeSerialStatus("localSetup complete");

  timer.setInterval( 1 * 1000L, oneSecondTick);
  timer.setInterval( 5 * 1000L, clockTick5);
  timer.setInterval(60 * 1000L, clockTick60);
  banner("Timer setup complete");
}

void readTime() {
  auto unixSeconds = WiFi.getTime();
  y2kSeconds = unixSeconds - UNIX_OFFSET;
  set_system_time(y2kSeconds);

  time_t now = time(NULL); auto nowyday = localtime(&now)->tm_yday;
  time_t newrise = sun_rise(&now); auto newriseyday = localtime(&newrise)->tm_yday;
  time_t newset = sun_set(&now); auto newsetyday = localtime(&newset)->tm_yday;

  if (rise == 0 || nowyday == newsetyday) {
    rise = newrise;
    set = newset;
  }
}

int rssiBars() {
  auto rssi = WiFi.RSSI();
  if (rssi <= MIN_RSSI) {
    return 0;
  } else if (rssi >= MAX_RSSI) {
    return NUM_BARS;
  } else {
    float inputRange = (MAX_RSSI - MIN_RSSI);
    float outputRange = (NUM_BARS - 1);
    return (int)( ((float)(rssi - MIN_RSSI) * outputRange / inputRange) + 0.5 );
  }
}

void writeLCDStatus() {
  bool daytime = isDaytime();
  bool button = haveButton(BUTTON_LIGHT);
  bool on = daytime && !button || !daytime && button;
  lcd.setBacklight(on ? BACKLIGHT_ON : BACKLIGHT_OFF);
  const char* line0 = formatLine0();
  lcd.setCursor(0, 0);
  lcd.print(line0);
  const char* line1 = formatLine1();
  lcd.setCursor(0, 1);
  writeStringToLCD(line1);

  writeVLCDStatus();
}
void writeStringToLCD(const char* string) {
  auto len = strlen(string);
  for (int idx = 0; idx < len; ++idx) {
    lcd.write(string[idx]);
  }
}

void writeVLCDStatus() {
  if (!isConnected()) {
    return;
  }
  vlcd.print(0, 0, formatLine0());
  char line1[LCDwidth + 1];
  cpynstr(line1, formatLine1(), sizeof(line1));
  for (int idx = 0; idx < strlen(line1); ++idx) {
    if (line1[idx] < ' ') {
      line1[idx] = '*';
    }
  }
  vlcd.print(0, 1, line1);
}

bool isConnected() {
  bool left = haveButton(BUTTON_WIFI);
  return !left && Blynk.connected();
}

void loop() {
  if (isConnected()) {
    Blynk.run();
  }
  timer.run();

  y2kSeconds = time(NULL);
  loopTick();
}

void reconnectBlynk() {
  if (!isConnected() || haveButton(BUTTON_RESET)) {
    banner("Lost connection, attempting reconnect");
    int retries = 0;
    while (haveButton(BUTTON_RESET) || Blynk.connect() == false) {
      if (RETRY_LIMIT != 0 && ++retries > RETRY_LIMIT) {
        banner("Not reconnected, resetting board. Uptime: %s", formatUptime(true));
        reboot();
      }
    }
    banner("Reconnected");
  }
}

void reboot() {
  static void(* resetArduino) (void) = 0;
  resetArduino();
}

void setup() {
  Serial.begin(9600);
  Serial.println("Beginning setup...");
  wifiSetup();
  applicationSetup();
  banner("All setup complete, start main loop");
}

void wifiSetup() {
  static const char auth[] = SECRET_AUTH;
  static const char ssid[] = SECRET_SSID;
  static const char pass[] = SECRET_PASS;
  Blynk.begin(auth, ssid, pass);
  while (Blynk.connect() == false) {  } 
  timer.setInterval(15 * 1000L, reconnectBlynk);   
}