mirror of
https://github.com/ddv2005/AirsoftTracker.git
synced 2026-01-09 12:36:54 +00:00
Add new files for version 1.0.1332
This commit is contained in:
90
src/atScreenLCDGfx.cpp
Normal file
90
src/atScreenLCDGfx.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "config.h"
|
||||
#ifdef USE_SCREEN_LCD_GFX
|
||||
#include "atScreenLCDGfx.h"
|
||||
#include "global.h"
|
||||
|
||||
cATScreenLCDGfx::cATScreenLCDGfx(atGlobal &global):cATScreen(global)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
cATScreenLCDGfx::~cATScreenLCDGfx()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool cATScreenLCDGfx::init()
|
||||
{
|
||||
#ifdef VEXT_EN
|
||||
pinMode(VEXT_EN, OUTPUT);
|
||||
digitalWrite(VEXT_EN, HIGH);
|
||||
#endif
|
||||
m_display = new DisplaySH1106_128x64_I2C(-1);
|
||||
m_display->begin();
|
||||
m_display->fill(BLACK);
|
||||
m_display->setFixedFont(ssd1306xled_font6x8);
|
||||
m_display->setColor(WHITE);
|
||||
drawCentreString("Booting", m_display->width()/2, m_display->height()/2);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cATScreenLCDGfx::showShutdownScreen()
|
||||
{
|
||||
m_display->fill(BLACK);
|
||||
drawCentreString("Shutdown...", m_display->width()/2, m_display->height()/2);
|
||||
}
|
||||
|
||||
void cATScreenLCDGfx::loop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void cATScreenLCDGfx::drawCentreString(const char *buf, int x, int y) {
|
||||
// Get the display's width and the string's dimensions
|
||||
int16_t x1, y1;
|
||||
uint16_t text_width, text_height;
|
||||
NanoFont & font = m_display->getFont();
|
||||
lcduint_t textHeight = 0;
|
||||
lcduint_t textWidth = font.getTextSize(buf,&textHeight);
|
||||
|
||||
m_display->printFixed(x-textWidth/2,y-textHeight/2,buf,STYLE_NORMAL);
|
||||
}
|
||||
|
||||
void cATScreenLCDGfx::showBootScreen(const char * msg1,const char * msg2)
|
||||
{
|
||||
m_display->fill(BLACK);
|
||||
if(msg1)
|
||||
drawCentreString(msg1, m_display->width()/2, 10);
|
||||
}
|
||||
|
||||
void cATScreenLCDGfx::closeBootScreen()
|
||||
{
|
||||
m_display->fill(BLACK);
|
||||
}
|
||||
|
||||
void cATScreenLCDGfx::beginMainScreen()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void cATScreenLCDGfx::screenOn()
|
||||
{
|
||||
m_display->getInterface().displayOn();
|
||||
}
|
||||
|
||||
void cATScreenLCDGfx::screenOff()
|
||||
{
|
||||
m_display->getInterface().displayOff();
|
||||
}
|
||||
|
||||
void cATScreenLCDGfx::endMainScreen()
|
||||
{
|
||||
char buffer[32];
|
||||
//m_tft->fillScreen(TFT_BLACK);
|
||||
snprintf(buffer,sizeof(buffer),"BAT: %0.2fV, SAT: %02d ", m_global.getPowerStatus()->getBatteryVoltageMv()/1000.0,m_global.m_gpsStatus->getNumSatellites());
|
||||
m_display->printFixed(0, 10,buffer,STYLE_NORMAL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
27
src/atScreenLCDGfx.h
Normal file
27
src/atScreenLCDGfx.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "atscreen.h"
|
||||
#include "lcdgfx.h"
|
||||
#include "lcdgfx_gui.h"
|
||||
|
||||
|
||||
class cATScreenLCDGfx:public cATScreen
|
||||
{
|
||||
protected:
|
||||
#ifdef LCD_SH1106
|
||||
DisplaySH1106_128x64_I2C *m_display;
|
||||
#endif
|
||||
void drawCentreString(const char *buf, int x, int y);
|
||||
public:
|
||||
cATScreenLCDGfx(atGlobal &global);
|
||||
virtual ~cATScreenLCDGfx();
|
||||
|
||||
virtual bool init();
|
||||
virtual void loop();
|
||||
virtual void showBootScreen(const char * msg1,const char * msg2);
|
||||
virtual void closeBootScreen();
|
||||
virtual void beginMainScreen();
|
||||
virtual void endMainScreen();
|
||||
virtual void screenOn();
|
||||
virtual void screenOff();
|
||||
virtual void showShutdownScreen();
|
||||
};
|
||||
82
src/atScreenTFTeSPI.cpp
Normal file
82
src/atScreenTFTeSPI.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "config.h"
|
||||
#ifdef USE_SCREEN_TFT_ESPI
|
||||
#include "atScreenTFTeSPI.h"
|
||||
#include "global.h"
|
||||
|
||||
cATScreenTFTeSPI::cATScreenTFTeSPI(atGlobal &global):cATScreen(global)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
cATScreenTFTeSPI::~cATScreenTFTeSPI()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool cATScreenTFTeSPI::init()
|
||||
{
|
||||
#ifdef VEXT_EN
|
||||
pinMode(VEXT_EN, OUTPUT);
|
||||
digitalWrite(VEXT_EN, HIGH);
|
||||
#endif
|
||||
|
||||
digitalWrite(TFT_BL,HIGH);
|
||||
m_tft = new TFT_eSPI(TFT_WIDTH, TFT_HEIGHT);
|
||||
m_tft->init();
|
||||
m_tft->setRotation(1);
|
||||
m_tft->fillScreen(TFT_BLACK);
|
||||
m_tft->setTextSize(1);
|
||||
m_tft->setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
m_tft->drawCentreString("Booting", m_tft->width()/2, m_tft->height()/2, 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cATScreenTFTeSPI::loop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void cATScreenTFTeSPI::showBootScreen(const char * msg1,const char * msg2)
|
||||
{
|
||||
m_tft->setTextSize(1);
|
||||
m_tft->setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
m_tft->fillScreen(TFT_BLACK);
|
||||
if(msg1)
|
||||
m_tft->drawCentreString(msg1, m_tft->width()/2, 10, 2);
|
||||
}
|
||||
|
||||
void cATScreenTFTeSPI::closeBootScreen()
|
||||
{
|
||||
m_tft->fillScreen(TFT_BLACK);
|
||||
}
|
||||
|
||||
void cATScreenTFTeSPI::beginMainScreen()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void cATScreenTFTeSPI::screenOn()
|
||||
{
|
||||
digitalWrite(TFT_BL,HIGH);
|
||||
}
|
||||
|
||||
void cATScreenTFTeSPI::screenOff()
|
||||
{
|
||||
digitalWrite(TFT_BL,LOW);
|
||||
}
|
||||
|
||||
void cATScreenTFTeSPI::endMainScreen()
|
||||
{
|
||||
char buffer[32];
|
||||
m_tft->setTextSize(1);
|
||||
m_tft->setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
//m_tft->fillScreen(TFT_BLACK);
|
||||
snprintf(buffer,sizeof(buffer),"BAT: %0.2fV, SAT: %02d", m_global.getPowerStatus()->getBatteryVoltageMv()/1000.0,m_global.m_gpsStatus->getNumSatellites());
|
||||
m_tft->drawString(buffer, 0, 10, 2);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
21
src/atScreenTFTeSPI.h
Normal file
21
src/atScreenTFTeSPI.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "atscreen.h"
|
||||
#include "TFT_eSPI.h"
|
||||
|
||||
class cATScreenTFTeSPI:public cATScreen
|
||||
{
|
||||
protected:
|
||||
TFT_eSPI *m_tft;
|
||||
public:
|
||||
cATScreenTFTeSPI(atGlobal &global);
|
||||
virtual ~cATScreenTFTeSPI();
|
||||
|
||||
virtual bool init();
|
||||
virtual void loop();
|
||||
virtual void showBootScreen(const char * msg1,const char * msg2);
|
||||
virtual void closeBootScreen();
|
||||
virtual void beginMainScreen();
|
||||
virtual void endMainScreen();
|
||||
virtual void screenOn();
|
||||
virtual void screenOff();
|
||||
};
|
||||
40
src/concurrency/BinarySemaphoreFreeRTOS.cpp
Normal file
40
src/concurrency/BinarySemaphoreFreeRTOS.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "concurrency/BinarySemaphoreFreeRTOS.h"
|
||||
#include "../freertosinc.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
|
||||
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS() : semaphore(xSemaphoreCreateBinary())
|
||||
{
|
||||
assert(semaphore);
|
||||
}
|
||||
|
||||
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()
|
||||
{
|
||||
vSemaphoreDelete(semaphore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if we were interrupted
|
||||
*/
|
||||
bool BinarySemaphoreFreeRTOS::take(uint32_t msec)
|
||||
{
|
||||
return xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec));
|
||||
}
|
||||
|
||||
void BinarySemaphoreFreeRTOS::give()
|
||||
{
|
||||
xSemaphoreGive(semaphore);
|
||||
}
|
||||
|
||||
IRAM_ATTR void BinarySemaphoreFreeRTOS::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken)
|
||||
{
|
||||
xSemaphoreGiveFromISR(semaphore, pxHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
|
||||
#endif
|
||||
30
src/concurrency/BinarySemaphoreFreeRTOS.h
Normal file
30
src/concurrency/BinarySemaphoreFreeRTOS.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "../freertosinc.h"
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
|
||||
class BinarySemaphoreFreeRTOS
|
||||
{
|
||||
SemaphoreHandle_t semaphore;
|
||||
|
||||
public:
|
||||
BinarySemaphoreFreeRTOS();
|
||||
~BinarySemaphoreFreeRTOS();
|
||||
|
||||
/**
|
||||
* Returns false if we timed out
|
||||
*/
|
||||
bool take(uint32_t msec);
|
||||
|
||||
void give();
|
||||
|
||||
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace concurrency
|
||||
28
src/concurrency/BinarySemaphorePosix.cpp
Normal file
28
src/concurrency/BinarySemaphorePosix.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "concurrency/BinarySemaphorePosix.h"
|
||||
#include "../freertosinc.h"
|
||||
|
||||
#ifndef HAS_FREE_RTOS
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
|
||||
BinarySemaphorePosix::BinarySemaphorePosix() {}
|
||||
|
||||
BinarySemaphorePosix::~BinarySemaphorePosix() {}
|
||||
|
||||
/**
|
||||
* Returns false if we timed out
|
||||
*/
|
||||
bool BinarySemaphorePosix::take(uint32_t msec)
|
||||
{
|
||||
delay(msec); // FIXME
|
||||
return false;
|
||||
}
|
||||
|
||||
void BinarySemaphorePosix::give() {}
|
||||
|
||||
IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) {}
|
||||
|
||||
} // namespace concurrency
|
||||
|
||||
#endif
|
||||
30
src/concurrency/BinarySemaphorePosix.h
Normal file
30
src/concurrency/BinarySemaphorePosix.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "../freertosinc.h"
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
|
||||
#ifndef HAS_FREE_RTOS
|
||||
|
||||
class BinarySemaphorePosix
|
||||
{
|
||||
// SemaphoreHandle_t semaphore;
|
||||
|
||||
public:
|
||||
BinarySemaphorePosix();
|
||||
~BinarySemaphorePosix();
|
||||
|
||||
/**
|
||||
* Returns false if we timed out
|
||||
*/
|
||||
bool take(uint32_t msec);
|
||||
|
||||
void give();
|
||||
|
||||
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace concurrency
|
||||
35
src/concurrency/InterruptableDelay.cpp
Normal file
35
src/concurrency/InterruptableDelay.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "concurrency/InterruptableDelay.h"
|
||||
#include "../freertosinc.h"
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
|
||||
InterruptableDelay::InterruptableDelay() {}
|
||||
|
||||
InterruptableDelay::~InterruptableDelay() {}
|
||||
|
||||
/**
|
||||
* Returns false if we were interrupted
|
||||
*/
|
||||
bool InterruptableDelay::delay(uint32_t msec)
|
||||
{
|
||||
// LOG_DEBUG("delay %u ", msec);
|
||||
|
||||
// sem take will return false if we timed out (i.e. were not interrupted)
|
||||
bool r = semaphore.take(msec);
|
||||
|
||||
// LOG_DEBUG("interrupt=%d\n", r);
|
||||
return !r;
|
||||
}
|
||||
|
||||
void InterruptableDelay::interrupt()
|
||||
{
|
||||
semaphore.give();
|
||||
}
|
||||
|
||||
IRAM_ATTR void InterruptableDelay::interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken)
|
||||
{
|
||||
semaphore.giveFromISR(pxHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
41
src/concurrency/InterruptableDelay.h
Normal file
41
src/concurrency/InterruptableDelay.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "../freertosinc.h"
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
#include "concurrency/BinarySemaphoreFreeRTOS.h"
|
||||
#define BinarySemaphore BinarySemaphoreFreeRTOS
|
||||
#else
|
||||
#include "concurrency/BinarySemaphorePosix.h"
|
||||
#define BinarySemaphore BinarySemaphorePosix
|
||||
#endif
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
|
||||
/**
|
||||
* An object that provides delay(msec) like functionality, but can be interrupted by calling interrupt().
|
||||
*
|
||||
* Useful for they top level loop() delay call to keep the CPU powered down until our next scheduled event or some external event.
|
||||
*
|
||||
* This is implemented for FreeRTOS but should be easy to port to other operating systems.
|
||||
*/
|
||||
class InterruptableDelay
|
||||
{
|
||||
BinarySemaphore semaphore;
|
||||
|
||||
public:
|
||||
InterruptableDelay();
|
||||
~InterruptableDelay();
|
||||
|
||||
/**
|
||||
* Returns false if we were interrupted
|
||||
*/
|
||||
bool delay(uint32_t msec);
|
||||
|
||||
void interrupt();
|
||||
|
||||
void interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken);
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
143
src/concurrency/OSThread.cpp
Normal file
143
src/concurrency/OSThread.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#include "OSThread.h"
|
||||
#include "../config.h"
|
||||
#include "../freertosinc.h"
|
||||
#include "memGet.h"
|
||||
#include <assert.h>
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
|
||||
/// Show debugging info for disabled threads
|
||||
bool OSThread::showDisabled;
|
||||
|
||||
/// Show debugging info for threads when we run them
|
||||
bool OSThread::showRun = false;
|
||||
|
||||
/// Show debugging info for threads we decide not to run;
|
||||
bool OSThread::showWaiting = false;
|
||||
|
||||
const OSThread *OSThread::currentThread;
|
||||
|
||||
ThreadController mainController, timerController;
|
||||
InterruptableDelay mainDelay;
|
||||
|
||||
void OSThread::setup()
|
||||
{
|
||||
mainController.ThreadName = "mainController";
|
||||
timerController.ThreadName = "timerController";
|
||||
}
|
||||
|
||||
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller)
|
||||
: Thread(NULL, period), controller(_controller)
|
||||
{
|
||||
assertIsSetup();
|
||||
|
||||
ThreadName = _name;
|
||||
|
||||
if (controller) {
|
||||
bool added = controller->add(this);
|
||||
assert(added);
|
||||
}
|
||||
}
|
||||
|
||||
OSThread::~OSThread()
|
||||
{
|
||||
if (controller)
|
||||
controller->remove(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
|
||||
*/
|
||||
void OSThread::setIntervalFromNow(unsigned long _interval)
|
||||
{
|
||||
// Save interval
|
||||
interval = _interval;
|
||||
|
||||
// Cache the next run based on the last_run
|
||||
_cached_next_run = millis() + interval;
|
||||
}
|
||||
|
||||
bool OSThread::shouldRun(unsigned long time)
|
||||
{
|
||||
bool r = Thread::shouldRun(time);
|
||||
|
||||
if (showRun && r) {
|
||||
DEBUG_MSG("Thread %s: run\n", ThreadName.c_str());
|
||||
}
|
||||
|
||||
if (showWaiting && enabled && !r) {
|
||||
DEBUG_MSG("Thread %s: wait %lu\n", ThreadName.c_str(), interval);
|
||||
}
|
||||
|
||||
if (showDisabled && !enabled) {
|
||||
DEBUG_MSG("Thread %s: disabled\n", ThreadName.c_str());
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void OSThread::run()
|
||||
{
|
||||
#ifdef DEBUG_HEAP
|
||||
auto heap = memGet.getFreeHeap();
|
||||
#endif
|
||||
currentThread = this;
|
||||
auto newDelay = runOnce();
|
||||
#ifdef DEBUG_HEAP
|
||||
auto newHeap = memGet.getFreeHeap();
|
||||
if (newHeap < heap)
|
||||
DEBUG_MSG("------ Thread %s leaked heap %d -> %d (%d) ------\n", ThreadName.c_str(), heap, newHeap, newHeap - heap);
|
||||
if (heap < newHeap)
|
||||
DEBUG_MSG("++++++ Thread %s freed heap %d -> %d (%d) ++++++\n", ThreadName.c_str(), heap, newHeap, newHeap - heap);
|
||||
#endif
|
||||
|
||||
runned();
|
||||
|
||||
if (newDelay >= 0)
|
||||
setInterval(newDelay);
|
||||
|
||||
currentThread = NULL;
|
||||
}
|
||||
|
||||
int32_t OSThread::disable()
|
||||
{
|
||||
enabled = false;
|
||||
setInterval(INT32_MAX);
|
||||
|
||||
return INT32_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
|
||||
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
|
||||
*
|
||||
* it is super important to never allocate those object statically. instead, you should explicitly
|
||||
* new them at a point where you are guaranteed that other objects that this instance
|
||||
* depends on have already been created.
|
||||
*
|
||||
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
|
||||
* this makes it guaranteed that the global mainController is fully constructed first.
|
||||
*/
|
||||
bool hasBeenSetup;
|
||||
|
||||
void assertIsSetup()
|
||||
{
|
||||
|
||||
/**
|
||||
* Dear developer comrade - If this assert fails() that means you need to fix the following:
|
||||
*
|
||||
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
|
||||
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
|
||||
*
|
||||
* it is super important to never allocate those object statically. instead, you should explicitly
|
||||
* new them at a point where you are guaranteed that other objects that this instance
|
||||
* depends on have already been created.
|
||||
*
|
||||
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
|
||||
* this makes it guaranteed that the global mainController is fully constructed first.
|
||||
*/
|
||||
assert(hasBeenSetup);
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
90
src/concurrency/OSThread.h
Normal file
90
src/concurrency/OSThread.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "ThreadController.h"
|
||||
#include "concurrency/InterruptableDelay.h"
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
|
||||
extern ThreadController mainController, timerController;
|
||||
extern InterruptableDelay mainDelay;
|
||||
|
||||
#define RUN_SAME -1
|
||||
|
||||
/**
|
||||
* @brief Base threading
|
||||
*
|
||||
* This is a pseudo threading layer that is super easy to port, well suited to our slow network and very ram & power efficient.
|
||||
*
|
||||
* TODO FIXME @geeksville
|
||||
*
|
||||
* move more things into OSThreads
|
||||
* remove lock/lockguard
|
||||
*
|
||||
* move typedQueue into concurrency
|
||||
* remove freertos from typedqueue
|
||||
*/
|
||||
class OSThread : public Thread
|
||||
{
|
||||
ThreadController *controller;
|
||||
|
||||
/// Show debugging info for disabled threads
|
||||
static bool showDisabled;
|
||||
|
||||
/// Show debugging info for threads when we run them
|
||||
static bool showRun;
|
||||
|
||||
/// Show debugging info for threads we decide not to run;
|
||||
static bool showWaiting;
|
||||
|
||||
public:
|
||||
/// For debug printing only (might be null)
|
||||
static const OSThread *currentThread;
|
||||
|
||||
OSThread(const char *name, uint32_t period = 0, ThreadController *controller = &mainController);
|
||||
|
||||
virtual ~OSThread();
|
||||
|
||||
virtual bool shouldRun(unsigned long time);
|
||||
|
||||
static void setup();
|
||||
|
||||
int32_t disable();
|
||||
|
||||
/**
|
||||
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
|
||||
*/
|
||||
void setIntervalFromNow(unsigned long _interval);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The method that will be called each time our thread gets a chance to run
|
||||
*
|
||||
* Returns desired period for next invocation (or RUN_SAME for no change)
|
||||
*/
|
||||
virtual int32_t runOnce() = 0;
|
||||
|
||||
// Do not override this
|
||||
virtual void run();
|
||||
};
|
||||
|
||||
/**
|
||||
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
|
||||
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
|
||||
*
|
||||
* it is super important to never allocate those object statically. instead, you should explicitly
|
||||
* new them at a point where you are guaranteed that other objects that this instance
|
||||
* depends on have already been created.
|
||||
*
|
||||
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
|
||||
* this makes it guaranteed that the global mainController is fully constructed first.
|
||||
*/
|
||||
extern bool hasBeenSetup;
|
||||
|
||||
void assertIsSetup();
|
||||
|
||||
} // namespace concurrency
|
||||
73
src/concurrency/memGet.cpp
Normal file
73
src/concurrency/memGet.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @file memGet.cpp
|
||||
* @brief Implementation of MemGet class that provides functions to get memory information.
|
||||
*
|
||||
* This file contains the implementation of MemGet class that provides functions to get
|
||||
* information about free heap, heap size, free psram and psram size. The functions are
|
||||
* implemented for ESP32 and NRF52 architectures. If the platform does not have heap
|
||||
* management function implemented, the functions return UINT32_MAX or 0.
|
||||
*/
|
||||
#include "memGet.h"
|
||||
#include "../freertosinc.h"
|
||||
|
||||
MemGet memGet;
|
||||
|
||||
/**
|
||||
* Returns the amount of free heap memory in bytes.
|
||||
* @return uint32_t The amount of free heap memory in bytes.
|
||||
*/
|
||||
uint32_t MemGet::getFreeHeap()
|
||||
{
|
||||
#ifdef ARCH_ESP32
|
||||
return ESP.getFreeHeap();
|
||||
#elif defined(ARCH_NRF52)
|
||||
return dbgHeapFree();
|
||||
#else
|
||||
// this platform does not have heap management function implemented
|
||||
return UINT32_MAX;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the heap memory in bytes.
|
||||
* @return uint32_t The size of the heap memory in bytes.
|
||||
*/
|
||||
uint32_t MemGet::getHeapSize()
|
||||
{
|
||||
#ifdef ARCH_ESP32
|
||||
return ESP.getHeapSize();
|
||||
#elif defined(ARCH_NRF52)
|
||||
return dbgHeapTotal();
|
||||
#else
|
||||
// this platform does not have heap management function implemented
|
||||
return UINT32_MAX;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of free psram memory in bytes.
|
||||
*
|
||||
* @return The amount of free psram memory in bytes.
|
||||
*/
|
||||
uint32_t MemGet::getFreePsram()
|
||||
{
|
||||
#ifdef ARCH_ESP32
|
||||
return ESP.getFreePsram();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the size of the PSRAM memory.
|
||||
*
|
||||
* @return uint32_t The size of the PSRAM memory.
|
||||
*/
|
||||
uint32_t MemGet::getPsramSize()
|
||||
{
|
||||
#ifdef ARCH_ESP32
|
||||
return ESP.getPsramSize();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
18
src/concurrency/memGet.h
Normal file
18
src/concurrency/memGet.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#ifndef _MT_MEMGET_H
|
||||
#define _MT_MEMGET_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class MemGet
|
||||
{
|
||||
public:
|
||||
uint32_t getFreeHeap();
|
||||
uint32_t getHeapSize();
|
||||
uint32_t getFreePsram();
|
||||
uint32_t getPsramSize();
|
||||
};
|
||||
|
||||
extern MemGet memGet;
|
||||
|
||||
#endif
|
||||
241
src/gps/GenericGPS.cpp
Normal file
241
src/gps/GenericGPS.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "GenericGPS.h"
|
||||
#include "global.h"
|
||||
|
||||
#define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway
|
||||
#define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc)
|
||||
|
||||
static int32_t toDegInt(RawDegrees d)
|
||||
{
|
||||
int32_t degMult = 10000000; // 1e7
|
||||
int32_t r = d.deg * degMult + d.billionths / 100;
|
||||
if (d.negative)
|
||||
r *= -1;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
bool GenericGPS::setup()
|
||||
{
|
||||
#ifdef VEXT_EN
|
||||
pinMode(VEXT_EN, OUTPUT);
|
||||
digitalWrite(VEXT_EN, HIGH);
|
||||
#endif
|
||||
|
||||
#ifdef VGNSS_CTRL
|
||||
pinMode(VGNSS_CTRL, OUTPUT);
|
||||
digitalWrite(VGNSS_CTRL, LOW);
|
||||
#endif
|
||||
delay(10);
|
||||
|
||||
#ifdef PIN_GPS_RESET
|
||||
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
|
||||
pinMode(PIN_GPS_RESET, OUTPUT);
|
||||
delay(10);
|
||||
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
|
||||
#endif
|
||||
|
||||
if(serialInitialized)
|
||||
{
|
||||
_serial_gps.end();
|
||||
delay(10);
|
||||
serialInitialized = false;
|
||||
}
|
||||
if(!serialInitialized)
|
||||
{
|
||||
_serial_gps.setRxBufferSize(2048);
|
||||
#ifdef GPS_RX_PIN
|
||||
_serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
|
||||
#else
|
||||
_serial_gps.begin(GPS_BAUDRATE);
|
||||
#endif
|
||||
serialInitialized = true;
|
||||
delay(30);
|
||||
}
|
||||
gnssModel = probe();
|
||||
|
||||
if (gnssModel == GNSS_MODEL_MTK) {
|
||||
_serial_gps.write("$PCAS04,7*1E\r\n");
|
||||
delay(250);
|
||||
// only ask for RMC and GGA
|
||||
_serial_gps.write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n");
|
||||
delay(250);
|
||||
// Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
|
||||
_serial_gps.write("$PCAS11,3*1E\r\n");
|
||||
delay(250);
|
||||
} else if (gnssModel == GNSS_MODEL_UC6580) {
|
||||
_serial_gps.write("$CFGSYS,h35155\r\n");
|
||||
//_serial_gps.write("$CFGSYS,h15\r\n");
|
||||
delay(250);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
GnssModel_t GenericGPS::probe()
|
||||
{
|
||||
#if defined(GPS_L76K)
|
||||
return GNSS_MODEL_MTK;
|
||||
#elif defined(GPS_UC6580)
|
||||
return GNSS_MODEL_UC6580;
|
||||
#else
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GenericGPS::whileIdle()
|
||||
{
|
||||
bool isValid = false;
|
||||
while (_serial_gps.available() > 0) {
|
||||
int c = _serial_gps.read();
|
||||
// DEBUG_MSG("%c",c);
|
||||
isValid |= reader.encode(c);
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
bool GenericGPS::lookForTime()
|
||||
{
|
||||
auto ti = reader.time;
|
||||
auto d = reader.date;
|
||||
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
|
||||
/* Convert to unix time
|
||||
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
|
||||
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
||||
*/
|
||||
struct tm t;
|
||||
t.tm_sec = ti.second();
|
||||
t.tm_min = ti.minute();
|
||||
t.tm_hour = ti.hour();
|
||||
t.tm_mday = d.day();
|
||||
t.tm_mon = d.month() - 1;
|
||||
t.tm_year = d.year() - 1900;
|
||||
t.tm_isdst = false;
|
||||
if (t.tm_mon > -1) {
|
||||
perhapsSetRTC(t,ti.centisecond()*10);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GenericGPS::hasLock() const
|
||||
{
|
||||
if (fixQual >= 1 && fixQual <= 5) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GenericGPS::lookForLocation()
|
||||
{
|
||||
// By default, TinyGPS++ does not parse GPGSA lines, which give us
|
||||
// the 2D/3D fixType (see NMEAGPS.h)
|
||||
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
|
||||
fixQual = reader.fixQuality();
|
||||
|
||||
// check if GPS has an acceptable lock
|
||||
if (!hasLock())
|
||||
return false;
|
||||
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("AGE: LOC=%d FIX=%d DATE=%d TIME=%d\n", reader.location.age(),
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
gsafixtype.age(),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
reader.date.age(), reader.time.age());
|
||||
#endif // GPS_EXTRAVERBOSE
|
||||
|
||||
// check if a complete GPS solution set is available for reading
|
||||
// tinyGPSDatum::age() also includes isValid() test
|
||||
// FIXME
|
||||
if (!((reader.location.age() < GPS_SOL_EXPIRY_MS) &&
|
||||
(reader.time.age() < GPS_SOL_EXPIRY_MS) && (reader.date.age() < GPS_SOL_EXPIRY_MS))) {
|
||||
DEBUG_MSG("SOME data is TOO OLD: LOC %u, TIME %u, DATE %u\n", reader.location.age(), reader.time.age(), reader.date.age());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is this a new point or are we re-reading the previous one?
|
||||
if (!reader.location.isUpdated())
|
||||
return false;
|
||||
|
||||
// We know the solution is fresh and valid, so just read the data
|
||||
auto loc = reader.location.value();
|
||||
|
||||
// Bail out EARLY to avoid overwriting previous good data (like #857)
|
||||
if (toDegInt(loc.lat) > 900000000) {
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("Bail out EARLY on LAT %i\n", toDegInt(loc.lat));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
if (toDegInt(loc.lng) > 1800000000) {
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("Bail out EARLY on LNG %i\n", toDegInt(loc.lng));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
dop = reader.hdop.value();
|
||||
|
||||
latitude = toDegInt(loc.lat);
|
||||
longitude = toDegInt(loc.lng);
|
||||
|
||||
altitude = reader.altitude.meters();
|
||||
|
||||
// Nice to have, if available
|
||||
if (reader.satellites.isUpdated()) {
|
||||
numSatellites = reader.satellites.value();
|
||||
}
|
||||
|
||||
if (reader.course.isUpdated() && reader.course.isValid()) {
|
||||
if (reader.course.value() < 36000) { // sanity check
|
||||
heading =
|
||||
reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
|
||||
} else {
|
||||
DEBUG_MSG("BOGUS course.value() REJECTED: %d\n", reader.course.value());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GenericGPS::shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
int32_t GenericGPS::runOnce()
|
||||
{
|
||||
if (whileIdle()) {
|
||||
// if we have received valid NMEA claim we are connected
|
||||
isConnected = true;
|
||||
};
|
||||
|
||||
// If we are overdue for an update, turn on the GPS and at least publish the current status
|
||||
uint32_t now = millis();
|
||||
|
||||
if ((now - lastWhileActiveMsec) > 5000) {
|
||||
lastWhileActiveMsec = now;
|
||||
whileActive();
|
||||
}
|
||||
|
||||
bool gotTime = false;
|
||||
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||
gotTime = true;
|
||||
}
|
||||
|
||||
bool gotLoc = lookForLocation();
|
||||
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
||||
hasValidLocation = true;
|
||||
}
|
||||
|
||||
DEBUG_MSG("GPS numSatellites %d, %d\n", numSatellites, fixQual);
|
||||
const GPSStatus status = GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, hacc, heading, numSatellites);
|
||||
newStatus.notifyObservers(&status);
|
||||
|
||||
return 1000;
|
||||
}
|
||||
|
||||
46
src/gps/GenericGPS.h
Normal file
46
src/gps/GenericGPS.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "GPS.h"
|
||||
#include "Observer.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "TinyGPS++.h"
|
||||
|
||||
typedef enum {
|
||||
GNSS_MODEL_MTK,
|
||||
GNSS_MODEL_UC6580,
|
||||
GNSS_MODEL_UNKNOWN,
|
||||
} GnssModel_t;
|
||||
|
||||
|
||||
class GenericGPS : public GPS, private concurrency::OSThread
|
||||
{
|
||||
uint32_t lastWhileActiveMsec = 0;
|
||||
TinyGPSPlus reader;
|
||||
uint8_t fixQual = 0; // fix quality from GPGGA
|
||||
uint32_t lastChecksumFailCount = 0;
|
||||
|
||||
GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN;
|
||||
bool serialInitialized = false;
|
||||
public:
|
||||
GenericGPS():concurrency::OSThread("GPS") {};
|
||||
virtual ~GenericGPS(){};
|
||||
|
||||
GnssModel_t probe();
|
||||
|
||||
virtual bool setup();
|
||||
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
/**
|
||||
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
|
||||
* called after the CPU wakes from light-sleep state */
|
||||
virtual void startLock(){};
|
||||
virtual void reset(){};
|
||||
virtual void loop(){};
|
||||
virtual void shutdown();
|
||||
virtual bool whileIdle();
|
||||
virtual void whileActive() {}
|
||||
virtual bool lookForTime();
|
||||
virtual bool lookForLocation();
|
||||
virtual bool hasLock() const;
|
||||
};
|
||||
Reference in New Issue
Block a user