diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index b6b17d391..100f3538d 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -12,7 +12,9 @@ build_src_filter = - - - - - + - + - + - +<../variants/portduino> lib_deps = ${env.lib_deps} diff --git a/protobufs b/protobufs index 400dc2358..cb01d7472 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 400dc2358942f1954b188cb5698a545850ca3d73 +Subproject commit cb01d7472fc22321652a5471155dfc6c04a612ab diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 04dbcdd49..39ea76c00 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -103,7 +103,7 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...) } r += vprintf(format, arg); -#if HAS_WIFI || HAS_ETHERNET +#if (HAS_WIFI || HAS_ETHERNET) && !defined(ARCH_PORTDUINO) // if syslog is in use, collect the log messages and send them to syslog if (syslog.isEnabled()) { int ll = 0; diff --git a/src/main.cpp b/src/main.cpp index 9f907aee0..2bd153ea5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -392,6 +392,19 @@ void setup() // radio init MUST BE AFTER service.init, so we have our radio config settings (from nodedb init) +#if !HAS_RADIO && defined(ARCH_PORTDUINO) + if (!rIf) { + rIf = new SimRadio; + if (!rIf->init()) { + LOG_WARN("Failed to find simulated radio\n"); + delete rIf; + rIf = NULL; + } else { + LOG_INFO("Using SIMULATED radio!\n"); + } + } +#endif + #if defined(RF95_IRQ) if (!rIf) { rIf = new RF95Interface(RF95_NSS, RF95_IRQ, RF95_RESET, SPI); @@ -457,19 +470,6 @@ void setup() } #endif -#ifdef ARCH_PORTDUINO - if (!rIf) { - rIf = new SimRadio; - if (!rIf->init()) { - LOG_WARN("Failed to find simulated radio\n"); - delete rIf; - rIf = NULL; - } else { - LOG_INFO("Using SIMULATED radio!\n"); - } - } -#endif - // check if the radio chip matches the selected region if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())) { @@ -535,6 +535,21 @@ uint32_t shutdownAtMsec; // If not zero we will shutdown at this time (used to s // This will supress the current delay and instead try to run ASAP. bool runASAP; +extern meshtastic_DeviceMetadata getDeviceMetadata() +{ + meshtastic_DeviceMetadata deviceMetadata; + strncpy(deviceMetadata.firmware_version, myNodeInfo.firmware_version, 18); + deviceMetadata.device_state_version = DEVICESTATE_CUR_VER; + deviceMetadata.canShutdown = pmu_found || HAS_CPU_SHUTDOWN; + deviceMetadata.hasBluetooth = HAS_BLUETOOTH; + deviceMetadata.hasWifi = HAS_WIFI; + deviceMetadata.hasEthernet = HAS_ETHERNET; + deviceMetadata.role = config.device.role; + deviceMetadata.position_flags = config.position.position_flags; + deviceMetadata.hw_model = HW_VENDOR; + return deviceMetadata; +} + void loop() { runASAP = false; diff --git a/src/main.h b/src/main.h index 66de776da..94f50feae 100644 --- a/src/main.h +++ b/src/main.h @@ -62,3 +62,5 @@ extern uint32_t serialSinceMsec; extern bool runASAP; void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), clearBonds(); + +meshtastic_DeviceMetadata getDeviceMetadata(); diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index cc54e78c2..d8dbf8a3a 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -77,6 +77,8 @@ void MeshModule::callPlugins(const meshtastic_MeshPacket &mp, RxSource src) currentReply = NULL; // No reply yet + bool ignoreRequest = false; // No module asked to ignore the request yet + // Was this message directed to us specifically? Will be false if we are sniffing someone elses packets auto ourNodeNum = nodeDB.getNodeNum(); bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum; @@ -135,7 +137,8 @@ void MeshModule::callPlugins(const meshtastic_MeshPacket &mp, RxSource src) // any other node. if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) { pi.sendResponse(mp); - LOG_INFO("Module '%s' sent a response\n", pi.name); + ignoreRequest = ignoreRequest || pi.ignoreRequest; // If at least one module asks it, we may ignore a request + LOG_INFO("Asked module '%s' to send a response\n", pi.name); } else { LOG_DEBUG("Module '%s' considered\n", pi.name); } @@ -162,8 +165,9 @@ void MeshModule::callPlugins(const meshtastic_MeshPacket &mp, RxSource src) printPacket("Sending response", currentReply); service.sendToMesh(currentReply); currentReply = NULL; - } else if (mp.from != ourNodeNum) { - // Note: if the message started with the local node we don't want to send a no response reply + } else if (mp.from != ourNodeNum && !ignoreRequest) { + // Note: if the message started with the local node or a module asked to ignore the request, we don't want to send a + // no response reply // No one wanted to reply to this requst, tell the requster that happened LOG_DEBUG("No one responded, send a nak\n"); diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h index c6550f3d9..2eee04f5d 100644 --- a/src/mesh/MeshModule.h +++ b/src/mesh/MeshModule.h @@ -94,6 +94,9 @@ class MeshModule * flag */ bool encryptedOk = false; + /* We allow modules to ignore a request without sending an error if they have a specific reason for it. */ + bool ignoreRequest = false; + /** If a bound channel name is set, we will only accept received packets that come in on that channel. * A special exception (FIXME, not sure if this is a good idea) - packets that arrive on the local interface * are allowed on any channel (this lets the local user do anything). diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 85a6390c9..80fea011b 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -75,9 +75,13 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp) { powerFSM.trigger(EVENT_PACKET_FOR_PHONE); // Possibly keep the node from sleeping - printPacket("Forwarding to phone", mp); nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio + if (!nodeDB.getNode(mp->from)->has_user && nodeInfoModule) { + LOG_INFO("Heard a node we don't know, sending NodeInfo and asking for a response.\n"); + nodeInfoModule->sendOurNodeInfo(mp->from, true); + } + printPacket("Forwarding to phone", mp); sendToPhone((meshtastic_MeshPacket *)mp); return 0; @@ -132,7 +136,7 @@ void MeshService::reloadOwner(bool shouldSave) */ void MeshService::handleToRadio(meshtastic_MeshPacket &p) { -#ifdef ARCH_PORTDUINO +#if defined(ARCH_PORTDUINO) && !HAS_RADIO // Simulates device is receiving a packet via the LoRa chip if (p.decoded.portnum == meshtastic_PortNum_SIMULATOR_APP) { // Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index b8abac80d..4314ea362 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -10,7 +10,7 @@ #include "MeshTypes.h" #include "Observer.h" #include "PointerQueue.h" -#ifdef ARCH_PORTDUINO +#if defined(ARCH_PORTDUINO) && !HAS_RADIO #include "../platform/portduino/SimRadio.h" #endif diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 89063f550..5c28f5261 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -170,6 +170,8 @@ void NodeDB::installDefaultConfig() config.lora.hop_limit = HOP_RELIABLE; config.position.gps_enabled = true; config.position.position_broadcast_smart_enabled = true; + if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER) + config.device.node_info_broadcast_secs = 3 * 60 * 60; config.device.serial_enabled = true; resetRadioConfig(); strncpy(config.network.ntp_server, "0.pool.ntp.org", 32); diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 0c430ea5c..cdda05913 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -6,6 +6,7 @@ #include "PowerFSM.h" #include "RadioInterface.h" #include "configuration.h" +#include "main.h" #include "xmodem.h" #if FromRadio_size > MAX_TO_FROM_RADIO_SIZE @@ -120,6 +121,7 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) * STATE_SEND_NODEINFO, // states progress in this order as the device sends to the client STATE_SEND_CONFIG, STATE_SEND_MODULE_CONFIG, + STATE_SEND_METADATA, STATE_SEND_COMPLETE_ID, STATE_SEND_PACKETS // send packets or debug strings */ @@ -277,11 +279,15 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) config_state++; // Advance when we have sent all of our ModuleConfig objects if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) { - state = STATE_SEND_COMPLETE_ID; + state = STATE_SEND_METADATA; config_state = 0; } break; - + case STATE_SEND_METADATA: + fromRadioScratch.which_payload_variant = meshtastic_FromRadio_metadata_tag; + fromRadioScratch.metadata = getDeviceMetadata(); + state = STATE_SEND_COMPLETE_ID; + break; case STATE_SEND_COMPLETE_ID: LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n"); fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag; @@ -361,6 +367,7 @@ bool PhoneAPI::available() case STATE_SEND_CHANNELS: case STATE_SEND_CONFIG: case STATE_SEND_MODULECONFIG: + case STATE_SEND_METADATA: case STATE_SEND_COMPLETE_ID: return true; diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 5a73f5874..af8f1be26 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -26,6 +26,7 @@ class PhoneAPI STATE_SEND_CHANNELS, // Send all channels STATE_SEND_CONFIG, // Replacement for the old Radioconfig STATE_SEND_MODULECONFIG, // Send Module specific config + STATE_SEND_METADATA, STATE_SEND_COMPLETE_ID, STATE_SEND_PACKETS // send packets or debug strings }; diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index f4344941d..2e378aacb 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -67,19 +67,12 @@ bool RF95Interface::init() #endif setTransmitEnable(false); - int res = lora->begin(getFreq(), bw, sf, cr, syncWord, power, currentLimit, preambleLength); + int res = lora->begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength); LOG_INFO("RF95 init result %d\n", res); - LOG_INFO("Frequency set to %f\n", getFreq()); LOG_INFO("Bandwidth set to %f\n", bw); LOG_INFO("Power output set to %d\n", power); - // current limit was removed from module' ctor - // override default value (60 mA) - res = lora->setCurrentLimit(currentLimit); - LOG_DEBUG("Current limit set to %f\n", currentLimit); - LOG_DEBUG("Current limit set result %d\n", res); - if (res == RADIOLIB_ERR_NONE) res = lora->setCRC(RADIOLIB_SX126X_LORA_CRC_ON); diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 1a84c87f9..b9511fd5d 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -63,7 +63,7 @@ class RadioInterface - Tx/Rx turnaround time (maximum of SX126x and SX127x); - MAC processing time (measured on T-beam) */ uint32_t slotTimeMsec = 8.5 * pow(2, sf) / bw + 0.2 + 0.4 + 7; - uint16_t preambleLength = 32; // 8 is default, but we use longer to increase the amount of sleep time when receiving + uint16_t preambleLength = 16; // 8 is default, but we use longer to increase the amount of sleep time when receiving const uint32_t PROCESSING_TIME_MSEC = 4500; // time to construct, process and construct a packet again (empirically determined) const uint8_t CWmin = 2; // minimum CWsize diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index 25bc77241..a9c415992 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -6,7 +6,6 @@ #include #include "meshtastic/channel.pb.h" #include "meshtastic/config.pb.h" -#include "meshtastic/device_metadata.pb.h" #include "meshtastic/mesh.pb.h" #include "meshtastic/module_config.pb.h" #include "meshtastic/connection_status.pb.h" @@ -67,6 +66,8 @@ typedef struct _meshtastic_HamParameters { Please respect your local laws, regulations, and band plans. Ensure your radio is capable of operating of the selected frequency before setting this. */ float frequency; + /* Optional short name of user */ + char short_name[6]; } meshtastic_HamParameters; /* This message is handled by the Admin module and is responsible for all settings/channel read/write operations. @@ -169,14 +170,15 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_AdminMessage_init_default {0, {0}} -#define meshtastic_HamParameters_init_default {"", 0, 0} +#define meshtastic_HamParameters_init_default {"", 0, 0, ""} #define meshtastic_AdminMessage_init_zero {0, {0}} -#define meshtastic_HamParameters_init_zero {"", 0, 0} +#define meshtastic_HamParameters_init_zero {"", 0, 0, ""} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_HamParameters_call_sign_tag 1 #define meshtastic_HamParameters_tx_power_tag 2 #define meshtastic_HamParameters_frequency_tag 3 +#define meshtastic_HamParameters_short_name_tag 4 #define meshtastic_AdminMessage_get_channel_request_tag 1 #define meshtastic_AdminMessage_get_channel_response_tag 2 #define meshtastic_AdminMessage_get_owner_request_tag 3 @@ -259,7 +261,8 @@ X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), #define meshtastic_HamParameters_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, STRING, call_sign, 1) \ X(a, STATIC, SINGULAR, INT32, tx_power, 2) \ -X(a, STATIC, SINGULAR, FLOAT, frequency, 3) +X(a, STATIC, SINGULAR, FLOAT, frequency, 3) \ +X(a, STATIC, SINGULAR, STRING, short_name, 4) #define meshtastic_HamParameters_CALLBACK NULL #define meshtastic_HamParameters_DEFAULT NULL @@ -272,7 +275,7 @@ extern const pb_msgdesc_t meshtastic_HamParameters_msg; /* Maximum encoded size of messages (where known) */ #define meshtastic_AdminMessage_size 234 -#define meshtastic_HamParameters_size 25 +#define meshtastic_HamParameters_size 32 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/device_metadata.pb.c b/src/mesh/generated/meshtastic/device_metadata.pb.c deleted file mode 100644 index 627ce3244..000000000 --- a/src/mesh/generated/meshtastic/device_metadata.pb.c +++ /dev/null @@ -1,12 +0,0 @@ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.7 */ - -#include "meshtastic/device_metadata.pb.h" -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -PB_BIND(meshtastic_DeviceMetadata, meshtastic_DeviceMetadata, AUTO) - - - diff --git a/src/mesh/generated/meshtastic/device_metadata.pb.h b/src/mesh/generated/meshtastic/device_metadata.pb.h deleted file mode 100644 index 44cf29079..000000000 --- a/src/mesh/generated/meshtastic/device_metadata.pb.h +++ /dev/null @@ -1,83 +0,0 @@ -/* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.7 */ - -#ifndef PB_MESHTASTIC_MESHTASTIC_DEVICE_METADATA_PB_H_INCLUDED -#define PB_MESHTASTIC_MESHTASTIC_DEVICE_METADATA_PB_H_INCLUDED -#include -#include "meshtastic/config.pb.h" -#include "meshtastic/mesh.pb.h" - -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -/* Struct definitions */ -/* Device metadata response */ -typedef struct _meshtastic_DeviceMetadata { - /* Device firmware version string */ - char firmware_version[18]; - /* Device state version */ - uint32_t device_state_version; - /* Indicates whether the device can shutdown CPU natively or via power management chip */ - bool canShutdown; - /* Indicates that the device has native wifi capability */ - bool hasWifi; - /* Indicates that the device has native bluetooth capability */ - bool hasBluetooth; - /* Indicates that the device has an ethernet peripheral */ - bool hasEthernet; - /* Indicates that the device's role in the mesh */ - meshtastic_Config_DeviceConfig_Role role; - /* Indicates the device's current enabled position flags */ - uint32_t position_flags; - /* Device hardware model */ - meshtastic_HardwareModel hw_model; -} meshtastic_DeviceMetadata; - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Initializer values for message structs */ -#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN} -#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN} - -/* Field tags (for use in manual encoding/decoding) */ -#define meshtastic_DeviceMetadata_firmware_version_tag 1 -#define meshtastic_DeviceMetadata_device_state_version_tag 2 -#define meshtastic_DeviceMetadata_canShutdown_tag 3 -#define meshtastic_DeviceMetadata_hasWifi_tag 4 -#define meshtastic_DeviceMetadata_hasBluetooth_tag 5 -#define meshtastic_DeviceMetadata_hasEthernet_tag 6 -#define meshtastic_DeviceMetadata_role_tag 7 -#define meshtastic_DeviceMetadata_position_flags_tag 8 -#define meshtastic_DeviceMetadata_hw_model_tag 9 - -/* Struct field encoding specification for nanopb */ -#define meshtastic_DeviceMetadata_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, STRING, firmware_version, 1) \ -X(a, STATIC, SINGULAR, UINT32, device_state_version, 2) \ -X(a, STATIC, SINGULAR, BOOL, canShutdown, 3) \ -X(a, STATIC, SINGULAR, BOOL, hasWifi, 4) \ -X(a, STATIC, SINGULAR, BOOL, hasBluetooth, 5) \ -X(a, STATIC, SINGULAR, BOOL, hasEthernet, 6) \ -X(a, STATIC, SINGULAR, UENUM, role, 7) \ -X(a, STATIC, SINGULAR, UINT32, position_flags, 8) \ -X(a, STATIC, SINGULAR, UENUM, hw_model, 9) -#define meshtastic_DeviceMetadata_CALLBACK NULL -#define meshtastic_DeviceMetadata_DEFAULT NULL - -extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg; - -/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ -#define meshtastic_DeviceMetadata_fields &meshtastic_DeviceMetadata_msg - -/* Maximum encoded size of messages (where known) */ -#define meshtastic_DeviceMetadata_size 44 - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/src/mesh/generated/meshtastic/mesh.pb.c b/src/mesh/generated/meshtastic/mesh.pb.c index 977e3916f..0c3c155f6 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.c +++ b/src/mesh/generated/meshtastic/mesh.pb.c @@ -48,6 +48,9 @@ PB_BIND(meshtastic_ToRadio, meshtastic_ToRadio, 2) PB_BIND(meshtastic_Compressed, meshtastic_Compressed, AUTO) +PB_BIND(meshtastic_DeviceMetadata, meshtastic_DeviceMetadata, AUTO) + + diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 0a8cf5cfe..25e5fbf05 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -87,6 +87,10 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_HELTEC_WSL_V3 = 44, /* New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU */ meshtastic_HardwareModel_BETAFPV_2400_TX = 45, + /* BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU */ + meshtastic_HardwareModel_BETAFPV_900_NANO_TX = 46, + /* B&Q Consulting Nano G1 Explorer: https://wiki.uniteng.com/en/meshtastic/nano-g1-explorer */ + meshtastic_HardwareModel_NANO_G1_EXPLORER = 47, /* 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. */ meshtastic_HardwareModel_PRIVATE_HW = 255 } meshtastic_HardwareModel; @@ -637,6 +641,61 @@ typedef struct _meshtastic_QueueStatus { uint32_t mesh_packet_id; } meshtastic_QueueStatus; +/* Packets/commands to the radio will be written (reliably) to the toRadio characteristic. + Once the write completes the phone can assume it is handled. */ +typedef struct _meshtastic_ToRadio { + pb_size_t which_payload_variant; + union { + /* Send this packet on the mesh */ + meshtastic_MeshPacket packet; + /* Phone wants radio to send full node db to the phone, This is + typically the first packet sent to the radio when the phone gets a + bluetooth connection. The radio will respond by sending back a + MyNodeInfo, a owner, a radio config and a series of + FromRadio.node_infos, and config_complete + the integer you write into this field will be reported back in the + config_complete_id response this allows clients to never be confused by + a stale old partially sent config. */ + uint32_t want_config_id; + /* Tell API server we are disconnecting now. + This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link. + (Sending this message is optional for clients) */ + bool disconnect; + meshtastic_XModem xmodemPacket; + }; +} meshtastic_ToRadio; + +typedef PB_BYTES_ARRAY_T(237) meshtastic_Compressed_data_t; +/* Compressed message payload */ +typedef struct _meshtastic_Compressed { + /* PortNum to determine the how to handle the compressed payload. */ + meshtastic_PortNum portnum; + /* Compressed data. */ + meshtastic_Compressed_data_t data; +} meshtastic_Compressed; + +/* Device metadata response */ +typedef struct _meshtastic_DeviceMetadata { + /* Device firmware version string */ + char firmware_version[18]; + /* Device state version */ + uint32_t device_state_version; + /* Indicates whether the device can shutdown CPU natively or via power management chip */ + bool canShutdown; + /* Indicates that the device has native wifi capability */ + bool hasWifi; + /* Indicates that the device has native bluetooth capability */ + bool hasBluetooth; + /* Indicates that the device has an ethernet peripheral */ + bool hasEthernet; + /* Indicates that the device's role in the mesh */ + meshtastic_Config_DeviceConfig_Role role; + /* Indicates the device's current enabled position flags */ + uint32_t position_flags; + /* Device hardware model */ + meshtastic_HardwareModel hw_model; +} meshtastic_DeviceMetadata; + /* Packets from the radio to the phone will appear on the fromRadio characteristic. It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? It will sit in that descriptor until consumed by the phone, @@ -677,42 +736,11 @@ typedef struct _meshtastic_FromRadio { meshtastic_QueueStatus queueStatus; /* File Transfer Chunk */ meshtastic_XModem xmodemPacket; + /* Device metadata message */ + meshtastic_DeviceMetadata metadata; }; } meshtastic_FromRadio; -/* Packets/commands to the radio will be written (reliably) to the toRadio characteristic. - Once the write completes the phone can assume it is handled. */ -typedef struct _meshtastic_ToRadio { - pb_size_t which_payload_variant; - union { - /* Send this packet on the mesh */ - meshtastic_MeshPacket packet; - /* Phone wants radio to send full node db to the phone, This is - typically the first packet sent to the radio when the phone gets a - bluetooth connection. The radio will respond by sending back a - MyNodeInfo, a owner, a radio config and a series of - FromRadio.node_infos, and config_complete - the integer you write into this field will be reported back in the - config_complete_id response this allows clients to never be confused by - a stale old partially sent config. */ - uint32_t want_config_id; - /* Tell API server we are disconnecting now. - This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link. - (Sending this message is optional for clients) */ - bool disconnect; - meshtastic_XModem xmodemPacket; - }; -} meshtastic_ToRadio; - -typedef PB_BYTES_ARRAY_T(237) meshtastic_Compressed_data_t; -/* Compressed message payload */ -typedef struct _meshtastic_Compressed { - /* PortNum to determine the how to handle the compressed payload. */ - meshtastic_PortNum portnum; - /* Compressed data. */ - meshtastic_Compressed_data_t data; -} meshtastic_Compressed; - #ifdef __cplusplus extern "C" { @@ -779,6 +807,9 @@ extern "C" { #define meshtastic_Compressed_portnum_ENUMTYPE meshtastic_PortNum +#define meshtastic_DeviceMetadata_role_ENUMTYPE meshtastic_Config_DeviceConfig_Role +#define meshtastic_DeviceMetadata_hw_model_ENUMTYPE meshtastic_HardwareModel + /* Initializer values for message structs */ #define meshtastic_Position_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} @@ -795,6 +826,7 @@ extern "C" { #define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}} #define meshtastic_ToRadio_init_default {0, {meshtastic_MeshPacket_init_default}} #define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}} +#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN} #define meshtastic_Position_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0} #define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} @@ -809,6 +841,7 @@ extern "C" { #define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}} #define meshtastic_ToRadio_init_zero {0, {meshtastic_MeshPacket_init_zero}} #define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}} +#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_Position_latitude_i_tag 1 @@ -902,6 +935,21 @@ extern "C" { #define meshtastic_QueueStatus_free_tag 2 #define meshtastic_QueueStatus_maxlen_tag 3 #define meshtastic_QueueStatus_mesh_packet_id_tag 4 +#define meshtastic_ToRadio_packet_tag 1 +#define meshtastic_ToRadio_want_config_id_tag 3 +#define meshtastic_ToRadio_disconnect_tag 4 +#define meshtastic_ToRadio_xmodemPacket_tag 5 +#define meshtastic_Compressed_portnum_tag 1 +#define meshtastic_Compressed_data_tag 2 +#define meshtastic_DeviceMetadata_firmware_version_tag 1 +#define meshtastic_DeviceMetadata_device_state_version_tag 2 +#define meshtastic_DeviceMetadata_canShutdown_tag 3 +#define meshtastic_DeviceMetadata_hasWifi_tag 4 +#define meshtastic_DeviceMetadata_hasBluetooth_tag 5 +#define meshtastic_DeviceMetadata_hasEthernet_tag 6 +#define meshtastic_DeviceMetadata_role_tag 7 +#define meshtastic_DeviceMetadata_position_flags_tag 8 +#define meshtastic_DeviceMetadata_hw_model_tag 9 #define meshtastic_FromRadio_id_tag 1 #define meshtastic_FromRadio_packet_tag 2 #define meshtastic_FromRadio_my_info_tag 3 @@ -914,12 +962,7 @@ extern "C" { #define meshtastic_FromRadio_channel_tag 10 #define meshtastic_FromRadio_queueStatus_tag 11 #define meshtastic_FromRadio_xmodemPacket_tag 12 -#define meshtastic_ToRadio_packet_tag 1 -#define meshtastic_ToRadio_want_config_id_tag 3 -#define meshtastic_ToRadio_disconnect_tag 4 -#define meshtastic_ToRadio_xmodemPacket_tag 5 -#define meshtastic_Compressed_portnum_tag 1 -#define meshtastic_Compressed_data_tag 2 +#define meshtastic_FromRadio_metadata_tag 13 /* Struct field encoding specification for nanopb */ #define meshtastic_Position_FIELDLIST(X, a) \ @@ -1075,7 +1118,8 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,rebooted,rebooted), 8) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,moduleConfig,moduleConfig), 9) \ 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,xmodemPacket,xmodemPacket), 12) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) #define meshtastic_FromRadio_CALLBACK NULL #define meshtastic_FromRadio_DEFAULT NULL #define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket @@ -1087,6 +1131,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), #define meshtastic_FromRadio_payload_variant_channel_MSGTYPE meshtastic_Channel #define meshtastic_FromRadio_payload_variant_queueStatus_MSGTYPE meshtastic_QueueStatus #define meshtastic_FromRadio_payload_variant_xmodemPacket_MSGTYPE meshtastic_XModem +#define meshtastic_FromRadio_payload_variant_metadata_MSGTYPE meshtastic_DeviceMetadata #define meshtastic_ToRadio_FIELDLIST(X, a) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 1) \ @@ -1104,6 +1149,19 @@ X(a, STATIC, SINGULAR, BYTES, data, 2) #define meshtastic_Compressed_CALLBACK NULL #define meshtastic_Compressed_DEFAULT NULL +#define meshtastic_DeviceMetadata_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, firmware_version, 1) \ +X(a, STATIC, SINGULAR, UINT32, device_state_version, 2) \ +X(a, STATIC, SINGULAR, BOOL, canShutdown, 3) \ +X(a, STATIC, SINGULAR, BOOL, hasWifi, 4) \ +X(a, STATIC, SINGULAR, BOOL, hasBluetooth, 5) \ +X(a, STATIC, SINGULAR, BOOL, hasEthernet, 6) \ +X(a, STATIC, SINGULAR, UENUM, role, 7) \ +X(a, STATIC, SINGULAR, UINT32, position_flags, 8) \ +X(a, STATIC, SINGULAR, UENUM, hw_model, 9) +#define meshtastic_DeviceMetadata_CALLBACK NULL +#define meshtastic_DeviceMetadata_DEFAULT NULL + extern const pb_msgdesc_t meshtastic_Position_msg; extern const pb_msgdesc_t meshtastic_User_msg; extern const pb_msgdesc_t meshtastic_RouteDiscovery_msg; @@ -1118,6 +1176,7 @@ extern const pb_msgdesc_t meshtastic_QueueStatus_msg; extern const pb_msgdesc_t meshtastic_FromRadio_msg; extern const pb_msgdesc_t meshtastic_ToRadio_msg; extern const pb_msgdesc_t meshtastic_Compressed_msg; +extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_Position_fields &meshtastic_Position_msg @@ -1134,10 +1193,12 @@ extern const pb_msgdesc_t meshtastic_Compressed_msg; #define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg #define meshtastic_ToRadio_fields &meshtastic_ToRadio_msg #define meshtastic_Compressed_fields &meshtastic_Compressed_msg +#define meshtastic_DeviceMetadata_fields &meshtastic_DeviceMetadata_msg /* Maximum encoded size of messages (where known) */ #define meshtastic_Compressed_size 243 #define meshtastic_Data_size 270 +#define meshtastic_DeviceMetadata_size 44 #define meshtastic_FromRadio_size 330 #define meshtastic_LogRecord_size 81 #define meshtastic_MeshPacket_size 321 diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 82730b9ca..4c50d7059 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -490,19 +490,7 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const void AdminModule::handleGetDeviceMetadata(const meshtastic_MeshPacket &req) { meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default; - - meshtastic_DeviceMetadata deviceMetadata; - strncpy(deviceMetadata.firmware_version, myNodeInfo.firmware_version, 18); - deviceMetadata.device_state_version = DEVICESTATE_CUR_VER; - deviceMetadata.canShutdown = pmu_found || HAS_CPU_SHUTDOWN; - deviceMetadata.hasBluetooth = HAS_BLUETOOTH; - deviceMetadata.hasWifi = HAS_WIFI; - deviceMetadata.hasEthernet = HAS_ETHERNET; - deviceMetadata.role = config.device.role; - deviceMetadata.position_flags = config.position.position_flags; - deviceMetadata.hw_model = HW_VENDOR; - - r.get_device_metadata_response = deviceMetadata; + r.get_device_metadata_response = getDeviceMetadata(); r.which_payload_variant = meshtastic_AdminMessage_get_device_metadata_response_tag; myReply = allocDataProtobuf(r); } @@ -603,6 +591,7 @@ void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p) { // Set call sign and override lora limitations for licensed use strncpy(owner.long_name, p.call_sign, sizeof(owner.long_name)); + strncpy(owner.short_name, p.short_name, sizeof(owner.short_name)); owner.is_licensed = true; config.lora.override_duty_cycle = true; config.lora.tx_power = p.tx_power; diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index bc874a9a3..3247d02c1 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -14,8 +14,10 @@ #include "modules/TraceRouteModule.h" #include "modules/WaypointModule.h" #if HAS_TELEMETRY -#include "modules/Telemetry/AirQualityTelemetry.h" #include "modules/Telemetry/DeviceTelemetry.h" +#endif +#if HAS_SENSOR +#include "modules/Telemetry/AirQualityTelemetry.h" #include "modules/Telemetry/EnvironmentTelemetry.h" #endif #ifdef ARCH_ESP32 @@ -63,6 +65,8 @@ void setupModules() #endif #if HAS_TELEMETRY new DeviceTelemetryModule(); +#endif +#if HAS_SENSOR new EnvironmentTelemetryModule(); if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I] > 0) { new AirQualityTelemetryModule(); diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index 15661e837..004ad75f0 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -34,20 +34,32 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies) service.cancelSending(prevPacketId); meshtastic_MeshPacket *p = allocReply(); - p->to = dest; - p->decoded.want_response = wantReplies; - p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; - prevPacketId = p->id; + if (p) { // Check whether we didn't ignore it + p->to = dest; + p->decoded.want_response = wantReplies; + p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; + prevPacketId = p->id; - service.sendToMesh(p); + service.sendToMesh(p); + } } meshtastic_MeshPacket *NodeInfoModule::allocReply() { - meshtastic_User &u = owner; + uint32_t now = millis(); + // If we sent our NodeInfo less than 1 min. ago, don't send it again as it may be still underway. + if (lastSentToMesh && (now - lastSentToMesh) < 60 * 1000) { + LOG_DEBUG("Sending NodeInfo will be ignored since we just sent it.\n"); + ignoreRequest = true; // Mark it as ignored for MeshModule + return NULL; + } else { + ignoreRequest = false; // Don't ignore requests anymore + meshtastic_User &u = owner; - LOG_INFO("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); - return allocDataProtobuf(u); + LOG_INFO("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); + lastSentToMesh = now; + return allocDataProtobuf(u); + } } NodeInfoModule::NodeInfoModule() diff --git a/src/modules/NodeInfoModule.h b/src/modules/NodeInfoModule.h index 4645b06b4..900d5983d 100644 --- a/src/modules/NodeInfoModule.h +++ b/src/modules/NodeInfoModule.h @@ -35,6 +35,9 @@ class NodeInfoModule : public ProtobufModule, private concurren /** Does our periodic broadcast */ virtual int32_t runOnce() override; + + private: + uint32_t lastSentToMesh = 0; // Last time we sent our NodeInfo to the mesh }; extern NodeInfoModule *nodeInfoModule; diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index 2b744c489..966e2d0ff 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -10,7 +10,6 @@ int32_t AirQualityTelemetryModule::runOnce() { -#ifndef ARCH_PORTDUINO int32_t result = INT32_MAX; /* Uncomment the preferences below if you want to use the module @@ -55,7 +54,6 @@ int32_t AirQualityTelemetryModule::runOnce() } } return sendToPhoneIntervalMs; -#endif } bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) diff --git a/src/modules/Telemetry/DeviceTelemetry.h b/src/modules/Telemetry/DeviceTelemetry.h index ba16e095a..bd2a69da5 100644 --- a/src/modules/Telemetry/DeviceTelemetry.h +++ b/src/modules/Telemetry/DeviceTelemetry.h @@ -12,7 +12,7 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu : concurrency::OSThread("DeviceTelemetryModule"), ProtobufModule("DeviceTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) { - setIntervalFromNow(10 * 1000); + 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 d9b129e70..bd7ed539a 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -52,7 +52,6 @@ SHT31Sensor sht31Sensor; int32_t EnvironmentTelemetryModule::runOnce() { -#ifndef ARCH_PORTDUINO int32_t result = INT32_MAX; /* Uncomment the preferences below if you want to use the module @@ -115,7 +114,6 @@ int32_t EnvironmentTelemetryModule::runOnce() } } return sendToPhoneIntervalMs; -#endif } bool EnvironmentTelemetryModule::wantUIFrame() diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 09b754d0f..90c016dae 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -27,6 +27,9 @@ #ifndef HAS_TELEMETRY #define HAS_TELEMETRY 1 #endif +#ifndef HAS_SENSOR +#define HAS_SENSOR 1 +#endif #ifndef HAS_RADIO #define HAS_RADIO 1 #endif diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index 383a0da4e..f21635560 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -23,6 +23,9 @@ #ifndef HAS_TELEMETRY #define HAS_TELEMETRY 1 #endif +#ifndef HAS_SENSOR +#define HAS_SENSOR 1 +#endif #ifndef HAS_RADIO #define HAS_RADIO 1 #endif diff --git a/src/platform/portduino/SimRadio.cpp b/src/platform/portduino/SimRadio.cpp index fa4212669..f71113ab4 100644 --- a/src/platform/portduino/SimRadio.cpp +++ b/src/platform/portduino/SimRadio.cpp @@ -2,7 +2,7 @@ #include "MeshService.h" #include "Router.h" -SimRadio::SimRadio() +SimRadio::SimRadio() : NotifiedWorkerThread("SimRadio") { instance = this; } @@ -53,10 +53,7 @@ void SimRadio::startTransmitTimer(bool withDelay) if (!txQueue.empty()) { uint32_t delayMsec = !withDelay ? 1 : getTxDelayMsec(); // LOG_DEBUG("xmit timer %d\n", delay); - delay(delayMsec); - onNotify(TRANSMIT_DELAY_COMPLETED); - } else { - LOG_DEBUG("TX QUEUE EMPTY!\n"); + notifyLater(delayMsec, TRANSMIT_DELAY_COMPLETED, false); } } @@ -66,8 +63,7 @@ void SimRadio::startTransmitTimerSNR(float snr) if (!txQueue.empty()) { uint32_t delayMsec = getTxDelayMsecWeighted(snr); // LOG_DEBUG("xmit timer %d\n", delay); - delay(delayMsec); - onNotify(TRANSMIT_DELAY_COMPLETED); + notifyLater(delayMsec, TRANSMIT_DELAY_COMPLETED, false); } } @@ -142,11 +138,12 @@ void SimRadio::onNotify(uint32_t notification) switch (notification) { case ISR_TX: handleTransmitInterrupt(); - LOG_DEBUG("tx complete - starting timer\n"); + // LOG_DEBUG("tx complete - starting timer\n"); startTransmitTimer(); break; case ISR_RX: - LOG_DEBUG("rx complete - starting timer\n"); + // LOG_DEBUG("rx complete - starting timer\n"); + startTransmitTimer(); break; case TRANSMIT_DELAY_COMPLETED: LOG_DEBUG("delay done\n"); @@ -170,8 +167,7 @@ void SimRadio::onNotify(uint32_t notification) uint32_t xmitMsec = getPacketTime(txp); airTime->logAirtime(TX_LOG, xmitMsec); - delay(xmitMsec); // Model the time it is busy sending - completeSending(); + notifyLater(xmitMsec, ISR_TX, false); // Model the time it is busy sending } } } else { @@ -242,8 +238,7 @@ void SimRadio::handleReceiveInterrupt(meshtastic_MeshPacket *p) xmitMsec = getPacketTime(length); // LOG_DEBUG("Payload size %d vs length (includes header) %d\n", p->decoded.payload.size, length); - meshtastic_MeshPacket *mp = packetPool.allocCopy(*p); // keep a copy in packtPool - mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // Mark that the payload is already decoded + meshtastic_MeshPacket *mp = packetPool.allocCopy(*p); // keep a copy in packetPool printPacket("Lora RX", mp); @@ -268,4 +263,4 @@ int16_t SimRadio::readData(uint8_t *data, size_t len) } return state; -} \ No newline at end of file +} diff --git a/src/platform/portduino/SimRadio.h b/src/platform/portduino/SimRadio.h index b78beb70a..1edb4963b 100644 --- a/src/platform/portduino/SimRadio.h +++ b/src/platform/portduino/SimRadio.h @@ -3,10 +3,11 @@ #include "MeshPacketQueue.h" #include "RadioInterface.h" #include "api/WiFiServerAPI.h" +#include "concurrency/NotifiedWorkerThread.h" #include -class SimRadio : public RadioInterface +class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThread { enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED }; diff --git a/src/platform/portduino/architecture.h b/src/platform/portduino/architecture.h index 54df672d8..9408ad19c 100644 --- a/src/platform/portduino/architecture.h +++ b/src/platform/portduino/architecture.h @@ -2,15 +2,18 @@ #define ARCH_PORTDUINO -// -// defaults for NRF52 architecture -// - // // set HW_VENDOR // #define HW_VENDOR meshtastic_HardwareModel_PORTDUINO -#define HAS_RTC 1 +#ifndef HAS_WIFI #define HAS_WIFI 1 +#endif +#ifndef HAS_RTC +#define HAS_RTC 1 +#endif +#ifndef HAS_TELEMETRY +#define HAS_TELEMETRY 1 +#endif diff --git a/version.properties b/version.properties index 6443d32c2..bdda0d97b 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 0 -build = 20 +build = 21