I, yesterday, switched from using the Adafruit Ultimate GPS library to using TinyGPS++. I use hardware serial.
Code: Select all
#include <driver/adc.h>
#include <Adafruit_BMP280.h>
#include <SPI.h>
#include "UTM.h"
#include <U8g2lib.h>
#include <stdio.h> //for function sprintf
#include <TinyGPS++.h>
#include <HardwareSerial.h>
#include "esp_system.h" //This inclusion configures the peripherals in the ESP system.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
/* define event bits */
#define CalDistance ( 1 << 0 ) //1
#define AnalogVoltReadTask ( 1 << 1 ) //10
#define DO_UTM_TASK_BIT ( 1 << 2 ) //100
#define UPDATE_DISPLAY_TASK_BIT ( 1 << 3 ) //1000
#define GPS_Parse ( 1 << 4 ) //10000
#define NotUsed ( 1 << 5 ) //100000
#define PressureEvent ( 1 << 6 ) //1000000
#define DoPressureDataCollectionEvent ( 1 << 7 ) //10000000
#define RunTimeEvent ( 1 << 8 ) //100000000
#define OneSecondGroupBits ( UPDATE_DISPLAY_TASK_BIT | RunTimeEvent )
////////////////////////////////////////////////////////////////////////////////////
/* create a hardware timer */
hw_timer_t * timer = NULL;
/* create event group */
EventGroupHandle_t eg;
//////////////////////////////////
SemaphoreHandle_t sema_CalDistance;
SemaphoreHandle_t sema_PressureDataCollection;
SemaphoreHandle_t sema_UTM;
SemaphoreHandle_t sema_RunTime;
SemaphoreHandle_t sema_AnalogVoltRead;
SemaphoreHandle_t sema_CheckPressure;
SemaphoreHandle_t sema_GPS_Gate;
SemaphoreHandle_t sema_Time;
SemaphoreHandle_t sema_Date;
SemaphoreHandle_t sema_Posit;
SemaphoreHandle_t sema_Sats;
SemaphoreHandle_t sema_Alti;
SemaphoreHandle_t sema_Hdg;
//SemaphoreHandle_t sema_UTM_Posit;
////
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
////
#define TimerDivider 80
#define TaskCore1 1
#define TaskCore0 0
// #define A0Pin 36 //analog pin
// #define A1Pin 39 //analog pin
#define BlinkLED 2
#define FiveHundred 500
#define EightHundred 800
#define OneK 1000
#define OneKfive 1500
#define TwoK 2000
#define ThreeThreeK 3300
#define BlinkBuiltInK 10000
#define TwentyK 20000
#define UpdateDisplayK 20500
#define Priority1 1
#define Priority2 2
#define Priority3 3
#define Priority4 4
#define Priority5 5
// #define uPvolts 3.3
// #define ADbits 4095
#define SerialDataBits 115200
#define GPS_DataBits 9600
#define TIMER_FOUR 3
#define PressureDataArrayCount 8
/////////////////////////////////
TickType_t xTicksToWait0 = 0;
TickType_t QueueReceiveDelayTime = 10;
TickType_t xTicksToWait20 = 20;
TickType_t xSemaphoreTicksToWait = 12;
////////////////////////////////
HardwareSerial GPSSerial(2);
// Connect to the GPS on the hardware port
// The TinyGPS++ object
TinyGPSPlus GPS;
// UltimateGPS rx to ESP32 I017 - Serial2
// UltimateGPS tx to ESP32 IO16 - Serial2
// pin 16=RX, pin 17=TX
//?????????????????????????????????????????
volatile int iTicCount = 0; //counts milliseconds
bool bLED_On = false;
///////////////////////////////////////////
QueueHandle_t xQ_DistanceMessage;
struct stuDistance
{
double iDistance = 0; //meters
float Speed = 0.0; // m.p.h.
} xDistanceMessage;
////
QueueHandle_t xQ_PowerMessage;
struct stuPower
{
float Vbatt = 0.0;
float BattPercent = 0;
} xPowerMessage;
/////////////////////////////////////
// from lat long to UTM conversion, shared data between two functions
QueueHandle_t xQ_UTM_Message;
struct stuUTM
{
float UTM_North = 0.0; // holds northing info
float UTM_West = 0.0; // holds easting info
int iZone = 0; // holds zone info
} xUTM_Message;
////////////////////////////////////////////////////////////////
//temperature, pressure, altitude, sealevelpressure
#define DEVICE_PIN 5
// BME_SCK 18 // keep for reference info
// BME_MISO 19
// BME_MOSI 23
Adafruit_BMP280 bme(DEVICE_PIN); // hardware SPI
// Adafruit_BMP280 bme(DEVICE_PIN, BME_MOSI, BME_MISO, BME_SCK); // keep for reference
QueueHandle_t xQ_PressureMessage;
struct stuPressureMessage
{
float BMEpressure = 0.0;
float BMEtemperature = 0.0;
} xPressureMessage;
// float BMEpressure = 0.0;
// float BMEtemperature = 0.0;
////////////////////////////////////////////////////////////////
QueueHandle_t xQ_PressureDataMessage;
struct stuPressureData
{
int mmHg_Hourly[8] =
{
0, 0, 0, 0, 0, 0, 0, 0
};
int BMEpressure = 0;
} xPressureData;
//
QueueHandle_t xQ_RunTimeMessage;
struct stuRunTimeData
{
int iSeconds;
int iMinutes;
int iHours;
} xRunTimeData;
//
QueueHandle_t xQ_Time;
struct stuTime
{
int iSeconds;
int iMinutes;
int iHours;
} xTime;
////
QueueHandle_t xQ_Date;
struct stuDate
{
int iDay;
int iMonth;
int iYear;
} xDate;
////
QueueHandle_t xQ_Posit;
struct stuPosit
{
double Lat;
double Lon;
} xPosit;
////
QueueHandle_t xQ_Sats;
////
QueueHandle_t xQ_Alti;
////
QueueHandle_t xQ_UTM_Posit;
struct stuUTM_Posit
{
float Lat;
float Lon;
} xUTM_Posit;
////
QueueHandle_t xQ_Hdg;
////
////////////////////////////////////////////////////////////////
// timer ISR callback set at 1000X a second
void IRAM_ATTR onTimer()
{
BaseType_t xHigherPriorityTaskWoken;
iTicCount++;
//
if ( iTicCount == OneK )
{
xEventGroupSetBitsFromISR(eg, OneSecondGroupBits, &xHigherPriorityTaskWoken); // trigger every 1X a second
}
//
if ( (xSemaphoreTakeFromISR(sema_GPS_Gate, &xHigherPriorityTaskWoken)) == pdTRUE ) // grab semaphore, no wait
{
xEventGroupSetBitsFromISR(eg, GPS_Parse, &xHigherPriorityTaskWoken); // trigger every 1mS, if not already processing
} //
if ( iTicCount == OneK )
{
// reset counter to start again
iTicCount = 0;
}
} // void IRAM_ATTR onTimer()
////
////
void setup()
{
////
Serial.begin( SerialDataBits );
pinMode( BlinkLED, OUTPUT ); // for blink LED
SPI.begin();
while (!bme.begin())
{
Serial.println("Could not find BME280 sensor!");
vTaskDelay( pdMS_TO_TICKS( OneK ) );
}
//
u8g2.begin();
////
eg = xEventGroupCreate();
//
GPSSerial.begin( GPS_DataBits ); // begin GPS hardwareware serial
//
vTaskDelay( pdMS_TO_TICKS( OneK ) ); // delay one second
// https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
// set up A:D channels
adc1_config_width(ADC_WIDTH_12Bit);
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_11db);
adc1_config_width(ADC_WIDTH_12Bit);
adc1_config_channel_atten(ADC1_CHANNEL_1, ADC_ATTEN_11db);
/*
When VDD_A is 3.3V: 11dB attenuation (ADC_ATTEN_11db) gives full-scale voltage 3.9V
*/
////
/* Use 4th timer of 4.
1 tick 1/(80MHZ/80) = 1us set divider 80 and count up.
Attach onTimer function to timer
Set alarm to call timer ISR, every 10000uS and repeat / reset ISR (true) after each alarm
Start an timer alarm
*/
timer = timerBegin( TIMER_FOUR, TimerDivider, true );
timerAttachInterrupt( timer, &onTimer, true );
timerAlarmWrite(timer, OneK, true);
timerAlarmEnable(timer);
////
///////////// create queues
// queue length, queue item size
xQ_PressureMessage = xQueueCreate( 1, sizeof(struct stuPressureMessage*) ); // sends a queue pointer to the structure
xQ_PressureDataMessage = xQueueCreate( 1, sizeof(struct stuPressureData*) ); // sends a queue pointer to the structure
xQ_DistanceMessage = xQueueCreate( 3, sizeof(struct stuDistance) ); // sends a copy of the structure
xQ_PowerMessage = xQueueCreate( 1, sizeof(struct stuPower*) ); // sends a queue pointer to the structure
xQ_UTM_Message = xQueueCreate( 1, sizeof(struct stuUTM) ); //sends copy of the structure
xQ_RunTimeMessage = xQueueCreate( 1, sizeof(struct stuRunTimeData*) ); //sends a queue pointer to the structure
xQ_Time = xQueueCreate( 1, sizeof(struct stuTime*) );
xQ_Date = xQueueCreate( 5, sizeof(struct stuDate*) );
xQ_Posit = xQueueCreate( 5, sizeof(struct stuPosit*) );
xQ_Sats = xQueueCreate( 1, sizeof(int) );
xQ_Alti = xQueueCreate( 1, sizeof(float) );
xQ_UTM_Posit = xQueueCreate( 1, sizeof(stuUTM_Posit*) );
xQ_Hdg = xQueueCreate( 1, sizeof(float) );
////////////////////////////
// Task function, name of task, Stack size of task, parameter of the task, priority of the task, Task handle to keep track of created task, task core
/////////////////////////// TASK CORE 0
xTaskCreatePinnedToCore( fBlinkBuiltIn, "fBlinkBuiltIn", BlinkBuiltInK, NULL, Priority4, NULL, TaskCore0 ); //assigned to core 0
//
xTaskCreatePinnedToCore( fGPS_Parse, "fGPS_Parse", 10200, NULL, Priority4, NULL, TaskCore0 ); // assigned to core 0
sema_Time = xSemaphoreCreateMutex();
xSemaphoreGive( sema_Time );
sema_Date = xSemaphoreCreateMutex();
xSemaphoreGive( sema_Date );
sema_Posit = xSemaphoreCreateMutex();
xSemaphoreGive( sema_Posit);
sema_Sats = xSemaphoreCreateMutex();
xSemaphoreGive( sema_Sats );
sema_Alti = xSemaphoreCreateMutex();
xSemaphoreGive( sema_Alti );
xQ_UTM_Posit = xSemaphoreCreateMutex();
xSemaphoreGive( xQ_UTM_Posit );
sema_CalDistance = xSemaphoreCreateMutex(); // create Semaphore
xSemaphoreGive( sema_CalDistance );
sema_Hdg = xSemaphoreCreateMutex();
xSemaphoreGive( sema_Hdg );
//
xTaskCreatePinnedToCore( TaskAnalogVoltRead, "fTaskAnalogVoltRead", BlinkBuiltInK, NULL, Priority4, NULL, TaskCore0 ); // assigned to core 0
sema_AnalogVoltRead = xSemaphoreCreateMutex();
xSemaphoreGive( sema_AnalogVoltRead );
//
xTaskCreatePinnedToCore( fGetUTM, "fGetUTM", 15000, NULL, Priority4, NULL, TaskCore0 ); // assigned to core 0
sema_UTM = xSemaphoreCreateMutex();
xSemaphoreGive( sema_UTM );
//
xTaskCreatePinnedToCore( fRunTime, "fRunTime", 10200, NULL, Priority4, NULL, TaskCore0 ); //assigned to core 0 keep opposite from fUpdateDisplay
sema_RunTime = xSemaphoreCreateMutex();
xSemaphoreGive( sema_RunTime );
//
xTaskCreatePinnedToCore( fDoPressureDataCollection, "fDoPressureDataCollection", BlinkBuiltInK, NULL, Priority4, NULL, TaskCore0 ); // assigned to core 0
sema_PressureDataCollection = xSemaphoreCreateMutex();
xSemaphoreGive( sema_PressureDataCollection );
//////////////////////////////// TASK CORE 1
xTaskCreatePinnedToCore( fUpdateDisplay, "fUpdateDisplay", UpdateDisplayK, NULL, Priority4, NULL, TaskCore1 ); // assigned to core 1
//
sema_GPS_Gate = xSemaphoreCreateMutex();
xSemaphoreGive( sema_GPS_Gate );
//
xTaskCreatePinnedToCore( fCheck_Pressure, "fCheck_Pressure", BlinkBuiltInK, NULL, Priority3, NULL, TaskCore1 ); //assigned to core 1
sema_CheckPressure = xSemaphoreCreateMutex(); // create Semaphore
xSemaphoreGive( sema_CheckPressure );
//////
} //void setup() // runs on core 1
///////////////////////////
void loop() {} // runs on core 1
//////////////////////////
/////////////////////////
//
void fRunTime( void * parameter )
{
struct stuRunTimeData *pxMessage;
for (;;)
{
xEventGroupWaitBits (eg, RunTimeEvent, pdTRUE, pdTRUE, portMAX_DELAY) ;
if ( xSemaphoreTake( sema_RunTime, xSemaphoreTicksToWait ) == pdTRUE )
{
xRunTimeData.iSeconds++;
// Serial.println( xRunTimeData.iSeconds );
if ( xRunTimeData.iSeconds == 60 )
{
xRunTimeData.iMinutes++;
xRunTimeData.iSeconds = 0;
}
if ( xRunTimeData.iMinutes == 60 )
{
xRunTimeData.iHours++;
xRunTimeData.iMinutes = 0;
}
pxMessage = &xRunTimeData;
xQueueOverwrite( xQ_RunTimeMessage, (void *) &pxMessage );
xSemaphoreGive( sema_RunTime );
}
// Serial.println( "fRunTime" );
// Serial.print( "fRunTime " );
// Serial.print(uxTaskGetStackHighWaterMark( NULL ));
// Serial.println();
// Serial.flush();
}
vTaskDelete( NULL );
} // void fRunTime( void * parameter )
//
void fDoPressureDataCollection( void * parameter )
{
struct stuPressureData *pxMessage;
int iArrayCell = 0;
bool bDidInitialArray = false;
int iPast = 0;
for (;;)
{
xEventGroupWaitBits (eg, DoPressureDataCollectionEvent, pdTRUE, pdTRUE, portMAX_DELAY) ;
//seed initial value and hour cell
if ( bDidInitialArray == false )
{
if ( xPressureData.BMEpressure != 0 )
{
if ( xSemaphoreTake( sema_Time, xSemaphoreTicksToWait ) == pdTRUE ) // grab semaphore to get minute data
{
iPast = xTime.iMinutes; // store periodic
xSemaphoreGive( sema_Time ); // give up semaphore
xPressureData.mmHg_Hourly[iArrayCell] = xPressureData.BMEpressure;
iArrayCell++;
pxMessage = &xPressureData;
xQueueOverwrite( xQ_PressureDataMessage, (void *) &pxMessage );
bDidInitialArray = true;
}
}
} // if ( bDidInitialArray == fasle )
else
{
if ( xSemaphoreTake( sema_Time, xSemaphoreTicksToWait ) == pdTRUE ) // grab semaphore to get minute data
{
int iCurrentMinute = xTime.iMinutes;
xSemaphoreGive( sema_Time ); // give up semaphore
if ( iPast != iCurrentMinute )
{
if ( ((iCurrentMinute % 15) == 0) )
{
if ( xSemaphoreTake( sema_PressureDataCollection, xSemaphoreTicksToWait ) == pdTRUE )
{
iPast = iCurrentMinute; // keep current minute
////
xPressureData.mmHg_Hourly[iArrayCell] = xPressureData.BMEpressure; //add new periodic reading to array
////
iArrayCell++; // go to the next cell
pxMessage = &xPressureData;
xQueueOverwrite( xQ_PressureDataMessage, (void *) &pxMessage );
if ( iArrayCell == PressureDataArrayCount )
{
iArrayCell = 0; // start array count over
} // if ( iArrayCell == PressureDataArrayCount )
xSemaphoreGive( sema_PressureDataCollection );
} // if( xSemaphoreTake( s_Pressure, xTicksToWait20 ) == pdTRUE )
} // if ( ((GPS.minute % 15) == 0) )
else // if ( iPast != GPS.minute )
{
// if message present in queue do not send
if ( xSemaphoreTake( sema_PressureDataCollection, xTicksToWait0 ) == pdTRUE )
{
pxMessage = &xPressureData;
xQueueOverwrite( xQ_PressureDataMessage, (void *) &pxMessage ); // just send the collected data without adding any new data
xSemaphoreGive( sema_PressureDataCollection );
} // if( xSemaphoreTake( s_Pressure, ( TickType_t ) TicDe
} // if ( iPast != GPS.minute )
} // else if ( iPast != GPS.minute )
}
else // if ( iPast != GPS.minute )
{
// if message present in queue do not send
if ( xSemaphoreTake( sema_PressureDataCollection, xTicksToWait0 ) == pdTRUE )
{
pxMessage = &xPressureData;
xQueueOverwrite( xQ_PressureDataMessage, (void *) &pxMessage ); // just send the collected data without adding any new data
xSemaphoreGive( sema_PressureDataCollection );
} // if( xSemaphoreTake( s_Pressure, ( TickType_t ) TicDe
}
}
// Serial.print( "fDoPressureDataGather " );
// Serial.print(uxTaskGetStackHighWaterMark( NULL ));
// Serial.println();
// Serial.flush();
}
vTaskDelete( NULL );
} // void fDoPressureDataCollection
/* The display routine will trigger the UTN update when needed.
The display runs once per second
the display will only call on the UTM update as needed
the UTM runs on the other core from the display
*/
void fGetUTM( void * parameter )
{
struct stuUTM pxMessage;
float tmpUTM_North = 0.0; // holds northing info
float tmpUTM_West = 0.0; // holds easting info
int tmpZone = 0; // holds zone info
// struct stuPosit *pxPosit;
int TicDelayTime = 20;
for (;;)
{
EventBits_t xbit = xEventGroupWaitBits (eg, DO_UTM_TASK_BIT, pdTRUE, pdTRUE, portMAX_DELAY) ;
// int LatLonToUTMXY (FLOAT lat, FLOAT lon, int zone, FLOAT& x, FLOAT& y)
// if ( GPS.fix )
// {
if ( xSemaphoreTake( sema_UTM, xSemaphoreTicksToWait ) == pdTRUE )
{
tmpZone = LatLonToUTMXY(xUTM_Posit.Lat, xUTM_Posit.Lon, 0, tmpUTM_North, tmpUTM_West );
pxMessage.UTM_North = tmpUTM_North;
pxMessage.UTM_West = tmpUTM_West;
pxMessage.iZone = tmpZone;
xQueueOverwrite( xQ_UTM_Message, (void *) &pxMessage );
xSemaphoreGive( sema_UTM );
}
}
vTaskDelete( NULL );
}
////
void fUpdateDisplay( void * parameter )
{
struct stuPressureMessage *pxRxedPressureMessage; // a pointer to a structure
struct stuDistance Distance_Received; // a copy of the queue sent
struct stuPower *pxRxedPowerMessage; // a pointer to a structure
struct stuUTM UTM_MessageReceiced; // a copy of the queue sent
struct stuPressureData *pxPressureDataMessage; // a pointer to a structure
struct stuRunTimeData *RunTimeDataMessage; // a pointer to a structure
struct stuTime *pxTime;
struct stuDate *pxDate;
struct stuPosit *pxPosit;
int iSats = 0;
static int iCount = 0;
float Alti = 0.0;
float Hdg = 0.0;
float f = 0.0;
static char buf[20];
int iYposition = 61;
int iLeftPosition = 0;
int iTotalCount = 30;
// int startTime = 0; // used to measure task ticks
u8g2.setFont( u8g2_font_t0_11_mr );
for (;;)
{
/* wait forever until event bit of task 1 is set */
EventBits_t xbit = xEventGroupWaitBits (eg, UPDATE_DISPLAY_TASK_BIT, pdTRUE, pdTRUE, portMAX_DELAY) ;
// startTime = xTaskGetTickCount(); // used to measure task ticks
// Serial.println( "fUpdateDisplay");
// Serial.flush();
u8g2.clearBuffer(); // clear the internal memory
//
//
if ( (xQ_Time != NULL) && (uxQueueMessagesWaiting(xQ_Time)) ) // if queue not null and something is waiting in queue
{
if (xQueueReceive ( xQ_Time, &(pxTime), QueueReceiveDelayTime ) ) // receive queue info
{
if ( xSemaphoreTake( sema_Time, xSemaphoreTicksToWait ) == pdTRUE ) // grab semaphore and place data
{
sprintf(buf, "UTC: %02d:%02d:%02d", pxTime->iHours, pxTime->iMinutes, pxTime->iSeconds);
xSemaphoreGive( sema_Time ); // give up semaphore
u8g2.setCursor(1, 10); u8g2.print(buf); // place data in display buffer
} // if ( xSemaphoreTake( sema_Time, xSemaphoreTicksToWait ) == pdTRUE )
} // if (xQueueReceive ( xQ_RunTimeMessage, &(RunTimeDataMessage), QueueReceiveDelayTime ) )
} // if ( (xQ_Time not NULL) && (uxQueueMessagesWaiting(xQ_Time)) )
if ( uxQueueMessagesWaiting(xQ_Sats) )
{
xSemaphoreTake( sema_Sats, xSemaphoreTicksToWait );
xQueueReceive(xQ_Sats, &iSats, QueueReceiveDelayTime );
xSemaphoreGive( sema_Sats );
}
u8g2.drawStr(85, 10, "^");
u8g2.setCursor(95, 10); u8g2.print( iSats );
if ( (iCount >= 0) && (iCount <= 9) )
{
if ( (xQ_Date != NULL) && (uxQueueMessagesWaiting(xQ_Date)) ) // if queue not null and something is waiting in queue
{
if (xQueueReceive ( xQ_Date, &(pxDate), QueueReceiveDelayTime ) ) // receive queue info
{
if ( xSemaphoreTake( sema_Date, xSemaphoreTicksToWait ) == pdTRUE ) // grab semaphore and place data
{
sprintf( buf, "Date: %02d/%02d/%02d", pxDate->iMonth, pxDate->iDay, pxDate->iYear );
xSemaphoreGive( sema_Date ); // give up semaphore
u8g2.setCursor(0, 21); u8g2.print(buf);
} // if ( xSemaphoreTake( sema_Date, xSemaphoreTicksToWait ) == pdTRUE )
} // if (xQueueReceive ( xQ_Date, &(pxDate), QueueReceiveDelayTime ) )
} // if ( (xQ_Date != NULL) && (uxQueueMessagesWaiting(xQ_Date)) )
} // if ( (iCount >= 0) && (iCount <= 9) )
else
{
// is the queue not NULL && is there a queue message waiting? 0 = no messages
if ( (xQ_RunTimeMessage != NULL) && (uxQueueMessagesWaiting(xQ_RunTimeMessage) > 0) )
{
if (xQueueReceive ( xQ_RunTimeMessage, &(RunTimeDataMessage), QueueReceiveDelayTime ) )
{
if ( xSemaphoreTake( sema_RunTime, xSemaphoreTicksToWait ) == pdTRUE )
{
sprintf(buf, "Run: %03d:%02d:%02d", RunTimeDataMessage->iHours, RunTimeDataMessage->iMinutes, RunTimeDataMessage->iSeconds);
xSemaphoreGive( sema_RunTime );
u8g2.setCursor(0, 23); u8g2.print(buf);
}
}
}
} // else if ( (iCount >= 0) && (iCount <= 9) )
// // //
u8g2.drawHLine(3, 27, 100);
u8g2.drawVLine(3, 27, 22);
u8g2.drawHLine(3, 49, 100);
u8g2.drawVLine(103, 27, 23);
// // //
// // Display Lat and long
if ( (iCount >= 0) && (iCount <= 9) )
{
if ( (xQ_Posit != NULL) && (uxQueueMessagesWaiting(xQ_Posit)) ) // if queue not null and something is waiting in queue
{
if (xQueueReceive ( xQ_Posit, &(pxPosit), QueueReceiveDelayTime ) ) // receive queue info
{
if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE ) // grab semaphore and place data
{
sprintf( buf, "%0.6f", pxPosit->Lat );
u8g2.setCursor(20, 37 ); u8g2.print(buf);
sprintf( buf, "%0.6f", pxPosit->Lon );
u8g2.setCursor(20, 47); u8g2.print( buf );
xSemaphoreGive( sema_Posit ); // give up semaphore
} // if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
} // if (xQueueReceive ( xQ_Posit, &(pxPosit), QueueReceiveDelayTime ) )
} // (xQ_Posit != NULL) && (uxQueueMessagesWaiting(xQ_Posit))
// f = (GPS.latitude / 100);
// u8g2.setCursor(20, 37) ; u8g2.print(int(f));
// f = GPS.latitude - (int(f) * 100);
// u8g2.setCursor(40, 37); u8g2.print(f);
// u8g2.setCursor(83, 37); u8g2.print(GPS.lat);
// //
// f = (GPS.longitude / 100);
// u8g2.setCursor(20, 47); u8g2.print(int(f));
// f = GPS.longitude - (int(f) * 100);
// u8g2.setCursor(40, 47); u8g2.print(f);
// //u8g2.drawCircle(45, 47, 1,u8g2_DRAW_ALL);
// u8g2.setCursor(83, 47); u8g2.print(GPS.lon);
} // if ( (iCount >= 0) && (iCount <= 9) )
if ( (iCount >= 10) && (iCount <= 19) )
{
if ( (xQ_UTM_Message != NULL ) && (uxQueueMessagesWaiting(xQ_UTM_Message)) )
{
if (xQueueReceive ( xQ_UTM_Message, &(UTM_MessageReceiced), QueueReceiveDelayTime ) )
{
if ( xSemaphoreTake( sema_UTM, xSemaphoreTicksToWait ) == pdTRUE )
{
//display UTM northing, easting, and zone
u8g2.setCursor(12, 37); u8g2.print( UTM_MessageReceiced.UTM_North );
u8g2.setCursor(12, 47); u8g2.print( UTM_MessageReceiced.UTM_West);
u8g2.setCursor(75, 37); u8g2.print( UTM_MessageReceiced.iZone);
xSemaphoreGive( sema_UTM );
}
}
}
} // if ( (iCount >= 10) && (iCount <= 19) )
if ( (iCount >= 20) && (iCount <= iTotalCount) )
{
//// portMAX_DELAY will cause a wait forever for something to show up in the buffer. time of portMAX_DELAY is 47 days
//// wait for 10 clock tics for data to arrive in the queue buffer
if ( (xQ_PressureDataMessage) != 0 && (uxQueueMessagesWaiting(xQ_PressureDataMessage)) )
{
if (xQueueReceive ( xQ_PressureDataMessage, &(pxPressureDataMessage), QueueReceiveDelayTime ) )
{
if ( pxPressureDataMessage != 0) // check if structure is null
{
if ( xSemaphoreTake( sema_PressureDataCollection, xSemaphoreTicksToWait ) == pdTRUE )
{
int x = 30; // set x position
int yBase = 38;
// int y1 = yBase;
int x2 = 5;
int pressureBase = 0;
int iBaseLineReading = pxPressureDataMessage->mmHg_Hourly[0];
u8g2.drawHLine( x, yBase, x2);
for (int i = 1; i <= (PressureDataArrayCount - 1); i++)
{
if ( pxPressureDataMessage->mmHg_Hourly[i] != 0 )
{
x = x + 9; // position next x
u8g2.drawHLine( x, yBase - (iBaseLineReading - pxPressureDataMessage->mmHg_Hourly[i] ), x2);
}
sprintf( buf, "%d", iBaseLineReading ) ; // display basy line reading to the left
u8g2.setCursor( 9, 43 ); u8g2.print( buf );
} // for (int i = 1; i <= (PressureDataArrayCount - 1); i++)
xSemaphoreGive( sema_PressureDataCollection );
} // if ( xSemaphoreTake( s_Pressure, ( TickType_t ) 10 ) == pdTRUE )
}
else
{
Serial.println( "pxPressureDataMessage" );
}
} // if (xQueueReceive ( xQ_PressureDataMessage, &(pxPressureDataMessage), ( TickType_t ) QueueReceiveDelayTime ) )
} // if ( xQ_PressureDataMessage != 0 )
} // if ( (iCount >= 20) && (iCount <= 30) )
if ( (iCount >= 0) && (iCount <= 4) )
{
if ( uxQueueMessagesWaiting(xQ_Hdg) )
{
xSemaphoreTake( sema_Hdg, xSemaphoreTicksToWait );
xQueueReceive(xQ_Hdg, &Hdg, QueueReceiveDelayTime );
xSemaphoreGive( sema_Hdg );
}
u8g2.drawStr(iLeftPosition, iYposition, "Hdg: ");
u8g2.setCursor(28, iYposition); u8g2.print( Hdg );
} // if ( (iCount >= 0) && (iCount <= 4) )
if ( (iCount >= 5) && (iCount <= 9) )
{
if ( uxQueueMessagesWaiting(xQ_Alti) )
{
xSemaphoreTake( sema_Alti, xSemaphoreTicksToWait );
xQueueReceive(xQ_Alti, &Alti, QueueReceiveDelayTime );
xSemaphoreGive( sema_Alti );
}
u8g2.drawStr(iLeftPosition, iYposition, "Alti: ");
u8g2.setCursor(30, iYposition); u8g2.print( (Alti * 3.2808) );
} // if ( (iCount >= 5) && (iCount <= 9) )
if ( (iCount >= 10) && (iCount <= 14) )
{
if ( (xQ_DistanceMessage != 0) && (uxQueueMessagesWaiting(xQ_DistanceMessage)) ) // if queue not null and something is waiting in queue
{
if ( xSemaphoreTake( sema_CalDistance, xSemaphoreTicksToWait ) == pdTRUE ) // grab semaphore to stop the queue from being over wrote
{
if (xQueueReceive ( xQ_DistanceMessage, &(Distance_Received), QueueReceiveDelayTime ) ) // receive queue
{
sprintf( buf, "Dst:%03dKm %.2f" , Distance_Received.iDistance, Distance_Received.Speed );
u8g2.setCursor( iLeftPosition, iYposition ); u8g2.print( buf );
xSemaphoreGive( sema_CalDistance ); // release semaphore
} // if ( xSemaphoreTake( sema_CalDistance, xSemaphoreTicksToWait ) == pdTRUE )
} // if (xQueueReceive ( xQ_DistanceMessage , &(Distance_Received), ( TickType_t ) QueueReceiveDelayTime ) )
} // if ( xQ_DistanceMessage != 0 )
} // if ( (iCount >= 10) && (iCount <= 14) )
if ( (iCount >= 15) && (iCount <= 19) )
{
if ( xQ_PowerMessage != 0 )
{
if ( xQueueReceive( xQ_PowerMessage, &( pxRxedPowerMessage ), QueueReceiveDelayTime ) )
{
if ( pxRxedPowerMessage != 0) // check if structure is null
{
if ( xSemaphoreTake( sema_AnalogVoltRead, xSemaphoreTicksToWait ) == pdTRUE )
{
sprintf( buf, "V: %.2f %.2f% ", pxRxedPowerMessage->Vbatt, pxRxedPowerMessage->BattPercent );
u8g2.setCursor( iLeftPosition, iYposition ); u8g2.print( buf );
xSemaphoreGive( sema_AnalogVoltRead );
}
}
else
{
Serial.println( "pxRxedPowerMessage" );
}
}
} // if ( xQ_PowerMessage != 0 )
} // if ( (iCount >= 15) && (iCount <= 19) )
if ( (iCount >= 20) && (iCount <= 24) )
{
// Receive a message on the created queue.
if ( xQ_PressureMessage != 0 )
{
if ( xQueueReceive( xQ_PressureMessage, &( pxRxedPressureMessage ), QueueReceiveDelayTime ) )
{
if ( pxRxedPressureMessage != 0) // check if structure is null
{
if ( xSemaphoreTake( sema_CheckPressure, xSemaphoreTicksToWait ) == pdTRUE )
{
sprintf( buf, "%.2fC %.2fF", pxRxedPressureMessage->BMEtemperature, ((pxRxedPressureMessage->BMEtemperature * 1.8) + 32) );
u8g2.setCursor( iLeftPosition, iYposition ); u8g2.print( buf );
xSemaphoreGive( sema_CheckPressure );
}
}
else
{
Serial.println( "pxRxedPressureMessage 1" );
}
}
}
} // if ( (iCount >= 20) && (iCount <= 24) )
if ( (iCount >= 25) && (iCount <= 29) )
{
if ( xQ_PressureMessage != 0 )
{
if ( xQueueReceive( xQ_PressureMessage, &( pxRxedPressureMessage ), QueueReceiveDelayTime ) )
{
if ( pxRxedPressureMessage != 0) // check if structure is null
{
if ( xSemaphoreTake( sema_CheckPressure, xSemaphoreTicksToWait ) == pdTRUE )
{
sprintf( buf, "%.2fmmHg", pxRxedPressureMessage->BMEpressure );
u8g2.setCursor( iLeftPosition, iYposition ); u8g2.print( buf );
xSemaphoreGive( sema_CheckPressure );
}
}
else
{
Serial.println( "pxRxedPressureMessage 2" );
}
}
}
} // if ( (iCount >= 25) && (iCount <= 29) )
u8g2.sendBuffer(); // transfer internal memory to the display
if ( (iCount >= 24) && (iCount <= 28) )
{
xEventGroupSetBits( eg, PressureEvent ); // trigger pressure readings
} // if ( (iCount >= 24) && (iCount <= 28) )
if ( (iCount >= 19) && (iCount <= 23) )
{
xEventGroupSetBits( eg, PressureEvent ); // trigger pressure readings
} // if ( (iCount >= 19) && (iCount <= 23) )
if ( (iCount >= 8) && (iCount <= 18) )
{
// trigger UTM update, update utm just before its used as a display value
xEventGroupSetBits( eg, DO_UTM_TASK_BIT ); // trigger UTM calculations
} // if ( (iCount >= 9) && (iCount <= 18) )
if ( (iCount >= 14) && (iCount <= 18) )
{
xEventGroupSetBits( eg, AnalogVoltReadTask ); // trigger analog voltage read and calculation
} // if ( (iCount >= 19) && (iCount <= 23) )
iCount++;
if ( iCount >= iTotalCount )
{
iCount = 0;
}
// Serial.print( "fUpdateDisplay " );
// Serial.print( " " );
// Serial.print(uxTaskGetStackHighWaterMark( NULL ));
// Serial.println();
// Serial.flush();
}
vTaskDelete( NULL );
} // void fUpdateDisplay( void * parameter )
//////
void fGPS_Parse( void * parameter )
{
TickType_t xDoDistanceExpireTicks;
struct stuTime *pxTime;
struct stuDate *pxDate;
struct stuPosit *pxPosit;
struct stuDistance pxDistance;
float Alti;
float Hdg;
int Sats;
double LatNow;
double LonNow;
double LatPast;
double LonPast;
int DoDistanceTicks = 1000;
////
xDoDistanceExpireTicks = xTaskGetTickCount() + pdMS_TO_TICKS( 1000 ); // add 1000 mS worth of ticks to current tick count
for (;;)
{
xEventGroupWaitBits (eg, GPS_Parse, pdTRUE, pdTRUE, portMAX_DELAY) ;
if ( GPSSerial.available() > 1 )
{
if ( GPS.encode(GPSSerial.read()) )
{
if ( GPS.location.isValid())
{
//used for distance calculation
LatNow = GPS.location.lat();
LonNow = GPS.location.lng();
//
xPosit.Lat = LatNow;
xPosit.Lon = LonNow;
xSemaphoreTake( sema_UTM, xSemaphoreTicksToWait ); // place lat lon for utm calculation
xUTM_Posit.Lat = xPosit.Lat;
xUTM_Posit.Lon = xPosit.Lon;
xSemaphoreGive( sema_UTM );
if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
{
pxPosit = &xPosit;
xQueueSendToBack( xQ_Posit, (void *) &pxPosit, QueueReceiveDelayTime );
xSemaphoreGive( sema_Posit );
}
}
if (GPS.time.isValid())
{
xTime.iSeconds = GPS.time.second();
xTime.iMinutes = GPS.time.minute();
xTime.iHours = GPS.time.hour();
if ( xSemaphoreTake( sema_Time, xSemaphoreTicksToWait ) == pdTRUE )
{
pxTime = &xTime;
xQueueOverwrite( xQ_Time, (void *) &pxTime );
xSemaphoreGive( sema_Time );
}
}
if (GPS.date.isValid())
{
xDate.iMonth = GPS.date. month();
xDate.iDay = GPS.date.day();
xDate.iYear = GPS.date.year();
if ( xSemaphoreTake( sema_Date, xSemaphoreTicksToWait ) == pdTRUE )
{
pxDate = &xDate;
xQueueSendToBack( xQ_Date, (void *) &pxDate, QueueReceiveDelayTime );
xSemaphoreGive( sema_Date );
}
}
if ( GPS.satellites.isValid() )
{
if ( xSemaphoreTake( sema_Sats, xSemaphoreTicksToWait ) == pdTRUE )
{
Sats = GPS.satellites.value();
xQueueOverwrite( xQ_Sats, ( void * ) &Sats );
xSemaphoreGive( sema_Sats );
}
}
if ( GPS.altitude.isValid() )
{
if ( xSemaphoreTake( sema_Alti, xSemaphoreTicksToWait ) == pdTRUE )
{
Alti = GPS.altitude.meters();
xQueueOverwrite( xQ_Alti, ( void * ) &Alti );
xSemaphoreGive( sema_Alti );
}
}
if (GPS.location.isValid())
{
if ( LatPast = 0.0 )
{
LatPast = LatNow;
LonPast = LonNow;
}
if ( xTaskGetTickCount() >= xDoDistanceExpireTicks ) // do only once every 1000mS
{
if ( xSemaphoreTake( sema_CalDistance, xSemaphoreTicksToWait ) == pdTRUE )
{
// xDoDistancePastTicks
// has tick count expired
xDistanceMessage.iDistance = ( TinyGPSPlus::distanceBetween( LatNow, LonNow, LatPast, LonPast) / 100 ); // in Kilometers
if (GPS.speed.isUpdated())
{
xDistanceMessage.Speed = GPS.speed.mph();
}
xQueueSendToBack( xQ_DistanceMessage, (void *) &pxDistance, QueueReceiveDelayTime ); // sends a copy of the structure in the queue
xQueueSendToBack( xQ_DistanceMessage, (void *) &pxDistance, QueueReceiveDelayTime ); // sends a copy of the structure in the queue
xQueueSendToBack( xQ_DistanceMessage, (void *) &pxDistance, QueueReceiveDelayTime ); // sends a copy of the structure in the queue
xSemaphoreGive( sema_CalDistance );
LatPast = LatNow; // update past lat n lon with now lat lon for next calculation
LonPast = LonNow;
}
xDoDistanceExpireTicks = xTaskGetTickCount() + pdMS_TO_TICKS( DoDistanceTicks );
}
} // if (gps.location.isValid())
if ( GPS.course.isUpdated() )
{
if ( xSemaphoreTake( sema_Hdg, xSemaphoreTicksToWait ) == pdTRUE )
{
Hdg = GPS.course.deg();
xQueueOverwrite( xQ_Hdg, ( void * ) &Hdg );
xSemaphoreGive( sema_Hdg );
}
}
// Serial.print( "fGPS_Parse " );
// Serial.print( " " );
// Serial.print(uxTaskGetStackHighWaterMark( NULL ));
// Serial.println();
// Serial.flush();
} // if ( GPS.encode(GPSSerial.read()))
} // if ( GPSSerial.available() > 0 )
xSemaphoreGive( sema_GPS_Gate );
} // for (;;)
vTaskDelete( NULL );
} // void fGPS_Parse( void *pdata )
//////////////////////////////////////////////////
void fBlinkBuiltIn( void* pvParameters )
{
// toggle built in LED off/on
for (;;)
{
vTaskDelay( pdMS_TO_TICKS( 10 ) );
REG_WRITE( GPIO_OUT_W1TC_REG, BIT2 ); // GPIO2 LOW (clear)
vTaskDelay( pdMS_TO_TICKS( OneK ) );
REG_WRITE( GPIO_OUT_W1TS_REG, BIT2 ); //GPIO2 HIGH (set)
}
vTaskDelete( NULL );
}
////////////////////////////////////////////////
// example of a ticks to wait use
void TaskAnalogVoltRead( void *pvParameters )
{
struct stuPower *pxMessage;
float ADbits = 4095.0;
float uPvolts = 3.3;
float r1 = 27000.0; // R1 in ohm, 27k = 27000.0 //actual resistor size 27K
float r2 = 10000.0; // R2 in ohm, 10k = 10000.0 //actual 10K
// ADC1 channel 0 is GPIO36
// ADC1 channel 1 is GPIO37
// https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
for (;;)
{
// group handle, WaitForBits, ClearOnExitBit, WaitForAllBits, TicksToWait
xEventGroupWaitBits( eg, AnalogVoltReadTask, pdTRUE, pdTRUE, portMAX_DELAY );
// read the input
// ADC1 channel 1 input is ground, any ground noise readings get substracted from voltage reading
if ( xSemaphoreTake( sema_AnalogVoltRead, xSemaphoreTicksToWait ) == pdTRUE )
{
xPowerMessage.Vbatt = ( (( uPvolts * float(adc1_get_raw(ADC1_CHANNEL_0) - adc1_get_raw(ADC1_CHANNEL_1))) / ADbits) / r2 * ( r1 + r2) );
fBatteryPercentRemaingCharge();
pxMessage = &xPowerMessage;
xQueueOverwrite( xQ_PowerMessage, (void *) &pxMessage );
xSemaphoreGive( sema_AnalogVoltRead );
}
// Serial.print( "TaskAnalogVoltRead " );
// Serial.print(uxTaskGetStackHighWaterMark( NULL ));
// Serial.println();
// Serial.flush();
}
vTaskDelete( NULL );
}
/////////////////////////////////////////////////////////
void fBatteryPercentRemaingCharge()
{
byte V_BattNominal = 8;
byte V_MinRange = 6; //volts is lowest recomemded operating voltage
////
if ( xPowerMessage.Vbatt >= V_BattNominal)
{
xPowerMessage.BattPercent = 100.00;
}
else
{
xPowerMessage.BattPercent = ( ((xPowerMessage.Vbatt - V_MinRange) * 100.0) / (V_BattNominal - V_MinRange) );
}
}// end fBatteryPercentRemaingCharge()
///////////////////////////////////////////////////////////////////////////////////////////
void fCheck_Pressure( void * parameter )
{
struct stuPressureMessage *pxMessage;
while (1)
{
xEventGroupWaitBits (eg, PressureEvent, pdTRUE, pdTRUE, portMAX_DELAY) ;
if ( xSemaphoreTake( sema_CheckPressure, xSemaphoreTicksToWait ) == pdTRUE )
{
xPressureMessage.BMEtemperature = bme.readTemperature();
xPressureMessage.BMEpressure = bme.readPressure() / 133.3223684; // mmHg
if ( xSemaphoreTake(sema_PressureDataCollection, xSemaphoreTicksToWait ) == pdTRUE )
{
xPressureData.BMEpressure = int( xPressureMessage.BMEpressure ); // transfer pressure reading into xPressureData.BMEpressure
xSemaphoreGive( sema_PressureDataCollection );
}
//// BMEpressure = bme.readPressure() * 0.0002952998751 ; // inHg
pxMessage = &xPressureMessage;
xQueueOverwrite( xQ_PressureMessage, (void *) &pxMessage ); // sends a copy of the structure in the queue
xEventGroupSetBits( eg, DoPressureDataCollectionEvent ); // trigger fDoPressureDataCollection
xSemaphoreGive( sema_CheckPressure );
}
// Serial.print( "fCheck_Pressure " );
// Serial.print(uxTaskGetStackHighWaterMark( NULL ));
// Serial.println();
// Serial.flush();
}
vTaskDelete( NULL );
} // void fCheck_Pressure( void * parameter )
//}
Still a work in progress.