Wemos Lolin32 Lite - Weather station software problem?

Hello,

I've created a small weather station, consisting of a solar/battery powered Wemos Lolin32 Lite, an BME280, TSL2591, a rain gauge (MS-WH-SP-RG) and wind speed/direction (WH-SP-WS01, MS-WH-SP-WD). Measurements will be collected every 30sec and send out via mqtt to my ioBroker installation.

In theory everything works fine (usb as well as solar/battery powered). But after about a day of running the weather station just stops working (not connected to mqtt/wifi, no led; just seems to be dead). As soon as I unplug/plug the power it works again (just hitting the reset button has no effect). Since the 3.3v bus is active I reckon that it is not a hardware problem.

I've appended the source code and fritzing diagram to proof read. Would be great if anyone can point me to a potential problem which might cause this behaviour.
I"ve added the ESP.restart just to troubleshoot. Behaviour was present before that change and is still present after this change. Battery voltage seems to be ok as well. Did not find any drop in voltage, which might result in a cut off.

Thanks in advance.

#include <WiFi.h>
#include <NTPClient.h>
#include <PubSubClient.h>
#include <Adafruit_BME280.h>
#include <Adafruit_TSL2591.h>

#define I2C_SDA 13
#define I2C_SCL 15

#define PIN_WIND_SPEED 16
#define PIN_WIND_DIR 34
#define PIN_RAIN 17

#define INTERVAL 30

const char *ssid = "***";
const char *password = "***";

WiFiClient espClient;
PubSubClient client(espClient);
Adafruit_BME280 bme;
Adafruit_TSL2591 tsl = Adafruit_TSL2591(2591);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org");

unsigned int windSpeedCounter = 0;
unsigned int rainCounter = 0;
unsigned int rainCounterHourly = 0;
int lastHour = 0;

void setup() {
  Serial.begin(115200);

  Serial.println();
  Serial.print("Connecting to ");
  Serial.print(ssid);
  Serial.print(" with mac ");
  Serial.print(WiFi.macAddress());

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  client.setServer("***", 1883);

  Wire.begin(I2C_SDA, I2C_SCL);

  bool bmeStatus = bme.begin(0x76, &Wire);  
  if (!bmeStatus) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  bool tslStatus = tsl.begin(&Wire, TSL2591_ADDR);
  if (!tslStatus) {
    Serial.println("Could not find a valid TSL2591 sensor, check wiring!");
    while (1);
  }
  tsl.setGain(TSL2591_GAIN_LOW);

  pinMode(PIN_WIND_SPEED, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PIN_WIND_SPEED), countWindSpeed, RISING);
  pinMode(PIN_RAIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PIN_RAIN), countRain, RISING);
  pinMode(PIN_WIND_DIR, INPUT);

  timeClient.begin();
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  timeClient.update();

  char temperature[8];
  sprintf(temperature, "%.2f", bme.readTemperature());
  client.publish("weather-station/temperature", temperature);
  char humidity[8];
  sprintf(humidity, "%.2f", bme.readHumidity());
  client.publish("weather-station/humidity", humidity);
  char pressure[8];
  sprintf(pressure, "%.2f", (bme.readPressure() / 100.0F));
  client.publish("weather-station/pressure", pressure);

  char luminosity[9];
  uint16_t lum = tsl.getLuminosity(TSL2591_VISIBLE);
  sprintf(luminosity, "%i", lum);
  client.publish("weather-station/lux", luminosity);

  Serial.print("Temperature ");
  Serial.print(temperature);
  Serial.print(" *C");
  
  Serial.print(", Pressure ");
  Serial.print(pressure);
  Serial.print(" hPa");
  
  Serial.print(", Humidity ");
  Serial.print(humidity);
  Serial.print(" %");

  Serial.print(", LUX ");
  Serial.print(lum);
  Serial.print(" ( ");
  Serial.print(luminosity);
  Serial.println(") lux");

  char windSpeed[8];
  sprintf(windSpeed, "%.2f", ((windSpeedCounter/INTERVAL) * 2.4));
  client.publish("weather-station/windSpeed", windSpeed);
  windSpeedCounter = 0;

  char rain[8];
  sprintf(rain, "%.2f", ((rainCounter/2)*0.2794));
  client.publish("weather-station/rain", rain);
  rainCounter = 0;

  int currentHour = timeClient.getHours();
  if (lastHour != currentHour) {
    char rainHourly[8];
    sprintf(rainHourly, "%.2f", ((rainCounterHourly/2)*0.2794));
    client.publish("weather-station/rainHourly", rainHourly);
    rainCounterHourly = 0;
    lastHour = currentHour;
  }

  String wd = "-";
  // create a mean value of the analog value
  int dirValue = 0;
  for (int i = 0; i < 10; i++){
    dirValue += analogRead(PIN_WIND_DIR);
  }
  dirValue = dirValue / 10.0;

  if (dirValue > 2978 && dirValue < 3226) {
    wd = "N";
  } else if (dirValue > 1387 && dirValue < 1734) {
    wd = "NNE";
  } else if (dirValue > 1735 && dirValue < 2120) {
    wd = "NE";
  } else if (dirValue > 300 && dirValue < 353) {
    wd = "ENE";
  } else if (dirValue > 354 && dirValue < 438) {
    wd = "E";
  } else if (dirValue > 10 && dirValue < 299) {
    wd = "ESE";
  } else if (dirValue > 623 && dirValue < 859) {
    wd = "SE";
  } else if (dirValue > 439 && dirValue < 622) {
    wd = "SSE";
  } else if (dirValue > 1065 && dirValue < 1386) {
    wd = "S";
  } else if (dirValue > 860 && dirValue < 1064) {
    wd = "SSW";
  } else if (dirValue > 2460 && dirValue < 2666) {
    wd = "SW";
  } else if (dirValue > 2121 && dirValue < 2459) {
    wd = "WSW";
  } else if (dirValue > 3666 && dirValue < 4095) {
    wd = "W";
  } else if (dirValue > 3227 && dirValue < 3429) {
    wd = "WNW";
  } else if (dirValue > 3430 && dirValue < 3665) {
    wd = "NW";
  } else if (dirValue > 2667 && dirValue < 2977) {
    wd = "NNW";
  }

  char windDirection[3];
  sprintf(windDirection, "%s", wd);
  client.publish("weather-station/windDirection", windDirection);

  Serial.print("Rain: ");
  Serial.print(rain);
  Serial.print(", WindSpeed: ");
  Serial.print(windSpeed);
  Serial.print(", WindDir: ");
  Serial.print(wd);
  Serial.print(" (");
  Serial.print(dirValue);
  Serial.println(")");

  delay(INTERVAL * 1000);
  
  int currentSecs = timeClient.getSeconds();
  if (currentHour == 0 && timeClient.getMinutes() == 0 && currentSecs > 0 && currentSecs < 30) {
    ESP.restart();
  }
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP-Weather-Station-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

// interrupt functions
void countWindSpeed() {
  windSpeedCounter++;
}

void countRain() {
  rainCounter++;
  rainCounterHourly++;
}

That might be a clue. If it was purely a software problem, I would expect the reset button to get things going again.

I wonder if there could be a problem with the i2c bus communication. If one of the i2c devices is not responding, locking up, then pressing reset on the esp board won't fix that, only powering off & on would reset it.

If that is what is happening, the code would get stuck in setup(). If you had the board connected to serial monitor at the time, you would be able to see which sensor was causing the problem.

Good point.
A serial monitor in case of failure would in deed be very helpful.
And yeah it is a possibility that it stops when initializing the i2c devices.
But if that is the case the device should show up in the wifi network, since the wifi init is done before, shouldn't it?

Also a good point. I'm not sure if it would or not. The lolin32 lite, despite it's name, seems to have dual cores, so perhaps you would expect it to use one core to connect to WiFi while the other runs (or gets stuck while trying to run) your sketch.

I can't see any obvious problem with your circuit or code. You need to find ways to debug this somehow.

I might bring the device in, leave it hooked up to serial monitor and wait for the problem to occur. Then you can hopefully learn something from the last messages on the monitor.

Before doing that, I would add lots more Serial.println("Doing XYZ now...") so you can see exactly where the code is freezing. Warning: put Serial.flush() after each one, or you won't see the last message before the freeze-up because it's stuck in the buffer.

Thanks again for your input.
I will look into it. Presumably I will make my code as robust as possible... Wish me luck :wink:

If anyone else has input, please just leave a comment. I'll be watching :wink:

An idea for a "hacky" fix:

The two sensors use very little current, even during measurement. Less than 1mA each. So they could easily be supplied from an esp32 output pin. This would give you a way to reset them under control of your code. It would happen automatically when the ESP32 gets reset because the pin would default to INPUT mode. Then in setup you can set that pin HIGH to power-up the sensors.

But also, you could have your code reset/restart the sensors on a regular basis, like maybe before you read them each time (allowing them a little time to start up before requesting the reading).

FYI, I have a BME280 sensor running 24/7 for 12+ weeks at a time without issue, connected to Wemos D1 mini (esp8266). My sensor is read only once every 15 mins, so 30 times less often than yours. (After 12+ weeks, it's 1,800mAh battery needs recharging, it's not solar recharged).

I don't have any TSL2591 sensors, but I do have a similar BH1750 lux sensor. No problems with that either, at least not of the type you describe. They fail after 2~3 years because, despite my best efforts, moisture gets into the enclosure and corrodes them.

Yeah this might be a hacky but good solution.

Currently I've just made the code more robust. The BME280 sensor library might sometimes return 0 or NAN, which will cause a crash as sprintf expects a float and not an int (or NAN), see Adafruit_BME280.cpp. This may fix a/the problem...
I will observe the weather station closely in the next time and if this problem persists I will try out the hacky solution. We will see.

PS: The source code above has a pretty obvious bug in the wind speed and rain calculation (using ints instead of floats... I love c...).

Update:
The BME280 robustness fix seems to do the trick.

Another problem that I discovered is that the esp32 does not automatically reconnect to the WiFi as soon as the connection is lost (even with WiFi.setAutoReconnect(true); and WiFi.persistent(true);).
Interesting fact: WiFi.getStatus() and WiFi.isConnected() both indicate that a WiFi connection exists but WiFi.localIP() results in 0.0.0.0...
Currently I simply reconnect to the WiFi if mqtt client cannot connect to the broker.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.