From 92225eb6c3ac60a12995432a4bfac0ee3641f093 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 13 Dec 2024 11:48:27 -0600 Subject: [PATCH 01/43] DIO3_TCXO_VOLTAGE in config.yaml can now take an exact voltage (#5558) --- src/mesh/LR11x0Interface.cpp | 4 +++- src/mesh/SX126xInterface.cpp | 4 +--- src/platform/portduino/PortduinoGlue.cpp | 5 ++++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index 30ef8f9af..ce4f912ba 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -48,8 +48,10 @@ template bool LR11x0Interface::init() digitalWrite(LR11X0_POWER_EN, HIGH); #endif +#if ARCH_PORTDUINO + float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; // FIXME: correct logic to default to not using TCXO if no voltage is specified for LR11x0_DIO3_TCXO_VOLTAGE -#if !defined(LR11X0_DIO3_TCXO_VOLTAGE) +#elif !defined(LR11X0_DIO3_TCXO_VOLTAGE) float tcxoVoltage = 0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per // https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/LR11x0/LR11x0.h#L471C26-L471C104 diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 002fb41ca..ed0267c5b 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -50,9 +50,7 @@ template bool SX126xInterface::init() #endif #if ARCH_PORTDUINO - float tcxoVoltage = 0; - if (settingsMap[dio3_tcxo_voltage]) - tcxoVoltage = 1.8; + float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; if (settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { digitalWrite(settingsMap[sx126x_ant_sw], HIGH); pinMode(settingsMap[sx126x_ant_sw], OUTPUT); diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 50b5c5b7b..fa0c8c502 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -365,7 +365,10 @@ bool loadConfig(const char *configPath) settingsMap[use_sx1268] = true; } settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as(false); - settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(false); + settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(0) * 1000; + if (settingsMap[dio3_tcxo_voltage] == 0 && yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(false)) { + settingsMap[dio3_tcxo_voltage] = 1800; // default millivolts for "true" + } settingsMap[cs] = yamlConfig["Lora"]["CS"].as(RADIOLIB_NC); settingsMap[irq] = yamlConfig["Lora"]["IRQ"].as(RADIOLIB_NC); settingsMap[busy] = yamlConfig["Lora"]["Busy"].as(RADIOLIB_NC); From 332dbaf57376cf93befaf8cf35e244e4ef9bf982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sat, 14 Dec 2024 10:59:15 +0100 Subject: [PATCH 02/43] Support TLORA_V3.0 (#5563) - Support TLORA_V3.0. Update of the legendary 2.1_1.6.1 with solar charger, TCXO and IPEX connector. - 'extra' some short-lived EOL intermediate boards in that range. If possible use T3S3 instead of all of these! - update trunk to latest version --- .trunk/trunk.yaml | 25 +++++++++++----------- platformio.ini | 1 + variants/tlora_v2_1_16_tcxo/platformio.ini | 1 + variants/tlora_v2_1_18/platformio.ini | 1 + variants/tlora_v3_3_0_tcxo/platformio.ini | 11 ++++++++++ 5 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 variants/tlora_v3_3_0_tcxo/platformio.ini diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 743f4214d..f2393592c 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -4,31 +4,32 @@ cli: plugins: sources: - id: trunk - ref: v1.6.4 + ref: v1.6.6 uri: https://github.com/trunk-io/plugins lint: enabled: - - trufflehog@3.83.6 + - prettier@3.4.2 + - trufflehog@3.86.1 - yamllint@1.35.1 - - bandit@1.7.10 - - checkov@3.2.287 + - bandit@1.8.0 + - checkov@3.2.334 - terrascan@1.19.9 - - trivy@0.56.2 + - trivy@0.58.0 #- trufflehog@3.63.2-rc0 - taplo@0.9.3 - - ruff@0.7.3 + - ruff@0.8.3 - isort@5.13.2 - - markdownlint@0.42.0 - - oxipng@9.1.2 + - markdownlint@0.43.0 + - oxipng@9.1.3 - svgo@3.3.2 - actionlint@1.7.4 - flake8@7.1.1 - - hadolint@2.12.0 + - hadolint@2.12.1-beta - shfmt@3.6.0 - shellcheck@0.10.0 - black@24.10.0 - git-diff-check - - gitleaks@8.21.1 + - gitleaks@8.21.2 - clang-format@16.0.3 #- prettier@3.3.3 ignore: @@ -39,11 +40,11 @@ runtimes: enabled: - python@3.10.8 - go@1.21.0 - - node@18.12.1 + - node@18.20.5 actions: disabled: - trunk-announce enabled: - trunk-fmt-pre-commit - trunk-check-pre-push - - trunk-upgrade-available \ No newline at end of file + - trunk-upgrade-available diff --git a/platformio.ini b/platformio.ini index cc08b33a0..08d21665f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,6 +16,7 @@ default_envs = tbeam ;default_envs = tlora-v2 ;default_envs = tlora-v2-1-1_6 ;default_envs = tlora-v2-1-1_6-tcxo +;default_envs = tlora-v3-3-0-tcxo ;default_envs = tlora-t3s3-v1 ;default_envs = t-echo ;default_envs = canaryone diff --git a/variants/tlora_v2_1_16_tcxo/platformio.ini b/variants/tlora_v2_1_16_tcxo/platformio.ini index e54c1a920..538fd81b0 100644 --- a/variants/tlora_v2_1_16_tcxo/platformio.ini +++ b/variants/tlora_v2_1_16_tcxo/platformio.ini @@ -1,5 +1,6 @@ [env:tlora-v2-1-1_6-tcxo] extends = esp32_base +board_level = extra board = ttgo-lora32-v21 build_flags = ${esp32_base.build_flags} diff --git a/variants/tlora_v2_1_18/platformio.ini b/variants/tlora_v2_1_18/platformio.ini index 36d6a3157..48a001ced 100644 --- a/variants/tlora_v2_1_18/platformio.ini +++ b/variants/tlora_v2_1_18/platformio.ini @@ -1,5 +1,6 @@ [env:tlora-v2-1-1_8] extends = esp32_base +board_level = extra board = ttgo-lora32-v21 build_flags = diff --git a/variants/tlora_v3_3_0_tcxo/platformio.ini b/variants/tlora_v3_3_0_tcxo/platformio.ini new file mode 100644 index 000000000..4066d64b0 --- /dev/null +++ b/variants/tlora_v3_3_0_tcxo/platformio.ini @@ -0,0 +1,11 @@ +[env:tlora-v3-3-0-tcxo] +extends = esp32_base +board = ttgo-lora32-v21 +board_level = extra +build_flags = + ${esp32_base.build_flags} + -D TLORA_V2_1_16 + -I variants/tlora_v2_1_16 + -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -D LORA_TCXO_GPIO=12 + -D BUTTON_PIN=0 \ No newline at end of file From c3f89a6db8feeb304b0969841db2c4ffba53a9c9 Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Sat, 14 Dec 2024 12:46:35 +0200 Subject: [PATCH 03/43] Create OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml (#5564) --- bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml diff --git a/bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml b/bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml new file mode 100644 index 000000000..ca5b27ebc --- /dev/null +++ b/bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml @@ -0,0 +1,9 @@ +## https://www.mikroe.com/lr-iot-click +Lora: + Module: lr1110 # OpenWRT ONE mikroBUS with LR-IOT-CLICK +# CS: 25 + IRQ: 10 + Busy: 12 +# Reset: 2 + spidev: spidev2.0 + DIO3_TCXO_VOLTAGE: 1.6 From 44cf6d388ebd9d284e490c150cc568f3199fba0f Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sat, 14 Dec 2024 11:55:32 +0100 Subject: [PATCH 04/43] Portduino: fix setting hwId via argument (#5565) --- src/platform/portduino/PortduinoGlue.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index fa0c8c502..750cc1630 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -87,7 +87,8 @@ void getMacAddr(uint8_t *dmac) if (strlen(optionMac) >= 12) { MAC_from_string(optionMac, dmac); } else { - uint32_t hwId = sscanf(optionMac, "%u", &hwId); + uint32_t hwId; + sscanf(optionMac, "%u", &hwId); dmac[0] = 0x80; dmac[1] = 0; dmac[2] = hwId >> 24; @@ -532,4 +533,4 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac) } else { return false; } -} +} \ No newline at end of file From 4a1239f811a803b2103cf99d27c9db7dd2f2efc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Perdig=C3=A3o=20Gon=C3=A7alves?= Date: Sun, 15 Dec 2024 00:43:41 +0000 Subject: [PATCH 05/43] Add new endpoint to retrieve node info (#5557) --- src/mesh/http/ContentHandler.cpp | 74 ++++++++++++++++++++++++++++++++ src/mesh/http/ContentHandler.h | 1 + 2 files changed, 75 insertions(+) diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 64f7164c9..2b88702ed 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -93,6 +93,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) ResourceNode *nodeJsonScanNetworks = new ResourceNode("/json/scanNetworks", "GET", &handleScanNetworks); ResourceNode *nodeJsonBlinkLED = new ResourceNode("/json/blink", "POST", &handleBlinkLED); ResourceNode *nodeJsonReport = new ResourceNode("/json/report", "GET", &handleReport); + ResourceNode *nodeJsonNodes = new ResourceNode("/json/nodes", "GET", &handleNodes); ResourceNode *nodeJsonFsBrowseStatic = new ResourceNode("/json/fs/browse/static", "GET", &handleFsBrowseStatic); ResourceNode *nodeJsonDelete = new ResourceNode("/json/fs/delete/static", "DELETE", &handleFsDeleteStatic); @@ -112,6 +113,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) secureServer->registerNode(nodeJsonFsBrowseStatic); secureServer->registerNode(nodeJsonDelete); secureServer->registerNode(nodeJsonReport); + secureServer->registerNode(nodeJsonNodes); // secureServer->registerNode(nodeUpdateFs); // secureServer->registerNode(nodeDeleteFs); secureServer->registerNode(nodeAdmin); @@ -680,6 +682,78 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) delete value; } +void handleNodes(HTTPRequest *req, HTTPResponse *res) +{ + ResourceParameters *params = req->getParams(); + std::string content; + + if (!params->getQueryParameter("content", content)) { + content = "json"; + } + + if (content == "json") { + res->setHeader("Content-Type", "application/json"); + res->setHeader("Access-Control-Allow-Origin", "*"); + res->setHeader("Access-Control-Allow-Methods", "GET"); + } else { + res->setHeader("Content-Type", "text/html"); + res->println("
");
+    }
+
+    JSONArray nodesArray;
+
+    uint32_t readIndex = 0;
+    const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
+    while (tempNodeInfo != NULL) {
+        if (tempNodeInfo->has_user) {
+            JSONObject node;
+
+            char id[16];
+            snprintf(id, sizeof(id), "!%08x", tempNodeInfo->num);
+
+            node["id"] = new JSONValue(id);
+            node["snr"] = new JSONValue(tempNodeInfo->snr);
+            node["via_mqtt"] = new JSONValue(BoolToString(tempNodeInfo->via_mqtt));
+            node["last_heard"] = new JSONValue((int)tempNodeInfo->last_heard);
+            node["position"] = new JSONValue();
+
+            if (nodeDB->hasValidPosition(tempNodeInfo)) {
+                JSONObject position;
+                position["latitude"] = new JSONValue((float)tempNodeInfo->position.latitude_i * 1e-7);
+                position["longitude"] = new JSONValue((float)tempNodeInfo->position.longitude_i * 1e-7);
+                position["altitude"] = new JSONValue((int)tempNodeInfo->position.altitude);
+                node["position"] = new JSONValue(position);
+            }
+
+            JSONObject user;
+            node["long_name"] = new JSONValue(tempNodeInfo->user.long_name);
+            node["short_name"] = new JSONValue(tempNodeInfo->user.short_name);
+            char macStr[18];
+            snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X", tempNodeInfo->user.macaddr[0],
+                     tempNodeInfo->user.macaddr[1], tempNodeInfo->user.macaddr[2], tempNodeInfo->user.macaddr[3],
+                     tempNodeInfo->user.macaddr[4], tempNodeInfo->user.macaddr[5]);
+            node["mac_address"] = new JSONValue(macStr);
+            node["hw_model"] = new JSONValue(tempNodeInfo->user.hw_model);
+
+            nodesArray.push_back(new JSONValue(node));
+        }
+        tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
+    }
+
+    // collect data to inner data object
+    JSONObject jsonObjInner;
+    jsonObjInner["nodes"] = new JSONValue(nodesArray);
+
+    // create json output structure
+    JSONObject jsonObjOuter;
+    jsonObjOuter["data"] = new JSONValue(jsonObjInner);
+    jsonObjOuter["status"] = new JSONValue("ok");
+    // serialize and write it to the stream
+    JSONValue *value = new JSONValue(jsonObjOuter);
+    res->print(value->Stringify().c_str());
+    delete value;
+}
+
 /*
     This supports the Apple Captive Network Assistant (CNA) Portal
 */
diff --git a/src/mesh/http/ContentHandler.h b/src/mesh/http/ContentHandler.h
index 987e3ffef..2066a6d57 100644
--- a/src/mesh/http/ContentHandler.h
+++ b/src/mesh/http/ContentHandler.h
@@ -13,6 +13,7 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
 void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
 void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
 void handleReport(HTTPRequest *req, HTTPResponse *res);
+void handleNodes(HTTPRequest *req, HTTPResponse *res);
 void handleUpdateFs(HTTPRequest *req, HTTPResponse *res);
 void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res);
 void handleFs(HTTPRequest *req, HTTPResponse *res);

From 6d8be13266c9e07caa42dc7a3420a9424c1aca89 Mon Sep 17 00:00:00 2001
From: Austin 
Date: Sat, 14 Dec 2024 20:19:19 -0500
Subject: [PATCH 06/43] Portduino-buildroot: Remove pkg-config optional libs
 (#5573)

---
 variants/portduino-buildroot/platformio.ini | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/variants/portduino-buildroot/platformio.ini b/variants/portduino-buildroot/platformio.ini
index 3c8f21537..683a3cecc 100644
--- a/variants/portduino-buildroot/platformio.ini
+++ b/variants/portduino-buildroot/platformio.ini
@@ -1,11 +1,9 @@
 [env:buildroot]
 extends = portduino_base
-; The pkg-config commands below optionally add link flags.
-; the || : is just a "or run the null command" to avoid returning an error code
+; Optional libraries should be appended to `PLATFORMIO_BUILD_FLAGS`
+; environment variable in the buildroot environment.
 build_flags = ${portduino_base.build_flags} -O0 -I variants/portduino-buildroot
   -std=c++17
-  !pkg-config --libs libulfius --silence-errors || :
-  !pkg-config --libs openssl --silence-errors || :
 board = buildroot
 lib_deps = ${portduino_base.lib_deps}
 build_src_filter = ${portduino_base.build_src_filter}

From 4024bfdeeb57f8c213577d576a08ff63bd835e83 Mon Sep 17 00:00:00 2001
From: "Aaron.Lee" <32860565+Heltec-Aaron-Lee@users.noreply.github.com>
Date: Sun, 15 Dec 2024 10:20:29 +0800
Subject: [PATCH 07/43] Add screen detection function (#5533)

---
 src/mesh/NodeDB.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 77 insertions(+)

diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 6ad1a953d..201304395 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -70,6 +70,76 @@ static unsigned char userprefs_admin_key_1[] = USERPREFS_USE_ADMIN_KEY_1;
 static unsigned char userprefs_admin_key_2[] = USERPREFS_USE_ADMIN_KEY_2;
 #endif
 
+#ifdef HELTEC_MESH_NODE_T114
+
+uint32_t read8(uint8_t bits, uint8_t dummy,uint8_t cs,uint8_t sck,uint8_t mosi,uint8_t dc,uint8_t rst)
+{
+    uint32_t ret = 0;
+    uint8_t SDAPIN = mosi;
+    pinMode(SDAPIN, INPUT_PULLUP);
+    digitalWrite(dc, HIGH);
+    for (int i = 0; i < dummy; i++) {  //any dummy clocks
+        digitalWrite(sck, HIGH);
+        delay(1);
+        digitalWrite(sck, LOW);
+        delay(1);
+    }
+    for (int i = 0; i < bits; i++) {  // read results
+        ret <<= 1;
+        delay(1);
+        if (digitalRead(SDAPIN)) ret |= 1;;
+        digitalWrite(sck, HIGH);
+        delay(1);
+        digitalWrite(sck, LOW);
+    }
+    return ret;
+}
+
+void write9(uint8_t val, uint8_t dc_val,uint8_t cs,uint8_t sck,uint8_t mosi,uint8_t dc,uint8_t rst)
+{
+    pinMode(mosi, OUTPUT);
+    digitalWrite(dc, dc_val);
+    for (int i = 0; i < 8; i++) {   //send command
+        digitalWrite(mosi, (val & 0x80) != 0);
+        delay(1);
+        digitalWrite(sck, HIGH);
+        delay(1);
+        digitalWrite(sck, LOW);
+        val <<= 1;
+    }
+}
+
+uint32_t readwrite8(uint8_t cmd, uint8_t bits, uint8_t dummy,uint8_t cs,uint8_t sck,uint8_t mosi,uint8_t dc,uint8_t rst)
+{
+    digitalWrite(cs, LOW);
+    write9(cmd, 0,cs,sck,mosi,dc,rst);
+    uint32_t ret = read8(bits, dummy,cs,sck,mosi,dc,rst);
+    digitalWrite(cs, HIGH);
+    return ret;
+}
+
+uint32_t get_st7789_id(uint8_t cs,uint8_t sck,uint8_t mosi,uint8_t dc,uint8_t rst)
+{
+    pinMode(cs, OUTPUT);
+    digitalWrite(cs, HIGH);
+    pinMode(cs, OUTPUT);
+    pinMode(sck, OUTPUT);
+    pinMode(mosi, OUTPUT);
+    pinMode(dc, OUTPUT);
+    pinMode(rst, OUTPUT);
+    digitalWrite(rst, LOW);   //Hardware Reset
+    delay(10);
+    digitalWrite(rst, HIGH);
+    delay(10);
+
+    uint32_t ID = 0;
+    ID = readwrite8(0x04, 24, 1,cs,sck,mosi,dc,rst);
+    ID = readwrite8(0x04, 24, 1,cs,sck,mosi,dc,rst);  //ST7789 needs twice
+    return ID;
+}
+
+#endif
+
 bool meshtastic_DeviceState_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field)
 {
     if (ostream) {
@@ -489,6 +559,13 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
 #if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) ||       \
     defined(HX8357_CS) || defined(USE_ST7789)
     bool hasScreen = true;
+#ifdef HELTEC_MESH_NODE_T114
+    uint32_t st7789_id=get_st7789_id(ST7789_NSS,ST7789_SCK,ST7789_SDA,ST7789_RS,ST7789_RESET);
+    if(st7789_id==0xFFFFFF)
+    {
+        hasScreen = false;
+    }
+#endif
 #elif ARCH_PORTDUINO
     bool hasScreen = false;
     if (settingsMap[displayPanel])

From ea72abff22e416b4c813d7ba6810c38d0c2755a4 Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Sat, 14 Dec 2024 20:21:19 -0600
Subject: [PATCH 08/43] Posthumous tronk

---
 src/mesh/NodeDB.cpp | 33 +++++++++++++++++----------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 201304395..7fe5bd656 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -72,22 +72,24 @@ static unsigned char userprefs_admin_key_2[] = USERPREFS_USE_ADMIN_KEY_2;
 
 #ifdef HELTEC_MESH_NODE_T114
 
-uint32_t read8(uint8_t bits, uint8_t dummy,uint8_t cs,uint8_t sck,uint8_t mosi,uint8_t dc,uint8_t rst)
+uint32_t read8(uint8_t bits, uint8_t dummy, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
 {
     uint32_t ret = 0;
     uint8_t SDAPIN = mosi;
     pinMode(SDAPIN, INPUT_PULLUP);
     digitalWrite(dc, HIGH);
-    for (int i = 0; i < dummy; i++) {  //any dummy clocks
+    for (int i = 0; i < dummy; i++) { // any dummy clocks
         digitalWrite(sck, HIGH);
         delay(1);
         digitalWrite(sck, LOW);
         delay(1);
     }
-    for (int i = 0; i < bits; i++) {  // read results
+    for (int i = 0; i < bits; i++) { // read results
         ret <<= 1;
         delay(1);
-        if (digitalRead(SDAPIN)) ret |= 1;;
+        if (digitalRead(SDAPIN))
+            ret |= 1;
+        ;
         digitalWrite(sck, HIGH);
         delay(1);
         digitalWrite(sck, LOW);
@@ -95,11 +97,11 @@ uint32_t read8(uint8_t bits, uint8_t dummy,uint8_t cs,uint8_t sck,uint8_t mosi,u
     return ret;
 }
 
-void write9(uint8_t val, uint8_t dc_val,uint8_t cs,uint8_t sck,uint8_t mosi,uint8_t dc,uint8_t rst)
+void write9(uint8_t val, uint8_t dc_val, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
 {
     pinMode(mosi, OUTPUT);
     digitalWrite(dc, dc_val);
-    for (int i = 0; i < 8; i++) {   //send command
+    for (int i = 0; i < 8; i++) { // send command
         digitalWrite(mosi, (val & 0x80) != 0);
         delay(1);
         digitalWrite(sck, HIGH);
@@ -109,16 +111,16 @@ void write9(uint8_t val, uint8_t dc_val,uint8_t cs,uint8_t sck,uint8_t mosi,uint
     }
 }
 
-uint32_t readwrite8(uint8_t cmd, uint8_t bits, uint8_t dummy,uint8_t cs,uint8_t sck,uint8_t mosi,uint8_t dc,uint8_t rst)
+uint32_t readwrite8(uint8_t cmd, uint8_t bits, uint8_t dummy, uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
 {
     digitalWrite(cs, LOW);
-    write9(cmd, 0,cs,sck,mosi,dc,rst);
-    uint32_t ret = read8(bits, dummy,cs,sck,mosi,dc,rst);
+    write9(cmd, 0, cs, sck, mosi, dc, rst);
+    uint32_t ret = read8(bits, dummy, cs, sck, mosi, dc, rst);
     digitalWrite(cs, HIGH);
     return ret;
 }
 
-uint32_t get_st7789_id(uint8_t cs,uint8_t sck,uint8_t mosi,uint8_t dc,uint8_t rst)
+uint32_t get_st7789_id(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_t rst)
 {
     pinMode(cs, OUTPUT);
     digitalWrite(cs, HIGH);
@@ -127,14 +129,14 @@ uint32_t get_st7789_id(uint8_t cs,uint8_t sck,uint8_t mosi,uint8_t dc,uint8_t rs
     pinMode(mosi, OUTPUT);
     pinMode(dc, OUTPUT);
     pinMode(rst, OUTPUT);
-    digitalWrite(rst, LOW);   //Hardware Reset
+    digitalWrite(rst, LOW); // Hardware Reset
     delay(10);
     digitalWrite(rst, HIGH);
     delay(10);
 
     uint32_t ID = 0;
-    ID = readwrite8(0x04, 24, 1,cs,sck,mosi,dc,rst);
-    ID = readwrite8(0x04, 24, 1,cs,sck,mosi,dc,rst);  //ST7789 needs twice
+    ID = readwrite8(0x04, 24, 1, cs, sck, mosi, dc, rst);
+    ID = readwrite8(0x04, 24, 1, cs, sck, mosi, dc, rst); // ST7789 needs twice
     return ID;
 }
 
@@ -560,9 +562,8 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
     defined(HX8357_CS) || defined(USE_ST7789)
     bool hasScreen = true;
 #ifdef HELTEC_MESH_NODE_T114
-    uint32_t st7789_id=get_st7789_id(ST7789_NSS,ST7789_SCK,ST7789_SDA,ST7789_RS,ST7789_RESET);
-    if(st7789_id==0xFFFFFF)
-    {
+    uint32_t st7789_id = get_st7789_id(ST7789_NSS, ST7789_SCK, ST7789_SDA, ST7789_RS, ST7789_RESET);
+    if (st7789_id == 0xFFFFFF) {
         hasScreen = false;
     }
 #endif

From 547a57256d7be033a598c713eaf4b41615a67daa Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Sun, 15 Dec 2024 05:23:15 -0600
Subject: [PATCH 09/43] [create-pull-request] automated change (#5577)

Co-authored-by: fifieldt <1287116+fifieldt@users.noreply.github.com>
---
 protobufs                                    | 2 +-
 src/mesh/generated/meshtastic/mesh.pb.h      | 5 +++++
 src/mesh/generated/meshtastic/telemetry.pb.h | 8 +++++---
 3 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/protobufs b/protobufs
index 00c9c9932..4a4e81951 160000
--- a/protobufs
+++ b/protobufs
@@ -1 +1 @@
-Subproject commit 00c9c9932ea50c14cdc44d497d2672a0031641ce
+Subproject commit 4a4e81951d64821a96a5131e50d2b44e5356372e
diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h
index da439c375..2c5213cff 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.h
+++ b/src/mesh/generated/meshtastic/mesh.pb.h
@@ -220,6 +220,9 @@ typedef enum _meshtastic_HardwareModel {
  the same frame format.
  Runs on linux, see https://github.com/Jorropo/routastic */
     meshtastic_HardwareModel_ROUTASTIC = 85,
+    /* Mesh-Tab, esp32 based
+ https://github.com/valzzu/Mesh-Tab */
+    meshtastic_HardwareModel_MESH_TAB = 86,
     /* ------------------------------------------------------------------------------------------------------------------------------------------
  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.
  ------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -414,6 +417,8 @@ typedef enum _meshtastic_MeshPacket_Priority {
     meshtastic_MeshPacket_Priority_RESPONSE = 80,
     /* Higher priority for specific message types (portnums) to distinguish between other reliable packets. */
     meshtastic_MeshPacket_Priority_HIGH = 100,
+    /* Higher priority alert message used for critical alerts which take priority over other reliable packets. */
+    meshtastic_MeshPacket_Priority_ALERT = 110,
     /* Ack/naks are sent with very high priority to ensure that retransmission
  stops as soon as possible */
     meshtastic_MeshPacket_Priority_ACK = 120,
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h
index 874eef60f..a6102e07d 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.h
+++ b/src/mesh/generated/meshtastic/telemetry.pb.h
@@ -79,7 +79,9 @@ typedef enum _meshtastic_TelemetrySensorType {
     /* SCD40/SCD41 CO2, humidity, temperature sensor */
     meshtastic_TelemetrySensorType_SCD4X = 32,
     /* ClimateGuard RadSens, radiation, Geiger-Muller Tube */
-    meshtastic_TelemetrySensorType_RADSENS = 33
+    meshtastic_TelemetrySensorType_RADSENS = 33,
+    /* High accuracy current and voltage */
+    meshtastic_TelemetrySensorType_INA226 = 34
 } meshtastic_TelemetrySensorType;
 
 /* Struct definitions */
@@ -304,8 +306,8 @@ extern "C" {
 
 /* Helper constants for enums */
 #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET
-#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_RADSENS
-#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_RADSENS+1))
+#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_INA226
+#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_INA226+1))
 
 
 

From 56002155c68c90121692b6e327c44aeb96fd2904 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Sun, 15 Dec 2024 23:23:27 +1100
Subject: [PATCH 10/43] Based default Node Names on NodeNum, rather than MAC
 address (#5576)

Presently we base the default long name (Meshtastic XXXX) and short
names (XXXX) on a node's MAC address. This works fine, unless you
have a node with no bluetooth, like Portduino.

Our logic for node numbers is also based on MAC address. However,
it has the added feature that it will create a random node number
if the Mac address is no good. The name is always "Meshtastic 0001".

This change switches node names (long and short) to instead rely
on the node number for defaults. For nodes with mac addresses,
there should be no user-visible change. For nodes without, they'll
now have a name other than "Meshtastic 0001".

Fixes https://github.com/meshtastic/firmware/issues/5370

Co-authored-by: Ben Meadors 
---
 src/mesh/NodeDB.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 7fe5bd656..2af85e4f5 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -852,12 +852,12 @@ void NodeDB::installDefaultDeviceState()
 #ifdef USERPREFS_CONFIG_OWNER_LONG_NAME
     snprintf(owner.long_name, sizeof(owner.long_name), USERPREFS_CONFIG_OWNER_LONG_NAME);
 #else
-    snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %02x%02x", ourMacAddr[4], ourMacAddr[5]);
+    snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %04x", getNodeNum() & 0x0ffff);
 #endif
 #ifdef USERPREFS_CONFIG_OWNER_SHORT_NAME
     snprintf(owner.short_name, sizeof(owner.short_name), USERPREFS_CONFIG_OWNER_SHORT_NAME);
 #else
-    snprintf(owner.short_name, sizeof(owner.short_name), "%02x%02x", ourMacAddr[4], ourMacAddr[5]);
+    snprintf(owner.short_name, sizeof(owner.short_name), "%04x", getNodeNum() & 0x0ffff);
 #endif
     snprintf(owner.id, sizeof(owner.id), "!%08x", getNodeNum()); // Default node ID now based on nodenum
     memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));

From 2d45afafe56091b757b7e23839564770ae1dfc1a Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Sun, 15 Dec 2024 06:52:45 -0600
Subject: [PATCH 11/43] Try docker authentication with command-line instead

---
 .github/workflows/build_native.yml | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml
index d4b0c8d58..d9591e72c 100644
--- a/.github/workflows/build_native.yml
+++ b/.github/workflows/build_native.yml
@@ -53,20 +53,18 @@ jobs:
 
       - name: Docker login
         if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
-        uses: docker/login-action@v3
-        continue-on-error: true # FIXME: Failing docker login auth
-        with:
-          username: meshtastic
-          password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
+        run: |
+          echo ${{ secrets.DOCKER_FIRMWARE_TOKEN }} | docker login -u meshtastic --password-stdin
+        continue-on-error: true
 
       - name: Docker setup
         if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
-        continue-on-error: true # FIXME: Failing docker login auth
+        continue-on-error: true
         uses: docker/setup-buildx-action@v3
 
       - name: Docker build and push tagged versions
         if: ${{ github.event_name == 'workflow_dispatch' }}
-        continue-on-error: true # FIXME: Failing docker login auth
+        continue-on-error: true
         uses: docker/build-push-action@v6
         with:
           context: .
@@ -76,7 +74,7 @@ jobs:
 
       - name: Docker build and push
         if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
-        continue-on-error: true # FIXME: Failing docker login auth
+        continue-on-error: true
         uses: docker/build-push-action@v6
         with:
           context: .

From 020e9102a8277a40cbbbb260cbca9582be14fa87 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Mon, 16 Dec 2024 00:14:48 +1100
Subject: [PATCH 12/43] Define BUTTON_PIN as -1 for RP2040-lora (#5574)

The previous approach of undef'ing meant that it was impossible
for users to change the button pin in the apps.

Fixes https://github.com/meshtastic/firmware/issues/5566
---
 variants/rp2040-lora/variant.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/variants/rp2040-lora/variant.h b/variants/rp2040-lora/variant.h
index f1826605f..92b067457 100644
--- a/variants/rp2040-lora/variant.h
+++ b/variants/rp2040-lora/variant.h
@@ -15,7 +15,7 @@
 // rxd = 9
 
 #define EXT_NOTIFY_OUT 22
-#undef BUTTON_PIN // Pin 17 used for antenna switching via DIO4
+#define BUTTON_PIN -1 // Pin 17 used for antenna switching via DIO4
 
 #define LED_PIN PIN_LED
 
@@ -57,4 +57,4 @@
 #define SX126X_DIO2_AS_RF_SWITCH // Antenna switch CTRL
 #define SX126X_RXEN LORA_DIO4    // Antenna switch !CTRL via GPIO17
 // #define SX126X_DIO3_TCXO_VOLTAGE 1.8
-#endif
\ No newline at end of file
+#endif

From 09c082fd004423374f1decd880ee7a448d6e3999 Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Sun, 15 Dec 2024 09:59:14 -0600
Subject: [PATCH 13/43] Fix omission of AQ metrics (#5584)

---
 src/modules/Telemetry/AirQualityTelemetry.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp
index 362d60252..6a8077f03 100644
--- a/src/modules/Telemetry/AirQualityTelemetry.cpp
+++ b/src/modules/Telemetry/AirQualityTelemetry.cpp
@@ -113,12 +113,18 @@ bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
 
     m->time = getTime();
     m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
+    m->variant.air_quality_metrics.has_pm10_standard = true;
     m->variant.air_quality_metrics.pm10_standard = data.pm10_standard;
+    m->variant.air_quality_metrics.has_pm25_standard = true;
     m->variant.air_quality_metrics.pm25_standard = data.pm25_standard;
+    m->variant.air_quality_metrics.has_pm100_standard = true;
     m->variant.air_quality_metrics.pm100_standard = data.pm100_standard;
 
+    m->variant.air_quality_metrics.has_pm10_environmental = true;
     m->variant.air_quality_metrics.pm10_environmental = data.pm10_env;
+    m->variant.air_quality_metrics.has_pm25_environmental = true;
     m->variant.air_quality_metrics.pm25_environmental = data.pm25_env;
+    m->variant.air_quality_metrics.has_pm100_environmental = true;
     m->variant.air_quality_metrics.pm100_environmental = data.pm100_env;
 
     LOG_INFO("Send: PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i", m->variant.air_quality_metrics.pm10_standard,

From 69d01a8088d89753cea18502e44777764774d765 Mon Sep 17 00:00:00 2001
From: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
Date: Sun, 15 Dec 2024 20:11:13 +0100
Subject: [PATCH 14/43] StoreForward: (tapback) reply support (#5585)

---
 src/modules/StoreForwardModule.cpp | 12 +++++++++---
 src/modules/StoreForwardModule.h   |  3 +++
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/src/modules/StoreForwardModule.cpp b/src/modules/StoreForwardModule.cpp
index 4cf06f5d2..0a6e1b4c4 100644
--- a/src/modules/StoreForwardModule.cpp
+++ b/src/modules/StoreForwardModule.cpp
@@ -73,11 +73,11 @@ void StoreForwardModule::populatePSRAM()
     LOG_DEBUG("Before PSRAM init: heap %d/%d PSRAM %d/%d", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(),
               memGet.getPsramSize());
 
-    /* Use a maximum of 2/3 the available PSRAM unless otherwise specified.
+    /* Use a maximum of 3/4 the available PSRAM unless otherwise specified.
         Note: This needs to be done after every thing that would use PSRAM
     */
     uint32_t numberOfPackets =
-        (this->records ? this->records : (((memGet.getFreePsram() / 3) * 2) / sizeof(PacketHistoryStruct)));
+        (this->records ? this->records : (((memGet.getFreePsram() / 4) * 3) / sizeof(PacketHistoryStruct)));
     this->records = numberOfPackets;
 #if defined(ARCH_ESP32)
     this->packetHistory = static_cast(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
@@ -198,6 +198,9 @@ void StoreForwardModule::historyAdd(const meshtastic_MeshPacket &mp)
     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].id = mp.id;
+    this->packetHistory[this->packetHistoryTotalCount].reply_id = p.reply_id;
+    this->packetHistory[this->packetHistoryTotalCount].emoji = (bool)p.emoji;
     this->packetHistory[this->packetHistoryTotalCount].payload_size = p.payload.size;
     memcpy(this->packetHistory[this->packetHistoryTotalCount].payload, p.payload.bytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
 
@@ -244,8 +247,11 @@ meshtastic_MeshPacket *StoreForwardModule::preparePayload(NodeNum dest, uint32_t
 
                 p->to = local ? this->packetHistory[i].to : dest; // PhoneAPI can handle original `to`
                 p->from = this->packetHistory[i].from;
+                p->id = this->packetHistory[i].id;
                 p->channel = this->packetHistory[i].channel;
+                p->decoded.reply_id = this->packetHistory[i].reply_id;
                 p->rx_time = this->packetHistory[i].time;
+                p->decoded.emoji = (uint32_t)this->packetHistory[i].emoji;
 
                 // Let's assume that if the server received the S&F request that the client is in range.
                 //   TODO: Make this configurable.
@@ -617,4 +623,4 @@ StoreForwardModule::StoreForwardModule()
         disable();
     }
 #endif
-}
+}
\ No newline at end of file
diff --git a/src/modules/StoreForwardModule.h b/src/modules/StoreForwardModule.h
index e3273470b..30db1625c 100644
--- a/src/modules/StoreForwardModule.h
+++ b/src/modules/StoreForwardModule.h
@@ -13,7 +13,10 @@ struct PacketHistoryStruct {
     uint32_t time;
     uint32_t to;
     uint32_t from;
+    uint32_t id;
     uint8_t channel;
+    uint32_t reply_id;
+    bool emoji;
     uint8_t payload[meshtastic_Constants_DATA_PAYLOAD_LEN];
     pb_size_t payload_size;
 };

From 1b2fc00b99f18c55f3643d6853505f35d0e9aec6 Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Tue, 17 Dec 2024 05:45:31 -0600
Subject: [PATCH 15/43] Update main_matrix.yml

---
 .github/workflows/main_matrix.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml
index 37164b758..86fb6e699 100644
--- a/.github/workflows/main_matrix.yml
+++ b/.github/workflows/main_matrix.yml
@@ -37,7 +37,7 @@ jobs:
           else  
             TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} quick)
           fi
-          echo "Name: ${{ github.ref_name }} Base: ${{ github.base_ref }} Head: ${{ github.head_ref }} Ref: ${{ github.ref }} Targets: $TARGETS"
+          echo "Name: ${{ github.ref_name }} Base: ${{ github.base_ref }} } Ref: ${{ github.ref }} Targets: $TARGETS"
           echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
     outputs:
       esp32: ${{ steps.jsonStep.outputs.esp32 }}

From b0a4087a0c4fc1b6aedbe2ae41291378291437b2 Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Tue, 17 Dec 2024 06:12:23 -0600
Subject: [PATCH 16/43] Bump nano-pb

---
 .github/workflows/update_protobufs.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/update_protobufs.yml b/.github/workflows/update_protobufs.yml
index f1c92b860..2732ab760 100644
--- a/.github/workflows/update_protobufs.yml
+++ b/.github/workflows/update_protobufs.yml
@@ -17,9 +17,9 @@ jobs:
 
       - name: Download nanopb
         run: |
-          wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.9-linux-x86.tar.gz
-          tar xvzf nanopb-0.4.9-linux-x86.tar.gz
-          mv nanopb-0.4.9-linux-x86 nanopb-0.4.9
+          wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.9.1-linux-x86.tar.gz
+          tar xvzf nanopb-0.4.9.1-linux-x86.tar.gz
+          mv nanopb-0.4.9.1-linux-x86 nanopb-0.4.9
 
       - name: Re-generate protocol buffers
         run: |

From 92511ab10b0d97735fb7dd1bd6234808cfc3ab95 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 17 Dec 2024 06:33:17 -0600
Subject: [PATCH 17/43] [create-pull-request] automated change (#5597)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
---
 protobufs                                              | 2 +-
 src/mesh/generated/meshtastic/admin.pb.cpp             | 2 +-
 src/mesh/generated/meshtastic/admin.pb.h               | 2 +-
 src/mesh/generated/meshtastic/apponly.pb.cpp           | 2 +-
 src/mesh/generated/meshtastic/apponly.pb.h             | 2 +-
 src/mesh/generated/meshtastic/atak.pb.cpp              | 2 +-
 src/mesh/generated/meshtastic/atak.pb.h                | 2 +-
 src/mesh/generated/meshtastic/cannedmessages.pb.cpp    | 2 +-
 src/mesh/generated/meshtastic/cannedmessages.pb.h      | 2 +-
 src/mesh/generated/meshtastic/channel.pb.cpp           | 2 +-
 src/mesh/generated/meshtastic/channel.pb.h             | 2 +-
 src/mesh/generated/meshtastic/clientonly.pb.cpp        | 2 +-
 src/mesh/generated/meshtastic/clientonly.pb.h          | 2 +-
 src/mesh/generated/meshtastic/config.pb.cpp            | 2 +-
 src/mesh/generated/meshtastic/config.pb.h              | 2 +-
 src/mesh/generated/meshtastic/connection_status.pb.cpp | 2 +-
 src/mesh/generated/meshtastic/connection_status.pb.h   | 2 +-
 src/mesh/generated/meshtastic/device_ui.pb.cpp         | 2 +-
 src/mesh/generated/meshtastic/device_ui.pb.h           | 2 +-
 src/mesh/generated/meshtastic/deviceonly.pb.cpp        | 2 +-
 src/mesh/generated/meshtastic/deviceonly.pb.h          | 2 +-
 src/mesh/generated/meshtastic/localonly.pb.cpp         | 2 +-
 src/mesh/generated/meshtastic/localonly.pb.h           | 2 +-
 src/mesh/generated/meshtastic/mesh.pb.cpp              | 2 +-
 src/mesh/generated/meshtastic/mesh.pb.h                | 2 +-
 src/mesh/generated/meshtastic/module_config.pb.cpp     | 2 +-
 src/mesh/generated/meshtastic/module_config.pb.h       | 2 +-
 src/mesh/generated/meshtastic/mqtt.pb.cpp              | 2 +-
 src/mesh/generated/meshtastic/mqtt.pb.h                | 2 +-
 src/mesh/generated/meshtastic/paxcount.pb.cpp          | 2 +-
 src/mesh/generated/meshtastic/paxcount.pb.h            | 2 +-
 src/mesh/generated/meshtastic/portnums.pb.cpp          | 2 +-
 src/mesh/generated/meshtastic/portnums.pb.h            | 4 +++-
 src/mesh/generated/meshtastic/powermon.pb.cpp          | 2 +-
 src/mesh/generated/meshtastic/powermon.pb.h            | 2 +-
 src/mesh/generated/meshtastic/remote_hardware.pb.cpp   | 2 +-
 src/mesh/generated/meshtastic/remote_hardware.pb.h     | 2 +-
 src/mesh/generated/meshtastic/rtttl.pb.cpp             | 2 +-
 src/mesh/generated/meshtastic/rtttl.pb.h               | 2 +-
 src/mesh/generated/meshtastic/storeforward.pb.cpp      | 2 +-
 src/mesh/generated/meshtastic/storeforward.pb.h        | 2 +-
 src/mesh/generated/meshtastic/telemetry.pb.cpp         | 2 +-
 src/mesh/generated/meshtastic/telemetry.pb.h           | 2 +-
 src/mesh/generated/meshtastic/xmodem.pb.cpp            | 2 +-
 src/mesh/generated/meshtastic/xmodem.pb.h              | 2 +-
 45 files changed, 47 insertions(+), 45 deletions(-)

diff --git a/protobufs b/protobufs
index 4a4e81951..2cffaf53e 160000
--- a/protobufs
+++ b/protobufs
@@ -1 +1 @@
-Subproject commit 4a4e81951d64821a96a5131e50d2b44e5356372e
+Subproject commit 2cffaf53e3faf1b6e41a8b8f05312f2f893be413
diff --git a/src/mesh/generated/meshtastic/admin.pb.cpp b/src/mesh/generated/meshtastic/admin.pb.cpp
index 8b3fd3d1b..7ce3c74ce 100644
--- a/src/mesh/generated/meshtastic/admin.pb.cpp
+++ b/src/mesh/generated/meshtastic/admin.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/admin.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h
index bbf633ef5..d9b8de384 100644
--- a/src/mesh/generated/meshtastic/admin.pb.h
+++ b/src/mesh/generated/meshtastic/admin.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_ADMIN_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_ADMIN_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/apponly.pb.cpp b/src/mesh/generated/meshtastic/apponly.pb.cpp
index 64d43b7ee..8b1b3da19 100644
--- a/src/mesh/generated/meshtastic/apponly.pb.cpp
+++ b/src/mesh/generated/meshtastic/apponly.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/apponly.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/apponly.pb.h b/src/mesh/generated/meshtastic/apponly.pb.h
index dc08d9ff3..f4c33bd79 100644
--- a/src/mesh/generated/meshtastic/apponly.pb.h
+++ b/src/mesh/generated/meshtastic/apponly.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_APPONLY_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_APPONLY_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/atak.pb.cpp b/src/mesh/generated/meshtastic/atak.pb.cpp
index 6dbc69fb4..a0368cf6b 100644
--- a/src/mesh/generated/meshtastic/atak.pb.cpp
+++ b/src/mesh/generated/meshtastic/atak.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/atak.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/atak.pb.h b/src/mesh/generated/meshtastic/atak.pb.h
index 15a86788b..8533bcbf9 100644
--- a/src/mesh/generated/meshtastic/atak.pb.h
+++ b/src/mesh/generated/meshtastic/atak.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_ATAK_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_ATAK_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/cannedmessages.pb.cpp b/src/mesh/generated/meshtastic/cannedmessages.pb.cpp
index 9f51e9634..1f4ebc927 100644
--- a/src/mesh/generated/meshtastic/cannedmessages.pb.cpp
+++ b/src/mesh/generated/meshtastic/cannedmessages.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/cannedmessages.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/cannedmessages.pb.h b/src/mesh/generated/meshtastic/cannedmessages.pb.h
index 06d14b98f..8343c4d6e 100644
--- a/src/mesh/generated/meshtastic/cannedmessages.pb.h
+++ b/src/mesh/generated/meshtastic/cannedmessages.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_CANNEDMESSAGES_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_CANNEDMESSAGES_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/channel.pb.cpp b/src/mesh/generated/meshtastic/channel.pb.cpp
index 52f923b13..6670a40fc 100644
--- a/src/mesh/generated/meshtastic/channel.pb.cpp
+++ b/src/mesh/generated/meshtastic/channel.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/channel.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/channel.pb.h b/src/mesh/generated/meshtastic/channel.pb.h
index 3d617ae39..ca4310bf1 100644
--- a/src/mesh/generated/meshtastic/channel.pb.h
+++ b/src/mesh/generated/meshtastic/channel.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/clientonly.pb.cpp b/src/mesh/generated/meshtastic/clientonly.pb.cpp
index d99af8cf5..8f380a972 100644
--- a/src/mesh/generated/meshtastic/clientonly.pb.cpp
+++ b/src/mesh/generated/meshtastic/clientonly.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/clientonly.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/clientonly.pb.h b/src/mesh/generated/meshtastic/clientonly.pb.h
index bf32d7875..5109e20b2 100644
--- a/src/mesh/generated/meshtastic/clientonly.pb.h
+++ b/src/mesh/generated/meshtastic/clientonly.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_CLIENTONLY_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_CLIENTONLY_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/config.pb.cpp b/src/mesh/generated/meshtastic/config.pb.cpp
index 23f4d542b..6fd2161ae 100644
--- a/src/mesh/generated/meshtastic/config.pb.cpp
+++ b/src/mesh/generated/meshtastic/config.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/config.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h
index fab23ae34..8e2264e93 100644
--- a/src/mesh/generated/meshtastic/config.pb.h
+++ b/src/mesh/generated/meshtastic/config.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/connection_status.pb.cpp b/src/mesh/generated/meshtastic/connection_status.pb.cpp
index d1495bb83..b0df459ad 100644
--- a/src/mesh/generated/meshtastic/connection_status.pb.cpp
+++ b/src/mesh/generated/meshtastic/connection_status.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/connection_status.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/connection_status.pb.h b/src/mesh/generated/meshtastic/connection_status.pb.h
index c433e370b..55559dcef 100644
--- a/src/mesh/generated/meshtastic/connection_status.pb.h
+++ b/src/mesh/generated/meshtastic/connection_status.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_CONNECTION_STATUS_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_CONNECTION_STATUS_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/device_ui.pb.cpp b/src/mesh/generated/meshtastic/device_ui.pb.cpp
index 6e0cf0cc8..3a9e28725 100644
--- a/src/mesh/generated/meshtastic/device_ui.pb.cpp
+++ b/src/mesh/generated/meshtastic/device_ui.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/device_ui.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/device_ui.pb.h b/src/mesh/generated/meshtastic/device_ui.pb.h
index 107aa8846..0c4f5384e 100644
--- a/src/mesh/generated/meshtastic/device_ui.pb.h
+++ b/src/mesh/generated/meshtastic/device_ui.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.cpp b/src/mesh/generated/meshtastic/deviceonly.pb.cpp
index 92853f00d..aa020467a 100644
--- a/src/mesh/generated/meshtastic/deviceonly.pb.cpp
+++ b/src/mesh/generated/meshtastic/deviceonly.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/deviceonly.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h
index e52a914e0..c0a0fee91 100644
--- a/src/mesh/generated/meshtastic/deviceonly.pb.h
+++ b/src/mesh/generated/meshtastic/deviceonly.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/localonly.pb.cpp b/src/mesh/generated/meshtastic/localonly.pb.cpp
index 0a752a5a8..34391df73 100644
--- a/src/mesh/generated/meshtastic/localonly.pb.cpp
+++ b/src/mesh/generated/meshtastic/localonly.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/localonly.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h
index 8f92b2a77..30f70ed90 100644
--- a/src/mesh/generated/meshtastic/localonly.pb.h
+++ b/src/mesh/generated/meshtastic/localonly.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/mesh.pb.cpp b/src/mesh/generated/meshtastic/mesh.pb.cpp
index a9f42f979..6c5c7a4be 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.cpp
+++ b/src/mesh/generated/meshtastic/mesh.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/mesh.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h
index 2c5213cff..14ed76f70 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.h
+++ b/src/mesh/generated/meshtastic/mesh.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_MESH_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_MESH_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/module_config.pb.cpp b/src/mesh/generated/meshtastic/module_config.pb.cpp
index c40041eab..9843f0e91 100644
--- a/src/mesh/generated/meshtastic/module_config.pb.cpp
+++ b/src/mesh/generated/meshtastic/module_config.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/module_config.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h
index 8f7bb701d..697b965c5 100644
--- a/src/mesh/generated/meshtastic/module_config.pb.h
+++ b/src/mesh/generated/meshtastic/module_config.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_MODULE_CONFIG_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_MODULE_CONFIG_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/mqtt.pb.cpp b/src/mesh/generated/meshtastic/mqtt.pb.cpp
index 74536cb79..2c32ef2e4 100644
--- a/src/mesh/generated/meshtastic/mqtt.pb.cpp
+++ b/src/mesh/generated/meshtastic/mqtt.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/mqtt.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/mqtt.pb.h b/src/mesh/generated/meshtastic/mqtt.pb.h
index 4d1027374..1726bc470 100644
--- a/src/mesh/generated/meshtastic/mqtt.pb.h
+++ b/src/mesh/generated/meshtastic/mqtt.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_MQTT_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_MQTT_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/paxcount.pb.cpp b/src/mesh/generated/meshtastic/paxcount.pb.cpp
index 403288147..ff738bde9 100644
--- a/src/mesh/generated/meshtastic/paxcount.pb.cpp
+++ b/src/mesh/generated/meshtastic/paxcount.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/paxcount.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/paxcount.pb.h b/src/mesh/generated/meshtastic/paxcount.pb.h
index b6b51fdd5..06078aef7 100644
--- a/src/mesh/generated/meshtastic/paxcount.pb.h
+++ b/src/mesh/generated/meshtastic/paxcount.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_PAXCOUNT_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_PAXCOUNT_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/portnums.pb.cpp b/src/mesh/generated/meshtastic/portnums.pb.cpp
index 8fca9af79..15a6ba372 100644
--- a/src/mesh/generated/meshtastic/portnums.pb.cpp
+++ b/src/mesh/generated/meshtastic/portnums.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/portnums.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h
index df6cf32c2..d7dc47785 100644
--- a/src/mesh/generated/meshtastic/portnums.pb.h
+++ b/src/mesh/generated/meshtastic/portnums.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_PORTNUMS_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_PORTNUMS_PB_H_INCLUDED
@@ -72,6 +72,8 @@ typedef enum _meshtastic_PortNum {
     /* Same as Text Message but originating from Detection Sensor Module.
  NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 */
     meshtastic_PortNum_DETECTION_SENSOR_APP = 10,
+    /* Same as Text Message but used for critical alerts. */
+    meshtastic_PortNum_ALERT_APP = 11,
     /* Provides a 'ping' service that replies to any packet it receives.
  Also serves as a small example module.
  ENCODING: ASCII Plaintext */
diff --git a/src/mesh/generated/meshtastic/powermon.pb.cpp b/src/mesh/generated/meshtastic/powermon.pb.cpp
index 6a9b7551a..8838e165f 100644
--- a/src/mesh/generated/meshtastic/powermon.pb.cpp
+++ b/src/mesh/generated/meshtastic/powermon.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/powermon.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/powermon.pb.h b/src/mesh/generated/meshtastic/powermon.pb.h
index 5add85b85..9d4d94193 100644
--- a/src/mesh/generated/meshtastic/powermon.pb.h
+++ b/src/mesh/generated/meshtastic/powermon.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/remote_hardware.pb.cpp b/src/mesh/generated/meshtastic/remote_hardware.pb.cpp
index 239950e7e..8942104b5 100644
--- a/src/mesh/generated/meshtastic/remote_hardware.pb.cpp
+++ b/src/mesh/generated/meshtastic/remote_hardware.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/remote_hardware.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/remote_hardware.pb.h b/src/mesh/generated/meshtastic/remote_hardware.pb.h
index ade250e93..9ab3413c3 100644
--- a/src/mesh/generated/meshtastic/remote_hardware.pb.h
+++ b/src/mesh/generated/meshtastic/remote_hardware.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_REMOTE_HARDWARE_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_REMOTE_HARDWARE_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/rtttl.pb.cpp b/src/mesh/generated/meshtastic/rtttl.pb.cpp
index 61ad8b73f..c994741f3 100644
--- a/src/mesh/generated/meshtastic/rtttl.pb.cpp
+++ b/src/mesh/generated/meshtastic/rtttl.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/rtttl.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/rtttl.pb.h b/src/mesh/generated/meshtastic/rtttl.pb.h
index 0572265f7..b6e152dbf 100644
--- a/src/mesh/generated/meshtastic/rtttl.pb.h
+++ b/src/mesh/generated/meshtastic/rtttl.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_RTTTL_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_RTTTL_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/storeforward.pb.cpp b/src/mesh/generated/meshtastic/storeforward.pb.cpp
index 71a232bf6..82db566a1 100644
--- a/src/mesh/generated/meshtastic/storeforward.pb.cpp
+++ b/src/mesh/generated/meshtastic/storeforward.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/storeforward.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/storeforward.pb.h b/src/mesh/generated/meshtastic/storeforward.pb.h
index 44ffd098c..75cff5205 100644
--- a/src/mesh/generated/meshtastic/storeforward.pb.h
+++ b/src/mesh/generated/meshtastic/storeforward.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_STOREFORWARD_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_STOREFORWARD_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.cpp b/src/mesh/generated/meshtastic/telemetry.pb.cpp
index f6d39da6e..c79941fa5 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.cpp
+++ b/src/mesh/generated/meshtastic/telemetry.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/telemetry.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h
index a6102e07d..85fe4bdc1 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.h
+++ b/src/mesh/generated/meshtastic/telemetry.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/xmodem.pb.cpp b/src/mesh/generated/meshtastic/xmodem.pb.cpp
index 3960ccdaa..09ae41d35 100644
--- a/src/mesh/generated/meshtastic/xmodem.pb.cpp
+++ b/src/mesh/generated/meshtastic/xmodem.pb.cpp
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #include "meshtastic/xmodem.pb.h"
 #if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/xmodem.pb.h b/src/mesh/generated/meshtastic/xmodem.pb.h
index 76edc0df6..3410fda0f 100644
--- a/src/mesh/generated/meshtastic/xmodem.pb.h
+++ b/src/mesh/generated/meshtastic/xmodem.pb.h
@@ -1,5 +1,5 @@
 /* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.9 */
+/* Generated by nanopb-0.4.9.1 */
 
 #ifndef PB_MESHTASTIC_MESHTASTIC_XMODEM_PB_H_INCLUDED
 #define PB_MESHTASTIC_MESHTASTIC_XMODEM_PB_H_INCLUDED

From b0e3039732059aba7714ffb61039c75220bde7aa Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Tue, 17 Dec 2024 06:52:26 -0600
Subject: [PATCH 18/43] Bump platform

---
 arch/nrf52/nrf52.ini | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini
index 778be5523..57b276978 100644
--- a/arch/nrf52/nrf52.ini
+++ b/arch/nrf52/nrf52.ini
@@ -1,6 +1,6 @@
 [nrf52_base]
 ; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
-platform = platformio/nordicnrf52@^10.6.0
+platform = platformio/nordicnrf52@^10.7.0
 extends = arduino_base
 platform_packages =
   ; our custom Git version until they merge our PR
@@ -29,4 +29,4 @@ lib_deps=
 
 lib_ignore =
   BluetoothOTA
-  lvgl
+  lvgl
\ No newline at end of file

From 4edeca5f846024365bbd8855a454d42d2eae4d57 Mon Sep 17 00:00:00 2001
From: Tom <116762865+Nestpebble@users.noreply.github.com>
Date: Tue, 17 Dec 2024 16:25:37 +0000
Subject: [PATCH 19/43] Added support for the LR1121 radio to the NRF52
 Pro-Micro (#5515)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Added support for the LR1121 radio

Added support for the LR1121 radio, tested as functional with an E80-900M2213S from CDEbyte.

* Swap PNG for PDF

* remove PNG

* put TCXO voltage to 1.8, as in example file

It worked at 1.6v, but ¯\_(ツ)_/¯

* Hopefully this will appease Trunk

* Update rf switch pins and Schematic

---------

Co-authored-by: Ben Meadors 
---
 ...Schematic_Pro-Micro_Pinouts 2024-12-14.pdf | 9836 +++++++++++++++++
 .../diy/nrf52_promicro_diy_tcxo/rfswitch.h    |   17 +
 .../diy/nrf52_promicro_diy_tcxo/variant.h     |   21 +-
 3 files changed, 9871 insertions(+), 3 deletions(-)
 create mode 100644 variants/diy/nrf52_promicro_diy_tcxo/Schematic_Pro-Micro_Pinouts 2024-12-14.pdf
 create mode 100644 variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h

diff --git a/variants/diy/nrf52_promicro_diy_tcxo/Schematic_Pro-Micro_Pinouts 2024-12-14.pdf b/variants/diy/nrf52_promicro_diy_tcxo/Schematic_Pro-Micro_Pinouts 2024-12-14.pdf
new file mode 100644
index 000000000..de87af141
--- /dev/null
+++ b/variants/diy/nrf52_promicro_diy_tcxo/Schematic_Pro-Micro_Pinouts 2024-12-14.pdf	
@@ -0,0 +1,9836 @@
+%PDF-1.4
+%߬
+3 0 obj
+<>
+endobj
+4 0 obj
+<<
+/Length 102720
+>>
+stream
+0.20 w
+0 G
+2 J
+0 j
+100 M
+1.00 g
+[] 0 d
+0.00 826.80 1169.00 -826.80 re
+f
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+216.000 806.000 m 
+216.000 816.000 l
+216.000 20.000 m 
+216.000 10.000 l
+412.000 806.000 m 
+412.000 816.000 l
+412.000 20.000 m 
+412.000 10.000 l
+608.000 806.000 m 
+608.000 816.000 l
+608.000 20.000 m 
+608.000 10.000 l
+804.000 806.000 m 
+804.000 816.000 l
+804.000 20.000 m 
+804.000 10.000 l
+1000.000 806.000 m 
+1000.000 816.000 l
+1000.000 20.000 m 
+1000.000 10.000 l
+20.000 610.000 m 
+10.000 610.000 l
+1149.000 610.000 m 
+1159.000 610.000 l
+20.000 414.000 m 
+10.000 414.000 l
+1149.000 414.000 m 
+1159.000 414.000 l
+20.000 218.000 m 
+10.000 218.000 l
+1149.000 218.000 m 
+1159.000 218.000 l
+20.000 22.000 m 
+10.000 22.000 l
+1149.000 22.000 m 
+1159.000 22.000 l
+S
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+11.50 708.00 Td
+(A) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+1150.50 708.00 Td
+(A) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+11.50 512.00 Td
+(B) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+1150.50 512.00 Td
+(B) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+11.50 316.00 Td
+(C) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+1150.50 316.00 Td
+(C) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+11.50 120.00 Td
+(D) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+1150.50 120.00 Td
+(D) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+118.00 807.50 Td
+(1) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+118.00 11.50 Td
+(1) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+314.00 807.50 Td
+(2) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+314.00 11.50 Td
+(2) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+510.00 807.50 Td
+(3) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+510.00 11.50 Td
+(3) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+706.00 807.50 Td
+(4) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+706.00 11.50 Td
+(4) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+902.00 807.50 Td
+(5) Tj
+ET
+10.00 w
+BT
+/F1 9 Tf
+9.00 TL
+0.533 0.000 0.000 rg
+902.00 11.50 Td
+(5) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+20.00 806.00 1129.00 -786.00 re
+S
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+10.00 816.00 1149.00 -806.00 re
+S
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+705.00 100.00 444.00 -80.00 re
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+705.100 60.750 m 
+1148.630 60.750 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+809.630 40.750 m 
+1148.630 40.750 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+1069.610 99.930 m 
+1069.630 60.750 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+1069.630 60.750 m 
+1069.630 40.750 l
+S
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+710.00 87.00 Td
+(TITLE:) Tj
+ET
+10.00 w
+BT
+/F1 13 Tf
+13.00 TL
+0.000 0.000 1.000 rg
+767.62 74.41 Td
+(Pro-Micro Pinouts) Tj
+ET
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+1074.62 73.75 Td
+(REV:) Tj
+ET
+10.00 w
+BT
+/F1 12 Tf
+12.00 TL
+0.000 0.000 1.000 rg
+1112.62 73.75 Td
+(1.0) Tj
+ET
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+814.62 25.00 Td
+(Date:) Tj
+ET
+10.00 w
+BT
+/F1 12 Tf
+12.00 TL
+0.000 0.000 1.000 rg
+861.62 24.52 Td
+(2024-12-17) Tj
+ET
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+1073.62 45.00 Td
+(Sheet:) Tj
+ET
+10.00 w
+BT
+/F1 12 Tf
+12.00 TL
+0.000 0.000 1.000 rg
+1118.62 44.52 Td
+(1/1) Tj
+ET
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+953.62 24.75 Td
+(Drawn By:) Tj
+ET
+10.00 w
+BT
+/F1 11 Tf
+11.00 TL
+0.533 0.000 0.000 rg
+814.62 46.75 Td
+(Company:) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+809.630 60.750 m 
+809.630 20.750 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+541.000 684.000 m 
+549.000 676.000 l
+549.000 684.000 m 
+541.000 676.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+665.000 680.000 m 
+635.000 680.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+986.000 649.000 m 
+994.000 641.000 l
+994.000 649.000 m 
+986.000 641.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+541.000 614.000 m 
+549.000 606.000 l
+549.000 614.000 m 
+541.000 606.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+541.000 624.000 m 
+549.000 616.000 l
+549.000 624.000 m 
+541.000 616.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+541.000 644.000 m 
+549.000 636.000 l
+549.000 644.000 m 
+541.000 636.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+631.000 634.000 m 
+639.000 626.000 l
+639.000 634.000 m 
+631.000 626.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 804.82 611.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+830.000 615.000 m 
+840.000 615.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+830.000 610.000 m 
+830.000 620.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 797.50 621.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+830.000 625.000 m 
+840.000 625.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+830.000 634.000 m 
+830.000 616.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+828.000 631.000 m 
+828.000 619.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+826.000 628.000 m 
+826.000 622.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+824.000 626.000 m 
+824.000 624.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 1006.50 651.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1000.000 655.000 m 
+990.000 655.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1000.000 646.000 m 
+1000.000 664.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1002.000 649.000 m 
+1002.000 661.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1004.000 652.000 m 
+1004.000 658.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1006.000 654.000 m 
+1006.000 656.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 1011.94 671.15 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+990.000 675.000 m 
+995.000 680.000 l
+1010.000 680.000 l
+1010.000 670.000 l
+995.000 670.000 l
+990.000 675.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 797.25 651.75 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+840.000 655.000 m 
+835.000 650.000 l
+820.000 650.000 l
+820.000 660.000 l
+835.000 660.000 l
+840.000 655.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 804.69 631.45 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+840.000 635.000 m 
+835.000 630.000 l
+820.000 630.000 l
+820.000 640.000 l
+835.000 640.000 l
+840.000 635.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 791.48 661.75 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+840.000 665.000 m 
+835.000 660.000 l
+820.000 660.000 l
+820.000 670.000 l
+835.000 670.000 l
+840.000 665.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 791.41 671.75 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+840.000 675.000 m 
+835.000 670.000 l
+820.000 670.000 l
+820.000 680.000 l
+835.000 680.000 l
+840.000 675.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 790.06 641.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+840.000 645.000 m 
+835.000 640.000 l
+820.000 640.000 l
+820.000 650.000 l
+835.000 650.000 l
+840.000 645.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 1011.92 661.15 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+990.000 665.000 m 
+995.000 670.000 l
+1010.000 670.000 l
+1010.000 660.000 l
+995.000 660.000 l
+990.000 665.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+908.96 708.00 Td
+(Seeed-wio-SX1262) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+908.96 717.00 Td
+(SEEED_WIO-SX1262) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 682.00 Td
+(RF_SW) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 686.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 685.000 m 
+860.000 685.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 672.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 676.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 675.000 m 
+860.000 675.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 662.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 666.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 665.000 m 
+860.000 665.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 652.00 Td
+(CLK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 656.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 655.000 m 
+860.000 655.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 642.00 Td
+(RST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 646.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 645.000 m 
+860.000 645.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 632.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 636.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 635.000 m 
+860.000 635.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 622.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 626.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 625.000 m 
+860.000 625.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+862.00 612.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+849.28 616.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+840.000 615.000 m 
+860.000 615.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+949.58 642.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+975.00 646.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+990.000 645.000 m 
+970.000 645.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+947.36 652.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+975.00 656.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+990.000 655.000 m 
+970.000 655.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+943.57 662.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+975.00 666.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+990.000 665.000 m 
+970.000 665.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+944.49 672.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+975.00 676.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+990.000 675.000 m 
+970.000 675.000 l
+S
+2 J
+0 j
+100 M
+1.00 w
+0.00 G
+[] 0 d
+860.00 705.00 110.00 -110.00 re
+S
+1.00 w
+0.00 G
+[] 0 d
+965.00 615.00 m 965.00 623.28 958.28 630.00 950.00 630.00 c
+941.72 630.00 935.00 623.28 935.00 615.00 c
+935.00 606.72 941.72 600.00 950.00 600.00 c
+958.28 600.00 965.00 606.72 965.00 615.00 c
+S
+2 J
+0 j
+100 M
+1.00 w
+0.00 G
+[] 0 d
+930.00 635.00 40.00 -40.00 re
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+535.000 660.000 m 
+545.000 660.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 499.82 655.93 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+525.000 660.000 m 
+535.000 660.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+525.000 655.000 m 
+525.000 665.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+520.000 670.000 m 
+545.000 670.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 652.00 699.09 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+665.000 690.000 m 
+665.000 680.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+674.000 690.000 m 
+656.000 690.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+671.000 692.000 m 
+659.000 692.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+668.000 694.000 m 
+662.000 694.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+666.000 696.000 m 
+664.000 696.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 507.00 689.09 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+520.000 680.000 m 
+520.000 670.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+529.000 680.000 m 
+511.000 680.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+526.000 682.000 m 
+514.000 682.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+523.000 684.000 m 
+517.000 684.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+521.000 686.000 m 
+519.000 686.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 622.00 582.76 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+635.000 600.000 m 
+635.000 610.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+626.000 600.000 m 
+644.000 600.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+629.000 598.000 m 
+641.000 598.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+632.000 596.000 m 
+638.000 596.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+634.000 594.000 m 
+636.000 594.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 656.92 616.15 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+635.000 620.000 m 
+640.000 625.000 l
+655.000 625.000 l
+655.000 615.000 l
+640.000 615.000 l
+635.000 620.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 656.55 636.15 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+635.000 640.000 m 
+640.000 645.000 l
+655.000 645.000 l
+655.000 635.000 l
+640.000 635.000 l
+635.000 640.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 656.92 646.15 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+635.000 650.000 m 
+640.000 655.000 l
+655.000 655.000 l
+655.000 645.000 l
+640.000 645.000 l
+635.000 650.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 656.85 656.15 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+635.000 660.000 m 
+640.000 665.000 l
+655.000 665.000 l
+655.000 655.000 l
+640.000 655.000 l
+635.000 660.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 657.00 666.45 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+635.000 670.000 m 
+640.000 675.000 l
+655.000 675.000 l
+655.000 665.000 l
+640.000 665.000 l
+635.000 670.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 504.19 626.40 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+545.000 630.000 m 
+540.000 625.000 l
+525.000 625.000 l
+525.000 635.000 l
+540.000 635.000 l
+545.000 630.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 495.06 646.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+545.000 650.000 m 
+540.000 645.000 l
+525.000 645.000 l
+525.000 655.000 l
+540.000 655.000 l
+545.000 650.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+584.95 693.33 Td
+(RA-01SH) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+584.95 702.33 Td
+(HT-RA62) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+557.000 690.000 m 
+623.000 690.000 l
+624.105 690.000 625.000 689.105 625.000 688.000 c
+625.000 602.000 l
+625.000 600.895 623.895 600.000 623.000 600.000 c
+557.000 600.000 l
+555.895 600.000 555.000 601.105 555.000 602.000 c
+555.000 688.000 l
+555.000 689.105 556.105 690.000 557.000 690.000 c
+S
+1.00 w
+0.53 0.00 0.00 RG
+0.53 0.00 0.00 rg
+[] 0 d
+561.50 685.00 m 561.50 685.83 560.83 686.50 560.00 686.50 c
+559.17 686.50 558.50 685.83 558.50 685.00 c
+558.50 684.17 559.17 683.50 560.00 683.50 c
+560.83 683.50 561.50 684.17 561.50 685.00 c
+B
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 676.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 681.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 680.000 m 
+555.000 680.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+558.70 666.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+548.78 671.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+545.000 670.000 m 
+555.000 670.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 656.00 Td
+(3.3V) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 661.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 660.000 m 
+555.000 660.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 646.00 Td
+(RESET) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 651.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 650.000 m 
+555.000 650.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 636.00 Td
+(TXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 641.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 640.000 m 
+555.000 640.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 626.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 631.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 630.000 m 
+555.000 630.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 616.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 621.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 620.000 m 
+555.000 620.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+558.70 606.00 Td
+(DIO3) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+548.78 611.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 610.000 m 
+555.000 610.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+600.66 606.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+625.50 611.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+635.000 610.000 m 
+625.000 610.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+596.87 616.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 621.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 620.000 m 
+625.000 620.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+596.46 626.00 Td
+(RXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 631.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 630.000 m 
+625.000 630.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+602.64 636.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 641.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 640.000 m 
+625.000 640.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+596.71 646.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 651.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 650.000 m 
+625.000 650.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+596.71 656.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 661.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 660.000 m 
+625.000 660.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+602.27 666.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+625.50 671.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+635.000 670.000 m 
+625.000 670.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+600.66 676.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+625.50 681.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+635.000 680.000 m 
+625.000 680.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+153.95 479.05 Td
+(AMC-U_FL) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+153.95 488.16 Td
+(U6) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+165.00 465.00 20.00 -20.00 re
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+0.00 1.00 -1.00 0.00 174.00 434.29 Tm
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+175.000 425.000 m 
+175.000 445.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+159.28 456.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+155.000 455.000 m 
+165.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+185.00 456.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+195.000 455.000 m 
+185.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+0.00 1.00 -1.00 0.00 174.00 465.00 Tm
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+175.000 475.000 m 
+175.000 465.000 l
+S
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+177.00 455.00 m 177.00 456.10 176.10 457.00 175.00 457.00 c
+173.90 457.00 173.00 456.10 173.00 455.00 c
+173.00 453.90 173.90 453.00 175.00 453.00 c
+176.10 453.00 177.00 453.90 177.00 455.00 c
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+175.000 453.000 m 
+175.000 445.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+506.000 394.000 m 
+514.000 386.000 l
+514.000 394.000 m 
+506.000 386.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+646.000 394.000 m 
+654.000 386.000 l
+654.000 394.000 m 
+646.000 386.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 666.50 415.93 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+660.000 420.000 m 
+650.000 420.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+660.000 411.000 m 
+660.000 429.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+662.000 414.000 m 
+662.000 426.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+664.000 417.000 m 
+664.000 423.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+666.000 419.000 m 
+666.000 421.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+650.000 430.000 m 
+650.000 400.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+796.000 389.000 m 
+804.000 381.000 l
+804.000 389.000 m 
+796.000 381.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+573.96 503.33 Td
+(E22-900M22S) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+573.96 512.33 Td
+(E22-900M22S) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 377.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 381.00 Td
+(22) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 380.000 m 
+530.000 380.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 387.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 391.00 Td
+(21) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 390.000 m 
+530.000 390.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 397.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 401.00 Td
+(20) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 400.000 m 
+530.000 400.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 417.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 421.00 Td
+(19) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 420.000 m 
+530.000 420.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 427.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 431.00 Td
+(18) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 430.000 m 
+530.000 430.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 437.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 441.00 Td
+(17) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 440.000 m 
+530.000 440.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 447.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 451.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 450.000 m 
+530.000 450.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 457.00 Td
+(NRST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 461.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 460.000 m 
+530.000 460.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 467.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 471.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 470.000 m 
+530.000 470.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 477.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 481.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 480.000 m 
+530.000 480.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+532.00 487.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+513.57 491.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+510.000 490.000 m 
+530.000 490.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 487.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 491.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 490.000 m 
+630.000 490.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 477.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 481.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 480.000 m 
+630.000 480.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+609.29 467.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 471.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 470.000 m 
+630.000 470.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+604.49 457.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 461.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 460.000 m 
+630.000 460.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+603.87 447.00 Td
+(TXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 451.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 450.000 m 
+630.000 450.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+603.16 437.00 Td
+(RXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 441.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 440.000 m 
+630.000 440.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 427.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 431.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 430.000 m 
+630.000 430.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 417.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 421.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 420.000 m 
+630.000 420.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 397.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 401.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 400.000 m 
+630.000 400.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 387.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 391.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 390.000 m 
+630.000 390.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+607.36 377.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+635.00 381.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+650.000 380.000 m 
+630.000 380.000 l
+S
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+532.000 500.000 m 
+628.000 500.000 l
+629.105 500.000 630.000 499.105 630.000 498.000 c
+630.000 362.000 l
+630.000 360.895 628.895 360.000 628.000 360.000 c
+532.000 360.000 l
+530.895 360.000 530.000 361.105 530.000 362.000 c
+530.000 498.000 l
+530.000 499.105 531.105 500.000 532.000 500.000 c
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+863.96 498.33 Td
+(E22-900M30S) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+863.96 507.33 Td
+(E22-900M30S) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+822.000 495.000 m 
+918.000 495.000 l
+919.105 495.000 920.000 494.105 920.000 493.000 c
+920.000 357.000 l
+920.000 355.895 918.895 355.000 918.000 355.000 c
+822.000 355.000 l
+820.895 355.000 820.000 356.105 820.000 357.000 c
+820.000 493.000 l
+820.000 494.105 821.105 495.000 822.000 495.000 c
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 372.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 376.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 375.000 m 
+920.000 375.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 382.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 386.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 385.000 m 
+920.000 385.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 392.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 396.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 395.000 m 
+920.000 395.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 412.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 416.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 415.000 m 
+920.000 415.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 422.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 426.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 425.000 m 
+920.000 425.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+893.16 432.00 Td
+(RXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 436.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 435.000 m 
+920.000 435.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+893.87 442.00 Td
+(TXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 446.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 445.000 m 
+920.000 445.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+894.49 452.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 456.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 455.000 m 
+920.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+899.29 462.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 466.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 465.000 m 
+920.000 465.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+899.29 472.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 476.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 475.000 m 
+920.000 475.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+897.36 482.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+925.00 486.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+940.000 485.000 m 
+920.000 485.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 482.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 486.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 485.000 m 
+820.000 485.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 472.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 476.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 475.000 m 
+820.000 475.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 462.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 466.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 465.000 m 
+820.000 465.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 452.00 Td
+(NRST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 456.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 455.000 m 
+820.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 442.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 446.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 445.000 m 
+820.000 445.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 432.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 436.00 Td
+(17) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 435.000 m 
+820.000 435.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 422.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 426.00 Td
+(18) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 425.000 m 
+820.000 425.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 412.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 416.00 Td
+(19) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 415.000 m 
+820.000 415.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 392.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 396.00 Td
+(20) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 395.000 m 
+820.000 395.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 382.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 386.00 Td
+(21) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 385.000 m 
+820.000 385.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+822.00 372.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+803.57 376.00 Td
+(22) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+800.000 375.000 m 
+820.000 375.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+293.99 488.33 Td
+(E22-400MM22S) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+293.99 497.33 Td
+(E22-900MM22S) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+267.000 485.000 m 
+333.000 485.000 l
+334.105 485.000 335.000 484.105 335.000 483.000 c
+335.000 377.000 l
+335.000 375.895 333.895 375.000 333.000 375.000 c
+267.000 375.000 l
+265.895 375.000 265.000 376.105 265.000 377.000 c
+265.000 483.000 l
+265.000 484.105 266.105 485.000 267.000 485.000 c
+S
+1.00 w
+0.53 0.00 0.00 RG
+0.53 0.00 0.00 rg
+[] 0 d
+271.50 480.00 m 271.50 480.83 270.83 481.50 270.00 481.50 c
+269.17 481.50 268.50 480.83 268.50 480.00 c
+268.50 479.17 269.17 478.50 270.00 478.50 c
+270.83 478.50 271.50 479.17 271.50 480.00 c
+B
+BT
+/F1 9 Tf
+9.00 TL
+1.000 0.000 0.000 rg
+268.70 471.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+1.000 0.000 0.000 rg
+258.79 476.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+1.00 0.00 0.00 RG
+[] 0 d
+255.000 475.000 m 
+265.000 475.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+268.70 461.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+258.79 466.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+255.000 465.000 m 
+265.000 465.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 451.00 Td
+(NRST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 456.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 455.000 m 
+265.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 441.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 446.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 445.000 m 
+265.000 445.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 431.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 436.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 435.000 m 
+265.000 435.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 421.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 426.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 425.000 m 
+265.000 425.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+268.70 411.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+258.79 416.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+255.000 415.000 m 
+265.000 415.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 401.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 406.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 405.000 m 
+265.000 405.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 391.00 Td
+(TXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+258.79 396.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 395.000 m 
+265.000 395.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+268.70 381.00 Td
+(RXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+253.07 386.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+255.000 385.000 m 
+265.000 385.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+306.87 381.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 386.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 385.000 m 
+335.000 385.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+306.71 391.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 396.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 395.000 m 
+335.000 395.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+306.71 401.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 406.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 405.000 m 
+335.000 405.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+312.27 411.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 416.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 415.000 m 
+335.000 415.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+312.64 421.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 426.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 425.000 m 
+335.000 425.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+310.66 431.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+335.50 436.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+345.000 435.000 m 
+335.000 435.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+318.29 441.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 446.00 Td
+(17) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 445.000 m 
+335.000 445.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+307.79 451.00 Td
+(DIO3) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 456.00 Td
+(18) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 455.000 m 
+335.000 455.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+307.79 461.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 466.00 Td
+(19) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 465.000 m 
+335.000 465.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+307.79 471.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+335.50 476.00 Td
+(20) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+345.000 475.000 m 
+335.000 475.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 366.70 381.60 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 385.000 m 
+350.000 390.000 l
+365.000 390.000 l
+365.000 380.000 l
+350.000 380.000 l
+345.000 385.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 203.19 381.40 Tm
+(RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+255.000 385.000 m 
+250.000 380.000 l
+235.000 380.000 l
+235.000 390.000 l
+250.000 390.000 l
+255.000 385.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 366.71 421.60 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 425.000 m 
+350.000 430.000 l
+365.000 430.000 l
+365.000 420.000 l
+350.000 420.000 l
+345.000 425.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.00 391.60 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 395.000 m 
+350.000 400.000 l
+365.000 400.000 l
+365.000 390.000 l
+350.000 390.000 l
+345.000 395.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.00 401.60 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 405.000 m 
+350.000 410.000 l
+365.000 410.000 l
+365.000 400.000 l
+350.000 400.000 l
+345.000 405.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.00 411.60 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 415.000 m 
+350.000 420.000 l
+365.000 420.000 l
+365.000 410.000 l
+350.000 410.000 l
+345.000 415.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.01 461.15 Tm
+(DIO2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 465.000 m 
+350.000 470.000 l
+365.000 470.000 l
+365.000 460.000 l
+350.000 460.000 l
+345.000 465.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.00 471.60 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 475.000 m 
+350.000 480.000 l
+365.000 480.000 l
+365.000 470.000 l
+350.000 470.000 l
+345.000 475.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 203.74 391.40 Tm
+(TXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+255.000 395.000 m 
+250.000 390.000 l
+235.000 390.000 l
+235.000 400.000 l
+250.000 400.000 l
+255.000 395.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 205.06 451.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+255.000 455.000 m 
+250.000 450.000 l
+235.000 450.000 l
+235.000 460.000 l
+250.000 460.000 l
+255.000 455.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 367.01 451.15 Tm
+(DIO3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+345.000 455.000 m 
+350.000 460.000 l
+365.000 460.000 l
+365.000 450.000 l
+350.000 450.000 l
+345.000 455.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 460.06 456.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 460.000 m 
+505.000 455.000 l
+490.000 455.000 l
+490.000 465.000 l
+505.000 465.000 l
+510.000 460.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 671.71 446.60 Tm
+(TXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+650.000 450.000 m 
+655.000 455.000 l
+670.000 455.000 l
+670.000 445.000 l
+655.000 445.000 l
+650.000 450.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 469.19 476.40 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 480.000 m 
+505.000 475.000 l
+490.000 475.000 l
+490.000 485.000 l
+505.000 485.000 l
+510.000 480.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 672.01 456.15 Tm
+(DIO2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+650.000 460.000 m 
+655.000 465.000 l
+670.000 465.000 l
+670.000 455.000 l
+655.000 455.000 l
+650.000 460.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 474.69 416.40 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 420.000 m 
+505.000 415.000 l
+490.000 415.000 l
+490.000 425.000 l
+505.000 425.000 l
+510.000 420.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 460.61 436.40 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 440.000 m 
+505.000 435.000 l
+490.000 435.000 l
+490.000 445.000 l
+505.000 445.000 l
+510.000 440.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 460.61 446.40 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 450.000 m 
+505.000 445.000 l
+490.000 445.000 l
+490.000 455.000 l
+505.000 455.000 l
+510.000 450.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 466.77 426.40 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 430.000 m 
+505.000 425.000 l
+490.000 425.000 l
+490.000 435.000 l
+505.000 435.000 l
+510.000 430.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 671.71 436.60 Tm
+(RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+650.000 440.000 m 
+655.000 445.000 l
+670.000 445.000 l
+670.000 435.000 l
+655.000 435.000 l
+650.000 440.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 458.85 466.40 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+510.000 470.000 m 
+505.000 465.000 l
+490.000 465.000 l
+490.000 475.000 l
+505.000 475.000 l
+510.000 470.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 467.50 395.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 400.000 m 
+510.000 400.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 409.000 m 
+500.000 391.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+498.000 406.000 m 
+498.000 394.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+496.000 403.000 m 
+496.000 397.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+494.000 401.000 m 
+494.000 399.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 467.50 375.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 380.000 m 
+510.000 380.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 389.000 m 
+500.000 371.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+498.000 386.000 m 
+498.000 374.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+496.000 383.000 m 
+496.000 377.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+494.000 381.000 m 
+494.000 379.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 637.00 352.76 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+650.000 370.000 m 
+650.000 380.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+641.000 370.000 m 
+659.000 370.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+644.000 368.000 m 
+656.000 368.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+647.000 366.000 m 
+653.000 366.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+649.000 364.000 m 
+651.000 364.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 637.00 509.13 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+650.000 500.000 m 
+650.000 490.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+659.000 500.000 m 
+641.000 500.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+656.000 502.000 m 
+644.000 502.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+653.000 504.000 m 
+647.000 504.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+651.000 506.000 m 
+649.000 506.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 467.50 485.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 490.000 m 
+510.000 490.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+500.000 499.000 m 
+500.000 481.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+498.000 496.000 m 
+498.000 484.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+496.000 493.000 m 
+496.000 487.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+494.000 491.000 m 
+494.000 489.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 361.50 430.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 435.000 m 
+345.000 435.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 426.000 m 
+355.000 444.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+357.000 429.000 m 
+357.000 441.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+359.000 432.000 m 
+359.000 438.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+361.000 434.000 m 
+361.000 436.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 212.50 410.93 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+245.000 415.000 m 
+255.000 415.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+245.000 424.000 m 
+245.000 406.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+243.000 421.000 m 
+243.000 409.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+241.000 418.000 m 
+241.000 412.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+239.000 416.000 m 
+239.000 414.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 212.50 460.93 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+245.000 465.000 m 
+255.000 465.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+245.000 474.000 m 
+245.000 456.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+243.000 471.000 m 
+243.000 459.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+241.000 468.000 m 
+241.000 462.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+239.000 466.000 m 
+239.000 464.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+940.000 370.000 m 
+940.000 425.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 757.50 480.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 485.000 m 
+800.000 485.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 494.000 m 
+790.000 476.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+788.000 491.000 m 
+788.000 479.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+786.000 488.000 m 
+786.000 482.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+784.000 486.000 m 
+784.000 484.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 927.00 504.09 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+940.000 495.000 m 
+940.000 485.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+949.000 495.000 m 
+931.000 495.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+946.000 497.000 m 
+934.000 497.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+943.000 499.000 m 
+937.000 499.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+941.000 501.000 m 
+939.000 501.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 757.50 370.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 375.000 m 
+800.000 375.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 384.000 m 
+790.000 366.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+788.000 381.000 m 
+788.000 369.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+786.000 378.000 m 
+786.000 372.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+784.000 376.000 m 
+784.000 374.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 757.50 390.92 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 395.000 m 
+800.000 395.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+790.000 404.000 m 
+790.000 386.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+788.000 401.000 m 
+788.000 389.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+786.000 398.000 m 
+786.000 392.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+784.000 396.000 m 
+784.000 394.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 748.85 461.40 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 465.000 m 
+795.000 460.000 l
+780.000 460.000 l
+780.000 470.000 l
+795.000 470.000 l
+800.000 465.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 961.71 431.60 Tm
+(RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+940.000 435.000 m 
+945.000 440.000 l
+960.000 440.000 l
+960.000 430.000 l
+945.000 430.000 l
+940.000 435.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 756.77 421.40 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 425.000 m 
+795.000 420.000 l
+780.000 420.000 l
+780.000 430.000 l
+795.000 430.000 l
+800.000 425.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 750.61 441.40 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 445.000 m 
+795.000 440.000 l
+780.000 440.000 l
+780.000 450.000 l
+795.000 450.000 l
+800.000 445.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 750.61 431.40 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 435.000 m 
+795.000 430.000 l
+780.000 430.000 l
+780.000 440.000 l
+795.000 440.000 l
+800.000 435.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 764.69 411.40 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 415.000 m 
+795.000 410.000 l
+780.000 410.000 l
+780.000 420.000 l
+795.000 420.000 l
+800.000 415.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 962.01 451.15 Tm
+(DIO2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+940.000 455.000 m 
+945.000 460.000 l
+960.000 460.000 l
+960.000 450.000 l
+945.000 450.000 l
+940.000 455.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 759.19 471.40 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 475.000 m 
+795.000 470.000 l
+780.000 470.000 l
+780.000 480.000 l
+795.000 480.000 l
+800.000 475.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 961.71 441.60 Tm
+(TXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+940.000 445.000 m 
+945.000 450.000 l
+960.000 450.000 l
+960.000 440.000 l
+945.000 440.000 l
+940.000 445.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 750.06 451.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+800.000 455.000 m 
+795.000 450.000 l
+780.000 450.000 l
+780.000 460.000 l
+795.000 460.000 l
+800.000 455.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 927.00 342.76 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+940.000 360.000 m 
+940.000 370.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+931.000 360.000 m 
+949.000 360.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+934.000 358.000 m 
+946.000 358.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+937.000 356.000 m 
+943.000 356.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+939.000 354.000 m 
+941.000 354.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 243.00 487.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+255.000 485.000 m 
+255.000 475.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+250.000 485.000 m 
+260.000 485.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 660.50 465.93 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+660.000 470.000 m 
+650.000 470.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+660.000 475.000 m 
+660.000 465.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+175.000 425.000 m 
+210.000 425.000 l
+255.000 425.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 142.00 429.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+155.000 445.000 m 
+155.000 455.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+146.000 445.000 m 
+164.000 445.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+149.000 443.000 m 
+161.000 443.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+152.000 441.000 m 
+158.000 441.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+154.000 439.000 m 
+156.000 439.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 182.00 428.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+195.000 445.000 m 
+195.000 455.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+186.000 445.000 m 
+204.000 445.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+189.000 443.000 m 
+201.000 443.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+192.000 441.000 m 
+198.000 441.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+194.000 439.000 m 
+196.000 439.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 950.50 460.92 Tm
+(+5V) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+950.000 465.000 m 
+940.000 465.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+950.000 470.000 m 
+950.000 460.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+650.000 490.000 m 
+650.000 480.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+940.000 465.000 m 
+940.000 475.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+231.000 294.000 m 
+239.000 286.000 l
+239.000 294.000 m 
+231.000 286.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+371.000 244.000 m 
+379.000 236.000 l
+379.000 244.000 m 
+371.000 236.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+371.000 284.000 m 
+379.000 276.000 l
+379.000 284.000 m 
+371.000 276.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+371.000 274.000 m 
+379.000 266.000 l
+379.000 274.000 m 
+371.000 266.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+231.000 204.000 m 
+239.000 196.000 l
+239.000 204.000 m 
+231.000 196.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+371.000 204.000 m 
+379.000 196.000 l
+379.000 204.000 m 
+371.000 196.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 397.71 246.28 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+375.000 250.000 m 
+380.000 255.000 l
+395.000 255.000 l
+395.000 245.000 l
+380.000 245.000 l
+375.000 250.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 397.00 256.60 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+375.000 260.000 m 
+380.000 265.000 l
+395.000 265.000 l
+395.000 255.000 l
+380.000 255.000 l
+375.000 260.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 183.85 236.40 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+235.000 240.000 m 
+230.000 235.000 l
+215.000 235.000 l
+215.000 245.000 l
+230.000 245.000 l
+235.000 240.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 199.69 246.40 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+235.000 250.000 m 
+230.000 245.000 l
+215.000 245.000 l
+215.000 255.000 l
+230.000 255.000 l
+235.000 250.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 185.61 266.40 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+235.000 270.000 m 
+230.000 265.000 l
+215.000 265.000 l
+215.000 275.000 l
+230.000 275.000 l
+235.000 270.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 191.77 256.40 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+235.000 260.000 m 
+230.000 255.000 l
+215.000 255.000 l
+215.000 265.000 l
+230.000 265.000 l
+235.000 260.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 185.61 276.40 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+235.000 280.000 m 
+230.000 275.000 l
+215.000 275.000 l
+215.000 285.000 l
+230.000 285.000 l
+235.000 280.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+298.96 313.33 Td
+(E80-900M2213S) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+298.96 322.33 Td
+(E80-900M2213S) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 187.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 191.00 Td
+(22) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 190.000 m 
+255.000 190.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 197.00 Td
+(ANT_2.4) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 201.00 Td
+(21) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 200.000 m 
+255.000 200.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 207.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 211.00 Td
+(20) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 210.000 m 
+255.000 210.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 227.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 231.00 Td
+(19) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 230.000 m 
+255.000 230.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 237.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 241.00 Td
+(18) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 240.000 m 
+255.000 240.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 247.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 251.00 Td
+(17) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 250.000 m 
+255.000 250.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 257.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 261.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 260.000 m 
+255.000 260.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 267.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 271.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 270.000 m 
+255.000 270.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 277.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 281.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 280.000 m 
+255.000 280.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 287.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 291.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 290.000 m 
+255.000 290.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+257.00 297.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+238.57 301.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+235.000 300.000 m 
+255.000 300.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+332.36 297.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 301.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 300.000 m 
+355.000 300.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+334.29 287.00 Td
+(VCC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 291.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 290.000 m 
+355.000 290.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+329.49 277.00 Td
+(DIO7) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 281.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 280.000 m 
+355.000 280.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+329.49 267.00 Td
+(DIO8) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 271.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 270.000 m 
+355.000 270.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+329.49 257.00 Td
+(DIO9) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 261.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 260.000 m 
+355.000 260.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+328.32 247.00 Td
+(NRST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 251.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 250.000 m 
+355.000 250.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+339.99 237.00 Td
+(NC) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 241.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 240.000 m 
+355.000 240.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+332.36 227.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 231.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 230.000 m 
+355.000 230.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+332.36 207.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 211.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 210.000 m 
+355.000 210.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+311.72 197.00 Td
+(ANT_900) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 201.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 200.000 m 
+355.000 200.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+332.36 187.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+360.00 191.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+375.000 190.000 m 
+355.000 190.000 l
+S
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+257.000 310.000 m 
+353.000 310.000 l
+354.105 310.000 355.000 309.105 355.000 308.000 c
+355.000 172.000 l
+355.000 170.895 353.895 170.000 353.000 170.000 c
+257.000 170.000 l
+255.895 170.000 255.000 171.105 255.000 172.000 c
+255.000 308.000 l
+255.000 309.105 256.105 310.000 257.000 310.000 c
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 362.00 163.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+375.000 180.000 m 
+375.000 190.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+366.000 180.000 m 
+384.000 180.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+369.000 178.000 m 
+381.000 178.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+372.000 176.000 m 
+378.000 176.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+374.000 174.000 m 
+376.000 174.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 391.50 206.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 210.000 m 
+375.000 210.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 201.000 m 
+385.000 219.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+387.000 204.000 m 
+387.000 216.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+389.000 207.000 m 
+389.000 213.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+391.000 209.000 m 
+391.000 211.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 391.50 226.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 230.000 m 
+375.000 230.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 221.000 m 
+385.000 239.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+387.000 224.000 m 
+387.000 236.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+389.000 227.000 m 
+389.000 233.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+391.000 229.000 m 
+391.000 231.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 391.50 296.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 300.000 m 
+375.000 300.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 291.000 m 
+385.000 309.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+387.000 294.000 m 
+387.000 306.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+389.000 297.000 m 
+389.000 303.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+391.000 299.000 m 
+391.000 301.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 192.50 296.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 300.000 m 
+235.000 300.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 309.000 m 
+225.000 291.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+223.000 306.000 m 
+223.000 294.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+221.000 303.000 m 
+221.000 297.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+219.000 301.000 m 
+219.000 299.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 192.50 226.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 230.000 m 
+235.000 230.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 239.000 m 
+225.000 221.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+223.000 236.000 m 
+223.000 224.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+221.000 233.000 m 
+221.000 227.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+219.000 231.000 m 
+219.000 229.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 192.50 206.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 210.000 m 
+235.000 210.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 219.000 m 
+225.000 201.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+223.000 216.000 m 
+223.000 204.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+221.000 213.000 m 
+221.000 207.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+219.000 211.000 m 
+219.000 209.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 192.50 186.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 190.000 m 
+235.000 190.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+225.000 199.000 m 
+225.000 181.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+223.000 196.000 m 
+223.000 184.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+221.000 193.000 m 
+221.000 187.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+219.000 191.000 m 
+219.000 189.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 386.00 286.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 290.000 m 
+375.000 290.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+385.000 295.000 m 
+385.000 285.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+160.000 675.000 m 
+160.000 680.000 l
+160.000 685.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 655.000 m 
+160.000 655.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 645.000 m 
+160.000 645.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 635.000 m 
+160.000 635.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 625.000 m 
+160.000 625.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 615.000 m 
+160.000 615.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+145.000 605.000 m 
+160.000 605.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 112.70 591.40 Tm
+(P1.06) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+160.000 595.000 m 
+155.000 590.000 l
+140.000 590.000 l
+140.000 600.000 l
+155.000 600.000 l
+160.000 595.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 112.70 701.40 Tm
+(P0.06) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+160.000 705.000 m 
+155.000 700.000 l
+140.000 700.000 l
+140.000 710.000 l
+155.000 710.000 l
+160.000 705.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 112.70 691.40 Tm
+(P0.08) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+160.000 695.000 m 
+155.000 690.000 l
+140.000 690.000 l
+140.000 700.000 l
+155.000 700.000 l
+160.000 695.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+417.00 632.75 Td
+(1.5M) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+417.00 641.75 Td
+(R2) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.63 0.00 0.00 RG
+[] 0 d
+405.00 655.00 10.00 -20.00 re
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+410.000 655.000 m 
+410.000 665.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+410.000 635.000 m 
+410.000 625.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+0.00 1.00 -1.00 0.00 413.70 727.50 Tm
+(Batt) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+410.000 705.000 m 
+405.000 710.000 l
+405.000 725.000 l
+415.000 725.000 l
+415.000 710.000 l
+410.000 705.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+186.000 549.000 m 
+194.000 541.000 l
+194.000 549.000 m 
+186.000 541.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+196.000 549.000 m 
+204.000 541.000 l
+204.000 549.000 m 
+196.000 541.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+206.000 549.000 m 
+214.000 541.000 l
+214.000 549.000 m 
+206.000 541.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+276.000 719.000 m 
+284.000 711.000 l
+284.000 719.000 m 
+276.000 711.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+156.000 719.000 m 
+164.000 711.000 l
+164.000 719.000 m 
+156.000 711.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 397.00 599.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+410.000 615.000 m 
+410.000 625.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+401.000 615.000 m 
+419.000 615.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+404.000 613.000 m 
+416.000 613.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+407.000 611.000 m 
+413.000 611.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+409.000 609.000 m 
+411.000 609.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+410.000 665.000 m 
+280.000 665.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+417.00 672.75 Td
+(1M) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+417.00 681.75 Td
+(R1) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.63 0.00 0.00 RG
+[] 0 d
+405.00 695.00 10.00 -20.00 re
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+410.000 695.000 m 
+410.000 705.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+410.000 675.000 m 
+410.000 665.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 685.000 m 
+280.000 685.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.01 681.07 Tm
+(RBtn) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 685.000 m 
+295.000 690.000 l
+310.000 690.000 l
+310.000 680.000 l
+295.000 680.000 l
+290.000 685.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 99.24 621.60 Tm
+(UBtn) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 625.000 m 
+140.000 620.000 l
+125.000 620.000 l
+125.000 630.000 l
+140.000 630.000 l
+145.000 625.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 117.50 676.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+150.000 680.000 m 
+160.000 680.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+150.000 689.000 m 
+150.000 671.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+148.000 686.000 m 
+148.000 674.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+146.000 683.000 m 
+146.000 677.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+144.000 681.000 m 
+144.000 679.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 595.000 m 
+280.000 595.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 655.000 m 
+280.000 655.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 605.000 m 
+280.000 605.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 625.000 m 
+280.000 625.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 92.64 631.40 Tm
+(GPSen) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 635.000 m 
+140.000 630.000 l
+125.000 630.000 l
+125.000 640.000 l
+140.000 640.000 l
+145.000 635.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 93.85 651.40 Tm
+(GPSrx) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 655.000 m 
+140.000 650.000 l
+125.000 650.000 l
+125.000 660.000 l
+140.000 660.000 l
+145.000 655.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 94.40 641.40 Tm
+(GPStx) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 645.000 m 
+140.000 640.000 l
+125.000 640.000 l
+125.000 650.000 l
+140.000 650.000 l
+145.000 645.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 615.000 m 
+280.000 615.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 635.000 m 
+280.000 635.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+290.000 645.000 m 
+280.000 645.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+345.000 695.000 m 
+280.000 695.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+345.000 675.000 m 
+280.000 675.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.00 601.60 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 605.000 m 
+295.000 610.000 l
+310.000 610.000 l
+310.000 600.000 l
+295.000 600.000 l
+290.000 605.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.50 591.60 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 595.000 m 
+295.000 600.000 l
+310.000 600.000 l
+310.000 590.000 l
+295.000 590.000 l
+290.000 595.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 356.00 671.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 675.000 m 
+345.000 675.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 680.000 m 
+355.000 670.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.00 621.60 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 625.000 m 
+295.000 630.000 l
+310.000 630.000 l
+310.000 620.000 l
+295.000 620.000 l
+290.000 625.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.00 631.60 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 635.000 m 
+295.000 640.000 l
+310.000 640.000 l
+310.000 630.000 l
+295.000 630.000 l
+290.000 635.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 312.00 641.60 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 645.000 m 
+295.000 650.000 l
+310.000 650.000 l
+310.000 640.000 l
+295.000 640.000 l
+290.000 645.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 311.84 611.60 Tm
+(SCk) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 615.000 m 
+295.000 620.000 l
+310.000 620.000 l
+310.000 610.000 l
+295.000 610.000 l
+290.000 615.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 311.70 651.60 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+290.000 655.000 m 
+295.000 660.000 l
+310.000 660.000 l
+310.000 650.000 l
+295.000 650.000 l
+290.000 655.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 361.50 691.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 695.000 m 
+345.000 695.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+355.000 686.000 m 
+355.000 704.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+357.000 689.000 m 
+357.000 701.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+359.000 692.000 m 
+359.000 698.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+361.000 694.000 m 
+361.000 696.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 302.50 701.30 Tm
+(Batt) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+280.000 705.000 m 
+285.000 710.000 l
+300.000 710.000 l
+300.000 700.000 l
+285.000 700.000 l
+280.000 705.000 l
+S
+10.00 w
+BT
+/F1 13 Tf
+13.00 TL
+0.000 0.000 1.000 rg
+1015.00 25.00 Td
+(Nom De Tom) Tj
+ET
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 647.00 251.60 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+625.000 255.000 m 
+630.000 260.000 l
+645.000 260.000 l
+645.000 250.000 l
+630.000 250.000 l
+625.000 255.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 513.00 297.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+525.000 295.000 m 
+525.000 285.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+520.000 295.000 m 
+530.000 295.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+625.000 175.000 m 
+625.000 215.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 612.00 149.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+625.000 165.000 m 
+625.000 175.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+616.000 165.000 m 
+634.000 165.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+619.000 163.000 m 
+631.000 163.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+622.000 161.000 m 
+628.000 161.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+624.000 159.000 m 
+626.000 159.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 474.33 201.85 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 205.000 m 
+520.000 200.000 l
+505.000 200.000 l
+505.000 210.000 l
+520.000 210.000 l
+525.000 205.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 473.74 191.85 Tm
+(RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 195.000 m 
+520.000 190.000 l
+505.000 190.000 l
+505.000 200.000 l
+520.000 200.000 l
+525.000 195.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 482.41 231.85 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 235.000 m 
+520.000 230.000 l
+505.000 230.000 l
+505.000 240.000 l
+520.000 240.000 l
+525.000 235.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 476.41 251.85 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 255.000 m 
+520.000 250.000 l
+505.000 250.000 l
+505.000 260.000 l
+520.000 260.000 l
+525.000 255.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 476.48 241.85 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 245.000 m 
+520.000 240.000 l
+505.000 240.000 l
+505.000 250.000 l
+520.000 250.000 l
+525.000 245.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 489.69 221.45 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 225.000 m 
+520.000 220.000 l
+505.000 220.000 l
+505.000 230.000 l
+520.000 230.000 l
+525.000 225.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 647.01 241.15 Tm
+(DIO2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+625.000 245.000 m 
+630.000 250.000 l
+645.000 250.000 l
+645.000 240.000 l
+630.000 240.000 l
+625.000 245.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 474.33 181.85 Tm
+(TXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 185.000 m 
+520.000 180.000 l
+505.000 180.000 l
+505.000 190.000 l
+520.000 190.000 l
+525.000 185.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 475.06 261.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+525.000 265.000 m 
+520.000 260.000 l
+505.000 260.000 l
+505.000 270.000 l
+520.000 270.000 l
+525.000 265.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+568.96 298.33 Td
+(SX1262_MOD) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+584.58 282.00 Td
+(ANT) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 286.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 285.000 m 
+605.000 285.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+568.96 307.33 Td
+(CORE_SX1262) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+582.36 202.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 206.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 205.000 m 
+605.000 205.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 192.00 Td
+(RXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+534.28 196.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 195.000 m 
+545.000 195.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 182.00 Td
+(TXEN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+534.28 186.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 185.000 m 
+545.000 185.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+579.49 242.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 246.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 245.000 m 
+605.000 245.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+579.49 252.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 256.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 255.000 m 
+605.000 255.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+582.36 192.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 196.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 195.000 m 
+605.000 195.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 282.00 Td
+(3V3) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+534.28 286.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 285.000 m 
+545.000 285.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 202.00 Td
+(BUSY) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+534.28 206.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 205.000 m 
+545.000 205.000 l
+S
+1.00 w
+0.53 0.00 0.00 RG
+545.00 265.00 m 545.00 266.66 543.66 268.00 542.00 268.00 c
+540.34 268.00 539.00 266.66 539.00 265.00 c
+539.00 263.34 540.34 262.00 542.00 262.00 c
+543.66 262.00 545.00 263.34 545.00 265.00 c
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 262.00 Td
+(RST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+528.57 266.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 265.000 m 
+539.000 265.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 252.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+528.57 256.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 255.000 m 
+545.000 255.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 242.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+528.57 246.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 245.000 m 
+545.000 245.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 232.000 m 
+548.000 235.000 l
+545.000 238.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 232.00 Td
+(CLK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+528.57 236.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 235.000 m 
+545.000 235.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+547.00 222.00 Td
+(CS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+528.57 226.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+525.000 225.000 m 
+545.000 225.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+582.36 182.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 186.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 185.000 m 
+605.000 185.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+582.36 212.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+610.00 216.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+625.000 215.000 m 
+605.000 215.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+550.000 295.000 m 
+600.000 295.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+605.000 290.000 m 
+605.000 180.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+600.000 175.000 m 
+550.000 175.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+0.00 g
+[] 0 d
+545.000 180.000 m 
+545.000 290.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 290.000 m 
+545.000 295.000 545.000 295.000 550.000 295.000 c
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+600.000 295.000 m 
+605.000 295.000 605.000 295.000 605.000 290.000 c
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+605.000 180.000 m 
+605.000 175.000 605.000 175.000 600.000 175.000 c
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+545.000 180.000 m 
+545.000 175.000 545.000 175.000 550.000 175.000 c
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 737.72 681.45 Tm
+(MCU_RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+820.000 685.000 m 
+815.000 680.000 l
+800.000 680.000 l
+800.000 690.000 l
+815.000 690.000 l
+820.000 685.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+1115.00 362.67 Td
+(100uF) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+1115.00 371.67 Td
+(C1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1113.000 373.000 m 
+1097.000 373.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+1105.000 365.000 m 
+1105.000 355.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1105.000 385.000 m 
+1105.000 377.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1097.000 377.000 m 
+1113.000 377.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+1105.000 385.000 m 
+1105.000 395.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1105.000 373.000 m 
+1105.000 365.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+1065.00 362.67 Td
+(100uF) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+1065.00 371.67 Td
+(C2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1063.000 373.000 m 
+1047.000 373.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+1055.000 365.000 m 
+1055.000 355.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1055.000 385.000 m 
+1055.000 377.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1047.000 377.000 m 
+1063.000 377.000 l
+S
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+1055.000 385.000 m 
+1055.000 395.000 l
+S
+1 J
+1 j
+1.00 w
+0.63 0.00 0.00 RG
+0.00 g
+[] 0 d
+1055.000 373.000 m 
+1055.000 365.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 1092.00 329.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1105.000 345.000 m 
+1105.000 355.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1096.000 345.000 m 
+1114.000 345.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1099.000 343.000 m 
+1111.000 343.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1102.000 341.000 m 
+1108.000 341.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1104.000 339.000 m 
+1106.000 339.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 1043.00 407.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1055.000 405.000 m 
+1055.000 395.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1050.000 405.000 m 
+1060.000 405.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 1042.00 327.76 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1055.000 345.000 m 
+1055.000 355.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1046.000 345.000 m 
+1064.000 345.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1049.000 343.000 m 
+1061.000 343.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1052.000 341.000 m 
+1058.000 341.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1054.000 339.000 m 
+1056.000 339.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 1095.00 407.00 Tm
+(+5V) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1105.000 405.000 m 
+1105.000 395.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+1100.000 405.000 m 
+1110.000 405.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 102.98 611.40 Tm
+(SCL) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 615.000 m 
+140.000 610.000 l
+125.000 610.000 l
+125.000 620.000 l
+140.000 620.000 l
+145.000 615.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 101.11 601.40 Tm
+(SDA) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+145.000 605.000 m 
+140.000 600.000 l
+125.000 600.000 l
+125.000 610.000 l
+140.000 610.000 l
+145.000 605.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+820.000 685.000 m 
+840.000 685.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 77.72 661.45 Tm
+(MCU_RXEN) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+160.000 665.000 m 
+155.000 660.000 l
+140.000 660.000 l
+140.000 670.000 l
+155.000 670.000 l
+160.000 665.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+160.000 715.000 m 
+165.000 715.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+621.000 289.000 m 
+629.000 281.000 l
+629.000 289.000 m 
+621.000 281.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+251.000 439.000 m 
+259.000 431.000 l
+259.000 439.000 m 
+251.000 431.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+251.000 449.000 m 
+259.000 441.000 l
+259.000 449.000 m 
+251.000 441.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+341.000 449.000 m 
+349.000 441.000 l
+349.000 449.000 m 
+341.000 441.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+251.000 409.000 m 
+259.000 401.000 l
+259.000 409.000 m 
+251.000 401.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+171.000 479.000 m 
+179.000 471.000 l
+179.000 479.000 m 
+171.000 471.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 432.69 661.28 Tm
+(ADC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+410.000 665.000 m 
+415.000 670.000 l
+430.000 670.000 l
+430.000 660.000 l
+415.000 660.000 l
+410.000 665.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+213.86 728.26 Td
+(PRO_MICRO_NRF52840_29P) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+213.86 737.11 Td
+(PRO-MICRO) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+180.00 725.00 80.00 -160.00 re
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+234.94 702.00 Td
+(BATIN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 706.00 Td
+(25) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 705.000 m 
+260.000 705.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+240.95 692.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 696.00 Td
+(24) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 695.000 m 
+260.000 695.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+243.04 682.00 Td
+(RST) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 686.00 Td
+(23) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 685.000 m 
+260.000 685.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+226.28 672.00 Td
+(3.3v Out) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 676.00 Td
+(22) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 675.000 m 
+260.000 675.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 632.00 Td
+(P1.15) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 636.00 Td
+(18) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 635.000 m 
+260.000 635.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 662.00 Td
+(P0.31) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 666.00 Td
+(21) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 665.000 m 
+260.000 665.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 692.00 Td
+(P0.08) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 696.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 695.000 m 
+180.000 695.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 622.00 Td
+(P1.13) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 626.00 Td
+(17) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 625.000 m 
+260.000 625.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 652.00 Td
+(P0.29) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 656.00 Td
+(20) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 655.000 m 
+260.000 655.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 612.00 Td
+(P1.11) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 616.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 615.000 m 
+260.000 615.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 642.00 Td
+(P0.02) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 646.00 Td
+(19) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 645.000 m 
+260.000 645.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 682.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 686.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 685.000 m 
+180.000 685.000 l
+S
+1.00 w
+0.55 0.14 0.14 RG
+180.00 705.00 m 180.00 706.66 178.66 708.00 177.00 708.00 c
+175.34 708.00 174.00 706.66 174.00 705.00 c
+174.00 703.34 175.34 702.00 177.00 702.00 c
+178.66 702.00 180.00 703.34 180.00 705.00 c
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 702.00 Td
+(P0.06) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 706.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 705.000 m 
+174.000 705.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 592.00 Td
+(P0.09) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 596.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 595.000 m 
+260.000 595.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+236.90 602.00 Td
+(P0.10) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 606.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 605.000 m 
+260.000 605.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 602.00 Td
+(P1.04) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+162.57 606.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 605.000 m 
+180.000 605.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 672.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 676.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 675.000 m 
+180.000 675.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 662.00 Td
+(P0.17) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 666.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 665.000 m 
+180.000 665.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 652.00 Td
+(P0.20) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 656.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 655.000 m 
+180.000 655.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 642.00 Td
+(P0.22) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 646.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 645.000 m 
+180.000 645.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 632.00 Td
+(P0.24) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 636.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 635.000 m 
+180.000 635.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 622.00 Td
+(P1.00) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+162.57 626.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 625.000 m 
+180.000 625.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 612.00 Td
+(P0.11) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+162.57 616.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 615.000 m 
+180.000 615.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 592.00 Td
+(P1.06) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+162.57 596.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 595.000 m 
+180.000 595.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 193.00 568.00 Tm
+(P1.01) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 189.00 547.57 Tm
+(27) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+190.000 545.000 m 
+190.000 565.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 203.00 568.00 Tm
+(P1.02) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 199.00 547.57 Tm
+(28) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+200.000 545.000 m 
+200.000 565.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 213.00 568.00 Tm
+(P1.07) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+0.00 1.00 -1.00 0.00 209.00 547.57 Tm
+(29) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+210.000 545.000 m 
+210.000 565.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+234.94 712.00 Td
+(BATIN) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+266.00 716.00 Td
+(26) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+280.000 715.000 m 
+260.000 715.000 l
+S
+BT
+/F1 7 Tf
+7.00 TL
+0.553 0.137 0.137 rg
+183.00 712.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.553 0.137 0.137 rg
+168.28 716.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.55 0.14 0.14 RG
+[] 0 d
+160.000 715.000 m 
+180.000 715.000 l
+S
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+864.25 253.00 Td
+(RA-02_C9900010926) Tj
+ET
+10.00 w
+BT
+/F3 9 Tf
+9.00 TL
+0.000 0.000 0.502 rg
+864.25 262.00 Td
+(RA-02) Tj
+ET
+2 J
+0 j
+100 M
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+837.000 250.000 m 
+903.000 250.000 l
+904.105 250.000 905.000 249.105 905.000 248.000 c
+905.000 162.000 l
+905.000 160.895 903.895 160.000 903.000 160.000 c
+837.000 160.000 l
+835.895 160.000 835.000 161.105 835.000 162.000 c
+835.000 248.000 l
+835.000 249.105 836.105 250.000 837.000 250.000 c
+S
+1.00 w
+0.53 0.00 0.00 RG
+0.53 0.00 0.00 rg
+[] 0 d
+841.50 245.00 m 841.50 245.83 840.83 246.50 840.00 246.50 c
+839.17 246.50 838.50 245.83 838.50 245.00 c
+838.50 244.17 839.17 243.50 840.00 243.50 c
+840.83 243.50 841.50 244.17 841.50 245.00 c
+B
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+838.70 236.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+828.78 241.00 Td
+(1) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+825.000 240.000 m 
+835.000 240.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+838.70 226.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+828.78 231.00 Td
+(2) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+825.000 230.000 m 
+835.000 230.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 216.00 Td
+(3.3V) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 221.00 Td
+(3) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 220.000 m 
+835.000 220.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 206.00 Td
+(RESET) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 211.00 Td
+(4) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 210.000 m 
+835.000 210.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 196.00 Td
+(DIO0) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 201.00 Td
+(5) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 200.000 m 
+835.000 200.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 186.00 Td
+(DIO1) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 191.00 Td
+(6) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 190.000 m 
+835.000 190.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 176.00 Td
+(DIO2) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 181.00 Td
+(7) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 180.000 m 
+835.000 180.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+838.70 166.00 Td
+(DIO3) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+828.78 171.00 Td
+(8) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+825.000 170.000 m 
+835.000 170.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+880.66 166.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+905.50 171.00 Td
+(9) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+915.000 170.000 m 
+905.000 170.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+877.79 176.00 Td
+(DIO4) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 181.00 Td
+(10) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 180.000 m 
+905.000 180.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+877.79 186.00 Td
+(DIO5) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 191.00 Td
+(11) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 190.000 m 
+905.000 190.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+882.64 196.00 Td
+(SCK) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 201.00 Td
+(12) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 200.000 m 
+905.000 200.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+876.71 206.00 Td
+(MISO) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 211.00 Td
+(13) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 210.000 m 
+905.000 210.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+876.71 216.00 Td
+(MOSI) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 221.00 Td
+(14) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 220.000 m 
+905.000 220.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+882.27 226.00 Td
+(NSS) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 0.000 1.000 rg
+905.50 231.00 Td
+(15) Tj
+ET
+1 J
+1 j
+1.00 w
+0.53 0.00 0.00 RG
+[] 0 d
+915.000 230.000 m 
+905.000 230.000 l
+S
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+880.66 236.00 Td
+(GND) Tj
+ET
+BT
+/F1 9 Tf
+9.00 TL
+0.000 g
+905.50 241.00 Td
+(16) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+[] 0 d
+915.000 240.000 m 
+905.000 240.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 773.85 196.40 Tm
+(BUSY) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+825.000 200.000 m 
+820.000 195.000 l
+805.000 195.000 l
+805.000 205.000 l
+820.000 205.000 l
+825.000 200.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 937.00 226.60 Tm
+(CS) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+915.000 230.000 m 
+920.000 235.000 l
+935.000 235.000 l
+935.000 225.000 l
+920.000 225.000 l
+915.000 230.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 936.71 196.60 Tm
+(SCK) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+915.000 200.000 m 
+920.000 205.000 l
+935.000 205.000 l
+935.000 195.000 l
+920.000 195.000 l
+915.000 200.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 937.00 216.60 Tm
+(MOSI) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+915.000 220.000 m 
+920.000 225.000 l
+935.000 225.000 l
+935.000 215.000 l
+920.000 215.000 l
+915.000 220.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 937.00 206.60 Tm
+(MISO) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+915.000 210.000 m 
+920.000 215.000 l
+935.000 215.000 l
+935.000 205.000 l
+920.000 205.000 l
+915.000 210.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 775.06 206.57 Tm
+(NRST) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+825.000 210.000 m 
+820.000 205.000 l
+805.000 205.000 l
+805.000 215.000 l
+820.000 215.000 l
+825.000 210.000 l
+S
+BT
+/F2 11 Tf
+11.00 TL
+0.000 0.000 1.000 rg
+1.00 -0.00 0.00 1.00 784.19 186.40 Tm
+(IRQ) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 0.00 1.00 RG
+0.00 g
+[] 0 d
+825.000 190.000 m 
+820.000 185.000 l
+805.000 185.000 l
+805.000 195.000 l
+820.000 195.000 l
+825.000 190.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 931.50 166.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+925.000 170.000 m 
+915.000 170.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+925.000 161.000 m 
+925.000 179.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+927.000 164.000 m 
+927.000 176.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+929.000 167.000 m 
+929.000 173.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+931.000 169.000 m 
+931.000 171.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 931.50 236.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+925.000 240.000 m 
+915.000 240.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+925.000 231.000 m 
+925.000 249.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+927.000 234.000 m 
+927.000 246.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+929.000 237.000 m 
+929.000 243.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+931.000 239.000 m 
+931.000 241.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 782.50 236.00 Tm
+(GND) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+815.000 240.000 m 
+825.000 240.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+815.000 249.000 m 
+815.000 231.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+813.000 246.000 m 
+813.000 234.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+811.000 243.000 m 
+811.000 237.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+809.000 241.000 m 
+809.000 239.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 0.53 0.00 RG
+0.00 g
+[] 0 d
+825.000 240.000 m 
+825.000 230.000 l
+S
+BT
+/F2 12 Tf
+12.00 TL
+0.000 g
+1.00 -0.00 0.00 1.00 789.82 216.00 Tm
+(VCC) Tj
+ET
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+815.000 220.000 m 
+825.000 220.000 l
+S
+1 J
+1 j
+1.00 w
+0.00 G
+0.00 g
+[] 0 d
+815.000 215.000 m 
+815.000 225.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+911.000 194.000 m 
+919.000 186.000 l
+919.000 194.000 m 
+911.000 186.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+911.000 184.000 m 
+919.000 176.000 l
+919.000 184.000 m 
+911.000 176.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+821.000 184.000 m 
+829.000 176.000 l
+829.000 184.000 m 
+821.000 176.000 l
+S
+1 J
+1 j
+1.00 w
+0.20 0.80 0.20 RG
+[] 0 d
+821.000 174.000 m 
+829.000 166.000 l
+829.000 174.000 m 
+821.000 166.000 l
+S
+0.80 0.00 0.00 rg
+652.50 420.00 m 652.50 421.38 651.38 422.50 650.00 422.50 c
+648.62 422.50 647.50 421.38 647.50 420.00 c
+647.50 418.62 648.62 417.50 650.00 417.50 c
+651.38 417.50 652.50 418.62 652.50 420.00 c
+f
+0.80 0.00 0.00 rg
+942.50 415.00 m 942.50 416.38 941.38 417.50 940.00 417.50 c
+938.62 417.50 937.50 416.38 937.50 415.00 c
+937.50 413.62 938.62 412.50 940.00 412.50 c
+941.38 412.50 942.50 413.62 942.50 415.00 c
+f
+0.80 0.00 0.00 rg
+942.50 395.00 m 942.50 396.38 941.38 397.50 940.00 397.50 c
+938.62 397.50 937.50 396.38 937.50 395.00 c
+937.50 393.62 938.62 392.50 940.00 392.50 c
+941.38 392.50 942.50 393.62 942.50 395.00 c
+f
+0.80 0.00 0.00 rg
+942.50 385.00 m 942.50 386.38 941.38 387.50 940.00 387.50 c
+938.62 387.50 937.50 386.38 937.50 385.00 c
+937.50 383.62 938.62 382.50 940.00 382.50 c
+941.38 382.50 942.50 383.62 942.50 385.00 c
+f
+0.80 0.00 0.00 rg
+942.50 375.00 m 942.50 376.38 941.38 377.50 940.00 377.50 c
+938.62 377.50 937.50 376.38 937.50 375.00 c
+937.50 373.62 938.62 372.50 940.00 372.50 c
+941.38 372.50 942.50 373.62 942.50 375.00 c
+f
+0.80 0.00 0.00 rg
+652.50 490.00 m 652.50 491.38 651.38 492.50 650.00 492.50 c
+648.62 492.50 647.50 491.38 647.50 490.00 c
+647.50 488.62 648.62 487.50 650.00 487.50 c
+651.38 487.50 652.50 488.62 652.50 490.00 c
+f
+0.80 0.00 0.00 rg
+942.50 465.00 m 942.50 466.38 941.38 467.50 940.00 467.50 c
+938.62 467.50 937.50 466.38 937.50 465.00 c
+937.50 463.62 938.62 462.50 940.00 462.50 c
+941.38 462.50 942.50 463.62 942.50 465.00 c
+f
+0.80 0.00 0.00 rg
+412.50 665.00 m 412.50 666.38 411.38 667.50 410.00 667.50 c
+408.62 667.50 407.50 666.38 407.50 665.00 c
+407.50 663.62 408.62 662.50 410.00 662.50 c
+411.38 662.50 412.50 663.62 412.50 665.00 c
+f
+0.80 0.00 0.00 rg
+162.50 680.00 m 162.50 681.38 161.38 682.50 160.00 682.50 c
+158.62 682.50 157.50 681.38 157.50 680.00 c
+157.50 678.62 158.62 677.50 160.00 677.50 c
+161.38 677.50 162.50 678.62 162.50 680.00 c
+f
+0.80 0.00 0.00 rg
+627.50 185.00 m 627.50 186.38 626.38 187.50 625.00 187.50 c
+623.62 187.50 622.50 186.38 622.50 185.00 c
+622.50 183.62 623.62 182.50 625.00 182.50 c
+626.38 182.50 627.50 183.62 627.50 185.00 c
+f
+0.80 0.00 0.00 rg
+627.50 195.00 m 627.50 196.38 626.38 197.50 625.00 197.50 c
+623.62 197.50 622.50 196.38 622.50 195.00 c
+622.50 193.62 623.62 192.50 625.00 192.50 c
+626.38 192.50 627.50 193.62 627.50 195.00 c
+f
+0.80 0.00 0.00 rg
+627.50 205.00 m 627.50 206.38 626.38 207.50 625.00 207.50 c
+623.62 207.50 622.50 206.38 622.50 205.00 c
+622.50 203.62 623.62 202.50 625.00 202.50 c
+626.38 202.50 627.50 203.62 627.50 205.00 c
+f
+0.80 0.00 0.00 rg
+827.50 240.00 m 827.50 241.38 826.38 242.50 825.00 242.50 c
+823.62 242.50 822.50 241.38 822.50 240.00 c
+822.50 238.62 823.62 237.50 825.00 237.50 c
+826.38 237.50 827.50 238.62 827.50 240.00 c
+f
+q
+102.00 0 0 20.00 706.00 30.50 cm
+/I0 Do
+Q
+endstream
+endobj
+1 0 obj
+<>
+endobj
+5 0 obj
+<<
+/Descent -209
+/CapHeight 727
+/StemV 0
+/Type /FontDescriptor
+/Flags 32
+/FontBBox [-559 -303 1446 1050]
+/FontName /Verdana
+/ItalicAngle 0
+/Ascent 1005
+>>
+endobj
+6 0 obj
+<>
+endobj
+7 0 obj
+<<
+/Type /Font
+/BaseFont /Times-Roman
+/Subtype /Type1
+/Encoding /WinAnsiEncoding
+/FirstChar 32
+/LastChar 255
+>>
+endobj
+8 0 obj
+<<
+/Descent -325
+/CapHeight 500
+/StemV 80
+/Type /FontDescriptor
+/Flags 32
+/FontBBox [-665 -325 2000 1006]
+/FontName /Arial
+/ItalicAngle 0
+/Ascent 1006
+>>
+endobj
+9 0 obj
+<>
+endobj
+10 0 obj
+<<
+/Type /XObject
+/Subtype /Image
+/Width 520
+/Height 105
+/ColorSpace /DeviceRGB
+/BitsPerComponent 8
+/DecodeParms <>
+/SMask 11 0 R
+/Length 6251
+/Filter /FlateDecode
+>>
+stream
+xKqǣ{$V>:dm]uXbřZ>ـlhy-a$`!qMrN|G^_LUz8gfdf0~	SEQe"EQaAQEۢ&\gn_@QJk#o(sdAO%1GŘf!Eykn!׍&쯯),rlUӳR(p\DUnGo),m(.@Qҳ\9TEYdd#ܼsu.)rUXWqvkG<7yW(Jk)˶yns
?'ȶ섡RtOQ:oNsk(kȯ`U7(ƬjY8n+jRݠ(ʚK@+U೸֙ٚ4Bd^T7(
+W㊞g)}]
4TIQc2+D+H	]noh6<+S`?w~N*c{!q~j`z&VPRe 4hy4AQ5!vQo_}?cEQր*Q	>yû(>?-hQV₞ɡ7ߦb~>HRǸ!<^}v~[TTaYUmAX$;9lφ"w9$3;yFQ<b~x-Q.}|	ӱu
+=9ԭ,jic>9R#bɪ|X_4eb'6~/vK!I"Nbejӎa?f򃔼b꧃1N3ȺwErU4;q5A6<0tϤ"/R>X$%AZ1c{od6^Sr)k-䔎zD/⇺l,bzs&gGL1HXɲUS]ҋRBrēwLwX|楔]2CI2w,!S*Nf&Ybd]]9U`~qQ]e3}GؑJRYQ඾r֌.͌uNO79ĝ(0'OvJg{d	٥wq9
d+,өQBV=>qU6FB%1ڷD؈_gVPW)By24qaJԌ$pk8q]t	q.!y-8́},wȷP%?C>4pTa߇8'ZNm19 m>QeMi$-:1̱mt^"\˩
84R!t	"!W'
}g>fd5hDuO3|m0I?x]r
fR4H48BKrYA1cL:{%o;gZpD1[{P|=`2(K^6gr_9Nو? 9,zYD4<*W)*/ws8y+<.E	嚞˹/u%NQA>9ϴch@93hvs~~I7k[|9)s&*þ[<#e
+ٜӓ(m!èkzP\`\̞hOa^Q]Bvl:P]}~Ո~zyfduۅjz'>)o\n`i%4Nk4GRI]/[|M=.-uV'W3A̯
_:<`I-jFLP]KcH&Ϙ~
W178YnW=!l 3n4)ZPeS	V5JR1=9ʾ0ɰAʛt1_d%ghl3%c
+nŔZ!g?+sDNoz8'"L5(BY;q5~'ųZ"慊o?JaЛc\%\MsɔiLq]PE['Uʎ_z7(C3rt$@EBe~9IoN@2?ԫ܍Q972ý.3@v^_}xZɶn\rR.ɳzz_MDI"t:V^Õ
+2/;؁y9
+©h	
+5vWP޻H#n
6*8W$1{ eB*rb4$!&?˵bJŰrtǀYGȥI	+~ǰװO
+NRT62Oy\Yw񑭖C{kKl/,4_}5iCpQ~"(Py.x1m0Mrڧ/ug]|&S_.=ę!V!t=)l7	t:ݤ'REgH&w9Zr]3ܺrc;hH9yK *g\mcbG`M@Qj<|)~Et
+8;N/$%4qI_KSp{pkx;Vi7u_N$C`F67ˣM-|2(/5)M*>]	&%[aǫG$	+B=etHLEy0{oD̔KMpT7FDA7?7nL
aRqQ[^1@(R@lk7	\pRa
[yC;eDx{RSZ\;GɞJh^ipNbH݈x;^XRƼ\gbMBTK-Pp;mpP.v:j;>*o
+ĽIG1|%z((y}D
+ć%O1$(wO _=Â7)^'*Bݲ#ULc6p7nPȫN+\^y;PjG82ܚG?!Vne2doŭϼ
+q1JO텘"ӻA n_5mE~qTiwu	8+i*x؆	Vu+^f|`(pp8ԔK}
+Zq9=	%&6紦uKe	}FX\q5&HOhӴTGw;G*@ӡ`n]0ss}ZWhNѰt\\ JYEpN1'{im[/`5a!np)'6}ڟBUǭ~嗩op:Vf/fVF[
!t'<}
S1ţc8}.4baΉ"+)!u(ofO|
qB	K?wCi!bѕ`+bjb1+/miɭg.X:rgUKK,3rڐf">[)˚+Djbna5[oL"pe|r(L^e̟gQYm5P]tݬĝ鐭xj1ՅDe\X[DV7TEF6/C@hbo1A`PoT:-݈}Vq!,J_j%D1#7;D$I(St(0'KK\SW}/;*b>)9]Jw+[CDi,ТˣRIᅖZe@l=Qbk tjAT`&wm3@] r28\ݸREMP+*zE❩{~|ԃMlwǶpZfo@3/M`cw$VLQ.%D6v]/p*4B)Ddb5~|
+J
+$ ۝zW3ovѨ}.R{L%o#4>݂q)C?.$a,nl&vJ,c>l*U4v)ɢ+iͿUM`娘lSƩP0؄Ha>"IUh+VQ7aJQr.6ҷǀf3@ŕ+z$e&SR#}[M"ڊPңey׀UA_bP1%2rJTd)J2&2MhIu*k
+F\UQkM}v(FjbS(I"kc
QO%)Ǧ(4L0gUwX^4'R_hXgmׯR9YiF|yLʕ.-%]g^\ϜTRSL1hAI6E	JZt)(PŠ(\@(rF-
+endstream
+endobj
+11 0 obj
+<<
+/Type /XObject
+/Subtype /Image
+/Width 520
+/Height 105
+/ColorSpace /DeviceGray
+/BitsPerComponent 8
+/DecodeParms <>
+/Length 6577
+/Filter /FlateDecode
+>>
+stream
+x]M?c=ȳJQj"*"xIJhGQ"o袌4(`B0_}{}3W3[g{{{k p*Bn"5-߯Ds0uN\'!WfBKl5Gt\1g}vD]EԄ\᾽3珧/Sډ&B!ދIɻ:fVW&>ľ~Jz\z
"`O>o/t[mGT]/u }EM4(uBW+I~
@SvXMq:~=|EP(BxF=hƕnWV/Ϗjpw,z~PvЬ]9ޕ,gv{NM[u)+++?DPɩ~^vJu?	)2tGqL
+Ï*ʋIȌ'#GR$$뵏[<ҡ䔊]]L'{KG9x)Lԉ
+0G6.ݡ92P-b|N2NU#4}K&jG
DԷc#By&ZdQ%ND#BݵԚv>)s\RPB>Wiк3k.
+p5|aΈ	ԈQQ5xO!XdAB"9g~2P<Z2s1v/tFtd/4Yx7wa鮋(:Bc7
E`~wa'S>{~
+0ㄠ)9ZmΔ>EE-#
+_xEbeAvc]DeCӏOs@j=oj+58`~W/x}љ^VzMQv7GU|CEbTK0CEңU%ێ^NO{K-q"+W`No`VW~md1JwKf.*(c*s=
+@qǭ/G|#A8tf:Vo	2g| <ʬ3i|r>V[%{gX7닅;%6&Z8(˰ؚ4+{>3'HgvM=MBt:LnukTl4ӉHDuNşvGXX~!,!5:M_cssQI>k#+0~ǎj'1\%P8ՠ*
+C"hoW!;yTv9.:64lLd6cm)gӕzʅwLd`:kj/f5O'3w2^$b^~ejS[z]*_~c#eI2[BӧC.⴨Q({z+WnݺB<=p2nCQ{w)[ב{\zZl[h.jyMt9]'pn7Fخ8Qp%| kN»3g2{ۜ#Հػ/r	@`Six΀?_wijpo"9{sS]r61B)z<;h!/
eT	nGAQis79Wbp k=i~ݵ)KX[n
+K3w̧$Apbz#D*J)2$)Wo&9VWeޱL\4@)69ֆՠH23`O@?zm~ GBQI
?=))Q,p1'nўXfb,̎ZY06GYw1=n32"0pyq0WH|,|WXm`R)^o;{ƌ&?Se
*:D?d6G+RQsi9+v
+EqwKmfP7JgNJ<ۀ3n-*:侟HvlwRs^0GjFy-NHu{T8xЪ̽
lmd`|8X~g.ty	H/m'4/8Nvor(dO9‹@1	bՄoA*e1Z쎕&L`- lR,l1xwݞ9t>D} p1~&)%,,0^j

4>N}춖1w6miW0crAI~aߊ?*{JIڢ	&ѿ+qTnLdz.~vKDFyо\y%[6mjrg">tCrRY)S@ڂ`ZرB8]`.f1J|!\2Yc:H6c/Qۜvo^aL%	Y"#qwǛqW_i,2Kz=◧IdV^\Y.G+ǻ6Dў4S)UrCD
+7#lg]|H'bRNUt~i'
n+
f&ݍPr'Rd:4".;mi氧/=j훌wJc	neWCn/PA5^b6I#6O%2A|ǧ㽎ԛ^MN@e.z	1ۓ	ý6(-+j	6g);8)tKTn?pgN{砱
+L!KI 4aҎ:
+r \4l'qK)o5UY#/h16ƁB2dV
yK
+6F8"ƍSSK_dRh4*_?LѫVS[+?޺IzE=B"ҔH׸3n+4d(b"`W~D}Gerp{)/B"hMh@#j~4-f`IkLtؐΒԣI4&ݿ֬_7KR$hyhJw3Tu0W|P&L/Y50_d&ןZB.B4eӋPB?%
+xYŰt#m#C`Xhѩp'zOCd~7p?DQaBC%'Qn'w 9{JCitWq,YO֦0:෤'҉FF߹:iy* ~%k/aXxQT4	EZ%{sIb&EjMQw4붘.qT7P@j@LJ7AhY<3P`P(&ia8=n5'>t^B͗Ym\_p2]KN/*5^1@&OVJ`
+hN%:OwoF|r`oX"-[ATw	m0y(
ǂہ[-68*6_558$Cne;h҉1c;[jȱ&gיDvoc߮I|RZ>BO>rBWȉT!9Oe	0+:BcbBP9i{6mB'e>Ty#xOZH{tT<`	3ރә@2l-&@WA>KZumx(11f8RuJWD+(f_ld9qmx1A65nd3RL~
+\rt4z*yKo܀LlYwO/;LoXeBG4WOpWCx) aR$OB?* ʹdՌ]lI yсK|3ܺ60s]n@{)R]#*1ѢX5K,-j."n^`W.8ufR;=wT"a´㓽l(.~iX\kf=Uaۏ*{IcEa1ʹTsxXQ,~Qė@梽R,@UR-J))J$}aͣ/)UXQ3[e¶5KFNe`fEїi	Ċ|r|+de&4\!qaj)2Z[E]oz#HFR?2.beгPd
+1^a4S)]Hph^@7
+WmNIY]|9ŋK]@toYɱJmͯ7^Al2+XWZ0^߱]^025_f4#/UGCJG[<~7ݲԎۭ5bչk}y1Ԭ0r2euZQM/6Z,`|S2ʺMJ(83w	[n
+}~s<_t)OΎT+(Zd%I5|`T뉃
+*͑tb"F42/*>>/ԣ@ ;$X2wׂiM=agCʽFWXl˨+RT^Or

Cwp]"H:u"##ҘRN/_z#)Kj=r+Vy<+Xn\G!!B_8V'w#Ev·s`EPR7«À!!ZAuTLmV#"oƐlogc"c';oHok/WE0]IXF]!wE0Xy-lx3ōaQ
+
tjMnUBQf<_E	X4f.]A0FIϋ*> ;\DPp#AT ?7
+endstream
+endobj
+2 0 obj
+<<
+/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
+/Font <<
+/F1 6 0 R
+/F2 7 0 R
+/F3 9 0 R
+>>
+/XObject <<
+/I0 10 0 R
+>>
+>>
+endobj
+12 0 obj
+<<
+/Producer (jsPDF 0.0.0)
+/CreationDate (D:20241217154849-00'00')
+>>
+endobj
+13 0 obj
+<<
+/Type /Catalog
+/Pages 1 0 R
+/OpenAction [3 0 R /FitH null]
+/PageLayout /OneColumn
+>>
+endobj
+xref
+0 14
+0000000000 65535 f 
+0000102899 00000 n 
+0000118853 00000 n 
+0000000015 00000 n 
+0000000125 00000 n 
+0000102956 00000 n 
+0000103126 00000 n 
+0000104180 00000 n 
+0000104307 00000 n 
+0000104476 00000 n 
+0000105520 00000 n 
+0000112030 00000 n 
+0000118988 00000 n 
+0000119074 00000 n 
+trailer
+<<
+/Size 14
+/Root 13 0 R
+/Info 12 0 R
+/ID [ <906A4C76C35816C42EB6FFD13B3B7D92> <906A4C76C35816C42EB6FFD13B3B7D92> ]
+>>
+startxref
+119178
+%%EOF
\ No newline at end of file
diff --git a/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h b/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h
new file mode 100644
index 000000000..2258c3135
--- /dev/null
+++ b/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h
@@ -0,0 +1,17 @@
+#include "RadioLib.h"
+
+// RF Switch Matrix SubG RFO_HP_LF / RFO_LP_LF / RFI_[NP]_LF0
+// DIO5 -> RFSW0_V1
+// DIO6 -> RFSW1_V2
+// DIO7 -> ANT_CTRL_ON + ESP_IO9/LR_GPS_ANT_DC_EN -> RFI_GPS (Bias-T GPS) (LR11x0 only)
+
+static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_LR11X0_DIO7, RADIOLIB_NC,
+                                             RADIOLIB_NC};
+
+static const Module::RfSwitchMode_t rfswitch_table[] = {
+    // mode                  DIO5  DIO6  DIO7
+    {LR11x0::MODE_STBY, {LOW, LOW, LOW}},  {LR11x0::MODE_RX, {HIGH, LOW, LOW}},
+    {LR11x0::MODE_TX, {LOW, HIGH, LOW}},   {LR11x0::MODE_TX_HP, {LOW, HIGH, LOW}},
+    {LR11x0::MODE_TX_HF, {LOW, LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW, HIGH}},
+    {LR11x0::MODE_WIFI, {LOW, LOW, LOW}},  END_OF_MODE_TABLE,
+};
\ No newline at end of file
diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h
index 5c535ba1e..6ffb86cff 100644
--- a/variants/diy/nrf52_promicro_diy_tcxo/variant.h
+++ b/variants/diy/nrf52_promicro_diy_tcxo/variant.h
@@ -122,12 +122,13 @@ NRF52 PRO MICRO PIN ASSIGNMENT
 #define USE_SX1262
 #define USE_RF95
 #define USE_SX1268
+#define USE_LR1121
 
 // RF95 CONFIG
 
-#define LORA_DIO0 (0 + 29) // P0.10 IRQ
+#define LORA_DIO0 (0 + 29) // P0.29 BUSY
 #define LORA_DIO1 (0 + 10) // P0.10 IRQ
-#define LORA_RESET (0 + 9) // P0.09
+#define LORA_RESET (0 + 9) // P0.09 NRST
 
 // RX/TX for RFM95/SX127x
 #define RF95_RXEN (0 + 17)    // P0.17
@@ -143,6 +144,19 @@ NRF52 PRO MICRO PIN ASSIGNMENT
 #define SX126X_RXEN (0 + 17)     // P0.17
 #define SX126X_TXEN RADIOLIB_NC  // Assuming that DIO2 is connected to TXEN pin. If not, TXEN must be connected.
 
+// LR1121
+#ifdef USE_LR1121
+#define LR1121_IRQ_PIN (0 + 10)      // P0.10 IRQ
+#define LR1121_NRESET_PIN LORA_RESET // P0.09 NRST
+#define LR1121_BUSY_PIN (0 + 29)     // P0.29 BUSY
+#define LR1121_SPI_NSS_PIN LORA_CS   // P1.13
+#define LR1121_SPI_SCK_PIN LORA_SCK
+#define LR1121_SPI_MOSI_PIN LORA_MOSI
+#define LR1121_SPI_MISO_PIN LORA_MISO
+#define LR11X0_DIO3_TCXO_VOLTAGE 1.8
+#define LR11X0_DIO_AS_RF_SWITCH
+#endif
+
 // #define SX126X_MAX_POWER 8 set this if using a high-power board!
 
 /*
@@ -164,6 +178,7 @@ settings.
 | Seeed        | Wio-SX1262       | yes  | Int       | Sooooo cute!                          |
 | AI-Thinker   | RA-02            | No   | Int       | SX1278 **433mhz band only**           |
 | RF Solutions | RFM95            | No   | Int       | Untested                              |
+| Ebyte        | E80-900M2213S    | Yes  | Int       | LR1121 radio                          |
 
 */
 
@@ -179,4 +194,4 @@ extern float tcxoVoltage; // make this available everywhere
  *        Arduino objects - C++ only
  *----------------------------------------------------------------------------*/
 
-#endif
+#endif
\ No newline at end of file

From af79970ad7a4f6f10d0a1cdc9374a07c78ddae96 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=F0=9F=93=A1=20WatskeBart=20=F0=9F=A4=96?=
 
Date: Wed, 18 Dec 2024 05:46:18 +0100
Subject: [PATCH 20/43] Added product url (#5594)

---
 boards/t-echo.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/boards/t-echo.json b/boards/t-echo.json
index fcfc8c50b..f891da94f 100644
--- a/boards/t-echo.json
+++ b/boards/t-echo.json
@@ -48,6 +48,6 @@
     "require_upload_port": true,
     "wait_for_upload_port": true
   },
-  "url": "FIXME",
-  "vendor": "TTGO"
+  "url": "https://lilygo.cc/products/t-echo-lilygo",
+  "vendor": "LILYGO"
 }

From 68413486e3401d7503efffa60723a352f6ab5fa2 Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Wed, 18 Dec 2024 07:15:48 -0600
Subject: [PATCH 21/43] Switch back docker/login-action

---
 .github/workflows/build_native.yml | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml
index d9591e72c..b1b012705 100644
--- a/.github/workflows/build_native.yml
+++ b/.github/workflows/build_native.yml
@@ -53,9 +53,12 @@ jobs:
 
       - name: Docker login
         if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
-        run: |
-          echo ${{ secrets.DOCKER_FIRMWARE_TOKEN }} | docker login -u meshtastic --password-stdin
-        continue-on-error: true
+        uses: docker/login-action@v3
+        continue-on-error: true # FIXME: Failing docker login auth
+        with:
+          logout: true
+          username: meshtastic
+          password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
 
       - name: Docker setup
         if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}

From 8c6eec52f2f7323cd5b62abacb4dab512742feb2 Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Thu, 19 Dec 2024 03:47:46 -0800
Subject: [PATCH 22/43] Refactor MQTT::onReceive to reduce if/else nesting
 (#5592)

* Refactor MQTT::onReceive to reduce if/else nesting

* Fix missing #include 

* const DecodedServiceEnvelope e

* Combine validDecode if statement.

* Only call pb_release when validDecode.

* s/ptr/channelName/

* Use reference type for deleter

* Use lambda instead of bind

* Document deleter

* Reorder 'if's to avoid object creation

* Remove unnecessary comment

* Remove 'else'; simpifies #5516

---------

Co-authored-by: Ben Meadors 
---
 src/mesh/MemoryPool.h |  23 +++
 src/mesh/MeshTypes.h  |   1 +
 src/mqtt/MQTT.cpp     | 365 ++++++++++++++++++++++--------------------
 src/mqtt/MQTT.h       |   3 -
 4 files changed, 213 insertions(+), 179 deletions(-)

diff --git a/src/mesh/MemoryPool.h b/src/mesh/MemoryPool.h
index d30404b9f..c4af3c4ac 100644
--- a/src/mesh/MemoryPool.h
+++ b/src/mesh/MemoryPool.h
@@ -2,6 +2,8 @@
 
 #include 
 #include 
+#include 
+#include 
 
 #include "PointerQueue.h"
 
@@ -9,6 +11,7 @@ template  class Allocator
 {
 
   public:
+    Allocator() : deleter([this](T *p) { this->release(p); }) {}
     virtual ~Allocator() {}
 
     /// Return a queable object which has been prefilled with zeros.  Panic if no buffer is available
@@ -43,12 +46,32 @@ template  class Allocator
         return p;
     }
 
+    /// Variations of the above methods that return std::unique_ptr instead of raw pointers.
+    using UniqueAllocation = std::unique_ptr &>;
+    /// Return a queable object which has been prefilled with zeros.
+    /// std::unique_ptr wrapped variant of allocZeroed().
+    UniqueAllocation allocUniqueZeroed() { return UniqueAllocation(allocZeroed(), deleter); }
+    /// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you probably
+    /// don't want this version).
+    /// std::unique_ptr wrapped variant of allocZeroed(TickType_t maxWait).
+    UniqueAllocation allocUniqueZeroed(TickType_t maxWait) { return UniqueAllocation(allocZeroed(maxWait), deleter); }
+    /// Return a queable object which is a copy of some other object
+    /// std::unique_ptr wrapped variant of allocCopy(const T &src, TickType_t maxWait).
+    UniqueAllocation allocUniqueCopy(const T &src, TickType_t maxWait = portMAX_DELAY)
+    {
+        return UniqueAllocation(allocCopy(src, maxWait), deleter);
+    }
+
     /// Return a buffer for use by others
     virtual void release(T *p) = 0;
 
   protected:
     // Alloc some storage
     virtual T *alloc(TickType_t maxWait) = 0;
+
+  private:
+    // std::unique_ptr Deleter function; calls release().
+    const std::function deleter;
 };
 
 /**
diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h
index cf1b54c78..1d6bd342d 100644
--- a/src/mesh/MeshTypes.h
+++ b/src/mesh/MeshTypes.h
@@ -44,6 +44,7 @@ typedef int ErrorCode;
 
 /// Alloc and free packets to our global, ISR safe pool
 extern Allocator &packetPool;
+using UniquePacketPoolPacket = Allocator::UniqueAllocation;
 
 /**
  * Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 967db04d6..1f7a06787 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -23,11 +23,14 @@
 #include "serialization/MeshPacketSerializer.h"
 #include 
 #include 
-
-const int reconnectMax = 5;
+#include 
 
 MQTT *mqtt;
 
+namespace
+{
+constexpr int reconnectMax = 5;
+
 static MemoryDynamic staticMqttPool;
 
 Allocator &mqttPool = staticMqttPool;
@@ -37,6 +40,167 @@ static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for cha
 
 static bool isMqttServerAddressPrivate = false;
 
+// meshtastic_ServiceEnvelope that automatically releases dynamically allocated memory when it goes out of scope.
+struct DecodedServiceEnvelope : public meshtastic_ServiceEnvelope {
+    DecodedServiceEnvelope() = delete;
+    DecodedServiceEnvelope(const uint8_t *payload, size_t length)
+        : meshtastic_ServiceEnvelope(meshtastic_ServiceEnvelope_init_default),
+          validDecode(pb_decode_from_bytes(payload, length, &meshtastic_ServiceEnvelope_msg, this))
+    {
+    }
+    ~DecodedServiceEnvelope()
+    {
+        if (validDecode)
+            pb_release(&meshtastic_ServiceEnvelope_msg, this);
+    }
+    // Clients must check that this is true before using.
+    const bool validDecode;
+};
+
+inline void onReceiveProto(char *topic, byte *payload, size_t length)
+{
+    const DecodedServiceEnvelope e(payload, length);
+    if (!e.validDecode || e.channel_id == NULL || e.gateway_id == NULL || e.packet == NULL) {
+        LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!", topic, length);
+        return;
+    }
+    const 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.
+        // We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node
+        // receives it when we get our own packet back. Then we'll stop our retransmissions.
+        if (isFromUs(e.packet))
+            routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index);
+        else
+            LOG_INFO("Ignore downlink message we originally sent");
+        return;
+    }
+    if (isFromUs(e.packet)) {
+        LOG_INFO("Ignore downlink message we originally sent");
+        return;
+    }
+
+    // Find channel by channel_id and check downlink_enabled
+    if (!(strcmp(e.channel_id, "PKI") == 0 ||
+          (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && ch.settings.downlink_enabled))) {
+        return;
+    }
+    LOG_INFO("Received MQTT topic %s, len=%u", topic, length);
+
+    UniquePacketPoolPacket p = packetPool.allocUniqueCopy(*e.packet);
+    p->via_mqtt = true; // Mark that the packet was received via MQTT
+
+    if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
+        if (moduleConfig.mqtt.encryption_enabled) {
+            LOG_INFO("Ignore decoded message on MQTT, encryption is enabled");
+            return;
+        }
+        if (p->decoded.portnum == meshtastic_PortNum_ADMIN_APP) {
+            LOG_INFO("Ignore decoded admin packet");
+            return;
+        }
+        p->channel = ch.index;
+    }
+
+    // PKI messages get accepted even if we can't decrypt
+    if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && strcmp(e.channel_id, "PKI") == 0) {
+        const meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p.get()));
+        const meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to);
+        // Only accept PKI messages to us, or if we have both the sender and receiver in our nodeDB, as then it's
+        // likely they discovered each other via a channel we have downlink enabled for
+        if (isToUs(p.get()) || (tx && tx->has_user && rx && rx->has_user))
+            router->enqueueReceivedMessage(p.release());
+    } else if (router && perhapsDecode(p.get())) // ignore messages if we don't have the channel key
+        router->enqueueReceivedMessage(p.release());
+}
+
+// returns true if this is a valid JSON envelope which we accept on downlink
+inline bool isValidJsonEnvelope(JSONObject &json)
+{
+    // if "sender" is provided, avoid processing packets we uplinked
+    return (json.find("sender") != json.end() ? (json["sender"]->AsString().compare(owner.id) != 0) : true) &&
+           (json.find("hopLimit") != json.end() ? json["hopLimit"]->IsNumber() : true) && // hop limit should be a number
+           (json.find("from") != json.end()) && json["from"]->IsNumber() &&
+           (json["from"]->AsNumber() == nodeDB->getNodeNum()) &&            // only accept message if the "from" is us
+           (json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type
+           (json.find("payload") != json.end());                            // should have a payload
+}
+
+inline void onReceiveJson(byte *payload, size_t length)
+{
+    char payloadStr[length + 1];
+    memcpy(payloadStr, payload, length);
+    payloadStr[length] = 0; // null terminated string
+    std::unique_ptr json_value(JSON::Parse(payloadStr));
+    if (json_value == nullptr) {
+        LOG_ERROR("JSON received payload on MQTT but not a valid JSON");
+        return;
+    }
+
+    JSONObject json;
+    json = json_value->AsObject();
+
+    if (!isValidJsonEnvelope(json)) {
+        LOG_ERROR("JSON received payload on MQTT but not a valid envelope");
+        return;
+    }
+
+    // this is a valid envelope
+    if (json["type"]->AsString().compare("sendtext") == 0 && json["payload"]->IsString()) {
+        std::string jsonPayloadStr = json["payload"]->AsString();
+        LOG_INFO("JSON payload %s, length %u", jsonPayloadStr.c_str(), jsonPayloadStr.length());
+
+        // construct protobuf data packet using TEXT_MESSAGE, send it to the mesh
+        meshtastic_MeshPacket *p = router->allocForSending();
+        p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
+        if (json.find("channel") != json.end() && json["channel"]->IsNumber() &&
+            (json["channel"]->AsNumber() < channels.getNumChannels()))
+            p->channel = json["channel"]->AsNumber();
+        if (json.find("to") != json.end() && json["to"]->IsNumber())
+            p->to = json["to"]->AsNumber();
+        if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber())
+            p->hop_limit = json["hopLimit"]->AsNumber();
+        if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) {
+            memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length());
+            p->decoded.payload.size = jsonPayloadStr.length();
+            service->sendToMesh(p, RX_SRC_LOCAL);
+        } else {
+            LOG_WARN("Received MQTT json payload too long, drop");
+        }
+    } else if (json["type"]->AsString().compare("sendposition") == 0 && json["payload"]->IsObject()) {
+        // invent the "sendposition" type for a valid envelope
+        JSONObject posit;
+        posit = json["payload"]->AsObject(); // get nested JSON Position
+        meshtastic_Position pos = meshtastic_Position_init_default;
+        if (posit.find("latitude_i") != posit.end() && posit["latitude_i"]->IsNumber())
+            pos.latitude_i = posit["latitude_i"]->AsNumber();
+        if (posit.find("longitude_i") != posit.end() && posit["longitude_i"]->IsNumber())
+            pos.longitude_i = posit["longitude_i"]->AsNumber();
+        if (posit.find("altitude") != posit.end() && posit["altitude"]->IsNumber())
+            pos.altitude = posit["altitude"]->AsNumber();
+        if (posit.find("time") != posit.end() && posit["time"]->IsNumber())
+            pos.time = posit["time"]->AsNumber();
+
+        // construct protobuf data packet using POSITION, send it to the mesh
+        meshtastic_MeshPacket *p = router->allocForSending();
+        p->decoded.portnum = meshtastic_PortNum_POSITION_APP;
+        if (json.find("channel") != json.end() && json["channel"]->IsNumber() &&
+            (json["channel"]->AsNumber() < channels.getNumChannels()))
+            p->channel = json["channel"]->AsNumber();
+        if (json.find("to") != json.end() && json["to"]->IsNumber())
+            p->to = json["to"]->AsNumber();
+        if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber())
+            p->hop_limit = json["hopLimit"]->AsNumber();
+        p->decoded.payload.size =
+            pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Position_msg,
+                               &pos); // make the Data protobuf from position
+        service->sendToMesh(p, RX_SRC_LOCAL);
+    } else {
+        LOG_DEBUG("JSON ignore downlink message with unsupported type");
+    }
+}
+} // namespace
+
 void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
 {
     mqtt->onReceive(topic, payload, length);
@@ -49,170 +213,30 @@ void MQTT::onClientProxyReceive(meshtastic_MqttClientProxyMessage msg)
 
 void MQTT::onReceive(char *topic, byte *payload, size_t length)
 {
-    meshtastic_ServiceEnvelope e = meshtastic_ServiceEnvelope_init_default;
-
-    if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) {
-        // check if this is a json payload message by comparing the topic start
-        char payloadStr[length + 1];
-        memcpy(payloadStr, payload, length);
-        payloadStr[length] = 0; // null terminated string
-        JSONValue *json_value = JSON::Parse(payloadStr);
-        if (json_value != NULL) {
-            // check if it is a valid envelope
-            JSONObject json;
-            json = json_value->AsObject();
-
-            // parse the channel name from the topic string
-            // the topic has been checked above for having jsonTopic prefix, so just move past it
-            char *ptr = topic + jsonTopic.length();
-            ptr = strtok(ptr, "/") ? strtok(ptr, "/") : ptr; // if another "/" was added, parse string up to that character
-            meshtastic_Channel sendChannel = channels.getByName(ptr);
-            // We allow downlink JSON packets only on a channel named "mqtt"
-            if (strncasecmp(channels.getGlobalId(sendChannel.index), Channels::mqttChannel, strlen(Channels::mqttChannel)) == 0 &&
-                sendChannel.settings.downlink_enabled) {
-                if (isValidJsonEnvelope(json)) {
-                    // this is a valid envelope
-                    if (json["type"]->AsString().compare("sendtext") == 0 && json["payload"]->IsString()) {
-                        std::string jsonPayloadStr = json["payload"]->AsString();
-                        LOG_INFO("JSON payload %s, length %u", jsonPayloadStr.c_str(), jsonPayloadStr.length());
-
-                        // construct protobuf data packet using TEXT_MESSAGE, send it to the mesh
-                        meshtastic_MeshPacket *p = router->allocForSending();
-                        p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
-                        if (json.find("channel") != json.end() && json["channel"]->IsNumber() &&
-                            (json["channel"]->AsNumber() < channels.getNumChannels()))
-                            p->channel = json["channel"]->AsNumber();
-                        if (json.find("to") != json.end() && json["to"]->IsNumber())
-                            p->to = json["to"]->AsNumber();
-                        if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber())
-                            p->hop_limit = json["hopLimit"]->AsNumber();
-                        if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) {
-                            memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length());
-                            p->decoded.payload.size = jsonPayloadStr.length();
-                            service->sendToMesh(p, RX_SRC_LOCAL);
-                        } else {
-                            LOG_WARN("Received MQTT json payload too long, drop");
-                        }
-                    } else if (json["type"]->AsString().compare("sendposition") == 0 && json["payload"]->IsObject()) {
-                        // invent the "sendposition" type for a valid envelope
-                        JSONObject posit;
-                        posit = json["payload"]->AsObject(); // get nested JSON Position
-                        meshtastic_Position pos = meshtastic_Position_init_default;
-                        if (posit.find("latitude_i") != posit.end() && posit["latitude_i"]->IsNumber())
-                            pos.latitude_i = posit["latitude_i"]->AsNumber();
-                        if (posit.find("longitude_i") != posit.end() && posit["longitude_i"]->IsNumber())
-                            pos.longitude_i = posit["longitude_i"]->AsNumber();
-                        if (posit.find("altitude") != posit.end() && posit["altitude"]->IsNumber())
-                            pos.altitude = posit["altitude"]->AsNumber();
-                        if (posit.find("time") != posit.end() && posit["time"]->IsNumber())
-                            pos.time = posit["time"]->AsNumber();
-
-                        // construct protobuf data packet using POSITION, send it to the mesh
-                        meshtastic_MeshPacket *p = router->allocForSending();
-                        p->decoded.portnum = meshtastic_PortNum_POSITION_APP;
-                        if (json.find("channel") != json.end() && json["channel"]->IsNumber() &&
-                            (json["channel"]->AsNumber() < channels.getNumChannels()))
-                            p->channel = json["channel"]->AsNumber();
-                        if (json.find("to") != json.end() && json["to"]->IsNumber())
-                            p->to = json["to"]->AsNumber();
-                        if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber())
-                            p->hop_limit = json["hopLimit"]->AsNumber();
-                        p->decoded.payload.size =
-                            pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes),
-                                               &meshtastic_Position_msg, &pos); // make the Data protobuf from position
-                        service->sendToMesh(p, RX_SRC_LOCAL);
-                    } else {
-                        LOG_DEBUG("JSON ignore downlink message with unsupported type");
-                    }
-                } else {
-                    LOG_ERROR("JSON received payload on MQTT but not a valid envelope");
-                }
-            } else {
-                LOG_WARN("JSON downlink received on channel not called 'mqtt' or without downlink enabled");
-            }
-        } else {
-            // no json, this is an invalid payload
-            LOG_ERROR("JSON received payload on MQTT but not a valid JSON");
-        }
-        delete json_value;
-    } else {
-        if (length == 0) {
-            LOG_WARN("Empty MQTT payload received, topic %s!", topic);
-            return;
-        } else if (!pb_decode_from_bytes(payload, length, &meshtastic_ServiceEnvelope_msg, &e)) {
-            LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!", topic, length);
-            return;
-        } else {
-            if (e.channel_id == NULL || e.gateway_id == NULL) {
-                LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!", 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.
-                // We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node
-                // receives it when we get our own packet back. Then we'll stop our retransmissions.
-                if (e.packet && isFromUs(e.packet))
-                    routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index);
-                else
-                    LOG_INFO("Ignore downlink message we originally sent");
-            } else {
-                // Find channel by channel_id and check downlink_enabled
-                if ((strcmp(e.channel_id, "PKI") == 0 && e.packet) ||
-                    (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled)) {
-                    LOG_INFO("Received MQTT topic %s, len=%u", topic, length);
-                    meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet);
-                    p->via_mqtt = true; // Mark that the packet was received via MQTT
-
-                    if (isFromUs(p)) {
-                        LOG_INFO("Ignore downlink message we originally sent");
-                        packetPool.release(p);
-                        free(e.channel_id);
-                        free(e.gateway_id);
-                        free(e.packet);
-                        return;
-                    }
-                    if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
-                        if (moduleConfig.mqtt.encryption_enabled) {
-                            LOG_INFO("Ignore decoded message on MQTT, encryption is enabled");
-                            packetPool.release(p);
-                            free(e.channel_id);
-                            free(e.gateway_id);
-                            free(e.packet);
-                            return;
-                        }
-                        if (p->decoded.portnum == meshtastic_PortNum_ADMIN_APP) {
-                            LOG_INFO("Ignore decoded admin packet");
-                            packetPool.release(p);
-                            free(e.channel_id);
-                            free(e.gateway_id);
-                            free(e.packet);
-                            return;
-                        }
-                        p->channel = ch.index;
-                    }
-
-                    // PKI messages get accepted even if we can't decrypt
-                    if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag &&
-                        strcmp(e.channel_id, "PKI") == 0) {
-                        const meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p));
-                        const meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to);
-                        // Only accept PKI messages to us, or if we have both the sender and receiver in our nodeDB, as then it's
-                        // likely they discovered each other via a channel we have downlink enabled for
-                        if (isToUs(p) || (tx && tx->has_user && rx && rx->has_user))
-                            router->enqueueReceivedMessage(p);
-                    } else if (router && perhapsDecode(p)) // ignore messages if we don't have the channel key
-                        router->enqueueReceivedMessage(p);
-                    else
-                        packetPool.release(p);
-                }
-            }
-        }
-        // make sure to free both strings and the MeshPacket (passing in NULL is acceptable)
-        free(e.channel_id);
-        free(e.gateway_id);
-        free(e.packet);
+    if (length == 0) {
+        LOG_WARN("Empty MQTT payload received, topic %s!", topic);
+        return;
     }
+
+    // check if this is a json payload message by comparing the topic start
+    if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) {
+        // parse the channel name from the topic string
+        // the topic has been checked above for having jsonTopic prefix, so just move past it
+        char *channelName = topic + jsonTopic.length();
+        // if another "/" was added, parse string up to that character
+        channelName = strtok(channelName, "/") ? strtok(channelName, "/") : channelName;
+        // We allow downlink JSON packets only on a channel named "mqtt"
+        meshtastic_Channel &sendChannel = channels.getByName(channelName);
+        if (!(strncasecmp(channels.getGlobalId(sendChannel.index), Channels::mqttChannel, strlen(Channels::mqttChannel)) == 0 &&
+              sendChannel.settings.downlink_enabled)) {
+            LOG_WARN("JSON downlink received on channel not called 'mqtt' or without downlink enabled");
+            return;
+        }
+        onReceiveJson(payload, length);
+        return;
+    }
+
+    onReceiveProto(topic, payload, length);
 }
 
 void mqttInit()
@@ -705,17 +729,6 @@ void MQTT::perhapsReportToMap()
     }
 }
 
-bool MQTT::isValidJsonEnvelope(JSONObject &json)
-{
-    // if "sender" is provided, avoid processing packets we uplinked
-    return (json.find("sender") != json.end() ? (json["sender"]->AsString().compare(owner.id) != 0) : true) &&
-           (json.find("hopLimit") != json.end() ? json["hopLimit"]->IsNumber() : true) && // hop limit should be a number
-           (json.find("from") != json.end()) && json["from"]->IsNumber() &&
-           (json["from"]->AsNumber() == nodeDB->getNodeNum()) &&            // only accept message if the "from" is us
-           (json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type
-           (json.find("payload") != json.end());                            // should have a payload
-}
-
 bool MQTT::isPrivateIpAddress(const char address[])
 {
     // Min. length like 10.0.0.0 (8), max like 192.168.255.255:65535 (21)
diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h
index 7e0378238..dc82c1a74 100644
--- a/src/mqtt/MQTT.h
+++ b/src/mqtt/MQTT.h
@@ -117,9 +117,6 @@ class MQTT : private concurrency::OSThread
     // Check if we should report unencrypted information about our node for consumption by a map
     void perhapsReportToMap();
 
-    // returns true if this is a valid JSON envelope which we accept on downlink
-    bool isValidJsonEnvelope(JSONObject &json);
-
     /// Determines if the given address is a private IPv4 address, i.e. not routable on the public internet.
     /// These are the ranges: 127.0.0.1, 10.0.0.0-10.255.255.255, 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255.
     bool isPrivateIpAddress(const char address[]);

From 63091b783840ba40379bd53ac695c9d1485edfe4 Mon Sep 17 00:00:00 2001
From: Lewis He 
Date: Thu, 19 Dec 2024 20:21:54 +0800
Subject: [PATCH 23/43] [T-Deck] Fixed the issue that some devices may
 experience low voltage reset due to excessive startup current (#5607)

Co-authored-by: Ben Meadors 
---
 src/main.cpp | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/src/main.cpp b/src/main.cpp
index 2357a00de..eb99f279a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -237,6 +237,17 @@ void printInfo()
 #ifndef PIO_UNIT_TESTING
 void setup()
 {
+#if defined(T_DECK)
+    // GPIO10 manages all peripheral power supplies
+    // Turn on peripheral power immediately after MUC starts.
+    // If some boards are turned on late, ESP32 will reset due to low voltage.
+    // ESP32-C3(Keyboard) , MAX98357A(Audio Power Amplifier) , 
+    // TF Card , Display backlight(AW9364DNR) , AN48841B(Trackball) , ES7210(Decoder)
+    pinMode(KB_POWERON, OUTPUT);
+    digitalWrite(KB_POWERON, HIGH);
+    delay(100);
+#endif
+
     concurrency::hasBeenSetup = true;
 #if ARCH_PORTDUINO
     SPISettings spiSettings(settingsMap[spiSpeed], MSBFIRST, SPI_MODE0);
@@ -409,14 +420,6 @@ void setup()
     digitalWrite(AQ_SET_PIN, HIGH);
 #endif
 
-#if defined(T_DECK)
-    // enable keyboard
-    pinMode(KB_POWERON, OUTPUT);
-    digitalWrite(KB_POWERON, HIGH);
-    // There needs to be a delay after power on, give LILYGO-KEYBOARD some startup time
-    // otherwise keyboard and touch screen will not work
-    delay(200);
-#endif
 
     // Currently only the tbeam has a PMU
     // PMU initialization needs to be placed before i2c scanning

From 7075a05bcde9b1b6c89da91de7b495e02554d58d Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Thu, 19 Dec 2024 06:27:19 -0600
Subject: [PATCH 24/43] Fix docker secret permission

---
 .github/workflows/build_docker.yml | 68 ++++++++++++++++++++++++++++++
 .github/workflows/build_native.yml | 34 ---------------
 .github/workflows/main_matrix.yml  |  4 ++
 3 files changed, 72 insertions(+), 34 deletions(-)
 create mode 100644 .github/workflows/build_docker.yml

diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml
new file mode 100644
index 000000000..a08f5afdf
--- /dev/null
+++ b/.github/workflows/build_docker.yml
@@ -0,0 +1,68 @@
+name: Build Docker
+
+on: workflow_call
+
+permissions:
+  contents: write
+  packages: write
+
+jobs:
+  build-native:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Install libs needed for native build
+        shell: bash
+        run: |
+          sudo apt-get update --fix-missing
+          sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev
+
+      - name: Checkout code
+        uses: actions/checkout@v4
+        with:
+          submodules: recursive
+          ref: ${{github.event.pull_request.head.ref}}
+          repository: ${{github.event.pull_request.head.repo.full_name}}
+
+      - name: Upgrade python tools
+        shell: bash
+        run: |
+          python -m pip install --upgrade pip
+          pip install -U platformio adafruit-nrfutil
+          pip install -U meshtastic --pre
+
+      - name: Upgrade platformio
+        shell: bash
+        run: |
+          pio upgrade
+
+      - name: Build Native
+        run: bin/build-native.sh
+
+      - name: Docker login
+        if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
+        uses: docker/login-action@v3
+        with:
+          username: meshtastic
+          password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
+
+      - name: Docker setup
+        if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
+        uses: docker/setup-buildx-action@v3
+
+      - name: Docker build and push tagged versions
+        if: ${{ github.event_name == 'workflow_dispatch' }}
+        uses: docker/build-push-action@v6
+        with:
+          context: .
+          file: ./Dockerfile
+          push: true
+          tags: meshtastic/meshtasticd:${{ steps.version.outputs.version }}
+
+      - name: Docker build and push
+        if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
+        uses: docker/build-push-action@v6
+        with:
+          context: .
+          file: ./Dockerfile
+          push: true
+          tags: meshtastic/meshtasticd:latest
diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml
index b1b012705..a57da5dfb 100644
--- a/.github/workflows/build_native.yml
+++ b/.github/workflows/build_native.yml
@@ -50,37 +50,3 @@ jobs:
           path: |
             release/meshtasticd_linux_x86_64
             bin/config-dist.yaml
-
-      - name: Docker login
-        if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
-        uses: docker/login-action@v3
-        continue-on-error: true # FIXME: Failing docker login auth
-        with:
-          logout: true
-          username: meshtastic
-          password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
-
-      - name: Docker setup
-        if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
-        continue-on-error: true
-        uses: docker/setup-buildx-action@v3
-
-      - name: Docker build and push tagged versions
-        if: ${{ github.event_name == 'workflow_dispatch' }}
-        continue-on-error: true
-        uses: docker/build-push-action@v6
-        with:
-          context: .
-          file: ./Dockerfile
-          push: true
-          tags: meshtastic/device-simulator:${{ steps.version.outputs.version }}
-
-      - name: Docker build and push
-        if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
-        continue-on-error: true
-        uses: docker/build-push-action@v6
-        with:
-          context: .
-          file: ./Dockerfile
-          push: true
-          tags: meshtastic/device-simulator:latest
diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml
index 86fb6e699..86b9dad18 100644
--- a/.github/workflows/main_matrix.yml
+++ b/.github/workflows/main_matrix.yml
@@ -137,6 +137,10 @@ jobs:
   package-native:
     uses: ./.github/workflows/package_amd64.yml
 
+  build-docker:
+    uses: ./.github/workflows/build_docker.yml
+    secrets: inherit
+
   after-checks:
     runs-on: ubuntu-latest
     if: ${{ github.event_name != 'workflow_dispatch' }}

From 445c64100481b5ce196dc8d7d99a9e9fdf28b4b2 Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Thu, 19 Dec 2024 07:52:17 -0600
Subject: [PATCH 25/43] Version

---
 .github/workflows/build_docker.yml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml
index a08f5afdf..bb5a394fd 100644
--- a/.github/workflows/build_docker.yml
+++ b/.github/workflows/build_docker.yml
@@ -38,6 +38,10 @@ jobs:
       - name: Build Native
         run: bin/build-native.sh
 
+      - name: Get release version string
+        run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+        id: version
+
       - name: Docker login
         if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
         uses: docker/login-action@v3

From 827553f4c77e535329fb59eb79ca502a0341b096 Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Thu, 19 Dec 2024 08:42:49 -0600
Subject: [PATCH 26/43] Only execute on workflow_dispatch

---
 .github/workflows/main_matrix.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml
index 86b9dad18..0109bef1a 100644
--- a/.github/workflows/main_matrix.yml
+++ b/.github/workflows/main_matrix.yml
@@ -138,6 +138,7 @@ jobs:
     uses: ./.github/workflows/package_amd64.yml
 
   build-docker:
+    if: ${{ github.event_name == 'workflow_dispatch' }}
     uses: ./.github/workflows/build_docker.yml
     secrets: inherit
 

From e1de439a7f7c132e469ac2ed32e9b90ad527c3e3 Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Thu, 19 Dec 2024 17:14:27 -0800
Subject: [PATCH 27/43] Remove unnecessary memcpy for PKI crypto (#5608)

* Remove unnecessary memcpy for PKI crypto

* Update comment s/packet_id/id/

* Create a copy of bytes for each channel decrypt

---------

Co-authored-by: Jonathan Bennett 
---
 src/mesh/CryptoEngine.cpp | 24 +++++++++++++++++-------
 src/mesh/CryptoEngine.h   |  4 ++--
 src/mesh/Router.cpp       | 14 +++++---------
 3 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp
index 94b9b6543..1624ab0d5 100644
--- a/src/mesh/CryptoEngine.cpp
+++ b/src/mesh/CryptoEngine.cpp
@@ -58,10 +58,16 @@ void CryptoEngine::clearKeys()
  * Encrypt a packet's payload using a key generated with Curve25519 and SHA256
  * for a specific node.
  *
- * @param bytes is updated in place
+ * @param toNode The MeshPacket `to` field.
+ * @param fromNode The MeshPacket `from` field.
+ * @param remotePublic The remote node's Curve25519 public key.
+ * @param packetId The MeshPacket `id` field.
+ * @param numBytes Number of bytes of plaintext in the bytes buffer.
+ * @param bytes Buffer containing plaintext input.
+ * @param bytesOut Output buffer to be populated with encrypted ciphertext.
  */
 bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtastic_UserLite_public_key_t remotePublic,
-                                     uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut)
+                                     uint64_t packetNum, size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut)
 {
     uint8_t *auth;
     long extraNonceTmp = random();
@@ -93,14 +99,18 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtas
  * Decrypt a packet's payload using a key generated with Curve25519 and SHA256
  * for a specific node.
  *
- * @param bytes is updated in place
+ * @param fromNode The MeshPacket `from` field.
+ * @param remotePublic The remote node's Curve25519 public key.
+ * @param packetId The MeshPacket `id` field.
+ * @param numBytes Number of bytes of ciphertext in the bytes buffer.
+ * @param bytes Buffer containing ciphertext input.
+ * @param bytesOut Output buffer to be populated with decrypted plaintext.
  */
 bool CryptoEngine::decryptCurve25519(uint32_t fromNode, meshtastic_UserLite_public_key_t remotePublic, uint64_t packetNum,
-                                     size_t numBytes, uint8_t *bytes, uint8_t *bytesOut)
+                                     size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut)
 {
-    uint8_t *auth;       // set to last 8 bytes of text?
-    uint32_t extraNonce; // pointer was not really used
-    auth = bytes + numBytes - 12;
+    const uint8_t *auth = bytes + numBytes - 12; // set to last 8 bytes of text?
+    uint32_t extraNonce;                         // pointer was not really used
     memcpy(&extraNonce, auth + 8,
            sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : (uint32_t *)(auth + 8);
     LOG_INFO("Random nonce value: %d", extraNonce);
diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h
index 32862d95c..6bbcb3b8a 100644
--- a/src/mesh/CryptoEngine.h
+++ b/src/mesh/CryptoEngine.h
@@ -40,9 +40,9 @@ class CryptoEngine
     void clearKeys();
     void setDHPrivateKey(uint8_t *_private_key);
     virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtastic_UserLite_public_key_t remotePublic,
-                                   uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut);
+                                   uint64_t packetNum, size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut);
     virtual bool decryptCurve25519(uint32_t fromNode, meshtastic_UserLite_public_key_t remotePublic, uint64_t packetNum,
-                                   size_t numBytes, uint8_t *bytes, uint8_t *bytesOut);
+                                   size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut);
     virtual bool setDHPublicKey(uint8_t *publicKey);
     virtual void hash(uint8_t *bytes, size_t numBytes);
 
diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp
index e714ef215..f55e7cc5a 100644
--- a/src/mesh/Router.cpp
+++ b/src/mesh/Router.cpp
@@ -37,7 +37,6 @@ static MemoryDynamic staticPool;
 Allocator &packetPool = staticPool;
 
 static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__));
-static uint8_t ScratchEncrypted[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__));
 
 /**
  * Constructor
@@ -327,9 +326,6 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
     }
     bool decrypted = false;
     ChannelIndex chIndex = 0;
-    memcpy(bytes, p->encrypted.bytes,
-           rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
-    memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize);
 #if !(MESHTASTIC_EXCLUDE_PKI)
     // Attempt PKI decryption first
     if (p->channel == 0 && isToUs(p) && p->to > 0 && !isBroadcast(p->to) && nodeDB->getMeshNode(p->from) != nullptr &&
@@ -337,7 +333,7 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
         rawSize > MESHTASTIC_PKC_OVERHEAD) {
         LOG_DEBUG("Attempt PKI decryption");
 
-        if (crypto->decryptCurve25519(p->from, nodeDB->getMeshNode(p->from)->user.public_key, p->id, rawSize, ScratchEncrypted,
+        if (crypto->decryptCurve25519(p->from, nodeDB->getMeshNode(p->from)->user.public_key, p->id, rawSize, p->encrypted.bytes,
                                       bytes)) {
             LOG_INFO("PKI Decryption worked!");
             memset(&p->decoded, 0, sizeof(p->decoded));
@@ -349,8 +345,6 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
                 p->pki_encrypted = true;
                 memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32);
                 p->public_key.size = 32;
-                // memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers
-                // chIndex = 8;
             } else {
                 LOG_ERROR("PKC Decrypted, but pb_decode failed!");
                 return false;
@@ -367,6 +361,9 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
         for (chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
             // Try to use this hash/channel pair
             if (channels.decryptForHash(chIndex, p->channel)) {
+                // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf. Create a
+                // fresh copy for each decrypt attempt.
+                memcpy(bytes, p->encrypted.bytes, rawSize);
                 // Try to decrypt the packet if we can
                 crypto->decrypt(p->from, p->id, rawSize, bytes);
 
@@ -515,9 +512,8 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
                          *node->user.public_key.bytes);
                 return meshtastic_Routing_Error_PKI_FAILED;
             }
-            crypto->encryptCurve25519(p->to, getFrom(p), node->user.public_key, p->id, numbytes, bytes, ScratchEncrypted);
+            crypto->encryptCurve25519(p->to, getFrom(p), node->user.public_key, p->id, numbytes, bytes, p->encrypted.bytes);
             numbytes += MESHTASTIC_PKC_OVERHEAD;
-            memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes);
             p->channel = 0;
             p->pki_encrypted = true;
         } else {

From 658459aaf3a6142445cf7494df7fe2291fd8138e Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Fri, 20 Dec 2024 12:59:23 -0800
Subject: [PATCH 28/43] Use encoded ServiceEnvelope in mqttQueue (#5619)

---
 src/mqtt/MQTT.cpp | 267 ++++++++++++++++++++++------------------------
 src/mqtt/MQTT.h   |   6 +-
 2 files changed, 133 insertions(+), 140 deletions(-)

diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 1f7a06787..e40578680 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -24,6 +24,7 @@
 #include 
 #include 
 #include 
+#include 
 
 MQTT *mqtt;
 
@@ -31,10 +32,6 @@ namespace
 {
 constexpr int reconnectMax = 5;
 
-static MemoryDynamic staticMqttPool;
-
-Allocator &mqttPool = staticMqttPool;
-
 // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
 static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for channel name and 16 for nodeid
 
@@ -528,39 +525,37 @@ void MQTT::publishNodeInfo()
 }
 void MQTT::publishQueuedMessages()
 {
-    if (!mqttQueue.isEmpty()) {
-        LOG_DEBUG("Publish enqueued MQTT message");
-        meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0);
-        size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
-        std::string topic;
-        if (env->packet->pki_encrypted) {
-            topic = cryptTopic + "PKI/" + owner.id;
-        } else {
-            topic = cryptTopic + env->channel_id + "/" + owner.id;
-        }
-        LOG_INFO("publish %s, %u bytes from queue", topic.c_str(), numBytes);
+    if (mqttQueue.isEmpty())
+        return;
 
-        publish(topic.c_str(), bytes, numBytes, false);
+    LOG_DEBUG("Publish enqueued MQTT message");
+    const std::unique_ptr entry(mqttQueue.dequeuePtr(0));
+    LOG_INFO("publish %s, %u bytes from queue", entry->topic.c_str(), entry->envBytes.size());
+    publish(entry->topic.c_str(), entry->envBytes.data(), entry->envBytes.size(), false);
 
 #if !defined(ARCH_NRF52) ||                                                                                                      \
     defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ###
-        if (moduleConfig.mqtt.json_enabled) {
-            // handle json topic
-            auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet);
-            if (jsonString.length() != 0) {
-                std::string topicJson;
-                if (env->packet->pki_encrypted) {
-                    topicJson = jsonTopic + "PKI/" + owner.id;
-                } else {
-                    topicJson = jsonTopic + env->channel_id + "/" + owner.id;
-                }
-                LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
-                publish(topicJson.c_str(), jsonString.c_str(), false);
-            }
-        }
-#endif // ARCH_NRF52 NRF52_USE_JSON
-        mqttPool.release(env);
+    if (!moduleConfig.mqtt.json_enabled)
+        return;
+
+    // handle json topic
+    const DecodedServiceEnvelope env(entry->envBytes.data(), entry->envBytes.size());
+    if (!env.validDecode || env.packet == NULL || env.channel_id == NULL)
+        return;
+
+    auto jsonString = MeshPacketSerializer::JsonSerialize(env.packet);
+    if (jsonString.length() == 0)
+        return;
+
+    std::string topicJson;
+    if (env.packet->pki_encrypted) {
+        topicJson = jsonTopic + "PKI/" + owner.id;
+    } else {
+        topicJson = jsonTopic + env.channel_id + "/" + owner.id;
     }
+    LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
+    publish(topicJson.c_str(), jsonString.c_str(), false);
+#endif // ARCH_NRF52 NRF52_USE_JSON
 }
 
 void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_MeshPacket &mp_decoded, ChannelIndex chIndex)
@@ -599,59 +594,56 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me
     // Either encrypted packet (we couldn't decrypt) is marked as pki_encrypted, or we could decode the PKI encrypted packet
     bool isPKIEncrypted = mp_encrypted.pki_encrypted || mp_decoded.pki_encrypted;
     // If it was to a channel, check uplink enabled, else must be pki_encrypted
-    if ((ch.settings.uplink_enabled && !isPKIEncrypted) || isPKIEncrypted) {
-        const char *channelId = isPKIEncrypted ? "PKI" : channels.getGlobalId(chIndex);
+    if (!(ch.settings.uplink_enabled || isPKIEncrypted))
+        return;
+    const char *channelId = isPKIEncrypted ? "PKI" : channels.getGlobalId(chIndex);
 
-        meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed();
-        env->channel_id = (char *)channelId;
-        env->gateway_id = owner.id;
+    LOG_DEBUG("MQTT onSend - Publish ");
+    const meshtastic_MeshPacket *p;
+    if (moduleConfig.mqtt.encryption_enabled) {
+        p = &mp_encrypted;
+        LOG_DEBUG("encrypted message");
+    } else if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
+        p = &mp_decoded;
+        LOG_DEBUG("portnum %i message", mp_decoded.decoded.portnum);
+    } else {
+        LOG_DEBUG("nothing, pkt not decrypted");
+        return; // Don't upload a still-encrypted PKI packet if not encryption_enabled
+    }
 
-        LOG_DEBUG("MQTT onSend - Publish ");
-        if (moduleConfig.mqtt.encryption_enabled) {
-            env->packet = (meshtastic_MeshPacket *)&mp_encrypted;
-            LOG_DEBUG("encrypted message");
-        } else if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
-            env->packet = (meshtastic_MeshPacket *)&mp_decoded;
-            LOG_DEBUG("portnum %i message", env->packet->decoded.portnum);
-        } else {
-            LOG_DEBUG("nothing, pkt not decrypted");
-            mqttPool.release(env);
-            return; // Don't upload a still-encrypted PKI packet if not encryption_enabled
-        }
+    const meshtastic_ServiceEnvelope env = {
+        .packet = const_cast(p), .channel_id = const_cast(channelId), .gateway_id = owner.id};
+    size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, &env);
+    std::string topic = cryptTopic + channelId + "/" + owner.id;
 
-        if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) {
-            size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
-            std::string topic = cryptTopic + channelId + "/" + owner.id;
-            LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes);
-
-            publish(topic.c_str(), bytes, numBytes, false);
+    if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) {
+        LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes);
+        publish(topic.c_str(), bytes, numBytes, false);
 
 #if !defined(ARCH_NRF52) ||                                                                                                      \
     defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ###
-            if (moduleConfig.mqtt.json_enabled) {
-                // handle json topic
-                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", topicJson.c_str(), jsonString.length(),
-                             jsonString.c_str());
-                    publish(topicJson.c_str(), jsonString.c_str(), false);
-                }
-            }
+        if (!moduleConfig.mqtt.json_enabled)
+            return;
+        // handle json topic
+        auto jsonString = MeshPacketSerializer::JsonSerialize(&mp_decoded);
+        if (jsonString.length() == 0)
+            return;
+        std::string topicJson = jsonTopic + channelId + "/" + owner.id;
+        LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
+        publish(topicJson.c_str(), jsonString.c_str(), false);
 #endif // ARCH_NRF52 NRF52_USE_JSON
+    } else {
+        LOG_INFO("MQTT not connected, queue packet");
+        QueueEntry *entry;
+        if (mqttQueue.numFree() == 0) {
+            LOG_WARN("MQTT queue is full, discard oldest");
+            entry = mqttQueue.dequeuePtr(0);
         } else {
-            LOG_INFO("MQTT not connected, queue packet");
-            if (mqttQueue.numFree() == 0) {
-                LOG_WARN("MQTT queue is full, discard oldest");
-                meshtastic_ServiceEnvelope *d = mqttQueue.dequeuePtr(0);
-                if (d)
-                    mqttPool.release(d);
-            }
-            // make a copy of serviceEnvelope and queue it
-            meshtastic_ServiceEnvelope *copied = mqttPool.allocCopy(*env);
-            assert(mqttQueue.enqueue(copied, 0));
+            entry = new QueueEntry;
         }
-        mqttPool.release(env);
+        entry->topic = std::move(topic);
+        entry->envBytes.assign(bytes, numBytes);
+        assert(mqttQueue.enqueue(entry, 0));
     }
 }
 
@@ -660,73 +652,70 @@ void MQTT::perhapsReportToMap()
     if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly()))
         return;
 
-    if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs)) {
+    if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs))
         return;
-    } else {
-        if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) {
-            last_report_to_map = millis();
-            if (map_position_precision == 0)
-                LOG_WARN("MQTT Map report enabled, but precision is 0");
-            if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)
-                LOG_WARN("MQTT Map report enabled, but no position available");
-            return;
-        }
 
-        // Allocate ServiceEnvelope and fill it
-        meshtastic_ServiceEnvelope *se = mqttPool.allocZeroed();
-        se->channel_id = (char *)channels.getGlobalId(channels.getPrimaryIndex()); // Use primary channel as the channel_id
-        se->gateway_id = owner.id;
-
-        // Allocate MeshPacket and fill it
-        meshtastic_MeshPacket *mp = packetPool.allocZeroed();
-        mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag;
-        mp->from = nodeDB->getNodeNum();
-        mp->to = NODENUM_BROADCAST;
-        mp->decoded.portnum = meshtastic_PortNum_MAP_REPORT_APP;
-
-        // Fill MapReport message
-        meshtastic_MapReport mapReport = meshtastic_MapReport_init_default;
-        memcpy(mapReport.long_name, owner.long_name, sizeof(owner.long_name));
-        memcpy(mapReport.short_name, owner.short_name, sizeof(owner.short_name));
-        mapReport.role = config.device.role;
-        mapReport.hw_model = owner.hw_model;
-        strncpy(mapReport.firmware_version, optstr(APP_VERSION), sizeof(mapReport.firmware_version));
-        mapReport.region = config.lora.region;
-        mapReport.modem_preset = config.lora.modem_preset;
-        mapReport.has_default_channel = channels.hasDefaultChannel();
-
-        // Set position with precision (same as in PositionModule)
-        if (map_position_precision < 32 && map_position_precision > 0) {
-            mapReport.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - map_position_precision));
-            mapReport.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - map_position_precision));
-            mapReport.latitude_i += (1 << (31 - map_position_precision));
-            mapReport.longitude_i += (1 << (31 - map_position_precision));
-        } else {
-            mapReport.latitude_i = localPosition.latitude_i;
-            mapReport.longitude_i = localPosition.longitude_i;
-        }
-        mapReport.altitude = localPosition.altitude;
-        mapReport.position_precision = map_position_precision;
-
-        mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true);
-
-        // Encode MapReport message and set it to MeshPacket in ServiceEnvelope
-        mp->decoded.payload.size = pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes),
-                                                      &meshtastic_MapReport_msg, &mapReport);
-        se->packet = mp;
-
-        size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, se);
-
-        LOG_INFO("MQTT Publish map report to %s", mapTopic.c_str());
-        publish(mapTopic.c_str(), bytes, numBytes, false);
-
-        // Release the allocated memory for ServiceEnvelope and MeshPacket
-        mqttPool.release(se);
-        packetPool.release(mp);
-
-        // Update the last report time
+    if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) {
         last_report_to_map = millis();
+        if (map_position_precision == 0)
+            LOG_WARN("MQTT Map report enabled, but precision is 0");
+        if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)
+            LOG_WARN("MQTT Map report enabled, but no position available");
+        return;
     }
+
+    // Allocate MeshPacket and fill it
+    meshtastic_MeshPacket *mp = packetPool.allocZeroed();
+    mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag;
+    mp->from = nodeDB->getNodeNum();
+    mp->to = NODENUM_BROADCAST;
+    mp->decoded.portnum = meshtastic_PortNum_MAP_REPORT_APP;
+
+    // Fill MapReport message
+    meshtastic_MapReport mapReport = meshtastic_MapReport_init_default;
+    memcpy(mapReport.long_name, owner.long_name, sizeof(owner.long_name));
+    memcpy(mapReport.short_name, owner.short_name, sizeof(owner.short_name));
+    mapReport.role = config.device.role;
+    mapReport.hw_model = owner.hw_model;
+    strncpy(mapReport.firmware_version, optstr(APP_VERSION), sizeof(mapReport.firmware_version));
+    mapReport.region = config.lora.region;
+    mapReport.modem_preset = config.lora.modem_preset;
+    mapReport.has_default_channel = channels.hasDefaultChannel();
+
+    // Set position with precision (same as in PositionModule)
+    if (map_position_precision < 32 && map_position_precision > 0) {
+        mapReport.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - map_position_precision));
+        mapReport.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - map_position_precision));
+        mapReport.latitude_i += (1 << (31 - map_position_precision));
+        mapReport.longitude_i += (1 << (31 - map_position_precision));
+    } else {
+        mapReport.latitude_i = localPosition.latitude_i;
+        mapReport.longitude_i = localPosition.longitude_i;
+    }
+    mapReport.altitude = localPosition.altitude;
+    mapReport.position_precision = map_position_precision;
+
+    mapReport.num_online_local_nodes = nodeDB->getNumOnlineMeshNodes(true);
+
+    // Encode MapReport message into the MeshPacket
+    mp->decoded.payload.size =
+        pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), &meshtastic_MapReport_msg, &mapReport);
+
+    // Encode the MeshPacket into a binary ServiceEnvelope and publish
+    const meshtastic_ServiceEnvelope se = {
+        .packet = mp,
+        .channel_id = (char *)channels.getGlobalId(channels.getPrimaryIndex()), // Use primary channel as the channel_id
+        .gateway_id = owner.id};
+    size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, &se);
+
+    LOG_INFO("MQTT Publish map report to %s", mapTopic.c_str());
+    publish(mapTopic.c_str(), bytes, numBytes, false);
+
+    // Release the allocated memory for MeshPacket
+    packetPool.release(mp);
+
+    // Update the last report time
+    last_report_to_map = millis();
 }
 
 bool MQTT::isPrivateIpAddress(const char address[])
diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h
index dc82c1a74..9db54ea4b 100644
--- a/src/mqtt/MQTT.h
+++ b/src/mqtt/MQTT.h
@@ -78,7 +78,11 @@ class MQTT : private concurrency::OSThread
     void start() { setIntervalFromNow(0); };
 
   protected:
-    PointerQueue mqttQueue;
+    struct QueueEntry {
+        std::string topic;
+        std::basic_string envBytes; // binary/pb_encode_to_bytes ServiceEnvelope
+    };
+    PointerQueue mqttQueue;
 
     int reconnectCount = 0;
 

From 960626e498395d641d98f1430d53327b1cbf69a7 Mon Sep 17 00:00:00 2001
From: Jonathan Bennett 
Date: Fri, 20 Dec 2024 17:34:02 -0600
Subject: [PATCH 29/43] Ch341 (#5474)

* Very hacky first attempt at usermod ech341

* Fixes and debug printfs

* Move to library version of libpinedio-usb

* Add spidev: ch341 option in meshtasticd config.yaml

* Only check settingsStrings on native

* Use new CH341 code

* Bump ch341 lib

* Cleanup USBHal

* Add ch341 config.d files

* Remove ch341quirk

* Bump to most recent spi-userspace driver

* Add handling for ch341 serial, pid, and vid

* Minor fixes from pio check

* Trunk

* Add include for musl compliance

* Point to upstream libch341
---
 arch/portduino/portduino.ini              |   3 +-
 bin/config-dist.yaml                      |   9 -
 bin/config.d/lora-meshstick-1262.yaml     |  11 ++
 bin/config.d/lora-pinedio-usb-sx1262.yaml |   5 +
 src/main.cpp                              |  31 ++--
 src/mesh/RadioLibInterface.cpp            |  26 +--
 src/mesh/RadioLibInterface.h              |   9 +-
 src/platform/portduino/PortduinoGlue.cpp  | 133 +++++++++------
 src/platform/portduino/PortduinoGlue.h    |   9 +-
 src/platform/portduino/USBHal.h           | 194 ++++++++++++++++++++++
 10 files changed, 326 insertions(+), 104 deletions(-)
 create mode 100644 bin/config.d/lora-meshstick-1262.yaml
 create mode 100644 bin/config.d/lora-pinedio-usb-sx1262.yaml
 create mode 100644 src/platform/portduino/USBHal.h

diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini
index bbafef4da..34f0dd8c9 100644
--- a/arch/portduino/portduino.ini
+++ b/arch/portduino/portduino.ini
@@ -26,6 +26,7 @@ lib_deps =
   ${radiolib_base.lib_deps}
   rweather/Crypto@^0.4.0
   https://github.com/lovyan03/LovyanGFX.git#1401c28a47646fe00538d487adcb2eb3c72de805
+  https://github.com/pine64/libch341-spi-userspace#8695637adeabf5abf5601d8e82cb0ba19ce9ec46
 
 build_flags =
   ${arduino_base.build_flags}
@@ -36,4 +37,4 @@ build_flags =
   -lstdc++fs
   -lbluetooth
   -lgpiod
-  -lyaml-cpp
+  -lyaml-cpp
\ No newline at end of file
diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml
index ec262536f..49de1675b 100644
--- a/bin/config-dist.yaml
+++ b/bin/config-dist.yaml
@@ -12,13 +12,6 @@ Lora:
 #  IRQ: 17
 #  Reset: 22
 
-#  Module: sx1262  # pinedio
-#  CS: 0
-#  IRQ: 10
-#  Busy: 11
-#  DIO2_AS_RF_SWITCH: true
-#  spidev: spidev0.1
-
 #  Module: RF95  # Adafruit RFM9x
 #  Reset: 25
 #  CS: 7
@@ -50,8 +43,6 @@ Lora:
 #  TXen: x  # TX and RX enable pins
 #  RXen: x
 
-#  ch341_quirk: true # Uncomment this to use the chunked SPI transfer that seems to fix the ch341
-
 #  spiSpeed: 2000000
 
 ### Set gpio chip to use in /dev/. Defaults to 0.
diff --git a/bin/config.d/lora-meshstick-1262.yaml b/bin/config.d/lora-meshstick-1262.yaml
new file mode 100644
index 000000000..3f8d6c617
--- /dev/null
+++ b/bin/config.d/lora-meshstick-1262.yaml
@@ -0,0 +1,11 @@
+Lora:
+  Module: sx1262
+  CS: 0
+  IRQ: 6
+  Reset: 2
+  Busy: 4
+  spidev: ch341
+  DIO3_TCXO_VOLTAGE: true
+#  USB_Serialnum: 12345678
+  USB_PID: 0x5512
+  USB_VID: 0x1A86
diff --git a/bin/config.d/lora-pinedio-usb-sx1262.yaml b/bin/config.d/lora-pinedio-usb-sx1262.yaml
new file mode 100644
index 000000000..6b8a9fc95
--- /dev/null
+++ b/bin/config.d/lora-pinedio-usb-sx1262.yaml
@@ -0,0 +1,5 @@
+Lora:
+  Module: sx1262
+  CS: 0 
+  IRQ: 10
+  spidev: ch341
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index eb99f279a..0409636b4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -90,6 +90,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
 #include "linux/LinuxHardwareI2C.h"
 #include "mesh/raspihttp/PiWebServer.h"
 #include "platform/portduino/PortduinoGlue.h"
+#include "platform/portduino/USBHal.h"
 #include 
 #include 
 #include 
@@ -213,6 +214,9 @@ static OSThread *powerFSMthread;
 static OSThread *ambientLightingThread;
 
 RadioInterface *rIf = NULL;
+#ifdef ARCH_PORTDUINO
+RadioLibHal *RadioLibHAL = NULL;
+#endif
 
 /**
  * Some platforms (nrf52) might provide an alterate version that suppresses calling delay from sleep.
@@ -241,7 +245,7 @@ void setup()
     // GPIO10 manages all peripheral power supplies
     // Turn on peripheral power immediately after MUC starts.
     // If some boards are turned on late, ESP32 will reset due to low voltage.
-    // ESP32-C3(Keyboard) , MAX98357A(Audio Power Amplifier) , 
+    // ESP32-C3(Keyboard) , MAX98357A(Audio Power Amplifier) ,
     // TF Card , Display backlight(AW9364DNR) , AN48841B(Trackball) , ES7210(Decoder)
     pinMode(KB_POWERON, OUTPUT);
     digitalWrite(KB_POWERON, HIGH);
@@ -420,7 +424,6 @@ void setup()
     digitalWrite(AQ_SET_PIN, HIGH);
 #endif
 
-
     // Currently only the tbeam has a PMU
     // PMU initialization needs to be placed before i2c scanning
     power = new Power();
@@ -706,12 +709,16 @@ void setup()
     pinMode(LORA_CS, OUTPUT);
     digitalWrite(LORA_CS, HIGH);
     SPI1.begin(false);
-#else                      // HW_SPI1_DEVICE
+#else  // HW_SPI1_DEVICE
     SPI.setSCK(LORA_SCK);
     SPI.setTX(LORA_MOSI);
     SPI.setRX(LORA_MISO);
     SPI.begin(false);
-#endif                     // HW_SPI1_DEVICE
+#endif // HW_SPI1_DEVICE
+#elif ARCH_PORTDUINO
+    if (settingsStrings[spidev] != "ch341") {
+        SPI.begin();
+    }
 #elif !defined(ARCH_ESP32) // ARCH_RP2040
     SPI.begin();
 #else
@@ -817,8 +824,11 @@ void setup()
     if (settingsMap[use_sx1262]) {
         if (!rIf) {
             LOG_DEBUG("Activate sx1262 radio on SPI port %s", settingsStrings[spidev].c_str());
-            LockingArduinoHal *RadioLibHAL =
-                new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC));
+            if (settingsStrings[spidev] == "ch341") {
+                RadioLibHAL = ch341Hal;
+            } else {
+                RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
+            }
             rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
                                       settingsMap[busy]);
             if (!rIf->init()) {
@@ -832,8 +842,7 @@ void setup()
     } else if (settingsMap[use_rf95]) {
         if (!rIf) {
             LOG_DEBUG("Activate rf95 radio on SPI port %s", settingsStrings[spidev].c_str());
-            LockingArduinoHal *RadioLibHAL =
-                new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC));
+            RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
             rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
                                     settingsMap[busy]);
             if (!rIf->init()) {
@@ -848,7 +857,7 @@ void setup()
     } else if (settingsMap[use_sx1280]) {
         if (!rIf) {
             LOG_DEBUG("Activate sx1280 radio on SPI port %s", settingsStrings[spidev].c_str());
-            LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
+            RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
             rIf = new SX1280Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
                                       settingsMap[busy]);
             if (!rIf->init()) {
@@ -908,7 +917,7 @@ void setup()
     } else if (settingsMap[use_sx1268]) {
         if (!rIf) {
             LOG_DEBUG("Activate sx1268 radio on SPI port %s", settingsStrings[spidev].c_str());
-            LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
+            RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
             rIf = new SX1268Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
                                       settingsMap[busy]);
             if (!rIf->init()) {
@@ -1265,4 +1274,4 @@ void loop()
         mainDelay.delay(delayMsec);
     }
 }
-#endif
+#endif
\ No newline at end of file
diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp
index 5f82a41ce..e416160eb 100644
--- a/src/mesh/RadioLibInterface.cpp
+++ b/src/mesh/RadioLibInterface.cpp
@@ -31,31 +31,7 @@ void LockingArduinoHal::spiEndTransaction()
 #if ARCH_PORTDUINO
 void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in)
 {
-    if (busy == RADIOLIB_NC) {
-        spi->transfer(out, in, len);
-    } else {
-        uint16_t offset = 0;
-
-        while (len) {
-            uint8_t block_size = (len < 20 ? len : 20);
-            spi->transfer((out != NULL ? out + offset : NULL), (in != NULL ? in + offset : NULL), block_size);
-            if (block_size == len)
-                return;
-
-            // ensure GPIO is low
-
-            uint32_t start = millis();
-            while (digitalRead(busy)) {
-                if (!Throttle::isWithinTimespanMs(start, 2000)) {
-                    LOG_ERROR("GPIO mid-transfer timeout, is it connected?");
-                    return;
-                }
-            }
-
-            offset += block_size;
-            len -= block_size;
-        }
-    }
+    spi->transfer(out, in, len);
 }
 #endif
 
diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h
index a5c2e30dd..d6101ae37 100644
--- a/src/mesh/RadioLibInterface.h
+++ b/src/mesh/RadioLibInterface.h
@@ -22,18 +22,11 @@
 class LockingArduinoHal : public ArduinoHal
 {
   public:
-    LockingArduinoHal(SPIClass &spi, SPISettings spiSettings, RADIOLIB_PIN_TYPE _busy = RADIOLIB_NC)
-        : ArduinoHal(spi, spiSettings)
-    {
-#if ARCH_PORTDUINO
-        busy = _busy;
-#endif
-    };
+    LockingArduinoHal(SPIClass &spi, SPISettings spiSettings) : ArduinoHal(spi, spiSettings){};
 
     void spiBeginTransaction() override;
     void spiEndTransaction() override;
 #if ARCH_PORTDUINO
-    RADIOLIB_PIN_TYPE busy;
     void spiTransfer(uint8_t *out, size_t len, uint8_t *in) override;
 
 #endif
diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index 750cc1630..82fd8de66 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -21,9 +21,12 @@
 #include 
 #include 
 
+#include "platform/portduino/USBHal.h"
+
 std::map settingsMap;
 std::map settingsStrings;
 std::ofstream traceFile;
+Ch341Hal *ch341Hal = nullptr;
 char *configPath = nullptr;
 char *optionMac = nullptr;
 
@@ -104,7 +107,6 @@ void getMacAddr(uint8_t *dmac)
         struct hci_dev_info di;
         di.dev_id = 0;
         bdaddr_t bdaddr;
-        char addr[18];
         int btsock;
         btsock = socket(AF_BLUETOOTH, SOCK_RAW, 1);
         if (btsock < 0) { // If anything fails, just return with the default value
@@ -201,8 +203,36 @@ void portduinoSetup()
             }
         }
     }
-
+    // if we're using a usermode driver, we need to initialize it here, to get a serial number back for mac address
     uint8_t dmac[6] = {0};
+    if (settingsStrings[spidev] == "ch341") {
+        ch341Hal = new Ch341Hal(0);
+        if (settingsStrings[lora_usb_serial_num] != "") {
+            ch341Hal->serial = settingsStrings[lora_usb_serial_num];
+        }
+        ch341Hal->vid = settingsMap[lora_usb_vid];
+        ch341Hal->pid = settingsMap[lora_usb_pid];
+        ch341Hal->init();
+        if (!ch341Hal->isInit()) {
+            std::cout << "Could not initialize CH341 device!" << std::endl;
+            exit(EXIT_FAILURE);
+        }
+        char serial[9] = {0};
+        ch341Hal->getSerialString(serial, 8);
+        std::cout << "Serial " << serial << std::endl;
+        if (strlen(serial) == 8 && settingsStrings[mac_address].length() < 12) {
+            uint8_t hash[32] = {0};
+            memcpy(hash, serial, 8);
+            crypto->hash(hash, 8);
+            dmac[0] = (hash[0] << 4) | 2;
+            dmac[1] = hash[1];
+            dmac[2] = hash[2];
+            dmac[3] = hash[3];
+            dmac[4] = hash[4];
+            dmac[5] = hash[5];
+        }
+    }
+
     getMacAddr(dmac);
     if (dmac[0] == 0 && dmac[1] == 0 && dmac[2] == 0 && dmac[3] == 0 && dmac[4] == 0 && dmac[5] == 0) {
         std::cout << "*** Blank MAC Address not allowed!" << std::endl;
@@ -225,47 +255,11 @@ void portduinoSetup()
     // Need to bind all the configured GPIO pins so they're not simulated
     // TODO: Can we do this in the for loop above?
     // TODO: If one of these fails, we should log and terminate
-    if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) {
-            settingsMap[cs] = RADIOLIB_NC;
-        }
-    }
-    if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) {
-            settingsMap[irq] = RADIOLIB_NC;
-        }
-    }
-    if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) {
-            settingsMap[busy] = RADIOLIB_NC;
-        }
-    }
-    if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) {
-            settingsMap[reset] = RADIOLIB_NC;
-        }
-    }
-    if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) {
-            settingsMap[sx126x_ant_sw] = RADIOLIB_NC;
-        }
-    }
     if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) {
         if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) {
             settingsMap[user] = RADIOLIB_NC;
         }
     }
-    if (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) {
-            settingsMap[rxen] = RADIOLIB_NC;
-        }
-    }
-    if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) {
-        if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) {
-            settingsMap[txen] = RADIOLIB_NC;
-        }
-    }
-
     if (settingsMap[displayPanel] != no_screen) {
         if (settingsMap[displayCS] > 0)
             initGPIOPin(settingsMap[displayCS], gpioChipName);
@@ -283,7 +277,43 @@ void portduinoSetup()
             initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName);
     }
 
-    if (settingsStrings[spidev] != "") {
+    // Only initialize the radio pins when dealing with real, kernel controlled SPI hardware
+    if (settingsStrings[spidev] != "" && settingsStrings[spidev] != "ch341") {
+        if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) {
+                settingsMap[cs] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) {
+                settingsMap[irq] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) {
+                settingsMap[busy] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) {
+                settingsMap[reset] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) {
+                settingsMap[sx126x_ant_sw] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) {
+                settingsMap[rxen] = RADIOLIB_NC;
+            }
+        }
+        if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) {
+            if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) {
+                settingsMap[txen] = RADIOLIB_NC;
+            }
+        }
         SPI.begin(settingsStrings[spidev].c_str());
     }
     if (settingsStrings[traceFilename] != "") {
@@ -378,17 +408,24 @@ bool loadConfig(const char *configPath)
             settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as(RADIOLIB_NC);
             settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as(RADIOLIB_NC);
             settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0);
-            settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as(false);
             settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000);
+            settingsStrings[lora_usb_serial_num] = yamlConfig["Lora"]["USB_Serialnum"].as("");
+            settingsMap[lora_usb_pid] = yamlConfig["Lora"]["USB_PID"].as(0x5512);
+            settingsMap[lora_usb_vid] = yamlConfig["Lora"]["USB_VID"].as(0x1A86);
 
-            settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as("spidev0.0");
-            if (settingsStrings[spidev].length() == 14) {
-                int x = settingsStrings[spidev].at(11) - '0';
-                int y = settingsStrings[spidev].at(13) - '0';
-                if (x >= 0 && x < 10 && y >= 0 && y < 10) {
-                    settingsMap[spidev] = x + y << 4;
-                    settingsMap[displayspidev] = settingsMap[spidev];
-                    settingsMap[touchscreenspidev] = settingsMap[spidev];
+            settingsStrings[spidev] = yamlConfig["Lora"]["spidev"].as("spidev0.0");
+            if (settingsStrings[spidev] != "ch341") {
+                settingsStrings[spidev] = "/dev/" + settingsStrings[spidev];
+                if (settingsStrings[spidev].length() == 14) {
+                    int x = settingsStrings[spidev].at(11) - '0';
+                    int y = settingsStrings[spidev].at(13) - '0';
+                    // Pretty sure this is always true
+                    if (x >= 0 && x < 10 && y >= 0 && y < 10) {
+                        // I believe this bit of weirdness is specifically for the new GUI
+                        settingsMap[spidev] = x + y << 4;
+                        settingsMap[displayspidev] = settingsMap[spidev];
+                        settingsMap[touchscreenspidev] = settingsMap[spidev];
+                    }
                 }
             }
         }
diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h
index 01541eeed..9cf9b6678 100644
--- a/src/platform/portduino/PortduinoGlue.h
+++ b/src/platform/portduino/PortduinoGlue.h
@@ -2,6 +2,8 @@
 #include 
 #include 
 
+#include "platform/portduino/USBHal.h"
+
 enum configNames {
     use_sx1262,
     cs,
@@ -13,13 +15,15 @@ enum configNames {
     rxen,
     dio2_as_rf_switch,
     dio3_tcxo_voltage,
-    ch341Quirk,
     use_rf95,
     use_sx1280,
     use_lr1110,
     use_lr1120,
     use_lr1121,
     use_sx1268,
+    lora_usb_serial_num,
+    lora_usb_pid,
+    lora_usb_vid,
     user,
     gpiochip,
     spidev,
@@ -69,8 +73,9 @@ enum { level_error, level_warn, level_info, level_debug, level_trace };
 extern std::map settingsMap;
 extern std::map settingsStrings;
 extern std::ofstream traceFile;
+extern Ch341Hal *ch341Hal;
 int initGPIOPin(int pinNum, std::string gpioChipname);
 bool loadConfig(const char *configPath);
 static bool ends_with(std::string_view str, std::string_view suffix);
 void getMacAddr(uint8_t *dmac);
-bool MAC_from_string(std::string mac_str, uint8_t *dmac);
\ No newline at end of file
+bool MAC_from_string(std::string mac_str, uint8_t *dmac);
diff --git a/src/platform/portduino/USBHal.h b/src/platform/portduino/USBHal.h
new file mode 100644
index 000000000..2b0302ced
--- /dev/null
+++ b/src/platform/portduino/USBHal.h
@@ -0,0 +1,194 @@
+#ifndef PI_HAL_LGPIO_H
+#define PI_HAL_LGPIO_H
+
+// include RadioLib
+#include "platform/portduino/PortduinoGlue.h"
+#include 
+#include 
+#include 
+#include 
+
+// include the library for Raspberry GPIO pins
+
+#define PI_RISING (PINEDIO_INT_MODE_RISING)
+#define PI_FALLING (PINEDIO_INT_MODE_FALLING)
+#define PI_INPUT (0)
+#define PI_OUTPUT (1)
+#define PI_LOW (0)
+#define PI_HIGH (1)
+
+#define CH341_PIN_CS (101)
+#define CH341_PIN_IRQ (0)
+
+// the HAL must inherit from the base RadioLibHal class
+// and implement all of its virtual methods
+class Ch341Hal : public RadioLibHal
+{
+  public:
+    // default constructor - initializes the base HAL and any needed private members
+    Ch341Hal(uint8_t spiChannel, uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0)
+        : RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING)
+    {
+    }
+
+    void getSerialString(char *_serial, size_t len)
+    {
+        if (!pinedio_is_init) {
+            return;
+        }
+        strncpy(_serial, pinedio.serial_number, len);
+    }
+
+    void init() override
+    {
+        // now the SPI
+        spiBegin();
+    }
+
+    void term() override
+    {
+        // stop the SPI
+        spiEnd();
+    }
+
+    // GPIO-related methods (pinMode, digitalWrite etc.) should check
+    // RADIOLIB_NC as an alias for non-connected pins
+    void pinMode(uint32_t pin, uint32_t mode) override
+    {
+        if (pin == RADIOLIB_NC) {
+            return;
+        }
+        pinedio_set_pin_mode(&pinedio, pin, mode);
+    }
+
+    void digitalWrite(uint32_t pin, uint32_t value) override
+    {
+        if (pin == RADIOLIB_NC) {
+            return;
+        }
+        pinedio_digital_write(&pinedio, pin, value);
+    }
+
+    uint32_t digitalRead(uint32_t pin) override
+    {
+        if (pin == RADIOLIB_NC) {
+            return 0;
+        }
+        return pinedio_digital_read(&pinedio, pin);
+    }
+
+    void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override
+    {
+        if ((interruptNum == RADIOLIB_NC)) {
+            return;
+        }
+        // LOG_DEBUG("Attach interrupt to pin %d", interruptNum);
+        pinedio_attach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum, (pinedio_int_mode)mode, interruptCb);
+    }
+
+    void detachInterrupt(uint32_t interruptNum) override
+    {
+        if ((interruptNum == RADIOLIB_NC)) {
+            return;
+        }
+        // LOG_DEBUG("Detach interrupt from pin %d", interruptNum);
+
+        pinedio_deattach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum);
+    }
+
+    void delay(unsigned long ms) override
+    {
+        if (ms == 0) {
+            sched_yield();
+            return;
+        }
+
+        usleep(ms * 1000);
+    }
+
+    void delayMicroseconds(unsigned long us) override
+    {
+        if (us == 0) {
+            sched_yield();
+            return;
+        }
+        usleep(us);
+    }
+
+    void yield() override { sched_yield(); }
+
+    unsigned long millis() override
+    {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000ULL);
+    }
+
+    unsigned long micros() override
+    {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
+    }
+
+    long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override
+    {
+        fprintf(stderr, "pulseIn for pin %u is not supported!\n", pin);
+        return 0;
+    }
+
+    void spiBegin()
+    {
+        if (!pinedio_is_init) {
+            if (serial != "") {
+                strncpy(pinedio.serial_number, serial.c_str(), 8);
+                pinedio_set_option(&pinedio, PINEDIO_OPTION_SEARCH_SERIAL, 1);
+            }
+            pinedio_set_option(&pinedio, PINEDIO_OPTION_PID, pid);
+            pinedio_set_option(&pinedio, PINEDIO_OPTION_VID, vid);
+            int32_t ret = pinedio_init(&pinedio, NULL);
+            if (ret != 0) {
+                fprintf(stderr, "Could not open SPI: %d\n", ret);
+            } else {
+                pinedio_is_init = true;
+                // LOG_INFO("USB Serial: %s", pinedio.serial_number);
+                pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0);
+                pinedio_set_pin_mode(&pinedio, 3, true);
+                pinedio_set_pin_mode(&pinedio, 5, true);
+            }
+        }
+    }
+
+    void spiBeginTransaction() {}
+
+    void spiTransfer(uint8_t *out, size_t len, uint8_t *in)
+    {
+        int32_t result = pinedio_transceive(&this->pinedio, out, in, len);
+        if (result < 0) {
+            fprintf(stderr, "Could not perform SPI transfer: %d\n", result);
+        }
+    }
+
+    void spiEndTransaction() {}
+
+    void spiEnd()
+    {
+        if (pinedio_is_init) {
+            pinedio_deinit(&pinedio);
+            pinedio_is_init = false;
+        }
+    }
+
+    bool isInit() { return pinedio_is_init; }
+
+    std::string serial = "";
+    uint32_t pid = 0x5512;
+    uint32_t vid = 0x1A86;
+
+  private:
+    // the HAL can contain any additional private members
+    pinedio_inst pinedio = {0};
+    bool pinedio_is_init = false;
+};
+
+#endif
\ No newline at end of file

From 58d80b8557a849b7d3331fb3318d521a0c6cbcab Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Fri, 20 Dec 2024 16:21:27 -0800
Subject: [PATCH 30/43] Use IPAddress.fromString for parsing private IPs
 (#5621)

---
 src/mqtt/MQTT.cpp | 103 +++++++++++++++-------------------------------
 src/mqtt/MQTT.h   |   4 --
 2 files changed, 33 insertions(+), 74 deletions(-)

diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index e40578680..ac4e9e786 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -26,6 +26,14 @@
 #include 
 #include 
 
+#include 
+#if defined(ARCH_PORTDUINO)
+#include 
+#elif !defined(ntohl)
+#include 
+#define ntohl __ntohl
+#endif
+
 MQTT *mqtt;
 
 namespace
@@ -196,6 +204,29 @@ inline void onReceiveJson(byte *payload, size_t length)
         LOG_DEBUG("JSON ignore downlink message with unsupported type");
     }
 }
+
+/// Determines if the given IPAddress is a private IPv4 address, i.e. not routable on the public internet.
+bool isPrivateIpAddress(const IPAddress &ip)
+{
+    constexpr struct {
+        uint32_t network;
+        uint32_t mask;
+    } privateCidrRanges[] = {
+        {.network = 192u << 24 | 168 << 16, .mask = 0xffff0000}, // 192.168.0.0/16
+        {.network = 172u << 24 | 16 << 16, .mask = 0xfff00000},  // 172.16.0.0/12
+        {.network = 169u << 24 | 254 << 16, .mask = 0xffff0000}, // 169.254.0.0/16
+        {.network = 10u << 24, .mask = 0xff000000},              // 10.0.0.0/8
+        {.network = 127u << 24 | 1, .mask = 0xffffffff},         // 127.0.0.1/32
+    };
+    const uint32_t addr = ntohl(ip);
+    for (const auto &cidrRange : privateCidrRanges) {
+        if (cidrRange.network == (addr & cidrRange.mask)) {
+            LOG_INFO("MQTT server on a private IP");
+            return true;
+        }
+    }
+    return false;
+}
 } // namespace
 
 void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
@@ -270,10 +301,8 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
                 moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs);
         }
 
-        isMqttServerAddressPrivate = isPrivateIpAddress(moduleConfig.mqtt.address);
-        if (isMqttServerAddressPrivate) {
-            LOG_INFO("MQTT server on a private IP");
-        }
+        IPAddress ip;
+        isMqttServerAddressPrivate = ip.fromString(moduleConfig.mqtt.address) && isPrivateIpAddress(ip);
 
 #if HAS_NETWORKING
         if (!moduleConfig.mqtt.proxy_to_client_enabled)
@@ -717,69 +746,3 @@ void MQTT::perhapsReportToMap()
     // Update the last report time
     last_report_to_map = millis();
 }
-
-bool MQTT::isPrivateIpAddress(const char address[])
-{
-    // Min. length like 10.0.0.0 (8), max like 192.168.255.255:65535 (21)
-    size_t length = strlen(address);
-    if (length < 8 || length > 21) {
-        return false;
-    }
-
-    // Ensure the address contains only digits and dots and maybe a colon.
-    // Some limited validation is done.
-    // Even if it's not a valid IP address, we will know it's not a domain.
-    bool hasColon = false;
-    int numDots = 0;
-    for (size_t i = 0; i < length; i++) {
-        if (!isdigit(address[i]) && address[i] != '.' && address[i] != ':') {
-            return false;
-        }
-
-        // Dots can't be the first character, immediately follow another dot,
-        // occur more than 3 times, or occur after a colon.
-        if (address[i] == '.') {
-            if (++numDots > 3 || i == 0 || address[i - 1] == '.' || hasColon) {
-                return false;
-            }
-        }
-        // There can only be a single colon, and it can only occur after 3 dots
-        else if (address[i] == ':') {
-            if (hasColon || numDots < 3) {
-                return false;
-            }
-
-            hasColon = true;
-        }
-    }
-
-    // Final validation for IPv4 address and port format.
-    // Note that the values of octets haven't been tested, only the address format.
-    if (numDots != 3) {
-        return false;
-    }
-
-    // Check the easy ones first.
-    if (strcmp(address, "127.0.0.1") == 0 || strncmp(address, "10.", 3) == 0 || strncmp(address, "192.168", 7) == 0 ||
-        strncmp(address, "169.254", 7) == 0) {
-        return true;
-    }
-
-    // See if it's definitely not a 172 address.
-    if (strncmp(address, "172", 3) != 0) {
-        return false;
-    }
-
-    // We know it's a 172 address, now see if the second octet is 2 digits.
-    if (address[6] != '.') {
-        return false;
-    }
-
-    // Copy the second octet into a secondary buffer we can null-terminate and parse.
-    char octet2[3];
-    strncpy(octet2, address + 4, 2);
-    octet2[2] = 0;
-
-    int octet2Num = atoi(octet2);
-    return octet2Num >= 16 && octet2Num <= 31;
-}
diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h
index 9db54ea4b..11621c55f 100644
--- a/src/mqtt/MQTT.h
+++ b/src/mqtt/MQTT.h
@@ -121,10 +121,6 @@ class MQTT : private concurrency::OSThread
     // Check if we should report unencrypted information about our node for consumption by a map
     void perhapsReportToMap();
 
-    /// Determines if the given address is a private IPv4 address, i.e. not routable on the public internet.
-    /// These are the ranges: 127.0.0.1, 10.0.0.0-10.255.255.255, 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255.
-    bool isPrivateIpAddress(const char address[]);
-
     /// Return 0 if sleep is okay, veto sleep if we are connected to pubsub server
     // int preflightSleepCb(void *unused = NULL) { return pubSub.connected() ? 1 : 0; }
 };

From 5fed679d331d7c40dc835c25cfd73bae34104c17 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Sat, 21 Dec 2024 12:24:29 +1100
Subject: [PATCH 31/43] Add detection code for INA226 (#5605)

INA226 is a high accuracy current and voltage sensor.
---
 src/detect/ScanI2C.h          |  5 +++--
 src/detect/ScanI2CTwoWire.cpp | 12 ++++++++++--
 src/main.cpp                  |  1 +
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index 7fe3aac89..2473a6573 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -63,7 +63,8 @@ class ScanI2C
         MAX30102,
         TPS65233,
         MPR121KB,
-        CGRADSENS
+        CGRADSENS,
+        INA226
     } DeviceType;
 
     // typedef uint8_t DeviceAddress;
@@ -127,4 +128,4 @@ class ScanI2C
 
   private:
     bool shouldSuppressScreen = false;
-};
\ No newline at end of file
+};
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 551b87d27..79c0deccf 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -250,8 +250,16 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
                 registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2);
                 LOG_DEBUG("Register MFG_UID: 0x%x", registerValue);
                 if (registerValue == 0x5449) {
-                    logFoundDevice("INA260", (uint8_t)addr.address);
-                    type = INA260;
+                    registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFF), 2);
+                    LOG_DEBUG("Register DIE_UID: 0x%x", registerValue);
+
+                    if (registerValue == 0x2260) {
+                        logFoundDevice("INA226", (uint8_t)addr.address);
+                        type = INA226;
+                    } else {
+                        logFoundDevice("INA260", (uint8_t)addr.address);
+                        type = INA260;
+                    }
                 } else { // Assume INA219 if INA260 ID is not found
                     logFoundDevice("INA219", (uint8_t)addr.address);
                     type = INA219;
diff --git a/src/main.cpp b/src/main.cpp
index 0409636b4..f4bb11535 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -579,6 +579,7 @@ void setup()
     scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX);
     scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085);
     scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260);
+    scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA226, meshtastic_TelemetrySensorType_INA226);
     scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219);
     scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221);
     scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048);

From 9a10907a2d79bee4c76b5618e177897bb4643b10 Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Fri, 20 Dec 2024 17:25:31 -0800
Subject: [PATCH 32/43] Check if MQTT remote IP is private (#5627)

---
 src/mqtt/MQTT.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index ac4e9e786..7b15e99f3 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -438,6 +438,9 @@ void MQTT::reconnect()
             enabled = true; // Start running background process again
             runASAP = true;
             reconnectCount = 0;
+#if !defined(ARCH_PORTDUINO)
+            isMqttServerAddressPrivate = isPrivateIpAddress(mqttClient.remoteIP());
+#endif
 
             publishNodeInfo();
             sendSubscriptions();

From df63423cdcc5b41e8a7b900a21807817c7dc488f Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Sat, 21 Dec 2024 12:26:23 +1100
Subject: [PATCH 33/43] Let RangeTest Module use Phone position if there's no
 GPS (#5623)

As reported by @Fastomat, if a user had enabled "Share Phone
Position" in the app, RangeTest did not use this position and
recorded a 0,0 lat/lon.

This change preferences GPS where avaialble, but otherwise
uses the position stored for the node in NodeDB.

fixes https://github.com/meshtastic/firmware/issues/5620
---
 src/modules/RangeTestModule.cpp | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp
index bf842ce55..c42839d97 100644
--- a/src/modules/RangeTestModule.cpp
+++ b/src/modules/RangeTestModule.cpp
@@ -155,8 +155,6 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket
             LOG_DEBUG("mp.from          %d", mp.from);
             LOG_DEBUG("mp.rx_snr        %f", mp.rx_snr);
             LOG_DEBUG("mp.hop_limit     %d", mp.hop_limit);
-            // LOG_DEBUG("mp.decoded.position.latitude_i     %d", mp.decoded.position.latitude_i); // Deprecated
-            // LOG_DEBUG("mp.decoded.position.longitude_i    %d", mp.decoded.position.longitude_i); // Deprecated
             LOG_DEBUG("---- Node Information of Received Packet (mp.from):");
             LOG_DEBUG("n->user.long_name         %s", n->user.long_name);
             LOG_DEBUG("n->user.short_name        %s", n->user.short_name);
@@ -194,8 +192,6 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
         LOG_DEBUG("mp.from          %d", mp.from);
         LOG_DEBUG("mp.rx_snr        %f", mp.rx_snr);
         LOG_DEBUG("mp.hop_limit     %d", mp.hop_limit);
-        // LOG_DEBUG("mp.decoded.position.latitude_i     %d", mp.decoded.position.latitude_i);  // Deprecated
-        // LOG_DEBUG("mp.decoded.position.longitude_i    %d", mp.decoded.position.longitude_i); // Deprecated
         LOG_DEBUG("---- Node Information of Received Packet (mp.from):");
         LOG_DEBUG("n->user.long_name         %s", n->user.long_name);
         LOG_DEBUG("n->user.short_name        %s", n->user.short_name);
@@ -265,13 +261,21 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
         fileToAppend.printf("??:??:??,"); // Time
     }
 
-    fileToAppend.printf("%d,", getFrom(&mp));                     // From
-    fileToAppend.printf("%s,", n->user.long_name);                // Long Name
-    fileToAppend.printf("%f,", n->position.latitude_i * 1e-7);    // Sender Lat
-    fileToAppend.printf("%f,", n->position.longitude_i * 1e-7);   // Sender Long
-    fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7);  // RX Lat
-    fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
-    fileToAppend.printf("%d,", gpsStatus->getAltitude());         // RX Altitude
+    fileToAppend.printf("%d,", getFrom(&mp));                   // From
+    fileToAppend.printf("%s,", n->user.long_name);              // Long Name
+    fileToAppend.printf("%f,", n->position.latitude_i * 1e-7);  // Sender Lat
+    fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
+    if (gpsStatus->getIsConnected() || config.position.fixed_position) {
+        fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7);  // RX Lat
+        fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
+        fileToAppend.printf("%d,", gpsStatus->getAltitude());         // RX Altitude
+    } else {
+        // When the phone API is in use, the node info will be updated with position
+        meshtastic_NodeInfoLite *us = nodeDB->getMeshNode(nodeDB->getNodeNum());
+        fileToAppend.printf("%f,", us->position.latitude_i * 1e-7);  // RX Lat
+        fileToAppend.printf("%f,", us->position.longitude_i * 1e-7); // RX Long
+        fileToAppend.printf("%d,", us->position.altitude);           // RX Altitude
+    }
 
     fileToAppend.printf("%f,", mp.rx_snr); // RX SNR
 
@@ -292,4 +296,4 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
 #endif
 
     return 1;
-}
+}
\ No newline at end of file

From 398d29064e770fbf1e79eec26542ec9fe9677fa4 Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Fri, 20 Dec 2024 19:06:01 -0800
Subject: [PATCH 34/43] Separate host/port before checking for private IP
 (#5630)

---
 src/mqtt/MQTT.cpp | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 7b15e99f3..c7db4672a 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -227,6 +227,16 @@ bool isPrivateIpAddress(const IPAddress &ip)
     }
     return false;
 }
+
+std::pair parseHostAndPort(std::string address, uint16_t port = 0)
+{
+    const size_t delimIndex = address.find_first_of(':');
+    if (delimIndex > 0) {
+        port = std::stoul(address.substr(delimIndex + 1, address.length()));
+        address.resize(delimIndex);
+    }
+    return std::make_pair(std::move(address), port);
+}
 } // namespace
 
 void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
@@ -302,7 +312,8 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
         }
 
         IPAddress ip;
-        isMqttServerAddressPrivate = ip.fromString(moduleConfig.mqtt.address) && isPrivateIpAddress(ip);
+        isMqttServerAddressPrivate =
+            ip.fromString(parseHostAndPort(moduleConfig.mqtt.address).first.c_str()) && isPrivateIpAddress(ip);
 
 #if HAS_NETWORKING
         if (!moduleConfig.mqtt.proxy_to_client_enabled)
@@ -418,14 +429,9 @@ void MQTT::reconnect()
         pubSub.setClient(mqttClient);
 #endif
 
-        String server = String(serverAddr);
-        int delimIndex = server.indexOf(':');
-        if (delimIndex > 0) {
-            String port = server.substring(delimIndex + 1, server.length());
-            server[delimIndex] = 0;
-            serverPort = port.toInt();
-            serverAddr = server.c_str();
-        }
+        std::pair hostAndPort = parseHostAndPort(serverAddr, serverPort);
+        serverAddr = hostAndPort.first.c_str();
+        serverPort = hostAndPort.second;
         pubSub.setServer(serverAddr, serverPort);
         pubSub.setBufferSize(512);
 

From f39a9c5083e061e5919495df3814e058b13bd263 Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Fri, 20 Dec 2024 21:42:54 -0600
Subject: [PATCH 35/43] Clean up some straggler NRF52 json (#5628)

---
 src/mqtt/MQTT.cpp | 8 +++++++-
 src/mqtt/MQTT.h   | 4 +++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index c7db4672a..74a3f357d 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -19,8 +19,10 @@
 #include 
 #endif
 #include "Default.h"
+#if !defined(ARCH_NRF52) || NRF52_USE_JSON
 #include "serialization/JSON.h"
 #include "serialization/MeshPacketSerializer.h"
+#endif
 #include 
 #include 
 #include 
@@ -119,6 +121,7 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
         router->enqueueReceivedMessage(p.release());
 }
 
+#if !defined(ARCH_NRF52) || NRF52_USE_JSON
 // returns true if this is a valid JSON envelope which we accept on downlink
 inline bool isValidJsonEnvelope(JSONObject &json)
 {
@@ -204,6 +207,7 @@ inline void onReceiveJson(byte *payload, size_t length)
         LOG_DEBUG("JSON ignore downlink message with unsupported type");
     }
 }
+#endif
 
 /// Determines if the given IPAddress is a private IPv4 address, i.e. not routable on the public internet.
 bool isPrivateIpAddress(const IPAddress &ip)
@@ -258,6 +262,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
 
     // check if this is a json payload message by comparing the topic start
     if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) {
+#if !defined(ARCH_NRF52) || NRF52_USE_JSON
         // parse the channel name from the topic string
         // the topic has been checked above for having jsonTopic prefix, so just move past it
         char *channelName = topic + jsonTopic.length();
@@ -271,6 +276,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
             return;
         }
         onReceiveJson(payload, length);
+#endif
         return;
     }
 
@@ -754,4 +760,4 @@ void MQTT::perhapsReportToMap()
 
     // Update the last report time
     last_report_to_map = millis();
-}
+}
\ No newline at end of file
diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h
index 11621c55f..81892f6f3 100644
--- a/src/mqtt/MQTT.h
+++ b/src/mqtt/MQTT.h
@@ -5,7 +5,9 @@
 #include "concurrency/OSThread.h"
 #include "mesh/Channels.h"
 #include "mesh/generated/meshtastic/mqtt.pb.h"
+#if !defined(ARCH_NRF52) || NRF52_USE_JSON
 #include "serialization/JSON.h"
+#endif
 #if HAS_WIFI
 #include 
 #if !defined(ARCH_PORTDUINO)
@@ -127,4 +129,4 @@ class MQTT : private concurrency::OSThread
 
 void mqttInit();
 
-extern MQTT *mqtt;
+extern MQTT *mqtt;
\ No newline at end of file

From d9b287880f0fc0760cbf73c35067d673c49da90a Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Sat, 21 Dec 2024 07:48:47 -0600
Subject: [PATCH 36/43] Revert "Separate host/port before checking for private
 IP (#5630)" (#5635)

This reverts commit 398d29064e770fbf1e79eec26542ec9fe9677fa4.
---
 src/mqtt/MQTT.cpp | 24 +++++++++---------------
 1 file changed, 9 insertions(+), 15 deletions(-)

diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 74a3f357d..ff7162db6 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -231,16 +231,6 @@ bool isPrivateIpAddress(const IPAddress &ip)
     }
     return false;
 }
-
-std::pair parseHostAndPort(std::string address, uint16_t port = 0)
-{
-    const size_t delimIndex = address.find_first_of(':');
-    if (delimIndex > 0) {
-        port = std::stoul(address.substr(delimIndex + 1, address.length()));
-        address.resize(delimIndex);
-    }
-    return std::make_pair(std::move(address), port);
-}
 } // namespace
 
 void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
@@ -318,8 +308,7 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
         }
 
         IPAddress ip;
-        isMqttServerAddressPrivate =
-            ip.fromString(parseHostAndPort(moduleConfig.mqtt.address).first.c_str()) && isPrivateIpAddress(ip);
+        isMqttServerAddressPrivate = ip.fromString(moduleConfig.mqtt.address) && isPrivateIpAddress(ip);
 
 #if HAS_NETWORKING
         if (!moduleConfig.mqtt.proxy_to_client_enabled)
@@ -435,9 +424,14 @@ void MQTT::reconnect()
         pubSub.setClient(mqttClient);
 #endif
 
-        std::pair hostAndPort = parseHostAndPort(serverAddr, serverPort);
-        serverAddr = hostAndPort.first.c_str();
-        serverPort = hostAndPort.second;
+        String server = String(serverAddr);
+        int delimIndex = server.indexOf(':');
+        if (delimIndex > 0) {
+            String port = server.substring(delimIndex + 1, server.length());
+            server[delimIndex] = 0;
+            serverPort = port.toInt();
+            serverAddr = server.c_str();
+        }
         pubSub.setServer(serverAddr, serverPort);
         pubSub.setBufferSize(512);
 

From fb7866fca7276a230a081d1f5caa75da0d18ceb2 Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Sat, 21 Dec 2024 07:49:25 -0600
Subject: [PATCH 37/43] Revert "Check if MQTT remote IP is private (#5627)"
 (#5636)

This reverts commit 9a10907a2d79bee4c76b5618e177897bb4643b10.
---
 src/mqtt/MQTT.cpp | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index ff7162db6..e1481d42c 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -444,9 +444,6 @@ void MQTT::reconnect()
             enabled = true; // Start running background process again
             runASAP = true;
             reconnectCount = 0;
-#if !defined(ARCH_PORTDUINO)
-            isMqttServerAddressPrivate = isPrivateIpAddress(mqttClient.remoteIP());
-#endif
 
             publishNodeInfo();
             sendSubscriptions();

From 8e6ef4ea0468c87f28371e802ba94bc34c7b9883 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= 
Date: Sat, 21 Dec 2024 14:57:01 +0100
Subject: [PATCH 38/43] add nugget and nibble boards for 38c3 (#5609)

* add nugget and nibble boards for 38c3

* mark those boards extra for now
---
 boards/esp32-s3-zero.json              | 41 ++++++++++++++++++++++++++
 src/platform/rp2xx0/architecture.h     |  2 ++
 variants/nibble_esp32/platformio.ini   |  6 ++++
 variants/nibble_esp32/variant.h        | 18 +++++++++++
 variants/nibble_rp2040/platformio.ini  | 17 +++++++++++
 variants/nibble_rp2040/variant.h       | 18 +++++++++++
 variants/nugget_s2_lora/platformio.ini |  6 ++++
 variants/nugget_s2_lora/variant.h      | 23 +++++++++++++++
 variants/nugget_s3_lora/platformio.ini |  6 ++++
 variants/nugget_s3_lora/variant.h      | 23 +++++++++++++++
 10 files changed, 160 insertions(+)
 create mode 100644 boards/esp32-s3-zero.json
 create mode 100644 variants/nibble_esp32/platformio.ini
 create mode 100644 variants/nibble_esp32/variant.h
 create mode 100644 variants/nibble_rp2040/platformio.ini
 create mode 100644 variants/nibble_rp2040/variant.h
 create mode 100644 variants/nugget_s2_lora/platformio.ini
 create mode 100644 variants/nugget_s2_lora/variant.h
 create mode 100644 variants/nugget_s3_lora/platformio.ini
 create mode 100644 variants/nugget_s3_lora/variant.h

diff --git a/boards/esp32-s3-zero.json b/boards/esp32-s3-zero.json
new file mode 100644
index 000000000..76cb34fa0
--- /dev/null
+++ b/boards/esp32-s3-zero.json
@@ -0,0 +1,41 @@
+{
+  "build": {
+    "arduino": {
+      "partitions": "default.csv",
+      "memory_type": "qio_qspi"
+    },
+    "core": "esp32",
+    "extra_flags": [
+      "-DARDUINO_ESP32S3_DEV",
+      "-DARDUINO_RUNNING_CORE=1",
+      "-DARDUINO_EVENT_RUNNING_CORE=1",
+      "-DARDUINO_USB_CDC_ON_BOOT=1",
+      "-DBOARD_HAS_PSRAM"
+    ],
+    "f_cpu": "240000000L",
+    "f_flash": "80000000L",
+    "flash_mode": "qio",
+    "psram_type": "qio",
+    "hwids": [["0x303A", "0x1001"]],
+    "mcu": "esp32s3",
+    "variant": "esp32s3"
+  },
+  "connectivity": ["wifi", "bluetooth"],
+  "debug": {
+    "default_tool": "esp-builtin",
+    "onboard_tools": ["esp-builtin"],
+    "openocd_target": "esp32s3.cfg"
+  },
+  "frameworks": ["arduino", "espidf"],
+  "platforms": ["espressif32"],
+  "name": "Espressif ESP32-S3-FH4R2 (4 MB QD, 2MB PSRAM)",
+  "upload": {
+    "flash_size": "4MB",
+    "maximum_ram_size": 327680,
+    "maximum_size": 4194304,
+    "require_upload_port": true,
+    "speed": 921600
+  },
+  "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/user-guide-devkitc-1.html",
+  "vendor": "Espressif"
+}
diff --git a/src/platform/rp2xx0/architecture.h b/src/platform/rp2xx0/architecture.h
index 8c7dfc0cd..506c19c83 100644
--- a/src/platform/rp2xx0/architecture.h
+++ b/src/platform/rp2xx0/architecture.h
@@ -33,4 +33,6 @@
 #define HW_VENDOR meshtastic_HardwareModel_RP2040_LORA
 #elif defined(RP2040_FEATHER_RFM95)
 #define HW_VENDOR meshtastic_HardwareModel_RP2040_FEATHER_RFM95
+#elif defined(PRIVATE_HW)
+#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
 #endif
\ No newline at end of file
diff --git a/variants/nibble_esp32/platformio.ini b/variants/nibble_esp32/platformio.ini
new file mode 100644
index 000000000..24d2ee2a5
--- /dev/null
+++ b/variants/nibble_esp32/platformio.ini
@@ -0,0 +1,6 @@
+[env:nibble-esp32]
+extends = esp32s3_base
+board = esp32-s3-zero
+board_level = extra
+build_flags = 
+  ${esp32_base.build_flags} -D PRIVATE_HW -I variants/nibble_esp32
\ No newline at end of file
diff --git a/variants/nibble_esp32/variant.h b/variants/nibble_esp32/variant.h
new file mode 100644
index 000000000..8ffbd9d59
--- /dev/null
+++ b/variants/nibble_esp32/variant.h
@@ -0,0 +1,18 @@
+#define I2C_SDA 11 // I2C pins for this board
+#define I2C_SCL 10
+
+#define LED_PIN 1 // If defined we will blink this LED
+
+#define BUTTON_PIN 0 // If defined, this will be used for user button presses
+#define BUTTON_NEED_PULLUP
+
+#define USE_RF95
+#define LORA_SCK 6
+#define LORA_MISO 7
+#define LORA_MOSI 8
+#define LORA_CS 9
+#define LORA_DIO0 5 // a No connect on the SX1262 module
+#define LORA_RESET 4
+
+#define LORA_DIO1 RADIOLIB_NC
+#define LORA_DIO2 RADIOLIB_NC
\ No newline at end of file
diff --git a/variants/nibble_rp2040/platformio.ini b/variants/nibble_rp2040/platformio.ini
new file mode 100644
index 000000000..ad987895f
--- /dev/null
+++ b/variants/nibble_rp2040/platformio.ini
@@ -0,0 +1,17 @@
+[env:nibble-rp2040]
+extends = rp2040_base
+board = rpipico
+board_level = extra
+upload_protocol = picotool
+
+# add our variants files to the include and src paths
+build_flags = ${rp2040_base.build_flags} 
+  -DPRIVATE_HW
+  -Ivariants/nibble_rp2040
+  -DDEBUG_RP2040_PORT=Serial
+  -DHW_SPI1_DEVICE
+  -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus"
+lib_deps =
+  ${rp2040_base.lib_deps}
+debug_build_flags = ${rp2040_base.build_flags}, -g
+debug_tool = cmsis-dap ; for e.g. Picotool
\ No newline at end of file
diff --git a/variants/nibble_rp2040/variant.h b/variants/nibble_rp2040/variant.h
new file mode 100644
index 000000000..ac105b439
--- /dev/null
+++ b/variants/nibble_rp2040/variant.h
@@ -0,0 +1,18 @@
+#define ARDUINO_ARCH_AVR
+
+#define BUTTON_PIN -1 // Pin 17 used for antenna switching via DIO4
+
+#define LED_PIN 1
+
+#define HAS_CPU_SHUTDOWN 1
+
+#define USE_RFM95
+#define LORA_SCK 10
+#define LORA_MISO 12
+#define LORA_MOSI 11
+#define LORA_CS 13
+
+#define LORA_DIO0 14
+#define LORA_RESET 15
+#define LORA_DIO1 RADIOLIB_NC
+#define LORA_DIO2 RADIOLIB_NC
diff --git a/variants/nugget_s2_lora/platformio.ini b/variants/nugget_s2_lora/platformio.ini
new file mode 100644
index 000000000..2a7ff1013
--- /dev/null
+++ b/variants/nugget_s2_lora/platformio.ini
@@ -0,0 +1,6 @@
+[env:nugget-s2-lora]
+extends = esp32s2_base
+board = lolin_s2_mini
+board_level = extra
+build_flags = 
+  ${esp32s2_base.build_flags} -D PRIVATE_HW -I variants/nugget_s2_lora
\ No newline at end of file
diff --git a/variants/nugget_s2_lora/variant.h b/variants/nugget_s2_lora/variant.h
new file mode 100644
index 000000000..2d123d603
--- /dev/null
+++ b/variants/nugget_s2_lora/variant.h
@@ -0,0 +1,23 @@
+#define I2C_SDA 34 // I2C pins for this board
+#define I2C_SCL 36
+
+#define LED_PIN 15 // If defined we will blink this LED
+
+#define HAS_NEOPIXEL                         // Enable the use of neopixels
+#define NEOPIXEL_COUNT 3                     // How many neopixels are connected
+#define NEOPIXEL_DATA 12                     // gpio pin used to send data to the neopixels
+#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use
+
+#define BUTTON_PIN 0 // If defined, this will be used for user button presses
+#define BUTTON_NEED_PULLUP
+
+#define USE_RF95
+#define LORA_SCK 6
+#define LORA_MISO 8
+#define LORA_MOSI 10
+#define LORA_CS 13
+#define LORA_DIO0 16
+#define LORA_RESET 5
+
+#define LORA_DIO1 RADIOLIB_NC
+#define LORA_DIO2 RADIOLIB_NC
\ No newline at end of file
diff --git a/variants/nugget_s3_lora/platformio.ini b/variants/nugget_s3_lora/platformio.ini
new file mode 100644
index 000000000..729a3ef23
--- /dev/null
+++ b/variants/nugget_s3_lora/platformio.ini
@@ -0,0 +1,6 @@
+[env:nugget-s3-lora]
+extends = esp32s3_base
+board = lolin_s3_mini
+board_level = extra
+build_flags = 
+  ${esp32s3_base.build_flags} -D PRIVATE_HW -I variants/nugget_s3_lora
\ No newline at end of file
diff --git a/variants/nugget_s3_lora/variant.h b/variants/nugget_s3_lora/variant.h
new file mode 100644
index 000000000..488fe4e44
--- /dev/null
+++ b/variants/nugget_s3_lora/variant.h
@@ -0,0 +1,23 @@
+#define I2C_SDA 34 // I2C pins for this board
+#define I2C_SCL 38
+
+#define LED_PIN 15 // If defined we will blink this LED
+
+#define HAS_NEOPIXEL                         // Enable the use of neopixels
+#define NEOPIXEL_COUNT 3                     // How many neopixels are connected
+#define NEOPIXEL_DATA 10                     // gpio pin used to send data to the neopixels
+#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use
+
+#define BUTTON_PIN 0 // If defined, this will be used for user button presses
+#define BUTTON_NEED_PULLUP
+
+#define USE_RF95
+#define LORA_SCK 6
+#define LORA_MISO 7
+#define LORA_MOSI 8
+#define LORA_CS 9
+#define LORA_DIO0 16 // a No connect on the SX1262 module
+#define LORA_RESET 4
+
+#define LORA_DIO1 RADIOLIB_NC
+#define LORA_DIO2 RADIOLIB_NC
\ No newline at end of file

From 1c8b1654087000a96a90eede34a62c1bd1b8f99e Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Sat, 21 Dec 2024 11:03:17 -0600
Subject: [PATCH 39/43] Add libusb to dockerfile for ch341 (#5641)

---
 Dockerfile | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index fc34fbd4c..ca216e04b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -14,7 +14,7 @@ USER root
 # trunk-ignore(hadolint/DL3008): Use latest version of packages for buildchain
 RUN apt-get update && apt-get install --no-install-recommends -y wget python3 python3-pip python3-wheel python3-venv g++ zip git \
                            ca-certificates libgpiod-dev libyaml-cpp-dev libbluetooth-dev \
-                           libulfius-dev liborcania-dev libssl-dev pkg-config && \
+                           libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev pkg-config && \
     apt-get clean && rm -rf /var/lib/apt/lists/* && mkdir /tmp/firmware
 
 RUN groupadd -g 1000 mesh && useradd -ml -u 1000 -g 1000 mesh && chown mesh:mesh /tmp/firmware
@@ -37,7 +37,7 @@ ENV TZ=Etc/UTC
 
 # trunk-ignore(terrascan/AC_DOCKER_0002): Known terrascan issue
 # trunk-ignore(hadolint/DL3008): Use latest version of packages for buildchain
-RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libulfius2.7 liborcania2.3 libssl3 && \
+RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libulfius2.7 libusb-1.0-0-dev liborcania2.3 libssl3 && \
     apt-get clean && rm -rf /var/lib/apt/lists/*
 
 RUN groupadd -g 1000 mesh && useradd -ml -u 1000 -g 1000 mesh
@@ -51,4 +51,4 @@ VOLUME /home/mesh/data
 
 CMD [ "sh",  "-cx", "./meshtasticd -d /home/mesh/data --hwid=${HWID:-$RANDOM}" ]
 
-HEALTHCHECK NONE
+HEALTHCHECK NONE
\ No newline at end of file

From f4cff334503deab564da47b6550444c8722e14f7 Mon Sep 17 00:00:00 2001
From: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
Date: Sat, 21 Dec 2024 18:13:03 +0100
Subject: [PATCH 40/43] Portduino: specify C++ version and add link pthread
 (#5642)

---
 arch/portduino/portduino.ini                | 4 +++-
 variants/portduino-buildroot/platformio.ini | 3 +--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini
index 34f0dd8c9..e4e32693c 100644
--- a/arch/portduino/portduino.ini
+++ b/arch/portduino/portduino.ini
@@ -34,7 +34,9 @@ build_flags =
   -Isrc/platform/portduino
   -DRADIOLIB_EEPROM_UNSUPPORTED
   -DPORTDUINO_LINUX_HARDWARE
+  -lpthread
   -lstdc++fs
   -lbluetooth
   -lgpiod
-  -lyaml-cpp
\ No newline at end of file
+  -lyaml-cpp
+  -std=c++17
\ No newline at end of file
diff --git a/variants/portduino-buildroot/platformio.ini b/variants/portduino-buildroot/platformio.ini
index 683a3cecc..3fbd26910 100644
--- a/variants/portduino-buildroot/platformio.ini
+++ b/variants/portduino-buildroot/platformio.ini
@@ -3,7 +3,6 @@ extends = portduino_base
 ; Optional libraries should be appended to `PLATFORMIO_BUILD_FLAGS`
 ; environment variable in the buildroot environment.
 build_flags = ${portduino_base.build_flags} -O0 -I variants/portduino-buildroot
-  -std=c++17
 board = buildroot
 lib_deps = ${portduino_base.lib_deps}
-build_src_filter = ${portduino_base.build_src_filter}
+build_src_filter = ${portduino_base.build_src_filter}
\ No newline at end of file

From 2fd5a4848af5b73b6a030df7e517e25da3778c6d Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Sat, 21 Dec 2024 12:07:20 -0800
Subject: [PATCH 41/43] Separate host:port before checking for private IP (x2)
 (#5643)

---
 src/mqtt/MQTT.cpp | 31 ++++++++++++++++++++++---------
 1 file changed, 22 insertions(+), 9 deletions(-)

diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index e1481d42c..d61e87855 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -231,6 +231,23 @@ bool isPrivateIpAddress(const IPAddress &ip)
     }
     return false;
 }
+
+// Separate a [:] string. Returns a pair containing the parsed host and port. If the port is
+// not present in the input string, or is invalid, the value of the `port` argument will be returned.
+std::pair parseHostAndPort(String server, uint16_t port = 0)
+{
+    const int delimIndex = server.indexOf(':');
+    if (delimIndex > 0) {
+        const long parsedPort = server.substring(delimIndex + 1, server.length()).toInt();
+        if (parsedPort < 1 || parsedPort > UINT16_MAX) {
+            LOG_WARN("Invalid MQTT port %d: %s", parsedPort, server.c_str());
+        } else {
+            port = parsedPort;
+        }
+        server[delimIndex] = 0;
+    }
+    return std::make_pair(std::move(server), port);
+}
 } // namespace
 
 void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
@@ -308,7 +325,8 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
         }
 
         IPAddress ip;
-        isMqttServerAddressPrivate = ip.fromString(moduleConfig.mqtt.address) && isPrivateIpAddress(ip);
+        isMqttServerAddressPrivate =
+            ip.fromString(parseHostAndPort(moduleConfig.mqtt.address).first.c_str()) && isPrivateIpAddress(ip);
 
 #if HAS_NETWORKING
         if (!moduleConfig.mqtt.proxy_to_client_enabled)
@@ -424,14 +442,9 @@ void MQTT::reconnect()
         pubSub.setClient(mqttClient);
 #endif
 
-        String server = String(serverAddr);
-        int delimIndex = server.indexOf(':');
-        if (delimIndex > 0) {
-            String port = server.substring(delimIndex + 1, server.length());
-            server[delimIndex] = 0;
-            serverPort = port.toInt();
-            serverAddr = server.c_str();
-        }
+        std::pair hostAndPort = parseHostAndPort(serverAddr, serverPort);
+        serverAddr = hostAndPort.first.c_str();
+        serverPort = hostAndPort.second;
         pubSub.setServer(serverAddr, serverPort);
         pubSub.setBufferSize(512);
 

From fa1a1fd869716fdfdc5a1efecb53f51c725b6af4 Mon Sep 17 00:00:00 2001
From: noon92 <40807970+noon92@users.noreply.github.com>
Date: Sun, 22 Dec 2024 01:04:18 +0200
Subject: [PATCH 42/43] Update Femtofox configs (#5646)

* Delete bin/config.d/femtofox/femtofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml

* Delete bin/config.d/femtofox/femtofox_EByte-E22-900MM22S.yaml

* Delete bin/config.d/femtofox/femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml

* Delete bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml

* Add files via upload

* Update and rename bin/config.d/femtofox_SX1262_XTAL.yaml to bin/config.d/femtofox/femtofox_SX1262_XTAL.yaml

* Update and rename bin/config.d/femtofox_LR1121_TCXO.yaml to bin/config.d/femtofox/femtofox_LR1121_TCXO.yaml

* Update and rename bin/config.d/femtofox_SX1262_TCXO.yaml to bin/config.d/femtofox/femtofox_SX1262_TCXO.yaml
---
 ...x_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml | 16 --------------
 .../femtofox/femtofox_EByte-E22-900MM22S.yaml | 16 --------------
 ...tofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml | 14 -------------
 .../femtofox/femtofox_LR1121_TCXO.yaml        | 20 ++++++++++++++++++
 .../femtofox/femtofox_SX1262_TCXO.yaml        | 21 +++++++++++++++++++
 .../femtofox/femtofox_SX1262_XTAL.yaml        | 21 +++++++++++++++++++
 ...eshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml | 13 ------------
 7 files changed, 62 insertions(+), 59 deletions(-)
 delete mode 100644 bin/config.d/femtofox/femtofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml
 delete mode 100644 bin/config.d/femtofox/femtofox_EByte-E22-900MM22S.yaml
 delete mode 100644 bin/config.d/femtofox/femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml
 create mode 100644 bin/config.d/femtofox/femtofox_LR1121_TCXO.yaml
 create mode 100644 bin/config.d/femtofox/femtofox_SX1262_TCXO.yaml
 create mode 100644 bin/config.d/femtofox/femtofox_SX1262_XTAL.yaml
 delete mode 100644 bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml

diff --git a/bin/config.d/femtofox/femtofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml b/bin/config.d/femtofox/femtofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml
deleted file mode 100644
index 6c88b1eb2..000000000
--- a/bin/config.d/femtofox/femtofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
----
-Lora:
-## Ebyte E22-900M30S, E22-900M22S with no external RF switching setup
-## Will work with any module without RF switching, and with TCXO
-  Module: sx1262
-  gpiochip: 1 # subtract 32 from the gpio numbers
-  DIO2_AS_RF_SWITCH: true
-  DIO3_TCXO_VOLTAGE: true
-  CS: 16 #pin6 / GPIO48 1C0
-  IRQ: 23  #pin17 / GPIO55 1C7
-  Busy: 22 #pin16 / GPIO54 1C6
-  Reset: 25 #pin13 / GPIO57 1D1
-  RXen: 24 #pin12 / GPIO56 1D0
-  #TXen: bridge to DIO2 on E22 module
-  spidev: spidev0.0
-  spiSpeed: 2000000
\ No newline at end of file
diff --git a/bin/config.d/femtofox/femtofox_EByte-E22-900MM22S.yaml b/bin/config.d/femtofox/femtofox_EByte-E22-900MM22S.yaml
deleted file mode 100644
index 451d5d3f4..000000000
--- a/bin/config.d/femtofox/femtofox_EByte-E22-900MM22S.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
----
-Lora:
-## Ebyte E22-900MM22S with no external RF switching setup
-## Will work with any module without RF switching and no TCXO
-  Module: sx1262  
-  gpiochip: 1 # subtract 32 from the gpio numbers
-  DIO2_AS_RF_SWITCH: true
-  DIO3_TCXO_VOLTAGE: true
-  CS: 16 #pin6 / GPIO48 1C0
-  IRQ: 23  #pin17 / GPIO55 1C7
-  Busy: 22 #pin16 / GPIO54 1C6
-  Reset: 25 #pin13 / GPIO57 1D1 
-  RXen: 24 #pin12 / GPIO56 1D0
-  #TXen: bridge to DIO2 on E22 module
-  spidev: spidev0.0
-  spiSpeed: 2000000
\ No newline at end of file
diff --git a/bin/config.d/femtofox/femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml b/bin/config.d/femtofox/femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml
deleted file mode 100644
index d5f02b42c..000000000
--- a/bin/config.d/femtofox/femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
----
-Lora:
-## Heltec HT-RA62, Seeed WIO SX1262
-## Will work with any module with automatic RF switching, and with TCXO
-  Module: sx1262
-  gpiochip: 1 # subtract 32 from the gpio numbers
-  DIO2_AS_RF_SWITCH: true
-  DIO3_TCXO_VOLTAGE: true
-  CS: 16 #pin6 (GPIO pin 48 1C0)
-  IRQ: 23  #pin17 (GPIO pin 55 1C7)
-  Reset: 25 #pin13 (GPIO pin 57 1D1)
-  Busy: 22 #pin16 (GPIO pin 54 1C6)
-  spidev: spidev0.0 #pins are (CS=6, CLK=7, MOSI=8, MISO=9)
-  spiSpeed: 2000000
\ No newline at end of file
diff --git a/bin/config.d/femtofox/femtofox_LR1121_TCXO.yaml b/bin/config.d/femtofox/femtofox_LR1121_TCXO.yaml
new file mode 100644
index 000000000..7aa860f61
--- /dev/null
+++ b/bin/config.d/femtofox/femtofox_LR1121_TCXO.yaml
@@ -0,0 +1,20 @@
+---
+Lora:
+## Ebyte E80-900M22S
+## This is a bit experimental
+## 
+##
+  Module: lr1121
+  gpiochip: 1 # subtract 32 from the gpio numbers
+  DIO3_TCXO_VOLTAGE: 1.8
+  CS: 16 #pin6 / GPIO48 1C0
+  IRQ: 23  #pin17 / GPIO55 1C7
+  Busy: 22 #pin16 / GPIO54 1C6
+  Reset: 25 #pin13 / GPIO57 1D1
+
+
+  spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19)
+  spiSpeed: 2000000
+  
+General:
+  MACAddressSource: eth0
diff --git a/bin/config.d/femtofox/femtofox_SX1262_TCXO.yaml b/bin/config.d/femtofox/femtofox_SX1262_TCXO.yaml
new file mode 100644
index 000000000..a4dec870a
--- /dev/null
+++ b/bin/config.d/femtofox/femtofox_SX1262_TCXO.yaml
@@ -0,0 +1,21 @@
+---
+Lora:
+## Ebyte E22-900M30S, E22-900M22S with or without external RF switching setup
+## HT-RA62 (Has internal switching, but whatever)
+## Seeed WIO SX1262 (already has TXEN-DIO2 link, but needs RXEN)
+## Will work with any module with or without RF switching, and with TCXO
+  Module: sx1262
+  gpiochip: 1 # subtract 32 from the gpio numbers
+  DIO2_AS_RF_SWITCH: true
+  DIO3_TCXO_VOLTAGE: true
+  CS: 16 #pin6 / GPIO48 1C0
+  IRQ: 23  #pin17 / GPIO55 1C7
+  Busy: 22 #pin16 / GPIO54 1C6
+  Reset: 25 #pin13 / GPIO57 1D1
+  RXen: 24 #pin12 / GPIO56 1D0 # Not strictly needed for auto-switching, but why complicate things?
+#  TXen: bridge to DIO2 on E22 module
+  spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19)
+  spiSpeed: 2000000
+  
+General:
+  MACAddressSource: eth0
diff --git a/bin/config.d/femtofox/femtofox_SX1262_XTAL.yaml b/bin/config.d/femtofox/femtofox_SX1262_XTAL.yaml
new file mode 100644
index 000000000..6b956f3e3
--- /dev/null
+++ b/bin/config.d/femtofox/femtofox_SX1262_XTAL.yaml
@@ -0,0 +1,21 @@
+---
+Lora:
+## Ebyte E22-900MM22S with no external RF switching setup
+## Waveshare SX126X XXXM, AI Thinker RA-01SH
+## Will work with any module with or without RF switching and no TCXO
+
+  Module: sx1262  
+  gpiochip: 1 # subtract 32 from the gpio numbers
+  DIO2_AS_RF_SWITCH: true
+  DIO3_TCXO_VOLTAGE: false
+  CS: 16 #pin6 / GPIO48 1C0
+  IRQ: 23  #pin17 / GPIO55 1C7
+  Busy: 22 #pin16 / GPIO54 1C6
+  Reset: 25 #pin13 / GPIO57 1D1 
+  RXen: 24 #pin12 / GPIO56 1D0 # Not strictly needed for auto-switching, but why complicate things?
+#  TXen: bridge to DIO2 on E22 module
+  spidev: spidev0.0 #pins are (CS=16, CLK=17, MOSI=18, MISO=19)
+  spiSpeed: 2000000
+
+General:
+  MACAddressSource: eth0
diff --git a/bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml b/bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml
deleted file mode 100644
index 23834adec..000000000
--- a/bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-Lora:
-## Waveshare SX126X XXXM, AI Thinker RA-01SH
-## Will work with any module with automatic RF switching, and with no TCXO
-  Module: sx1262  
-  gpiochip: 1 # subtract 32 from the gpio numbers
-  DIO2_AS_RF_SWITCH: true
-  CS: 16 #pin6 (GPIO pin 48 1C0)
-  IRQ: 23  #pin17 (GPIO pin 55 1C7)
-  Reset: 25 #pin13 (GPIO pin 57 1D1)
-  Busy: 22 #pin16 (GPIO pin 54 1C6)
-  spidev: spidev0.0 #pins are (CS=6, CLK=7, MOSI=8, MISO=9)
-  spiSpeed: 2000000

From 80fc0f2bdafe2cca248afec92589bad341143d75 Mon Sep 17 00:00:00 2001
From: nebman 
Date: Sun, 22 Dec 2024 05:02:50 +0100
Subject: [PATCH 43/43] Detect charging status by measuring current flow with
 configured INA battery sensor (#5271)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* INA219 charging detection

minimal implementation: if there is a configured INA219 sensor for battery monitoring we can take the current flow across the shunt resistor to know if we are charging the battery - negative milliamps indicate charging

* Update Power.cpp

added comments and 2 extra defines to disable and swap detection direction

* Update Power.cpp

fix disabled case

* move getCurrentMa() to new CurrentSensor class

* INA219 charging detection

minimal implementation: if there is a configured INA219 sensor for battery monitoring we can take the current flow across the shunt resistor to know if we are charging the battery - negative milliamps indicate charging

* Update Power.cpp

added comments and 2 extra defines to disable and swap detection direction

* Update Power.cpp

fix disabled case

* move getCurrentMa() to new CurrentSensor class

* add INA3221 charging detection

* RP2040: Update core; add mDNS support (#5355)

* Update arduino-pico core

* RP2040: Add mDNS support

* SimpleMDNS `begin` now returns a bool

* Add `-g` option to `debug_build_flags` to link files for gdb

* RAK11310 needs old platform as well

* Change defines to specific architecture

* Core version 4.2.1 is out

* Add sudo to apt-get commands for Raspbian Build (#5364)

Without sudo, inadequate permissions to runs the commands meant
the build was failing.

* Typo fix in build_raspbian.yml (#5365)

s/sudp/sudo :(:(:(

* Rework some things

* Trunk

* Separate littlefs bundle

* version tags

* Diag

* Add littlefswebui

* Bug fixed in ExternalNotificationModule (#5375)

While `nagging` setExternalState wasn't written to Buzzer & Vibra so output was never toggled.

Possible fix for #5348

* Cleanup static files from bad Web UI bundle on 2.5.13 release (#5376)

* Cleanup static files from bad Web UI bundle on 2.5.13 release

* Check existence first

* Esp32 is the only one we care about

* Move some actions to after `startTransmit()` (#5383)

To minimize the time between channel scan and actual transmit

* [create-pull-request] automated change (#5380)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Allows all 3 PKI keys to be added to userPrefs.h (#4969) and a tool. (#5368)

* more userPrefs.h

Added PKI Admin keys to userPrefs.h

* Update userPrefs.h

Allows all 3 PKI keys to be added to userPrefs.h (#4969)

* Update NodeDB.cpp

Trunk

* Update userPrefs.h

Changed wording

* Create base64_to_hex.py

A little tool for converting base64 PKI Keys to decoded byte that userPrefs.h can understand.

* more userPrefs.h

Added PKI Admin keys to userPrefs.h

* Update userPrefs.h

Allows all 3 PKI keys to be added to userPrefs.h (#4969)

* Update NodeDB.cpp

Trunk

* Update userPrefs.h

Changed wording

* Create base64_to_hex.py

A little tool for converting base64 PKI Keys to decoded byte that userPrefs.h can understand.

* [create-pull-request] automated change (#5388)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* add smiley emoji (#5391)

* add smiley emoji

* clang-formatted

* Anable trace route function on wismeshtap platform (#5389)

* fix 'symbal' typo (#5395)

* [create-pull-request] automated change (#5399)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* /api/v1/fromradio: add OPTIONS handler for CORS. (#5386)

This avoids hitting the 404 Not Found handler, which breaks connection
keep-alive, so this change fixes a big performance regression for Web Client in
Chrome: https://github.com/meshtastic/firmware/issues/5385

Tested on Heltec V3.

Co-authored-by: Ben Meadors 

* Make heart emoji usable (#5403)

* Create a specific hw_model for WisMesh Tap (#5400)

* Create a specific hw_model for WisMesh Tap

* Trunk

* HAS_ETHERNET

* Remove it altogether

* Don't need these either

* Fix RTC time injection and consolidate position logic (#5396)

* Fix RTC time injection and consolidate position logic

* Comment out unused var warning

* Backerds

* Update arduino-pico core to fix sporadic hangs (#5406)

* Update platform-raspberrypi also (#5407)

* Update arduino-pico core to fix sporadic hangs

* Update platform-raspberrypi also

* --web added to device-install(.sh/.bat) (#5405)

* Add --web

* Update device-install.bat

Forgot a "-" a few places.

---------

Co-authored-by: Ben Meadors 

* add GPS in indicator board (#5411)

* Fixed NMEA sentence issue in CalTopo as well as bug with no printing all of the nodes (#5412)

* --web littlefswebui-* typo fix (#5416)

* Add --web

* Update device-install.bat

Forgot a "-" a few places.

* Typo fix.

* Typo fix

---------

Co-authored-by: Ben Meadors 
Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com>

* Temporarily disable MDNS when MQTT is enabled (#5418)

Leads to a panic

* Check for OkToMqtt flag presence before uplinking to MQTT (#5413)

* Check for oktomqtt flag presence before uplinking to MQTT

* Move to mqtt->onSend

* Temetry can respond to want-response for LocalStats variant (#5414)

* Seems like the last DIY board that's not "extra" (#5420)

* Cherry pick tdeck fixes (#5422)

* Try-fix (workaround) T-Deck audio crash

* set T-Deck audio to unused 48 (mem mclk)

* swap mclk to gpio 21

* dreamcatcher: assign GPIO44 to audio mclk

---------

Co-authored-by: mverch67 

* add canned message and keyboard in indicator board (#5410)

* add canned message and keyboard in indicator board

* Added virtual keyboard macro and enabled for Indicator

* Cleanup macros by applying USE_VIRTUAL_KEYBOARD and DISPLAY_CLOCK_FRAME

---------

Co-authored-by: Ben Meadors 

* Update build-native.sh (#5415)

* Update build-native.sh

Device-install.sh and device-update.sh are not used on native platform, skip copying to release directory after build and copy native-install.sh and native-run.sh instead.

* Update build-native.sh

Skip native-run.sh copy

* Cleans up visibility in GPS.h (#5426)

Signed-off-by: Christopher Hoover 

* Fix admin key loading from userPrefs.h (#5417)

* Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28

* Merge PR #420

* Fixed double and missing Default class.

* Use correct format specifier and fixed typo.

* Removed duplicate code.

* Fix error: #if with no expression

* Fix warning: extra tokens at end of #endif directive.

* Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board.

* Fix deprecated macros.

* Set RP2040 in dormant mode when deep sleep is triggered.

* Fix array out of bounds read.

* Admin key count needs to be set otherwise the key will be zero loaded after reset.

* Don't reset the admin key size when loading defaults. Preserve an existing key in config if possible.

* Remove log spam when reading INA voltage sensor.

* Remove static declaration for admin keys from userPrefs.h. Load hard coded admin keys in case config file has empty slots.

* Removed newlines from log.

---------

Co-authored-by: Ben Meadors 
Co-authored-by: Thomas Göttgens 

* try to detect dfrobot station to tell it apart from an ublox gps. (#5393)

* Remove BMA-423 and STK8X by default (#5429)

* Remove BMA-423 by default

* STK

* Wrong macro

* Helps if you include the file

* [create-pull-request] automated change (#5431)

Co-authored-by: caveman99 <25002+caveman99@users.noreply.github.com>

* Support for the ClimateGuard RadSens Geiger-Muller tube (#5425)

* fixes https://github.com/meshtastic/firmware/issues/5434 (#5435)

* update libpax
* fix interval init

* Fix memory leaks by adding missing `free()` calls before early returns in `MQTT::onReceive` (#5439)

This fix addresses memory leaks in the `MQTT::onReceive` function by ensuring that dynamically allocated resources (`e.channel_id`, `e.gateway_id` and `e.packet`) are properly freed before each early return. Previously, these resources were only freed at the end of the function, leaving them unhandled in certain exit paths. Adding the missing `free()` calls prevents memory leaks and ensures proper resource cleanup in all scenarios.

* Removing 1.0 legacy boards from releases and completely removing Heltec wireless capsule from support (#5436)

Co-authored-by: Tom Fifield 

* A second round of cleanup on GPS.h. (#5433)

* Move yet more stuff out of GPS.h and into file scope.

* Protect code macros from eating semicolons.

* Remove unused (and unimplemented) getDOPString.

* clang-format with project style file on affected files.

Signed-off-by: Christopher Hoover 

* enable MQTT with TLS on RPi picow (#5442)

Co-authored-by: Ben Meadors 

* Don't powersave on Wifi (#5443)

* Don't go into light sleep with wifi enabled

* Move

* Trunk

* Revert "Seems like the last DIY board that's not "extra" (#5420)" (#5446)

This reverts commit e6fb6b115aebb12b31fb93ed9d1508a6109b2f03.

* Actually gunzip all the files when building a .deb (#5449)

* [create-pull-request] automated change (#5457)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Cleanup i2c scan logs and macro to save some bytes and remain consistent (#5455)

* Cleanup i2c scan logs and macro to save some bytes and remain consistent

* Functions are better than macros

* Exclude i2c scan for STM32

* Useless log

* Clean up some inline functions (#5454)

* Use isWithinTimespanMs to avoid refererence to NodeDb instance inside of NodeDb (#5453)

* fix cors for meshtasticd to allow use of cross origin clients (#5463)

* Remove ATECC crypto chip placeholder code (#5461)

* GPS.h cleanups round 3. (#5447)

* GPS.h cleanups round 3.

No effective behavior change.

Protected members can be private so make it so.  (Supporting
subclasses needs a lot more work.)

Moves uBloxGnssModelInfo into file scope.

Moves uBloxProtocolVersion into uBloxGnssModelInfo.

Moves baud rate arrays into file scope.

Removes unused/ unimplemented powerStateToString.

Signed-off-by: Christopher Hoover 

* Trunk Format.

---------

Signed-off-by: Christopher Hoover 
Co-authored-by: Tom Fifield 

* Fix ukrainian fonts (#5468)

* FIX:  rollback to !4624

* UPDATE: new 16 and 24 UA Fonts and fixes

* fix: Solve the lightsleep crash problem via disable  lightsleep for indicator. (#5470)

* Trunk

* Warnings and log cleanup (#5472)

* Don't log if keyboard not found

* Signed comparison issue

* [create-pull-request] automated change (#5475)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Adds libusb dev package to Raspbian build steps (#5480)

* [create-pull-request] automated change (#5478)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Portduino fixes (#5479)

* Set config.yaml defaults even if General is missing

* Unsigned values should get %u in logging

* Update arduino-pico core and remove MDNS restriction (#5483)

* Update xiao_esp32 fully support L67K (#5488)

L67K module hardware changed

* Convert userprefs to a json file instead of header file which has to be included everywhere (#5471)

* WIP

* Got string quoting and macro expansion working

* Need the placeholder

* Cleanup

* Missed a user prefs reference

* Update jsonc

* SimRadio: clean-up and emulate collisions (#5487)

* Clean up SimRadio and don't let it use PKC

* Add collision emulation for SimRadio

* Add stats from SimRadio to LocalStats

* Make emulating collisions optional

* add nodeId to nodeinfo update log lines and removed redundant nodeinfo update log line (#5493)

* Refact the macro definition of GPS initialization of GPSDEFAULTD_NOT_PRESENT and added  seeeed Indicator to this sequence (#5494)

Co-authored-by: Ben Meadors 

* Extend Length of Source and Destination Node IDs Logged (#5492)

* show 8 chars for logging source and destination ids

* extend legnth of source and destination nodes in log

* Added femtofox configs (#5477)

* added femtofox configs

* Rename bin/config.d/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml to bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml

* moved femtofox configs to subdir

* [Add] LR1110, LR1120 and LR1121 to linux native Portduino (#5496)

* Update main.cpp

* Update PortduinoGlue.h

* Update PortduinoGlue.cpp

* Update PortduinoGlue.cpp

* Update PortduinoGlue.cpp

* Update main.cpp

* [create-pull-request] automated change (#5500)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Fix minor typos in package workflows (#5505)

* Don't use channel index for encrypted packet (#5509)

* Don't use channel index for encrypted packet

* Remove assert in `getKey`, set invalid key length
So encrypting will fail without reboot

* Reset channel to 0 when unable to encrypt
Such that the NAK doesn't use the failing channel hash

* Always Announce MDNS meshtastic service (#5503)

* refactor server api port into define

* always announce MDNS meshtastic service

* fix nodeDB erase loop when free mem returns invalid value (0, -1). (#5519)

Co-authored-by: mverch67 

* Add heltec capsule back

* Revert "Add heltec capsule back"

This reverts commit fc16d9342116235fa86cf6ac163b17125bb4b50e.

* Lets try this again minus device ui

* Add popular nrf52 pro micro to the builds (#5523)

* Add MACAddress to config.yaml (#5506)

* Add MACAddress to config.yaml

* Better error handling on native, including failing to launch with blank MAC Address and real hardware.

* Re-arrange Mac Address handling and add MACAddressSource

* Bump portduino to remove macaddr function there

---------

Co-authored-by: Ben Meadors 

* Configure Seeed Xiao S3 RX enable pin (#5517)

* Create OpenWRT_One_mikroBUS_sx1262.yaml (#5529)

* tlora_v2_1_16: Unset BUTTON_PIN and BUTTON_NEED_PULLUP (#5535)

Unset BUTTON_PIN and BUTTON_NEED_PULLUP as the board ships without a user button.

Devices and users expecting a button on GPIO12 have to set [GPIO for user button](https://meshtastic.org/docs/configuration/radio/device/#gpio-for-user-button) to 12 (or any GPIO pin the momentary switch was connected to) to restore functionality.

Signed-off-by: Andrew Yong 

* [create-pull-request] automated change (#5530)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Fix detection for some RadSens hardware versions (#5542)

Co-authored-by: Jake-B 

* Initialize dmac array to nulls (#5538)

* Initialize dmac array to nulls

* Use std::cout for print before console is init.

* Update OpenWRT_One_mikroBUS_sx1262.yaml (#5544)

* Add portduino-buildroot variant (#5540)

* Add portduino-buildroot variant

* Update platform-native for platform-buildroot

* portduino-buildroot: Define c standard (#5547)

* Portduino: Move meshtasticd/web out of /usr/share/doc/ (#5548)

* Portduino: fix transitional symlinks (#5550)

* Windows Support - Trunk and Platformio (#5397) (#5518)

* Add support for GPG
* Add usb device support
* Add trunk.io to devcontainer
* Trunk things
* trunk fmt
* formatting
* fix trivy/DS002, checkov/CKV_DOCKER_3
* hide docker extension popup
* fix trivy/DS026, checkov/CKV_DOCKER_2

Co-authored-by: Kalle Lilja <15094562+ThatKalle@users.noreply.github.com>

* Synch minor changes from TFT branch (#5520)

* Synch minor changes from TFT branch

Includes:
* New nordicnrf52 minor version (10.5.0 --> 10.6.0)
* Optimisations for T_DECK
* preparation for MESH_TAB
* add ext notification module to portduino

---------

Co-authored-by: mverch67 

* DIO3_TCXO_VOLTAGE in config.yaml can now take an exact voltage (#5558)

* Support TLORA_V3.0 (#5563)

- Support TLORA_V3.0. Update of the legendary 2.1_1.6.1 with solar charger, TCXO and IPEX connector.
- 'extra' some short-lived EOL intermediate boards in that range. If possible use T3S3 instead of all of these!
- update trunk to latest version

* Create OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml (#5564)

* Portduino: fix setting hwId via argument (#5565)

* INA219 charging detection

minimal implementation: if there is a configured INA219 sensor for battery monitoring we can take the current flow across the shunt resistor to know if we are charging the battery - negative milliamps indicate charging

* Update Power.cpp

added comments and 2 extra defines to disable and swap detection direction

* Trunk Fixes

* Add INA226 support

---------

Signed-off-by: Christopher Hoover 
Signed-off-by: Andrew Yong 
Co-authored-by: Ben Meadors 
Co-authored-by: Jonathan Bennett 
Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
Co-authored-by: Tom Fifield 
Co-authored-by: Michael Gjelsø <36234524+gjelsoe@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
Co-authored-by: jcyrio <50239349+jcyrio@users.noreply.github.com>
Co-authored-by: Daniel.Cao <144674500+DanielCao0@users.noreply.github.com>
Co-authored-by: Catalin Patulea 
Co-authored-by: dylanli 
Co-authored-by: mverch67 
Co-authored-by: madeofstown <33820964+madeofstown@users.noreply.github.com>
Co-authored-by: Christopher Hoover 
Co-authored-by: Mictronics 
Co-authored-by: Thomas Göttgens 
Co-authored-by: caveman99 <25002+caveman99@users.noreply.github.com>
Co-authored-by: jake-b <1012393+jake-b@users.noreply.github.com>
Co-authored-by: César de Tassis Filho 
Co-authored-by: Tomas Dubec 
Co-authored-by: Liam Cottle 
Co-authored-by: panaceya 
Co-authored-by: virgil 
Co-authored-by: Robert 
Co-authored-by: noon92 <40807970+noon92@users.noreply.github.com>
Co-authored-by: Mark Trevor Birss 
Co-authored-by: broglep <20624281+broglep@users.noreply.github.com>
Co-authored-by: Matthias Granberry 
Co-authored-by: Andrew Yong 
Co-authored-by: Jake-B 
Co-authored-by: Austin 
Co-authored-by: Kalle Lilja <15094562+ThatKalle@users.noreply.github.com>
---
 platformio.ini                                |  3 +-
 src/Power.cpp                                 | 39 ++++++++++++-
 src/modules/Telemetry/PowerTelemetry.cpp      |  6 +-
 src/modules/Telemetry/Sensor/CurrentSensor.h  | 13 +++++
 src/modules/Telemetry/Sensor/INA219Sensor.cpp |  5 ++
 src/modules/Telemetry/Sensor/INA219Sensor.h   |  4 +-
 src/modules/Telemetry/Sensor/INA226Sensor.cpp | 58 +++++++++++++++++++
 src/modules/Telemetry/Sensor/INA226Sensor.h   | 30 ++++++++++
 .../Telemetry/Sensor/INA3221Sensor.cpp        |  5 ++
 src/modules/Telemetry/Sensor/INA3221Sensor.h  |  4 +-
 src/power.h                                   |  6 +-
 11 files changed, 165 insertions(+), 8 deletions(-)
 create mode 100644 src/modules/Telemetry/Sensor/CurrentSensor.h
 create mode 100644 src/modules/Telemetry/Sensor/INA226Sensor.cpp
 create mode 100644 src/modules/Telemetry/Sensor/INA226Sensor.h

diff --git a/platformio.ini b/platformio.ini
index 08d21665f..41f1ca764 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -160,4 +160,5 @@ lib_deps =
 	https://github.com/KodinLanewave/INA3221@1.0.1
 	mprograms/QMC5883LCompass@1.2.3
 	dfrobot/DFRobot_RTU@1.0.3
-	https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d
\ No newline at end of file
+	https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d
+        robtillaart/INA226@0.6.0
diff --git a/src/Power.cpp b/src/Power.cpp
index a354b74e2..ae0908ec6 100644
--- a/src/Power.cpp
+++ b/src/Power.cpp
@@ -72,8 +72,9 @@ static const uint8_t ext_chrg_detect_value = EXT_CHRG_DETECT_VALUE;
 #endif
 
 #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
-INA260Sensor ina260Sensor;
 INA219Sensor ina219Sensor;
+INA226Sensor ina226Sensor;
+INA260Sensor ina260Sensor;
 INA3221Sensor ina3221Sensor;
 #endif
 
@@ -413,7 +414,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
 #ifdef EXT_CHRG_DETECT
         return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
 #else
+#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) &&           \
+    !defined(DISABLE_INA_CHARGING_DETECTION)
+        if (hasINA()) {
+            // get current flow from INA sensor - negative value means power flowing into the battery
+            // default assuming  BATTERY+  <--> INA_VIN+ <--> SHUNT RESISTOR <--> INA_VIN- <--> LOAD
+            LOG_DEBUG("Using INA on I2C addr 0x%x for charging detection", config.power.device_battery_ina_address);
+#if defined(INA_CHARGING_DETECTION_INVERT)
+            return getINACurrent() > 0;
+#else
+            return getINACurrent() < 0;
+#endif
+        }
         return isBatteryConnect() && isVbusIn();
+#endif
 #endif
     }
 
@@ -450,6 +464,9 @@ class AnalogBatteryLevel : public HasBatteryLevel
     {
         if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
             return ina219Sensor.getBusVoltageMv();
+        } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first ==
+                   config.power.device_battery_ina_address) {
+            return ina226Sensor.getBusVoltageMv();
         } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
                    config.power.device_battery_ina_address) {
             return ina260Sensor.getBusVoltageMv();
@@ -460,6 +477,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
         return 0;
     }
 
+    int16_t getINACurrent()
+    {
+        if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
+            return ina219Sensor.getCurrentMa();
+        } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first ==
+                   config.power.device_battery_ina_address) {
+            return ina226Sensor.getCurrentMa();
+        } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first ==
+                   config.power.device_battery_ina_address) {
+            return ina3221Sensor.getCurrentMa();
+        }
+        return 0;
+    }
+
     bool hasINA()
     {
         if (!config.power.device_battery_ina_address) {
@@ -469,6 +500,10 @@ class AnalogBatteryLevel : public HasBatteryLevel
             if (!ina219Sensor.isInitialized())
                 return ina219Sensor.runOnce() > 0;
             return ina219Sensor.isRunning();
+        } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first ==
+                   config.power.device_battery_ina_address) {
+            if (!ina226Sensor.isInitialized())
+                return ina226Sensor.runOnce() > 0;
         } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
                    config.power.device_battery_ina_address) {
             if (!ina260Sensor.isInitialized())
@@ -1154,4 +1189,4 @@ bool Power::lipoInit()
 {
     return false;
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp
index 367643849..10133fca5 100644
--- a/src/modules/Telemetry/PowerTelemetry.cpp
+++ b/src/modules/Telemetry/PowerTelemetry.cpp
@@ -56,6 +56,8 @@ int32_t PowerTelemetryModule::runOnce()
             // therefore, we should only enable the sensor loop if measurement is also enabled
             if (ina219Sensor.hasSensor() && !ina219Sensor.isInitialized())
                 result = ina219Sensor.runOnce();
+            if (ina226Sensor.hasSensor() && !ina226Sensor.isInitialized())
+                result = ina226Sensor.runOnce();
             if (ina260Sensor.hasSensor() && !ina260Sensor.isInitialized())
                 result = ina260Sensor.runOnce();
             if (ina3221Sensor.hasSensor() && !ina3221Sensor.isInitialized())
@@ -170,6 +172,8 @@ bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m)
 #if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
     if (ina219Sensor.hasSensor())
         valid = ina219Sensor.getMetrics(m);
+    if (ina226Sensor.hasSensor())
+        valid = ina226Sensor.getMetrics(m);
     if (ina260Sensor.hasSensor())
         valid = ina260Sensor.getMetrics(m);
     if (ina3221Sensor.hasSensor())
@@ -253,4 +257,4 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
     return false;
 }
 
-#endif
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/CurrentSensor.h b/src/modules/Telemetry/Sensor/CurrentSensor.h
new file mode 100644
index 000000000..9827a9aa4
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/CurrentSensor.h
@@ -0,0 +1,13 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#pragma once
+
+class CurrentSensor
+{
+  public:
+    virtual int16_t getCurrentMa() = 0;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.cpp b/src/modules/Telemetry/Sensor/INA219Sensor.cpp
index de69163b4..ea47e265d 100644
--- a/src/modules/Telemetry/Sensor/INA219Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA219Sensor.cpp
@@ -45,4 +45,9 @@ uint16_t INA219Sensor::getBusVoltageMv()
     return lround(ina219.getBusVoltage_V() * 1000);
 }
 
+int16_t INA219Sensor::getCurrentMa()
+{
+    return lround(ina219.getCurrent_mA());
+}
+
 #endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.h b/src/modules/Telemetry/Sensor/INA219Sensor.h
index 9dded067b..9b6a2fcca 100644
--- a/src/modules/Telemetry/Sensor/INA219Sensor.h
+++ b/src/modules/Telemetry/Sensor/INA219Sensor.h
@@ -3,11 +3,12 @@
 #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
 
 #include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "CurrentSensor.h"
 #include "TelemetrySensor.h"
 #include "VoltageSensor.h"
 #include 
 
-class INA219Sensor : public TelemetrySensor, VoltageSensor
+class INA219Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
 {
   private:
     Adafruit_INA219 ina219;
@@ -20,6 +21,7 @@ class INA219Sensor : public TelemetrySensor, VoltageSensor
     virtual int32_t runOnce() override;
     virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
     virtual uint16_t getBusVoltageMv() override;
+    virtual int16_t getCurrentMa() override;
 };
 
 #endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA226Sensor.cpp b/src/modules/Telemetry/Sensor/INA226Sensor.cpp
new file mode 100644
index 000000000..1ee7cd92e
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/INA226Sensor.cpp
@@ -0,0 +1,58 @@
+#include "configuration.h"
+
+#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "INA226.h"
+#include "INA226Sensor.h"
+#include "TelemetrySensor.h"
+
+INA226Sensor::INA226Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_INA226, "INA226") {}
+
+int32_t INA226Sensor::runOnce()
+{
+    LOG_INFO("Init sensor: %s", sensorName);
+    if (!hasSensor()) {
+        return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
+    }
+
+    begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first);
+
+    if (!status) {
+        status = ina226.begin();
+    }
+    return initI2CSensor();
+}
+
+void INA226Sensor::setup() {}
+
+void INA226Sensor::begin(TwoWire *wire, uint8_t addr)
+{
+    _wire = wire;
+    _addr = addr;
+    ina226 = INA226(_addr, _wire);
+    _wire->begin();
+}
+
+bool INA226Sensor::getMetrics(meshtastic_Telemetry *measurement)
+{
+    measurement->variant.environment_metrics.has_voltage = true;
+    measurement->variant.environment_metrics.has_current = true;
+
+    // mV conversion to V
+    measurement->variant.environment_metrics.voltage = ina226.getBusVoltage() / 1000;
+    measurement->variant.environment_metrics.current = ina226.getCurrent_mA();
+    return true;
+}
+
+uint16_t INA226Sensor::getBusVoltageMv()
+{
+    return lround(ina226.getBusVoltage());
+}
+
+int16_t INA226Sensor::getCurrentMa()
+{
+    return lround(ina226.getCurrent_mA());
+}
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA226Sensor.h b/src/modules/Telemetry/Sensor/INA226Sensor.h
new file mode 100644
index 000000000..2f71c5b86
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/INA226Sensor.h
@@ -0,0 +1,30 @@
+#include "configuration.h"
+
+#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "CurrentSensor.h"
+#include "TelemetrySensor.h"
+#include "VoltageSensor.h"
+#include 
+
+class INA226Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
+{
+  private:
+    uint8_t _addr = INA_ADDR;
+    TwoWire *_wire = &Wire;
+    INA226 ina226 = INA226(_addr, _wire);
+
+  protected:
+    virtual void setup() override;
+    void begin(TwoWire *wire = &Wire, uint8_t addr = INA_ADDR);
+
+  public:
+    INA226Sensor();
+    virtual int32_t runOnce() override;
+    virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+    virtual uint16_t getBusVoltageMv() override;
+    virtual int16_t getCurrentMa() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
index ed09856e2..7ac11dfde 100644
--- a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
@@ -102,4 +102,9 @@ uint16_t INA3221Sensor::getBusVoltageMv()
     return lround(ina3221.getVoltage(BAT_CH) * 1000);
 }
 
+int16_t INA3221Sensor::getCurrentMa()
+{
+    return lround(ina3221.getCurrent(BAT_CH));
+}
+
 #endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.h b/src/modules/Telemetry/Sensor/INA3221Sensor.h
index d5121aab6..8eeda3e02 100644
--- a/src/modules/Telemetry/Sensor/INA3221Sensor.h
+++ b/src/modules/Telemetry/Sensor/INA3221Sensor.h
@@ -3,11 +3,12 @@
 #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
 
 #include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "CurrentSensor.h"
 #include "TelemetrySensor.h"
 #include "VoltageSensor.h"
 #include 
 
-class INA3221Sensor : public TelemetrySensor, VoltageSensor
+class INA3221Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
 {
   private:
     INA3221 ina3221 = INA3221(INA3221_ADDR42_SDA);
@@ -35,6 +36,7 @@ class INA3221Sensor : public TelemetrySensor, VoltageSensor
     int32_t runOnce() override;
     bool getMetrics(meshtastic_Telemetry *measurement) override;
     virtual uint16_t getBusVoltageMv() override;
+    virtual int16_t getCurrentMa() override;
 };
 
 struct _INA3221Measurement {
diff --git a/src/power.h b/src/power.h
index 63335104b..ab55fc7e1 100644
--- a/src/power.h
+++ b/src/power.h
@@ -42,10 +42,12 @@ extern RTC_NOINIT_ATTR uint64_t RTC_reg_b;
 
 #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
 #include "modules/Telemetry/Sensor/INA219Sensor.h"
+#include "modules/Telemetry/Sensor/INA226Sensor.h"
 #include "modules/Telemetry/Sensor/INA260Sensor.h"
 #include "modules/Telemetry/Sensor/INA3221Sensor.h"
-extern INA260Sensor ina260Sensor;
 extern INA219Sensor ina219Sensor;
+extern INA226Sensor ina226Sensor;
+extern INA260Sensor ina260Sensor;
 extern INA3221Sensor ina3221Sensor;
 #endif
 
@@ -99,4 +101,4 @@ class Power : private concurrency::OSThread
 #endif
 };
 
-extern Power *power;
\ No newline at end of file
+extern Power *power;