codmpm / esp-ccs811-bme280-mqtt

ESP8266 CCS811/BME280 MQTT air quality sensor
MIT License
16 stars 8 forks source link

WDT reset and CCS811 initialization fails #5

Open photogeen opened 5 years ago

photogeen commented 5 years ago

Hi,

I have a very strange behavior on my NodeMCU. Everything is connected the right way so I2C scanner is seeing both sensors. My BMP280 is not a SparkFun one. It's a chinese one with address 0x76 (changed that in the code). It already worked for me some hours but then stopped.

Right now I'm stuck with this resets:

13:02:43.303 -> Soft WDT reset
13:02:43.303 -> 
13:02:43.303 -> >>>stack>>>
13:02:43.303 -> 
13:02:43.303 -> ctx: cont
13:02:43.303 -> sp: 3ffffde0 end: 3fffffc0 offset: 01b0
13:02:43.303 -> 3fffff90:  feefeffe feefeffe feefeffe feefeffe  
13:02:43.303 -> 3fffffa0:  3fffdad0 00000000 3ffeeddc 40207c20  
13:02:43.303 -> 3fffffb0:  feefeffe feefeffe 3ffe853c 401011b9  
13:02:43.341 -> <<<stack<<<
13:02:43.341 -> 
13:02:43.341 ->  ets Jan  8 2013,rst cause:2, boot mode:(3,6)
13:02:43.341 -> 
13:02:43.341 -> load 0x4010f000, len 1384, room 16 
13:02:43.443 -> tail 8
13:02:43.443 -> chksum 0x2d
13:02:43.443 -> csum 0x2d
13:02:43.443 -> v8b899c12
13:02:43.443 -> ~ld
13:02:43.443 -> Starting up...
13:02:43.510 -> CCS811 Initialization failed.

I added some lines of code because I wanted to calculate an Air Quality Value for Homebridge (String: GOOD, BAD, POOR, ...) but this was also before it stopped working. I don't know where i made the mistake.

I have no pullups (I know it is recomended) but I haven't found out so far where to place them and how big the need to be (10k?). Sorry, I'm new to this and trying to find out by myself. Had no luck so far.

Here is the code:

`/****************************************************
  ESP8266 CSS811 compensated via BME280 with MQTT
  2018, Patrik Mayer - patrik.mayer@codm.de

  mqtt client from https://github.com/knolleary/pubsubclient/
  Arduino OTA from ESP8266 Arduino Core V2.4
  Adafruit CCS811 from https://github.com/adafruit/Adafruit_CCS811
  Sparkfun BME280 from https://github.com/sparkfun/SparkFun_CCS811_Arduino_Library
  Tasker from https://github.com/sticilface/Tasker

  Connect the sensors via I2C, (SDA -> GPIO4 / SCL -> GPIO5). Don't forget 
  the I2C Pull-Ups. 
  The used Adafruit CCS811 library expects I2C address 0x5A, so configure your 
  CCS811 accordingly.
  !WAKE is currently not used.
  Serial is left open for debugging (115200).

  The following topics will be dropped to mqtt, all retained:

   <mqttTopicPrefix>status          online/offline (last will)
   <mqttTopicPrefix>ip              system ip
   <mqttTopicPrefix>temperature     temperature in °C
   <mqttTopicPrefix>humidity        relative humidity in %
   <mqttTopicPrefix>pressure        pressure in hPa
   <mqttTopicPrefix>altitude        altitude in m
   <mqttTopicPrefix>co2             CO2 concentration in ppm
   <mqttTopicPrefix>tvoc            total volatile compound in ppb

  If you want to read Farenheit/Feet change readTempC() to readTempF() and 
  readFloatAltitudeMeters() to readFloatAltitudeFeet(). 
  ccs811.setEnvironmentalData() expects the temperature as °C

  ArduinoOTA is enabled, the mqttClientName is used as OTA name, see config.

****************************************************/

#include <Tasker.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <PubSubClient.h>

#include <Wire.h>

#include "Adafruit_CCS811.h"
#include "SparkFunBME280.h"

#include "config.h"

// internal vars
Adafruit_CCS811 ccs811;
BME280 myBME280;

long lastReconnectAttempt = 0; //For the non blocking mqtt reconnect (in millis)

WiFiClient espClient;
PubSubClient mqttClient(espClient);

Tasker tasker;

char mqttTopicStatus[64];
char mqttTopicIp[64];
char mqttTopicTemperatureC[64];
char mqttTopicHumidity[64];
char mqttTopicPressure[64];
char mqttTopicAltitude[64];
char mqttTopicCO2[64];
char mqttTopicTVOC[64];
char mqttTopicIAQ[64];

float bme280TemperatureC;
float bme280Humidity;
float bme280Pressure;
float bme280Altitude;

uint16_t ccs811co2;
uint16_t ccs811tvoc;

void meassureEnvironment(void);

void setup() {
  Serial.begin(115200);
  Serial.println("Starting up...");

  pinMode(LED_BUILTIN, OUTPUT);

  //put in mqtt prefix
  sprintf(mqttTopicStatus, "%sstatus", mqttTopicPrefix);
  sprintf(mqttTopicIp, "%sip", mqttTopicPrefix);
  sprintf(mqttTopicTemperatureC, "%stemperature", mqttTopicPrefix);
  sprintf(mqttTopicHumidity, "%shumidity", mqttTopicPrefix);
  sprintf(mqttTopicPressure, "%spressure", mqttTopicPrefix);
  sprintf(mqttTopicAltitude, "%saltitude", mqttTopicPrefix);
  sprintf(mqttTopicCO2, "%sco2", mqttTopicPrefix);
  sprintf(mqttTopicTVOC, "%stvoc", mqttTopicPrefix);
  sprintf(mqttTopicIAQ, "%siaq", mqttTopicPrefix);

  if (!ccs811.begin()) {
    Serial.println("CCS811 Initialization failed.");
    while (1);
  }

  //wait for ccs811 to be available
  while(!ccs811.available());

  //Initialize BME280
  myBME280.settings.commInterface = I2C_MODE;
  myBME280.settings.I2CAddress = 0x76;
  myBME280.settings.runMode = 3; //Normal mode
  myBME280.settings.tStandby = 0;
  myBME280.settings.filter = 4;
  myBME280.settings.tempOverSample = 5;
  myBME280.settings.pressOverSample = 5;
  myBME280.settings.humidOverSample = 5;

  //Calling .begin() causes the settings to be loaded
  delay(10);  //Make sure sensor had enough time to turn on. BME280 requires 2ms to start up.

  if (!myBME280.begin()) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  setup_wifi();
  mqttClient.setServer(mqttServer, 1883);

  //----------- OTA
  ArduinoOTA.setHostname(mqttClientName);

  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else { // U_SPIFFS
      type = "filesystem";
    }
    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    Serial.println("Start updating " + type);
  });

  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
    delay(1000);
    ESP.restart();
  });

  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });

  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();

  Serial.println("ready...");
  digitalWrite(LED_BUILTIN, LOW);
  delay(500);
  //ready, blink the led twice
  for (uint8_t i = 0; i < 5; i++ ) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    delay(200);
  }

  //measure every <measureDelay>ms - GO!
  tasker.setInterval(meassureEnvironment, measureDelay);

}

void loop() {

  //handle mqtt connection, non-blocking
  if (!mqttClient.connected()) {
    long now = millis();
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      // Attempt to reconnect
      if (MqttReconnect()) {
        lastReconnectAttempt = 0;
      }
    }
  }

  mqttClient.loop();
  tasker.loop();

  //handle OTA
  ArduinoOTA.handle();

}

void meassureEnvironment(void) {
  bme280TemperatureC = myBME280.readTempC();;
  bme280Humidity = myBME280.readFloatHumidity();
  bme280Pressure = myBME280.readFloatPressure();
  bme280Altitude = myBME280.readFloatAltitudeMeters();

  Serial.print("Temperature = ");
  Serial.print(bme280TemperatureC);
  Serial.println(" *C");

  Serial.print("Pressure = ");
  Serial.print(bme280Pressure);
  Serial.println(" hPa");

  Serial.print("Approx. Altitude = ");
  Serial.print(bme280Altitude);
  Serial.println(" m");

  Serial.print("Humidity = ");
  Serial.print(bme280Humidity);
  Serial.println(" %");

  ccs811.setEnvironmentalData(bme280Humidity, bme280TemperatureC); 
  //wait for ccs811 reading
  while(ccs811.readData());

  ccs811co2 = ccs811.geteCO2();
  ccs811tvoc = ccs811.getTVOC();

  char* iaq; {
    if      (ccs811co2 >= 1600)                         iaq = "POOR";
    else if (ccs811co2 >= 1100 && ccs811co2 <= 1599)    iaq = "INFERIOR";
    else if (ccs811co2 >= 900 && ccs811co2 <= 1099)     iaq = "FAIR";
    else if (ccs811co2 >= 700 && ccs811co2 <= 899)      iaq = "GOOD";
    else if (ccs811co2 >= 400 && ccs811co2 <= 699)      iaq = "EXCELLENT";
  }

  Serial.print("CO2 concentration : ");
  Serial.print(ccs811co2);
  Serial.println(" ppm");

  Serial.print("TVOC concentration : ");
  Serial.print(ccs811tvoc);
  Serial.println(" ppb");

  Serial.print("Air Quality : ");
  Serial.println(iaq);

  char buf[8] = "";
  sprintf(buf, "%f", bme280TemperatureC);
  mqttClient.publish(mqttTopicTemperatureC, buf, true);

  sprintf(buf, "%f", bme280Humidity);
  mqttClient.publish(mqttTopicHumidity, buf, true);

  sprintf(buf, "%f", bme280Pressure);
  mqttClient.publish(mqttTopicPressure, buf, true);

  sprintf(buf, "%f", bme280Altitude);
  mqttClient.publish(mqttTopicAltitude, buf, true);

  sprintf(buf, "%d", ccs811co2);
  mqttClient.publish(mqttTopicCO2, buf, true);

  sprintf(buf, "%d", ccs811tvoc);
  mqttClient.publish(mqttTopicTVOC, buf, true);

  sprintf(buf, "%d", iaq);
  mqttClient.publish(mqttTopicIAQ, buf, true);

  Serial.println();
}

void setup_wifi() {

  delay(10);

  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA); //disable AP mode, only station
  WiFi.hostname(mqttClientName);
  WiFi.begin(ssid, password);

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

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

bool MqttReconnect() {

  if (!mqttClient.connected()) {

    Serial.print("Attempting MQTT connection...");

    // Attempt to connect with last will retained
    if (mqttClient.connect(mqttClientName, mqttUser, mqttPass, mqttTopicStatus, 1, true, "offline")) {

      Serial.println("connected");

      // Once connected, publish an announcement...
      char curIp[16];
      sprintf(curIp, "%d.%d.%d.%d", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]);

      mqttClient.publish(mqttTopicStatus, "online", true);
      mqttClient.publish(mqttTopicIp, curIp, true);

    } else {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
    }
  }
  return mqttClient.connected();
}

Could someone please point me in the right direction? Great work! Thank you for that!

Eugen

codmpm commented 5 years ago

Hey Eugen,

I could not reproduce your behaviour. To my knowledge this is by 90% a power issue. Could you verify your power source? To check you could hook up a oscilloscope and observe the voltage when the WDT happens.

Another possibility could be a faulty ESP. Do you have a second board to test?

Cheers, Patrik

photogeen commented 5 years ago

Hi Patrik,

I have two identical NodeMCUs and both are getting the same WDT reset. After reading other posts in the last two days I also think it has to do with the power source. First I tried it just by powering the whole thing through the USB-C>Micro-USB cable from my mac (to also have serial output). I think it happens on WiFi load. I removed the OTA part and it started to work for an indefinite time than stops again.

Today I tried to power the MCU trough the VCC pin with a dedicated 5V 2A power adapter. But still the same WDT reset after a few minutes.

My next try will be a professional power supply (where I can set voltage and amps separately) to power the MCU and the two sensors separately with 3.3V.

This is my first project where I have more than one device on the I2C bus. This never happend with only one sensor.

Could you also please tell me where to place the pull-ups.

Thank You! I'm from Germany too ;-)...

codmpm commented 5 years ago

Oh, hi from Hessen then :-)

I don't know which DC-DC-Converter the NodeMCU uses and if it can provide power fast enough in voltage peaks. I had no success with different ones which delivered 3.3V directly. Currently I have good resulst with an AP2112k. A good linear power supply (Labornetzteil) should be feasable.

For the pullups, jusst connect 4k7Ω or 10kΩ between VDD and SDL/SDA. Explained here: https://www.edn.com/design/analog/4371297/Design-calculations-for-robust-I2C-communications. The Rp resistors...

Cheers!

froderi1 commented 3 years ago

Hi there, I have been unsuccessful to get a CJMCU ccs811, that’s the one with the pink PCB, to work with any ESP8266. The reason seems to be that it uses I2C clock stretching which is not supported on these. I tried an ESP32 and it worked right away. The model used for the setup described here seems to work in a different manner. Greetings from Cologne