add a .clang-format file (#9154)

This commit is contained in:
Jorropo
2026-01-03 14:19:24 -06:00
committed by GitHub
parent abab6ce815
commit 0d11331d18
771 changed files with 77752 additions and 83184 deletions
+2
View File
@@ -0,0 +1,2 @@
BasedOnStyle: LLVM
ColumnLimit: 150
+10 -17
View File
@@ -16,8 +16,7 @@
#include "mesh/TypeConversions.h" #include "mesh/TypeConversions.h"
#include "mesh/mesh-pb-constants.h" #include "mesh/mesh-pb-constants.h"
namespace namespace {
{
constexpr uint32_t nodeId = 0x12345678; constexpr uint32_t nodeId = 0x12345678;
// Set to true when lateInitVariant finishes. Used to ensure lateInitVariant was called during startup. // Set to true when lateInitVariant finishes. Used to ensure lateInitVariant was called during startup.
bool hasBeenConfigured = false; bool hasBeenConfigured = false;
@@ -35,17 +34,15 @@ std::condition_variable loopCV;
std::thread meshtasticThread; std::thread meshtasticThread;
// This exception is thrown when the portuino main thread should exit. // This exception is thrown when the portuino main thread should exit.
class ShouldExitException : public std::runtime_error class ShouldExitException : public std::runtime_error {
{ public:
public:
using std::runtime_error::runtime_error; using std::runtime_error::runtime_error;
}; };
// Start the loop for one test case and wait till the loop has completed. This ensures fuzz // Start the loop for one test case and wait till the loop has completed. This ensures fuzz
// test cases do not overlap with one another. This helps the fuzzer attribute a crash to the // test cases do not overlap with one another. This helps the fuzzer attribute a crash to the
// single, currently running, test case. // single, currently running, test case.
void runLoopOnce() void runLoopOnce() {
{
realHardware = true; // Avoids delay(100) within portduino/main.cpp realHardware = true; // Avoids delay(100) within portduino/main.cpp
std::unique_lock<std::mutex> lck(loopLock); std::unique_lock<std::mutex> lck(loopLock);
fuzzerRunning = true; fuzzerRunning = true;
@@ -58,8 +55,7 @@ void runLoopOnce()
// Called in the main Arduino loop function to determine if the loop can delay/sleep before running again. // Called in the main Arduino loop function to determine if the loop can delay/sleep before running again.
// We use this as a way to block the loop from sleeping and to start the loop function immediately when a // We use this as a way to block the loop from sleeping and to start the loop function immediately when a
// fuzzer input is ready. // fuzzer input is ready.
bool loopCanSleep() bool loopCanSleep() {
{
std::unique_lock<std::mutex> lck(loopLock); std::unique_lock<std::mutex> lck(loopLock);
loopIsWaiting = true; loopIsWaiting = true;
loopCV.notify_one(); loopCV.notify_one();
@@ -74,8 +70,7 @@ bool loopCanSleep()
} }
// Called just prior to starting Meshtastic. Allows for setting config values before startup. // Called just prior to starting Meshtastic. Allows for setting config values before startup.
void lateInitVariant() void lateInitVariant() {
{
portduino_config.logoutputlevel = level_error; portduino_config.logoutputlevel = level_error;
channelFile.channels[0] = meshtastic_Channel{ channelFile.channels[0] = meshtastic_Channel{
.has_settings = true, .has_settings = true,
@@ -130,8 +125,7 @@ extern "C" {
int portduino_main(int argc, char **argv); // Renamed "main" function from Meshtastic binary. int portduino_main(int argc, char **argv); // Renamed "main" function from Meshtastic binary.
// Start Meshtastic in a thread and wait till it has reached the ON state. // Start Meshtastic in a thread and wait till it has reached the ON state.
int LLVMFuzzerInitialize(int *argc, char ***argv) int LLVMFuzzerInitialize(int *argc, char ***argv) {
{
portduino_config.maxtophone = 5; portduino_config.maxtophone = 5;
meshtasticThread = std::thread([program = *argv[0]]() { meshtasticThread = std::thread([program = *argv[0]]() {
@@ -173,13 +167,12 @@ int LLVMFuzzerInitialize(int *argc, char ***argv)
// //
// This guide provides best practices for writing a fuzzer target. // This guide provides best practices for writing a fuzzer target.
// https://github.com/google/fuzzing/blob/master/docs/good-fuzz-target.md // https://github.com/google/fuzzing/blob/master/docs/good-fuzz-target.md
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t length) int LLVMFuzzerTestOneInput(const uint8_t *data, size_t length) {
{
meshtastic_MeshPacket p = meshtastic_MeshPacket_init_default; meshtastic_MeshPacket p = meshtastic_MeshPacket_init_default;
pb_istream_t stream = pb_istream_from_buffer(data, length); pb_istream_t stream = pb_istream_from_buffer(data, length);
// Ignore any inputs that fail to decode or have fields set that are not transmitted over LoRa. // Ignore any inputs that fail to decode or have fields set that are not transmitted over LoRa.
if (!pb_decode(&stream, &meshtastic_MeshPacket_msg, &p) || p.rx_time || p.rx_snr || p.priority || p.rx_rssi || p.delayed || if (!pb_decode(&stream, &meshtastic_MeshPacket_msg, &p) || p.rx_time || p.rx_snr || p.priority || p.rx_rssi || p.delayed || p.public_key.size ||
p.public_key.size || p.next_hop || p.relay_node || p.tx_after) p.next_hop || p.relay_node || p.tx_after)
return -1; // Reject: The input will not be added to the corpus. return -1; // Reject: The input will not be added to the corpus.
if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
meshtastic_Data d; meshtastic_Data d;
+13 -22
View File
@@ -21,13 +21,10 @@ Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_DATA, NEOPIXEL_TYPE);
extern unPhone unphone; extern unPhone unphone;
#endif #endif
namespace concurrency namespace concurrency {
{ class AmbientLightingThread : public concurrency::OSThread {
class AmbientLightingThread : public concurrency::OSThread public:
{ explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLighting") {
public:
explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLighting")
{
notifyDeepSleepObserver.observe(&notifyDeepSleep); // Let us know when shutdown() is issued. notifyDeepSleepObserver.observe(&notifyDeepSleep); // Let us know when shutdown() is issued.
// Enables Ambient Lighting by default if conditions are meet. // Enables Ambient Lighting by default if conditions are meet.
@@ -85,8 +82,7 @@ class AmbientLightingThread : public concurrency::OSThread
} }
protected: protected:
int32_t runOnce() override int32_t runOnce() override {
{
#ifdef HAS_RGB_LED #ifdef HAS_RGB_LED
#if defined(HAS_NCP5623) || defined(HAS_LP5562) #if defined(HAS_NCP5623) || defined(HAS_LP5562)
if ((_type == ScanI2C::NCP5623 || _type == ScanI2C::LP5562) && moduleConfig.ambient_lighting.led_state) { if ((_type == ScanI2C::NCP5623 || _type == ScanI2C::LP5562) && moduleConfig.ambient_lighting.led_state) {
@@ -108,8 +104,7 @@ class AmbientLightingThread : public concurrency::OSThread
ScanI2C::DeviceType _type = ScanI2C::DeviceType::NONE; ScanI2C::DeviceType _type = ScanI2C::DeviceType::NONE;
// Turn RGB lighting off, is used in junction to shutdown() // Turn RGB lighting off, is used in junction to shutdown()
int setLightingOff(void *unused) int setLightingOff(void *unused) {
{
#ifdef HAS_NCP5623 #ifdef HAS_NCP5623
rgb.setCurrent(0); rgb.setCurrent(0);
rgb.setRed(0); rgb.setRed(0);
@@ -148,16 +143,14 @@ class AmbientLightingThread : public concurrency::OSThread
return 0; return 0;
} }
void setLighting() void setLighting() {
{
#ifdef HAS_NCP5623 #ifdef HAS_NCP5623
rgb.setCurrent(moduleConfig.ambient_lighting.current); rgb.setCurrent(moduleConfig.ambient_lighting.current);
rgb.setRed(moduleConfig.ambient_lighting.red); rgb.setRed(moduleConfig.ambient_lighting.red);
rgb.setGreen(moduleConfig.ambient_lighting.green); rgb.setGreen(moduleConfig.ambient_lighting.green);
rgb.setBlue(moduleConfig.ambient_lighting.blue); rgb.setBlue(moduleConfig.ambient_lighting.blue);
LOG_DEBUG("Init NCP5623 Ambient light w/ current=%d, red=%d, green=%d, blue=%d", LOG_DEBUG("Init NCP5623 Ambient light w/ current=%d, red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.current,
moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif #endif
#ifdef HAS_LP5562 #ifdef HAS_LP5562
rgbw.setCurrent(moduleConfig.ambient_lighting.current); rgbw.setCurrent(moduleConfig.ambient_lighting.current);
@@ -168,9 +161,8 @@ class AmbientLightingThread : public concurrency::OSThread
moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue); moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif #endif
#ifdef HAS_NEOPIXEL #ifdef HAS_NEOPIXEL
pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue), 0,
moduleConfig.ambient_lighting.blue), NEOPIXEL_COUNT);
0, NEOPIXEL_COUNT);
// RadioMaster Bandit has addressable LED at the two buttons // RadioMaster Bandit has addressable LED at the two buttons
// this allow us to set different lighting for them in variant.h file. // this allow us to set different lighting for them in variant.h file.
@@ -201,10 +193,9 @@ class AmbientLightingThread : public concurrency::OSThread
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue); moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif #endif
#ifdef UNPHONE #ifdef UNPHONE
unphone.rgb(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, unphone.rgb(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
LOG_DEBUG("Init unPhone Ambient light w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
moduleConfig.ambient_lighting.blue); moduleConfig.ambient_lighting.blue);
LOG_DEBUG("Init unPhone Ambient light w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif #endif
} }
}; };
+10 -17
View File
@@ -18,13 +18,11 @@ extern ExtensionIOXL9555 io;
#define AUDIO_THREAD_INTERVAL_MS 100 #define AUDIO_THREAD_INTERVAL_MS 100
class AudioThread : public concurrency::OSThread class AudioThread : public concurrency::OSThread {
{ public:
public:
AudioThread() : OSThread("Audio") { initOutput(); } AudioThread() : OSThread("Audio") { initOutput(); }
void beginRttl(const void *data, uint32_t len) void beginRttl(const void *data, uint32_t len) {
{
#ifdef T_LORA_PAGER #ifdef T_LORA_PAGER
io.digitalWrite(EXPANDS_AMP_EN, HIGH); io.digitalWrite(EXPANDS_AMP_EN, HIGH);
#endif #endif
@@ -35,16 +33,14 @@ class AudioThread : public concurrency::OSThread
} }
// Also handles actually playing the RTTTL, needs to be called in loop // Also handles actually playing the RTTTL, needs to be called in loop
bool isPlaying() bool isPlaying() {
{
if (i2sRtttl != nullptr) { if (i2sRtttl != nullptr) {
return i2sRtttl->isRunning() && i2sRtttl->loop(); return i2sRtttl->isRunning() && i2sRtttl->loop();
} }
return false; return false;
} }
void stop() void stop() {
{
if (i2sRtttl != nullptr) { if (i2sRtttl != nullptr) {
i2sRtttl->stop(); i2sRtttl->stop();
delete i2sRtttl; delete i2sRtttl;
@@ -62,8 +58,7 @@ class AudioThread : public concurrency::OSThread
#endif #endif
} }
void readAloud(const char *text) void readAloud(const char *text) {
{
if (i2sRtttl != nullptr) { if (i2sRtttl != nullptr) {
i2sRtttl->stop(); i2sRtttl->stop();
delete i2sRtttl; delete i2sRtttl;
@@ -82,9 +77,8 @@ class AudioThread : public concurrency::OSThread
#endif #endif
} }
protected: protected:
int32_t runOnce() override int32_t runOnce() override {
{
canSleep = true; // Assume we should not keep the board awake canSleep = true; // Assume we should not keep the board awake
// if (i2sRtttl != nullptr && i2sRtttl->isRunning()) { // if (i2sRtttl != nullptr && i2sRtttl->isRunning()) {
@@ -93,9 +87,8 @@ class AudioThread : public concurrency::OSThread
return AUDIO_THREAD_INTERVAL_MS; return AUDIO_THREAD_INTERVAL_MS;
} }
private: private:
void initOutput() void initOutput() {
{
audioOut = new AudioOutputI2S(1, AudioOutputI2S::EXTERNAL_I2S); audioOut = new AudioOutputI2S(1, AudioOutputI2S::EXTERNAL_I2S);
audioOut->SetPinout(DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT, DAC_I2S_MCLK); audioOut->SetPinout(DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT, DAC_I2S_MCLK);
audioOut->SetGain(0.2); audioOut->SetGain(0.2);
+6 -12
View File
@@ -3,15 +3,9 @@
// NRF52 wants these constants as byte arrays // NRF52 wants these constants as byte arrays
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER // Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
const uint8_t MESH_SERVICE_UUID_16[16u] = {0xfd, 0xea, 0x73, 0xe2, 0xca, 0x5d, 0xa8, 0x9f, const uint8_t MESH_SERVICE_UUID_16[16u] = {0xfd, 0xea, 0x73, 0xe2, 0xca, 0x5d, 0xa8, 0x9f, 0x1f, 0x46, 0xa8, 0x15, 0x18, 0xb2, 0xa1, 0x6b};
0x1f, 0x46, 0xa8, 0x15, 0x18, 0xb2, 0xa1, 0x6b}; const uint8_t TORADIO_UUID_16[16u] = {0xe7, 0x01, 0x44, 0x12, 0x66, 0x78, 0xdd, 0xa1, 0xad, 0x4d, 0x9e, 0x12, 0xd2, 0x76, 0x5c, 0xf7};
const uint8_t TORADIO_UUID_16[16u] = {0xe7, 0x01, 0x44, 0x12, 0x66, 0x78, 0xdd, 0xa1, const uint8_t FROMRADIO_UUID_16[16u] = {0x02, 0x00, 0x12, 0xac, 0x42, 0x02, 0x78, 0xb8, 0xed, 0x11, 0x93, 0x49, 0x9e, 0xe6, 0x55, 0x2c};
0xad, 0x4d, 0x9e, 0x12, 0xd2, 0x76, 0x5c, 0xf7}; const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70, 0xa6, 0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed};
const uint8_t FROMRADIO_UUID_16[16u] = {0x02, 0x00, 0x12, 0xac, 0x42, 0x02, 0x78, 0xb8, const uint8_t LEGACY_LOGRADIO_UUID_16[16u] = {0xe2, 0xf2, 0x1e, 0xbe, 0xc5, 0x15, 0xcf, 0xaa, 0x6b, 0x43, 0xfa, 0x78, 0x38, 0xd2, 0x6f, 0x6c};
0xed, 0x11, 0x93, 0x49, 0x9e, 0xe6, 0x55, 0x2c}; const uint8_t LOGRADIO_UUID_16[16u] = {0x47, 0x95, 0xDF, 0x8C, 0xDE, 0xE9, 0x44, 0x99, 0x23, 0x44, 0xE6, 0x06, 0x49, 0x6E, 0x3D, 0x5A};
const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70, 0xa6,
0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed};
const uint8_t LEGACY_LOGRADIO_UUID_16[16u] = {0xe2, 0xf2, 0x1e, 0xbe, 0xc5, 0x15, 0xcf, 0xaa,
0x6b, 0x43, 0xfa, 0x78, 0x38, 0xd2, 0x6f, 0x6c};
const uint8_t LOGRADIO_UUID_16[16u] = {0x47, 0x95, 0xDF, 0x8C, 0xDE, 0xE9, 0x44, 0x99,
0x23, 0x44, 0xE6, 0x06, 0x49, 0x6E, 0x3D, 0x5A};
+2 -3
View File
@@ -21,9 +21,8 @@ extern const uint8_t MESH_SERVICE_UUID_16[], TORADIO_UUID_16[16u], FROMRADIO_UUI
/// Given a level between 0-100, update the BLE attribute /// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level); void updateBatteryLevel(uint8_t level);
class BluetoothApi class BluetoothApi {
{ public:
public:
virtual void setup(); virtual void setup();
virtual void shutdown(); virtual void shutdown();
virtual void clearBonds(); virtual void clearBonds();
+10 -17
View File
@@ -5,41 +5,37 @@
#include "meshUtils.h" #include "meshUtils.h"
#include <Arduino.h> #include <Arduino.h>
namespace meshtastic namespace meshtastic {
{
// Describes the state of the Bluetooth connection // Describes the state of the Bluetooth connection
// Allows display to handle pairing events without each UI needing to explicitly hook the Bluefruit / NimBLE code // Allows display to handle pairing events without each UI needing to explicitly hook the Bluefruit / NimBLE code
class BluetoothStatus : public Status class BluetoothStatus : public Status {
{ public:
public:
enum class ConnectionState { enum class ConnectionState {
DISCONNECTED, DISCONNECTED,
PAIRING, PAIRING,
CONNECTED, CONNECTED,
}; };
private: private:
CallbackObserver<BluetoothStatus, const BluetoothStatus *> statusObserver = CallbackObserver<BluetoothStatus, const BluetoothStatus *> statusObserver =
CallbackObserver<BluetoothStatus, const BluetoothStatus *>(this, &BluetoothStatus::updateStatus); CallbackObserver<BluetoothStatus, const BluetoothStatus *>(this, &BluetoothStatus::updateStatus);
ConnectionState state = ConnectionState::DISCONNECTED; ConnectionState state = ConnectionState::DISCONNECTED;
std::string passkey; // Stored as string, because Bluefruit allows passkeys with a leading zero std::string passkey; // Stored as string, because Bluefruit allows passkeys with a leading zero
public: public:
BluetoothStatus() { statusType = STATUS_TYPE_BLUETOOTH; } BluetoothStatus() { statusType = STATUS_TYPE_BLUETOOTH; }
// New BluetoothStatus: connected or disconnected // New BluetoothStatus: connected or disconnected
explicit BluetoothStatus(ConnectionState state) explicit BluetoothStatus(ConnectionState state) {
{
assert(state != ConnectionState::PAIRING); // If pairing, use constructor which specifies passkey assert(state != ConnectionState::PAIRING); // If pairing, use constructor which specifies passkey
statusType = STATUS_TYPE_BLUETOOTH; statusType = STATUS_TYPE_BLUETOOTH;
this->state = state; this->state = state;
} }
// New BluetoothStatus: pairing, with passkey // New BluetoothStatus: pairing, with passkey
explicit BluetoothStatus(const std::string &passkey) : Status() explicit BluetoothStatus(const std::string &passkey) : Status() {
{
statusType = STATUS_TYPE_BLUETOOTH; statusType = STATUS_TYPE_BLUETOOTH;
this->state = ConnectionState::PAIRING; this->state = ConnectionState::PAIRING;
this->passkey = passkey; this->passkey = passkey;
@@ -47,16 +43,14 @@ class BluetoothStatus : public Status
ConnectionState getConnectionState() const { return this->state; } ConnectionState getConnectionState() const { return this->state; }
std::string getPasskey() const std::string getPasskey() const {
{
assert(state == ConnectionState::PAIRING); assert(state == ConnectionState::PAIRING);
return this->passkey; return this->passkey;
} }
void observe(Observable<const BluetoothStatus *> *source) { statusObserver.observe(source); } void observe(Observable<const BluetoothStatus *> *source) { statusObserver.observe(source); }
bool matches(const BluetoothStatus *newStatus) const bool matches(const BluetoothStatus *newStatus) const {
{
if (this->state == newStatus->getConnectionState()) { if (this->state == newStatus->getConnectionState()) {
// Same state: CONNECTED / DISCONNECTED // Same state: CONNECTED / DISCONNECTED
if (this->state != ConnectionState::PAIRING) if (this->state != ConnectionState::PAIRING)
@@ -69,8 +63,7 @@ class BluetoothStatus : public Status
return false; return false;
} }
int updateStatus(const BluetoothStatus *newStatus) int updateStatus(const BluetoothStatus *newStatus) {
{
// Has the status changed? // Has the status changed?
if (!matches(newStatus)) { if (!matches(newStatus)) {
// Copy the members // Copy the members
+14 -32
View File
@@ -31,8 +31,7 @@ SOFTWARE.*/
#endif #endif
/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic /// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic
extern "C" void logLegacy(const char *level, const char *fmt, ...) extern "C" void logLegacy(const char *level, const char *fmt, ...) {
{
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
if (console) if (console)
@@ -42,8 +41,7 @@ extern "C" void logLegacy(const char *level, const char *fmt, ...)
#if HAS_NETWORKING #if HAS_NETWORKING
Syslog::Syslog(UDP &client) Syslog::Syslog(UDP &client) {
{
this->_client = &client; this->_client = &client;
this->_server = NULL; this->_server = NULL;
this->_port = 0; this->_port = 0;
@@ -52,8 +50,7 @@ Syslog::Syslog(UDP &client)
this->_priDefault = LOGLEVEL_KERN; this->_priDefault = LOGLEVEL_KERN;
} }
Syslog &Syslog::server(const char *server, uint16_t port) Syslog &Syslog::server(const char *server, uint16_t port) {
{
if (this->_ip.fromString(server)) { if (this->_ip.fromString(server)) {
this->_server = NULL; this->_server = NULL;
} else { } else {
@@ -63,62 +60,48 @@ Syslog &Syslog::server(const char *server, uint16_t port)
return *this; return *this;
} }
Syslog &Syslog::server(IPAddress ip, uint16_t port) Syslog &Syslog::server(IPAddress ip, uint16_t port) {
{
this->_ip = ip; this->_ip = ip;
this->_server = NULL; this->_server = NULL;
this->_port = port; this->_port = port;
return *this; return *this;
} }
Syslog &Syslog::deviceHostname(const char *deviceHostname) Syslog &Syslog::deviceHostname(const char *deviceHostname) {
{
this->_deviceHostname = (deviceHostname == NULL) ? SYSLOG_NILVALUE : deviceHostname; this->_deviceHostname = (deviceHostname == NULL) ? SYSLOG_NILVALUE : deviceHostname;
return *this; return *this;
} }
Syslog &Syslog::appName(const char *appName) Syslog &Syslog::appName(const char *appName) {
{
this->_appName = (appName == NULL) ? SYSLOG_NILVALUE : appName; this->_appName = (appName == NULL) ? SYSLOG_NILVALUE : appName;
return *this; return *this;
} }
Syslog &Syslog::defaultPriority(uint16_t pri) Syslog &Syslog::defaultPriority(uint16_t pri) {
{
this->_priDefault = pri; this->_priDefault = pri;
return *this; return *this;
} }
Syslog &Syslog::logMask(uint8_t priMask) Syslog &Syslog::logMask(uint8_t priMask) {
{
this->_priMask = priMask; this->_priMask = priMask;
return *this; return *this;
} }
void Syslog::enable() void Syslog::enable() {
{
this->_client->begin(this->_port); this->_client->begin(this->_port);
this->_enabled = true; this->_enabled = true;
} }
void Syslog::disable() void Syslog::disable() {
{
this->_enabled = false; this->_enabled = false;
this->_client->stop(); this->_client->stop();
} }
bool Syslog::isEnabled() bool Syslog::isEnabled() { return this->_enabled; }
{
return this->_enabled;
}
bool Syslog::vlogf(uint16_t pri, const char *fmt, va_list args) bool Syslog::vlogf(uint16_t pri, const char *fmt, va_list args) { return this->vlogf(pri, this->_appName, fmt, args); }
{
return this->vlogf(pri, this->_appName, fmt, args);
}
bool Syslog::vlogf(uint16_t pri, const char *appName, const char *fmt, va_list args) bool Syslog::vlogf(uint16_t pri, const char *appName, const char *fmt, va_list args) {
{
char *message; char *message;
size_t initialLen; size_t initialLen;
size_t len; size_t len;
@@ -142,8 +125,7 @@ bool Syslog::vlogf(uint16_t pri, const char *appName, const char *fmt, va_list a
return result; return result;
} }
inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *message) inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *message) {
{
int result; int result;
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
bool utf = !portduino_config.ascii_logs; bool utf = !portduino_config.ascii_logs;
+3 -4
View File
@@ -162,9 +162,8 @@ extern "C" void logLegacy(const char *level, const char *fmt, ...);
#if HAS_NETWORKING #if HAS_NETWORKING
class Syslog class Syslog {
{ private:
private:
UDP *_client; UDP *_client;
IPAddress _ip; IPAddress _ip;
const char *_server; const char *_server;
@@ -177,7 +176,7 @@ class Syslog
bool _sendLog(uint16_t pri, const char *appName, const char *message); bool _sendLog(uint16_t pri, const char *appName, const char *message);
public: public:
explicit Syslog(UDP &client); explicit Syslog(UDP &client);
Syslog &server(const char *server, uint16_t port); Syslog &server(const char *server, uint16_t port);
+2 -5
View File
@@ -1,8 +1,6 @@
#include "DisplayFormatters.h" #include "DisplayFormatters.h"
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName, const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName, bool usePreset) {
bool usePreset)
{
// If use_preset is false, always return "Custom" // If use_preset is false, always return "Custom"
if (!usePreset) { if (!usePreset) {
@@ -43,8 +41,7 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaC
} }
} }
const char *DisplayFormatters::getDeviceRole(meshtastic_Config_DeviceConfig_Role role) const char *DisplayFormatters::getDeviceRole(meshtastic_Config_DeviceConfig_Role role) {
{
switch (role) { switch (role) {
case meshtastic_Config_DeviceConfig_Role_CLIENT: case meshtastic_Config_DeviceConfig_Role_CLIENT:
return "Client"; return "Client";
+3 -5
View File
@@ -1,10 +1,8 @@
#pragma once #pragma once
#include "NodeDB.h" #include "NodeDB.h"
class DisplayFormatters class DisplayFormatters {
{ public:
public: static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName, bool usePreset);
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
bool usePreset);
static const char *getDeviceRole(meshtastic_Config_DeviceConfig_Role role); static const char *getDeviceRole(meshtastic_Config_DeviceConfig_Role role);
}; };
+14 -22
View File
@@ -1,11 +1,11 @@
/** /**
* @file FSCommon.cpp * @file FSCommon.cpp
* @brief This file contains functions for common filesystem operations such as copying, renaming, listing and deleting files and * @brief This file contains functions for common filesystem operations such as copying, renaming, listing and deleting
* directories. * files and directories.
* *
* The functions in this file are used to perform common filesystem operations such as copying, renaming, listing and deleting * The functions in this file are used to perform common filesystem operations such as copying, renaming, listing and
* files and directories. These functions are used in the Meshtastic-device project to manage files and directories on the * deleting files and directories. These functions are used in the Meshtastic-device project to manage files and
* device's filesystem. * directories on the device's filesystem.
* *
*/ */
#include "FSCommon.h" #include "FSCommon.h"
@@ -37,8 +37,7 @@ SPIClass SPI_HSPI(HSPI);
* @param to The path of the destination file. * @param to The path of the destination file.
* @return true if the file was successfully copied, false otherwise. * @return true if the file was successfully copied, false otherwise.
*/ */
bool copyFile(const char *from, const char *to) bool copyFile(const char *from, const char *to) {
{
#ifdef FSCom #ifdef FSCom
// take SPI Lock // take SPI Lock
concurrency::LockGuard g(spiLock); concurrency::LockGuard g(spiLock);
@@ -76,8 +75,7 @@ bool copyFile(const char *from, const char *to)
* *
* @return True if the file was successfully renamed, false otherwise. * @return True if the file was successfully renamed, false otherwise.
*/ */
bool renameFile(const char *pathFrom, const char *pathTo) bool renameFile(const char *pathFrom, const char *pathTo) {
{
#ifdef FSCom #ifdef FSCom
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
@@ -111,8 +109,7 @@ bool renameFile(const char *pathFrom, const char *pathTo)
* @param levels The number of levels of subdirectories to list. * @param levels The number of levels of subdirectories to list.
* @return A vector of strings containing the full path of each file in the directory. * @return A vector of strings containing the full path of each file in the directory.
*/ */
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels) std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels) {
{
std::vector<meshtastic_FileInfo> filenames = {}; std::vector<meshtastic_FileInfo> filenames = {};
#ifdef FSCom #ifdef FSCom
File root = FSCom.open(dirname, FILE_O_READ); File root = FSCom.open(dirname, FILE_O_READ);
@@ -160,8 +157,7 @@ std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels)
* @param levels The number of levels of subdirectories to list. * @param levels The number of levels of subdirectories to list.
* @param del Whether or not to delete the contents of the directory after listing. * @param del Whether or not to delete the contents of the directory after listing.
*/ */
void listDir(const char *dirname, uint8_t levels, bool del) void listDir(const char *dirname, uint8_t levels, bool del) {
{
#ifdef FSCom #ifdef FSCom
#if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) #if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
char buffer[255]; char buffer[255];
@@ -175,9 +171,8 @@ void listDir(const char *dirname, uint8_t levels, bool del)
} }
File file = root.openNextFile(); File file = root.openNextFile();
while ( while (file && file.name()[0]) { // This file.name() check is a workaround for a bug in the Adafruit LittleFS nrf52
file && // glue (see issue 4395)
file.name()[0]) { // This file.name() check is a workaround for a bug in the Adafruit LittleFS nrf52 glue (see issue 4395)
if (file.isDirectory() && !String(file.name()).endsWith(".")) { if (file.isDirectory() && !String(file.name()).endsWith(".")) {
if (levels) { if (levels) {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
@@ -265,8 +260,7 @@ void listDir(const char *dirname, uint8_t levels, bool del)
* *
* @param dirname The name of the directory to remove. * @param dirname The name of the directory to remove.
*/ */
void rmDir(const char *dirname) void rmDir(const char *dirname) {
{
#ifdef FSCom #ifdef FSCom
#if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) #if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
@@ -284,8 +278,7 @@ void rmDir(const char *dirname)
*/ */
__attribute__((weak, noinline)) void preFSBegin() {} __attribute__((weak, noinline)) void preFSBegin() {}
void fsInit() void fsInit() {
{
#ifdef FSCom #ifdef FSCom
concurrency::LockGuard g(spiLock); concurrency::LockGuard g(spiLock);
preFSBegin(); preFSBegin();
@@ -305,8 +298,7 @@ void fsInit()
/** /**
* Initializes the SD card and mounts the file system. * Initializes the SD card and mounts the file system.
*/ */
void setupSDCard() void setupSDCard() {
{
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI) #if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI)
concurrency::LockGuard g(spiLock); concurrency::LockGuard g(spiLock);
SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI); SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
+30 -62
View File
@@ -43,8 +43,7 @@ static inline int Clamp(const int value, const int min, const int max);
* @brief Initialises the AHRS algorithm structure. * @brief Initialises the AHRS algorithm structure.
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
*/ */
void FusionAhrsInitialise(FusionAhrs *const ahrs) void FusionAhrsInitialise(FusionAhrs *const ahrs) {
{
const FusionAhrsSettings settings = { const FusionAhrsSettings settings = {
.convention = FusionConventionNwu, .convention = FusionConventionNwu,
.gain = 0.5f, .gain = 0.5f,
@@ -62,8 +61,7 @@ void FusionAhrsInitialise(FusionAhrs *const ahrs)
* algorithm while maintaining the current settings. * algorithm while maintaining the current settings.
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
*/ */
void FusionAhrsReset(FusionAhrs *const ahrs) void FusionAhrsReset(FusionAhrs *const ahrs) {
{
ahrs->quaternion = FUSION_IDENTITY_QUATERNION; ahrs->quaternion = FUSION_IDENTITY_QUATERNION;
ahrs->accelerometer = FUSION_VECTOR_ZERO; ahrs->accelerometer = FUSION_VECTOR_ZERO;
ahrs->initialising = true; ahrs->initialising = true;
@@ -84,21 +82,18 @@ void FusionAhrsReset(FusionAhrs *const ahrs)
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
* @param settings Settings. * @param settings Settings.
*/ */
void FusionAhrsSetSettings(FusionAhrs *const ahrs, const FusionAhrsSettings *const settings) void FusionAhrsSetSettings(FusionAhrs *const ahrs, const FusionAhrsSettings *const settings) {
{
ahrs->settings.convention = settings->convention; ahrs->settings.convention = settings->convention;
ahrs->settings.gain = settings->gain; ahrs->settings.gain = settings->gain;
ahrs->settings.gyroscopeRange = settings->gyroscopeRange == 0.0f ? FLT_MAX : 0.98f * settings->gyroscopeRange; ahrs->settings.gyroscopeRange = settings->gyroscopeRange == 0.0f ? FLT_MAX : 0.98f * settings->gyroscopeRange;
ahrs->settings.accelerationRejection = settings->accelerationRejection == 0.0f ahrs->settings.accelerationRejection =
? FLT_MAX settings->accelerationRejection == 0.0f ? FLT_MAX : powf(0.5f * sinf(FusionDegreesToRadians(settings->accelerationRejection)), 2);
: powf(0.5f * sinf(FusionDegreesToRadians(settings->accelerationRejection)), 2);
ahrs->settings.magneticRejection = ahrs->settings.magneticRejection =
settings->magneticRejection == 0.0f ? FLT_MAX : powf(0.5f * sinf(FusionDegreesToRadians(settings->magneticRejection)), 2); settings->magneticRejection == 0.0f ? FLT_MAX : powf(0.5f * sinf(FusionDegreesToRadians(settings->magneticRejection)), 2);
ahrs->settings.recoveryTriggerPeriod = settings->recoveryTriggerPeriod; ahrs->settings.recoveryTriggerPeriod = settings->recoveryTriggerPeriod;
ahrs->accelerationRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod; ahrs->accelerationRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod;
ahrs->magneticRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod; ahrs->magneticRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod;
if ((settings->gain == 0.0f) || if ((settings->gain == 0.0f) || (settings->recoveryTriggerPeriod == 0)) { // disable acceleration and magnetic rejection features if gain is zero
(settings->recoveryTriggerPeriod == 0)) { // disable acceleration and magnetic rejection features if gain is zero
ahrs->settings.accelerationRejection = FLT_MAX; ahrs->settings.accelerationRejection = FLT_MAX;
ahrs->settings.magneticRejection = FLT_MAX; ahrs->settings.magneticRejection = FLT_MAX;
} }
@@ -117,9 +112,8 @@ void FusionAhrsSetSettings(FusionAhrs *const ahrs, const FusionAhrsSettings *con
* @param magnetometer Magnetometer measurement in arbitrary units. * @param magnetometer Magnetometer measurement in arbitrary units.
* @param deltaTime Delta time in seconds. * @param deltaTime Delta time in seconds.
*/ */
void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const FusionVector magnetometer,
const FusionVector magnetometer, const float deltaTime) const float deltaTime) {
{
#define Q ahrs->quaternion.element #define Q ahrs->quaternion.element
// Store accelerometer // Store accelerometer
@@ -156,8 +150,7 @@ void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, cons
ahrs->halfAccelerometerFeedback = Feedback(FusionVectorNormalise(accelerometer), halfGravity); ahrs->halfAccelerometerFeedback = Feedback(FusionVectorNormalise(accelerometer), halfGravity);
// Don't ignore accelerometer if acceleration error below threshold // Don't ignore accelerometer if acceleration error below threshold
if (ahrs->initialising || if (ahrs->initialising || ((FusionVectorMagnitudeSquared(ahrs->halfAccelerometerFeedback) <= ahrs->settings.accelerationRejection))) {
((FusionVectorMagnitudeSquared(ahrs->halfAccelerometerFeedback) <= ahrs->settings.accelerationRejection))) {
ahrs->accelerometerIgnored = false; ahrs->accelerometerIgnored = false;
ahrs->accelerationRecoveryTrigger -= 9; ahrs->accelerationRecoveryTrigger -= 9;
} else { } else {
@@ -188,12 +181,10 @@ void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, cons
const FusionVector halfMagnetic = HalfMagnetic(ahrs); const FusionVector halfMagnetic = HalfMagnetic(ahrs);
// Calculate magnetometer feedback scaled by 0.5 // Calculate magnetometer feedback scaled by 0.5
ahrs->halfMagnetometerFeedback = ahrs->halfMagnetometerFeedback = Feedback(FusionVectorNormalise(FusionVectorCrossProduct(halfGravity, magnetometer)), halfMagnetic);
Feedback(FusionVectorNormalise(FusionVectorCrossProduct(halfGravity, magnetometer)), halfMagnetic);
// Don't ignore magnetometer if magnetic error below threshold // Don't ignore magnetometer if magnetic error below threshold
if (ahrs->initialising || if (ahrs->initialising || ((FusionVectorMagnitudeSquared(ahrs->halfMagnetometerFeedback) <= ahrs->settings.magneticRejection))) {
((FusionVectorMagnitudeSquared(ahrs->halfMagnetometerFeedback) <= ahrs->settings.magneticRejection))) {
ahrs->magnetometerIgnored = false; ahrs->magnetometerIgnored = false;
ahrs->magneticRecoveryTrigger -= 9; ahrs->magneticRecoveryTrigger -= 9;
} else { } else {
@@ -220,13 +211,11 @@ void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, cons
// Apply feedback to gyroscope // Apply feedback to gyroscope
const FusionVector adjustedHalfGyroscope = FusionVectorAdd( const FusionVector adjustedHalfGyroscope = FusionVectorAdd(
halfGyroscope, halfGyroscope, FusionVectorMultiplyScalar(FusionVectorAdd(halfAccelerometerFeedback, halfMagnetometerFeedback), ahrs->rampedGain));
FusionVectorMultiplyScalar(FusionVectorAdd(halfAccelerometerFeedback, halfMagnetometerFeedback), ahrs->rampedGain));
// Integrate rate of change of quaternion // Integrate rate of change of quaternion
ahrs->quaternion = FusionQuaternionAdd( ahrs->quaternion = FusionQuaternionAdd(
ahrs->quaternion, ahrs->quaternion, FusionQuaternionMultiplyVector(ahrs->quaternion, FusionVectorMultiplyScalar(adjustedHalfGyroscope, deltaTime)));
FusionQuaternionMultiplyVector(ahrs->quaternion, FusionVectorMultiplyScalar(adjustedHalfGyroscope, deltaTime)));
// Normalise quaternion // Normalise quaternion
ahrs->quaternion = FusionQuaternionNormalise(ahrs->quaternion); ahrs->quaternion = FusionQuaternionNormalise(ahrs->quaternion);
@@ -238,8 +227,7 @@ void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, cons
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
* @return Direction of gravity scaled by 0.5. * @return Direction of gravity scaled by 0.5.
*/ */
static inline FusionVector HalfGravity(const FusionAhrs *const ahrs) static inline FusionVector HalfGravity(const FusionAhrs *const ahrs) {
{
#define Q ahrs->quaternion.element #define Q ahrs->quaternion.element
switch (ahrs->settings.convention) { switch (ahrs->settings.convention) {
case FusionConventionNwu: case FusionConventionNwu:
@@ -269,8 +257,7 @@ static inline FusionVector HalfGravity(const FusionAhrs *const ahrs)
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
* @return Direction of the magnetic field scaled by 0.5. * @return Direction of the magnetic field scaled by 0.5.
*/ */
static inline FusionVector HalfMagnetic(const FusionAhrs *const ahrs) static inline FusionVector HalfMagnetic(const FusionAhrs *const ahrs) {
{
#define Q ahrs->quaternion.element #define Q ahrs->quaternion.element
switch (ahrs->settings.convention) { switch (ahrs->settings.convention) {
case FusionConventionNwu: { case FusionConventionNwu: {
@@ -308,8 +295,7 @@ static inline FusionVector HalfMagnetic(const FusionAhrs *const ahrs)
* @param reference Reference. * @param reference Reference.
* @return Feedback. * @return Feedback.
*/ */
static inline FusionVector Feedback(const FusionVector sensor, const FusionVector reference) static inline FusionVector Feedback(const FusionVector sensor, const FusionVector reference) {
{
if (FusionVectorDotProduct(sensor, reference) < 0.0f) { // if error is >90 degrees if (FusionVectorDotProduct(sensor, reference) < 0.0f) { // if error is >90 degrees
return FusionVectorNormalise(FusionVectorCrossProduct(sensor, reference)); return FusionVectorNormalise(FusionVectorCrossProduct(sensor, reference));
} }
@@ -323,8 +309,7 @@ static inline FusionVector Feedback(const FusionVector sensor, const FusionVecto
* @param max Maximum value. * @param max Maximum value.
* @return Value limited to maximum and minimum. * @return Value limited to maximum and minimum.
*/ */
static inline int Clamp(const int value, const int min, const int max) static inline int Clamp(const int value, const int min, const int max) {
{
if (value < min) { if (value < min) {
return min; return min;
} }
@@ -342,9 +327,7 @@ static inline int Clamp(const int value, const int min, const int max)
* @param accelerometer Accelerometer measurement in g. * @param accelerometer Accelerometer measurement in g.
* @param deltaTime Delta time in seconds. * @param deltaTime Delta time in seconds.
*/ */
void FusionAhrsUpdateNoMagnetometer(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, void FusionAhrsUpdateNoMagnetometer(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const float deltaTime) {
const float deltaTime)
{
// Update AHRS algorithm // Update AHRS algorithm
FusionAhrsUpdate(ahrs, gyroscope, accelerometer, FUSION_VECTOR_ZERO, deltaTime); FusionAhrsUpdate(ahrs, gyroscope, accelerometer, FUSION_VECTOR_ZERO, deltaTime);
@@ -364,9 +347,8 @@ void FusionAhrsUpdateNoMagnetometer(FusionAhrs *const ahrs, const FusionVector g
* @param heading Heading measurement in degrees. * @param heading Heading measurement in degrees.
* @param deltaTime Delta time in seconds. * @param deltaTime Delta time in seconds.
*/ */
void FusionAhrsUpdateExternalHeading(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, void FusionAhrsUpdateExternalHeading(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const float heading,
const float heading, const float deltaTime) const float deltaTime) {
{
#define Q ahrs->quaternion.element #define Q ahrs->quaternion.element
// Calculate roll // Calculate roll
@@ -391,20 +373,14 @@ void FusionAhrsUpdateExternalHeading(FusionAhrs *const ahrs, const FusionVector
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
* @return Quaternion describing the sensor relative to the Earth. * @return Quaternion describing the sensor relative to the Earth.
*/ */
FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs *const ahrs) FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs *const ahrs) { return ahrs->quaternion; }
{
return ahrs->quaternion;
}
/** /**
* @brief Sets the quaternion describing the sensor relative to the Earth. * @brief Sets the quaternion describing the sensor relative to the Earth.
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
* @param quaternion Quaternion describing the sensor relative to the Earth. * @param quaternion Quaternion describing the sensor relative to the Earth.
*/ */
void FusionAhrsSetQuaternion(FusionAhrs *const ahrs, const FusionQuaternion quaternion) void FusionAhrsSetQuaternion(FusionAhrs *const ahrs, const FusionQuaternion quaternion) { ahrs->quaternion = quaternion; }
{
ahrs->quaternion = quaternion;
}
/** /**
* @brief Returns the linear acceleration measurement equal to the accelerometer * @brief Returns the linear acceleration measurement equal to the accelerometer
@@ -412,8 +388,7 @@ void FusionAhrsSetQuaternion(FusionAhrs *const ahrs, const FusionQuaternion quat
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
* @return Linear acceleration measurement in g. * @return Linear acceleration measurement in g.
*/ */
FusionVector FusionAhrsGetLinearAcceleration(const FusionAhrs *const ahrs) FusionVector FusionAhrsGetLinearAcceleration(const FusionAhrs *const ahrs) {
{
#define Q ahrs->quaternion.element #define Q ahrs->quaternion.element
// Calculate gravity in the sensor coordinate frame // Calculate gravity in the sensor coordinate frame
@@ -443,8 +418,7 @@ FusionVector FusionAhrsGetLinearAcceleration(const FusionAhrs *const ahrs)
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
* @return Earth acceleration measurement in g. * @return Earth acceleration measurement in g.
*/ */
FusionVector FusionAhrsGetEarthAcceleration(const FusionAhrs *const ahrs) FusionVector FusionAhrsGetEarthAcceleration(const FusionAhrs *const ahrs) {
{
#define Q ahrs->quaternion.element #define Q ahrs->quaternion.element
#define A ahrs->accelerometer.axis #define A ahrs->accelerometer.axis
@@ -482,20 +456,16 @@ FusionVector FusionAhrsGetEarthAcceleration(const FusionAhrs *const ahrs)
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
* @return AHRS algorithm internal states. * @return AHRS algorithm internal states.
*/ */
FusionAhrsInternalStates FusionAhrsGetInternalStates(const FusionAhrs *const ahrs) FusionAhrsInternalStates FusionAhrsGetInternalStates(const FusionAhrs *const ahrs) {
{
const FusionAhrsInternalStates internalStates = { const FusionAhrsInternalStates internalStates = {
.accelerationError = FusionRadiansToDegrees(FusionAsin(2.0f * FusionVectorMagnitude(ahrs->halfAccelerometerFeedback))), .accelerationError = FusionRadiansToDegrees(FusionAsin(2.0f * FusionVectorMagnitude(ahrs->halfAccelerometerFeedback))),
.accelerometerIgnored = ahrs->accelerometerIgnored, .accelerometerIgnored = ahrs->accelerometerIgnored,
.accelerationRecoveryTrigger = .accelerationRecoveryTrigger =
ahrs->settings.recoveryTriggerPeriod == 0 ahrs->settings.recoveryTriggerPeriod == 0 ? 0.0f : (float)ahrs->accelerationRecoveryTrigger / (float)ahrs->settings.recoveryTriggerPeriod,
? 0.0f
: (float)ahrs->accelerationRecoveryTrigger / (float)ahrs->settings.recoveryTriggerPeriod,
.magneticError = FusionRadiansToDegrees(FusionAsin(2.0f * FusionVectorMagnitude(ahrs->halfMagnetometerFeedback))), .magneticError = FusionRadiansToDegrees(FusionAsin(2.0f * FusionVectorMagnitude(ahrs->halfMagnetometerFeedback))),
.magnetometerIgnored = ahrs->magnetometerIgnored, .magnetometerIgnored = ahrs->magnetometerIgnored,
.magneticRecoveryTrigger = ahrs->settings.recoveryTriggerPeriod == 0 .magneticRecoveryTrigger =
? 0.0f ahrs->settings.recoveryTriggerPeriod == 0 ? 0.0f : (float)ahrs->magneticRecoveryTrigger / (float)ahrs->settings.recoveryTriggerPeriod,
: (float)ahrs->magneticRecoveryTrigger / (float)ahrs->settings.recoveryTriggerPeriod,
}; };
return internalStates; return internalStates;
} }
@@ -505,8 +475,7 @@ FusionAhrsInternalStates FusionAhrsGetInternalStates(const FusionAhrs *const ahr
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
* @return AHRS algorithm flags. * @return AHRS algorithm flags.
*/ */
FusionAhrsFlags FusionAhrsGetFlags(const FusionAhrs *const ahrs) FusionAhrsFlags FusionAhrsGetFlags(const FusionAhrs *const ahrs) {
{
const FusionAhrsFlags flags = { const FusionAhrsFlags flags = {
.initialising = ahrs->initialising, .initialising = ahrs->initialising,
.angularRateRecovery = ahrs->angularRateRecovery, .angularRateRecovery = ahrs->angularRateRecovery,
@@ -523,8 +492,7 @@ FusionAhrsFlags FusionAhrsGetFlags(const FusionAhrs *const ahrs)
* @param ahrs AHRS algorithm structure. * @param ahrs AHRS algorithm structure.
* @param heading Heading angle in degrees. * @param heading Heading angle in degrees.
*/ */
void FusionAhrsSetHeading(FusionAhrs *const ahrs, const float heading) void FusionAhrsSetHeading(FusionAhrs *const ahrs, const float heading) {
{
#define Q ahrs->quaternion.element #define Q ahrs->quaternion.element
const float yaw = atan2f(Q.w * Q.z + Q.x * Q.y, 0.5f - Q.y * Q.y - Q.z * Q.z); const float yaw = atan2f(Q.w * Q.z + Q.x * Q.y, 0.5f - Q.y * Q.y - Q.z * Q.z);
const float halfYawMinusHeading = 0.5f * (yaw - FusionDegreesToRadians(heading)); const float halfYawMinusHeading = 0.5f * (yaw - FusionDegreesToRadians(heading));
+5 -6
View File
@@ -83,14 +83,13 @@ void FusionAhrsReset(FusionAhrs *const ahrs);
void FusionAhrsSetSettings(FusionAhrs *const ahrs, const FusionAhrsSettings *const settings); void FusionAhrsSetSettings(FusionAhrs *const ahrs, const FusionAhrsSettings *const settings);
void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const FusionVector magnetometer,
const FusionVector magnetometer, const float deltaTime);
void FusionAhrsUpdateNoMagnetometer(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer,
const float deltaTime); const float deltaTime);
void FusionAhrsUpdateExternalHeading(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, void FusionAhrsUpdateNoMagnetometer(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const float deltaTime);
const float heading, const float deltaTime);
void FusionAhrsUpdateExternalHeading(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const float heading,
const float deltaTime);
FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs *const ahrs); FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs *const ahrs);
+1 -2
View File
@@ -57,8 +57,7 @@ typedef enum {
* @param alignment Axes alignment. * @param alignment Axes alignment.
* @return Sensor axes aligned with the body axes. * @return Sensor axes aligned with the body axes.
*/ */
static inline FusionVector FusionAxesSwap(const FusionVector sensor, const FusionAxesAlignment alignment) static inline FusionVector FusionAxesSwap(const FusionVector sensor, const FusionAxesAlignment alignment) {
{
FusionVector result; FusionVector result;
switch (alignment) { switch (alignment) {
case FusionAxesAlignmentPXPYPZ: case FusionAxesAlignmentPXPYPZ:
+4 -7
View File
@@ -23,11 +23,9 @@
* @param offset Offset. * @param offset Offset.
* @return Calibrated measurement. * @return Calibrated measurement.
*/ */
static inline FusionVector FusionCalibrationInertial(const FusionVector uncalibrated, const FusionMatrix misalignment, static inline FusionVector FusionCalibrationInertial(const FusionVector uncalibrated, const FusionMatrix misalignment, const FusionVector sensitivity,
const FusionVector sensitivity, const FusionVector offset) const FusionVector offset) {
{ return FusionMatrixMultiplyVector(misalignment, FusionVectorHadamardProduct(FusionVectorSubtract(uncalibrated, offset), sensitivity));
return FusionMatrixMultiplyVector(misalignment,
FusionVectorHadamardProduct(FusionVectorSubtract(uncalibrated, offset), sensitivity));
} }
/** /**
@@ -38,8 +36,7 @@ static inline FusionVector FusionCalibrationInertial(const FusionVector uncalibr
* @return Calibrated measurement. * @return Calibrated measurement.
*/ */
static inline FusionVector FusionCalibrationMagnetic(const FusionVector uncalibrated, const FusionMatrix softIronMatrix, static inline FusionVector FusionCalibrationMagnetic(const FusionVector uncalibrated, const FusionMatrix softIronMatrix,
const FusionVector hardIronOffset) const FusionVector hardIronOffset) {
{
return FusionMatrixMultiplyVector(softIronMatrix, FusionVectorSubtract(uncalibrated, hardIronOffset)); return FusionMatrixMultiplyVector(softIronMatrix, FusionVectorSubtract(uncalibrated, hardIronOffset));
} }
+1 -3
View File
@@ -22,9 +22,7 @@
* @param magnetometer Magnetometer measurement in any calibrated units. * @param magnetometer Magnetometer measurement in any calibrated units.
* @return Heading angle in degrees. * @return Heading angle in degrees.
*/ */
float FusionCompassCalculateHeading(const FusionConvention convention, const FusionVector accelerometer, float FusionCompassCalculateHeading(const FusionConvention convention, const FusionVector accelerometer, const FusionVector magnetometer) {
const FusionVector magnetometer)
{
switch (convention) { switch (convention) {
case FusionConventionNwu: { case FusionConventionNwu: {
const FusionVector west = FusionVectorNormalise(FusionVectorCrossProduct(accelerometer, magnetometer)); const FusionVector west = FusionVectorNormalise(FusionVectorCrossProduct(accelerometer, magnetometer));
+1 -2
View File
@@ -17,8 +17,7 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Function declarations // Function declarations
float FusionCompassCalculateHeading(const FusionConvention convention, const FusionVector accelerometer, float FusionCompassCalculateHeading(const FusionConvention convention, const FusionVector accelerometer, const FusionVector magnetometer);
const FusionVector magnetometer);
#endif #endif
+22 -54
View File
@@ -124,20 +124,14 @@ typedef union {
* @param degrees Degrees. * @param degrees Degrees.
* @return Radians. * @return Radians.
*/ */
static inline float FusionDegreesToRadians(const float degrees) static inline float FusionDegreesToRadians(const float degrees) { return degrees * ((float)M_PI / 180.0f); }
{
return degrees * ((float)M_PI / 180.0f);
}
/** /**
* @brief Converts radians to degrees. * @brief Converts radians to degrees.
* @param radians Radians. * @param radians Radians.
* @return Degrees. * @return Degrees.
*/ */
static inline float FusionRadiansToDegrees(const float radians) static inline float FusionRadiansToDegrees(const float radians) { return radians * (180.0f / (float)M_PI); }
{
return radians * (180.0f / (float)M_PI);
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Inline functions - Arc sine // Inline functions - Arc sine
@@ -147,8 +141,7 @@ static inline float FusionRadiansToDegrees(const float radians)
* @param value Value. * @param value Value.
* @return Arc sine of the value. * @return Arc sine of the value.
*/ */
static inline float FusionAsin(const float value) static inline float FusionAsin(const float value) {
{
if (value <= -1.0f) { if (value <= -1.0f) {
return (float)M_PI / -2.0f; return (float)M_PI / -2.0f;
} }
@@ -169,8 +162,7 @@ static inline float FusionAsin(const float value)
* @param x Operand. * @param x Operand.
* @return Reciprocal of the square root of x. * @return Reciprocal of the square root of x.
*/ */
static inline float FusionFastInverseSqrt(const float x) static inline float FusionFastInverseSqrt(const float x) {
{
typedef union { typedef union {
float f; float f;
@@ -192,8 +184,7 @@ static inline float FusionFastInverseSqrt(const float x)
* @param vector Vector. * @param vector Vector.
* @return True if the vector is zero. * @return True if the vector is zero.
*/ */
static inline bool FusionVectorIsZero(const FusionVector vector) static inline bool FusionVectorIsZero(const FusionVector vector) {
{
return (vector.axis.x == 0.0f) && (vector.axis.y == 0.0f) && (vector.axis.z == 0.0f); return (vector.axis.x == 0.0f) && (vector.axis.y == 0.0f) && (vector.axis.z == 0.0f);
} }
@@ -203,8 +194,7 @@ static inline bool FusionVectorIsZero(const FusionVector vector)
* @param vectorB Vector B. * @param vectorB Vector B.
* @return Sum of two vectors. * @return Sum of two vectors.
*/ */
static inline FusionVector FusionVectorAdd(const FusionVector vectorA, const FusionVector vectorB) static inline FusionVector FusionVectorAdd(const FusionVector vectorA, const FusionVector vectorB) {
{
const FusionVector result = {.axis = { const FusionVector result = {.axis = {
.x = vectorA.axis.x + vectorB.axis.x, .x = vectorA.axis.x + vectorB.axis.x,
.y = vectorA.axis.y + vectorB.axis.y, .y = vectorA.axis.y + vectorB.axis.y,
@@ -219,8 +209,7 @@ static inline FusionVector FusionVectorAdd(const FusionVector vectorA, const Fus
* @param vectorB Vector B. * @param vectorB Vector B.
* @return Vector B subtracted from vector A. * @return Vector B subtracted from vector A.
*/ */
static inline FusionVector FusionVectorSubtract(const FusionVector vectorA, const FusionVector vectorB) static inline FusionVector FusionVectorSubtract(const FusionVector vectorA, const FusionVector vectorB) {
{
const FusionVector result = {.axis = { const FusionVector result = {.axis = {
.x = vectorA.axis.x - vectorB.axis.x, .x = vectorA.axis.x - vectorB.axis.x,
.y = vectorA.axis.y - vectorB.axis.y, .y = vectorA.axis.y - vectorB.axis.y,
@@ -234,10 +223,7 @@ static inline FusionVector FusionVectorSubtract(const FusionVector vectorA, cons
* @param vector Vector. * @param vector Vector.
* @return Sum of the elements. * @return Sum of the elements.
*/ */
static inline float FusionVectorSum(const FusionVector vector) static inline float FusionVectorSum(const FusionVector vector) { return vector.axis.x + vector.axis.y + vector.axis.z; }
{
return vector.axis.x + vector.axis.y + vector.axis.z;
}
/** /**
* @brief Returns the multiplication of a vector by a scalar. * @brief Returns the multiplication of a vector by a scalar.
@@ -245,8 +231,7 @@ static inline float FusionVectorSum(const FusionVector vector)
* @param scalar Scalar. * @param scalar Scalar.
* @return Multiplication of a vector by a scalar. * @return Multiplication of a vector by a scalar.
*/ */
static inline FusionVector FusionVectorMultiplyScalar(const FusionVector vector, const float scalar) static inline FusionVector FusionVectorMultiplyScalar(const FusionVector vector, const float scalar) {
{
const FusionVector result = {.axis = { const FusionVector result = {.axis = {
.x = vector.axis.x * scalar, .x = vector.axis.x * scalar,
.y = vector.axis.y * scalar, .y = vector.axis.y * scalar,
@@ -261,8 +246,7 @@ static inline FusionVector FusionVectorMultiplyScalar(const FusionVector vector,
* @param vectorB Vector B. * @param vectorB Vector B.
* @return Hadamard product. * @return Hadamard product.
*/ */
static inline FusionVector FusionVectorHadamardProduct(const FusionVector vectorA, const FusionVector vectorB) static inline FusionVector FusionVectorHadamardProduct(const FusionVector vectorA, const FusionVector vectorB) {
{
const FusionVector result = {.axis = { const FusionVector result = {.axis = {
.x = vectorA.axis.x * vectorB.axis.x, .x = vectorA.axis.x * vectorB.axis.x,
.y = vectorA.axis.y * vectorB.axis.y, .y = vectorA.axis.y * vectorB.axis.y,
@@ -277,8 +261,7 @@ static inline FusionVector FusionVectorHadamardProduct(const FusionVector vector
* @param vectorB Vector B. * @param vectorB Vector B.
* @return Cross product. * @return Cross product.
*/ */
static inline FusionVector FusionVectorCrossProduct(const FusionVector vectorA, const FusionVector vectorB) static inline FusionVector FusionVectorCrossProduct(const FusionVector vectorA, const FusionVector vectorB) {
{
#define A vectorA.axis #define A vectorA.axis
#define B vectorB.axis #define B vectorB.axis
const FusionVector result = {.axis = { const FusionVector result = {.axis = {
@@ -297,8 +280,7 @@ static inline FusionVector FusionVectorCrossProduct(const FusionVector vectorA,
* @param vectorB Vector B. * @param vectorB Vector B.
* @return Dot product. * @return Dot product.
*/ */
static inline float FusionVectorDotProduct(const FusionVector vectorA, const FusionVector vectorB) static inline float FusionVectorDotProduct(const FusionVector vectorA, const FusionVector vectorB) {
{
return FusionVectorSum(FusionVectorHadamardProduct(vectorA, vectorB)); return FusionVectorSum(FusionVectorHadamardProduct(vectorA, vectorB));
} }
@@ -307,28 +289,21 @@ static inline float FusionVectorDotProduct(const FusionVector vectorA, const Fus
* @param vector Vector. * @param vector Vector.
* @return Vector magnitude squared. * @return Vector magnitude squared.
*/ */
static inline float FusionVectorMagnitudeSquared(const FusionVector vector) static inline float FusionVectorMagnitudeSquared(const FusionVector vector) { return FusionVectorSum(FusionVectorHadamardProduct(vector, vector)); }
{
return FusionVectorSum(FusionVectorHadamardProduct(vector, vector));
}
/** /**
* @brief Returns the vector magnitude. * @brief Returns the vector magnitude.
* @param vector Vector. * @param vector Vector.
* @return Vector magnitude. * @return Vector magnitude.
*/ */
static inline float FusionVectorMagnitude(const FusionVector vector) static inline float FusionVectorMagnitude(const FusionVector vector) { return sqrtf(FusionVectorMagnitudeSquared(vector)); }
{
return sqrtf(FusionVectorMagnitudeSquared(vector));
}
/** /**
* @brief Returns the normalised vector. * @brief Returns the normalised vector.
* @param vector Vector. * @param vector Vector.
* @return Normalised vector. * @return Normalised vector.
*/ */
static inline FusionVector FusionVectorNormalise(const FusionVector vector) static inline FusionVector FusionVectorNormalise(const FusionVector vector) {
{
#ifdef FUSION_USE_NORMAL_SQRT #ifdef FUSION_USE_NORMAL_SQRT
const float magnitudeReciprocal = 1.0f / sqrtf(FusionVectorMagnitudeSquared(vector)); const float magnitudeReciprocal = 1.0f / sqrtf(FusionVectorMagnitudeSquared(vector));
#else #else
@@ -346,8 +321,7 @@ static inline FusionVector FusionVectorNormalise(const FusionVector vector)
* @param quaternionB Quaternion B. * @param quaternionB Quaternion B.
* @return Sum of two quaternions. * @return Sum of two quaternions.
*/ */
static inline FusionQuaternion FusionQuaternionAdd(const FusionQuaternion quaternionA, const FusionQuaternion quaternionB) static inline FusionQuaternion FusionQuaternionAdd(const FusionQuaternion quaternionA, const FusionQuaternion quaternionB) {
{
const FusionQuaternion result = {.element = { const FusionQuaternion result = {.element = {
.w = quaternionA.element.w + quaternionB.element.w, .w = quaternionA.element.w + quaternionB.element.w,
.x = quaternionA.element.x + quaternionB.element.x, .x = quaternionA.element.x + quaternionB.element.x,
@@ -363,8 +337,7 @@ static inline FusionQuaternion FusionQuaternionAdd(const FusionQuaternion quater
* @param quaternionB Quaternion B (to be pre-multiplied). * @param quaternionB Quaternion B (to be pre-multiplied).
* @return Multiplication of two quaternions. * @return Multiplication of two quaternions.
*/ */
static inline FusionQuaternion FusionQuaternionMultiply(const FusionQuaternion quaternionA, const FusionQuaternion quaternionB) static inline FusionQuaternion FusionQuaternionMultiply(const FusionQuaternion quaternionA, const FusionQuaternion quaternionB) {
{
#define A quaternionA.element #define A quaternionA.element
#define B quaternionB.element #define B quaternionB.element
const FusionQuaternion result = {.element = { const FusionQuaternion result = {.element = {
@@ -387,8 +360,7 @@ static inline FusionQuaternion FusionQuaternionMultiply(const FusionQuaternion q
* @param vector Vector. * @param vector Vector.
* @return Multiplication of a quaternion with a vector. * @return Multiplication of a quaternion with a vector.
*/ */
static inline FusionQuaternion FusionQuaternionMultiplyVector(const FusionQuaternion quaternion, const FusionVector vector) static inline FusionQuaternion FusionQuaternionMultiplyVector(const FusionQuaternion quaternion, const FusionVector vector) {
{
#define Q quaternion.element #define Q quaternion.element
#define V vector.axis #define V vector.axis
const FusionQuaternion result = {.element = { const FusionQuaternion result = {.element = {
@@ -407,8 +379,7 @@ static inline FusionQuaternion FusionQuaternionMultiplyVector(const FusionQuater
* @param quaternion Quaternion. * @param quaternion Quaternion.
* @return Normalised quaternion. * @return Normalised quaternion.
*/ */
static inline FusionQuaternion FusionQuaternionNormalise(const FusionQuaternion quaternion) static inline FusionQuaternion FusionQuaternionNormalise(const FusionQuaternion quaternion) {
{
#define Q quaternion.element #define Q quaternion.element
#ifdef FUSION_USE_NORMAL_SQRT #ifdef FUSION_USE_NORMAL_SQRT
const float magnitudeReciprocal = 1.0f / sqrtf(Q.w * Q.w + Q.x * Q.x + Q.y * Q.y + Q.z * Q.z); const float magnitudeReciprocal = 1.0f / sqrtf(Q.w * Q.w + Q.x * Q.x + Q.y * Q.y + Q.z * Q.z);
@@ -434,8 +405,7 @@ static inline FusionQuaternion FusionQuaternionNormalise(const FusionQuaternion
* @param vector Vector. * @param vector Vector.
* @return Multiplication of a matrix with a vector. * @return Multiplication of a matrix with a vector.
*/ */
static inline FusionVector FusionMatrixMultiplyVector(const FusionMatrix matrix, const FusionVector vector) static inline FusionVector FusionMatrixMultiplyVector(const FusionMatrix matrix, const FusionVector vector) {
{
#define R matrix.element #define R matrix.element
const FusionVector result = {.axis = { const FusionVector result = {.axis = {
.x = R.xx * vector.axis.x + R.xy * vector.axis.y + R.xz * vector.axis.z, .x = R.xx * vector.axis.x + R.xy * vector.axis.y + R.xz * vector.axis.z,
@@ -454,8 +424,7 @@ static inline FusionVector FusionMatrixMultiplyVector(const FusionMatrix matrix,
* @param quaternion Quaternion. * @param quaternion Quaternion.
* @return Rotation matrix. * @return Rotation matrix.
*/ */
static inline FusionMatrix FusionQuaternionToMatrix(const FusionQuaternion quaternion) static inline FusionMatrix FusionQuaternionToMatrix(const FusionQuaternion quaternion) {
{
#define Q quaternion.element #define Q quaternion.element
const float qwqw = Q.w * Q.w; // calculate common terms to avoid repeated operations const float qwqw = Q.w * Q.w; // calculate common terms to avoid repeated operations
const float qwqx = Q.w * Q.x; const float qwqx = Q.w * Q.x;
@@ -484,8 +453,7 @@ static inline FusionMatrix FusionQuaternionToMatrix(const FusionQuaternion quate
* @param quaternion Quaternion. * @param quaternion Quaternion.
* @return Euler angles in degrees. * @return Euler angles in degrees.
*/ */
static inline FusionEuler FusionQuaternionToEuler(const FusionQuaternion quaternion) static inline FusionEuler FusionQuaternionToEuler(const FusionQuaternion quaternion) {
{
#define Q quaternion.element #define Q quaternion.element
const float halfMinusQySquared = 0.5f - Q.y * Q.y; // calculate common terms to avoid repeated operations const float halfMinusQySquared = 0.5f - Q.y * Q.y; // calculate common terms to avoid repeated operations
const FusionEuler euler = {.angle = { const FusionEuler euler = {.angle = {
+3 -6
View File
@@ -37,8 +37,7 @@
* @param offset Gyroscope offset algorithm structure. * @param offset Gyroscope offset algorithm structure.
* @param sampleRate Sample rate in Hz. * @param sampleRate Sample rate in Hz.
*/ */
void FusionOffsetInitialise(FusionOffset *const offset, const unsigned int sampleRate) void FusionOffsetInitialise(FusionOffset *const offset, const unsigned int sampleRate) {
{
offset->filterCoefficient = 2.0f * (float)M_PI * CUTOFF_FREQUENCY * (1.0f / (float)sampleRate); offset->filterCoefficient = 2.0f * (float)M_PI * CUTOFF_FREQUENCY * (1.0f / (float)sampleRate);
offset->timeout = TIMEOUT * sampleRate; offset->timeout = TIMEOUT * sampleRate;
offset->timer = 0; offset->timer = 0;
@@ -52,8 +51,7 @@ void FusionOffsetInitialise(FusionOffset *const offset, const unsigned int sampl
* @param gyroscope Gyroscope measurement in degrees per second. * @param gyroscope Gyroscope measurement in degrees per second.
* @return Corrected gyroscope measurement in degrees per second. * @return Corrected gyroscope measurement in degrees per second.
*/ */
FusionVector FusionOffsetUpdate(FusionOffset *const offset, FusionVector gyroscope) FusionVector FusionOffsetUpdate(FusionOffset *const offset, FusionVector gyroscope) {
{
// Subtract offset from gyroscope measurement // Subtract offset from gyroscope measurement
gyroscope = FusionVectorSubtract(gyroscope, offset->gyroscopeOffset); gyroscope = FusionVectorSubtract(gyroscope, offset->gyroscopeOffset);
@@ -71,8 +69,7 @@ FusionVector FusionOffsetUpdate(FusionOffset *const offset, FusionVector gyrosco
} }
// Adjust offset if timer has elapsed // Adjust offset if timer has elapsed
offset->gyroscopeOffset = offset->gyroscopeOffset = FusionVectorAdd(offset->gyroscopeOffset, FusionVectorMultiplyScalar(gyroscope, offset->filterCoefficient));
FusionVectorAdd(offset->gyroscopeOffset, FusionVectorMultiplyScalar(gyroscope, offset->filterCoefficient));
return gyroscope; return gyroscope;
} }
+17 -29
View File
@@ -4,16 +4,13 @@
#include "configuration.h" #include "configuration.h"
#include <Arduino.h> #include <Arduino.h>
namespace meshtastic namespace meshtastic {
{
/// Describes the state of the GPS system. /// Describes the state of the GPS system.
class GPSStatus : public Status class GPSStatus : public Status {
{
private: private:
CallbackObserver<GPSStatus, const GPSStatus *> statusObserver = CallbackObserver<GPSStatus, const GPSStatus *> statusObserver = CallbackObserver<GPSStatus, const GPSStatus *>(this, &GPSStatus::updateStatus);
CallbackObserver<GPSStatus, const GPSStatus *>(this, &GPSStatus::updateStatus);
bool hasLock = false; // default to false, until we complete our first read bool hasLock = false; // default to false, until we complete our first read
bool isConnected = false; // Do we have a GPS we are talking to bool isConnected = false; // Do we have a GPS we are talking to
@@ -25,12 +22,11 @@ class GPSStatus : public Status
/// Time of last valid GPS fix (millis since boot) /// Time of last valid GPS fix (millis since boot)
uint32_t lastFixMillis = 0; uint32_t lastFixMillis = 0;
public: public:
GPSStatus() { statusType = STATUS_TYPE_GPS; } GPSStatus() { statusType = STATUS_TYPE_GPS; }
// preferred method // preferred method
GPSStatus(bool hasLock, bool isConnected, bool isPowerSaving, const meshtastic_Position &pos) : Status() GPSStatus(bool hasLock, bool isConnected, bool isPowerSaving, const meshtastic_Position &pos) : Status() {
{
this->hasLock = hasLock; this->hasLock = hasLock;
this->isConnected = isConnected; this->isConnected = isConnected;
this->isPowerSaving = isPowerSaving; this->isPowerSaving = isPowerSaving;
@@ -50,8 +46,7 @@ class GPSStatus : public Status
bool getIsPowerSaving() const { return isPowerSaving; } bool getIsPowerSaving() const { return isPowerSaving; }
int32_t getLatitude() const int32_t getLatitude() const {
{
if (config.position.fixed_position) { if (config.position.fixed_position) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
return node->position.latitude_i; return node->position.latitude_i;
@@ -60,8 +55,7 @@ class GPSStatus : public Status
} }
} }
int32_t getLongitude() const int32_t getLongitude() const {
{
if (config.position.fixed_position) { if (config.position.fixed_position) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
return node->position.longitude_i; return node->position.longitude_i;
@@ -70,8 +64,7 @@ class GPSStatus : public Status
} }
} }
int32_t getAltitude() const int32_t getAltitude() const {
{
if (config.position.fixed_position) { if (config.position.fixed_position) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
return node->position.altitude; return node->position.altitude;
@@ -89,21 +82,17 @@ class GPSStatus : public Status
/// Return millis() when the last GPS fix occurred (0 = never) /// Return millis() when the last GPS fix occurred (0 = never)
uint32_t getLastFixMillis() const { return lastFixMillis; } uint32_t getLastFixMillis() const { return lastFixMillis; }
bool matches(const GPSStatus *newStatus) const bool matches(const GPSStatus *newStatus) const {
{
#ifdef GPS_DEBUG #ifdef GPS_DEBUG
LOG_DEBUG("GPSStatus.match() new pos@%x to old pos@%x", newStatus->p.timestamp, p.timestamp); LOG_DEBUG("GPSStatus.match() new pos@%x to old pos@%x", newStatus->p.timestamp, p.timestamp);
#endif #endif
return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected || return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected || newStatus->isPowerSaving != isPowerSaving ||
newStatus->isPowerSaving != isPowerSaving || newStatus->p.latitude_i != p.latitude_i || newStatus->p.latitude_i != p.latitude_i || newStatus->p.longitude_i != p.longitude_i || newStatus->p.altitude != p.altitude ||
newStatus->p.longitude_i != p.longitude_i || newStatus->p.altitude != p.altitude || newStatus->p.altitude_hae != p.altitude_hae || newStatus->p.PDOP != p.PDOP || newStatus->p.ground_track != p.ground_track ||
newStatus->p.altitude_hae != p.altitude_hae || newStatus->p.PDOP != p.PDOP || newStatus->p.ground_speed != p.ground_speed || newStatus->p.sats_in_view != p.sats_in_view);
newStatus->p.ground_track != p.ground_track || newStatus->p.ground_speed != p.ground_speed ||
newStatus->p.sats_in_view != p.sats_in_view);
} }
int updateStatus(const GPSStatus *newStatus) int updateStatus(const GPSStatus *newStatus) {
{
// Only update the status if values have actually changed // Only update the status if values have actually changed
bool isDirty = matches(newStatus); bool isDirty = matches(newStatus);
@@ -124,9 +113,8 @@ class GPSStatus : public Status
lastFixMillis = millis(); lastFixMillis = millis();
// In debug logs, identify position by @timestamp:stage (stage 3 = notify) // In debug logs, identify position by @timestamp:stage (stage 3 = notify)
LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d", p.timestamp, LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d", p.timestamp, p.latitude_i * 1e-7,
p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5, p.ground_speed * 1e-2, p.sats_in_view);
p.ground_speed * 1e-2, p.sats_in_view);
} else { } else {
LOG_DEBUG("No GPS lock"); LOG_DEBUG("No GPS lock");
} }
+12 -24
View File
@@ -1,8 +1,7 @@
#include "GpioLogic.h" #include "GpioLogic.h"
#include <assert.h> #include <assert.h>
void GpioVirtPin::set(bool value) void GpioVirtPin::set(bool value) {
{
if (value != this->value) { if (value != this->value) {
this->value = value ? PinState::On : PinState::Off; this->value = value ? PinState::On : PinState::Off;
if (dependentPin) if (dependentPin)
@@ -10,34 +9,27 @@ void GpioVirtPin::set(bool value)
} }
} }
void GpioHwPin::set(bool value) void GpioHwPin::set(bool value) {
{
pinMode(num, OUTPUT); pinMode(num, OUTPUT);
digitalWrite(num, value); digitalWrite(num, value);
} }
GpioTransformer::GpioTransformer(GpioPin *outPin) : outPin(outPin) {} GpioTransformer::GpioTransformer(GpioPin *outPin) : outPin(outPin) {}
void GpioTransformer::set(bool value) void GpioTransformer::set(bool value) { outPin->set(value); }
{
outPin->set(value);
}
GpioUnaryTransformer::GpioUnaryTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioTransformer(outPin), inPin(inPin) GpioUnaryTransformer::GpioUnaryTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioTransformer(outPin), inPin(inPin) {
{
assert(!inPin->dependentPin); // We only allow one dependent pin assert(!inPin->dependentPin); // We only allow one dependent pin
inPin->dependentPin = this; inPin->dependentPin = this;
// Don't update at construction time, because various GpioPins might be global constructor based not yet initied because // Don't update at construction time, because various GpioPins might be global constructor based not yet initied
// order of operations for global constructors is not defined. // because order of operations for global constructors is not defined. update();
// update();
} }
/** /**
* Update the output pin based on the current state of the input pin. * Update the output pin based on the current state of the input pin.
*/ */
void GpioUnaryTransformer::update() void GpioUnaryTransformer::update() {
{
auto p = inPin->get(); auto p = inPin->get();
if (p == GpioVirtPin::PinState::Unset) if (p == GpioVirtPin::PinState::Unset)
return; // Not yet fully initialized return; // Not yet fully initialized
@@ -48,8 +40,7 @@ void GpioUnaryTransformer::update()
/** /**
* Update the output pin based on the current state of the input pin. * Update the output pin based on the current state of the input pin.
*/ */
void GpioNotTransformer::update() void GpioNotTransformer::update() {
{
auto p = inPin->get(); auto p = inPin->get();
if (p == GpioVirtPin::PinState::Unset) if (p == GpioVirtPin::PinState::Unset)
return; // Not yet fully initialized return; // Not yet fully initialized
@@ -58,20 +49,17 @@ void GpioNotTransformer::update()
} }
GpioBinaryTransformer::GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation) GpioBinaryTransformer::GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation)
: GpioTransformer(outPin), inPin1(inPin1), inPin2(inPin2), operation(operation) : GpioTransformer(outPin), inPin1(inPin1), inPin2(inPin2), operation(operation) {
{
assert(!inPin1->dependentPin); // We only allow one dependent pin assert(!inPin1->dependentPin); // We only allow one dependent pin
inPin1->dependentPin = this; inPin1->dependentPin = this;
assert(!inPin2->dependentPin); // We only allow one dependent pin assert(!inPin2->dependentPin); // We only allow one dependent pin
inPin2->dependentPin = this; inPin2->dependentPin = this;
// Don't update at construction time, because various GpioPins might be global constructor based not yet initiated because // Don't update at construction time, because various GpioPins might be global constructor based not yet initiated
// order of operations for global constructors is not defined. // because order of operations for global constructors is not defined. update();
// update();
} }
void GpioBinaryTransformer::update() void GpioBinaryTransformer::update() {
{
auto p1 = inPin1->get(), p2 = inPin2->get(); auto p1 = inPin1->get(), p2 = inPin2->get();
GpioVirtPin::PinState newValue = GpioVirtPin::PinState::Unset; GpioVirtPin::PinState newValue = GpioVirtPin::PinState::Unset;
+30 -38
View File
@@ -3,8 +3,9 @@
#include "configuration.h" #include "configuration.h"
/**This is a set of classes to mediate access to GPIOs in a structured way. Most usage of GPIOs do not /**This is a set of classes to mediate access to GPIOs in a structured way. Most usage of GPIOs do not
require these classes! But if your hardware has a GPIO that is 'shared' between multiple devices (i.e. a shared power enable) require these classes! But if your hardware has a GPIO that is 'shared' between multiple devices (i.e. a shared
then using these classes might be able to let you cleanly turn on that enable when either dependent device is needed. power enable) then using these classes might be able to let you cleanly turn on that enable when either dependent
device is needed.
Note: these classes are intended to be 99% inline for the common case so should have minimal impact on flash or RAM Note: these classes are intended to be 99% inline for the common case so should have minimal impact on flash or RAM
requirements. requirements.
@@ -13,20 +14,18 @@
/** /**
* A logical GPIO pin (not necessary raw hardware). * A logical GPIO pin (not necessary raw hardware).
*/ */
class GpioPin class GpioPin {
{ public:
public:
virtual void set(bool value) = 0; virtual void set(bool value) = 0;
}; };
/** /**
* A physical GPIO hw pin. * A physical GPIO hw pin.
*/ */
class GpioHwPin : public GpioPin class GpioHwPin : public GpioPin {
{
uint32_t num; uint32_t num;
public: public:
explicit GpioHwPin(uint32_t num) : num(num) {} explicit GpioHwPin(uint32_t num) : num(num) {}
void set(bool value); void set(bool value);
@@ -39,18 +38,17 @@ class GpioBinaryTransformer;
/** /**
* A virtual GPIO pin. * A virtual GPIO pin.
*/ */
class GpioVirtPin : public GpioPin class GpioVirtPin : public GpioPin {
{
friend class GpioBinaryTransformer; friend class GpioBinaryTransformer;
friend class GpioUnaryTransformer; friend class GpioUnaryTransformer;
public: public:
enum PinState { On = true, Off = false, Unset = 2 }; enum PinState { On = true, Off = false, Unset = 2 };
void set(bool value); void set(bool value);
PinState get() const { return value; } PinState get() const { return value; }
private: private:
PinState value = PinState::Unset; PinState value = PinState::Unset;
GpioTransformer *dependentPin = NULL; GpioTransformer *dependentPin = NULL;
}; };
@@ -58,35 +56,33 @@ class GpioVirtPin : public GpioPin
#include <assert.h> #include <assert.h>
/** /**
* A 'smart' trigger that can depend in a fake GPIO and if that GPIO changes, drive some other downstream GPIO to change. * A 'smart' trigger that can depend in a fake GPIO and if that GPIO changes, drive some other downstream GPIO to
* notably: the set method is not public (because it always is calculated by a subclass) * change. notably: the set method is not public (because it always is calculated by a subclass)
*/ */
class GpioTransformer class GpioTransformer {
{ public:
public:
/** /**
* Update the output pin based on the current state of the input pin. * Update the output pin based on the current state of the input pin.
*/ */
virtual void update() = 0; virtual void update() = 0;
protected: protected:
GpioTransformer(GpioPin *outPin); GpioTransformer(GpioPin *outPin);
void set(bool value); void set(bool value);
private: private:
GpioPin *outPin; GpioPin *outPin;
}; };
/** /**
* A transformer that just drives a hw pin based on a virtual pin. * A transformer that just drives a hw pin based on a virtual pin.
*/ */
class GpioUnaryTransformer : public GpioTransformer class GpioUnaryTransformer : public GpioTransformer {
{ public:
public:
GpioUnaryTransformer(GpioVirtPin *inPin, GpioPin *outPin); GpioUnaryTransformer(GpioVirtPin *inPin, GpioPin *outPin);
protected: protected:
friend class GpioVirtPin; friend class GpioVirtPin;
/** /**
@@ -100,12 +96,11 @@ class GpioUnaryTransformer : public GpioTransformer
/** /**
* A transformer that performs a unary NOT operation from an input. * A transformer that performs a unary NOT operation from an input.
*/ */
class GpioNotTransformer : public GpioUnaryTransformer class GpioNotTransformer : public GpioUnaryTransformer {
{ public:
public:
GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioUnaryTransformer(inPin, outPin) {} GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioUnaryTransformer(inPin, outPin) {}
protected: protected:
friend class GpioVirtPin; friend class GpioVirtPin;
/** /**
@@ -117,15 +112,14 @@ class GpioNotTransformer : public GpioUnaryTransformer
/** /**
* A transformer that combines multiple virtual pins to drive an output pin * A transformer that combines multiple virtual pins to drive an output pin
*/ */
class GpioBinaryTransformer : public GpioTransformer class GpioBinaryTransformer : public GpioTransformer {
{
public: public:
enum Operation { And, Or, Xor }; enum Operation { And, Or, Xor };
GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation); GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation);
protected: protected:
friend class GpioVirtPin; friend class GpioVirtPin;
/** /**
@@ -133,7 +127,7 @@ class GpioBinaryTransformer : public GpioTransformer
*/ */
void update(); void update();
private: private:
GpioVirtPin *inPin1; GpioVirtPin *inPin1;
GpioVirtPin *inPin2; GpioVirtPin *inPin2;
Operation operation; Operation operation;
@@ -142,19 +136,17 @@ class GpioBinaryTransformer : public GpioTransformer
/** /**
* Sometimes a single output GPIO single needs to drive multiple physical GPIOs. This class provides that. * Sometimes a single output GPIO single needs to drive multiple physical GPIOs. This class provides that.
*/ */
class GpioSplitter : public GpioPin class GpioSplitter : public GpioPin {
{
public: public:
GpioSplitter(GpioPin *outPin1, GpioPin *outPin2); GpioSplitter(GpioPin *outPin1, GpioPin *outPin2);
void set(bool value) void set(bool value) {
{
outPin1->set(value); outPin1->set(value);
outPin2->set(value); outPin2->set(value);
} }
private: private:
GpioPin *outPin1; GpioPin *outPin1;
GpioPin *outPin2; GpioPin *outPin2;
}; };
+6 -10
View File
@@ -23,11 +23,9 @@ static GpioPin &ledHwPin = ledRawHwPin;
/** /**
* A GPIO controlled by the PMU * A GPIO controlled by the PMU
*/ */
class GpioPmuPin : public GpioPin class GpioPmuPin : public GpioPin {
{ public:
public: void set(bool value) {
void set(bool value)
{
if (pmu_found && PMU) { if (pmu_found && PMU) {
// blink the axp led // blink the axp led
PMU->setChargingLedMode(value ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF); PMU->setChargingLedMode(value ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
@@ -45,11 +43,9 @@ static GpioPin &ledFinalPin = ledHwPin;
/** /**
* We monitor changes to the LED drive output because we use that as a sanity test in our power monitor stuff. * We monitor changes to the LED drive output because we use that as a sanity test in our power monitor stuff.
*/ */
class MonitoredLedPin : public GpioPin class MonitoredLedPin : public GpioPin {
{ public:
public: void set(bool value) {
void set(bool value)
{
if (powerMon) { if (powerMon) {
if (value) if (value)
powerMon->setState(meshtastic_PowerMon_State_LED_On); powerMon->setState(meshtastic_PowerMon_State_LED_On);
+27 -58
View File
@@ -18,8 +18,7 @@ static char *g_messagePool = nullptr;
static size_t g_poolWritePos = 0; static size_t g_poolWritePos = 0;
// Reset pool (called on boot or clear) // Reset pool (called on boot or clear)
static inline void resetMessagePool() static inline void resetMessagePool() {
{
if (!g_messagePool) { if (!g_messagePool) {
g_messagePool = static_cast<char *>(malloc(MESSAGE_TEXT_POOL_SIZE)); g_messagePool = static_cast<char *>(malloc(MESSAGE_TEXT_POOL_SIZE));
if (!g_messagePool) { if (!g_messagePool) {
@@ -33,8 +32,7 @@ static inline void resetMessagePool()
// Allocate text in pool and return offset // Allocate text in pool and return offset
// If not enough space remains, wrap around (ring buffer style) // If not enough space remains, wrap around (ring buffer style)
static inline uint16_t storeTextInPool(const char *src, size_t len) static inline uint16_t storeTextInPool(const char *src, size_t len) {
{
if (len >= MAX_MESSAGE_SIZE) if (len >= MAX_MESSAGE_SIZE)
len = MAX_MESSAGE_SIZE - 1; len = MAX_MESSAGE_SIZE - 1;
@@ -51,16 +49,14 @@ static inline uint16_t storeTextInPool(const char *src, size_t len)
} }
// Retrieve a const pointer to message text by offset // Retrieve a const pointer to message text by offset
static inline const char *getTextFromPool(uint16_t offset) static inline const char *getTextFromPool(uint16_t offset) {
{
if (!g_messagePool || offset >= MESSAGE_TEXT_POOL_SIZE) if (!g_messagePool || offset >= MESSAGE_TEXT_POOL_SIZE)
return ""; return "";
return &g_messagePool[offset]; return &g_messagePool[offset];
} }
// Helper: assign a timestamp (RTC if available, else boot-relative) // Helper: assign a timestamp (RTC if available, else boot-relative)
static inline void assignTimestamp(StoredMessage &sm) static inline void assignTimestamp(StoredMessage &sm) {
{
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true); uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
if (nowSecs) { if (nowSecs) {
sm.timestamp = nowSecs; sm.timestamp = nowSecs;
@@ -72,39 +68,29 @@ static inline void assignTimestamp(StoredMessage &sm)
} }
// Generic push with cap (used by live + persisted queues) // Generic push with cap (used by live + persisted queues)
template <typename T> static inline void pushWithLimit(std::deque<T> &queue, const T &msg) template <typename T> static inline void pushWithLimit(std::deque<T> &queue, const T &msg) {
{
if (queue.size() >= MAX_MESSAGES_SAVED) if (queue.size() >= MAX_MESSAGES_SAVED)
queue.pop_front(); queue.pop_front();
queue.push_back(msg); queue.push_back(msg);
} }
template <typename T> static inline void pushWithLimit(std::deque<T> &queue, T &&msg) template <typename T> static inline void pushWithLimit(std::deque<T> &queue, T &&msg) {
{
if (queue.size() >= MAX_MESSAGES_SAVED) if (queue.size() >= MAX_MESSAGES_SAVED)
queue.pop_front(); queue.pop_front();
queue.emplace_back(std::move(msg)); queue.emplace_back(std::move(msg));
} }
MessageStore::MessageStore(const std::string &label) MessageStore::MessageStore(const std::string &label) {
{
filename = "/Messages_" + label + ".msgs"; filename = "/Messages_" + label + ".msgs";
resetMessagePool(); // initialize text pool on boot resetMessagePool(); // initialize text pool on boot
} }
// Live message handling (RAM only) // Live message handling (RAM only)
void MessageStore::addLiveMessage(StoredMessage &&msg) void MessageStore::addLiveMessage(StoredMessage &&msg) { pushWithLimit(liveMessages, std::move(msg)); }
{ void MessageStore::addLiveMessage(const StoredMessage &msg) { pushWithLimit(liveMessages, msg); }
pushWithLimit(liveMessages, std::move(msg));
}
void MessageStore::addLiveMessage(const StoredMessage &msg)
{
pushWithLimit(liveMessages, msg);
}
// Add from incoming/outgoing packet // Add from incoming/outgoing packet
const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet) const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet) {
{
StoredMessage sm; StoredMessage sm;
assignTimestamp(sm); assignTimestamp(sm);
sm.channelIndex = packet.channel; sm.channelIndex = packet.channel;
@@ -135,8 +121,7 @@ const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &pa
} }
// Outgoing/manual message // Outgoing/manual message
void MessageStore::addFromString(uint32_t sender, uint8_t channelIndex, const std::string &text) void MessageStore::addFromString(uint32_t sender, uint8_t channelIndex, const std::string &text) {
{
StoredMessage sm; StoredMessage sm;
// Always use our local time (helper handles RTC vs boot time) // Always use our local time (helper handles RTC vs boot time)
@@ -173,8 +158,7 @@ struct __attribute__((packed)) StoredMessageRecord {
}; };
// Serialize one StoredMessage to flash // Serialize one StoredMessage to flash
static inline void writeMessageRecord(SafeFile &f, const StoredMessage &m) static inline void writeMessageRecord(SafeFile &f, const StoredMessage &m) {
{
StoredMessageRecord rec = {}; StoredMessageRecord rec = {};
rec.timestamp = m.timestamp; rec.timestamp = m.timestamp;
rec.sender = m.sender; rec.sender = m.sender;
@@ -194,8 +178,7 @@ static inline void writeMessageRecord(SafeFile &f, const StoredMessage &m)
} }
// Deserialize one StoredMessage from flash; returns false on short read // Deserialize one StoredMessage from flash; returns false on short read
static inline bool readMessageRecord(File &f, StoredMessage &m) static inline bool readMessageRecord(File &f, StoredMessage &m) {
{
StoredMessageRecord rec = {}; StoredMessageRecord rec = {};
if (f.readBytes(reinterpret_cast<char *>(&rec), sizeof(rec)) != sizeof(rec)) if (f.readBytes(reinterpret_cast<char *>(&rec), sizeof(rec)) != sizeof(rec))
return false; return false;
@@ -216,8 +199,7 @@ static inline bool readMessageRecord(File &f, StoredMessage &m)
return true; return true;
} }
void MessageStore::saveToFlash() void MessageStore::saveToFlash() {
{
#ifdef FSCom #ifdef FSCom
// Ensure root exists // Ensure root exists
spiLock->lock(); spiLock->lock();
@@ -241,8 +223,7 @@ void MessageStore::saveToFlash()
#endif #endif
} }
void MessageStore::loadFromFlash() void MessageStore::loadFromFlash() {
{
std::deque<StoredMessage>().swap(liveMessages); std::deque<StoredMessage>().swap(liveMessages);
resetMessagePool(); // reset pool when loading resetMessagePool(); // reset pool when loading
@@ -279,8 +260,7 @@ void MessageStore::loadFromFlash() {}
#endif #endif
// Clear all messages (RAM + persisted queue) // Clear all messages (RAM + persisted queue)
void MessageStore::clearAllMessages() void MessageStore::clearAllMessages() {
{
std::deque<StoredMessage>().swap(liveMessages); std::deque<StoredMessage>().swap(liveMessages);
resetMessagePool(); resetMessagePool();
@@ -293,8 +273,7 @@ void MessageStore::clearAllMessages()
} }
// Internal helper: erase first or last message matching a predicate // Internal helper: erase first or last message matching a predicate
template <typename Predicate> static void eraseIf(std::deque<StoredMessage> &deque, Predicate pred, bool fromBack = false) template <typename Predicate> static void eraseIf(std::deque<StoredMessage> &deque, Predicate pred, bool fromBack = false) {
{
if (fromBack) { if (fromBack) {
// Iterate from the back and erase all matches from the end // Iterate from the back and erase all matches from the end
for (auto it = deque.rbegin(); it != deque.rend();) { for (auto it = deque.rbegin(); it != deque.rend();) {
@@ -317,29 +296,25 @@ template <typename Predicate> static void eraseIf(std::deque<StoredMessage> &deq
} }
// Delete oldest message (RAM + persisted queue) // Delete oldest message (RAM + persisted queue)
void MessageStore::deleteOldestMessage() void MessageStore::deleteOldestMessage() {
{
eraseIf(liveMessages, [](StoredMessage &) { return true; }); eraseIf(liveMessages, [](StoredMessage &) { return true; });
saveToFlash(); saveToFlash();
} }
// Delete oldest message in a specific channel // Delete oldest message in a specific channel
void MessageStore::deleteOldestMessageInChannel(uint8_t channel) void MessageStore::deleteOldestMessageInChannel(uint8_t channel) {
{
auto pred = [channel](const StoredMessage &m) { return m.type == MessageType::BROADCAST && m.channelIndex == channel; }; auto pred = [channel](const StoredMessage &m) { return m.type == MessageType::BROADCAST && m.channelIndex == channel; };
eraseIf(liveMessages, pred); eraseIf(liveMessages, pred);
saveToFlash(); saveToFlash();
} }
void MessageStore::deleteAllMessagesInChannel(uint8_t channel) void MessageStore::deleteAllMessagesInChannel(uint8_t channel) {
{
auto pred = [channel](const StoredMessage &m) { return m.type == MessageType::BROADCAST && m.channelIndex == channel; }; auto pred = [channel](const StoredMessage &m) { return m.type == MessageType::BROADCAST && m.channelIndex == channel; };
eraseIf(liveMessages, pred, false /* delete ALL, not just first */); eraseIf(liveMessages, pred, false /* delete ALL, not just first */);
saveToFlash(); saveToFlash();
} }
void MessageStore::deleteAllMessagesWithPeer(uint32_t peer) void MessageStore::deleteAllMessagesWithPeer(uint32_t peer) {
{
uint32_t local = nodeDB->getNodeNum(); uint32_t local = nodeDB->getNodeNum();
auto pred = [&](const StoredMessage &m) { auto pred = [&](const StoredMessage &m) {
if (m.type != MessageType::DM_TO_US) if (m.type != MessageType::DM_TO_US)
@@ -352,8 +327,7 @@ void MessageStore::deleteAllMessagesWithPeer(uint32_t peer)
} }
// Delete oldest message in a direct chat with a node // Delete oldest message in a direct chat with a node
void MessageStore::deleteOldestMessageWithPeer(uint32_t peer) void MessageStore::deleteOldestMessageWithPeer(uint32_t peer) {
{
auto pred = [peer](const StoredMessage &m) { auto pred = [peer](const StoredMessage &m) {
if (m.type != MessageType::DM_TO_US) if (m.type != MessageType::DM_TO_US)
return false; return false;
@@ -364,8 +338,7 @@ void MessageStore::deleteOldestMessageWithPeer(uint32_t peer)
saveToFlash(); saveToFlash();
} }
std::deque<StoredMessage> MessageStore::getChannelMessages(uint8_t channel) const std::deque<StoredMessage> MessageStore::getChannelMessages(uint8_t channel) const {
{
std::deque<StoredMessage> result; std::deque<StoredMessage> result;
for (const auto &m : liveMessages) { for (const auto &m : liveMessages) {
if (m.type == MessageType::BROADCAST && m.channelIndex == channel) { if (m.type == MessageType::BROADCAST && m.channelIndex == channel) {
@@ -375,8 +348,7 @@ std::deque<StoredMessage> MessageStore::getChannelMessages(uint8_t channel) cons
return result; return result;
} }
std::deque<StoredMessage> MessageStore::getDirectMessages() const std::deque<StoredMessage> MessageStore::getDirectMessages() const {
{
std::deque<StoredMessage> result; std::deque<StoredMessage> result;
for (const auto &m : liveMessages) { for (const auto &m : liveMessages) {
if (m.type == MessageType::DM_TO_US) { if (m.type == MessageType::DM_TO_US) {
@@ -389,8 +361,7 @@ std::deque<StoredMessage> MessageStore::getDirectMessages() const
// Upgrade boot-relative timestamps once RTC is valid // Upgrade boot-relative timestamps once RTC is valid
// Only same-boot boot-relative messages are healed. // Only same-boot boot-relative messages are healed.
// Persisted boot-relative messages from old boots stay ??? forever. // Persisted boot-relative messages from old boots stay ??? forever.
void MessageStore::upgradeBootRelativeTimestamps() void MessageStore::upgradeBootRelativeTimestamps() {
{
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true); uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
if (nowSecs == 0) if (nowSecs == 0)
return; // Still no valid RTC return; // Still no valid RTC
@@ -409,14 +380,12 @@ void MessageStore::upgradeBootRelativeTimestamps()
fix(liveMessages); fix(liveMessages);
} }
const char *MessageStore::getText(const StoredMessage &msg) const char *MessageStore::getText(const StoredMessage &msg) {
{
// Wrapper around the internal helper // Wrapper around the internal helper
return getTextFromPool(msg.textOffset); return getTextFromPool(msg.textOffset);
} }
uint16_t MessageStore::storeText(const char *src, size_t len) uint16_t MessageStore::storeText(const char *src, size_t len) {
{
// Wrapper around the internal helper // Wrapper around the internal helper
return storeTextInPool(src, len); return storeTextInPool(src, len);
} }
+5 -8
View File
@@ -67,15 +67,12 @@ struct StoredMessage {
// Default constructor initializes all fields safely // Default constructor initializes all fields safely
StoredMessage() StoredMessage()
: timestamp(0), sender(0), channelIndex(0), dest(0xffffffff), type(MessageType::BROADCAST), isBootRelative(false), : timestamp(0), sender(0), channelIndex(0), dest(0xffffffff), type(MessageType::BROADCAST), isBootRelative(false), ackStatus(AckStatus::NONE),
ackStatus(AckStatus::NONE), textOffset(0), textLength(0) textOffset(0), textLength(0) {}
{
}
}; };
class MessageStore class MessageStore {
{ public:
public:
explicit MessageStore(const std::string &label); explicit MessageStore(const std::string &label);
// Live RAM methods (always current, used by UI and runtime) // Live RAM methods (always current, used by UI and runtime)
@@ -120,7 +117,7 @@ class MessageStore
// Used when loading from flash to rebuild the text pool // Used when loading from flash to rebuild the text pool
static uint16_t rebuildTextFromFlash(const char *src, size_t len); static uint16_t rebuildTextFromFlash(const char *src, size_t len);
private: private:
std::deque<StoredMessage> liveMessages; // Single in-RAM message buffer (also used for persistence) std::deque<StoredMessage> liveMessages; // Single in-RAM message buffer (also used for persistence)
std::string filename; // Flash filename for persistence std::string filename; // Flash filename for persistence
}; };
+8 -16
View File
@@ -3,28 +3,24 @@
#include "configuration.h" #include "configuration.h"
#include <Arduino.h> #include <Arduino.h>
namespace meshtastic namespace meshtastic {
{
/// Describes the state of the NodeDB system. /// Describes the state of the NodeDB system.
class NodeStatus : public Status class NodeStatus : public Status {
{
private: private:
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver = CallbackObserver<NodeStatus, const NodeStatus *> statusObserver = CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
uint16_t numOnline = 0; uint16_t numOnline = 0;
uint16_t numTotal = 0; uint16_t numTotal = 0;
uint16_t lastNumTotal = 0; uint16_t lastNumTotal = 0;
public: public:
bool forceUpdate = false; bool forceUpdate = false;
NodeStatus() { statusType = STATUS_TYPE_NODE; } NodeStatus() { statusType = STATUS_TYPE_NODE; }
NodeStatus(uint16_t numOnline, uint16_t numTotal, bool forceUpdate = false) : Status() NodeStatus(uint16_t numOnline, uint16_t numTotal, bool forceUpdate = false) : Status() {
{
this->forceUpdate = forceUpdate; this->forceUpdate = forceUpdate;
this->numOnline = numOnline; this->numOnline = numOnline;
this->numTotal = numTotal; this->numTotal = numTotal;
@@ -40,12 +36,8 @@ class NodeStatus : public Status
uint16_t getLastNumTotal() const { return lastNumTotal; } uint16_t getLastNumTotal() const { return lastNumTotal; }
bool matches(const NodeStatus *newStatus) const bool matches(const NodeStatus *newStatus) const { return (newStatus->getNumOnline() != numOnline || newStatus->getNumTotal() != numTotal); }
{ int updateStatus(const NodeStatus *newStatus) {
return (newStatus->getNumOnline() != numOnline || newStatus->getNumTotal() != numTotal);
}
int updateStatus(const NodeStatus *newStatus)
{
// Only update the status if values have actually changed // Only update the status if values have actually changed
lastNumTotal = numTotal; lastNumTotal = numTotal;
bool isDirty; bool isDirty;
+18 -27
View File
@@ -8,11 +8,10 @@ template <class T> class Observable;
/** /**
* An observer which can be mixed in as a baseclass. Implement onNotify as a method in your class. * An observer which can be mixed in as a baseclass. Implement onNotify as a method in your class.
*/ */
template <class T> class Observer template <class T> class Observer {
{
std::list<Observable<T> *> observables; std::list<Observable<T> *> observables;
public: public:
virtual ~Observer(); virtual ~Observer();
/// Stop watching the observable /// Stop watching the observable
@@ -21,10 +20,10 @@ template <class T> class Observer
/// Start watching a specified observable /// Start watching a specified observable
void observe(Observable<T> *o); void observe(Observable<T> *o);
private: private:
friend class Observable<T>; friend class Observable<T>;
protected: protected:
/** /**
* returns 0 if other observers should continue to be called * returns 0 if other observers should continue to be called
* returns !0 if the observe calls should be aborted and this result code returned for notifyObservers * returns !0 if the observe calls should be aborted and this result code returned for notifyObservers
@@ -35,38 +34,34 @@ template <class T> class Observer
/** /**
* An observer that calls an arbitrary method * An observer that calls an arbitrary method
*/ */
template <class Callback, class T> class CallbackObserver : public Observer<T> template <class Callback, class T> class CallbackObserver : public Observer<T> {
{
typedef int (Callback::*ObserverCallback)(T arg); typedef int (Callback::*ObserverCallback)(T arg);
Callback *objPtr; Callback *objPtr;
ObserverCallback method; ObserverCallback method;
public: public:
CallbackObserver(Callback *_objPtr, ObserverCallback _method) : objPtr(_objPtr), method(_method) {} CallbackObserver(Callback *_objPtr, ObserverCallback _method) : objPtr(_objPtr), method(_method) {}
protected: protected:
virtual int onNotify(T arg) override { return (objPtr->*method)(arg); } virtual int onNotify(T arg) override { return (objPtr->*method)(arg); }
}; };
/** /**
* An observable class that will notify observers anytime notifyObservers is called. Argument type T can be any type, but for * An observable class that will notify observers anytime notifyObservers is called. Argument type T can be any type,
* performance reasons a pointer or word sized object is recommended. * but for performance reasons a pointer or word sized object is recommended.
*/ */
template <class T> class Observable template <class T> class Observable {
{
std::list<Observer<T> *> observers; std::list<Observer<T> *> observers;
public: public:
/** /**
* Tell all observers about a change, observers can process arg as they wish * Tell all observers about a change, observers can process arg as they wish
* *
* returns !0 if an observer chose to abort processing by returning this code * returns !0 if an observer chose to abort processing by returning this code
*/ */
int notifyObservers(T arg) int notifyObservers(T arg) {
{ for (typename std::list<Observer<T> *>::const_iterator iterator = observers.begin(); iterator != observers.end(); ++iterator) {
for (typename std::list<Observer<T> *>::const_iterator iterator = observers.begin(); iterator != observers.end();
++iterator) {
int result = (*iterator)->onNotify(arg); int result = (*iterator)->onNotify(arg);
if (result != 0) if (result != 0)
return result; return result;
@@ -75,7 +70,7 @@ template <class T> class Observable
return 0; return 0;
} }
private: private:
friend class Observer<T>; friend class Observer<T>;
// Not called directly, instead call observer.observe // Not called directly, instead call observer.observe
@@ -84,23 +79,19 @@ template <class T> class Observable
void removeObserver(Observer<T> *o) { observers.remove(o); } void removeObserver(Observer<T> *o) { observers.remove(o); }
}; };
template <class T> Observer<T>::~Observer() template <class T> Observer<T>::~Observer() {
{ for (typename std::list<Observable<T> *>::const_iterator iterator = observables.begin(); iterator != observables.end(); ++iterator) {
for (typename std::list<Observable<T> *>::const_iterator iterator = observables.begin(); iterator != observables.end();
++iterator) {
(*iterator)->removeObserver(this); (*iterator)->removeObserver(this);
} }
observables.clear(); observables.clear();
} }
template <class T> void Observer<T>::unobserve(Observable<T> *o) template <class T> void Observer<T>::unobserve(Observable<T> *o) {
{
o->removeObserver(this); o->removeObserver(this);
observables.remove(o); observables.remove(o);
} }
template <class T> void Observer<T>::observe(Observable<T> *o) template <class T> void Observer<T>::observe(Observable<T> *o) {
{
observables.push_back(o); observables.push_back(o);
o->addObserver(this); o->addObserver(this);
} }
+90 -157
View File
@@ -1,11 +1,11 @@
/** /**
* @file Power.cpp * @file Power.cpp
* @brief This file contains the implementation of the Power class, which is responsible for managing power-related functionality * @brief This file contains the implementation of the Power class, which is responsible for managing power-related
* of the device. It includes battery level sensing, power management unit (PMU) control, and power state machine management. The * functionality of the device. It includes battery level sensing, power management unit (PMU) control, and power state
* Power class is used by the main device class to manage power-related functionality. * machine management. The Power class is used by the main device class to manage power-related functionality.
* *
* The file also includes implementations of various battery level sensors, such as the AnalogBatteryLevel class, which assumes * The file also includes implementations of various battery level sensors, such as the AnalogBatteryLevel class, which
* the battery voltage is attached via a voltage-divider to an analog input. * assumes the battery voltage is attached via a voltage-divider to an analog input.
* *
* This file is part of the Meshtastic project. * This file is part of the Meshtastic project.
* For more information, see: https://meshtastic.org/ * For more information, see: https://meshtastic.org/
@@ -142,9 +142,8 @@ XPowersLibInterface *PMU = NULL;
// Copy of the base class defined in axp20x.h. // Copy of the base class defined in axp20x.h.
// I'd rather not include axp20x.h as it brings Wire dependency. // I'd rather not include axp20x.h as it brings Wire dependency.
class HasBatteryLevel class HasBatteryLevel {
{ public:
public:
/** /**
* Battery state of charge, from 0 to 100 or -1 for unknown * Battery state of charge, from 0 to 100 or -1 for unknown
*/ */
@@ -195,8 +194,7 @@ static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level se
#ifdef BATTERY_PIN #ifdef BATTERY_PIN
void battery_adcEnable() void battery_adcEnable() {
{
#ifdef ADC_CTRL // enable adc voltage divider when we need to read #ifdef ADC_CTRL // enable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP #ifdef ADC_USE_PULLUP
pinMode(ADC_CTRL, INPUT_PULLUP); pinMode(ADC_CTRL, INPUT_PULLUP);
@@ -215,8 +213,7 @@ void battery_adcEnable()
#endif #endif
} }
static void battery_adcDisable() static void battery_adcDisable() {
{
#ifdef ADC_CTRL // disable adc voltage divider when we need to read #ifdef ADC_CTRL // disable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP #ifdef ADC_USE_PULLUP
pinMode(ADC_CTRL, INPUT_PULLDOWN); pinMode(ADC_CTRL, INPUT_PULLDOWN);
@@ -235,14 +232,12 @@ static void battery_adcDisable()
/** /**
* A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input * A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input
*/ */
class AnalogBatteryLevel : public HasBatteryLevel class AnalogBatteryLevel : public HasBatteryLevel {
{ public:
public:
/** /**
* Battery state of charge, from 0 to 100 or -1 for unknown * Battery state of charge, from 0 to 100 or -1 for unknown
*/ */
virtual int getBatteryPercent() override virtual int getBatteryPercent() override {
{
#if defined(HAS_RAKPROT) && !defined(HAS_PMU) #if defined(HAS_RAKPROT) && !defined(HAS_PMU)
if (hasRAK()) { if (hasRAK()) {
return rak9154Sensor.getBusBatteryPercent(); return rak9154Sensor.getBusBatteryPercent();
@@ -273,8 +268,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
battery_SOC = 100.0; // 100% full battery_SOC = 100.0; // 100% full
} else { } else {
// interpolate between OCV[i] and OCV[i-1] // interpolate between OCV[i] and OCV[i-1]
battery_SOC = (float)100.0 / (NUM_OCV_POINTS - 1.0) * battery_SOC = (float)100.0 / (NUM_OCV_POINTS - 1.0) * (NUM_OCV_POINTS - 1.0 - i + ((float)voltage - OCV[i]) / (OCV[i - 1] - OCV[i]));
(NUM_OCV_POINTS - 1.0 - i + ((float)voltage - OCV[i]) / (OCV[i - 1] - OCV[i]));
} }
break; break;
} }
@@ -290,8 +284,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
/** /**
* The raw voltage of the batteryin millivolts or NAN if unknown * The raw voltage of the batteryin millivolts or NAN if unknown
*/ */
virtual uint16_t getBattVoltage() override virtual uint16_t getBattVoltage() override {
{
#if HAS_TELEMETRY && defined(HAS_RAKPROT) && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR #if HAS_TELEMETRY && defined(HAS_RAKPROT) && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
if (hasRAK()) { if (hasRAK()) {
@@ -310,14 +303,12 @@ class AnalogBatteryLevel : public HasBatteryLevel
#endif #endif
#ifndef BATTERY_SENSE_SAMPLES #ifndef BATTERY_SENSE_SAMPLES
#define BATTERY_SENSE_SAMPLES \ #define BATTERY_SENSE_SAMPLES 15 // Set the number of samples, it has an effect of increasing sensitivity in complex electromagnetic environment.
15 // Set the number of samples, it has an effect of increasing sensitivity in complex electromagnetic environment.
#endif #endif
#ifdef BATTERY_PIN #ifdef BATTERY_PIN
// Override variant or default ADC_MULTIPLIER if we have the override pref // Override variant or default ADC_MULTIPLIER if we have the override pref
float operativeAdcMultiplier = float operativeAdcMultiplier = config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER;
config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER;
// Do not call analogRead() often. // Do not call analogRead() often.
const uint32_t min_read_interval = 5000; const uint32_t min_read_interval = 5000;
if (!initial_read_done || !Throttle::isWithinTimespanMs(last_read_time_ms, min_read_interval)) { if (!initial_read_done || !Throttle::isWithinTimespanMs(last_read_time_ms, min_read_interval)) {
@@ -362,8 +353,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
/** /**
* ESP32 specific function for getting calibrated ADC reads * ESP32 specific function for getting calibrated ADC reads
*/ */
uint32_t espAdcRead() uint32_t espAdcRead() {
{
uint32_t raw = 0; uint32_t raw = 0;
uint8_t raw_c = 0; // raw reading counter uint8_t raw_c = 0; // raw reading counter
@@ -424,8 +414,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
#ifdef BATTERY_IMMUTABLE #ifdef BATTERY_IMMUTABLE
virtual bool isBatteryConnect() override { return true; } virtual bool isBatteryConnect() override { return true; }
#elif defined(ADC_V) #elif defined(ADC_V)
virtual bool isBatteryConnect() override virtual bool isBatteryConnect() override {
{
int lastReading = digitalRead(ADC_V); int lastReading = digitalRead(ADC_V);
// 判断值是否变化 // 判断值是否变化
for (int i = 2; i < 500; i++) { for (int i = 2; i < 500; i++) {
@@ -445,8 +434,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
/// in power /// in power
/// On some boards we don't have the power management chip (like AXPxxxx) /// On some boards we don't have the power management chip (like AXPxxxx)
/// so we use EXT_PWR_DETECT GPIO pin to detect external power source /// so we use EXT_PWR_DETECT GPIO pin to detect external power source
virtual bool isVbusIn() override virtual bool isVbusIn() override {
{
#ifdef EXT_PWR_DETECT #ifdef EXT_PWR_DETECT
#if defined(HELTEC_CAPSULE_SENSOR_V3) || defined(HELTEC_SENSOR_HUB) #if defined(HELTEC_CAPSULE_SENSOR_V3) || defined(HELTEC_SENSOR_HUB)
// if external powered that pin will be pulled down // if external powered that pin will be pulled down
@@ -469,8 +457,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
/// Assume charging if we have a battery and external power is connected. /// Assume charging if we have a battery and external power is connected.
/// we can't be smart enough to say 'full'? /// we can't be smart enough to say 'full'?
virtual bool isCharging() override virtual bool isCharging() override {
{
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) && !defined(HAS_PMU) #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) && !defined(HAS_PMU)
if (hasRAK()) { if (hasRAK()) {
return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse; return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse;
@@ -499,7 +486,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
return isVbusIn(); return isVbusIn();
} }
private: private:
/// If we see a battery voltage higher than physics allows - assume charger is pumping /// If we see a battery voltage higher than physics allows - assume charger is pumping
/// in power /// in power
@@ -519,8 +506,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
uint16_t getRAKVoltage() { return rak9154Sensor.getBusVoltageMv(); } uint16_t getRAKVoltage() { return rak9154Sensor.getBusVoltageMv(); }
bool hasRAK() bool hasRAK() {
{
if (!rak9154Sensor.isInitialized()) if (!rak9154Sensor.isInitialized())
return rak9154Sensor.runOnce() > 0; return rak9154Sensor.runOnce() > 0;
return rak9154Sensor.isRunning(); return rak9154Sensor.isRunning();
@@ -528,39 +514,31 @@ class AnalogBatteryLevel : public HasBatteryLevel
#endif #endif
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
uint16_t getINAVoltage() uint16_t getINAVoltage() {
{
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) { if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
return ina219Sensor.getBusVoltageMv(); return ina219Sensor.getBusVoltageMv();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == config.power.device_battery_ina_address) {
config.power.device_battery_ina_address) {
return ina226Sensor.getBusVoltageMv(); return ina226Sensor.getBusVoltageMv();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == config.power.device_battery_ina_address) {
config.power.device_battery_ina_address) {
return ina260Sensor.getBusVoltageMv(); return ina260Sensor.getBusVoltageMv();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == config.power.device_battery_ina_address) {
config.power.device_battery_ina_address) {
return ina3221Sensor.getBusVoltageMv(); return ina3221Sensor.getBusVoltageMv();
} }
return 0; return 0;
} }
int16_t getINACurrent() int16_t getINACurrent() {
{
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) { if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
return ina219Sensor.getCurrentMa(); return ina219Sensor.getCurrentMa();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == config.power.device_battery_ina_address) {
config.power.device_battery_ina_address) {
return ina226Sensor.getCurrentMa(); return ina226Sensor.getCurrentMa();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == config.power.device_battery_ina_address) {
config.power.device_battery_ina_address) {
return ina3221Sensor.getCurrentMa(); return ina3221Sensor.getCurrentMa();
} }
return 0; return 0;
} }
bool hasINA() bool hasINA() {
{
if (!config.power.device_battery_ina_address) { if (!config.power.device_battery_ina_address) {
return false; return false;
} }
@@ -568,18 +546,15 @@ class AnalogBatteryLevel : public HasBatteryLevel
if (!ina219Sensor.isInitialized()) if (!ina219Sensor.isInitialized())
return ina219Sensor.runOnce() > 0; return ina219Sensor.runOnce() > 0;
return ina219Sensor.isRunning(); return ina219Sensor.isRunning();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == config.power.device_battery_ina_address) {
config.power.device_battery_ina_address) {
if (!ina226Sensor.isInitialized()) if (!ina226Sensor.isInitialized())
return ina226Sensor.runOnce() > 0; return ina226Sensor.runOnce() > 0;
return ina226Sensor.isRunning(); return ina226Sensor.isRunning();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == config.power.device_battery_ina_address) {
config.power.device_battery_ina_address) {
if (!ina260Sensor.isInitialized()) if (!ina260Sensor.isInitialized())
return ina260Sensor.runOnce() > 0; return ina260Sensor.runOnce() > 0;
return ina260Sensor.isRunning(); return ina260Sensor.isRunning();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == config.power.device_battery_ina_address) {
config.power.device_battery_ina_address) {
if (!ina3221Sensor.isInitialized()) if (!ina3221Sensor.isInitialized())
return ina3221Sensor.runOnce() > 0; return ina3221Sensor.runOnce() > 0;
return ina3221Sensor.isRunning(); return ina3221Sensor.isRunning();
@@ -591,8 +566,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
static AnalogBatteryLevel analogLevel; static AnalogBatteryLevel analogLevel;
Power::Power() : OSThread("Power") Power::Power() : OSThread("Power") {
{
statusHandler = {}; statusHandler = {};
low_voltage_counter = 0; low_voltage_counter = 0;
#ifdef DEBUG_HEAP #ifdef DEBUG_HEAP
@@ -600,8 +574,7 @@ Power::Power() : OSThread("Power")
#endif #endif
} }
bool Power::analogInit() bool Power::analogInit() {
{
#ifdef EXT_PWR_DETECT #ifdef EXT_PWR_DETECT
#if defined(HELTEC_CAPSULE_SENSOR_V3) || defined(HELTEC_SENSOR_HUB) #if defined(HELTEC_CAPSULE_SENSOR_V3) || defined(HELTEC_SENSOR_HUB)
pinMode(EXT_PWR_DETECT, INPUT_PULLUP); pinMode(EXT_PWR_DETECT, INPUT_PULLUP);
@@ -684,8 +657,7 @@ bool Power::analogInit()
* *
* @return true if the setup was successful, false otherwise. * @return true if the setup was successful, false otherwise.
*/ */
bool Power::setup() bool Power::setup() {
{
bool found = false; bool found = false;
if (axpChipInit()) { if (axpChipInit()) {
found = true; found = true;
@@ -726,8 +698,7 @@ bool Power::setup()
return found; return found;
} }
void Power::powerCommandsCheck() void Power::powerCommandsCheck() {
{
if (rebootAtMsec && millis() > rebootAtMsec) { if (rebootAtMsec && millis() > rebootAtMsec) {
LOG_INFO("Rebooting"); LOG_INFO("Rebooting");
reboot(); reboot();
@@ -739,8 +710,7 @@ void Power::powerCommandsCheck()
} }
} }
void Power::reboot() void Power::reboot() {
{
notifyReboot.notifyObservers(NULL); notifyReboot.notifyObservers(NULL);
#if defined(ARCH_ESP32) #if defined(ARCH_ESP32)
ESP.restart(); ESP.restart();
@@ -769,15 +739,15 @@ void Power::reboot()
#endif #endif
} }
void Power::shutdown() void Power::shutdown() {
{
#if HAS_SCREEN #if HAS_SCREEN
if (screen) { if (screen) {
#ifdef T_DECK_PRO #ifdef T_DECK_PRO
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
#elif defined(USE_EINK) #elif defined(USE_EINK)
screen->showSimpleBanner("Shutting Down...", 2250); // dismiss after 3 seconds to avoid the banner on the sleep screen screen->showSimpleBanner("Shutting Down...",
2250); // dismiss after 3 seconds to avoid the banner on the sleep screen
#else #else
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
#endif #endif
@@ -811,8 +781,7 @@ void Power::shutdown()
/// Reads power status to powerStatus singleton. /// Reads power status to powerStatus singleton.
// //
// TODO(girts): move this and other axp stuff to power.h/power.cpp. // TODO(girts): move this and other axp stuff to power.h/power.cpp.
void Power::readPowerStatus() void Power::readPowerStatus() {
{
int32_t batteryVoltageMv = -1; // Assume unknown int32_t batteryVoltageMv = -1; // Assume unknown
int8_t batteryChargePercent = -1; int8_t batteryChargePercent = -1;
OptionalBool usbPowered = OptUnknown; OptionalBool usbPowered = OptUnknown;
@@ -839,12 +808,11 @@ void Power::readPowerStatus()
} }
} }
// FIXME: IMO we shouldn't be littering our code with all these ifdefs. Way better instead to make a Nrf52IsUsbPowered subclass // FIXME: IMO we shouldn't be littering our code with all these ifdefs. Way better instead to make a Nrf52IsUsbPowered
// (which shares a superclass with the BatteryLevel stuff) // subclass (which shares a superclass with the BatteryLevel stuff) that just provides a few methods. But in the
// that just provides a few methods. But in the interest of fixing this bug I'm going to follow current // interest of fixing this bug I'm going to follow current practice.
// practice. #ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so
#ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so to detect // to detect changes.
// changes.
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get(); nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
// LOG_DEBUG("NRF Power %d", nrf_usb_state); // LOG_DEBUG("NRF Power %d", nrf_usb_state);
@@ -861,8 +829,8 @@ void Power::readPowerStatus()
// Notify any status instances that are observing us // Notify any status instances that are observing us
const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isChargingNow, batteryVoltageMv, batteryChargePercent); const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isChargingNow, batteryVoltageMv, batteryChargePercent);
if (millis() > lastLogTime + 50 * 1000) { if (millis() > lastLogTime + 50 * 1000) {
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(), LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(), powerStatus2.getIsCharging(),
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
lastLogTime = millis(); lastLogTime = millis();
} }
newStatus.notifyObservers(&powerStatus2); newStatus.notifyObservers(&powerStatus2);
@@ -887,8 +855,8 @@ void Power::readPowerStatus()
} }
} }
LOG_HEAP(threadlist); LOG_HEAP(threadlist);
LOG_HEAP("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(), LOG_HEAP("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreeHeap() - lastheap,
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false)); running, concurrency::mainController.size(false));
lastheap = memGet.getFreeHeap(); lastheap = memGet.getFreeHeap();
} }
#ifdef DEBUG_HEAP_MQTT #ifdef DEBUG_HEAP_MQTT
@@ -936,8 +904,7 @@ void Power::readPowerStatus()
} }
} }
int32_t Power::runOnce() int32_t Power::runOnce() {
{
readPowerStatus(); readPowerStatus();
#ifdef HAS_PMU #ifdef HAS_PMU
@@ -993,13 +960,12 @@ int32_t Power::runOnce()
* *
* axp192 power * axp192 power
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the
axp192 share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this axp192 share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep
on!) LDO1 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of this on!) LDO1 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for
days), can not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS a couple of days), can not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
* *
*/ */
bool Power::axpChipInit() bool Power::axpChipInit() {
{
#ifdef HAS_PMU #ifdef HAS_PMU
@@ -1041,8 +1007,8 @@ bool Power::axpChipInit()
if (!PMU) { if (!PMU) {
/* /*
* In XPowersLib, if the XPowersAXPxxx object is released, Wire.end() will be called at the same time. * In XPowersLib, if the XPowersAXPxxx object is released, Wire.end() will be called at the same time.
* In order not to affect other devices, if the initialization of the PMU fails, Wire needs to be re-initialized once, * In order not to affect other devices, if the initialization of the PMU fails, Wire needs to be re-initialized
* if there are multiple devices sharing the bus. * once, if there are multiple devices sharing the bus.
* * */ * * */
#ifndef PMU_USE_WIRE1 #ifndef PMU_USE_WIRE1
w->begin(I2C_SDA, I2C_SCL); w->begin(I2C_SDA, I2C_SCL);
@@ -1117,8 +1083,7 @@ bool Power::axpChipInit()
// GNSS VDD 3300mV // GNSS VDD 3300mV
PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300); PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
PMU->enablePowerOutput(XPOWERS_ALDO3); PMU->enablePowerOutput(XPOWERS_ALDO3);
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE || } else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE || HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3) {
HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3) {
// t-beam s3 core // t-beam s3 core
/** /**
* gnss module power channel * gnss module power channel
@@ -1191,52 +1156,40 @@ bool Power::axpChipInit()
PMU->enableBattVoltageMeasure(); PMU->enableBattVoltageMeasure();
if (PMU->isChannelAvailable(XPOWERS_DCDC1)) { if (PMU->isChannelAvailable(XPOWERS_DCDC1)) {
LOG_DEBUG("DC1 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-", LOG_DEBUG("DC1 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC1));
PMU->getPowerChannelVoltage(XPOWERS_DCDC1));
} }
if (PMU->isChannelAvailable(XPOWERS_DCDC2)) { if (PMU->isChannelAvailable(XPOWERS_DCDC2)) {
LOG_DEBUG("DC2 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC2) ? "+" : "-", LOG_DEBUG("DC2 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC2));
PMU->getPowerChannelVoltage(XPOWERS_DCDC2));
} }
if (PMU->isChannelAvailable(XPOWERS_DCDC3)) { if (PMU->isChannelAvailable(XPOWERS_DCDC3)) {
LOG_DEBUG("DC3 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC3) ? "+" : "-", LOG_DEBUG("DC3 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC3));
PMU->getPowerChannelVoltage(XPOWERS_DCDC3));
} }
if (PMU->isChannelAvailable(XPOWERS_DCDC4)) { if (PMU->isChannelAvailable(XPOWERS_DCDC4)) {
LOG_DEBUG("DC4 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC4) ? "+" : "-", LOG_DEBUG("DC4 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC4) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC4));
PMU->getPowerChannelVoltage(XPOWERS_DCDC4));
} }
if (PMU->isChannelAvailable(XPOWERS_LDO2)) { if (PMU->isChannelAvailable(XPOWERS_LDO2)) {
LOG_DEBUG("LDO2 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_LDO2) ? "+" : "-", LOG_DEBUG("LDO2 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_LDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_LDO2));
PMU->getPowerChannelVoltage(XPOWERS_LDO2));
} }
if (PMU->isChannelAvailable(XPOWERS_LDO3)) { if (PMU->isChannelAvailable(XPOWERS_LDO3)) {
LOG_DEBUG("LDO3 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_LDO3) ? "+" : "-", LOG_DEBUG("LDO3 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_LDO3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_LDO3));
PMU->getPowerChannelVoltage(XPOWERS_LDO3));
} }
if (PMU->isChannelAvailable(XPOWERS_ALDO1)) { if (PMU->isChannelAvailable(XPOWERS_ALDO1)) {
LOG_DEBUG("ALDO1: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO1) ? "+" : "-", LOG_DEBUG("ALDO1: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO1));
PMU->getPowerChannelVoltage(XPOWERS_ALDO1));
} }
if (PMU->isChannelAvailable(XPOWERS_ALDO2)) { if (PMU->isChannelAvailable(XPOWERS_ALDO2)) {
LOG_DEBUG("ALDO2: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO2) ? "+" : "-", LOG_DEBUG("ALDO2: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO2));
PMU->getPowerChannelVoltage(XPOWERS_ALDO2));
} }
if (PMU->isChannelAvailable(XPOWERS_ALDO3)) { if (PMU->isChannelAvailable(XPOWERS_ALDO3)) {
LOG_DEBUG("ALDO3: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO3) ? "+" : "-", LOG_DEBUG("ALDO3: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO3));
PMU->getPowerChannelVoltage(XPOWERS_ALDO3));
} }
if (PMU->isChannelAvailable(XPOWERS_ALDO4)) { if (PMU->isChannelAvailable(XPOWERS_ALDO4)) {
LOG_DEBUG("ALDO4: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO4) ? "+" : "-", LOG_DEBUG("ALDO4: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO4) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO4));
PMU->getPowerChannelVoltage(XPOWERS_ALDO4));
} }
if (PMU->isChannelAvailable(XPOWERS_BLDO1)) { if (PMU->isChannelAvailable(XPOWERS_BLDO1)) {
LOG_DEBUG("BLDO1: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_BLDO1) ? "+" : "-", LOG_DEBUG("BLDO1: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_BLDO1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO1));
PMU->getPowerChannelVoltage(XPOWERS_BLDO1));
} }
if (PMU->isChannelAvailable(XPOWERS_BLDO2)) { if (PMU->isChannelAvailable(XPOWERS_BLDO2)) {
LOG_DEBUG("BLDO2: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-", LOG_DEBUG("BLDO2: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO2));
PMU->getPowerChannelVoltage(XPOWERS_BLDO2));
} }
// We can safely ignore this approach for most (or all) boards because MCU turned off // We can safely ignore this approach for most (or all) boards because MCU turned off
@@ -1287,17 +1240,15 @@ bool Power::axpChipInit()
/** /**
* Wrapper class for an I2C MAX17048 Lipo battery sensor. * Wrapper class for an I2C MAX17048 Lipo battery sensor.
*/ */
class LipoBatteryLevel : public HasBatteryLevel class LipoBatteryLevel : public HasBatteryLevel {
{ private:
private:
MAX17048Singleton *max17048 = nullptr; MAX17048Singleton *max17048 = nullptr;
public: public:
/** /**
* Init the I2C MAX17048 Lipo battery level sensor * Init the I2C MAX17048 Lipo battery level sensor
*/ */
bool runOnce() bool runOnce() {
{
if (max17048 == nullptr) { if (max17048 == nullptr) {
max17048 = MAX17048Singleton::GetInstance(); max17048 = MAX17048Singleton::GetInstance();
} }
@@ -1340,8 +1291,7 @@ LipoBatteryLevel lipoLevel;
/** /**
* Init the Lipo battery level sensor * Init the Lipo battery level sensor
*/ */
bool Power::lipoInit() bool Power::lipoInit() {
{
bool result = lipoLevel.runOnce(); bool result = lipoLevel.runOnce();
LOG_DEBUG("Power::lipoInit lipo sensor is %s", result ? "ready" : "not ready yet"); LOG_DEBUG("Power::lipoInit lipo sensor is %s", result ? "ready" : "not ready yet");
if (!result) if (!result)
@@ -1354,10 +1304,7 @@ bool Power::lipoInit()
/** /**
* The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel * The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel
*/ */
bool Power::lipoInit() bool Power::lipoInit() { return false; }
{
return false;
}
#endif #endif
#if defined(HAS_PPM) && HAS_PPM #if defined(HAS_PPM) && HAS_PPM
@@ -1365,17 +1312,15 @@ bool Power::lipoInit()
/** /**
* Adapter class for BQ25896/BQ27220 Lipo battery charger. * Adapter class for BQ25896/BQ27220 Lipo battery charger.
*/ */
class LipoCharger : public HasBatteryLevel class LipoCharger : public HasBatteryLevel {
{ private:
private:
BQ27220 *bq = nullptr; BQ27220 *bq = nullptr;
public: public:
/** /**
* Init the I2C BQ25896 Lipo battery charger * Init the I2C BQ25896 Lipo battery charger
*/ */
bool runOnce() bool runOnce() {
{
if (PPM == nullptr) { if (PPM == nullptr) {
PPM = new XPowersPPM; PPM = new XPowersPPM;
bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR); bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR);
@@ -1437,8 +1382,7 @@ class LipoCharger : public HasBatteryLevel
/** /**
* Battery state of charge, from 0 to 100 or -1 for unknown * Battery state of charge, from 0 to 100 or -1 for unknown
*/ */
virtual int getBatteryPercent() override virtual int getBatteryPercent() override {
{
return -1; return -1;
// return bq->getChargePercent(); // don't use BQ27220 for battery percent, it is not calibrated // return bq->getChargePercent(); // don't use BQ27220 for battery percent, it is not calibrated
} }
@@ -1461,8 +1405,7 @@ class LipoCharger : public HasBatteryLevel
/** /**
* return true if the battery is currently charging * return true if the battery is currently charging
*/ */
virtual bool isCharging() override virtual bool isCharging() override {
{
bool isCharging = PPM->isCharging(); bool isCharging = PPM->isCharging();
if (isCharging) { if (isCharging) {
LOG_DEBUG("BQ27220 time to full charge: %d min", bq->getTimeToFull()); LOG_DEBUG("BQ27220 time to full charge: %d min", bq->getTimeToFull());
@@ -1480,8 +1423,7 @@ LipoCharger lipoCharger;
/** /**
* Init the Lipo battery charger * Init the Lipo battery charger
*/ */
bool Power::lipoChargerInit() bool Power::lipoChargerInit() {
{
bool result = lipoCharger.runOnce(); bool result = lipoCharger.runOnce();
LOG_DEBUG("Power::lipoChargerInit lipo sensor is %s", result ? "ready" : "not ready yet"); LOG_DEBUG("Power::lipoChargerInit lipo sensor is %s", result ? "ready" : "not ready yet");
if (!result) if (!result)
@@ -1494,10 +1436,7 @@ bool Power::lipoChargerInit()
/** /**
* The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel * The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel
*/ */
bool Power::lipoChargerInit() bool Power::lipoChargerInit() { return false; }
{
return false;
}
#endif #endif
#ifdef HELTEC_MESH_SOLAR #ifdef HELTEC_MESH_SOLAR
@@ -1506,15 +1445,13 @@ bool Power::lipoChargerInit()
/** /**
* meshSolar class for an SMBUS battery sensor. * meshSolar class for an SMBUS battery sensor.
*/ */
class meshSolarBatteryLevel : public HasBatteryLevel class meshSolarBatteryLevel : public HasBatteryLevel {
{
public: public:
/** /**
* Init the I2C meshSolar battery level sensor * Init the I2C meshSolar battery level sensor
*/ */
bool runOnce() bool runOnce() {
{
meshSolarStart(); meshSolarStart();
return true; return true;
} }
@@ -1550,8 +1487,7 @@ meshSolarBatteryLevel meshSolarLevel;
/** /**
* Init the meshSolar battery level sensor * Init the meshSolar battery level sensor
*/ */
bool Power::meshSolarInit() bool Power::meshSolarInit() {
{
bool result = meshSolarLevel.runOnce(); bool result = meshSolarLevel.runOnce();
LOG_DEBUG("Power::meshSolarInit mesh solar sensor is %s", result ? "ready" : "not ready yet"); LOG_DEBUG("Power::meshSolarInit mesh solar sensor is %s", result ? "ready" : "not ready yet");
if (!result) if (!result)
@@ -1564,8 +1500,5 @@ bool Power::meshSolarInit()
/** /**
* The meshSolar battery level sensor is unavailable - default to AnalogBatteryLevel * The meshSolar battery level sensor is unavailable - default to AnalogBatteryLevel
*/ */
bool Power::meshSolarInit() bool Power::meshSolarInit() { return false; }
{
return false;
}
#endif #endif
+38 -64
View File
@@ -31,8 +31,7 @@ FakeFsm powerFSM;
void PowerFSM_setup(){}; void PowerFSM_setup(){};
#else #else
/// Should we behave as if we have AC power now? /// Should we behave as if we have AC power now?
static bool isPowered() static bool isPowered() {
{
// Circumvent the battery sensing logic and assumes constant power if no battery pin or power mgmt IC // Circumvent the battery sensing logic and assumes constant power if no battery pin or power mgmt IC
#if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101) && !defined(NRF_APM) #if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101) && !defined(NRF_APM)
return true; return true;
@@ -45,32 +44,30 @@ static bool isPowered()
bool isPowerSavingMode = config.power.is_power_saving || isRouter; bool isPowerSavingMode = config.power.is_power_saving || isRouter;
/* To determine if we're externally powered, assumptions /* To determine if we're externally powered, assumptions
1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead otherwise) 1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead
otherwise)
2) If we detect USB power from the power management chip, we must be getting power externally. 2) If we detect USB power from the power management chip, we must be getting power externally.
3) On some boards we don't have the power management chip (like AXPxxxx) so we use EXT_PWR_DETECT GPIO pin to detect 3) On some boards we don't have the power management chip (like AXPxxxx) so we use EXT_PWR_DETECT GPIO pin to
external power source (see `isVbusIn()` in `Power.cpp`) detect external power source (see `isVbusIn()` in `Power.cpp`)
*/ */
return !isPowerSavingMode && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB()); return !isPowerSavingMode && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB());
} }
static void sdsEnter() static void sdsEnter() {
{
LOG_POWERFSM("State: SDS"); LOG_POWERFSM("State: SDS");
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw // FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, false); doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, false);
} }
static void lowBattSDSEnter() static void lowBattSDSEnter() {
{
LOG_POWERFSM("State: Lower batt SDS"); LOG_POWERFSM("State: Lower batt SDS");
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, true); doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, true);
} }
extern Power *power; extern Power *power;
static void shutdownEnter() static void shutdownEnter() {
{
LOG_POWERFSM("State: SHUTDOWN"); LOG_POWERFSM("State: SHUTDOWN");
shutdownAtMsec = millis(); shutdownAtMsec = millis();
} }
@@ -79,8 +76,7 @@ static void shutdownEnter()
static uint32_t secsSlept; static uint32_t secsSlept;
static void lsEnter() static void lsEnter() {
{
LOG_POWERFSM("lsEnter begin, ls_secs=%u", config.power.ls_secs); LOG_POWERFSM("lsEnter begin, ls_secs=%u", config.power.ls_secs);
if (screen) if (screen)
screen->setOn(false); screen->setOn(false);
@@ -89,8 +85,7 @@ static void lsEnter()
// LOG_INFO("lsEnter end"); // LOG_INFO("lsEnter end");
} }
static void lsIdle() static void lsIdle() {
{
// LOG_INFO("lsIdle begin ls_secs=%u", getPref_ls_secs()); // LOG_INFO("lsIdle begin ls_secs=%u", getPref_ls_secs());
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
@@ -153,13 +148,9 @@ static void lsIdle()
#endif #endif
} }
static void lsExit() static void lsExit() { LOG_POWERFSM("State: lsExit"); }
{
LOG_POWERFSM("State: lsExit");
}
static void nbEnter() static void nbEnter() {
{
LOG_POWERFSM("State: nbEnter"); LOG_POWERFSM("State: nbEnter");
if (screen) if (screen)
screen->setOn(false); screen->setOn(false);
@@ -171,16 +162,14 @@ static void nbEnter()
// FIXME - check if we already have packets for phone and immediately trigger EVENT_PACKETS_FOR_PHONE // FIXME - check if we already have packets for phone and immediately trigger EVENT_PACKETS_FOR_PHONE
} }
static void darkEnter() static void darkEnter() {
{
LOG_POWERFSM("State: darkEnter"); LOG_POWERFSM("State: darkEnter");
setBluetoothEnable(true); setBluetoothEnable(true);
if (screen) if (screen)
screen->setOn(false); screen->setOn(false);
} }
static void serialEnter() static void serialEnter() {
{
LOG_POWERFSM("State: serialEnter"); LOG_POWERFSM("State: serialEnter");
setBluetoothEnable(false); setBluetoothEnable(false);
if (screen) { if (screen) {
@@ -188,15 +177,13 @@ static void serialEnter()
} }
} }
static void serialExit() static void serialExit() {
{
LOG_POWERFSM("State: serialExit"); LOG_POWERFSM("State: serialExit");
// Turn bluetooth back on when we leave serial stream API // Turn bluetooth back on when we leave serial stream API
setBluetoothEnable(true); setBluetoothEnable(true);
} }
static void powerEnter() static void powerEnter() {
{
LOG_POWERFSM("State: powerEnter"); LOG_POWERFSM("State: powerEnter");
if (!isPowered()) { if (!isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state handle things // If we got here, we are in the wrong state - we should be in powered, let that state handle things
@@ -210,8 +197,7 @@ static void powerEnter()
} }
} }
static void powerIdle() static void powerIdle() {
{
// LOG_POWERFSM("State: powerIdle"); // very chatty // LOG_POWERFSM("State: powerIdle"); // very chatty
if (!isPowered()) { if (!isPowered()) {
// If we got here, we are in the wrong state // If we got here, we are in the wrong state
@@ -220,22 +206,19 @@ static void powerIdle()
} }
} }
static void powerExit() static void powerExit() {
{
LOG_POWERFSM("State: powerExit"); LOG_POWERFSM("State: powerExit");
setBluetoothEnable(true); setBluetoothEnable(true);
} }
static void onEnter() static void onEnter() {
{
LOG_POWERFSM("State: onEnter"); LOG_POWERFSM("State: onEnter");
if (screen) if (screen)
screen->setOn(true); screen->setOn(true);
setBluetoothEnable(true); setBluetoothEnable(true);
} }
static void onIdle() static void onIdle() {
{
LOG_POWERFSM("State: onIdle"); LOG_POWERFSM("State: onIdle");
if (isPowered()) { if (isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state handle things // If we got here, we are in the wrong state - we should be in powered, let that state handle things
@@ -243,10 +226,7 @@ static void onIdle()
} }
} }
static void bootEnter() static void bootEnter() { LOG_POWERFSM("State: bootEnter"); }
{
LOG_POWERFSM("State: bootEnter");
}
State stateSHUTDOWN(shutdownEnter, NULL, NULL, "SHUTDOWN"); State stateSHUTDOWN(shutdownEnter, NULL, NULL, "SHUTDOWN");
State stateSDS(sdsEnter, NULL, NULL, "SDS"); State stateSDS(sdsEnter, NULL, NULL, "SDS");
@@ -260,8 +240,7 @@ State stateON(onEnter, onIdle, NULL, "ON");
State statePOWER(powerEnter, powerIdle, powerExit, "POWER"); State statePOWER(powerEnter, powerIdle, powerExit, "POWER");
Fsm powerFSM(&stateBOOT); Fsm powerFSM(&stateBOOT);
void PowerFSM_setup() void PowerFSM_setup() {
{
bool isRouter = (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ? 1 : 0); bool isRouter = (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ? 1 : 0);
bool hasPower = isPowered(); bool hasPower = isPowered();
@@ -269,17 +248,17 @@ void PowerFSM_setup()
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout"); powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
// wake timer expired or a packet arrived // wake timer expired or a packet arrived
// if we are a router node, we go to NB (no need for bluetooth) otherwise we go to DARK (so we can send message to phone) // if we are a router node, we go to NB (no need for bluetooth) otherwise we go to DARK (so we can send message to
// phone)
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_WAKE_TIMER, NULL, "Wake timer"); powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_WAKE_TIMER, NULL, "Wake timer");
#else // Don't go into a no-bluetooth state on low power platforms #else // Don't go into a no-bluetooth state on low power platforms
powerFSM.add_transition(&stateLS, &stateDARK, EVENT_WAKE_TIMER, NULL, "Wake timer"); powerFSM.add_transition(&stateLS, &stateDARK, EVENT_WAKE_TIMER, NULL, "Wake timer");
#endif #endif
// We need this transition, because we might not transition if we were waiting to enter light-sleep, because when we wake from // We need this transition, because we might not transition if we were waiting to enter light-sleep, because when we
// light sleep we _always_ transition to NB or dark and // wake from light sleep we _always_ transition to NB or dark and
powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, exiting light sleep");
"Received packet, exiting light sleep");
powerFSM.add_transition(&stateNB, &stateNB, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, resetting win wake"); powerFSM.add_transition(&stateNB, &stateNB, EVENT_PACKET_FOR_PHONE, NULL, "Received packet, resetting win wake");
// Handle press events - note: we ignore button presses when in API mode // Handle press events - note: we ignore button presses when in API mode
@@ -346,7 +325,8 @@ void PowerFSM_setup()
// powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected"); // powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
// the only way to leave state serial is for the client to disconnect (or we timeout and force disconnect them) // the only way to leave state serial is for the client to disconnect (or we timeout and force disconnect them)
// when we leave, go to ON (which might not be the correct state if we have power connected, we will fix that in onEnter) // when we leave, go to ON (which might not be the correct state if we have power connected, we will fix that in
// onEnter)
powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect"); powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect");
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone"); powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");
@@ -356,19 +336,17 @@ void PowerFSM_setup()
if (config.display.screen_on_secs > 0) if (config.display.screen_on_secs > 0)
#endif #endif
{ {
powerFSM.add_timed_transition(&stateON, &stateDARK, powerFSM.add_timed_transition(&stateON, &stateDARK, Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
NULL, "Screen-on timeout"); NULL, "Screen-on timeout");
powerFSM.add_timed_transition(&statePOWER, &stateDARK, powerFSM.add_timed_transition(&statePOWER, &stateDARK, Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
NULL, "Screen-on timeout"); NULL, "Screen-on timeout");
} }
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally) // We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
// See: https://github.com/meshtastic/firmware/issues/1071 // See: https://github.com/meshtastic/firmware/issues/1071
// Don't add power saving transitions if we are a power saving tracker or sensor or have Wifi enabled. Sleep will be initiated // Don't add power saving transitions if we are a power saving tracker or sensor or have Wifi enabled. Sleep will be
// through the modules // initiated through the modules
#if HAS_WIFI && !defined(MESHTASTIC_EXCLUDE_WIFI) #if HAS_WIFI && !defined(MESHTASTIC_EXCLUDE_WIFI)
bool isTrackerOrSensor = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER || bool isTrackerOrSensor = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
@@ -376,29 +354,25 @@ void PowerFSM_setup()
config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR; config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR;
if ((isRouter || config.power.is_power_saving) && !isWifiAvailable() && !isTrackerOrSensor) { if ((isRouter || config.power.is_power_saving) && !isWifiAvailable() && !isTrackerOrSensor) {
powerFSM.add_timed_transition(&stateNB, &stateLS, powerFSM.add_timed_transition(&stateNB, &stateLS, Default::getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL,
Default::getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL,
"Min wake timeout"); "Min wake timeout");
// If ESP32 and using power-saving, timer mover from DARK to light-sleep // If ESP32 and using power-saving, timer mover from DARK to light-sleep
// Also serves purpose of the old DARK to DARK transition(?) See https://github.com/meshtastic/firmware/issues/3517 // Also serves purpose of the old DARK to DARK transition(?) See https://github.com/meshtastic/firmware/issues/3517
powerFSM.add_timed_transition( powerFSM.add_timed_transition(&stateDARK, &stateLS,
&stateDARK, &stateLS,
Default::getConfiguredOrDefaultMs(config.power.wait_bluetooth_secs, default_wait_bluetooth_secs), NULL, Default::getConfiguredOrDefaultMs(config.power.wait_bluetooth_secs, default_wait_bluetooth_secs), NULL,
"Bluetooth timeout"); "Bluetooth timeout");
} else { } else {
// If ESP32, but not using power-saving, check periodically if config has drifted out of stateDark // If ESP32, but not using power-saving, check periodically if config has drifted out of stateDark
powerFSM.add_timed_transition(&stateDARK, &stateDARK, powerFSM.add_timed_transition(&stateDARK, &stateDARK, Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
NULL, "Screen-on timeout"); NULL, "Screen-on timeout");
} }
#endif // HAS_WIFI || !defined(MESHTASTIC_EXCLUDE_WIFI) #endif // HAS_WIFI || !defined(MESHTASTIC_EXCLUDE_WIFI)
#else // (not) ARCH_ESP32 #else // (not) ARCH_ESP32
// If not ESP32, light-sleep not used. Check periodically if config has drifted out of stateDark // If not ESP32, light-sleep not used. Check periodically if config has drifted out of stateDark
powerFSM.add_timed_transition(&stateDARK, &stateDARK, powerFSM.add_timed_transition(&stateDARK, &stateDARK, Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL, NULL, "Screen-on timeout");
"Screen-on timeout");
#endif #endif
powerFSM.run_machine(); // run one iteration of the state machine, so we run our on enter tasks for the initial DARK state powerFSM.run_machine(); // run one iteration of the state machine, so we run our on enter tasks for the initial DARK state
+6 -7
View File
@@ -17,7 +17,8 @@
#define EVENT_RECEIVED_MSG 5 #define EVENT_RECEIVED_MSG 5
// #define EVENT_BOOT 6 // now done with a timed transition // #define EVENT_BOOT 6 // now done with a timed transition
#define EVENT_BLUETOOTH_PAIR 7 #define EVENT_BLUETOOTH_PAIR 7
// #define EVENT_NODEDB_UPDATED 8 // Now defunct: NodeDB has a big enough change that we think you should turn on the screen // #define EVENT_NODEDB_UPDATED 8 // Now defunct: NodeDB has a big enough change that we think you should turn on
// the screen
#define EVENT_CONTACT_FROM_PHONE 9 // the phone just talked to us over bluetooth #define EVENT_CONTACT_FROM_PHONE 9 // the phone just talked to us over bluetooth
#define EVENT_LOW_BATTERY 10 // Battery is critically low, go to sleep #define EVENT_LOW_BATTERY 10 // Battery is critically low, go to sleep
#define EVENT_SERIAL_CONNECTED 11 #define EVENT_SERIAL_CONNECTED 11
@@ -29,11 +30,9 @@
#define EVENT_INPUT 17 // input broker wants something, we need to wake up and enable screen #define EVENT_INPUT 17 // input broker wants something, we need to wake up and enable screen
#if MESHTASTIC_EXCLUDE_POWER_FSM #if MESHTASTIC_EXCLUDE_POWER_FSM
class FakeFsm class FakeFsm {
{ public:
public: void trigger(int event) {
void trigger(int event)
{
if (event == EVENT_SERIAL_CONNECTED) { if (event == EVENT_SERIAL_CONNECTED) {
serialConnected = true; serialConnected = true;
} else if (event == EVENT_SERIAL_DISCONNECTED) { } else if (event == EVENT_SERIAL_DISCONNECTED) {
@@ -42,7 +41,7 @@ class FakeFsm
}; };
bool getState() { return serialConnected; }; bool getState() { return serialConnected; };
private: private:
bool serialConnected = false; bool serialConnected = false;
}; };
extern FakeFsm powerFSM; extern FakeFsm powerFSM;
+6 -10
View File
@@ -6,18 +6,15 @@
#include "main.h" #include "main.h"
#include "power.h" #include "power.h"
namespace concurrency namespace concurrency {
{
/// Wrapper to convert our powerFSM stuff into a 'thread' /// Wrapper to convert our powerFSM stuff into a 'thread'
class PowerFSMThread : public OSThread class PowerFSMThread : public OSThread {
{ public:
public:
// callback returns the period for the next callback invocation (or 0 if we should no longer be called) // callback returns the period for the next callback invocation (or 0 if we should no longer be called)
PowerFSMThread() : OSThread("PowerFSM") {} PowerFSMThread() : OSThread("PowerFSM") {}
protected: protected:
int32_t runOnce() override int32_t runOnce() override {
{
#if !MESHTASTIC_EXCLUDE_POWER_FSM #if !MESHTASTIC_EXCLUDE_POWER_FSM
powerFSM.run_machine(); powerFSM.run_machine();
@@ -30,8 +27,7 @@ class PowerFSMThread : public OSThread
timeLastPowered = millis(); timeLastPowered = millis();
} else if (config.power.on_battery_shutdown_after_secs > 0 && config.power.on_battery_shutdown_after_secs != UINT32_MAX && } else if (config.power.on_battery_shutdown_after_secs > 0 && config.power.on_battery_shutdown_after_secs != UINT32_MAX &&
millis() > (timeLastPowered + millis() > (timeLastPowered +
Default::getConfiguredOrDefaultMs( Default::getConfiguredOrDefaultMs(config.power.on_battery_shutdown_after_secs))) { // shutdown after 30 minutes unpowered
config.power.on_battery_shutdown_after_secs))) { // shutdown after 30 minutes unpowered
powerFSM.trigger(EVENT_SHUTDOWN); powerFSM.trigger(EVENT_SHUTDOWN);
} }
+7 -14
View File
@@ -2,15 +2,13 @@
#include "NodeDB.h" #include "NodeDB.h"
// Use the 'live' config flag to figure out if we should be showing this message // Use the 'live' config flag to figure out if we should be showing this message
bool PowerMon::is_power_enabled(uint64_t m) bool PowerMon::is_power_enabled(uint64_t m) {
{ // FIXME: VERY STRANGE BUG: if I or in "force_enabled || " the flashed image on a rak4631 is not accepted by the
// FIXME: VERY STRANGE BUG: if I or in "force_enabled || " the flashed image on a rak4631 is not accepted by the bootloader as // bootloader as valid!!! Possibly a linker/gcc/bootloader bug somewhere?
// valid!!! Possibly a linker/gcc/bootloader bug somewhere?
return ((m & config.power.powermon_enables) ? true : false); return ((m & config.power.powermon_enables) ? true : false);
} }
void PowerMon::setState(_meshtastic_PowerMon_State state, const char *reason) void PowerMon::setState(_meshtastic_PowerMon_State state, const char *reason) {
{
#ifdef USE_POWERMON #ifdef USE_POWERMON
auto oldstates = states; auto oldstates = states;
states |= state; states |= state;
@@ -20,8 +18,7 @@ void PowerMon::setState(_meshtastic_PowerMon_State state, const char *reason)
#endif #endif
} }
void PowerMon::clearState(_meshtastic_PowerMon_State state, const char *reason) void PowerMon::clearState(_meshtastic_PowerMon_State state, const char *reason) {
{
#ifdef USE_POWERMON #ifdef USE_POWERMON
auto oldstates = states; auto oldstates = states;
states &= ~state; states &= ~state;
@@ -31,8 +28,7 @@ void PowerMon::clearState(_meshtastic_PowerMon_State state, const char *reason)
#endif #endif
} }
void PowerMon::emitLog(const char *reason) void PowerMon::emitLog(const char *reason) {
{
#ifdef USE_POWERMON #ifdef USE_POWERMON
// The nrf52 printf doesn't understand 64 bit ints, so if we ever reach that point this function will need to change. // The nrf52 printf doesn't understand 64 bit ints, so if we ever reach that point this function will need to change.
LOG_INFO("S:PM:0x%08lx,%s", (uint32_t)states, reason); LOG_INFO("S:PM:0x%08lx,%s", (uint32_t)states, reason);
@@ -41,7 +37,4 @@ void PowerMon::emitLog(const char *reason)
PowerMon *powerMon; PowerMon *powerMon;
void powerMonInit() void powerMonInit() { powerMon = new PowerMon(); }
{
powerMon = new PowerMon();
}
+3 -4
View File
@@ -13,8 +13,7 @@
* *
* For more information see the PowerMon docs. * For more information see the PowerMon docs.
*/ */
class PowerMon class PowerMon {
{
uint64_t states = 0UL; uint64_t states = 0UL;
friend class PowerStressModule; friend class PowerStressModule;
@@ -24,14 +23,14 @@ class PowerMon
*/ */
bool force_enabled = false; bool force_enabled = false;
public: public:
PowerMon() {} PowerMon() {}
// Mark entry/exit of a power consuming state // Mark entry/exit of a power consuming state
void setState(_meshtastic_PowerMon_State state, const char *reason = ""); void setState(_meshtastic_PowerMon_State state, const char *reason = "");
void clearState(_meshtastic_PowerMon_State state, const char *reason = ""); void clearState(_meshtastic_PowerMon_State state, const char *reason = "");
private: private:
// Emit the coded log message // Emit the coded log message
void emitLog(const char *reason); void emitLog(const char *reason);
+9 -16
View File
@@ -3,8 +3,7 @@
#include "configuration.h" #include "configuration.h"
#include <Arduino.h> #include <Arduino.h>
namespace meshtastic namespace meshtastic {
{
/** /**
* A boolean where we have a third state of Unknown * A boolean where we have a third state of Unknown
@@ -12,10 +11,9 @@ namespace meshtastic
enum OptionalBool { OptFalse = 0, OptTrue = 1, OptUnknown = 2 }; enum OptionalBool { OptFalse = 0, OptTrue = 1, OptUnknown = 2 };
/// Describes the state of the Power system. /// Describes the state of the Power system.
class PowerStatus : public Status class PowerStatus : public Status {
{
private: private:
CallbackObserver<PowerStatus, const PowerStatus *> statusObserver = CallbackObserver<PowerStatus, const PowerStatus *> statusObserver =
CallbackObserver<PowerStatus, const PowerStatus *>(this, &PowerStatus::updateStatus); CallbackObserver<PowerStatus, const PowerStatus *>(this, &PowerStatus::updateStatus);
@@ -30,12 +28,10 @@ class PowerStatus : public Status
/// Whether we are charging the battery /// Whether we are charging the battery
OptionalBool isCharging = OptUnknown; OptionalBool isCharging = OptUnknown;
public: public:
PowerStatus() { statusType = STATUS_TYPE_POWER; } PowerStatus() { statusType = STATUS_TYPE_POWER; }
PowerStatus(OptionalBool hasBattery, OptionalBool hasUSB, OptionalBool isCharging, int batteryVoltageMv = -1, PowerStatus(OptionalBool hasBattery, OptionalBool hasUSB, OptionalBool isCharging, int batteryVoltageMv = -1, int8_t batteryChargePercent = 0)
int8_t batteryChargePercent = 0) : Status() {
: Status()
{
this->hasBattery = hasBattery; this->hasBattery = hasBattery;
this->hasUSB = hasUSB; this->hasUSB = hasUSB;
this->isCharging = isCharging; this->isCharging = isCharging;
@@ -72,13 +68,10 @@ class PowerStatus : public Status
uint8_t getBatteryChargePercent() const { return getHasBattery() ? batteryChargePercent : 101; } uint8_t getBatteryChargePercent() const { return getHasBattery() ? batteryChargePercent : 101; }
#endif #endif
bool matches(const PowerStatus *newStatus) const bool matches(const PowerStatus *newStatus) const {
{ return (newStatus->getHasBattery() != hasBattery || newStatus->getHasUSB() != hasUSB || newStatus->getBatteryVoltageMv() != batteryVoltageMv);
return (newStatus->getHasBattery() != hasBattery || newStatus->getHasUSB() != hasUSB ||
newStatus->getBatteryVoltageMv() != batteryVoltageMv);
} }
int updateStatus(const PowerStatus *newStatus) int updateStatus(const PowerStatus *newStatus) {
{
// Only update the status if values have actually changed // Only update the status if values have actually changed
bool isDirty; bool isDirty;
{ {
+13 -24
View File
@@ -20,21 +20,18 @@
#if HAS_NETWORKING #if HAS_NETWORKING
extern Syslog syslog; extern Syslog syslog;
#endif #endif
void RedirectablePrint::rpInit() void RedirectablePrint::rpInit() {
{
#ifdef HAS_FREE_RTOS #ifdef HAS_FREE_RTOS
inDebugPrint = xSemaphoreCreateMutexStatic(&this->_MutexStorageSpace); inDebugPrint = xSemaphoreCreateMutexStatic(&this->_MutexStorageSpace);
#endif #endif
} }
void RedirectablePrint::setDestination(Print *_dest) void RedirectablePrint::setDestination(Print *_dest) {
{
assert(_dest); assert(_dest);
dest = _dest; dest = _dest;
} }
size_t RedirectablePrint::write(uint8_t c) size_t RedirectablePrint::write(uint8_t c) {
{
// Always send the characters to our segger JTAG debugger // Always send the characters to our segger JTAG debugger
#ifdef USE_SEGGER #ifdef USE_SEGGER
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c); SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
@@ -48,8 +45,7 @@ size_t RedirectablePrint::write(uint8_t c)
// serial port said (which could be zero) // serial port said (which could be zero)
} }
size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_list arg) size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_list arg) {
{
va_list copy; va_list copy;
#if ENABLE_JSON_LOGGING || ARCH_PORTDUINO #if ENABLE_JSON_LOGGING || ARCH_PORTDUINO
static char printBuf[512]; static char printBuf[512];
@@ -67,8 +63,8 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l
size_t len = vsnprintf(printBuf, sizeof(printBuf), format, copy); size_t len = vsnprintf(printBuf, sizeof(printBuf), format, copy);
va_end(copy); va_end(copy);
// If the resulting string is longer than sizeof(printBuf)-1 characters, the remaining characters are still counted for the // If the resulting string is longer than sizeof(printBuf)-1 characters, the remaining characters are still counted
// return value // for the return value
if (len > sizeof(printBuf) - 1) { if (len > sizeof(printBuf) - 1) {
len = sizeof(printBuf) - 1; len = sizeof(printBuf) - 1;
@@ -95,8 +91,7 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l
return len; return len;
} }
void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, va_list arg) void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, va_list arg) {
{
size_t r = 0; size_t r = 0;
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
@@ -181,8 +176,7 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
r += vprintf(logLevel, format, arg); r += vprintf(logLevel, format, arg);
} }
void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format, va_list arg) void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format, va_list arg) {
{
#if HAS_NETWORKING && !defined(ARCH_PORTDUINO) #if HAS_NETWORKING && !defined(ARCH_PORTDUINO)
// if syslog is in use, collect the log messages and send them to syslog // if syslog is in use, collect the log messages and send them to syslog
if (syslog.isEnabled()) { if (syslog.isEnabled()) {
@@ -216,8 +210,7 @@ void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format,
#endif #endif
} }
void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg) void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg) {
{
#if !MESHTASTIC_EXCLUDE_BLUETOOTH #if !MESHTASTIC_EXCLUDE_BLUETOOTH
if (config.security.debug_log_api_enabled && !pauseBluetoothLogging) { if (config.security.debug_log_api_enabled && !pauseBluetoothLogging) {
bool isBleConnected = false; bool isBleConnected = false;
@@ -264,8 +257,7 @@ void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_
#endif #endif
} }
meshtastic_LogRecord_Level RedirectablePrint::getLogLevel(const char *logLevel) meshtastic_LogRecord_Level RedirectablePrint::getLogLevel(const char *logLevel) {
{
meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset
switch (logLevel[0]) { switch (logLevel[0]) {
case 'D': case 'D':
@@ -287,8 +279,7 @@ meshtastic_LogRecord_Level RedirectablePrint::getLogLevel(const char *logLevel)
return ll; return ll;
} }
void RedirectablePrint::log(const char *logLevel, const char *format, ...) void RedirectablePrint::log(const char *logLevel, const char *format, ...) {
{
// append \n to format // append \n to format
size_t len = strlen(format); size_t len = strlen(format);
@@ -356,8 +347,7 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
return; return;
} }
void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len) void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len) {
{
const char alphabet[17] = "0123456789abcdef"; const char alphabet[17] = "0123456789abcdef";
log(logLevel, " +------------------------------------------------+ +----------------+"); log(logLevel, " +------------------------------------------------+ +----------------+");
log(logLevel, " |.0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .a .b .c .d .e .f | | ASCII |"); log(logLevel, " |.0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .a .b .c .d .e .f | | ASCII |");
@@ -386,8 +376,7 @@ void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16
log(logLevel, " +------------------------------------------------+ +----------------+"); log(logLevel, " +------------------------------------------------+ +----------------+");
} }
std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...) std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...) {
{
int n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */ int n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
std::unique_ptr<char[]> formatted; std::unique_ptr<char[]> formatted;
va_list ap; va_list ap;
+4 -5
View File
@@ -11,8 +11,7 @@
* This class is mostly useful to allow debug printing to be redirected away from Serial * This class is mostly useful to allow debug printing to be redirected away from Serial
* to some other transport if we switch Serial usage (on the fly) to some other purpose. * to some other transport if we switch Serial usage (on the fly) to some other purpose.
*/ */
class RedirectablePrint : public Print class RedirectablePrint : public Print {
{
Print *dest; Print *dest;
#ifdef HAS_FREE_RTOS #ifdef HAS_FREE_RTOS
@@ -21,7 +20,7 @@ class RedirectablePrint : public Print
#else #else
volatile bool inDebugPrint = false; volatile bool inDebugPrint = false;
#endif #endif
public: public:
explicit RedirectablePrint(Print *_dest) : dest(_dest) {} explicit RedirectablePrint(Print *_dest) : dest(_dest) {}
/** /**
@@ -48,12 +47,12 @@ class RedirectablePrint : public Print
std::string mt_sprintf(const std::string fmt_str, ...); std::string mt_sprintf(const std::string fmt_str, ...);
protected: protected:
/// Subclasses can override if they need to change how we format over the serial port /// Subclasses can override if they need to change how we format over the serial port
virtual void log_to_serial(const char *logLevel, const char *format, va_list arg); virtual void log_to_serial(const char *logLevel, const char *format, va_list arg);
meshtastic_LogRecord_Level getLogLevel(const char *logLevel); meshtastic_LogRecord_Level getLogLevel(const char *logLevel);
private: private:
void log_to_syslog(const char *logLevel, const char *format, va_list arg); void log_to_syslog(const char *logLevel, const char *format, va_list arg);
void log_to_ble(const char *logLevel, const char *format, va_list arg); void log_to_ble(const char *logLevel, const char *format, va_list arg);
}; };
+1 -2
View File
@@ -5,8 +5,7 @@
concurrency::Lock *spiLock; concurrency::Lock *spiLock;
void initSPI() void initSPI() {
{
assert(!spiLock); assert(!spiLock);
spiLock = new concurrency::Lock(); spiLock = new concurrency::Lock();
} }
+8 -16
View File
@@ -3,8 +3,7 @@
#ifdef FSCom #ifdef FSCom
// Only way to work on both esp32 and nrf52 // Only way to work on both esp32 and nrf52
static File openFile(const char *filename, bool fullAtomic) static File openFile(const char *filename, bool fullAtomic) {
{
concurrency::LockGuard g(spiLock); concurrency::LockGuard g(spiLock);
LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic); LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic);
#ifdef ARCH_NRF52 #ifdef ARCH_NRF52
@@ -27,13 +26,9 @@ static File openFile(const char *filename, bool fullAtomic)
return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE); return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE);
} }
SafeFile::SafeFile(const char *_filename, bool fullAtomic) SafeFile::SafeFile(const char *_filename, bool fullAtomic) : filename(_filename), f(openFile(_filename, fullAtomic)), fullAtomic(fullAtomic) {}
: filename(_filename), f(openFile(_filename, fullAtomic)), fullAtomic(fullAtomic)
{
}
size_t SafeFile::write(uint8_t ch) size_t SafeFile::write(uint8_t ch) {
{
if (!f) if (!f)
return 0; return 0;
@@ -41,16 +36,15 @@ size_t SafeFile::write(uint8_t ch)
return f.write(ch); return f.write(ch);
} }
size_t SafeFile::write(const uint8_t *buffer, size_t size) size_t SafeFile::write(const uint8_t *buffer, size_t size) {
{
if (!f) if (!f)
return 0; return 0;
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
hash ^= buffer[i]; hash ^= buffer[i];
} }
return f.write((uint8_t const *)buffer, size); // This nasty cast is _IMPORTANT_ otherwise the correct adafruit method does return f.write((uint8_t const *)buffer, size); // This nasty cast is _IMPORTANT_ otherwise the correct adafruit method
// not get used (they made a mistake in their typing) // does not get used (they made a mistake in their typing)
} }
/** /**
@@ -58,8 +52,7 @@ size_t SafeFile::write(const uint8_t *buffer, size_t size)
* *
* @return false for failure * @return false for failure
*/ */
bool SafeFile::close() bool SafeFile::close() {
{
if (!f) if (!f)
return false; return false;
@@ -93,8 +86,7 @@ bool SafeFile::close()
} }
/// Read our (closed) tempfile back in and compare the hash /// Read our (closed) tempfile back in and compare the hash
bool SafeFile::testReadback() bool SafeFile::testReadback() {
{
concurrency::LockGuard g(spiLock); concurrency::LockGuard g(spiLock);
String filenameTmp = filename; String filenameTmp = filename;
+11 -11
View File
@@ -10,21 +10,21 @@
* This class provides 'safe'/paranoid file writing. * This class provides 'safe'/paranoid file writing.
* *
* Some of our filesystems (in particular the nrf52) may have bugs beneath our layer. Therefore we want to * Some of our filesystems (in particular the nrf52) may have bugs beneath our layer. Therefore we want to
* be very careful about how we write files. This class provides a restricted (Stream only) writing API for writing to files. * be very careful about how we write files. This class provides a restricted (Stream only) writing API for writing to
* files.
* *
* Notably: * Notably:
* - we keep a simple xor hash of all characters that were written. * - we keep a simple xor hash of all characters that were written.
* - We do not allow seeking (because we want to maintain our hash) * - We do not allow seeking (because we want to maintain our hash)
* - we provide an close() method which is similar to close but returns false if we were unable to successfully write the * - we provide an close() method which is similar to close but returns false if we were unable to successfully write
* file. Also this method * the file. Also this method
* - atomically replaces any old version of the file on the disk with our new file (after first rereading the file from the disk * - atomically replaces any old version of the file on the disk with our new file (after first rereading the file from
* to confirm the hash matches) * the disk to confirm the hash matches)
* - Some files are super huge so we can't do the full atomic rename/copy (because of filesystem size limits). If !fullAtomic * - Some files are super huge so we can't do the full atomic rename/copy (because of filesystem size limits). If
* then we still do the readback to verify file is valid so higher level code can handle failures. * !fullAtomic then we still do the readback to verify file is valid so higher level code can handle failures.
*/ */
class SafeFile : public Print class SafeFile : public Print {
{ public:
public:
explicit SafeFile(char const *filepath, bool fullAtomic = false); explicit SafeFile(char const *filepath, bool fullAtomic = false);
virtual size_t write(uint8_t); virtual size_t write(uint8_t);
@@ -37,7 +37,7 @@ class SafeFile : public Print
*/ */
bool close(); bool close();
private: private:
/// Read our (closed) tempfile back in and compare the hash /// Read our (closed) tempfile back in and compare the hash
bool testReadback(); bool testReadback();
+14 -30
View File
@@ -28,8 +28,7 @@
SerialConsole *console; SerialConsole *console;
void consoleInit() void consoleInit() {
{
auto sc = new SerialConsole(); // Must be dynamically allocated because we are now inheriting from thread auto sc = new SerialConsole(); // Must be dynamically allocated because we are now inheriting from thread
#if defined(SERIAL_HAS_ON_RECEIVE) #if defined(SERIAL_HAS_ON_RECEIVE)
@@ -39,8 +38,7 @@ void consoleInit()
DEBUG_PORT.rpInit(); // Simply sets up semaphore DEBUG_PORT.rpInit(); // Simply sets up semaphore
} }
void consolePrintf(const char *format, ...) void consolePrintf(const char *format, ...) {
{
va_list arg; va_list arg;
va_start(arg, format); va_start(arg, format);
console->vprintf(nullptr, format, arg); console->vprintf(nullptr, format, arg);
@@ -48,8 +46,7 @@ void consolePrintf(const char *format, ...)
console->flush(); console->flush();
} }
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), concurrency::OSThread("SerialConsole") SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), concurrency::OSThread("SerialConsole") {
{
api_type = TYPE_SERIAL; api_type = TYPE_SERIAL;
assert(!console); assert(!console);
console = this; console = this;
@@ -76,10 +73,10 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
#endif #endif
} }
int32_t SerialConsole::runOnce() int32_t SerialConsole::runOnce() {
{
#ifdef HELTEC_MESH_SOLAR #ifdef HELTEC_MESH_SOLAR
// After enabling the mesh solar serial port module configuration, command processing is handled by the serial port module. // After enabling the mesh solar serial port module configuration, command processing is handled by the serial port
// module.
if (moduleConfig.serial.enabled && moduleConfig.serial.override_console_serial_port && if (moduleConfig.serial.enabled && moduleConfig.serial.override_console_serial_port &&
moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG) { moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG) {
return 250; return 250;
@@ -96,35 +93,23 @@ int32_t SerialConsole::runOnce()
#endif #endif
} }
void SerialConsole::flush() void SerialConsole::flush() { Port.flush(); }
{
Port.flush();
}
// trigger tx of serial data // trigger tx of serial data
void SerialConsole::onNowHasData(uint32_t fromRadioNum) void SerialConsole::onNowHasData(uint32_t fromRadioNum) { setIntervalFromNow(0); }
{
setIntervalFromNow(0);
}
// trigger rx of serial data // trigger rx of serial data
void SerialConsole::rxInt() void SerialConsole::rxInt() { setIntervalFromNow(0); }
{
setIntervalFromNow(0);
}
// For the serial port we can't really detect if any client is on the other side, so instead just look for recent messages // For the serial port we can't really detect if any client is on the other side, so instead just look for recent
bool SerialConsole::checkIsConnected() // messages
{ bool SerialConsole::checkIsConnected() { return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT); }
return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT);
}
/** /**
* we override this to notice when we've received a protobuf over the serial * we override this to notice when we've received a protobuf over the serial
* stream. Then we shut off debug serial output. * stream. Then we shut off debug serial output.
*/ */
bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) {
{
// only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled. // only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled.
if (config.has_lora && config.security.serial_enabled) { if (config.has_lora && config.security.serial_enabled) {
// Switch to protobufs for log messages // Switch to protobufs for log messages
@@ -137,8 +122,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
} }
} }
void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg) void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg) {
{
if (usingProtobufs && config.security.debug_log_api_enabled) { if (usingProtobufs && config.security.debug_log_api_enabled) {
meshtastic_LogRecord_Level ll = RedirectablePrint::getLogLevel(logLevel); meshtastic_LogRecord_Level ll = RedirectablePrint::getLogLevel(logLevel);
auto thread = concurrency::OSThread::currentThread; auto thread = concurrency::OSThread::currentThread;
+4 -6
View File
@@ -6,14 +6,13 @@
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs * Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs). * (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/ */
class SerialConsole : public StreamAPI, public RedirectablePrint, private concurrency::OSThread class SerialConsole : public StreamAPI, public RedirectablePrint, private concurrency::OSThread {
{
/** /**
* If true we are talking to a smart host and all messages (including log messages) must be framed as protobufs. * If true we are talking to a smart host and all messages (including log messages) must be framed as protobufs.
*/ */
bool usingProtobufs = false; bool usingProtobufs = false;
public: public:
SerialConsole(); SerialConsole();
/** /**
@@ -22,8 +21,7 @@ class SerialConsole : public StreamAPI, public RedirectablePrint, private concur
*/ */
virtual bool handleToRadio(const uint8_t *buf, size_t len) override; virtual bool handleToRadio(const uint8_t *buf, size_t len) override;
virtual size_t write(uint8_t c) override virtual size_t write(uint8_t c) override {
{
if (c == '\n') // prefix any newlines with carriage return if (c == '\n') // prefix any newlines with carriage return
RedirectablePrint::write('\r'); RedirectablePrint::write('\r');
return RedirectablePrint::write(c); return RedirectablePrint::write(c);
@@ -34,7 +32,7 @@ class SerialConsole : public StreamAPI, public RedirectablePrint, private concur
void flush(); void flush();
void rxInt(); void rxInt();
protected: protected:
/// Check the current underlying physical link to see if the client is currently connected /// Check the current underlying physical link to see if the client is currently connected
virtual bool checkIsConnected() override; virtual bool checkIsConnected() override;
+6 -10
View File
@@ -9,29 +9,25 @@
#define STATUS_TYPE_NODE 3 #define STATUS_TYPE_NODE 3
#define STATUS_TYPE_BLUETOOTH 4 #define STATUS_TYPE_BLUETOOTH 4
namespace meshtastic namespace meshtastic {
{
// A base class for observable status // A base class for observable status
class Status class Status {
{ protected:
protected:
// Allows us to observe an Observable // Allows us to observe an Observable
CallbackObserver<Status, const Status *> statusObserver = CallbackObserver<Status, const Status *> statusObserver = CallbackObserver<Status, const Status *>(this, &Status::updateStatus);
CallbackObserver<Status, const Status *>(this, &Status::updateStatus);
bool initialized = false; bool initialized = false;
// Workaround for no typeid support // Workaround for no typeid support
int statusType = 0; int statusType = 0;
public: public:
// Allows us to generate observable events // Allows us to generate observable events
Observable<const Status *> onNewStatus; Observable<const Status *> onNewStatus;
// Enable polymorphism ? // Enable polymorphism ?
virtual ~Status() = default; virtual ~Status() = default;
Status() Status() {
{
if (!statusType) { if (!statusType) {
statusType = STATUS_TYPE_BASE; statusType = STATUS_TYPE_BASE;
} }
+15 -42
View File
@@ -9,8 +9,7 @@ AirTime *airTime = NULL;
uint32_t air_period_tx[PERIODS_TO_LOG]; uint32_t air_period_tx[PERIODS_TO_LOG];
uint32_t air_period_rx[PERIODS_TO_LOG]; uint32_t air_period_rx[PERIODS_TO_LOG];
void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms) void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms) {
{
if (reportType == TX_LOG) { if (reportType == TX_LOG) {
LOG_DEBUG("Packet TX: %ums", airtime_ms); LOG_DEBUG("Packet TX: %ums", airtime_ms);
@@ -31,23 +30,13 @@ void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms)
this->channelUtilization[this->getPeriodUtilMinute()] = channelUtilization[this->getPeriodUtilMinute()] + airtime_ms; this->channelUtilization[this->getPeriodUtilMinute()] = channelUtilization[this->getPeriodUtilMinute()] + airtime_ms;
} }
uint8_t AirTime::currentPeriodIndex() uint8_t AirTime::currentPeriodIndex() { return ((getSecondsSinceBoot() / SECONDS_PER_PERIOD) % PERIODS_TO_LOG); }
{
return ((getSecondsSinceBoot() / SECONDS_PER_PERIOD) % PERIODS_TO_LOG);
}
uint8_t AirTime::getPeriodUtilMinute() uint8_t AirTime::getPeriodUtilMinute() { return (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS; }
{
return (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS;
}
uint8_t AirTime::getPeriodUtilHour() uint8_t AirTime::getPeriodUtilHour() { return (getSecondsSinceBoot() / 60) % MINUTES_IN_HOUR; }
{
return (getSecondsSinceBoot() / 60) % MINUTES_IN_HOUR;
}
void AirTime::airtimeRotatePeriod() void AirTime::airtimeRotatePeriod() {
{
if (this->airtimes.lastPeriodIndex != this->currentPeriodIndex()) { if (this->airtimes.lastPeriodIndex != this->currentPeriodIndex()) {
LOG_DEBUG("Rotate airtimes to a new period = %u", this->currentPeriodIndex()); LOG_DEBUG("Rotate airtimes to a new period = %u", this->currentPeriodIndex());
@@ -72,8 +61,7 @@ void AirTime::airtimeRotatePeriod()
} }
} }
uint32_t *AirTime::airtimeReport(reportTypes reportType) uint32_t *AirTime::airtimeReport(reportTypes reportType) {
{
if (reportType == TX_LOG) { if (reportType == TX_LOG) {
return this->airtimes.periodTX; return this->airtimes.periodTX;
@@ -85,23 +73,13 @@ uint32_t *AirTime::airtimeReport(reportTypes reportType)
return 0; return 0;
} }
uint8_t AirTime::getPeriodsToLog() uint8_t AirTime::getPeriodsToLog() { return PERIODS_TO_LOG; }
{
return PERIODS_TO_LOG;
}
uint32_t AirTime::getSecondsPerPeriod() uint32_t AirTime::getSecondsPerPeriod() { return SECONDS_PER_PERIOD; }
{
return SECONDS_PER_PERIOD;
}
uint32_t AirTime::getSecondsSinceBoot() uint32_t AirTime::getSecondsSinceBoot() { return this->secSinceBoot; }
{
return this->secSinceBoot;
}
float AirTime::channelUtilizationPercent() float AirTime::channelUtilizationPercent() {
{
uint32_t sum = 0; uint32_t sum = 0;
for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) { for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) {
sum += this->channelUtilization[i]; sum += this->channelUtilization[i];
@@ -110,8 +88,7 @@ float AirTime::channelUtilizationPercent()
return (float(sum) / float(CHANNEL_UTILIZATION_PERIODS * 10 * 1000)) * 100; return (float(sum) / float(CHANNEL_UTILIZATION_PERIODS * 10 * 1000)) * 100;
} }
float AirTime::utilizationTXPercent() float AirTime::utilizationTXPercent() {
{
uint32_t sum = 0; uint32_t sum = 0;
for (uint32_t i = 0; i < MINUTES_IN_HOUR; i++) { for (uint32_t i = 0; i < MINUTES_IN_HOUR; i++) {
sum += this->utilizationTX[i]; sum += this->utilizationTX[i];
@@ -120,8 +97,7 @@ float AirTime::utilizationTXPercent()
return (float(sum) / float(MS_IN_HOUR)) * 100; return (float(sum) / float(MS_IN_HOUR)) * 100;
} }
bool AirTime::isTxAllowedChannelUtil(bool polite) bool AirTime::isTxAllowedChannelUtil(bool polite) {
{
uint8_t percentage = (polite ? polite_channel_util_percent : max_channel_util_percent); uint8_t percentage = (polite ? polite_channel_util_percent : max_channel_util_percent);
if (channelUtilizationPercent() < percentage) { if (channelUtilizationPercent() < percentage) {
return true; return true;
@@ -131,8 +107,7 @@ bool AirTime::isTxAllowedChannelUtil(bool polite)
} }
} }
bool AirTime::isTxAllowedAirUtil() bool AirTime::isTxAllowedAirUtil() {
{
if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) { if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) {
if (utilizationTXPercent() < myRegion->dutyCycle * polite_duty_cycle_percent / 100) { if (utilizationTXPercent() < myRegion->dutyCycle * polite_duty_cycle_percent / 100) {
return true; return true;
@@ -145,8 +120,7 @@ bool AirTime::isTxAllowedAirUtil()
} }
// Get the amount of minutes we have to be silent before we can send again // Get the amount of minutes we have to be silent before we can send again
uint8_t AirTime::getSilentMinutes(float txPercent, float dutyCycle) uint8_t AirTime::getSilentMinutes(float txPercent, float dutyCycle) {
{
float newTxPercent = txPercent; float newTxPercent = txPercent;
for (int8_t i = MINUTES_IN_HOUR - 1; i >= 0; --i) { for (int8_t i = MINUTES_IN_HOUR - 1; i >= 0; --i) {
newTxPercent -= ((float)this->utilizationTX[i] / (MS_IN_MINUTE * MINUTES_IN_HOUR / 100)); newTxPercent -= ((float)this->utilizationTX[i] / (MS_IN_MINUTE * MINUTES_IN_HOUR / 100));
@@ -159,8 +133,7 @@ uint8_t AirTime::getSilentMinutes(float txPercent, float dutyCycle)
AirTime::AirTime() : concurrency::OSThread("AirTime"), airtimes({}) {} AirTime::AirTime() : concurrency::OSThread("AirTime"), airtimes({}) {}
int32_t AirTime::runOnce() int32_t AirTime::runOnce() {
{
secSinceBoot++; secSinceBoot++;
uint8_t utilPeriod = this->getPeriodUtilMinute(); uint8_t utilPeriod = this->getPeriodUtilMinute();
+4 -5
View File
@@ -39,10 +39,9 @@ void logAirtime(reportTypes reportType, uint32_t airtime_ms);
uint32_t *airtimeReport(reportTypes reportType); uint32_t *airtimeReport(reportTypes reportType);
class AirTime : private concurrency::OSThread class AirTime : private concurrency::OSThread {
{
public: public:
AirTime(); AirTime();
void logAirtime(reportTypes reportType, uint32_t airtime_ms); void logAirtime(reportTypes reportType, uint32_t airtime_ms);
@@ -62,7 +61,7 @@ class AirTime : private concurrency::OSThread
bool isTxAllowedChannelUtil(bool polite = false); bool isTxAllowedChannelUtil(bool polite = false);
bool isTxAllowedAirUtil(); bool isTxAllowedAirUtil();
private: private:
bool firstTime = true; bool firstTime = true;
uint8_t lastUtilPeriod = 0; uint8_t lastUtilPeriod = 0;
uint8_t lastUtilPeriodTX = 0; uint8_t lastUtilPeriodTX = 0;
@@ -82,7 +81,7 @@ class AirTime : private concurrency::OSThread
uint8_t getPeriodUtilHour(); uint8_t getPeriodUtilHour();
uint8_t currentPeriodIndex(); uint8_t currentPeriodIndex();
protected: protected:
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
}; };
+2 -4
View File
@@ -5,14 +5,12 @@
BuzzerFeedbackThread *buzzerFeedbackThread; BuzzerFeedbackThread *buzzerFeedbackThread;
BuzzerFeedbackThread::BuzzerFeedbackThread() BuzzerFeedbackThread::BuzzerFeedbackThread() {
{
if (inputBroker) if (inputBroker)
inputObserver.observe(inputBroker); inputObserver.observe(inputBroker);
} }
int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event) int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event) {
{
// Only provide feedback if buzzer is enabled for notifications // Only provide feedback if buzzer is enabled for notifications
if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED || if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED ||
config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY || config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY ||
+2 -3
View File
@@ -4,12 +4,11 @@
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "input/InputBroker.h" #include "input/InputBroker.h"
class BuzzerFeedbackThread class BuzzerFeedbackThread {
{
CallbackObserver<BuzzerFeedbackThread, const InputEvent *> inputObserver = CallbackObserver<BuzzerFeedbackThread, const InputEvent *> inputObserver =
CallbackObserver<BuzzerFeedbackThread, const InputEvent *>(this, &BuzzerFeedbackThread::handleInputEvent); CallbackObserver<BuzzerFeedbackThread, const InputEvent *>(this, &BuzzerFeedbackThread::handleInputEvent);
public: public:
BuzzerFeedbackThread(); BuzzerFeedbackThread();
int handleInputEvent(const InputEvent *event); int handleInputEvent(const InputEvent *event);
}; };
+17 -35
View File
@@ -42,8 +42,7 @@ const int DURATION_1_2 = 500; // 1/2 note
const int DURATION_3_4 = 750; // 3/4 note const int DURATION_3_4 = 750; // 3/4 note
const int DURATION_1_1 = 1000; // 1/1 note const int DURATION_1_1 = 1000; // 1/1 note
void playTones(const ToneDuration *tone_durations, int size) void playTones(const ToneDuration *tone_durations, int size) {
{
if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED || if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED ||
config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY) { config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY) {
// Buzzer is disabled or not set to system tones // Buzzer is disabled or not set to system tones
@@ -63,76 +62,64 @@ void playTones(const ToneDuration *tone_durations, int size)
} }
} }
void playBeep() void playBeep() {
{
ToneDuration melody[] = {{NOTE_B3, DURATION_1_8}}; ToneDuration melody[] = {{NOTE_B3, DURATION_1_8}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playLongBeep() void playLongBeep() {
{
ToneDuration melody[] = {{NOTE_B3, DURATION_1_1}}; ToneDuration melody[] = {{NOTE_B3, DURATION_1_1}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playGPSEnableBeep() void playGPSEnableBeep() {
{
#if defined(R1_NEO) || defined(MUZI_BASE) #if defined(R1_NEO) || defined(MUZI_BASE)
ToneDuration melody[] = { ToneDuration melody[] = {{NOTE_F5, DURATION_1_2}, {NOTE_G6, DURATION_1_8}, {NOTE_E7, DURATION_1_4}, {NOTE_SILENT, DURATION_1_2}};
{NOTE_F5, DURATION_1_2}, {NOTE_G6, DURATION_1_8}, {NOTE_E7, DURATION_1_4}, {NOTE_SILENT, DURATION_1_2}};
#else #else
ToneDuration melody[] = {{NOTE_C3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_CS4, DURATION_1_4}}; ToneDuration melody[] = {{NOTE_C3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_CS4, DURATION_1_4}};
#endif #endif
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playGPSDisableBeep() void playGPSDisableBeep() {
{
#if defined(R1_NEO) || defined(MUZI_BASE) #if defined(R1_NEO) || defined(MUZI_BASE)
ToneDuration melody[] = {{NOTE_B4, DURATION_1_16}, {NOTE_B4, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8}, ToneDuration melody[] = {{NOTE_B4, DURATION_1_16}, {NOTE_B4, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8}, {NOTE_F3, DURATION_1_16},
{NOTE_F3, DURATION_1_16}, {NOTE_F3, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8}, {NOTE_F3, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8}, {NOTE_C3, DURATION_1_1}, {NOTE_SILENT, DURATION_1_1}};
{NOTE_C3, DURATION_1_1}, {NOTE_SILENT, DURATION_1_1}};
#else #else
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_C3, DURATION_1_4}}; ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_C3, DURATION_1_4}};
#endif #endif
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playStartMelody() void playStartMelody() {
{
ToneDuration melody[] = {{NOTE_FS3, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_CS4, DURATION_1_4}}; ToneDuration melody[] = {{NOTE_FS3, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_CS4, DURATION_1_4}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playShutdownMelody() void playShutdownMelody() {
{
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}}; ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playChirp() void playChirp() {
{
// A short, friendly "chirp" sound for key presses // A short, friendly "chirp" sound for key presses
ToneDuration melody[] = {{NOTE_AS3, 20}}; // Short AS3 note ToneDuration melody[] = {{NOTE_AS3, 20}}; // Short AS3 note
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playClick() void playClick() {
{
// A very short "click" sound with minimum delay; ideal for rotary encoder events // A very short "click" sound with minimum delay; ideal for rotary encoder events
ToneDuration melody[] = {{NOTE_AS3, 1}}; // Very Short AS3 ToneDuration melody[] = {{NOTE_AS3, 1}}; // Very Short AS3
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playBoop() void playBoop() {
{
// A short, friendly "boop" sound for button presses // A short, friendly "boop" sound for button presses
ToneDuration melody[] = {{NOTE_A3, 50}}; // Very short A3 note ToneDuration melody[] = {{NOTE_A3, 50}}; // Very short A3 note
playTones(melody, sizeof(melody) / sizeof(ToneDuration)); playTones(melody, sizeof(melody) / sizeof(ToneDuration));
} }
void playLongPressLeadUp() void playLongPressLeadUp() {
{
// An ascending lead-up sequence for long press - builds anticipation // An ascending lead-up sequence for long press - builds anticipation
ToneDuration melody[] = { ToneDuration melody[] = {
{NOTE_C3, 100}, // Start low {NOTE_C3, 100}, // Start low
@@ -153,8 +140,7 @@ static const ToneDuration leadUpNotes[] = {
}; };
static const int leadUpNotesCount = sizeof(leadUpNotes) / sizeof(ToneDuration); static const int leadUpNotesCount = sizeof(leadUpNotes) / sizeof(ToneDuration);
bool playNextLeadUpNote() bool playNextLeadUpNote() {
{
if (leadUpNoteIndex >= leadUpNotesCount) { if (leadUpNoteIndex >= leadUpNotesCount) {
return false; // All notes have been played return false; // All notes have been played
} }
@@ -171,13 +157,9 @@ bool playNextLeadUpNote()
return true; // Note was played (playTones handles buzzer availability internally) return true; // Note was played (playTones handles buzzer availability internally)
} }
void resetLeadUpSequence() void resetLeadUpSequence() { leadUpNoteIndex = 0; }
{
leadUpNoteIndex = 0;
}
void playComboTune() void playComboTune() {
{
// Quick high-pitched notes with trills // Quick high-pitched notes with trills
ToneDuration melody[] = { ToneDuration melody[] = {
{NOTE_G3, 80}, // Quick chirp {NOTE_G3, 80}, // Quick chirp
+6 -20
View File
@@ -4,34 +4,20 @@
#ifdef HAS_FREE_RTOS #ifdef HAS_FREE_RTOS
namespace concurrency namespace concurrency {
{
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS() : semaphore(xSemaphoreCreateBinary()) BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS() : semaphore(xSemaphoreCreateBinary()) { assert(semaphore); }
{
assert(semaphore);
}
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS() BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS() { vSemaphoreDelete(semaphore); }
{
vSemaphoreDelete(semaphore);
}
/** /**
* Returns false if we were interrupted * Returns false if we were interrupted
*/ */
bool BinarySemaphoreFreeRTOS::take(uint32_t msec) bool BinarySemaphoreFreeRTOS::take(uint32_t msec) { return xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec)); }
{
return xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec));
}
void BinarySemaphoreFreeRTOS::give() void BinarySemaphoreFreeRTOS::give() { xSemaphoreGive(semaphore); }
{
xSemaphoreGive(semaphore);
}
IRAM_ATTR void BinarySemaphoreFreeRTOS::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) IRAM_ATTR void BinarySemaphoreFreeRTOS::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) {
{
xSemaphoreGiveFromISR(semaphore, pxHigherPriorityTaskWoken); xSemaphoreGiveFromISR(semaphore, pxHigherPriorityTaskWoken);
} }
+3 -5
View File
@@ -2,16 +2,14 @@
#include "../freertosinc.h" #include "../freertosinc.h"
namespace concurrency namespace concurrency {
{
#ifdef HAS_FREE_RTOS #ifdef HAS_FREE_RTOS
class BinarySemaphoreFreeRTOS class BinarySemaphoreFreeRTOS {
{
SemaphoreHandle_t semaphore; SemaphoreHandle_t semaphore;
public: public:
BinarySemaphoreFreeRTOS(); BinarySemaphoreFreeRTOS();
~BinarySemaphoreFreeRTOS(); ~BinarySemaphoreFreeRTOS();
+2 -4
View File
@@ -3,8 +3,7 @@
#ifndef HAS_FREE_RTOS #ifndef HAS_FREE_RTOS
namespace concurrency namespace concurrency {
{
BinarySemaphorePosix::BinarySemaphorePosix() {} BinarySemaphorePosix::BinarySemaphorePosix() {}
@@ -13,8 +12,7 @@ BinarySemaphorePosix::~BinarySemaphorePosix() {}
/** /**
* Returns false if we timed out * Returns false if we timed out
*/ */
bool BinarySemaphorePosix::take(uint32_t msec) bool BinarySemaphorePosix::take(uint32_t msec) {
{
delay(msec); // FIXME delay(msec); // FIXME
return false; return false;
} }
+3 -5
View File
@@ -2,16 +2,14 @@
#include "../freertosinc.h" #include "../freertosinc.h"
namespace concurrency namespace concurrency {
{
#ifndef HAS_FREE_RTOS #ifndef HAS_FREE_RTOS
class BinarySemaphorePosix class BinarySemaphorePosix {
{
// SemaphoreHandle_t semaphore; // SemaphoreHandle_t semaphore;
public: public:
BinarySemaphorePosix(); BinarySemaphorePosix();
~BinarySemaphorePosix(); ~BinarySemaphorePosix();
+4 -12
View File
@@ -1,8 +1,7 @@
#include "concurrency/InterruptableDelay.h" #include "concurrency/InterruptableDelay.h"
#include "configuration.h" #include "configuration.h"
namespace concurrency namespace concurrency {
{
InterruptableDelay::InterruptableDelay() {} InterruptableDelay::InterruptableDelay() {}
@@ -11,8 +10,7 @@ InterruptableDelay::~InterruptableDelay() {}
/** /**
* Returns false if we were interrupted * Returns false if we were interrupted
*/ */
bool InterruptableDelay::delay(uint32_t msec) bool InterruptableDelay::delay(uint32_t msec) {
{
// LOG_DEBUG("delay %u ", msec); // LOG_DEBUG("delay %u ", msec);
// sem take will return false if we timed out (i.e. were not interrupted) // sem take will return false if we timed out (i.e. were not interrupted)
@@ -22,14 +20,8 @@ bool InterruptableDelay::delay(uint32_t msec)
return !r; return !r;
} }
void InterruptableDelay::interrupt() void InterruptableDelay::interrupt() { semaphore.give(); }
{
semaphore.give();
}
IRAM_ATTR void InterruptableDelay::interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken) IRAM_ATTR void InterruptableDelay::interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken) { semaphore.giveFromISR(pxHigherPriorityTaskWoken); }
{
semaphore.giveFromISR(pxHigherPriorityTaskWoken);
}
} // namespace concurrency } // namespace concurrency
+5 -6
View File
@@ -10,21 +10,20 @@
#define BinarySemaphore BinarySemaphorePosix #define BinarySemaphore BinarySemaphorePosix
#endif #endif
namespace concurrency namespace concurrency {
{
/** /**
* An object that provides delay(msec) like functionality, but can be interrupted by calling interrupt(). * 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. * 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. * This is implemented for FreeRTOS but should be easy to port to other operating systems.
*/ */
class InterruptableDelay class InterruptableDelay {
{
BinarySemaphore semaphore; BinarySemaphore semaphore;
public: public:
InterruptableDelay(); InterruptableDelay();
~InterruptableDelay(); ~InterruptableDelay();
+4 -8
View File
@@ -2,27 +2,23 @@
#include "configuration.h" #include "configuration.h"
#include <cassert> #include <cassert>
namespace concurrency namespace concurrency {
{
#ifdef HAS_FREE_RTOS #ifdef HAS_FREE_RTOS
Lock::Lock() : handle(xSemaphoreCreateBinary()) Lock::Lock() : handle(xSemaphoreCreateBinary()) {
{
assert(handle); assert(handle);
if (xSemaphoreGive(handle) == false) { if (xSemaphoreGive(handle) == false) {
abort(); abort();
} }
} }
void Lock::lock() void Lock::lock() {
{
if (xSemaphoreTake(handle, portMAX_DELAY) == false) { if (xSemaphoreTake(handle, portMAX_DELAY) == false) {
abort(); abort();
} }
} }
void Lock::unlock() void Lock::unlock() {
{
if (xSemaphoreGive(handle) == false) { if (xSemaphoreGive(handle) == false) {
abort(); abort();
} }
+4 -6
View File
@@ -2,15 +2,13 @@
#include "../freertosinc.h" #include "../freertosinc.h"
namespace concurrency namespace concurrency {
{
/** /**
* @brief Simple wrapper around FreeRTOS API for implementing a mutex lock * @brief Simple wrapper around FreeRTOS API for implementing a mutex lock
*/ */
class Lock class Lock {
{ public:
public:
Lock(); Lock();
Lock(const Lock &) = delete; Lock(const Lock &) = delete;
@@ -26,7 +24,7 @@ class Lock
// Must not be called from an ISR. // Must not be called from an ISR.
void unlock(); void unlock();
private: private:
#ifdef HAS_FREE_RTOS #ifdef HAS_FREE_RTOS
SemaphoreHandle_t handle; SemaphoreHandle_t handle;
#endif #endif
+3 -10
View File
@@ -1,17 +1,10 @@
#include "LockGuard.h" #include "LockGuard.h"
#include "configuration.h" #include "configuration.h"
namespace concurrency namespace concurrency {
{
LockGuard::LockGuard(Lock *lock) : lock(lock) LockGuard::LockGuard(Lock *lock) : lock(lock) { lock->lock(); }
{
lock->lock();
}
LockGuard::~LockGuard() LockGuard::~LockGuard() { lock->unlock(); }
{
lock->unlock();
}
} // namespace concurrency } // namespace concurrency
+4 -6
View File
@@ -2,22 +2,20 @@
#include "Lock.h" #include "Lock.h"
namespace concurrency namespace concurrency {
{
/** /**
* @brief RAII lock guard * @brief RAII lock guard
*/ */
class LockGuard class LockGuard {
{ public:
public:
explicit LockGuard(Lock *lock); explicit LockGuard(Lock *lock);
~LockGuard(); ~LockGuard();
LockGuard(const LockGuard &) = delete; LockGuard(const LockGuard &) = delete;
LockGuard &operator=(const LockGuard &) = delete; LockGuard &operator=(const LockGuard &) = delete;
private: private:
Lock *lock; Lock *lock;
}; };
+7 -14
View File
@@ -2,16 +2,14 @@
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
namespace concurrency namespace concurrency {
{
static bool debugNotification; static bool debugNotification;
/** /**
* Notify this thread so it can run * Notify this thread so it can run
*/ */
bool NotifiedWorkerThread::notify(uint32_t v, bool overwrite) bool NotifiedWorkerThread::notify(uint32_t v, bool overwrite) {
{
bool r = notifyCommon(v, overwrite); bool r = notifyCommon(v, overwrite);
if (r) if (r)
@@ -23,8 +21,7 @@ bool NotifiedWorkerThread::notify(uint32_t v, bool overwrite)
/** /**
* Notify this thread so it can run * Notify this thread so it can run
*/ */
IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite) IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite) {
{
if (overwrite || notification == 0) { if (overwrite || notification == 0) {
enabled = true; enabled = true;
setInterval(0); // Run ASAP setInterval(0); // Run ASAP
@@ -48,8 +45,7 @@ IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite)
* *
* This must be inline or IRAM_ATTR on ESP32 * This must be inline or IRAM_ATTR on ESP32
*/ */
IRAM_ATTR bool NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite) IRAM_ATTR bool NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite) {
{
bool r = notifyCommon(v, overwrite); bool r = notifyCommon(v, overwrite);
if (r) if (r)
mainDelay.interruptFromISR(highPriWoken); mainDelay.interruptFromISR(highPriWoken);
@@ -60,8 +56,7 @@ IRAM_ATTR bool NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uin
/** /**
* Schedule a notification to fire in delay msecs * Schedule a notification to fire in delay msecs
*/ */
bool NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrite) bool NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrite) {
{
bool didIt = notify(v, overwrite); bool didIt = notify(v, overwrite);
if (didIt) { // If we didn't already have something queued, override the delay to be larger if (didIt) { // If we didn't already have something queued, override the delay to be larger
@@ -74,8 +69,7 @@ bool NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrit
return didIt; return didIt;
} }
void NotifiedWorkerThread::checkNotification() void NotifiedWorkerThread::checkNotification() {
{
auto n = notification; auto n = notification;
notification = 0; // clear notification notification = 0; // clear notification
if (n) { if (n) {
@@ -83,8 +77,7 @@ void NotifiedWorkerThread::checkNotification()
} }
} }
int32_t NotifiedWorkerThread::runOnce() int32_t NotifiedWorkerThread::runOnce() {
{
enabled = false; // Only run once per notification enabled = false; // Only run once per notification
checkNotification(); checkNotification();
+8 -10
View File
@@ -2,20 +2,18 @@
#include "OSThread.h" #include "OSThread.h"
namespace concurrency namespace concurrency {
{
/** /**
* @brief A worker thread that waits on a freertos notification * @brief A worker thread that waits on a freertos notification
*/ */
class NotifiedWorkerThread : public OSThread class NotifiedWorkerThread : public OSThread {
{
/** /**
* The notification that was most recently used to wake the thread. Read from runOnce() * The notification that was most recently used to wake the thread. Read from runOnce()
*/ */
uint32_t notification = 0; uint32_t notification = 0;
public: public:
NotifiedWorkerThread(const char *name) : OSThread(name) {} NotifiedWorkerThread(const char *name) : OSThread(name) {}
/** /**
@@ -35,18 +33,18 @@ class NotifiedWorkerThread : public OSThread
*/ */
bool notifyLater(uint32_t delay, uint32_t v, bool overwrite); bool notifyLater(uint32_t delay, uint32_t v, bool overwrite);
protected: protected:
virtual void onNotify(uint32_t notification) = 0; virtual void onNotify(uint32_t notification) = 0;
/// just calls checkNotification() /// just calls checkNotification()
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
/// Sometimes we might want to check notifications independently of when our thread was getting woken up (i.e. if we are about /// Sometimes we might want to check notifications independently of when our thread was getting woken up (i.e. if we
/// to change radio transmit/receive modes we want to handle any pending interrupts first). You can call this method and if /// are about to change radio transmit/receive modes we want to handle any pending interrupts first). You can call
/// any notifications are currently pending they will be handled immediately. /// this method and if any notifications are currently pending they will be handled immediately.
void checkNotification(); void checkNotification();
private: private:
/** /**
* Notify this thread so it can run * Notify this thread so it can run
*/ */
+11 -21
View File
@@ -3,8 +3,7 @@
#include "memGet.h" #include "memGet.h"
#include <assert.h> #include <assert.h>
namespace concurrency namespace concurrency {
{
/// Show debugging info for disabled threads /// Show debugging info for disabled threads
bool OSThread::showDisabled; bool OSThread::showDisabled;
@@ -20,15 +19,12 @@ const OSThread *OSThread::currentThread;
ThreadController mainController, timerController; ThreadController mainController, timerController;
InterruptableDelay mainDelay; InterruptableDelay mainDelay;
void OSThread::setup() void OSThread::setup() {
{
mainController.ThreadName = "mainController"; mainController.ThreadName = "mainController";
timerController.ThreadName = "timerController"; timerController.ThreadName = "timerController";
} }
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller) OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller) : Thread(NULL, period), controller(_controller) {
: Thread(NULL, period), controller(_controller)
{
assertIsSetup(); assertIsSetup();
ThreadName = _name; ThreadName = _name;
@@ -39,8 +35,7 @@ OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_contro
} }
} }
OSThread::~OSThread() OSThread::~OSThread() {
{
if (controller) if (controller)
controller->remove(this); controller->remove(this);
} }
@@ -48,8 +43,7 @@ OSThread::~OSThread()
/** /**
* Wait a specified number msecs starting from the current time (rather than the last time we were run) * Wait a specified number msecs starting from the current time (rather than the last time we were run)
*/ */
void OSThread::setIntervalFromNow(unsigned long _interval) void OSThread::setIntervalFromNow(unsigned long _interval) {
{
// Save interval // Save interval
interval = _interval; interval = _interval;
@@ -57,8 +51,7 @@ void OSThread::setIntervalFromNow(unsigned long _interval)
_cached_next_run = millis() + interval; _cached_next_run = millis() + interval;
} }
bool OSThread::shouldRun(unsigned long time) bool OSThread::shouldRun(unsigned long time) {
{
bool r = Thread::shouldRun(time); bool r = Thread::shouldRun(time);
if (showRun && r) { if (showRun && r) {
@@ -76,8 +69,7 @@ bool OSThread::shouldRun(unsigned long time)
return r; return r;
} }
void OSThread::run() void OSThread::run() {
{
#ifdef DEBUG_HEAP #ifdef DEBUG_HEAP
auto heap = memGet.getFreeHeap(); auto heap = memGet.getFreeHeap();
#endif #endif
@@ -101,8 +93,7 @@ void OSThread::run()
currentThread = NULL; currentThread = NULL;
} }
int32_t OSThread::disable() int32_t OSThread::disable() {
{
enabled = false; enabled = false;
setInterval(INT32_MAX); setInterval(INT32_MAX);
@@ -122,14 +113,13 @@ int32_t OSThread::disable()
*/ */
bool hasBeenSetup; bool hasBeenSetup;
void assertIsSetup() void assertIsSetup() {
{
/** /**
* Dear developer comrade - If this assert fails() that means you need to fix the following: * 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. * This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor
* Call assertIsSetup() to force a crash if someone tries to create an instance too early. * 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 * 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 * new them at a point where you are guaranteed that other objects that this instance
+6 -7
View File
@@ -7,8 +7,7 @@
#include "ThreadController.h" #include "ThreadController.h"
#include "concurrency/InterruptableDelay.h" #include "concurrency/InterruptableDelay.h"
namespace concurrency namespace concurrency {
{
extern ThreadController mainController, timerController; extern ThreadController mainController, timerController;
extern InterruptableDelay mainDelay; extern InterruptableDelay mainDelay;
@@ -18,7 +17,8 @@ extern InterruptableDelay mainDelay;
/** /**
* @brief Base threading * @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. * 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 * TODO FIXME @geeksville
* *
@@ -28,8 +28,7 @@ extern InterruptableDelay mainDelay;
* move typedQueue into concurrency * move typedQueue into concurrency
* remove freertos from typedqueue * remove freertos from typedqueue
*/ */
class OSThread : public Thread class OSThread : public Thread {
{
ThreadController *controller; ThreadController *controller;
/// Show debugging info for disabled threads /// Show debugging info for disabled threads
@@ -41,7 +40,7 @@ class OSThread : public Thread
/// Show debugging info for threads we decide not to run; /// Show debugging info for threads we decide not to run;
static bool showWaiting; static bool showWaiting;
public: public:
/// For debug printing only (might be null) /// For debug printing only (might be null)
static const OSThread *currentThread; static const OSThread *currentThread;
@@ -60,7 +59,7 @@ class OSThread : public Thread
*/ */
void setIntervalFromNow(unsigned long _interval); void setIntervalFromNow(unsigned long _interval);
protected: protected:
/** /**
* The method that will be called each time our thread gets a chance to run * The method that will be called each time our thread gets a chance to run
* *
+4 -6
View File
@@ -2,22 +2,20 @@
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
namespace concurrency namespace concurrency {
{
/** /**
* @brief Periodically invoke a callback. This just provides C-style callback conventions * @brief Periodically invoke a callback. This just provides C-style callback conventions
* rather than a virtual function - FIXME, remove? * rather than a virtual function - FIXME, remove?
*/ */
class Periodic : public OSThread class Periodic : public OSThread {
{
int32_t (*callback)(); int32_t (*callback)();
public: public:
// callback returns the period for the next callback invocation (or 0 if we should no longer be called) // callback returns the period for the next callback invocation (or 0 if we should no longer be called)
Periodic(const char *name, int32_t (*_callback)()) : OSThread(name), callback(_callback) {} Periodic(const char *name, int32_t (*_callback)()) : OSThread(name), callback(_callback) {}
protected: protected:
int32_t runOnce() override { return callback(); } int32_t runOnce() override { return callback(); }
}; };
+2 -2
View File
@@ -68,8 +68,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#error APP_VERSION must be set by the build environment #error APP_VERSION must be set by the build environment
#endif #endif
// FIXME: This is still needed by the Bluetooth Stack and needs to be replaced by something better. Remnant of the old versioning // FIXME: This is still needed by the Bluetooth Stack and needs to be replaced by something better. Remnant of the old
// system. // versioning system.
#ifndef HW_VERSION #ifndef HW_VERSION
#define HW_VERSION "1.0" #define HW_VERSION "1.0"
#endif #endif
+12 -34
View File
@@ -8,13 +8,9 @@ ScanI2C::ScanI2C() = default;
void ScanI2C::scanPort(ScanI2C::I2CPort port) {} void ScanI2C::scanPort(ScanI2C::I2CPort port) {}
void ScanI2C::scanPort(ScanI2C::I2CPort port, uint8_t *address, uint8_t asize) {} void ScanI2C::scanPort(ScanI2C::I2CPort port, uint8_t *address, uint8_t asize) {}
void ScanI2C::setSuppressScreen() void ScanI2C::setSuppressScreen() { shouldSuppressScreen = true; }
{
shouldSuppressScreen = true;
}
ScanI2C::FoundDevice ScanI2C::firstScreen() const ScanI2C::FoundDevice ScanI2C::firstScreen() const {
{
// Allow to override the scanner results for screen // Allow to override the scanner results for screen
if (shouldSuppressScreen) if (shouldSuppressScreen)
return DEVICE_NONE; return DEVICE_NONE;
@@ -23,62 +19,44 @@ ScanI2C::FoundDevice ScanI2C::firstScreen() const
return firstOfOrNONE(4, types); return firstOfOrNONE(4, types);
} }
ScanI2C::FoundDevice ScanI2C::firstRTC() const ScanI2C::FoundDevice ScanI2C::firstRTC() const {
{
ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563, RTC_PCF85063, RTC_RX8130CE}; ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563, RTC_PCF85063, RTC_RX8130CE};
return firstOfOrNONE(4, types); return firstOfOrNONE(4, types);
} }
ScanI2C::FoundDevice ScanI2C::firstKeyboard() const ScanI2C::FoundDevice ScanI2C::firstKeyboard() const {
{
ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004, MPR121KB, TCA8418KB}; ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004, MPR121KB, TCA8418KB};
return firstOfOrNONE(6, types); return firstOfOrNONE(6, types);
} }
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const {
{
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P, BMM150}; ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P, BMM150};
return firstOfOrNONE(9, types); return firstOfOrNONE(9, types);
} }
ScanI2C::FoundDevice ScanI2C::firstAQI() const ScanI2C::FoundDevice ScanI2C::firstAQI() const {
{
ScanI2C::DeviceType types[] = {PMSA0031, SCD4X}; ScanI2C::DeviceType types[] = {PMSA0031, SCD4X};
return firstOfOrNONE(2, types); return firstOfOrNONE(2, types);
} }
ScanI2C::FoundDevice ScanI2C::firstRGBLED() const ScanI2C::FoundDevice ScanI2C::firstRGBLED() const {
{
ScanI2C::DeviceType types[] = {NCP5623, LP5562}; ScanI2C::DeviceType types[] = {NCP5623, LP5562};
return firstOfOrNONE(2, types); return firstOfOrNONE(2, types);
} }
ScanI2C::FoundDevice ScanI2C::find(ScanI2C::DeviceType) const ScanI2C::FoundDevice ScanI2C::find(ScanI2C::DeviceType) const { return DEVICE_NONE; }
{
return DEVICE_NONE;
}
bool ScanI2C::exists(ScanI2C::DeviceType) const bool ScanI2C::exists(ScanI2C::DeviceType) const { return false; }
{
return false;
}
ScanI2C::FoundDevice ScanI2C::firstOfOrNONE(size_t count, ScanI2C::DeviceType *types) const ScanI2C::FoundDevice ScanI2C::firstOfOrNONE(size_t count, ScanI2C::DeviceType *types) const { return DEVICE_NONE; }
{
return DEVICE_NONE;
}
size_t ScanI2C::countDevices() const size_t ScanI2C::countDevices() const { return 0; }
{
return 0;
}
ScanI2C::DeviceAddress::DeviceAddress(ScanI2C::I2CPort port, uint8_t address) : port(port), address(address) {} ScanI2C::DeviceAddress::DeviceAddress(ScanI2C::I2CPort port, uint8_t address) : port(port), address(address) {}
ScanI2C::DeviceAddress::DeviceAddress() : DeviceAddress(I2CPort::NO_I2C, 0) {} ScanI2C::DeviceAddress::DeviceAddress() : DeviceAddress(I2CPort::NO_I2C, 0) {}
bool ScanI2C::DeviceAddress::operator<(const ScanI2C::DeviceAddress &other) const bool ScanI2C::DeviceAddress::operator<(const ScanI2C::DeviceAddress &other) const {
{
return return
// If this one has no port and other has a port // If this one has no port and other has a port
(port == NO_I2C && other.port != NO_I2C) (port == NO_I2C && other.port != NO_I2C)
+5 -6
View File
@@ -3,9 +3,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
class ScanI2C class ScanI2C {
{ public:
public:
typedef enum DeviceType { typedef enum DeviceType {
NONE, NONE,
SCREEN_SSD1306, SCREEN_SSD1306,
@@ -121,7 +120,7 @@ class ScanI2C
static const FoundDevice DEVICE_NONE; static const FoundDevice DEVICE_NONE;
public: public:
ScanI2C(); ScanI2C();
virtual void scanPort(ScanI2C::I2CPort); virtual void scanPort(ScanI2C::I2CPort);
@@ -150,9 +149,9 @@ class ScanI2C
virtual size_t countDevices() const; virtual size_t countDevices() const;
protected: protected:
virtual FoundDevice firstOfOrNONE(size_t, DeviceType[]) const; virtual FoundDevice firstOfOrNONE(size_t, DeviceType[]) const;
private: private:
bool shouldSuppressScreen = false; bool shouldSuppressScreen = false;
}; };
+2 -6
View File
@@ -3,13 +3,9 @@
static std::forward_list<ScanI2CConsumer *> ScanI2CConsumers; static std::forward_list<ScanI2CConsumer *> ScanI2CConsumers;
ScanI2CConsumer::ScanI2CConsumer() ScanI2CConsumer::ScanI2CConsumer() { ScanI2CConsumers.push_front(this); }
{
ScanI2CConsumers.push_front(this);
}
void ScanI2CCompleted(ScanI2C *i2cScanner) void ScanI2CCompleted(ScanI2C *i2cScanner) {
{
for (ScanI2CConsumer *consumer : ScanI2CConsumers) { for (ScanI2CConsumer *consumer : ScanI2CConsumers) {
consumer->i2cScanFinished(i2cScanner); consumer->i2cScanFinished(i2cScanner);
} }
+2 -3
View File
@@ -3,9 +3,8 @@
#include "ScanI2C.h" #include "ScanI2C.h"
#include <stddef.h> #include <stddef.h>
class ScanI2CConsumer class ScanI2CConsumer {
{ public:
public:
ScanI2CConsumer(); ScanI2CConsumer();
virtual void i2cScanFinished(ScanI2C *i2cScanner) = 0; virtual void i2cScanFinished(ScanI2C *i2cScanner) = 0;
}; };
+12 -31
View File
@@ -10,8 +10,7 @@
#include "meshUtils.h" // vformat #include "meshUtils.h" // vformat
#endif #endif
bool in_array(uint8_t *array, int size, uint8_t lookfor) bool in_array(uint8_t *array, int size, uint8_t lookfor) {
{
int i; int i;
for (i = 0; i < size; i++) for (i = 0; i < size; i++)
if (lookfor == array[i]) if (lookfor == array[i])
@@ -19,20 +18,15 @@ bool in_array(uint8_t *array, int size, uint8_t lookfor)
return false; return false;
} }
ScanI2C::FoundDevice ScanI2CTwoWire::find(ScanI2C::DeviceType type) const ScanI2C::FoundDevice ScanI2CTwoWire::find(ScanI2C::DeviceType type) const {
{
concurrency::LockGuard guard((concurrency::Lock *)&lock); concurrency::LockGuard guard((concurrency::Lock *)&lock);
return exists(type) ? ScanI2C::FoundDevice(type, deviceAddresses.at(type)) : DEVICE_NONE; return exists(type) ? ScanI2C::FoundDevice(type, deviceAddresses.at(type)) : DEVICE_NONE;
} }
bool ScanI2CTwoWire::exists(ScanI2C::DeviceType type) const bool ScanI2CTwoWire::exists(ScanI2C::DeviceType type) const { return deviceAddresses.find(type) != deviceAddresses.end(); }
{
return deviceAddresses.find(type) != deviceAddresses.end();
}
ScanI2C::FoundDevice ScanI2CTwoWire::firstOfOrNONE(size_t count, DeviceType types[]) const ScanI2C::FoundDevice ScanI2CTwoWire::firstOfOrNONE(size_t count, DeviceType types[]) const {
{
concurrency::LockGuard guard((concurrency::Lock *)&lock); concurrency::LockGuard guard((concurrency::Lock *)&lock);
for (size_t k = 0; k < count; k++) { for (size_t k = 0; k < count; k++) {
@@ -46,8 +40,7 @@ ScanI2C::FoundDevice ScanI2CTwoWire::firstOfOrNONE(size_t count, DeviceType type
return DEVICE_NONE; return DEVICE_NONE;
} }
ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const {
{
TwoWire *i2cBus = fetchI2CBus(addr); TwoWire *i2cBus = fetchI2CBus(addr);
uint8_t r = 0; uint8_t r = 0;
@@ -78,9 +71,8 @@ ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const
return o_probe; return o_probe;
} }
uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation &registerLocation, uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation &registerLocation, ScanI2CTwoWire::ResponseWidth responseWidth,
ScanI2CTwoWire::ResponseWidth responseWidth, bool zeropad = false) const bool zeropad = false) const {
{
uint16_t value = 0x00; uint16_t value = 0x00;
TwoWire *i2cBus = fetchI2CBus(registerLocation.i2cAddress); TwoWire *i2cBus = fetchI2CBus(registerLocation.i2cAddress);
@@ -116,8 +108,7 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation
type = T; \ type = T; \
break; break;
void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) {
{
concurrency::LockGuard guard((concurrency::Lock *)&lock); concurrency::LockGuard guard((concurrency::Lock *)&lock);
LOG_DEBUG("Scan for I2C devices on port %d", port); LOG_DEBUG("Scan for I2C devices on port %d", port);
@@ -634,13 +625,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
} }
} }
void ScanI2CTwoWire::scanPort(I2CPort port) void ScanI2CTwoWire::scanPort(I2CPort port) { scanPort(port, nullptr, 0); }
{
scanPort(port, nullptr, 0);
}
TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) {
{
if (address.port == ScanI2C::I2CPort::WIRE) { if (address.port == ScanI2C::I2CPort::WIRE) {
return &Wire; return &Wire;
} else { } else {
@@ -652,13 +639,7 @@ TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address)
} }
} }
size_t ScanI2CTwoWire::countDevices() const size_t ScanI2CTwoWire::countDevices() const { return foundDevices.size(); }
{
return foundDevices.size();
}
void ScanI2CTwoWire::logFoundDevice(const char *device, uint8_t address) void ScanI2CTwoWire::logFoundDevice(const char *device, uint8_t address) { LOG_INFO("%s found at address 0x%x", device, address); }
{
LOG_INFO("%s found at address 0x%x", device, address);
}
#endif #endif
+5 -9
View File
@@ -14,9 +14,8 @@
#include "../concurrency/Lock.h" #include "../concurrency/Lock.h"
class ScanI2CTwoWire : public ScanI2C class ScanI2CTwoWire : public ScanI2C {
{ public:
public:
void scanPort(ScanI2C::I2CPort) override; void scanPort(ScanI2C::I2CPort) override;
void scanPort(ScanI2C::I2CPort, uint8_t *, uint8_t) override; void scanPort(ScanI2C::I2CPort, uint8_t *, uint8_t) override;
@@ -29,18 +28,15 @@ class ScanI2CTwoWire : public ScanI2C
static TwoWire *fetchI2CBus(ScanI2C::DeviceAddress); static TwoWire *fetchI2CBus(ScanI2C::DeviceAddress);
protected: protected:
FoundDevice firstOfOrNONE(size_t, DeviceType[]) const override; FoundDevice firstOfOrNONE(size_t, DeviceType[]) const override;
private: private:
typedef struct RegisterLocation { typedef struct RegisterLocation {
DeviceAddress i2cAddress; DeviceAddress i2cAddress;
RegisterAddress registerAddress; RegisterAddress registerAddress;
RegisterLocation(DeviceAddress deviceAddress, RegisterAddress registerAddress) RegisterLocation(DeviceAddress deviceAddress, RegisterAddress registerAddress) : i2cAddress(deviceAddress), registerAddress(registerAddress) {}
: i2cAddress(deviceAddress), registerAddress(registerAddress)
{
}
} RegisterLocation; } RegisterLocation;
+4 -8
View File
@@ -4,8 +4,7 @@
#include "../main.h" #include "../main.h"
#include <SPI.h> #include <SPI.h>
void d_writeCommand(uint8_t c) void d_writeCommand(uint8_t c) {
{
SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
if (PIN_EINK_DC >= 0) if (PIN_EINK_DC >= 0)
digitalWrite(PIN_EINK_DC, LOW); digitalWrite(PIN_EINK_DC, LOW);
@@ -19,8 +18,7 @@ void d_writeCommand(uint8_t c)
SPI1.endTransaction(); SPI1.endTransaction();
} }
void d_writeData(uint8_t d) void d_writeData(uint8_t d) {
{
SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
if (PIN_EINK_CS >= 0) if (PIN_EINK_CS >= 0)
digitalWrite(PIN_EINK_CS, LOW); digitalWrite(PIN_EINK_CS, LOW);
@@ -30,8 +28,7 @@ void d_writeData(uint8_t d)
SPI1.endTransaction(); SPI1.endTransaction();
} }
unsigned long d_waitWhileBusy(uint16_t busy_time) unsigned long d_waitWhileBusy(uint16_t busy_time) {
{
if (PIN_EINK_BUSY >= 0) { if (PIN_EINK_BUSY >= 0) {
delay(1); // add some margin to become active delay(1); // add some margin to become active
unsigned long start = micros(); unsigned long start = micros();
@@ -51,8 +48,7 @@ unsigned long d_waitWhileBusy(uint16_t busy_time)
return busy_time; return busy_time;
} }
void scanEInkDevice(void) void scanEInkDevice(void) {
{
SPI1.begin(); SPI1.begin();
d_writeCommand(0x22); d_writeCommand(0x22);
d_writeData(0x83); d_writeData(0x83);
+2 -2
View File
@@ -1,7 +1,7 @@
#pragma once #pragma once
// The FreeRTOS includes are in a different directory on ESP32 and I can't figure out how to make that work with platformio gcc // The FreeRTOS includes are in a different directory on ESP32 and I can't figure out how to make that work with
// options so this is my quick hack to make things work // platformio gcc options so this is my quick hack to make things work
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
#define HAS_FREE_RTOS #define HAS_FREE_RTOS
+51 -98
View File
@@ -33,10 +33,7 @@
#endif #endif
// Not all platforms have std::size(). // Not all platforms have std::size().
template <typename T, std::size_t N> std::size_t array_count(const T (&)[N]) template <typename T, std::size_t N> std::size_t array_count(const T (&)[N]) { return N; }
{
return N;
}
#ifndef GPS_SERIAL_PORT #ifndef GPS_SERIAL_PORT
#define GPS_SERIAL_PORT Serial1 #define GPS_SERIAL_PORT Serial1
@@ -72,8 +69,7 @@ static struct uBloxGnssModelInfo {
#define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc) #define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc)
// For logging // For logging
static const char *getGPSPowerStateString(GPSPowerState state) static const char *getGPSPowerStateString(GPSPowerState state) {
{
switch (state) { switch (state) {
case GPS_ACTIVE: case GPS_ACTIVE:
return "ACTIVE"; return "ACTIVE";
@@ -98,8 +94,7 @@ static const char *getGPSPowerStateString(GPSPowerState state)
int lastState = LOW; int lastState = LOW;
bool firstrun = true; bool firstrun = true;
static int32_t gpsSwitch() static int32_t gpsSwitch() {
{
if (gps) { if (gps) {
int currentState = digitalRead(PIN_GPS_SWITCH); int currentState = digitalRead(PIN_GPS_SWITCH);
@@ -130,8 +125,7 @@ static int32_t gpsSwitch()
static concurrency::Periodic *gpsPeriodic; static concurrency::Periodic *gpsPeriodic;
#endif #endif
static void UBXChecksum(uint8_t *message, size_t length) static void UBXChecksum(uint8_t *message, size_t length) {
{
uint8_t CK_A = 0, CK_B = 0; uint8_t CK_A = 0, CK_B = 0;
// Calculate the checksum, starting from the CLASS field (which is message[2]) // Calculate the checksum, starting from the CLASS field (which is message[2])
@@ -146,8 +140,7 @@ static void UBXChecksum(uint8_t *message, size_t length)
} }
// Calculate the checksum for a CAS packet // Calculate the checksum for a CAS packet
static void CASChecksum(uint8_t *message, size_t length) static void CASChecksum(uint8_t *message, size_t length) {
{
uint32_t cksum = ((uint32_t)message[5] << 24); // Message ID uint32_t cksum = ((uint32_t)message[5] << 24); // Message ID
cksum += ((uint32_t)message[4]) << 16; // Class cksum += ((uint32_t)message[4]) << 16; // Class
cksum += message[2]; // Payload Len cksum += message[2]; // Payload Len
@@ -168,8 +161,7 @@ static void CASChecksum(uint8_t *message, size_t length)
} }
// Function to create a ublox packet for editing in memory // Function to create a ublox packet for editing in memory
uint8_t GPS::makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg) uint8_t GPS::makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg) {
{
// Construct the UBX packet // Construct the UBX packet
UBXscratch[0] = 0xB5; // header UBXscratch[0] = 0xB5; // header
UBXscratch[1] = 0x62; // header UBXscratch[1] = 0x62; // header
@@ -189,8 +181,7 @@ uint8_t GPS::makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_siz
} }
// Function to create a CAS packet for editing in memory // Function to create a CAS packet for editing in memory
uint8_t GPS::makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg) uint8_t GPS::makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg) {
{
// General CAS structure // General CAS structure
// | H1 | H2 | payload_len | cls | msg | Payload ... | Checksum | // | H1 | H2 | payload_len | cls | msg | Payload ... | Checksum |
// Size: | 1 | 1 | 2 | 1 | 1 | payload_len | 4 | // Size: | 1 | 1 | 2 | 1 | 1 | payload_len | 4 |
@@ -223,8 +214,7 @@ uint8_t GPS::makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_siz
return (payload_size + 10); return (payload_size + 10);
} }
GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis) GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis) {
{
uint8_t buffer[768] = {0}; uint8_t buffer[768] = {0};
uint8_t b; uint8_t b;
int bytesRead = 0; int bytesRead = 0;
@@ -259,8 +249,7 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
return GNSS_RESPONSE_NONE; return GNSS_RESPONSE_NONE;
} }
GPS_RESPONSE GPS::getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) GPS_RESPONSE GPS::getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) {
{
uint32_t startTime = millis(); uint32_t startTime = millis();
uint8_t buffer[CAS_ACK_NACK_MSG_SIZE] = {0}; uint8_t buffer[CAS_ACK_NACK_MSG_SIZE] = {0};
uint8_t bufferPos = 0; uint8_t bufferPos = 0;
@@ -320,8 +309,7 @@ GPS_RESPONSE GPS::getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMilli
return GNSS_RESPONSE_NONE; return GNSS_RESPONSE_NONE;
} }
GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) {
{
uint8_t b; uint8_t b;
uint8_t ack = 0; uint8_t ack = 0;
const uint8_t ackP[2] = {class_id, msg_id}; const uint8_t ackP[2] = {class_id, msg_id};
@@ -392,14 +380,14 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis)
/** /**
* @brief * @brief
* @note New method, this method can wait for the specified class and message ID, and return the payload * @note New method, this method can wait for the specified class and message ID, and return the payload
* @param *buffer: The message buffer, if there is a response payload message, it will be returned through the buffer parameter * @param *buffer: The message buffer, if there is a response payload message, it will be returned through the buffer
* parameter
* @param size: size of buffer * @param size: size of buffer
* @param requestedClass: request class constant * @param requestedClass: request class constant
* @param requestedID: request message ID constant * @param requestedID: request message ID constant
* @retval length of payload message * @retval length of payload message
*/ */
int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis) int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis) {
{
uint16_t ubxFrameCounter = 0; uint16_t ubxFrameCounter = 0;
uint32_t startTime = millis(); uint32_t startTime = millis();
uint16_t needRead = 0; uint16_t needRead = 0;
@@ -491,8 +479,7 @@ static const int rareSerialSpeeds[3] = {4800, 57600, GPS_BAUDRATE};
* to known GPS responses. * to known GPS responses.
* @retval Whether setup reached the end of its potential to configure the GPS. * @retval Whether setup reached the end of its potential to configure the GPS.
*/ */
bool GPS::setup() bool GPS::setup() {
{
if (!didSerialInit) { if (!didSerialInit) {
int msglen = 0; int msglen = 0;
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
@@ -633,8 +620,7 @@ bool GPS::setup()
delay(250); delay(250);
} else if (IS_ONE_OF(gnssModel, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352)) { } else if (IS_ONE_OF(gnssModel, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352)) {
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_IN || if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_IN || config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_NP_865) {
config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_NP_865) {
_serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC _serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC
// GPS GLONASS GALILEO BDS QZSS NAVIC // GPS GLONASS GALILEO BDS QZSS NAVIC
// 1 0 1 0 0 1 // 1 0 1 0 0 1
@@ -815,15 +801,13 @@ bool GPS::setup()
return true; return true;
} }
GPS::~GPS() GPS::~GPS() {
{
// we really should unregister our sleep observer // we really should unregister our sleep observer
notifyDeepSleepObserver.unobserve(&notifyDeepSleep); notifyDeepSleepObserver.unobserve(&notifyDeepSleep);
} }
// Put the GPS hardware into a specified state // Put the GPS hardware into a specified state
void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) {
{
// Update the stored GPSPowerstate, and create local copies // Update the stored GPSPowerstate, and create local copies
GPSPowerState oldState = powerState; GPSPowerState oldState = powerState;
powerState = newState; powerState = newState;
@@ -877,8 +861,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
} }
// Set power with EN pin, if relevant // Set power with EN pin, if relevant
void GPS::writePinEN(bool on) void GPS::writePinEN(bool on) {
{
// Abort: if conflict with Canned Messages when using Wisblock(?) // Abort: if conflict with Canned Messages when using Wisblock(?)
if ((HW_VENDOR == meshtastic_HardwareModel_RAK4631 || HW_VENDOR == meshtastic_HardwareModel_WISMESH_TAP) && if ((HW_VENDOR == meshtastic_HardwareModel_RAK4631 || HW_VENDOR == meshtastic_HardwareModel_WISMESH_TAP) &&
(rotaryEncoderInterruptImpl1 || upDownInterruptImpl1)) (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1))
@@ -893,8 +876,7 @@ void GPS::writePinEN(bool on)
// Set the value of the STANDBY pin, if relevant // Set the value of the STANDBY pin, if relevant
// true for standby state, false for awake // true for standby state, false for awake
void GPS::writePinStandby(bool standby) void GPS::writePinStandby(bool standby) {
{
#ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76B, L76K and clones #ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76B, L76K and clones
// Determine the new value for the pin // Determine the new value for the pin
@@ -915,8 +897,7 @@ void GPS::writePinStandby(bool standby)
} }
// Enable / Disable GPS with PMU, if present // Enable / Disable GPS with PMU, if present
void GPS::setPowerPMU(bool on) void GPS::setPowerPMU(bool on) {
{
// We only have PMUs on the T-Beam, and that board has a tiny battery to save GPS ephemera, // We only have PMUs on the T-Beam, and that board has a tiny battery to save GPS ephemera,
// so treat as a standby. // so treat as a standby.
#ifdef HAS_PMU #ifdef HAS_PMU
@@ -948,8 +929,7 @@ void GPS::setPowerPMU(bool on)
} }
// Set UBLOX power, if relevant // Set UBLOX power, if relevant
void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) {
{
// Abort: if not UBLOX hardware // Abort: if not UBLOX hardware
if (!IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX6, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9, GNSS_MODEL_UBLOX10)) if (!IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX6, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9, GNSS_MODEL_UBLOX10))
return; return;
@@ -996,8 +976,7 @@ void GPS::setPowerUBLOX(bool on, uint32_t sleepMs)
} }
/// Record that we have a GPS /// Record that we have a GPS
void GPS::setConnected() void GPS::setConnected() {
{
if (!hasGPS) { if (!hasGPS) {
hasGPS = true; hasGPS = true;
shouldPublish = true; shouldPublish = true;
@@ -1005,15 +984,13 @@ void GPS::setConnected()
} }
// We want a GPS lock. Wake the hardware // We want a GPS lock. Wake the hardware
void GPS::up() void GPS::up() {
{
scheduling.informSearching(); scheduling.informSearching();
setPowerState(GPS_ACTIVE); setPowerState(GPS_ACTIVE);
} }
// We've got a GPS lock. Enter a low power state, potentially. // We've got a GPS lock. Enter a low power state, potentially.
void GPS::down() void GPS::down() {
{
scheduling.informGotLock(); scheduling.informGotLock();
uint32_t predictedSearchDuration = scheduling.predictedSearchDurationMs(); uint32_t predictedSearchDuration = scheduling.predictedSearchDurationMs();
uint32_t sleepTime = scheduling.msUntilNextSearch(); uint32_t sleepTime = scheduling.msUntilNextSearch();
@@ -1058,8 +1035,7 @@ void GPS::down()
} }
} }
void GPS::publishUpdate() void GPS::publishUpdate() {
{
if (shouldPublish) { if (shouldPublish) {
shouldPublish = false; shouldPublish = false;
@@ -1075,8 +1051,7 @@ void GPS::publishUpdate()
} }
} }
int32_t GPS::runOnce() int32_t GPS::runOnce() {
{
if (!GPSInitFinished) { if (!GPSInitFinished) {
if (!_serial_gps || config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) { if (!_serial_gps || config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) {
LOG_INFO("GPS set to not-present. Skip probe"); LOG_INFO("GPS set to not-present. Skip probe");
@@ -1210,8 +1185,7 @@ int32_t GPS::runOnce()
} }
// clear the GPS rx/tx buffer as quickly as possible // clear the GPS rx/tx buffer as quickly as possible
void GPS::clearBuffer() void GPS::clearBuffer() {
{
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
_serial_gps->flush(false); _serial_gps->flush(false);
#else #else
@@ -1222,8 +1196,7 @@ void GPS::clearBuffer()
} }
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs /// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int GPS::prepareDeepSleep(void *unused) int GPS::prepareDeepSleep(void *unused) {
{
LOG_INFO("GPS deep sleep!"); LOG_INFO("GPS deep sleep!");
disable(); disable();
return 0; return 0;
@@ -1254,8 +1227,7 @@ static const char *DETECTED_MESSAGE = "%s detected";
} \ } \
} while (0) } while (0)
GnssModel_t GPS::probe(int serialSpeed) GnssModel_t GPS::probe(int serialSpeed) {
{
uint8_t buffer[768] = {0}; uint8_t buffer[768] = {0};
switch (currentStep) { switch (currentStep) {
@@ -1283,8 +1255,7 @@ GnssModel_t GPS::probe(int serialSpeed)
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE); digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
// attempt to detect the chip based on boot messages // attempt to detect the chip based on boot messages
std::vector<ChipInfo> passive_detect = { std::vector<ChipInfo> passive_detect = {{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352}, {"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352}, {"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352},
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UC6580", "UC6580", GNSS_MODEL_UC6580},
@@ -1322,9 +1293,9 @@ GnssModel_t GPS::probe(int serialSpeed)
return GNSS_MODEL_UNKNOWN; return GNSS_MODEL_UNKNOWN;
} }
case 2: { case 2: {
std::vector<ChipInfo> atgm = { std::vector<ChipInfo> atgm = {{"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H},
{"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H}, /* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS),
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */ -71-0(GPS+BDS+GLONASS)) based on AT6558 */
{"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}}; {"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}};
PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500); PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500);
currentDelay = 20; currentDelay = 20;
@@ -1468,8 +1439,7 @@ GnssModel_t GPS::probe(int serialSpeed)
return GNSS_MODEL_UNKNOWN; return GNSS_MODEL_UNKNOWN;
} }
GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipInfo> &responseMap, int serialSpeed) GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipInfo> &responseMap, int serialSpeed) {
{
// Calculate buffer size based on baud rate - 256 bytes for 9600 baud as baseline // Calculate buffer size based on baud rate - 256 bytes for 9600 baud as baseline
// Higher baud rates get proportionally larger buffers to handle more data // Higher baud rates get proportionally larger buffers to handle more data
int bufferSize = (serialSpeed * 256) / 9600; int bufferSize = (serialSpeed * 256) / 9600;
@@ -1522,8 +1492,7 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
return GNSS_MODEL_UNKNOWN; // Return unknown on timeout return GNSS_MODEL_UNKNOWN; // Return unknown on timeout
} }
GPS *GPS::createGps() GPS *GPS::createGps() {
{
int8_t _rx_gpio = config.position.rx_gpio; int8_t _rx_gpio = config.position.rx_gpio;
int8_t _tx_gpio = config.position.tx_gpio; int8_t _tx_gpio = config.position.tx_gpio;
int8_t _en_gpio = config.position.gps_en_gpio; int8_t _en_gpio = config.position.gps_en_gpio;
@@ -1557,12 +1526,10 @@ GPS *GPS::createGps()
GpioPin *p = new GpioHwPin(_en_gpio); GpioPin *p = new GpioHwPin(_en_gpio);
if (!GPS_EN_ACTIVE) { // Need to invert the pin before hardware if (!GPS_EN_ACTIVE) { // Need to invert the pin before hardware
new GpioNotTransformer( new GpioNotTransformer(virtPin,
virtPin,
p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio
} else { } else {
new GpioUnaryTransformer( new GpioUnaryTransformer(virtPin,
virtPin,
p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio
} }
} }
@@ -1628,8 +1595,7 @@ GPS *GPS::createGps()
return new_gps; return new_gps;
} }
static int32_t toDegInt(RawDegrees d) static int32_t toDegInt(RawDegrees d) {
{
int32_t degMult = 10000000; // 1e7 int32_t degMult = 10000000; // 1e7
int32_t r = d.deg * degMult + d.billionths / 100; int32_t r = d.deg * degMult + d.billionths / 100;
if (d.negative) if (d.negative)
@@ -1643,14 +1609,13 @@ static int32_t toDegInt(RawDegrees d)
* *
* @return true if we've set a new time * @return true if we've set a new time
*/ */
bool GPS::lookForTime() bool GPS::lookForTime() {
{
auto ti = reader.time; auto ti = reader.time;
auto d = reader.date; auto d = reader.date;
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
/* Convert to unix time /* 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, The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January
1970 (midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z). 1, 1970 (midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
*/ */
struct tm t; struct tm t;
t.tm_sec = ti.second() + round(ti.age() / 1000); t.tm_sec = ti.second() + round(ti.age() / 1000);
@@ -1662,8 +1627,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
t.tm_isdst = false; t.tm_isdst = false;
if (t.tm_mon > -1) { if (t.tm_mon > -1) {
if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultSuccess) { if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultSuccess) {
LOG_DEBUG("NMEA GPS time set %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour, LOG_DEBUG("NMEA GPS time set %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, ti.age());
t.tm_min, t.tm_sec, ti.age());
return true; return true;
} else { } else {
return false; return false;
@@ -1680,8 +1644,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
* *
* @return true if we've acquired a new location * @return true if we've acquired a new location
*/ */
bool GPS::lookForLocation() bool GPS::lookForLocation() {
{
// By default, TinyGPS++ does not parse GPGSA lines, which give us // By default, TinyGPS++ does not parse GPGSA lines, which give us
// the 2D/3D fixType (see NMEAGPS.h) // the 2D/3D fixType (see NMEAGPS.h)
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?) // At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
@@ -1693,8 +1656,7 @@ bool GPS::lookForLocation()
#ifndef GPS_DEBUG #ifndef GPS_DEBUG
if (reader.failedChecksum() > 4) if (reader.failedChecksum() > 4)
#endif #endif
LOG_WARN("%u new GPS checksum failures, for a total of %u", reader.failedChecksum() - lastChecksumFailCount, LOG_WARN("%u new GPS checksum failures, for a total of %u", reader.failedChecksum() - lastChecksumFailCount, reader.failedChecksum());
reader.failedChecksum());
lastChecksumFailCount = reader.failedChecksum(); lastChecksumFailCount = reader.failedChecksum();
} }
#endif #endif
@@ -1799,8 +1761,7 @@ bool GPS::lookForLocation()
if (reader.course.isUpdated() && reader.course.isValid()) { if (reader.course.isUpdated() && reader.course.isValid()) {
if (reader.course.value() < 36000) { // sanity check if (reader.course.value() < 36000) { // sanity check
p.ground_track = p.ground_track = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
} else { } else {
LOG_WARN("BOGUS course.value() REJECTED: %d", reader.course.value()); LOG_WARN("BOGUS course.value() REJECTED: %d", reader.course.value());
} }
@@ -1813,8 +1774,7 @@ bool GPS::lookForLocation()
return true; return true;
} }
bool GPS::hasLock() bool GPS::hasLock() {
{
// Using GPGGA fix quality indicator // Using GPGGA fix quality indicator
if (fixQual >= 1 && fixQual <= 5) { if (fixQual >= 1 && fixQual <= 5) {
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
@@ -1827,13 +1787,9 @@ bool GPS::hasLock()
return false; return false;
} }
bool GPS::hasFlow() bool GPS::hasFlow() { return reader.passedChecksum() > 0; }
{
return reader.passedChecksum() > 0;
}
bool GPS::whileActive() bool GPS::whileActive() {
{
unsigned int charsInBuf = 0; unsigned int charsInBuf = 0;
bool isValid = false; bool isValid = false;
#ifdef GPS_DEBUG #ifdef GPS_DEBUG
@@ -1873,8 +1829,7 @@ bool GPS::whileActive()
#endif #endif
return isValid; return isValid;
} }
void GPS::enable() void GPS::enable() {
{
// Clear the old scheduling info (reset the lock-time prediction) // Clear the old scheduling info (reset the lock-time prediction)
scheduling.reset(); scheduling.reset();
@@ -1885,8 +1840,7 @@ void GPS::enable()
setPowerState(GPS_ACTIVE); setPowerState(GPS_ACTIVE);
} }
int32_t GPS::disable() int32_t GPS::disable() {
{
enabled = false; enabled = false;
setInterval(INT32_MAX); setInterval(INT32_MAX);
setPowerState(GPS_OFF); setPowerState(GPS_OFF);
@@ -1894,8 +1848,7 @@ int32_t GPS::disable()
return INT32_MAX; return INT32_MAX;
} }
void GPS::toggleGpsMode() void GPS::toggleGpsMode() {
{
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) { if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED; config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
LOG_INFO("User toggled GpsMode. Now DISABLED"); LOG_INFO("User toggled GpsMode. Now DISABLED");
+5 -6
View File
@@ -63,13 +63,12 @@ struct ChipInfo {
* *
* When new data is available it will notify observers. * When new data is available it will notify observers.
*/ */
class GPS : private concurrency::OSThread class GPS : private concurrency::OSThread {
{ public:
public:
meshtastic_Position p = meshtastic_Position_init_default; meshtastic_Position p = meshtastic_Position_init_default;
/** This is normally bound to config.position.gps_en_gpio but some rare boards (like heltec tracker) need more advanced /** This is normally bound to config.position.gps_en_gpio but some rare boards (like heltec tracker) need more
* implementations. Those boards will set this public variable to a custom implementation. * advanced implementations. Those boards will set this public variable to a custom implementation.
* *
* Normally set by GPS::createGPS() * Normally set by GPS::createGPS()
*/ */
@@ -121,7 +120,7 @@ class GPS : private concurrency::OSThread
// Let the GPS hardware save power between updates // Let the GPS hardware save power between updates
void down(); void down();
private: private:
GPS() : concurrency::OSThread("GPS") {} GPS() : concurrency::OSThread("GPS") {}
/// Record that we have a GPS /// Record that we have a GPS
+10 -26
View File
@@ -3,15 +3,11 @@
#include "Default.h" #include "Default.h"
// Mark the time when searching for GPS position begins // Mark the time when searching for GPS position begins
void GPSUpdateScheduling::informSearching() void GPSUpdateScheduling::informSearching() { searchStartedMs = millis(); }
{
searchStartedMs = millis();
}
// Mark the time when searching for GPS is complete, // Mark the time when searching for GPS is complete,
// then update the predicted lock-time // then update the predicted lock-time
void GPSUpdateScheduling::informGotLock() void GPSUpdateScheduling::informGotLock() {
{
searchEndedMs = millis(); searchEndedMs = millis();
LOG_DEBUG("Took %us to get lock", (searchEndedMs - searchStartedMs) / 1000); LOG_DEBUG("Took %us to get lock", (searchEndedMs - searchStartedMs) / 1000);
updateLockTimePrediction(); updateLockTimePrediction();
@@ -19,8 +15,7 @@ void GPSUpdateScheduling::informGotLock()
// Clear old lock-time prediction data. // Clear old lock-time prediction data.
// When re-enabling GPS with user button. // When re-enabling GPS with user button.
void GPSUpdateScheduling::reset() void GPSUpdateScheduling::reset() {
{
searchStartedMs = 0; searchStartedMs = 0;
searchEndedMs = 0; searchEndedMs = 0;
searchCount = 0; searchCount = 0;
@@ -29,8 +24,7 @@ void GPSUpdateScheduling::reset()
// How many milliseconds before we should next search for GPS position // How many milliseconds before we should next search for GPS position
// Used by GPS hardware directly, to enter timed hardware sleep // Used by GPS hardware directly, to enter timed hardware sleep
uint32_t GPSUpdateScheduling::msUntilNextSearch() uint32_t GPSUpdateScheduling::msUntilNextSearch() {
{
uint32_t now = millis(); uint32_t now = millis();
// Target interval (seconds), between GPS updates // Target interval (seconds), between GPS updates
@@ -50,8 +44,7 @@ uint32_t GPSUpdateScheduling::msUntilNextSearch()
// How long have we already been searching? // How long have we already been searching?
// Used to abort a search in progress, if it runs unacceptably long // Used to abort a search in progress, if it runs unacceptably long
uint32_t GPSUpdateScheduling::elapsedSearchMs() uint32_t GPSUpdateScheduling::elapsedSearchMs() {
{
// If searching // If searching
if (searchStartedMs > searchEndedMs) if (searchStartedMs > searchEndedMs)
return millis() - searchStartedMs; return millis() - searchStartedMs;
@@ -62,16 +55,11 @@ uint32_t GPSUpdateScheduling::elapsedSearchMs()
} }
// Is it now time to begin searching for a GPS position? // Is it now time to begin searching for a GPS position?
bool GPSUpdateScheduling::isUpdateDue() bool GPSUpdateScheduling::isUpdateDue() { return (msUntilNextSearch() == 0); }
{
return (msUntilNextSearch() == 0);
}
// Have we been searching for a GPS position for too long? // Have we been searching for a GPS position for too long?
bool GPSUpdateScheduling::searchedTooLong() bool GPSUpdateScheduling::searchedTooLong() {
{ uint32_t minimumOrConfiguredSecs = Default::getConfiguredOrMinimumValue(config.position.position_broadcast_secs, default_broadcast_interval_secs);
uint32_t minimumOrConfiguredSecs =
Default::getConfiguredOrMinimumValue(config.position.position_broadcast_secs, default_broadcast_interval_secs);
uint32_t maxSearchMs = Default::getConfiguredOrDefaultMs(minimumOrConfiguredSecs, default_broadcast_interval_secs); uint32_t maxSearchMs = Default::getConfiguredOrDefaultMs(minimumOrConfiguredSecs, default_broadcast_interval_secs);
// If broadcast interval set to max, no such thing as "too long" // If broadcast interval set to max, no such thing as "too long"
if (maxSearchMs == UINT32_MAX) if (maxSearchMs == UINT32_MAX)
@@ -87,8 +75,7 @@ bool GPSUpdateScheduling::searchedTooLong()
} }
// Updates the predicted time-to-get-lock, by exponentially smoothing the latest observation // Updates the predicted time-to-get-lock, by exponentially smoothing the latest observation
void GPSUpdateScheduling::updateLockTimePrediction() void GPSUpdateScheduling::updateLockTimePrediction() {
{
// How long did it take to get GPS lock this time? // How long did it take to get GPS lock this time?
// Duration between down() calls // Duration between down() calls
@@ -112,7 +99,4 @@ void GPSUpdateScheduling::updateLockTimePrediction()
} }
// How long do we expect to spend searching for a lock? // How long do we expect to spend searching for a lock?
uint32_t GPSUpdateScheduling::predictedSearchDurationMs() uint32_t GPSUpdateScheduling::predictedSearchDurationMs() { return GPSUpdateScheduling::predictedMsToGetLock; }
{
return GPSUpdateScheduling::predictedMsToGetLock;
}
+3 -4
View File
@@ -3,9 +3,8 @@
#include "configuration.h" #include "configuration.h"
// Encapsulates code responsible for the timing of GPS updates // Encapsulates code responsible for the timing of GPS updates
class GPSUpdateScheduling class GPSUpdateScheduling {
{ public:
public:
// Marks the time of these events, for calculation use // Marks the time of these events, for calculation use
void informSearching(); void informSearching();
void informGotLock(); // Predicted lock-time is recalculated here void informGotLock(); // Predicted lock-time is recalculated here
@@ -18,7 +17,7 @@ class GPSUpdateScheduling
uint32_t elapsedSearchMs(); // How long have we been searching so far? uint32_t elapsedSearchMs(); // How long have we been searching so far?
uint32_t predictedSearchDurationMs(); // How long do we expect to spend searching for a lock? uint32_t predictedSearchDurationMs(); // How long do we expect to spend searching for a lock?
private: private:
void updateLockTimePrediction(); // Called from informGotLock void updateLockTimePrediction(); // Called from informGotLock
uint32_t searchStartedMs = 0; uint32_t searchStartedMs = 0;
uint32_t searchEndedMs = 0; uint32_t searchEndedMs = 0;
+35 -76
View File
@@ -1,25 +1,17 @@
#include "GeoCoord.h" #include "GeoCoord.h"
GeoCoord::GeoCoord() GeoCoord::GeoCoord() { _dirty = true; }
{
_dirty = true;
}
GeoCoord::GeoCoord(int32_t lat, int32_t lon, int32_t alt) : _latitude(lat), _longitude(lon), _altitude(alt) GeoCoord::GeoCoord(int32_t lat, int32_t lon, int32_t alt) : _latitude(lat), _longitude(lon), _altitude(alt) { GeoCoord::setCoords(); }
{
GeoCoord::setCoords();
}
GeoCoord::GeoCoord(float lat, float lon, int32_t alt) : _altitude(alt) GeoCoord::GeoCoord(float lat, float lon, int32_t alt) : _altitude(alt) {
{
// Change decimial representation to int32_t. I.e., 12.345 becomes 123450000 // Change decimial representation to int32_t. I.e., 12.345 becomes 123450000
_latitude = int32_t(lat * 1e+7); _latitude = int32_t(lat * 1e+7);
_longitude = int32_t(lon * 1e+7); _longitude = int32_t(lon * 1e+7);
GeoCoord::setCoords(); GeoCoord::setCoords();
} }
GeoCoord::GeoCoord(double lat, double lon, int32_t alt) : _altitude(alt) GeoCoord::GeoCoord(double lat, double lon, int32_t alt) : _altitude(alt) {
{
// Change decimial representation to int32_t. I.e., 12.345 becomes 123450000 // Change decimial representation to int32_t. I.e., 12.345 becomes 123450000
_latitude = int32_t(lat * 1e+7); _latitude = int32_t(lat * 1e+7);
_longitude = int32_t(lon * 1e+7); _longitude = int32_t(lon * 1e+7);
@@ -27,8 +19,7 @@ GeoCoord::GeoCoord(double lat, double lon, int32_t alt) : _altitude(alt)
} }
// Initialize all the coordinate systems // Initialize all the coordinate systems
void GeoCoord::setCoords() void GeoCoord::setCoords() {
{
double lat = _latitude * 1e-7; double lat = _latitude * 1e-7;
double lon = _longitude * 1e-7; double lon = _longitude * 1e-7;
GeoCoord::latLongToDMS(lat, lon, _dms); GeoCoord::latLongToDMS(lat, lon, _dms);
@@ -39,8 +30,7 @@ void GeoCoord::setCoords()
_dirty = false; _dirty = false;
} }
void GeoCoord::updateCoords(int32_t lat, int32_t lon, int32_t alt) void GeoCoord::updateCoords(int32_t lat, int32_t lon, int32_t alt) {
{
// If marked dirty or new coordinates // If marked dirty or new coordinates
if (_dirty || _latitude != lat || _longitude != lon || _altitude != alt) { if (_dirty || _latitude != lat || _longitude != lon || _altitude != alt) {
_dirty = true; _dirty = true;
@@ -51,8 +41,7 @@ void GeoCoord::updateCoords(int32_t lat, int32_t lon, int32_t alt)
} }
} }
void GeoCoord::updateCoords(const double lat, const double lon, const int32_t alt) void GeoCoord::updateCoords(const double lat, const double lon, const int32_t alt) {
{
int32_t iLat = lat * 1e+7; int32_t iLat = lat * 1e+7;
int32_t iLon = lon * 1e+7; int32_t iLon = lon * 1e+7;
// If marked dirty or new coordinates // If marked dirty or new coordinates
@@ -65,8 +54,7 @@ void GeoCoord::updateCoords(const double lat, const double lon, const int32_t al
} }
} }
void GeoCoord::updateCoords(const float lat, const float lon, const int32_t alt) void GeoCoord::updateCoords(const float lat, const float lon, const int32_t alt) {
{
int32_t iLat = lat * 1e+7; int32_t iLat = lat * 1e+7;
int32_t iLon = lon * 1e+7; int32_t iLon = lon * 1e+7;
// If marked dirty or new coordinates // If marked dirty or new coordinates
@@ -83,8 +71,7 @@ void GeoCoord::updateCoords(const float lat, const float lon, const int32_t alt)
* Converts lat long coordinates from decimal degrees to degrees minutes seconds format. * Converts lat long coordinates from decimal degrees to degrees minutes seconds format.
* DD°MM'SS"C DDD°MM'SS"C * DD°MM'SS"C DDD°MM'SS"C
*/ */
void GeoCoord::latLongToDMS(const double lat, const double lon, DMS &dms) void GeoCoord::latLongToDMS(const double lat, const double lon, DMS &dms) {
{
if (lat < 0) if (lat < 0)
dms.latCP = 'S'; dms.latCP = 'S';
else else
@@ -120,8 +107,7 @@ void GeoCoord::latLongToDMS(const double lat, const double lon, DMS &dms)
* Converts lat long coordinates to UTM. * Converts lat long coordinates to UTM.
* based on this: https://github.com/walvok/LatLonToUTM/blob/master/latlon_utm.ino * based on this: https://github.com/walvok/LatLonToUTM/blob/master/latlon_utm.ino
*/ */
void GeoCoord::latLongToUTM(const double lat, const double lon, UTM &utm) void GeoCoord::latLongToUTM(const double lat, const double lon, UTM &utm) {
{
const std::string latBands = "CDEFGHJKLMNPQRSTUVWXX"; const std::string latBands = "CDEFGHJKLMNPQRSTUVWXX";
utm.zone = int((lon + 180) / 6 + 1); utm.zone = int((lon + 180) / 6 + 1);
@@ -154,18 +140,13 @@ void GeoCoord::latLongToUTM(const double lat, const double lon, UTM &utm)
double T = tan(latRad) * tan(latRad); double T = tan(latRad) * tan(latRad);
double C = eccPrimeSquared * cos(latRad) * cos(latRad); double C = eccPrimeSquared * cos(latRad) * cos(latRad);
double A = cos(latRad) * (lonRad - lonOriginRad); double A = cos(latRad) * (lonRad - lonOriginRad);
double M = double M = a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256) * latRad -
a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256) * latRad - (3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + 45 * eccSquared * eccSquared * eccSquared / 1024) * sin(2 * latRad) +
(3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + 45 * eccSquared * eccSquared * eccSquared / 1024) *
sin(2 * latRad) +
(15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * sin(4 * latRad) - (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * sin(4 * latRad) -
(35 * eccSquared * eccSquared * eccSquared / 3072) * sin(6 * latRad)); (35 * eccSquared * eccSquared * eccSquared / 3072) * sin(6 * latRad));
utm.easting = (double)(k0 * N * utm.easting = (double)(k0 * N * (A + (1 - T + C) * pow(A, 3) / 6 + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120) +
(A + (1 - T + C) * pow(A, 3) / 6 +
(5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120) +
500000.0); 500000.0);
utm.northing = utm.northing = (double)(k0 * (M + N * tan(latRad) *
(double)(k0 * (M + N * tan(latRad) *
(A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 + (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 +
(61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720))); (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720)));
@@ -174,8 +155,7 @@ void GeoCoord::latLongToUTM(const double lat, const double lon, UTM &utm)
} }
// Converts lat long coordinates to an MGRS. // Converts lat long coordinates to an MGRS.
void GeoCoord::latLongToMGRS(const double lat, const double lon, MGRS &mgrs) void GeoCoord::latLongToMGRS(const double lat, const double lon, MGRS &mgrs) {
{
const std::string e100kLetters[3] = {"ABCDEFGH", "JKLMNPQR", "STUVWXYZ"}; const std::string e100kLetters[3] = {"ABCDEFGH", "JKLMNPQR", "STUVWXYZ"};
const std::string n100kLetters[2] = {"ABCDEFGHJKLMNPQRSTUV", "FGHJKLMNPQRSTUVABCDE"}; const std::string n100kLetters[2] = {"ABCDEFGHJKLMNPQRSTUV", "FGHJKLMNPQRSTUVABCDE"};
UTM utm; UTM utm;
@@ -194,8 +174,7 @@ void GeoCoord::latLongToMGRS(const double lat, const double lon, MGRS &mgrs)
* Converts lat long coordinates to Ordnance Survey Grid Reference (UK National Grid Ref). * Converts lat long coordinates to Ordnance Survey Grid Reference (UK National Grid Ref).
* Based on: https://www.movable-type.co.uk/scripts/latlong-os-gridref.html * Based on: https://www.movable-type.co.uk/scripts/latlong-os-gridref.html
*/ */
void GeoCoord::latLongToOSGR(const double lat, const double lon, OSGR &osgr) void GeoCoord::latLongToOSGR(const double lat, const double lon, OSGR &osgr) {
{
const char letter[] = "ABCDEFGHJKLMNOPQRSTUVWXYZ"; // No 'I' in OSGR const char letter[] = "ABCDEFGHJKLMNOPQRSTUVWXYZ"; // No 'I' in OSGR
double a = 6377563.396; // Airy 1830 semi-major axis double a = 6377563.396; // Airy 1830 semi-major axis
double b = 6356256.909; // Airy 1830 semi-minor axis double b = 6356256.909; // Airy 1830 semi-minor axis
@@ -236,8 +215,7 @@ void GeoCoord::latLongToOSGR(const double lat, const double lon, OSGR &osgr)
double deltaLambda = lambda - lambda0; double deltaLambda = lambda - lambda0;
double deltaLambda2 = deltaLambda * deltaLambda; double deltaLambda2 = deltaLambda * deltaLambda;
double northing = double northing = I + II * deltaLambda2 + III * deltaLambda2 * deltaLambda2 + IIIA * deltaLambda2 * deltaLambda2 * deltaLambda2;
I + II * deltaLambda2 + III * deltaLambda2 * deltaLambda2 + IIIA * deltaLambda2 * deltaLambda2 * deltaLambda2;
double easting = e0 + IV * deltaLambda + V * deltaLambda2 * deltaLambda + VI * deltaLambda2 * deltaLambda2 * deltaLambda; double easting = e0 + IV * deltaLambda + V * deltaLambda2 * deltaLambda + VI * deltaLambda2 * deltaLambda2 * deltaLambda;
if (easting < 0 || easting > 700000 || northing < 0 || northing > 1300000) // Check if out of boundaries if (easting < 0 || easting > 700000 || northing < 0 || northing > 1300000) // Check if out of boundaries
@@ -258,8 +236,7 @@ void GeoCoord::latLongToOSGR(const double lat, const double lon, OSGR &osgr)
* Converts lat long coordinates to Open Location Code. * Converts lat long coordinates to Open Location Code.
* Based on: https://github.com/google/open-location-code/blob/main/c/src/olc.c * Based on: https://github.com/google/open-location-code/blob/main/c/src/olc.c
*/ */
void GeoCoord::latLongToOLC(double lat, double lon, OLC &olc) void GeoCoord::latLongToOLC(double lat, double lon, OLC &olc) {
{
char tempCode[] = "1234567890abc"; char tempCode[] = "1234567890abc";
const char kAlphabet[] = "23456789CFGHJMPQRVWX"; const char kAlphabet[] = "23456789CFGHJMPQRVWX";
double latitude; double latitude;
@@ -331,8 +308,7 @@ void GeoCoord::latLongToOLC(double lat, double lon, OLC &olc)
} }
// Converts the coordinate in WGS84 datum to the OSGB36 datum. // Converts the coordinate in WGS84 datum to the OSGB36 datum.
void GeoCoord::convertWGS84ToOSGB36(const double lat, const double lon, double &osgb_Latitude, double &osgb_Longitude) void GeoCoord::convertWGS84ToOSGB36(const double lat, const double lon, double &osgb_Latitude, double &osgb_Longitude) {
{
// Convert lat long to cartesian // Convert lat long to cartesian
double phi = toRadians(lat); double phi = toRadians(lat);
double lambda = toRadians(lon); double lambda = toRadians(lon);
@@ -372,13 +348,13 @@ void GeoCoord::convertWGS84ToOSGB36(const double lat, const double lon, double &
p - airyEcc * airyA * cosBeta * cosBeta * cosBeta); // leave in radians p - airyEcc * airyA * cosBeta * cosBeta * cosBeta); // leave in radians
osgb_Longitude = atan2(osgbY, osgbX); // leave in radians osgb_Longitude = atan2(osgbY, osgbX); // leave in radians
// osgb height = p*cos(osgb.latitude) + osgbZ*sin(osgb.latitude) - // osgb height = p*cos(osgb.latitude) + osgbZ*sin(osgb.latitude) -
//(airyA*airyA/(airyA / sqrt(1 - airyEcc*sin(osgb.latitude)*sin(osgb.latitude)))); // Not used, no OSTN data //(airyA*airyA/(airyA / sqrt(1 -
// airyEcc*sin(osgb.latitude)*sin(osgb.latitude)))); // Not used, no OSTN data
} }
/// Ported from my old java code, returns distance in meters along the globe /// Ported from my old java code, returns distance in meters along the globe
/// surface (by Haversine formula) /// surface (by Haversine formula)
float GeoCoord::latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b) float GeoCoord::latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b) {
{
// Don't do math if the points are the same // Don't do math if the points are the same
if (lat_a == lat_b && lng_a == lng_b) if (lat_a == lat_b && lng_a == lng_b)
return 0.0; return 0.0;
@@ -414,8 +390,7 @@ float GeoCoord::latLongToMeter(double lat_a, double lng_a, double lat_b, double
* @return Bearing from point 1 to point 2 in radians. A value of 0 means due * @return Bearing from point 1 to point 2 in radians. A value of 0 means due
* north. * north.
*/ */
float GeoCoord::bearing(double lat1, double lon1, double lat2, double lon2) float GeoCoord::bearing(double lat1, double lon1, double lat2, double lon2) {
{
double lat1Rad = toRadians(lat1); double lat1Rad = toRadians(lat1);
double lat2Rad = toRadians(lat2); double lat2Rad = toRadians(lat2);
double deltaLonRad = toRadians(lon2 - lon1); double deltaLonRad = toRadians(lon2 - lon1);
@@ -431,8 +406,7 @@ float GeoCoord::bearing(double lat1, double lon1, double lat2, double lon2)
* The range in meters * The range in meters
* @return range in radians on a great circle * @return range in radians on a great circle
*/ */
float GeoCoord::rangeMetersToRadians(double range_meters) float GeoCoord::rangeMetersToRadians(double range_meters) {
{
// 1 nm is 1852 meters // 1 nm is 1852 meters
double distance_nm = range_meters * 1852; double distance_nm = range_meters * 1852;
return (PI / (180 * 60)) * distance_nm; return (PI / (180 * 60)) * distance_nm;
@@ -445,25 +419,20 @@ float GeoCoord::rangeMetersToRadians(double range_meters)
* The range in radians * The range in radians
* @return Range in meters on a great circle * @return Range in meters on a great circle
*/ */
float GeoCoord::rangeRadiansToMeters(double range_radians) float GeoCoord::rangeRadiansToMeters(double range_radians) {
{
double distance_nm = ((180 * 60) / PI) * range_radians; double distance_nm = ((180 * 60) / PI) * range_radians;
// 1 meter is 0.000539957 nm // 1 meter is 0.000539957 nm
return distance_nm * 0.000539957; return distance_nm * 0.000539957;
} }
// Find distance from point to passed in point // Find distance from point to passed in point
int32_t GeoCoord::distanceTo(const GeoCoord &pointB) int32_t GeoCoord::distanceTo(const GeoCoord &pointB) {
{ return latLongToMeter(this->getLatitude() * 1e-7, this->getLongitude() * 1e-7, pointB.getLatitude() * 1e-7, pointB.getLongitude() * 1e-7);
return latLongToMeter(this->getLatitude() * 1e-7, this->getLongitude() * 1e-7, pointB.getLatitude() * 1e-7,
pointB.getLongitude() * 1e-7);
} }
// Find bearing from point to passed in point // Find bearing from point to passed in point
int32_t GeoCoord::bearingTo(const GeoCoord &pointB) int32_t GeoCoord::bearingTo(const GeoCoord &pointB) {
{ return bearing(this->getLatitude() * 1e-7, this->getLongitude() * 1e-7, pointB.getLatitude() * 1e-7, pointB.getLongitude() * 1e-7);
return bearing(this->getLatitude() * 1e-7, this->getLongitude() * 1e-7, pointB.getLatitude() * 1e-7,
pointB.getLongitude() * 1e-7);
} }
/** /**
@@ -475,8 +444,7 @@ int32_t GeoCoord::bearingTo(const GeoCoord &pointB)
* range in meters * range in meters
* @return GeoCoord object of point at bearing and range from initial point * @return GeoCoord object of point at bearing and range from initial point
*/ */
std::shared_ptr<GeoCoord> GeoCoord::pointAtDistance(double bearing, double range_meters) std::shared_ptr<GeoCoord> GeoCoord::pointAtDistance(double bearing, double range_meters) {
{
double range_radians = rangeMetersToRadians(range_meters); double range_radians = rangeMetersToRadians(range_meters);
double lat1 = this->getLatitude() * 1e-7; double lat1 = this->getLatitude() * 1e-7;
double lon1 = this->getLongitude() * 1e-7; double lon1 = this->getLongitude() * 1e-7;
@@ -493,8 +461,7 @@ std::shared_ptr<GeoCoord> GeoCoord::pointAtDistance(double bearing, double range
* The bearing in string format * The bearing in string format
* @return Bearing in degrees * @return Bearing in degrees
*/ */
unsigned int GeoCoord::bearingToDegrees(const char *bearing) unsigned int GeoCoord::bearingToDegrees(const char *bearing) {
{
if (strcmp(bearing, "N") == 0) if (strcmp(bearing, "N") == 0)
return 0; return 0;
else if (strcmp(bearing, "NNE") == 0) else if (strcmp(bearing, "NNE") == 0)
@@ -537,8 +504,7 @@ unsigned int GeoCoord::bearingToDegrees(const char *bearing)
* The bearing in degrees * The bearing in degrees
* @return Bearing in string format * @return Bearing in string format
*/ */
const char *GeoCoord::degreesToBearing(unsigned int degrees) const char *GeoCoord::degreesToBearing(unsigned int degrees) {
{
if (degrees >= 348 || degrees < 11) if (degrees >= 348 || degrees < 11)
return "N"; return "N";
else if (degrees >= 11 && degrees < 34) else if (degrees >= 11 && degrees < 34)
@@ -575,8 +541,7 @@ const char *GeoCoord::degreesToBearing(unsigned int degrees)
return "N"; return "N";
} }
double GeoCoord::pow_neg(double base, double exponent) double GeoCoord::pow_neg(double base, double exponent) {
{
if (exponent == 0) { if (exponent == 0) {
return 1; return 1;
} else if (exponent > 0) { } else if (exponent > 0) {
@@ -585,12 +550,6 @@ double GeoCoord::pow_neg(double base, double exponent)
return 1 / pow(base, -exponent); return 1 / pow(base, -exponent);
} }
double GeoCoord::toRadians(double deg) double GeoCoord::toRadians(double deg) { return deg * PI / 180; }
{
return deg * PI / 180;
}
double GeoCoord::toDegrees(double r) double GeoCoord::toDegrees(double r) { return r * 180 / PI; }
{
return r * 180 / PI;
}
+3 -4
View File
@@ -57,9 +57,8 @@ struct OLC {
char code[OLC_CODE_LEN + 1]; // +1 for null termination char code[OLC_CODE_LEN + 1]; // +1 for null termination
}; };
class GeoCoord class GeoCoord {
{ private:
private:
int32_t _latitude = 0; int32_t _latitude = 0;
int32_t _longitude = 0; int32_t _longitude = 0;
int32_t _altitude = 0; int32_t _altitude = 0;
@@ -74,7 +73,7 @@ class GeoCoord
void setCoords(); void setCoords();
public: public:
GeoCoord(); GeoCoord();
GeoCoord(int32_t lat, int32_t lon, int32_t alt); GeoCoord(int32_t lat, int32_t lon, int32_t alt);
GeoCoord(double lat, double lon, int32_t alt); GeoCoord(double lat, double lon, int32_t alt);
+12 -17
View File
@@ -19,14 +19,12 @@
* ------------------------------------------- * -------------------------------------------
*/ */
uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_PositionLite &pos, const char *name, bool isCaltopoMode) uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_PositionLite &pos, const char *name, bool isCaltopoMode) {
{
GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude); GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
char type = isCaltopoMode ? 'P' : 'N'; char type = isCaltopoMode ? 'P' : 'N';
uint32_t len = snprintf(buf, bufsz, "\r\n$G%cWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", type, geoCoord.getDMSLatDeg(), uint32_t len = snprintf(buf, bufsz, "\r\n$G%cWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", type, geoCoord.getDMSLatDeg(),
(abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(), (abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(), geoCoord.getDMSLonDeg(),
geoCoord.getDMSLonDeg(), (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6, (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6, geoCoord.getDMSLonCP(), name);
geoCoord.getDMSLonCP(), name);
uint32_t chk = 0; uint32_t chk = 0;
for (uint32_t i = 1; i < len; i++) { for (uint32_t i = 1; i < len; i++) {
chk ^= buf[i]; chk ^= buf[i];
@@ -35,14 +33,12 @@ uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_PositionLite &pos, c
return len; return len;
} }
uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const char *name, bool isCaltopoMode) uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const char *name, bool isCaltopoMode) {
{
GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude); GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
char type = isCaltopoMode ? 'P' : 'N'; char type = isCaltopoMode ? 'P' : 'N';
uint32_t len = snprintf(buf, bufsz, "$G%cWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", type, geoCoord.getDMSLatDeg(), uint32_t len = snprintf(buf, bufsz, "$G%cWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", type, geoCoord.getDMSLatDeg(),
(abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(), (abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(), geoCoord.getDMSLonDeg(),
geoCoord.getDMSLonDeg(), (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6, (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6, geoCoord.getDMSLonCP(), name);
geoCoord.getDMSLonCP(), name);
uint32_t chk = 0; uint32_t chk = 0;
for (uint32_t i = 1; i < len; i++) { for (uint32_t i = 1; i < len; i++) {
chk ^= buf[i]; chk ^= buf[i];
@@ -66,14 +62,14 @@ uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const
* 8 Horizontal Dilution of precision (meters) * 8 Horizontal Dilution of precision (meters)
* 9 Antenna Altitude above/below mean-sea-level (geoid) (in meters) * 9 Antenna Altitude above/below mean-sea-level (geoid) (in meters)
* 10 Units of antenna altitude, meters * 10 Units of antenna altitude, meters
* 11 Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level * 11 Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level (geoid), "-" means
* below ellipsoid 12 Units of geoidal separation, meters 13 Age of differential GPS data, time in seconds since last SC104 type 1 * mean-sea-level below ellipsoid 12 Units of geoidal separation, meters 13 Age of differential GPS data, time in
* or 9 update, null field when DGPS is not used 14 Differential reference station ID, 0000-1023 15 Checksum * seconds since last SC104 type 1 or 9 update, null field when DGPS is not used 14 Differential reference station ID,
* 0000-1023 15 Checksum
* ------------------------------------------- * -------------------------------------------
*/ */
uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos) uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos) {
{
GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude); GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
time_t timestamp = pos.timestamp; time_t timestamp = pos.timestamp;
@@ -84,8 +80,7 @@ uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos)
t = gmtime(&timestamp); t = gmtime(&timestamp);
} }
uint32_t len = snprintf( uint32_t len = snprintf(buf, bufsz, "$GNGGA,%02d%02d%02d.%02d,%02d%07.4f,%c,%03d%07.4f,%c,%u,%02u,%04u,%04d,%c,%04d,%c,%d,%04d", t->tm_hour,
buf, bufsz, "$GNGGA,%02d%02d%02d.%02d,%02d%07.4f,%c,%03d%07.4f,%c,%u,%02u,%04u,%04d,%c,%04d,%c,%d,%04d", t->tm_hour,
t->tm_min, t->tm_sec, pos.timestamp_millis_adjust, geoCoord.getDMSLatDeg(), t->tm_min, t->tm_sec, pos.timestamp_millis_adjust, geoCoord.getDMSLatDeg(),
(abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(), geoCoord.getDMSLonDeg(), (abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(), geoCoord.getDMSLonDeg(),
(abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6, geoCoord.getDMSLonCP(), pos.fix_quality, (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6, geoCoord.getDMSLonCP(), pos.fix_quality,
+30 -44
View File
@@ -12,22 +12,18 @@ uint32_t lastSetFromPhoneNtpOrGps = 0;
static uint32_t lastTimeValidationWarning = 0; static uint32_t lastTimeValidationWarning = 0;
static const uint32_t TIME_VALIDATION_WARNING_INTERVAL_MS = 15000; // 15 seconds static const uint32_t TIME_VALIDATION_WARNING_INTERVAL_MS = 15000; // 15 seconds
RTCQuality getRTCQuality() RTCQuality getRTCQuality() { return currentQuality; }
{
return currentQuality;
}
// stuff that really should be in in the instance instead... // stuff that really should be in in the instance instead...
static uint32_t static uint32_t timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds
timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds to that time // to that time
static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock
/** /**
* Reads the current date and time from the RTC module and updates the system time. * Reads the current date and time from the RTC module and updates the system time.
* @return True if the RTC was successfully read and the system time was updated, false otherwise. * @return True if the RTC was successfully read and the system time was updated, false otherwise.
*/ */
RTCSetResult readFromRTC() RTCSetResult readFromRTC() {
{
struct timeval tv; /* btw settimeofday() is helpful here too*/ struct timeval tv; /* btw settimeofday() is helpful here too*/
#ifdef RV3028_RTC #ifdef RV3028_RTC
if (rtc_found.address == RV3028_RTC) { if (rtc_found.address == RV3028_RTC) {
@@ -58,8 +54,8 @@ RTCSetResult readFromRTC()
} }
#endif #endif
LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1, LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); t.tm_min, t.tm_sec, printableEpoch);
if (currentQuality == RTCQualityNone) { if (currentQuality == RTCQualityNone) {
timeStartMsec = now; timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec; zeroOffsetSecs = tv.tv_sec;
@@ -100,8 +96,8 @@ RTCSetResult readFromRTC()
} }
#endif #endif
LOG_DEBUG("Read RTC time from %s getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", rtc.getChipName(), t.tm_year + 1900, LOG_DEBUG("Read RTC time from %s getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", rtc.getChipName(), t.tm_year + 1900, t.tm_mon + 1,
t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
if (currentQuality == RTCQualityNone) { if (currentQuality == RTCQualityNone) {
timeStartMsec = now; timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec; zeroOffsetSecs = tv.tv_sec;
@@ -125,8 +121,8 @@ RTCSetResult readFromRTC()
tv.tv_usec = 0; tv.tv_usec = 0;
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
LOG_DEBUG("Read RTC time from RX8130CE getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, LOG_DEBUG("Read RTC time from RX8130CE getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
#ifdef BUILD_EPOCH #ifdef BUILD_EPOCH
if (tv.tv_sec < BUILD_EPOCH) { if (tv.tv_sec < BUILD_EPOCH) {
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) { if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
@@ -166,8 +162,7 @@ RTCSetResult readFromRTC()
* *
* If we haven't yet set our RTC this boot, set it from a GPS derived time * If we haven't yet set our RTC this boot, set it from a GPS derived time
*/ */
RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) {
{
static uint32_t lastSetMsec = 0; static uint32_t lastSetMsec = 0;
uint32_t now = millis(); uint32_t now = millis();
uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
@@ -183,8 +178,8 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
// Calculate max allowed time safely to avoid overflow in logging // Calculate max allowed time safely to avoid overflow in logging
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS; uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime; uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, (uint32_t)BUILD_EPOCH,
(uint32_t)BUILD_EPOCH, maxAllowedPrintable); maxAllowedPrintable);
lastTimeValidationWarning = millis(); lastTimeValidationWarning = millis();
} }
return RTCSetResultInvalidTime; return RTCSetResultInvalidTime;
@@ -194,8 +189,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
bool shouldSet; bool shouldSet;
if (forceUpdate) { if (forceUpdate) {
shouldSet = true; shouldSet = true;
LOG_DEBUG("Override current RTC quality (%s) with incoming time of RTC quality of %s", RtcName(currentQuality), LOG_DEBUG("Override current RTC quality (%s) with incoming time of RTC quality of %s", RtcName(currentQuality), RtcName(q));
RtcName(q));
} else if (q > currentQuality) { } else if (q > currentQuality) {
shouldSet = true; shouldSet = true;
LOG_DEBUG("Upgrade time to quality %s", RtcName(q)); LOG_DEBUG("Upgrade time to quality %s", RtcName(q));
@@ -232,8 +226,8 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
#endif #endif
tm *t = gmtime(&tv->tv_sec); tm *t = gmtime(&tv->tv_sec);
rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min,
t->tm_hour, t->tm_min, t->tm_sec, printableEpoch); t->tm_sec, printableEpoch);
} else { } else {
LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address); LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address);
} }
@@ -252,8 +246,8 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
#endif #endif
tm *t = gmtime(&tv->tv_sec); tm *t = gmtime(&tv->tv_sec);
rtc.setDateTime(*t); rtc.setDateTime(*t);
LOG_DEBUG("%s setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", rtc.getChipName(), t->tm_year + 1900, t->tm_mon + 1, LOG_DEBUG("%s setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", rtc.getChipName(), t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour,
t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch); t->tm_min, t->tm_sec, printableEpoch);
} else { } else {
LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address); LOG_WARN("RTC not found (found address 0x%02X)", rtc_found.address);
} }
@@ -266,8 +260,8 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
#endif #endif
tm *t = gmtime(&tv->tv_sec); tm *t = gmtime(&tv->tv_sec);
if (rtc.setTime(*t)) { if (rtc.setTime(*t)) {
LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min,
t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch); t->tm_sec, printableEpoch);
} else { } else {
LOG_WARN("Failed to set time for RX8130CE"); LOG_WARN("Failed to set time for RX8130CE");
} }
@@ -287,8 +281,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
} }
} }
const char *RtcName(RTCQuality quality) const char *RtcName(RTCQuality quality) {
{
switch (quality) { switch (quality) {
case RTCQualityNone: case RTCQualityNone:
return "None"; return "None";
@@ -312,11 +305,10 @@ const char *RtcName(RTCQuality quality)
* @param t The time to potentially set the RTC to. * @param t The time to potentially set the RTC to.
* @return True if the RTC was set to the provided time, false otherwise. * @return True if the RTC was set to the provided time, false otherwise.
*/ */
RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t) RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t) {
{
/* Convert to unix time /* 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 The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z). 1, 1970 (midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
*/ */
// horrible hack to make mktime TZ agnostic - best practise according to // horrible hack to make mktime TZ agnostic - best practise according to
// https://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html // https://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html
@@ -337,8 +329,8 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
// Calculate max allowed time safely to avoid overflow in logging // Calculate max allowed time safely to avoid overflow in logging
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS; uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime; uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
LOG_WARN("Ignore time (%lu) too far in the future (build epoch: %lu, max allowed: %lu)!", printableEpoch, LOG_WARN("Ignore time (%lu) too far in the future (build epoch: %lu, max allowed: %lu)!", printableEpoch, (uint32_t)BUILD_EPOCH,
(uint32_t)BUILD_EPOCH, maxAllowedPrintable); maxAllowedPrintable);
lastTimeValidationWarning = millis(); lastTimeValidationWarning = millis();
} }
return RTCSetResultInvalidTime; return RTCSetResultInvalidTime;
@@ -359,8 +351,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
* *
* @return The timezone offset in seconds. * @return The timezone offset in seconds.
*/ */
int32_t getTZOffset() int32_t getTZOffset() {
{
#if MESHTASTIC_EXCLUDE_TZ #if MESHTASTIC_EXCLUDE_TZ
return 0; return 0;
#else #else
@@ -377,8 +368,7 @@ int32_t getTZOffset()
* *
* @return The current time in seconds since the Unix epoch. * @return The current time in seconds since the Unix epoch.
*/ */
uint32_t getTime(bool local) uint32_t getTime(bool local) {
{
if (local) { if (local) {
return (((uint32_t)millis() - timeStartMsec) / 1000) + zeroOffsetSecs + getTZOffset(); return (((uint32_t)millis() - timeStartMsec) / 1000) + zeroOffsetSecs + getTZOffset();
} else { } else {
@@ -392,13 +382,9 @@ uint32_t getTime(bool local)
* @param minQuality The minimum quality of the RTC time required for it to be considered valid. * @param minQuality The minimum quality of the RTC time required for it to be considered valid.
* @return The current time from the RTC if it meets the minimum quality requirement, or 0 if the time is not valid. * @return The current time from the RTC if it meets the minimum quality requirement, or 0 if the time is not valid.
*/ */
uint32_t getValidTime(RTCQuality minQuality, bool local) uint32_t getValidTime(RTCQuality minQuality, bool local) { return (currentQuality >= minQuality) ? getTime(local) : 0; }
{
return (currentQuality >= minQuality) ? getTime(local) : 0;
}
time_t gm_mktime(struct tm *tm) time_t gm_mktime(struct tm *tm) {
{
#if !MESHTASTIC_EXCLUDE_TZ #if !MESHTASTIC_EXCLUDE_TZ
time_t result = 0; time_t result = 0;
+16 -18
View File
@@ -337,8 +337,8 @@ static const uint8_t _message_SAVE_10[] = {
// As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR. // As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR.
// BBR will survive a restart, and power off for a while, but modules with small backup // BBR will survive a restart, and power off for a while, but modules with small backup
// batteries or super caps will not retain the config for a long power off time. // batteries or super caps will not retain the config for a long power off time.
// for all configurations using sleep / low power modes, V_BCKP needs to be hooked to permanent power for fast aquisition after // for all configurations using sleep / low power modes, V_BCKP needs to be hooked to permanent power for fast
// sleep // aquisition after sleep
// VALSET Commands for M10 // VALSET Commands for M10
// Please refer to the M10 Protocol Specification: // Please refer to the M10 Protocol Specification:
@@ -370,11 +370,13 @@ EXTINTACTIVITY U4 0 no ext ints
LIMITPEAKCURRENT L 1 LIMITPEAKCURRENT L 1
// Ram layer config message: // Ram layer config message:
// b5 62 06 8a 26 00 00 01 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01 10 00 d0 // b5 62 06 8a 26 00 00 01 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01
10 00 d0
// 10 01 8b de // 10 01 8b de
// BBR layer config message: // BBR layer config message:
// b5 62 06 8a 26 00 00 02 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01 10 00 d0 // b5 62 06 8a 26 00 00 02 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01
10 00 d0
// 10 01 8c 03 // 10 01 8c 03
*/ */
static const uint8_t _message_VALSET_PM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, static const uint8_t _message_VALSET_PM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40,
@@ -396,21 +398,21 @@ CFG-ITFM replaced by 5 valset messages which can be combined into one for RAM an
b5 62 06 8a 0e 00 00 01 00 00 0d 00 41 10 01 13 00 41 10 01 63 c6 b5 62 06 8a 0e 00 00 01 00 00 0d 00 41 10 01 13 00 41 10 01 63 c6
*/ */
static const uint8_t _message_VALSET_ITFM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x41, static const uint8_t _message_VALSET_ITFM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x41, 0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01};
0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01}; static const uint8_t _message_VALSET_ITFM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x0d, 0x00, 0x41, 0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01};
static const uint8_t _message_VALSET_ITFM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x0d, 0x00, 0x41,
0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01};
// Turn off all NMEA messages: // Turn off all NMEA messages:
// Ram layer config message: // Ram layer config message:
// b5 62 06 8a 22 00 00 01 00 00 c0 00 91 20 00 ca 00 91 20 00 c5 00 91 20 00 ac 00 91 20 00 b1 00 91 20 00 bb 00 91 20 00 40 8f // b5 62 06 8a 22 00 00 01 00 00 c0 00 91 20 00 ca 00 91 20 00 c5 00 91 20 00 ac 00 91 20 00 b1 00 91 20 00 bb 00 91 20
// 00 40 8f
// Disable GLL, GSV, VTG messages in BBR layer // Disable GLL, GSV, VTG messages in BBR layer
// BBR layer config message: // BBR layer config message:
// b5 62 06 8a 13 00 00 02 00 00 ca 00 91 20 00 c5 00 91 20 00 b1 00 91 20 00 f8 4e // b5 62 06 8a 13 00 00 02 00 00 ca 00 91 20 00 c5 00 91 20 00 b1 00 91 20 00 f8 4e
static const uint8_t _message_VALSET_DISABLE_NMEA_RAM[] = { static const uint8_t _message_VALSET_DISABLE_NMEA_RAM[] = {
/*0x00, 0x01, 0x00, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00 */ /*0x00, 0x01, 0x00, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00
*/
0x00, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x91, 0x20, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, 0x00, 0x91, 0x00, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x91, 0x20, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, 0x00, 0x91,
0x20, 0x00, 0xac, 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00, 0xbb, 0x00, 0x91, 0x20, 0x00}; 0x20, 0x00, 0xac, 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00, 0xbb, 0x00, 0x91, 0x20, 0x00};
@@ -437,14 +439,10 @@ static const uint8_t _message_VALSET_DISABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x0
static const uint8_t _message_VALSET_DISABLE_TXT_INFO_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03}; static const uint8_t _message_VALSET_DISABLE_TXT_INFO_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03};
static const uint8_t _message_VALSET_DISABLE_TXT_INFO_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03}; static const uint8_t _message_VALSET_DISABLE_TXT_INFO_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03};
static const uint8_t _message_VALSET_ENABLE_NMEA_RAM[] = {0x00, 0x01, 0x00, 0x00, 0xbb, 0x00, 0x91, static const uint8_t _message_VALSET_ENABLE_NMEA_RAM[] = {0x00, 0x01, 0x00, 0x00, 0xbb, 0x00, 0x91, 0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01};
0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01}; static const uint8_t _message_VALSET_ENABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, 0xbb, 0x00, 0x91, 0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01};
static const uint8_t _message_VALSET_ENABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, 0xbb, 0x00, 0x91, static const uint8_t _message_VALSET_DISABLE_SBAS_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x31, 0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00};
0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01}; static const uint8_t _message_VALSET_DISABLE_SBAS_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x31, 0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00};
static const uint8_t _message_VALSET_DISABLE_SBAS_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x31,
0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00};
static const uint8_t _message_VALSET_DISABLE_SBAS_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x31,
0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00};
/* /*
Operational issues with the M10: Operational issues with the M10:
+9 -18
View File
@@ -33,8 +33,7 @@
*/ */
// Constructor // Constructor
EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) {
{
// Set dimensions in OLEDDisplay base class // Set dimensions in OLEDDisplay base class
this->geometry = GEOMETRY_RAWMODE; this->geometry = GEOMETRY_RAWMODE;
this->displayWidth = EINK_WIDTH; this->displayWidth = EINK_WIDTH;
@@ -52,8 +51,7 @@ EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY
/** /**
* Force a display update if we haven't drawn within the specified msecLimit * Force a display update if we haven't drawn within the specified msecLimit
*/ */
bool EInkDisplay::forceDisplay(uint32_t msecLimit) bool EInkDisplay::forceDisplay(uint32_t msecLimit) {
{
// No need to grab this lock because we are on our own SPI bus // No need to grab this lock because we are on our own SPI bus
// concurrency::LockGuard g(spiLock); // concurrency::LockGuard g(spiLock);
@@ -102,15 +100,13 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
} }
// End the update process - virtual method, overriden in derived class // End the update process - virtual method, overriden in derived class
void EInkDisplay::endUpdate() void EInkDisplay::endUpdate() {
{
// Power off display hardware, then deep-sleep (Except Wireless Paper V1.1, no deep-sleep) // Power off display hardware, then deep-sleep (Except Wireless Paper V1.1, no deep-sleep)
adafruitDisplay->hibernate(); adafruitDisplay->hibernate();
} }
// Write the buffer to the display memory // Write the buffer to the display memory
void EInkDisplay::display(void) void EInkDisplay::display(void) {
{
// We don't allow regular 'dumb' display() calls to draw on eink until we've shown // We don't allow regular 'dumb' display() calls to draw on eink until we've shown
// at least one forceDisplay() keyframe. This prevents flashing when we should the critical // at least one forceDisplay() keyframe. This prevents flashing when we should the critical
// bootscreen (that we want to look nice) // bootscreen (that we want to look nice)
@@ -121,20 +117,15 @@ void EInkDisplay::display(void)
} }
// Send a command to the display (low level function) // Send a command to the display (low level function)
void EInkDisplay::sendCommand(uint8_t com) void EInkDisplay::sendCommand(uint8_t com) {
{
(void)com; (void)com;
// Drop all commands to device (we just update the buffer) // Drop all commands to device (we just update the buffer)
} }
void EInkDisplay::setDetected(uint8_t detected) void EInkDisplay::setDetected(uint8_t detected) { (void)detected; }
{
(void)detected;
}
// Connect to the display - variant specific // Connect to the display - variant specific
bool EInkDisplay::connect() bool EInkDisplay::connect() {
{
LOG_INFO("Do EInk init"); LOG_INFO("Do EInk init");
#ifdef PIN_EINK_EN #ifdef PIN_EINK_EN
@@ -269,8 +260,8 @@ bool EInkDisplay::connect()
hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS
// Create GxEPD2 object // Create GxEPD2 object
adafruitDisplay = new GxEPD2_Multi<GXEPD2_DRIVER_0, GXEPD2_DRIVER_1>((uint8_t)displayModel, PIN_EINK_CS, PIN_EINK_DC, adafruitDisplay =
PIN_EINK_RES, PIN_EINK_BUSY, *hspi); new GxEPD2_Multi<GXEPD2_DRIVER_0, GXEPD2_DRIVER_1>((uint8_t)displayModel, PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
// Init GxEPD2 // Init GxEPD2
adafruitDisplay->init(); adafruitDisplay->init();
+4 -5
View File
@@ -22,13 +22,12 @@
* turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted? * turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted?
* Suggestion: perhaps similar to HELTEC_WIRELESS_PAPER issue, which resolved with rtc_gpio_hold_dis() * Suggestion: perhaps similar to HELTEC_WIRELESS_PAPER issue, which resolved with rtc_gpio_hold_dis()
*/ */
class EInkDisplay : public OLEDDisplay class EInkDisplay : public OLEDDisplay {
{
/// How often should we update the display /// How often should we update the display
/// thereafter we do once per 5 minutes /// thereafter we do once per 5 minutes
uint32_t slowUpdateMsec = 5 * 60 * 1000; uint32_t slowUpdateMsec = 5 * 60 * 1000;
public: public:
/* constructor /* constructor
FIXME - the parameters are not used, just a temporary hack to keep working like the old displays FIXME - the parameters are not used, just a temporary hack to keep working like the old displays
*/ */
@@ -57,7 +56,7 @@ class EInkDisplay : public OLEDDisplay
*/ */
void setDetected(uint8_t detected); void setDetected(uint8_t detected);
protected: protected:
// the header size of the buffer used, e.g. for the SPI command header // the header size of the buffer used, e.g. for the SPI command header
virtual int getBufferOffset(void) override { return 0; } virtual int getBufferOffset(void) override { return 0; }
@@ -88,7 +87,7 @@ class EInkDisplay : public OLEDDisplay
SPIClass *spi1 = NULL; SPIClass *spi1 = NULL;
#endif #endif
private: private:
// FIXME quick hack to limit drawing to a very slow rate // FIXME quick hack to limit drawing to a very slow rate
uint32_t lastDrawMsec = 0; uint32_t lastDrawMsec = 0;
}; };
+34 -68
View File
@@ -6,8 +6,7 @@
// Constructor // Constructor
EInkDynamicDisplay::EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) EInkDynamicDisplay::EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus)
: EInkDisplay(address, sda, scl, geometry, i2cBus), NotifiedWorkerThread("EInkDynamicDisplay") : EInkDisplay(address, sda, scl, geometry, i2cBus), NotifiedWorkerThread("EInkDynamicDisplay") {
{
// If tracking ghost pixels, grab memory // If tracking ghost pixels, grab memory
#ifdef EINK_LIMIT_GHOSTING_PX #ifdef EINK_LIMIT_GHOSTING_PX
dirtyPixels = new uint8_t[EInkDisplay::displayBufferSize](); // Init with zeros dirtyPixels = new uint8_t[EInkDisplay::displayBufferSize](); // Init with zeros
@@ -15,8 +14,7 @@ EInkDynamicDisplay::EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDI
} }
// Destructor // Destructor
EInkDynamicDisplay::~EInkDynamicDisplay() EInkDynamicDisplay::~EInkDynamicDisplay() {
{
// If we were tracking ghost pixels, free the memory // If we were tracking ghost pixels, free the memory
#ifdef EINK_LIMIT_GHOSTING_PX #ifdef EINK_LIMIT_GHOSTING_PX
delete[] dirtyPixels; delete[] dirtyPixels;
@@ -24,29 +22,25 @@ EInkDynamicDisplay::~EInkDynamicDisplay()
} }
// Screen requests a BACKGROUND frame // Screen requests a BACKGROUND frame
void EInkDynamicDisplay::display() void EInkDynamicDisplay::display() {
{
addFrameFlag(BACKGROUND); addFrameFlag(BACKGROUND);
update(); update();
} }
// Screen requests a RESPONSIVE frame // Screen requests a RESPONSIVE frame
bool EInkDynamicDisplay::forceDisplay(uint32_t msecLimit) bool EInkDynamicDisplay::forceDisplay(uint32_t msecLimit) {
{
addFrameFlag(RESPONSIVE); addFrameFlag(RESPONSIVE);
return update(); // (Unutilized) Base class promises to return true if update ran return update(); // (Unutilized) Base class promises to return true if update ran
} }
// Add flag for the next frame // Add flag for the next frame
void EInkDynamicDisplay::addFrameFlag(frameFlagTypes flag) void EInkDynamicDisplay::addFrameFlag(frameFlagTypes flag) {
{
// OR the new flag into the existing flags // OR the new flag into the existing flags
this->frameFlags = (frameFlagTypes)(this->frameFlags | flag); this->frameFlags = (frameFlagTypes)(this->frameFlags | flag);
} }
// GxEPD2 code to set fast refresh // GxEPD2 code to set fast refresh
void EInkDynamicDisplay::configForFastRefresh() void EInkDynamicDisplay::configForFastRefresh() {
{
// Variant-specific code can go here // Variant-specific code can go here
#if defined(PRIVATE_HW) #if defined(PRIVATE_HW)
#else #else
@@ -56,8 +50,7 @@ void EInkDynamicDisplay::configForFastRefresh()
} }
// GxEPD2 code to set full refresh // GxEPD2 code to set full refresh
void EInkDynamicDisplay::configForFullRefresh() void EInkDynamicDisplay::configForFullRefresh() {
{
// Variant-specific code can go here // Variant-specific code can go here
#if defined(PRIVATE_HW) #if defined(PRIVATE_HW)
#else #else
@@ -67,8 +60,7 @@ void EInkDynamicDisplay::configForFullRefresh()
} }
// Run any relevant GxEPD2 code, so next update will use correct refresh type // Run any relevant GxEPD2 code, so next update will use correct refresh type
void EInkDynamicDisplay::applyRefreshMode() void EInkDynamicDisplay::applyRefreshMode() {
{
// Change from FULL to FAST // Change from FULL to FAST
if (currentConfig == FULL && refresh == FAST) { if (currentConfig == FULL && refresh == FAST) {
configForFastRefresh(); configForFastRefresh();
@@ -83,8 +75,7 @@ void EInkDynamicDisplay::applyRefreshMode()
} }
// Update fastRefreshCount // Update fastRefreshCount
void EInkDynamicDisplay::adjustRefreshCounters() void EInkDynamicDisplay::adjustRefreshCounters() {
{
if (refresh == FAST) if (refresh == FAST)
fastRefreshCount++; fastRefreshCount++;
@@ -93,8 +84,7 @@ void EInkDynamicDisplay::adjustRefreshCounters()
} }
// Trigger the display update by calling base class // Trigger the display update by calling base class
bool EInkDynamicDisplay::update() bool EInkDynamicDisplay::update() {
{
// Detemine the refresh mode to use, and start the update // Detemine the refresh mode to use, and start the update
bool refreshApproved = determineMode(); bool refreshApproved = determineMode();
if (refreshApproved) { if (refreshApproved) {
@@ -108,8 +98,7 @@ bool EInkDynamicDisplay::update()
} }
// Figure out who runs the post-update code // Figure out who runs the post-update code
void EInkDynamicDisplay::endOrDetach() void EInkDynamicDisplay::endOrDetach() {
{
// If the GxEPD2 version reports that it has the async modifications // If the GxEPD2 version reports that it has the async modifications
#ifdef HAS_EINK_ASYNCFULL #ifdef HAS_EINK_ASYNCFULL
if (previousRefresh == FULL) { if (previousRefresh == FULL) {
@@ -131,8 +120,8 @@ void EInkDynamicDisplay::endOrDetach()
// Fallback - If using an unmodified version of GxEPD2 for some reason // Fallback - If using an unmodified version of GxEPD2 for some reason
#else #else
if (previousRefresh == FULL || previousRefresh == FAST) { // If refresh wasn't skipped (on unspecified..) if (previousRefresh == FULL || previousRefresh == FAST) { // If refresh wasn't skipped (on unspecified..)
LOG_WARN( LOG_WARN("GxEPD2 version has not been modified to support async refresh; using fallback behavior. Please update "
"GxEPD2 version has not been modified to support async refresh; using fallback behavior. Please update lib_deps in " "lib_deps in "
"variant's platformio.ini file"); "variant's platformio.ini file");
EInkDisplay::endUpdate(); EInkDisplay::endUpdate();
} }
@@ -140,8 +129,7 @@ void EInkDynamicDisplay::endOrDetach()
} }
// Assess situation, pick a refresh type // Assess situation, pick a refresh type
bool EInkDynamicDisplay::determineMode() bool EInkDynamicDisplay::determineMode() {
{
checkInitialized(); checkInitialized();
checkForPromotion(); checkForPromotion();
#if defined(HAS_EINK_ASYNCFULL) #if defined(HAS_EINK_ASYNCFULL)
@@ -190,8 +178,7 @@ bool EInkDynamicDisplay::determineMode()
} }
// Is this the very first frame? // Is this the very first frame?
void EInkDynamicDisplay::checkInitialized() void EInkDynamicDisplay::checkInitialized() {
{
if (!initialized) { if (!initialized) {
// Undo GxEPD2_BW::partialWindow(), if set by developer in EInkDisplay::connect() // Undo GxEPD2_BW::partialWindow(), if set by developer in EInkDisplay::connect()
configForFullRefresh(); configForFullRefresh();
@@ -208,8 +195,7 @@ void EInkDynamicDisplay::checkInitialized()
} }
// Was a frame skipped (rate, display busy) that should have been a FAST refresh? // Was a frame skipped (rate, display busy) that should have been a FAST refresh?
void EInkDynamicDisplay::checkForPromotion() void EInkDynamicDisplay::checkForPromotion() {
{
// If a frame was skipped (rate, display busy), then promote a BACKGROUND frame // If a frame was skipped (rate, display busy), then promote a BACKGROUND frame
// Because we DID want a RESPONSIVE/COSMETIC/DEMAND_FULL frame last time, we just didn't get it // Because we DID want a RESPONSIVE/COSMETIC/DEMAND_FULL frame last time, we just didn't get it
@@ -230,8 +216,7 @@ void EInkDynamicDisplay::checkForPromotion()
} }
// Is it too soon for another frame of this type? // Is it too soon for another frame of this type?
void EInkDynamicDisplay::checkRateLimiting() void EInkDynamicDisplay::checkRateLimiting() {
{
// Sanity check: millis() overflow - just let the update run.. // Sanity check: millis() overflow - just let the update run..
if (previousRunMs > millis()) if (previousRunMs > millis())
return; return;
@@ -261,8 +246,7 @@ void EInkDynamicDisplay::checkRateLimiting()
} }
// Is this frame COSMETIC (splash screens?) // Is this frame COSMETIC (splash screens?)
void EInkDynamicDisplay::checkCosmetic() void EInkDynamicDisplay::checkCosmetic() {
{
// If a decision was already reached, don't run the check // If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED) if (refresh != UNSPECIFIED)
return; return;
@@ -276,8 +260,7 @@ void EInkDynamicDisplay::checkCosmetic()
} }
// Is this a one-off special circumstance, where we REALLY want a fast refresh? // Is this a one-off special circumstance, where we REALLY want a fast refresh?
void EInkDynamicDisplay::checkDemandingFast() void EInkDynamicDisplay::checkDemandingFast() {
{
// If a decision was already reached, don't run the check // If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED) if (refresh != UNSPECIFIED)
return; return;
@@ -291,8 +274,7 @@ void EInkDynamicDisplay::checkDemandingFast()
} }
// Does the new frame match the currently displayed image? // Does the new frame match the currently displayed image?
void EInkDynamicDisplay::checkFrameMatchesPrevious() void EInkDynamicDisplay::checkFrameMatchesPrevious() {
{
// If a decision was already reached, don't run the check // If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED) if (refresh != UNSPECIFIED)
return; return;
@@ -318,8 +300,7 @@ void EInkDynamicDisplay::checkFrameMatchesPrevious()
} }
// Have too many fast-refreshes occured consecutively, since last full refresh? // Have too many fast-refreshes occured consecutively, since last full refresh?
void EInkDynamicDisplay::checkConsecutiveFastRefreshes() void EInkDynamicDisplay::checkConsecutiveFastRefreshes() {
{
// If a decision was already reached, don't run the check // If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED) if (refresh != UNSPECIFIED)
return; return;
@@ -341,8 +322,7 @@ void EInkDynamicDisplay::checkConsecutiveFastRefreshes()
} }
// No objections, we can perform fast-refresh, if desired // No objections, we can perform fast-refresh, if desired
void EInkDynamicDisplay::checkFastRequested() void EInkDynamicDisplay::checkFastRequested() {
{
if (refresh != UNSPECIFIED) if (refresh != UNSPECIFIED)
return; return;
@@ -351,8 +331,7 @@ void EInkDynamicDisplay::checkFastRequested()
// If we want BACKGROUND to use fast. (FULL only when a limit is hit) // If we want BACKGROUND to use fast. (FULL only when a limit is hit)
refresh = FAST; refresh = FAST;
reason = BACKGROUND_USES_FAST; reason = BACKGROUND_USES_FAST;
LOG_DEBUG("refresh=FAST, reason=BACKGROUND_USES_FAST, fastRefreshCount=%lu, frameFlags=0x%x", fastRefreshCount, LOG_DEBUG("refresh=FAST, reason=BACKGROUND_USES_FAST, fastRefreshCount=%lu, frameFlags=0x%x", fastRefreshCount, frameFlags);
frameFlags);
#else #else
// If we do want to use FULL for BACKGROUND updates // If we do want to use FULL for BACKGROUND updates
refresh = FULL; refresh = FULL;
@@ -370,14 +349,10 @@ void EInkDynamicDisplay::checkFastRequested()
} }
// Reset the timer used for rate-limiting // Reset the timer used for rate-limiting
void EInkDynamicDisplay::resetRateLimiting() void EInkDynamicDisplay::resetRateLimiting() { previousRunMs = millis(); }
{
previousRunMs = millis();
}
// Generate a hash of this frame, to compare against previous update // Generate a hash of this frame, to compare against previous update
void EInkDynamicDisplay::hashImage() void EInkDynamicDisplay::hashImage() {
{
imageHash = 0; imageHash = 0;
// Sum all bytes of the image buffer together // Sum all bytes of the image buffer together
@@ -387,8 +362,7 @@ void EInkDynamicDisplay::hashImage()
} }
// Store the results of determineMode() for future use, and reset for next call // Store the results of determineMode() for future use, and reset for next call
void EInkDynamicDisplay::storeAndReset() void EInkDynamicDisplay::storeAndReset() {
{
previousFrameFlags = frameFlags; previousFrameFlags = frameFlags;
previousRefresh = refresh; previousRefresh = refresh;
previousReason = reason; previousReason = reason;
@@ -404,8 +378,7 @@ void EInkDynamicDisplay::storeAndReset()
#ifdef EINK_LIMIT_GHOSTING_PX #ifdef EINK_LIMIT_GHOSTING_PX
// Count how many ghost pixels the new image will display // Count how many ghost pixels the new image will display
void EInkDynamicDisplay::countGhostPixels() void EInkDynamicDisplay::countGhostPixels() {
{
// If a decision was already reached, don't run the check // If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED) if (refresh != UNSPECIFIED)
return; return;
@@ -434,8 +407,7 @@ void EInkDynamicDisplay::countGhostPixels()
} }
// Check if ghost pixel count exceeds the defined limit // Check if ghost pixel count exceeds the defined limit
void EInkDynamicDisplay::checkExcessiveGhosting() void EInkDynamicDisplay::checkExcessiveGhosting() {
{
// If a decision was already reached, don't run the check // If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED) if (refresh != UNSPECIFIED)
return; return;
@@ -451,16 +423,14 @@ void EInkDynamicDisplay::checkExcessiveGhosting()
} }
// Clear the dirty pixels array. Call when full-refresh cleans the display. // Clear the dirty pixels array. Call when full-refresh cleans the display.
void EInkDynamicDisplay::resetGhostPixelTracking() void EInkDynamicDisplay::resetGhostPixelTracking() {
{
// Copy the current frame into dirtyPixels[] from the display buffer // Copy the current frame into dirtyPixels[] from the display buffer
memcpy(dirtyPixels, EInkDisplay::buffer, EInkDisplay::displayBufferSize); memcpy(dirtyPixels, EInkDisplay::buffer, EInkDisplay::displayBufferSize);
} }
#endif // EINK_LIMIT_GHOSTING_PX #endif // EINK_LIMIT_GHOSTING_PX
// Handle any asyc tasks // Handle any asyc tasks
void EInkDynamicDisplay::onNotify(uint32_t notification) void EInkDynamicDisplay::onNotify(uint32_t notification) {
{
// Which task // Which task
switch (notification) { switch (notification) {
case DUE_POLL_ASYNCREFRESH: case DUE_POLL_ASYNCREFRESH:
@@ -471,8 +441,7 @@ void EInkDynamicDisplay::onNotify(uint32_t notification)
#ifdef HAS_EINK_ASYNCFULL #ifdef HAS_EINK_ASYNCFULL
// Public: wait for an refresh already in progress, then run the post-update code. See Screen::setScreensaverFrames() // Public: wait for an refresh already in progress, then run the post-update code. See Screen::setScreensaverFrames()
void EInkDynamicDisplay::joinAsyncRefresh() void EInkDynamicDisplay::joinAsyncRefresh() {
{
// If no async refresh running, nothing to do // If no async refresh running, nothing to do
if (!asyncRefreshRunning) if (!asyncRefreshRunning)
return; return;
@@ -494,8 +463,7 @@ void EInkDynamicDisplay::joinAsyncRefresh()
} }
// Called from NotifiedWorkerThread. Run the post-update code if the hardware is ready // Called from NotifiedWorkerThread. Run the post-update code if the hardware is ready
void EInkDynamicDisplay::pollAsyncRefresh() void EInkDynamicDisplay::pollAsyncRefresh() {
{
// In theory, this condition should never be met // In theory, this condition should never be met
if (!asyncRefreshRunning) if (!asyncRefreshRunning)
return; return;
@@ -518,8 +486,7 @@ void EInkDynamicDisplay::pollAsyncRefresh()
} }
// Check the status of "async full-refresh"; skip if running // Check the status of "async full-refresh"; skip if running
void EInkDynamicDisplay::checkBusyAsyncRefresh() void EInkDynamicDisplay::checkBusyAsyncRefresh() {
{
// No refresh taking place, continue with determineMode() // No refresh taking place, continue with determineMode()
if (!asyncRefreshRunning) if (!asyncRefreshRunning)
return; return;
@@ -548,8 +515,7 @@ void EInkDynamicDisplay::checkBusyAsyncRefresh()
} }
// Hold control while an async refresh runs // Hold control while an async refresh runs
void EInkDynamicDisplay::awaitRefresh() void EInkDynamicDisplay::awaitRefresh() {
{
// Continually poll the BUSY pin // Continually poll the BUSY pin
while (adafruitDisplay->epd2.isBusy()) while (adafruitDisplay->epd2.isBusy())
yield(); yield();
+7 -8
View File
@@ -15,9 +15,8 @@
(Full, Fast, Skip) (Full, Fast, Skip)
*/ */
class EInkDynamicDisplay : public EInkDisplay, protected concurrency::NotifiedWorkerThread class EInkDynamicDisplay : public EInkDisplay, protected concurrency::NotifiedWorkerThread {
{ public:
public:
// Constructor // Constructor
// ( Parameters unused, passed to EInkDisplay. Maintains compatibility OLEDDisplay class ) // ( Parameters unused, passed to EInkDisplay. Maintains compatibility OLEDDisplay class )
EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus); EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus);
@@ -42,7 +41,7 @@ class EInkDynamicDisplay : public EInkDisplay, protected concurrency::NotifiedWo
void display() override; void display() override;
bool forceDisplay(uint32_t msecLimit) override; // Shadows base class. Parameter and return val unused. bool forceDisplay(uint32_t msecLimit) override; // Shadows base class. Parameter and return val unused.
protected: protected:
enum refreshTypes : uint8_t { // Which refresh operation will be used enum refreshTypes : uint8_t { // Which refresh operation will be used
UNSPECIFIED, UNSPECIFIED,
FULL, FULL,
@@ -125,20 +124,20 @@ class EInkDynamicDisplay : public EInkDisplay, protected concurrency::NotifiedWo
// Conditional - async full refresh - only with modified meshtastic/GxEPD2 // Conditional - async full refresh - only with modified meshtastic/GxEPD2
#if defined(HAS_EINK_ASYNCFULL) #if defined(HAS_EINK_ASYNCFULL)
public: public:
void joinAsyncRefresh(); // Main thread joins an async refresh already in progress. Blocks, then runs post-update code void joinAsyncRefresh(); // Main thread joins an async refresh already in progress. Blocks, then runs post-update code
protected: protected:
void pollAsyncRefresh(); // Run the post-update code if the hardware is ready void pollAsyncRefresh(); // Run the post-update code if the hardware is ready
void checkBusyAsyncRefresh(); // Check if display is busy running an async full-refresh (rejecting new frames) void checkBusyAsyncRefresh(); // Check if display is busy running an async full-refresh (rejecting new frames)
void awaitRefresh(); // Hold control while an async refresh runs void awaitRefresh(); // Hold control while an async refresh runs
void endUpdate() override {} // Disable base-class behavior of running post-update immediately after forceDisplay() void endUpdate() override {} // Disable base-class behavior of running post-update immediately after forceDisplay()
bool asyncRefreshRunning = false; // Flag, checked by checkBusyAsyncRefresh() bool asyncRefreshRunning = false; // Flag, checked by checkBusyAsyncRefresh()
#else #else
public: public:
void joinAsyncRefresh() {} // Dummy method void joinAsyncRefresh() {} // Dummy method
protected: protected:
void pollAsyncRefresh() {} // Dummy method. In theory, not reachable void pollAsyncRefresh() {} // Dummy method. In theory, not reachable
#endif #endif
}; };
+17 -32
View File
@@ -4,99 +4,86 @@
// Workaround for issue of GxEPD2_BW objects not having a shared base class // Workaround for issue of GxEPD2_BW objects not having a shared base class
// Only exposes methods which we are actually using // Only exposes methods which we are actually using
template <typename Driver0, typename Driver1> class GxEPD2_Multi template <typename Driver0, typename Driver1> class GxEPD2_Multi {
{ public:
public: void drawPixel(int16_t x, int16_t y, uint16_t color) {
void drawPixel(int16_t x, int16_t y, uint16_t color)
{
if (which == 0) if (which == 0)
driver0->drawPixel(x, y, color); driver0->drawPixel(x, y, color);
else else
driver1->drawPixel(x, y, color); driver1->drawPixel(x, y, color);
} }
bool nextPage() bool nextPage() {
{
if (which == 0) if (which == 0)
return driver0->nextPage(); return driver0->nextPage();
else else
return driver1->nextPage(); return driver1->nextPage();
} }
void hibernate() void hibernate() {
{
if (which == 0) if (which == 0)
driver0->hibernate(); driver0->hibernate();
else else
driver1->hibernate(); driver1->hibernate();
} }
void init(uint32_t serial_diag_bitrate = 0) void init(uint32_t serial_diag_bitrate = 0) {
{
if (which == 0) if (which == 0)
driver0->init(serial_diag_bitrate); driver0->init(serial_diag_bitrate);
else else
driver1->init(serial_diag_bitrate); driver1->init(serial_diag_bitrate);
} }
void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 20, bool pulldown_rst_mode = false) void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 20, bool pulldown_rst_mode = false) {
{
if (which == 0) if (which == 0)
driver0->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode); driver0->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
else else
driver1->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode); driver1->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
} }
void setRotation(uint8_t x) void setRotation(uint8_t x) {
{
if (which == 0) if (which == 0)
driver0->setRotation(x); driver0->setRotation(x);
else else
driver1->setRotation(x); driver1->setRotation(x);
} }
void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
{
if (which == 0) if (which == 0)
driver0->setPartialWindow(x, y, w, h); driver0->setPartialWindow(x, y, w, h);
else else
driver1->setPartialWindow(x, y, w, h); driver1->setPartialWindow(x, y, w, h);
} }
void setFullWindow() void setFullWindow() {
{
if (which == 0) if (which == 0)
driver0->setFullWindow(); driver0->setFullWindow();
else else
driver1->setFullWindow(); driver1->setFullWindow();
} }
int16_t width() int16_t width() {
{
if (which == 0) if (which == 0)
return driver0->width(); return driver0->width();
else else
return driver1->width(); return driver1->width();
} }
int16_t height() int16_t height() {
{
if (which == 0) if (which == 0)
return driver0->height(); return driver0->height();
else else
return driver1->height(); return driver1->height();
} }
void clearScreen(uint8_t value = 0xFF) void clearScreen(uint8_t value = 0xFF) {
{
if (which == 0) if (which == 0)
driver0->clearScreen(); driver0->clearScreen();
else else
driver1->clearScreen(); driver1->clearScreen();
} }
void endAsyncFull() void endAsyncFull() {
{
if (which == 0) if (which == 0)
driver0->endAsyncFull(); driver0->endAsyncFull();
else else
@@ -104,8 +91,7 @@ template <typename Driver0, typename Driver1> class GxEPD2_Multi
} }
// Exposes methods of the GxEPD2_EPD object which is usually available as GxEPD2_BW::epd // Exposes methods of the GxEPD2_EPD object which is usually available as GxEPD2_BW::epd
class Epd2Wrapper class Epd2Wrapper {
{
public: public:
bool isBusy() { return m_epd2->isBusy(); } bool isBusy() { return m_epd2->isBusy(); }
GxEPD2_EPD *m_epd2; GxEPD2_EPD *m_epd2;
@@ -113,8 +99,7 @@ template <typename Driver0, typename Driver1> class GxEPD2_Multi
// Constructor // Constructor
// Select driver by passing whichDriver as 0 or 1 // Select driver by passing whichDriver as 0 or 1
GxEPD2_Multi(uint8_t whichDriver, int16_t cs, int16_t dc, int16_t rst, int16_t busy, SPIClass &spi) GxEPD2_Multi(uint8_t whichDriver, int16_t cs, int16_t dc, int16_t rst, int16_t busy, SPIClass &spi) {
{
assert(whichDriver == 0 || whichDriver == 1); assert(whichDriver == 0 || whichDriver == 1);
which = whichDriver; which = whichDriver;
LOG_DEBUG("GxEPD2_Multi driver: %d", which); LOG_DEBUG("GxEPD2_Multi driver: %d", which);
@@ -128,7 +113,7 @@ template <typename Driver0, typename Driver1> class GxEPD2_Multi
} }
} }
private: private:
uint8_t which; uint8_t which;
GxEPD2_BW<Driver0, Driver0::HEIGHT> *driver0; GxEPD2_BW<Driver0, Driver0::HEIGHT> *driver0;
GxEPD2_BW<Driver1, Driver1::HEIGHT> *driver1; GxEPD2_BW<Driver1, Driver1::HEIGHT> *driver1;
+42 -88
View File
@@ -33,10 +33,8 @@ Porting for SDL:
#define M_PI 3.14159265358979323846 #define M_PI 3.14159265358979323846
#endif #endif
namespace lgfx namespace lgfx {
{ inline namespace v1 {
inline namespace v1
{
SDL_Keymod Panel_sdl::_keymod = KMOD_NONE; SDL_Keymod Panel_sdl::_keymod = KMOD_NONE;
static SDL_semaphore *_update_in_semaphore = nullptr; static SDL_semaphore *_update_in_semaphore = nullptr;
static SDL_semaphore *_update_out_semaphore = nullptr; static SDL_semaphore *_update_out_semaphore = nullptr;
@@ -47,19 +45,12 @@ static bool _all_close = false;
volatile uint8_t Panel_sdl::_gpio_dummy_values[EMULATED_GPIO_MAX]; volatile uint8_t Panel_sdl::_gpio_dummy_values[EMULATED_GPIO_MAX];
static inline void *heap_alloc_dma(size_t length) static inline void *heap_alloc_dma(size_t length) { return malloc(length); } // aligned_alloc(16, length);
{ static inline void heap_free(void *buf) { free(buf); }
return malloc(length);
} // aligned_alloc(16, length);
static inline void heap_free(void *buf)
{
free(buf);
}
static std::list<monitor_t *> _list_monitor; static std::list<monitor_t *> _list_monitor;
static monitor_t *const getMonitorByWindowID(uint32_t windowID) static monitor_t *const getMonitorByWindowID(uint32_t windowID) {
{
for (auto &m : _list_monitor) { for (auto &m : _list_monitor) {
if (SDL_GetWindowID(m->window) == windowID) { if (SDL_GetWindowID(m->window) == windowID) {
return m; return m;
@@ -71,8 +62,7 @@ static monitor_t *const getMonitorByWindowID(uint32_t windowID)
static std::vector<Panel_sdl::KeyCodeMapping_t> _key_code_map; static std::vector<Panel_sdl::KeyCodeMapping_t> _key_code_map;
void Panel_sdl::addKeyCodeMapping(SDL_KeyCode keyCode, uint8_t gpio) void Panel_sdl::addKeyCodeMapping(SDL_KeyCode keyCode, uint8_t gpio) {
{
if (gpio > EMULATED_GPIO_MAX) if (gpio > EMULATED_GPIO_MAX)
return; return;
KeyCodeMapping_t map; KeyCodeMapping_t map;
@@ -81,8 +71,7 @@ void Panel_sdl::addKeyCodeMapping(SDL_KeyCode keyCode, uint8_t gpio)
_key_code_map.push_back(map); _key_code_map.push_back(map);
} }
int Panel_sdl::getKeyCodeMapping(SDL_KeyCode keyCode) int Panel_sdl::getKeyCodeMapping(SDL_KeyCode keyCode) {
{
for (const auto &i : _key_code_map) { for (const auto &i : _key_code_map) {
if (i.keycode == keyCode) if (i.keycode == keyCode)
return i.gpio; return i.gpio;
@@ -90,8 +79,7 @@ int Panel_sdl::getKeyCodeMapping(SDL_KeyCode keyCode)
return -1; return -1;
} }
void Panel_sdl::_event_proc(void) void Panel_sdl::_event_proc(void) {
{
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
if ((event.type == SDL_KEYDOWN) || (event.type == SDL_KEYUP)) { if ((event.type == SDL_KEYDOWN) || (event.type == SDL_KEYUP)) {
@@ -200,8 +188,7 @@ void Panel_sdl::_event_proc(void)
} }
/// デバッガでステップ実行されていることを検出するスレッド用関数。 /// デバッガでステップ実行されていることを検出するスレッド用関数。
static int detectDebugger(bool *running) static int detectDebugger(bool *running) {
{
uint32_t prev_ms = SDL_GetTicks(); uint32_t prev_ms = SDL_GetTicks();
do { do {
SDL_Delay(1); SDL_Delay(1);
@@ -218,8 +205,7 @@ static int detectDebugger(bool *running)
return 0; return 0;
} }
void Panel_sdl::_update_proc(void) void Panel_sdl::_update_proc(void) {
{
for (auto it = _list_monitor.begin(); it != _list_monitor.end();) { for (auto it = _list_monitor.begin(); it != _list_monitor.end();) {
if ((*it)->closing) { if ((*it)->closing) {
if ((*it)->texture_frameimage) { if ((*it)->texture_frameimage) {
@@ -240,8 +226,7 @@ void Panel_sdl::_update_proc(void)
} }
} }
int Panel_sdl::setup(void) int Panel_sdl::setup(void) {
{
if (_inited) if (_inited)
return 1; return 1;
_inited = true; _inited = true;
@@ -268,8 +253,7 @@ int Panel_sdl::setup(void)
return 0; return 0;
} }
int Panel_sdl::loop(void) int Panel_sdl::loop(void) {
{
if (!_inited) if (!_inited)
return 1; return 1;
@@ -284,8 +268,7 @@ int Panel_sdl::loop(void)
return _all_close; return _all_close;
} }
int Panel_sdl::close(void) int Panel_sdl::close(void) {
{
if (!_inited) if (!_inited)
return 1; return 1;
_inited = false; _inited = false;
@@ -297,8 +280,7 @@ int Panel_sdl::close(void)
return 0; return 0;
} }
int Panel_sdl::main(int (*fn)(bool *), uint32_t msec_step_exec) int Panel_sdl::main(int (*fn)(bool *), uint32_t msec_step_exec) {
{
_msec_step_exec = msec_step_exec; _msec_step_exec = msec_step_exec;
/// SDLの準備 /// SDLの準備
@@ -324,14 +306,12 @@ int Panel_sdl::main(int (*fn)(bool *), uint32_t msec_step_exec)
return Panel_sdl::close(); return Panel_sdl::close();
} }
void Panel_sdl::setScaling(uint_fast8_t scaling_x, uint_fast8_t scaling_y) void Panel_sdl::setScaling(uint_fast8_t scaling_x, uint_fast8_t scaling_y) {
{
monitor.scaling_x = scaling_x; monitor.scaling_x = scaling_x;
monitor.scaling_y = scaling_y; monitor.scaling_y = scaling_y;
} }
void Panel_sdl::setFrameImage(const void *frame_image, int frame_width, int frame_height, int inner_x, int inner_y) void Panel_sdl::setFrameImage(const void *frame_image, int frame_width, int frame_height, int inner_x, int inner_y) {
{
monitor.frame_image = frame_image; monitor.frame_image = frame_image;
monitor.frame_width = frame_width; monitor.frame_width = frame_width;
monitor.frame_height = frame_height; monitor.frame_height = frame_height;
@@ -339,27 +319,23 @@ void Panel_sdl::setFrameImage(const void *frame_image, int frame_width, int fram
monitor.frame_inner_y = inner_y; monitor.frame_inner_y = inner_y;
} }
void Panel_sdl::setFrameRotation(uint_fast16_t frame_rotation) void Panel_sdl::setFrameRotation(uint_fast16_t frame_rotation) {
{
monitor.frame_rotation = frame_rotation; monitor.frame_rotation = frame_rotation;
monitor.frame_angle = (monitor.frame_rotation) * 90; monitor.frame_angle = (monitor.frame_rotation) * 90;
} }
Panel_sdl::~Panel_sdl(void) Panel_sdl::~Panel_sdl(void) {
{
_list_monitor.remove(&monitor); _list_monitor.remove(&monitor);
SDL_DestroyMutex(_sdl_mutex); SDL_DestroyMutex(_sdl_mutex);
} }
Panel_sdl::Panel_sdl(void) : Panel_FrameBufferBase() Panel_sdl::Panel_sdl(void) : Panel_FrameBufferBase() {
{
_sdl_mutex = SDL_CreateMutex(); _sdl_mutex = SDL_CreateMutex();
_auto_display = true; _auto_display = true;
monitor.panel = this; monitor.panel = this;
} }
bool Panel_sdl::init(bool use_reset) bool Panel_sdl::init(bool use_reset) {
{
initFrameBuffer(_cfg.panel_width * 4, _cfg.panel_height); initFrameBuffer(_cfg.panel_width * 4, _cfg.panel_height);
bool res = Panel_FrameBufferBase::init(use_reset); bool res = Panel_FrameBufferBase::init(use_reset);
@@ -368,8 +344,7 @@ bool Panel_sdl::init(bool use_reset)
return res; return res;
} }
color_depth_t Panel_sdl::setColorDepth(color_depth_t depth) color_depth_t Panel_sdl::setColorDepth(color_depth_t depth) {
{
auto bits = depth & color_depth_t::bit_mask; auto bits = depth & color_depth_t::bit_mask;
if (bits >= 16) { if (bits >= 16) {
depth = (bits > 16) ? rgb888_3Byte : rgb565_2Byte; depth = (bits > 16) ? rgb888_3Byte : rgb565_2Byte;
@@ -382,13 +357,9 @@ color_depth_t Panel_sdl::setColorDepth(color_depth_t depth)
return depth; return depth;
} }
Panel_sdl::lock_t::lock_t(Panel_sdl *parent) : _parent{parent} Panel_sdl::lock_t::lock_t(Panel_sdl *parent) : _parent{parent} { SDL_LockMutex(parent->_sdl_mutex); };
{
SDL_LockMutex(parent->_sdl_mutex);
};
Panel_sdl::lock_t::~lock_t(void) Panel_sdl::lock_t::~lock_t(void) {
{
++_parent->_modified_counter; ++_parent->_modified_counter;
SDL_UnlockMutex(_parent->_sdl_mutex); SDL_UnlockMutex(_parent->_sdl_mutex);
if (SDL_SemValue(_update_in_semaphore) < 2) { if (SDL_SemValue(_update_in_semaphore) < 2) {
@@ -399,44 +370,37 @@ Panel_sdl::lock_t::~lock_t(void)
} }
}; };
void Panel_sdl::drawPixelPreclipped(uint_fast16_t x, uint_fast16_t y, uint32_t rawcolor) void Panel_sdl::drawPixelPreclipped(uint_fast16_t x, uint_fast16_t y, uint32_t rawcolor) {
{
lock_t lock(this); lock_t lock(this);
Panel_FrameBufferBase::drawPixelPreclipped(x, y, rawcolor); Panel_FrameBufferBase::drawPixelPreclipped(x, y, rawcolor);
} }
void Panel_sdl::writeFillRectPreclipped(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, uint32_t rawcolor) void Panel_sdl::writeFillRectPreclipped(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, uint32_t rawcolor) {
{
lock_t lock(this); lock_t lock(this);
Panel_FrameBufferBase::writeFillRectPreclipped(x, y, w, h, rawcolor); Panel_FrameBufferBase::writeFillRectPreclipped(x, y, w, h, rawcolor);
} }
void Panel_sdl::writeBlock(uint32_t rawcolor, uint32_t length) void Panel_sdl::writeBlock(uint32_t rawcolor, uint32_t length) {
{
// lock_t lock(this); // lock_t lock(this);
Panel_FrameBufferBase::writeBlock(rawcolor, length); Panel_FrameBufferBase::writeBlock(rawcolor, length);
} }
void Panel_sdl::writeImage(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param, bool use_dma) void Panel_sdl::writeImage(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param, bool use_dma) {
{
lock_t lock(this); lock_t lock(this);
Panel_FrameBufferBase::writeImage(x, y, w, h, param, use_dma); Panel_FrameBufferBase::writeImage(x, y, w, h, param, use_dma);
} }
void Panel_sdl::writeImageARGB(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param) void Panel_sdl::writeImageARGB(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param) {
{
lock_t lock(this); lock_t lock(this);
Panel_FrameBufferBase::writeImageARGB(x, y, w, h, param); Panel_FrameBufferBase::writeImageARGB(x, y, w, h, param);
} }
void Panel_sdl::writePixels(pixelcopy_t *param, uint32_t len, bool use_dma) void Panel_sdl::writePixels(pixelcopy_t *param, uint32_t len, bool use_dma) {
{
lock_t lock(this); lock_t lock(this);
Panel_FrameBufferBase::writePixels(param, len, use_dma); Panel_FrameBufferBase::writePixels(param, len, use_dma);
} }
void Panel_sdl::display(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h) void Panel_sdl::display(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h) {
{
(void)x; (void)x;
(void)y; (void)y;
(void)w; (void)w;
@@ -452,8 +416,7 @@ void Panel_sdl::display(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_
} }
} }
uint_fast8_t Panel_sdl::getTouchRaw(touch_point_t *tp, uint_fast8_t count) uint_fast8_t Panel_sdl::getTouchRaw(touch_point_t *tp, uint_fast8_t count) {
{
(void)count; (void)count;
tp->x = monitor.touch_x; tp->x = monitor.touch_x;
tp->y = monitor.touch_y; tp->y = monitor.touch_y;
@@ -462,16 +425,14 @@ uint_fast8_t Panel_sdl::getTouchRaw(touch_point_t *tp, uint_fast8_t count)
return monitor.touched; return monitor.touched;
} }
void Panel_sdl::setWindowTitle(const char *title) void Panel_sdl::setWindowTitle(const char *title) {
{
_window_title = title; _window_title = title;
if (monitor.window) { if (monitor.window) {
SDL_SetWindowTitle(monitor.window, _window_title); SDL_SetWindowTitle(monitor.window, _window_title);
} }
} }
void Panel_sdl::_update_scaling(monitor_t *mon, float sx, float sy) void Panel_sdl::_update_scaling(monitor_t *mon, float sx, float sy) {
{
mon->scaling_x = sx; mon->scaling_x = sx;
mon->scaling_y = sy; mon->scaling_y = sy;
int nw = mon->frame_width; int nw = mon->frame_width;
@@ -492,8 +453,7 @@ void Panel_sdl::_update_scaling(monitor_t *mon, float sx, float sy)
mon->panel->sdl_invalidate(); mon->panel->sdl_invalidate();
} }
void Panel_sdl::sdl_create(monitor_t *m) void Panel_sdl::sdl_create(monitor_t *m) {
{
int flag = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; int flag = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
#if SDL_FULLSCREEN #if SDL_FULLSCREEN
flag |= SDL_WINDOW_FULLSCREEN; flag |= SDL_WINDOW_FULLSCREEN;
@@ -520,14 +480,13 @@ void Panel_sdl::sdl_create(monitor_t *m)
flag); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/ flag); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/
} }
m->renderer = SDL_CreateRenderer(m->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); m->renderer = SDL_CreateRenderer(m->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
m->texture = m->texture = SDL_CreateTexture(m->renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, _cfg.panel_width, _cfg.panel_height);
SDL_CreateTexture(m->renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, _cfg.panel_width, _cfg.panel_height);
SDL_SetTextureBlendMode(m->texture, SDL_BLENDMODE_NONE); SDL_SetTextureBlendMode(m->texture, SDL_BLENDMODE_NONE);
if (m->frame_image) { if (m->frame_image) {
// 枠画像用のサーフェイスを作成 // 枠画像用のサーフェイスを作成
auto sf = SDL_CreateRGBSurfaceFrom((void *)m->frame_image, m->frame_width, m->frame_height, 32, m->frame_width * 4, auto sf =
0xFF000000, 0xFF0000, 0xFF00, 0xFF); SDL_CreateRGBSurfaceFrom((void *)m->frame_image, m->frame_width, m->frame_height, 32, m->frame_width * 4, 0xFF000000, 0xFF0000, 0xFF00, 0xFF);
if (sf != nullptr) { if (sf != nullptr) {
// 枠画像からテクスチャを作成 // 枠画像からテクスチャを作成
m->texture_frameimage = SDL_CreateTextureFromSurface(m->renderer, sf); m->texture_frameimage = SDL_CreateTextureFromSurface(m->renderer, sf);
@@ -538,8 +497,7 @@ void Panel_sdl::sdl_create(monitor_t *m)
_update_scaling(m, scaling_x, scaling_y); _update_scaling(m, scaling_x, scaling_y);
} }
void Panel_sdl::sdl_update(void) void Panel_sdl::sdl_update(void) {
{
if (monitor.renderer == nullptr) { if (monitor.renderer == nullptr) {
sdl_create(&monitor); sdl_create(&monitor);
} }
@@ -616,16 +574,14 @@ void Panel_sdl::sdl_update(void)
_invalidated = false; _invalidated = false;
SDL_SetRenderDrawColor(monitor.renderer, 0, 0, 0, 0xFF); SDL_SetRenderDrawColor(monitor.renderer, 0, 0, 0, 0xFF);
SDL_RenderClear(monitor.renderer); SDL_RenderClear(monitor.renderer);
render_texture(monitor.texture, monitor.frame_inner_x, monitor.frame_inner_y, _cfg.panel_width, _cfg.panel_height, render_texture(monitor.texture, monitor.frame_inner_x, monitor.frame_inner_y, _cfg.panel_width, _cfg.panel_height, angle);
angle);
render_texture(monitor.texture_frameimage, 0, 0, monitor.frame_width, monitor.frame_height, angle); render_texture(monitor.texture_frameimage, 0, 0, monitor.frame_width, monitor.frame_height, angle);
SDL_RenderPresent(monitor.renderer); SDL_RenderPresent(monitor.renderer);
} }
} }
} }
void Panel_sdl::render_texture(SDL_Texture *texture, int tx, int ty, int tw, int th, float angle) void Panel_sdl::render_texture(SDL_Texture *texture, int tx, int ty, int tw, int th, float angle) {
{
SDL_Point pivot; SDL_Point pivot;
pivot.x = (monitor.frame_width / 2.0f - tx) * (float)monitor.scaling_x; pivot.x = (monitor.frame_width / 2.0f - tx) * (float)monitor.scaling_x;
pivot.y = (monitor.frame_height / 2.0f - ty) * (float)monitor.scaling_y; pivot.y = (monitor.frame_height / 2.0f - ty) * (float)monitor.scaling_y;
@@ -639,8 +595,7 @@ void Panel_sdl::render_texture(SDL_Texture *texture, int tx, int ty, int tw, int
SDL_RenderCopyEx(monitor.renderer, texture, nullptr, &dstrect, angle, &pivot, SDL_RendererFlip::SDL_FLIP_NONE); SDL_RenderCopyEx(monitor.renderer, texture, nullptr, &dstrect, angle, &pivot, SDL_RendererFlip::SDL_FLIP_NONE);
} }
bool Panel_sdl::initFrameBuffer(size_t width, size_t height) bool Panel_sdl::initFrameBuffer(size_t width, size_t height) {
{
uint8_t **lineArray = (uint8_t **)heap_alloc_dma(height * sizeof(uint8_t *)); uint8_t **lineArray = (uint8_t **)heap_alloc_dma(height * sizeof(uint8_t *));
if (nullptr == lineArray) { if (nullptr == lineArray) {
return false; return false;
@@ -666,8 +621,7 @@ bool Panel_sdl::initFrameBuffer(size_t width, size_t height)
return true; return true;
} }
void Panel_sdl::deinitFrameBuffer(void) void Panel_sdl::deinitFrameBuffer(void) {
{
auto lines = _lines_buffer; auto lines = _lines_buffer;
_lines_buffer = nullptr; _lines_buffer = nullptr;
if (lines != nullptr) { if (lines != nullptr) {
+5 -8
View File
@@ -36,10 +36,8 @@ Porting for SDL:
#include "lgfx/v1/panel/Panel_FrameBufferBase.hpp" #include "lgfx/v1/panel/Panel_FrameBufferBase.hpp"
#include <cstdint> #include <cstdint>
namespace lgfx namespace lgfx {
{ inline namespace v1 {
inline namespace v1
{
struct Panel_sdl; struct Panel_sdl;
struct monitor_t { struct monitor_t {
@@ -80,7 +78,7 @@ struct Panel_sdl : public Panel_FrameBufferBase {
static constexpr size_t EMULATED_GPIO_MAX = 128; static constexpr size_t EMULATED_GPIO_MAX = 128;
static volatile uint8_t _gpio_dummy_values[EMULATED_GPIO_MAX]; static volatile uint8_t _gpio_dummy_values[EMULATED_GPIO_MAX];
public: public:
Panel_sdl(void); Panel_sdl(void);
virtual ~Panel_sdl(void); virtual ~Panel_sdl(void);
@@ -94,8 +92,7 @@ struct Panel_sdl : public Panel_FrameBufferBase {
void drawPixelPreclipped(uint_fast16_t x, uint_fast16_t y, uint32_t rawcolor) override; void drawPixelPreclipped(uint_fast16_t x, uint_fast16_t y, uint32_t rawcolor) override;
void writeFillRectPreclipped(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, uint32_t rawcolor) override; void writeFillRectPreclipped(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, uint32_t rawcolor) override;
void writeBlock(uint32_t rawcolor, uint32_t length) override; void writeBlock(uint32_t rawcolor, uint32_t length) override;
void writeImage(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param, void writeImage(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param, bool use_dma) override;
bool use_dma) override;
void writeImageARGB(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param) override; void writeImageARGB(uint_fast16_t x, uint_fast16_t y, uint_fast16_t w, uint_fast16_t h, pixelcopy_t *param) override;
void writePixels(pixelcopy_t *param, uint32_t len, bool use_dma) override; void writePixels(pixelcopy_t *param, uint32_t len, bool use_dma) override;
@@ -126,7 +123,7 @@ struct Panel_sdl : public Panel_FrameBufferBase {
static void addKeyCodeMapping(SDL_KeyCode keyCode, uint8_t gpio); static void addKeyCodeMapping(SDL_KeyCode keyCode, uint8_t gpio);
static int getKeyCodeMapping(SDL_KeyCode keyCode); static int getKeyCodeMapping(SDL_KeyCode keyCode);
protected: protected:
const char *_window_title = "LGFX Simulator"; const char *_window_title = "LGFX Simulator";
SDL_mutex *_sdl_mutex = nullptr; SDL_mutex *_sdl_mutex = nullptr;
+71 -132
View File
@@ -92,8 +92,7 @@ uint16_t TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
using namespace meshtastic; /** @todo remove */ using namespace meshtastic; /** @todo remove */
namespace graphics namespace graphics {
{
// This means the *visible* area (sh1106 can address 132, but shows 128 for example) // This means the *visible* area (sh1106 can address 132, but shows 128 for example)
#define IDLE_FRAMERATE 1 // in fps #define IDLE_FRAMERATE 1 // in fps
@@ -140,8 +139,7 @@ extern bool hasUnreadMessage;
// Displays a temporary centered banner message (e.g., warning, status, etc.) // Displays a temporary centered banner message (e.g., warning, status, etc.)
// The banner appears in the center of the screen and disappears after the specified duration // The banner appears in the center of the screen and disappears after the specified duration
void Screen::showSimpleBanner(const char *message, uint32_t durationMs) void Screen::showSimpleBanner(const char *message, uint32_t durationMs) {
{
BannerOverlayOptions options; BannerOverlayOptions options;
options.message = message; options.message = message;
options.durationMs = durationMs; options.durationMs = durationMs;
@@ -150,16 +148,14 @@ void Screen::showSimpleBanner(const char *message, uint32_t durationMs)
} }
// Called to trigger a banner with custom message and duration // Called to trigger a banner with custom message and duration
void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options) void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options) {
{
#ifdef USE_EINK #ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
#endif #endif
// Store the message and set the expiration timestamp // Store the message and set the expiration timestamp
strncpy(NotificationRenderer::alertBannerMessage, banner_overlay_options.message, 255); strncpy(NotificationRenderer::alertBannerMessage, banner_overlay_options.message, 255);
NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination
NotificationRenderer::alertBannerUntil = NotificationRenderer::alertBannerUntil = (banner_overlay_options.durationMs == 0) ? 0 : millis() + banner_overlay_options.durationMs;
(banner_overlay_options.durationMs == 0) ? 0 : millis() + banner_overlay_options.durationMs;
NotificationRenderer::optionsArrayPtr = banner_overlay_options.optionsArrayPtr; NotificationRenderer::optionsArrayPtr = banner_overlay_options.optionsArrayPtr;
NotificationRenderer::optionsEnumPtr = banner_overlay_options.optionsEnumPtr; NotificationRenderer::optionsEnumPtr = banner_overlay_options.optionsEnumPtr;
NotificationRenderer::alertBannerOptions = banner_overlay_options.optionsCount; NotificationRenderer::alertBannerOptions = banner_overlay_options.optionsCount;
@@ -174,8 +170,7 @@ void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options)
} }
// Called to trigger a banner with custom message and duration // Called to trigger a banner with custom message and duration
void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback) void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback) {
{
#ifdef USE_EINK #ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
#endif #endif
@@ -196,9 +191,7 @@ void Screen::showNodePicker(const char *message, uint32_t durationMs, std::funct
} }
// Called to trigger a banner with custom message and duration // Called to trigger a banner with custom message and duration
void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function<void(uint32_t)> bannerCallback) {
std::function<void(uint32_t)> bannerCallback)
{
#ifdef USE_EINK #ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
#endif #endif
@@ -219,9 +212,7 @@ void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t
ui->update(); ui->update();
} }
void Screen::showTextInput(const char *header, const char *initialText, uint32_t durationMs, void Screen::showTextInput(const char *header, const char *initialText, uint32_t durationMs, std::function<void(const std::string &)> textCallback) {
std::function<void(const std::string &)> textCallback)
{
LOG_INFO("showTextInput called with header='%s', durationMs=%d", header ? header : "NULL", durationMs); LOG_INFO("showTextInput called with header='%s', durationMs=%d", header ? header : "NULL", durationMs);
// Start OnScreenKeyboardModule session (non-touch variant) // Start OnScreenKeyboardModule session (non-touch variant)
@@ -242,8 +233,7 @@ void Screen::showTextInput(const char *header, const char *initialText, uint32_t
ui->update(); ui->update();
} }
static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) {
{
uint8_t module_frame; uint8_t module_frame;
// there's a little but in the UI transition code // there's a little but in the UI transition code
// where it invokes the function at the correct offset // where it invokes the function at the correct offset
@@ -268,8 +258,7 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
* We keep a series of "after you've gone 10 meters, what is your heading since * We keep a series of "after you've gone 10 meters, what is your heading since
* the last reference point?" * the last reference point?"
*/ */
float Screen::estimatedHeading(double lat, double lon) float Screen::estimatedHeading(double lat, double lon) {
{
static double oldLat, oldLon; static double oldLat, oldLon;
static float b; static float b;
@@ -304,8 +293,7 @@ SPIClass SPI1(HSPI);
#endif #endif
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry) Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32) : concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32) {
{
graphics::normalFrames = new FrameCallback[MAX_NUM_NODES + NUM_EXTRA_FRAMES]; graphics::normalFrames = new FrameCallback[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
int32_t rawRGB = uiconfig.screen_rgb_color; int32_t rawRGB = uiconfig.screen_rgb_color;
@@ -322,25 +310,22 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
} }
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64) #if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
dispdev = new SH1106Wire(address.address, -1, -1, geometry, dispdev = new SH1106Wire(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_ST7789) #elif defined(USE_ST7789)
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7789_SDA, dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7789_SDA, ST7789_MISO, ST7789_SCK);
ST7789_MISO, ST7789_SCK);
#else #else
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT); dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif #endif
#elif defined(USE_ST7796) #elif defined(USE_ST7796)
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7796_SDA, dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7796_SDA, ST7796_MISO, ST7796_SCK,
ST7796_MISO, ST7796_SCK, TFT_SPI_FREQUENCY); TFT_SPI_FREQUENCY);
#else #else
dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT); dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif #endif
#elif defined(USE_SSD1306) #elif defined(USE_SSD1306)
dispdev = new SSD1306Wire(address.address, -1, -1, geometry, dispdev = new SSD1306Wire(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_SPISSD1306) #elif defined(USE_SPISSD1306)
dispdev = new SSD1306Spi(SSD1306_RESET, SSD1306_RS, SSD1306_NSS, GEOMETRY_64_48); dispdev = new SSD1306Spi(SSD1306_RESET, SSD1306_RS, SSD1306_NSS, GEOMETRY_64_48);
if (!dispdev->init()) { if (!dispdev->init()) {
@@ -349,34 +334,27 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
static_cast<SSD1306Spi *>(dispdev)->setHorizontalOffset(32); static_cast<SSD1306Spi *>(dispdev)->setHorizontalOffset(32);
LOG_INFO("SSD1306 init success"); LOG_INFO("SSD1306 init success");
} }
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \ #elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || defined(RAK14014) || \
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR) defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR)
dispdev = new TFTDisplay(address.address, -1, -1, geometry, dispdev = new TFTDisplay(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY) #elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
dispdev = new EInkDisplay(address.address, -1, -1, geometry, dispdev = new EInkDisplay(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY) #elif defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY)
dispdev = new EInkDynamicDisplay(address.address, -1, -1, geometry, dispdev = new EInkDynamicDisplay(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_ST7567) #elif defined(USE_ST7567)
dispdev = new ST7567Wire(address.address, -1, -1, geometry, dispdev = new ST7567Wire(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
if (portduino_config.displayPanel != no_screen) { if (portduino_config.displayPanel != no_screen) {
LOG_DEBUG("Make TFTDisplay!"); LOG_DEBUG("Make TFTDisplay!");
dispdev = new TFTDisplay(address.address, -1, -1, geometry, dispdev = new TFTDisplay(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
} else { } else {
dispdev = new AutoOLEDWire(address.address, -1, -1, geometry, dispdev = new AutoOLEDWire(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
isAUTOOled = true; isAUTOOled = true;
} }
} }
#else #else
dispdev = new AutoOLEDWire(address.address, -1, -1, geometry, dispdev = new AutoOLEDWire(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
isAUTOOled = true; isAUTOOled = true;
#endif #endif
@@ -390,17 +368,13 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
cmdQueue.setReader(this); cmdQueue.setReader(this);
} }
Screen::~Screen() Screen::~Screen() { delete[] graphics::normalFrames; }
{
delete[] graphics::normalFrames;
}
/** /**
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just * Prepare the display for the unit going to the lowest power mode possible. Most screens will just
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code * poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
*/ */
void Screen::doDeepSleep() void Screen::doDeepSleep() {
{
#ifdef USE_EINK #ifdef USE_EINK
setOn(false, graphics::UIRenderer::drawDeepSleepFrame); setOn(false, graphics::UIRenderer::drawDeepSleepFrame);
#else #else
@@ -409,8 +383,7 @@ void Screen::doDeepSleep()
#endif #endif
} }
void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) {
{
if (!useDisplay) if (!useDisplay)
return; return;
@@ -442,8 +415,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
io.digitalWrite(PCA_PIN_EINK_EN, HIGH); io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
#endif #endif
#if defined(ST7789_CS) && \ #if defined(ST7789_CS) && !defined(M5STACK) // set display brightness when turning on screens. Just moved function from TFTDisplay to here.
!defined(M5STACK) // set display brightness when turning on screens. Just moved function from TFTDisplay to here.
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness); static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
#endif #endif
@@ -533,8 +505,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
} }
} }
void Screen::setup() void Screen::setup() {
{
// Enable display rendering // Enable display rendering
useDisplay = true; useDisplay = true;
@@ -630,8 +601,8 @@ void Screen::setup()
dispdev->mirrorScreen(); dispdev->mirrorScreen();
#else #else
if (!config.display.flip_screen) { if (!config.display.flip_screen) {
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \ #if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || defined(RAK14014) || \
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR) defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR)
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically(); static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7789) #elif defined(USE_ST7789)
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically(); static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
@@ -664,14 +635,12 @@ void Screen::setup()
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
if (portduino_config.touchscreenModule) { if (portduino_config.touchscreenModule) {
touchScreenImpl1 = touchScreenImpl1 = new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
touchScreenImpl1->init(); touchScreenImpl1->init();
} }
} }
#elif HAS_TOUCHSCREEN && !defined(USE_EINK) && !HAS_CST226SE #elif HAS_TOUCHSCREEN && !defined(USE_EINK) && !HAS_CST226SE
touchScreenImpl1 = touchScreenImpl1 = new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
touchScreenImpl1->init(); touchScreenImpl1->init();
#endif #endif
@@ -694,8 +663,7 @@ void Screen::setup()
MeshModule::observeUIEvents(&uiFrameEventObserver); MeshModule::observeUIEvents(&uiFrameEventObserver);
} }
void Screen::setOn(bool on, FrameCallback einkScreensaver) void Screen::setOn(bool on, FrameCallback einkScreensaver) {
{
#if defined(T_LORA_PAGER) #if defined(T_LORA_PAGER)
if (cardKbI2cImpl) if (cardKbI2cImpl)
cardKbI2cImpl->toggleBacklight(on); cardKbI2cImpl->toggleBacklight(on);
@@ -707,8 +675,7 @@ void Screen::setOn(bool on, FrameCallback einkScreensaver)
enqueueCmd(ScreenCmd{.cmd = Cmd::SET_ON}); enqueueCmd(ScreenCmd{.cmd = Cmd::SET_ON});
} }
void Screen::forceDisplay(bool forceUiUpdate) void Screen::forceDisplay(bool forceUiUpdate) {
{
// Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup. // Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup.
#ifdef USE_EINK #ifdef USE_EINK
// If requested, make sure queued commands are run, and UI has rendered a new frame // If requested, make sure queued commands are run, and UI has rendered a new frame
@@ -750,8 +717,7 @@ void Screen::forceDisplay(bool forceUiUpdate)
static uint32_t lastScreenTransition; static uint32_t lastScreenTransition;
int32_t Screen::runOnce() int32_t Screen::runOnce() {
{
// If we don't have a screen, don't ever spend any CPU for us. // If we don't have a screen, don't ever spend any CPU for us.
if (!useDisplay) { if (!useDisplay) {
enabled = false; enabled = false;
@@ -904,8 +870,7 @@ int32_t Screen::runOnce()
// standard screen switching is stopped. // standard screen switching is stopped.
if (showingNormalScreen) { if (showingNormalScreen) {
// standard screen loop handling here // standard screen loop handling here
if (config.display.auto_screen_carousel_secs > 0 && if (config.display.auto_screen_carousel_secs > 0 && NotificationRenderer::current_notification_type != notificationTypeEnum::text_input &&
NotificationRenderer::current_notification_type != notificationTypeEnum::text_input &&
!Throttle::isWithinTimespanMs(lastScreenTransition, config.display.auto_screen_carousel_secs * 1000)) { !Throttle::isWithinTimespanMs(lastScreenTransition, config.display.auto_screen_carousel_secs * 1000)) {
// If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead // If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead
@@ -929,8 +894,7 @@ int32_t Screen::runOnce()
/* show a message that the SSL cert is being built /* show a message that the SSL cert is being built
* it is expected that this will be used during the boot phase */ * it is expected that this will be used during the boot phase */
void Screen::setSSLFrames() void Screen::setSSLFrames() {
{
if (address_found.address) { if (address_found.address) {
// LOG_DEBUG("Show SSL frames"); // LOG_DEBUG("Show SSL frames");
static FrameCallback sslFrames[] = {NotificationRenderer::drawSSLScreen}; static FrameCallback sslFrames[] = {NotificationRenderer::drawSSLScreen};
@@ -941,8 +905,7 @@ void Screen::setSSLFrames()
#ifdef USE_EINK #ifdef USE_EINK
/// Determine which screensaver frame to use, then set the FrameCallback /// Determine which screensaver frame to use, then set the FrameCallback
void Screen::setScreensaverFrames(FrameCallback einkScreensaver) void Screen::setScreensaverFrames(FrameCallback einkScreensaver) {
{
// Retain specified frame / overlay callback beyond scope of this method // Retain specified frame / overlay callback beyond scope of this method
static FrameCallback screensaverFrame; static FrameCallback screensaverFrame;
static OverlayCallback screensaverOverlay; static OverlayCallback screensaverOverlay;
@@ -994,8 +957,7 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
// Regenerate the normal set of frames, focusing a specific frame if requested // Regenerate the normal set of frames, focusing a specific frame if requested
// Called when a frame should be added / removed, or custom frames should be cleared // Called when a frame should be added / removed, or custom frames should be cleared
void Screen::setFrames(FrameFocus focus) void Screen::setFrames(FrameFocus focus) {
{
// Block setFrames calls when virtual keyboard is active to prevent overlay interference // Block setFrames calls when virtual keyboard is active to prevent overlay interference
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) { if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
return; return;
@@ -1028,8 +990,8 @@ void Screen::setFrames(FrameFocus focus)
#if defined(M5STACK_UNITC6L) #if defined(M5STACK_UNITC6L)
normalFrames[numframes++] = graphics::ClockRenderer::drawAnalogClockFrame; normalFrames[numframes++] = graphics::ClockRenderer::drawAnalogClockFrame;
#else #else
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame normalFrames[numframes++] =
: graphics::ClockRenderer::drawDigitalClockFrame; uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame : graphics::ClockRenderer::drawDigitalClockFrame;
#endif #endif
indicatorIcons.push_back(digital_icon_clock); indicatorIcons.push_back(digital_icon_clock);
} }
@@ -1103,8 +1065,8 @@ void Screen::setFrames(FrameFocus focus)
#if !defined(DISPLAY_CLOCK_FRAME) #if !defined(DISPLAY_CLOCK_FRAME)
if (!hiddenFrames.clock) { if (!hiddenFrames.clock) {
fsi.positions.clock = numframes; fsi.positions.clock = numframes;
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame normalFrames[numframes++] =
: graphics::ClockRenderer::drawDigitalClockFrame; uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame : graphics::ClockRenderer::drawDigitalClockFrame;
indicatorIcons.push_back(digital_icon_clock); indicatorIcons.push_back(digital_icon_clock);
} }
#endif #endif
@@ -1235,15 +1197,13 @@ void Screen::setFrames(FrameFocus focus)
setFastFramerate(); // Draw ASAP setFastFramerate(); // Draw ASAP
} }
void Screen::setFrameImmediateDraw(FrameCallback *drawFrames) void Screen::setFrameImmediateDraw(FrameCallback *drawFrames) {
{
ui->disableAllIndicators(); ui->disableAllIndicators();
ui->setFrames(drawFrames, 1); ui->setFrames(drawFrames, 1);
setFastFramerate(); setFastFramerate();
} }
void Screen::toggleFrameVisibility(const std::string &frameName) void Screen::toggleFrameVisibility(const std::string &frameName) {
{
#ifndef USE_EINK #ifndef USE_EINK
if (frameName == "nodelist_nodes") { if (frameName == "nodelist_nodes") {
hiddenFrames.nodelist_nodes = !hiddenFrames.nodelist_nodes; hiddenFrames.nodelist_nodes = !hiddenFrames.nodelist_nodes;
@@ -1287,8 +1247,7 @@ void Screen::toggleFrameVisibility(const std::string &frameName)
} }
} }
bool Screen::isFrameHidden(const std::string &frameName) const bool Screen::isFrameHidden(const std::string &frameName) const {
{
#ifndef USE_EINK #ifndef USE_EINK
if (frameName == "nodelist_nodes") if (frameName == "nodelist_nodes")
return hiddenFrames.nodelist_nodes; return hiddenFrames.nodelist_nodes;
@@ -1323,8 +1282,7 @@ bool Screen::isFrameHidden(const std::string &frameName) const
return false; return false;
} }
void Screen::handleStartFirmwareUpdateScreen() void Screen::handleStartFirmwareUpdateScreen() {
{
LOG_DEBUG("Show firmware screen"); LOG_DEBUG("Show firmware screen");
showingNormalScreen = false; showingNormalScreen = false;
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
@@ -1333,8 +1291,7 @@ void Screen::handleStartFirmwareUpdateScreen()
setFrameImmediateDraw(frames); setFrameImmediateDraw(frames);
} }
void Screen::blink() void Screen::blink() {
{
setFastFramerate(); setFastFramerate();
uint8_t count = 10; uint8_t count = 10;
dispdev->setBrightness(254); dispdev->setBrightness(254);
@@ -1352,8 +1309,7 @@ void Screen::blink()
dispdev->setBrightness(brightness); dispdev->setBrightness(brightness);
} }
void Screen::increaseBrightness() void Screen::increaseBrightness() {
{
brightness = ((brightness + 62) > 254) ? brightness : (brightness + 62); brightness = ((brightness + 62) > 254) ? brightness : (brightness + 62);
#if defined(ST7789_CS) #if defined(ST7789_CS)
@@ -1364,8 +1320,7 @@ void Screen::increaseBrightness()
/* TO DO: add little popup in center of screen saying what brightness level it is set to*/ /* TO DO: add little popup in center of screen saying what brightness level it is set to*/
} }
void Screen::decreaseBrightness() void Screen::decreaseBrightness() {
{
brightness = (brightness < 70) ? brightness : (brightness - 62); brightness = (brightness < 70) ? brightness : (brightness - 62);
#if defined(ST7789_CS) #if defined(ST7789_CS)
@@ -1375,8 +1330,7 @@ void Screen::decreaseBrightness()
/* TO DO: add little popup in center of screen saying what brightness level it is set to*/ /* TO DO: add little popup in center of screen saying what brightness level it is set to*/
} }
void Screen::handleOnPress() void Screen::handleOnPress() {
{
// If screen was off, just wake it, otherwise advance to next frame // If screen was off, just wake it, otherwise advance to next frame
// If we are in a transition, the press must have bounced, drop it. // If we are in a transition, the press must have bounced, drop it.
if (ui->getUiState()->frameState == FIXED) { if (ui->getUiState()->frameState == FIXED) {
@@ -1386,8 +1340,7 @@ void Screen::handleOnPress()
} }
} }
void Screen::showFrame(FrameDirection direction) void Screen::showFrame(FrameDirection direction) {
{
// Only advance frames when UI is stable // Only advance frames when UI is stable
if (ui->getUiState()->frameState == FIXED) { if (ui->getUiState()->frameState == FIXED) {
@@ -1406,8 +1359,7 @@ void Screen::showFrame(FrameDirection direction)
#define SCREEN_TRANSITION_FRAMERATE 30 // fps #define SCREEN_TRANSITION_FRAMERATE 30 // fps
#endif #endif
void Screen::setFastFramerate() void Screen::setFastFramerate() {
{
#if defined(M5STACK_UNITC6L) #if defined(M5STACK_UNITC6L)
dispdev->clear(); dispdev->clear();
dispdev->display(); dispdev->display();
@@ -1420,8 +1372,7 @@ void Screen::setFastFramerate()
runASAP = true; runASAP = true;
} }
int Screen::handleStatusUpdate(const meshtastic::Status *arg) int Screen::handleStatusUpdate(const meshtastic::Status *arg) {
{
switch (arg->getStatusType()) { switch (arg->getStatusType()) {
case STATUS_TYPE_NODE: case STATUS_TYPE_NODE:
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) { if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
@@ -1438,8 +1389,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
} }
// Handles when message is received; will jump to text message frame. // Handles when message is received; will jump to text message frame.
int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) {
{
if (showingNormalScreen) { if (showingNormalScreen) {
if (packet->from == 0) { if (packet->from == 0) {
// Outgoing message (likely sent from phone) // Outgoing message (likely sent from phone)
@@ -1462,8 +1412,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
} }
// === Prepare banner/popup content === // === Prepare banner/popup content ===
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from); const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
const meshtastic_Channel channel = const meshtastic_Channel channel = channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr; const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes); const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
@@ -1523,8 +1472,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
// Maintain existing buzzer behavior on M5 if applicable // Maintain existing buzzer behavior on M5 if applicable
#if defined(M5STACK_UNITC6L) #if defined(M5STACK_UNITC6L)
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY || if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) || (isAlert && moduleConfig.external_notification.alert_bell_buzzer) || (!isBroadcast(packet->to) && isToUs(packet))) {
(!isBroadcast(packet->to) && isToUs(packet))) {
playLongBeep(); playLongBeep();
} }
#endif #endif
@@ -1551,8 +1499,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
screen->setOn(true); screen->setOn(true);
screen->showSimpleBanner(banner, 1500); screen->showSimpleBanner(banner, 1500);
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY || if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) || (isAlert && moduleConfig.external_notification.alert_bell_buzzer) || (!isBroadcast(packet->to) && isToUs(packet))) {
(!isBroadcast(packet->to) && isToUs(packet))) {
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either // Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
// - packet contains an alert and alert bell buzzer is enabled // - packet contains an alert and alert bell buzzer is enabled
// - packet is a non-broadcast that is addressed to this node // - packet is a non-broadcast that is addressed to this node
@@ -1570,8 +1517,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
} }
// Triggered by MeshModules // Triggered by MeshModules
int Screen::handleUIFrameEvent(const UIFrameEvent *event) int Screen::handleUIFrameEvent(const UIFrameEvent *event) {
{
// Block UI frame events when virtual keyboard is active // Block UI frame events when virtual keyboard is active
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) { if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
return 0; return 0;
@@ -1604,8 +1550,7 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
return 0; return 0;
} }
int Screen::handleInputEvent(const InputEvent *event) int Screen::handleInputEvent(const InputEvent *event) {
{
LOG_INPUT("Screen Input event %u! kb %u", event->inputEvent, event->kbchar); LOG_INPUT("Screen Input event %u! kb %u", event->inputEvent, event->kbchar);
if (!screenOn) if (!screenOn)
return 0; return 0;
@@ -1620,7 +1565,8 @@ int Screen::handleInputEvent(const InputEvent *event)
return 0; return 0;
} }
#ifdef USE_EINK // the screen is the last input handler, so if an event makes it here, we can assume it will prompt a screen draw. #ifdef USE_EINK // the screen is the last input handler, so if an event makes it here, we can assume it will prompt a
// screen draw.
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please
EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update
handleSetOn(true); // Ensure power-on to receive deep-sleep screensaver (PowerFSM should handle?) handleSetOn(true); // Ensure power-on to receive deep-sleep screensaver (PowerFSM should handle?)
@@ -1693,8 +1639,8 @@ int Screen::handleInputEvent(const InputEvent *event)
if (!inputIntercepted) { if (!inputIntercepted) {
#if defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2 #if defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2
bool handledEncoderScroll = false; bool handledEncoderScroll = false;
const bool isTextMessageFrame = (framesetInfo.positions.textMessage != 255 && const bool isTextMessageFrame =
this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage && (framesetInfo.positions.textMessage != 255 && this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage &&
!messageStore.getMessages().empty()); !messageStore.getMessages().empty());
if (isTextMessageFrame) { if (isTextMessageFrame) {
if (event->inputEvent == INPUT_BROKER_UP_LONG) { if (event->inputEvent == INPUT_BROKER_UP_LONG) {
@@ -1747,8 +1693,7 @@ int Screen::handleInputEvent(const InputEvent *event)
menuHandler::textMessageBaseMenu(); menuHandler::textMessageBaseMenu();
} }
} }
} else if (framesetInfo.positions.firstFavorite != 255 && } else if (framesetInfo.positions.firstFavorite != 255 && this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) { this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) {
menuHandler::favoriteBaseMenu(); menuHandler::favoriteBaseMenu();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_nodes || } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_nodes ||
@@ -1773,8 +1718,7 @@ int Screen::handleInputEvent(const InputEvent *event)
return 0; return 0;
} }
int Screen::handleAdminMessage(AdminModule_ObserverData *arg) int Screen::handleAdminMessage(AdminModule_ObserverData *arg) {
{
switch (arg->request->which_payload_variant) { switch (arg->request->which_payload_variant) {
// Node removed manually (i.e. via app) // Node removed manually (i.e. via app)
case meshtastic_AdminMessage_remove_by_nodenum_tag: case meshtastic_AdminMessage_remove_by_nodenum_tag:
@@ -1789,10 +1733,7 @@ int Screen::handleAdminMessage(AdminModule_ObserverData *arg)
return 0; return 0;
} }
bool Screen::isOverlayBannerShowing() bool Screen::isOverlayBannerShowing() { return NotificationRenderer::isOverlayBannerShowing(); }
{
return NotificationRenderer::isOverlayBannerShowing();
}
} // namespace graphics } // namespace graphics
@@ -1800,8 +1741,7 @@ bool Screen::isOverlayBannerShowing()
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
#endif // HAS_SCREEN #endif // HAS_SCREEN
bool shouldWakeOnReceivedMessage() bool shouldWakeOnReceivedMessage() {
{
/* /*
The goal here is to determine when we do NOT wake up the screen on message received: The goal here is to determine when we do NOT wake up the screen on message received:
- Any ext. notifications are turned on - Any ext. notifications are turned on
@@ -1811,9 +1751,8 @@ bool shouldWakeOnReceivedMessage()
if (moduleConfig.external_notification.enabled) { if (moduleConfig.external_notification.enabled) {
return false; return false;
} }
if (!IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_CLIENT, if (!IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_CLIENT, meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE,
meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE, meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN, meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN, meshtastic_Config_DeviceConfig_Role_CLIENT_BASE)) {
meshtastic_Config_DeviceConfig_Role_CLIENT_BASE)) {
return false; return false;
} }
if (powerStatus && powerStatus->getBatteryChargePercent() < 10) { if (powerStatus && powerStatus->getBatteryChargePercent() < 10) {
+35 -57
View File
@@ -10,8 +10,7 @@
#include <vector> #include <vector>
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2) #define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
namespace graphics namespace graphics {
{
enum notificationTypeEnum { none, text_banner, selection_picker, node_picker, number_picker, text_input }; enum notificationTypeEnum { none, text_banner, selection_picker, node_picker, number_picker, text_input };
struct BannerOverlayOptions { struct BannerOverlayOptions {
@@ -30,12 +29,10 @@ bool shouldWakeOnReceivedMessage();
#if !HAS_SCREEN #if !HAS_SCREEN
#include "power.h" #include "power.h"
namespace graphics namespace graphics {
{
// Noop class for boards without screen. // Noop class for boards without screen.
class Screen class Screen {
{ public:
public:
enum FrameFocus : uint8_t { enum FrameFocus : uint8_t {
FOCUS_DEFAULT, // No specific frame FOCUS_DEFAULT, // No specific frame
FOCUS_PRESERVE, // Return to the previous frame FOCUS_PRESERVE, // Return to the previous frame
@@ -129,19 +126,16 @@ class Screen
/// Convert an integer GPS coords to a floating point /// Convert an integer GPS coords to a floating point
#define DegD(i) (i * 1e-7) #define DegD(i) (i * 1e-7)
extern bool hasUnreadMessage; extern bool hasUnreadMessage;
namespace namespace {
{
/// A basic 2D point class for drawing /// A basic 2D point class for drawing
class Point class Point {
{ public:
public:
float x, y; float x, y;
Point(float _x, float _y) : x(_x), y(_y) {} Point(float _x, float _y) : x(_x), y(_y) {}
/// Apply a rotation around zero (standard rotation matrix math) /// Apply a rotation around zero (standard rotation matrix math)
void rotate(float radian) void rotate(float radian) {
{
float cos = cosf(radian), sin = sinf(radian); float cos = cosf(radian), sin = sinf(radian);
float rx = x * cos + y * sin, ry = -x * sin + y * cos; float rx = x * cos + y * sin, ry = -x * sin + y * cos;
@@ -149,14 +143,12 @@ class Point
y = ry; y = ry;
} }
void translate(int16_t dx, int dy) void translate(int16_t dx, int dy) {
{
x += dx; x += dx;
y += dy; y += dy;
} }
void scale(float f) void scale(float f) {
{
// We use -f here to counter the flip that happens // We use -f here to counter the flip that happens
// on the y axis when drawing and rotating on screen // on the y axis when drawing and rotating on screen
x *= f; x *= f;
@@ -166,8 +158,7 @@ class Point
} // namespace } // namespace
namespace graphics namespace graphics {
{
enum class FrameDirection { NEXT, PREVIOUS }; enum class FrameDirection { NEXT, PREVIOUS };
@@ -175,13 +166,12 @@ enum class FrameDirection { NEXT, PREVIOUS };
class Screen; class Screen;
/// Handles gathering and displaying debug information. /// Handles gathering and displaying debug information.
class DebugInfo class DebugInfo {
{ public:
public:
DebugInfo(const DebugInfo &) = delete; DebugInfo(const DebugInfo &) = delete;
DebugInfo &operator=(const DebugInfo &) = delete; DebugInfo &operator=(const DebugInfo &) = delete;
private: private:
friend Screen; friend Screen;
DebugInfo() {} DebugInfo() {}
@@ -202,8 +192,7 @@ class DebugInfo
* multiple times simultaneously. All state-changing calls are queued and executed * multiple times simultaneously. All state-changing calls are queued and executed
* when the main loop calls us. * when the main loop calls us.
*/ */
class Screen : public concurrency::OSThread class Screen : public concurrency::OSThread {
{
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver = CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate); CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> gpsStatusObserver = CallbackObserver<Screen, const meshtastic::Status *> gpsStatusObserver =
@@ -212,12 +201,11 @@ class Screen : public concurrency::OSThread
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate); CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver = CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules
CallbackObserver<Screen, const InputEvent *> inputObserver = CallbackObserver<Screen, const InputEvent *> inputObserver = CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent);
CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent);
CallbackObserver<Screen, AdminModule_ObserverData *> adminMessageObserver = CallbackObserver<Screen, AdminModule_ObserverData *> adminMessageObserver =
CallbackObserver<Screen, AdminModule_ObserverData *>(this, &Screen::handleAdminMessage); CallbackObserver<Screen, AdminModule_ObserverData *>(this, &Screen::handleAdminMessage);
public: public:
OLEDDisplay *getDisplayDevice() { return dispdev; } OLEDDisplay *getDisplayDevice() { return dispdev; }
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY); explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
@@ -282,16 +270,14 @@ class Screen : public concurrency::OSThread
void showFrame(FrameDirection direction); void showFrame(FrameDirection direction);
// generic alert start // generic alert start
void startAlert(FrameCallback _alertFrame) void startAlert(FrameCallback _alertFrame) {
{
alertFrame = _alertFrame; alertFrame = _alertFrame;
ScreenCmd cmd; ScreenCmd cmd;
cmd.cmd = Cmd::START_ALERT_FRAME; cmd.cmd = Cmd::START_ALERT_FRAME;
enqueueCmd(cmd); enqueueCmd(cmd);
} }
void startAlert(const char *_alertMessage) void startAlert(const char *_alertMessage) {
{
startAlert([_alertMessage](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { startAlert([_alertMessage](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
uint16_t x_offset = display->width() / 2; uint16_t x_offset = display->width() / 2;
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
@@ -300,8 +286,7 @@ class Screen : public concurrency::OSThread
}); });
} }
void endAlert() void endAlert() {
{
ScreenCmd cmd; ScreenCmd cmd;
cmd.cmd = Cmd::STOP_ALERT_FRAME; cmd.cmd = Cmd::STOP_ALERT_FRAME;
enqueueCmd(cmd); enqueueCmd(cmd);
@@ -312,17 +297,14 @@ class Screen : public concurrency::OSThread
void showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback); void showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback);
void showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function<void(uint32_t)> bannerCallback); void showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function<void(uint32_t)> bannerCallback);
void showTextInput(const char *header, const char *initialText, uint32_t durationMs, void showTextInput(const char *header, const char *initialText, uint32_t durationMs, std::function<void(const std::string &)> textCallback);
std::function<void(const std::string &)> textCallback);
void requestMenu(graphics::menuHandler::screenMenus menuToShow) void requestMenu(graphics::menuHandler::screenMenus menuToShow) {
{
graphics::menuHandler::menuQueue = menuToShow; graphics::menuHandler::menuQueue = menuToShow;
runNow(); runNow();
} }
void startFirmwareUpdateScreen() void startFirmwareUpdateScreen() {
{
ScreenCmd cmd; ScreenCmd cmd;
cmd.cmd = Cmd::START_FIRMWARE_UPDATE_SCREEN; cmd.cmd = Cmd::START_FIRMWARE_UPDATE_SCREEN;
enqueueCmd(cmd); enqueueCmd(cmd);
@@ -330,8 +312,7 @@ class Screen : public concurrency::OSThread
// Function to allow the AccelerometerThread to set the heading if a sensor provides it // Function to allow the AccelerometerThread to set the heading if a sensor provides it
// Mutex needed? // Mutex needed?
void setHeading(long _heading) void setHeading(long _heading) {
{
hasCompass = true; hasCompass = true;
compassHeading = fmod(_heading, 360); compassHeading = fmod(_heading, 360);
} }
@@ -350,15 +331,13 @@ class Screen : public concurrency::OSThread
/// Stops showing the boot screen. /// Stops showing the boot screen.
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); } void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
void runNow() void runNow() {
{
setFastFramerate(); setFastFramerate();
enqueueCmd(ScreenCmd{.cmd = Cmd::NOOP}); enqueueCmd(ScreenCmd{.cmd = Cmd::NOOP});
} }
/// Overrides the default utf8 character conversion, to replace empty space with question marks /// Overrides the default utf8 character conversion, to replace empty space with question marks
static char customFontTableLookup(const uint8_t ch) static char customFontTableLookup(const uint8_t ch) {
{
// UTF-8 to font table index converter // UTF-8 to font table index converter
// Code from http://playground.arduino.cc/Main/Utf8ascii // Code from http://playground.arduino.cc/Main/Utf8ascii
static uint8_t LASTCHAR; static uint8_t LASTCHAR;
@@ -434,8 +413,8 @@ class Screen : public concurrency::OSThread
return (uint8_t)(ch | 0xC0); return (uint8_t)(ch | 0xC0);
} }
// map UTF-8 cyrillic chars to it Windows-1251 (CP-1251) ASCII codes // map UTF-8 cyrillic chars to it Windows-1251 (CP-1251) ASCII codes
// note: in this case we must use compatible font - provided ArialMT_Plain_10/16/24 by 'ThingPulse/esp8266-oled-ssd1306' // note: in this case we must use compatible font - provided ArialMT_Plain_10/16/24 by
// library have empty chars for non-latin ASCII symbols // 'ThingPulse/esp8266-oled-ssd1306' library have empty chars for non-latin ASCII symbols
case 0xD0: { case 0xD0: {
SKIPREST = false; SKIPREST = false;
if (ch == 132) if (ch == 132)
@@ -560,14 +539,14 @@ class Screen : public concurrency::OSThread
#endif #endif
// If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the // If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs
// rest of it // for the rest of it
if (SKIPREST) if (SKIPREST)
return (uint8_t)0; return (uint8_t)0;
SKIPREST = true; SKIPREST = true;
return (uint8_t)191; // otherwise: return ¿ if character can't be converted (note that the font map we're using doesn't return (uint8_t)191; // otherwise: return ¿ if character can't be converted (note that the font map we're using
// stick to standard EASCII codes) // doesn't stick to standard EASCII codes)
} }
/// Returns a handle to the DebugInfo screen. /// Returns a handle to the DebugInfo screen.
@@ -597,7 +576,7 @@ class Screen : public concurrency::OSThread
void setScreensaverFrames(FrameCallback einkScreensaver = NULL); void setScreensaverFrames(FrameCallback einkScreensaver = NULL);
#endif #endif
protected: protected:
/// Updates the UI. /// Updates the UI.
// //
// Called periodically from the main loop. // Called periodically from the main loop.
@@ -610,7 +589,7 @@ class Screen : public concurrency::OSThread
uint16_t displayWidth = 0; uint16_t displayWidth = 0;
uint16_t displayHeight = 0; uint16_t displayHeight = 0;
private: private:
FrameCallback alertFrames[1]; FrameCallback alertFrames[1];
struct ScreenCmd { struct ScreenCmd {
Cmd cmd; Cmd cmd;
@@ -621,8 +600,7 @@ class Screen : public concurrency::OSThread
}; };
/// Enques given command item to be processed by main loop(). /// Enques given command item to be processed by main loop().
bool enqueueCmd(const ScreenCmd &cmd) bool enqueueCmd(const ScreenCmd &cmd) {
{
if (!useDisplay) if (!useDisplay)
return false; // not enqueued if our display is not in use return false; // not enqueued if our display is not in use
else { else {
+3 -3
View File
@@ -72,9 +72,9 @@
#endif #endif
#endif #endif
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || \ defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \ defined(USE_ST7796)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS) !defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts // The screen is bigger so use bigger fonts
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19 #define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
+15 -27
View File
@@ -13,11 +13,9 @@
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
#include <graphics/images.h> #include <graphics/images.h>
namespace graphics namespace graphics {
{
ScreenResolution determineScreenResolution(int16_t screenheight, int16_t screenwidth) ScreenResolution determineScreenResolution(int16_t screenheight, int16_t screenwidth) {
{
#ifdef FORCE_LOW_RES #ifdef FORCE_LOW_RES
return ScreenResolution::Low; return ScreenResolution::Low;
@@ -42,8 +40,7 @@ ScreenResolution determineScreenResolution(int16_t screenheight, int16_t screenw
#endif #endif
} }
void decomposeTime(uint32_t rtc_sec, int &hour, int &minute, int &second) void decomposeTime(uint32_t rtc_sec, int &hour, int &minute, int &second) {
{
hour = 0; hour = 0;
minute = 0; minute = 0;
second = 0; second = 0;
@@ -68,8 +65,7 @@ uint32_t lastMailBlink = 0;
// ********************************* // *********************************
// * Rounded Header when inverted * // * Rounded Header when inverted *
// ********************************* // *********************************
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r) void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r) {
{
// Draw the center and side rectangles // Draw the center and side rectangles
display->fillRect(x + r, y, w - 2 * r, h); // center bar display->fillRect(x + r, y, w - 2 * r, h); // center bar
display->fillRect(x, y + r, r, h - 2 * r); // left edge display->fillRect(x, y + r, r, h - 2 * r); // left edge
@@ -85,8 +81,7 @@ void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w,
// ************************* // *************************
// * Common Header Drawing * // * Common Header Drawing *
// ************************* // *************************
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool force_no_invert, bool show_date) void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool force_no_invert, bool show_date) {
{
constexpr int HEADER_OFFSET_Y = 1; constexpr int HEADER_OFFSET_Y = 1;
y += HEADER_OFFSET_Y; y += HEADER_OFFSET_Y;
@@ -138,8 +133,8 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
isCharging = false; isCharging = false;
} }
if (chargePercent == 101) { if (chargePercent == 101) {
usbPowered = true; // Forcing this flag on for the express purpose that some devices have no concept of having a USB cable usbPowered = true; // Forcing this flag on for the express purpose that some devices have no concept of having a USB
// plugged in // cable plugged in
} }
uint32_t now = millis(); uint32_t now = millis();
@@ -399,8 +394,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
display->setColor(WHITE); // Reset for other UI display->setColor(WHITE); // Reset for other UI
} }
const int *getTextPositions(OLEDDisplay *display) const int *getTextPositions(OLEDDisplay *display) {
{
static int textPositions[7]; // Static array that persists beyond function scope static int textPositions[7]; // Static array that persists beyond function scope
if (currentResolution == ScreenResolution::High) { if (currentResolution == ScreenResolution::High) {
@@ -426,12 +420,10 @@ const int *getTextPositions(OLEDDisplay *display)
// ************************* // *************************
// * Common Footer Drawing * // * Common Footer Drawing *
// ************************* // *************************
void drawCommonFooter(OLEDDisplay *display, int16_t x, int16_t y) void drawCommonFooter(OLEDDisplay *display, int16_t x, int16_t y) {
{
bool drawConnectionState = false; bool drawConnectionState = false;
if (service->api_state == service->STATE_BLE || service->api_state == service->STATE_WIFI || if (service->api_state == service->STATE_BLE || service->api_state == service->STATE_WIFI || service->api_state == service->STATE_SERIAL ||
service->api_state == service->STATE_SERIAL || service->api_state == service->STATE_PACKET || service->api_state == service->STATE_PACKET || service->api_state == service->STATE_HTTP || service->api_state == service->STATE_ETH) {
service->api_state == service->STATE_HTTP || service->api_state == service->STATE_ETH) {
drawConnectionState = true; drawConnectionState = true;
} }
@@ -458,20 +450,17 @@ void drawCommonFooter(OLEDDisplay *display, int16_t x, int16_t y)
} }
} else { } else {
display->drawXbm(0, SCREEN_HEIGHT - connection_icon_height, connection_icon_width, connection_icon_height, display->drawXbm(0, SCREEN_HEIGHT - connection_icon_height, connection_icon_width, connection_icon_height, connection_icon);
connection_icon);
} }
} }
} }
bool isAllowedPunctuation(char c) bool isAllowedPunctuation(char c) {
{
const std::string allowed = ".,!?;:-_()[]{}'\"@#$/\\&+=%~^ "; const std::string allowed = ".,!?;:-_()[]{}'\"@#$/\\&+=%~^ ";
return allowed.find(c) != std::string::npos; return allowed.find(c) != std::string::npos;
} }
static void replaceAll(std::string &s, const std::string &from, const std::string &to) static void replaceAll(std::string &s, const std::string &from, const std::string &to) {
{
if (from.empty()) if (from.empty())
return; return;
size_t pos = 0; size_t pos = 0;
@@ -481,8 +470,7 @@ static void replaceAll(std::string &s, const std::string &from, const std::strin
} }
} }
std::string sanitizeString(const std::string &input) std::string sanitizeString(const std::string &input) {
{
std::string output; std::string output;
bool inReplacement = false; bool inReplacement = false;
+2 -4
View File
@@ -3,8 +3,7 @@
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
#include <string> #include <string>
namespace graphics namespace graphics {
{
// ======================= // =======================
// Shared UI Helpers // Shared UI Helpers
@@ -51,8 +50,7 @@ void decomposeTime(uint32_t rtc_sec, int &hour, int &minute, int &second);
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r); void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r);
// Shared battery/time/mail header // Shared battery/time/mail header
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr = "", bool force_no_invert = false, void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr = "", bool force_no_invert = false, bool show_date = false);
bool show_date = false);
// Shared battery/time/mail header // Shared battery/time/mail header
void drawCommonFooter(OLEDDisplay *display, int16_t x, int16_t y); void drawCommonFooter(OLEDDisplay *display, int16_t x, int16_t y);
+74 -125
View File
@@ -29,23 +29,21 @@ uint16_t TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
#define TFT_INVERT true #define TFT_INVERT true
#endif #endif
class LGFX : public lgfx::LGFX_Device class LGFX : public lgfx::LGFX_Device {
{
lgfx::Panel_ST7735S _panel_instance; lgfx::Panel_ST7735S _panel_instance;
lgfx::Bus_SPI _bus_instance; lgfx::Bus_SPI _bus_instance;
lgfx::Light_PWM _light_instance; lgfx::Light_PWM _light_instance;
public: public:
LGFX(void) LGFX(void) {
{
{ {
auto cfg = _bus_instance.config(); auto cfg = _bus_instance.config();
// configure SPI // configure SPI
cfg.spi_host = ST7735_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST cfg.spi_host = ST7735_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
cfg.spi_mode = 0; cfg.spi_mode = 0;
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by
// 80MHz by an integer) // dividing 80MHz by an integer)
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin
cfg.use_lock = true; // Set to true to use transaction locking cfg.use_lock = true; // Set to true to use transaction locking
@@ -80,8 +78,7 @@ class LGFX : public lgfx::LGFX_Device
cfg.readable = true; // Set to true if data can be read cfg.readable = true; // Set to true if data can be read
cfg.invert = TFT_INVERT; // Set to true if the light/darkness of the panel is reversed cfg.invert = TFT_INVERT; // Set to true if the light/darkness of the panel is reversed
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
cfg.dlen_16bit = cfg.dlen_16bit = false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.) cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
// Set the following only when the display is shifted with a driver with a variable number of pixels, such as the // Set the following only when the display is shifted with a driver with a variable number of pixels, such as the
@@ -119,10 +116,7 @@ TFT_eSPI *tft = nullptr;
FT6336U ft6336u; FT6336U ft6336u;
static uint8_t _rak14014_touch_int = false; // TP interrupt generation flag. static uint8_t _rak14014_touch_int = false; // TP interrupt generation flag.
static void rak14014_tpIntHandle(void) static void rak14014_tpIntHandle(void) { _rak14014_touch_int = true; }
{
_rak14014_touch_int = true;
}
#elif defined(HACKADAY_COMMUNICATOR) #elif defined(HACKADAY_COMMUNICATOR)
#include <Arduino_GFX_Library.h> #include <Arduino_GFX_Library.h>
@@ -136,18 +130,16 @@ Arduino_GFX *tft = nullptr;
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp> #include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
TCA9534 ioex; TCA9534 ioex;
class LGFX : public lgfx::LGFX_Device class LGFX : public lgfx::LGFX_Device {
{
lgfx::Bus_RGB _bus_instance; lgfx::Bus_RGB _bus_instance;
lgfx::Panel_RGB _panel_instance; lgfx::Panel_RGB _panel_instance;
lgfx::Touch_GT911 _touch_instance; lgfx::Touch_GT911 _touch_instance;
public: public:
const uint16_t screenWidth = TFT_WIDTH; const uint16_t screenWidth = TFT_WIDTH;
const uint16_t screenHeight = TFT_HEIGHT; const uint16_t screenHeight = TFT_HEIGHT;
bool init_impl(bool use_reset, bool use_clear) override bool init_impl(bool use_reset, bool use_clear) override {
{
ioex.attach(Wire); ioex.attach(Wire);
ioex.setDeviceAddress(0x18); ioex.setDeviceAddress(0x18);
ioex.config(1, TCA9534::Config::OUT); ioex.config(1, TCA9534::Config::OUT);
@@ -170,8 +162,7 @@ class LGFX : public lgfx::LGFX_Device
return LGFX_Device::init_impl(use_reset, use_clear); return LGFX_Device::init_impl(use_reset, use_clear);
} }
LGFX(void) LGFX(void) {
{
{ {
auto cfg = _panel_instance.config(); auto cfg = _panel_instance.config();
@@ -301,24 +292,22 @@ static LGFX *tft = nullptr;
#elif defined(ILI9488_CS) #elif defined(ILI9488_CS)
#include <LovyanGFX.hpp> // Graphics and font library for ILI9488 driver chip #include <LovyanGFX.hpp> // Graphics and font library for ILI9488 driver chip
class LGFX : public lgfx::LGFX_Device class LGFX : public lgfx::LGFX_Device {
{
lgfx::Panel_ILI9488 _panel_instance; lgfx::Panel_ILI9488 _panel_instance;
lgfx::Bus_SPI _bus_instance; lgfx::Bus_SPI _bus_instance;
lgfx::Light_PWM _light_instance; lgfx::Light_PWM _light_instance;
lgfx::Touch_GT911 _touch_instance; lgfx::Touch_GT911 _touch_instance;
public: public:
LGFX(void) LGFX(void) {
{
{ {
auto cfg = _bus_instance.config(); auto cfg = _bus_instance.config();
// configure SPI // configure SPI
cfg.spi_host = ILI9488_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST cfg.spi_host = ILI9488_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
cfg.spi_mode = 0; cfg.spi_mode = 0;
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by
// 80MHz by an integer) // dividing 80MHz by an integer)
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin
cfg.use_lock = true; // Set to true to use transaction locking cfg.use_lock = true; // Set to true to use transaction locking
@@ -359,8 +348,7 @@ class LGFX : public lgfx::LGFX_Device
cfg.readable = true; // Set to true if data can be read cfg.readable = true; // Set to true if data can be read
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
cfg.dlen_16bit = cfg.dlen_16bit = false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.) cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
// Set the following only when the display is shifted with a driver with a variable number of pixels, such as the // Set the following only when the display is shifted with a driver with a variable number of pixels, such as the
@@ -431,15 +419,11 @@ static LGFX *tft = nullptr;
#ifdef HELTEC_V4_TFT #ifdef HELTEC_V4_TFT
#include "chsc6x.h" #include "chsc6x.h"
#include "lgfx/v1/Touch.hpp" #include "lgfx/v1/Touch.hpp"
namespace lgfx namespace lgfx {
{ inline namespace v1 {
inline namespace v1 class TOUCH_CHSC6X : public ITouch {
{ public:
class TOUCH_CHSC6X : public ITouch TOUCH_CHSC6X(void) {
{
public:
TOUCH_CHSC6X(void)
{
_cfg.i2c_addr = TOUCH_SLAVE_ADDRESS; _cfg.i2c_addr = TOUCH_SLAVE_ADDRESS;
_cfg.x_min = 0; _cfg.x_min = 0;
_cfg.x_max = 240; _cfg.x_max = 240;
@@ -447,8 +431,7 @@ class TOUCH_CHSC6X : public ITouch
_cfg.y_max = 320; _cfg.y_max = 320;
}; };
bool init(void) override bool init(void) override {
{
if (chsc6xTouch == nullptr) { if (chsc6xTouch == nullptr) {
chsc6xTouch = new chsc6x(&Wire1, TOUCH_SDA_PIN, TOUCH_SCL_PIN, TOUCH_INT_PIN, TOUCH_RST_PIN); chsc6xTouch = new chsc6x(&Wire1, TOUCH_SDA_PIN, TOUCH_SCL_PIN, TOUCH_INT_PIN, TOUCH_RST_PIN);
} }
@@ -456,8 +439,7 @@ class TOUCH_CHSC6X : public ITouch
return true; return true;
}; };
uint_fast8_t getTouchRaw(touch_point_t *tp, uint_fast8_t count) override uint_fast8_t getTouchRaw(touch_point_t *tp, uint_fast8_t count) override {
{
uint16_t raw_x, raw_y; uint16_t raw_x, raw_y;
if (chsc6xTouch->chsc6x_read_touch_info(&raw_x, &raw_y) == 0) { if (chsc6xTouch->chsc6x_read_touch_info(&raw_x, &raw_y) == 0) {
tp[0].x = 320 - 1 - raw_y; tp[0].x = 320 - 1 - raw_y;
@@ -473,14 +455,13 @@ class TOUCH_CHSC6X : public ITouch
void wakeup(void) override{}; void wakeup(void) override{};
void sleep(void) override{}; void sleep(void) override{};
private: private:
chsc6x *chsc6xTouch = nullptr; chsc6x *chsc6xTouch = nullptr;
}; };
} // namespace v1 } // namespace v1
} // namespace lgfx } // namespace lgfx
#endif #endif
class LGFX : public lgfx::LGFX_Device class LGFX : public lgfx::LGFX_Device {
{
lgfx::Panel_ST7789 _panel_instance; lgfx::Panel_ST7789 _panel_instance;
lgfx::Bus_SPI _bus_instance; lgfx::Bus_SPI _bus_instance;
lgfx::Light_PWM _light_instance; lgfx::Light_PWM _light_instance;
@@ -494,17 +475,16 @@ class LGFX : public lgfx::LGFX_Device
#endif #endif
#endif #endif
public: public:
LGFX(void) LGFX(void) {
{
{ {
auto cfg = _bus_instance.config(); auto cfg = _bus_instance.config();
// SPI // SPI
cfg.spi_host = ST7789_SPI_HOST; cfg.spi_host = ST7789_SPI_HOST;
cfg.spi_mode = 0; cfg.spi_mode = 0;
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by
// 80MHz by an integer) // dividing 80MHz by an integer)
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
cfg.spi_3wire = false; cfg.spi_3wire = false;
cfg.use_lock = true; // Set to true to use transaction locking cfg.use_lock = true; // Set to true to use transaction locking
@@ -554,8 +534,7 @@ class LGFX : public lgfx::LGFX_Device
cfg.readable = true; // Set to true if data can be read cfg.readable = true; // Set to true if data can be read
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
cfg.dlen_16bit = cfg.dlen_16bit = false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.) cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
// Set the following only when the display is shifted with a driver with a variable number of pixels, such as the // Set the following only when the display is shifted with a driver with a variable number of pixels, such as the
@@ -623,23 +602,21 @@ static LGFX *tft = nullptr;
#elif defined(ST7796_CS) #elif defined(ST7796_CS)
#include <LovyanGFX.hpp> // Graphics and font library for ST7796 driver chip #include <LovyanGFX.hpp> // Graphics and font library for ST7796 driver chip
class LGFX : public lgfx::LGFX_Device class LGFX : public lgfx::LGFX_Device {
{
lgfx::Panel_ST7796 _panel_instance; lgfx::Panel_ST7796 _panel_instance;
lgfx::Bus_SPI _bus_instance; lgfx::Bus_SPI _bus_instance;
lgfx::Light_PWM _light_instance; lgfx::Light_PWM _light_instance;
public: public:
LGFX(void) LGFX(void) {
{
{ {
auto cfg = _bus_instance.config(); auto cfg = _bus_instance.config();
// SPI // SPI
cfg.spi_host = ST7796_SPI_HOST; cfg.spi_host = ST7796_SPI_HOST;
cfg.spi_mode = 0; cfg.spi_mode = 0;
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by
// 80MHz by an integer) // dividing 80MHz by an integer)
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
cfg.spi_3wire = false; cfg.spi_3wire = false;
cfg.use_lock = true; // Set to true to use transaction locking cfg.use_lock = true; // Set to true to use transaction locking
@@ -677,8 +654,7 @@ class LGFX : public lgfx::LGFX_Device
cfg.readable = true; // Set to true if data can be read cfg.readable = true; // Set to true if data can be read
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
cfg.dlen_16bit = cfg.dlen_16bit = false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.) cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
_panel_instance.config(cfg); _panel_instance.config(cfg);
@@ -713,8 +689,7 @@ static LGFX *tft = nullptr;
#define TFT_BL ILI9341_BACKLIGHT_EN #define TFT_BL ILI9341_BACKLIGHT_EN
#endif #endif
class LGFX : public lgfx::LGFX_Device class LGFX : public lgfx::LGFX_Device {
{
#if defined(ILI9341_DRIVER) #if defined(ILI9341_DRIVER)
lgfx::Panel_ILI9341 _panel_instance; lgfx::Panel_ILI9341 _panel_instance;
#elif defined(ILI9342_DRIVER) #elif defined(ILI9342_DRIVER)
@@ -723,9 +698,8 @@ class LGFX : public lgfx::LGFX_Device
lgfx::Bus_SPI _bus_instance; lgfx::Bus_SPI _bus_instance;
lgfx::Light_PWM _light_instance; lgfx::Light_PWM _light_instance;
public: public:
LGFX(void) LGFX(void) {
{
{ {
auto cfg = _bus_instance.config(); auto cfg = _bus_instance.config();
@@ -736,8 +710,8 @@ class LGFX : public lgfx::LGFX_Device
cfg.spi_host = ILI9342_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST cfg.spi_host = ILI9342_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
#endif #endif
cfg.spi_mode = 0; cfg.spi_mode = 0;
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by
// 80MHz by an integer) // dividing 80MHz by an integer)
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin
cfg.use_lock = true; // Set to true to use transaction locking cfg.use_lock = true; // Set to true to use transaction locking
@@ -772,8 +746,7 @@ class LGFX : public lgfx::LGFX_Device
cfg.readable = true; // Set to true if data can be read cfg.readable = true; // Set to true if data can be read
cfg.invert = false; // Set to true if the light/darkness of the panel is reversed cfg.invert = false; // Set to true if the light/darkness of the panel is reversed
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
cfg.dlen_16bit = cfg.dlen_16bit = false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.) cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
// Set the following only when the display is shifted with a driver with a variable number of pixels, such as the // Set the following only when the display is shifted with a driver with a variable number of pixels, such as the
@@ -812,17 +785,15 @@ static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
#include "Panel_sdl.hpp" #include "Panel_sdl.hpp"
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip #include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
class LGFX : public lgfx::LGFX_Device class LGFX : public lgfx::LGFX_Device {
{
lgfx::Bus_SPI _bus_instance; lgfx::Bus_SPI _bus_instance;
lgfx::ITouch *_touch_instance; lgfx::ITouch *_touch_instance;
public: public:
lgfx::Panel_Device *_panel_instance; lgfx::Panel_Device *_panel_instance;
LGFX(void) LGFX(void) {
{
if (portduino_config.displayPanel == st7789) if (portduino_config.displayPanel == st7789)
_panel_instance = new lgfx::Panel_ST7789; _panel_instance = new lgfx::Panel_ST7789;
else if (portduino_config.displayPanel == st7735) else if (portduino_config.displayPanel == st7735)
@@ -921,17 +892,15 @@ static LGFX *tft = nullptr;
#elif defined(HX8357_CS) #elif defined(HX8357_CS)
#include <LovyanGFX.hpp> // Graphics and font library for HX8357 driver chip #include <LovyanGFX.hpp> // Graphics and font library for HX8357 driver chip
class LGFX : public lgfx::LGFX_Device class LGFX : public lgfx::LGFX_Device {
{
lgfx::Panel_HX8357D _panel_instance; lgfx::Panel_HX8357D _panel_instance;
lgfx::Bus_SPI _bus_instance; lgfx::Bus_SPI _bus_instance;
#if defined(USE_XPT2046) #if defined(USE_XPT2046)
lgfx::Touch_XPT2046 _touch_instance; lgfx::Touch_XPT2046 _touch_instance;
#endif #endif
public: public:
LGFX(void) LGFX(void) {
{
// Panel_HX8357D // Panel_HX8357D
{ {
// configure SPI // configure SPI
@@ -939,8 +908,8 @@ class LGFX : public lgfx::LGFX_Device
cfg.spi_host = HX8357_SPI_HOST; cfg.spi_host = HX8357_SPI_HOST;
cfg.spi_mode = 0; cfg.spi_mode = 0;
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by
// 80MHz by an integer) // dividing 80MHz by an integer)
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin
cfg.use_lock = true; // Set to true to use transaction locking cfg.use_lock = true; // Set to true to use transaction locking
@@ -1006,11 +975,9 @@ static LGFX *tft = nullptr;
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp> #include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp>
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp> #include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
class PanelInit_ST7701 : public lgfx::Panel_ST7701 class PanelInit_ST7701 : public lgfx::Panel_ST7701 {
{ public:
public: const uint8_t *getInitCommands(uint8_t listno) const override {
const uint8_t *getInitCommands(uint8_t listno) const override
{
// 180 degree hw rotation: vertical flip, horizontal flip // 180 degree hw rotation: vertical flip, horizontal flip
static constexpr const uint8_t list1[] = {0x36, 1, 0x10, // MADCTL for vertical flip static constexpr const uint8_t list1[] = {0x36, 1, 0x10, // MADCTL for vertical flip
0xFF, 5, 0x77, 0x01, 0x00, 0x00, 0x10, // Command2 BK0 SEL 0xFF, 5, 0x77, 0x01, 0x00, 0x00, 0x10, // Command2 BK0 SEL
@@ -1026,16 +993,14 @@ class PanelInit_ST7701 : public lgfx::Panel_ST7701
} }
}; };
class LGFX : public lgfx::LGFX_Device class LGFX : public lgfx::LGFX_Device {
{
PanelInit_ST7701 _panel_instance; PanelInit_ST7701 _panel_instance;
lgfx::Bus_RGB _bus_instance; lgfx::Bus_RGB _bus_instance;
lgfx::Light_PWM _light_instance; lgfx::Light_PWM _light_instance;
lgfx::Touch_FT5x06 _touch_instance; lgfx::Touch_FT5x06 _touch_instance;
public: public:
LGFX(void) LGFX(void) {
{
{ {
auto cfg = _panel_instance.config(); auto cfg = _panel_instance.config();
cfg.memory_width = 800; cfg.memory_width = 800;
@@ -1150,8 +1115,7 @@ extern unPhone unphone;
GpioPin *TFTDisplay::backlightEnable = NULL; GpioPin *TFTDisplay::backlightEnable = NULL;
TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) {
{
LOG_DEBUG("TFTDisplay!"); LOG_DEBUG("TFTDisplay!");
#ifdef TFT_BL #ifdef TFT_BL
@@ -1159,8 +1123,8 @@ TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY g
if (!TFT_BACKLIGHT_ON) { // Need to invert the pin before hardware if (!TFT_BACKLIGHT_ON) { // Need to invert the pin before hardware
auto virtPin = new GpioVirtPin(); auto virtPin = new GpioVirtPin();
new GpioNotTransformer( new GpioNotTransformer(virtPin,
virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio
p = virtPin; p = virtPin;
} }
#else #else
@@ -1182,8 +1146,7 @@ TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY g
#endif #endif
} }
TFTDisplay::~TFTDisplay() TFTDisplay::~TFTDisplay() {
{
// Clean up allocated line pixel buffer to prevent memory leak // Clean up allocated line pixel buffer to prevent memory leak
if (linePixelBuffer != nullptr) { if (linePixelBuffer != nullptr) {
free(linePixelBuffer); free(linePixelBuffer);
@@ -1192,8 +1155,7 @@ TFTDisplay::~TFTDisplay()
} }
// Write the buffer to the display memory // Write the buffer to the display memory
void TFTDisplay::display(bool fromBlank) void TFTDisplay::display(bool fromBlank) {
{
if (fromBlank) if (fromBlank)
tft->fillScreen(TFT_BLACK); tft->fillScreen(TFT_BLACK);
@@ -1275,13 +1237,11 @@ void TFTDisplay::display(bool fromBlank)
} }
} }
#if defined(HACKADAY_COMMUNICATOR) #if defined(HACKADAY_COMMUNICATOR)
tft->draw16bitBeRGBBitmap(x_FirstPixelUpdate, y, &linePixelBuffer[x_FirstPixelUpdate], tft->draw16bitBeRGBBitmap(x_FirstPixelUpdate, y, &linePixelBuffer[x_FirstPixelUpdate], (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1);
(x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1);
#else #else
// Step 4: Send the changed pixels on this line to the screen as a single block transfer. // Step 4: Send the changed pixels on this line to the screen as a single block transfer.
// This function accepts pixel data MSB first so it can dump the memory straight out the SPI port. // This function accepts pixel data MSB first so it can dump the memory straight out the SPI port.
tft->pushRect(x_FirstPixelUpdate, y, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1, tft->pushRect(x_FirstPixelUpdate, y, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1, &linePixelBuffer[x_FirstPixelUpdate]);
&linePixelBuffer[x_FirstPixelUpdate]);
#endif #endif
somethingChanged = true; somethingChanged = true;
} }
@@ -1292,8 +1252,7 @@ void TFTDisplay::display(bool fromBlank)
memcpy(buffer_back, buffer, displayBufferSize); memcpy(buffer_back, buffer, displayBufferSize);
} }
void TFTDisplay::sdlLoop() void TFTDisplay::sdlLoop() {
{
#if defined(SDL_h_) #if defined(SDL_h_)
static int lastPressed = 0; static int lastPressed = 0;
static int shuttingDown = false; static int shuttingDown = false;
@@ -1335,8 +1294,7 @@ void TFTDisplay::sdlLoop()
} }
// Send a command to the display (low level function) // Send a command to the display (low level function)
void TFTDisplay::sendCommand(uint8_t com) void TFTDisplay::sendCommand(uint8_t com) {
{
// handle display on/off directly // handle display on/off directly
switch (com) { switch (com) {
case DISPLAYON: { case DISPLAYON: {
@@ -1399,8 +1357,7 @@ void TFTDisplay::sendCommand(uint8_t com)
// Drop all other commands to device (we just update the buffer) // Drop all other commands to device (we just update the buffer)
} }
void TFTDisplay::setDisplayBrightness(uint8_t _brightness) void TFTDisplay::setDisplayBrightness(uint8_t _brightness) {
{
#ifdef RAK14014 #ifdef RAK14014
// todo // todo
#elif !defined(HACKADAY_COMMUNICATOR) #elif !defined(HACKADAY_COMMUNICATOR)
@@ -1409,16 +1366,14 @@ void TFTDisplay::setDisplayBrightness(uint8_t _brightness)
#endif #endif
} }
void TFTDisplay::flipScreenVertically() void TFTDisplay::flipScreenVertically() {
{
#if defined(T_WATCH_S3) #if defined(T_WATCH_S3)
LOG_DEBUG("Flip TFT vertically"); // T-Watch S3 right-handed orientation LOG_DEBUG("Flip TFT vertically"); // T-Watch S3 right-handed orientation
tft->setRotation(0); tft->setRotation(0);
#endif #endif
} }
bool TFTDisplay::hasTouch(void) bool TFTDisplay::hasTouch(void) {
{
#ifdef RAK14014 #ifdef RAK14014
return true; return true;
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) #elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
@@ -1428,8 +1383,7 @@ bool TFTDisplay::hasTouch(void)
#endif #endif
} }
bool TFTDisplay::getTouch(int16_t *x, int16_t *y) bool TFTDisplay::getTouch(int16_t *x, int16_t *y) {
{
#ifdef RAK14014 #ifdef RAK14014
if (_rak14014_touch_int) { if (_rak14014_touch_int) {
_rak14014_touch_int = false; _rak14014_touch_int = false;
@@ -1447,23 +1401,18 @@ bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
#endif #endif
} }
void TFTDisplay::setDetected(uint8_t detected) void TFTDisplay::setDetected(uint8_t detected) { (void)detected; }
{
(void)detected;
}
// Connect to the display // Connect to the display
bool TFTDisplay::connect() bool TFTDisplay::connect() {
{
concurrency::LockGuard g(spiLock); concurrency::LockGuard g(spiLock);
LOG_INFO("Do TFT init"); LOG_INFO("Do TFT init");
#ifdef RAK14014 #ifdef RAK14014
tft = new TFT_eSPI; tft = new TFT_eSPI;
#elif defined(HACKADAY_COMMUNICATOR) #elif defined(HACKADAY_COMMUNICATOR)
bus = new Arduino_ESP32SPI(TFT_DC, TFT_CS, 38 /* SCK */, 21 /* MOSI */, GFX_NOT_DEFINED /* MISO */, HSPI /* spi_num */); bus = new Arduino_ESP32SPI(TFT_DC, TFT_CS, 38 /* SCK */, 21 /* MOSI */, GFX_NOT_DEFINED /* MISO */, HSPI /* spi_num */);
tft = new Arduino_NV3007(bus, 40, 0 /* rotation */, false /* IPS */, 142 /* width */, 428 /* height */, 12 /* col offset 1 */, tft = new Arduino_NV3007(bus, 40, 0 /* rotation */, false /* IPS */, 142 /* width */, 428 /* height */, 12 /* col offset 1 */, 0 /* row offset 1 */,
0 /* row offset 1 */, 14 /* col offset 2 */, 0 /* row offset 2 */, nv3007_279_init_operations, 14 /* col offset 2 */, 0 /* row offset 2 */, nv3007_279_init_operations, sizeof(nv3007_279_init_operations));
sizeof(nv3007_279_init_operations));
#else #else
tft = new LGFX; tft = new LGFX;
+5 -6
View File
@@ -12,9 +12,8 @@
* *
* turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted? * turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted?
*/ */
class TFTDisplay : public OLEDDisplay class TFTDisplay : public OLEDDisplay {
{ public:
public:
/* constructor /* constructor
FIXME - the parameters are not used, just a temporary hack to keep working like the old displays FIXME - the parameters are not used, just a temporary hack to keep working like the old displays
*/ */
@@ -45,14 +44,14 @@ class TFTDisplay : public OLEDDisplay
void setDetected(uint8_t detected); void setDetected(uint8_t detected);
/** /**
* This is normally managed entirely by TFTDisplay, but some rare applications (heltec tracker) might need to replace the * This is normally managed entirely by TFTDisplay, but some rare applications (heltec tracker) might need to replace
* default GPIO behavior with something a bit more complex. * the default GPIO behavior with something a bit more complex.
* *
* We (cruftily) make it static so that variant.cpp can access it without needing a ptr to the TFTDisplay instance. * We (cruftily) make it static so that variant.cpp can access it without needing a ptr to the TFTDisplay instance.
*/ */
static GpioPin *backlightEnable; static GpioPin *backlightEnable;
protected: protected:
// the header size of the buffer used, e.g. for the SPI command header // the header size of the buffer used, e.g. for the SPI command header
virtual int getBufferOffset(void) override { return 0; } virtual int getBufferOffset(void) override { return 0; }
+3 -6
View File
@@ -4,8 +4,7 @@
#include "mesh/NodeDB.h" #include "mesh/NodeDB.h"
#include <cstring> #include <cstring>
bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int32_t *daysAgo) bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int32_t *daysAgo) {
{
// Cache the result - avoid frequent recalculation // Cache the result - avoid frequent recalculation
static uint8_t hoursCached = 0, minutesCached = 0; static uint8_t hoursCached = 0, minutesCached = 0;
static uint32_t daysAgoCached = 0; static uint32_t daysAgoCached = 0;
@@ -73,8 +72,7 @@ bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int
return validCached; return validCached;
} }
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength) void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength) {
{
// Use an absolute timestamp in some cases. // Use an absolute timestamp in some cases.
// Particularly useful with E-Ink displays. Static UI, fewer refreshes. // Particularly useful with E-Ink displays. Static UI, fewer refreshes.
uint8_t timestampHours, timestampMinutes; uint8_t timestampHours, timestampMinutes;
@@ -102,8 +100,7 @@ void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
snprintf(timeStr, maxLength, "unknown age"); snprintf(timeStr, maxLength, "unknown age");
} }
void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs) void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs) {
{
uint32_t days = uptimeMillis / 86400000; uint32_t days = uptimeMillis / 86400000;
uint32_t hours = (uptimeMillis % 86400000) / 3600000; uint32_t hours = (uptimeMillis % 86400000) / 3600000;
uint32_t mins = (uptimeMillis % 3600000) / 60000; uint32_t mins = (uptimeMillis % 3600000) / 60000;
+26 -65
View File
@@ -8,11 +8,9 @@
#include <Arduino.h> #include <Arduino.h>
#include <vector> #include <vector>
namespace graphics namespace graphics {
{
VirtualKeyboard::VirtualKeyboard() : cursorRow(0), cursorCol(0), lastActivityTime(millis()) VirtualKeyboard::VirtualKeyboard() : cursorRow(0), cursorCol(0), lastActivityTime(millis()) {
{
initializeKeyboard(); initializeKeyboard();
// Set cursor to H(2, 5) // Set cursor to H(2, 5)
cursorRow = 2; cursorRow = 2;
@@ -21,8 +19,7 @@ VirtualKeyboard::VirtualKeyboard() : cursorRow(0), cursorCol(0), lastActivityTim
VirtualKeyboard::~VirtualKeyboard() {} VirtualKeyboard::~VirtualKeyboard() {}
void VirtualKeyboard::initializeKeyboard() void VirtualKeyboard::initializeKeyboard() {
{
// New 4 row, 11 column keyboard layout: // New 4 row, 11 column keyboard layout:
static const char LAYOUT[KEYBOARD_ROWS][KEYBOARD_COLS] = {{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '\b'}, static const char LAYOUT[KEYBOARD_ROWS][KEYBOARD_COLS] = {{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '\b'},
{'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '\n'}, {'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '\n'},
@@ -66,8 +63,7 @@ void VirtualKeyboard::initializeKeyboard()
} }
} }
void VirtualKeyboard::draw(OLEDDisplay *display, int16_t offsetX, int16_t offsetY) void VirtualKeyboard::draw(OLEDDisplay *display, int16_t offsetX, int16_t offsetY) {
{
// Repeat ticking is driven by NotificationRenderer once per frame // Repeat ticking is driven by NotificationRenderer once per frame
// Base styles // Base styles
display->setColor(WHITE); display->setColor(WHITE);
@@ -77,8 +73,8 @@ void VirtualKeyboard::draw(OLEDDisplay *display, int16_t offsetX, int16_t offset
const int screenW = display->getWidth(); const int screenW = display->getWidth();
const int screenH = display->getHeight(); const int screenH = display->getHeight();
// Decide wide-screen mode: if there is comfortable width, allow taller keys and reserve fixed width for last column labels // Decide wide-screen mode: if there is comfortable width, allow taller keys and reserve fixed width for last column
// Heuristic: if screen width >= 200px (e.g., 240x135), treat as wide // labels Heuristic: if screen width >= 200px (e.g., 240x135), treat as wide
const bool isWide = screenW >= 200; const bool isWide = screenW >= 200;
// Determine last-column label max width // Determine last-column label max width
@@ -184,8 +180,7 @@ void VirtualKeyboard::draw(OLEDDisplay *display, int16_t offsetX, int16_t offset
} }
} }
void VirtualKeyboard::drawInputArea(OLEDDisplay *display, int16_t offsetX, int16_t offsetY, int16_t keyboardStartY) void VirtualKeyboard::drawInputArea(OLEDDisplay *display, int16_t offsetX, int16_t offsetY, int16_t keyboardStartY) {
{
display->setColor(WHITE); display->setColor(WHITE);
const int screenWidth = display->getWidth(); const int screenWidth = display->getWidth();
@@ -408,9 +403,8 @@ void VirtualKeyboard::drawInputArea(OLEDDisplay *display, int16_t offsetX, int16
} }
} }
void VirtualKeyboard::drawKey(OLEDDisplay *display, const VirtualKey &key, bool selected, int16_t x, int16_t y, uint8_t width, void VirtualKeyboard::drawKey(OLEDDisplay *display, const VirtualKey &key, bool selected, int16_t x, int16_t y, uint8_t width, uint8_t height,
uint8_t height, bool isLastCol) bool isLastCol) {
{
// Draw key content // Draw key content
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
const int fontH = FONT_HEIGHT_SMALL; const int fontH = FONT_HEIGHT_SMALL;
@@ -510,8 +504,7 @@ void VirtualKeyboard::drawKey(OLEDDisplay *display, const VirtualKey &key, bool
display->drawString(textX, centeredTextY, keyText.c_str()); display->drawString(textX, centeredTextY, keyText.c_str());
} }
char VirtualKeyboard::getCharForKey(const VirtualKey &key, bool isLongPress) char VirtualKeyboard::getCharForKey(const VirtualKey &key, bool isLongPress) {
{
if (key.type != VK_CHAR) { if (key.type != VK_CHAR) {
return key.character; return key.character;
} }
@@ -526,8 +519,7 @@ char VirtualKeyboard::getCharForKey(const VirtualKey &key, bool isLongPress)
return c; return c;
} }
void VirtualKeyboard::moveCursorDelta(int dRow, int dCol) void VirtualKeyboard::moveCursorDelta(int dRow, int dCol) {
{
resetTimeout(); resetTimeout();
// wrap around rows and cols in the 4x11 grid // wrap around rows and cols in the 4x11 grid
int r = (int)cursorRow + dRow; int r = (int)cursorRow + dRow;
@@ -544,16 +536,9 @@ void VirtualKeyboard::moveCursorDelta(int dRow, int dCol)
cursorCol = (uint8_t)c; cursorCol = (uint8_t)c;
} }
void VirtualKeyboard::moveCursorUp() void VirtualKeyboard::moveCursorUp() { moveCursorDelta(-1, 0); }
{ void VirtualKeyboard::moveCursorDown() { moveCursorDelta(1, 0); }
moveCursorDelta(-1, 0); void VirtualKeyboard::moveCursorLeft() {
}
void VirtualKeyboard::moveCursorDown()
{
moveCursorDelta(1, 0);
}
void VirtualKeyboard::moveCursorLeft()
{
resetTimeout(); resetTimeout();
if (cursorCol > 0) { if (cursorCol > 0) {
@@ -568,8 +553,7 @@ void VirtualKeyboard::moveCursorLeft()
} }
} }
} }
void VirtualKeyboard::moveCursorRight() void VirtualKeyboard::moveCursorRight() {
{
resetTimeout(); resetTimeout();
if (cursorCol < KEYBOARD_COLS - 1) { if (cursorCol < KEYBOARD_COLS - 1) {
@@ -585,8 +569,7 @@ void VirtualKeyboard::moveCursorRight()
} }
} }
void VirtualKeyboard::handlePress() void VirtualKeyboard::handlePress() {
{
resetTimeout(); // Reset timeout on any input activity resetTimeout(); // Reset timeout on any input activity
const VirtualKey &key = keyboard[cursorRow][cursorCol]; const VirtualKey &key = keyboard[cursorRow][cursorCol];
@@ -626,8 +609,7 @@ void VirtualKeyboard::handlePress()
} }
} }
void VirtualKeyboard::handleLongPress() void VirtualKeyboard::handleLongPress() {
{
resetTimeout(); // Reset timeout on any input activity resetTimeout(); // Reset timeout on any input activity
const VirtualKey &key = keyboard[cursorRow][cursorCol]; const VirtualKey &key = keyboard[cursorRow][cursorCol];
@@ -668,22 +650,19 @@ void VirtualKeyboard::handleLongPress()
} }
} }
void VirtualKeyboard::insertCharacter(char c) void VirtualKeyboard::insertCharacter(char c) {
{
if (inputText.length() < 160) { // Reasonable text length limit if (inputText.length() < 160) { // Reasonable text length limit
inputText += c; inputText += c;
} }
} }
void VirtualKeyboard::deleteCharacter() void VirtualKeyboard::deleteCharacter() {
{
if (!inputText.empty()) { if (!inputText.empty()) {
inputText.pop_back(); inputText.pop_back();
} }
} }
void VirtualKeyboard::submitText() void VirtualKeyboard::submitText() {
{
LOG_INFO("Virtual keyboard: submitting text '%s'", inputText.c_str()); LOG_INFO("Virtual keyboard: submitting text '%s'", inputText.c_str());
// Only submit if text is not empty // Only submit if text is not empty
@@ -707,35 +686,17 @@ void VirtualKeyboard::submitText()
} }
} }
void VirtualKeyboard::setInputText(const std::string &text) void VirtualKeyboard::setInputText(const std::string &text) { inputText = text; }
{
inputText = text;
}
std::string VirtualKeyboard::getInputText() const std::string VirtualKeyboard::getInputText() const { return inputText; }
{
return inputText;
}
void VirtualKeyboard::setHeader(const std::string &header) void VirtualKeyboard::setHeader(const std::string &header) { headerText = header; }
{
headerText = header;
}
void VirtualKeyboard::setCallback(std::function<void(const std::string &)> callback) void VirtualKeyboard::setCallback(std::function<void(const std::string &)> callback) { onTextEntered = callback; }
{
onTextEntered = callback;
}
void VirtualKeyboard::resetTimeout() void VirtualKeyboard::resetTimeout() { lastActivityTime = millis(); }
{
lastActivityTime = millis();
}
bool VirtualKeyboard::isTimedOut() const bool VirtualKeyboard::isTimedOut() const { return (millis() - lastActivityTime) > TIMEOUT_MS; }
{
return (millis() - lastActivityTime) > TIMEOUT_MS;
}
} // namespace graphics } // namespace graphics
#endif #endif
+5 -8
View File
@@ -5,8 +5,7 @@
#include <functional> #include <functional>
#include <string> #include <string>
namespace graphics namespace graphics {
{
enum VirtualKeyType { VK_CHAR, VK_BACKSPACE, VK_ENTER, VK_SHIFT, VK_ESC, VK_SPACE }; enum VirtualKeyType { VK_CHAR, VK_BACKSPACE, VK_ENTER, VK_SHIFT, VK_ESC, VK_SPACE };
@@ -19,9 +18,8 @@ struct VirtualKey {
uint8_t height; uint8_t height;
}; };
class VirtualKeyboard class VirtualKeyboard {
{ public:
public:
VirtualKeyboard(); VirtualKeyboard();
~VirtualKeyboard(); ~VirtualKeyboard();
@@ -43,7 +41,7 @@ class VirtualKeyboard
void resetTimeout(); void resetTimeout();
bool isTimedOut() const; bool isTimedOut() const;
private: private:
static const uint8_t KEYBOARD_ROWS = 4; static const uint8_t KEYBOARD_ROWS = 4;
static const uint8_t KEYBOARD_COLS = 11; static const uint8_t KEYBOARD_COLS = 11;
static const uint8_t KEY_WIDTH = 9; static const uint8_t KEY_WIDTH = 9;
@@ -64,8 +62,7 @@ class VirtualKeyboard
static const uint32_t TIMEOUT_MS = 60000; // 1 minute timeout static const uint32_t TIMEOUT_MS = 60000; // 1 minute timeout
void initializeKeyboard(); void initializeKeyboard();
void drawKey(OLEDDisplay *display, const VirtualKey &key, bool selected, int16_t x, int16_t y, uint8_t w, uint8_t h, void drawKey(OLEDDisplay *display, const VirtualKey &key, bool selected, int16_t x, int16_t y, uint8_t w, uint8_t h, bool isLastCol);
bool isLastCol);
void drawInputArea(OLEDDisplay *display, int16_t offsetX, int16_t offsetY, int16_t keyboardStartY); void drawInputArea(OLEDDisplay *display, int16_t offsetX, int16_t offsetY, int16_t keyboardStartY);
// Unified cursor movement helper // Unified cursor movement helper
+12 -24
View File
@@ -12,11 +12,9 @@
#include "nimble/NimbleBluetooth.h" #include "nimble/NimbleBluetooth.h"
#endif #endif
namespace graphics namespace graphics {
{
namespace ClockRenderer namespace ClockRenderer {
{
// Segment bitmaps for numerals 0-9 stored in flash to save RAM. // Segment bitmaps for numerals 0-9 stored in flash to save RAM.
// Each row is a digit, each column is a segment state (1 = on, 0 = off). // Each row is a digit, each column is a segment state (1 = on, 0 = off).
@@ -43,8 +41,7 @@ static const uint8_t PROGMEM digitSegments[10][7] = {
{1, 1, 1, 1, 0, 1, 1} // 9 {1, 1, 1, 1, 0, 1, 1} // 9
}; };
void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale) void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale) {
{
uint16_t segmentWidth = SEGMENT_WIDTH * scale; uint16_t segmentWidth = SEGMENT_WIDTH * scale;
uint16_t segmentHeight = SEGMENT_HEIGHT * scale; uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
@@ -61,8 +58,7 @@ void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale)
display->fillRect(topAndBottomX, bottomY, segmentHeight, segmentHeight); display->fillRect(topAndBottomX, bottomY, segmentHeight, segmentHeight);
} }
void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale) void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale) {
{
// Read 7-segment pattern for the digit from flash // Read 7-segment pattern for the digit from flash
uint8_t seg[7]; uint8_t seg[7];
for (uint8_t i = 0; i < 7; i++) { for (uint8_t i = 0; i < 7; i++) {
@@ -111,8 +107,7 @@ void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t n
drawHorizontalSegment(display, segmentSevenX, segmentSevenY, segmentWidth, segmentHeight); drawHorizontalSegment(display, segmentSevenX, segmentSevenY, segmentWidth, segmentHeight);
} }
void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height) void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height) {
{
int halfHeight = height / 2; int halfHeight = height / 2;
// draw central rectangle // draw central rectangle
@@ -124,8 +119,7 @@ void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int he
display->fillTriangle(x + width, y, x + width + halfHeight, y + halfHeight, x + width, y + height - 1); display->fillTriangle(x + width, y, x + width + halfHeight, y + halfHeight, x + width, y + height - 1);
} }
void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height) void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height) {
{
int halfHeight = height / 2; int halfHeight = height / 2;
// draw central rectangle // draw central rectangle
@@ -137,8 +131,7 @@ void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int heig
display->fillTriangle(x, y + width, x + height - 1, y + width, x + halfHeight, y + width + halfHeight); display->fillTriangle(x, y + width, x + height - 1, y + width, x + halfHeight, y + width + halfHeight);
} }
void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) {
{
display->clear(); display->clear();
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
@@ -189,8 +182,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
float target_width = display->getWidth() * screenwidth_target_ratio; float target_width = display->getWidth() * screenwidth_target_ratio;
float target_height = float target_height =
display->getHeight() - display->getHeight() - ((currentResolution == ScreenResolution::High)
((currentResolution == ScreenResolution::High)
? 46 ? 46
: 33); // Be careful adjusting this number, we have to account for header and the text under the time : 33); // Be careful adjusting this number, we have to account for header and the text under the time
@@ -283,16 +275,14 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
if (scale >= 2.0f) { if (scale >= 2.0f) {
xOffset -= (int)(4.5f * scale); xOffset -= (int)(4.5f * scale);
} }
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - 1, display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - 1, secondString);
secondString);
#endif #endif
graphics::drawCommonFooter(display, x, y); graphics::drawCommonFooter(display, x, y);
} }
// Draw an analog clock // Draw an analog clock
void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) {
{
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
// === Set Title, Blank for Clock // === Set Title, Blank for Clock
const char *titleStr = ""; const char *titleStr = "";
@@ -350,8 +340,7 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
#ifdef USE_EINK #ifdef USE_EINK
yOffset += 3; yOffset += 3;
#endif #endif
display->drawString(centerX - (display->getStringWidth(isPM ? "pm" : "am") / 2), centerY + yOffset, display->drawString(centerX - (display->getStringWidth(isPM ? "pm" : "am") / 2), centerY + yOffset, isPM ? "pm" : "am");
isPM ? "pm" : "am");
} }
hour %= 12; hour %= 12;
if (hour == 0) if (hour == 0)
@@ -442,8 +431,7 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt); display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt);
} }
#else #else
if (currentResolution == ScreenResolution::High && if (currentResolution == ScreenResolution::High && (hourInt == 3 || hourInt == 6 || hourInt == 9 || hourInt == 12)) {
(hourInt == 3 || hourInt == 6 || hourInt == 9 || hourInt == 12)) {
// draw hour number // draw hour number
display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt); display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt);
} }
+2 -4
View File
@@ -3,14 +3,12 @@
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
#include <OLEDDisplayUi.h> #include <OLEDDisplayUi.h>
namespace graphics namespace graphics {
{
/// Forward declarations /// Forward declarations
class Screen; class Screen;
namespace ClockRenderer namespace ClockRenderer {
{
// Clock frame functions // Clock frame functions
void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);

Some files were not shown because too many files have changed in this diff Show More