add a .clang-format file (#9154)
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
BasedOnStyle: LLVM
|
||||||
|
ColumnLimit: 150
|
||||||
@@ -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,8 +34,7 @@ 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;
|
||||||
};
|
};
|
||||||
@@ -44,8 +42,7 @@ class ShouldExitException : public std::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;
|
||||||
|
|||||||
+12
-21
@@ -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:
|
public:
|
||||||
explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLighting")
|
explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLighting") {
|
||||||
{
|
|
||||||
notifyDeepSleepObserver.observe(¬ifyDeepSleep); // Let us know when shutdown() is issued.
|
notifyDeepSleepObserver.observe(¬ifyDeepSleep); // 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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+7
-14
@@ -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;
|
||||||
@@ -83,8 +78,7 @@ class AudioThread : public concurrency::OSThread
|
|||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
||||||
@@ -94,8 +88,7 @@ class AudioThread : public concurrency::OSThread
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
@@ -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};
|
|
||||||
@@ -21,8 +21,7 @@ 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();
|
||||||
|
|||||||
+7
-14
@@ -5,13 +5,11 @@
|
|||||||
#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,
|
||||||
@@ -30,16 +28,14 @@ class BluetoothStatus : public Status
|
|||||||
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
@@ -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;
|
||||||
|
|||||||
@@ -162,8 +162,7 @@ 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;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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,
|
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName, bool usePreset);
|
||||||
bool usePreset);
|
|
||||||
static const char *getDeviceRole(meshtastic_Config_DeviceConfig_Role role);
|
static const char *getDeviceRole(meshtastic_Config_DeviceConfig_Role role);
|
||||||
};
|
};
|
||||||
+14
-22
@@ -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
@@ -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));
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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
@@ -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 = {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-27
@@ -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
|
||||||
@@ -29,8 +26,7 @@ class GPSStatus : public Status
|
|||||||
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
@@ -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;
|
||||||
|
|
||||||
|
|||||||
+14
-22
@@ -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,8 +14,7 @@
|
|||||||
/**
|
/**
|
||||||
* 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;
|
||||||
};
|
};
|
||||||
@@ -22,8 +22,7 @@ class GpioPin
|
|||||||
/**
|
/**
|
||||||
* 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:
|
||||||
@@ -39,8 +38,7 @@ 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;
|
||||||
|
|
||||||
@@ -58,11 +56,10 @@ 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.
|
||||||
@@ -81,8 +78,7 @@ class GpioTransformer
|
|||||||
/**
|
/**
|
||||||
* 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);
|
||||||
|
|
||||||
@@ -100,8 +96,7 @@ 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) {}
|
||||||
|
|
||||||
@@ -117,8 +112,7 @@ 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 };
|
||||||
@@ -142,14 +136,12 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -67,14 +67,11 @@ 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);
|
||||||
|
|
||||||
|
|||||||
+6
-14
@@ -3,16 +3,13 @@
|
|||||||
#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;
|
||||||
@@ -23,8 +20,7 @@ class NodeStatus : public Status
|
|||||||
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;
|
||||||
|
|||||||
+11
-20
@@ -8,8 +8,7 @@ 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:
|
||||||
@@ -35,8 +34,7 @@ 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;
|
||||||
@@ -50,11 +48,10 @@ template <class Callback, class T> class CallbackObserver : public Observer<T>
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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:
|
||||||
@@ -63,10 +60,8 @@ template <class T> class Observable
|
|||||||
*
|
*
|
||||||
* 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;
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
+82
-149
@@ -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,8 +142,7 @@ 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;
|
||||||
@@ -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,8 +1240,7 @@ 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;
|
||||||
|
|
||||||
@@ -1296,8 +1248,7 @@ class LipoBatteryLevel : public HasBatteryLevel
|
|||||||
/**
|
/**
|
||||||
* 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,8 +1312,7 @@ 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;
|
||||||
|
|
||||||
@@ -1374,8 +1320,7 @@ class LipoCharger : public HasBatteryLevel
|
|||||||
/**
|
/**
|
||||||
* 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
@@ -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
|
||||||
|
|||||||
+4
-5
@@ -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) {
|
||||||
|
|||||||
@@ -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
@@ -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();
|
|
||||||
}
|
|
||||||
+1
-2
@@ -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;
|
||||||
|
|||||||
+7
-14
@@ -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,8 +11,7 @@ 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 =
|
||||||
@@ -32,10 +30,8 @@ class PowerStatus : public Status
|
|||||||
|
|
||||||
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
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+1
-2
@@ -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
@@ -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;
|
||||||
|
|||||||
+9
-9
@@ -10,20 +10,20 @@
|
|||||||
* 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);
|
||||||
|
|
||||||
|
|||||||
+14
-30
@@ -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;
|
||||||
|
|||||||
+2
-4
@@ -6,8 +6,7 @@
|
|||||||
* 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.
|
||||||
*/
|
*/
|
||||||
@@ -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);
|
||||||
|
|||||||
+4
-8
@@ -9,16 +9,13 @@
|
|||||||
#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;
|
||||||
@@ -30,8 +27,7 @@ class Status
|
|||||||
// 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
@@ -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();
|
||||||
|
|||||||
+1
-2
@@ -39,8 +39,7 @@ 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();
|
||||||
|
|||||||
@@ -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 ||
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
#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);
|
||||||
|
|
||||||
|
|||||||
+17
-35
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,11 @@
|
|||||||
|
|
||||||
#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:
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,11 @@
|
|||||||
|
|
||||||
#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:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -10,18 +10,17 @@
|
|||||||
#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:
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,12 @@
|
|||||||
|
|
||||||
#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();
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -2,14 +2,12 @@
|
|||||||
|
|
||||||
#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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,12 @@
|
|||||||
|
|
||||||
#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()
|
||||||
*/
|
*/
|
||||||
@@ -41,9 +39,9 @@ class NotifiedWorkerThread : public OSThread
|
|||||||
/// 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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -2,15 +2,13 @@
|
|||||||
|
|
||||||
#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:
|
||||||
|
|||||||
+2
-2
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
#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,
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
#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;
|
||||||
|
|||||||
@@ -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 ®isterLocation,
|
uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation ®isterLocation, 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
|
||||||
@@ -14,8 +14,7 @@
|
|||||||
|
|
||||||
#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;
|
||||||
|
|
||||||
@@ -37,10 +36,7 @@ class ScanI2CTwoWire : public ScanI2C
|
|||||||
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 +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
@@ -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
@@ -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(¬ifyDeepSleep);
|
notifyDeepSleepObserver.unobserve(¬ifyDeepSleep);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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");
|
||||||
|
|||||||
+3
-4
@@ -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()
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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,8 +3,7 @@
|
|||||||
#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();
|
||||||
|
|||||||
+35
-76
@@ -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;
|
|
||||||
}
|
|
||||||
+1
-2
@@ -57,8 +57,7 @@ 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;
|
||||||
|
|||||||
+12
-17
@@ -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(×tamp);
|
t = gmtime(×tamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
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
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -22,8 +22,7 @@
|
|||||||
* 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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
(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 )
|
||||||
|
|||||||
+15
-30
@@ -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);
|
||||||
|
|||||||
+42
-88
@@ -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) {
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
+71
-132
@@ -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) {
|
||||||
|
|||||||
+28
-50
@@ -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,11 +29,9 @@ 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
|
||||||
@@ -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,8 +166,7 @@ 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;
|
||||||
@@ -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,8 +201,7 @@ 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);
|
||||||
|
|
||||||
@@ -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.
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
+62
-113
@@ -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,8 +130,7 @@ 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;
|
||||||
@@ -146,8 +139,7 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
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 {
|
||||||
{
|
|
||||||
class TOUCH_CHSC6X : public ITouch
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
TOUCH_CHSC6X(void)
|
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;
|
||||||
@@ -479,8 +461,7 @@ class TOUCH_CHSC6X : public ITouch
|
|||||||
} // 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;
|
||||||
@@ -495,16 +476,15 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
#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)
|
||||||
@@ -724,8 +699,7 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
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,8 +785,7 @@ 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;
|
||||||
@@ -821,8 +793,7 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
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,8 +892,7 @@ 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)
|
||||||
@@ -930,8 +900,7 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
#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;
|
||||||
|
|||||||
@@ -12,8 +12,7 @@
|
|||||||
*
|
*
|
||||||
* 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,8 +44,8 @@ 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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 +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,8 +18,7 @@ struct VirtualKey {
|
|||||||
uint8_t height;
|
uint8_t height;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VirtualKeyboard
|
class VirtualKeyboard {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
VirtualKeyboard();
|
VirtualKeyboard();
|
||||||
~VirtualKeyboard();
|
~VirtualKeyboard();
|
||||||
@@ -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,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user