diff --git a/boards/ESP32-S3-WROOM-1-N4.json b/boards/ESP32-S3-WROOM-1-N4.json new file mode 100644 index 000000000..3620a711d --- /dev/null +++ b/boards/ESP32-S3-WROOM-1-N4.json @@ -0,0 +1,39 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-D ARDUINO_USB_CDC_ON_BOOT=0", + "-D ARDUINO_USB_MSC_ON_BOOT=0", + "-D ARDUINO_USB_DFU_ON_BOOT=0", + "-D ARDUINO_USB_MODE=0", + "-D ARDUINO_RUNNING_CORE=1", + "-D ARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x303A", "0x1001"]], + "mcu": "esp32s3", + "variant": "ESP32-S3-WROOM-1-N4" + }, + "connectivity": ["wifi"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "ESP32-S3-WROOM-1-N4 (4 MB Flash, No PSRAM)", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 524288, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://www.espressif.com/sites/default/files/documentation/esp32-s3-wroom-1_wroom-1u_datasheet_en.pdf", + "vendor": "Espressif" +} diff --git a/protobufs b/protobufs index ecf105f66..eade2c6be 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit ecf105f66d182531423b73f4408c53701313c4eb +Subproject commit eade2c6befb65a9c46c5d28ae1e8e24c37a1a3d0 diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 13c2f4609..e2e2188b6 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -271,9 +271,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port) } break; case INA3221_ADDR: - LOG_INFO("INA3221 sensor found at address 0x%x\n", (uint8_t)addr.address); - type = INA3221; - break; + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2); + LOG_DEBUG("Register MFG_UID: 0x%x\n", registerValue); + if (registerValue == 0x5449) { + LOG_INFO("INA3221 sensor found at address 0x%x\n", (uint8_t)addr.address); + type = INA3221; + } else { // Unknown device + LOG_INFO("No INA3221 found at address 0x%x\n", (uint8_t)addr.address); + } case MCP9808_ADDR: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x07), 2); if (registerValue == 0x0400) { diff --git a/src/mesh/Default.h b/src/mesh/Default.h index 734cdf519..95723744b 100644 --- a/src/mesh/Default.h +++ b/src/mesh/Default.h @@ -2,6 +2,7 @@ #include #include #define ONE_DAY 24 * 60 * 60 +#define ONE_MINUTE_MS 60 * 1000 #define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60) #define default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 15 * 60) @@ -27,4 +28,4 @@ class Default static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval); static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval); static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue); -}; +}; \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 710b21593..39422b454 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -813,6 +813,7 @@ size_t NodeDB::getNumOnlineMeshNodes(bool localOnly) } #include "MeshModule.h" +#include "Throttle.h" /** Update position info for this node based on received position data */ @@ -907,8 +908,10 @@ bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t chann powerFSM.trigger(EVENT_NODEDB_UPDATED); notifyObservers(true); // Force an update whether or not our node counts have changed - // We just changed something important about the user, store our DB - saveToDisk(SEGMENT_DEVICESTATE); + // We just changed something about the user, store our DB + Throttle::execute( + &lastNodeDbSave, ONE_MINUTE_MS, []() { nodeDB->saveToDisk(SEGMENT_DEVICESTATE); }, + []() { LOG_DEBUG("Deferring NodeDB saveToDisk for now, since we saved less than a minute ago\n"); }); } return changed; diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 1c1736f78..8e3784e58 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -150,16 +150,18 @@ class NodeDB void setLocalPosition(meshtastic_Position position, bool timeOnly = false) { if (timeOnly) { - LOG_DEBUG("Setting local position time only: time=%i\n", position.time); + LOG_DEBUG("Setting local position time only: time=%i timestamp=%i\n", position.time, position.timestamp); localPosition.time = position.time; + localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time; return; } - LOG_DEBUG("Setting local position: latitude=%i, longitude=%i, time=%i\n", position.latitude_i, position.longitude_i, - position.time); + LOG_DEBUG("Setting local position: latitude=%i, longitude=%i, time=%i, timeestamp=%i\n", position.latitude_i, + position.longitude_i, position.time, position.timestamp); localPosition = position; } private: + uint32_t lastNodeDbSave = 0; // when we last saved our db to flash /// Find a node in our DB, create an empty NodeInfoLite if missing meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n); @@ -229,4 +231,4 @@ extern uint32_t error_address; ModuleConfig_RangeTestConfig_size + ModuleConfig_SerialConfig_size + ModuleConfig_StoreForwardConfig_size + \ ModuleConfig_TelemetryConfig_size + ModuleConfig_size) -// Please do not remove this comment, it makes trunk and compiler happy at the same time. +// Please do not remove this comment, it makes trunk and compiler happy at the same time. \ No newline at end of file diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index e4d67f019..4189bca66 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -465,21 +465,22 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) cancelSending(p->from, p->id); skipHandle = true; } -#if !MESHTASTIC_EXCLUDE_MQTT - // Publish received message to MQTT if we're not the original transmitter of the packet - if (!skipHandle && moduleConfig.mqtt.enabled && getFrom(p) != nodeDB->getNodeNum() && mqtt) - mqtt->onSend(*p_encrypted, *p, p->channel); -#endif - } else { printPacket("packet decoding failed or skipped (no PSK?)", p); } - packetPool.release(p_encrypted); // Release the encrypted packet - // call modules here - if (!skipHandle) + if (!skipHandle) { MeshModule::callModules(*p, src); + +#if !MESHTASTIC_EXCLUDE_MQTT + // After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet + if (decoded && moduleConfig.mqtt.enabled && getFrom(p) != nodeDB->getNodeNum() && mqtt) + mqtt->onSend(*p_encrypted, *p, p->channel); +#endif + } + + packetPool.release(p_encrypted); // Release the encrypted packet } void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) diff --git a/src/mesh/Throttle.cpp b/src/mesh/Throttle.cpp new file mode 100644 index 000000000..d8f23f9dc --- /dev/null +++ b/src/mesh/Throttle.cpp @@ -0,0 +1,27 @@ +#include "Throttle.h" +#include + +/// @brief Execute a function throttled to a minimum interval +/// @param lastExecutionMs Pointer to the last execution time in milliseconds +/// @param minumumIntervalMs Minimum execution interval in milliseconds +/// @param throttleFunc Function to execute if the execution is not deferred +/// @param onDefer Default to NULL, execute the function if the execution is deferred +/// @return true if the function was executed, false if it was deferred +bool Throttle::execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, void (*throttleFunc)(void), void (*onDefer)(void)) +{ + if (*lastExecutionMs == 0) { + *lastExecutionMs = millis(); + throttleFunc(); + return true; + } + uint32_t now = millis(); + + if ((now - *lastExecutionMs) >= minumumIntervalMs) { + throttleFunc(); + *lastExecutionMs = now; + return true; + } else if (onDefer != NULL) { + onDefer(); + } + return false; +} \ No newline at end of file diff --git a/src/mesh/Throttle.h b/src/mesh/Throttle.h new file mode 100644 index 000000000..8115595a4 --- /dev/null +++ b/src/mesh/Throttle.h @@ -0,0 +1,9 @@ +#pragma once +#include +#include + +class Throttle +{ + public: + static bool execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, void (*func)(void), void (*onDefer)(void) = NULL); +}; \ No newline at end of file diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index fd040c57f..0830ed851 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -283,6 +283,8 @@ typedef struct _meshtastic_Config_DeviceConfig { bool disable_triple_click; /* POSIX Timezone definition string from https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv. */ char tzdef[65]; + /* If true, disable the default blinking LED (LED_PIN) behavior on the device */ + bool led_heartbeat_disabled; } meshtastic_Config_DeviceConfig; /* Position Config */ @@ -324,35 +326,30 @@ typedef struct _meshtastic_Config_PositionConfig { /* Power Config\ See [Power Config](/docs/settings/config/power) for additional power config details. */ typedef struct _meshtastic_Config_PowerConfig { - /* If set, we are powered from a low-current source (i.e. solar), so even if it looks like we have power flowing in - we should try to minimize power consumption as much as possible. - YOU DO NOT NEED TO SET THIS IF YOU'VE set is_router (it is implied in that case). - Advanced Option */ + /* Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio. + Don't use this setting if you want to use your device with the phone apps or are using a device without a user button. + Technical Details: Works for ESP32 devices and NRF52 devices in the Sensor or Tracker roles */ bool is_power_saving; - /* If non-zero, the device will fully power off this many seconds after external power is removed. */ + /* Description: If non-zero, the device will fully power off this many seconds after external power is removed. */ uint32_t on_battery_shutdown_after_secs; /* Ratio of voltage divider for battery pin eg. 3.20 (R1=100k, R2=220k) Overrides the ADC_MULTIPLIER defined in variant for battery voltage calculation. - Should be set to floating point value between 2 and 4 - Fixes issues on Heltec v2 */ + https://meshtastic.org/docs/configuration/radio/power/#adc-multiplier-override + Should be set to floating point value between 2 and 6 */ float adc_multiplier_override; - /* Wait Bluetooth Seconds - The number of seconds for to wait before turning off BLE in No Bluetooth states - 0 for default of 1 minute */ + /* Description: The number of seconds for to wait before turning off BLE in No Bluetooth states + Technical Details: ESP32 Only 0 for default of 1 minute */ uint32_t wait_bluetooth_secs; /* Super Deep Sleep Seconds While in Light Sleep if mesh_sds_timeout_secs is exceeded we will lower into super deep sleep for this value (default 1 year) or a button press 0 for default of one year */ uint32_t sds_secs; - /* Light Sleep Seconds - In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on - ESP32 Only - 0 for default of 300 */ + /* Description: In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on + Technical Details: ESP32 Only 0 for default of 300 */ uint32_t ls_secs; - /* Minimum Wake Seconds - While in light sleep when we receive packets on the LoRa radio we will wake and handle them and stay awake in no BLE mode for this value - 0 for default of 10 seconds */ + /* Description: While in light sleep when we receive packets on the LoRa radio we will wake and handle them and stay awake in no BLE mode for this value + Technical Details: ESP32 Only 0 for default of 10 seconds */ uint32_t min_wake_secs; /* I2C address of INA_2XX to use for reading device battery voltage */ uint8_t device_battery_ina_address; @@ -585,7 +582,7 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}} -#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, ""} +#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""} @@ -594,7 +591,7 @@ extern "C" { #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} -#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, ""} +#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""} @@ -615,6 +612,7 @@ extern "C" { #define meshtastic_Config_DeviceConfig_is_managed_tag 9 #define meshtastic_Config_DeviceConfig_disable_triple_click_tag 10 #define meshtastic_Config_DeviceConfig_tzdef_tag 11 +#define meshtastic_Config_DeviceConfig_led_heartbeat_disabled_tag 12 #define meshtastic_Config_PositionConfig_position_broadcast_secs_tag 1 #define meshtastic_Config_PositionConfig_position_broadcast_smart_enabled_tag 2 #define meshtastic_Config_PositionConfig_fixed_position_tag 3 @@ -715,7 +713,8 @@ X(a, STATIC, SINGULAR, UINT32, node_info_broadcast_secs, 7) \ X(a, STATIC, SINGULAR, BOOL, double_tap_as_button_press, 8) \ X(a, STATIC, SINGULAR, BOOL, is_managed, 9) \ X(a, STATIC, SINGULAR, BOOL, disable_triple_click, 10) \ -X(a, STATIC, SINGULAR, STRING, tzdef, 11) +X(a, STATIC, SINGULAR, STRING, tzdef, 11) \ +X(a, STATIC, SINGULAR, BOOL, led_heartbeat_disabled, 12) #define meshtastic_Config_DeviceConfig_CALLBACK NULL #define meshtastic_Config_DeviceConfig_DEFAULT NULL @@ -834,7 +833,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size #define meshtastic_Config_BluetoothConfig_size 10 -#define meshtastic_Config_DeviceConfig_size 98 +#define meshtastic_Config_DeviceConfig_size 100 #define meshtastic_Config_DisplayConfig_size 28 #define meshtastic_Config_LoRaConfig_size 80 #define meshtastic_Config_NetworkConfig_IpV4Config_size 20 diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 856eb6f4a..2506ec647 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -308,7 +308,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 702 #define meshtastic_NodeInfoLite_size 166 -#define meshtastic_OEMStore_size 3344 +#define meshtastic_OEMStore_size 3346 #define meshtastic_PositionLite_size 28 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index fa7ebcfee..1799f49da 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -181,7 +181,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 535 +#define meshtastic_LocalConfig_size 537 #define meshtastic_LocalModuleConfig_size 663 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index b6d3811a4..e670dd340 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -43,7 +43,9 @@ typedef enum _meshtastic_TelemetrySensorType { /* INA3221 3 Channel Voltage / Current Sensor */ meshtastic_TelemetrySensorType_INA3221 = 14, /* BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) */ - meshtastic_TelemetrySensorType_BMP085 = 15 + meshtastic_TelemetrySensorType_BMP085 = 15, + /* RCWL-9620 Doppler Radar Distance Sensor, used for water level detection */ + meshtastic_TelemetrySensorType_RCWL9620 = 16 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -78,6 +80,8 @@ typedef struct _meshtastic_EnvironmentMetrics { /* relative scale IAQ value as measured by Bosch BME680 . value 0-500. Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. */ uint16_t iaq; + /* RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm. */ + float distance; } meshtastic_EnvironmentMetrics; /* Power Metrics (voltage / current / etc) */ @@ -148,8 +152,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_BMP085 -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_BMP085+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_RCWL9620 +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_RCWL9620+1)) @@ -159,12 +163,12 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0} -#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0} +#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0} #define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0} -#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0} +#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0} #define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}} @@ -182,6 +186,7 @@ extern "C" { #define meshtastic_EnvironmentMetrics_voltage_tag 5 #define meshtastic_EnvironmentMetrics_current_tag 6 #define meshtastic_EnvironmentMetrics_iaq_tag 7 +#define meshtastic_EnvironmentMetrics_distance_tag 8 #define meshtastic_PowerMetrics_ch1_voltage_tag 1 #define meshtastic_PowerMetrics_ch1_current_tag 2 #define meshtastic_PowerMetrics_ch2_voltage_tag 3 @@ -223,7 +228,8 @@ X(a, STATIC, SINGULAR, FLOAT, barometric_pressure, 3) \ X(a, STATIC, SINGULAR, FLOAT, gas_resistance, 4) \ X(a, STATIC, SINGULAR, FLOAT, voltage, 5) \ X(a, STATIC, SINGULAR, FLOAT, current, 6) \ -X(a, STATIC, SINGULAR, UINT32, iaq, 7) +X(a, STATIC, SINGULAR, UINT32, iaq, 7) \ +X(a, STATIC, SINGULAR, FLOAT, distance, 8) #define meshtastic_EnvironmentMetrics_CALLBACK NULL #define meshtastic_EnvironmentMetrics_DEFAULT NULL @@ -283,7 +289,7 @@ extern const pb_msgdesc_t meshtastic_Telemetry_msg; #define MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_MAX_SIZE meshtastic_Telemetry_size #define meshtastic_AirQualityMetrics_size 72 #define meshtastic_DeviceMetrics_size 27 -#define meshtastic_EnvironmentMetrics_size 34 +#define meshtastic_EnvironmentMetrics_size 39 #define meshtastic_PowerMetrics_size 30 #define meshtastic_Telemetry_size 79 diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index 470234047..8c8135deb 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -4,11 +4,8 @@ #include "NodeDB.h" #include "RTC.h" -#define MAX_NUM_NEIGHBORS 10 // also defined in NeighborInfo protobuf options NeighborInfoModule *neighborInfoModule; -static const char *neighborInfoConfigFile = "/prefs/neighbors.proto"; - /* Prints a single neighbor info packet and associated neighbors Uses LOG_DEBUG, which equates to Console.log @@ -30,26 +27,23 @@ NOTE: for debugging only */ void NeighborInfoModule::printNodeDBNeighbors() { - int num_neighbors = getNumNeighbors(); - LOG_DEBUG("Our NodeDB contains %d neighbors\n", num_neighbors); - for (int i = 0; i < num_neighbors; i++) { - const meshtastic_Neighbor *dbEntry = getNeighborByIndex(i); - LOG_DEBUG(" Node %d: node_id=0x%x, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr); + LOG_DEBUG("Our NodeDB contains %d neighbors\n", neighbors.size()); + for (size_t i = 0; i < neighbors.size(); i++) { + LOG_DEBUG("Node %d: node_id=0x%x, snr=%.2f\n", i, neighbors[i].node_id, neighbors[i].snr); } } /* Send our initial owner announcement 35 seconds after we start (to give network time to setup) */ NeighborInfoModule::NeighborInfoModule() : ProtobufModule("neighborinfo", meshtastic_PortNum_NEIGHBORINFO_APP, &meshtastic_NeighborInfo_msg), - concurrency::OSThread("NeighborInfoModule"), neighbors(neighborState.neighbors), - numNeighbors(&neighborState.neighbors_count) + concurrency::OSThread("NeighborInfoModule") { ourPortNum = meshtastic_PortNum_NEIGHBORINFO_APP; if (moduleConfig.neighbor_info.enabled) { isPromiscuous = true; // Update neighbors from all packets - this->loadProtoForModule(); - setIntervalFromNow(35 * 1000); + setIntervalFromNow( + Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_broadcast_interval_secs)); } else { LOG_DEBUG("NeighborInfoModule is disabled\n"); disable(); @@ -63,18 +57,17 @@ Assumes that the neighborInfo packet has been allocated */ uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo) { - uint my_node_id = nodeDB->getNodeNum(); + NodeNum my_node_id = nodeDB->getNodeNum(); neighborInfo->node_id = my_node_id; neighborInfo->last_sent_by_id = my_node_id; neighborInfo->node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval; - int num_neighbors = cleanUpNeighbors(); + cleanUpNeighbors(); - for (int i = 0; i < num_neighbors; i++) { - const meshtastic_Neighbor *dbEntry = getNeighborByIndex(i); - if ((neighborInfo->neighbors_count < MAX_NUM_NEIGHBORS) && (dbEntry->node_id != my_node_id)) { - neighborInfo->neighbors[neighborInfo->neighbors_count].node_id = dbEntry->node_id; - neighborInfo->neighbors[neighborInfo->neighbors_count].snr = dbEntry->snr; + for (auto nbr : neighbors) { + if ((neighborInfo->neighbors_count < MAX_NUM_NEIGHBORS) && (nbr.node_id != my_node_id)) { + neighborInfo->neighbors[neighborInfo->neighbors_count].node_id = nbr.node_id; + neighborInfo->neighbors[neighborInfo->neighbors_count].snr = nbr.snr; // Note: we don't set the last_rx_time and node_broadcast_intervals_secs here, because we don't want to send this over // the mesh neighborInfo->neighbors_count++; @@ -85,41 +78,22 @@ uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighb } /* -Remove neighbors from the database that we haven't heard from in a while -@returns new number of neighbors + Remove neighbors from the database that we haven't heard from in a while */ -size_t NeighborInfoModule::cleanUpNeighbors() +void NeighborInfoModule::cleanUpNeighbors() { uint32_t now = getTime(); - int num_neighbors = getNumNeighbors(); NodeNum my_node_id = nodeDB->getNodeNum(); - - // Find neighbors to remove - std::vector indices_to_remove; - for (int i = 0; i < num_neighbors; i++) { - const meshtastic_Neighbor *dbEntry = getNeighborByIndex(i); + for (auto it = neighbors.rbegin(); it != neighbors.rend();) { // We will remove a neighbor if we haven't heard from them in twice the broadcast interval - if ((now - dbEntry->last_rx_time > dbEntry->node_broadcast_interval_secs * 2) && (dbEntry->node_id != my_node_id)) { - indices_to_remove.push_back(i); + if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) { + LOG_DEBUG("Removing neighbor with node ID 0x%x\n", it->node_id); + it = std::vector::reverse_iterator( + neighbors.erase(std::next(it).base())); // Erase the element and update the iterator + } else { + ++it; } } - - // Update the neighbor list - for (uint i = 0; i < indices_to_remove.size(); i++) { - int index = indices_to_remove[i]; - LOG_DEBUG("Removing neighbor with node ID 0x%x\n", neighbors[index].node_id); - for (int j = index; j < num_neighbors - 1; j++) { - neighbors[j] = neighbors[j + 1]; - } - (*numNeighbors)--; - } - - // Save the neighbor list if we removed any neighbors or neighbors were already updated upon receiving a packet - if (indices_to_remove.size() > 0 || shouldSave) { - saveProtoForModule(); - } - - return *numNeighbors; } /* Send neighbor info to the mesh */ @@ -143,7 +117,9 @@ Will be used for broadcast. int32_t NeighborInfoModule::runOnce() { bool requestReplies = false; - sendNeighborInfo(NODENUM_BROADCAST, requestReplies); + if (airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) { + sendNeighborInfo(NODENUM_BROADCAST, requestReplies); + } return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_broadcast_interval_secs); } @@ -178,10 +154,7 @@ void NeighborInfoModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtas void NeighborInfoModule::resetNeighbors() { - *numNeighbors = 0; - neighborState.neighbors_count = 0; - memset(neighborState.neighbors, 0, sizeof(neighborState.neighbors)); - saveProtoForModule(); + neighbors.clear(); } void NeighborInfoModule::updateNeighbors(const meshtastic_MeshPacket &mp, const meshtastic_NeighborInfo *np) @@ -201,61 +174,36 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen n = nodeDB->getNodeNum(); } // look for one in the existing list - for (int i = 0; i < (*numNeighbors); i++) { - meshtastic_Neighbor *nbr = &neighbors[i]; - if (nbr->node_id == n) { + for (size_t i = 0; i < neighbors.size(); i++) { + if (neighbors[i].node_id == n) { // if found, update it - nbr->snr = snr; - nbr->last_rx_time = getTime(); + neighbors[i].snr = snr; + neighbors[i].last_rx_time = getTime(); // Only if this is the original sender, the broadcast interval corresponds to it if (originalSender == n && node_broadcast_interval_secs != 0) - nbr->node_broadcast_interval_secs = node_broadcast_interval_secs; - return nbr; + neighbors[i].node_broadcast_interval_secs = node_broadcast_interval_secs; + return &neighbors[i]; } } // otherwise, allocate one and assign data to it - // TODO: max memory for the database should take neighbors into account, but currently doesn't - if (*numNeighbors < MAX_NUM_NEIGHBORS) { - (*numNeighbors)++; - } - meshtastic_Neighbor *new_nbr = &neighbors[((*numNeighbors) - 1)]; - new_nbr->node_id = n; - new_nbr->snr = snr; - new_nbr->last_rx_time = getTime(); + + meshtastic_Neighbor new_nbr = meshtastic_Neighbor_init_zero; + new_nbr.node_id = n; + new_nbr.snr = snr; + new_nbr.last_rx_time = getTime(); // Only if this is the original sender, the broadcast interval corresponds to it if (originalSender == n && node_broadcast_interval_secs != 0) - new_nbr->node_broadcast_interval_secs = node_broadcast_interval_secs; + new_nbr.node_broadcast_interval_secs = node_broadcast_interval_secs; else // Assume the same broadcast interval as us for the neighbor if we don't know it - new_nbr->node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval; - shouldSave = true; // Save the new neighbor upon next cleanup - return new_nbr; -} + new_nbr.node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval; -void NeighborInfoModule::loadProtoForModule() -{ - if (nodeDB->loadProto(neighborInfoConfigFile, meshtastic_NeighborInfo_size, sizeof(meshtastic_NeighborInfo), - &meshtastic_NeighborInfo_msg, &neighborState) != LoadFileResult::SUCCESS) { - neighborState = meshtastic_NeighborInfo_init_zero; + if (neighbors.size() < MAX_NUM_NEIGHBORS) { + neighbors.push_back(new_nbr); + } else { + // If we have too many neighbors, replace the oldest one + LOG_WARN("Neighbor DB is full, replacing oldest neighbor\n"); + neighbors.erase(neighbors.begin()); + neighbors.push_back(new_nbr); } -} - -/** - * @brief Save the module config to file. - * - * @return true On success. - * @return false On error. - */ -bool NeighborInfoModule::saveProtoForModule() -{ - bool okay = true; - -#ifdef FS - FS.mkdir("/prefs"); -#endif - - okay &= nodeDB->saveProto(neighborInfoConfigFile, meshtastic_NeighborInfo_size, &meshtastic_NeighborInfo_msg, &neighborState); - if (okay) - shouldSave = false; - - return okay; + return &neighbors.back(); } \ No newline at end of file diff --git a/src/modules/NeighborInfoModule.h b/src/modules/NeighborInfoModule.h index d47004981..496fdece5 100644 --- a/src/modules/NeighborInfoModule.h +++ b/src/modules/NeighborInfoModule.h @@ -1,13 +1,13 @@ #pragma once #include "ProtobufModule.h" +#define MAX_NUM_NEIGHBORS 10 // also defined in NeighborInfo protobuf options /* * Neighborinfo module for sending info on each node's 0-hop neighbors to the mesh */ class NeighborInfoModule : public ProtobufModule, private concurrency::OSThread { - meshtastic_Neighbor *neighbors; - pb_size_t *numNeighbors; + std::vector neighbors; public: /* @@ -18,15 +18,7 @@ class NeighborInfoModule : public ProtobufModule, priva /* Reset neighbor info after clearing nodeDB*/ void resetNeighbors(); - bool saveProtoForModule(); - - private: - bool shouldSave = false; // Whether we should save the neighbor info to flash - protected: - // Note: this holds our local info. - meshtastic_NeighborInfo neighborState; - /* * Called to handle a particular incoming message * @return true if you've guaranteed you've handled this message and no other handlers should be considered for it @@ -40,10 +32,9 @@ class NeighborInfoModule : public ProtobufModule, priva uint32_t collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo); /* - Remove neighbors from the database that we haven't heard from in a while - @returns new number of neighbors + Remove neighbors from the database that we haven't heard from in a while */ - size_t cleanUpNeighbors(); + void cleanUpNeighbors(); /* Allocate a new NeighborInfo packet */ meshtastic_NeighborInfo *allocateNeighborInfoPacket(); @@ -56,22 +47,12 @@ class NeighborInfoModule : public ProtobufModule, priva */ void sendNeighborInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); - size_t getNumNeighbors() { return *numNeighbors; } - - meshtastic_Neighbor *getNeighborByIndex(size_t x) - { - assert(x < *numNeighbors); - return &neighbors[x]; - } - /* update neighbors with subpacket sniffed from network */ void updateNeighbors(const meshtastic_MeshPacket &mp, const meshtastic_NeighborInfo *np); /* update a NeighborInfo packet with our NodeNum as last_sent_by_id */ void alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_NeighborInfo *n) override; - void loadProtoForModule(); - /* Does our periodic broadcast */ int32_t runOnce() override; diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index dcfe03f7f..250daec57 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -17,6 +17,7 @@ extern "C" { #include "mesh/compression/unishox2.h" +#include } PositionModule *positionModule; @@ -63,11 +64,11 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes } // Log packet size and data fields - LOG_INFO("POSITION node=%08x l=%d latI=%d lonI=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d " - "time=%d\n", - getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae, - p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp, - p.time); + LOG_DEBUG("POSITION node=%08x l=%d latI=%d lonI=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d " + "time=%d\n", + getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae, + p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp, + p.time); if (p.time && channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) { struct timeval tv; @@ -222,6 +223,16 @@ meshtastic_MeshPacket *PositionModule::allocAtakPli() return mp; } +void PositionModule::sendOurPosition() +{ + bool requestReplies = currentGeneration != radioGeneration; + currentGeneration = radioGeneration; + + // If we changed channels, ask everyone else for their latest info + LOG_INFO("Sending pos@%x:6 to mesh (wantReplies=%d)\n", localPosition.timestamp, requestReplies); + sendOurPosition(NODENUM_BROADCAST, requestReplies); +} + void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t channel) { // cancel any not yet sent (now stale) position packets @@ -299,12 +310,7 @@ int32_t PositionModule::runOnce() lastGpsLatitude = node->position.latitude_i; lastGpsLongitude = node->position.longitude_i; - // If we changed channels, ask everyone else for their latest info - bool requestReplies = currentGeneration != radioGeneration; - currentGeneration = radioGeneration; - - LOG_INFO("Sending pos@%x:6 to mesh (wantReplies=%d)\n", localPosition.timestamp, requestReplies); - sendOurPosition(NODENUM_BROADCAST, requestReplies); + sendOurPosition(); if (config.device.role == meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND) { sendLostAndFoundText(); } @@ -314,29 +320,23 @@ int32_t PositionModule::runOnce() if (hasValidPosition(node2)) { // The minimum time (in seconds) that would pass before we are able to send a new position packet. - const uint32_t minimumTimeThreshold = - Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30); auto smartPosition = getDistanceTraveledSinceLastSend(node->position); + uint32_t msSinceLastSend = now - lastGpsSend; - if (smartPosition.hasTraveledOverThreshold && msSinceLastSend >= minimumTimeThreshold) { - bool requestReplies = currentGeneration != radioGeneration; - currentGeneration = radioGeneration; + if (smartPosition.hasTraveledOverThreshold && + Throttle::execute( + &lastGpsSend, minimumTimeThreshold, []() { positionModule->sendOurPosition(); }, + []() { LOG_DEBUG("Skipping send smart broadcast due to time throttling\n"); })) { - LOG_INFO("Sending smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, " - "minTimeInterval=%ims)\n", - localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, - msSinceLastSend, minimumTimeThreshold); - sendOurPosition(NODENUM_BROADCAST, requestReplies); + LOG_DEBUG("Sent smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, " + "minTimeInterval=%ims)\n", + localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, + msSinceLastSend, minimumTimeThreshold); // Set the current coords as our last ones, after we've compared distance with current and decided to send lastGpsLatitude = node->position.latitude_i; lastGpsLongitude = node->position.longitude_i; - - /* Update lastGpsSend to now. This means if the device is stationary, then - getPref_position_broadcast_secs will still apply. - */ - lastGpsSend = now; } } } @@ -396,27 +396,21 @@ void PositionModule::handleNewPosition() meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); const meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position // We limit our GPS broadcasts to a max rate - uint32_t now = millis(); - uint32_t msSinceLastSend = now - lastGpsSend; - if (hasValidPosition(node2)) { auto smartPosition = getDistanceTraveledSinceLastSend(node->position); - if (smartPosition.hasTraveledOverThreshold) { - bool requestReplies = currentGeneration != radioGeneration; - currentGeneration = radioGeneration; - - LOG_INFO("Sending smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims)\n", - localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, msSinceLastSend); - sendOurPosition(NODENUM_BROADCAST, requestReplies); + uint32_t msSinceLastSend = millis() - lastGpsSend; + if (smartPosition.hasTraveledOverThreshold && + Throttle::execute( + &lastGpsSend, minimumTimeThreshold, []() { positionModule->sendOurPosition(); }, + []() { LOG_DEBUG("Skipping send smart broadcast due to time throttling\n"); })) { + LOG_DEBUG("Sent smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, " + "minTimeInterval=%ims)\n", + localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, msSinceLastSend, + minimumTimeThreshold); // Set the current coords as our last ones, after we've compared distance with current and decided to send lastGpsLatitude = node->position.latitude_i; lastGpsLongitude = node->position.longitude_i; - - /* Update lastGpsSend to now. This means if the device is stationary, then - getPref_position_broadcast_secs will still apply. - */ - lastGpsSend = now; } } } diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h index 68171ab0e..0e24e3a9e 100644 --- a/src/modules/PositionModule.h +++ b/src/modules/PositionModule.h @@ -1,4 +1,5 @@ #pragma once +#include "Default.h" #include "ProtobufModule.h" #include "concurrency/OSThread.h" @@ -29,7 +30,8 @@ class PositionModule : public ProtobufModule, private concu /** * Send our position into the mesh */ - void sendOurPosition(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false, uint8_t channel = 0); + void sendOurPosition(NodeNum dest, bool wantReplies = false, uint8_t channel = 0); + void sendOurPosition(); void handleNewPosition(); @@ -52,6 +54,9 @@ class PositionModule : public ProtobufModule, private concu meshtastic_MeshPacket *allocAtakPli(); uint32_t precision; void sendLostAndFoundText(); + + const uint32_t minimumTimeThreshold = + Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30); }; struct SmartPosition { diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp index a60065e56..12cddc520 100644 --- a/src/modules/esp32/StoreForwardModule.cpp +++ b/src/modules/esp32/StoreForwardModule.cpp @@ -506,7 +506,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, break; default: - assert(0); // unexpected state + break; // no need to do anything } return true; // There's no need for others to look at this message. } diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index 092aef470..8f7e00461 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -106,20 +106,26 @@ static NimbleBluetoothFromRadioCallback *fromRadioCallbacks; void NimbleBluetooth::shutdown() { + // No measurable power saving for ESP32 during light-sleep(?) +#ifndef ARCH_ESP32 // Shutdown bluetooth for minimum power draw LOG_INFO("Disable bluetooth\n"); - // Bluefruit.Advertising.stop(); NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); pAdvertising->reset(); pAdvertising->stop(); +#endif } -// Extra power-saving on some devices +// Proper shutdown for ESP32. Needs reboot to reverse. void NimbleBluetooth::deinit() { +#ifdef ARCH_ESP32 + LOG_INFO("Disable bluetooth until reboot\n"); NimBLEDevice::deinit(); +#endif } +// Has initial setup been completed bool NimbleBluetooth::isActive() { return bleServer; diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 15e437bb5..27088f86f 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -117,8 +117,8 @@ #define HW_VENDOR meshtastic_HardwareModel_HELTEC_WIRELESS_PAPER #elif defined(TLORA_T3S3_V1) #define HW_VENDOR meshtastic_HardwareModel_TLORA_T3_S3 -#elif defined(CDEBYTE_ELORA_S3) -#define HW_VENDOR meshtastic_HardwareModel_CDEBYTE_ELORA_S3 +#elif defined(CDEBYTE_EORA_S3) +#define HW_VENDOR meshtastic_HardwareModel_CDEBYTE_EORA_S3 #elif defined(BETAFPV_2400_TX) #define HW_VENDOR meshtastic_HardwareModel_BETAFPV_2400_TX #elif defined(NANO_G1_EXPLORER) @@ -127,6 +127,10 @@ #define HW_VENDOR meshtastic_HardwareModel_BETAFPV_900_NANO_TX #elif defined(PICOMPUTER_S3) #define HW_VENDOR meshtastic_HardwareModel_PICOMPUTER_S3 +#elif defined(HELTEC_HT62) +#define HW_VENDOR meshtastic_HardwareModel_HELTEC_HT62 +#elif defined(EBYTE_ESP32_S3) +#define HW_VENDOR meshtastic_HardwareModel_EBYTE_ESP32_S3 #elif defined(ESP32_S3_PICO) #define HW_VENDOR meshtastic_HardwareModel_ESP32_S3_PICO #elif defined(SENSELORA_S3) diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 3fb6e7774..2894a49fc 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -30,9 +30,10 @@ void setBluetoothEnable(bool enable) } if (enable && !nimbleBluetooth->isActive()) { nimbleBluetooth->setup(); - } else if (!enable) { - nimbleBluetooth->shutdown(); } + // For ESP32, no way to recover from bluetooth shutdown without reboot + // BLE advertising automatically stops when MCU enters light-sleep(?) + // For deep-sleep, shutdown hardware with nimbleBluetooth->deinit(). Requires reboot to reverse } } #else diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index f3415aaee..a04c9c12c 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -178,18 +178,31 @@ void portduinoSetup() settingsMap[displayPanel] = st7735; else if (yamlConfig["Display"]["Panel"].as("") == "ST7735S") settingsMap[displayPanel] = st7735s; + else if (yamlConfig["Display"]["Panel"].as("") == "ST7796") + settingsMap[displayPanel] = st7796; else if (yamlConfig["Display"]["Panel"].as("") == "ILI9341") settingsMap[displayPanel] = ili9341; + else if (yamlConfig["Display"]["Panel"].as("") == "ILI9488") + settingsMap[displayPanel] = ili9488; + else if (yamlConfig["Display"]["Panel"].as("") == "HX8357D") + settingsMap[displayPanel] = hx8357d; + else if (yamlConfig["Display"]["Panel"].as("") == "X11") + settingsMap[displayPanel] = x11; settingsMap[displayHeight] = yamlConfig["Display"]["Height"].as(0); settingsMap[displayWidth] = yamlConfig["Display"]["Width"].as(0); settingsMap[displayDC] = yamlConfig["Display"]["DC"].as(-1); settingsMap[displayCS] = yamlConfig["Display"]["CS"].as(-1); + settingsMap[displayRGBOrder] = yamlConfig["Display"]["RGBOrder"].as(false); settingsMap[displayBacklight] = yamlConfig["Display"]["Backlight"].as(-1); + settingsMap[displayBacklightInvert] = yamlConfig["Display"]["BacklightInvert"].as(false); + settingsMap[displayBacklightPWMChannel] = yamlConfig["Display"]["BacklightPWMChannel"].as(-1); settingsMap[displayReset] = yamlConfig["Display"]["Reset"].as(-1); settingsMap[displayOffsetX] = yamlConfig["Display"]["OffsetX"].as(0); settingsMap[displayOffsetY] = yamlConfig["Display"]["OffsetY"].as(0); settingsMap[displayRotate] = yamlConfig["Display"]["Rotate"].as(false); + settingsMap[displayOffsetRotate] = yamlConfig["Display"]["OffsetRotate"].as(1); settingsMap[displayInvert] = yamlConfig["Display"]["Invert"].as(false); + settingsMap[displayBusFrequency] = yamlConfig["Display"]["BusFrequency"].as(40000000); if (yamlConfig["Display"]["spidev"]) { settingsStrings[displayspidev] = "/dev/" + yamlConfig["Display"]["spidev"].as("spidev0.1"); } @@ -200,8 +213,14 @@ void portduinoSetup() settingsMap[touchscreenModule] = xpt2046; else if (yamlConfig["Touchscreen"]["Module"].as("") == "STMPE610") settingsMap[touchscreenModule] = stmpe610; + else if (yamlConfig["Touchscreen"]["Module"].as("") == "GT911") + settingsMap[touchscreenModule] = gt911; + else if (yamlConfig["Touchscreen"]["Module"].as("") == "FT5x06") + settingsMap[touchscreenModule] = ft5x06; settingsMap[touchscreenCS] = yamlConfig["Touchscreen"]["CS"].as(-1); settingsMap[touchscreenIRQ] = yamlConfig["Touchscreen"]["IRQ"].as(-1); + settingsMap[touchscreenBusFrequency] = yamlConfig["Touchscreen"]["BusFrequency"].as(1000000); + settingsMap[touchscreenRotate] = yamlConfig["Touchscreen"]["Rotate"].as(-1); if (yamlConfig["Touchscreen"]["spidev"]) { settingsStrings[touchscreenspidev] = "/dev/" + yamlConfig["Touchscreen"]["spidev"].as(""); } diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 980fc63b8..ed2954eef 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -21,16 +21,23 @@ enum configNames { touchscreenModule, touchscreenCS, touchscreenIRQ, + touchscreenBusFrequency, + touchscreenRotate, touchscreenspidev, displayspidev, + displayBusFrequency, displayPanel, displayWidth, displayHeight, displayCS, displayDC, + displayRGBOrder, displayBacklight, + displayBacklightPWMChannel, + displayBacklightInvert, displayReset, displayRotate, + displayOffsetRotate, displayOffsetX, displayOffsetY, displayInvert, @@ -41,8 +48,8 @@ enum configNames { webserverrootpath, maxnodes }; -enum { no_screen, st7789, st7735, st7735s, ili9341 }; -enum { no_touchscreen, xpt2046, stmpe610 }; +enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9488, hx8357d }; +enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 }; enum { level_error, level_warn, level_info, level_debug }; extern std::map settingsMap; diff --git a/src/sleep.cpp b/src/sleep.cpp index 860a676df..e58c3872a 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -157,6 +157,10 @@ void initDeepSleep() for (uint8_t i = 0; i <= GPIO_NUM_MAX; i++) { if (rtc_gpio_is_valid_gpio((gpio_num_t)i)) rtc_gpio_hold_dis((gpio_num_t)i); + + // ESP32 (original) + else if (GPIO_IS_VALID_OUTPUT_GPIO((gpio_num_t)i)) + gpio_hold_dis((gpio_num_t)i); } } #endif @@ -207,9 +211,10 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) // esp_wifi_stop(); waitEnterSleep(skipPreflight); -#ifdef NIMBLE_DEINIT_FOR_DEEPSLEEP - // Extra power saving on some devices - nimbleBluetooth->deinit(); +#ifdef ARCH_ESP32 + // Full shutdown of bluetooth hardware + if (nimbleBluetooth) + nimbleBluetooth->deinit(); #endif #ifdef ARCH_ESP32 @@ -258,14 +263,17 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) } #ifdef BUTTON_PIN // Avoid leakage through button pin - pinMode(BUTTON_PIN, INPUT); - gpio_hold_en((gpio_num_t)BUTTON_PIN); + if (GPIO_IS_VALID_OUTPUT_GPIO(BUTTON_PIN)) { + pinMode(BUTTON_PIN, INPUT); + gpio_hold_en((gpio_num_t)BUTTON_PIN); + } #endif - - // LoRa CS (RADIO_NSS) needs to stay HIGH, even during deep sleep - pinMode(LORA_CS, OUTPUT); - digitalWrite(LORA_CS, HIGH); - gpio_hold_en((gpio_num_t)LORA_CS); + if (GPIO_IS_VALID_OUTPUT_GPIO(LORA_CS)) { + // LoRa CS (RADIO_NSS) needs to stay HIGH, even during deep sleep + pinMode(LORA_CS, OUTPUT); + digitalWrite(LORA_CS, HIGH); + gpio_hold_en((gpio_num_t)LORA_CS); + } #endif #ifdef HAS_PMU diff --git a/variants/EBYTE_ESP32-S3/pins_arduino.h b/variants/EBYTE_ESP32-S3/pins_arduino.h new file mode 100644 index 000000000..38a9103f0 --- /dev/null +++ b/variants/EBYTE_ESP32-S3/pins_arduino.h @@ -0,0 +1,37 @@ +// Need this file for ESP32-S3 +// No need to modify this file, changes to pins imported from variant.h +// Most is similar to https://github.com/espressif/arduino-esp32/blob/master/variants/esp32s3/pins_arduino.h + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +#define EXTERNAL_NUM_INTERRUPTS 46 +#define NUM_DIGITAL_PINS 48 +#define NUM_ANALOG_INPUTS 20 + +#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) +#define digitalPinToInterrupt(p) \ + (((p) < 48) ? (p) : -1) // Maybe it should be <= 48 but this is from a trustworthy source so it is likely correct +#define digitalPinHasPWM(p) (p < 46) + +// Serial +static const uint8_t TX = UART_TX; +static const uint8_t RX = UART_RX; + +// Default SPI will be mapped to Radio +static const uint8_t SS = LORA_CS; +static const uint8_t SCK = LORA_SCK; +static const uint8_t MOSI = LORA_MOSI; +static const uint8_t MISO = LORA_MISO; + +// The default Wire will be mapped to PMU and RTC +static const uint8_t SCL = I2C_SCL; +static const uint8_t SDA = I2C_SDA; + +#endif /* Pins_Arduino_h */ diff --git a/variants/EBYTE_ESP32-S3/platformio.ini b/variants/EBYTE_ESP32-S3/platformio.ini new file mode 100644 index 000000000..10de91386 --- /dev/null +++ b/variants/EBYTE_ESP32-S3/platformio.ini @@ -0,0 +1,9 @@ +[env:EBYTE_ESP32-S3] +extends = esp32s3_base +; board assumes the lowest spec WROOM module: 4 MB (Quad SPI) Flash, No PSRAM +board = ESP32-S3-WROOM-1-N4 +board_level = extra +build_flags = + ${esp32s3_base.build_flags} + -D EBYTE_ESP32_S3 + -I variants/EBYTE_ESP32-S3 diff --git a/variants/EBYTE_ESP32-S3/variant.h b/variants/EBYTE_ESP32-S3/variant.h new file mode 100644 index 000000000..10b39617b --- /dev/null +++ b/variants/EBYTE_ESP32-S3/variant.h @@ -0,0 +1,193 @@ +// Supporting information: https://github.com/S5NC/EBYTE_ESP32-S3/ + +// Originally developed for E22-900M30S with ESP32-S3-WROOM-1-N4 +// NOTE: Uses ESP32-S3-WROOM-1-N4.json in boards folder (via platformio.ini board field), assumes 4 MB (quad SPI) flash, no PSRAM + +// FIXME: implement SX12 module type autodetection and have setup for each case (add E32 support) +// E32 has same pinout except having extra pins. I assume that the GND on it is connected internally to other GNDs so it is not a +// problem to NC the extra GND pins. + +// For each EBYTE module pin in this section, provide the pin number of the ESP32-S3 you connected it to +// The ESP32-S3 is great because YOU CAN USE PRACTICALLY ANY PINS for the connections, but avoid some pins (such as on the WROOM +// modules the following): strapping pins (except 0 as a user button input as it already has a pulldown resistor in typical +// application schematic) (0, 3, 45, 46), USB-reserved (19, 20), and pins which aren't present on the WROOM-2 module for +// compatiblity as it uses octal SPI, or are likely connected internally in either WROOM version (26-37), and avoid pins whose +// voltages are set by the SPI voltage (47, 48), and pins that don't exist (22-25) You can ALSO set the SPI pins (SX126X_CS, +// SX126X_SCK, SX126X_MISO, SX126X_MOSI) to any pin with the ESP32-S3 due to \ GPIO Matrix / IO MUX / RTC IO MUX \, and also the +// serial pins, but this isn't recommended for Serial0 as the WROOM modules have a 499 Ohm resistor on U0TXD (to reduce harmonics +// but also acting as a sort of protection) + +// We have many free pins on the ESP32-S3-WROOM-X-Y module, perhaps it is best to use one of its pins to control TXEN, and use +// DIO2 as an extra interrupt, but right now Meshtastic does not benefit from having another interrupt pin available. + +// Adding two 0-ohm links on your PCB design so that you can choose between the two modes for controlling the E22's TXEN would +// enable future software to make the most of an extra available interrupt pin + +// Possible improvement: can add extremely low resistance MOSFET to physically toggle power to E22 module when in full sleep (not +// waiting for interrupt)? + +// PA stands for Power Amplifier, used when transmitting to increase output power +// LNA stands for Low Noise Amplifier, used when \ listening for / receiving \ data to increase sensitivity + +////////////////////////////////////////////////////////////////////////////////// +// // +// Have custom connections or functionality? Configure them in this section // +// // +////////////////////////////////////////////////////////////////////////////////// + +#define SX126X_CS 14 // EBYTE module's NSS pin // FIXME: rename to SX126X_SS +#define LORA_SCK 21 // EBYTE module's SCK pin +#define LORA_MOSI 38 // EBYTE module's MOSI pin +#define LORA_MISO 39 // EBYTE module's MISO pin +#define SX126X_RESET 40 // EBYTE module's NRST pin +#define SX126X_BUSY 41 // EBYTE module's BUSY pin +#define SX126X_DIO1 42 // EBYTE module's DIO1 pin +// We don't define a pin for SX126X_DIO2 as Meshtastic doesn't use it as an interrupt output, so it is never connected to an MCU +// pin! Also E22 module datasheets say not to connect it to an MCU pin. +// We don't define a pin for SX126X_DIO3 as Meshtastic doesn't use it as an interrupt output, so it is never connected to an MCU +// pin! Also E22 module datasheets say to use it as the TCXO's reference voltage. +// E32 module (which uses SX1276) may not have ability to set TCXO voltage using a DIO pin. + +// The radio module needs to be told whether to enable RX mode or TX mode. Each radio module takes different actions based on +// these values, but generally the path from the antenna to SX1262 is changed from signal output to signal input. Also, if there +// are LNAs (Low-Noise Amplifiers) or PAs (Power Amplifiers) in the output or input paths, their power is also controlled by +// these pins. You should never have both TXEN and RXEN set high, this can cause problems for some radio modules, and is +// commonly referred to as 'undefined behaviour' in datasheets. For the SX1262, you shouldn't connect DIO2 to the MCU. DIO2 is +// an output only, and can be controlled via SPI instructions, the use for this is to save an MCU pin by using the DIO2 pin to +// control the RF switching mode. + +// Choose ONLY ONE option from below, comment in/out the '/*'s and '*/'s +// SX126X_TXEN is the E22's [SX1262's] TXEN pin, SX126X_RXEN is the E22's [SX1262's] RXEN pin + +// Option 1: E22's TXEN pin connected to E22's DIO2 pin, E22's RXEN pin connected to NEGATED output of E22's DIO2 pin (more +// expensive option hardware-wise, is the 'most proper' way, removes need for routing one/two traces from MCU to RF switching +// pins), however you can't have E22 in low-power 'sleep' mode (TXEN and RXEN both low cannot be achieved this this option). +/* +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_TXEN RADIOLIB_NC +#define SX126X_RXEN RADIOLIB_NC +*/ + +// Option 2: E22's TXEN pin connected to E22's DIO2 pin, E22's RXEN pin connected to MCU pin (cheaper option hardware-wise, +// removes need for routing another trace from MCU to an RF switching pin). +// /* +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_TXEN RADIOLIB_NC +#define SX126X_RXEN 10 +// */ + +// Option 3: E22's TXEN pin connected to MCU pin, E22's RXEN pin connected to MCU pin (cheaper option hardware-wise, allows for +// ramping up PA before transmission (add/expand on feature yourself in RadioLib) if PA takes a while to stabilise) +// Don't define DIO2_AS_RF_SWITCH because we only use DIO2 or an MCU pin mutually exclusively to connect to E22's TXEN (to prevent +// a short if they are both connected at the same time (suboptimal PCB design) and there's a slight non-neglibible delay and/or +// voltage difference between DIO2 and TXEN). Can use DIO2 as an IRQ (but not in Meshtastic at the moment). +/* +#define SX126X_TXEN 9 +#define SX126X_RXEN 10 +*/ + +// (NOT RECOMMENDED, if need to ramp up PA before transmission, better to use option 3) +// Option 4: E22's TXEN pin connected to MCU pin, E22's RXEN pin connected to NEGATED output of E22's DIO2 pin (more expensive +// option hardware-wise, allows for ramping up PA before transmission (add/expand on feature yourself in RadioLib) if PA takes +// a while to stabilise, removes need for routing another trace from MCU to an RF switching pin, however may mean if in +// RadioLib you don't tell DIO2 to go high to indicate transmission (so the negated output goes to RXEN to turn the LNA off) +// then you may end up enabling E22's TXEN and RXEN pins at the same time whilst you ramp up the PA which is not ideal, +// changing DIO2's switching advance in RadioLib may not even be possible, may be baked into the SX126x). +/* +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_TXEN 9 +#define SX126X_RXEN RADIOLIB_NC +*/ + +// Status +#define LED_PIN 1 +#define LED_INVERTED 0 +// External notification +// FIXME: Check if EXT_NOTIFY_OUT actualy has any effect and removes the need for setting the external notication pin in the +// app/preferences +#define EXT_NOTIFY_OUT 2 // The GPIO pin that acts as the external notification output (here we connect an LED to it) +// Buzzer +#define PIN_BUZZER 11 +// Buttons +#define BUTTON_PIN 0 // Use the BOOT button as the user button +// I2C +#define I2C_SCL 18 +#define I2C_SDA 8 +// UART +#define UART_TX 43 +#define UART_RX 44 + +// Power +// Outputting 22dBm from SX1262 results in ~30dBm E22-900M30S output (module only uses last stage of the YP2233W PA) +// Respect local regulations! If your E22-900M30S outputs the advertised 30 dBm and you use a 6 dBi antenna, you are at the +// equivalent of 36 EIRP (Effective Isotropic Radiated Power), which in this case is the limit for non-HAM users in the US (4W +// EIRP, at SPECIFIC frequencies). +// In the EU (and UK), as of now, you are allowed 27 dBm ERP which is 29.15 EIRP. +// https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32022D0180 +// https://www.legislation.gov.uk/uksi/1999/930/schedule/6/made +// To respect the 29.15 dBm EIRP (at SPECIFIC frequencies, others are lower) EU limit with a 2.5 dBi gain antenna, consulting +// https://github.com/S5NC/EBYTE_ESP32-S3/blob/main/power%20testing.txt, assuming 0.1 dBm insertion loss, output 20 dBm from the +// E22-900M30S's SX1262. It is worth noting that if you are in this situation and don't have a HAM license, you may be better off +// with a lower gain antenna, and output the difference as a higher total power input into the antenna, as your EIRP would be the +// same, but you would get a wider angle of coverage. Also take insertion loss and possibly VSWR into account +// (https://www.everythingrf.com/tech-resources/vswr). Please check regulations yourself and check airtime, usage (for example +// whether you are airborne), frequency, and power laws. +#define SX126X_MAX_POWER 22 // SX126xInterface.cpp defaults to 22 if not defined, but here we define it for good practice + +// Display +// FIXME: change behavior in src to default to not having screen if is undefined +// FIXME: remove 0/1 option for HAS_SCREEN in src, change to being defined or not +// FIXME: check if it actually causes a crash when not specifiying that a display isn't present +#define HAS_SCREEN 0 // Assume no screen present by default to prevent crash... + +// GPS +// FIXME: unsure what to define HAS_GPS as if GPS isn't always present +#define HAS_GPS 1 // Don't need to set this to 0 to prevent a crash as it doesn't crash if GPS not found, will probe by default +#define PIN_GPS_EN 15 +#define GPS_EN_ACTIVE 1 +#define GPS_TX_PIN 16 +#define GPS_RX_PIN 17 + +///////////////////////////////////////////////////////////////////////////////// +// // +// You should have no need to modify the code below, nor in pins_arduino.h // +// // +///////////////////////////////////////////////////////////////////////////////// + +#define USE_SX1262 // E22-900M30S, E22-900M22S, and E22-900MM22S (not E220!) use SX1262 +#define USE_SX1268 // E22-400M30S, E22-400M33S, E22-400M22S, and E22-400MM22S use SX1268 + +// The below isn't needed as we directly define SX126X_TXEN and SX126X_RXEN instead of using proxies E22_TXEN and E22_RXEN +/* +// FALLBACK: If somehow E22_TXEN isn't defined or clearly isn't a valid pin number, set it to RADIOLIB_NC to avoid SX126X_TXEN +being defined but having no value #if (!defined(E22_TXEN) || !(0 <= E22_TXEN && E22_TXEN <= 48)) #define E22_TXEN RADIOLIB_NC +#endif +// FALLBACK: If somehow E22_RXEN isn't defined or clearly isn't a valid pin number, set it to RADIOLIB_NC to avoid SX126X_RXEN +being defined but having no value #if (!defined(E22_RXEN) || !(0 <= E22_RXEN && E22_RXEN <= 48)) #define E22_RXEN RADIOLIB_NC +#endif +#define SX126X_TXEN E22_TXEN +#define SX126X_RXEN E22_RXEN +*/ + +// E22 series TCXO voltage is 1.8V per https://www.ebyte.com/en/pdf-down.aspx?id=781 (source +// https://github.com/jgromes/RadioLib/issues/12#issuecomment-520695575), so set it as such +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +#define LORA_CS SX126X_CS // FIXME: for some reason both are used in /src + +// Many of the below values would only be used if USE_RF95 was defined, but it's not as we aren't actually using an RF95, just +// that the 4 pins above are named like it If they aren't used they don't need to be defined and doing so cause confusion to those +// adapting this file LORA_RESET value is never used in src (as we are not using RF95), so no need to define LORA_DIO0 is not used +// in src (as we are not using RF95) as SX1262 does not have it per SX1262 datasheet, so no need to define +// FIXME: confirm that the linked lines below are actually only called when using the SX126x or SX128x and no other modules +// then use SX126X_DIO1 and SX128X_DIO1 respectively for that purpose, removing the need for RF95-style LORA_* definitions when +// the RF95 isn't used +#define LORA_DIO1 \ + SX126X_DIO1 // The old name is used in + // https://github.com/meshtastic/firmware/blob/7eff5e7bcb2084499b723c5e3846c15ee089e36d/src/sleep.cpp#L298, so + // must also define the old name +// LORA_DIO2 value is never used in src (as we are not using RF95), so no need to define, and if DIO2_AS_RF_SWITCH is set then it +// cannot serve any extra function even if requested to LORA_DIO3 value is never used in src (as we are not using RF95), so no +// need to define, and DIO3_AS_TCXO_AT_1V8 is set so it cannot serve any extra function even if requested to (from 13.3.2.1 +// DioxMask in SX1262 datasheet: Note that if DIO2 or DIO3 are used to control the RF Switch or the TCXO, the IRQ will not be +// generated even if it is mapped to the pins.) \ No newline at end of file diff --git a/variants/heltec_wireless_paper/variant.h b/variants/heltec_wireless_paper/variant.h index 466925a2e..29b8bbbd1 100644 --- a/variants/heltec_wireless_paper/variant.h +++ b/variants/heltec_wireless_paper/variant.h @@ -55,6 +55,3 @@ #define SX126X_DIO2_AS_RF_SWITCH #define SX126X_DIO3_TCXO_VOLTAGE 1.8 - -// Power management -#define NIMBLE_DEINIT_FOR_DEEPSLEEP // Required to reach manufacturers claim of 18uA diff --git a/variants/heltec_wireless_paper_v1/variant.h b/variants/heltec_wireless_paper_v1/variant.h index 466925a2e..29b8bbbd1 100644 --- a/variants/heltec_wireless_paper_v1/variant.h +++ b/variants/heltec_wireless_paper_v1/variant.h @@ -55,6 +55,3 @@ #define SX126X_DIO2_AS_RF_SWITCH #define SX126X_DIO3_TCXO_VOLTAGE 1.8 - -// Power management -#define NIMBLE_DEINIT_FOR_DEEPSLEEP // Required to reach manufacturers claim of 18uA diff --git a/version.properties b/version.properties index f5ad818a2..485f55130 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 3 -build = 6 +build = 7