I have following program running on ESP32 I need help understanding how its working
void setup()
{
Serial.begin(112500);
/* we create a new task here */
xTaskCreate(
anotherTask, /* Task function. */
"another Task", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
NULL); /* Task handle to keep track of created task */
}
void loop()
{
Serial.println("Task 1");
delay(1000);
}
void anotherTask( void * parameter )
{
/* loop forever */
for(;;)
{
Serial.println("another Task");
delay(1000);
}
/* delete a task when finish,
this will never happen because this is infinity loop */
vTaskDelete( NULL );
}
output
19:46:25.113 -> another Task
19:46:25.113 -> Task 1
19:46:26.111 -> another Task
19:46:26.111 -> Task 1
19:46:27.109 -> another Task
19:46:27.109 -> Task 1
19:46:28.090 -> another Task
19:46:28.090 -> Task 1
19:46:29.088 -> another Task
19:46:29.088 -> Task 1
19:46:30.117 -> another Task
19:46:30.117 -> Task 1
19:46:31.108 -> another Task
19:46:31.108 -> Task 1
19:46:32.105 -> another Task
19:46:32.105 -> Task 1
19:46:33.086 -> another Task
19:46:33.086 -> Task 1
19:46:34.082 -> another Task
19:46:34.082 -> Task 1
19:46:35.097 -> another Task
19:46:35.097 -> Task 1
19:46:36.108 -> another Task
19:46:36.108 -> Task 1
19:46:37.121 -> another Task
19:46:37.121 -> Task 1
19:46:38.117 -> another Task
19:46:38.117 -> Task 1
19:46:39.113 -> another Task
19:46:39.113 -> Task 1
My understanding is (I am new to this myself) that LOOP in your sketch is running as a task, when you issue the command "xTaskCreate" this then creates a second task (anotherTask) which starts running along side LOOP (so you then effectively have two programs running at the same time on the esp32).
I have been experimenting with using tasks myself recently and created this sketch which may be of interest/help
As soon as that task is created it starts running.
With freeRTOS and an ESP32 putting code in the loop() defeats the memory cleanup functions and code in loop() is NOT guaranteed any run time as loop() has a priority if 1; the lowest priority.
/*
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;
} 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( fStoreAirPressure, "fStoreAirPressure", 10000, NULL, 2, NULL, 1 );
} //void setup()
////
void fStoreAirPressure ( 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;
}
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) );
// 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;
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;
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" );
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.setCursor( CurrentX + 246, CurrentY + 50 );
display.print( "%" );
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)) + "%" );
//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++ )
{
//log_i( " %d , %f", j,CollectionPressure[j] );
if ( CollectionPressure[j] != 0.0f )
{
int yAdj = (int)CollectionPressure[j] - BaseLine;
display.setCursor( CurrentX + offsetX, CurrentY + yAdj );
display.print( "-" );
offsetX += 5;
log_i( "pressure %f item %d", CollectionPressure[j], j );
}
}
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() { }
I have written following code just for experiement. I was expecting to print only first task because it has highest priority but my code is executing both tasks. I don't understand why the second task is executing
void setup()
{
Serial.begin(112500);
/* Create two tasks. */
xTaskCreate( vTask1, "Task 1", 10000, NULL, 2, NULL);
xTaskCreate( vTask2, "Task 2", 10000, NULL, 1, NULL);
}
void loop() {
// Do nothing
}
void vTask1( void *pvParameters )
{
/* As per most tasks, this task is implemented in an infinite loop. */
for(;;)
{
Serial.println("Task 1 is running");
delay(1000);
}
}
void vTask2( void *pvParameters )
{
for(;;)
{
Serial.println("Task 2 is running");
delay(1000);
}
}
02:01:51.278 -> Task 1 is running
02:01:51.278 -> Task 2 is running
02:01:52.275 -> Task 1 is running
02:01:52.275 -> Task 2 is running
02:01:53.257 -> Task 1 is running
02:01:53.257 -> Task 2 is running
02:01:54.254 -> Task 1 is running
02:01:54.254 -> Task 2 is running
02:01:55.252 -> Task 1 is running
02:01:55.252 -> Task 2 is running
02:01:56.252 -> Task 1 is running
02:01:56.252 -> Task 2 is running
02:01:57.250 -> Task 1 is running
02:01:57.250 -> Task 2 is running
02:01:58.263 -> Task 1 is running
02:01:58.263 -> Task 2 is running
02:01:59.243 -> Task 1 is running
02:01:59.243 -> Task 2 is running
02:02:00.274 -> Task 1 is running
02:02:00.274 -> Task 2 is running
The vTaskDelete ( NULL ); prevents the task from doing to much damage to the other tasks in case a task jumps its stack.
Use vTaskDelay instead of delay()
How long does task 1 take to run its code? pico seconds. When task1 gets to sleep what happens next? Why task2 has time to run. so task2 runs and sleeps. task 1 is created slightly ahead of task2.