ESP32 NodeMCU-ESP-32S board halt / sleep / freeze after running

Im using to my project Esp32, because stronger than arduino boards.

I have noticed a weird effect, mabye somebody have noticed too this effect - after 30 minute/hour i dont know accurate lookslike board goes in sleep (or freeze) - just if im press the EN button - again working.

The uploaded code controll a oled with SPI connection what receive analog reading values for displaying spectrum. Compile give no warning or error.

-Im choosing as board ESP32 Dev Module

  • Im running from USB power source.

  • I have not defined any power management option, code looks running well.

Why Esp32 simple stop working after minutes?

Have anybody experience with esp32 boards to give advise, where is the problem?

After press EN i see in serial monitor this log:

11:27:40.555 -> ets Jun 8 2016 00:22:57
11:27:40.555 ->
11:27:40.555 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
11:27:40.555 -> configsip: 0, SPIWP:0xee
11:27:40.555 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
11:27:40.555 -> mode:DIO, clock div:1
11:27:40.555 -> load:0x3fff0018,len:4
11:27:40.555 -> load:0x3fff001c,len:1216
11:27:40.555 -> ho 0 tail 12 room 4
11:27:40.555 -> load:0x40078000,len:10944
11:27:40.555 -> load:0x40080400,len:6388
11:27:40.603 -> entry 0x400806b4

thanks for any help.

The posted info shows a normal ESP32 boot.

You might want to post your code and images of the project and a description of the project. Have you added serial prints to see when and where the unit has stopped processing?

1 Like

Thank you for the help.

Im continue testing maybe i can tell more about this weird halt.

I have noticed this halt first after i have leaved long time ,,non used" without reading any analog input with active usb power supply. First i thinked just OLED giving not response, but i have added a blinking LED too, whats also stopped blinking, so esp32 fully stopped by this event. i have tryed with external USB power adapter same effect like by PC USB port. Freeze or a power save mode, i dont know.

Now im leaving running the board and see what will be the result. Now, since 15 minute running well.

This serial print is a good question, originaly remains in setup a Serial.begin and i have uploaded the code selecting in Arduino IDE Core Debug level too, but i see just this normal boot log in serial monitor.

I will post the code and schematic, but need some cleaning and few photo or drawing something for this.

This is my first esp32 project - this serial monitor log comes automaticaly from esp32 board (after select before compile the debug level in Arduino IDE) or need add anything in code too?

I have looked few topic but thats was not clear. After checking after this halt serial monitor was clear, wroted nothing out.

I used this tutorial to connect SPI OLED to Esp32:

I used this code for upload:

#include "arduinoFFT.h" 
arduinoFFT FFT = arduinoFFT();
#include "SSD1306Spi.h"
SSD1306Spi display(17, 16, 5); 

#define SAMPLES 512              
#define SAMPLING_FREQUENCY 40000 
#define amplitude 1000           
unsigned int sampling_period_us;
unsigned long microseconds;
byte peak[] = {0,0,0,0,0,0,0};
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime, oldTime;

void setup() {
  
  Serial.begin(115200);
  display.init();
  display.flipScreenVertically(); 
  sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));

}

void loop() {
  
  display.clear();
  display.drawString(0,0,"0.1 0.2 0.5 1K  2K  4K  8K");
  for (int i = 0; i < SAMPLES; i++) {
    newTime = micros()-oldTime;
    oldTime = newTime;
    vReal[i] = analogRead(A0); // A conversion takes about 1uS on an ESP32
    vImag[i] = 0;
    while (micros() < (newTime + sampling_period_us)) { /* do nothing to wait */ }
  }
  FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
  FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
  for (int i = 2; i < (SAMPLES/2); i++){ 
    if (vReal[i] > 2000) { // Add a crude noise filter, 10 x amplitude or more
      if (i<=2 )             displayBand(0,(int)vReal[i]/amplitude); 
      if (i >3   && i<=5 )   displayBand(1,(int)vReal[i]/amplitude); 
      if (i >5   && i<=7 )   displayBand(2,(int)vReal[i]/amplitude); 
      if (i >7   && i<=15 )  displayBand(3,(int)vReal[i]/amplitude); 
      if (i >15  && i<=30 )  displayBand(4,(int)vReal[i]/amplitude); 
      if (i >30  && i<=53 )  displayBand(5,(int)vReal[i]/amplitude); 
      if (i >53  && i<=200 ) displayBand(6,(int)vReal[i]/amplitude); 
      if (i >200           ) displayBand(7,(int)vReal[i]/amplitude); 
      //Serial.println(i);
    }
    for (byte band = 0; band <= 6; band++) display.drawHorizontalLine(18*band,64-peak[band],14);
  }
  if (millis()%4 == 0) {for (byte band = 0; band <= 6; band++) {if (peak[band] > 0) peak[band] -= 1;}} // Decay the peak
  display.display();
}

void displayBand(int band, int dsize){
  int dmax = 50;
  if (dsize > dmax) dsize = dmax;
  if (band == 7) display.drawHorizontalLine(18*6,0, 14);
  for (int s = 0; s <= dsize; s=s+2){display.drawHorizontalLine(18*band,64-s, 14);}
  if (dsize > peak[band]) {peak[band] = dsize;}

}

Schematic, sorry if looks ugly, i dont have tools:

OLED SPI - ESP32 connections, from the link:

The only one difference OLED can run from 5V, so practicaly (because 3.3V used for spectrum analyse) im driving from 5V.

When you see that the ESP32 has stopped and you've added the above change, does the serial printing stop?

And another one test result, leave running the display, board/code feeding audio input freezed too, only EN helped restart running. Maybe this is too much fro Esp32? I dont know.

Ok im adding this change in to and upload again, im back if get any result.

So, the display is being ran on 5V and the ESP32 is not 5V tolerant.

The ESP32 can handle a lot.

/*
  https://github.com/G6EJD/ESP32-e-Paper-Weather-Display/blob/master/examples/Waveshare_4_2/Waveshare_4_2.ino
  Stole some code from that guy.
*/
#include "MyBitmap.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include "certs.h"
#include "sdkconfig.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"
#include <GxEPD2_BW.h>
#include <U8g2_for_Adafruit_GFX.h> // Select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall
#include <Fonts/FreeMonoBold9pt7b.h> //https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts
#include <Fonts/FreeMono9pt7b.h>
#include <HardwareSerial.h>
#include <SimpleKalmanFilter.h>
#include "MHZ19.h"
#include <ESP32Time.h>
#include <SolarCalculator.h>
////
ESP32Time rtc;
MHZ19 myMHZ19;
GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> display(GxEPD2_420(/*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4)); // GDEW042T2
Adafruit_BME680 bme( GPIO_NUM_15 );
WiFiClient   wifiClient; // do the WiFi instantiation thing
PubSubClient MQTTclient( mqtt_server, mqtt_port, wifiClient );
U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;
////
#define evtStoreAirPressure   ( 1 << 0 )
#define evtWaitForBME         ( 1 << 1 )
#define evtParseMQTT          ( 1 << 3 )
#define evtDisplayUpdate      ( 1 << 4 )
#define evtDoBME              ( 1 << 5 )
#define OneMinuteGroup ( evtDoBME )
EventGroupHandle_t eg;
//////
QueueHandle_t xQ_WindChillDewPoint;
QueueHandle_t xQ_eData;
struct stu_eData
{
  float  oTemperature = 0.0f;
  float  oHumidity    = 0.0f;
  float  oPressure    = 0.0f;
  // for outside aqi???????????????????????? what does the RPi send an int or float???
  float  Temperature  = 0.0f;
  float  Pressure    = 0.0f;
  float  Humidity    = 0.0f;
  float  IAQ         = 0.0f; // Index Air Quality
  float  RM0         = 0.0f; // Remaining Moisture from sensor 0
  //float  PM2         = 0.0f; // particles in air
  float  WS          = 0.0f; // wind speed
  String WD          = "";   // wind direction
  float  RF          = 0.0f; // rainfall
  //float  WSV         = 0.0f; // weather station volts
  //float  WSC         = 0.0f; // weather station current
  //float  WSP         = 0.0f; // weather station power
  float  WindChill   = 0.0f; //windchill
  float  DewPoint    = 0.0f; //dew point or dew index
  int    SunRiseHr   = 0;    // sunrise hour
  int    SunRiseMin  = 0;    //sunrise minute
  int    SunSetHr    = 0;    //sunset hour
  int    SunSetMin   = 0;    //sunset minute
  int    DuskHr      = 0;    //dusk
  int    DuskMin     = 0;    //dusk
  int    DawnHr      = 0;    // dawn
  int    DawnMin     = 0;    // dawn
  int    TransitHr   = 0;    // 'noon' time
  int    TransitMin  = 0;    // 'noon' time
  double azimuth     = 0.0f;   // Sun's azimuth, in degrees
  double elevation   = 0.0f;     // Sun's elevation, in degrees
  float  CO2         = 0.0f;
  float  PressureH   = 0.0f;
  float  PressureL   = 10000.0f;
  int    cngPress    = 0; // pressure change 0= no change, -1 slow change +1 fast change 
} x_eData; // environmental data
QueueHandle_t xQ_Message; // payload and topic queue of MQTT payload and topic
const int payloadSize = 100;
struct stu_message
{
  char payload [payloadSize] = {'\0'};
  String topic ;
} x_message;
////
const float oGasResistanceBaseLine = 149598.0f;
int mqttOK = 0;
volatile bool TimeSet = false;
const int BufferCount = 60;
float CollectionPressure[BufferCount] = {0.0f};
boolean LargeIcon = true, SmallIcon = false;
//bool CollectionDone = false;
#define Large  11           // For icon drawing, needs to be odd number for best effect
#define Small  5            // For icon drawing, needs to be odd number for best effect
enum alignment {LEFT, RIGHT, CENTER};
SemaphoreHandle_t sema_MQTT_KeepAlive;
SemaphoreHandle_t sema_PublishPM;
SemaphoreHandle_t sema_mqttOK;
SemaphoreHandle_t sema_CollectPressure;
////
//serial(2) = pin25 RX, pin26 TX
HardwareSerial co2Serial ( 2 );
////
void IRAM_ATTR onTimer()
{
  BaseType_t xHigherPriorityTaskWoken;
  xEventGroupSetBitsFromISR(eg, OneMinuteGroup, &xHigherPriorityTaskWoken);
} // void IRAM_ATTR onTimer()
// interrupt service routine for WiFi events put into IRAM
void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
      break;
    default: break;
  }
} // void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
void IRAM_ATTR mqttCallback(char* topic, byte * payload, unsigned int length)
{
  memset( x_message.payload, '\0', payloadSize ); // clear payload char buffer
  x_message.topic = ""; //clear topic string buffer
  x_message.topic = topic; //store new topic
  int i = 0; // extract payload
  for ( i; i < length; i++)
  {
    x_message.payload[i] = ((char)payload[i]);
  }
  x_message.payload[i] = '\0';
  xQueueOverwrite( xQ_Message, (void *) &x_message );// send data to queue
} // void mqttCallback(char* topic, byte* payload, unsigned int length)
////
void setup()
{
  // hardware timer 4 set for one minute alarm
  hw_timer_t * timer = NULL;
  timer = timerBegin( 3, 80, true );
  timerAttachInterrupt( timer, &onTimer, true );
  timerAlarmWrite(timer, 60000000, true);
  timerAlarmEnable(timer);
  ///
  co2Serial.begin( 9600 , SERIAL_8N1, 25, 26 ); // pin25 RX, pin26 TX
  x_eData.WD.reserve(50);
  x_message.topic.reserve( payloadSize );
  xQ_WindChillDewPoint = xQueueCreate( 1, sizeof(stu_eData) );
  xQ_Message  = xQueueCreate( 1, sizeof(stu_message) );
  xQ_eData    = xQueueCreate( 1, sizeof(stu_eData) ); // sends a queue copy of the structure
  //
  sema_PublishPM = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_PublishPM );
  sema_mqttOK    =  xSemaphoreCreateBinary();
  xSemaphoreGive( sema_mqttOK );
  sema_CollectPressure = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_CollectPressure );
  //
  eg = xEventGroupCreate(); // get an event group handle
  //
  xTaskCreatePinnedToCore( fparseMQTT, "fparseMQTT", 7000,  NULL, 5, NULL, 1 );
  xTaskCreatePinnedToCore( MQTTkeepalive, "MQTTkeepalive", 5000, NULL, 6, NULL, 1 );
  xTaskCreatePinnedToCore( DoTheBME680Thing, "DoTheBME280Thing", 20000, NULL, 5, NULL, 1);
  xTaskCreatePinnedToCore( fmqttWatchDog, "fmqttWatchDog", 5000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fDoTheDisplayThing, "fDoTheDisplayThing", 30000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fGetCO2, "fGetCO2", 4500, NULL, 2, NULL, 1 );
  xTaskCreatePinnedToCore( fParseDewPointWindChill, "fParseDewPointWindChill", 4500, NULL, 2, NULL, 1 );
  xTaskCreatePinnedToCore( fSolarCalculations, "fSolarCalculations", 10000, NULL, 2, NULL, 1 );
  xTaskCreatePinnedToCore( fProcessAirPressure, "fProcessAirPressure", 10000, NULL, 2, NULL, 1 );
} //void setup()
////
/*
   Approaching storms and wind cause barometric pressure to decrease.
   Rising pressure indicates fair weather.
   The longer it takes barometric pressure to change,
   the longer the coming weather pattern can be expected to last.
   It is possible that a small weather event, such as a passing shower, may trigger no change in barometric pressure.
   slow rate of change .0762mmHg - 1.016mmHg < 3 hours
   fast rate of change 4.572mmHg in < 3 hours
   steady <=.0762Hg in <= 3 hours
*/
int CalculatePressureFactors( float pastPressure, float currentPressure )
{
  float rateChange = abs(pastPressure - currentPressure);
  //log_i ( " pressure rate of change %f", rateChange );
  if ( (rateChange >= .0762f) && (rateChange <= 1.016f ) )
  {
    log_i( "slow %f", rateChange );
    return -1; //slow
  }
  if ( (rateChange > 1.016f) && (rateChange < 4.572f) )
  {
    log_i( "unsettled %f", rateChange );
    return 2; 
  }
  if ( rateChange >= 4.572f )
    {
      log_i( "fast %f", rateChange );
      return 1;//fast
   }
   if ( rateChange <= .0762 )
   {
    log_i( "steady %f", rateChange );
    return 0;//steady
   }
} //void CalculatePressureFactors()
////
void fProcessAirPressure ( void *pvParemeters )
{
  int cellCount = 58;
  int Ticks     = 118;
  bool Filled   = false;
  const int ticksTrigger = 120; // triggered at 1 minute intervals
  for (;;)
  {
    //triggered by BME which is triggered by the 1 minute hardware timer.
    xEventGroupWaitBits (eg, evtStoreAirPressure, pdTRUE, pdTRUE, portMAX_DELAY );
    xSemaphoreTake( sema_CollectPressure, portMAX_DELAY );
    if ( !Filled )
    {
      for ( int j = 0; j < BufferCount; j++ )
      {
        CollectionPressure[j] = x_eData.oPressure;
      }
      Filled = true;
    } else {
      if ( Ticks == ticksTrigger )
      {
        //shift contents left and insert new value at the end
        for ( int i = 0; i <= BufferCount - 2; i++ )
        {
          CollectionPressure[i] = CollectionPressure[i + 1];
        }
      }
      CollectionPressure[BufferCount - 1] = x_eData.oPressure;
    }
    if ( x_eData.oPressure > x_eData.PressureH )
    {
      x_eData.PressureH = x_eData.oPressure;
    }
    if ( x_eData.oPressure < x_eData.PressureL )
    {
      x_eData.PressureL = x_eData.oPressure;
    }
    Ticks++;
    if ( Ticks >= (ticksTrigger + 1) )
    {
      Ticks = 1;
      cellCount++;
    }
    if ( cellCount == (BufferCount - 1) )
    {
      cellCount = 0;
    }
    x_eData.cngPress = CalculatePressureFactors( CollectionPressure[57], CollectionPressure[59] ); // going back 4 hours
    xSemaphoreGive( sema_CollectPressure );
    //
    //log_i( " high watermark % d",  uxTaskGetStackHighWaterMark( NULL ) );
  } //for (;;)
  vTaskDelete( NULL );

} //void fStoreAirPressure ( void *pvParemeters )
////
void fSolarCalculations ( void *pvParameters )
{
  double sunrise;  // Sunrise, in hours (UTC)
  double transit;  // Solar noon, in hours (UTC)
  double sunset;   // Sunset, in hours (UTC)
  double dawn;     // Civil dawn, in hours (UTC)
  double dusk;     // Civil dusk, in hours (UTC)
  //double rt_ascension;  // Sun's right ascension, in degrees
  //double declination;   // Sun's declination, in degrees
  const  float time_zone = -7.0f;
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 1000; //delay for mS
  int count  = 3590;
  int monthX = 1;
  int dayX   = 1;
  for (;;)
  {
    if ( count % 60 == 0 )
    {
      if ( (rtc.getHour(true) >= 12) & (rtc.getHour(true) <= 23) )
      {
        dayX = 0;
      } else {
        dayX = 1;
      }
      calcSunriseSunset( rtc.getYear(), (rtc.getMonth() + monthX) , (rtc.getDay() + dayX), latitude, longitude, transit, sunrise, sunset );  // Calculate the times of sunrise, transit and sunset
      sunrise += time_zone;
      sunset  += time_zone;
      SolarTimeFormat( sunrise, 0 );
      SolarTimeFormat( sunset, 1 );
      calcCivilDawnDusk( rtc.getYear(), (rtc.getMonth() + monthX) , rtc.getDay(), latitude, longitude, transit, dawn, dusk); // Calculate the times of civil dawn and dusk (UTC)
      transit += time_zone;
      dawn    += time_zone;
      dusk    += time_zone;
      SolarTimeFormat( dawn, 2 );
      SolarTimeFormat( dusk, 3 );
      SolarTimeFormat( transit, 4 );
      calcHorizontalCoordinates( rtc.getYear(), (rtc.getMonth() + monthX) , rtc.getDay(),  rtc.getHour(true) , rtc.getMinute(), rtc.getSecond(), latitude, longitude, x_eData.azimuth, x_eData.elevation );
      x_eData.azimuth   = double(round(x_eData.azimuth * 100)) / 100; // Round to two decimal places
      x_eData.elevation = double(round(x_eData.elevation * 100)) / 100;
      if (count >= 3600 )
      {
        SolarTimeFormat( 0.0f, 5 ); // publish MQTT
        //log_i( "Hour:%d Azimuth %f, elevation %f, transit %dhr %dmin, dawn %dhr %dmin, dusk %dhr %dmin", rtc.getHour(true), x_eData.azimuth, x_eData.elevation, x_eData.TransitHr, x_eData.TransitMin, x_eData.DawnHr, x_eData.DawnMin, x_eData.DuskHr, x_eData.DuskMin );
        count = 0;
      }
    }
    //log_i( " high watermark % d",  uxTaskGetStackHighWaterMark( NULL ) );
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
    count++;
  } //for (;;)
  vTaskDelete( NULL );
} //void fSolarCalculations ( )
////
void SolarTimeFormat( double h, int i  )
{
  int hours   = 0;
  int minutes = 0;
  if ( h != 0.0f )
  {
    int m = int(round(h * 60));
    hours = (m / 60) % 24;
    minutes = m % 60;
  }
  switch ( i )
  {
    case 0:
      x_eData.SunRiseHr = hours;
      x_eData.SunRiseMin = minutes;
      break;
    case 1:
      x_eData.SunSetHr = hours;
      x_eData.SunSetMin = minutes;
      break;
    case 2:
      x_eData.DawnHr = hours;
      x_eData.DawnMin = minutes;
      break;
    case 3:
      x_eData.DuskHr = hours;
      x_eData.DawnMin = minutes;
      break;
    case 4:
      x_eData.TransitHr = hours;
      x_eData.TransitMin = minutes;
      break;
    case 5:
      String sTopic = "";
      sTopic.reserve( 35 );
      sTopic.concat( String(x_eData.SunRiseHr) + "," );
      sTopic.concat( String(x_eData.SunRiseMin) + "," );
      sTopic.concat( String(x_eData.SunSetHr) + "," );
      sTopic.concat( String(x_eData.SunSetMin) + "," );
      sTopic.concat( String(x_eData.DawnHr) + "," );
      sTopic.concat( String(x_eData.DawnMin) + "," );
      sTopic.concat( String(x_eData.TransitHr) + "," );
      sTopic.concat( String(x_eData.TransitMin) );
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY );
      MQTTclient.publish( topicSRSSDDT, sTopic.c_str() );
      xSemaphoreGive( sema_MQTT_KeepAlive );
      sTopic = "";
      sTopic.concat( String(x_eData.azimuth) + "," + String(x_eData.elevation) );
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY );
      MQTTclient.publish( topicAzEle, sTopic.c_str() );
      xSemaphoreGive( sema_MQTT_KeepAlive );
      sTopic = "";
      break;
  } // switch ( i ) {
} // void SolarTimeFormat( double h, int i  )
/*
  250-400ppm Normal background concentration in outdoor ambient air
  400-1,000ppm  Concentrations typical of occupied indoor spaces with good air exchange
  1,000-2,000ppm  Complaints of drowsiness and poor air.
  2,000-5,000 ppm Headaches, sleepiness and stagnant, stale, stuffy air. Poor concentration, loss of attention, increased heart rate and slight nausea may also be present.
  5,000 Workplace exposure limit (as 8-hour TWA) in most jurisdictions.
  >40,000 ppm Exposure may lead to serious oxygen deprivation resulting in permanent brain damage, coma, even death.
*/
void fParseDewPointWindChill( void *pvParameters )
{
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  struct stu_message px_message;
  String sDewPoint = "";
  String sWindChill = "";
  sDewPoint.reserve( payloadSize );
  sWindChill.reserve( payloadSize );
  for (;;)
  {
    if ( xQueueReceive(xQ_WindChillDewPoint, &px_message, portMAX_DELAY) == pdTRUE )
    {
      sDewPoint = px_message.payload;
      int commaIndex = sDewPoint.indexOf(',');
      sWindChill.concat ( sDewPoint.substring(0, commaIndex) );
      sDewPoint.remove( 0, (commaIndex + 1) );
      x_eData.WindChill = sWindChill.toFloat();
      x_eData.DewPoint = sDewPoint.toFloat();
      sDewPoint = "";
      sWindChill = "";
    }
    //log_i( " high watermark % d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
}
////
void fGetCO2 ( void *pvParameters )
{
  uint64_t TimePastKalman  = esp_timer_get_time();
  myMHZ19.begin( co2Serial );
  myMHZ19.autoCalibration();
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 1000; //delay for mS
  SimpleKalmanFilter KF_CO2( 1.0f, 1.0f, .01f );
  for ( ;; )
  {
    KF_CO2.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f );
    x_eData.CO2 = KF_CO2.updateEstimate( myMHZ19.getCO2() ); // apply simple Kalman filter
    TimePastKalman = esp_timer_get_time();
    xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY );
    MQTTclient.publish( topicCO2, String(round(x_eData.CO2)).c_str() );
    xSemaphoreGive( sema_MQTT_KeepAlive );
    // process wind chill and dew point
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
    //log_i( " high watermark % d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
} //void fMHZ19B ( void *pvParameters )
////
void fparseMQTT( void *pvParameters )
{
  struct stu_message px_message;
  for (;;)
  {
    if ( xQueueReceive(xQ_Message, &px_message, portMAX_DELAY) == pdTRUE )
    {
      xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
      mqttOK = 0;
      xSemaphoreGive( sema_mqttOK );
      if (px_message.topic == topicAQIndex )
      {

      }

      if ( px_message.topic == topicOutsidePressure )
      {
        x_eData.oPressure = String(px_message.payload).toFloat();
      }
      if ( px_message.topic == topicOutsideHumidity )
      {
        x_eData.oHumidity = String(px_message.payload).toFloat();
      }
      if ( px_message.topic == topicOutsideTemperature )
      {
        x_eData.oTemperature = String(px_message.payload).toFloat();
      }
      if ( px_message.topic == topicRemainingMoisture_0 )
      {
        x_eData.RM0  = String(px_message.payload).toFloat();
      }
      if ( px_message.topic == topicWindSpeed )
      {
        x_eData.WS = String(px_message.payload).toFloat();
      }
      if ( px_message.topic == topicWindDirection )
      {
        x_eData.WD = "";
        x_eData.WD = String(px_message.payload);
      }
      if ( px_message.topic == topicRainfall )
      {
        x_eData.RF = String(px_message.payload).toFloat();
      }

      //      if ( px_message.topic == topicWSVolts )
      //      {
      //        x_eData.WSV = String(px_message.payload).toFloat();
      //      }
      //      if ( px_message.topic == topicWSCurrent )
      //      {
      //        x_eData.WSC = String(px_message.payload).toFloat();
      //      }
      //      if ( px_message.topic == topicWSPower )
      //      {
      //        x_eData.WSP = String(px_message.payload).toFloat();
      //      }
      if ( px_message.topic == topicDPnWI )
      {
        xQueueSend( xQ_WindChillDewPoint, (void *) &px_message, 1 );
      }
      if ( String(px_message.topic) == topicOK )
      {
        if ( !TimeSet)
        {
          String temp = "";
          temp.reserve(50);
          temp.concat( String(px_message.payload[0]) );
          temp.concat( String(px_message.payload[1]) );
          temp.concat( String(px_message.payload[2]) );
          temp.concat( String(px_message.payload[3]) );
          int year =  temp.toInt();
          temp = "";
          temp.concat( String(px_message.payload[5]) + String(px_message.payload[6]) );
          int month =  temp.toInt();
          temp =  "";
          temp.concat(String(px_message.payload[8]) + String(px_message.payload[9]) );
          int day =  temp.toInt();
          temp = "";
          temp.concat( String(px_message.payload[11]) + String(px_message.payload[12]) );
          int hour =  temp.toInt();
          temp = "";
          temp.concat( String(px_message.payload[14]) + String(px_message.payload[15]) );
          int min =  temp.toInt();
          rtc.setTime( 0, min, hour, day, month, year );
          log_i( "rtc  %s Year %d month %d day %d", rtc.getTime(), rtc.getYear(), (rtc.getMonth() + 1), rtc.getDay() );
          TimeSet = true;
        }
      }
    } //if ( xQueueReceive(xQ_Message, &px_message, portMAX_DELAY) == pdTRUE )
  } //for(;;)
  vTaskDelete( NULL );
} // void fparseMQTT( void *pvParameters )
////
void fDoTheDisplayThing( void * parameter )
{
  struct stu_eData px_eData;
  const char HelloWorld[] = "Hello World!";
  //display.init();
  //display.setFont(&FreeMonoBold9pt7b);
  //display.setTextColor(GxEPD_BLACK);
  int yIncrement = 18;
  int CurrentY = 20;
  int CurrentX = 5;
  String temp1 = "";
  temp1.reserve(10);
  String temp2 = "";
  temp2.reserve(10);
  int boxSpacing = 80;
  size_t item_size;
  for (;;)
  {

    // add in dew point display


    xEventGroupWaitBits (eg, evtDisplayUpdate, pdTRUE, pdTRUE, portMAX_DELAY );
    //log_i( "pip" );
    CurrentY = 20;
    display.init();
    //display.setFont(&FreeMonoBold9pt7b);
    display.setFont(&FreeMono9pt7b);
    //u8g2Fonts.setFont(u8g2_font_helvB08_tf);
    display.setTextColor(GxEPD_BLACK);
    display.setFullWindow();
    display.fillScreen(GxEPD_WHITE); // set the background to white (fill the buffer with value for white)
    display.setCursor( CurrentX, CurrentY );
    // first line
    display.drawRect( CurrentX, CurrentY , 70, 55, GxEPD_BLACK);
    display.drawBitmap( CurrentX + 10, CurrentY + 5, temperature_icon16x16, 16, 16, GxEPD_BLACK);
    display.setCursor( CurrentX + 30, CurrentY + 15 );
    //display.print( char(223) + "F" );
    display.print( "F" );
    display.setCursor( CurrentX + 10, CurrentY + 40);
    display.print( String(x_eData.oTemperature) );
    display.drawRect( CurrentX + boxSpacing, CurrentY , 70, 55, GxEPD_BLACK);
    display.setCursor( CurrentX + 90, CurrentY + 15 );
    display.print( "R.H.");
    display.setCursor( CurrentX + 90, CurrentY + 35 );
    display.print( String((int)x_eData.oHumidity) + "%" );
    display.setCursor( CurrentX, CurrentY + 40);
    display.drawRect( CurrentX + (boxSpacing * 2 ), CurrentY , 70, 55, GxEPD_BLACK);
    display.setCursor( CurrentX + 163, CurrentY + 15 );
    display.print( "Dew Pt" );
    display.setCursor( CurrentX + 165, CurrentY + 35 );
    display.print( String(x_eData.DewPoint) );
    display.drawRect( CurrentX + (boxSpacing * 3 ), CurrentY , 70, 55, GxEPD_BLACK);
    display.setCursor( CurrentX + 246, CurrentY + 15 );
    display.print( "AQI" );
    display.setCursor( CurrentX + 246, CurrentY + 35 );
    display.print( String(int(x_eData.IAQ)) + "%" );
    display.drawRect( CurrentX + (boxSpacing * 4 ), CurrentY , 70, 55, GxEPD_BLACK);
    display.setCursor( CurrentX + 327, CurrentY + 15 );
    display.print( "R.M." );
    display.setCursor( CurrentX + 327, CurrentY + 35 );
    display.print( String(int(x_eData.RM0)) + "%" );
    // end of first line
    if ( x_eData.SunRiseMin < 10 )
    {
      temp1.concat( "0" + String(x_eData.SunRiseMin) );
    } else {
      temp1.concat( String(x_eData.SunRiseMin) );
    }
    if ( x_eData.SunSetMin < 10 )
    {
      temp2.concat( "0" + String(x_eData.SunSetMin) );
    } else {
      temp2.concat( String(x_eData.SunSetMin) );
    }
    CurrentY += yIncrement;
    CurrentY += yIncrement;
    CurrentY += yIncrement;
    CurrentY += yIncrement;
    display.setCursor( CurrentX, CurrentY );
    display.print( "Wind: " );
    CurrentY += yIncrement;
    display.setCursor( CurrentX, CurrentY );
    display.print( "Speed " + String(x_eData.WS) + "KPH, Dir " + String(x_eData.WD) + " Chill " + String(x_eData.WindChill) + "F" );
    CurrentY += yIncrement;
    display.drawRect( CurrentX, CurrentY , 70, 55, GxEPD_BLACK);
    addsun( 35, CurrentY + 30 , Small, SmallIcon );
    display.setCursor( CurrentX + 5, CurrentY + 15 );
    display.print( "0" + String(x_eData.SunRiseHr) + ":" + temp1 );
    display.setCursor( CurrentX + 5, CurrentY + 50 );
    display.print( String(x_eData.SunSetHr) + ":" + temp2 );
    display.drawRect( CurrentX + boxSpacing, CurrentY , 70, 55, GxEPD_BLACK);
    addraindrop(CurrentX + 110, CurrentY + 15, 7);
    display.setCursor( CurrentX + 90, CurrentY + 35 );
    display.print( String(x_eData.RF) );
    display.setCursor( CurrentX + 100, CurrentY + 50 );
    display.print( "mm" );
    display.drawRect( CurrentX + (boxSpacing * 2 ), CurrentY , 70, 55, GxEPD_BLACK);
    display.setCursor( CurrentX + 177, CurrentY + 15 );
    display.print( "C02" );
    display.setCursor( CurrentX + 165, CurrentY + 35 );
    display.print( String(int(x_eData.CO2)) );
    display.setCursor( CurrentX + 165, CurrentY + 50 );
    display.print( "PPM" );
    //make graph
    xSemaphoreTake( sema_CollectPressure, portMAX_DELAY );
    CurrentY += yIncrement * 6;
    display.setCursor( CurrentX, CurrentY); //set cursor position
    //display.drawLine( CurrentX, CurrentY, CurrentX + 200, CurrentY, GxEPD_BLACK);
    int BaseLine = CollectionPressure[0];
    int offsetX = 0;
    for ( int j = 0; j < BufferCount; j++ )
    {
      if ( CollectionPressure[j] != 0.0f )
      {
        int yAdj = BaseLine - (int)CollectionPressure[j];
        display.setCursor( CurrentX + offsetX, CurrentY + yAdj );
        display.print( "-" );
        offsetX += 5;
        // log_i( "pressure %f item %d", CollectionPressure[j], j );
      }
    }
    CurrentY += yIncrement;
    display.setCursor( CurrentX, CurrentY );
    display.print( String(x_eData.oPressure) + "mmHg" );
    int Xone = 48;
    int Yone = 59;
    // slope of air pressure (y2-y1)/ (x2-x1)
    CurrentY += yIncrement;
    display.setCursor( CurrentX, CurrentY );
    //display.print( String( (CollectionPressure[(int)Yone] - Yone) / (CollectionPressure[(int)Xone] - Xone), 2) );
    display.print( String( CollectionPressure[Yone] / CollectionPressure[Xone], 6) );
    xSemaphoreGive( sema_CollectPressure );
    temp2 = "";
    temp1 = "";
    //
    display.display(false); // full update
    display.hibernate();
    //log_i( "DoTheBME280Thing high watermark % d",  uxTaskGetStackHighWaterMark( NULL ) );
  } //for (;;)
  vTaskDelete( NULL );
} //void fDoTheDisplayTHing( void * parameter )
////
void fmqttWatchDog( void * paramater )
{
  int UpdateImeTrigger = 86400; //seconds in a day
  int UpdateTimeInterval = 86300; // 1st time update in 100 counts
  int maxNonMQTTresponse = 15;
  for (;;)
  {
    vTaskDelay( 1000 );
    if ( mqttOK >= maxNonMQTTresponse )
    {
      ESP.restart();
    }
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
    mqttOK++;
    xSemaphoreGive( sema_mqttOK );
    UpdateTimeInterval++; // trigger new time get
    if ( UpdateTimeInterval >= UpdateImeTrigger )
    {
      TimeSet = false; // sets doneTime to false to get an updated time after a days count of seconds
      UpdateTimeInterval = 0;
    }
  }
  vTaskDelete( NULL );
}
////
float fCalulate_IAQ_Index( int gasResistance, float Humidity)
{
  float hum_baseline = 40.0f;
  float hum_weighting = 0.25f;
  float gas_offset = 0.0f;
  float hum_offset = 0.0f;
  float hum_score = 0.0f;
  float gas_score = 0.0f;
  gas_offset = oGasResistanceBaseLine - float( gasResistance );
  hum_offset = float( Humidity ) - hum_baseline;
  // calculate hum_score as distance from hum_baseline
  if ( hum_offset > 0.0f )
  {
    hum_score = 100.0f - hum_baseline - hum_offset;
    hum_score /= ( 100.0f - hum_baseline );
    hum_score *= ( hum_weighting * 100.0f );
  } else {
    hum_score = hum_baseline + hum_offset;
    hum_score /= hum_baseline;
    hum_score *= ( 100.0f - (hum_weighting * 100.0f) );
  }
  //calculate gas score as distance from baseline
  if ( gas_offset > 0.0f )
  {
    gas_score = float( gasResistance ) / oGasResistanceBaseLine;
    gas_score *= ( 100.0f - (hum_weighting * 100.0f ) );
  } else {
    gas_score = 100.0f - ( hum_weighting * 100.0f );
  }
  return ( hum_score + gas_score );
} //void fCalulate_IAQ_Index( int gasResistance, float Humidity):
////
void DoTheBME680Thing( void *pvParameters )
{
  SPI.begin(); // initialize the SPI library
  vTaskDelay( 10 );
  if (!bme.begin()) {
    log_i("Could not find a valid BME680 sensor, check wiring!");
    while (1);
  }
  // Set up oversampling and filter initialization
  bme.setTemperatureOversampling(BME680_OS_8X);
  bme.setHumidityOversampling(BME680_OS_2X);
  bme.setPressureOversampling(BME680_OS_4X);
  bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
  bme.setGasHeater(320, 150); // 320*C for 150 ms
  //wait for a mqtt connection
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  xEventGroupSetBits( eg, evtWaitForBME );
  String bmeInfo = "";
  bmeInfo.reserve( 100 );
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDoBME, pdTRUE, pdTRUE, portMAX_DELAY );
    x_eData.Temperature  = bme.readTemperature();
    x_eData.Temperature  = ( x_eData.Temperature * 1.8f ) + 32.0f; // (Celsius x 1.8) + 32
    x_eData.Pressure     = bme.readPressure();
    x_eData.Pressure     = x_eData.Pressure / 133.3223684f; //converts to mmHg
    x_eData.Humidity     = bme.readHumidity();
    x_eData.IAQ          = fCalulate_IAQ_Index( bme.readGas(), x_eData.Humidity );
    //log_i( " temperature % f, Pressure % f, Humidity % f IAQ % f", x_eData.Temperature, x_eData.Pressure, x_eData.Humidity, x_eData.IAQ);
    bmeInfo.concat( String(x_eData.Temperature, 2) );
    bmeInfo.concat( "," );
    bmeInfo.concat( String(x_eData.Pressure, 2) );
    bmeInfo.concat( "," );
    bmeInfo.concat( String(x_eData.Humidity, 2) );
    bmeInfo.concat( "," );
    bmeInfo.concat( String(x_eData.IAQ, 2) );
    xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY );
    if ( MQTTclient.connected() )
    {
      MQTTclient.publish( topicInsideInfo, bmeInfo.c_str() );
    }
    xSemaphoreGive( sema_MQTT_KeepAlive );
    xSemaphoreGive( sema_PublishPM ); // release publish of dust density
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
    mqttOK ++;
    xSemaphoreGive( sema_mqttOK );
    xEventGroupSetBits( eg, evtDisplayUpdate );
    bmeInfo = ""; // empty the string buffer
    findDewPointWithHumidity( x_eData.Humidity, x_eData.Temperature );
    xEventGroupSetBits( eg, evtStoreAirPressure );
    // log_i( "DoTheBME280Thing high watermark % d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete ( NULL );
}
////
/*
  Important to not set vTaskDelay/vTaskDelayUntil to less then 10. Errors begin to develop with the MQTT and network connection.
  makes the initial wifi/mqtt connection and works to keeps those connections open.
*/
void MQTTkeepalive( void *pvParameters )
{
  sema_MQTT_KeepAlive   = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_MQTT_KeepAlive ); // found keep alive can mess with a publish, stop keep alive during publish
  MQTTclient.setKeepAlive( 90 ); // setting keep alive to 90 seconds makes for a very reliable connection, must be set before the 1st connection is made.
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 250; //delay for ms
  for (;;)
  {
    //check for a is-connected and if the WiFi 'thinks' its connected, found checking on both is more realible than just a single check
    if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
    {
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // whiles MQTTlient.loop() is running no other mqtt operations should be in process
      MQTTclient.loop();
      xSemaphoreGive( sema_MQTT_KeepAlive );
    }
    else {
      log_i( "MQTT keep alive found MQTT status % s WiFi status % s", String(wifiClient.connected()), String(WiFi.status()) );
      if ( !(wifiClient.connected()) || !(WiFi.status() == WL_CONNECTED) )
      {
        connectToWiFi();
      }
      connectToMQTT();
    }
    //log_i( " high watermark % d",  uxTaskGetStackHighWaterMark( NULL ) );
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
  }
  vTaskDelete ( NULL );
}
////
void connectToMQTT()
{
  byte mac[5]; // create client ID from mac address
  WiFi.macAddress(mac); // get mac address
  String clientID = String(mac[0]) + String(mac[4]) ; // use mac address to create clientID
  while ( !MQTTclient.connected() )
  {
    MQTTclient.connect( clientID.c_str(), mqtt_username, mqtt_password );
    vTaskDelay( 250 );
  }
  MQTTclient.setCallback ( mqttCallback );
  MQTTclient.subscribe   ( topicOK );
  MQTTclient.subscribe   ( topicRemainingMoisture_0 );
  MQTTclient.subscribe   ( topicWindSpeed );
  MQTTclient.subscribe   ( topicWindDirection );
  MQTTclient.subscribe   ( topicDPnWI );
  MQTTclient.subscribe   ( topicOutsideTemperature );
  MQTTclient.subscribe   ( topicOutsideHumidity );
  MQTTclient.subscribe   ( topicOutsidePressure );
  MQTTclient.subscribe   ( topicRainfall );
} //void connectToMQTT()
void connectToWiFi()
{
  int TryCount = 0;
  while ( WiFi.status() != WL_CONNECTED )
  {
    TryCount++;
    WiFi.disconnect();
    WiFi.begin( SSID, PASSWORD );
    vTaskDelay( 4000 );
    if ( TryCount == 10 )
    {
      ESP.restart();
    }
  }
  WiFi.onEvent( WiFiEvent );
}
////
float findDewPointWithHumidity( float humi, float temperature )
{
  //Celcius
  float ans =  (temperature - (14.55 + 0.114 * temperature) * (1 - (0.01 * humi)) - pow(((2.5 + 0.007 * temperature) * (1 - (0.01 * humi))), 3) - (15.9 + 0.117 * temperature) * pow((1 - (0.01 * humi)), 14));
  //log_i( "%f", ans );
  return ans;
}
////
void addraindrop(int x, int y, int scale)
{
  display.fillCircle(x, y, scale / 2, GxEPD_BLACK);
  display.fillTriangle(x - scale / 2, y, x, y - scale * 1.2, x + scale / 2, y , GxEPD_BLACK);
  x = x + scale * 1.6; y = y + scale / 3;
  display.fillCircle(x, y, scale / 2, GxEPD_BLACK);
  display.fillTriangle(x - scale / 2, y, x, y - scale * 1.2, x + scale / 2, y , GxEPD_BLACK);
}
////
void addsun(int x, int y, int scale, bool IconSize)
{
  int linesize = 3;
  if (IconSize == SmallIcon) linesize = 1;
  display.fillRect(x - scale * 2, y, scale * 4, linesize, GxEPD_BLACK);
  display.fillRect(x, y - scale * 2, linesize, scale * 4, GxEPD_BLACK);
  display.drawLine(x - scale * 1.3, y - scale * 1.3, x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
  display.drawLine(x - scale * 1.3, y + scale * 1.3, x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
  if (IconSize == LargeIcon) {
    display.drawLine(1 + x - scale * 1.3, y - scale * 1.3, 1 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
    display.drawLine(2 + x - scale * 1.3, y - scale * 1.3, 2 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
    display.drawLine(3 + x - scale * 1.3, y - scale * 1.3, 3 + x + scale * 1.3, y + scale * 1.3, GxEPD_BLACK);
    display.drawLine(1 + x - scale * 1.3, y + scale * 1.3, 1 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
    display.drawLine(2 + x - scale * 1.3, y + scale * 1.3, 2 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
    display.drawLine(3 + x - scale * 1.3, y + scale * 1.3, 3 + x + scale * 1.3, y - scale * 1.3, GxEPD_BLACK);
  }
  display.fillCircle(x, y, scale * 1.3, GxEPD_WHITE);
  display.fillCircle(x, y, scale, GxEPD_BLACK);
  display.fillCircle(x, y, scale - linesize, GxEPD_WHITE);
}
////
void loop() { }

Some ESP32 code I am working on.

Powering ESP32 from PC USB port will not like OLED VCC placed on 5V pin? this will be the problem?

I have started the recomended serial print test:

Now we wait to see if you see that it has stopped but does it keep printing or not.

1 Like

Code looks running well during getting samples from mini jack:

Now running since 10 min:>

Running ended 15:58:37 no new lines on serial monitor. Lookslike serial.println also gived up, oled screan also death.

Im sure if im press EN, will work again til a random time.

I saw maybe esp can give us log printed to serial monitor, but i dont know how.

Show an image of your project, post schematic.

Is your debug level set to debug? Did you get a fault print out in the serial monitor?

1 Like

I have posted ealier a self created schematic, not the best, but everything visible. Let me know if something missing.

Log level was set to verbose - no any bad line, simple stopped printing.

I have weird idea - in a similar project developer requested lower the version of the board driver to 1.04. because esp32 got several time crash.

This project:

I have downgraded esp32 board driver and set debug level to debug andcomplied with the downgraded esp32 board driver, now serial monitor again runs and i will leave run - im back later with the result.

In the tutorial oled display Vcc is 3.3V.

Post pics of your project, not just the display. How are you powering the project?

Sure, but the OLED display support working with 5V too. But i can try give power to OLED not from board.

Now the esp32 crashed, without using/working display - OLED required if i good know minimal power, if not displaying anything.