2021-06-27 10:56:28 -07:00
|
|
|
#include "configuration.h"
|
2024-03-25 05:33:57 -06:00
|
|
|
#if !MESHTASTIC_EXCLUDE_GPS
|
|
|
|
|
#include "GPS.h"
|
|
|
|
|
#endif
|
2023-03-08 19:13:46 -08:00
|
|
|
#include "../detect/ScanI2C.h"
|
2021-02-17 13:06:23 +08:00
|
|
|
#include "Channels.h"
|
2020-05-09 19:08:04 -07:00
|
|
|
#include "CryptoEngine.h"
|
2024-03-17 08:18:30 -05:00
|
|
|
#include "Default.h"
|
2021-01-08 11:52:43 +08:00
|
|
|
#include "FSCommon.h"
|
2020-09-25 12:52:08 -07:00
|
|
|
#include "MeshRadio.h"
|
2020-03-18 19:15:51 -07:00
|
|
|
#include "NodeDB.h"
|
2020-06-03 13:15:45 -07:00
|
|
|
#include "PacketHistory.h"
|
2020-02-22 18:02:44 -08:00
|
|
|
#include "PowerFSM.h"
|
2020-10-08 07:28:57 +08:00
|
|
|
#include "RTC.h"
|
2020-06-03 13:15:45 -07:00
|
|
|
#include "Router.h"
|
2025-01-03 10:05:26 +08:00
|
|
|
#include "SPILock.h"
|
2024-08-06 11:59:06 -07:00
|
|
|
#include "SafeFile.h"
|
2023-06-17 09:10:09 -05:00
|
|
|
#include "TypeConversions.h"
|
2020-03-24 13:33:24 -07:00
|
|
|
#include "error.h"
|
2021-02-16 15:41:52 +08:00
|
|
|
#include "main.h"
|
2020-03-18 19:15:51 -07:00
|
|
|
#include "mesh-pb-constants.h"
|
2024-08-10 13:45:41 -05:00
|
|
|
#include "meshUtils.h"
|
2023-05-31 13:18:09 +02:00
|
|
|
#include "modules/NeighborInfoModule.h"
|
2023-01-21 14:34:29 +01:00
|
|
|
#include <ErriezCRC32.h>
|
2024-03-21 09:06:37 -05:00
|
|
|
#include <algorithm>
|
2020-03-18 19:15:51 -07:00
|
|
|
#include <pb_decode.h>
|
|
|
|
|
#include <pb_encode.h>
|
2024-03-21 09:06:37 -05:00
|
|
|
#include <vector>
|
2020-02-03 09:13:19 -08:00
|
|
|
|
2022-07-31 07:11:47 -05:00
|
|
|
#ifdef ARCH_ESP32
|
2024-07-03 15:39:09 -07:00
|
|
|
#if HAS_WIFI
|
2023-12-02 21:47:52 +01:00
|
|
|
#include "mesh/wifi/WiFiAPClient.h"
|
2024-03-25 05:33:57 -06:00
|
|
|
#endif
|
2024-10-25 16:09:49 +02:00
|
|
|
#include "SPILock.h"
|
2024-09-27 21:22:27 -05:00
|
|
|
#include "modules/StoreForwardModule.h"
|
2021-03-27 01:20:07 -07:00
|
|
|
#include <Preferences.h>
|
2024-10-23 21:18:37 -05:00
|
|
|
#include <esp_efuse.h>
|
|
|
|
|
#include <esp_efuse_table.h>
|
2022-01-08 11:54:02 -08:00
|
|
|
#include <nvs_flash.h>
|
2024-10-23 21:18:37 -05:00
|
|
|
#include <soc/efuse_reg.h>
|
|
|
|
|
#include <soc/soc.h>
|
2021-01-09 19:31:16 -08:00
|
|
|
#endif
|
|
|
|
|
|
2024-01-12 02:00:31 -06:00
|
|
|
#ifdef ARCH_PORTDUINO
|
2024-09-27 21:22:27 -05:00
|
|
|
#include "modules/StoreForwardModule.h"
|
2023-12-12 20:27:31 -06:00
|
|
|
#include "platform/portduino/PortduinoGlue.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-07-31 07:11:47 -05:00
|
|
|
#ifdef ARCH_NRF52
|
2022-02-01 18:32:26 -06:00
|
|
|
#include <bluefruit.h>
|
|
|
|
|
#include <utility/bonding.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-03-21 09:06:37 -05:00
|
|
|
NodeDB *nodeDB = nullptr;
|
2020-02-04 09:00:17 -08:00
|
|
|
|
2020-02-07 09:36:15 -08:00
|
|
|
// we have plenty of ram so statically alloc this tempbuf (for now)
|
2024-09-12 22:42:10 +02:00
|
|
|
EXT_RAM_BSS_ATTR meshtastic_DeviceState devicestate;
|
2023-01-21 18:22:19 +01:00
|
|
|
meshtastic_MyNodeInfo &myNodeInfo = devicestate.my_node;
|
2025-01-11 09:02:05 -06:00
|
|
|
meshtastic_NodeDatabase nodeDatabase;
|
2023-01-21 18:22:19 +01:00
|
|
|
meshtastic_LocalConfig config;
|
2024-12-27 10:46:21 +11:00
|
|
|
meshtastic_DeviceUIConfig uiconfig{.screen_brightness = 153, .screen_timeout = 30};
|
2023-01-21 18:22:19 +01:00
|
|
|
meshtastic_LocalModuleConfig moduleConfig;
|
|
|
|
|
meshtastic_ChannelFile channelFile;
|
2024-08-06 11:59:06 -07:00
|
|
|
|
2024-11-23 16:08:18 +01:00
|
|
|
#ifdef USERPREFS_USE_ADMIN_KEY_0
|
|
|
|
|
static unsigned char userprefs_admin_key_0[] = USERPREFS_USE_ADMIN_KEY_0;
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef USERPREFS_USE_ADMIN_KEY_1
|
|
|
|
|
static unsigned char userprefs_admin_key_1[] = USERPREFS_USE_ADMIN_KEY_1;
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef USERPREFS_USE_ADMIN_KEY_2
|
|
|
|
|
static unsigned char userprefs_admin_key_2[] = USERPREFS_USE_ADMIN_KEY_2;
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-12-15 10:20:29 +08:00
|
|
|
#ifdef HELTEC_MESH_NODE_T114
|
|
|
|
|
|
2024-12-14 20:21:19 -06:00
|
|
|
uint32_t read8(uint8_t bits, uint8_t dummy, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
|
2024-12-15 10:20:29 +08:00
|
|
|
{
|
|
|
|
|
uint32_t ret = 0;
|
|
|
|
|
uint8_t SDAPIN = mosi;
|
|
|
|
|
pinMode(SDAPIN, INPUT_PULLUP);
|
|
|
|
|
digitalWrite(dc, HIGH);
|
2024-12-14 20:21:19 -06:00
|
|
|
for (int i = 0; i < dummy; i++) { // any dummy clocks
|
2024-12-15 10:20:29 +08:00
|
|
|
digitalWrite(sck, HIGH);
|
|
|
|
|
delay(1);
|
|
|
|
|
digitalWrite(sck, LOW);
|
|
|
|
|
delay(1);
|
|
|
|
|
}
|
2024-12-14 20:21:19 -06:00
|
|
|
for (int i = 0; i < bits; i++) { // read results
|
2024-12-15 10:20:29 +08:00
|
|
|
ret <<= 1;
|
|
|
|
|
delay(1);
|
2024-12-14 20:21:19 -06:00
|
|
|
if (digitalRead(SDAPIN))
|
|
|
|
|
ret |= 1;
|
|
|
|
|
;
|
2024-12-15 10:20:29 +08:00
|
|
|
digitalWrite(sck, HIGH);
|
|
|
|
|
delay(1);
|
|
|
|
|
digitalWrite(sck, LOW);
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 20:21:19 -06:00
|
|
|
void write9(uint8_t val, uint8_t dc_val, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
|
2024-12-15 10:20:29 +08:00
|
|
|
{
|
|
|
|
|
pinMode(mosi, OUTPUT);
|
|
|
|
|
digitalWrite(dc, dc_val);
|
2024-12-14 20:21:19 -06:00
|
|
|
for (int i = 0; i < 8; i++) { // send command
|
2024-12-15 10:20:29 +08:00
|
|
|
digitalWrite(mosi, (val & 0x80) != 0);
|
|
|
|
|
delay(1);
|
|
|
|
|
digitalWrite(sck, HIGH);
|
|
|
|
|
delay(1);
|
|
|
|
|
digitalWrite(sck, LOW);
|
|
|
|
|
val <<= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 20:21:19 -06:00
|
|
|
uint32_t readwrite8(uint8_t cmd, uint8_t bits, uint8_t dummy, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
|
2024-12-15 10:20:29 +08:00
|
|
|
{
|
|
|
|
|
digitalWrite(cs, LOW);
|
2024-12-14 20:21:19 -06:00
|
|
|
write9(cmd, 0, cs, sck, mosi, dc, rst);
|
|
|
|
|
uint32_t ret = read8(bits, dummy, cs, sck, mosi, dc, rst);
|
2024-12-15 10:20:29 +08:00
|
|
|
digitalWrite(cs, HIGH);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 20:21:19 -06:00
|
|
|
uint32_t get_st7789_id(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
|
2024-12-15 10:20:29 +08:00
|
|
|
{
|
|
|
|
|
pinMode(cs, OUTPUT);
|
|
|
|
|
digitalWrite(cs, HIGH);
|
|
|
|
|
pinMode(cs, OUTPUT);
|
|
|
|
|
pinMode(sck, OUTPUT);
|
|
|
|
|
pinMode(mosi, OUTPUT);
|
|
|
|
|
pinMode(dc, OUTPUT);
|
|
|
|
|
pinMode(rst, OUTPUT);
|
2024-12-14 20:21:19 -06:00
|
|
|
digitalWrite(rst, LOW); // Hardware Reset
|
2024-12-15 10:20:29 +08:00
|
|
|
delay(10);
|
|
|
|
|
digitalWrite(rst, HIGH);
|
|
|
|
|
delay(10);
|
|
|
|
|
|
|
|
|
|
uint32_t ID = 0;
|
2024-12-14 20:21:19 -06:00
|
|
|
ID = readwrite8(0x04, 24, 1, cs, sck, mosi, dc, rst);
|
|
|
|
|
ID = readwrite8(0x04, 24, 1, cs, sck, mosi, dc, rst); // ST7789 needs twice
|
2024-12-15 10:20:29 +08:00
|
|
|
return ID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-01-11 09:02:05 -06:00
|
|
|
bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field)
|
2024-03-21 09:06:37 -05:00
|
|
|
{
|
|
|
|
|
if (ostream) {
|
2024-05-03 15:10:57 +02:00
|
|
|
std::vector<meshtastic_NodeInfoLite> const *vec = (std::vector<meshtastic_NodeInfoLite> *)field->pData;
|
2024-03-21 09:06:37 -05:00
|
|
|
for (auto item : *vec) {
|
|
|
|
|
if (!pb_encode_tag_for_field(ostream, field))
|
|
|
|
|
return false;
|
|
|
|
|
pb_encode_submessage(ostream, meshtastic_NodeInfoLite_fields, &item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (istream) {
|
|
|
|
|
meshtastic_NodeInfoLite node; // this gets good data
|
|
|
|
|
std::vector<meshtastic_NodeInfoLite> *vec = (std::vector<meshtastic_NodeInfoLite> *)field->pData;
|
|
|
|
|
|
|
|
|
|
if (istream->bytes_left && pb_decode(istream, meshtastic_NodeInfoLite_fields, &node))
|
|
|
|
|
vec->push_back(node);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-08 11:52:43 +08:00
|
|
|
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
|
2020-12-17 10:53:29 +08:00
|
|
|
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
|
|
|
|
*/
|
|
|
|
|
uint32_t radioGeneration;
|
|
|
|
|
|
2020-04-14 20:22:27 -07:00
|
|
|
// FIXME - move this somewhere else
|
|
|
|
|
extern void getMacAddr(uint8_t *dmac);
|
2020-02-07 09:36:15 -08:00
|
|
|
|
2020-03-18 19:15:51 -07:00
|
|
|
/**
|
|
|
|
|
*
|
2020-02-04 09:00:17 -08:00
|
|
|
* Normally userids are unique and start with +country code to look like Signal phone numbers.
|
|
|
|
|
* But there are some special ids used when we haven't yet been configured by a user. In that case
|
|
|
|
|
* we use !macaddr (no colons).
|
|
|
|
|
*/
|
2023-01-21 18:22:19 +01:00
|
|
|
meshtastic_User &owner = devicestate.owner;
|
2023-06-17 09:10:09 -05:00
|
|
|
meshtastic_Position localPosition = meshtastic_Position_init_default;
|
2023-06-08 08:07:32 -05:00
|
|
|
meshtastic_CriticalErrorCode error_code =
|
|
|
|
|
meshtastic_CriticalErrorCode_NONE; // For the error code, only show values from this boot (discard value from flash)
|
|
|
|
|
uint32_t error_address = 0;
|
|
|
|
|
|
2020-02-04 09:00:17 -08:00
|
|
|
static uint8_t ourMacAddr[6];
|
2020-02-03 09:13:19 -08:00
|
|
|
|
2024-03-21 13:14:02 -05:00
|
|
|
NodeDB::NodeDB()
|
|
|
|
|
{
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Init NodeDB");
|
2024-03-21 13:14:02 -05:00
|
|
|
loadFromDisk();
|
|
|
|
|
cleanupMeshDB();
|
|
|
|
|
|
|
|
|
|
uint32_t devicestateCRC = crc32Buffer(&devicestate, sizeof(devicestate));
|
2025-01-11 09:02:05 -06:00
|
|
|
uint32_t nodeDatabaseCRC = crc32Buffer(&nodeDatabase, sizeof(nodeDatabase));
|
2024-03-21 13:14:02 -05:00
|
|
|
uint32_t configCRC = crc32Buffer(&config, sizeof(config));
|
|
|
|
|
uint32_t channelFileCRC = crc32Buffer(&channelFile, sizeof(channelFile));
|
|
|
|
|
|
|
|
|
|
int saveWhat = 0;
|
2024-10-23 21:18:37 -05:00
|
|
|
// Get device unique id
|
2025-01-16 16:00:23 -06:00
|
|
|
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
2024-10-23 21:18:37 -05:00
|
|
|
uint32_t unique_id[4];
|
|
|
|
|
// ESP32 factory burns a unique id in efuse for S2+ series and evidently C3+ series
|
|
|
|
|
// This is used for HMACs in the esp-rainmaker AIOT platform and seems to be a good choice for us
|
|
|
|
|
esp_err_t err = esp_efuse_read_field_blob(ESP_EFUSE_OPTIONAL_UNIQUE_ID, unique_id, sizeof(unique_id) * 8);
|
|
|
|
|
if (err == ESP_OK) {
|
|
|
|
|
memcpy(myNodeInfo.device_id.bytes, unique_id, sizeof(unique_id));
|
|
|
|
|
myNodeInfo.device_id.size = 16;
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARN("Failed to read unique id from efuse");
|
|
|
|
|
}
|
|
|
|
|
#elif defined(ARCH_NRF52)
|
|
|
|
|
// Nordic applies a FIPS compliant Random ID to each chip at the factory
|
|
|
|
|
// We concatenate the device address to the Random ID to create a unique ID for now
|
|
|
|
|
// This will likely utilize a crypto module in the future
|
|
|
|
|
uint64_t device_id_start = ((uint64_t)NRF_FICR->DEVICEID[1] << 32) | NRF_FICR->DEVICEID[0];
|
|
|
|
|
uint64_t device_id_end = ((uint64_t)NRF_FICR->DEVICEADDR[1] << 32) | NRF_FICR->DEVICEADDR[0];
|
|
|
|
|
memcpy(myNodeInfo.device_id.bytes, &device_id_start, sizeof(device_id_start));
|
|
|
|
|
memcpy(myNodeInfo.device_id.bytes + sizeof(device_id_start), &device_id_end, sizeof(device_id_end));
|
|
|
|
|
myNodeInfo.device_id.size = 16;
|
2024-11-01 15:46:11 -05:00
|
|
|
// Uncomment below to print the device id
|
2025-01-16 16:00:23 -06:00
|
|
|
|
2024-10-23 21:18:37 -05:00
|
|
|
#else
|
|
|
|
|
// FIXME - implement for other platforms
|
|
|
|
|
#endif
|
2024-11-01 15:46:11 -05:00
|
|
|
|
2025-01-16 16:00:23 -06:00
|
|
|
// if (myNodeInfo.device_id.size == 16) {
|
2024-10-23 21:18:37 -05:00
|
|
|
// std::string deviceIdHex;
|
|
|
|
|
// for (size_t i = 0; i < myNodeInfo.device_id.size; ++i) {
|
|
|
|
|
// char buf[3];
|
|
|
|
|
// snprintf(buf, sizeof(buf), "%02X", myNodeInfo.device_id.bytes[i]);
|
|
|
|
|
// deviceIdHex += buf;
|
|
|
|
|
// }
|
|
|
|
|
// LOG_DEBUG("Device ID (HEX): %s", deviceIdHex.c_str());
|
|
|
|
|
// }
|
2024-03-21 13:14:02 -05:00
|
|
|
|
|
|
|
|
// likewise - we always want the app requirements to come from the running appload
|
|
|
|
|
myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00
|
|
|
|
|
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
|
|
|
|
|
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
|
|
|
|
|
pickNewNodeNum();
|
|
|
|
|
|
|
|
|
|
// Set our board type so we can share it with others
|
|
|
|
|
owner.hw_model = HW_VENDOR;
|
|
|
|
|
// Ensure user (nodeinfo) role is set to whatever we're configured to
|
|
|
|
|
owner.role = config.device.role;
|
2024-09-09 21:22:32 +02:00
|
|
|
// Ensure macaddr is set to our macaddr as it will be copied in our info below
|
|
|
|
|
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
|
2024-03-21 13:14:02 -05:00
|
|
|
|
2024-08-12 23:29:54 -05:00
|
|
|
if (!config.has_security) {
|
|
|
|
|
config.has_security = true;
|
2025-01-11 09:02:05 -06:00
|
|
|
config.security = meshtastic_Config_SecurityConfig_init_default;
|
2024-08-12 23:29:54 -05:00
|
|
|
config.security.serial_enabled = config.device.serial_enabled;
|
|
|
|
|
config.security.is_managed = config.device.is_managed;
|
|
|
|
|
}
|
2024-09-30 17:06:31 -05:00
|
|
|
|
|
|
|
|
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
2025-01-11 09:02:05 -06:00
|
|
|
|
2024-10-29 14:17:14 -05:00
|
|
|
if (!owner.is_licensed) {
|
|
|
|
|
bool keygenSuccess = false;
|
|
|
|
|
if (config.security.private_key.size == 32) {
|
|
|
|
|
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
|
|
|
|
keygenSuccess = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Generate new PKI keys");
|
2024-10-29 14:17:14 -05:00
|
|
|
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
2024-09-30 17:06:31 -05:00
|
|
|
keygenSuccess = true;
|
|
|
|
|
}
|
2024-10-29 14:17:14 -05:00
|
|
|
if (keygenSuccess) {
|
|
|
|
|
config.security.public_key.size = 32;
|
|
|
|
|
config.security.private_key.size = 32;
|
|
|
|
|
owner.public_key.size = 32;
|
|
|
|
|
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
|
|
|
|
}
|
2024-09-30 17:06:31 -05:00
|
|
|
}
|
|
|
|
|
#elif !(MESHTASTIC_EXCLUDE_PKI)
|
2024-08-10 13:45:41 -05:00
|
|
|
// Calculate Curve25519 public and private keys
|
|
|
|
|
if (config.security.private_key.size == 32 && config.security.public_key.size == 32) {
|
|
|
|
|
owner.public_key.size = config.security.public_key.size;
|
|
|
|
|
memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size);
|
2024-08-10 15:45:29 -05:00
|
|
|
crypto->setDHPrivateKey(config.security.private_key.bytes);
|
2024-08-10 13:45:41 -05:00
|
|
|
}
|
|
|
|
|
#endif
|
2025-01-11 09:02:05 -06:00
|
|
|
// Include our owner in the node db under our nodenum
|
|
|
|
|
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
|
2024-08-11 17:22:11 -05:00
|
|
|
info->user = TypeConversions::ConvertToUserLite(owner);
|
2024-03-21 13:14:02 -05:00
|
|
|
info->has_user = true;
|
|
|
|
|
|
2025-01-12 19:03:21 -06:00
|
|
|
// If node database has not been saved for the first time, save it now
|
|
|
|
|
#ifdef FSCom
|
|
|
|
|
if (!FSCom.exists(nodeDatabaseFileName)) {
|
|
|
|
|
saveNodeDatabaseToDisk();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-03-21 13:14:02 -05:00
|
|
|
#ifdef ARCH_ESP32
|
|
|
|
|
Preferences preferences;
|
|
|
|
|
preferences.begin("meshtastic", false);
|
|
|
|
|
myNodeInfo.reboot_count = preferences.getUInt("rebootCounter", 0);
|
|
|
|
|
preferences.end();
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("Number of Device Reboots: %d", myNodeInfo.reboot_count);
|
2024-03-21 13:14:02 -05:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
resetRadioConfig(); // If bogus settings got saved, then fix them
|
2024-10-14 06:11:43 +02:00
|
|
|
// nodeDB->LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d", config.lora.region, myNodeInfo.my_node_num, numMeshNodes);
|
2024-03-21 13:14:02 -05:00
|
|
|
|
2025-01-11 09:01:49 -06:00
|
|
|
// Uncomment below to always enable UDP broadcasts
|
|
|
|
|
// config.network.enabled_protocols = meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST;
|
|
|
|
|
|
2024-10-17 13:33:52 -05:00
|
|
|
// If we are setup to broadcast on the default channel, ensure that the telemetry intervals are coerced to the minimum value
|
|
|
|
|
// of 30 minutes or more
|
|
|
|
|
if (channels.isDefaultChannel(channels.getPrimaryIndex())) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_DEBUG("Coerce telemetry to min of 30 minutes on defaults");
|
2024-10-17 13:33:52 -05:00
|
|
|
moduleConfig.telemetry.device_update_interval = Default::getConfiguredOrMinimumValue(
|
|
|
|
|
moduleConfig.telemetry.device_update_interval, min_default_telemetry_interval_secs);
|
|
|
|
|
moduleConfig.telemetry.environment_update_interval = Default::getConfiguredOrMinimumValue(
|
|
|
|
|
moduleConfig.telemetry.environment_update_interval, min_default_telemetry_interval_secs);
|
|
|
|
|
moduleConfig.telemetry.air_quality_interval = Default::getConfiguredOrMinimumValue(
|
|
|
|
|
moduleConfig.telemetry.air_quality_interval, min_default_telemetry_interval_secs);
|
|
|
|
|
moduleConfig.telemetry.power_update_interval = Default::getConfiguredOrMinimumValue(
|
|
|
|
|
moduleConfig.telemetry.power_update_interval, min_default_telemetry_interval_secs);
|
|
|
|
|
moduleConfig.telemetry.health_update_interval = Default::getConfiguredOrMinimumValue(
|
|
|
|
|
moduleConfig.telemetry.health_update_interval, min_default_telemetry_interval_secs);
|
|
|
|
|
}
|
2024-11-11 07:00:56 -06:00
|
|
|
// Ensure that the neighbor info update interval is coerced to the minimum
|
|
|
|
|
moduleConfig.neighbor_info.update_interval =
|
|
|
|
|
Default::getConfiguredOrMinimumValue(moduleConfig.neighbor_info.update_interval, min_neighbor_info_broadcast_secs);
|
2024-10-17 13:33:52 -05:00
|
|
|
|
2024-03-21 13:14:02 -05:00
|
|
|
if (devicestateCRC != crc32Buffer(&devicestate, sizeof(devicestate)))
|
|
|
|
|
saveWhat |= SEGMENT_DEVICESTATE;
|
2025-01-11 09:02:05 -06:00
|
|
|
if (nodeDatabaseCRC != crc32Buffer(&nodeDatabase, sizeof(nodeDatabase)))
|
|
|
|
|
saveWhat |= SEGMENT_NODEDATABASE;
|
2024-03-21 13:14:02 -05:00
|
|
|
if (configCRC != crc32Buffer(&config, sizeof(config)))
|
|
|
|
|
saveWhat |= SEGMENT_CONFIG;
|
|
|
|
|
if (channelFileCRC != crc32Buffer(&channelFile, sizeof(channelFile)))
|
|
|
|
|
saveWhat |= SEGMENT_CHANNELS;
|
|
|
|
|
|
|
|
|
|
if (config.position.gps_enabled) {
|
|
|
|
|
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
|
|
|
|
|
config.position.gps_enabled = 0;
|
|
|
|
|
}
|
2024-11-14 13:56:22 +01:00
|
|
|
#ifdef USERPREFS_FIXED_GPS
|
|
|
|
|
if (myNodeInfo.reboot_count == 1) { // Check if First boot ever or after Factory Reset.
|
|
|
|
|
meshtastic_Position fixedGPS = meshtastic_Position_init_default;
|
|
|
|
|
#ifdef USERPREFS_FIXED_GPS_LAT
|
|
|
|
|
fixedGPS.latitude_i = (int32_t)(USERPREFS_FIXED_GPS_LAT * 1e7);
|
|
|
|
|
fixedGPS.has_latitude_i = true;
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef USERPREFS_FIXED_GPS_LON
|
|
|
|
|
fixedGPS.longitude_i = (int32_t)(USERPREFS_FIXED_GPS_LON * 1e7);
|
|
|
|
|
fixedGPS.has_longitude_i = true;
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef USERPREFS_FIXED_GPS_ALT
|
|
|
|
|
fixedGPS.altitude = USERPREFS_FIXED_GPS_ALT;
|
|
|
|
|
fixedGPS.has_altitude = true;
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(USERPREFS_FIXED_GPS_LAT) && defined(USERPREFS_FIXED_GPS_LON)
|
|
|
|
|
fixedGPS.location_source = meshtastic_Position_LocSource_LOC_MANUAL;
|
|
|
|
|
config.has_position = true;
|
|
|
|
|
info->has_position = true;
|
|
|
|
|
info->position = TypeConversions::ConvertToPositionLite(fixedGPS);
|
|
|
|
|
nodeDB->setLocalPosition(fixedGPS);
|
|
|
|
|
config.position.fixed_position = true;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2024-03-21 13:14:02 -05:00
|
|
|
saveToDisk(saveWhat);
|
|
|
|
|
}
|
2020-02-04 09:00:17 -08:00
|
|
|
|
2021-03-05 10:19:27 +08:00
|
|
|
/**
|
2021-03-11 13:02:00 +08:00
|
|
|
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
|
|
|
|
|
* the local node. If from is zero this function returns our node number instead
|
2021-03-05 10:19:27 +08:00
|
|
|
*/
|
2023-01-21 18:22:19 +01:00
|
|
|
NodeNum getFrom(const meshtastic_MeshPacket *p)
|
2021-03-11 13:02:00 +08:00
|
|
|
{
|
2024-03-21 09:06:37 -05:00
|
|
|
return (p->from == 0) ? nodeDB->getNodeNum() : p->from;
|
2021-03-05 10:19:27 +08:00
|
|
|
}
|
|
|
|
|
|
2024-10-04 13:28:51 +02:00
|
|
|
// Returns true if the packet originated from the local node
|
|
|
|
|
bool isFromUs(const meshtastic_MeshPacket *p)
|
|
|
|
|
{
|
|
|
|
|
return p->from == 0 || p->from == nodeDB->getNodeNum();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns true if the packet is destined to us
|
|
|
|
|
bool isToUs(const meshtastic_MeshPacket *p)
|
|
|
|
|
{
|
|
|
|
|
return p->to == nodeDB->getNodeNum();
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-19 12:48:00 -05:00
|
|
|
bool isBroadcast(uint32_t dest)
|
|
|
|
|
{
|
|
|
|
|
return dest == NODENUM_BROADCAST || dest == NODENUM_BROADCAST_NO_LORA;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-09 12:51:41 +02:00
|
|
|
bool NodeDB::resetRadioConfig(bool factory_reset)
|
2020-02-07 09:36:15 -08:00
|
|
|
{
|
2020-09-19 11:19:42 -07:00
|
|
|
bool didFactoryReset = false;
|
|
|
|
|
|
2020-12-17 10:53:29 +08:00
|
|
|
radioGeneration++;
|
|
|
|
|
|
2022-09-09 12:51:41 +02:00
|
|
|
if (factory_reset) {
|
2023-01-21 14:34:29 +01:00
|
|
|
didFactoryReset = factoryReset();
|
2021-03-23 11:54:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (channelFile.channels_count != MAX_NUM_CHANNELS) {
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Set default channel and radio preferences!");
|
2020-03-30 16:05:28 -07:00
|
|
|
|
2021-02-16 15:41:52 +08:00
|
|
|
channels.initDefaults();
|
2020-03-30 16:05:28 -07:00
|
|
|
}
|
2020-04-17 14:30:42 -07:00
|
|
|
|
2021-02-16 15:41:52 +08:00
|
|
|
channels.onConfigChanged();
|
2020-05-09 19:08:04 -07:00
|
|
|
|
2020-10-29 13:26:36 +08:00
|
|
|
// Update the global myRegion
|
|
|
|
|
initRegion();
|
2020-11-28 12:10:19 +08:00
|
|
|
|
2022-08-25 17:07:20 +00:00
|
|
|
if (didFactoryReset) {
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Reboot due to factory reset");
|
2024-06-25 11:26:02 -05:00
|
|
|
screen->startAlert("Rebooting...");
|
2022-08-25 17:07:20 +00:00
|
|
|
rebootAtMsec = millis() + (5 * 1000);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-06 21:11:17 +01:00
|
|
|
#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3)) && HAS_TFT
|
2024-12-31 13:00:37 +11:00
|
|
|
// as long as PhoneAPI shares BT and TFT app switch BT off
|
|
|
|
|
config.bluetooth.enabled = false;
|
|
|
|
|
if (moduleConfig.external_notification.nag_timeout == 60)
|
|
|
|
|
moduleConfig.external_notification.nag_timeout = 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-09-19 11:19:42 -07:00
|
|
|
return didFactoryReset;
|
2020-03-30 16:05:28 -07:00
|
|
|
}
|
2020-02-04 09:00:17 -08:00
|
|
|
|
2024-08-09 08:38:29 -05:00
|
|
|
bool NodeDB::factoryReset(bool eraseBleBonds)
|
2022-07-02 09:09:41 -05:00
|
|
|
{
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_INFO("Perform factory reset!");
|
2022-07-02 09:09:41 -05:00
|
|
|
// first, remove the "/prefs" (this removes most prefs)
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->lock();
|
|
|
|
|
rmDir("/prefs"); // this uses spilock internally...
|
|
|
|
|
|
2024-07-26 03:16:21 +02:00
|
|
|
#ifdef FSCom
|
2024-03-03 10:33:30 -06:00
|
|
|
if (FSCom.exists("/static/rangetest.csv") && !FSCom.remove("/static/rangetest.csv")) {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_ERROR("Could not remove rangetest.csv file");
|
2024-03-03 10:33:30 -06:00
|
|
|
}
|
2024-07-26 03:16:21 +02:00
|
|
|
#endif
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->unlock();
|
2022-07-02 09:09:41 -05:00
|
|
|
// second, install default state (this will deal with the duplicate mac address issue)
|
2025-01-11 09:02:05 -06:00
|
|
|
installDefaultNodeDatabase();
|
2022-07-02 09:09:41 -05:00
|
|
|
installDefaultDeviceState();
|
2024-09-11 08:42:26 -05:00
|
|
|
installDefaultConfig(!eraseBleBonds); // Also preserve the private key if we're not erasing BLE bonds
|
2022-10-20 07:51:52 -05:00
|
|
|
installDefaultModuleConfig();
|
|
|
|
|
installDefaultChannels();
|
2022-10-04 14:32:07 +02:00
|
|
|
// third, write everything to disk
|
2022-07-02 09:09:41 -05:00
|
|
|
saveToDisk();
|
2024-08-09 08:38:29 -05:00
|
|
|
if (eraseBleBonds) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_INFO("Erase BLE bonds");
|
2022-07-31 07:11:47 -05:00
|
|
|
#ifdef ARCH_ESP32
|
2024-08-09 08:38:29 -05:00
|
|
|
// This will erase what's in NVS including ssl keys, persistent variables and ble pairing
|
|
|
|
|
nvs_flash_erase();
|
2022-07-02 09:09:41 -05:00
|
|
|
#endif
|
2022-07-31 07:11:47 -05:00
|
|
|
#ifdef ARCH_NRF52
|
2024-08-09 08:38:29 -05:00
|
|
|
Bluefruit.begin();
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_INFO("Clear bluetooth bonds!");
|
2024-08-09 08:38:29 -05:00
|
|
|
bond_print_list(BLE_GAP_ROLE_PERIPH);
|
|
|
|
|
bond_print_list(BLE_GAP_ROLE_CENTRAL);
|
|
|
|
|
Bluefruit.Periph.clearBonds();
|
|
|
|
|
Bluefruit.Central.clearBonds();
|
2022-07-02 09:09:41 -05:00
|
|
|
#endif
|
2024-08-09 08:38:29 -05:00
|
|
|
}
|
2022-07-02 09:09:41 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-11 09:02:05 -06:00
|
|
|
void NodeDB::installDefaultNodeDatabase()
|
|
|
|
|
{
|
|
|
|
|
LOG_DEBUG("Install default NodeDatabase");
|
|
|
|
|
nodeDatabase.version = DEVICESTATE_CUR_VER;
|
|
|
|
|
nodeDatabase.nodes = std::vector<meshtastic_NodeInfoLite>(MAX_NUM_NODES);
|
|
|
|
|
numMeshNodes = 0;
|
|
|
|
|
meshNodes = &nodeDatabase.nodes;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-11 08:42:26 -05:00
|
|
|
void NodeDB::installDefaultConfig(bool preserveKey = false)
|
2022-05-01 20:30:19 -05:00
|
|
|
{
|
2024-09-11 08:42:26 -05:00
|
|
|
uint8_t private_key_temp[32];
|
|
|
|
|
bool shouldPreserveKey = preserveKey && config.has_security && config.security.private_key.size > 0;
|
|
|
|
|
if (shouldPreserveKey) {
|
|
|
|
|
memcpy(private_key_temp, config.security.private_key.bytes, config.security.private_key.size);
|
|
|
|
|
}
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Install default LocalConfig");
|
2023-01-21 18:22:19 +01:00
|
|
|
memset(&config, 0, sizeof(meshtastic_LocalConfig));
|
2022-06-15 16:52:04 +02:00
|
|
|
config.version = DEVICESTATE_CUR_VER;
|
2022-05-22 13:54:24 +02:00
|
|
|
config.has_device = true;
|
|
|
|
|
config.has_display = true;
|
|
|
|
|
config.has_lora = true;
|
|
|
|
|
config.has_position = true;
|
|
|
|
|
config.has_power = true;
|
2022-09-09 12:51:41 +02:00
|
|
|
config.has_network = true;
|
2024-03-25 05:33:57 -06:00
|
|
|
config.has_bluetooth = (HAS_BLUETOOTH ? true : false);
|
2024-08-10 13:45:41 -05:00
|
|
|
config.has_security = true;
|
2023-01-29 14:22:51 -06:00
|
|
|
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL;
|
2023-07-22 09:26:54 -05:00
|
|
|
|
2023-07-28 10:39:40 -05:00
|
|
|
config.lora.sx126x_rx_boosted_gain = true;
|
2023-01-21 14:34:29 +01:00
|
|
|
config.lora.tx_enabled =
|
|
|
|
|
true; // FIXME: maybe false in the future, and setting region to enable it. (unset region forces it off)
|
|
|
|
|
config.lora.override_duty_cycle = false;
|
2024-09-07 18:21:59 -05:00
|
|
|
config.lora.config_ok_to_mqtt = false;
|
2024-10-02 06:14:24 -05:00
|
|
|
#ifdef USERPREFS_CONFIG_LORA_REGION
|
|
|
|
|
config.lora.region = USERPREFS_CONFIG_LORA_REGION;
|
2024-07-28 14:12:30 -05:00
|
|
|
#else
|
2023-01-21 18:22:19 +01:00
|
|
|
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
|
2024-07-28 14:12:30 -05:00
|
|
|
#endif
|
2024-10-02 06:14:24 -05:00
|
|
|
#ifdef USERPREFS_LORACONFIG_MODEM_PRESET
|
|
|
|
|
config.lora.modem_preset = USERPREFS_LORACONFIG_MODEM_PRESET;
|
2024-07-28 14:12:30 -05:00
|
|
|
#else
|
2023-01-21 18:22:19 +01:00
|
|
|
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
|
2024-07-28 14:12:30 -05:00
|
|
|
#endif
|
2022-08-27 19:03:26 +10:00
|
|
|
config.lora.hop_limit = HOP_RELIABLE;
|
2024-10-02 06:14:24 -05:00
|
|
|
#ifdef USERPREFS_CONFIG_LORA_IGNORE_MQTT
|
|
|
|
|
config.lora.ignore_mqtt = USERPREFS_CONFIG_LORA_IGNORE_MQTT;
|
2024-07-28 14:12:30 -05:00
|
|
|
#else
|
2024-01-20 21:22:09 +01:00
|
|
|
config.lora.ignore_mqtt = false;
|
2024-07-28 14:12:30 -05:00
|
|
|
#endif
|
2024-11-17 19:36:41 +01:00
|
|
|
// Initialize admin_key_count to zero
|
|
|
|
|
byte numAdminKeys = 0;
|
|
|
|
|
|
2024-11-23 16:08:18 +01:00
|
|
|
#ifdef USERPREFS_USE_ADMIN_KEY_0
|
2024-11-17 19:36:41 +01:00
|
|
|
// Check if USERPREFS_ADMIN_KEY_0 is non-empty
|
2024-11-23 16:08:18 +01:00
|
|
|
if (sizeof(userprefs_admin_key_0) > 0) {
|
|
|
|
|
memcpy(config.security.admin_key[0].bytes, userprefs_admin_key_0, 32);
|
|
|
|
|
config.security.admin_key[0].size = 32;
|
2024-11-17 19:36:41 +01:00
|
|
|
numAdminKeys++;
|
|
|
|
|
}
|
2024-11-23 16:08:18 +01:00
|
|
|
#endif
|
2024-11-17 19:36:41 +01:00
|
|
|
|
2024-11-23 16:08:18 +01:00
|
|
|
#ifdef USERPREFS_USE_ADMIN_KEY_1
|
2024-11-17 19:36:41 +01:00
|
|
|
// Check if USERPREFS_ADMIN_KEY_1 is non-empty
|
2024-11-23 16:08:18 +01:00
|
|
|
if (sizeof(userprefs_admin_key_1) > 0) {
|
|
|
|
|
memcpy(config.security.admin_key[1].bytes, userprefs_admin_key_1, 32);
|
|
|
|
|
config.security.admin_key[1].size = 32;
|
2024-11-17 19:36:41 +01:00
|
|
|
numAdminKeys++;
|
|
|
|
|
}
|
2024-11-23 16:08:18 +01:00
|
|
|
#endif
|
2024-11-17 19:36:41 +01:00
|
|
|
|
2024-11-23 16:08:18 +01:00
|
|
|
#ifdef USERPREFS_USE_ADMIN_KEY_2
|
2024-11-17 19:36:41 +01:00
|
|
|
// Check if USERPREFS_ADMIN_KEY_2 is non-empty
|
2024-11-23 16:08:18 +01:00
|
|
|
if (sizeof(userprefs_admin_key_2) > 0) {
|
|
|
|
|
memcpy(config.security.admin_key[2].bytes, userprefs_admin_key_2, 32);
|
|
|
|
|
config.security.admin_key[2].size = 32;
|
2024-11-17 19:36:41 +01:00
|
|
|
numAdminKeys++;
|
|
|
|
|
}
|
2024-08-10 13:45:41 -05:00
|
|
|
#endif
|
2024-11-23 16:08:18 +01:00
|
|
|
config.security.admin_key_count = numAdminKeys;
|
|
|
|
|
|
2024-09-11 08:42:26 -05:00
|
|
|
if (shouldPreserveKey) {
|
|
|
|
|
config.security.private_key.size = 32;
|
|
|
|
|
memcpy(config.security.private_key.bytes, private_key_temp, config.security.private_key.size);
|
|
|
|
|
printBytes("Restored key", config.security.private_key.bytes, config.security.private_key.size);
|
|
|
|
|
} else {
|
|
|
|
|
config.security.private_key.size = 0;
|
|
|
|
|
}
|
2024-08-10 13:45:41 -05:00
|
|
|
config.security.public_key.size = 0;
|
2023-09-23 23:45:35 -05:00
|
|
|
#ifdef PIN_GPS_EN
|
|
|
|
|
config.position.gps_en_gpio = PIN_GPS_EN;
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef GPS_POWER_TOGGLE
|
|
|
|
|
config.device.disable_triple_click = false;
|
|
|
|
|
#else
|
|
|
|
|
config.device.disable_triple_click = true;
|
|
|
|
|
#endif
|
2024-10-06 02:32:07 -05:00
|
|
|
#if defined(USERPREFS_CONFIG_GPS_MODE)
|
|
|
|
|
config.position.gps_mode = USERPREFS_CONFIG_GPS_MODE;
|
2024-12-03 20:29:33 +08:00
|
|
|
#elif !HAS_GPS || GPS_DEFAULT_NOT_PRESENT
|
2024-02-01 15:24:39 -06:00
|
|
|
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT;
|
|
|
|
|
#elif !defined(GPS_RX_PIN)
|
|
|
|
|
if (config.position.rx_gpio == 0)
|
|
|
|
|
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT;
|
|
|
|
|
else
|
|
|
|
|
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
|
|
|
|
|
#else
|
|
|
|
|
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
|
|
|
|
|
#endif
|
2022-09-11 23:36:47 +10:00
|
|
|
config.position.position_broadcast_smart_enabled = true;
|
2023-03-27 14:09:22 -05:00
|
|
|
config.position.broadcast_smart_minimum_distance = 100;
|
|
|
|
|
config.position.broadcast_smart_minimum_interval_secs = 30;
|
2023-02-11 17:17:11 +01:00
|
|
|
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER)
|
2024-03-15 07:12:03 -05:00
|
|
|
config.device.node_info_broadcast_secs = default_node_info_broadcast_secs;
|
2024-08-10 13:45:41 -05:00
|
|
|
config.security.serial_enabled = true;
|
|
|
|
|
config.security.admin_channel_enabled = false;
|
2022-05-21 22:38:33 +02:00
|
|
|
resetRadioConfig();
|
2024-05-07 07:57:30 -05:00
|
|
|
strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32);
|
2022-08-15 21:06:55 -05:00
|
|
|
// FIXME: Default to bluetooth capability of platform as default
|
|
|
|
|
config.bluetooth.enabled = true;
|
|
|
|
|
config.bluetooth.fixed_pin = defaultBLEPin;
|
2024-10-04 14:47:14 +02:00
|
|
|
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
|
|
|
|
defined(HX8357_CS) || defined(USE_ST7789)
|
2022-08-25 11:25:05 -05:00
|
|
|
bool hasScreen = true;
|
2024-12-15 10:20:29 +08:00
|
|
|
#ifdef HELTEC_MESH_NODE_T114
|
2024-12-14 20:21:19 -06:00
|
|
|
uint32_t st7789_id = get_st7789_id(ST7789_NSS, ST7789_SCK, ST7789_SDA, ST7789_RS, ST7789_RESET);
|
|
|
|
|
if (st7789_id == 0xFFFFFF) {
|
2024-12-15 10:20:29 +08:00
|
|
|
hasScreen = false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2024-01-12 02:00:31 -06:00
|
|
|
#elif ARCH_PORTDUINO
|
2023-12-12 20:27:31 -06:00
|
|
|
bool hasScreen = false;
|
|
|
|
|
if (settingsMap[displayPanel])
|
|
|
|
|
hasScreen = true;
|
|
|
|
|
else
|
|
|
|
|
hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C;
|
InkHUD UI for E-Ink (#6034)
* Decouple ButtonThread from sleep.cpp
Reorganize sleep observables. Don't call ButtonThread methods inside doLightSleep. Instead, handle in class with new lightsleep Observables.
* InkHUD: initial commit (WIP)
Publicly discloses the current work in progress. Not ready for use.
* feat: battery icon
* chore: implement meshtastic/firmware #5454
Clean up some inline functions
* feat: menu & settings for "jump to applet"
* Remove the beforeRender pattern
It hugely complicates things. If we can achieve acceptable performance without it, so much the better.
* Remove previous Map Applet
Needs re-implementation to work without the beforeRender pattern
* refactor: reimplement map applet
Doesn't require own position
Doesn't require the beforeRender pattern to precalculate; now all-at-once in render
Lays groundwork for fixed-size map with custom background image
* feat: autoshow
Allow user to select which applets (if any) should be automatically brought to foreground when they have new data to display
* refactor: tidy-up applet constructors
misc. jobs including:
- consistent naming
- move initializer-list-only constructors to header
- give derived applets unique identifiers for MeshModule and OSThread logging
* hotfix: autoshow always uses FAST update
In future, it *will* often use FAST, but this will be controlled by a WindowManager component which has not yet been written.
Hotfixed, in case anybody is attempting to use this development version on their deployed devices.
* refactor: bringToForeground no longer requests FAST update
In situations where an applet has moved to foreground because of user input, requestUpdate can be manually called, to upgrade to FAST refresh.
More permanent solution for #23e1dfc
* refactor: extract string storage from ThreadedMessageApplet
Separates the code responsible for storing the limited message history, which was previously part of the ThreadedMessageApplet.
We're now also using this code to store the "most recent message". Previously, this was stored in the `InkHUD::settings` struct, which was much less space-efficient.
We're also now storing the latest DM, laying the foundation for an applet to display only DMs, which will complement the threaded message applet.
* fix: text wrapping
Attempts to fix a disparity between `Applet::printWrapped` and `Applet::getWrappedTextHeight`, which would occasionally cause a ThreadedMessageApplet message to render "too short", overlapping other text.
* fix: purge old constructor
This one slipped through the last commit..
* feat: DM Applet
Useful in combination with the ThreadedMessageApplets, which don't show DMs
* fix: applets shouldn't handle events while deactivated
Only one or two applets were actually doing this, but I'm making a habit of having all applets return early from their event handling methods (as good practice), even if those methods are disabled elsewhere (e.g. not observing observable, return false from wantPacket)
* refactor: allow requesting update without requesting autoshow
Some applets may want to redraw, if they are displayed, but not feel the information is worth being brought to foreground for. Example: ActiveNodesApplet, when purging old nodes from list.
* feat: custom "Recently Active" duration
Allows users to tailor how long nodes will appear in the "Recents" applets, to suit the activity level of their mesh.
* refactor: rename some applets
* fix: autoshow
* fix: getWrappedTextHeight
Remove the "simulate" option from printWrapped; too hard to keep inline with genuine printing (because of AdafruitGFX Fonts' xAdvance, mabye?). Instead of simulating, we printWrapped as normal, and discard pixel output by setting crop. Both methods are similarly inefficient, apparently.
* fix: text wrapping in ThreadedMessageApplet
Wrong arguments were passed to Applet::printWrapped
* feat: notifications for text messages
Only shown if current applet does not already display the same info. Autoshow takes priority over notifications, if both would be used to display the same info.
* feat: optimize FAST vs FULL updates
New UpdateMediator class counts the number of each update type, and suggets which one to use, if the code doesn't already have an explicit prefence. Also performs "maintenance refreshes" unprovoked if display is not given an opportunity to before a FULL refresh through organic use.
* chore: update todo list
* fix: rare lock-up of buttons
* refactor: backlight
Replaces the initial proof-of-concept frontlight code for T-Echo
Presses less than 5 seconds momentarily illuminate the display
Presses longer than 5 seconds latch the light, requiring another tap to disable
If user has previously removed the T-Echo's capacitive touch button (some DIY projects), the light is controlled by the on-screen menu. This fallback is used by all T-Echo devices, until a press of the capacitive touch button is detected.
* feat: change tile with aux button
Applied to VM-E290.
Working as is, but a refactor of WindowManager::render is expected shortly, which will also tidy code from this push.
* fix: specify out-of-the-box tile assignments
Prevents placeholder applet showing on initial boot, for devices which use a mult-tile layout by default (VM-E290)
* fix: verify settings version when loading
* fix: wrong settings version
* refactor: remove unimplemented argument from requestUpdate
Specified whether or not to update "async", however the implementation was slightly broken, Applet::requestUpdate is only handled next time WindowManager::runOnce is called. This didn't allow code to actually await an update, which was misleading.
* refactor: renaming
Applet::render becomes Applet::onRender.
Tile::displayedApplet becomes Tile::assignedApplet.
New onRender method name allows us to move some of the pre and post render code from WindowManager into new Applet::render method, which will call onRender for us.
* refactor: rendering
Bit of a tidy-up. No intended change in behavior.
* fix: optimize refresh times
Shorter wait between retrying update if display was previously busy.
Set anticipated update durations closer to observed values. No signifacant performance increase, but does decrease the amount of polling required.
* feat: blocking update for E-Ink
Option to wait for display update to complete before proceeding. Important when shutting down the device.
* refactor: allow system applets to lock rendering
Temporarily prevents other applets from rendering.
* feat: boot and shutdown screens
* feat: BluetoothStatus
Adds a meshtastic::Status object which exposes the state of the Bluetooth connection. Intends to allow decoupling of UI code.
* feat: Bluetooth pairing screen
* fix: InkHUD defaults not honored
* fix: random Bluetooth pin for NicheGraphics UIs
* chore: button interrupts tested
* fix: emoji reactions show as blank messages
* fix: autoshow and notification triggered by outgoing message
* feat: save InkHUD data before reboot
Implemented with a new Observable. Previously, config and a few recent messages were saved on shutdown. These were lost if the device rebooted, for example when firmware settings were changed by a client. Now, the InkHUD config and recent messages saved on reboot, the same as during an intentional shutdown.
* feat: imperial distances
Controlled by the config.display.units setting
* fix: hide features which are not yet implemented
* refactor: faster rendering
Previously, only tiles which requested update were re-rendered. Affected tiles had their region blanked before render, pixel by pixel. Benchmarking revealed that it is significantly faster to memset the framebuffer and redraw all tiles.
* refactor: tile ownership
Tiles and Applets now maintain a reciprocal link, which is enforced by asserts. Less confusing than the old situation, where an applet and a tile may disagree on their relationship. Empty tiles are now identified by a nullptr *Applet, instead of by having the placeholderApplet assigned.
* fix: notifications and battery when menu open
Do render notifications in front of menu; don't render battery icon in front of menu.
* fix: simpler defaults
Don't expose new users to multiplexed applets straight away: make them enable the feature for themselves.
* fix: Inputs::TwoButton interrupts, when only one button in use
* fix: ensure display update is complete when ESP32 enters light sleep
Many panels power down automatically, but some require active intervention from us. If light sleep (ESP32) occurs during a display update, these panels could potentially remain powered on, applying voltage the pixels for an extended period of time, and potentially damaging the display.
* fix: honor per-variant user tile limit
Set as the default value for InkHUD::settings.userTiles.maxCount in nicheGraphics.h
* feat: initial InkHUD support for Wireless Paper v1.1 and VM-E213
* refactor: Heard and Recents Applets
Tidier code, significant speed boost. Possibly no noticable change in responsiveness, but rendering now spends much less time blocking execution, which is important for correction functioning of the other firmware components.
* refactor: use a common pio base config
Easier to make any future PlatformIO config changes
* feat: tips
Show information that we think the user might find helpful. Some info shown first boot only. Other info shown when / if relevant.
* fix: text wrapping for '\n'
Previously, the newline was honored, but the adojining word was not printed.
* Decouple ButtonThread from sleep.cpp
Reorganize sleep observables. Don't call ButtonThread methods inside doLightSleep. Instead, handle in class with new lightsleep Observables.
* feat: BluetoothStatus
Adds a meshtastic::Status object which exposes the state of the Bluetooth connection. Intends to allow decoupling of UI code.
* feat: observable for reboot
* refactor: Heltec VM-E290 installDefaultConfig
* fix: random Bluetooth pin for NicheGraphics UIs
2025-02-12 11:01:17 +13:00
|
|
|
#elif MESHTASTIC_INCLUDE_NICHE_GRAPHICS // See "src/graphics/niche"
|
|
|
|
|
bool hasScreen = true; // Use random pin for Bluetooth pairing
|
2022-08-25 11:25:05 -05:00
|
|
|
#else
|
2023-03-08 19:13:46 -08:00
|
|
|
bool hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C;
|
2022-08-25 11:25:05 -05:00
|
|
|
#endif
|
2024-11-14 13:56:22 +01:00
|
|
|
#ifdef USERPREFS_FIXED_BLUETOOTH
|
|
|
|
|
config.bluetooth.fixed_pin = USERPREFS_FIXED_BLUETOOTH;
|
|
|
|
|
config.bluetooth.mode = meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN;
|
|
|
|
|
#else
|
2023-01-21 18:39:58 +01:00
|
|
|
config.bluetooth.mode = hasScreen ? meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN
|
|
|
|
|
: meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN;
|
2024-11-14 13:56:22 +01:00
|
|
|
#endif
|
2022-05-21 22:38:33 +02:00
|
|
|
// for backward compat, default position flags are ALT+MSL
|
2023-01-21 14:34:29 +01:00
|
|
|
config.position.position_flags =
|
2023-09-04 06:46:27 -05:00
|
|
|
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL |
|
|
|
|
|
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING |
|
2023-11-25 19:34:30 -06:00
|
|
|
meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW);
|
2023-01-21 14:34:29 +01:00
|
|
|
|
2024-08-01 18:53:38 +02:00
|
|
|
#ifdef DISPLAY_FLIP_SCREEN
|
2024-05-31 10:56:04 -05:00
|
|
|
config.display.flip_screen = true;
|
|
|
|
|
#endif
|
2024-09-06 13:55:56 +02:00
|
|
|
#ifdef RAK4630
|
|
|
|
|
config.display.wake_on_tap_or_motion = true;
|
|
|
|
|
#endif
|
2024-11-23 08:54:06 +08:00
|
|
|
#if defined(T_WATCH_S3) || defined(SENSECAP_INDICATOR)
|
2023-07-22 09:26:54 -05:00
|
|
|
config.display.screen_on_secs = 30;
|
|
|
|
|
config.display.wake_on_tap_or_motion = true;
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-09-10 12:35:36 -05:00
|
|
|
initConfigIntervals();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
void NodeDB::initConfigIntervals()
|
2022-09-10 12:35:36 -05:00
|
|
|
{
|
2022-09-12 07:55:17 -05:00
|
|
|
config.position.gps_update_interval = default_gps_update_interval;
|
2022-09-10 12:35:36 -05:00
|
|
|
config.position.position_broadcast_secs = default_broadcast_interval_secs;
|
|
|
|
|
|
|
|
|
|
config.power.ls_secs = default_ls_secs;
|
|
|
|
|
config.power.min_wake_secs = default_min_wake_secs;
|
|
|
|
|
config.power.sds_secs = default_sds_secs;
|
|
|
|
|
config.power.wait_bluetooth_secs = default_wait_bluetooth_secs;
|
2023-01-21 14:34:29 +01:00
|
|
|
|
2022-09-10 12:35:36 -05:00
|
|
|
config.display.screen_on_secs = default_screen_on_secs;
|
2024-04-14 16:11:27 +02:00
|
|
|
|
2024-12-13 03:42:41 +11:00
|
|
|
#if defined(T_WATCH_S3) || defined(T_DECK) || defined(MESH_TAB) || defined(RAK14014)
|
2024-04-14 16:11:27 +02:00
|
|
|
config.power.is_power_saving = true;
|
|
|
|
|
config.display.screen_on_secs = 30;
|
|
|
|
|
config.power.wait_bluetooth_secs = 30;
|
|
|
|
|
#endif
|
2022-05-01 20:30:19 -05:00
|
|
|
}
|
|
|
|
|
|
2022-05-02 22:00:24 +10:00
|
|
|
void NodeDB::installDefaultModuleConfig()
|
|
|
|
|
{
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Install default ModuleConfig");
|
2023-01-21 18:22:19 +01:00
|
|
|
memset(&moduleConfig, 0, sizeof(meshtastic_ModuleConfig));
|
2023-01-21 14:34:29 +01:00
|
|
|
|
2022-06-15 16:52:04 +02:00
|
|
|
moduleConfig.version = DEVICESTATE_CUR_VER;
|
2022-05-22 13:54:24 +02:00
|
|
|
moduleConfig.has_mqtt = true;
|
|
|
|
|
moduleConfig.has_range_test = true;
|
|
|
|
|
moduleConfig.has_serial = true;
|
|
|
|
|
moduleConfig.has_store_forward = true;
|
|
|
|
|
moduleConfig.has_telemetry = true;
|
2022-07-02 09:09:41 -05:00
|
|
|
moduleConfig.has_external_notification = true;
|
2024-08-28 07:24:41 -05:00
|
|
|
#if defined(PIN_BUZZER)
|
|
|
|
|
moduleConfig.external_notification.enabled = true;
|
|
|
|
|
moduleConfig.external_notification.output_buzzer = PIN_BUZZER;
|
2024-08-29 11:42:27 -05:00
|
|
|
moduleConfig.external_notification.use_pwm = true;
|
2024-08-28 07:24:41 -05:00
|
|
|
moduleConfig.external_notification.alert_message_buzzer = true;
|
|
|
|
|
moduleConfig.external_notification.nag_timeout = 60;
|
|
|
|
|
#endif
|
2023-06-27 12:21:06 -05:00
|
|
|
#if defined(RAK4630) || defined(RAK11310)
|
|
|
|
|
// Default to RAK led pin 2 (blue)
|
|
|
|
|
moduleConfig.external_notification.enabled = true;
|
|
|
|
|
moduleConfig.external_notification.output = PIN_LED2;
|
|
|
|
|
moduleConfig.external_notification.active = true;
|
|
|
|
|
moduleConfig.external_notification.alert_message = true;
|
|
|
|
|
moduleConfig.external_notification.output_ms = 1000;
|
|
|
|
|
moduleConfig.external_notification.nag_timeout = 60;
|
2023-07-22 09:26:54 -05:00
|
|
|
#endif
|
2024-08-29 11:42:27 -05:00
|
|
|
|
2023-12-12 08:36:37 -06:00
|
|
|
#ifdef HAS_I2S
|
|
|
|
|
// Don't worry about the other settings for T-Watch, we'll also use the DRV2056 behavior for notifications
|
2023-07-22 09:26:54 -05:00
|
|
|
moduleConfig.external_notification.enabled = true;
|
2023-12-12 08:36:37 -06:00
|
|
|
moduleConfig.external_notification.use_i2s_as_buzzer = true;
|
|
|
|
|
moduleConfig.external_notification.alert_message_buzzer = true;
|
|
|
|
|
moduleConfig.external_notification.nag_timeout = 60;
|
2023-11-30 20:49:00 -06:00
|
|
|
#endif
|
|
|
|
|
#ifdef NANO_G2_ULTRA
|
|
|
|
|
moduleConfig.external_notification.enabled = true;
|
|
|
|
|
moduleConfig.external_notification.alert_message = true;
|
|
|
|
|
moduleConfig.external_notification.output_ms = 100;
|
2023-12-01 07:17:38 -06:00
|
|
|
moduleConfig.external_notification.active = true;
|
2023-06-27 12:21:06 -05:00
|
|
|
#endif
|
2024-08-07 10:16:56 +12:00
|
|
|
#ifdef BUTTON_SECONDARY_CANNEDMESSAGES
|
|
|
|
|
// Use a board's second built-in button as input source for canned messages
|
|
|
|
|
moduleConfig.canned_message.enabled = true;
|
|
|
|
|
moduleConfig.canned_message.inputbroker_pin_press = BUTTON_PIN_SECONDARY;
|
|
|
|
|
strcpy(moduleConfig.canned_message.allow_input_source, "scanAndSelect");
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-07-02 09:09:41 -05:00
|
|
|
moduleConfig.has_canned_message = true;
|
2022-09-10 12:35:36 -05:00
|
|
|
|
2022-12-16 20:26:22 +01:00
|
|
|
strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(moduleConfig.mqtt.address));
|
|
|
|
|
strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(moduleConfig.mqtt.username));
|
|
|
|
|
strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(moduleConfig.mqtt.password));
|
2023-10-15 20:56:47 -03:00
|
|
|
strncpy(moduleConfig.mqtt.root, default_mqtt_root, sizeof(moduleConfig.mqtt.root));
|
|
|
|
|
moduleConfig.mqtt.encryption_enabled = true;
|
2022-11-20 12:29:10 -06:00
|
|
|
|
2023-07-27 12:51:31 -05:00
|
|
|
moduleConfig.has_neighbor_info = true;
|
|
|
|
|
moduleConfig.neighbor_info.enabled = false;
|
|
|
|
|
|
2023-08-14 19:00:51 -05:00
|
|
|
moduleConfig.has_detection_sensor = true;
|
|
|
|
|
moduleConfig.detection_sensor.enabled = false;
|
2024-09-25 07:01:15 -05:00
|
|
|
moduleConfig.detection_sensor.detection_trigger_type = meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH;
|
2023-08-14 19:00:51 -05:00
|
|
|
moduleConfig.detection_sensor.minimum_broadcast_secs = 45;
|
|
|
|
|
|
2023-09-07 12:24:47 -05:00
|
|
|
moduleConfig.has_ambient_lighting = true;
|
|
|
|
|
moduleConfig.ambient_lighting.current = 10;
|
|
|
|
|
// Default to a color based on our node number
|
|
|
|
|
moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
|
|
|
|
|
moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
|
|
|
|
|
moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
|
|
|
|
|
|
2022-09-10 12:35:36 -05:00
|
|
|
initModuleConfigIntervals();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-28 14:32:57 -06:00
|
|
|
void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
|
|
|
|
|
{
|
|
|
|
|
if (role == meshtastic_Config_DeviceConfig_Role_ROUTER) {
|
|
|
|
|
initConfigIntervals();
|
|
|
|
|
initModuleConfigIntervals();
|
2024-10-29 05:44:32 -05:00
|
|
|
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY;
|
2023-01-28 14:32:57 -06:00
|
|
|
} else if (role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
|
|
|
|
config.display.screen_on_secs = 1;
|
2024-10-29 05:44:32 -05:00
|
|
|
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY;
|
2023-02-24 11:25:50 -06:00
|
|
|
} else if (role == meshtastic_Config_DeviceConfig_Role_SENSOR) {
|
|
|
|
|
moduleConfig.telemetry.environment_measurement_enabled = true;
|
2023-02-24 12:49:10 -06:00
|
|
|
moduleConfig.telemetry.environment_update_interval = 300;
|
2023-12-13 17:43:20 -06:00
|
|
|
} else if (role == meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND) {
|
|
|
|
|
config.position.position_broadcast_smart_enabled = false;
|
|
|
|
|
config.position.position_broadcast_secs = 300; // Every 5 minutes
|
2023-10-14 06:49:38 -05:00
|
|
|
} else if (role == meshtastic_Config_DeviceConfig_Role_TAK) {
|
|
|
|
|
config.device.node_info_broadcast_secs = ONE_DAY;
|
|
|
|
|
config.position.position_broadcast_smart_enabled = false;
|
|
|
|
|
config.position.position_broadcast_secs = ONE_DAY;
|
|
|
|
|
// Remove Altitude MSL from flags since CoTs use HAE (height above ellipsoid)
|
|
|
|
|
config.position.position_flags =
|
|
|
|
|
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_SPEED |
|
|
|
|
|
meshtastic_Config_PositionConfig_PositionFlags_HEADING | meshtastic_Config_PositionConfig_PositionFlags_DOP);
|
2024-02-16 20:04:21 -06:00
|
|
|
moduleConfig.telemetry.device_update_interval = ONE_DAY;
|
|
|
|
|
} else if (role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) {
|
|
|
|
|
config.device.node_info_broadcast_secs = ONE_DAY;
|
|
|
|
|
config.position.position_broadcast_smart_enabled = true;
|
|
|
|
|
config.position.position_broadcast_secs = 3 * 60; // Every 3 minutes
|
|
|
|
|
config.position.broadcast_smart_minimum_distance = 20;
|
|
|
|
|
config.position.broadcast_smart_minimum_interval_secs = 15;
|
|
|
|
|
// Remove Altitude MSL from flags since CoTs use HAE (height above ellipsoid)
|
|
|
|
|
config.position.position_flags =
|
|
|
|
|
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_SPEED |
|
|
|
|
|
meshtastic_Config_PositionConfig_PositionFlags_HEADING | meshtastic_Config_PositionConfig_PositionFlags_DOP);
|
2023-10-14 06:49:38 -05:00
|
|
|
moduleConfig.telemetry.device_update_interval = ONE_DAY;
|
2023-12-06 14:04:09 -06:00
|
|
|
} else if (role == meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) {
|
|
|
|
|
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY;
|
|
|
|
|
config.device.node_info_broadcast_secs = UINT32_MAX;
|
|
|
|
|
config.position.position_broadcast_smart_enabled = false;
|
|
|
|
|
config.position.position_broadcast_secs = UINT32_MAX;
|
|
|
|
|
moduleConfig.neighbor_info.update_interval = UINT32_MAX;
|
|
|
|
|
moduleConfig.telemetry.device_update_interval = UINT32_MAX;
|
|
|
|
|
moduleConfig.telemetry.environment_update_interval = UINT32_MAX;
|
|
|
|
|
moduleConfig.telemetry.air_quality_interval = UINT32_MAX;
|
2024-10-07 19:50:44 -05:00
|
|
|
moduleConfig.telemetry.health_update_interval = UINT32_MAX;
|
2023-01-28 14:32:57 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
void NodeDB::initModuleConfigIntervals()
|
2022-09-10 12:35:36 -05:00
|
|
|
{
|
2024-07-16 09:37:50 -05:00
|
|
|
// Zero out telemetry intervals so that they coalesce to defaults in Default.h
|
|
|
|
|
moduleConfig.telemetry.device_update_interval = 0;
|
|
|
|
|
moduleConfig.telemetry.environment_update_interval = 0;
|
|
|
|
|
moduleConfig.telemetry.air_quality_interval = 0;
|
|
|
|
|
moduleConfig.telemetry.power_update_interval = 0;
|
2024-10-07 19:50:44 -05:00
|
|
|
moduleConfig.telemetry.health_update_interval = 0;
|
2024-07-16 09:37:50 -05:00
|
|
|
moduleConfig.neighbor_info.update_interval = 0;
|
|
|
|
|
moduleConfig.paxcounter.paxcounter_update_interval = 0;
|
2022-05-02 22:00:24 +10:00
|
|
|
}
|
|
|
|
|
|
2021-03-11 13:02:00 +08:00
|
|
|
void NodeDB::installDefaultChannels()
|
|
|
|
|
{
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Install default ChannelFile");
|
2023-01-21 18:22:19 +01:00
|
|
|
memset(&channelFile, 0, sizeof(meshtastic_ChannelFile));
|
2022-06-15 16:52:04 +02:00
|
|
|
channelFile.version = DEVICESTATE_CUR_VER;
|
2021-03-11 13:02:00 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-28 16:47:26 -05:00
|
|
|
void NodeDB::resetNodes()
|
|
|
|
|
{
|
2024-10-14 21:32:25 -03:00
|
|
|
if (!config.position.fixed_position)
|
|
|
|
|
clearLocalPosition();
|
2024-03-21 09:06:37 -05:00
|
|
|
numMeshNodes = 1;
|
2025-01-11 09:02:05 -06:00
|
|
|
std::fill(nodeDatabase.nodes.begin() + 1, nodeDatabase.nodes.end(), meshtastic_NodeInfoLite());
|
2024-10-10 19:24:37 -05:00
|
|
|
devicestate.has_rx_text_message = false;
|
|
|
|
|
devicestate.has_rx_waypoint = false;
|
2025-01-11 09:02:05 -06:00
|
|
|
saveNodeDatabaseToDisk();
|
2022-09-28 16:47:26 -05:00
|
|
|
saveDeviceStateToDisk();
|
2023-08-02 20:55:59 +02:00
|
|
|
if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
|
|
|
|
|
neighborInfoModule->resetNeighbors();
|
2022-09-28 16:47:26 -05:00
|
|
|
}
|
|
|
|
|
|
2024-05-03 22:49:22 +02:00
|
|
|
void NodeDB::removeNodeByNum(NodeNum nodeNum)
|
2023-11-16 06:57:22 -06:00
|
|
|
{
|
|
|
|
|
int newPos = 0, removed = 0;
|
2024-03-21 09:06:37 -05:00
|
|
|
for (int i = 0; i < numMeshNodes; i++) {
|
|
|
|
|
if (meshNodes->at(i).num != nodeNum)
|
|
|
|
|
meshNodes->at(newPos++) = meshNodes->at(i);
|
2023-11-16 06:57:22 -06:00
|
|
|
else
|
|
|
|
|
removed++;
|
|
|
|
|
}
|
2024-03-21 09:06:37 -05:00
|
|
|
numMeshNodes -= removed;
|
2025-01-11 09:02:05 -06:00
|
|
|
std::fill(nodeDatabase.nodes.begin() + numMeshNodes, nodeDatabase.nodes.begin() + numMeshNodes + 1,
|
2024-03-21 09:06:37 -05:00
|
|
|
meshtastic_NodeInfoLite());
|
2024-11-06 07:03:25 -06:00
|
|
|
LOG_DEBUG("NodeDB::removeNodeByNum purged %d entries. Save changes", removed);
|
2025-01-11 09:02:05 -06:00
|
|
|
saveNodeDatabaseToDisk();
|
2023-11-16 06:57:22 -06:00
|
|
|
}
|
|
|
|
|
|
2024-03-21 14:43:10 -05:00
|
|
|
void NodeDB::clearLocalPosition()
|
|
|
|
|
{
|
|
|
|
|
meshtastic_NodeInfoLite *node = getMeshNode(nodeDB->getNodeNum());
|
|
|
|
|
node->position.latitude_i = 0;
|
|
|
|
|
node->position.longitude_i = 0;
|
|
|
|
|
node->position.altitude = 0;
|
|
|
|
|
node->position.time = 0;
|
|
|
|
|
setLocalPosition(meshtastic_Position_init_default);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-18 13:16:37 +02:00
|
|
|
void NodeDB::cleanupMeshDB()
|
|
|
|
|
{
|
|
|
|
|
int newPos = 0, removed = 0;
|
2024-03-21 09:06:37 -05:00
|
|
|
for (int i = 0; i < numMeshNodes; i++) {
|
2024-08-13 06:31:05 -05:00
|
|
|
if (meshNodes->at(i).has_user) {
|
|
|
|
|
if (meshNodes->at(i).user.public_key.size > 0) {
|
2024-08-23 06:26:19 -05:00
|
|
|
if (memfll(meshNodes->at(i).user.public_key.bytes, 0, meshNodes->at(i).user.public_key.size)) {
|
|
|
|
|
meshNodes->at(i).user.public_key.size = 0;
|
2024-08-13 06:31:05 -05:00
|
|
|
}
|
|
|
|
|
}
|
2024-03-21 09:06:37 -05:00
|
|
|
meshNodes->at(newPos++) = meshNodes->at(i);
|
2024-08-13 06:31:05 -05:00
|
|
|
} else {
|
2023-09-18 13:16:37 +02:00
|
|
|
removed++;
|
2024-08-13 06:31:05 -05:00
|
|
|
}
|
2023-09-18 13:16:37 +02:00
|
|
|
}
|
2024-03-21 09:06:37 -05:00
|
|
|
numMeshNodes -= removed;
|
2025-01-11 09:02:05 -06:00
|
|
|
std::fill(nodeDatabase.nodes.begin() + numMeshNodes, nodeDatabase.nodes.begin() + numMeshNodes + removed,
|
2024-03-21 09:06:37 -05:00
|
|
|
meshtastic_NodeInfoLite());
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("cleanupMeshDB purged %d entries", removed);
|
2023-09-18 13:16:37 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-16 15:26:30 -07:00
|
|
|
void NodeDB::installDefaultDeviceState()
|
|
|
|
|
{
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Install default DeviceState");
|
2024-03-21 09:06:37 -05:00
|
|
|
// memset(&devicestate, 0, sizeof(meshtastic_DeviceState));
|
2020-06-16 15:26:30 -07:00
|
|
|
|
2020-02-07 09:36:15 -08:00
|
|
|
// init our devicestate with valid flags so protobuf writing/reading will work
|
|
|
|
|
devicestate.has_my_node = true;
|
|
|
|
|
devicestate.has_owner = true;
|
2021-03-12 15:45:28 +08:00
|
|
|
devicestate.version = DEVICESTATE_CUR_VER;
|
2020-06-16 15:26:30 -07:00
|
|
|
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
|
2024-10-07 06:43:55 -05:00
|
|
|
devicestate.has_rx_waypoint = false;
|
|
|
|
|
devicestate.has_rx_text_message = false;
|
2020-02-07 09:36:15 -08:00
|
|
|
|
2020-06-03 13:15:45 -07:00
|
|
|
generatePacketId(); // FIXME - ugly way to init current_packet_id;
|
2020-03-25 13:36:54 -07:00
|
|
|
|
2020-06-07 17:22:07 -07:00
|
|
|
// Set default owner name
|
2021-02-08 10:15:02 +08:00
|
|
|
pickNewNodeNum(); // based on macaddr now
|
2024-10-02 06:14:24 -05:00
|
|
|
#ifdef USERPREFS_CONFIG_OWNER_LONG_NAME
|
|
|
|
|
snprintf(owner.long_name, sizeof(owner.long_name), USERPREFS_CONFIG_OWNER_LONG_NAME);
|
2024-08-13 03:31:45 +02:00
|
|
|
#else
|
2024-12-15 23:23:27 +11:00
|
|
|
snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %04x", getNodeNum() & 0x0ffff);
|
2024-08-13 03:31:45 +02:00
|
|
|
#endif
|
2024-10-02 06:14:24 -05:00
|
|
|
#ifdef USERPREFS_CONFIG_OWNER_SHORT_NAME
|
|
|
|
|
snprintf(owner.short_name, sizeof(owner.short_name), USERPREFS_CONFIG_OWNER_SHORT_NAME);
|
2024-08-13 03:31:45 +02:00
|
|
|
#else
|
2024-12-15 23:23:27 +11:00
|
|
|
snprintf(owner.short_name, sizeof(owner.short_name), "%04x", getNodeNum() & 0x0ffff);
|
2024-08-13 03:31:45 +02:00
|
|
|
#endif
|
2023-01-16 10:55:40 +01:00
|
|
|
snprintf(owner.id, sizeof(owner.id), "!%08x", getNodeNum()); // Default node ID now based on nodenum
|
2023-06-20 23:29:25 +02:00
|
|
|
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
|
2020-06-16 15:02:11 -07:00
|
|
|
}
|
|
|
|
|
|
2020-02-08 12:42:54 -08:00
|
|
|
// We reserve a few nodenums for future use
|
|
|
|
|
#define NUM_RESERVED 4
|
|
|
|
|
|
|
|
|
|
/**
|
2020-03-18 19:15:51 -07:00
|
|
|
* get our starting (provisional) nodenum from flash.
|
2020-02-08 12:42:54 -08:00
|
|
|
*/
|
|
|
|
|
void NodeDB::pickNewNodeNum()
|
|
|
|
|
{
|
2024-04-10 18:29:29 +02:00
|
|
|
NodeNum nodeNum = myNodeInfo.my_node_num;
|
2024-04-17 14:07:40 +02:00
|
|
|
getMacAddr(ourMacAddr); // Make sure ourMacAddr is set
|
2024-04-10 18:29:29 +02:00
|
|
|
if (nodeNum == 0) {
|
|
|
|
|
// Pick an initial nodenum based on the macaddr
|
|
|
|
|
nodeNum = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5];
|
|
|
|
|
}
|
2020-02-08 12:42:54 -08:00
|
|
|
|
2023-06-17 09:10:09 -05:00
|
|
|
meshtastic_NodeInfoLite *found;
|
2024-09-17 17:33:21 +02:00
|
|
|
while (((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0) ||
|
|
|
|
|
(nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED)) {
|
2023-09-18 13:16:37 +02:00
|
|
|
NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice
|
2024-09-23 13:56:26 -05:00
|
|
|
if (found)
|
|
|
|
|
LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, by MAC ending in 0x%02x%02x vs our 0x%02x%02x, so "
|
2024-10-14 06:11:43 +02:00
|
|
|
"trying for 0x%x",
|
2024-09-23 13:56:26 -05:00
|
|
|
nodeNum, found->user.macaddr[4], found->user.macaddr[5], ourMacAddr[4], ourMacAddr[5], candidate);
|
2023-09-18 13:16:37 +02:00
|
|
|
nodeNum = candidate;
|
2020-02-08 12:42:54 -08:00
|
|
|
}
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_DEBUG("Use nodenum 0x%x ", nodeNum);
|
2020-02-08 12:42:54 -08:00
|
|
|
|
2023-09-18 13:16:37 +02:00
|
|
|
myNodeInfo.my_node_num = nodeNum;
|
2020-02-08 12:42:54 -08:00
|
|
|
}
|
|
|
|
|
|
2024-04-15 07:22:05 -05:00
|
|
|
/** Load a protobuf from a file, return LoadFileResult */
|
|
|
|
|
LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields,
|
|
|
|
|
void *dest_struct)
|
2020-02-07 09:36:15 -08:00
|
|
|
{
|
2024-04-15 07:22:05 -05:00
|
|
|
LoadFileResult state = LoadFileResult::OTHER_FAILURE;
|
2022-02-14 18:45:29 +01:00
|
|
|
#ifdef FSCom
|
2025-01-03 10:05:26 +08:00
|
|
|
concurrency::LockGuard g(spiLock);
|
2024-04-15 07:22:05 -05:00
|
|
|
|
2022-08-08 23:11:19 +02:00
|
|
|
auto f = FSCom.open(filename, FILE_O_READ);
|
2021-03-11 17:54:16 +08:00
|
|
|
|
2020-03-18 19:15:51 -07:00
|
|
|
if (f) {
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Load %s", filename);
|
2021-03-11 13:02:00 +08:00
|
|
|
pb_istream_t stream = {&readcb, &f, protoSize};
|
2020-02-07 09:36:15 -08:00
|
|
|
|
2021-03-11 13:02:00 +08:00
|
|
|
memset(dest_struct, 0, objSize);
|
|
|
|
|
if (!pb_decode(&stream, fields, dest_struct)) {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_ERROR("Error: can't decode protobuf %s", PB_GET_ERROR(&stream));
|
2024-04-15 07:22:05 -05:00
|
|
|
state = LoadFileResult::DECODE_FAILED;
|
2020-03-18 19:15:51 -07:00
|
|
|
} else {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_INFO("Loaded %s successfully", filename);
|
2024-07-26 03:16:21 +02:00
|
|
|
state = LoadFileResult::LOAD_SUCCESS;
|
2020-02-08 07:41:04 -08:00
|
|
|
}
|
2020-02-07 09:36:15 -08:00
|
|
|
f.close();
|
2020-03-18 19:15:51 -07:00
|
|
|
} else {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_ERROR("Could not open / read %s", filename);
|
2020-02-07 09:36:15 -08:00
|
|
|
}
|
2020-04-14 20:22:27 -07:00
|
|
|
#else
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_ERROR("ERROR: Filesystem not implemented");
|
2024-07-26 03:16:21 +02:00
|
|
|
state = LoadFileResult::NO_FILESYSTEM;
|
2020-04-14 20:22:27 -07:00
|
|
|
#endif
|
2024-04-15 07:22:05 -05:00
|
|
|
return state;
|
2021-03-11 13:02:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NodeDB::loadFromDisk()
|
|
|
|
|
{
|
2025-01-11 09:02:05 -06:00
|
|
|
// Mark the current device state as completely unusable, so that if we fail reading the entire file from
|
2024-08-06 11:59:06 -07:00
|
|
|
// disk we will still factoryReset to restore things.
|
2025-01-11 09:02:05 -06:00
|
|
|
devicestate.version = 0;
|
2024-08-06 11:59:06 -07:00
|
|
|
|
2024-11-17 07:57:59 -06:00
|
|
|
#ifdef ARCH_ESP32
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->lock();
|
2025-01-11 09:02:05 -06:00
|
|
|
// If the legacy deviceState exists, start over with a factory reset
|
2025-01-12 19:03:21 -06:00
|
|
|
if (FSCom.exists("/static/static"))
|
|
|
|
|
rmDir("/static/static"); // Remove bad static web files bundle from initial 2.5.13 release
|
|
|
|
|
spiLock->unlock();
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef FSCom
|
|
|
|
|
spiLock->lock();
|
2025-01-11 09:02:05 -06:00
|
|
|
if (FSCom.exists(legacyPrefFileName)) {
|
|
|
|
|
rmDir("/prefs");
|
|
|
|
|
}
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->unlock();
|
2024-11-17 07:57:59 -06:00
|
|
|
#endif
|
2025-01-11 09:02:05 -06:00
|
|
|
auto state = loadProto(nodeDatabaseFileName, getMaxNodesAllocatedSize(), sizeof(meshtastic_NodeDatabase),
|
|
|
|
|
&meshtastic_NodeDatabase_msg, &nodeDatabase);
|
|
|
|
|
if (nodeDatabase.version < DEVICESTATE_MIN_VER) {
|
|
|
|
|
LOG_WARN("NodeDatabase %d is old, discard", nodeDatabase.version);
|
|
|
|
|
installDefaultNodeDatabase();
|
|
|
|
|
} else {
|
|
|
|
|
meshNodes = &nodeDatabase.nodes;
|
|
|
|
|
numMeshNodes = nodeDatabase.nodes.size();
|
|
|
|
|
LOG_INFO("Loaded saved nodedatabase version %d, with nodes count: %d", nodeDatabase.version, nodeDatabase.nodes.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (numMeshNodes > MAX_NUM_NODES) {
|
|
|
|
|
LOG_WARN("Node count %d exceeds MAX_NUM_NODES %d, truncating", numMeshNodes, MAX_NUM_NODES);
|
|
|
|
|
numMeshNodes = MAX_NUM_NODES;
|
|
|
|
|
}
|
|
|
|
|
meshNodes->resize(MAX_NUM_NODES);
|
|
|
|
|
|
2021-03-11 13:02:00 +08:00
|
|
|
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
|
2025-01-11 09:02:05 -06:00
|
|
|
state = loadProto(deviceStateFileName, meshtastic_DeviceState_size, sizeof(meshtastic_DeviceState),
|
|
|
|
|
&meshtastic_DeviceState_msg, &devicestate);
|
2024-04-15 07:22:05 -05:00
|
|
|
|
2024-08-06 11:59:06 -07:00
|
|
|
// See https://github.com/meshtastic/firmware/issues/4184#issuecomment-2269390786
|
|
|
|
|
// It is very important to try and use the saved prefs even if we fail to read meshtastic_DeviceState. Because most of our
|
|
|
|
|
// critical config may still be valid (in the other files - loaded next).
|
|
|
|
|
// Also, if we did fail on reading we probably failed on the enormous (and non critical) nodeDB. So DO NOT install default
|
|
|
|
|
// device state.
|
|
|
|
|
// if (state != LoadFileResult::LOAD_SUCCESS) {
|
|
|
|
|
// installDefaultDeviceState(); // Our in RAM copy might now be corrupt
|
|
|
|
|
//} else {
|
|
|
|
|
if (devicestate.version < DEVICESTATE_MIN_VER) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_WARN("Devicestate %d is old, discard", devicestate.version);
|
2024-09-11 08:42:26 -05:00
|
|
|
installDefaultDeviceState();
|
2021-03-11 13:02:00 +08:00
|
|
|
} else {
|
2025-01-11 09:02:05 -06:00
|
|
|
LOG_INFO("Loaded saved devicestate version %d", devicestate.version);
|
2024-11-13 18:44:35 -06:00
|
|
|
}
|
2021-03-11 13:02:00 +08:00
|
|
|
|
2024-04-15 07:22:05 -05:00
|
|
|
state = loadProto(configFileName, meshtastic_LocalConfig_size, sizeof(meshtastic_LocalConfig), &meshtastic_LocalConfig_msg,
|
|
|
|
|
&config);
|
2024-07-26 03:16:21 +02:00
|
|
|
if (state != LoadFileResult::LOAD_SUCCESS) {
|
2022-05-01 20:30:19 -05:00
|
|
|
installDefaultConfig(); // Our in RAM copy might now be corrupt
|
2022-06-15 16:52:04 +02:00
|
|
|
} else {
|
|
|
|
|
if (config.version < DEVICESTATE_MIN_VER) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_WARN("config %d is old, discard", config.version);
|
2024-09-11 08:42:26 -05:00
|
|
|
installDefaultConfig(true);
|
2022-06-15 16:52:04 +02:00
|
|
|
} else {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_INFO("Loaded saved config version %d", config.version);
|
2022-06-15 16:52:04 +02:00
|
|
|
}
|
2022-05-01 20:30:19 -05:00
|
|
|
}
|
|
|
|
|
|
2024-11-23 16:08:18 +01:00
|
|
|
// Make sure we load hard coded admin keys even when the configuration file has none.
|
|
|
|
|
// Initialize admin_key_count to zero
|
|
|
|
|
byte numAdminKeys = 0;
|
2024-12-31 15:58:59 +01:00
|
|
|
#if defined(USERPREFS_USE_ADMIN_KEY_0) || defined(USERPREFS_USE_ADMIN_KEY_1) || defined(USERPREFS_USE_ADMIN_KEY_2)
|
2024-11-23 16:08:18 +01:00
|
|
|
uint16_t sum = 0;
|
2024-12-31 15:58:59 +01:00
|
|
|
#endif
|
2024-11-23 16:08:18 +01:00
|
|
|
#ifdef USERPREFS_USE_ADMIN_KEY_0
|
2024-12-31 15:58:59 +01:00
|
|
|
|
2024-11-23 16:08:18 +01:00
|
|
|
for (uint8_t b = 0; b < 32; b++) {
|
|
|
|
|
sum += config.security.admin_key[0].bytes[b];
|
|
|
|
|
}
|
|
|
|
|
if (sum == 0) {
|
|
|
|
|
numAdminKeys += 1;
|
|
|
|
|
LOG_INFO("Admin 0 key zero. Loading hard coded key from user preferences.");
|
|
|
|
|
memcpy(config.security.admin_key[0].bytes, userprefs_admin_key_0, 32);
|
|
|
|
|
config.security.admin_key[0].size = 32;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef USERPREFS_USE_ADMIN_KEY_1
|
|
|
|
|
sum = 0;
|
|
|
|
|
for (uint8_t b = 0; b < 32; b++) {
|
|
|
|
|
sum += config.security.admin_key[1].bytes[b];
|
|
|
|
|
}
|
|
|
|
|
if (sum == 0) {
|
|
|
|
|
numAdminKeys += 1;
|
|
|
|
|
LOG_INFO("Admin 1 key zero. Loading hard coded key from user preferences.");
|
|
|
|
|
memcpy(config.security.admin_key[1].bytes, userprefs_admin_key_1, 32);
|
|
|
|
|
config.security.admin_key[1].size = 32;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef USERPREFS_USE_ADMIN_KEY_2
|
|
|
|
|
sum = 0;
|
|
|
|
|
for (uint8_t b = 0; b < 32; b++) {
|
|
|
|
|
sum += config.security.admin_key[2].bytes[b];
|
|
|
|
|
}
|
|
|
|
|
if (sum == 0) {
|
|
|
|
|
numAdminKeys += 1;
|
|
|
|
|
LOG_INFO("Admin 2 key zero. Loading hard coded key from user preferences.");
|
|
|
|
|
memcpy(config.security.admin_key[2].bytes, userprefs_admin_key_2, 32);
|
|
|
|
|
config.security.admin_key[2].size = 32;
|
2024-12-31 15:58:59 +01:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (numAdminKeys > 0) {
|
|
|
|
|
LOG_INFO("Saving %d hard coded admin keys.", numAdminKeys);
|
2024-11-23 16:08:18 +01:00
|
|
|
config.security.admin_key_count = numAdminKeys;
|
|
|
|
|
saveToDisk(SEGMENT_CONFIG);
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-15 07:22:05 -05:00
|
|
|
state = loadProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, sizeof(meshtastic_LocalModuleConfig),
|
|
|
|
|
&meshtastic_LocalModuleConfig_msg, &moduleConfig);
|
2024-07-26 03:16:21 +02:00
|
|
|
if (state != LoadFileResult::LOAD_SUCCESS) {
|
2022-05-02 22:00:24 +10:00
|
|
|
installDefaultModuleConfig(); // Our in RAM copy might now be corrupt
|
2022-06-15 16:52:04 +02:00
|
|
|
} else {
|
|
|
|
|
if (moduleConfig.version < DEVICESTATE_MIN_VER) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_WARN("moduleConfig %d is old, discard", moduleConfig.version);
|
2022-06-15 16:52:04 +02:00
|
|
|
installDefaultModuleConfig();
|
|
|
|
|
} else {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_INFO("Loaded saved moduleConfig version %d", moduleConfig.version);
|
2022-06-15 16:52:04 +02:00
|
|
|
}
|
2022-05-02 22:00:24 +10:00
|
|
|
}
|
|
|
|
|
|
2024-04-15 07:22:05 -05:00
|
|
|
state = loadProto(channelFileName, meshtastic_ChannelFile_size, sizeof(meshtastic_ChannelFile), &meshtastic_ChannelFile_msg,
|
|
|
|
|
&channelFile);
|
2024-07-26 03:16:21 +02:00
|
|
|
if (state != LoadFileResult::LOAD_SUCCESS) {
|
2021-03-11 17:54:16 +08:00
|
|
|
installDefaultChannels(); // Our in RAM copy might now be corrupt
|
2022-06-15 16:52:04 +02:00
|
|
|
} else {
|
|
|
|
|
if (channelFile.version < DEVICESTATE_MIN_VER) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_WARN("channelFile %d is old, discard", channelFile.version);
|
2022-06-15 16:52:04 +02:00
|
|
|
installDefaultChannels();
|
|
|
|
|
} else {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_INFO("Loaded saved channelFile version %d", channelFile.version);
|
2022-06-15 16:52:04 +02:00
|
|
|
}
|
2021-03-11 17:54:16 +08:00
|
|
|
}
|
2022-10-19 15:39:06 +02:00
|
|
|
|
2024-12-27 10:46:21 +11:00
|
|
|
state = loadProto(uiconfigFileName, meshtastic_DeviceUIConfig_size, sizeof(meshtastic_DeviceUIConfig),
|
|
|
|
|
&meshtastic_DeviceUIConfig_msg, &uiconfig);
|
|
|
|
|
if (state == LoadFileResult::LOAD_SUCCESS) {
|
2024-12-27 18:01:02 +11:00
|
|
|
LOG_INFO("Loaded UIConfig");
|
2024-12-27 10:46:21 +11:00
|
|
|
}
|
|
|
|
|
|
2024-07-16 09:37:50 -05:00
|
|
|
// 2.4.X - configuration migration to update new default intervals
|
|
|
|
|
if (moduleConfig.version < 23) {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("ModuleConfig version %d is stale, upgrading to new default intervals", moduleConfig.version);
|
2024-07-16 09:37:50 -05:00
|
|
|
moduleConfig.version = DEVICESTATE_CUR_VER;
|
|
|
|
|
if (moduleConfig.telemetry.device_update_interval == 900)
|
|
|
|
|
moduleConfig.telemetry.device_update_interval = 0;
|
|
|
|
|
if (moduleConfig.telemetry.environment_update_interval == 900)
|
|
|
|
|
moduleConfig.telemetry.environment_update_interval = 0;
|
|
|
|
|
if (moduleConfig.telemetry.air_quality_interval == 900)
|
|
|
|
|
moduleConfig.telemetry.air_quality_interval = 0;
|
|
|
|
|
if (moduleConfig.telemetry.power_update_interval == 900)
|
|
|
|
|
moduleConfig.telemetry.power_update_interval = 0;
|
|
|
|
|
if (moduleConfig.neighbor_info.update_interval == 900)
|
|
|
|
|
moduleConfig.neighbor_info.update_interval = 0;
|
|
|
|
|
if (moduleConfig.paxcounter.paxcounter_update_interval == 900)
|
|
|
|
|
moduleConfig.paxcounter.paxcounter_update_interval = 0;
|
|
|
|
|
|
|
|
|
|
saveToDisk(SEGMENT_MODULECONFIG);
|
|
|
|
|
}
|
2020-02-07 09:36:15 -08:00
|
|
|
}
|
|
|
|
|
|
2021-03-11 17:54:16 +08:00
|
|
|
/** Save a protobuf from a file, return true for success */
|
2024-08-06 11:59:06 -07:00
|
|
|
bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
|
|
|
|
|
bool fullAtomic)
|
2020-02-07 09:36:15 -08:00
|
|
|
{
|
2022-06-07 20:55:05 +03:00
|
|
|
bool okay = false;
|
2022-02-14 18:45:29 +01:00
|
|
|
#ifdef FSCom
|
2024-08-06 11:59:06 -07:00
|
|
|
auto f = SafeFile(filename, fullAtomic);
|
2020-02-12 13:31:09 -08:00
|
|
|
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_INFO("Save %s", filename);
|
2024-08-06 11:59:06 -07:00
|
|
|
pb_ostream_t stream = {&writecb, static_cast<Print *>(&f), protoSize};
|
2020-02-07 09:36:15 -08:00
|
|
|
|
2024-08-06 11:59:06 -07:00
|
|
|
if (!pb_encode(&stream, fields, dest_struct)) {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_ERROR("Error: can't encode protobuf %s", PB_GET_ERROR(&stream));
|
2021-03-11 17:54:16 +08:00
|
|
|
} else {
|
2024-08-06 11:59:06 -07:00
|
|
|
okay = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool writeSucceeded = f.close();
|
|
|
|
|
|
|
|
|
|
if (!okay || !writeSucceeded) {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_ERROR("Can't write prefs!");
|
2021-03-11 17:54:16 +08:00
|
|
|
}
|
|
|
|
|
#else
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_ERROR("ERROR: Filesystem not implemented");
|
2021-03-11 17:54:16 +08:00
|
|
|
#endif
|
|
|
|
|
return okay;
|
|
|
|
|
}
|
2020-02-07 09:36:15 -08:00
|
|
|
|
2024-08-06 11:59:06 -07:00
|
|
|
bool NodeDB::saveChannelsToDisk()
|
2021-03-11 18:29:47 +08:00
|
|
|
{
|
2022-02-14 18:45:29 +01:00
|
|
|
#ifdef FSCom
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->lock();
|
2024-03-17 08:16:22 -05:00
|
|
|
FSCom.mkdir("/prefs");
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->unlock();
|
2021-03-11 18:29:47 +08:00
|
|
|
#endif
|
2024-08-06 11:59:06 -07:00
|
|
|
return saveProto(channelFileName, meshtastic_ChannelFile_size, &meshtastic_ChannelFile_msg, &channelFile);
|
2021-03-11 18:29:47 +08:00
|
|
|
}
|
|
|
|
|
|
2024-08-06 11:59:06 -07:00
|
|
|
bool NodeDB::saveDeviceStateToDisk()
|
2022-08-03 07:16:41 -05:00
|
|
|
{
|
|
|
|
|
#ifdef FSCom
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->lock();
|
2024-03-17 08:16:22 -05:00
|
|
|
FSCom.mkdir("/prefs");
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->unlock();
|
2022-08-03 07:16:41 -05:00
|
|
|
#endif
|
2024-08-06 11:59:06 -07:00
|
|
|
// Note: if MAX_NUM_NODES=100 and meshtastic_NodeInfoLite_size=166, so will be approximately 17KB
|
|
|
|
|
// Because so huge we _must_ not use fullAtomic, because the filesystem is probably too small to hold two copies of this
|
2025-01-11 09:02:05 -06:00
|
|
|
return saveProto(deviceStateFileName, meshtastic_DeviceState_size, &meshtastic_DeviceState_msg, &devicestate, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NodeDB::saveNodeDatabaseToDisk()
|
|
|
|
|
{
|
|
|
|
|
#ifdef FSCom
|
|
|
|
|
spiLock->lock();
|
|
|
|
|
FSCom.mkdir("/prefs");
|
|
|
|
|
spiLock->unlock();
|
|
|
|
|
#endif
|
|
|
|
|
size_t nodeDatabaseSize;
|
|
|
|
|
pb_get_encoded_size(&nodeDatabaseSize, meshtastic_NodeDatabase_fields, &nodeDatabase);
|
|
|
|
|
return saveProto(nodeDatabaseFileName, nodeDatabaseSize, &meshtastic_NodeDatabase_msg, &nodeDatabase, false);
|
2022-08-03 07:16:41 -05:00
|
|
|
}
|
|
|
|
|
|
2024-08-06 11:59:06 -07:00
|
|
|
bool NodeDB::saveToDiskNoRetry(int saveWhat)
|
2021-03-11 17:54:16 +08:00
|
|
|
{
|
2024-08-06 11:59:06 -07:00
|
|
|
bool success = true;
|
2022-02-14 18:45:29 +01:00
|
|
|
#ifdef FSCom
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->lock();
|
2024-03-17 08:16:22 -05:00
|
|
|
FSCom.mkdir("/prefs");
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->unlock();
|
2021-03-11 17:54:16 +08:00
|
|
|
#endif
|
2024-03-17 08:16:22 -05:00
|
|
|
if (saveWhat & SEGMENT_CONFIG) {
|
|
|
|
|
config.has_device = true;
|
|
|
|
|
config.has_display = true;
|
|
|
|
|
config.has_lora = true;
|
|
|
|
|
config.has_position = true;
|
|
|
|
|
config.has_power = true;
|
|
|
|
|
config.has_network = true;
|
|
|
|
|
config.has_bluetooth = true;
|
2024-08-10 13:45:41 -05:00
|
|
|
config.has_security = true;
|
2024-04-15 16:36:22 -05:00
|
|
|
|
2024-08-06 11:59:06 -07:00
|
|
|
success &= saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config);
|
2024-03-17 08:16:22 -05:00
|
|
|
}
|
2022-05-22 13:54:24 +02:00
|
|
|
|
2024-03-17 08:16:22 -05:00
|
|
|
if (saveWhat & SEGMENT_MODULECONFIG) {
|
|
|
|
|
moduleConfig.has_canned_message = true;
|
|
|
|
|
moduleConfig.has_external_notification = true;
|
|
|
|
|
moduleConfig.has_mqtt = true;
|
|
|
|
|
moduleConfig.has_range_test = true;
|
|
|
|
|
moduleConfig.has_serial = true;
|
|
|
|
|
moduleConfig.has_store_forward = true;
|
|
|
|
|
moduleConfig.has_telemetry = true;
|
2024-04-15 16:36:22 -05:00
|
|
|
moduleConfig.has_neighbor_info = true;
|
|
|
|
|
moduleConfig.has_detection_sensor = true;
|
|
|
|
|
moduleConfig.has_ambient_lighting = true;
|
|
|
|
|
moduleConfig.has_audio = true;
|
|
|
|
|
moduleConfig.has_paxcounter = true;
|
|
|
|
|
|
2024-08-06 11:59:06 -07:00
|
|
|
success &=
|
|
|
|
|
saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-17 08:16:22 -05:00
|
|
|
if (saveWhat & SEGMENT_CHANNELS) {
|
2024-08-06 11:59:06 -07:00
|
|
|
success &= saveChannelsToDisk();
|
2020-02-07 09:36:15 -08:00
|
|
|
}
|
2024-08-06 11:59:06 -07:00
|
|
|
|
|
|
|
|
if (saveWhat & SEGMENT_DEVICESTATE) {
|
|
|
|
|
success &= saveDeviceStateToDisk();
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-11 09:02:05 -06:00
|
|
|
if (saveWhat & SEGMENT_NODEDATABASE) {
|
|
|
|
|
success &= saveNodeDatabaseToDisk();
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-06 11:59:06 -07:00
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NodeDB::saveToDisk(int saveWhat)
|
|
|
|
|
{
|
2025-01-11 09:02:05 -06:00
|
|
|
LOG_DEBUG("Save to disk %d", saveWhat);
|
2024-08-06 11:59:06 -07:00
|
|
|
bool success = saveToDiskNoRetry(saveWhat);
|
|
|
|
|
|
|
|
|
|
if (!success) {
|
2024-11-06 07:03:25 -06:00
|
|
|
LOG_ERROR("Failed to save to disk, retrying");
|
2024-08-06 11:59:06 -07:00
|
|
|
#ifdef ARCH_NRF52 // @geeksville is not ready yet to say we should do this on other platforms. See bug #4184 discussion
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->lock();
|
2024-08-06 11:59:06 -07:00
|
|
|
FSCom.format();
|
2025-01-03 10:05:26 +08:00
|
|
|
spiLock->unlock();
|
2024-08-06 11:59:06 -07:00
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
success = saveToDiskNoRetry(saveWhat);
|
|
|
|
|
|
|
|
|
|
RECORD_CRITICALERROR(success ? meshtastic_CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE
|
|
|
|
|
: meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success;
|
2020-02-04 09:00:17 -08:00
|
|
|
}
|
|
|
|
|
|
2023-06-17 09:10:09 -05:00
|
|
|
const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
|
|
|
|
|
{
|
2024-03-21 09:06:37 -05:00
|
|
|
if (readIndex < numMeshNodes)
|
|
|
|
|
return &meshNodes->at(readIndex++);
|
2023-06-17 09:10:09 -05:00
|
|
|
else
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-12 11:52:53 -08:00
|
|
|
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
|
2023-06-17 09:10:09 -05:00
|
|
|
uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n)
|
2020-02-12 11:52:53 -08:00
|
|
|
{
|
2020-05-04 11:15:05 -07:00
|
|
|
uint32_t now = getTime();
|
2020-02-12 11:52:53 -08:00
|
|
|
|
2021-03-26 09:30:15 +08:00
|
|
|
int delta = (int)(now - n->last_heard);
|
2020-02-12 11:52:53 -08:00
|
|
|
if (delta < 0) // our clock must be slightly off still - not set from GPS yet
|
2023-02-03 00:40:51 +01:00
|
|
|
delta = 0;
|
|
|
|
|
|
|
|
|
|
return delta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t sinceReceived(const meshtastic_MeshPacket *p)
|
|
|
|
|
{
|
|
|
|
|
uint32_t now = getTime();
|
|
|
|
|
|
|
|
|
|
int delta = (int)(now - p->rx_time);
|
|
|
|
|
if (delta < 0) // our clock must be slightly off still - not set from GPS yet
|
2020-02-12 11:52:53 -08:00
|
|
|
delta = 0;
|
|
|
|
|
|
|
|
|
|
return delta;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-05 14:46:39 -04:00
|
|
|
#define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline
|
2020-02-12 11:52:53 -08:00
|
|
|
|
2024-03-10 14:52:37 +01:00
|
|
|
size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
|
2020-02-12 13:31:09 -08:00
|
|
|
{
|
2020-02-12 11:52:53 -08:00
|
|
|
size_t numseen = 0;
|
|
|
|
|
|
|
|
|
|
// FIXME this implementation is kinda expensive
|
2024-03-21 09:06:37 -05:00
|
|
|
for (int i = 0; i < numMeshNodes; i++) {
|
|
|
|
|
if (localOnly && meshNodes->at(i).via_mqtt)
|
2024-03-10 14:52:37 +01:00
|
|
|
continue;
|
2024-03-21 09:06:37 -05:00
|
|
|
if (sinceLastSeen(&meshNodes->at(i)) < NUM_ONLINE_SECS)
|
2020-02-12 11:52:53 -08:00
|
|
|
numseen++;
|
2024-03-10 14:52:37 +01:00
|
|
|
}
|
2020-02-12 11:52:53 -08:00
|
|
|
|
|
|
|
|
return numseen;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-09 19:01:43 +11:00
|
|
|
#include "MeshModule.h"
|
2024-04-18 14:20:39 -05:00
|
|
|
#include "Throttle.h"
|
2020-11-28 12:10:19 +08:00
|
|
|
|
2020-12-03 16:48:44 +08:00
|
|
|
/** Update position info for this node based on received position data
|
|
|
|
|
*/
|
2023-01-21 18:22:19 +01:00
|
|
|
void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSource src)
|
2020-12-05 10:00:46 +08:00
|
|
|
{
|
2023-06-17 09:10:09 -05:00
|
|
|
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
2022-02-10 18:44:58 +01:00
|
|
|
if (!info) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-12-05 10:00:46 +08:00
|
|
|
|
2021-10-26 12:22:34 +00:00
|
|
|
if (src == RX_SRC_LOCAL) {
|
|
|
|
|
// Local packet, fully authoritative
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_INFO("updatePosition LOCAL pos@%x time=%u lat=%d lon=%d alt=%d", p.timestamp, p.time, p.latitude_i, p.longitude_i,
|
2024-06-21 17:25:54 -05:00
|
|
|
p.altitude);
|
2023-06-17 09:10:09 -05:00
|
|
|
|
2023-10-09 18:33:04 -05:00
|
|
|
setLocalPosition(p);
|
2023-10-11 06:17:05 -05:00
|
|
|
info->position = TypeConversions::ConvertToPositionLite(p);
|
2022-09-09 12:51:41 +02:00
|
|
|
} else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) {
|
2021-10-28 11:31:19 +00:00
|
|
|
// FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO
|
|
|
|
|
// (stop-gap fix for issue #900)
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("updatePosition SPECIAL time setting time=%u", p.time);
|
2021-10-28 11:31:19 +00:00
|
|
|
info->position.time = p.time;
|
2021-10-26 12:22:34 +00:00
|
|
|
} else {
|
|
|
|
|
// Be careful to only update fields that have been set by the REMOTE sender
|
|
|
|
|
// A lot of position reports don't have time populated. In that case, be careful to not blow away the time we
|
|
|
|
|
// recorded based on the packet rxTime
|
2021-10-28 11:31:19 +00:00
|
|
|
//
|
|
|
|
|
// FIXME perhaps handle RX_SRC_USER separately?
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_INFO("updatePosition REMOTE node=0x%x time=%u lat=%d lon=%d", nodeId, p.time, p.latitude_i, p.longitude_i);
|
2021-10-26 12:22:34 +00:00
|
|
|
|
|
|
|
|
// First, back up fields that we want to protect from overwrite
|
|
|
|
|
uint32_t tmp_time = info->position.time;
|
|
|
|
|
|
|
|
|
|
// Next, update atomically
|
2023-10-11 06:17:05 -05:00
|
|
|
info->position = TypeConversions::ConvertToPositionLite(p);
|
2021-10-26 12:22:34 +00:00
|
|
|
|
|
|
|
|
// Last, restore any fields that may have been overwritten
|
2022-05-02 22:00:24 +10:00
|
|
|
if (!info->position.time)
|
2021-10-26 12:22:34 +00:00
|
|
|
info->position.time = tmp_time;
|
2021-02-17 19:04:41 +08:00
|
|
|
}
|
2020-12-05 10:00:46 +08:00
|
|
|
info->has_position = true;
|
|
|
|
|
updateGUIforNode = info;
|
|
|
|
|
notifyObservers(true); // Force an update whether or not our node counts have changed
|
|
|
|
|
}
|
2020-12-03 16:48:44 +08:00
|
|
|
|
2022-03-20 14:55:38 +00:00
|
|
|
/** Update telemetry info for this node based on received metrics
|
2022-03-27 14:55:35 +00:00
|
|
|
* We only care about device telemetry here
|
2022-03-20 14:55:38 +00:00
|
|
|
*/
|
2023-01-21 18:22:19 +01:00
|
|
|
void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxSource src)
|
2022-03-20 14:55:38 +00:00
|
|
|
{
|
2023-06-17 09:10:09 -05:00
|
|
|
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
2022-03-27 14:55:35 +00:00
|
|
|
// Environment metrics should never go to NodeDb but we'll safegaurd anyway
|
2023-01-21 18:22:19 +01:00
|
|
|
if (!info || t.which_variant != meshtastic_Telemetry_device_metrics_tag) {
|
2022-03-20 14:55:38 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (src == RX_SRC_LOCAL) {
|
|
|
|
|
// Local packet, fully authoritative
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("updateTelemetry LOCAL");
|
2022-03-20 14:55:38 +00:00
|
|
|
} else {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("updateTelemetry REMOTE node=0x%x ", nodeId);
|
2022-03-20 14:55:38 +00:00
|
|
|
}
|
2022-04-01 23:35:18 +00:00
|
|
|
info->device_metrics = t.variant.device_metrics;
|
|
|
|
|
info->has_device_metrics = true;
|
2022-03-20 14:55:38 +00:00
|
|
|
updateGUIforNode = info;
|
|
|
|
|
notifyObservers(true); // Force an update whether or not our node counts have changed
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-16 03:11:35 +01:00
|
|
|
/** Update user info and channel for this node based on received user data
|
2020-12-05 10:00:46 +08:00
|
|
|
*/
|
2024-08-10 13:45:41 -05:00
|
|
|
bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex)
|
2020-12-05 10:00:46 +08:00
|
|
|
{
|
2023-06-17 09:10:09 -05:00
|
|
|
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
2022-02-10 18:44:58 +01:00
|
|
|
if (!info) {
|
2023-04-11 13:16:19 +02:00
|
|
|
return false;
|
2022-02-10 18:44:58 +01:00
|
|
|
}
|
2020-12-03 16:48:44 +08:00
|
|
|
|
2024-08-10 13:45:41 -05:00
|
|
|
#if !(MESHTASTIC_EXCLUDE_PKI)
|
2024-08-12 11:26:43 -05:00
|
|
|
if (p.public_key.size > 0) {
|
|
|
|
|
printBytes("Incoming Pubkey: ", p.public_key.bytes, 32);
|
|
|
|
|
if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_INFO("Public Key set for node, not updating!");
|
2024-08-12 11:26:43 -05:00
|
|
|
// we copy the key into the incoming packet, to prevent overwrite
|
|
|
|
|
memcpy(p.public_key.bytes, info->user.public_key.bytes, 32);
|
|
|
|
|
} else {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_INFO("Update Node Pubkey!");
|
2024-08-12 11:26:43 -05:00
|
|
|
}
|
2024-08-10 13:45:41 -05:00
|
|
|
}
|
|
|
|
|
#endif
|
2020-12-05 10:00:46 +08:00
|
|
|
|
2024-01-16 03:11:35 +01:00
|
|
|
// Both of info->user and p start as filled with zero so I think this is okay
|
2024-08-24 12:19:31 -05:00
|
|
|
auto lite = TypeConversions::ConvertToUserLite(p);
|
|
|
|
|
bool changed = memcmp(&info->user, &lite, sizeof(info->user)) || (info->channel != channelIndex);
|
2020-12-05 10:00:46 +08:00
|
|
|
|
2024-08-24 12:19:31 -05:00
|
|
|
info->user = lite;
|
2024-08-12 11:26:43 -05:00
|
|
|
if (info->user.public_key.size == 32) {
|
|
|
|
|
printBytes("Saved Pubkey: ", info->user.public_key.bytes, 32);
|
|
|
|
|
}
|
2024-01-16 03:11:35 +01:00
|
|
|
if (nodeId != getNodeNum())
|
|
|
|
|
info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel)
|
2024-12-03 06:28:46 -06:00
|
|
|
LOG_DEBUG("Update changed=%d user %s/%s, id=0x%08x, channel=%d", changed, info->user.long_name, info->user.short_name, nodeId,
|
|
|
|
|
info->channel);
|
2020-12-05 10:00:46 +08:00
|
|
|
info->has_user = true;
|
|
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
|
updateGUIforNode = info;
|
|
|
|
|
powerFSM.trigger(EVENT_NODEDB_UPDATED);
|
|
|
|
|
notifyObservers(true); // Force an update whether or not our node counts have changed
|
|
|
|
|
|
2024-11-26 16:39:16 -06:00
|
|
|
// We just changed something about a User,
|
|
|
|
|
// store our DB unless we just did so less than a minute ago
|
2025-01-12 19:03:21 -06:00
|
|
|
|
2024-11-26 16:39:16 -06:00
|
|
|
if (!Throttle::isWithinTimespanMs(lastNodeDbSave, ONE_MINUTE_MS)) {
|
2025-01-11 09:02:05 -06:00
|
|
|
saveToDisk(SEGMENT_NODEDATABASE);
|
2024-11-26 16:39:16 -06:00
|
|
|
lastNodeDbSave = millis();
|
|
|
|
|
} else {
|
|
|
|
|
LOG_DEBUG("Defer NodeDB saveToDisk for now");
|
|
|
|
|
}
|
2020-12-05 10:00:46 +08:00
|
|
|
}
|
2023-04-11 13:16:19 +02:00
|
|
|
|
|
|
|
|
return changed;
|
2020-12-03 16:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-03 09:13:19 -08:00
|
|
|
/// given a subpacket sniffed from the network, update our DB state
|
|
|
|
|
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
|
2023-01-21 18:22:19 +01:00
|
|
|
void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
|
2020-02-03 09:13:19 -08:00
|
|
|
{
|
2025-01-11 09:02:05 -06:00
|
|
|
// if (mp.from == getNodeNum()) {
|
|
|
|
|
// LOG_DEBUG("Ignore update from self");
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
2023-01-21 18:22:19 +01:00
|
|
|
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("Update DB node 0x%x, rx_time=%u", mp.from, mp.rx_time);
|
2020-02-03 09:13:19 -08:00
|
|
|
|
2023-06-17 09:10:09 -05:00
|
|
|
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getFrom(&mp));
|
2022-02-10 18:44:58 +01:00
|
|
|
if (!info) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-02-03 09:13:19 -08:00
|
|
|
|
2021-03-26 09:30:15 +08:00
|
|
|
if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard
|
|
|
|
|
info->last_heard = mp.rx_time;
|
2020-02-03 09:13:19 -08:00
|
|
|
|
2021-03-24 13:25:21 +08:00
|
|
|
if (mp.rx_snr)
|
|
|
|
|
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
|
2024-03-08 14:13:57 +01:00
|
|
|
|
|
|
|
|
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
|
|
|
|
|
|
|
|
|
|
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
|
2024-09-17 12:08:04 -05:00
|
|
|
if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) {
|
|
|
|
|
info->has_hops_away = true;
|
2024-03-08 14:13:57 +01:00
|
|
|
info->hops_away = mp.hop_start - mp.hop_limit;
|
2024-09-17 12:08:04 -05:00
|
|
|
}
|
2023-03-29 13:51:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-17 09:10:09 -05:00
|
|
|
uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
|
2023-03-29 13:51:22 +02:00
|
|
|
{
|
2023-08-12 09:29:19 -05:00
|
|
|
const meshtastic_NodeInfoLite *info = getMeshNode(n);
|
2023-03-29 13:51:22 +02:00
|
|
|
if (!info) {
|
|
|
|
|
return 0; // defaults to PRIMARY
|
2020-02-03 09:13:19 -08:00
|
|
|
}
|
2023-03-29 13:51:22 +02:00
|
|
|
return info->channel;
|
2020-02-03 09:13:19 -08:00
|
|
|
}
|
|
|
|
|
|
2023-06-17 09:10:09 -05:00
|
|
|
/// Find a node in our DB, return null for missing
|
|
|
|
|
/// NOTE: This function might be called from an ISR
|
|
|
|
|
meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)
|
|
|
|
|
{
|
2024-03-21 09:06:37 -05:00
|
|
|
for (int i = 0; i < numMeshNodes; i++)
|
|
|
|
|
if (meshNodes->at(i).num == n)
|
|
|
|
|
return &meshNodes->at(i);
|
2023-06-17 09:10:09 -05:00
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-04 21:06:12 +01:00
|
|
|
// returns true if the maximum number of nodes is reached or we are running low on memory
|
|
|
|
|
bool NodeDB::isFull()
|
|
|
|
|
{
|
|
|
|
|
return (numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-03 09:13:19 -08:00
|
|
|
/// Find a node in our DB, create an empty NodeInfo if missing
|
2023-06-17 09:10:09 -05:00
|
|
|
meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
2020-02-03 09:13:19 -08:00
|
|
|
{
|
2023-06-17 09:10:09 -05:00
|
|
|
meshtastic_NodeInfoLite *lite = getMeshNode(n);
|
2020-02-03 09:13:19 -08:00
|
|
|
|
2023-06-17 09:10:09 -05:00
|
|
|
if (!lite) {
|
2024-11-04 21:06:12 +01:00
|
|
|
if (isFull()) {
|
2024-11-29 21:29:45 -06:00
|
|
|
LOG_INFO("Node database full with %i nodes and %u bytes free. Erasing oldest entry", numMeshNodes,
|
2024-08-04 18:52:10 -05:00
|
|
|
memGet.getFreeHeap());
|
2022-10-08 17:07:16 +02:00
|
|
|
// look for oldest node and erase it
|
|
|
|
|
uint32_t oldest = UINT32_MAX;
|
2024-08-12 11:37:50 -05:00
|
|
|
uint32_t oldestBoring = UINT32_MAX;
|
2024-08-12 06:43:54 -05:00
|
|
|
int oldestIndex = -1;
|
|
|
|
|
int oldestBoringIndex = -1;
|
2024-03-21 09:06:37 -05:00
|
|
|
for (int i = 1; i < numMeshNodes; i++) {
|
2024-08-12 06:43:54 -05:00
|
|
|
// Simply the oldest non-favorite node
|
2024-11-12 11:27:44 -08:00
|
|
|
if (!meshNodes->at(i).is_favorite && !meshNodes->at(i).is_ignored && meshNodes->at(i).last_heard < oldest) {
|
2024-03-21 09:06:37 -05:00
|
|
|
oldest = meshNodes->at(i).last_heard;
|
2022-10-08 17:07:16 +02:00
|
|
|
oldestIndex = i;
|
|
|
|
|
}
|
2024-08-12 06:43:54 -05:00
|
|
|
// The oldest "boring" node
|
2024-11-12 11:27:44 -08:00
|
|
|
if (!meshNodes->at(i).is_favorite && !meshNodes->at(i).is_ignored && meshNodes->at(i).user.public_key.size == 0 &&
|
2024-08-12 06:43:54 -05:00
|
|
|
meshNodes->at(i).last_heard < oldestBoring) {
|
|
|
|
|
oldestBoring = meshNodes->at(i).last_heard;
|
|
|
|
|
oldestBoringIndex = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// if we found a "boring" node, evict it
|
|
|
|
|
if (oldestBoringIndex != -1) {
|
|
|
|
|
oldestIndex = oldestBoringIndex;
|
2022-10-08 17:07:16 +02:00
|
|
|
}
|
2024-12-07 11:39:30 +11:00
|
|
|
|
|
|
|
|
if (oldestIndex != -1) {
|
|
|
|
|
// Shove the remaining nodes down the chain
|
|
|
|
|
for (int i = oldestIndex; i < numMeshNodes - 1; i++) {
|
|
|
|
|
meshNodes->at(i) = meshNodes->at(i + 1);
|
|
|
|
|
}
|
|
|
|
|
(numMeshNodes)--;
|
2022-10-08 17:07:16 +02:00
|
|
|
}
|
2022-02-10 18:44:58 +01:00
|
|
|
}
|
2022-10-08 17:07:16 +02:00
|
|
|
// add the node at the end
|
2024-03-21 09:06:37 -05:00
|
|
|
lite = &meshNodes->at((numMeshNodes)++);
|
2020-02-03 09:13:19 -08:00
|
|
|
|
|
|
|
|
// everything is missing except the nodenum
|
2023-06-17 09:10:09 -05:00
|
|
|
memset(lite, 0, sizeof(*lite));
|
|
|
|
|
lite->num = n;
|
2024-11-29 21:29:45 -06:00
|
|
|
LOG_INFO("Adding node to database with %i nodes and %u bytes free!", numMeshNodes, memGet.getFreeHeap());
|
2020-02-03 09:13:19 -08:00
|
|
|
}
|
|
|
|
|
|
2023-06-17 09:10:09 -05:00
|
|
|
return lite;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 14:00:10 -06:00
|
|
|
/// Sometimes we will have Position objects that only have a time, so check for
|
|
|
|
|
/// valid lat/lon
|
|
|
|
|
bool NodeDB::hasValidPosition(const meshtastic_NodeInfoLite *n)
|
|
|
|
|
{
|
|
|
|
|
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-24 13:33:24 -07:00
|
|
|
/// Record an error that should be reported via analytics
|
2023-01-21 18:22:19 +01:00
|
|
|
void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, const char *filename)
|
2020-03-24 13:33:24 -07:00
|
|
|
{
|
2021-01-19 18:21:54 -08:00
|
|
|
// Print error to screen and serial port
|
|
|
|
|
String lcd = String("Critical error ") + code + "!\n";
|
2024-10-10 14:58:42 -05:00
|
|
|
if (screen)
|
|
|
|
|
screen->print(lcd.c_str());
|
2023-05-13 13:33:14 +03:00
|
|
|
if (filename) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_ERROR("NOTE! Record critical error %d at %s:%lu", code, filename, address);
|
2023-05-13 13:33:14 +03:00
|
|
|
} else {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_ERROR("NOTE! Record critical error %d, address=0x%lx", code, address);
|
2023-05-13 13:33:14 +03:00
|
|
|
}
|
2021-02-16 15:41:52 +08:00
|
|
|
|
2021-01-19 18:21:54 -08:00
|
|
|
// Record error to DB
|
2023-06-08 08:07:32 -05:00
|
|
|
error_code = code;
|
|
|
|
|
error_address = address;
|
2021-05-01 11:27:37 +08:00
|
|
|
|
2023-07-14 17:25:20 -04:00
|
|
|
// Currently portuino is mostly used for simulation. Make sure the user notices something really bad happened
|
2022-07-31 07:11:47 -05:00
|
|
|
#ifdef ARCH_PORTDUINO
|
2024-11-06 07:03:25 -06:00
|
|
|
LOG_ERROR("A critical failure occurred, portduino is exiting");
|
2021-05-01 11:27:37 +08:00
|
|
|
exit(2);
|
|
|
|
|
#endif
|
2025-01-03 10:05:26 +08:00
|
|
|
}
|