::startReceive()
LOG_ERROR("Radiolib error %d when attempting SX128X startReceive!\n", err);
assert(err == RADIOLIB_ERR_NONE);
- isReceiving = true;
+ RadioLibInterface::startReceive();
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0);
diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp
index 4d04dffe48..9f59aa971c 100644
--- a/src/mesh/StreamAPI.cpp
+++ b/src/mesh/StreamAPI.cpp
@@ -1,5 +1,6 @@
#include "StreamAPI.h"
#include "PowerFSM.h"
+#include "RTC.h"
#include "configuration.h"
#define START1 0x94
@@ -96,7 +97,6 @@ void StreamAPI::writeStream()
void StreamAPI::emitTxBuffer(size_t len)
{
if (len != 0) {
- // LOG_DEBUG("emit tx %d\n", len);
txBuf[0] = START1;
txBuf[1] = START2;
txBuf[2] = (len >> 8) & 0xff;
@@ -119,6 +119,25 @@ void StreamAPI::emitRebooted()
emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch));
}
+void StreamAPI::emitLogRecord(meshtastic_LogRecord_Level level, const char *src, const char *format, va_list arg)
+{
+ // In case we send a FromRadio packet
+ memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
+ fromRadioScratch.which_payload_variant = meshtastic_FromRadio_log_record_tag;
+ fromRadioScratch.log_record.level = level;
+
+ uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
+ fromRadioScratch.log_record.time = rtc_sec;
+ strncpy(fromRadioScratch.log_record.source, src, sizeof(fromRadioScratch.log_record.source) - 1);
+
+ auto num_printed =
+ vsnprintf(fromRadioScratch.log_record.message, sizeof(fromRadioScratch.log_record.message) - 1, format, arg);
+ if (num_printed > 0 && fromRadioScratch.log_record.message[num_printed - 1] ==
+ '\n') // Strip any ending newline, because we have records for framing instead.
+ fromRadioScratch.log_record.message[num_printed - 1] = '\0';
+ emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch));
+}
+
/// Hookable to find out when connection changes
void StreamAPI::onConnectionChanged(bool connected)
{
@@ -131,4 +150,4 @@ void StreamAPI::onConnectionChanged(bool connected)
// received a packet in a while
powerFSM.trigger(EVENT_SERIAL_DISCONNECTED);
}
-}
+}
\ No newline at end of file
diff --git a/src/mesh/StreamAPI.h b/src/mesh/StreamAPI.h
index 3196e96f8b..45cbb231c7 100644
--- a/src/mesh/StreamAPI.h
+++ b/src/mesh/StreamAPI.h
@@ -82,4 +82,7 @@ class StreamAPI : public PhoneAPI
/// Subclasses can use this scratch buffer if they wish
uint8_t txBuf[MAX_STREAM_BUF_SIZE] = {0};
-};
+
+ /// Low level function to emit a protobuf encapsulated log record
+ void emitLogRecord(meshtastic_LogRecord_Level level, const char *src, const char *format, va_list arg);
+};
\ No newline at end of file
diff --git a/src/mesh/compression/unishox2.c b/src/mesh/compression/unishox2.cpp
similarity index 98%
rename from src/mesh/compression/unishox2.c
rename to src/mesh/compression/unishox2.cpp
index 99c62f6590..fcb12a2229 100644
--- a/src/mesh/compression/unishox2.c
+++ b/src/mesh/compression/unishox2.cpp
@@ -15,6 +15,7 @@
*
* @author Arundale Ramanathan
*
+ * Port for Particle (particle.io) / Aruino - Jonathan Greenblatt
*/
/**
* @file unishox2.c
@@ -36,6 +37,14 @@
/// uint8_t is unsigned char
typedef unsigned char uint8_t;
+const char *USX_FREQ_SEQ_DFLT[] = {"\": \"", "\": ", "", "=\"", "\":\"", "://"};
+const char *USX_FREQ_SEQ_TXT[] = {" the ", " and ", "tion", " with", "ing", "ment"};
+const char *USX_FREQ_SEQ_URL[] = {"https://", "www.", ".com", "http://", ".org", ".net"};
+const char *USX_FREQ_SEQ_JSON[] = {"\": \"", "\": ", "\",", "}}}", "\":\"", "}}"};
+const char *USX_FREQ_SEQ_HTML[] = {"", "=\"", "div", "href", "class", ""};
+const char *USX_FREQ_SEQ_XML[] = {"", "=\"", "\">", "" \
- }
-/// Frequently occurring sequences in XML content
-#define USX_FREQ_SEQ_XML \
- (const char *[]) \
- { \
- "", "=\"", "\">", "
#include
+#if HAS_NETWORKING
+
#ifndef DISABLE_NTP
#include
@@ -183,3 +185,5 @@ bool isEthernetAvailable()
return true;
}
}
+
+#endif
\ No newline at end of file
diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h
index 2a209ad0a9..d0e643dffc 100644
--- a/src/mesh/generated/meshtastic/admin.pb.h
+++ b/src/mesh/generated/meshtastic/admin.pb.h
@@ -135,6 +135,8 @@ typedef struct _meshtastic_AdminMessage {
bool enter_dfu_mode_request;
/* Delete the file by the specified path from the device */
char delete_file_request[201];
+ /* Set zero and offset for scale chips */
+ uint32_t set_scale;
/* Set the owner for this node */
meshtastic_User set_owner;
/* Set channels (using the new API).
@@ -238,6 +240,7 @@ extern "C" {
#define meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag 20
#define meshtastic_AdminMessage_enter_dfu_mode_request_tag 21
#define meshtastic_AdminMessage_delete_file_request_tag 22
+#define meshtastic_AdminMessage_set_scale_tag 23
#define meshtastic_AdminMessage_set_owner_tag 32
#define meshtastic_AdminMessage_set_channel_tag 33
#define meshtastic_AdminMessage_set_config_tag 34
@@ -281,6 +284,7 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,get_node_remote_hardware_pin
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,get_node_remote_hardware_pins_response,get_node_remote_hardware_pins_response), 20) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,enter_dfu_mode_request,enter_dfu_mode_request), 21) \
X(a, STATIC, ONEOF, STRING, (payload_variant,delete_file_request,delete_file_request), 22) \
+X(a, STATIC, ONEOF, UINT32, (payload_variant,set_scale,set_scale), 23) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_owner,set_owner), 32) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_channel,set_channel), 33) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_config,set_config), 34) \
diff --git a/src/mesh/generated/meshtastic/apponly.pb.h b/src/mesh/generated/meshtastic/apponly.pb.h
index ba9f90873b..f5bacea52d 100644
--- a/src/mesh/generated/meshtastic/apponly.pb.h
+++ b/src/mesh/generated/meshtastic/apponly.pb.h
@@ -55,7 +55,7 @@ extern const pb_msgdesc_t meshtastic_ChannelSet_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_APPONLY_PB_H_MAX_SIZE meshtastic_ChannelSet_size
-#define meshtastic_ChannelSet_size 674
+#define meshtastic_ChannelSet_size 676
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h
index 781538d11e..27a7c13830 100644
--- a/src/mesh/generated/meshtastic/config.pb.h
+++ b/src/mesh/generated/meshtastic/config.pb.h
@@ -22,7 +22,6 @@ typedef enum _meshtastic_Config_DeviceConfig_Role {
The wifi radio and the oled screen will be put to sleep.
This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. */
meshtastic_Config_DeviceConfig_Role_ROUTER = 2,
- /* Description: Combination of both ROUTER and CLIENT. Not for mobile devices. */
meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT = 3,
/* Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
@@ -304,6 +303,8 @@ typedef struct _meshtastic_Config_DeviceConfig {
char tzdef[65];
/* If true, disable the default blinking LED (LED_PIN) behavior on the device */
bool led_heartbeat_disabled;
+ /* Enable LogRecord messages over Serial for API connections */
+ bool logrecord_serial_enabled;
} meshtastic_Config_DeviceConfig;
/* Position Config */
@@ -372,6 +373,9 @@ typedef struct _meshtastic_Config_PowerConfig {
uint32_t min_wake_secs;
/* I2C address of INA_2XX to use for reading device battery voltage */
uint8_t device_battery_ina_address;
+ /* If non-zero, we want powermon log outputs. With the particular (bitfield) sources enabled.
+ Note: we picked an ID of 32 so that lower more efficient IDs can be used for more frequently used options. */
+ uint64_t powermon_enables;
} meshtastic_Config_PowerConfig;
typedef struct _meshtastic_Config_NetworkConfig_IpV4Config {
@@ -495,6 +499,8 @@ typedef struct _meshtastic_Config_LoRaConfig {
Please respect your local laws and regulations. If you are a HAM, make sure you
enable HAM mode and turn off encryption. */
float override_frequency;
+ /* If true, disable the build-in PA FAN using pin define in RF95_FAN_EN. */
+ bool pa_fan_disabled;
/* For testing it is useful sometimes to force a node to never listen to
particular other nodes (simulating radio out of range). All nodenums listed
in ignore_incoming will have packets they send dropped on receive (by router.cpp) */
@@ -511,6 +517,8 @@ typedef struct _meshtastic_Config_BluetoothConfig {
meshtastic_Config_BluetoothConfig_PairingMode mode;
/* Specified PIN for PairingMode.FixedPin */
uint32_t fixed_pin;
+ /* Enables device (serial style logs) over Bluetooth */
+ bool device_logging_enabled;
} meshtastic_Config_BluetoothConfig;
typedef struct _meshtastic_Config {
@@ -608,23 +616,23 @@ 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, "", 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, 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_PowerConfig_init_default {0, 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, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
-#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_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}, 0}
+#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 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, "", 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, 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_PowerConfig_init_zero {0, 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, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
-#define meshtastic_Config_LoRaConfig_init_zero {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_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
+#define meshtastic_Config_LoRaConfig_init_zero {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}, 0}
+#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_Config_DeviceConfig_role_tag 1
@@ -639,6 +647,7 @@ extern "C" {
#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_DeviceConfig_logrecord_serial_enabled_tag 13
#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
@@ -660,6 +669,7 @@ extern "C" {
#define meshtastic_Config_PowerConfig_ls_secs_tag 7
#define meshtastic_Config_PowerConfig_min_wake_secs_tag 8
#define meshtastic_Config_PowerConfig_device_battery_ina_address_tag 9
+#define meshtastic_Config_PowerConfig_powermon_enables_tag 32
#define meshtastic_Config_NetworkConfig_IpV4Config_ip_tag 1
#define meshtastic_Config_NetworkConfig_IpV4Config_gateway_tag 2
#define meshtastic_Config_NetworkConfig_IpV4Config_subnet_tag 3
@@ -697,11 +707,13 @@ extern "C" {
#define meshtastic_Config_LoRaConfig_override_duty_cycle_tag 12
#define meshtastic_Config_LoRaConfig_sx126x_rx_boosted_gain_tag 13
#define meshtastic_Config_LoRaConfig_override_frequency_tag 14
+#define meshtastic_Config_LoRaConfig_pa_fan_disabled_tag 15
#define meshtastic_Config_LoRaConfig_ignore_incoming_tag 103
#define meshtastic_Config_LoRaConfig_ignore_mqtt_tag 104
#define meshtastic_Config_BluetoothConfig_enabled_tag 1
#define meshtastic_Config_BluetoothConfig_mode_tag 2
#define meshtastic_Config_BluetoothConfig_fixed_pin_tag 3
+#define meshtastic_Config_BluetoothConfig_device_logging_enabled_tag 4
#define meshtastic_Config_device_tag 1
#define meshtastic_Config_position_tag 2
#define meshtastic_Config_power_tag 3
@@ -741,7 +753,8 @@ 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, BOOL, led_heartbeat_disabled, 12)
+X(a, STATIC, SINGULAR, BOOL, led_heartbeat_disabled, 12) \
+X(a, STATIC, SINGULAR, BOOL, logrecord_serial_enabled, 13)
#define meshtastic_Config_DeviceConfig_CALLBACK NULL
#define meshtastic_Config_DeviceConfig_DEFAULT NULL
@@ -770,7 +783,8 @@ X(a, STATIC, SINGULAR, UINT32, wait_bluetooth_secs, 4) \
X(a, STATIC, SINGULAR, UINT32, sds_secs, 6) \
X(a, STATIC, SINGULAR, UINT32, ls_secs, 7) \
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 8) \
-X(a, STATIC, SINGULAR, UINT32, device_battery_ina_address, 9)
+X(a, STATIC, SINGULAR, UINT32, device_battery_ina_address, 9) \
+X(a, STATIC, SINGULAR, UINT64, powermon_enables, 32)
#define meshtastic_Config_PowerConfig_CALLBACK NULL
#define meshtastic_Config_PowerConfig_DEFAULT NULL
@@ -825,6 +839,7 @@ X(a, STATIC, SINGULAR, UINT32, channel_num, 11) \
X(a, STATIC, SINGULAR, BOOL, override_duty_cycle, 12) \
X(a, STATIC, SINGULAR, BOOL, sx126x_rx_boosted_gain, 13) \
X(a, STATIC, SINGULAR, FLOAT, override_frequency, 14) \
+X(a, STATIC, SINGULAR, BOOL, pa_fan_disabled, 15) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104)
#define meshtastic_Config_LoRaConfig_CALLBACK NULL
@@ -833,7 +848,8 @@ X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104)
#define meshtastic_Config_BluetoothConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
X(a, STATIC, SINGULAR, UENUM, mode, 2) \
-X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3)
+X(a, STATIC, SINGULAR, UINT32, fixed_pin, 3) \
+X(a, STATIC, SINGULAR, BOOL, device_logging_enabled, 4)
#define meshtastic_Config_BluetoothConfig_CALLBACK NULL
#define meshtastic_Config_BluetoothConfig_DEFAULT NULL
@@ -860,14 +876,14 @@ 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 100
+#define meshtastic_Config_BluetoothConfig_size 12
+#define meshtastic_Config_DeviceConfig_size 102
#define meshtastic_Config_DisplayConfig_size 30
-#define meshtastic_Config_LoRaConfig_size 80
+#define meshtastic_Config_LoRaConfig_size 82
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
#define meshtastic_Config_NetworkConfig_size 196
#define meshtastic_Config_PositionConfig_size 62
-#define meshtastic_Config_PowerConfig_size 40
+#define meshtastic_Config_PowerConfig_size 52
#define meshtastic_Config_size 199
#ifdef __cplusplus
diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h
index 0e3e28ba1b..fb6ab1bf28 100644
--- a/src/mesh/generated/meshtastic/deviceonly.pb.h
+++ b/src/mesh/generated/meshtastic/deviceonly.pb.h
@@ -8,7 +8,6 @@
#include "meshtastic/channel.pb.h"
#include "meshtastic/localonly.pb.h"
#include "meshtastic/mesh.pb.h"
-#include "meshtastic/module_config.pb.h"
#include "meshtastic/telemetry.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -308,7 +307,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg;
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size
#define meshtastic_ChannelFile_size 718
#define meshtastic_NodeInfoLite_size 166
-#define meshtastic_OEMStore_size 3370
+#define meshtastic_OEMStore_size 3390
#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 160202d9bd..9eff147541 100644
--- a/src/mesh/generated/meshtastic/localonly.pb.h
+++ b/src/mesh/generated/meshtastic/localonly.pb.h
@@ -181,8 +181,8 @@ 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 539
-#define meshtastic_LocalModuleConfig_size 685
+#define meshtastic_LocalConfig_size 557
+#define meshtastic_LocalModuleConfig_size 687
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/mesh/generated/meshtastic/mesh.pb.cpp b/src/mesh/generated/meshtastic/mesh.pb.cpp
index 46d59d6094..3fa81e1312 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.cpp
+++ b/src/mesh/generated/meshtastic/mesh.pb.cpp
@@ -36,7 +36,7 @@ PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, AUTO)
PB_BIND(meshtastic_MyNodeInfo, meshtastic_MyNodeInfo, AUTO)
-PB_BIND(meshtastic_LogRecord, meshtastic_LogRecord, AUTO)
+PB_BIND(meshtastic_LogRecord, meshtastic_LogRecord, 2)
PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
@@ -45,6 +45,9 @@ PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
PB_BIND(meshtastic_FromRadio, meshtastic_FromRadio, 2)
+PB_BIND(meshtastic_FileInfo, meshtastic_FileInfo, AUTO)
+
+
PB_BIND(meshtastic_ToRadio, meshtastic_ToRadio, 2)
diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h
index ad97cb80f8..ca860aed53 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.h
+++ b/src/mesh/generated/meshtastic/mesh.pb.h
@@ -67,6 +67,10 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_WIPHONE = 20,
/* WIO Tracker WM1110 family from Seeed Studio. Includes wio-1110-tracker and wio-1110-sdk */
meshtastic_HardwareModel_WIO_WM1110 = 21,
+ /* RAK2560 Solar base station based on RAK4630 */
+ meshtastic_HardwareModel_RAK2560 = 22,
+ /* Heltec HRU-3601: https://heltec.org/project/hru-3601/ */
+ meshtastic_HardwareModel_HELTEC_HRU_3601 = 23,
/* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */
meshtastic_HardwareModel_STATION_G1 = 25,
/* RAK11310 (RP2040 + SX1262) */
@@ -161,6 +165,21 @@ typedef enum _meshtastic_HardwareModel {
/* RadioMaster 900 Bandit Nano, https://www.radiomasterrc.com/products/bandit-nano-expresslrs-rf-module
ESP32-D0WDQ6 With SX1276/SKY66122, SSD1306 OLED and No GPS */
meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO = 64,
+ /* Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors */
+ meshtastic_HardwareModel_HELTEC_CAPSULE_SENSOR_V3 = 65,
+ /* Heltec Vision Master T190 with ESP32-S3 CPU, and a 1.90 inch TFT display */
+ meshtastic_HardwareModel_HELTEC_VISION_MASTER_T190 = 66,
+ /* Heltec Vision Master E213 with ESP32-S3 CPU, and a 2.13 inch E-Ink display */
+ meshtastic_HardwareModel_HELTEC_VISION_MASTER_E213 = 67,
+ /* Heltec Vision Master E290 with ESP32-S3 CPU, and a 2.9 inch E-Ink display */
+ meshtastic_HardwareModel_HELTEC_VISION_MASTER_E290 = 68,
+ /* Heltec Mesh Node T114 board with nRF52840 CPU, and a 1.14 inch TFT display, Ultimate low-power design,
+ specifically adapted for the Meshtatic project */
+ meshtastic_HardwareModel_HELTEC_MESH_NODE_T114 = 69,
+ /* Sensecap Indicator from Seeed Studio. ESP32-S3 device with TFT and RP2040 coprocessor */
+ meshtastic_HardwareModel_SENSECAP_INDICATOR = 70,
+ /* Seeed studio T1000-E tracker card. NRF52840 w/ LR1110 radio, GPS, button, buzzer, and sensors. */
+ meshtastic_HardwareModel_TRACKER_T1000_E = 71,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -685,11 +704,11 @@ typedef struct _meshtastic_MyNodeInfo {
and then extend as needed by emitting multiple records. */
typedef struct _meshtastic_LogRecord {
/* Log levels, chosen to match python logging conventions. */
- char message[64];
+ char message[384];
/* Seconds since 1970 - or 0 for unknown/unset */
uint32_t time;
/* Usually based on thread name - if known */
- char source[8];
+ char source[32];
/* Not yet set */
meshtastic_LogRecord_Level level;
} meshtastic_LogRecord;
@@ -705,6 +724,14 @@ typedef struct _meshtastic_QueueStatus {
uint32_t mesh_packet_id;
} meshtastic_QueueStatus;
+/* Individual File info for the device */
+typedef struct _meshtastic_FileInfo {
+ /* The fully qualified path of the file */
+ char file_name[228];
+ /* The size of the file in bytes */
+ uint32_t size_bytes;
+} meshtastic_FileInfo;
+
typedef PB_BYTES_ARRAY_T(237) meshtastic_Compressed_data_t;
/* Compressed message payload */
typedef struct _meshtastic_Compressed {
@@ -809,6 +836,8 @@ typedef struct _meshtastic_FromRadio {
meshtastic_DeviceMetadata metadata;
/* MQTT Client Proxy Message (device sending to client / phone for publishing to MQTT) */
meshtastic_MqttClientProxyMessage mqttClientProxyMessage;
+ /* File system manifest messages */
+ meshtastic_FileInfo fileInfo;
};
} meshtastic_FromRadio;
@@ -952,6 +981,7 @@ extern "C" {
+
#define meshtastic_Compressed_portnum_ENUMTYPE meshtastic_PortNum
@@ -979,6 +1009,7 @@ extern "C" {
#define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_default {0, 0, 0, 0}
#define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}}
+#define meshtastic_FileInfo_init_default {"", 0}
#define meshtastic_ToRadio_init_default {0, {meshtastic_MeshPacket_init_default}}
#define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}}
#define meshtastic_NeighborInfo_init_default {0, 0, 0, 0, {meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default}}
@@ -1002,6 +1033,7 @@ extern "C" {
#define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_zero {0, 0, 0, 0}
#define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}}
+#define meshtastic_FileInfo_init_zero {"", 0}
#define meshtastic_ToRadio_init_zero {0, {meshtastic_MeshPacket_init_zero}}
#define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}}
#define meshtastic_NeighborInfo_init_zero {0, 0, 0, 0, {meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero}}
@@ -1104,6 +1136,8 @@ extern "C" {
#define meshtastic_QueueStatus_free_tag 2
#define meshtastic_QueueStatus_maxlen_tag 3
#define meshtastic_QueueStatus_mesh_packet_id_tag 4
+#define meshtastic_FileInfo_file_name_tag 1
+#define meshtastic_FileInfo_size_bytes_tag 2
#define meshtastic_Compressed_portnum_tag 1
#define meshtastic_Compressed_data_tag 2
#define meshtastic_Neighbor_node_id_tag 1
@@ -1138,6 +1172,7 @@ extern "C" {
#define meshtastic_FromRadio_xmodemPacket_tag 12
#define meshtastic_FromRadio_metadata_tag 13
#define meshtastic_FromRadio_mqttClientProxyMessage_tag 14
+#define meshtastic_FromRadio_fileInfo_tag 15
#define meshtastic_ToRadio_packet_tag 1
#define meshtastic_ToRadio_want_config_id_tag 3
#define meshtastic_ToRadio_disconnect_tag 4
@@ -1315,7 +1350,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,channel,channel), 10) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 11) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 12) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \
-X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14)
+X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14) \
+X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15)
#define meshtastic_FromRadio_CALLBACK NULL
#define meshtastic_FromRadio_DEFAULT NULL
#define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket
@@ -1329,6 +1365,13 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttC
#define meshtastic_FromRadio_payload_variant_xmodemPacket_MSGTYPE meshtastic_XModem
#define meshtastic_FromRadio_payload_variant_metadata_MSGTYPE meshtastic_DeviceMetadata
#define meshtastic_FromRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage
+#define meshtastic_FromRadio_payload_variant_fileInfo_MSGTYPE meshtastic_FileInfo
+
+#define meshtastic_FileInfo_FIELDLIST(X, a) \
+X(a, STATIC, SINGULAR, STRING, file_name, 1) \
+X(a, STATIC, SINGULAR, UINT32, size_bytes, 2)
+#define meshtastic_FileInfo_CALLBACK NULL
+#define meshtastic_FileInfo_DEFAULT NULL
#define meshtastic_ToRadio_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 1) \
@@ -1428,6 +1471,7 @@ extern const pb_msgdesc_t meshtastic_MyNodeInfo_msg;
extern const pb_msgdesc_t meshtastic_LogRecord_msg;
extern const pb_msgdesc_t meshtastic_QueueStatus_msg;
extern const pb_msgdesc_t meshtastic_FromRadio_msg;
+extern const pb_msgdesc_t meshtastic_FileInfo_msg;
extern const pb_msgdesc_t meshtastic_ToRadio_msg;
extern const pb_msgdesc_t meshtastic_Compressed_msg;
extern const pb_msgdesc_t meshtastic_NeighborInfo_msg;
@@ -1453,6 +1497,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_LogRecord_fields &meshtastic_LogRecord_msg
#define meshtastic_QueueStatus_fields &meshtastic_QueueStatus_msg
#define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg
+#define meshtastic_FileInfo_fields &meshtastic_FileInfo_msg
#define meshtastic_ToRadio_fields &meshtastic_ToRadio_msg
#define meshtastic_Compressed_fields &meshtastic_Compressed_msg
#define meshtastic_NeighborInfo_fields &meshtastic_NeighborInfo_msg
@@ -1472,9 +1517,10 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_Compressed_size 243
#define meshtastic_Data_size 270
#define meshtastic_DeviceMetadata_size 46
+#define meshtastic_FileInfo_size 236
#define meshtastic_FromRadio_size 510
#define meshtastic_Heartbeat_size 0
-#define meshtastic_LogRecord_size 81
+#define meshtastic_LogRecord_size 426
#define meshtastic_MeshPacket_size 326
#define meshtastic_MqttClientProxyMessage_size 501
#define meshtastic_MyNodeInfo_size 18
diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h
index f3c48ee6df..7fd57fe006 100644
--- a/src/mesh/generated/meshtastic/module_config.pb.h
+++ b/src/mesh/generated/meshtastic/module_config.pb.h
@@ -60,7 +60,9 @@ typedef enum _meshtastic_ModuleConfig_SerialConfig_Serial_Mode {
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG = 3,
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA = 4,
/* NMEA messages specifically tailored for CalTopo */
- meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO = 5
+ meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO = 5,
+ /* Ecowitt WS85 weather station */
+ meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85 = 6
} meshtastic_ModuleConfig_SerialConfig_Serial_Mode;
/* TODO: REPLACE */
@@ -273,6 +275,8 @@ typedef struct _meshtastic_ModuleConfig_StoreForwardConfig {
uint32_t history_return_max;
/* TODO: REPLACE */
uint32_t history_return_window;
+ /* Set to true to let this node act as a server that stores received messages and resends them upon request. */
+ bool is_server;
} meshtastic_ModuleConfig_StoreForwardConfig;
/* Preferences for the RangeTestModule */
@@ -432,8 +436,8 @@ extern "C" {
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Baud)(meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600+1))
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT
-#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MAX meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO
-#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Mode)(meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO+1))
+#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MAX meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85
+#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Mode)(meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85+1))
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MAX meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK
@@ -474,7 +478,7 @@ extern "C" {
#define meshtastic_ModuleConfig_PaxcounterConfig_init_default {0, 0, 0, 0}
#define meshtastic_ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0}
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
-#define meshtastic_ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0}
+#define meshtastic_ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_RangeTestConfig_init_default {0, 0, 0}
#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
@@ -490,7 +494,7 @@ extern "C" {
#define meshtastic_ModuleConfig_PaxcounterConfig_init_zero {0, 0, 0, 0}
#define meshtastic_ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0}
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
-#define meshtastic_ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0}
+#define meshtastic_ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_RangeTestConfig_init_zero {0, 0, 0}
#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
@@ -560,6 +564,7 @@ extern "C" {
#define meshtastic_ModuleConfig_StoreForwardConfig_records_tag 3
#define meshtastic_ModuleConfig_StoreForwardConfig_history_return_max_tag 4
#define meshtastic_ModuleConfig_StoreForwardConfig_history_return_window_tag 5
+#define meshtastic_ModuleConfig_StoreForwardConfig_is_server_tag 6
#define meshtastic_ModuleConfig_RangeTestConfig_enabled_tag 1
#define meshtastic_ModuleConfig_RangeTestConfig_sender_tag 2
#define meshtastic_ModuleConfig_RangeTestConfig_save_tag 3
@@ -743,7 +748,8 @@ X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
X(a, STATIC, SINGULAR, BOOL, heartbeat, 2) \
X(a, STATIC, SINGULAR, UINT32, records, 3) \
X(a, STATIC, SINGULAR, UINT32, history_return_max, 4) \
-X(a, STATIC, SINGULAR, UINT32, history_return_window, 5)
+X(a, STATIC, SINGULAR, UINT32, history_return_window, 5) \
+X(a, STATIC, SINGULAR, BOOL, is_server, 6)
#define meshtastic_ModuleConfig_StoreForwardConfig_CALLBACK NULL
#define meshtastic_ModuleConfig_StoreForwardConfig_DEFAULT NULL
@@ -848,7 +854,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
#define meshtastic_ModuleConfig_RangeTestConfig_size 10
#define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96
#define meshtastic_ModuleConfig_SerialConfig_size 28
-#define meshtastic_ModuleConfig_StoreForwardConfig_size 22
+#define meshtastic_ModuleConfig_StoreForwardConfig_size 24
#define meshtastic_ModuleConfig_TelemetryConfig_size 36
#define meshtastic_ModuleConfig_size 257
#define meshtastic_RemoteHardwarePin_size 21
diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h
index 233e8d6534..6cc82352ab 100644
--- a/src/mesh/generated/meshtastic/portnums.pb.h
+++ b/src/mesh/generated/meshtastic/portnums.pb.h
@@ -124,6 +124,8 @@ typedef enum _meshtastic_PortNum {
meshtastic_PortNum_ATAK_PLUGIN = 72,
/* Provides unencrypted information about a node for consumption by a map via MQTT */
meshtastic_PortNum_MAP_REPORT_APP = 73,
+ /* PowerStress based monitoring support (for automated power consumption testing) */
+ meshtastic_PortNum_POWERSTRESS_APP = 74,
/* Private applications should use portnums >= 256.
To simplify initial development and testing you can use "PRIVATE_APP"
in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) */
diff --git a/src/mesh/generated/meshtastic/powermon.pb.cpp b/src/mesh/generated/meshtastic/powermon.pb.cpp
new file mode 100644
index 0000000000..ce41ea0217
--- /dev/null
+++ b/src/mesh/generated/meshtastic/powermon.pb.cpp
@@ -0,0 +1,17 @@
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.4.8 */
+
+#include "meshtastic/powermon.pb.h"
+#if PB_PROTO_HEADER_VERSION != 40
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+PB_BIND(meshtastic_PowerMon, meshtastic_PowerMon, AUTO)
+
+
+PB_BIND(meshtastic_PowerStressMessage, meshtastic_PowerStressMessage, AUTO)
+
+
+
+
+
diff --git a/src/mesh/generated/meshtastic/powermon.pb.h b/src/mesh/generated/meshtastic/powermon.pb.h
new file mode 100644
index 0000000000..7de0618e9b
--- /dev/null
+++ b/src/mesh/generated/meshtastic/powermon.pb.h
@@ -0,0 +1,138 @@
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.4.8 */
+
+#ifndef PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
+#define PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
+#include
+
+#if PB_PROTO_HEADER_VERSION != 40
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+/* Enum definitions */
+/* Any significant power changing event in meshtastic should be tagged with a powermon state transition.
+If you are making new meshtastic features feel free to add new entries at the end of this definition. */
+typedef enum _meshtastic_PowerMon_State {
+ meshtastic_PowerMon_State_None = 0,
+ meshtastic_PowerMon_State_CPU_DeepSleep = 1,
+ meshtastic_PowerMon_State_CPU_LightSleep = 2,
+ /* The external Vext1 power is on. Many boards have auxillary power rails that the CPU turns on only
+occasionally. In cases where that rail has multiple devices on it we usually want to have logging on
+the state of that rail as an independent record.
+For instance on the Heltec Tracker 1.1 board, this rail is the power source for the GPS and screen.
+
+The log messages will be short and complete (see PowerMon.Event in the protobufs for details).
+something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of all current states.
+(We use a bitmask for states so that if a log message gets lost it won't be fatal) */
+ meshtastic_PowerMon_State_Vext1_On = 4,
+ meshtastic_PowerMon_State_Lora_RXOn = 8,
+ meshtastic_PowerMon_State_Lora_TXOn = 16,
+ meshtastic_PowerMon_State_Lora_RXActive = 32,
+ meshtastic_PowerMon_State_BT_On = 64,
+ meshtastic_PowerMon_State_LED_On = 128,
+ meshtastic_PowerMon_State_Screen_On = 256,
+ meshtastic_PowerMon_State_Screen_Drawing = 512,
+ meshtastic_PowerMon_State_Wifi_On = 1024,
+ /* GPS is actively trying to find our location
+See GPSPowerState for more details */
+ meshtastic_PowerMon_State_GPS_Active = 2048
+} meshtastic_PowerMon_State;
+
+/* What operation would we like the UUT to perform.
+note: senders should probably set want_response in their request packets, so that they can know when the state
+machine has started processing their request */
+typedef enum _meshtastic_PowerStressMessage_Opcode {
+ /* Unset/unused */
+ meshtastic_PowerStressMessage_Opcode_UNSET = 0,
+ meshtastic_PowerStressMessage_Opcode_PRINT_INFO = 1, /* Print board version slog and send an ack that we are alive and ready to process commands */
+ meshtastic_PowerStressMessage_Opcode_FORCE_QUIET = 2, /* Try to turn off all automatic processing of packets, screen, sleeping, etc (to make it easier to measure in isolation) */
+ meshtastic_PowerStressMessage_Opcode_END_QUIET = 3, /* Stop powerstress processing - probably by just rebooting the board */
+ meshtastic_PowerStressMessage_Opcode_SCREEN_ON = 16, /* Turn the screen on */
+ meshtastic_PowerStressMessage_Opcode_SCREEN_OFF = 17, /* Turn the screen off */
+ meshtastic_PowerStressMessage_Opcode_CPU_IDLE = 32, /* Let the CPU run but we assume mostly idling for num_seconds */
+ meshtastic_PowerStressMessage_Opcode_CPU_DEEPSLEEP = 33, /* Force deep sleep for FIXME seconds */
+ meshtastic_PowerStressMessage_Opcode_CPU_FULLON = 34, /* Spin the CPU as fast as possible for num_seconds */
+ meshtastic_PowerStressMessage_Opcode_LED_ON = 48, /* Turn the LED on for num_seconds (and leave it on - for baseline power measurement purposes) */
+ meshtastic_PowerStressMessage_Opcode_LED_OFF = 49, /* Force the LED off for num_seconds */
+ meshtastic_PowerStressMessage_Opcode_LORA_OFF = 64, /* Completely turn off the LORA radio for num_seconds */
+ meshtastic_PowerStressMessage_Opcode_LORA_TX = 65, /* Send Lora packets for num_seconds */
+ meshtastic_PowerStressMessage_Opcode_LORA_RX = 66, /* Receive Lora packets for num_seconds (node will be mostly just listening, unless an external agent is helping stress this by sending packets on the current channel) */
+ meshtastic_PowerStressMessage_Opcode_BT_OFF = 80, /* Turn off the BT radio for num_seconds */
+ meshtastic_PowerStressMessage_Opcode_BT_ON = 81, /* Turn on the BT radio for num_seconds */
+ meshtastic_PowerStressMessage_Opcode_WIFI_OFF = 96, /* Turn off the WIFI radio for num_seconds */
+ meshtastic_PowerStressMessage_Opcode_WIFI_ON = 97, /* Turn on the WIFI radio for num_seconds */
+ meshtastic_PowerStressMessage_Opcode_GPS_OFF = 112, /* Turn off the GPS radio for num_seconds */
+ meshtastic_PowerStressMessage_Opcode_GPS_ON = 113 /* Turn on the GPS radio for num_seconds */
+} meshtastic_PowerStressMessage_Opcode;
+
+/* Struct definitions */
+/* Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
+But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */
+typedef struct _meshtastic_PowerMon {
+ char dummy_field;
+} meshtastic_PowerMon;
+
+/* PowerStress testing support via the C++ PowerStress module */
+typedef struct _meshtastic_PowerStressMessage {
+ /* What type of HardwareMessage is this? */
+ meshtastic_PowerStressMessage_Opcode cmd;
+ float num_seconds;
+} meshtastic_PowerStressMessage;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Helper constants for enums */
+#define _meshtastic_PowerMon_State_MIN meshtastic_PowerMon_State_None
+#define _meshtastic_PowerMon_State_MAX meshtastic_PowerMon_State_GPS_Active
+#define _meshtastic_PowerMon_State_ARRAYSIZE ((meshtastic_PowerMon_State)(meshtastic_PowerMon_State_GPS_Active+1))
+
+#define _meshtastic_PowerStressMessage_Opcode_MIN meshtastic_PowerStressMessage_Opcode_UNSET
+#define _meshtastic_PowerStressMessage_Opcode_MAX meshtastic_PowerStressMessage_Opcode_GPS_ON
+#define _meshtastic_PowerStressMessage_Opcode_ARRAYSIZE ((meshtastic_PowerStressMessage_Opcode)(meshtastic_PowerStressMessage_Opcode_GPS_ON+1))
+
+
+#define meshtastic_PowerStressMessage_cmd_ENUMTYPE meshtastic_PowerStressMessage_Opcode
+
+
+/* Initializer values for message structs */
+#define meshtastic_PowerMon_init_default {0}
+#define meshtastic_PowerStressMessage_init_default {_meshtastic_PowerStressMessage_Opcode_MIN, 0}
+#define meshtastic_PowerMon_init_zero {0}
+#define meshtastic_PowerStressMessage_init_zero {_meshtastic_PowerStressMessage_Opcode_MIN, 0}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define meshtastic_PowerStressMessage_cmd_tag 1
+#define meshtastic_PowerStressMessage_num_seconds_tag 2
+
+/* Struct field encoding specification for nanopb */
+#define meshtastic_PowerMon_FIELDLIST(X, a) \
+
+#define meshtastic_PowerMon_CALLBACK NULL
+#define meshtastic_PowerMon_DEFAULT NULL
+
+#define meshtastic_PowerStressMessage_FIELDLIST(X, a) \
+X(a, STATIC, SINGULAR, UENUM, cmd, 1) \
+X(a, STATIC, SINGULAR, FLOAT, num_seconds, 2)
+#define meshtastic_PowerStressMessage_CALLBACK NULL
+#define meshtastic_PowerStressMessage_DEFAULT NULL
+
+extern const pb_msgdesc_t meshtastic_PowerMon_msg;
+extern const pb_msgdesc_t meshtastic_PowerStressMessage_msg;
+
+/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
+#define meshtastic_PowerMon_fields &meshtastic_PowerMon_msg
+#define meshtastic_PowerStressMessage_fields &meshtastic_PowerStressMessage_msg
+
+/* Maximum encoded size of messages (where known) */
+#define MESHTASTIC_MESHTASTIC_POWERMON_PB_H_MAX_SIZE meshtastic_PowerStressMessage_size
+#define meshtastic_PowerMon_size 0
+#define meshtastic_PowerStressMessage_size 7
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.cpp b/src/mesh/generated/meshtastic/telemetry.pb.cpp
index 6388e37a0a..c93483a152 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.cpp
+++ b/src/mesh/generated/meshtastic/telemetry.pb.cpp
@@ -21,5 +21,8 @@ PB_BIND(meshtastic_AirQualityMetrics, meshtastic_AirQualityMetrics, AUTO)
PB_BIND(meshtastic_Telemetry, meshtastic_Telemetry, AUTO)
+PB_BIND(meshtastic_Nau7802Config, meshtastic_Nau7802Config, AUTO)
+
+
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h
index 02b0bdd6dd..82cd0a55d9 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.h
+++ b/src/mesh/generated/meshtastic/telemetry.pb.h
@@ -61,7 +61,9 @@ typedef enum _meshtastic_TelemetrySensorType {
/* AHT10 Integrated temperature and humidity sensor */
meshtastic_TelemetrySensorType_AHT10 = 23,
/* DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) */
- meshtastic_TelemetrySensorType_DFROBOT_LARK = 24
+ meshtastic_TelemetrySensorType_DFROBOT_LARK = 24,
+ /* NAU7802 Scale Chip or compatible */
+ meshtastic_TelemetrySensorType_NAU7802 = 25
} meshtastic_TelemetrySensorType;
/* Struct definitions */
@@ -111,6 +113,12 @@ typedef struct _meshtastic_EnvironmentMetrics {
uint16_t wind_direction;
/* Wind speed in m/s */
float wind_speed;
+ /* Weight in KG */
+ float weight;
+ /* Wind gust in m/s */
+ float wind_gust;
+ /* Wind lull in m/s */
+ float wind_lull;
} meshtastic_EnvironmentMetrics;
/* Power Metrics (voltage / current / etc) */
@@ -174,6 +182,14 @@ typedef struct _meshtastic_Telemetry {
} variant;
} meshtastic_Telemetry;
+/* NAU7802 Telemetry configuration, for saving to flash */
+typedef struct _meshtastic_Nau7802Config {
+ /* The offset setting for the NAU7802 */
+ int32_t zeroOffset;
+ /* The calibration factor for the NAU7802 */
+ float calibrationFactor;
+} meshtastic_Nau7802Config;
+
#ifdef __cplusplus
extern "C" {
@@ -181,8 +197,9 @@ extern "C" {
/* Helper constants for enums */
#define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET
-#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_DFROBOT_LARK
-#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_DFROBOT_LARK+1))
+#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_NAU7802
+#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_NAU7802+1))
+
@@ -192,15 +209,17 @@ 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, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 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_Nau7802Config_init_default {0, 0}
#define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0}
-#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 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}}
+#define meshtastic_Nau7802Config_init_zero {0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_DeviceMetrics_battery_level_tag 1
@@ -222,6 +241,9 @@ extern "C" {
#define meshtastic_EnvironmentMetrics_uv_lux_tag 12
#define meshtastic_EnvironmentMetrics_wind_direction_tag 13
#define meshtastic_EnvironmentMetrics_wind_speed_tag 14
+#define meshtastic_EnvironmentMetrics_weight_tag 15
+#define meshtastic_EnvironmentMetrics_wind_gust_tag 16
+#define meshtastic_EnvironmentMetrics_wind_lull_tag 17
#define meshtastic_PowerMetrics_ch1_voltage_tag 1
#define meshtastic_PowerMetrics_ch1_current_tag 2
#define meshtastic_PowerMetrics_ch2_voltage_tag 3
@@ -245,6 +267,8 @@ extern "C" {
#define meshtastic_Telemetry_environment_metrics_tag 3
#define meshtastic_Telemetry_air_quality_metrics_tag 4
#define meshtastic_Telemetry_power_metrics_tag 5
+#define meshtastic_Nau7802Config_zeroOffset_tag 1
+#define meshtastic_Nau7802Config_calibrationFactor_tag 2
/* Struct field encoding specification for nanopb */
#define meshtastic_DeviceMetrics_FIELDLIST(X, a) \
@@ -270,7 +294,10 @@ X(a, STATIC, SINGULAR, FLOAT, white_lux, 10) \
X(a, STATIC, SINGULAR, FLOAT, ir_lux, 11) \
X(a, STATIC, SINGULAR, FLOAT, uv_lux, 12) \
X(a, STATIC, SINGULAR, UINT32, wind_direction, 13) \
-X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14)
+X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14) \
+X(a, STATIC, SINGULAR, FLOAT, weight, 15) \
+X(a, STATIC, SINGULAR, FLOAT, wind_gust, 16) \
+X(a, STATIC, SINGULAR, FLOAT, wind_lull, 17)
#define meshtastic_EnvironmentMetrics_CALLBACK NULL
#define meshtastic_EnvironmentMetrics_DEFAULT NULL
@@ -313,11 +340,18 @@ X(a, STATIC, ONEOF, MESSAGE, (variant,power_metrics,variant.power_metrics)
#define meshtastic_Telemetry_variant_air_quality_metrics_MSGTYPE meshtastic_AirQualityMetrics
#define meshtastic_Telemetry_variant_power_metrics_MSGTYPE meshtastic_PowerMetrics
+#define meshtastic_Nau7802Config_FIELDLIST(X, a) \
+X(a, STATIC, SINGULAR, INT32, zeroOffset, 1) \
+X(a, STATIC, SINGULAR, FLOAT, calibrationFactor, 2)
+#define meshtastic_Nau7802Config_CALLBACK NULL
+#define meshtastic_Nau7802Config_DEFAULT NULL
+
extern const pb_msgdesc_t meshtastic_DeviceMetrics_msg;
extern const pb_msgdesc_t meshtastic_EnvironmentMetrics_msg;
extern const pb_msgdesc_t meshtastic_PowerMetrics_msg;
extern const pb_msgdesc_t meshtastic_AirQualityMetrics_msg;
extern const pb_msgdesc_t meshtastic_Telemetry_msg;
+extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define meshtastic_DeviceMetrics_fields &meshtastic_DeviceMetrics_msg
@@ -325,14 +359,16 @@ extern const pb_msgdesc_t meshtastic_Telemetry_msg;
#define meshtastic_PowerMetrics_fields &meshtastic_PowerMetrics_msg
#define meshtastic_AirQualityMetrics_fields &meshtastic_AirQualityMetrics_msg
#define meshtastic_Telemetry_fields &meshtastic_Telemetry_msg
+#define meshtastic_Nau7802Config_fields &meshtastic_Nau7802Config_msg
/* Maximum encoded size of messages (where known) */
#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 68
+#define meshtastic_EnvironmentMetrics_size 85
+#define meshtastic_Nau7802Config_size 16
#define meshtastic_PowerMetrics_size 30
-#define meshtastic_Telemetry_size 79
+#define meshtastic_Telemetry_size 92
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp
index 7f9df058dd..ca2c5d4be7 100644
--- a/src/mesh/http/ContentHandler.cpp
+++ b/src/mesh/http/ContentHandler.cpp
@@ -6,11 +6,11 @@
#include "main.h"
#include "mesh/http/ContentHelper.h"
#include "mesh/http/WebServer.h"
-#if !MESHTASTIC_EXCLUDE_WIFI
+#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
-#include "mqtt/JSON.h"
#include "power.h"
+#include "serialization/JSON.h"
#include "sleep.h"
#include
#include
diff --git a/src/mesh/mesh-pb-constants.cpp b/src/mesh/mesh-pb-constants.cpp
index 93dbf0178b..676208e25a 100644
--- a/src/mesh/mesh-pb-constants.cpp
+++ b/src/mesh/mesh-pb-constants.cpp
@@ -1,6 +1,7 @@
-#include "mesh-pb-constants.h"
-#include "FSCommon.h"
#include "configuration.h"
+
+#include "FSCommon.h"
+#include "mesh-pb-constants.h"
#include
#include
#include
@@ -15,6 +16,7 @@ size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc
LOG_ERROR("Panic: can't encode protobuf reason='%s'\n", PB_GET_ERROR(&stream));
assert(
0); // If this assert fails it probably means you made a field too large for the max limits specified in mesh.options
+ return 0;
} else {
return stream.bytes_written;
}
diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp
index ffb16bd3e5..e733d18011 100644
--- a/src/mesh/wifi/WiFiAPClient.cpp
+++ b/src/mesh/wifi/WiFiAPClient.cpp
@@ -1,5 +1,5 @@
#include "configuration.h"
-#if !MESHTASTIC_EXCLUDE_WIFI
+#if HAS_WIFI
#include "NodeDB.h"
#include "RTC.h"
#include "concurrency/Periodic.h"
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index 0915864623..c1e9b388b5 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -137,7 +137,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
if (BleOta::getOtaAppVersion().isEmpty()) {
LOG_INFO("No OTA firmware available, scheduling regular reboot in %d seconds\n", s);
- screen->startRebootScreen();
+ screen->startAlert("Rebooting...");
} else {
screen->startFirmwareUpdateScreen();
BleOta::switchToOtaApp();
@@ -145,7 +145,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
}
#else
LOG_INFO("Not on ESP32, scheduling regular reboot in %d seconds\n", s);
- screen->startRebootScreen();
+ screen->startAlert("Rebooting...");
#endif
rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000);
break;
@@ -200,6 +200,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
case meshtastic_AdminMessage_remove_by_nodenum_tag: {
LOG_INFO("Client is receiving a remove_nodenum command.\n");
nodeDB->removeNodeByNum(r->remove_by_nodenum);
+ this->notifyObservers(r); // Observed by screen
break;
}
case meshtastic_AdminMessage_set_favorite_node_tag: {
@@ -232,9 +233,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
#if !MESHTASTIC_EXCLUDE_GPS
if (gps != nullptr)
gps->enable();
-#endif
// Send our new fixed position to the mesh for good measure
positionModule->sendOurPosition();
+#endif
}
break;
}
@@ -258,11 +259,13 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
}
case meshtastic_AdminMessage_delete_file_request_tag: {
LOG_DEBUG("Client is requesting to delete file: %s\n", r->delete_file_request);
+#ifdef FSCom
if (FSCom.remove(r->delete_file_request)) {
LOG_DEBUG("Successfully deleted file\n");
} else {
LOG_DEBUG("Failed to delete file\n");
}
+#endif
break;
}
#ifdef ARCH_PORTDUINO
@@ -299,8 +302,8 @@ void AdminModule::handleGetModuleConfigResponse(const meshtastic_MeshPacket &mp,
{
// Skip if it's disabled or no pins are exposed
if (!r->get_module_config_response.payload_variant.remote_hardware.enabled ||
- !r->get_module_config_response.payload_variant.remote_hardware.available_pins) {
- LOG_DEBUG("Remote hardware module disabled or no vailable_pins. Skipping...\n");
+ r->get_module_config_response.payload_variant.remote_hardware.available_pins_count == 0) {
+ LOG_DEBUG("Remote hardware module disabled or no available_pins. Skipping...\n");
return;
}
for (uint8_t i = 0; i < devicestate.node_remote_hardware_pins_count; i++) {
@@ -388,6 +391,22 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
LOG_DEBUG("Tried to set node_info_broadcast_secs too low, setting to %d\n", min_node_info_broadcast_secs);
config.device.node_info_broadcast_secs = min_node_info_broadcast_secs;
}
+ // Router Client is deprecated; Set it to client
+ if (c.payload_variant.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT) {
+ config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
+ if (moduleConfig.store_forward.enabled && !moduleConfig.store_forward.is_server) {
+ moduleConfig.store_forward.is_server = true;
+ changes |= SEGMENT_MODULECONFIG;
+ requiresReboot = true;
+ }
+ }
+#if EVENT_MODE
+ // If we're in event mode, nobody is a Router or Repeater
+ if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
+ config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
+ config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
+ }
+#endif
break;
case meshtastic_Config_position_tag:
LOG_INFO("Setting config: Position\n");
@@ -811,7 +830,7 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch
void AdminModule::reboot(int32_t seconds)
{
LOG_INFO("Rebooting in %d seconds\n", seconds);
- screen->startRebootScreen();
+ screen->startAlert("Rebooting...");
rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000);
}
diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h
index 32b32c253a..a5ffeb7d60 100644
--- a/src/modules/AdminModule.h
+++ b/src/modules/AdminModule.h
@@ -1,13 +1,13 @@
#pragma once
#include "ProtobufModule.h"
-#if HAS_WIFI && !MESHTASTIC_EXCLUDE_WIFI
+#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
/**
* Admin module for admin messages
*/
-class AdminModule : public ProtobufModule
+class AdminModule : public ProtobufModule, public Observable
{
public:
/** Constructor
diff --git a/src/modules/AtakPluginModule.cpp b/src/modules/AtakPluginModule.cpp
index 59263415c8..c05f055d41 100644
--- a/src/modules/AtakPluginModule.cpp
+++ b/src/modules/AtakPluginModule.cpp
@@ -5,11 +5,8 @@
#include "PowerFSM.h"
#include "configuration.h"
#include "main.h"
-#include "meshtastic/atak.pb.h"
-
-extern "C" {
#include "mesh/compression/unishox2.h"
-}
+#include "meshtastic/atak.pb.h"
AtakPluginModule *atakPluginModule;
@@ -69,30 +66,53 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast
auto compressed = cloneTAKPacketData(t);
compressed.is_compressed = true;
if (t->has_contact) {
- auto length = unishox2_compress_simple(t->contact.callsign, strlen(t->contact.callsign), compressed.contact.callsign);
+ auto length = unishox2_compress_lines(t->contact.callsign, strlen(t->contact.callsign), compressed.contact.callsign,
+ sizeof(compressed.contact.callsign) - 1, USX_PSET_DFLT, NULL);
+ if (length < 0) {
+ LOG_WARN("Compression overflowed contact.callsign. Reverting to uncompressed packet\n");
+ return;
+ }
LOG_DEBUG("Compressed callsign: %d bytes\n", length);
-
- length = unishox2_compress_simple(t->contact.device_callsign, strlen(t->contact.device_callsign),
- compressed.contact.device_callsign);
+ length = unishox2_compress_lines(t->contact.device_callsign, strlen(t->contact.device_callsign),
+ compressed.contact.device_callsign, sizeof(compressed.contact.device_callsign) - 1,
+ USX_PSET_DFLT, NULL);
+ if (length < 0) {
+ LOG_WARN("Compression overflowed contact.device_callsign. Reverting to uncompressed packet\n");
+ return;
+ }
LOG_DEBUG("Compressed device_callsign: %d bytes\n", length);
}
if (t->which_payload_variant == meshtastic_TAKPacket_chat_tag) {
- auto length = unishox2_compress_simple(t->payload_variant.chat.message, strlen(t->payload_variant.chat.message),
- compressed.payload_variant.chat.message);
+ auto length = unishox2_compress_lines(t->payload_variant.chat.message, strlen(t->payload_variant.chat.message),
+ compressed.payload_variant.chat.message,
+ sizeof(compressed.payload_variant.chat.message) - 1, USX_PSET_DFLT, NULL);
+ if (length < 0) {
+ LOG_WARN("Compression overflowed chat.message. Reverting to uncompressed packet\n");
+ return;
+ }
LOG_DEBUG("Compressed chat message: %d bytes\n", length);
if (t->payload_variant.chat.has_to) {
compressed.payload_variant.chat.has_to = true;
- length = unishox2_compress_simple(t->payload_variant.chat.to, strlen(t->payload_variant.chat.to),
- compressed.payload_variant.chat.to);
+ length = unishox2_compress_lines(t->payload_variant.chat.to, strlen(t->payload_variant.chat.to),
+ compressed.payload_variant.chat.to,
+ sizeof(compressed.payload_variant.chat.to) - 1, USX_PSET_DFLT, NULL);
+ if (length < 0) {
+ LOG_WARN("Compression overflowed chat.to. Reverting to uncompressed packet\n");
+ return;
+ }
LOG_DEBUG("Compressed chat to: %d bytes\n", length);
}
if (t->payload_variant.chat.has_to_callsign) {
compressed.payload_variant.chat.has_to_callsign = true;
- length =
- unishox2_compress_simple(t->payload_variant.chat.to_callsign, strlen(t->payload_variant.chat.to_callsign),
- compressed.payload_variant.chat.to_callsign);
+ length = unishox2_compress_lines(t->payload_variant.chat.to_callsign, strlen(t->payload_variant.chat.to_callsign),
+ compressed.payload_variant.chat.to_callsign,
+ sizeof(compressed.payload_variant.chat.to_callsign) - 1, USX_PSET_DFLT, NULL);
+ if (length < 0) {
+ LOG_WARN("Compression overflowed chat.to_callsign. Reverting to uncompressed packet\n");
+ return;
+ }
LOG_DEBUG("Compressed chat to_callsign: %d bytes\n", length);
}
}
@@ -102,7 +122,7 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast
} else {
if (!t->is_compressed) {
// Not compressed. Something is wrong
- LOG_ERROR("Received uncompressed TAKPacket over radio!\n");
+ LOG_WARN("Received uncompressed TAKPacket over radio! Skipping\n");
return;
}
@@ -112,32 +132,55 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast
uncompressed.is_compressed = false;
if (t->has_contact) {
auto length =
- unishox2_decompress_simple(t->contact.callsign, strlen(t->contact.callsign), uncompressed.contact.callsign);
-
+ unishox2_decompress_lines(t->contact.callsign, strlen(t->contact.callsign), uncompressed.contact.callsign,
+ sizeof(uncompressed.contact.callsign) - 1, USX_PSET_DFLT, NULL);
+ if (length < 0) {
+ LOG_WARN("Decompression overflowed contact.callsign. Bailing out\n");
+ return;
+ }
LOG_DEBUG("Decompressed callsign: %d bytes\n", length);
- length = unishox2_decompress_simple(t->contact.device_callsign, strlen(t->contact.device_callsign),
- uncompressed.contact.device_callsign);
-
+ length = unishox2_decompress_lines(t->contact.device_callsign, strlen(t->contact.device_callsign),
+ uncompressed.contact.device_callsign,
+ sizeof(uncompressed.contact.device_callsign) - 1, USX_PSET_DFLT, NULL);
+ if (length < 0) {
+ LOG_WARN("Decompression overflowed contact.device_callsign. Bailing out\n");
+ return;
+ }
LOG_DEBUG("Decompressed device_callsign: %d bytes\n", length);
}
if (uncompressed.which_payload_variant == meshtastic_TAKPacket_chat_tag) {
- auto length = unishox2_decompress_simple(t->payload_variant.chat.message, strlen(t->payload_variant.chat.message),
- uncompressed.payload_variant.chat.message);
+ auto length = unishox2_decompress_lines(t->payload_variant.chat.message, strlen(t->payload_variant.chat.message),
+ uncompressed.payload_variant.chat.message,
+ sizeof(uncompressed.payload_variant.chat.message) - 1, USX_PSET_DFLT, NULL);
+ if (length < 0) {
+ LOG_WARN("Decompression overflowed chat.message. Bailing out\n");
+ return;
+ }
LOG_DEBUG("Decompressed chat message: %d bytes\n", length);
if (t->payload_variant.chat.has_to) {
uncompressed.payload_variant.chat.has_to = true;
- length = unishox2_decompress_simple(t->payload_variant.chat.to, strlen(t->payload_variant.chat.to),
- uncompressed.payload_variant.chat.to);
+ length = unishox2_decompress_lines(t->payload_variant.chat.to, strlen(t->payload_variant.chat.to),
+ uncompressed.payload_variant.chat.to,
+ sizeof(uncompressed.payload_variant.chat.to) - 1, USX_PSET_DFLT, NULL);
+ if (length < 0) {
+ LOG_WARN("Decompression overflowed chat.to. Bailing out\n");
+ return;
+ }
LOG_DEBUG("Decompressed chat to: %d bytes\n", length);
}
if (t->payload_variant.chat.has_to_callsign) {
uncompressed.payload_variant.chat.has_to_callsign = true;
length =
- unishox2_decompress_simple(t->payload_variant.chat.to_callsign, strlen(t->payload_variant.chat.to_callsign),
- uncompressed.payload_variant.chat.to_callsign);
+ unishox2_decompress_lines(t->payload_variant.chat.to_callsign, strlen(t->payload_variant.chat.to_callsign),
+ uncompressed.payload_variant.chat.to_callsign,
+ sizeof(uncompressed.payload_variant.chat.to_callsign) - 1, USX_PSET_DFLT, NULL);
+ if (length < 0) {
+ LOG_WARN("Decompression overflowed chat.to_callsign. Bailing out\n");
+ return;
+ }
LOG_DEBUG("Decompressed chat to_callsign: %d bytes\n", length);
}
}
@@ -148,4 +191,4 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast
service.sendToPhone(decompressedCopy);
}
return;
-}
\ No newline at end of file
+}
diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp
index 9b993ae5a3..524d37a3d9 100644
--- a/src/modules/CannedMessageModule.cpp
+++ b/src/modules/CannedMessageModule.cpp
@@ -49,7 +49,7 @@ CannedMessageModule::CannedMessageModule()
LOG_INFO("CannedMessageModule is enabled\n");
// T-Watch interface currently has no way to select destination type, so default to 'node'
-#ifdef T_WATCH_S3
+#if defined(T_WATCH_S3) || defined(RAK14014)
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE;
#endif
@@ -75,7 +75,7 @@ int CannedMessageModule::splitConfiguredMessages()
String messages = cannedMessageModuleConfig.messages;
-#ifdef T_WATCH_S3
+#if defined(T_WATCH_S3) || defined(RAK14014)
String separator = messages.length() ? "|" : "";
messages = "[---- Free Text ----]" + separator + messages;
@@ -144,12 +144,13 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
}
if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT)) {
-#ifdef T_WATCH_S3
+#if defined(T_WATCH_S3) || defined(RAK14014)
if (this->currentMessageIndex == 0) {
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
- UIFrameEvent e = {false, true};
- e.frameChanged = true;
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs
+ UIFrameEvent e;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->notifyObservers(&e);
return 0;
@@ -166,11 +167,11 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
}
}
if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) {
- UIFrameEvent e = {false, true};
- e.frameChanged = true;
+ UIFrameEvent e;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1;
-#ifndef T_WATCH_S3
+#if !defined(T_WATCH_S3) && !defined(RAK14014)
this->freetext = ""; // clear freetext
this->cursor = 0;
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
@@ -183,7 +184,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
(event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) ||
(event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) {
-#ifdef T_WATCH_S3
+#if defined(T_WATCH_S3) || defined(RAK14014)
if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
this->payload = 0xb4;
} else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
@@ -283,7 +284,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
}
}
-#ifdef T_WATCH_S3
+#if defined(T_WATCH_S3) || defined(RAK14014)
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
String keyTapped = keyForCoordinates(event->touchX, event->touchY);
@@ -353,6 +354,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
}
if (validEvent) {
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs
+
// Let runOnce to be called immediately.
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) {
setIntervalFromNow(0); // on fast keypresses, this isn't fast enough.
@@ -378,6 +381,11 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
p->decoded.payload.size++;
}
+ // Only receive routing messages when expecting ACK for a canned message
+ // Prevents the canned message module from regenerating the screen's frameset at unexpected times,
+ // or raising a UIFrameEvent before another module has the chance
+ this->waitingForAck = true;
+
LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
service.sendToMesh(
@@ -393,18 +401,18 @@ int32_t CannedMessageModule::runOnce()
return INT32_MAX;
}
// LOG_DEBUG("Check status\n");
- UIFrameEvent e = {false, true};
+ UIFrameEvent e;
if ((this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) ||
(this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) || (this->runState == CANNED_MESSAGE_RUN_STATE_MESSAGE)) {
// TODO: might have some feedback of sending state
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
temporaryMessage = "";
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext
this->cursor = 0;
-#ifndef T_WATCH_S3
+#if !defined(T_WATCH_S3) && !defined(RAK14014)
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
@@ -412,12 +420,12 @@ int32_t CannedMessageModule::runOnce()
} else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) &&
((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) {
// Reset module
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext
this->cursor = 0;
-#ifndef T_WATCH_S3
+#if !defined(T_WATCH_S3) && !defined(RAK14014)
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
@@ -437,7 +445,7 @@ int32_t CannedMessageModule::runOnce()
powerFSM.trigger(EVENT_PRESS);
return INT32_MAX;
} else {
-#ifdef T_WATCH_S3
+#if defined(T_WATCH_S3) || defined(RAK14014)
sendText(this->dest, indexChannels[this->channel], this->messages[this->currentMessageIndex], true);
#else
sendText(NODENUM_BROADCAST, channels.getPrimaryIndex(), this->messages[this->currentMessageIndex], true);
@@ -449,12 +457,12 @@ int32_t CannedMessageModule::runOnce()
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
}
}
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext
this->cursor = 0;
-#ifndef T_WATCH_S3
+#if !defined(T_WATCH_S3) && !defined(RAK14014)
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
@@ -463,7 +471,7 @@ int32_t CannedMessageModule::runOnce()
} else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) {
this->currentMessageIndex = 0;
LOG_DEBUG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) {
if (this->messagesCount > 0) {
@@ -471,7 +479,7 @@ int32_t CannedMessageModule::runOnce()
this->freetext = ""; // clear freetext
this->cursor = 0;
-#ifndef T_WATCH_S3
+#if !defined(T_WATCH_S3) && !defined(RAK14014)
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
@@ -484,7 +492,7 @@ int32_t CannedMessageModule::runOnce()
this->freetext = ""; // clear freetext
this->cursor = 0;
-#ifndef T_WATCH_S3
+#if !defined(T_WATCH_S3) && !defined(RAK14014)
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
@@ -567,7 +575,7 @@ int32_t CannedMessageModule::runOnce()
break;
}
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
switch (this->payload) { // code below all trigger the freetext window (where you type to send a message) or reset the
// display back to the default window
case 0x08: // backspace
@@ -597,14 +605,14 @@ int32_t CannedMessageModule::runOnce()
// handle fn+s for shutdown
case 0x9b:
if (screen)
- screen->startShutdownScreen();
+ screen->startAlert("Shutting down...");
shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000;
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
break;
// and fn+r for reboot
case 0x90:
if (screen)
- screen->startRebootScreen();
+ screen->startAlert("Rebooting...");
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
break;
@@ -706,15 +714,15 @@ int CannedMessageModule::getPrevIndex()
void CannedMessageModule::showTemporaryMessage(const String &message)
{
temporaryMessage = message;
- UIFrameEvent e = {false, true};
- e.frameChanged = true;
+ UIFrameEvent e;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
notifyObservers(&e);
runState = CANNED_MESSAGE_RUN_STATE_MESSAGE;
// run this loop again in 2 seconds, next iteration will clear the display
setIntervalFromNow(2000);
}
-#ifdef T_WATCH_S3
+#if defined(T_WATCH_S3) || defined(RAK14014)
String CannedMessageModule::keyForCoordinates(uint x, uint y)
{
@@ -914,11 +922,13 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
char buffer[50];
if (temporaryMessage.length() != 0) {
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame
LOG_DEBUG("Drawing temporary message: %s", temporaryMessage.c_str());
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage);
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) {
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
String displayString;
@@ -940,6 +950,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi);
}
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending...");
@@ -948,8 +959,8 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->setFont(FONT_SMALL);
display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled.");
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
-
-#ifdef T_WATCH_S3
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame
+#if defined(T_WATCH_S3) || defined(RAK14014)
drawKeyboard(display, state, 0, 0);
#else
@@ -1030,16 +1041,18 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
{
- if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP) {
+ if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP && waitingForAck) {
// look for a request_id
if (mp.decoded.request_id != 0) {
- UIFrameEvent e = {false, true};
- e.frameChanged = true;
+ UIFrameEvent e;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
+ requestFocus(); // Tell Screen::setFrames that our module's frame should be shown, even if not "first" in the frameset
this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED;
this->incoming = service.getNodenumFromRequestId(mp.decoded.request_id);
meshtastic_Routing decoded = meshtastic_Routing_init_default;
pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded);
this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE;
+ waitingForAck = false; // No longer want routing packets
this->notifyObservers(&e);
// run the next time 2 seconds later
setIntervalFromNow(2000);
@@ -1053,7 +1066,7 @@ void CannedMessageModule::loadProtoForModule()
{
if (nodeDB->loadProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,
sizeof(meshtastic_CannedMessageModuleConfig), &meshtastic_CannedMessageModuleConfig_msg,
- &cannedMessageModuleConfig) != LoadFileResult::SUCCESS) {
+ &cannedMessageModuleConfig) != LoadFileResult::LOAD_SUCCESS) {
installDefaultCannedMessageModuleConfig();
}
}
diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h
index 43897e782b..797b9f7cff 100644
--- a/src/modules/CannedMessageModule.h
+++ b/src/modules/CannedMessageModule.h
@@ -81,9 +81,8 @@ class CannedMessageModule : public SinglePortModule, public Observabledecoded.portnum) {
- case meshtastic_PortNum_TEXT_MESSAGE_APP:
case meshtastic_PortNum_ROUTING_APP:
- return true;
+ return waitingForAck;
default:
return false;
}
@@ -98,7 +97,7 @@ class CannedMessageModule : public SinglePortModule, public Observable 0 &&
- (millis() - lastSentToMesh) >=
- Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs)) {
+ (millis() - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs,
+ default_telemetry_broadcast_interval_secs)) {
sendCurrentStateMessage();
return DELAYED_INTERVAL;
}
diff --git a/src/modules/DropzoneModule.cpp b/src/modules/DropzoneModule.cpp
new file mode 100644
index 0000000000..8c5b5dcdd8
--- /dev/null
+++ b/src/modules/DropzoneModule.cpp
@@ -0,0 +1,95 @@
+#if !MESHTASTIC_EXCLUDE_DROPZONE
+
+#include "DropzoneModule.h"
+#include "MeshService.h"
+#include "configuration.h"
+#include "gps/GeoCoord.h"
+#include "gps/RTC.h"
+#include "main.h"
+
+#include
+
+#include "modules/Telemetry/Sensor/DFRobotLarkSensor.h"
+#include "modules/Telemetry/UnitConversions.h"
+
+#include
+
+DropzoneModule *dropzoneModule;
+
+int32_t DropzoneModule::runOnce()
+{
+ // Send on a 5 second delay from receiving the matching request
+ if (startSendConditions != 0 && (startSendConditions + 5000U) < millis()) {
+ service.sendToMesh(sendConditions(), RX_SRC_LOCAL);
+ startSendConditions = 0;
+ }
+ // Run every second to check if we need to send conditions
+ return 1000;
+}
+
+ProcessMessage DropzoneModule::handleReceived(const meshtastic_MeshPacket &mp)
+{
+ auto &p = mp.decoded;
+ char matchCompare[54];
+ auto incomingMessage = reinterpret_cast(p.payload.bytes);
+ sprintf(matchCompare, "%s conditions", owner.short_name);
+ if (strncasecmp(incomingMessage, matchCompare, strlen(matchCompare)) == 0) {
+ LOG_DEBUG("Received dropzone conditions request\n");
+ startSendConditions = millis();
+ }
+
+ sprintf(matchCompare, "%s conditions", owner.long_name);
+ if (strncasecmp(incomingMessage, matchCompare, strlen(matchCompare)) == 0) {
+ LOG_DEBUG("Received dropzone conditions request\n");
+ startSendConditions = millis();
+ }
+ return ProcessMessage::CONTINUE;
+}
+
+meshtastic_MeshPacket *DropzoneModule::sendConditions()
+{
+ char replyStr[200];
+ /*
+ CLOSED @ {HH:MM:SS}z
+ Wind 2 kts @ 125°
+ 29.25 inHg 72°C
+ */
+ uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
+ int hour = 0, min = 0, sec = 0;
+ if (rtc_sec > 0) {
+ long hms = rtc_sec % SEC_PER_DAY;
+ hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
+
+ hour = hms / SEC_PER_HOUR;
+ min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
+ sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN;
+ }
+
+ // Check if the dropzone is open or closed by reading the analog pin
+ // If pin is connected to GND (below 100 should be lower than floating voltage),
+ // the dropzone is open
+ auto dropzoneStatus = analogRead(A1) < 100 ? "OPEN" : "CLOSED";
+ auto reply = allocDataPacket();
+
+ auto node = nodeDB->getMeshNode(nodeDB->getNodeNum());
+ if (sensor.hasSensor()) {
+ meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero;
+ sensor.getMetrics(&telemetry);
+ auto windSpeed = UnitConversions::MetersPerSecondToKnots(telemetry.variant.environment_metrics.wind_speed);
+ auto windDirection = telemetry.variant.environment_metrics.wind_direction;
+ auto temp = telemetry.variant.environment_metrics.temperature;
+ auto baro = UnitConversions::HectoPascalToInchesOfMercury(telemetry.variant.environment_metrics.barometric_pressure);
+ sprintf(replyStr, "%s @ %02d:%02d:%02dz\nWind %.2f kts @ %d°\nBaro %.2f inHg %.2f°C", dropzoneStatus, hour, min, sec,
+ windSpeed, windDirection, baro, temp);
+ } else {
+ LOG_ERROR("No sensor found\n");
+ sprintf(replyStr, "%s @ %02d:%02d:%02d\nNo sensor found", dropzoneStatus, hour, min, sec);
+ }
+ LOG_DEBUG("Conditions reply: %s\n", replyStr);
+ reply->decoded.payload.size = strlen(replyStr); // You must specify how many bytes are in the reply
+ memcpy(reply->decoded.payload.bytes, replyStr, reply->decoded.payload.size);
+
+ return reply;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/modules/DropzoneModule.h b/src/modules/DropzoneModule.h
new file mode 100644
index 0000000000..28f54ee0f2
--- /dev/null
+++ b/src/modules/DropzoneModule.h
@@ -0,0 +1,37 @@
+#pragma once
+#if !MESHTASTIC_EXCLUDE_DROPZONE
+#include "SinglePortModule.h"
+#include "modules/Telemetry/Sensor/DFRobotLarkSensor.h"
+
+/**
+ * An example module that replies to a message with the current conditions
+ * and status at the dropzone when it receives a text message mentioning it's name followed by "conditions"
+ */
+class DropzoneModule : public SinglePortModule, private concurrency::OSThread
+{
+ DFRobotLarkSensor sensor;
+
+ public:
+ /** Constructor
+ * name is for debugging output
+ */
+ DropzoneModule() : SinglePortModule("dropzone", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("DropzoneModule")
+ {
+ // Set up the analog pin for reading the dropzone status
+ pinMode(PIN_A1, INPUT);
+ }
+
+ virtual int32_t runOnce() override;
+
+ protected:
+ /** Called to handle a particular incoming message
+ */
+ virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
+
+ private:
+ meshtastic_MeshPacket *sendConditions();
+ uint32_t startSendConditions = 0;
+};
+
+extern DropzoneModule *dropzoneModule;
+#endif
\ No newline at end of file
diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp
index c025592401..652db04d3d 100644
--- a/src/modules/ExternalNotificationModule.cpp
+++ b/src/modules/ExternalNotificationModule.cpp
@@ -355,7 +355,7 @@ ExternalNotificationModule::ExternalNotificationModule()
if (moduleConfig.external_notification.enabled) {
if (nodeDB->loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig),
- &meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::SUCCESS) {
+ &meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) {
memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone));
strncpy(rtttlConfig.ringtone,
"24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p",
diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp
index 3e3a961ac8..40352a56e9 100644
--- a/src/modules/Modules.cpp
+++ b/src/modules/Modules.cpp
@@ -8,7 +8,9 @@
#include "input/kbMatrixImpl.h"
#include "input/SerialKeyboardImpl.h"
#endif
+#if !MESHTASTIC_EXCLUDE_ADMIN
#include "modules/AdminModule.h"
+#endif
#if !MESHTASTIC_EXCLUDE_ATAK
#include "modules/AtakPluginModule.h"
#endif
@@ -21,13 +23,18 @@
#if !MESHTASTIC_EXCLUDE_NEIGHBORINFO
#include "modules/NeighborInfoModule.h"
#endif
+#if !MESHTASTIC_EXCLUDE_NODEINFO
#include "modules/NodeInfoModule.h"
+#endif
#if !MESHTASTIC_EXCLUDE_GPS
#include "modules/PositionModule.h"
#endif
#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE
#include "modules/RemoteHardwareModule.h"
#endif
+#if !MESHTASTIC_EXCLUDE_POWERSTRESS
+#include "modules/PowerStressModule.h"
+#endif
#include "modules/RoutingModule.h"
#include "modules/TextMessageModule.h"
#if !MESHTASTIC_EXCLUDE_TRACEROUTE
@@ -43,6 +50,7 @@
#include "modules/Telemetry/DeviceTelemetry.h"
#endif
#if HAS_SENSOR && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+#include "main.h"
#include "modules/Telemetry/AirQualityTelemetry.h"
#include "modules/Telemetry/EnvironmentTelemetry.h"
#endif
@@ -71,6 +79,11 @@
#include "modules/SerialModule.h"
#endif
#endif
+
+#if !MESHTASTIC_EXCLUDE_DROPZONE
+#include "modules/DropzoneModule.h"
+#endif
+
/**
* Create module instances here. If you are adding a new module, you must 'new' it here (or somewhere else)
*/
@@ -80,8 +93,12 @@ void setupModules()
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
inputBroker = new InputBroker();
#endif
+#if !MESHTASTIC_EXCLUDE_ADMIN
adminModule = new AdminModule();
+#endif
+#if !MESHTASTIC_EXCLUDE_NODEINFO
nodeInfoModule = new NodeInfoModule();
+#endif
#if !MESHTASTIC_EXCLUDE_GPS
positionModule = new PositionModule();
#endif
@@ -101,11 +118,18 @@ void setupModules()
#if !MESHTASTIC_EXCLUDE_ATAK
atakPluginModule = new AtakPluginModule();
#endif
+
+#if !MESHTASTIC_EXCLUDE_DROPZONE
+ dropzoneModule = new DropzoneModule();
+#endif
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
// to a global variable.
#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE
new RemoteHardwareModule();
+#endif
+#if !MESHTASTIC_EXCLUDE_POWERSTRESS
+ new PowerStressModule();
#endif
// Example: Put your module here
// new ReplyModule();
@@ -181,7 +205,9 @@ void setupModules()
#endif
#endif
} else {
+#if !MESHTASTIC_EXCLUDE_ADMIN
adminModule = new AdminModule();
+#endif
#if HAS_TELEMETRY
new DeviceTelemetryModule();
#endif
diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp
index 3925bea9a8..774b42d7bd 100644
--- a/src/modules/NeighborInfoModule.cpp
+++ b/src/modules/NeighborInfoModule.cpp
@@ -39,11 +39,12 @@ NeighborInfoModule::NeighborInfoModule()
concurrency::OSThread("NeighborInfoModule")
{
ourPortNum = meshtastic_PortNum_NEIGHBORINFO_APP;
+ nodeStatusObserver.observe(&nodeStatus->onNewStatus);
if (moduleConfig.neighbor_info.enabled) {
isPromiscuous = true; // Update neighbors from all packets
- setIntervalFromNow(
- Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_broadcast_interval_secs));
+ setIntervalFromNow(Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval,
+ default_telemetry_broadcast_interval_secs));
} else {
LOG_DEBUG("NeighborInfoModule is disabled\n");
disable();
@@ -119,7 +120,8 @@ int32_t NeighborInfoModule::runOnce()
if (airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) {
sendNeighborInfo(NODENUM_BROADCAST, false);
}
- return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_broadcast_interval_secs);
+ return Default::getConfiguredOrDefaultMsScaled(moduleConfig.neighbor_info.update_interval, default_broadcast_interval_secs,
+ numOnlineNodes);
}
/*
diff --git a/src/modules/NeighborInfoModule.h b/src/modules/NeighborInfoModule.h
index 496fdece58..aa76a21871 100644
--- a/src/modules/NeighborInfoModule.h
+++ b/src/modules/NeighborInfoModule.h
@@ -7,6 +7,9 @@
*/
class NeighborInfoModule : public ProtobufModule, private concurrency::OSThread
{
+ CallbackObserver nodeStatusObserver =
+ CallbackObserver(this, &NeighborInfoModule::handleStatusUpdate);
+
std::vector neighbors;
public:
diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp
index 49f2b808b9..228929e963 100644
--- a/src/modules/PositionModule.cpp
+++ b/src/modules/PositionModule.cpp
@@ -11,12 +11,12 @@
#include "configuration.h"
#include "gps/GeoCoord.h"
#include "main.h"
+#include "mesh/compression/unishox2.h"
#include "meshtastic/atak.pb.h"
#include "sleep.h"
#include "target_specific.h"
extern "C" {
-#include "mesh/compression/unishox2.h"
#include
}
@@ -28,6 +28,8 @@ PositionModule::PositionModule()
{
precision = 0; // safe starting value
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
+ nodeStatusObserver.observe(&nodeStatus->onNewStatus);
+
if (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)
setIntervalFromNow(60 * 1000);
@@ -73,7 +75,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
}
// Log packet size and data fields
- 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 "
+ LOG_DEBUG("POSITION node=%08x l=%d lat=%d lon=%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,
@@ -219,7 +221,7 @@ meshtastic_MeshPacket *PositionModule::allocReply()
LOG_INFO("Providing time to mesh %u\n", p.time);
}
- LOG_INFO("Position reply: time=%i, latI=%i, lonI=%i\n", p.time, p.latitude_i, p.longitude_i);
+ LOG_INFO("Position reply: time=%i lat=%i lon=%i\n", p.time, p.latitude_i, p.longitude_i);
// TAK Tracker devices should send their position in a TAK packet over the ATAK port
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)
@@ -253,10 +255,12 @@ meshtastic_MeshPacket *PositionModule::allocAtakPli()
.course = static_cast(localPosition.ground_track),
}}};
- auto length = unishox2_compress_simple(owner.long_name, strlen(owner.long_name), takPacket.contact.device_callsign);
+ auto length = unishox2_compress_lines(owner.long_name, strlen(owner.long_name), takPacket.contact.device_callsign,
+ sizeof(takPacket.contact.device_callsign) - 1, USX_PSET_DFLT, NULL);
LOG_DEBUG("Uncompressed device_callsign '%s' - %d bytes\n", owner.long_name, strlen(owner.long_name));
LOG_DEBUG("Compressed device_callsign '%s' - %d bytes\n", takPacket.contact.device_callsign, length);
- length = unishox2_compress_simple(owner.long_name, strlen(owner.long_name), takPacket.contact.callsign);
+ length = unishox2_compress_lines(owner.long_name, strlen(owner.long_name), takPacket.contact.callsign,
+ sizeof(takPacket.contact.callsign) - 1, USX_PSET_DFLT, NULL);
mp->decoded.payload.size =
pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), &meshtastic_TAKPacket_msg, &takPacket);
return mp;
@@ -333,8 +337,8 @@ int32_t PositionModule::runOnce()
// We limit our GPS broadcasts to a max rate
uint32_t now = millis();
- uint32_t intervalMs =
- Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs);
+ uint32_t intervalMs = Default::getConfiguredOrDefaultMsScaled(config.position.position_broadcast_secs,
+ default_broadcast_interval_secs, numOnlineNodes);
uint32_t msSinceLastSend = now - lastGpsSend;
// Only send packets if the channel util. is less than 25% utilized or we're a tracker with less than 40% utilized.
if (!airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h
index 763b51e5cb..a5277aff69 100644
--- a/src/modules/PositionModule.h
+++ b/src/modules/PositionModule.h
@@ -8,6 +8,9 @@
*/
class PositionModule : public ProtobufModule, private concurrency::OSThread
{
+ CallbackObserver nodeStatusObserver =
+ CallbackObserver(this, &PositionModule::handleStatusUpdate);
+
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher
PacketId prevPacketId = 0;
@@ -59,7 +62,7 @@ class PositionModule : public ProtobufModule, private concu
void sendLostAndFoundText();
const uint32_t minimumTimeThreshold =
- Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30);
+ Default::getConfiguredOrDefaultMsScaled(config.position.broadcast_smart_minimum_interval_secs, 30, numOnlineNodes);
};
struct SmartPosition {
diff --git a/src/modules/PowerStressModule.cpp b/src/modules/PowerStressModule.cpp
new file mode 100644
index 0000000000..c86017ae28
--- /dev/null
+++ b/src/modules/PowerStressModule.cpp
@@ -0,0 +1,77 @@
+#include "PowerStressModule.h"
+#include "MeshService.h"
+#include "NodeDB.h"
+#include "RTC.h"
+#include "Router.h"
+#include "configuration.h"
+#include "main.h"
+
+extern void printInfo();
+
+PowerStressModule::PowerStressModule()
+ : ProtobufModule("powerstress", meshtastic_PortNum_POWERSTRESS_APP, &meshtastic_PowerStressMessage_msg),
+ concurrency::OSThread("PowerStressModule")
+{
+}
+
+bool PowerStressModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, meshtastic_PowerStressMessage *pptr)
+{
+ // We only respond to messages if powermon debugging is already on
+ if (config.power.powermon_enables) {
+ auto p = *pptr;
+ LOG_INFO("Received PowerStress cmd=%d\n", p.cmd);
+
+ // Some commands we can handle immediately, anything else gets deferred to be handled by our thread
+ switch (p.cmd) {
+ case meshtastic_PowerStressMessage_Opcode_UNSET:
+ LOG_ERROR("PowerStress operation unset\n");
+ break;
+
+ case meshtastic_PowerStressMessage_Opcode_PRINT_INFO:
+ printInfo();
+ break;
+
+ default:
+ if (currentMessage.cmd != meshtastic_PowerStressMessage_Opcode_UNSET)
+ LOG_ERROR("PowerStress operation %d already in progress! Can't start new command\n", currentMessage.cmd);
+ else
+ currentMessage = p; // copy for use by thread (the message provided to us will be getting freed)
+ break;
+ }
+ }
+ return true;
+}
+
+int32_t PowerStressModule::runOnce()
+{
+
+ if (!config.power.powermon_enables) {
+ // Powermon not enabled - stop using CPU/stop this thread
+ return disable();
+ }
+
+ int32_t sleep_msec = 10; // when not active check for new messages every 10ms
+
+ auto &p = currentMessage;
+
+ if (isRunningCommand) {
+ // Done with the previous command - our sleep must have finished
+ p.cmd = meshtastic_PowerStressMessage_Opcode_UNSET;
+ p.num_seconds = 0;
+ } else {
+ sleep_msec = (int32_t)(p.num_seconds * 1000);
+ isRunningCommand = !!sleep_msec; // if the command wants us to sleep, make sure to mark that we have something running
+
+ switch (p.cmd) {
+ case meshtastic_PowerStressMessage_Opcode_UNSET: // No need to start a new command
+ break;
+ case meshtastic_PowerStressMessage_Opcode_LED_ON:
+ break;
+ default:
+ LOG_ERROR("PowerStress operation %d not yet implemented!\n", p.cmd);
+ sleep_msec = 0; // Don't do whatever sleep was requested...
+ break;
+ }
+ }
+ return sleep_msec;
+}
\ No newline at end of file
diff --git a/src/modules/PowerStressModule.h b/src/modules/PowerStressModule.h
new file mode 100644
index 0000000000..2d449f690c
--- /dev/null
+++ b/src/modules/PowerStressModule.h
@@ -0,0 +1,38 @@
+#pragma once
+#include "ProtobufModule.h"
+#include "concurrency/OSThread.h"
+#include "mesh/generated/meshtastic/powermon.pb.h"
+
+/**
+ * A module that provides easy low-level remote access to device hardware.
+ */
+class PowerStressModule : public ProtobufModule, private concurrency::OSThread
+{
+ meshtastic_PowerStressMessage currentMessage = meshtastic_PowerStressMessage_init_default;
+ bool isRunningCommand = false;
+
+ public:
+ /** Constructor
+ * name is for debugging output
+ */
+ PowerStressModule();
+
+ protected:
+ /** 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
+ */
+ virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_PowerStressMessage *p) override;
+
+ /**
+ * Periodically read the gpios we have been asked to WATCH, if they have changed,
+ * broadcast a message with the change information.
+ *
+ * The method that will be called each time our thread gets a chance to run
+ *
+ * Returns desired period for next invocation (or RUN_SAME for no change)
+ */
+ virtual int32_t runOnce() override;
+};
+
+extern PowerStressModule powerStressModule;
\ No newline at end of file
diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp
index fe1abab05d..80ac92fff1 100644
--- a/src/modules/RoutingModule.cpp
+++ b/src/modules/RoutingModule.cpp
@@ -1,4 +1,5 @@
#include "RoutingModule.h"
+#include "Default.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "Router.h"
@@ -50,12 +51,15 @@ uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit
// Hops used by the request. If somebody in between running modified firmware modified it, ignore it
uint8_t hopsUsed = hopStart < hopLimit ? config.lora.hop_limit : hopStart - hopLimit;
if (hopsUsed > config.lora.hop_limit) {
+// In event mode, we never want to send packets with more than our default 3 hops.
+#if !(EVENTMODE) // This falls through to the default.
return hopsUsed; // If the request used more hops than the limit, use the same amount of hops
+#endif
} else if ((uint8_t)(hopsUsed + 2) < config.lora.hop_limit) {
return hopsUsed + 2; // Use only the amount of hops needed with some margin as the way back may be different
}
}
- return config.lora.hop_limit; // Use the default hop limit
+ return Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); // Use the default hop limit
}
RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg)
diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp
index 96a99b13e3..4b8a4d2284 100644
--- a/src/modules/SerialModule.cpp
+++ b/src/modules/SerialModule.cpp
@@ -1,4 +1,5 @@
#include "SerialModule.h"
+#include "GeoCoord.h"
#include "MeshService.h"
#include "NMEAWPL.h"
#include "NodeDB.h"
@@ -66,7 +67,7 @@ SerialModule::SerialModule() : StreamAPI(&Serial2), concurrency::OSThread("Seria
static Print *serialPrint = &Serial2;
#endif
-char serialBytes[meshtastic_Constants_DATA_PAYLOAD_LEN];
+char serialBytes[512];
size_t serialPayloadSize;
SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio")
@@ -198,8 +199,12 @@ int32_t SerialModule::runOnce()
}
}
}
+
#if !defined(TTGO_T_ECHO) && !defined(CANARYONE)
- else {
+ else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85)) {
+ processWXSerial();
+
+ } else {
while (Serial2.available()) {
serialPayloadSize = Serial2.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
serialModuleRadio->sendPayload();
@@ -213,6 +218,27 @@ int32_t SerialModule::runOnce()
}
}
+/**
+ * Sends telemetry packet over the mesh network.
+ *
+ * @param m The telemetry data to be sent
+ *
+ * @return void
+ *
+ * @throws None
+ */
+void SerialModule::sendTelemetry(meshtastic_Telemetry m)
+{
+ meshtastic_MeshPacket *p = router->allocForSending();
+ p->decoded.portnum = meshtastic_PortNum_TELEMETRY_APP;
+ p->decoded.payload.size =
+ pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Telemetry_msg, &m);
+ p->to = NODENUM_BROADCAST;
+ p->decoded.want_response = false;
+ p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
+ service.sendToMesh(p, RX_SRC_LOCAL, true);
+}
+
/**
* Allocates a new mesh packet for use as a reply to a received packet.
*
@@ -357,4 +383,162 @@ uint32_t SerialModule::getBaudRate()
}
return BAUD;
}
+
+/**
+ * Process the received weather station serial data, extract wind, voltage, and temperature information,
+ * calculate averages and send telemetry data over the mesh network.
+ *
+ * @return void
+ */
+void SerialModule::processWXSerial()
+{
+#if !defined(TTGO_T_ECHO) && !defined(CANARYONE)
+ static unsigned int lastAveraged = 0;
+ static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded.
+ static double dir_sum_sin = 0;
+ static double dir_sum_cos = 0;
+ static float velSum = 0;
+ static float gust = 0;
+ static float lull = -1;
+ static int velCount = 0;
+ static int dirCount = 0;
+ static char windDir[4] = "xxx"; // Assuming windDir is 3 characters long + null terminator
+ static char windVel[5] = "xx.x"; // Assuming windVel is 4 characters long + null terminator
+ static char windGust[5] = "xx.x"; // Assuming windGust is 4 characters long + null terminator
+ static char batVoltage[5] = "0.0V";
+ static char capVoltage[5] = "0.0V";
+ static float batVoltageF = 0;
+ static float capVoltageF = 0;
+ bool gotwind = false;
+
+ while (Serial2.available()) {
+ // clear serialBytes buffer
+ memset(serialBytes, '\0', sizeof(serialBytes));
+ // memset(formattedString, '\0', sizeof(formattedString));
+ serialPayloadSize = Serial2.readBytes(serialBytes, 512);
+ // check for a strings we care about
+ // example output of serial data fields from the WS85
+ // WindDir = 79
+ // WindSpeed = 0.5
+ // WindGust = 0.6
+ // GXTS04Temp = 24.4
+ if (serialPayloadSize > 0) {
+ // Define variables for line processing
+ int lineStart = 0;
+ int lineEnd = -1;
+
+ // Process each byte in the received data
+ for (size_t i = 0; i < serialPayloadSize; i++) {
+ // go until we hit the end of line and then process the line
+ if (serialBytes[i] == '\n') {
+ lineEnd = i;
+ // Extract the current line
+ char line[meshtastic_Constants_DATA_PAYLOAD_LEN];
+ memset(line, '\0', sizeof(line));
+ memcpy(line, &serialBytes[lineStart], lineEnd - lineStart);
+
+ if (strstr(line, "Wind") != NULL) // we have a wind line
+ {
+ gotwind = true;
+ // Find the positions of "=" signs in the line
+ char *windDirPos = strstr(line, "WindDir = ");
+ char *windSpeedPos = strstr(line, "WindSpeed = ");
+ char *windGustPos = strstr(line, "WindGust = ");
+
+ if (windDirPos != NULL) {
+ // Extract data after "=" for WindDir
+ strcpy(windDir, windDirPos + 15); // Add 15 to skip "WindDir = "
+ double radians = toRadians(strtof(windDir, nullptr));
+ dir_sum_sin += sin(radians);
+ dir_sum_cos += cos(radians);
+ dirCount++;
+ } else if (windSpeedPos != NULL) {
+ // Extract data after "=" for WindSpeed
+ strcpy(windVel, windSpeedPos + 15); // Add 15 to skip "WindSpeed = "
+ float newv = strtof(windVel, nullptr);
+ velSum += newv;
+ velCount++;
+ if (newv < lull || lull == -1)
+ lull = newv;
+
+ } else if (windGustPos != NULL) {
+ strcpy(windGust, windGustPos + 15); // Add 15 to skip "WindSpeed = "
+ float newg = strtof(windGust, nullptr);
+ if (newg > gust)
+ gust = newg;
+ }
+
+ // these are also voltage data we care about possibly
+ } else if (strstr(line, "BatVoltage") != NULL) { // we have a battVoltage line
+ char *batVoltagePos = strstr(line, "BatVoltage = ");
+ if (batVoltagePos != NULL) {
+ strcpy(batVoltage, batVoltagePos + 17); // 18 for ws 80, 17 for ws85
+ batVoltageF = strtof(batVoltage, nullptr);
+ break; // last possible data we want so break
+ }
+ } else if (strstr(line, "CapVoltage") != NULL) { // we have a cappVoltage line
+ char *capVoltagePos = strstr(line, "CapVoltage = ");
+ if (capVoltagePos != NULL) {
+ strcpy(capVoltage, capVoltagePos + 17); // 18 for ws 80, 17 for ws85
+ capVoltageF = strtof(capVoltage, nullptr);
+ }
+ }
+
+ // Update lineStart for the next line
+ lineStart = lineEnd + 1;
+ }
+ }
+ break;
+ // clear the input buffer
+ while (Serial2.available() > 0) {
+ Serial2.read(); // Read and discard the bytes in the input buffer
+ }
+ }
+ }
+ if (gotwind) {
+
+ LOG_INFO("WS85 : %i %.1fg%.1f %.1fv %.1fv\n", atoi(windDir), strtof(windVel, nullptr), strtof(windGust, nullptr),
+ batVoltageF, capVoltageF);
+ }
+ if (gotwind && millis() - lastAveraged > averageIntervalMillis) {
+ // calulate averages and send to the mesh
+ float velAvg = 1.0 * velSum / velCount;
+
+ double avgSin = dir_sum_sin / dirCount;
+ double avgCos = dir_sum_cos / dirCount;
+
+ double avgRadians = atan2(avgSin, avgCos);
+ float dirAvg = toDegrees(avgRadians);
+
+ if (dirAvg < 0) {
+ dirAvg += 360.0;
+ }
+ lastAveraged = millis();
+
+ // make a telemetry packet with the data
+ meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
+ m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
+ m.variant.environment_metrics.wind_speed = velAvg;
+ m.variant.environment_metrics.wind_direction = dirAvg;
+ m.variant.environment_metrics.wind_gust = gust;
+ m.variant.environment_metrics.wind_lull = lull;
+ m.variant.environment_metrics.voltage =
+ capVoltageF > batVoltageF ? capVoltageF : batVoltageF; // send the larger of the two voltage values.
+
+ LOG_INFO("WS85 Transmit speed=%fm/s, direction=%d , lull=%f, gust=%f, voltage=%f\n",
+ m.variant.environment_metrics.wind_speed, m.variant.environment_metrics.wind_direction,
+ m.variant.environment_metrics.wind_lull, m.variant.environment_metrics.wind_gust,
+ m.variant.environment_metrics.voltage);
+
+ sendTelemetry(m);
+
+ // reset counters and gust/lull
+ velSum = velCount = dirCount = 0;
+ dir_sum_sin = dir_sum_cos = 0;
+ gust = 0;
+ lull = -1;
+ }
+#endif
+ return;
+}
#endif
\ No newline at end of file
diff --git a/src/modules/SerialModule.h b/src/modules/SerialModule.h
index 18ad8a1bab..fa86db28f5 100644
--- a/src/modules/SerialModule.h
+++ b/src/modules/SerialModule.h
@@ -28,6 +28,8 @@ class SerialModule : public StreamAPI, private concurrency::OSThread
private:
uint32_t getBaudRate();
+ void sendTelemetry(meshtastic_Telemetry m);
+ void processWXSerial();
};
extern SerialModule *serialModule;
diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp
index 4f5fbcd131..6d2bf5e015 100644
--- a/src/modules/Telemetry/AirQualityTelemetry.cpp
+++ b/src/modules/Telemetry/AirQualityTelemetry.cpp
@@ -47,7 +47,9 @@ int32_t AirQualityTelemetryModule::runOnce()
uint32_t now = millis();
if (((lastSentToMesh == 0) ||
- ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.air_quality_interval))) &&
+ ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.air_quality_interval,
+ default_telemetry_broadcast_interval_secs,
+ numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
@@ -85,53 +87,90 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
return false; // Let others look at this message also if they want
}
-bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
+bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
{
if (!aqi.read(&data)) {
LOG_WARN("Skipping send measurements. Could not read AQIn\n");
return false;
}
- meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
- m.time = getTime();
- m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
- m.variant.air_quality_metrics.pm10_standard = data.pm10_standard;
- m.variant.air_quality_metrics.pm25_standard = data.pm25_standard;
- m.variant.air_quality_metrics.pm100_standard = data.pm100_standard;
+ m->time = getTime();
+ m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
+ m->variant.air_quality_metrics.pm10_standard = data.pm10_standard;
+ m->variant.air_quality_metrics.pm25_standard = data.pm25_standard;
+ m->variant.air_quality_metrics.pm100_standard = data.pm100_standard;
- m.variant.air_quality_metrics.pm10_environmental = data.pm10_env;
- m.variant.air_quality_metrics.pm25_environmental = data.pm25_env;
- m.variant.air_quality_metrics.pm100_environmental = data.pm100_env;
+ m->variant.air_quality_metrics.pm10_environmental = data.pm10_env;
+ m->variant.air_quality_metrics.pm25_environmental = data.pm25_env;
+ m->variant.air_quality_metrics.pm100_environmental = data.pm100_env;
LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i\n",
- m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard,
- m.variant.air_quality_metrics.pm100_standard);
+ m->variant.air_quality_metrics.pm10_standard, m->variant.air_quality_metrics.pm25_standard,
+ m->variant.air_quality_metrics.pm100_standard);
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n",
- m.variant.air_quality_metrics.pm10_environmental, m.variant.air_quality_metrics.pm25_environmental,
- m.variant.air_quality_metrics.pm100_environmental);
-
- meshtastic_MeshPacket *p = allocDataProtobuf(m);
- p->to = dest;
- p->decoded.want_response = false;
- if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
- p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
- else
- p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
-
- // release previous packet before occupying a new spot
- if (lastMeasurementPacket != nullptr)
- packetPool.release(lastMeasurementPacket);
-
- lastMeasurementPacket = packetPool.allocCopy(*p);
- if (phoneOnly) {
- LOG_INFO("Sending packet to phone\n");
- service.sendToPhone(p);
- } else {
- LOG_INFO("Sending packet to mesh\n");
- service.sendToMesh(p, RX_SRC_LOCAL, true);
- }
+ m->variant.air_quality_metrics.pm10_environmental, m->variant.air_quality_metrics.pm25_environmental,
+ m->variant.air_quality_metrics.pm100_environmental);
+
return true;
}
+meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply()
+{
+ if (currentRequest) {
+ auto req = *currentRequest;
+ const auto &p = req.decoded;
+ meshtastic_Telemetry scratch;
+ meshtastic_Telemetry *decoded = NULL;
+ memset(&scratch, 0, sizeof(scratch));
+ if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
+ decoded = &scratch;
+ } else {
+ LOG_ERROR("Error decoding AirQualityTelemetry module!\n");
+ return NULL;
+ }
+ // Check for a request for air quality metrics
+ if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
+ meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
+ if (getAirQualityTelemetry(&m)) {
+ LOG_INFO("Air quality telemetry replying to request\n");
+ return allocDataProtobuf(m);
+ } else {
+ return NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
+{
+ meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
+ if (getAirQualityTelemetry(&m)) {
+ meshtastic_MeshPacket *p = allocDataProtobuf(m);
+ p->to = dest;
+ p->decoded.want_response = false;
+ if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
+ p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
+ else
+ p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
+
+ // release previous packet before occupying a new spot
+ if (lastMeasurementPacket != nullptr)
+ packetPool.release(lastMeasurementPacket);
+
+ lastMeasurementPacket = packetPool.allocCopy(*p);
+ if (phoneOnly) {
+ LOG_INFO("Sending packet to phone\n");
+ service.sendToPhone(p);
+ } else {
+ LOG_INFO("Sending packet to mesh\n");
+ service.sendToMesh(p, RX_SRC_LOCAL, true);
+ }
+ return true;
+ }
+
+ return false;
+}
+
#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/AirQualityTelemetry.h b/src/modules/Telemetry/AirQualityTelemetry.h
index eb0355001e..23df6ac586 100644
--- a/src/modules/Telemetry/AirQualityTelemetry.h
+++ b/src/modules/Telemetry/AirQualityTelemetry.h
@@ -10,6 +10,10 @@
class AirQualityTelemetryModule : private concurrency::OSThread, public ProtobufModule
{
+ CallbackObserver nodeStatusObserver =
+ CallbackObserver(this,
+ &AirQualityTelemetryModule::handleStatusUpdate);
+
public:
AirQualityTelemetryModule()
: concurrency::OSThread("AirQualityTelemetryModule"),
@@ -18,6 +22,7 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
lastMeasurementPacket = nullptr;
setIntervalFromNow(10 * 1000);
aqi = Adafruit_PM25AQI();
+ nodeStatusObserver.observe(&nodeStatus->onNewStatus);
}
protected:
@@ -26,6 +31,11 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
+ /** Called to get current Air Quality data
+ @return true if it contains valid data
+ */
+ bool getAirQualityTelemetry(meshtastic_Telemetry *m);
+ virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp
index b64e8d1130..9c1ac289ca 100644
--- a/src/modules/Telemetry/DeviceTelemetry.cpp
+++ b/src/modules/Telemetry/DeviceTelemetry.cpp
@@ -17,7 +17,9 @@ int32_t DeviceTelemetryModule::runOnce()
{
refreshUptime();
if (((lastSentToMesh == 0) ||
- ((uptimeLastMs - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval))) &&
+ ((uptimeLastMs - lastSentToMesh) >=
+ Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.device_update_interval,
+ default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil() && config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) {
@@ -52,14 +54,27 @@ bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
{
- if (ignoreRequest) {
- return NULL;
- }
-
- LOG_INFO("Device telemetry replying to request\n");
+ if (currentRequest) {
+ auto req = *currentRequest;
+ const auto &p = req.decoded;
+ meshtastic_Telemetry scratch;
+ meshtastic_Telemetry *decoded = NULL;
+ memset(&scratch, 0, sizeof(scratch));
+ if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
+ decoded = &scratch;
+ } else {
+ LOG_ERROR("Error decoding DeviceTelemetry module!\n");
+ return NULL;
+ }
+ // Check for a request for device metrics
+ if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
+ LOG_INFO("Device telemetry replying to request\n");
- meshtastic_Telemetry telemetry = getDeviceTelemetry();
- return allocDataProtobuf(telemetry);
+ meshtastic_Telemetry telemetry = getDeviceTelemetry();
+ return allocDataProtobuf(telemetry);
+ }
+ }
+ return NULL;
}
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
@@ -104,4 +119,4 @@ bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
service.sendToMesh(p, RX_SRC_LOCAL, true);
}
return true;
-}
+}
\ No newline at end of file
diff --git a/src/modules/Telemetry/DeviceTelemetry.h b/src/modules/Telemetry/DeviceTelemetry.h
index 5f4e761f96..baaf59f280 100644
--- a/src/modules/Telemetry/DeviceTelemetry.h
+++ b/src/modules/Telemetry/DeviceTelemetry.h
@@ -7,6 +7,9 @@
class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModule
{
+ CallbackObserver nodeStatusObserver =
+ CallbackObserver(this, &DeviceTelemetryModule::handleStatusUpdate);
+
public:
DeviceTelemetryModule()
: concurrency::OSThread("DeviceTelemetryModule"),
@@ -14,6 +17,7 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu
{
uptimeWrapCount = 0;
uptimeLastMs = millis();
+ nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(45 * 1000); // Wait until NodeInfo is sent
}
virtual bool wantUIFrame() { return false; }
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index 46b8a1ad80..fec1ee4619 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -27,11 +27,13 @@
#include "Sensor/LPS22HBSensor.h"
#include "Sensor/MCP9808Sensor.h"
#include "Sensor/MLX90632Sensor.h"
+#include "Sensor/NAU7802Sensor.h"
#include "Sensor/OPT3001Sensor.h"
#include "Sensor/RCWL9620Sensor.h"
#include "Sensor/SHT31Sensor.h"
#include "Sensor/SHT4XSensor.h"
#include "Sensor/SHTC3Sensor.h"
+#include "Sensor/T1000xSensor.h"
#include "Sensor/TSL2591Sensor.h"
#include "Sensor/VEML7700Sensor.h"
@@ -51,6 +53,10 @@ RCWL9620Sensor rcwl9620Sensor;
AHT10Sensor aht10Sensor;
MLX90632Sensor mlx90632Sensor;
DFRobotLarkSensor dfRobotLarkSensor;
+NAU7802Sensor nau7802Sensor;
+#ifdef T1000X_SENSOR_EN
+T1000xSensor t1000xSensor;
+#endif
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
@@ -61,7 +67,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
{
if (sleepOnNextExecution == true) {
sleepOnNextExecution = false;
- uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval);
+ uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval,
+ default_telemetry_broadcast_interval_secs);
LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs);
doDeepSleep(nightyNightMs, true);
}
@@ -89,6 +96,9 @@ int32_t EnvironmentTelemetryModule::runOnce()
LOG_INFO("Environment Telemetry: Initializing\n");
// it's possible to have this module enabled, only for displaying values on the screen.
// therefore, we should only enable the sensor loop if measurement is also enabled
+#ifdef T1000X_SENSOR_EN
+ result = t1000xSensor.runOnce();
+#else
if (dfRobotLarkSensor.hasSensor())
result = dfRobotLarkSensor.runOnce();
if (bmp085Sensor.hasSensor())
@@ -113,6 +123,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = ina219Sensor.runOnce();
if (ina260Sensor.hasSensor())
result = ina260Sensor.runOnce();
+ if (ina3221Sensor.hasSensor())
+ result = ina3221Sensor.runOnce();
if (veml7700Sensor.hasSensor())
result = veml7700Sensor.runOnce();
if (tsl2591Sensor.hasSensor())
@@ -125,6 +137,9 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = aht10Sensor.runOnce();
if (mlx90632Sensor.hasSensor())
result = mlx90632Sensor.runOnce();
+ if (nau7802Sensor.hasSensor())
+ result = nau7802Sensor.runOnce();
+#endif
}
return result;
} else {
@@ -138,7 +153,9 @@ int32_t EnvironmentTelemetryModule::runOnce()
uint32_t now = millis();
if (((lastSentToMesh == 0) ||
- ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval))) &&
+ ((now - lastSentToMesh) >=
+ Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.environment_update_interval,
+ default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
@@ -184,7 +201,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
if (lastMeasurementPacket == nullptr) {
// If there's no valid packet, display "Environment"
display->drawString(x, y, "Environment");
- display->drawString(x, y += fontHeight(FONT_SMALL), "No measurement");
+ display->drawString(x, y += _fontHeight(FONT_SMALL), "No measurement");
return;
}
@@ -209,26 +226,32 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
}
// Continue with the remaining details
- display->drawString(x, y += fontHeight(FONT_SMALL),
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
"Temp/Hum: " + last_temp + " / " +
String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%");
if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0) {
- display->drawString(x, y += fontHeight(FONT_SMALL),
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
"Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA");
}
if (lastMeasurement.variant.environment_metrics.voltage != 0) {
- display->drawString(x, y += fontHeight(FONT_SMALL),
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
"Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " +
String(lastMeasurement.variant.environment_metrics.current, 0) + "mA");
}
+
if (lastMeasurement.variant.environment_metrics.iaq != 0) {
- display->drawString(x, y += fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq));
+ display->drawString(x, y += _fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq));
}
+
if (lastMeasurement.variant.environment_metrics.distance != 0)
- display->drawString(x, y += fontHeight(FONT_SMALL),
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
"Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm");
+
+ if (lastMeasurement.variant.environment_metrics.weight != 0)
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
+ "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg");
}
bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
@@ -245,8 +268,9 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", sender, t->variant.environment_metrics.voltage,
t->variant.environment_metrics.iaq, t->variant.environment_metrics.distance, t->variant.environment_metrics.lux);
- LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees\n", sender,
- t->variant.environment_metrics.wind_speed, t->variant.environment_metrics.wind_direction);
+ LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees, weight=%fkg\n", sender,
+ t->variant.environment_metrics.wind_speed, t->variant.environment_metrics.wind_direction,
+ t->variant.environment_metrics.weight);
#endif
// release previous packet before occupying a new spot
@@ -259,94 +283,146 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
return false; // Let others look at this message also if they want
}
-bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
+bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m)
{
- meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
bool valid = true;
bool hasSensor = false;
- m.time = getTime();
- m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
+ m->time = getTime();
+ m->which_variant = meshtastic_Telemetry_environment_metrics_tag;
+#ifdef T1000X_SENSOR_EN // add by WayenWeng
+ valid = valid && t1000xSensor.getMetrics(m);
+ hasSensor = true;
+#else
if (dfRobotLarkSensor.hasSensor()) {
- valid = valid && dfRobotLarkSensor.getMetrics(&m);
+ valid = valid && dfRobotLarkSensor.getMetrics(m);
hasSensor = true;
}
if (sht31Sensor.hasSensor()) {
- valid = valid && sht31Sensor.getMetrics(&m);
+ valid = valid && sht31Sensor.getMetrics(m);
+ hasSensor = true;
+ }
+ if (sht4xSensor.hasSensor()) {
+ valid = valid && sht4xSensor.getMetrics(m);
hasSensor = true;
}
if (lps22hbSensor.hasSensor()) {
- valid = valid && lps22hbSensor.getMetrics(&m);
+ valid = valid && lps22hbSensor.getMetrics(m);
hasSensor = true;
}
if (shtc3Sensor.hasSensor()) {
- valid = valid && shtc3Sensor.getMetrics(&m);
+ valid = valid && shtc3Sensor.getMetrics(m);
hasSensor = true;
}
if (bmp085Sensor.hasSensor()) {
- valid = valid && bmp085Sensor.getMetrics(&m);
+ valid = valid && bmp085Sensor.getMetrics(m);
hasSensor = true;
}
if (bmp280Sensor.hasSensor()) {
- valid = valid && bmp280Sensor.getMetrics(&m);
+ valid = valid && bmp280Sensor.getMetrics(m);
hasSensor = true;
}
if (bme280Sensor.hasSensor()) {
- valid = valid && bme280Sensor.getMetrics(&m);
+ valid = valid && bme280Sensor.getMetrics(m);
hasSensor = true;
}
if (bme680Sensor.hasSensor()) {
- valid = valid && bme680Sensor.getMetrics(&m);
+ valid = valid && bme680Sensor.getMetrics(m);
hasSensor = true;
}
if (mcp9808Sensor.hasSensor()) {
- valid = valid && mcp9808Sensor.getMetrics(&m);
+ valid = valid && mcp9808Sensor.getMetrics(m);
hasSensor = true;
}
if (ina219Sensor.hasSensor()) {
- valid = valid && ina219Sensor.getMetrics(&m);
+ valid = valid && ina219Sensor.getMetrics(m);
hasSensor = true;
}
if (ina260Sensor.hasSensor()) {
- valid = valid && ina260Sensor.getMetrics(&m);
+ valid = valid && ina260Sensor.getMetrics(m);
+ hasSensor = true;
+ }
+ if (ina3221Sensor.hasSensor()) {
+ valid = valid && ina3221Sensor.getMetrics(m);
hasSensor = true;
}
if (veml7700Sensor.hasSensor()) {
- valid = valid && veml7700Sensor.getMetrics(&m);
+ valid = valid && veml7700Sensor.getMetrics(m);
hasSensor = true;
}
if (tsl2591Sensor.hasSensor()) {
- valid = valid && tsl2591Sensor.getMetrics(&m);
+ valid = valid && tsl2591Sensor.getMetrics(m);
hasSensor = true;
}
if (opt3001Sensor.hasSensor()) {
- valid = valid && opt3001Sensor.getMetrics(&m);
+ valid = valid && opt3001Sensor.getMetrics(m);
hasSensor = true;
}
if (mlx90632Sensor.hasSensor()) {
- valid = valid && mlx90632Sensor.getMetrics(&m);
+ valid = valid && mlx90632Sensor.getMetrics(m);
hasSensor = true;
}
if (rcwl9620Sensor.hasSensor()) {
- valid = valid && rcwl9620Sensor.getMetrics(&m);
+ valid = valid && rcwl9620Sensor.getMetrics(m);
+ hasSensor = true;
+ }
+ if (nau7802Sensor.hasSensor()) {
+ valid = valid && nau7802Sensor.getMetrics(m);
hasSensor = true;
}
if (aht10Sensor.hasSensor()) {
if (!bmp280Sensor.hasSensor()) {
- valid = valid && aht10Sensor.getMetrics(&m);
+ valid = valid && aht10Sensor.getMetrics(m);
hasSensor = true;
} else {
// prefer bmp280 temp if both sensors are present, fetch only humidity
meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero;
LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n");
aht10Sensor.getMetrics(&m_ahtx);
- m.variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
+ m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
}
}
- valid = valid && hasSensor;
+#endif
+ return valid && hasSensor;
+}
- if (valid) {
+meshtastic_MeshPacket *EnvironmentTelemetryModule::allocReply()
+{
+ if (currentRequest) {
+ auto req = *currentRequest;
+ const auto &p = req.decoded;
+ meshtastic_Telemetry scratch;
+ meshtastic_Telemetry *decoded = NULL;
+ memset(&scratch, 0, sizeof(scratch));
+ if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
+ decoded = &scratch;
+ } else {
+ LOG_ERROR("Error decoding EnvironmentTelemetry module!\n");
+ return NULL;
+ }
+ // Check for a request for environment metrics
+ if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
+ meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
+ if (getEnvironmentTelemetry(&m)) {
+ LOG_INFO("Environment telemetry replying to request\n");
+ return allocDataProtobuf(m);
+ } else {
+ return NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
+{
+ meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
+#ifdef T1000X_SENSOR_EN
+ if (t1000xSensor.getMetrics(&m)) {
+#else
+ if (getEnvironmentTelemetry(&m)) {
+#endif
LOG_INFO("(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f\n",
m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current,
m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity,
@@ -354,8 +430,8 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
LOG_INFO("(Sending): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", m.variant.environment_metrics.voltage,
m.variant.environment_metrics.iaq, m.variant.environment_metrics.distance, m.variant.environment_metrics.lux);
- LOG_INFO("(Sending): wind speed=%fm/s, direction=%d degrees\n", m.variant.environment_metrics.wind_speed,
- m.variant.environment_metrics.wind_direction);
+ LOG_INFO("(Sending): wind speed=%fm/s, direction=%d degrees, weight=%fkg\n", m.variant.environment_metrics.wind_speed,
+ m.variant.environment_metrics.wind_direction, m.variant.environment_metrics.weight);
sensor_read_error_count = 0;
@@ -384,8 +460,112 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
setIntervalFromNow(5000);
}
}
+ return true;
+ }
+ return false;
+}
+
+AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
+ meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response)
+{
+ AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED;
+ if (dfRobotLarkSensor.hasSensor()) {
+ result = dfRobotLarkSensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (sht31Sensor.hasSensor()) {
+ result = sht31Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (lps22hbSensor.hasSensor()) {
+ result = lps22hbSensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (shtc3Sensor.hasSensor()) {
+ result = shtc3Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (bmp085Sensor.hasSensor()) {
+ result = bmp085Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (bmp280Sensor.hasSensor()) {
+ result = bmp280Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (bme280Sensor.hasSensor()) {
+ result = bme280Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (bme680Sensor.hasSensor()) {
+ result = bme680Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (mcp9808Sensor.hasSensor()) {
+ result = mcp9808Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (ina219Sensor.hasSensor()) {
+ result = ina219Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (ina260Sensor.hasSensor()) {
+ result = ina260Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (ina3221Sensor.hasSensor()) {
+ result = ina3221Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (veml7700Sensor.hasSensor()) {
+ result = veml7700Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (tsl2591Sensor.hasSensor()) {
+ result = tsl2591Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (opt3001Sensor.hasSensor()) {
+ result = opt3001Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (mlx90632Sensor.hasSensor()) {
+ result = mlx90632Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (rcwl9620Sensor.hasSensor()) {
+ result = rcwl9620Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (nau7802Sensor.hasSensor()) {
+ result = nau7802Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (aht10Sensor.hasSensor()) {
+ result = aht10Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
}
- return valid;
+ return result;
}
#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.h b/src/modules/Telemetry/EnvironmentTelemetry.h
index cdd9491d41..59d272e78d 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.h
+++ b/src/modules/Telemetry/EnvironmentTelemetry.h
@@ -11,12 +11,17 @@
class EnvironmentTelemetryModule : private concurrency::OSThread, public ProtobufModule
{
+ CallbackObserver nodeStatusObserver =
+ CallbackObserver(this,
+ &EnvironmentTelemetryModule::handleStatusUpdate);
+
public:
EnvironmentTelemetryModule()
: concurrency::OSThread("EnvironmentTelemetryModule"),
ProtobufModule("EnvironmentTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
{
lastMeasurementPacket = nullptr;
+ nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(10 * 1000);
}
virtual bool wantUIFrame() override;
@@ -32,11 +37,20 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
+ /** Called to get current Environment telemetry data
+ @return true if it contains valid data
+ */
+ bool getEnvironmentTelemetry(meshtastic_Telemetry *m);
+ virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
+ virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
+ meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response) override;
+
private:
float CelsiusToFahrenheit(float c);
bool firstTime = 1;
diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp
index 826de8a4ab..a6f922e563 100644
--- a/src/modules/Telemetry/PowerTelemetry.cpp
+++ b/src/modules/Telemetry/PowerTelemetry.cpp
@@ -24,7 +24,8 @@ int32_t PowerTelemetryModule::runOnce()
{
if (sleepOnNextExecution == true) {
sleepOnNextExecution = false;
- uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval);
+ uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval,
+ default_telemetry_broadcast_interval_secs);
LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs);
doDeepSleep(nightyNightMs, true);
}
@@ -70,7 +71,9 @@ int32_t PowerTelemetryModule::runOnce()
uint32_t now = millis();
if (((lastSentToMesh == 0) ||
- ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval))) &&
+ ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.power_update_interval,
+ default_telemetry_broadcast_interval_secs,
+ numOnlineNodes))) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
lastSentToMesh = now;
@@ -108,7 +111,7 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s
display->drawString(x, y, "Power Telemetry");
if (lastMeasurementPacket == nullptr) {
display->setFont(FONT_SMALL);
- display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement");
+ display->drawString(x, y += _fontHeight(FONT_MEDIUM), "No measurement");
return;
}
@@ -120,22 +123,22 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s
auto &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
display->setFont(FONT_SMALL);
- display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
+ display->drawString(x, y += _fontHeight(FONT_MEDIUM), "Measurement Error");
LOG_ERROR("Unable to decode last packet");
return;
}
display->setFont(FONT_SMALL);
String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C";
- display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
+ display->drawString(x, y += _fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
if (lastMeasurement.variant.power_metrics.ch1_voltage != 0) {
- display->drawString(x, y += fontHeight(FONT_SMALL),
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
"Ch 1 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 0) + "V / " +
String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA");
- display->drawString(x, y += fontHeight(FONT_SMALL),
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
"Ch 2 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 0) + "V / " +
String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA");
- display->drawString(x, y += fontHeight(FONT_SMALL),
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
"Ch 3 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 0) + "V / " +
String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA");
}
@@ -163,29 +166,63 @@ bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &m
return false; // Let others look at this message also if they want
}
-bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
+bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m)
{
- meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
bool valid = false;
- m.time = getTime();
- m.which_variant = meshtastic_Telemetry_power_metrics_tag;
-
- m.variant.power_metrics.ch1_voltage = 0;
- m.variant.power_metrics.ch1_current = 0;
- m.variant.power_metrics.ch2_voltage = 0;
- m.variant.power_metrics.ch2_current = 0;
- m.variant.power_metrics.ch3_voltage = 0;
- m.variant.power_metrics.ch3_current = 0;
+ m->time = getTime();
+ m->which_variant = meshtastic_Telemetry_power_metrics_tag;
+
+ m->variant.power_metrics.ch1_voltage = 0;
+ m->variant.power_metrics.ch1_current = 0;
+ m->variant.power_metrics.ch2_voltage = 0;
+ m->variant.power_metrics.ch2_current = 0;
+ m->variant.power_metrics.ch3_voltage = 0;
+ m->variant.power_metrics.ch3_current = 0;
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
if (ina219Sensor.hasSensor())
- valid = ina219Sensor.getMetrics(&m);
+ valid = ina219Sensor.getMetrics(m);
if (ina260Sensor.hasSensor())
- valid = ina260Sensor.getMetrics(&m);
+ valid = ina260Sensor.getMetrics(m);
if (ina3221Sensor.hasSensor())
- valid = ina3221Sensor.getMetrics(&m);
+ valid = ina3221Sensor.getMetrics(m);
#endif
- if (valid) {
+ return valid;
+}
+
+meshtastic_MeshPacket *PowerTelemetryModule::allocReply()
+{
+ if (currentRequest) {
+ auto req = *currentRequest;
+ const auto &p = req.decoded;
+ meshtastic_Telemetry scratch;
+ meshtastic_Telemetry *decoded = NULL;
+ memset(&scratch, 0, sizeof(scratch));
+ if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
+ decoded = &scratch;
+ } else {
+ LOG_ERROR("Error decoding PowerTelemetry module!\n");
+ return NULL;
+ }
+ // Check for a request for power metrics
+ if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
+ meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
+ if (getPowerTelemetry(&m)) {
+ LOG_INFO("Power telemetry replying to request\n");
+ return allocDataProtobuf(m);
+ } else {
+ return NULL;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
+{
+ meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
+ if (getPowerTelemetry(&m)) {
LOG_INFO("(Sending): ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, "
"ch3_voltage=%f, ch3_current=%f\n",
m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage,
@@ -218,8 +255,9 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
setIntervalFromNow(5000);
}
}
+ return true;
}
- return valid;
+ return false;
}
#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/PowerTelemetry.h b/src/modules/Telemetry/PowerTelemetry.h
index 3d6b686f22..f8248304eb 100644
--- a/src/modules/Telemetry/PowerTelemetry.h
+++ b/src/modules/Telemetry/PowerTelemetry.h
@@ -12,12 +12,16 @@
class PowerTelemetryModule : private concurrency::OSThread, public ProtobufModule
{
+ CallbackObserver nodeStatusObserver =
+ CallbackObserver(this, &PowerTelemetryModule::handleStatusUpdate);
+
public:
PowerTelemetryModule()
: concurrency::OSThread("PowerTelemetryModule"),
ProtobufModule("PowerTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
{
lastMeasurementPacket = nullptr;
+ nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(10 * 1000);
}
virtual bool wantUIFrame() override;
@@ -33,6 +37,11 @@ class PowerTelemetryModule : private concurrency::OSThread, public ProtobufModul
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
+ /** Called to get current Power telemetry data
+ @return true if it contains valid data
+ */
+ bool getPowerTelemetry(meshtastic_Telemetry *m);
+ virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
diff --git a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h
index b26d690b15..7a988e84a5 100644
--- a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h
+++ b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h
@@ -1,3 +1,7 @@
+#pragma once
+
+#ifndef _MT_DFROBOTLARKSENSOR_H
+#define _MT_DFROBOTLARKSENSOR_H
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
@@ -21,4 +25,5 @@ class DFRobotLarkSensor : public TelemetrySensor
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
+#endif
#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
index ea2cb4ea8c..dec99c551c 100644
--- a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
@@ -16,8 +16,7 @@ int32_t INA3221Sensor::runOnce()
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (!status) {
- ina3221.setAddr(INA3221_ADDR42_SDA); // i2c address 0x42
- ina3221.begin();
+ ina3221.begin(nodeTelemetrySensorsMap[sensorType].second);
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
status = true;
} else {
@@ -28,22 +27,69 @@ int32_t INA3221Sensor::runOnce()
void INA3221Sensor::setup() {}
+struct _INA3221Measurement INA3221Sensor::getMeasurement(ina3221_ch_t ch)
+{
+ struct _INA3221Measurement measurement;
+
+ measurement.voltage = ina3221.getVoltage(ch);
+ measurement.current = ina3221.getCurrent(ch);
+
+ return measurement;
+}
+
+struct _INA3221Measurements INA3221Sensor::getMeasurements()
+{
+ struct _INA3221Measurements measurements;
+
+ // INA3221 has 3 channels starting from 0
+ for (int i = 0; i < 3; i++) {
+ measurements.measurements[i] = getMeasurement((ina3221_ch_t)i);
+ }
+
+ return measurements;
+}
+
bool INA3221Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
- measurement->variant.environment_metrics.voltage = ina3221.getVoltage(INA3221_CH1);
- measurement->variant.environment_metrics.current = ina3221.getCurrent(INA3221_CH1);
- measurement->variant.power_metrics.ch1_voltage = ina3221.getVoltage(INA3221_CH1);
- measurement->variant.power_metrics.ch1_current = ina3221.getCurrent(INA3221_CH1);
- measurement->variant.power_metrics.ch2_voltage = ina3221.getVoltage(INA3221_CH2);
- measurement->variant.power_metrics.ch2_current = ina3221.getCurrent(INA3221_CH2);
- measurement->variant.power_metrics.ch3_voltage = ina3221.getVoltage(INA3221_CH3);
- measurement->variant.power_metrics.ch3_current = ina3221.getCurrent(INA3221_CH3);
+ switch (measurement->which_variant) {
+ case meshtastic_Telemetry_environment_metrics_tag:
+ return getEnvironmentMetrics(measurement);
+
+ case meshtastic_Telemetry_power_metrics_tag:
+ return getPowerMetrics(measurement);
+ }
+
+ // unsupported metric
+ return false;
+}
+
+bool INA3221Sensor::getEnvironmentMetrics(meshtastic_Telemetry *measurement)
+{
+ struct _INA3221Measurement m = getMeasurement(ENV_CH);
+
+ measurement->variant.environment_metrics.voltage = m.voltage;
+ measurement->variant.environment_metrics.current = m.current;
+
+ return true;
+}
+
+bool INA3221Sensor::getPowerMetrics(meshtastic_Telemetry *measurement)
+{
+ struct _INA3221Measurements m = getMeasurements();
+
+ measurement->variant.power_metrics.ch1_voltage = m.measurements[INA3221_CH1].voltage;
+ measurement->variant.power_metrics.ch1_current = m.measurements[INA3221_CH1].current;
+ measurement->variant.power_metrics.ch2_voltage = m.measurements[INA3221_CH2].voltage;
+ measurement->variant.power_metrics.ch2_current = m.measurements[INA3221_CH2].current;
+ measurement->variant.power_metrics.ch3_voltage = m.measurements[INA3221_CH3].voltage;
+ measurement->variant.power_metrics.ch3_current = m.measurements[INA3221_CH3].current;
+
return true;
}
uint16_t INA3221Sensor::getBusVoltageMv()
{
- return lround(ina3221.getVoltage(INA3221_CH1) * 1000);
+ return lround(ina3221.getVoltage(BAT_CH) * 1000);
}
#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.h b/src/modules/Telemetry/Sensor/INA3221Sensor.h
index 3b8e382eea..d5121aab60 100644
--- a/src/modules/Telemetry/Sensor/INA3221Sensor.h
+++ b/src/modules/Telemetry/Sensor/INA3221Sensor.h
@@ -12,6 +12,21 @@ class INA3221Sensor : public TelemetrySensor, VoltageSensor
private:
INA3221 ina3221 = INA3221(INA3221_ADDR42_SDA);
+ // channel to report voltage/current for environment metrics
+ ina3221_ch_t ENV_CH = INA3221_CH1;
+
+ // channel to report battery voltage for device_battery_ina_address
+ ina3221_ch_t BAT_CH = INA3221_CH1;
+
+ // get a single measurement for a channel
+ struct _INA3221Measurement getMeasurement(ina3221_ch_t ch);
+
+ // get all measurements for all channels
+ struct _INA3221Measurements getMeasurements();
+
+ bool getEnvironmentMetrics(meshtastic_Telemetry *measurement);
+ bool getPowerMetrics(meshtastic_Telemetry *measurement);
+
protected:
void setup() override;
@@ -22,4 +37,14 @@ class INA3221Sensor : public TelemetrySensor, VoltageSensor
virtual uint16_t getBusVoltageMv() override;
};
+struct _INA3221Measurement {
+ float voltage;
+ float current;
+};
+
+struct _INA3221Measurements {
+ // INA3221 has 3 channels
+ struct _INA3221Measurement measurements[3];
+};
+
#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp
new file mode 100644
index 0000000000..39ac4b08b8
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp
@@ -0,0 +1,143 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "FSCommon.h"
+#include "NAU7802Sensor.h"
+#include "TelemetrySensor.h"
+#include
+#include
+
+meshtastic_Nau7802Config nau7802config = meshtastic_Nau7802Config_init_zero;
+
+NAU7802Sensor::NAU7802Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_NAU7802, "NAU7802") {}
+
+int32_t NAU7802Sensor::runOnce()
+{
+ LOG_INFO("Init sensor: %s\n", sensorName);
+ if (!hasSensor()) {
+ return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
+ }
+ status = nau7802.begin(*nodeTelemetrySensorsMap[sensorType].second);
+ nau7802.setSampleRate(NAU7802_SPS_320);
+ if (!loadCalibrationData()) {
+ LOG_ERROR("Failed to load calibration data\n");
+ }
+ nau7802.calibrateAFE();
+ LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
+ return initI2CSensor();
+}
+
+void NAU7802Sensor::setup() {}
+
+bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement)
+{
+ LOG_DEBUG("NAU7802Sensor::getMetrics\n");
+ nau7802.powerUp();
+ // Wait for the sensor to become ready for one second max
+ uint32_t start = millis();
+ while (!nau7802.available()) {
+ delay(100);
+ if (millis() - start > 1000) {
+ nau7802.powerDown();
+ return false;
+ }
+ }
+ // Check if we have correct calibration values after powerup
+ LOG_DEBUG("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
+ measurement->variant.environment_metrics.weight = nau7802.getWeight() / 1000; // sample is in kg
+ nau7802.powerDown();
+ return true;
+}
+
+void NAU7802Sensor::calibrate(float weight)
+{
+ nau7802.calculateCalibrationFactor(weight * 1000, 64); // internal sample is in grams
+ if (!saveCalibrationData()) {
+ LOG_WARN("Failed to save calibration data\n");
+ }
+ LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
+}
+
+AdminMessageHandleResult NAU7802Sensor::handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response)
+{
+ AdminMessageHandleResult result;
+
+ switch (request->which_payload_variant) {
+ case meshtastic_AdminMessage_set_scale_tag:
+ if (request->set_scale == 0) {
+ this->tare();
+ LOG_DEBUG("Client requested to tare scale\n");
+ } else {
+ this->calibrate(request->set_scale);
+ LOG_DEBUG("Client requested to calibrate to %d kg\n", request->set_scale);
+ }
+ result = AdminMessageHandleResult::HANDLED;
+ break;
+
+ default:
+ result = AdminMessageHandleResult::NOT_HANDLED;
+ }
+
+ return result;
+}
+
+void NAU7802Sensor::tare()
+{
+ nau7802.calculateZeroOffset(64);
+ if (!saveCalibrationData()) {
+ LOG_WARN("Failed to save calibration data\n");
+ }
+ LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
+}
+
+bool NAU7802Sensor::saveCalibrationData()
+{
+ if (FSCom.exists(nau7802ConfigFileName) && !FSCom.remove(nau7802ConfigFileName)) {
+ LOG_WARN("Can't remove old state file\n");
+ }
+ auto file = FSCom.open(nau7802ConfigFileName, FILE_O_WRITE);
+ nau7802config.zeroOffset = nau7802.getZeroOffset();
+ nau7802config.calibrationFactor = nau7802.getCalibrationFactor();
+ bool okay = false;
+ if (file) {
+ LOG_INFO("%s state write to %s.\n", sensorName, nau7802ConfigFileName);
+ pb_ostream_t stream = {&writecb, &file, meshtastic_Nau7802Config_size};
+
+ if (!pb_encode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) {
+ LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
+ } else {
+ okay = true;
+ }
+ file.flush();
+ file.close();
+ } else {
+ LOG_INFO("Can't write %s state (File: %s).\n", sensorName, nau7802ConfigFileName);
+ }
+ return okay;
+}
+
+bool NAU7802Sensor::loadCalibrationData()
+{
+ auto file = FSCom.open(nau7802ConfigFileName, FILE_O_READ);
+ bool okay = false;
+ if (file) {
+ LOG_INFO("%s state read from %s.\n", sensorName, nau7802ConfigFileName);
+ pb_istream_t stream = {&readcb, &file, meshtastic_Nau7802Config_size};
+ if (!pb_decode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) {
+ LOG_ERROR("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream));
+ } else {
+ nau7802.setZeroOffset(nau7802config.zeroOffset);
+ nau7802.setCalibrationFactor(nau7802config.calibrationFactor);
+ okay = true;
+ }
+ file.close();
+ } else {
+ LOG_INFO("No %s state found (File: %s).\n", sensorName, nau7802ConfigFileName);
+ }
+ return okay;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.h b/src/modules/Telemetry/Sensor/NAU7802Sensor.h
new file mode 100644
index 0000000000..c53a3b31a7
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.h
@@ -0,0 +1,31 @@
+#include "MeshModule.h"
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "TelemetrySensor.h"
+#include
+
+class NAU7802Sensor : public TelemetrySensor
+{
+ private:
+ NAU7802 nau7802;
+
+ protected:
+ virtual void setup() override;
+ const char *nau7802ConfigFileName = "/prefs/nau7802.dat";
+ bool saveCalibrationData();
+ bool loadCalibrationData();
+
+ public:
+ NAU7802Sensor();
+ virtual int32_t runOnce() override;
+ virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+ void tare();
+ void calibrate(float weight);
+ AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response) override;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp
index 0d76e2897f..d0e38bf889 100644
--- a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp
@@ -1,7 +1,10 @@
-#include "OPT3001Sensor.h"
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "OPT3001Sensor.h"
#include "TelemetrySensor.h"
-#include "configuration.h"
#include
OPT3001Sensor::OPT3001Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_OPT3001, "OPT3001") {}
@@ -41,4 +44,6 @@ bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement)
LOG_INFO("Lux: %f\n", measurement->variant.environment_metrics.lux);
return true;
-}
\ No newline at end of file
+}
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/OPT3001Sensor.h b/src/modules/Telemetry/Sensor/OPT3001Sensor.h
index 4a8deef218..2ac149319a 100644
--- a/src/modules/Telemetry/Sensor/OPT3001Sensor.h
+++ b/src/modules/Telemetry/Sensor/OPT3001Sensor.h
@@ -1,3 +1,8 @@
+#pragma once
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include
@@ -14,4 +19,6 @@ class OPT3001Sensor : public TelemetrySensor
OPT3001Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
-};
\ No newline at end of file
+};
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/T1000xSensor.cpp b/src/modules/Telemetry/Sensor/T1000xSensor.cpp
new file mode 100644
index 0000000000..e544d0dc5b
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/T1000xSensor.cpp
@@ -0,0 +1,116 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(T1000X_SENSOR_EN)
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "T1000xSensor.h"
+#include "TelemetrySensor.h"
+#include
+
+#define T1000X_SENSE_SAMPLES 15
+#define T1000X_LIGHT_REF_VCC 2400
+
+#define HEATER_NTC_BX 4250 // thermistor coefficient B
+#define HEATER_NTC_RP 8250 // ohm, series resistance to thermistor
+#define HEATER_NTC_KA 273.15 // 25 Celsius at Kelvin
+#define NTC_REF_VCC 3000 // mV, output voltage of LDO
+
+// ntc res table
+uint32_t ntc_res2[136] = {
+ 113347, 107565, 102116, 96978, 92132, 87559, 83242, 79166, 75316, 71677, 68237, 64991, 61919, 59011, 56258, 53650, 51178,
+ 48835, 46613, 44506, 42506, 40600, 38791, 37073, 35442, 33892, 32420, 31020, 29689, 28423, 27219, 26076, 24988, 23951,
+ 22963, 22021, 21123, 20267, 19450, 18670, 17926, 17214, 16534, 15886, 15266, 14674, 14108, 13566, 13049, 12554, 12081,
+ 11628, 11195, 10780, 10382, 10000, 9634, 9284, 8947, 8624, 8315, 8018, 7734, 7461, 7199, 6948, 6707, 6475,
+ 6253, 6039, 5834, 5636, 5445, 5262, 5086, 4917, 4754, 4597, 4446, 4301, 4161, 4026, 3896, 3771, 3651,
+ 3535, 3423, 3315, 3211, 3111, 3014, 2922, 2834, 2748, 2666, 2586, 2509, 2435, 2364, 2294, 2228, 2163,
+ 2100, 2040, 1981, 1925, 1870, 1817, 1766, 1716, 1669, 1622, 1578, 1535, 1493, 1452, 1413, 1375, 1338,
+ 1303, 1268, 1234, 1202, 1170, 1139, 1110, 1081, 1053, 1026, 999, 974, 949, 925, 902, 880, 858,
+};
+
+int8_t ntc_temp2[136] = {
+ -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8,
+ -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+};
+
+T1000xSensor::T1000xSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SENSOR_UNSET, "T1000x") {}
+
+int32_t T1000xSensor::runOnce()
+{
+ LOG_INFO("Init sensor: %s\n", sensorName);
+ if (!hasSensor()) {
+ return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
+ }
+ return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
+}
+
+void T1000xSensor::setup()
+{
+ // Set up oversampling and filter initialization
+}
+
+float T1000xSensor::getLux()
+{
+ uint32_t lux_vot = 0;
+ float lux_level = 0;
+
+ for (uint32_t i = 0; i < T1000X_SENSE_SAMPLES; i++) {
+ lux_vot += analogRead(T1000X_LUX_PIN);
+ }
+ lux_vot = lux_vot / T1000X_SENSE_SAMPLES;
+ lux_vot = ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * lux_vot;
+
+ if (lux_vot <= 80)
+ lux_level = 0;
+ else if (lux_vot >= 2480)
+ lux_level = 100;
+ else
+ lux_level = 100 * (lux_vot - 80) / T1000X_LIGHT_REF_VCC;
+
+ return lux_level;
+}
+
+float T1000xSensor::getTemp()
+{
+ uint32_t vcc_vot = 0, ntc_vot = 0;
+
+ uint8_t u8i = 0;
+ float Vout = 0, Rt = 0, temp = 0;
+ float Temp = 0;
+
+ for (uint32_t i = 0; i < T1000X_SENSE_SAMPLES; i++) {
+ vcc_vot += analogRead(T1000X_VCC_PIN);
+ }
+ vcc_vot = vcc_vot / T1000X_SENSE_SAMPLES;
+ vcc_vot = 2 * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * vcc_vot;
+
+ for (uint32_t i = 0; i < T1000X_SENSE_SAMPLES; i++) {
+ ntc_vot += analogRead(T1000X_NTC_PIN);
+ }
+ ntc_vot = ntc_vot / T1000X_SENSE_SAMPLES;
+ ntc_vot = ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * ntc_vot;
+
+ Vout = ntc_vot;
+ Rt = (HEATER_NTC_RP * vcc_vot) / Vout - HEATER_NTC_RP;
+ for (u8i = 0; u8i < 136; u8i++) {
+ if (Rt >= ntc_res2[u8i]) {
+ break;
+ }
+ }
+ temp = ntc_temp2[u8i - 1] + 1 * (ntc_res2[u8i - 1] - Rt) / (float)(ntc_res2[u8i - 1] - ntc_res2[u8i]);
+ Temp = (temp * 100 + 5) / 100; // half adjust
+
+ return Temp;
+}
+
+bool T1000xSensor::getMetrics(meshtastic_Telemetry *measurement)
+{
+ measurement->variant.environment_metrics.temperature = getTemp();
+ measurement->variant.environment_metrics.lux = getLux();
+ return true;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/T1000xSensor.h b/src/modules/Telemetry/Sensor/T1000xSensor.h
new file mode 100644
index 0000000000..a1c771cfae
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/T1000xSensor.h
@@ -0,0 +1,21 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "TelemetrySensor.h"
+
+class T1000xSensor : public TelemetrySensor
+{
+ protected:
+ virtual void setup() override;
+
+ public:
+ T1000xSensor();
+ virtual int32_t runOnce() override;
+ virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+ virtual float getLux();
+ virtual float getTemp();
+};
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/TelemetrySensor.h b/src/modules/Telemetry/Sensor/TelemetrySensor.h
index 35cb7965d3..da376ad31a 100644
--- a/src/modules/Telemetry/Sensor/TelemetrySensor.h
+++ b/src/modules/Telemetry/Sensor/TelemetrySensor.h
@@ -4,6 +4,7 @@
#pragma once
#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "MeshModule.h"
#include "NodeDB.h"
#include
@@ -42,6 +43,12 @@ class TelemetrySensor
virtual void setup();
public:
+ virtual AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response)
+ {
+ return AdminMessageHandleResult::NOT_HANDLED;
+ }
+
bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; }
virtual int32_t runOnce() = 0;
diff --git a/src/modules/Telemetry/UnitConversions.cpp b/src/modules/Telemetry/UnitConversions.cpp
new file mode 100644
index 0000000000..9f40de40fc
--- /dev/null
+++ b/src/modules/Telemetry/UnitConversions.cpp
@@ -0,0 +1,21 @@
+#include "UnitConversions.h"
+
+float UnitConversions::CelsiusToFahrenheit(float celcius)
+{
+ return (celcius * 9) / 5 + 32;
+}
+
+float UnitConversions::MetersPerSecondToKnots(float metersPerSecond)
+{
+ return metersPerSecond * 1.94384;
+}
+
+float UnitConversions::MetersPerSecondToMilesPerHour(float metersPerSecond)
+{
+ return metersPerSecond * 2.23694;
+}
+
+float UnitConversions::HectoPascalToInchesOfMercury(float hectoPascal)
+{
+ return hectoPascal * 0.029529983071445;
+}
diff --git a/src/modules/Telemetry/UnitConversions.h b/src/modules/Telemetry/UnitConversions.h
new file mode 100644
index 0000000000..60f9b664ae
--- /dev/null
+++ b/src/modules/Telemetry/UnitConversions.h
@@ -0,0 +1,10 @@
+#pragma once
+
+class UnitConversions
+{
+ public:
+ static float CelsiusToFahrenheit(float celcius);
+ static float MetersPerSecondToKnots(float metersPerSecond);
+ static float MetersPerSecondToMilesPerHour(float metersPerSecond);
+ static float HectoPascalToInchesOfMercury(float hectoPascal);
+};
diff --git a/src/modules/TextMessageModule.cpp b/src/modules/TextMessageModule.cpp
index 0f86a6470d..2933718af7 100644
--- a/src/modules/TextMessageModule.cpp
+++ b/src/modules/TextMessageModule.cpp
@@ -2,8 +2,8 @@
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
+#include "buzz.h"
#include "configuration.h"
-
TextMessageModule *textMessageModule;
ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
@@ -12,7 +12,6 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp
auto &p = mp.decoded;
LOG_INFO("Received text msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
#endif
-
// We only store/display messages destined for us.
// Keep a copy of the most recent text message.
devicestate.rx_text_message = mp;
diff --git a/src/modules/WaypointModule.cpp b/src/modules/WaypointModule.cpp
index 83485c8eee..e1974db730 100644
--- a/src/modules/WaypointModule.cpp
+++ b/src/modules/WaypointModule.cpp
@@ -2,6 +2,11 @@
#include "NodeDB.h"
#include "PowerFSM.h"
#include "configuration.h"
+#if HAS_SCREEN
+#include "gps/RTC.h"
+#include "graphics/Screen.h"
+#include "main.h"
+#endif
WaypointModule *waypointModule;
@@ -11,14 +16,171 @@ ProcessMessage WaypointModule::handleReceived(const meshtastic_MeshPacket &mp)
auto &p = mp.decoded;
LOG_INFO("Received waypoint msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
#endif
-
// We only store/display messages destined for us.
// Keep a copy of the most recent text message.
devicestate.rx_waypoint = mp;
devicestate.has_rx_waypoint = true;
powerFSM.trigger(EVENT_RECEIVED_MSG);
- notifyObservers(&mp);
+
+#if HAS_SCREEN
+
+ UIFrameEvent e;
+
+ // New or updated waypoint: focus on this frame next time Screen::setFrames runs
+ if (shouldDraw()) {
+ requestFocus();
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
+ }
+
+ // Deleting an old waypoint: remove the frame quietly, don't change frame position if possible
+ else
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND;
+
+ notifyObservers(&e);
+
+#endif
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
+
+#if HAS_SCREEN
+bool WaypointModule::shouldDraw()
+{
+#if !MESHTASTIC_EXCLUDE_WAYPOINT
+ // If no waypoint to show
+ if (!devicestate.has_rx_waypoint)
+ return false;
+
+ // Decode the message, to find the expiration time (is waypoint still valid)
+ // This handles "deletion" as well as expiration
+ meshtastic_Waypoint wp;
+ memset(&wp, 0, sizeof(wp));
+ if (pb_decode_from_bytes(devicestate.rx_waypoint.decoded.payload.bytes, devicestate.rx_waypoint.decoded.payload.size,
+ &meshtastic_Waypoint_msg, &wp)) {
+ // Valid waypoint
+ if (wp.expire > getTime())
+ return devicestate.has_rx_waypoint = true;
+
+ // Expired, or deleted
+ else
+ return devicestate.has_rx_waypoint = false;
+ }
+
+ // If decoding failed
+ LOG_ERROR("Failed to decode waypoint\n");
+ devicestate.has_rx_waypoint = false;
+ return false;
+#else
+ return false;
+#endif
+}
+
+/// Draw the last waypoint we received
+void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ // Prepare to draw
+ display->setFont(FONT_SMALL);
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+
+ // Handle inverted display
+ // Unsure of expected behavior: for now, copy drawNodeInfo
+ if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED)
+ display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
+
+ // Decode the waypoint
+ meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
+ meshtastic_Waypoint wp;
+ memset(&wp, 0, sizeof(wp));
+ if (!pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &wp)) {
+ // This *should* be caught by shouldDrawWaypoint, but we'll short-circuit here just in case
+ display->drawStringMaxWidth(0 + x, 0 + y, x + display->getWidth(), "Couldn't decode waypoint");
+ devicestate.has_rx_waypoint = false;
+ return;
+ }
+
+ // Get timestamp info. Will pass as a field to drawColumns
+ static char lastStr[20];
+ screen->getTimeAgoStr(sinceReceived(&mp), lastStr, sizeof(lastStr));
+
+ // Will contain distance information, passed as a field to drawColumns
+ static char distStr[20];
+
+ // Get our node, to use our own position
+ meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
+
+ // Text fields to draw (left of compass)
+ // Last element must be NULL. This signals the end of the char*[] to drawColumns
+ const char *fields[] = {"Waypoint", lastStr, wp.name, distStr, NULL};
+
+ // Dimensions / co-ordinates for the compass/circle
+ int16_t compassX = 0, compassY = 0;
+ uint16_t compassDiam = graphics::Screen::getCompassDiam(display->getWidth(), display->getHeight());
+
+ if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
+ compassX = x + display->getWidth() - compassDiam / 2 - 5;
+ compassY = y + display->getHeight() / 2;
+ } else {
+ compassX = x + display->getWidth() - compassDiam / 2 - 5;
+ compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2;
+ }
+
+ // If our node has a position:
+ if (ourNode && (hasValidPosition(ourNode) || screen->hasHeading())) {
+ const meshtastic_PositionLite &op = ourNode->position;
+ float myHeading;
+ if (screen->hasHeading())
+ myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
+ else
+ myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
+ screen->drawCompassNorth(display, compassX, compassY, myHeading);
+
+ // Distance to Waypoint
+ float d = GeoCoord::latLongToMeter(DegD(wp.latitude_i), DegD(wp.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
+ if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
+ if (d < (2 * MILES_TO_FEET))
+ snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET);
+ else
+ snprintf(distStr, sizeof(distStr), "%.1f mi", d * METERS_TO_FEET / MILES_TO_FEET);
+ } else {
+ if (d < 2000)
+ snprintf(distStr, sizeof(distStr), "%.0f m", d);
+ else
+ snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
+ }
+
+ // Compass bearing to waypoint
+ float bearingToOther =
+ GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(wp.latitude_i), DegD(wp.longitude_i));
+ // If the top of the compass is a static north then bearingToOther can be drawn on the compass directly
+ // If the top of the compass is not a static north we need adjust bearingToOther based on heading
+ if (!config.display.compass_north_top)
+ bearingToOther -= myHeading;
+ screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther);
+ }
+
+ // If our node doesn't have position
+ else {
+ // ? in the compass
+ display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
+
+ // ? in the distance field
+ if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
+ strncpy(distStr, "? mi", sizeof(distStr));
+ else
+ strncpy(distStr, "? km", sizeof(distStr));
+ }
+
+ // Undo color-inversion, if set prior to drawing header
+ // Unsure of expected behavior? For now: copy drawNodeInfo
+ if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
+ display->setColor(BLACK);
+ }
+
+ // Draw compass circle
+ display->drawCircle(compassX, compassY, compassDiam / 2);
+
+ // Must be after distStr is populated
+ screen->drawColumns(display, x, y, fields);
+}
+#endif
\ No newline at end of file
diff --git a/src/modules/WaypointModule.h b/src/modules/WaypointModule.h
index ddbabf4deb..4c9c7b86b0 100644
--- a/src/modules/WaypointModule.h
+++ b/src/modules/WaypointModule.h
@@ -5,21 +5,29 @@
/**
* Waypoint message handling for meshtastic
*/
-class WaypointModule : public SinglePortModule, public Observable
+class WaypointModule : public SinglePortModule, public Observable
{
public:
/** Constructor
* name is for debugging output
*/
WaypointModule() : SinglePortModule("waypoint", meshtastic_PortNum_WAYPOINT_APP) {}
-
+#if HAS_SCREEN
+ bool shouldDraw();
+#endif
protected:
/** Called to handle a particular incoming message
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
it
*/
+
+ virtual Observable *getUIFrameObservable() override { return this; }
+#if HAS_SCREEN
+ virtual bool wantUIFrame() override { return this->shouldDraw(); }
+ virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
+#endif
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
};
-extern WaypointModule *waypointModule;
+extern WaypointModule *waypointModule;
\ No newline at end of file
diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp
index 4a7b1c2c60..2e2e4f5287 100644
--- a/src/modules/esp32/AudioModule.cpp
+++ b/src/modules/esp32/AudioModule.cpp
@@ -190,13 +190,13 @@ int32_t AudioModule::runOnce()
firstTime = false;
} else {
- UIFrameEvent e = {false, true};
+ UIFrameEvent e;
// Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive.
if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) {
if (radio_state == RadioState::rx) {
LOG_INFO("PTT pressed, switching to TX\n");
radio_state = RadioState::tx;
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->notifyObservers(&e);
}
} else {
@@ -209,7 +209,7 @@ int32_t AudioModule::runOnce()
}
tx_encode_frame_index = sizeof(tx_header);
radio_state = RadioState::rx;
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->notifyObservers(&e);
}
}
diff --git a/src/modules/esp32/PaxcounterModule.cpp b/src/modules/esp32/PaxcounterModule.cpp
index e6712871d0..8953282345 100644
--- a/src/modules/esp32/PaxcounterModule.cpp
+++ b/src/modules/esp32/PaxcounterModule.cpp
@@ -66,10 +66,6 @@ bool PaxcounterModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, m
meshtastic_MeshPacket *PaxcounterModule::allocReply()
{
- if (ignoreRequest) {
- return NULL;
- }
-
meshtastic_Paxcount pl = meshtastic_Paxcount_init_default;
pl.wifi = count_from_libpax.wifi_count;
pl.ble = count_from_libpax.ble_count;
@@ -84,7 +80,7 @@ int32_t PaxcounterModule::runOnce()
firstTime = false;
LOG_DEBUG("Paxcounter starting up with interval of %d seconds\n",
Default::getConfiguredOrDefault(moduleConfig.paxcounter.paxcounter_update_interval,
- default_broadcast_interval_secs));
+ default_telemetry_broadcast_interval_secs));
struct libpax_config_t configuration;
libpax_default_config(&configuration);
@@ -104,8 +100,8 @@ int32_t PaxcounterModule::runOnce()
} else {
sendInfo(NODENUM_BROADCAST);
}
- return Default::getConfiguredOrDefaultMs(moduleConfig.paxcounter.paxcounter_update_interval,
- default_broadcast_interval_secs);
+ return Default::getConfiguredOrDefaultMsScaled(moduleConfig.paxcounter.paxcounter_update_interval,
+ default_telemetry_broadcast_interval_secs, numOnlineNodes);
} else {
return disable();
}
@@ -131,4 +127,4 @@ void PaxcounterModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state
}
#endif // HAS_SCREEN
-#endif
+#endif
\ No newline at end of file
diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp
index 12cddc5202..ff0f796a1b 100644
--- a/src/modules/esp32/StoreForwardModule.cpp
+++ b/src/modules/esp32/StoreForwardModule.cpp
@@ -35,13 +35,10 @@ int32_t StoreForwardModule::runOnce()
if (moduleConfig.store_forward.enabled && is_server) {
// Send out the message queue.
if (this->busy) {
- // Only send packets if the channel is less than 25% utilized.
- if (airTime->isTxAllowedChannelUtil(true)) {
- storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
- if (this->packetHistoryTXQueue_index < packetHistoryTXQueue_size - 1) {
- this->packetHistoryTXQueue_index++;
- } else {
- this->packetHistoryTXQueue_index = 0;
+ // Only send packets if the channel is less than 25% utilized and until historyReturnMax
+ if (airTime->isTxAllowedChannelUtil(true) && this->requestCount < this->historyReturnMax) {
+ if (!storeForwardModule->sendPayload(this->busyTo, this->last_time)) {
+ this->requestCount = 0;
this->busy = false;
}
}
@@ -75,9 +72,6 @@ void StoreForwardModule::populatePSRAM()
LOG_DEBUG("*** Before PSRAM initialization: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(),
memGet.getFreePsram(), memGet.getPsramSize());
- this->packetHistoryTXQueue =
- static_cast(ps_calloc(this->historyReturnMax, sizeof(PacketHistoryStruct)));
-
/* Use a maximum of 2/3 the available PSRAM unless otherwise specified.
Note: This needs to be done after every thing that would use PSRAM
*/
@@ -95,13 +89,15 @@ void StoreForwardModule::populatePSRAM()
/**
* Sends messages from the message history to the specified recipient.
*
- * @param msAgo The number of milliseconds ago from which to start sending messages.
+ * @param sAgo The number of seconds ago from which to start sending messages.
* @param to The recipient ID to send the messages to.
*/
-void StoreForwardModule::historySend(uint32_t msAgo, uint32_t to)
+void StoreForwardModule::historySend(uint32_t secAgo, uint32_t to)
{
- uint32_t lastIndex = lastRequest.find(to) != lastRequest.end() ? lastRequest[to] : 0;
- uint32_t queueSize = storeForwardModule->historyQueueCreate(msAgo, to, &lastIndex);
+ this->last_time = getTime() < secAgo ? 0 : getTime() - secAgo;
+ uint32_t queueSize = getNumAvailablePackets(to, last_time);
+ if (queueSize > this->historyReturnMax)
+ queueSize = this->historyReturnMax;
if (queueSize) {
LOG_INFO("*** S&F - Sending %u message(s)\n", queueSize);
@@ -114,62 +110,66 @@ void StoreForwardModule::historySend(uint32_t msAgo, uint32_t to)
sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_HISTORY;
sf.which_variant = meshtastic_StoreAndForward_history_tag;
sf.variant.history.history_messages = queueSize;
- sf.variant.history.window = msAgo;
- sf.variant.history.last_request = lastIndex;
- lastRequest[to] = lastIndex;
+ sf.variant.history.window = secAgo * 1000;
+ sf.variant.history.last_request = lastRequest[to];
storeForwardModule->sendMessage(to, sf);
+ setIntervalFromNow(this->packetTimeMax); // Delay start of sending payloads
}
/**
- * Creates a new history queue with messages that were received within the specified time frame.
+ * Returns the number of available packets in the message history for a specified destination node.
*
- * @param msAgo The number of milliseconds ago to start the history queue.
- * @param to The NodeNum of the recipient.
- * @param last_request_index The index in the packet history of the last request from this node.
- * @return The ID of the newly created history queue.
+ * @param dest The destination node number.
+ * @param last_time The relative time to start counting messages from.
+ * @return The number of available packets in the message history.
*/
-uint32_t StoreForwardModule::historyQueueCreate(uint32_t msAgo, uint32_t to, uint32_t *last_request_index)
+uint32_t StoreForwardModule::getNumAvailablePackets(NodeNum dest, uint32_t last_time)
{
+ uint32_t count = 0;
+ if (lastRequest.find(dest) == lastRequest.end()) {
+ lastRequest[dest] = 0;
+ }
+ for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) {
+ if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) {
+ // Client is only interested in packets not from itself and only in broadcast packets or packets towards it.
+ if (this->packetHistory[i].from != dest &&
+ (this->packetHistory[i].to == NODENUM_BROADCAST || this->packetHistory[i].to == dest)) {
+ count++;
+ }
+ }
+ }
+ return count;
+}
- this->packetHistoryTXQueue_size = 0;
- // If our history was cleared, ignore the last request index
- uint32_t last_index = *last_request_index > this->packetHistoryCurrent ? 0 : *last_request_index;
-
- for (uint32_t i = last_index; i < this->packetHistoryCurrent; i++) {
- /*
- LOG_DEBUG("SF historyQueueCreate\n");
- LOG_DEBUG("SF historyQueueCreate - time %d\n", this->packetHistory[i].time);
- LOG_DEBUG("SF historyQueueCreate - millis %d\n", millis());
- LOG_DEBUG("SF historyQueueCreate - math %d\n", (millis() - msAgo));
- */
- if (this->packetHistoryTXQueue_size < this->historyReturnMax) {
- if (this->packetHistory[i].time && (this->packetHistory[i].time < (millis() - msAgo))) {
- /* Copy the messages that were received by the router in the last msAgo
- to the packetHistoryTXQueue structure.
- Client not interested in packets from itself and only in broadcast packets or packets towards it. */
- if (this->packetHistory[i].from != to &&
- (this->packetHistory[i].to == NODENUM_BROADCAST || this->packetHistory[i].to == to)) {
- this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].time = this->packetHistory[i].time;
- this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].to = this->packetHistory[i].to;
- this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].from = this->packetHistory[i].from;
- this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].channel = this->packetHistory[i].channel;
- this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].payload_size =
- this->packetHistory[i].payload_size;
- memcpy(this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].payload, this->packetHistory[i].payload,
- meshtastic_Constants_DATA_PAYLOAD_LEN);
- this->packetHistoryTXQueue_size++;
- *last_request_index = i + 1; // Set to one higher such that we don't send the same message again
-
- LOG_DEBUG("*** PacketHistoryStruct time=%d, msg=%s\n", this->packetHistory[i].time,
- this->packetHistory[i].payload);
- }
+/**
+ * Allocates a mesh packet for sending to the phone.
+ *
+ * @return A pointer to the allocated mesh packet or nullptr if none is available.
+ */
+meshtastic_MeshPacket *StoreForwardModule::getForPhone()
+{
+ if (moduleConfig.store_forward.enabled && is_server) {
+ NodeNum to = nodeDB->getNodeNum();
+ if (!this->busy) {
+ // Get number of packets we're going to send in this loop
+ uint32_t histSize = getNumAvailablePackets(to, 0); // No time limit
+ if (histSize) {
+ this->busy = true;
+ this->busyTo = to;
+ } else {
+ return nullptr;
}
- } else {
- LOG_WARN("*** S&F - Maximum history return reached.\n");
- return this->packetHistoryTXQueue_size;
+ }
+
+ // We're busy with sending to us until no payload is available anymore
+ if (this->busy && this->busyTo == to) {
+ meshtastic_MeshPacket *p = preparePayload(to, 0, true); // No time limit
+ if (!p) // No more messages to send
+ this->busy = false;
+ return p;
}
}
- return this->packetHistoryTXQueue_size;
+ return nullptr;
}
/**
@@ -181,66 +181,97 @@ void StoreForwardModule::historyAdd(const meshtastic_MeshPacket &mp)
{
const auto &p = mp.decoded;
- if (this->packetHistoryCurrent == this->records) {
+ if (this->packetHistoryTotalCount == this->records) {
LOG_WARN("*** S&F - PSRAM Full. Starting overwrite now.\n");
- this->packetHistoryCurrent = 0;
- this->packetHistoryMax = 0;
+ this->packetHistoryTotalCount = 0;
for (auto &i : lastRequest) {
i.second = 0; // Clear the last request index for each client device
}
}
- this->packetHistory[this->packetHistoryCurrent].time = millis();
- this->packetHistory[this->packetHistoryCurrent].to = mp.to;
- this->packetHistory[this->packetHistoryCurrent].channel = mp.channel;
- this->packetHistory[this->packetHistoryCurrent].from = mp.from;
- this->packetHistory[this->packetHistoryCurrent].payload_size = p.payload.size;
- memcpy(this->packetHistory[this->packetHistoryCurrent].payload, p.payload.bytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
+ this->packetHistory[this->packetHistoryTotalCount].time = getTime();
+ this->packetHistory[this->packetHistoryTotalCount].to = mp.to;
+ this->packetHistory[this->packetHistoryTotalCount].channel = mp.channel;
+ this->packetHistory[this->packetHistoryTotalCount].from = getFrom(&mp);
+ this->packetHistory[this->packetHistoryTotalCount].payload_size = p.payload.size;
+ memcpy(this->packetHistory[this->packetHistoryTotalCount].payload, p.payload.bytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
- this->packetHistoryCurrent++;
- this->packetHistoryMax++;
+ this->packetHistoryTotalCount++;
}
-meshtastic_MeshPacket *StoreForwardModule::allocReply()
+/**
+ * Sends a payload to a specified destination node using the store and forward mechanism.
+ *
+ * @param dest The destination node number.
+ * @param last_time The relative time to start sending messages from.
+ * @return True if a packet was successfully sent, false otherwise.
+ */
+bool StoreForwardModule::sendPayload(NodeNum dest, uint32_t last_time)
{
- auto reply = allocDataPacket(); // Allocate a packet for sending
- return reply;
+ meshtastic_MeshPacket *p = preparePayload(dest, last_time);
+ if (p) {
+ LOG_INFO("*** Sending S&F Payload\n");
+ service.sendToMesh(p);
+ this->requestCount++;
+ return true;
+ }
+ return false;
}
/**
- * Sends a payload to a specified destination node using the store and forward mechanism.
+ * Prepares a payload to be sent to a specified destination node from the S&F packet history.
*
* @param dest The destination node number.
- * @param packetHistory_index The index of the packet in the packet history buffer.
+ * @param last_time The relative time to start sending messages from.
+ * @return A pointer to the prepared mesh packet or nullptr if none is available.
*/
-void StoreForwardModule::sendPayload(NodeNum dest, uint32_t packetHistory_index)
+meshtastic_MeshPacket *StoreForwardModule::preparePayload(NodeNum dest, uint32_t last_time, bool local)
{
- LOG_INFO("*** Sending S&F Payload\n");
- meshtastic_MeshPacket *p = allocReply();
+ for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) {
+ if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) {
+ /* Copy the messages that were received by the server in the last msAgo
+ to the packetHistoryTXQueue structure.
+ Client not interested in packets from itself and only in broadcast packets or packets towards it. */
+ if (this->packetHistory[i].from != dest &&
+ (this->packetHistory[i].to == NODENUM_BROADCAST || this->packetHistory[i].to == dest)) {
+
+ meshtastic_MeshPacket *p = allocDataPacket();
+
+ p->to = local ? this->packetHistory[i].to : dest; // PhoneAPI can handle original `to`
+ p->from = this->packetHistory[i].from;
+ p->channel = this->packetHistory[i].channel;
+ p->rx_time = this->packetHistory[i].time;
+
+ // Let's assume that if the server received the S&F request that the client is in range.
+ // TODO: Make this configurable.
+ p->want_ack = false;
+
+ if (local) { // PhoneAPI gets normal TEXT_MESSAGE_APP
+ p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
+ memcpy(p->decoded.payload.bytes, this->packetHistory[i].payload, this->packetHistory[i].payload_size);
+ p->decoded.payload.size = this->packetHistory[i].payload_size;
+ } else {
+ meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero;
+ sf.which_variant = meshtastic_StoreAndForward_text_tag;
+ sf.variant.text.size = this->packetHistory[i].payload_size;
+ memcpy(sf.variant.text.bytes, this->packetHistory[i].payload, this->packetHistory[i].payload_size);
+ if (this->packetHistory[i].to == NODENUM_BROADCAST) {
+ sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_TEXT_BROADCAST;
+ } else {
+ sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_TEXT_DIRECT;
+ }
- p->to = dest;
- p->from = this->packetHistoryTXQueue[packetHistory_index].from;
- p->channel = this->packetHistoryTXQueue[packetHistory_index].channel;
+ p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes),
+ &meshtastic_StoreAndForward_msg, &sf);
+ }
- // Let's assume that if the router received the S&F request that the client is in range.
- // TODO: Make this configurable.
- p->want_ack = false;
+ lastRequest[dest] = i + 1; // Update the last request index for the client device
- meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero;
- sf.which_variant = meshtastic_StoreAndForward_text_tag;
- sf.variant.text.size = this->packetHistoryTXQueue[packetHistory_index].payload_size;
- memcpy(sf.variant.text.bytes, this->packetHistoryTXQueue[packetHistory_index].payload,
- this->packetHistoryTXQueue[packetHistory_index].payload_size);
- if (this->packetHistoryTXQueue[packetHistory_index].to == NODENUM_BROADCAST) {
- sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_TEXT_BROADCAST;
- } else {
- sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_TEXT_DIRECT;
+ return p;
+ }
+ }
}
-
- p->decoded.payload.size =
- pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_StoreAndForward_msg, &sf);
-
- service.sendToMesh(p);
+ return nullptr;
}
/**
@@ -257,11 +288,7 @@ void StoreForwardModule::sendMessage(NodeNum dest, const meshtastic_StoreAndForw
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
- // FIXME - Determine if the delayed packet is broadcast or delayed. For now, assume
- // everything is broadcast.
- p->delayed = meshtastic_MeshPacket_Delayed_DELAYED_BROADCAST;
-
- // Let's assume that if the router received the S&F request that the client is in range.
+ // Let's assume that if the server received the S&F request that the client is in range.
// TODO: Make this configurable.
p->want_ack = false;
p->decoded.want_response = false;
@@ -283,6 +310,35 @@ void StoreForwardModule::sendMessage(NodeNum dest, meshtastic_StoreAndForward_Re
storeForwardModule->sendMessage(dest, sf);
}
+/**
+ * Sends a text message with an error (busy or channel not available) to the specified destination node.
+ *
+ * @param dest The destination node number.
+ * @param want_response True if the original message requested a response, false otherwise.
+ */
+void StoreForwardModule::sendErrorTextMessage(NodeNum dest, bool want_response)
+{
+ meshtastic_MeshPacket *pr = allocDataPacket();
+ pr->to = dest;
+ pr->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
+ pr->want_ack = false;
+ pr->decoded.want_response = false;
+ pr->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
+ const char *str;
+ if (this->busy) {
+ str = "** S&F - Busy. Try again shortly.";
+ } else {
+ str = "** S&F - Not available on this channel.";
+ }
+ LOG_WARN("%s\n", str);
+ memcpy(pr->decoded.payload.bytes, str, strlen(str));
+ pr->decoded.payload.size = strlen(str);
+ if (want_response) {
+ ignoreRequest = true; // This text message counts as response.
+ }
+ service.sendToMesh(pr);
+}
+
/**
* Sends statistics about the store and forward module to the specified node.
*
@@ -294,8 +350,8 @@ void StoreForwardModule::statsSend(uint32_t to)
sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_STATS;
sf.which_variant = meshtastic_StoreAndForward_stats_tag;
- sf.variant.stats.messages_total = this->packetHistoryMax;
- sf.variant.stats.messages_saved = this->packetHistoryCurrent;
+ sf.variant.stats.messages_total = this->records;
+ sf.variant.stats.messages_saved = this->packetHistoryTotalCount;
sf.variant.stats.messages_max = this->records;
sf.variant.stats.up_time = millis() / 1000;
sf.variant.stats.requests = this->requests;
@@ -319,51 +375,37 @@ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &m
#ifdef ARCH_ESP32
if (moduleConfig.store_forward.enabled) {
- // The router node should not be sending messages as a client. Unless he is a ROUTER_CLIENT
- if ((getFrom(&mp) != nodeDB->getNodeNum()) || (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT)) {
-
- if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) {
- auto &p = mp.decoded;
- if (mp.to == nodeDB->getNodeNum() && (p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') &&
- (p.payload.bytes[2] == 0x00)) {
- LOG_DEBUG("*** Legacy Request to send\n");
-
- // Send the last 60 minutes of messages.
- if (this->busy) {
- storeForwardModule->sendMessage(getFrom(&mp), meshtastic_StoreAndForward_RequestResponse_ROUTER_BUSY);
- LOG_INFO("*** S&F - Busy. Try again shortly.\n");
- meshtastic_MeshPacket *pr = allocReply();
- pr->to = getFrom(&mp);
- pr->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
- pr->want_ack = false;
- pr->decoded.want_response = false;
- pr->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
- memcpy(pr->decoded.payload.bytes, "** S&F - Busy. Try again shortly.", 34);
- pr->decoded.payload.size = 34;
- service.sendToMesh(pr);
- } else {
- storeForwardModule->historySend(historyReturnWindow * 60000, getFrom(&mp));
- }
+ if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) {
+ auto &p = mp.decoded;
+ if (mp.to == nodeDB->getNodeNum() && (p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') &&
+ (p.payload.bytes[2] == 0x00)) {
+ LOG_DEBUG("*** Legacy Request to send\n");
+
+ // Send the last 60 minutes of messages.
+ if (this->busy || channels.isDefaultChannel(channels.getByIndex(mp.channel))) {
+ sendErrorTextMessage(getFrom(&mp), mp.decoded.want_response);
} else {
- storeForwardModule->historyAdd(mp);
- LOG_INFO("*** S&F stored. Message history contains %u records now.\n", this->packetHistoryCurrent);
+ storeForwardModule->historySend(historyReturnWindow * 60, getFrom(&mp));
}
- } else if (mp.decoded.portnum == meshtastic_PortNum_STORE_FORWARD_APP) {
- auto &p = mp.decoded;
- meshtastic_StoreAndForward scratch;
- meshtastic_StoreAndForward *decoded = NULL;
- if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
- if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_StoreAndForward_msg, &scratch)) {
- decoded = &scratch;
- } else {
- LOG_ERROR("Error decoding protobuf module!\n");
- // if we can't decode it, nobody can process it!
- return ProcessMessage::STOP;
- }
- return handleReceivedProtobuf(mp, decoded) ? ProcessMessage::STOP : ProcessMessage::CONTINUE;
+ } else {
+ storeForwardModule->historyAdd(mp);
+ LOG_INFO("*** S&F stored. Message history contains %u records now.\n", this->packetHistoryTotalCount);
+ }
+ } else if (getFrom(&mp) != nodeDB->getNodeNum() && mp.decoded.portnum == meshtastic_PortNum_STORE_FORWARD_APP) {
+ auto &p = mp.decoded;
+ meshtastic_StoreAndForward scratch;
+ meshtastic_StoreAndForward *decoded = NULL;
+ if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
+ if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_StoreAndForward_msg, &scratch)) {
+ decoded = &scratch;
+ } else {
+ LOG_ERROR("Error decoding protobuf module!\n");
+ // if we can't decode it, nobody can process it!
+ return ProcessMessage::STOP;
}
- } // all others are irrelevant
- }
+ return handleReceivedProtobuf(mp, decoded) ? ProcessMessage::STOP : ProcessMessage::CONTINUE;
+ }
+ } // all others are irrelevant
}
#endif
@@ -394,7 +436,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
// stop sending stuff, the client wants to abort or has another error
if ((this->busy) && (this->busyTo == getFrom(&mp))) {
LOG_ERROR("*** Client in ERROR or ABORT requested\n");
- this->packetHistoryTXQueue_index = 0;
+ this->requestCount = 0;
this->busy = false;
}
}
@@ -405,15 +447,14 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
requests_history++;
LOG_INFO("*** Client Request to send HISTORY\n");
// Send the last 60 minutes of messages.
- if (this->busy) {
- storeForwardModule->sendMessage(getFrom(&mp), meshtastic_StoreAndForward_RequestResponse_ROUTER_BUSY);
- LOG_INFO("*** S&F - Busy. Try again shortly.\n");
+ if (this->busy || channels.isDefaultChannel(channels.getByIndex(mp.channel))) {
+ sendErrorTextMessage(getFrom(&mp), mp.decoded.want_response);
} else {
if ((p->which_variant == meshtastic_StoreAndForward_history_tag) && (p->variant.history.window > 0)) {
// window is in minutes
- storeForwardModule->historySend(p->variant.history.window * 60000, getFrom(&mp));
+ storeForwardModule->historySend(p->variant.history.window * 60, getFrom(&mp));
} else {
- storeForwardModule->historySend(historyReturnWindow * 60000, getFrom(&mp)); // defaults to 4 hours
+ storeForwardModule->historySend(historyReturnWindow * 60, getFrom(&mp)); // defaults to 4 hours
}
}
}
@@ -451,7 +492,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
if (is_client) {
LOG_DEBUG("*** StoreAndForward_RequestResponse_ROUTER_BUSY\n");
// retry in messages_saved * packetTimeMax ms
- retry_delay = millis() + packetHistoryCurrent * packetTimeMax *
+ retry_delay = millis() + getNumAvailablePackets(this->busyTo, this->last_time) * packetTimeMax *
(meshtastic_StoreAndForward_RequestResponse_ROUTER_ERROR ? 2 : 1);
}
break;
@@ -482,8 +523,6 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
LOG_DEBUG("*** Router Response STATS\n");
// These fields only have informational purpose on a client. Fill them to consume later.
if (p->which_variant == meshtastic_StoreAndForward_stats_tag) {
- this->packetHistoryMax = p->variant.stats.messages_total;
- this->packetHistoryCurrent = p->variant.stats.messages_saved;
this->records = p->variant.stats.messages_max;
this->requests = p->variant.stats.requests;
this->requests_history = p->variant.stats.requests_history;
@@ -508,7 +547,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
default:
break; // no need to do anything
}
- return true; // There's no need for others to look at this message.
+ return false; // RoutingModule sends it to the phone
}
StoreForwardModule::StoreForwardModule()
@@ -532,9 +571,8 @@ StoreForwardModule::StoreForwardModule()
if (moduleConfig.store_forward.enabled) {
// Router
- if ((config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER) ||
- (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT)) {
- LOG_INFO("*** Initializing Store & Forward Module in Router mode\n");
+ if ((config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || moduleConfig.store_forward.is_server)) {
+ LOG_INFO("*** Initializing Store & Forward Module in Server mode\n");
if (memGet.getPsramSize() > 0) {
if (memGet.getFreePsram() >= 1024 * 1024) {
diff --git a/src/modules/esp32/StoreForwardModule.h b/src/modules/esp32/StoreForwardModule.h
index 0d2fb9fceb..e3273470b6 100644
--- a/src/modules/esp32/StoreForwardModule.h
+++ b/src/modules/esp32/StoreForwardModule.h
@@ -25,12 +25,9 @@ class StoreForwardModule : private concurrency::OSThread, public ProtobufModule<
char routerMessage[meshtastic_Constants_DATA_PAYLOAD_LEN] = {0};
PacketHistoryStruct *packetHistory = 0;
- uint32_t packetHistoryCurrent = 0;
- uint32_t packetHistoryMax = 0;
-
- PacketHistoryStruct *packetHistoryTXQueue = 0;
- uint32_t packetHistoryTXQueue_size = 0;
- uint32_t packetHistoryTXQueue_index = 0;
+ uint32_t packetHistoryTotalCount = 0;
+ uint32_t last_time = 0;
+ uint32_t requestCount = 0;
uint32_t packetTimeMax = 5000; // Interval between sending history packets as a server.
@@ -52,18 +49,21 @@ class StoreForwardModule : private concurrency::OSThread, public ProtobufModule<
*/
void historyAdd(const meshtastic_MeshPacket &mp);
void statsSend(uint32_t to);
- void historySend(uint32_t msAgo, uint32_t to);
-
- uint32_t historyQueueCreate(uint32_t msAgo, uint32_t to, uint32_t *last_request_index);
+ void historySend(uint32_t secAgo, uint32_t to);
+ uint32_t getNumAvailablePackets(NodeNum dest, uint32_t last_time);
/**
* Send our payload into the mesh
*/
- void sendPayload(NodeNum dest = NODENUM_BROADCAST, uint32_t packetHistory_index = 0);
+ bool sendPayload(NodeNum dest = NODENUM_BROADCAST, uint32_t packetHistory_index = 0);
+ meshtastic_MeshPacket *preparePayload(NodeNum dest, uint32_t packetHistory_index, bool local = false);
void sendMessage(NodeNum dest, const meshtastic_StoreAndForward &payload);
void sendMessage(NodeNum dest, meshtastic_StoreAndForward_RequestResponse rr);
+ void sendErrorTextMessage(NodeNum dest, bool want_response);
+ meshtastic_MeshPacket *getForPhone();
+ // Returns true if we are configured as server AND we could allocate PSRAM.
+ bool isServer() { return is_server; }
- virtual meshtastic_MeshPacket *allocReply() override;
/*
-Override the wantPacket method.
*/
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 566eb352db..2fce526a09 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -14,11 +14,13 @@
#endif
#include "mesh/generated/meshtastic/remote_hardware.pb.h"
#include "sleep.h"
-#if HAS_WIFI && !MESHTASTIC_EXCLUDE_WIFI
+#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#include
#endif
#include "Default.h"
+#include "serialization/JSON.h"
+#include "serialization/MeshPacketSerializer.h"
#include
const int reconnectMax = 5;
@@ -135,6 +137,10 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length);
return;
} else {
+ if (e.channel_id == NULL || e.gateway_id == NULL) {
+ LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length);
+ return;
+ }
meshtastic_Channel ch = channels.getByName(e.channel_id);
if (strcmp(e.gateway_id, owner.id) == 0) {
// Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message.
@@ -175,7 +181,7 @@ void mqttInit()
new MQTT();
}
-#ifdef HAS_NETWORKING
+#if HAS_NETWORKING
MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient), mqttQueue(MAX_MQTT_QUEUE)
#else
MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
@@ -188,12 +194,10 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
mqtt = this;
if (*moduleConfig.mqtt.root) {
- statusTopic = moduleConfig.mqtt.root + statusTopic;
cryptTopic = moduleConfig.mqtt.root + cryptTopic;
jsonTopic = moduleConfig.mqtt.root + jsonTopic;
mapTopic = moduleConfig.mqtt.root + mapTopic;
} else {
- statusTopic = "msh" + statusTopic;
cryptTopic = "msh" + cryptTopic;
jsonTopic = "msh" + jsonTopic;
mapTopic = "msh" + mapTopic;
@@ -206,7 +210,7 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs);
}
-#ifdef HAS_NETWORKING
+#if HAS_NETWORKING
if (!moduleConfig.mqtt.proxy_to_client_enabled)
pubSub.setCallback(mqttCallback);
#endif
@@ -216,7 +220,7 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
enabled = true;
runASAP = true;
reconnectCount = 0;
- publishStatus();
+ publishNodeInfo();
}
// preflightSleepObserver.observe(&preflightSleep);
} else {
@@ -226,7 +230,7 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
bool MQTT::isConnectedDirectly()
{
-#ifdef HAS_NETWORKING
+#if HAS_NETWORKING
return pubSub.connected();
#else
return false;
@@ -244,7 +248,7 @@ bool MQTT::publish(const char *topic, const char *payload, bool retained)
service.sendMqttMessageToClientProxy(msg);
return true;
}
-#ifdef HAS_NETWORKING
+#if HAS_NETWORKING
else if (isConnectedDirectly()) {
return pubSub.publish(topic, payload, retained);
}
@@ -264,7 +268,7 @@ bool MQTT::publish(const char *topic, const uint8_t *payload, size_t length, boo
service.sendMqttMessageToClientProxy(msg);
return true;
}
-#ifdef HAS_NETWORKING
+#if HAS_NETWORKING
else if (isConnectedDirectly()) {
return pubSub.publish(topic, payload, length, retained);
}
@@ -281,10 +285,10 @@ void MQTT::reconnect()
runASAP = true;
reconnectCount = 0;
- publishStatus();
+ publishNodeInfo();
return; // Don't try to connect directly to the server
}
-#ifdef HAS_NETWORKING
+#if HAS_NETWORKING
// Defaults
int serverPort = 1883;
const char *serverAddr = default_mqtt_address;
@@ -330,15 +334,14 @@ void MQTT::reconnect()
LOG_INFO("Attempting to connect directly to MQTT server %s, port: %d, username: %s, password: %s\n", serverAddr,
serverPort, mqttUsername, mqttPassword);
- auto myStatus = (statusTopic + owner.id);
- bool connected = pubSub.connect(owner.id, mqttUsername, mqttPassword, myStatus.c_str(), 1, true, "offline");
+ bool connected = pubSub.connect(owner.id, mqttUsername, mqttPassword);
if (connected) {
LOG_INFO("MQTT connected\n");
enabled = true; // Start running background process again
runASAP = true;
reconnectCount = 0;
- publishStatus();
+ publishNodeInfo();
sendSubscriptions();
} else {
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
@@ -357,7 +360,7 @@ void MQTT::reconnect()
void MQTT::sendSubscriptions()
{
-#ifdef HAS_NETWORKING
+#if HAS_NETWORKING
size_t numChan = channels.getNumChannels();
for (size_t i = 0; i < numChan; i++) {
const auto &ch = channels.getByIndex(i);
@@ -396,7 +399,7 @@ bool MQTT::wantsLink() const
int32_t MQTT::runOnce()
{
-#ifdef HAS_NETWORKING
+#if HAS_NETWORKING
if (!moduleConfig.mqtt.enabled || !(moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled()))
return disable();
@@ -437,14 +440,10 @@ int32_t MQTT::runOnce()
return 30000;
}
-/// FIXME, include more information in the status text
-void MQTT::publishStatus()
+void MQTT::publishNodeInfo()
{
- auto myStatus = (statusTopic + owner.id);
- bool ok = publish(myStatus.c_str(), "online", true);
- LOG_INFO("published online=%d\n", ok);
+ // TODO: NodeInfo broadcast over MQTT only (NODENUM_BROADCAST_NO_LORA)
}
-
void MQTT::publishQueuedMessages()
{
if (!mqttQueue.isEmpty()) {
@@ -462,7 +461,7 @@ void MQTT::publishQueuedMessages()
#ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804
if (moduleConfig.mqtt.json_enabled) {
// handle json topic
- auto jsonString = this->meshPacketToJson(env->packet);
+ auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet);
if (jsonString.length() != 0) {
std::string topicJson = jsonTopic + env->channel_id + "/" + owner.id;
LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(),
@@ -482,7 +481,12 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
auto &ch = channels.getByIndex(chIndex);
- if (&mp_decoded.decoded && strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 &&
+ if (mp_decoded.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
+ LOG_CRIT("MQTT::onSend(): mp_decoded isn't actually decoded\n");
+ return;
+ }
+
+ if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 &&
(mp_decoded.decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP ||
mp_decoded.decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP)) {
LOG_DEBUG("MQTT onSend - Ignoring range test or detection sensor message on public mqtt\n");
@@ -518,7 +522,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
#ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804
if (moduleConfig.mqtt.json_enabled) {
// handle json topic
- auto jsonString = this->meshPacketToJson((meshtastic_MeshPacket *)&mp_decoded);
+ auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded);
if (jsonString.length() != 0) {
std::string topicJson = jsonTopic + channelId + "/" + owner.id;
LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(),
@@ -619,300 +623,6 @@ void MQTT::perhapsReportToMap()
}
}
-// converts a downstream packet into a json message
-std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
-{
- // the created jsonObj is immutable after creation, so
- // we need to do the heavy lifting before assembling it.
- std::string msgType;
- JSONObject jsonObj;
-
- if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
- JSONObject msgPayload;
- switch (mp->decoded.portnum) {
- case meshtastic_PortNum_TEXT_MESSAGE_APP: {
- msgType = "text";
- // convert bytes to string
- LOG_DEBUG("got text message of size %u\n", mp->decoded.payload.size);
- char payloadStr[(mp->decoded.payload.size) + 1];
- memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
- payloadStr[mp->decoded.payload.size] = 0; // null terminated string
- // check if this is a JSON payload
- JSONValue *json_value = JSON::Parse(payloadStr);
- if (json_value != NULL) {
- LOG_INFO("text message payload is of type json\n");
- // if it is, then we can just use the json object
- jsonObj["payload"] = json_value;
- } else {
- // if it isn't, then we need to create a json object
- // with the string as the value
- LOG_INFO("text message payload is of type plaintext\n");
- msgPayload["text"] = new JSONValue(payloadStr);
- jsonObj["payload"] = new JSONValue(msgPayload);
- }
- break;
- }
- case meshtastic_PortNum_TELEMETRY_APP: {
- msgType = "telemetry";
- meshtastic_Telemetry scratch;
- meshtastic_Telemetry *decoded = NULL;
- memset(&scratch, 0, sizeof(scratch));
- if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
- decoded = &scratch;
- if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
- msgPayload["battery_level"] = new JSONValue((unsigned int)decoded->variant.device_metrics.battery_level);
- msgPayload["voltage"] = new JSONValue(decoded->variant.device_metrics.voltage);
- msgPayload["channel_utilization"] = new JSONValue(decoded->variant.device_metrics.channel_utilization);
- msgPayload["air_util_tx"] = new JSONValue(decoded->variant.device_metrics.air_util_tx);
- msgPayload["uptime_seconds"] = new JSONValue((unsigned int)decoded->variant.device_metrics.uptime_seconds);
- } else if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
- msgPayload["temperature"] = new JSONValue(decoded->variant.environment_metrics.temperature);
- msgPayload["relative_humidity"] = new JSONValue(decoded->variant.environment_metrics.relative_humidity);
- msgPayload["barometric_pressure"] = new JSONValue(decoded->variant.environment_metrics.barometric_pressure);
- msgPayload["gas_resistance"] = new JSONValue(decoded->variant.environment_metrics.gas_resistance);
- msgPayload["voltage"] = new JSONValue(decoded->variant.environment_metrics.voltage);
- msgPayload["current"] = new JSONValue(decoded->variant.environment_metrics.current);
- msgPayload["lux"] = new JSONValue(decoded->variant.environment_metrics.lux);
- msgPayload["white_lux"] = new JSONValue(decoded->variant.environment_metrics.white_lux);
- msgPayload["iaq"] = new JSONValue((uint)decoded->variant.environment_metrics.iaq);
- msgPayload["wind_speed"] = new JSONValue((uint)decoded->variant.environment_metrics.wind_speed);
- msgPayload["wind_direction"] = new JSONValue((uint)decoded->variant.environment_metrics.wind_direction);
- } else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
- msgPayload["voltage_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_voltage);
- msgPayload["current_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_current);
- msgPayload["voltage_ch2"] = new JSONValue(decoded->variant.power_metrics.ch2_voltage);
- msgPayload["current_ch2"] = new JSONValue(decoded->variant.power_metrics.ch2_current);
- msgPayload["voltage_ch3"] = new JSONValue(decoded->variant.power_metrics.ch3_voltage);
- msgPayload["current_ch3"] = new JSONValue(decoded->variant.power_metrics.ch3_current);
- }
- jsonObj["payload"] = new JSONValue(msgPayload);
- } else {
- LOG_ERROR("Error decoding protobuf for telemetry message!\n");
- }
- break;
- }
- case meshtastic_PortNum_NODEINFO_APP: {
- msgType = "nodeinfo";
- meshtastic_User scratch;
- meshtastic_User *decoded = NULL;
- memset(&scratch, 0, sizeof(scratch));
- if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) {
- decoded = &scratch;
- msgPayload["id"] = new JSONValue(decoded->id);
- msgPayload["longname"] = new JSONValue(decoded->long_name);
- msgPayload["shortname"] = new JSONValue(decoded->short_name);
- msgPayload["hardware"] = new JSONValue(decoded->hw_model);
- jsonObj["payload"] = new JSONValue(msgPayload);
- } else {
- LOG_ERROR("Error decoding protobuf for nodeinfo message!\n");
- }
- break;
- }
- case meshtastic_PortNum_POSITION_APP: {
- msgType = "position";
- meshtastic_Position scratch;
- meshtastic_Position *decoded = NULL;
- memset(&scratch, 0, sizeof(scratch));
- if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) {
- decoded = &scratch;
- if ((int)decoded->time) {
- msgPayload["time"] = new JSONValue((unsigned int)decoded->time);
- }
- if ((int)decoded->timestamp) {
- msgPayload["timestamp"] = new JSONValue((unsigned int)decoded->timestamp);
- }
- msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i);
- msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i);
- if ((int)decoded->altitude) {
- msgPayload["altitude"] = new JSONValue((int)decoded->altitude);
- }
- if ((int)decoded->ground_speed) {
- msgPayload["ground_speed"] = new JSONValue((unsigned int)decoded->ground_speed);
- }
- if (int(decoded->ground_track)) {
- msgPayload["ground_track"] = new JSONValue((unsigned int)decoded->ground_track);
- }
- if (int(decoded->sats_in_view)) {
- msgPayload["sats_in_view"] = new JSONValue((unsigned int)decoded->sats_in_view);
- }
- if ((int)decoded->PDOP) {
- msgPayload["PDOP"] = new JSONValue((int)decoded->PDOP);
- }
- if ((int)decoded->HDOP) {
- msgPayload["HDOP"] = new JSONValue((int)decoded->HDOP);
- }
- if ((int)decoded->VDOP) {
- msgPayload["VDOP"] = new JSONValue((int)decoded->VDOP);
- }
- if ((int)decoded->precision_bits) {
- msgPayload["precision_bits"] = new JSONValue((int)decoded->precision_bits);
- }
- jsonObj["payload"] = new JSONValue(msgPayload);
- } else {
- LOG_ERROR("Error decoding protobuf for position message!\n");
- }
- break;
- }
- case meshtastic_PortNum_WAYPOINT_APP: {
- msgType = "position";
- meshtastic_Waypoint scratch;
- meshtastic_Waypoint *decoded = NULL;
- memset(&scratch, 0, sizeof(scratch));
- if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
- decoded = &scratch;
- msgPayload["id"] = new JSONValue((unsigned int)decoded->id);
- msgPayload["name"] = new JSONValue(decoded->name);
- msgPayload["description"] = new JSONValue(decoded->description);
- msgPayload["expire"] = new JSONValue((unsigned int)decoded->expire);
- msgPayload["locked_to"] = new JSONValue((unsigned int)decoded->locked_to);
- msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i);
- msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i);
- jsonObj["payload"] = new JSONValue(msgPayload);
- } else {
- LOG_ERROR("Error decoding protobuf for position message!\n");
- }
- break;
- }
- case meshtastic_PortNum_NEIGHBORINFO_APP: {
- msgType = "neighborinfo";
- meshtastic_NeighborInfo scratch;
- meshtastic_NeighborInfo *decoded = NULL;
- memset(&scratch, 0, sizeof(scratch));
- if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg,
- &scratch)) {
- decoded = &scratch;
- msgPayload["node_id"] = new JSONValue((unsigned int)decoded->node_id);
- msgPayload["node_broadcast_interval_secs"] = new JSONValue((unsigned int)decoded->node_broadcast_interval_secs);
- msgPayload["last_sent_by_id"] = new JSONValue((unsigned int)decoded->last_sent_by_id);
- msgPayload["neighbors_count"] = new JSONValue(decoded->neighbors_count);
- JSONArray neighbors;
- for (uint8_t i = 0; i < decoded->neighbors_count; i++) {
- JSONObject neighborObj;
- neighborObj["node_id"] = new JSONValue((unsigned int)decoded->neighbors[i].node_id);
- neighborObj["snr"] = new JSONValue((int)decoded->neighbors[i].snr);
- neighbors.push_back(new JSONValue(neighborObj));
- }
- msgPayload["neighbors"] = new JSONValue(neighbors);
- jsonObj["payload"] = new JSONValue(msgPayload);
- } else {
- LOG_ERROR("Error decoding protobuf for neighborinfo message!\n");
- }
- break;
- }
- case meshtastic_PortNum_TRACEROUTE_APP: {
- if (mp->decoded.request_id) { // Only report the traceroute response
- msgType = "traceroute";
- meshtastic_RouteDiscovery scratch;
- meshtastic_RouteDiscovery *decoded = NULL;
- memset(&scratch, 0, sizeof(scratch));
- if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_RouteDiscovery_msg,
- &scratch)) {
- decoded = &scratch;
- JSONArray route; // Route this message took
- // Lambda function for adding a long name to the route
- auto addToRoute = [](JSONArray *route, NodeNum num) {
- char long_name[40] = "Unknown";
- meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
- bool name_known = node ? node->has_user : false;
- if (name_known)
- memcpy(long_name, node->user.long_name, sizeof(long_name));
- route->push_back(new JSONValue(long_name));
- };
- addToRoute(&route, mp->to); // Started at the original transmitter (destination of response)
- for (uint8_t i = 0; i < decoded->route_count; i++) {
- addToRoute(&route, decoded->route[i]);
- }
- addToRoute(&route, mp->from); // Ended at the original destination (source of response)
-
- msgPayload["route"] = new JSONValue(route);
- jsonObj["payload"] = new JSONValue(msgPayload);
- } else {
- LOG_ERROR("Error decoding protobuf for traceroute message!\n");
- }
- }
- break;
- }
- case meshtastic_PortNum_DETECTION_SENSOR_APP: {
- msgType = "detection";
- char payloadStr[(mp->decoded.payload.size) + 1];
- memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
- payloadStr[mp->decoded.payload.size] = 0; // null terminated string
- msgPayload["text"] = new JSONValue(payloadStr);
- jsonObj["payload"] = new JSONValue(msgPayload);
- break;
- }
-#ifdef ARCH_ESP32
- case meshtastic_PortNum_PAXCOUNTER_APP: {
- msgType = "paxcounter";
- meshtastic_Paxcount scratch;
- meshtastic_Paxcount *decoded = NULL;
- memset(&scratch, 0, sizeof(scratch));
- if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Paxcount_msg, &scratch)) {
- decoded = &scratch;
- msgPayload["wifi_count"] = new JSONValue((unsigned int)decoded->wifi);
- msgPayload["ble_count"] = new JSONValue((unsigned int)decoded->ble);
- msgPayload["uptime"] = new JSONValue((unsigned int)decoded->uptime);
- jsonObj["payload"] = new JSONValue(msgPayload);
- } else {
- LOG_ERROR("Error decoding protobuf for Paxcount message!\n");
- }
- break;
- }
-#endif
- case meshtastic_PortNum_REMOTE_HARDWARE_APP: {
- meshtastic_HardwareMessage scratch;
- meshtastic_HardwareMessage *decoded = NULL;
- memset(&scratch, 0, sizeof(scratch));
- if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_HardwareMessage_msg,
- &scratch)) {
- decoded = &scratch;
- if (decoded->type == meshtastic_HardwareMessage_Type_GPIOS_CHANGED) {
- msgType = "gpios_changed";
- msgPayload["gpio_value"] = new JSONValue((unsigned int)decoded->gpio_value);
- jsonObj["payload"] = new JSONValue(msgPayload);
- } else if (decoded->type == meshtastic_HardwareMessage_Type_READ_GPIOS_REPLY) {
- msgType = "gpios_read_reply";
- msgPayload["gpio_value"] = new JSONValue((unsigned int)decoded->gpio_value);
- msgPayload["gpio_mask"] = new JSONValue((unsigned int)decoded->gpio_mask);
- jsonObj["payload"] = new JSONValue(msgPayload);
- }
- } else {
- LOG_ERROR("Error decoding protobuf for RemoteHardware message!\n");
- }
- break;
- }
- // add more packet types here if needed
- default:
- break;
- }
- } else {
- LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON\n");
- }
-
- jsonObj["id"] = new JSONValue((unsigned int)mp->id);
- jsonObj["timestamp"] = new JSONValue((unsigned int)mp->rx_time);
- jsonObj["to"] = new JSONValue((unsigned int)mp->to);
- jsonObj["from"] = new JSONValue((unsigned int)mp->from);
- jsonObj["channel"] = new JSONValue((unsigned int)mp->channel);
- jsonObj["type"] = new JSONValue(msgType.c_str());
- jsonObj["sender"] = new JSONValue(owner.id);
- if (mp->rx_rssi != 0)
- jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
- if (mp->rx_snr != 0)
- jsonObj["snr"] = new JSONValue((float)mp->rx_snr);
- if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start)
- jsonObj["hops_away"] = new JSONValue((unsigned int)(mp->hop_start - mp->hop_limit));
-
- // serialize and write it to the stream
- JSONValue *value = new JSONValue(jsonObj);
- std::string jsonStr = value->Stringify();
-
- LOG_INFO("serialized json message: %s\n", jsonStr.c_str());
-
- delete value;
- return jsonStr;
-}
-
bool MQTT::isValidJsonEnvelope(JSONObject &json)
{
// if "sender" is provided, avoid processing packets we uplinked
diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h
index f2eb6b1204..ba09877839 100644
--- a/src/mqtt/MQTT.h
+++ b/src/mqtt/MQTT.h
@@ -5,20 +5,18 @@
#include "concurrency/OSThread.h"
#include "mesh/Channels.h"
#include "mesh/generated/meshtastic/mqtt.pb.h"
-#include "mqtt/JSON.h"
+#include "serialization/JSON.h"
#if HAS_WIFI
#include
-#define HAS_NETWORKING 1
#if !defined(ARCH_PORTDUINO)
#include
#endif
#endif
#if HAS_ETHERNET
#include
-#define HAS_NETWORKING 1
#endif
-#ifdef HAS_NETWORKING
+#if HAS_NETWORKING
#include
#endif
@@ -43,7 +41,7 @@ class MQTT : private concurrency::OSThread
#endif
public:
-#ifdef HAS_NETWORKING
+#if HAS_NETWORKING
PubSubClient pubSub;
#endif
MQTT();
@@ -83,10 +81,9 @@ class MQTT : private concurrency::OSThread
virtual int32_t runOnce() override;
private:
- std::string statusTopic = "/2/stat/"; // For "online"/"offline" message
- std::string cryptTopic = "/2/e/"; // msh/2/e/CHANNELID/NODEID
- std::string jsonTopic = "/2/json/"; // msh/2/json/CHANNELID/NODEID
- std::string mapTopic = "/2/map/"; // For protobuf-encoded MapReport messages
+ std::string cryptTopic = "/2/e/"; // msh/2/e/CHANNELID/NODEID
+ std::string jsonTopic = "/2/json/"; // msh/2/json/CHANNELID/NODEID
+ std::string mapTopic = "/2/map/"; // For protobuf-encoded MapReport messages
// For map reporting (only applies when enabled)
const uint32_t default_map_position_precision = 14; // defaults to max. offset of ~1459m
@@ -109,12 +106,10 @@ class MQTT : private concurrency::OSThread
/// Called when a new publish arrives from the MQTT server
void onReceive(char *topic, byte *payload, size_t length);
- /// Called when a new publish arrives from the MQTT server
- std::string meshPacketToJson(meshtastic_MeshPacket *mp);
-
- void publishStatus();
void publishQueuedMessages();
+ void publishNodeInfo();
+
// Check if we should report unencrypted information about our node for consumption by a map
void perhapsReportToMap();
diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp
index 68aa9b4653..d959553a4b 100644
--- a/src/nimble/NimbleBluetooth.cpp
+++ b/src/nimble/NimbleBluetooth.cpp
@@ -12,6 +12,7 @@
NimBLECharacteristic *fromNumCharacteristic;
NimBLECharacteristic *BatteryCharacteristic;
+NimBLECharacteristic *logRadioCharacteristic;
NimBLEServer *bleServer;
static bool passkeyShowing;
@@ -58,7 +59,6 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
{
virtual void onRead(NimBLECharacteristic *pCharacteristic)
{
- LOG_INFO("From Radio onread\n");
uint8_t fromRadioBytes[meshtastic_FromRadio_size];
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
@@ -82,7 +82,33 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
LOG_INFO("*** Enter passkey %d on the peer side ***\n", passkey);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
- screen->startBluetoothPinScreen(passkey);
+#if HAS_SCREEN
+ screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
+ char btPIN[16] = "888888";
+ snprintf(btPIN, sizeof(btPIN), "%06u", passkey);
+ int x_offset = display->width() / 2;
+ int y_offset = display->height() <= 80 ? 0 : 32;
+ display->setTextAlignment(TEXT_ALIGN_CENTER);
+ display->setFont(FONT_MEDIUM);
+ display->drawString(x_offset + x, y_offset + y, "Bluetooth");
+
+ display->setFont(FONT_SMALL);
+ y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
+ display->drawString(x_offset + x, y_offset + y, "Enter this code");
+
+ display->setFont(FONT_LARGE);
+ String displayPin(btPIN);
+ String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
+ y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
+ display->drawString(x_offset + x, y_offset + y, pin);
+
+ display->setFont(FONT_SMALL);
+ String deviceName = "Name: ";
+ deviceName.concat(getDeviceName());
+ y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
+ display->drawString(x_offset + x, y_offset + y, deviceName);
+ });
+#endif
passkeyShowing = true;
return passkey;
@@ -94,7 +120,7 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
if (passkeyShowing) {
passkeyShowing = false;
- screen->stopBluetoothPinScreen();
+ screen->endAlert();
}
}
@@ -180,6 +206,8 @@ void NimbleBluetooth::setupService()
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ);
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
+ logRadioCharacteristic =
+ bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ, 512U);
} else {
ToRadioCharacteristic = bleService->createCharacteristic(
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
@@ -188,6 +216,9 @@ void NimbleBluetooth::setupService()
fromNumCharacteristic =
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
+ logRadioCharacteristic = bleService->createCharacteristic(
+ LOGRADIO_UUID,
+ NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC, 512U);
}
bluetoothPhoneAPI = new BluetoothPhoneAPI();
@@ -236,6 +267,14 @@ void NimbleBluetooth::clearBonds()
NimBLEDevice::deleteAllBonds();
}
+void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
+{
+ if (!bleServer || !isConnected() || length > 512) {
+ return;
+ }
+ logRadioCharacteristic->notify(logMessage, length, true);
+}
+
void clearNVS()
{
NimBLEDevice::deleteAllBonds();
diff --git a/src/nimble/NimbleBluetooth.h b/src/nimble/NimbleBluetooth.h
index d1e347830a..45602e0887 100644
--- a/src/nimble/NimbleBluetooth.h
+++ b/src/nimble/NimbleBluetooth.h
@@ -11,6 +11,7 @@ class NimbleBluetooth : BluetoothApi
bool isActive();
bool isConnected();
int getRssi();
+ void sendLog(const uint8_t *logMessage, size_t length);
private:
void setupService();
diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h
index 824c11bdd3..fd3f92a9c3 100644
--- a/src/platform/esp32/architecture.h
+++ b/src/platform/esp32/architecture.h
@@ -101,6 +101,8 @@
#define HW_VENDOR meshtastic_HardwareModel_STATION_G1
#elif defined(DR_DEV)
#define HW_VENDOR meshtastic_HardwareModel_DR_DEV
+#elif defined(HELTEC_HRU_3601)
+#define HW_VENDOR meshtastic_HardwareModel_HELTEC_HRU_3601
#elif defined(HELTEC_V3)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_V3
#elif defined(HELTEC_WSL_V3)
@@ -147,6 +149,16 @@
#define HW_VENDOR meshtastic_HardwareModel_WIPHONE
#elif defined(RADIOMASTER_900_BANDIT_NANO)
#define HW_VENDOR meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO
+#elif defined(HELTEC_CAPSULE_SENSOR_V3)
+#define HW_VENDOR meshtastic_HardwareModel_HELTEC_CAPSULE_SENSOR_V3
+#elif defined(HELTEC_VISION_MASTER_T190)
+#define HW_VENDOR meshtastic_HardwareModel_HELTEC_VISION_MASTER_T190
+#elif defined(HELTEC_VISION_MASTER_E213)
+#define HW_VENDOR meshtastic_HardwareModel_HELTEC_VISION_MASTER_E213
+#elif defined(HELTEC_VISION_MASTER_E290)
+#define HW_VENDOR meshtastic_HardwareModel_HELTEC_VISION_MASTER_E290
+#elif defined(HELTEC_MESH_NODE_T114)
+#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_NODE_T114
#endif
// -----------------------------------------------------------------------------
diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp
index 1dd7a389af..3910f718f1 100644
--- a/src/platform/esp32/main-esp32.cpp
+++ b/src/platform/esp32/main-esp32.cpp
@@ -8,7 +8,7 @@
#include "nimble/NimbleBluetooth.h"
#endif
-#if !MESHTASTIC_EXCLUDE_WIFI
+#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
@@ -24,23 +24,22 @@
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
void setBluetoothEnable(bool enable)
{
-#ifndef MESHTASTIC_EXCLUDE_WIFI
+#if HAS_WIFI
if (!isWifiAvailable() && config.bluetooth.enabled == true)
+#else
+ if (config.bluetooth.enabled == true)
#endif
-#ifdef MESHTASTIC_EXCLUDE_WIFI
- if (config.bluetooth.enabled == true)
-#endif
- {
- if (!nimbleBluetooth) {
- nimbleBluetooth = new NimbleBluetooth();
- }
- if (enable && !nimbleBluetooth->isActive()) {
- nimbleBluetooth->setup();
- }
- // 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
+ {
+ if (!nimbleBluetooth) {
+ nimbleBluetooth = new NimbleBluetooth();
+ }
+ if (enable && !nimbleBluetooth->isActive()) {
+ nimbleBluetooth->setup();
}
+ // 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
void setBluetoothEnable(bool enable) {}
@@ -92,8 +91,12 @@ void enableSlowCLK()
void esp32Setup()
{
+ /* We explicitly don't want to do call randomSeed,
+ // as that triggers the esp32 core to use a less secure pseudorandom function.
uint32_t seed = esp_random();
LOG_DEBUG("Setting random seed %u\n", seed);
+ randomSeed(seed);
+ */
LOG_DEBUG("Total heap: %d\n", ESP.getHeapSize());
LOG_DEBUG("Free heap: %d\n", ESP.getFreeHeap());
diff --git a/src/platform/nrf52/BLEDfuScure.cpp b/src/platform/nrf52/BLEDfuScure.cpp
new file mode 100644
index 0000000000..8f7a327c4f
--- /dev/null
+++ b/src/platform/nrf52/BLEDfuScure.cpp
@@ -0,0 +1,144 @@
+/**************************************************************************/
+/*!
+ @file BLEDfu.cpp
+ @author hathach (tinyusb.org)
+
+ @section LICENSE
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2018, Adafruit Industries (adafruit.com)
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holders nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+
+#include "BLEDfuSecure.h"
+#include "bluefruit.h"
+
+#define DFU_REV_APPMODE 0x0001
+
+const uint16_t UUID16_SVC_DFU_OTA = 0xFE59;
+
+const uint8_t UUID128_CHR_DFU_CONTROL[16] = {0x50, 0xEA, 0xDA, 0x30, 0x88, 0x83, 0xB8, 0x9F,
+ 0x60, 0x4F, 0x15, 0xF3, 0x03, 0x00, 0xC9, 0x8E};
+
+extern "C" void bootloader_util_app_start(uint32_t start_addr);
+
+static uint16_t crc16(const uint8_t *data_p, uint8_t length)
+{
+ uint8_t x;
+ uint16_t crc = 0xFFFF;
+
+ while (length--) {
+ x = crc >> 8 ^ *data_p++;
+ x ^= x >> 4;
+ crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x << 5)) ^ ((uint16_t)x);
+ }
+ return crc;
+}
+
+static void bledfu_control_wr_authorize_cb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_write_t *request)
+{
+ if ((request->handle == chr->handles().value_handle) && (request->op != BLE_GATTS_OP_PREP_WRITE_REQ) &&
+ (request->op != BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) && (request->op != BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)) {
+ BLEConnection *conn = Bluefruit.Connection(conn_hdl);
+
+ ble_gatts_rw_authorize_reply_params_t reply = {.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE};
+
+ if (!chr->indicateEnabled(conn_hdl)) {
+ reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_CPS_CCCD_CONFIG_ERROR;
+ sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
+ return;
+ }
+
+ reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
+ sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
+
+ enum { START_DFU = 1 };
+ if (request->data[0] == START_DFU) {
+ // Peer data information so that bootloader could re-connect after reboot
+ typedef struct {
+ ble_gap_addr_t addr;
+ ble_gap_irk_t irk;
+ ble_gap_enc_key_t enc_key;
+ uint8_t sys_attr[8];
+ uint16_t crc16;
+ } peer_data_t;
+
+ VERIFY_STATIC(offsetof(peer_data_t, crc16) == 60);
+
+ /* Save Peer data
+ * Peer data address is defined in bootloader linker @0x20007F80
+ * - If bonded : save Security information
+ * - Otherwise : save Address for direct advertising
+ *
+ * TODO may force bonded only for security reason
+ */
+ peer_data_t *peer_data = (peer_data_t *)(0x20007F80UL);
+ varclr(peer_data);
+
+ // Get CCCD
+ uint16_t sysattr_len = sizeof(peer_data->sys_attr);
+ sd_ble_gatts_sys_attr_get(conn_hdl, peer_data->sys_attr, &sysattr_len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
+
+ // Get Bond Data or using Address if not bonded
+ peer_data->addr = conn->getPeerAddr();
+
+ if (conn->secured()) {
+ bond_keys_t bkeys;
+ if (conn->loadBondKey(&bkeys)) {
+ peer_data->addr = bkeys.peer_id.id_addr_info;
+ peer_data->irk = bkeys.peer_id.id_info;
+ peer_data->enc_key = bkeys.own_enc;
+ }
+ }
+
+ // Calculate crc
+ peer_data->crc16 = crc16((uint8_t *)peer_data, offsetof(peer_data_t, crc16));
+
+ // Initiate DFU Sequence and reboot into DFU OTA mode
+ Bluefruit.Advertising.restartOnDisconnect(false);
+ conn->disconnect();
+
+ NRF_POWER->GPREGRET = 0xB1;
+ NVIC_SystemReset();
+ }
+ }
+}
+
+BLEDfuSecure::BLEDfuSecure(void) : BLEService(UUID16_SVC_DFU_OTA), _chr_control(UUID128_CHR_DFU_CONTROL) {}
+
+err_t BLEDfuSecure::begin(void)
+{
+ // Invoke base class begin()
+ VERIFY_STATUS(BLEService::begin());
+
+ _chr_control.setProperties(CHR_PROPS_WRITE | CHR_PROPS_INDICATE);
+ _chr_control.setMaxLen(23);
+ _chr_control.setWriteAuthorizeCallback(bledfu_control_wr_authorize_cb);
+ VERIFY_STATUS(_chr_control.begin());
+
+ return ERROR_NONE;
+}
diff --git a/src/platform/nrf52/BLEDfuSecure.h b/src/platform/nrf52/BLEDfuSecure.h
new file mode 100644
index 0000000000..bd5d910e8f
--- /dev/null
+++ b/src/platform/nrf52/BLEDfuSecure.h
@@ -0,0 +1,55 @@
+/**************************************************************************/
+/*!
+ @file BLEDfu.h
+ @author hathach (tinyusb.org)
+
+ @section LICENSE
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2018, Adafruit Industries (adafruit.com)
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holders nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+#ifndef BLEDFUSECURE_H_
+#define BLEDFUSECURE_H_
+
+#include "bluefruit_common.h"
+
+#include "BLECharacteristic.h"
+#include "BLEService.h"
+
+class BLEDfuSecure : public BLEService
+{
+ protected:
+ BLECharacteristic _chr_control;
+
+ public:
+ BLEDfuSecure(void);
+
+ virtual err_t begin(void);
+};
+
+#endif /* BLEDFUSECURE_H_ */
diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp
index 39898ab259..81a165f2df 100644
--- a/src/platform/nrf52/NRF52Bluetooth.cpp
+++ b/src/platform/nrf52/NRF52Bluetooth.cpp
@@ -1,4 +1,5 @@
#include "NRF52Bluetooth.h"
+#include "BLEDfuSecure.h"
#include "BluetoothCommon.h"
#include "PowerFSM.h"
#include "configuration.h"
@@ -7,15 +8,19 @@
#include "mesh/mesh-pb-constants.h"
#include
#include
-
static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16));
static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16));
static BLECharacteristic fromRadio = BLECharacteristic(BLEUuid(FROMRADIO_UUID_16));
static BLECharacteristic toRadio = BLECharacteristic(BLEUuid(TORADIO_UUID_16));
+static BLECharacteristic logRadio = BLECharacteristic(BLEUuid(LOGRADIO_UUID_16));
static BLEDis bledis; // DIS (Device Information Service) helper class instance
static BLEBas blebas; // BAS (Battery Service) helper class instance
+#ifndef BLE_DFU_SECURE
static BLEDfu bledfu; // DFU software update helper service
+#else
+static BLEDfuSecure bledfusecure; // DFU software update helper service
+#endif
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
// process at once
@@ -50,16 +55,14 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI;
void onConnect(uint16_t conn_handle)
{
+
// Get the reference to current connection
BLEConnection *connection = Bluefruit.Connection(conn_handle);
connectionHandle = conn_handle;
-
char central_name[32] = {0};
connection->getPeerName(central_name, sizeof(central_name));
-
LOG_INFO("BLE Connected to %s\n", central_name);
}
-
/**
* Callback invoked when a connection is dropped
* @param conn_handle connection where this event happens
@@ -70,37 +73,36 @@ void onDisconnect(uint16_t conn_handle, uint8_t reason)
// FIXME - we currently assume only one active connection
LOG_INFO("BLE Disconnected, reason = 0x%x\n", reason);
}
-
void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
{
// Display the raw request packet
LOG_INFO("CCCD Updated: %u\n", cccd_value);
-
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
- if (chr->uuid == fromNum.uuid) {
- if (chr->notifyEnabled(conn_hdl)) {
- LOG_INFO("fromNum 'Notify' enabled\n");
+
+ // According to the GATT spec: cccd value = 0x0001 means notifications are enabled
+ // and cccd value = 0x0002 means indications are enabled
+
+ if (chr->uuid == fromNum.uuid || chr->uuid == logRadio.uuid) {
+ auto result = cccd_value == 2 ? chr->indicateEnabled(conn_hdl) : chr->notifyEnabled(conn_hdl);
+ if (result) {
+ LOG_INFO("Notify/Indicate enabled\n");
} else {
- LOG_INFO("fromNum 'Notify' disabled\n");
+ LOG_INFO("Notify/Indicate disabled\n");
}
}
}
-
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
-
// IncludeService UUID
// Bluefruit.ScanResponse.addService(meshBleService);
Bluefruit.ScanResponse.addTxPower();
Bluefruit.ScanResponse.addName();
-
// Include Name
// Bluefruit.Advertising.addName();
Bluefruit.Advertising.addService(meshBleService);
-
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
@@ -115,7 +117,6 @@ void startAdv(void)
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X
}
-
// Just ack that the caller is allowed to read
static void authorizeRead(uint16_t conn_hdl)
{
@@ -123,7 +124,6 @@ static void authorizeRead(uint16_t conn_hdl)
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
}
-
/**
* client is starting read, pull the bytes from our API class
*/
@@ -132,7 +132,6 @@ void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e
if (request->offset == 0) {
// If the read is long, we will get multiple authorize invocations - we only populate data on the first
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
-
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
// or make empty if the queue is empty
fromRadio.write(fromRadioBytes, numBytes);
@@ -141,37 +140,22 @@ void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e
}
authorizeRead(conn_hdl);
}
-
void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
{
LOG_INFO("toRadioWriteCb data %p, len %u\n", data, len);
-
bluetoothPhoneAPI->handleToRadio(data, len);
}
-/**
- * client is starting read, pull the bytes from our API class
- */
-void onFromNumAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
-{
- LOG_INFO("fromNumAuthorizeCb\n");
-
- authorizeRead(conn_hdl);
-}
-
void setupMeshService(void)
{
bluetoothPhoneAPI = new BluetoothPhoneAPI();
-
meshBleService.begin();
-
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.. Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was 'begin()'ed!
auto secMode =
config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN ? SECMODE_OPEN : SECMODE_ENC_NO_MITM;
-
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
fromNum.setPermission(secMode, SECMODE_NO_ACCESS); // FIXME, secure this!!!
fromNum.setFixedLen(
@@ -201,10 +185,15 @@ void setupMeshService(void)
// We don't call this callback via the adafruit queue, because we can safely run in the BLE context
toRadio.setWriteCallback(onToRadioWrite, false);
toRadio.begin();
-}
+ logRadio.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_NOTIFY | CHR_PROPS_READ);
+ logRadio.setPermission(secMode, SECMODE_NO_ACCESS);
+ logRadio.setMaxLen(512);
+ logRadio.setCccdWriteCallback(onCccd);
+ logRadio.write32(0);
+ logRadio.begin();
+}
static uint32_t configuredPasskey;
-
void NRF52Bluetooth::shutdown()
{
// Shutdown bluetooth for minimum power draw
@@ -214,17 +203,23 @@ void NRF52Bluetooth::shutdown()
}
Bluefruit.Advertising.stop();
}
-
+void NRF52Bluetooth::startDisabled()
+{
+ // Setup Bluetooth
+ nrf52Bluetooth->setup();
+ // Shutdown bluetooth for minimum power draw
+ Bluefruit.Advertising.stop();
+ Bluefruit.setTxPower(-40); // Minimum power
+ LOG_INFO("Disabling NRF52 Bluetooth. (Workaround: tx power min, advertising stopped)\n");
+}
bool NRF52Bluetooth::isConnected()
{
return Bluefruit.connected(connectionHandle);
}
-
int NRF52Bluetooth::getRssi()
{
return 0; // FIXME figure out where to source this
}
-
void NRF52Bluetooth::setup()
{
// Initialise the Bluefruit module
@@ -232,12 +227,10 @@ void NRF52Bluetooth::setup()
Bluefruit.autoConnLed(false);
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
-
// Clear existing data.
Bluefruit.Advertising.stop();
Bluefruit.Advertising.clearData();
Bluefruit.ScanResponse.clearData();
-
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
configuredPasskey = config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN
? config.bluetooth.fixed_pin
@@ -256,37 +249,34 @@ void NRF52Bluetooth::setup()
}
// Set the advertised device name (keep it short!)
Bluefruit.setName(getDeviceName());
-
// Set the connect/disconnect callback handlers
Bluefruit.Periph.setConnectCallback(onConnect);
Bluefruit.Periph.setDisconnectCallback(onDisconnect);
-
+#ifndef BLE_DFU_SECURE
bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
bledfu.begin(); // Install the DFU helper
-
+#else
+ bledfusecure.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // add by WayenWeng
+ bledfusecure.begin(); // Install the DFU helper
+#endif
// Configure and Start the Device Information Service
LOG_INFO("Configuring the Device Information Service\n");
bledis.setModel(optstr(HW_VERSION));
bledis.setFirmwareRev(optstr(APP_VERSION));
bledis.begin();
-
// Start the BLE Battery Service and set it to 100%
LOG_INFO("Configuring the Battery Service\n");
blebas.begin();
blebas.write(0); // Unknown battery level for now
-
// Setup the Heart Rate Monitor service using
// BLEService and BLECharacteristic classes
LOG_INFO("Configuring the Mesh bluetooth service\n");
setupMeshService();
-
// Setup the advertising packet(s)
LOG_INFO("Setting up the advertising payload(s)\n");
startAdv();
-
LOG_INFO("Advertising\n");
}
-
void NRF52Bluetooth::resumeAdvertising()
{
Bluefruit.Advertising.restartOnDisconnect(true);
@@ -294,34 +284,52 @@ void NRF52Bluetooth::resumeAdvertising()
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0);
}
-
/// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level)
{
blebas.write(level);
}
-
void NRF52Bluetooth::clearBonds()
{
LOG_INFO("Clearing bluetooth bonds!\n");
bond_print_list(BLE_GAP_ROLE_PERIPH);
bond_print_list(BLE_GAP_ROLE_CENTRAL);
-
Bluefruit.Periph.clearBonds();
Bluefruit.Central.clearBonds();
}
-
void NRF52Bluetooth::onConnectionSecured(uint16_t conn_handle)
{
LOG_INFO("BLE connection secured\n");
}
-
bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request)
{
LOG_INFO("BLE pairing process started with passkey %.3s %.3s\n", passkey, passkey + 3);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
- screen->startBluetoothPinScreen(configuredPasskey);
-
+ screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
+ char btPIN[16] = "888888";
+ snprintf(btPIN, sizeof(btPIN), "%06u", configuredPasskey);
+ int x_offset = display->width() / 2;
+ int y_offset = display->height() <= 80 ? 0 : 32;
+ display->setTextAlignment(TEXT_ALIGN_CENTER);
+ display->setFont(FONT_MEDIUM);
+ display->drawString(x_offset + x, y_offset + y, "Bluetooth");
+
+ display->setFont(FONT_SMALL);
+ y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
+ display->drawString(x_offset + x, y_offset + y, "Enter this code");
+
+ display->setFont(FONT_LARGE);
+ String displayPin(btPIN);
+ String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
+ y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
+ display->drawString(x_offset + x, y_offset + y, pin);
+
+ display->setFont(FONT_SMALL);
+ String deviceName = "Name: ";
+ deviceName.concat(getDeviceName());
+ y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
+ display->drawString(x_offset + x, y_offset + y, deviceName);
+ });
if (match_request) {
uint32_t start_time = millis();
while (millis() < start_time + 30000) {
@@ -332,13 +340,21 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke
LOG_INFO("BLE passkey pairing: match_request=%i\n", match_request);
return true;
}
-
void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_status)
{
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS)
LOG_INFO("BLE pairing success\n");
else
LOG_INFO("BLE pairing failed\n");
+ screen->endAlert();
+}
- screen->stopBluetoothPinScreen();
+void NRF52Bluetooth::sendLog(const uint8_t *logMessage, size_t length)
+{
+ if (!isConnected() || length > 512)
+ return;
+ if (logRadio.indicateEnabled())
+ logRadio.indicate(logMessage, (uint16_t)length);
+ else
+ logRadio.notify(logMessage, (uint16_t)length);
}
\ No newline at end of file
diff --git a/src/platform/nrf52/NRF52Bluetooth.h b/src/platform/nrf52/NRF52Bluetooth.h
index 11e18c1272..2229163f81 100644
--- a/src/platform/nrf52/NRF52Bluetooth.h
+++ b/src/platform/nrf52/NRF52Bluetooth.h
@@ -8,14 +8,15 @@ class NRF52Bluetooth : BluetoothApi
public:
void setup();
void shutdown();
+ void startDisabled();
void resumeAdvertising();
void clearBonds();
bool isConnected();
int getRssi();
+ void sendLog(const uint8_t *logMessage, size_t length);
private:
static void onConnectionSecured(uint16_t conn_handle);
- void convertToUint8(uint8_t target[4], uint32_t source);
static bool onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request);
static void onPairingCompleted(uint16_t conn_handle, uint8_t auth_status);
};
\ No newline at end of file
diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h
index 18a4d75f57..a276a60810 100644
--- a/src/platform/nrf52/architecture.h
+++ b/src/platform/nrf52/architecture.h
@@ -42,6 +42,8 @@
#define HW_VENDOR meshtastic_HardwareModel_NRF52840DK
#elif defined(ARDUINO_NRF52840_PPR)
#define HW_VENDOR meshtastic_HardwareModel_PPR
+#elif defined(RAK2560)
+#define HW_VENDOR meshtastic_HardwareModel_RAK2560
#elif defined(RAK4630)
#define HW_VENDOR meshtastic_HardwareModel_RAK4631
#elif defined(TTGO_T_ECHO)
@@ -58,6 +60,8 @@
#define HW_VENDOR meshtastic_HardwareModel_NRF52_PROMICRO_DIY
#elif defined(WIO_WM1110)
#define HW_VENDOR meshtastic_HardwareModel_WIO_WM1110
+#elif defined(TRACKER_T1000_E)
+#define HW_VENDOR meshtastic_HardwareModel_TRACKER_T1000_E
#elif defined(PRIVATE_HW) || defined(FEATHER_DIY)
#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
#else
diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp
index 9cc52a7de8..7334f3a041 100644
--- a/src/platform/nrf52/main-nrf52.cpp
+++ b/src/platform/nrf52/main-nrf52.cpp
@@ -63,33 +63,53 @@ static void initBrownout()
// We don't bother with setting up brownout if soft device is disabled - because during production we always use softdevice
}
-static const bool useSoftDevice = true; // Set to false for easier debugging
+// This is a public global so that the debugger can set it to false automatically from our gdbinit
+bool useSoftDevice = true; // Set to false for easier debugging
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
void setBluetoothEnable(bool enable)
{
- if (enable && config.bluetooth.enabled) {
- if (!useSoftDevice) {
+ // For debugging use: don't use bluetooth
+ if (!useSoftDevice) {
+ if (enable)
LOG_INFO("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n");
- } else {
- if (!nrf52Bluetooth) {
- LOG_DEBUG("Initializing NRF52 Bluetooth\n");
- nrf52Bluetooth = new NRF52Bluetooth();
- nrf52Bluetooth->setup();
-
- // We delay brownout init until after BLE because BLE starts soft device
- initBrownout();
- } else {
- nrf52Bluetooth->resumeAdvertising();
- }
+ return;
+ }
+
+ // If user disabled bluetooth: init then disable advertising & reduce power
+ // Workaround. Avoid issue where device hangs several days after boot..
+ // Allegedly, no significant increase in power consumption
+ if (!config.bluetooth.enabled) {
+ static bool initialized = false;
+ if (!initialized) {
+ nrf52Bluetooth = new NRF52Bluetooth();
+ nrf52Bluetooth->startDisabled();
+ initBrownout();
+ initialized = true;
}
- } else {
- if (nrf52Bluetooth) {
- nrf52Bluetooth->shutdown();
+ return;
+ }
+
+ if (enable) {
+ // If not yet set-up
+ if (!nrf52Bluetooth) {
+ LOG_DEBUG("Initializing NRF52 Bluetooth\n");
+ nrf52Bluetooth = new NRF52Bluetooth();
+ nrf52Bluetooth->setup();
+
+ // We delay brownout init until after BLE because BLE starts soft device
+ initBrownout();
}
+ // Already setup, apparently
+ else
+ nrf52Bluetooth->resumeAdvertising();
}
+ // Disable (if previously set-up)
+ else if (nrf52Bluetooth)
+ nrf52Bluetooth->shutdown();
}
#else
+#warning NRF52 "Bluetooth disable" workaround does not apply to builds with MESHTASTIC_EXCLUDE_BLUETOOTH
void setBluetoothEnable(bool enable) {}
#endif
/**
@@ -130,13 +150,43 @@ void nrf52Loop()
checkSDEvents();
}
+#ifdef USE_SEMIHOSTING
+#include
+
+/**
+ * Note: this variable is in BSS and therfore false by default. But the gdbinit
+ * file will be installing a temporary breakpoint that changes wantSemihost to true.
+ */
+bool wantSemihost;
+
+/**
+ * Turn on semihosting if the ICE debugger wants it.
+ */
+void nrf52InitSemiHosting()
+{
+ if (wantSemihost) {
+ static SemihostingStream semiStream;
+ // We must dynamically alloc because the constructor does semihost operations which
+ // would crash any load not talking to a debugger
+ semiStream.open();
+ semiStream.println("Semihosting starts!");
+ // Redirect our serial output to instead go via the ICE port
+ console->setDestination(&semiStream);
+ }
+}
+#endif
+
void nrf52Setup()
{
- auto why = NRF_POWER->RESETREAS;
+ uint32_t why = NRF_POWER->RESETREAS;
// per
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpower.html
LOG_DEBUG("Reset reason: 0x%x\n", why);
+#ifdef USE_SEMIHOSTING
+ nrf52InitSemiHosting();
+#endif
+
// Per
// https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/monitor-mode-debugging-with-j-link-and-gdbeclipse
// This is the recommended setting for Monitor Mode Debugging
@@ -184,6 +234,18 @@ void cpuDeepSleep(uint32_t msecToWake)
// RAK-12039 set pin for Air quality sensor
digitalWrite(AQ_SET_PIN, LOW);
#endif
+#ifdef RAK14014
+ // GPIO restores input status, otherwise there will be leakage current
+ nrf_gpio_cfg_default(TFT_BL);
+ nrf_gpio_cfg_default(TFT_DC);
+ nrf_gpio_cfg_default(TFT_CS);
+ nrf_gpio_cfg_default(TFT_SCLK);
+ nrf_gpio_cfg_default(TFT_MOSI);
+ nrf_gpio_cfg_default(TFT_MISO);
+ nrf_gpio_cfg_default(SCREEN_TOUCH_INT);
+ nrf_gpio_cfg_default(WB_I2C1_SCL);
+ nrf_gpio_cfg_default(WB_I2C1_SDA);
+#endif
#endif
// Sleepy trackers or sensors can low power "sleep"
// Don't enter this if we're sleeping portMAX_DELAY, since that's a shutdown event
@@ -224,5 +286,10 @@ void clearBonds()
void enterDfuMode()
{
+// SDK kit does not have native USB like almost all other NRF52 boards
+#ifdef NRF_USE_SERIAL_DFU
+ enterSerialDfu();
+#else
enterUf2Dfu();
+#endif
}
\ No newline at end of file
diff --git a/variants/xiao_ble/nrf52840_s140_v7.ld b/src/platform/nrf52/nrf52840_s140_v7.ld
similarity index 100%
rename from variants/xiao_ble/nrf52840_s140_v7.ld
rename to src/platform/nrf52/nrf52840_s140_v7.ld
diff --git a/variants/xiao_ble/softdevice/ble.h b/src/platform/nrf52/softdevice/ble.h
similarity index 100%
rename from variants/xiao_ble/softdevice/ble.h
rename to src/platform/nrf52/softdevice/ble.h
diff --git a/variants/xiao_ble/softdevice/ble_err.h b/src/platform/nrf52/softdevice/ble_err.h
similarity index 100%
rename from variants/xiao_ble/softdevice/ble_err.h
rename to src/platform/nrf52/softdevice/ble_err.h
diff --git a/variants/xiao_ble/softdevice/ble_gap.h b/src/platform/nrf52/softdevice/ble_gap.h
similarity index 100%
rename from variants/xiao_ble/softdevice/ble_gap.h
rename to src/platform/nrf52/softdevice/ble_gap.h
diff --git a/variants/xiao_ble/softdevice/ble_gatt.h b/src/platform/nrf52/softdevice/ble_gatt.h
similarity index 100%
rename from variants/xiao_ble/softdevice/ble_gatt.h
rename to src/platform/nrf52/softdevice/ble_gatt.h
diff --git a/variants/xiao_ble/softdevice/ble_gattc.h b/src/platform/nrf52/softdevice/ble_gattc.h
similarity index 100%
rename from variants/xiao_ble/softdevice/ble_gattc.h
rename to src/platform/nrf52/softdevice/ble_gattc.h
diff --git a/variants/xiao_ble/softdevice/ble_gatts.h b/src/platform/nrf52/softdevice/ble_gatts.h
similarity index 100%
rename from variants/xiao_ble/softdevice/ble_gatts.h
rename to src/platform/nrf52/softdevice/ble_gatts.h
diff --git a/variants/xiao_ble/softdevice/ble_hci.h b/src/platform/nrf52/softdevice/ble_hci.h
similarity index 100%
rename from variants/xiao_ble/softdevice/ble_hci.h
rename to src/platform/nrf52/softdevice/ble_hci.h
diff --git a/variants/xiao_ble/softdevice/ble_l2cap.h b/src/platform/nrf52/softdevice/ble_l2cap.h
similarity index 100%
rename from variants/xiao_ble/softdevice/ble_l2cap.h
rename to src/platform/nrf52/softdevice/ble_l2cap.h
diff --git a/variants/xiao_ble/softdevice/ble_ranges.h b/src/platform/nrf52/softdevice/ble_ranges.h
similarity index 100%
rename from variants/xiao_ble/softdevice/ble_ranges.h
rename to src/platform/nrf52/softdevice/ble_ranges.h
diff --git a/variants/xiao_ble/softdevice/ble_types.h b/src/platform/nrf52/softdevice/ble_types.h
similarity index 100%
rename from variants/xiao_ble/softdevice/ble_types.h
rename to src/platform/nrf52/softdevice/ble_types.h
diff --git a/variants/xiao_ble/softdevice/nrf52/nrf_mbr.h b/src/platform/nrf52/softdevice/nrf52/nrf_mbr.h
similarity index 100%
rename from variants/xiao_ble/softdevice/nrf52/nrf_mbr.h
rename to src/platform/nrf52/softdevice/nrf52/nrf_mbr.h
diff --git a/variants/xiao_ble/softdevice/nrf_error.h b/src/platform/nrf52/softdevice/nrf_error.h
similarity index 100%
rename from variants/xiao_ble/softdevice/nrf_error.h
rename to src/platform/nrf52/softdevice/nrf_error.h
diff --git a/variants/xiao_ble/softdevice/nrf_error_sdm.h b/src/platform/nrf52/softdevice/nrf_error_sdm.h
similarity index 100%
rename from variants/xiao_ble/softdevice/nrf_error_sdm.h
rename to src/platform/nrf52/softdevice/nrf_error_sdm.h
diff --git a/variants/xiao_ble/softdevice/nrf_error_soc.h b/src/platform/nrf52/softdevice/nrf_error_soc.h
similarity index 100%
rename from variants/xiao_ble/softdevice/nrf_error_soc.h
rename to src/platform/nrf52/softdevice/nrf_error_soc.h
diff --git a/variants/xiao_ble/softdevice/nrf_nvic.h b/src/platform/nrf52/softdevice/nrf_nvic.h
similarity index 100%
rename from variants/xiao_ble/softdevice/nrf_nvic.h
rename to src/platform/nrf52/softdevice/nrf_nvic.h
diff --git a/variants/xiao_ble/softdevice/nrf_sdm.h b/src/platform/nrf52/softdevice/nrf_sdm.h
similarity index 99%
rename from variants/xiao_ble/softdevice/nrf_sdm.h
rename to src/platform/nrf52/softdevice/nrf_sdm.h
index 2786a86a45..02bf135b43 100644
--- a/variants/xiao_ble/softdevice/nrf_sdm.h
+++ b/src/platform/nrf52/softdevice/nrf_sdm.h
@@ -141,7 +141,7 @@ the start of the SoftDevice (without MBR)*/
* Add @ref MBR_SIZE to find the first available flash address when the SoftDevice is installed
* just above the MBR (the usual case).
*/
-#define SD_FLASH_SIZE 0x26000
+#define SD_FLASH_SIZE 0x27000
/** @brief Defines a macro for retrieving the actual FWID value from a given base address. Use
* @ref MBR_SIZE as the argument when the SoftDevice is installed just above the MBR (the usual
@@ -377,4 +377,4 @@ SVCALL(SD_SOFTDEVICE_VECTOR_TABLE_BASE_SET, uint32_t, sd_softdevice_vector_table
/**
@}
-*/
+*/
\ No newline at end of file
diff --git a/variants/xiao_ble/softdevice/nrf_soc.h b/src/platform/nrf52/softdevice/nrf_soc.h
similarity index 100%
rename from variants/xiao_ble/softdevice/nrf_soc.h
rename to src/platform/nrf52/softdevice/nrf_soc.h
diff --git a/variants/xiao_ble/softdevice/nrf_svc.h b/src/platform/nrf52/softdevice/nrf_svc.h
similarity index 100%
rename from variants/xiao_ble/softdevice/nrf_svc.h
rename to src/platform/nrf52/softdevice/nrf_svc.h
diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index 89ac806dd0..b910206ec2 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -17,6 +17,7 @@
std::map settingsMap;
std::map settingsStrings;
+std::ofstream traceFile;
char *configPath = nullptr;
// FIXME - move setBluetoothEnable into a HALPlatform class
@@ -134,7 +135,9 @@ void portduinoSetup()
try {
if (yamlConfig["Logging"]) {
- if (yamlConfig["Logging"]["LogLevel"].as("info") == "debug") {
+ if (yamlConfig["Logging"]["LogLevel"].as("info") == "trace") {
+ settingsMap[logoutputlevel] = level_trace;
+ } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "debug") {
settingsMap[logoutputlevel] = level_debug;
} else if (yamlConfig["Logging"]["LogLevel"].as("info") == "info") {
settingsMap[logoutputlevel] = level_info;
@@ -143,6 +146,7 @@ void portduinoSetup()
} else if (yamlConfig["Logging"]["LogLevel"].as("info") == "error") {
settingsMap[logoutputlevel] = level_error;
}
+ settingsStrings[traceFilename] = yamlConfig["Logging"]["TraceFile"].as("");
}
if (yamlConfig["Lora"]) {
settingsMap[use_sx1262] = false;
@@ -346,6 +350,14 @@ void portduinoSetup()
if (settingsStrings[spidev] != "") {
SPI.begin(settingsStrings[spidev].c_str());
}
+ if (settingsStrings[traceFilename] != "") {
+ try {
+ traceFile.open(settingsStrings[traceFilename], std::ios::out | std::ios::app);
+ } catch (std::ofstream::failure &e) {
+ std::cout << "*** traceFile Exception " << e.what() << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ }
return;
}
diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h
index ca935ea3bf..6b9a8eb8e9 100644
--- a/src/platform/portduino/PortduinoGlue.h
+++ b/src/platform/portduino/PortduinoGlue.h
@@ -1,4 +1,5 @@
#pragma once
+#include
#include