mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-26 13:42:21 +00:00
Merge upstream/dev into observer-firmware-dev
Preserve observer discovery when forwarding is disabled while honoring hidden-node settings. Keep GPS power ownership paired and incorporate upstream GPS diagnostics.
This commit is contained in:
commit
7bab7e0e73
42 files changed
+582
-125
No files matched your search
@@ -19,6 +19,7 @@ Once you have a working app/project, you need to be able to demonstrate it exist
|
||||
| 0110 - 011F | Ripple | ripple_biz@protonmail.com — https://buymeacoffee.com/ripplebiz |
|
||||
| 0120 | MCO Advanced | most.original.address@gmail.com — https://hdden.ru/MCOa/ |
|
||||
| 0130 - 013F | StreamSensor | william@housedillon.com - https://housedillon.com/blog/lora-e5-with-seeed-fusion |
|
||||
| 0140 | Nelos | support@nelos.app — https://nelos.app |
|
||||
| FF00 - FFFF | -reserved for testing/dev- | |
|
||||
|
||||
(add rows, inside the range 0100 - FEFF for custom apps)
|
||||
@@ -944,7 +944,8 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t
|
||||
|
||||
void MyMesh::onControlDataRecv(mesh::Packet* packet) {
|
||||
uint8_t type = packet->payload[0] & 0xF0; // just test upper 4 bits
|
||||
if (type == CTL_TYPE_NODE_DISCOVER_REQ && packet->payload_len >= 6 && discover_limiter.allow(rtc_clock.getCurrentTime())) {
|
||||
if (type == CTL_TYPE_NODE_DISCOVER_REQ && packet->payload_len >= 6
|
||||
&& !isHiddenNode() && discover_limiter.allow(rtc_clock.getCurrentTime())) {
|
||||
int i = 1;
|
||||
uint8_t filter = packet->payload[i++];
|
||||
uint32_t tag;
|
||||
|
||||
@@ -300,6 +300,9 @@ public:
|
||||
NodePrefs* getNodePrefs() {
|
||||
return &_prefs;
|
||||
}
|
||||
bool isHiddenNode() const {
|
||||
return _prefs.advert_interval == 0 && _prefs.flood_advert_interval == 0;
|
||||
}
|
||||
|
||||
#ifdef DISPLAY_ACTIVITY_DASHBOARD
|
||||
RadioActivityWindow* getActivityWindow() { return &_activity; }
|
||||
|
||||
@@ -1554,6 +1554,8 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
|
||||
addSystemPost(msg);
|
||||
snprintf(reply, MAX_POST_TEXT_LEN, "OK");
|
||||
}
|
||||
} else if (memcmp(command, "set repeat on", 13) == 0) {
|
||||
strcpy(reply, "ERR - repeat now deprecated");
|
||||
} else{
|
||||
_cli.handleCommand(sender_timestamp, command, reply); // common CLI commands
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
#define FIRMWARE_ROLE "sensor"
|
||||
|
||||
#define MAX_SEARCH_RESULTS 8
|
||||
#define MAX_SEARCH_RESULTS 24
|
||||
#define MAX_CONCURRENT_ALERTS 4
|
||||
|
||||
class SensorMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
|
||||
+6
-6
@@ -87,13 +87,14 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
|
||||
}
|
||||
|
||||
if (self_id.isHashMatch(pkt->path, pkt->getPathHashSize()) && allowPacketForward(pkt)) {
|
||||
uint32_t d = getDirectRetransmitDelay(pkt);
|
||||
if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) {
|
||||
return forwardMultipartDirect(pkt);
|
||||
return forwardMultipartDirect(pkt, d);
|
||||
} else if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) {
|
||||
if (!_tables->wasSeen(pkt)) { // don't retransmit!
|
||||
_tables->markSeen(pkt);
|
||||
removeSelfFromPath(pkt);
|
||||
routeDirectRecvAcks(pkt, 0);
|
||||
routeDirectRecvAcks(pkt, d);
|
||||
}
|
||||
return ACTION_RELEASE;
|
||||
}
|
||||
@@ -102,7 +103,6 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
|
||||
_tables->markSeen(pkt);
|
||||
removeSelfFromPath(pkt);
|
||||
|
||||
uint32_t d = getDirectRetransmitDelay(pkt);
|
||||
return ACTION_RETRANSMIT_DELAYED(0, d); // Routed traffic is HIGHEST priority
|
||||
}
|
||||
}
|
||||
@@ -356,7 +356,7 @@ DispatcherAction Mesh::routeRecvPacket(Packet* packet) {
|
||||
return ACTION_RELEASE;
|
||||
}
|
||||
|
||||
DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) {
|
||||
DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt, uint32_t delay_millis) {
|
||||
uint8_t remaining = pkt->payload[0] >> 4; // num of packets in this multipart sequence still to be sent
|
||||
uint8_t type = pkt->payload[0] & 0x0F;
|
||||
|
||||
@@ -370,7 +370,7 @@ DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) {
|
||||
if (!_tables->wasSeen(&tmp)) { // don't retransmit!
|
||||
_tables->markSeen(&tmp);
|
||||
removeSelfFromPath(&tmp);
|
||||
routeDirectRecvAcks(&tmp, ((uint32_t)remaining + 1) * 300); // expect multipart ACKs 300ms apart (x2)
|
||||
routeDirectRecvAcks(&tmp, delay_millis + ((uint32_t)remaining + 1) * 300); // expect multipart ACKs 300ms apart (x2)
|
||||
}
|
||||
}
|
||||
return ACTION_RELEASE;
|
||||
@@ -380,13 +380,13 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) {
|
||||
if (!packet->isMarkedDoNotRetransmit()) {
|
||||
uint8_t extra = getExtraAckTransmitCount();
|
||||
while (extra > 0) {
|
||||
delay_millis += getDirectRetransmitDelay(packet) + 300;
|
||||
auto a1 = createMultiAck(packet->payload, packet->payload_len, extra);
|
||||
if (a1) {
|
||||
a1->path_len = Packet::copyPath(a1->path, packet->path, packet->path_len);
|
||||
a1->header &= ~PH_ROUTE_MASK;
|
||||
a1->header |= ROUTE_TYPE_DIRECT;
|
||||
sendPacket(a1, 0, delay_millis);
|
||||
delay_millis += 300;
|
||||
}
|
||||
extra--;
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ class Mesh : public Dispatcher {
|
||||
void removeSelfFromPath(Packet* packet);
|
||||
void routeDirectRecvAcks(Packet* packet, uint32_t delay_millis);
|
||||
//void routeRecvAcks(Packet* packet, uint32_t delay_millis);
|
||||
DispatcherAction forwardMultipartDirect(Packet* pkt);
|
||||
DispatcherAction forwardMultipartDirect(Packet* pkt, uint32_t delay_millis);
|
||||
|
||||
protected:
|
||||
DispatcherAction onRecvPacket(Packet* pkt) override;
|
||||
|
||||
@@ -80,6 +80,16 @@ public:
|
||||
// Power management interface (boards with power management override these)
|
||||
virtual bool isPwrMgtInitialised() const { return false; }
|
||||
virtual bool isExternalPowered() { return false; }
|
||||
// True while a board can identify an active battery-charging source.
|
||||
virtual bool isChargerActive() { return false; }
|
||||
|
||||
// Optional, source-specific power detection. Boards that can distinguish a USB
|
||||
// supply from a solar charger override these; defaults keep every other board
|
||||
// unaffected (the generic CLI prints "n/a" when a capability is absent).
|
||||
virtual bool hasUsbPowerDetect() const { return false; }
|
||||
virtual bool isUsbPowered() { return false; }
|
||||
virtual bool hasSolarChargerDetect() const { return false; }
|
||||
virtual bool isSolarChargerActive() { return false; }
|
||||
virtual uint16_t getBootVoltage() { return 0; }
|
||||
virtual bool getWakeLpcompSupported() const { return false; }
|
||||
virtual uint32_t getResetReason() const { return 0; }
|
||||
|
||||
+5
-2
@@ -147,6 +147,9 @@ int Utils::encryptThenMAC(const uint8_t* shared_secret, uint8_t* dest, const uin
|
||||
int Utils::MACThenDecrypt(const uint8_t* shared_secret, uint8_t* dest, const uint8_t* src, int src_len) {
|
||||
if (src_len <= CIPHER_MAC_SIZE) return 0; // invalid src bytes
|
||||
|
||||
int enc_len = src_len - CIPHER_MAC_SIZE;
|
||||
if (enc_len % CIPHER_BLOCK_SIZE != 0) return 0; // reject non-block-aligned ciphertext
|
||||
|
||||
uint8_t hmac[CIPHER_MAC_SIZE];
|
||||
#ifdef USE_CC310_HW_CRYPTO
|
||||
{
|
||||
@@ -161,12 +164,12 @@ int Utils::MACThenDecrypt(const uint8_t* shared_secret, uint8_t* dest, const uin
|
||||
{
|
||||
SHA256 sha;
|
||||
sha.resetHMAC(shared_secret, PUB_KEY_SIZE);
|
||||
sha.update(src + CIPHER_MAC_SIZE, src_len - CIPHER_MAC_SIZE);
|
||||
sha.update(src + CIPHER_MAC_SIZE, enc_len);
|
||||
sha.finalizeHMAC(shared_secret, PUB_KEY_SIZE, hmac, CIPHER_MAC_SIZE);
|
||||
}
|
||||
#endif
|
||||
if (memcmp(hmac, src, CIPHER_MAC_SIZE) == 0) {
|
||||
return decrypt(shared_secret, dest, src + CIPHER_MAC_SIZE, src_len - CIPHER_MAC_SIZE);
|
||||
return decrypt(shared_secret, dest, src + CIPHER_MAC_SIZE, enc_len);
|
||||
}
|
||||
return 0; // invalid HMAC
|
||||
}
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ public:
|
||||
|
||||
/**
|
||||
* \brief checks the MAC (in leading bytes of 'src'), then if valid, decrypts remaining bytes in src.
|
||||
* \returns zero if MAC is invalid, otherwise the length of decrypted bytes in 'dest'
|
||||
* \returns zero if MAC is invalid or ciphertext is not block-aligned, otherwise the length of decrypted bytes in 'dest'
|
||||
*/
|
||||
static int MACThenDecrypt(const uint8_t* shared_secret, uint8_t* dest, const uint8_t* src, int src_len);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include "ContactInfo.h"
|
||||
|
||||
#define MAX_SEARCH_RESULTS 8
|
||||
#define MAX_SEARCH_RESULTS 24
|
||||
|
||||
#define MSG_SEND_FAILED 0
|
||||
#define MSG_SEND_SENT_FLOOD 1
|
||||
|
||||
@@ -1360,6 +1360,19 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
|
||||
sprintf(reply, "%s (Build: %s)", _callbacks->getFirmwareVer(), _callbacks->getBuildDate());
|
||||
} else if (memcmp(command, "board", 5) == 0) {
|
||||
sprintf(reply, "%s", _board->getManufacturerName());
|
||||
} else if (strcmp(command, "power") == 0) {
|
||||
const char* usb_state = _board->hasUsbPowerDetect()
|
||||
? (_board->isUsbPowered() ? "yes" : "no")
|
||||
: "n/a";
|
||||
const char* solar_state = _board->hasSolarChargerDetect()
|
||||
? (_board->isSolarChargerActive() ? "yes" : "no")
|
||||
: "n/a";
|
||||
snprintf(reply, 160, "batt:%u mV usb:%s solar_chg:%s ext:%s charger:%s",
|
||||
(unsigned)_board->getBattMilliVolts(),
|
||||
usb_state,
|
||||
solar_state,
|
||||
_board->isExternalPowered() ? "yes" : "no",
|
||||
_board->isChargerActive() ? "yes" : "no");
|
||||
} else if (memcmp(command, "sensor get ", 11) == 0) {
|
||||
const char* key = command + 11;
|
||||
const char* val = _sensors->getSettingByKey(key);
|
||||
@@ -1431,6 +1444,20 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
|
||||
} else {
|
||||
strcpy(reply, "gps provider not found");
|
||||
}
|
||||
} else if (strcmp(command, "gps diag") == 0) {
|
||||
LocationProvider * l = _sensors->getLocationProvider();
|
||||
if (l != NULL) {
|
||||
const char* setting = _sensors->getSettingByKey("gps");
|
||||
bool requested = setting != NULL && strcmp(setting, "1") == 0;
|
||||
int prefix_len = snprintf(reply, 160, "req:%u ", requested ? 1U : 0U);
|
||||
if (prefix_len < 0 || prefix_len >= 160) {
|
||||
reply[159] = 0;
|
||||
} else {
|
||||
l->formatDiagnostics(reply + prefix_len, 160 - prefix_len);
|
||||
}
|
||||
} else {
|
||||
strcpy(reply, "gps provider not found");
|
||||
}
|
||||
} else if (memcmp(command, "gps setloc", 10) == 0) {
|
||||
_prefs->node_lat = _sensors->node_lat;
|
||||
_prefs->node_lon = _sensors->node_lon;
|
||||
|
||||
@@ -208,19 +208,30 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static RTC_NOINIT_ATTR uint32_t _rtc_backup_time;
|
||||
static RTC_NOINIT_ATTR uint32_t _rtc_backup_magic;
|
||||
#define RTC_BACKUP_MAGIC 0xAA55CC33
|
||||
#define RTC_TIME_MIN 1772323200 // 1 Mar 2026
|
||||
|
||||
class ESP32RTCClock : public mesh::RTCClock {
|
||||
public:
|
||||
ESP32RTCClock() { }
|
||||
void begin() {
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
if (reason == ESP_RST_POWERON) {
|
||||
// start with some date/time in the recent past
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 1715770351; // 15 May 2024, 8:50pm
|
||||
if (reason == ESP_RST_DEEPSLEEP) {
|
||||
return; // ESP-IDF preserves system time across deep sleep
|
||||
}
|
||||
// All other resets (power-on, crash, WDT, brownout) lose system time.
|
||||
// Restore from RTC backup if valid, otherwise use hardcoded seed.
|
||||
struct timeval tv;
|
||||
if (_rtc_backup_magic == RTC_BACKUP_MAGIC && _rtc_backup_time > RTC_TIME_MIN) {
|
||||
tv.tv_sec = _rtc_backup_time;
|
||||
} else {
|
||||
tv.tv_sec = RTC_TIME_MIN;
|
||||
}
|
||||
tv.tv_usec = 0;
|
||||
settimeofday(&tv, NULL);
|
||||
}
|
||||
}
|
||||
uint32_t getCurrentTime() override {
|
||||
time_t _now;
|
||||
time(&_now);
|
||||
@@ -231,6 +242,16 @@ public:
|
||||
tv.tv_sec = time;
|
||||
tv.tv_usec = 0;
|
||||
settimeofday(&tv, NULL);
|
||||
_rtc_backup_time = time;
|
||||
_rtc_backup_magic = RTC_BACKUP_MAGIC;
|
||||
}
|
||||
void tick() override {
|
||||
time_t now;
|
||||
time(&now);
|
||||
if (now > RTC_TIME_MIN && (uint32_t)now != _rtc_backup_time) {
|
||||
_rtc_backup_time = (uint32_t)now;
|
||||
_rtc_backup_magic = RTC_BACKUP_MAGIC;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -188,6 +188,7 @@ int RegionMap::getTransportKeysFor(const RegionEntry& src, TransportKey dest[],
|
||||
}
|
||||
|
||||
RegionEntry* RegionMap::findMatch(mesh::Packet* packet, uint8_t mask) {
|
||||
if (packet->transport_codes[0] == 0) return &wildcard;
|
||||
for (int i = 0; i < num_regions; i++) {
|
||||
auto region = ®ions[i];
|
||||
if ((region->flags & mask) == 0) { // does region allow this? (per 'mask' param)
|
||||
|
||||
@@ -129,8 +129,12 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
|
||||
char charpin[20];
|
||||
snprintf(charpin, sizeof(charpin), "%lu", (unsigned long)pin_code);
|
||||
|
||||
// If we want to control BLE LED ourselves, uncomment this:
|
||||
// Bluefruit.autoConnLed(false);
|
||||
#ifdef DISABLE_BLE_LED // use variant platformio.ini build_flags macro: -D DISABLE_BLE_LED
|
||||
Bluefruit.autoConnLed(false); // false removes the automatic control of the LED by the Bluefruit library
|
||||
#else
|
||||
Bluefruit.autoConnLed(true); // true leaves the default automatic control of the LED by the Bluefruit library
|
||||
#endif
|
||||
|
||||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
|
||||
Bluefruit.begin();
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ class CustomLR1121 : public LR1121 {
|
||||
uint32_t _activityAt = 0;
|
||||
bool _headerSeen = false;
|
||||
bool _rx_boosted = false;
|
||||
int8_t _txPower = 0;
|
||||
bool _txPowerSet = false;
|
||||
|
||||
public:
|
||||
CustomLR1121(Module *mod) : LR1121(mod) { }
|
||||
@@ -36,6 +38,33 @@ class CustomLR1121 : public LR1121 {
|
||||
return len;
|
||||
}
|
||||
|
||||
using LR1121::setOutputPower;
|
||||
using LR1121::setFrequency;
|
||||
|
||||
int16_t setOutputPower(int8_t power) override {
|
||||
#ifdef LR1121_FORCE_HP_PA
|
||||
const bool force_hp = true; // boards with only RFO_HP_LF wired: never select the LP PA
|
||||
#else
|
||||
const bool force_hp = false;
|
||||
#endif
|
||||
// clip to the active PA's range (HF PA tops out at 13 dBm) rather than fail and keep the other band's PA
|
||||
int8_t clipped = power;
|
||||
checkOutputPower(power, &clipped, force_hp);
|
||||
int16_t state = LR1121::setOutputPower(clipped, force_hp);
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
_txPower = power;
|
||||
_txPowerSet = true;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
int16_t setFrequency(float freq) override {
|
||||
int16_t state = LR1121::setFrequency(freq);
|
||||
// PA selection depends on the band, and set radio / tempradio don't call setTxPower
|
||||
if (state == RADIOLIB_ERR_NONE && _txPowerSet) state = setOutputPower(_txPower);
|
||||
return state;
|
||||
}
|
||||
|
||||
float getFreqMHz() const { return freqMHz; }
|
||||
|
||||
int16_t setRxBoostedGainMode(bool en) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <helpers/sensors/LocationProvider.h>
|
||||
|
||||
// Board must keep the GPS RTC backup pin powered and can cut GPS power once we return.
|
||||
// Returns true if the command was acked within 3 attempts, otherwise returns false.
|
||||
static inline bool airohaEnterSleep(LocationProvider* nmea) {
|
||||
for (uint8_t attempt = 0; attempt < 3; attempt++) {
|
||||
nmea->drain();
|
||||
nmea->sendSentence("$PAIR650,0");
|
||||
if (nmea->waitFor("$PAIR001,650,0", 50)) { // wait for the command received signal
|
||||
#ifdef GPS_NMEA_DEBUG
|
||||
Serial.printf("Airoha RTC Backup sleep command accepted by GPS after %u attempts\r\n", attempt + 1);
|
||||
#endif
|
||||
nmea->waitFor("$PAIR650,0", 50); // give the GPS 50ms grace to signal it is ready for sleep
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -765,14 +765,18 @@ void EnvironmentSensorManager::initBasicGPS() {
|
||||
MESH_DEBUG_PRINTLN("No GPS wake/reset pin found for this board. Continuing on...");
|
||||
#endif
|
||||
|
||||
// Give GPS a moment to power up and send data
|
||||
delay(1000);
|
||||
|
||||
// We'll consider GPS detected if we see any data on Serial1
|
||||
// Give the GPS time to power up and emit its first NMEA sentence. Cold-start
|
||||
// modules can take several seconds, so poll for up to ~5s instead of checking
|
||||
// once after a fixed 1s delay -- returning as soon as any data appears, so
|
||||
// fast modules add no boot delay (issue #2258).
|
||||
#ifdef ENV_SKIP_GPS_DETECT
|
||||
gps_detected = true;
|
||||
#else
|
||||
gps_detected = (Serial1.available() > 0);
|
||||
gps_detected = false;
|
||||
for (int i = 0; i < 20 && !gps_detected; i++) {
|
||||
delay(250);
|
||||
gps_detected = (Serial1.available() > 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gps_detected) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Mesh.h"
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
class LocationProvider {
|
||||
@@ -17,9 +19,21 @@ public:
|
||||
virtual bool isValid() = 0;
|
||||
virtual long getTimestamp() = 0;
|
||||
virtual void sendSentence(const char * sentence);
|
||||
virtual bool waitFor(const char* prefix, uint32_t timeout_ms) { return false; }
|
||||
virtual void drain() { }
|
||||
virtual void reset() = 0;
|
||||
virtual void begin() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void loop() = 0;
|
||||
virtual bool isEnabled() = 0;
|
||||
|
||||
// Format compact diagnostics in the caller-provided buffer. Providers that
|
||||
// have more information (for example UART counters) can override this.
|
||||
virtual void formatDiagnostics(char* out, size_t out_size) {
|
||||
if (out_size == 0) return;
|
||||
snprintf(out, out_size, "en:%u sat:%ld fix:%u",
|
||||
isEnabled() ? 1U : 0U,
|
||||
satellitesCount(),
|
||||
isValid() ? 1U : 0U);
|
||||
}
|
||||
};
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <MicroNMEA.h>
|
||||
#include <RTClib.h>
|
||||
#include <helpers/RefCountedDigitalPin.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef GPS_EN
|
||||
#ifdef PIN_GPS_EN
|
||||
@@ -49,6 +50,42 @@ class MicroNMEALocationProvider : public LocationProvider {
|
||||
unsigned long next_check = 0;
|
||||
long time_valid = 0;
|
||||
|
||||
#ifdef GPS_L76K
|
||||
void configureL76K() {
|
||||
// Reapply the constellation mode at each provider start instead of
|
||||
// assuming it survives a receiver power cycle.
|
||||
// Mode 7 enables GPS + BeiDou + GLONASS.
|
||||
delay(250);
|
||||
MicroNMEA::sendSentence(*_gps_serial, "$PCAS04,7");
|
||||
delay(250);
|
||||
}
|
||||
#endif
|
||||
// Observation-only diagnostics. These fields do not change GPS power,
|
||||
// timing, reset behaviour, or parser input.
|
||||
uint32_t _uart_bytes = 0;
|
||||
uint32_t _nmea_checksum_ok = 0;
|
||||
uint32_t _nmea_checksum_bad = 0;
|
||||
uint32_t _begin_calls = 0;
|
||||
uint32_t _stop_calls = 0;
|
||||
unsigned long _last_uart_ms = 0;
|
||||
unsigned long _last_nmea_ms = 0;
|
||||
unsigned long _last_fix_ms = 0;
|
||||
bool _uart_seen = false;
|
||||
bool _nmea_seen = false;
|
||||
bool _fix_seen = false;
|
||||
|
||||
static uint32_t ageMs(unsigned long timestamp) {
|
||||
return (uint32_t)(millis() - timestamp);
|
||||
}
|
||||
|
||||
static void formatAge(char* out, size_t out_size, bool seen, unsigned long timestamp) {
|
||||
if (!seen) {
|
||||
snprintf(out, out_size, "never");
|
||||
} else {
|
||||
snprintf(out, out_size, "%lu", (unsigned long)ageMs(timestamp));
|
||||
}
|
||||
}
|
||||
|
||||
public :
|
||||
MicroNMEALocationProvider(Stream& ser, mesh::RTCClock* clock = NULL, int pin_reset = GPS_RESET, int pin_en = GPS_EN,RefCountedDigitalPin* peripher_power=NULL) :
|
||||
nmea(_nmeaBuffer, sizeof(_nmeaBuffer)), _clock(clock), _gps_serial(&ser), _peripher_power(peripher_power), _pin_reset(pin_reset), _pin_en(pin_en) {
|
||||
@@ -74,6 +111,7 @@ public :
|
||||
}
|
||||
|
||||
void begin() override {
|
||||
_begin_calls++;
|
||||
if (_peripher_power) _peripher_power->claim();
|
||||
if (_pin_en != -1) {
|
||||
digitalWrite(_pin_en, GPS_EN_ACTIVE);
|
||||
@@ -89,9 +127,15 @@ public :
|
||||
delay(10);
|
||||
digitalWrite(_pin_reset, !GPS_RESET_ACTIVE);
|
||||
}
|
||||
#ifdef GPS_L76K
|
||||
// EnvironmentSensorManager calls reset() immediately after begin().
|
||||
// Configure only after that reset so the setting is not discarded.
|
||||
configureL76K();
|
||||
#endif
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
_stop_calls++;
|
||||
if (_pin_en != -1) {
|
||||
digitalWrite(_pin_en, !GPS_EN_ACTIVE);
|
||||
}
|
||||
@@ -127,18 +171,65 @@ public :
|
||||
return dt.unixtime();
|
||||
}
|
||||
|
||||
void drain() { while (_gps_serial->available()) _gps_serial->read(); }
|
||||
|
||||
const char* getSentence() const { return nmea.getSentence(); }
|
||||
|
||||
void sendSentence(const char *sentence) override {
|
||||
nmea.sendSentence(*_gps_serial, sentence);
|
||||
}
|
||||
|
||||
bool waitFor(const char* prefix, uint32_t timeout_ms) {
|
||||
size_t plen = strlen(prefix);
|
||||
uint32_t timeout = millis() + timeout_ms;
|
||||
while ((int32_t)(millis() - timeout) < 0) {
|
||||
if (_gps_serial->available()) {
|
||||
char c = _gps_serial->read();
|
||||
#ifdef GPS_NMEA_DEBUG
|
||||
Serial.print(c);
|
||||
#endif
|
||||
if (nmea.process(c) && strncmp(nmea.getSentence(), prefix, plen) == 0) return true;
|
||||
} else {
|
||||
yield();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
|
||||
while (_gps_serial->available()) {
|
||||
char c = _gps_serial->read();
|
||||
_uart_bytes++;
|
||||
_last_uart_ms = millis();
|
||||
_uart_seen = true;
|
||||
#ifdef GPS_NMEA_DEBUG
|
||||
Serial.print(c);
|
||||
#endif
|
||||
nmea.process(c);
|
||||
bool parsed = nmea.process(c);
|
||||
|
||||
// MicroNMEA leaves the completed sentence in its buffer after the
|
||||
// first line terminator. CRLF therefore counts once: CR observes the
|
||||
// sentence, then LF clears the now-empty buffer.
|
||||
if ((c == '\0' || c == '\r' || c == '\n') && nmea.getSentence()[0] != '\0') {
|
||||
if (MicroNMEA::testChecksum(nmea.getSentence())) {
|
||||
_nmea_checksum_ok++;
|
||||
_last_nmea_ms = millis();
|
||||
_nmea_seen = true;
|
||||
|
||||
// Refresh fix age only when a newly parsed GGA/RMC sentence
|
||||
// carries a valid fix. Other valid NMEA sentences must not
|
||||
// make an old position look fresh.
|
||||
const char* message_id = nmea.getMessageID();
|
||||
if (parsed && isValid() &&
|
||||
(strcmp(message_id, "GGA") == 0 || strcmp(message_id, "RMC") == 0)) {
|
||||
_last_fix_ms = millis();
|
||||
_fix_seen = true;
|
||||
}
|
||||
} else {
|
||||
_nmea_checksum_bad++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid()) time_valid = 0;
|
||||
@@ -156,4 +247,31 @@ public :
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void formatDiagnostics(char* out, size_t out_size) override {
|
||||
if (out_size == 0) return;
|
||||
|
||||
char uart_age[11];
|
||||
char nmea_age[11];
|
||||
char fix_age[11];
|
||||
formatAge(uart_age, sizeof(uart_age), _uart_seen, _last_uart_ms);
|
||||
formatAge(nmea_age, sizeof(nmea_age), _nmea_seen, _last_nmea_ms);
|
||||
formatAge(fix_age, sizeof(fix_age), _fix_seen, _last_fix_ms);
|
||||
|
||||
// Compact enough for CommonCLI's 160-byte reply buffer, including the
|
||||
// "req:" prefix added by CommonCLI.
|
||||
snprintf(out, out_size,
|
||||
"en:%u ub:%lu ua:%s ok:%lu bad:%lu na:%s sat:%ld fix:%u fa:%s bc:%lu sc:%lu",
|
||||
isEnabled() ? 1U : 0U,
|
||||
(unsigned long)_uart_bytes,
|
||||
uart_age,
|
||||
(unsigned long)_nmea_checksum_ok,
|
||||
(unsigned long)_nmea_checksum_bad,
|
||||
nmea_age,
|
||||
satellitesCount(),
|
||||
isValid() ? 1U : 0U,
|
||||
fix_age,
|
||||
(unsigned long)_begin_calls,
|
||||
(unsigned long)_stop_calls);
|
||||
}
|
||||
};
|
||||
@@ -9,6 +9,12 @@
|
||||
#define USE_LFXO
|
||||
#define VARIANT_MCK (64000000ul)
|
||||
|
||||
// This board has no external RTC chip -- the ICM-42607-P IMU sits at 0x68,
|
||||
// the same I2C address DS3231 probing checks, so without this the IMU gets
|
||||
// misidentified as a DS3231 and its unrelated registers get read/written
|
||||
// as if they were time/status registers, producing a bogus, unfixable clock.
|
||||
#define DISABLE_DS3231_PROBE
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Number of pins
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ protected:
|
||||
uint8_t btn_prev_state;
|
||||
|
||||
public:
|
||||
TImpulsePlusBoard() : NRF52Board("T-Impulse-Plus OTA") {}
|
||||
TImpulsePlusBoard() : NRF52Board("T-Impulse-Plus_OTA") {}
|
||||
void begin();
|
||||
|
||||
#if defined(P_LORA_TX_LED)
|
||||
|
||||
@@ -98,6 +98,32 @@ lib_deps =
|
||||
${LilyGo_T-Echo-Lite.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:LilyGo_T-Echo-Lite_kiss_modem]
|
||||
extends = LilyGo_T-Echo-Lite
|
||||
build_src_filter = ${LilyGo_T-Echo-Lite.build_src_filter}
|
||||
+<../examples/kiss_modem/>
|
||||
[env:LilyGo_T-Echo-Lite_companion_radio_usb]
|
||||
extends = LilyGo_T-Echo-Lite
|
||||
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
|
||||
board_upload.maximum_size = 712704
|
||||
build_flags =
|
||||
${LilyGo_T-Echo-Lite.build_flags}
|
||||
-I src/helpers/ui
|
||||
-I examples/companion_radio/ui-new
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-D UI_RECENT_LIST_SIZE=9
|
||||
-D UI_SENSORS_PAGE=1
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3300
|
||||
; -D QSPIFLASH=1
|
||||
build_src_filter = ${LilyGo_T-Echo-Lite.build_src_filter}
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
+<../examples/companion_radio/ui-new/*.cpp>
|
||||
lib_deps =
|
||||
${LilyGo_T-Echo-Lite.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:LilyGo_T-Echo-Lite_non_shell_companion_radio_ble]
|
||||
extends = LilyGo_T-Echo-Lite
|
||||
upload_protocol = nrfutil
|
||||
@@ -187,8 +213,3 @@ build_src_filter = ${nrf52_base.build_src_filter}
|
||||
lib_deps =
|
||||
${LilyGo_T-Echo-Lite.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:LilyGo_T-Echo-Lite_kiss_modem]
|
||||
extends = LilyGo_T-Echo-Lite
|
||||
build_src_filter = ${LilyGo_T-Echo-Lite.build_src_filter}
|
||||
+<../examples/kiss_modem/>
|
||||
@@ -70,9 +70,6 @@
|
||||
#define PIN_QSPI_IO2 _PINNUM(1, 9)
|
||||
#define PIN_QSPI_IO3 _PINNUM(0, 26)
|
||||
|
||||
#define EXTERNAL_FLASH_DEVICES ZD25WQ32CEIGR
|
||||
#define EXTERNAL_FLASH_USE_QSPI
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Builtin LEDs
|
||||
|
||||
@@ -95,9 +92,6 @@
|
||||
#define PIN_BUTTON2 _PINNUM(0, 18)
|
||||
#define BUTTON_PIN2 PIN_BUTTON2
|
||||
|
||||
#define EXTERNAL_FLASH_DEVICES MX25R1635F
|
||||
#define EXTERNAL_FLASH_USE_QSPI
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Lora
|
||||
|
||||
@@ -154,8 +148,8 @@ extern const int SCK;
|
||||
// GPS — per LilyGo t_echo_lite_config.h
|
||||
// PIN_GPS_TX/RX named from GPS module's perspective
|
||||
|
||||
#define PIN_GPS_TX _PINNUM(0, 29) // GPS UART TX → MCU RX
|
||||
#define PIN_GPS_RX _PINNUM(1, 10) // GPS UART RX ← MCU TX
|
||||
#define PIN_GPS_RX _PINNUM(0, 29) // GPS UART TX → MCU RX
|
||||
#define PIN_GPS_TX _PINNUM(1, 10) // GPS UART RX ← MCU TX
|
||||
#define GPS_EN _PINNUM(1, 11) // GPS RT9080 power enable
|
||||
#define PIN_GPS_STANDBY _PINNUM(1, 13) // GPS wake-up
|
||||
#define PIN_GPS_PPS _PINNUM(1, 15) // GPS 1PPS
|
||||
#define PIN_GPS_PPS _PINNUM(1, 15) // GPS 1PPS
|
||||
@@ -9,7 +9,7 @@ protected:
|
||||
uint8_t btn_prev_state;
|
||||
|
||||
public:
|
||||
MeshtinyBoard() : NRF52Board("Meshtiny OTA") {}
|
||||
MeshtinyBoard() : NRF52Board("Meshtiny_OTA") {}
|
||||
void begin();
|
||||
|
||||
#if defined(P_LORA_TX_LED)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
#include "target.h"
|
||||
#include <helpers/sensors/MicroNMEALocationProvider.h>
|
||||
#include <helpers/sensors/AirohaSleep.h>
|
||||
|
||||
MeshTrackerX1Board board;
|
||||
|
||||
@@ -27,46 +28,27 @@ mesh::LocalIdentity radio_new_identity() {
|
||||
|
||||
void MeshTrackerX1SensorManager::start_gps() {
|
||||
gps_active = true;
|
||||
// this init sequence comes from seeed examples and deals with all gps pins
|
||||
pinMode(GPS_EN, OUTPUT);
|
||||
digitalWrite(GPS_EN, HIGH);
|
||||
delay(10);
|
||||
pinMode(GPS_VRTC_EN, OUTPUT);
|
||||
digitalWrite(GPS_VRTC_EN, HIGH);
|
||||
delay(10);
|
||||
|
||||
pinMode(GPS_RESET, OUTPUT);
|
||||
digitalWrite(GPS_RESET, HIGH);
|
||||
delay(10);
|
||||
digitalWrite(GPS_RESET, LOW);
|
||||
|
||||
pinMode(GPS_SLEEP_INT, OUTPUT);
|
||||
digitalWrite(GPS_SLEEP_INT, HIGH);
|
||||
pinMode(GPS_RTC_INT, OUTPUT);
|
||||
digitalWrite(GPS_RTC_INT, LOW);
|
||||
}
|
||||
|
||||
void MeshTrackerX1SensorManager::sleep_gps() {
|
||||
gps_active = false;
|
||||
digitalWrite(GPS_VRTC_EN, HIGH); // keep RTC alive for faster fix on wake
|
||||
digitalWrite(GPS_EN, LOW);
|
||||
digitalWrite(GPS_RESET, LOW);
|
||||
digitalWrite(GPS_SLEEP_INT, HIGH);
|
||||
digitalWrite(GPS_RTC_INT, HIGH);
|
||||
delay(5);
|
||||
digitalWrite(GPS_RTC_INT, LOW);
|
||||
}
|
||||
|
||||
void MeshTrackerX1SensorManager::stop_gps() {
|
||||
gps_active = false;
|
||||
digitalWrite(GPS_VRTC_EN, LOW);
|
||||
digitalWrite(GPS_VRTC_EN, HIGH); // keep GPS RTC alive for faster fix on wake
|
||||
digitalWrite(GPS_RTC_INT, LOW); // make sure this is LOW so we can pulse it to wake
|
||||
airohaEnterSleep(_nmea);
|
||||
digitalWrite(GPS_EN, LOW);
|
||||
digitalWrite(GPS_RESET, LOW);
|
||||
digitalWrite(GPS_SLEEP_INT, HIGH);
|
||||
digitalWrite(GPS_RTC_INT, LOW);
|
||||
}
|
||||
|
||||
bool MeshTrackerX1SensorManager::begin() {
|
||||
// init GPS
|
||||
Serial1.begin(GPS_BAUD_RATE);
|
||||
digitalWrite(GPS_RESET, HIGH);
|
||||
delay(10);
|
||||
digitalWrite(GPS_RESET, LOW);
|
||||
|
||||
// init SPA06-003 barometer
|
||||
baro_ok = spa06.begin(SPA06_003_DEFAULT_ADDR, &Wire) || spa06.begin(0x76, &Wire);
|
||||
@@ -122,7 +104,7 @@ const char* MeshTrackerX1SensorManager::getSettingValue(int i) const {
|
||||
bool MeshTrackerX1SensorManager::setSettingValue(const char* name, const char* value) {
|
||||
if (strcmp(name, "gps") == 0) {
|
||||
if (strcmp(value, "0") == 0) {
|
||||
sleep_gps(); // sleep for faster fix !
|
||||
stop_gps();
|
||||
} else {
|
||||
start_gps();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ build_flags = ${nrf52_base.build_flags}
|
||||
-D ENV_INCLUDE_BMP280=1
|
||||
-D ENV_INCLUDE_INA3221=1
|
||||
-D ENV_INCLUDE_INA219=1
|
||||
-D ENV_INCLUDE_INA226=1
|
||||
build_src_filter = ${nrf52_base.build_src_filter}
|
||||
+<helpers/sensors>
|
||||
+<../variants/promicro>
|
||||
@@ -34,6 +35,7 @@ lib_deps= ${nrf52_base.lib_deps}
|
||||
adafruit/Adafruit BME280 Library @ ^2.3.0
|
||||
adafruit/Adafruit BMP280 Library@^2.6.8
|
||||
stevemarple/MicroNMEA @ ^2.0.6
|
||||
robtillaart/INA226@^0.6.6
|
||||
|
||||
[env:ProMicro_repeater]
|
||||
extends = Promicro
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <MeshCore.h>
|
||||
#include <helpers/KeyValueStore.h>
|
||||
|
||||
// from https://github.com/RAKWireless/RAK11300-AT-Command-Firmware/blob/9c48409a43620a828d653501d536473200aa33af/RAK11300-AT-Arduino/batt.cpp#L17-L19
|
||||
#define VBAT_MV_PER_LSB (0.806F) // 3.0V ADC range and 12 - bit ADC resolution = 3300mV / 4096
|
||||
@@ -46,4 +47,6 @@ public:
|
||||
void reboot() override { rp2040.reboot(); }
|
||||
|
||||
bool startOTAUpdate(const char *id, char reply[]) override;
|
||||
|
||||
void attachDynamicPrefs(KeyValueStore* prefs) { } // no-op
|
||||
};
|
||||
@@ -69,10 +69,6 @@ public:
|
||||
#ifdef PIN_3V3_ACC_EN
|
||||
digitalWrite(PIN_3V3_ACC_EN, LOW);
|
||||
#endif
|
||||
#ifdef SENSOR_EN
|
||||
digitalWrite(SENSOR_EN, LOW);
|
||||
#endif
|
||||
|
||||
// set led on and wait for button release before poweroff
|
||||
#ifdef LED_PIN
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
|
||||
@@ -72,7 +72,6 @@ float t1000e_get_temperature(void) {
|
||||
unsigned int ntc_v, vcc_v;
|
||||
|
||||
digitalWrite(PIN_3V3_EN, HIGH);
|
||||
digitalWrite(SENSOR_EN, HIGH);
|
||||
analogReference(AR_INTERNAL_3_0);
|
||||
analogReadResolution(12);
|
||||
delay(10);
|
||||
@@ -80,7 +79,6 @@ float t1000e_get_temperature(void) {
|
||||
vcc_v = (rail_v > NTC_REF_VCC) ? NTC_REF_VCC : rail_v;
|
||||
ntc_v = (1000.0 * AREF_VOLTAGE * analogRead(TEMP_SENSOR)) / 4096;
|
||||
digitalWrite(PIN_3V3_EN, LOW);
|
||||
digitalWrite(SENSOR_EN, LOW);
|
||||
|
||||
return get_heater_temperature(vcc_v, ntc_v);
|
||||
}
|
||||
@@ -90,13 +88,11 @@ uint32_t t1000e_get_light(void) {
|
||||
unsigned int lux_v = 0;
|
||||
|
||||
digitalWrite(PIN_3V3_EN, HIGH);
|
||||
digitalWrite(SENSOR_EN, HIGH);
|
||||
analogReference(AR_INTERNAL_3_0);
|
||||
analogReadResolution(12);
|
||||
delay(10);
|
||||
lux_v = 1000 * analogRead(LUX_SENSOR) * AREF_VOLTAGE / 4096;
|
||||
lux = get_light_lv(lux_v);
|
||||
digitalWrite(SENSOR_EN, LOW);
|
||||
digitalWrite(PIN_3V3_EN, LOW);
|
||||
|
||||
return lux;
|
||||
|
||||
+10
-38
@@ -2,6 +2,7 @@
|
||||
#include "t1000e_sensors.h"
|
||||
#include "target.h"
|
||||
#include <helpers/sensors/MicroNMEALocationProvider.h>
|
||||
#include <helpers/sensors/AirohaSleep.h>
|
||||
|
||||
T1000eBoard board;
|
||||
|
||||
@@ -81,56 +82,28 @@ mesh::LocalIdentity radio_new_identity() {
|
||||
|
||||
void T1000SensorManager::start_gps() {
|
||||
gps_active = true;
|
||||
//_nmea->begin();
|
||||
// this init sequence should be better
|
||||
// comes from seeed examples and deals with all gps pins
|
||||
pinMode(GPS_EN, OUTPUT);
|
||||
digitalWrite(GPS_EN, HIGH);
|
||||
delay(10);
|
||||
pinMode(GPS_VRTC_EN, OUTPUT);
|
||||
digitalWrite(GPS_VRTC_EN, HIGH);
|
||||
delay(10);
|
||||
|
||||
pinMode(GPS_RESET, OUTPUT);
|
||||
digitalWrite(GPS_RESET, HIGH);
|
||||
delay(10);
|
||||
digitalWrite(GPS_RESET, LOW);
|
||||
|
||||
pinMode(GPS_SLEEP_INT, OUTPUT);
|
||||
digitalWrite(GPS_SLEEP_INT, HIGH);
|
||||
pinMode(GPS_RTC_INT, OUTPUT);
|
||||
digitalWrite(GPS_RTC_INT, HIGH);
|
||||
delay(5);
|
||||
digitalWrite(GPS_RTC_INT, LOW);
|
||||
pinMode(GPS_RESETB, INPUT_PULLUP);
|
||||
}
|
||||
|
||||
void T1000SensorManager::sleep_gps() {
|
||||
gps_active = false;
|
||||
digitalWrite(GPS_VRTC_EN, HIGH);
|
||||
digitalWrite(GPS_EN, LOW);
|
||||
digitalWrite(GPS_RESET, HIGH);
|
||||
digitalWrite(GPS_SLEEP_INT, HIGH);
|
||||
digitalWrite(GPS_RTC_INT, LOW);
|
||||
pinMode(GPS_RESETB, OUTPUT);
|
||||
digitalWrite(GPS_RESETB, LOW);
|
||||
//_nmea->stop();
|
||||
}
|
||||
|
||||
void T1000SensorManager::stop_gps() {
|
||||
gps_active = false;
|
||||
digitalWrite(GPS_VRTC_EN, LOW);
|
||||
digitalWrite(GPS_VRTC_EN, HIGH); // keep GPS RTC alive for faster fix on wake
|
||||
digitalWrite(GPS_RTC_INT, LOW); // make sure this is LOW so we can pulse it to wake
|
||||
airohaEnterSleep(_nmea); // send command to put the GPS into RTC backup sleep
|
||||
digitalWrite(GPS_EN, LOW);
|
||||
digitalWrite(GPS_RESET, HIGH);
|
||||
digitalWrite(GPS_SLEEP_INT, HIGH);
|
||||
digitalWrite(GPS_RTC_INT, LOW);
|
||||
pinMode(GPS_RESETB, OUTPUT);
|
||||
digitalWrite(GPS_RESETB, LOW);
|
||||
//_nmea->stop();
|
||||
}
|
||||
|
||||
|
||||
bool T1000SensorManager::begin() {
|
||||
// init GPS
|
||||
Serial1.begin(115200);
|
||||
digitalWrite(GPS_RESET, HIGH);
|
||||
delay(10);
|
||||
digitalWrite(GPS_RESET, LOW);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -156,7 +129,6 @@ void T1000SensorManager::loop() {
|
||||
node_lat = ((double)_nmea->getLatitude())/1000000.;
|
||||
node_lon = ((double)_nmea->getLongitude())/1000000.;
|
||||
node_altitude = ((double)_nmea->getAltitude()) / 1000.0;
|
||||
//Serial.printf("lat %f lon %f\r\n", _lat, _lon);
|
||||
}
|
||||
next_gps_update = millis() + 1000;
|
||||
}
|
||||
@@ -176,7 +148,7 @@ const char* T1000SensorManager::getSettingValue(int i) const {
|
||||
bool T1000SensorManager::setSettingValue(const char* name, const char* value) {
|
||||
if (strcmp(name, "gps") == 0) {
|
||||
if (strcmp(value, "0") == 0) {
|
||||
sleep_gps(); // sleep for faster fix !
|
||||
stop_gps();
|
||||
} else {
|
||||
start_gps();
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ const uint32_t g_ADigitalPinMap[PINS_COUNT + 1] =
|
||||
1, // P0.01
|
||||
2, // P0.02, AIN0 BATTERY_PIN
|
||||
3, // P0.03
|
||||
4, // P0.04, SENSOR_EN
|
||||
4, // P0.04
|
||||
5, // P0.05, EXT_PWR_DETEC
|
||||
6, // P0.06, PIN_BUTTON1
|
||||
7, // P0.07, LORA_BUSY
|
||||
@@ -71,13 +71,12 @@ void initVariant()
|
||||
pinMode(LUX_SENSOR, INPUT);
|
||||
pinMode(EXT_CHRG_DETECT, INPUT);
|
||||
pinMode(EXT_PWR_DETECT, INPUT);
|
||||
pinMode(GPS_RESETB, INPUT);
|
||||
pinMode(GPS_RESETB, INPUT_PULLUP);
|
||||
pinMode(PIN_BUTTON1, INPUT);
|
||||
|
||||
pinMode(PIN_3V3_EN, OUTPUT);
|
||||
pinMode(PIN_3V3_ACC_EN, OUTPUT);
|
||||
pinMode(BUZZER_EN, OUTPUT);
|
||||
pinMode(SENSOR_EN, OUTPUT);
|
||||
pinMode(GPS_EN, OUTPUT);
|
||||
pinMode(GPS_RESET, OUTPUT);
|
||||
pinMode(GPS_VRTC_EN, OUTPUT);
|
||||
@@ -88,11 +87,10 @@ void initVariant()
|
||||
digitalWrite(PIN_3V3_EN, LOW);
|
||||
digitalWrite(PIN_3V3_ACC_EN, LOW);
|
||||
digitalWrite(BUZZER_EN, LOW);
|
||||
digitalWrite(SENSOR_EN, LOW);
|
||||
digitalWrite(GPS_EN, LOW);
|
||||
digitalWrite(GPS_RESET, LOW);
|
||||
digitalWrite(GPS_VRTC_EN, LOW);
|
||||
digitalWrite(GPS_SLEEP_INT, HIGH);
|
||||
digitalWrite(GPS_VRTC_EN, HIGH);
|
||||
digitalWrite(GPS_SLEEP_INT, LOW);
|
||||
digitalWrite(GPS_RTC_INT, LOW);
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
}
|
||||
@@ -120,7 +120,6 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Temp+Lux Sensor
|
||||
|
||||
#define SENSOR_EN (4) // P0.4
|
||||
#define TEMP_SENSOR (31) // P0.31/AIN7
|
||||
#define LUX_SENSOR (29) // P0.29/AIN5
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ void ThinkNodeM6Board::begin() {
|
||||
digitalWrite(P_LORA_TX_LED, LOW);
|
||||
#endif
|
||||
|
||||
pinMode(EXT_PWR_DETECT, INPUT);
|
||||
pinMode(EXT_CHRG_DETECT, INPUT);
|
||||
|
||||
delay(10); // give sx1262 some time to power up
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,35 @@ public:
|
||||
return "Elecrow ThinkNode M6";
|
||||
}
|
||||
|
||||
bool hasUsbPowerDetect() const override { return true; }
|
||||
|
||||
bool isUsbPowered() override {
|
||||
// P0.13 is the dedicated USB / external power-detect line on the M6 PCB
|
||||
// (same pin the Meshtastic M6 variant uses for USB detection).
|
||||
return digitalRead(EXT_PWR_DETECT) == EXT_PWR_DETECT_VALUE;
|
||||
}
|
||||
|
||||
bool hasSolarChargerDetect() const override { return true; }
|
||||
|
||||
bool isSolarChargerActive() override {
|
||||
// P0.15 is the active-low solar-charger status line. It reports the
|
||||
// charger's state, not photovoltaic voltage or current.
|
||||
return digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE;
|
||||
}
|
||||
|
||||
bool isExternalPowered() override {
|
||||
if (isUsbPowered() || isSolarChargerActive()) {
|
||||
return true;
|
||||
}
|
||||
return NRF52Board::isExternalPowered();
|
||||
}
|
||||
|
||||
bool isChargerActive() override {
|
||||
// Preserve a generic charging indication while also exposing both
|
||||
// source-specific states (USB / solar) separately through the CLI.
|
||||
return isSolarChargerActive() || isUsbPowered();
|
||||
}
|
||||
|
||||
void powerOff() override {
|
||||
|
||||
// turn off all leds, sd_power_system_off will not do this for us
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
|
||||
#define AREF_VOLTAGE (3.0)
|
||||
|
||||
// External power and charger status lines.
|
||||
#define EXT_PWR_DETECT (13)
|
||||
#define EXT_PWR_DETECT_VALUE HIGH
|
||||
#define EXT_CHRG_DETECT (15)
|
||||
#define EXT_CHRG_DETECT_VALUE LOW
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Number of pins
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <helpers/ESP32Board.h>
|
||||
|
||||
class WaveshareS3LR1121Board : public ESP32Board {
|
||||
public:
|
||||
const char* getManufacturerName() const override {
|
||||
return "Waveshare ESP32-S3-LR1121";
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
; Waveshare ESP32-S3-LR1121 (HF: 850-930 MHz + 2.4 GHz)
|
||||
; ESP32-S3FH4R2 (4MB flash, 2MB PSRAM), native USB-CDC via the Tiny-Adapter FPC.
|
||||
; No user LED, battery sense or user button (BOOT is GPIO0).
|
||||
[Waveshare_S3_LR1121]
|
||||
extends = esp32_base
|
||||
board = esp32-s3-zero
|
||||
; min_spiffs: 1.9MB OTA app slots; one layout for every role so switching roles keeps the filesystem
|
||||
board_build.partitions = min_spiffs.csv
|
||||
build_flags = ${esp32_base.build_flags}
|
||||
-I variants/waveshare_esp32s3_lr1121
|
||||
-D BOARD_HAS_PSRAM
|
||||
-D P_LORA_DIO_1=38
|
||||
-D P_LORA_NSS=42
|
||||
-D P_LORA_RESET=39
|
||||
-D P_LORA_BUSY=41
|
||||
-D P_LORA_SCLK=40
|
||||
-D P_LORA_MISO=46
|
||||
-D P_LORA_MOSI=45
|
||||
-D RADIO_CLASS=CustomLR1121
|
||||
-D WRAPPER_CLASS=CustomLR1121Wrapper
|
||||
-D LR11X0_DIO3_TCXO_VOLTAGE=1.8
|
||||
-D LR1121_FORCE_HP_PA ; RFO_LP_LF is not fitted (R4 NC)
|
||||
-D RX_BOOSTED_GAIN=true
|
||||
-D LORA_TX_POWER=22
|
||||
build_src_filter = ${esp32_base.build_src_filter}
|
||||
+<../variants/waveshare_esp32s3_lr1121>
|
||||
lib_deps =
|
||||
${esp32_base.lib_deps}
|
||||
|
||||
[env:Waveshare_S3_LR1121_repeater]
|
||||
extends = Waveshare_S3_LR1121
|
||||
build_flags =
|
||||
${Waveshare_S3_LR1121.build_flags}
|
||||
-D ADVERT_NAME='"Waveshare LR1121 Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Waveshare_S3_LR1121.build_src_filter}
|
||||
+<../examples/simple_repeater/*.cpp>
|
||||
lib_deps =
|
||||
${Waveshare_S3_LR1121.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:Waveshare_S3_LR1121_room_server]
|
||||
extends = Waveshare_S3_LR1121
|
||||
build_flags =
|
||||
${Waveshare_S3_LR1121.build_flags}
|
||||
-D ADVERT_NAME='"Waveshare LR1121 Room"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D ROOM_PASSWORD='"hello"'
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Waveshare_S3_LR1121.build_src_filter}
|
||||
+<../examples/simple_room_server>
|
||||
lib_deps =
|
||||
${Waveshare_S3_LR1121.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
|
||||
[env:Waveshare_S3_LR1121_companion_radio_usb]
|
||||
extends = Waveshare_S3_LR1121
|
||||
build_flags =
|
||||
${Waveshare_S3_LR1121.build_flags}
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-D ENABLE_USB_INTERFACE
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Waveshare_S3_LR1121.build_src_filter}
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
lib_deps =
|
||||
${Waveshare_S3_LR1121.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:Waveshare_S3_LR1121_companion_radio_ble]
|
||||
extends = Waveshare_S3_LR1121
|
||||
build_flags =
|
||||
${Waveshare_S3_LR1121.build_flags}
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-D BLE_PIN_CODE=123456
|
||||
; -D BLE_DEBUG_LOGGING=1
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
build_src_filter = ${Waveshare_S3_LR1121.build_src_filter}
|
||||
+<helpers/esp32/*.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
lib_deps =
|
||||
${Waveshare_S3_LR1121.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:Waveshare_S3_LR1121_kiss_modem]
|
||||
extends = Waveshare_S3_LR1121
|
||||
build_src_filter = ${Waveshare_S3_LR1121.build_src_filter}
|
||||
+<../examples/kiss_modem/>
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <Arduino.h>
|
||||
#include "target.h"
|
||||
|
||||
WaveshareS3LR1121Board board;
|
||||
|
||||
static SPIClass spi;
|
||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
|
||||
WRAPPER_CLASS radio_driver(radio, board);
|
||||
|
||||
ESP32RTCClock fallback_clock;
|
||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||
SensorManager sensors;
|
||||
|
||||
#ifndef LORA_CR
|
||||
#define LORA_CR 5
|
||||
#endif
|
||||
|
||||
// DIO5/DIO6 drive the RTC6603SP sub-GHz T/R switch; the 2.4 GHz port (RFIO_HF) is unswitched
|
||||
static const uint32_t rfswitch_dios[Module::RFSWITCH_MAX_PINS] = {
|
||||
RADIOLIB_LR11X0_DIO5,
|
||||
RADIOLIB_LR11X0_DIO6,
|
||||
RADIOLIB_NC
|
||||
};
|
||||
|
||||
static const Module::RfSwitchMode_t rfswitch_table[] = {
|
||||
// mode DIO5 DIO6
|
||||
{ LR11x0::MODE_STBY, {LOW, LOW}},
|
||||
{ LR11x0::MODE_RX, {HIGH, LOW}},
|
||||
{ LR11x0::MODE_TX, {LOW, HIGH}},
|
||||
{ LR11x0::MODE_TX_HP, {LOW, HIGH}},
|
||||
{ LR11x0::MODE_TX_HF, {LOW, LOW}},
|
||||
{ LR11x0::MODE_GNSS, {LOW, LOW}},
|
||||
{ LR11x0::MODE_WIFI, {LOW, LOW}},
|
||||
END_OF_MODE_TABLE,
|
||||
};
|
||||
|
||||
bool radio_init() {
|
||||
fallback_clock.begin();
|
||||
rtc_clock.begin(Wire);
|
||||
|
||||
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
|
||||
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE,
|
||||
LORA_TX_POWER, 16, LR11X0_DIO3_TCXO_VOLTAGE);
|
||||
if (status != RADIOLIB_ERR_NONE) {
|
||||
Serial.print("ERROR: radio init failed: ");
|
||||
Serial.println(status);
|
||||
return false;
|
||||
}
|
||||
|
||||
radio.setCRC(2);
|
||||
radio.explicitHeader();
|
||||
radio.setRfSwitchTable(rfswitch_dios, rfswitch_table);
|
||||
#ifdef RX_BOOSTED_GAIN
|
||||
radio.setRxBoostedGainMode(RX_BOOSTED_GAIN);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mesh::LocalIdentity radio_new_identity() {
|
||||
RadioNoiseListener rng(radio);
|
||||
return mesh::LocalIdentity(&rng);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include <RadioLib.h>
|
||||
#include <helpers/radiolib/RadioLibWrappers.h>
|
||||
#include <helpers/radiolib/CustomLR1121Wrapper.h>
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#include "WaveshareS3LR1121Board.h"
|
||||
|
||||
extern WaveshareS3LR1121Board board;
|
||||
extern WRAPPER_CLASS radio_driver;
|
||||
extern AutoDiscoverRTCClock rtc_clock;
|
||||
extern SensorManager sensors;
|
||||
|
||||
bool radio_init();
|
||||
mesh::LocalIdentity radio_new_identity();
|
||||
@@ -14,7 +14,7 @@ protected:
|
||||
bool isLoRaFemPaGainEnabled() const { return _is_pa_enabled; }
|
||||
|
||||
public:
|
||||
WioTrackerL1Board() : NRF52Board("WioTrackerL1 OTA") {}
|
||||
WioTrackerL1Board() : NRF52Board("WioTrackerL1_OTA") {}
|
||||
void begin();
|
||||
void attachDynamicPrefs(KeyValueStore* prefs);
|
||||
bool handleCommand(const char* command, uint32_t sender_timestamp, char* reply) override;
|
||||
|
||||
@@ -9,7 +9,7 @@ protected:
|
||||
uint8_t btn_prev_state;
|
||||
|
||||
public:
|
||||
WioTrackerL1Board() : NRF52Board("WioTrackerL1 OTA") {}
|
||||
WioTrackerL1Board() : NRF52Board("WioTrackerL1_OTA") {}
|
||||
void begin();
|
||||
|
||||
#if defined(P_LORA_TX_LED)
|
||||
|
||||
Reference in new issue
Block a user