Compare commits

...

15 Commits

Author SHA1 Message Date
HarukiToreda
a6fac5bdb7
Merge 648808405a into 1a8ab2aadc 2025-07-29 03:28:37 +10:00
Ben Meadors
1a8ab2aadc
NodeDB count on MyNodeInfo for client progress reporting (#7489) 2025-07-28 12:23:59 -05:00
Thomas Göttgens
608fdc6f52
Santa may be checking his list twice, but we only need this in the platformio.ini (#7490) 2025-07-28 09:47:46 -05:00
rradillen
1d8638b47d
[7353] Add all telemetry fields to json output (#7363)
* Serializer bugfix

* Remove duplicate test

* fix tests

* fix float precision issues

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-28 09:23:04 -05:00
Ben Meadors
3ecff48722
Set firmware edition (for events) from userprefs (#7488)
* Set firmware edition (for events) from userprefs

* Spaces in the right places
2025-07-28 07:31:33 -05:00
HarukiToreda
648808405a trunk check 2025-07-27 02:02:21 -04:00
HarukiToreda
739343ae31
Merge branch 'master' into RememberDestination-Fix 2025-07-26 23:19:37 -04:00
HarukiToreda
37bc831849 Fix for reply function to remember last messaged 2025-07-26 23:15:44 -04:00
Jonathan Bennett
5f583fed0a
Merge branch 'master' into RememberDestination-Fix 2025-07-23 17:31:50 -05:00
HarukiToreda
ec39b5c9a0
Merge branch 'master' into RememberDestination-Fix 2025-07-23 11:17:45 -04:00
HarukiToreda
7880d732d9 Rember Last Receipient Node or channel
When a new freetext or preset message is sent and a destination is selected, the next message would forget the previously selected destination and would need to be selected again. With this fix it will remember the last destination selected until changed again.
2025-07-23 01:16:09 -04:00
HarukiToreda
bc6d65b804
Merge branch 'meshtastic:master' into master 2025-07-23 00:52:44 -04:00
HarukiToreda
a4b5d867b9
Merge branch 'meshtastic:master' into master 2025-07-07 21:31:33 -04:00
HarukiToreda
8aabbd4c56 Trunk fix 2025-06-08 23:17:26 -04:00
HarukiToreda
87ae2987d8 T-watch screen misalignment fix 2025-06-08 22:14:30 -04:00
15 changed files with 947 additions and 2 deletions

View File

@ -406,6 +406,9 @@ NodeDB::NodeDB()
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
config.position.gps_enabled = 0;
}
#ifdef USERPREFS_FIRMWARE_EDITION
myNodeInfo.firmware_edition = USERPREFS_FIRMWARE_EDITION;
#endif
#ifdef USERPREFS_FIXED_GPS
if (myNodeInfo.reboot_count == 1) { // Check if First boot ever or after Factory Reset.
meshtastic_Position fixedGPS = meshtastic_Position_init_default;

View File

@ -205,6 +205,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
// app not to send locations on our behalf.
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag;
strncpy(myNodeInfo.pio_env, optstr(APP_ENV), sizeof(myNodeInfo.pio_env));
myNodeInfo.nodedb_count = static_cast<uint16_t>(nodeDB->getNumMeshNodes());
fromRadioScratch.my_info = myNodeInfo;
state = STATE_SEND_UIDATA;

View File

@ -40,6 +40,9 @@ extern ScanI2C::DeviceAddress cardkb_found;
extern bool graphics::isMuted;
static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto";
static NodeNum lastDest = NODENUM_BROADCAST;
static uint8_t lastChannel = 0;
static bool lastDestSet = false;
meshtastic_CannedMessageModuleConfig cannedMessageModuleConfig;
@ -63,8 +66,18 @@ CannedMessageModule::CannedMessageModule()
void CannedMessageModule::LaunchWithDestination(NodeNum newDest, uint8_t newChannel)
{
// Use the requested destination, unless it's "broadcast" and we have a previous node/channel
if (newDest == NODENUM_BROADCAST && lastDestSet) {
newDest = lastDest;
newChannel = lastChannel;
}
dest = newDest;
channel = newChannel;
lastDest = dest;
lastChannel = channel;
lastDestSet = true;
// Rest of function unchanged...
// Always select the first real canned message on activation
int firstRealMsgIdx = 0;
for (int i = 0; i < messagesCount; ++i) {
@ -84,10 +97,28 @@ void CannedMessageModule::LaunchWithDestination(NodeNum newDest, uint8_t newChan
notifyObservers(&e);
}
void CannedMessageModule::LaunchRepeatDestination()
{
if (!lastDestSet) {
LaunchWithDestination(NODENUM_BROADCAST, 0);
} else {
LaunchWithDestination(lastDest, lastChannel);
}
}
void CannedMessageModule::LaunchFreetextWithDestination(NodeNum newDest, uint8_t newChannel)
{
// Use the requested destination, unless it's "broadcast" and we have a previous node/channel
if (newDest == NODENUM_BROADCAST && lastDestSet) {
newDest = lastDest;
newChannel = lastChannel;
}
dest = newDest;
channel = newChannel;
lastDest = dest;
lastChannel = channel;
lastDestSet = true;
runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
requestFocus();
UIFrameEvent e;
@ -479,6 +510,9 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
if (destIndex < static_cast<int>(activeChannelIndices.size())) {
dest = NODENUM_BROADCAST;
channel = activeChannelIndices[destIndex];
lastDest = dest;
lastChannel = channel;
lastDestSet = true;
} else {
int nodeIndex = destIndex - static_cast<int>(activeChannelIndices.size());
if (nodeIndex >= 0 && nodeIndex < static_cast<int>(filteredNodes.size())) {
@ -486,6 +520,10 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
if (selectedNode) {
dest = selectedNode->num;
channel = selectedNode->channel;
// Already saves here, but for clarity, also:
lastDest = dest;
lastChannel = channel;
lastDestSet = true;
}
}
}
@ -827,6 +865,9 @@ int CannedMessageModule::handleEmotePickerInput(const InputEvent *event)
void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const char *message, bool wantReplies)
{
lastDest = dest;
lastChannel = channel;
lastDestSet = true;
// === Prepare packet ===
meshtastic_MeshPacket *p = allocDataPacket();
p->to = dest;

View File

@ -59,6 +59,7 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
CannedMessageModule();
void LaunchWithDestination(NodeNum, uint8_t newChannel = 0);
void LaunchRepeatDestination();
void LaunchFreetextWithDestination(NodeNum, uint8_t newChannel = 0);
// === Emote Picker navigation ===

View File

@ -100,6 +100,9 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
if (decoded->variant.environment_metrics.has_iaq) {
msgPayload["iaq"] = new JSONValue((uint)decoded->variant.environment_metrics.iaq);
}
if (decoded->variant.environment_metrics.has_distance) {
msgPayload["distance"] = new JSONValue(decoded->variant.environment_metrics.distance);
}
if (decoded->variant.environment_metrics.has_wind_speed) {
msgPayload["wind_speed"] = new JSONValue(decoded->variant.environment_metrics.wind_speed);
}
@ -115,6 +118,27 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
if (decoded->variant.environment_metrics.has_radiation) {
msgPayload["radiation"] = new JSONValue(decoded->variant.environment_metrics.radiation);
}
if (decoded->variant.environment_metrics.has_ir_lux) {
msgPayload["ir_lux"] = new JSONValue(decoded->variant.environment_metrics.ir_lux);
}
if (decoded->variant.environment_metrics.has_uv_lux) {
msgPayload["uv_lux"] = new JSONValue(decoded->variant.environment_metrics.uv_lux);
}
if (decoded->variant.environment_metrics.has_weight) {
msgPayload["weight"] = new JSONValue(decoded->variant.environment_metrics.weight);
}
if (decoded->variant.environment_metrics.has_rainfall_1h) {
msgPayload["rainfall_1h"] = new JSONValue(decoded->variant.environment_metrics.rainfall_1h);
}
if (decoded->variant.environment_metrics.has_rainfall_24h) {
msgPayload["rainfall_24h"] = new JSONValue(decoded->variant.environment_metrics.rainfall_24h);
}
if (decoded->variant.environment_metrics.has_soil_moisture) {
msgPayload["soil_moisture"] = new JSONValue((uint)decoded->variant.environment_metrics.soil_moisture);
}
if (decoded->variant.environment_metrics.has_soil_temperature) {
msgPayload["soil_temperature"] = new JSONValue(decoded->variant.environment_metrics.soil_temperature);
}
} else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
if (decoded->variant.air_quality_metrics.has_pm10_standard) {
msgPayload["pm10"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm10_standard);

View File

@ -0,0 +1,50 @@
#include "../test_helpers.h"
// Test encrypted packet serialization
void test_encrypted_packet_serialization()
{
meshtastic_MeshPacket packet = meshtastic_MeshPacket_init_zero;
packet.from = 0x11223344;
packet.to = 0x55667788;
packet.id = 0x9999;
packet.which_payload_variant = meshtastic_MeshPacket_encrypted_tag;
// Add some dummy encrypted data
const char *encrypted_data = "encrypted_payload_data";
packet.encrypted.size = strlen(encrypted_data);
memcpy(packet.encrypted.bytes, encrypted_data, packet.encrypted.size);
std::string json = MeshPacketSerializer::JsonSerializeEncrypted(&packet);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check basic packet fields
TEST_ASSERT_TRUE(jsonObj.find("from") != jsonObj.end());
TEST_ASSERT_EQUAL(0x11223344, (uint32_t)jsonObj["from"]->AsNumber());
TEST_ASSERT_TRUE(jsonObj.find("to") != jsonObj.end());
TEST_ASSERT_EQUAL(0x55667788, (uint32_t)jsonObj["to"]->AsNumber());
TEST_ASSERT_TRUE(jsonObj.find("id") != jsonObj.end());
TEST_ASSERT_EQUAL(0x9999, (uint32_t)jsonObj["id"]->AsNumber());
// Check that it has encrypted data fields (not "payload" but "bytes" and "size")
TEST_ASSERT_TRUE(jsonObj.find("bytes") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["bytes"]->IsString());
TEST_ASSERT_TRUE(jsonObj.find("size") != jsonObj.end());
TEST_ASSERT_EQUAL(22, (int)jsonObj["size"]->AsNumber()); // strlen("encrypted_payload_data") = 22
// The encrypted data should be hex-encoded
std::string encrypted_hex = jsonObj["bytes"]->AsString();
TEST_ASSERT_TRUE(encrypted_hex.length() > 0);
// Should be twice the size of the original data (hex encoding)
TEST_ASSERT_EQUAL(44, encrypted_hex.length()); // 22 * 2 = 44
delete root;
}

View File

@ -0,0 +1,51 @@
#include "../test_helpers.h"
static size_t encode_user_info(uint8_t *buffer, size_t buffer_size)
{
meshtastic_User user = meshtastic_User_init_zero;
strcpy(user.short_name, "TEST");
strcpy(user.long_name, "Test User");
strcpy(user.id, "!12345678");
user.hw_model = meshtastic_HardwareModel_HELTEC_V3;
pb_ostream_t stream = pb_ostream_from_buffer(buffer, buffer_size);
pb_encode(&stream, &meshtastic_User_msg, &user);
return stream.bytes_written;
}
// Test NODEINFO_APP port
void test_nodeinfo_serialization()
{
uint8_t buffer[256];
size_t payload_size = encode_user_info(buffer, sizeof(buffer));
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_NODEINFO_APP, buffer, payload_size);
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check message type
TEST_ASSERT_TRUE(jsonObj.find("type") != jsonObj.end());
TEST_ASSERT_EQUAL_STRING("nodeinfo", jsonObj["type"]->AsString().c_str());
// Check payload
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
JSONObject payload = jsonObj["payload"]->AsObject();
// Verify user data
TEST_ASSERT_TRUE(payload.find("shortname") != payload.end());
TEST_ASSERT_EQUAL_STRING("TEST", payload["shortname"]->AsString().c_str());
TEST_ASSERT_TRUE(payload.find("longname") != payload.end());
TEST_ASSERT_EQUAL_STRING("Test User", payload["longname"]->AsString().c_str());
delete root;
}

View File

@ -0,0 +1,57 @@
#include "../test_helpers.h"
static size_t encode_position(uint8_t *buffer, size_t buffer_size)
{
meshtastic_Position position = meshtastic_Position_init_zero;
position.latitude_i = 374208000; // 37.4208 degrees * 1e7
position.longitude_i = -1221981000; // -122.1981 degrees * 1e7
position.altitude = 123;
position.time = 1609459200;
position.has_altitude = true;
position.has_latitude_i = true;
position.has_longitude_i = true;
pb_ostream_t stream = pb_ostream_from_buffer(buffer, buffer_size);
pb_encode(&stream, &meshtastic_Position_msg, &position);
return stream.bytes_written;
}
// Test POSITION_APP port
void test_position_serialization()
{
uint8_t buffer[256];
size_t payload_size = encode_position(buffer, sizeof(buffer));
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_POSITION_APP, buffer, payload_size);
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check message type
TEST_ASSERT_TRUE(jsonObj.find("type") != jsonObj.end());
TEST_ASSERT_EQUAL_STRING("position", jsonObj["type"]->AsString().c_str());
// Check payload
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
JSONObject payload = jsonObj["payload"]->AsObject();
// Verify position data
TEST_ASSERT_TRUE(payload.find("latitude_i") != payload.end());
TEST_ASSERT_EQUAL(374208000, (int)payload["latitude_i"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("longitude_i") != payload.end());
TEST_ASSERT_EQUAL(-1221981000, (int)payload["longitude_i"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("altitude") != payload.end());
TEST_ASSERT_EQUAL(123, (int)payload["altitude"]->AsNumber());
delete root;
}

View File

@ -0,0 +1,528 @@
#include "../test_helpers.h"
// Helper function to create and encode device metrics
static size_t encode_telemetry_device_metrics(uint8_t *buffer, size_t buffer_size)
{
meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero;
telemetry.time = 1609459200;
telemetry.which_variant = meshtastic_Telemetry_device_metrics_tag;
telemetry.variant.device_metrics.battery_level = 85;
telemetry.variant.device_metrics.has_battery_level = true;
telemetry.variant.device_metrics.voltage = 3.72f;
telemetry.variant.device_metrics.has_voltage = true;
telemetry.variant.device_metrics.channel_utilization = 15.56f;
telemetry.variant.device_metrics.has_channel_utilization = true;
telemetry.variant.device_metrics.air_util_tx = 8.23f;
telemetry.variant.device_metrics.has_air_util_tx = true;
telemetry.variant.device_metrics.uptime_seconds = 12345;
telemetry.variant.device_metrics.has_uptime_seconds = true;
pb_ostream_t stream = pb_ostream_from_buffer(buffer, buffer_size);
pb_encode(&stream, &meshtastic_Telemetry_msg, &telemetry);
return stream.bytes_written;
}
// Helper function to create and encode empty environment metrics (no fields set)
static size_t encode_telemetry_environment_metrics_empty(uint8_t *buffer, size_t buffer_size)
{
meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero;
telemetry.time = 1609459200;
telemetry.which_variant = meshtastic_Telemetry_environment_metrics_tag;
// NO fields are set - all has_* flags remain false
// This tests that empty environment metrics don't produce any JSON fields
pb_ostream_t stream = pb_ostream_from_buffer(buffer, buffer_size);
pb_encode(&stream, &meshtastic_Telemetry_msg, &telemetry);
return stream.bytes_written;
}
// Helper function to create environment metrics with ALL possible fields set
// This function should be updated whenever new fields are added to the protobuf
static size_t encode_telemetry_environment_metrics_all_fields(uint8_t *buffer, size_t buffer_size)
{
meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero;
telemetry.time = 1609459200;
telemetry.which_variant = meshtastic_Telemetry_environment_metrics_tag;
// Basic environment metrics
telemetry.variant.environment_metrics.temperature = 23.56f;
telemetry.variant.environment_metrics.has_temperature = true;
telemetry.variant.environment_metrics.relative_humidity = 65.43f;
telemetry.variant.environment_metrics.has_relative_humidity = true;
telemetry.variant.environment_metrics.barometric_pressure = 1013.27f;
telemetry.variant.environment_metrics.has_barometric_pressure = true;
// Gas and air quality
telemetry.variant.environment_metrics.gas_resistance = 50.58f;
telemetry.variant.environment_metrics.has_gas_resistance = true;
telemetry.variant.environment_metrics.iaq = 120;
telemetry.variant.environment_metrics.has_iaq = true;
// Power measurements
telemetry.variant.environment_metrics.voltage = 3.34f;
telemetry.variant.environment_metrics.has_voltage = true;
telemetry.variant.environment_metrics.current = 0.53f;
telemetry.variant.environment_metrics.has_current = true;
// Light measurements (ALL 4 types)
telemetry.variant.environment_metrics.lux = 450.12f;
telemetry.variant.environment_metrics.has_lux = true;
telemetry.variant.environment_metrics.white_lux = 380.95f;
telemetry.variant.environment_metrics.has_white_lux = true;
telemetry.variant.environment_metrics.ir_lux = 25.37f;
telemetry.variant.environment_metrics.has_ir_lux = true;
telemetry.variant.environment_metrics.uv_lux = 15.68f;
telemetry.variant.environment_metrics.has_uv_lux = true;
// Distance measurement
telemetry.variant.environment_metrics.distance = 150.29f;
telemetry.variant.environment_metrics.has_distance = true;
// Wind measurements (ALL 4 types)
telemetry.variant.environment_metrics.wind_direction = 180;
telemetry.variant.environment_metrics.has_wind_direction = true;
telemetry.variant.environment_metrics.wind_speed = 5.52f;
telemetry.variant.environment_metrics.has_wind_speed = true;
telemetry.variant.environment_metrics.wind_gust = 8.24f;
telemetry.variant.environment_metrics.has_wind_gust = true;
telemetry.variant.environment_metrics.wind_lull = 2.13f;
telemetry.variant.environment_metrics.has_wind_lull = true;
// Weight measurement
telemetry.variant.environment_metrics.weight = 75.56f;
telemetry.variant.environment_metrics.has_weight = true;
// Radiation measurement
telemetry.variant.environment_metrics.radiation = 0.13f;
telemetry.variant.environment_metrics.has_radiation = true;
// Rainfall measurements (BOTH types)
telemetry.variant.environment_metrics.rainfall_1h = 2.57f;
telemetry.variant.environment_metrics.has_rainfall_1h = true;
telemetry.variant.environment_metrics.rainfall_24h = 15.89f;
telemetry.variant.environment_metrics.has_rainfall_24h = true;
// Soil measurements (BOTH types)
telemetry.variant.environment_metrics.soil_moisture = 85;
telemetry.variant.environment_metrics.has_soil_moisture = true;
telemetry.variant.environment_metrics.soil_temperature = 18.54f;
telemetry.variant.environment_metrics.has_soil_temperature = true;
// IMPORTANT: When new environment fields are added to the protobuf,
// they MUST be added here too, or the coverage test will fail!
pb_ostream_t stream = pb_ostream_from_buffer(buffer, buffer_size);
pb_encode(&stream, &meshtastic_Telemetry_msg, &telemetry);
return stream.bytes_written;
}
// Helper function to create and encode environment metrics with all current fields
static size_t encode_telemetry_environment_metrics(uint8_t *buffer, size_t buffer_size)
{
meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero;
telemetry.time = 1609459200;
telemetry.which_variant = meshtastic_Telemetry_environment_metrics_tag;
// Basic environment metrics
telemetry.variant.environment_metrics.temperature = 23.56f;
telemetry.variant.environment_metrics.has_temperature = true;
telemetry.variant.environment_metrics.relative_humidity = 65.43f;
telemetry.variant.environment_metrics.has_relative_humidity = true;
telemetry.variant.environment_metrics.barometric_pressure = 1013.27f;
telemetry.variant.environment_metrics.has_barometric_pressure = true;
// Gas and air quality
telemetry.variant.environment_metrics.gas_resistance = 50.58f;
telemetry.variant.environment_metrics.has_gas_resistance = true;
telemetry.variant.environment_metrics.iaq = 120;
telemetry.variant.environment_metrics.has_iaq = true;
// Power measurements
telemetry.variant.environment_metrics.voltage = 3.34f;
telemetry.variant.environment_metrics.has_voltage = true;
telemetry.variant.environment_metrics.current = 0.53f;
telemetry.variant.environment_metrics.has_current = true;
// Light measurements
telemetry.variant.environment_metrics.lux = 450.12f;
telemetry.variant.environment_metrics.has_lux = true;
telemetry.variant.environment_metrics.white_lux = 380.95f;
telemetry.variant.environment_metrics.has_white_lux = true;
telemetry.variant.environment_metrics.ir_lux = 25.37f;
telemetry.variant.environment_metrics.has_ir_lux = true;
telemetry.variant.environment_metrics.uv_lux = 15.68f;
telemetry.variant.environment_metrics.has_uv_lux = true;
// Distance measurement
telemetry.variant.environment_metrics.distance = 150.29f;
telemetry.variant.environment_metrics.has_distance = true;
// Wind measurements
telemetry.variant.environment_metrics.wind_direction = 180;
telemetry.variant.environment_metrics.has_wind_direction = true;
telemetry.variant.environment_metrics.wind_speed = 5.52f;
telemetry.variant.environment_metrics.has_wind_speed = true;
telemetry.variant.environment_metrics.wind_gust = 8.24f;
telemetry.variant.environment_metrics.has_wind_gust = true;
telemetry.variant.environment_metrics.wind_lull = 2.13f;
telemetry.variant.environment_metrics.has_wind_lull = true;
// Weight measurement
telemetry.variant.environment_metrics.weight = 75.56f;
telemetry.variant.environment_metrics.has_weight = true;
// Radiation measurement
telemetry.variant.environment_metrics.radiation = 0.13f;
telemetry.variant.environment_metrics.has_radiation = true;
// Rainfall measurements
telemetry.variant.environment_metrics.rainfall_1h = 2.57f;
telemetry.variant.environment_metrics.has_rainfall_1h = true;
telemetry.variant.environment_metrics.rainfall_24h = 15.89f;
telemetry.variant.environment_metrics.has_rainfall_24h = true;
// Soil measurements
telemetry.variant.environment_metrics.soil_moisture = 85;
telemetry.variant.environment_metrics.has_soil_moisture = true;
telemetry.variant.environment_metrics.soil_temperature = 18.54f;
telemetry.variant.environment_metrics.has_soil_temperature = true;
pb_ostream_t stream = pb_ostream_from_buffer(buffer, buffer_size);
pb_encode(&stream, &meshtastic_Telemetry_msg, &telemetry);
return stream.bytes_written;
}
// Test TELEMETRY_APP port with device metrics
void test_telemetry_device_metrics_serialization()
{
uint8_t buffer[256];
size_t payload_size = encode_telemetry_device_metrics(buffer, sizeof(buffer));
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_TELEMETRY_APP, buffer, payload_size);
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check message type
TEST_ASSERT_TRUE(jsonObj.find("type") != jsonObj.end());
TEST_ASSERT_EQUAL_STRING("telemetry", jsonObj["type"]->AsString().c_str());
// Check payload
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
JSONObject payload = jsonObj["payload"]->AsObject();
// Verify telemetry data
TEST_ASSERT_TRUE(payload.find("battery_level") != payload.end());
TEST_ASSERT_EQUAL(85, (int)payload["battery_level"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("voltage") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 3.72f, payload["voltage"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("channel_utilization") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 15.56f, payload["channel_utilization"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("uptime_seconds") != payload.end());
TEST_ASSERT_EQUAL(12345, (int)payload["uptime_seconds"]->AsNumber());
// Note: JSON serialization may not preserve exact 2-decimal formatting due to float precision
// We verify the numeric values are correct within tolerance
delete root;
}
// Test that telemetry environment metrics are properly serialized
void test_telemetry_environment_metrics_serialization()
{
uint8_t buffer[256];
size_t payload_size = encode_telemetry_environment_metrics(buffer, sizeof(buffer));
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_TELEMETRY_APP, buffer, payload_size);
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check payload exists
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
JSONObject payload = jsonObj["payload"]->AsObject();
// Test key fields that should be present in the serializer
TEST_ASSERT_TRUE(payload.find("temperature") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 23.56f, payload["temperature"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("relative_humidity") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 65.43f, payload["relative_humidity"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("distance") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 150.29f, payload["distance"]->AsNumber());
// Note: JSON serialization may have float precision limitations
// We focus on verifying numeric accuracy rather than exact string formatting
delete root;
}
// Test comprehensive environment metrics coverage
void test_telemetry_environment_metrics_comprehensive()
{
uint8_t buffer[256];
size_t payload_size = encode_telemetry_environment_metrics(buffer, sizeof(buffer));
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_TELEMETRY_APP, buffer, payload_size);
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check payload exists
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
JSONObject payload = jsonObj["payload"]->AsObject();
// Check all 15 originally supported fields
TEST_ASSERT_TRUE(payload.find("temperature") != payload.end());
TEST_ASSERT_TRUE(payload.find("relative_humidity") != payload.end());
TEST_ASSERT_TRUE(payload.find("barometric_pressure") != payload.end());
TEST_ASSERT_TRUE(payload.find("gas_resistance") != payload.end());
TEST_ASSERT_TRUE(payload.find("voltage") != payload.end());
TEST_ASSERT_TRUE(payload.find("current") != payload.end());
TEST_ASSERT_TRUE(payload.find("iaq") != payload.end());
TEST_ASSERT_TRUE(payload.find("distance") != payload.end());
TEST_ASSERT_TRUE(payload.find("lux") != payload.end());
TEST_ASSERT_TRUE(payload.find("white_lux") != payload.end());
TEST_ASSERT_TRUE(payload.find("wind_direction") != payload.end());
TEST_ASSERT_TRUE(payload.find("wind_speed") != payload.end());
TEST_ASSERT_TRUE(payload.find("wind_gust") != payload.end());
TEST_ASSERT_TRUE(payload.find("wind_lull") != payload.end());
TEST_ASSERT_TRUE(payload.find("radiation") != payload.end());
delete root;
}
// Test for the 7 environment fields that were added to complete coverage
void test_telemetry_environment_metrics_missing_fields()
{
uint8_t buffer[256];
size_t payload_size = encode_telemetry_environment_metrics(buffer, sizeof(buffer));
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_TELEMETRY_APP, buffer, payload_size);
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check payload exists
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
JSONObject payload = jsonObj["payload"]->AsObject();
// Check the 7 fields that were previously missing
TEST_ASSERT_TRUE(payload.find("ir_lux") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 25.37f, payload["ir_lux"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("uv_lux") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 15.68f, payload["uv_lux"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("weight") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 75.56f, payload["weight"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("rainfall_1h") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 2.57f, payload["rainfall_1h"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("rainfall_24h") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 15.89f, payload["rainfall_24h"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("soil_moisture") != payload.end());
TEST_ASSERT_EQUAL(85, (int)payload["soil_moisture"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("soil_temperature") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 18.54f, payload["soil_temperature"]->AsNumber());
// Note: JSON float serialization may not preserve exact decimal formatting
// We verify the values are numerically correct within tolerance
delete root;
}
// Test that ALL environment fields are serialized (canary test for forgotten fields)
// This test will FAIL if a new environment field is added to the protobuf but not to the serializer
void test_telemetry_environment_metrics_complete_coverage()
{
uint8_t buffer[256];
size_t payload_size = encode_telemetry_environment_metrics_all_fields(buffer, sizeof(buffer));
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_TELEMETRY_APP, buffer, payload_size);
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check payload exists
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
JSONObject payload = jsonObj["payload"]->AsObject();
// ✅ ALL 22 environment fields MUST be present and correct
// If this test fails, it means either:
// 1. A new field was added to the protobuf but not to the serializer
// 2. The encode_telemetry_environment_metrics_all_fields() function wasn't updated
// Basic environment (3 fields)
TEST_ASSERT_TRUE(payload.find("temperature") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 23.56f, payload["temperature"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("relative_humidity") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 65.43f, payload["relative_humidity"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("barometric_pressure") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 1013.27f, payload["barometric_pressure"]->AsNumber());
// Gas and air quality (2 fields)
TEST_ASSERT_TRUE(payload.find("gas_resistance") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 50.58f, payload["gas_resistance"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("iaq") != payload.end());
TEST_ASSERT_EQUAL(120, (int)payload["iaq"]->AsNumber());
// Power measurements (2 fields)
TEST_ASSERT_TRUE(payload.find("voltage") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 3.34f, payload["voltage"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("current") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 0.53f, payload["current"]->AsNumber());
// Light measurements (4 fields)
TEST_ASSERT_TRUE(payload.find("lux") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 450.12f, payload["lux"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("white_lux") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 380.95f, payload["white_lux"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("ir_lux") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 25.37f, payload["ir_lux"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("uv_lux") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 15.68f, payload["uv_lux"]->AsNumber());
// Distance measurement (1 field)
TEST_ASSERT_TRUE(payload.find("distance") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 150.29f, payload["distance"]->AsNumber());
// Wind measurements (4 fields)
TEST_ASSERT_TRUE(payload.find("wind_direction") != payload.end());
TEST_ASSERT_EQUAL(180, (int)payload["wind_direction"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("wind_speed") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 5.52f, payload["wind_speed"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("wind_gust") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 8.24f, payload["wind_gust"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("wind_lull") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 2.13f, payload["wind_lull"]->AsNumber());
// Weight measurement (1 field)
TEST_ASSERT_TRUE(payload.find("weight") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 75.56f, payload["weight"]->AsNumber());
// Radiation measurement (1 field)
TEST_ASSERT_TRUE(payload.find("radiation") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 0.13f, payload["radiation"]->AsNumber());
// Rainfall measurements (2 fields)
TEST_ASSERT_TRUE(payload.find("rainfall_1h") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 2.57f, payload["rainfall_1h"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("rainfall_24h") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 15.89f, payload["rainfall_24h"]->AsNumber());
// Soil measurements (2 fields)
TEST_ASSERT_TRUE(payload.find("soil_moisture") != payload.end());
TEST_ASSERT_EQUAL(85, (int)payload["soil_moisture"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("soil_temperature") != payload.end());
TEST_ASSERT_FLOAT_WITHIN(0.01f, 18.54f, payload["soil_temperature"]->AsNumber());
// Total: 22 environment fields
// This test ensures 100% coverage of environment metrics
// Note: JSON float serialization precision may vary due to the underlying library
// The important aspect is that all values are numerically accurate within tolerance
delete root;
}
// Test that unset environment fields are not present in JSON
void test_telemetry_environment_metrics_unset_fields()
{
uint8_t buffer[256];
size_t payload_size = encode_telemetry_environment_metrics_empty(buffer, sizeof(buffer));
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_TELEMETRY_APP, buffer, payload_size);
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check payload exists
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
JSONObject payload = jsonObj["payload"]->AsObject();
// With completely empty environment metrics, NO fields should be present
// Only basic telemetry fields like "time" might be present
// All 22 environment fields should be absent (none were set)
TEST_ASSERT_TRUE(payload.find("temperature") == payload.end());
TEST_ASSERT_TRUE(payload.find("relative_humidity") == payload.end());
TEST_ASSERT_TRUE(payload.find("barometric_pressure") == payload.end());
TEST_ASSERT_TRUE(payload.find("gas_resistance") == payload.end());
TEST_ASSERT_TRUE(payload.find("iaq") == payload.end());
TEST_ASSERT_TRUE(payload.find("voltage") == payload.end());
TEST_ASSERT_TRUE(payload.find("current") == payload.end());
TEST_ASSERT_TRUE(payload.find("lux") == payload.end());
TEST_ASSERT_TRUE(payload.find("white_lux") == payload.end());
TEST_ASSERT_TRUE(payload.find("ir_lux") == payload.end());
TEST_ASSERT_TRUE(payload.find("uv_lux") == payload.end());
TEST_ASSERT_TRUE(payload.find("distance") == payload.end());
TEST_ASSERT_TRUE(payload.find("wind_direction") == payload.end());
TEST_ASSERT_TRUE(payload.find("wind_speed") == payload.end());
TEST_ASSERT_TRUE(payload.find("wind_gust") == payload.end());
TEST_ASSERT_TRUE(payload.find("wind_lull") == payload.end());
TEST_ASSERT_TRUE(payload.find("weight") == payload.end());
TEST_ASSERT_TRUE(payload.find("radiation") == payload.end());
TEST_ASSERT_TRUE(payload.find("rainfall_1h") == payload.end());
TEST_ASSERT_TRUE(payload.find("rainfall_24h") == payload.end());
TEST_ASSERT_TRUE(payload.find("soil_moisture") == payload.end());
TEST_ASSERT_TRUE(payload.find("soil_temperature") == payload.end());
delete root;
}

View File

@ -0,0 +1,42 @@
#include "../test_helpers.h"
// Test TEXT_MESSAGE_APP port
void test_text_message_serialization()
{
const char *test_text = "Hello Meshtastic!";
meshtastic_MeshPacket packet =
create_test_packet(meshtastic_PortNum_TEXT_MESSAGE_APP, (const uint8_t *)test_text, strlen(test_text));
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check basic packet fields
TEST_ASSERT_TRUE(jsonObj.find("from") != jsonObj.end());
TEST_ASSERT_EQUAL(0x11223344, (uint32_t)jsonObj["from"]->AsNumber());
TEST_ASSERT_TRUE(jsonObj.find("to") != jsonObj.end());
TEST_ASSERT_EQUAL(0x55667788, (uint32_t)jsonObj["to"]->AsNumber());
TEST_ASSERT_TRUE(jsonObj.find("id") != jsonObj.end());
TEST_ASSERT_EQUAL(0x9999, (uint32_t)jsonObj["id"]->AsNumber());
// Check message type
TEST_ASSERT_TRUE(jsonObj.find("type") != jsonObj.end());
TEST_ASSERT_EQUAL_STRING("text", jsonObj["type"]->AsString().c_str());
// Check payload
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
JSONObject payload = jsonObj["payload"]->AsObject();
TEST_ASSERT_TRUE(payload.find("text") != payload.end());
TEST_ASSERT_EQUAL_STRING("Hello Meshtastic!", payload["text"]->AsString().c_str());
delete root;
}

View File

@ -0,0 +1,53 @@
#include "../test_helpers.h"
static size_t encode_waypoint(uint8_t *buffer, size_t buffer_size)
{
meshtastic_Waypoint waypoint = meshtastic_Waypoint_init_zero;
waypoint.id = 12345;
waypoint.latitude_i = 374208000;
waypoint.longitude_i = -1221981000;
waypoint.expire = 1609459200 + 3600; // 1 hour from now
strcpy(waypoint.name, "Test Point");
strcpy(waypoint.description, "Test waypoint description");
pb_ostream_t stream = pb_ostream_from_buffer(buffer, buffer_size);
pb_encode(&stream, &meshtastic_Waypoint_msg, &waypoint);
return stream.bytes_written;
}
// Test WAYPOINT_APP port
void test_waypoint_serialization()
{
uint8_t buffer[256];
size_t payload_size = encode_waypoint(buffer, sizeof(buffer));
meshtastic_MeshPacket packet = create_test_packet(meshtastic_PortNum_WAYPOINT_APP, buffer, payload_size);
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
TEST_ASSERT_TRUE(json.length() > 0);
JSONValue *root = JSON::Parse(json.c_str());
TEST_ASSERT_NOT_NULL(root);
TEST_ASSERT_TRUE(root->IsObject());
JSONObject jsonObj = root->AsObject();
// Check message type
TEST_ASSERT_TRUE(jsonObj.find("type") != jsonObj.end());
TEST_ASSERT_EQUAL_STRING("waypoint", jsonObj["type"]->AsString().c_str());
// Check payload
TEST_ASSERT_TRUE(jsonObj.find("payload") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj["payload"]->IsObject());
JSONObject payload = jsonObj["payload"]->AsObject();
// Verify waypoint data
TEST_ASSERT_TRUE(payload.find("id") != payload.end());
TEST_ASSERT_EQUAL(12345, (int)payload["id"]->AsNumber());
TEST_ASSERT_TRUE(payload.find("name") != payload.end());
TEST_ASSERT_EQUAL_STRING("Test Point", payload["name"]->AsString().c_str());
delete root;
}

View File

@ -0,0 +1,44 @@
#pragma once
#include "serialization/JSON.h"
#include "serialization/MeshPacketSerializer.h"
#include <Arduino.h>
#include <meshtastic/mesh.pb.h>
#include <meshtastic/mqtt.pb.h>
#include <meshtastic/telemetry.pb.h>
#include <pb_decode.h>
#include <pb_encode.h>
#include <unity.h>
// Helper function to create a test packet with the given port and payload
static meshtastic_MeshPacket create_test_packet(meshtastic_PortNum port, const uint8_t *payload, size_t payload_size)
{
meshtastic_MeshPacket packet = meshtastic_MeshPacket_init_zero;
packet.id = 0x9999;
packet.from = 0x11223344;
packet.to = 0x55667788;
packet.channel = 0;
packet.hop_limit = 3;
packet.want_ack = false;
packet.priority = meshtastic_MeshPacket_Priority_UNSET;
packet.rx_time = 1609459200;
packet.rx_snr = 10.5f;
packet.hop_start = 3;
packet.rx_rssi = -85;
packet.delayed = meshtastic_MeshPacket_Delayed_NO_DELAY;
// Set decoded variant
packet.which_payload_variant = meshtastic_MeshPacket_decoded_tag;
packet.decoded.portnum = port;
memcpy(packet.decoded.payload.bytes, payload, payload_size);
packet.decoded.payload.size = payload_size;
packet.decoded.want_response = false;
packet.decoded.dest = 0x55667788;
packet.decoded.source = 0x11223344;
packet.decoded.request_id = 0;
packet.decoded.reply_id = 0;
packet.decoded.emoji = 0;
return packet;
}

View File

@ -0,0 +1,51 @@
#include "test_helpers.h"
#include <Arduino.h>
#include <unity.h>
// Forward declarations for test functions
void test_text_message_serialization();
void test_position_serialization();
void test_nodeinfo_serialization();
void test_waypoint_serialization();
void test_telemetry_device_metrics_serialization();
void test_telemetry_environment_metrics_serialization();
void test_telemetry_environment_metrics_comprehensive();
void test_telemetry_environment_metrics_missing_fields();
void test_telemetry_environment_metrics_complete_coverage();
void test_telemetry_environment_metrics_unset_fields();
void test_encrypted_packet_serialization();
void setup()
{
UNITY_BEGIN();
// Text message tests
RUN_TEST(test_text_message_serialization);
// Position tests
RUN_TEST(test_position_serialization);
// Nodeinfo tests
RUN_TEST(test_nodeinfo_serialization);
// Waypoint tests
RUN_TEST(test_waypoint_serialization);
// Telemetry tests
RUN_TEST(test_telemetry_device_metrics_serialization);
RUN_TEST(test_telemetry_environment_metrics_serialization);
RUN_TEST(test_telemetry_environment_metrics_comprehensive);
RUN_TEST(test_telemetry_environment_metrics_missing_fields);
RUN_TEST(test_telemetry_environment_metrics_complete_coverage);
RUN_TEST(test_telemetry_environment_metrics_unset_fields);
// Encrypted packet test
RUN_TEST(test_encrypted_packet_serialization);
UNITY_END();
}
void loop()
{
delay(1000);
}

View File

@ -23,6 +23,7 @@
// "USERPREFS_CONFIG_OWNER_SHORT_NAME": "MLN",
// "USERPREFS_CONFIG_DEVICE_ROLE": "meshtastic_Config_DeviceConfig_Role_CLIENT", // Defaults to CLIENT. ROUTER*, LOST AND FOUND, and REPEATER roles are restricted.
// "USERPREFS_EVENT_MODE": "1",
// "USERPREFS_FIRMWARE_EDITION": "meshtastic_FirmwareEdition_BURNING_MAN",
// "USERPREFS_FIXED_BLUETOOTH": "121212",
// "USERPREFS_FIXED_GPS": "",
// "USERPREFS_FIXED_GPS_ALT": "0",

View File

@ -19,8 +19,6 @@
#ifndef _VARIANT_GAT562_MESH_TRIAL_TRACKER_
#define _VARIANT_GAT562_MESH_TRIAL_TRACKER_
#define GAT562_MESH_TRIAL_TRACKER
// led pin 2 (blue), see https://github.com/meshtastic/firmware/blob/master/src/mesh/NodeDB.cpp#L723
#define RAK4630