From 37da78919a679a632eea29f4b3d6d1a4cbbd84fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20de=20Tassis=20Filho?= Date: Mon, 25 Nov 2024 01:05:06 -0300 Subject: [PATCH 001/132] 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. --- src/mqtt/MQTT.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 3d5948976..967b7ba50 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -167,17 +167,26 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) 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; @@ -771,4 +780,4 @@ bool MQTT::isPrivateIpAddress(const char address[]) int octet2Num = atoi(octet2); return octet2Num >= 16 && octet2Num <= 31; -} \ No newline at end of file +} From 6018c0a830ba105b536feec8742e0e19ff7ab22e Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 25 Nov 2024 05:14:48 -0600 Subject: [PATCH 002/132] Removing 1.0 legacy boards from releases and completely removing Heltec wireless capsule from support (#5436) Co-authored-by: Tom Fifield --- platformio.ini | 1 - .../heltec_capsule_sensor_v3/platformio.ini | 11 ---- variants/heltec_capsule_sensor_v3/variant.h | 53 ------------------- .../heltec_wireless_paper_v1/platformio.ini | 1 + .../platformio.ini | 3 +- 5 files changed, 2 insertions(+), 67 deletions(-) delete mode 100644 variants/heltec_capsule_sensor_v3/platformio.ini delete mode 100644 variants/heltec_capsule_sensor_v3/variant.h diff --git a/platformio.ini b/platformio.ini index 982848f41..7f4fbc3a5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,7 +34,6 @@ default_envs = tbeam ;default_envs = radiomaster_900_bandit_nano ;default_envs = radiomaster_900_bandit_micro ;default_envs = radiomaster_900_bandit -;default_envs = heltec_capsule_sensor_v3 ;default_envs = heltec_vision_master_t190 ;default_envs = heltec_vision_master_e213 ;default_envs = heltec_vision_master_e290 diff --git a/variants/heltec_capsule_sensor_v3/platformio.ini b/variants/heltec_capsule_sensor_v3/platformio.ini deleted file mode 100644 index f1aef925d..000000000 --- a/variants/heltec_capsule_sensor_v3/platformio.ini +++ /dev/null @@ -1,11 +0,0 @@ -[env:heltec_capsule_sensor_v3] -extends = esp32s3_base -board = heltec_wifi_lora_32_V3 -board_check = true - -build_flags = - ${esp32s3_base.build_flags} -I variants/heltec_capsule_sensor_v3 - -D HELTEC_CAPSULE_SENSOR_V3 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. - ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output - diff --git a/variants/heltec_capsule_sensor_v3/variant.h b/variants/heltec_capsule_sensor_v3/variant.h deleted file mode 100644 index 415de0559..000000000 --- a/variants/heltec_capsule_sensor_v3/variant.h +++ /dev/null @@ -1,53 +0,0 @@ -#define LED_PIN 33 -#define LED_PIN2 34 -#define EXT_PWR_DETECT 35 - -#define BUTTON_PIN 18 - -#define BATTERY_PIN 7 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage -#define ADC_CHANNEL ADC1_GPIO7_CHANNEL -#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider -#define ADC_MULTIPLIER (4.9 * 1.045) -#define ADC_CTRL 36 // active HIGH, powers the voltage divider. Only on 1.1 -#define ADC_CTRL_ENABLED HIGH - -#undef GPS_RX_PIN -#undef GPS_TX_PIN -#define GPS_RX_PIN 5 -#define GPS_TX_PIN 4 -#define PIN_GPS_RESET 3 -#define GPS_RESET_MODE LOW -#define PIN_GPS_PPS 1 -#define PIN_GPS_EN 21 -#define GPS_EN_ACTIVE HIGH - -#define USE_SX1262 -#define LORA_DIO0 -1 // a No connect on the SX1262 module -#define LORA_RESET 12 -#define LORA_DIO1 14 // SX1262 IRQ -#define LORA_DIO2 13 // SX1262 BUSY -#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled - -#define LORA_SCK 9 -#define LORA_MISO 11 -#define LORA_MOSI 10 -#define LORA_CS 8 - -#define SX126X_CS LORA_CS -#define SX126X_DIO1 LORA_DIO1 -#define SX126X_BUSY LORA_DIO2 -#define SX126X_RESET LORA_RESET - -#define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - -#define I2C_SDA 1 -#define I2C_SCL 2 -#define HAS_SCREEN 0 -#define SENSOR_POWER_CTRL_PIN 21 -#define SENSOR_POWER_ON 1 - -#define PERIPHERAL_WARMUP_MS 100 -#define SENSOR_GPS_CONFLICT - -#define ESP32S3_WAKE_TYPE ESP_EXT1_WAKEUP_ANY_HIGH \ No newline at end of file diff --git a/variants/heltec_wireless_paper_v1/platformio.ini b/variants/heltec_wireless_paper_v1/platformio.ini index 999f1586a..c94bcacca 100644 --- a/variants/heltec_wireless_paper_v1/platformio.ini +++ b/variants/heltec_wireless_paper_v1/platformio.ini @@ -1,5 +1,6 @@ [env:heltec-wireless-paper-v1_0] extends = esp32s3_base +board_level = extra board = heltec_wifi_lora_32_V3 build_flags = ${esp32s3_base.build_flags} diff --git a/variants/heltec_wireless_tracker_V1_0/platformio.ini b/variants/heltec_wireless_tracker_V1_0/platformio.ini index 303e27dbf..0e48c72f2 100644 --- a/variants/heltec_wireless_tracker_V1_0/platformio.ini +++ b/variants/heltec_wireless_tracker_V1_0/platformio.ini @@ -1,14 +1,13 @@ [env:heltec-wireless-tracker-V1-0] extends = esp32s3_base +board_level = extra board = heltec_wireless_tracker upload_protocol = esptool - build_flags = ${esp32s3_base.build_flags} -I variants/heltec_wireless_tracker_V1_0 -D HELTEC_TRACKER_V1_0 -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output - lib_deps = ${esp32s3_base.lib_deps} lovyan03/LovyanGFX@^1.1.8 \ No newline at end of file From 0048e3cdcb80d114f4f934529b0d74489699d788 Mon Sep 17 00:00:00 2001 From: Christopher Hoover Date: Mon, 25 Nov 2024 03:32:04 -0800 Subject: [PATCH 003/132] 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 --- src/gps/GPS.cpp | 46 +++++++++++--------- src/gps/GPS.h | 53 ----------------------- src/gps/cas.h | 8 ++-- src/gps/ubx.h | 112 ++++++++++++++++++++++++------------------------ 4 files changed, 86 insertions(+), 133 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index a6db85950..d49092fff 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -33,24 +33,26 @@ HardwareSerial *GPS::_serial_gps = &Serial1; #elif defined(ARCH_RP2040) SerialUART *GPS::_serial_gps = &Serial1; #else -HardwareSerial *GPS::_serial_gps = NULL; +HardwareSerial *GPS::_serial_gps = nullptr; #endif GPS *gps = nullptr; -GPSUpdateScheduling scheduling; +static const char *ACK_SUCCESS_MESSAGE = "Get ack success!"; + +static GPSUpdateScheduling scheduling; /// Multiple GPS instances might use the same serial port (in sequence), but we can /// only init that port once. static bool didSerialInit; -struct uBloxGnssModelInfo info; -uint8_t uBloxProtocolVersion; +static struct uBloxGnssModelInfo info; +static uint8_t uBloxProtocolVersion; #define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway #define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc) // For logging -const char *getGPSPowerStateString(GPSPowerState state) +static const char *getGPSPowerStateString(GPSPowerState state) { switch (state) { case GPS_ACTIVE: @@ -69,7 +71,7 @@ const char *getGPSPowerStateString(GPSPowerState state) } } -void GPS::UBXChecksum(uint8_t *message, size_t length) +static void UBXChecksum(uint8_t *message, size_t length) { uint8_t CK_A = 0, CK_B = 0; @@ -85,7 +87,7 @@ void GPS::UBXChecksum(uint8_t *message, size_t length) } // Calculate the checksum for a CAS packet -void GPS::CASChecksum(uint8_t *message, size_t length) +static void CASChecksum(uint8_t *message, size_t length) { uint32_t cksum = ((uint32_t)message[5] << 24); // Message ID cksum += ((uint32_t)message[4]) << 16; // Class @@ -419,7 +421,6 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t */ bool GPS::setup() { - if (!didSerialInit) { int msglen = 0; if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { @@ -718,6 +719,7 @@ GPS::~GPS() // we really should unregister our sleep observer notifyDeepSleepObserver.unobserve(¬ifyDeepSleep); } + // Put the GPS hardware into a specified state void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) { @@ -882,17 +884,17 @@ void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) if (gnssModel != GNSS_MODEL_UBLOX10) { // Encode the sleep time in millis into the packet for (int i = 0; i < 4; i++) - gps->_message_PMREQ[0 + i] = sleepMs >> (i * 8); + _message_PMREQ[0 + i] = sleepMs >> (i * 8); // Record the message length - msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ), gps->_message_PMREQ); + msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ), _message_PMREQ); } else { // Encode the sleep time in millis into the packet for (int i = 0; i < 4; i++) - gps->_message_PMREQ_10[4 + i] = sleepMs >> (i * 8); + _message_PMREQ_10[4 + i] = sleepMs >> (i * 8); // Record the message length - msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ_10), gps->_message_PMREQ_10); + msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ_10), _message_PMREQ_10); } // Send the UBX packet @@ -1099,17 +1101,19 @@ int GPS::prepareDeepSleep(void *unused) return 0; } -const char *PROBE_MESSAGE = "Trying %s (%s)..."; -const char *DETECTED_MESSAGE = "%s detected, using %s Module"; +static const char *PROBE_MESSAGE = "Trying %s (%s)..."; +static const char *DETECTED_MESSAGE = "%s detected, using %s Module"; #define PROBE_SIMPLE(CHIP, TOWRITE, RESPONSE, DRIVER, TIMEOUT, ...) \ - LOG_DEBUG(PROBE_MESSAGE, TOWRITE, CHIP); \ - clearBuffer(); \ - _serial_gps->write(TOWRITE "\r\n"); \ - if (getACK(RESPONSE, TIMEOUT) == GNSS_RESPONSE_OK) { \ - LOG_INFO(DETECTED_MESSAGE, CHIP, #DRIVER); \ - return DRIVER; \ - } + do { \ + LOG_DEBUG(PROBE_MESSAGE, TOWRITE, CHIP); \ + clearBuffer(); \ + _serial_gps->write(TOWRITE "\r\n"); \ + if (getACK(RESPONSE, TIMEOUT) == GNSS_RESPONSE_OK) { \ + LOG_INFO(DETECTED_MESSAGE, CHIP, #DRIVER); \ + return DRIVER; \ + } \ + } while (0) GnssModel_t GPS::probe(int serialSpeed) { diff --git a/src/gps/GPS.h b/src/gps/GPS.h index cb970f7db..cc72350d2 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -54,9 +54,6 @@ enum GPSPowerState : uint8_t { GPS_OFF // Powered off indefinitely }; -// Generate a string representation of DOP -const char *getDOPString(uint32_t dop); - /** * A gps class that only reads from the GPS periodically and keeps the gps powered down except when reading * @@ -207,52 +204,6 @@ class GPS : private concurrency::OSThread #else static HardwareSerial *_serial_gps; #endif - static uint8_t _message_PMREQ[]; - static uint8_t _message_PMREQ_10[]; - static const uint8_t _message_CFG_RXM_PSM[]; - static const uint8_t _message_CFG_RXM_ECO[]; - static const uint8_t _message_CFG_PM2[]; - static const uint8_t _message_GNSS_7[]; - static const uint8_t _message_GNSS_8[]; - static const uint8_t _message_JAM_6_7[]; - static const uint8_t _message_JAM_8[]; - static const uint8_t _message_NAVX5[]; - static const uint8_t _message_NAVX5_8[]; - static const uint8_t _message_NMEA[]; - static const uint8_t _message_DISABLE_TXT_INFO[]; - static const uint8_t _message_1HZ[]; - static const uint8_t _message_GLL[]; - static const uint8_t _message_GSA[]; - static const uint8_t _message_GSV[]; - static const uint8_t _message_VTG[]; - static const uint8_t _message_RMC[]; - static const uint8_t _message_AID[]; - static const uint8_t _message_GGA[]; - static const uint8_t _message_PMS[]; - static const uint8_t _message_SAVE[]; - static const uint8_t _message_SAVE_10[]; - - // VALSET Commands for M10 - static const uint8_t _message_VALSET_PM[]; - static const uint8_t _message_VALSET_PM_RAM[]; - static const uint8_t _message_VALSET_PM_BBR[]; - static const uint8_t _message_VALSET_ITFM_RAM[]; - static const uint8_t _message_VALSET_ITFM_BBR[]; - static const uint8_t _message_VALSET_DISABLE_NMEA_RAM[]; - static const uint8_t _message_VALSET_DISABLE_NMEA_BBR[]; - static const uint8_t _message_VALSET_DISABLE_TXT_INFO_RAM[]; - static const uint8_t _message_VALSET_DISABLE_TXT_INFO_BBR[]; - static const uint8_t _message_VALSET_ENABLE_NMEA_RAM[]; - static const uint8_t _message_VALSET_ENABLE_NMEA_BBR[]; - static const uint8_t _message_VALSET_DISABLE_SBAS_RAM[]; - static const uint8_t _message_VALSET_DISABLE_SBAS_BBR[]; - - // CASIC commands for ATGM336H - static const uint8_t _message_CAS_CFG_RST_FACTORY[]; - static const uint8_t _message_CAS_CFG_NAVX_CONF[]; - static const uint8_t _message_CAS_CFG_RATE_1HZ[]; - - const char *ACK_SUCCESS_MESSAGE = "Get ack success!"; // Create a ublox packet for editing in memory uint8_t makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg); @@ -273,10 +224,6 @@ class GPS : private concurrency::OSThread /// always returns 0 to indicate okay to sleep int prepareDeepSleep(void *unused); - // Calculate checksum - void UBXChecksum(uint8_t *message, size_t length); - void CASChecksum(uint8_t *message, size_t length); - /** Set power with EN pin, if relevant */ void writePinEN(bool on); diff --git a/src/gps/cas.h b/src/gps/cas.h index 53d75cda9..725fd07b3 100644 --- a/src/gps/cas.h +++ b/src/gps/cas.h @@ -21,7 +21,7 @@ // CFG-RST (0x06, 0x02) // Factory reset -const uint8_t GPS::_message_CAS_CFG_RST_FACTORY[] = { +static const uint8_t _message_CAS_CFG_RST_FACTORY[] = { 0xFF, 0x03, // Fields to clear 0x01, // Reset Mode: Controlled Software reset 0x03 // Startup Mode: Factory @@ -30,7 +30,7 @@ const uint8_t GPS::_message_CAS_CFG_RST_FACTORY[] = { // CFG_RATE (0x06, 0x01) // 1HZ update rate, this should always be the case after // factory reset but update it regardless -const uint8_t GPS::_message_CAS_CFG_RATE_1HZ[] = { +static const uint8_t _message_CAS_CFG_RATE_1HZ[] = { 0xE8, 0x03, // Update Rate: 0x03E8 = 1000ms 0x00, 0x00 // Reserved }; @@ -39,7 +39,7 @@ const uint8_t GPS::_message_CAS_CFG_RATE_1HZ[] = { // Initial ATGM33H-5N configuration, Updates for Dynamic Mode, Fix Mode, and SV system // Qwirk: The ATGM33H-5N-31 should only support GPS+BDS, however it will happily enable // and use GPS+BDS+GLONASS iff the correct CFG_NAVX command is used. -const uint8_t GPS::_message_CAS_CFG_NAVX_CONF[] = { +static const uint8_t _message_CAS_CFG_NAVX_CONF[] = { 0x03, 0x01, 0x00, 0x00, // Update Mask: Dynamic Mode, Fix Mode, Nav Settings 0x03, // Dynamic Mode: Automotive 0x03, // Fix Mode: Auto 2D/3D @@ -60,4 +60,4 @@ const uint8_t GPS::_message_CAS_CFG_NAVX_CONF[] = { 0x00, 0x00, 0x00, 0x00, // Position Accuracy Max 0x00, 0x00, 0x00, 0x00, // Time Accuracy Max 0x00, 0x00, 0x00, 0x00 // Static Hold Threshold -}; \ No newline at end of file +}; diff --git a/src/gps/ubx.h b/src/gps/ubx.h index 551921384..d674bed51 100644 --- a/src/gps/ubx.h +++ b/src/gps/ubx.h @@ -1,20 +1,22 @@ -const char *failMessage = "Unable to %s"; +static const char *failMessage = "Unable to %s"; #define SEND_UBX_PACKET(TYPE, ID, DATA, ERRMSG, TIMEOUT) \ - msglen = makeUBXPacket(TYPE, ID, sizeof(DATA), DATA); \ - _serial_gps->write(UBXscratch, msglen); \ - if (getACK(TYPE, ID, TIMEOUT) != GNSS_RESPONSE_OK) { \ - LOG_WARN(failMessage, #ERRMSG); \ - } + do { \ + msglen = makeUBXPacket(TYPE, ID, sizeof(DATA), DATA); \ + _serial_gps->write(UBXscratch, msglen); \ + if (getACK(TYPE, ID, TIMEOUT) != GNSS_RESPONSE_OK) { \ + LOG_WARN(failMessage, #ERRMSG); \ + } \ + } while (0) // Power Management -uint8_t GPS::_message_PMREQ[] PROGMEM = { +static uint8_t _message_PMREQ[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, // 4 bytes duration of request task (milliseconds) 0x02, 0x00, 0x00, 0x00 // Bitfield, set backup = 1 }; -uint8_t GPS::_message_PMREQ_10[] PROGMEM = { +static uint8_t _message_PMREQ_10[] PROGMEM = { 0x00, // version (0 for this version) 0x00, 0x00, 0x00, // Reserved 1 0x00, 0x00, 0x00, 0x00, // 4 bytes duration of request task (milliseconds) @@ -22,18 +24,18 @@ uint8_t GPS::_message_PMREQ_10[] PROGMEM = { 0x08, 0x00, 0x00, 0x00 // wakeupSources Wake on uartrx }; -const uint8_t GPS::_message_CFG_RXM_PSM[] PROGMEM = { +static const uint8_t _message_CFG_RXM_PSM[] PROGMEM = { 0x08, // Reserved 0x01 // Power save mode }; // only for Neo-6 -const uint8_t GPS::_message_CFG_RXM_ECO[] PROGMEM = { +static const uint8_t _message_CFG_RXM_ECO[] PROGMEM = { 0x08, // Reserved 0x04 // eco mode }; -const uint8_t GPS::_message_CFG_PM2[] PROGMEM = { +static const uint8_t _message_CFG_PM2[] PROGMEM = { 0x01, // version 0x00, // Reserved 1, set to 0x06 by u-Center 0x00, // Reserved 2 @@ -58,7 +60,7 @@ const uint8_t GPS::_message_CFG_PM2[] PROGMEM = { // Constallation setup, none required for Neo-6 // For Neo-7 GPS & SBAS -const uint8_t GPS::_message_GNSS_7[] = { +static const uint8_t _message_GNSS_7[] = { 0x00, // msgVer (0 for this version) 0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0) 0xff, // numTrkChUse (max number of channels to use, 0xff = max available) @@ -76,7 +78,7 @@ const uint8_t GPS::_message_GNSS_7[] = { // There is also a possibility that the module may be GPS-only. // For M8 GPS, GLONASS, Galileo, SBAS, QZSS -const uint8_t GPS::_message_GNSS_8[] = { +static const uint8_t _message_GNSS_8[] = { 0x00, // msgVer (0 for this version) 0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0) 0xff, // numTrkChUse (max number of channels to use, 0xff = max available) @@ -90,7 +92,7 @@ const uint8_t GPS::_message_GNSS_8[] = { }; /* // For M8 GPS, GLONASS, BeiDou, SBAS, QZSS -const uint8_t GPS::_message_GNSS_8_B[] = { +static const uint8_t _message_GNSS_8_B[] = { 0x00, // msgVer (0 for this version) 0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0) 0xff, // numTrkChUse (max number of channels to use, 0xff = max available) read only for protocol >23 @@ -105,7 +107,7 @@ const uint8_t GPS::_message_GNSS_8_B[] = { */ // For M8 we want to enable NMEA version 4.10 messages to allow for Galileo and or BeiDou -const uint8_t GPS::_message_NMEA[]{ +static const uint8_t _message_NMEA[]{ 0x00, // filter flags 0x41, // NMEA Version 0x00, // Max number of SVs to report per TaklerId @@ -121,13 +123,13 @@ const uint8_t GPS::_message_NMEA[]{ // Enable jamming/interference monitor // For Neo-6, Max-7 and Neo-7 -const uint8_t GPS::_message_JAM_6_7[] = { +static const uint8_t _message_JAM_6_7[] = { 0xf3, 0xac, 0x62, 0xad, // config1 bbThreshold = 3, cwThreshold = 15, enable = 1, reserved bits 0x16B156 0x1e, 0x03, 0x00, 0x00 // config2 antennaSetting Unknown = 0, reserved 3, = 0x00,0x00, reserved 2 = 0x31E }; // For M8 -const uint8_t GPS::_message_JAM_8[] = { +static const uint8_t _message_JAM_8[] = { 0xf3, 0xac, 0x62, 0xad, // config1 bbThreshold = 3, cwThreshold = 15, enable1 = 1, reserved bits 0x16B156 0x1e, 0x43, 0x00, 0x00 // config2 antennaSetting Unknown = 0, enable2 = 1, generalBits = 0x31E }; @@ -137,7 +139,7 @@ const uint8_t GPS::_message_JAM_8[] = { // ToDo: check UBX-MON-VER for module type and protocol version // For the Neo-6 -const uint8_t GPS::_message_NAVX5[] = { +static const uint8_t _message_NAVX5[] = { 0x00, 0x00, // msgVer (0 for this version) 0x4c, 0x66, // mask1 0x00, 0x00, 0x00, 0x00, // Reserved 0 @@ -166,7 +168,7 @@ const uint8_t GPS::_message_NAVX5[] = { 0x00, 0x00, 0x00, 0x00 // Reserved 4 }; // For the M8 -const uint8_t GPS::_message_NAVX5_8[] = { +static const uint8_t _message_NAVX5_8[] = { 0x02, 0x00, // msgVer (2 for this version) 0x4c, 0x66, // mask1 0x00, 0x00, 0x00, 0x00, // mask2 @@ -197,7 +199,7 @@ const uint8_t GPS::_message_NAVX5_8[] = { // Additionally, for some new modules like the M9/M10, an update rate lower than 5Hz // is recommended to avoid a known issue with satellites disappearing. // The module defaults for M8, M9, M10 are the same as we use here so no update is necessary -const uint8_t GPS::_message_1HZ[] = { +static const uint8_t _message_1HZ[] = { 0xE8, 0x03, // Measurement Rate (1000ms for 1Hz) 0x01, 0x00, // Navigation rate, always 1 in GPS mode 0x01, 0x00 // Time reference @@ -205,7 +207,7 @@ const uint8_t GPS::_message_1HZ[] = { // Disable GLL. GLL - Geographic position (latitude and longitude), which provides the current geographical // coordinates. -const uint8_t GPS::_message_GLL[] = { +static const uint8_t _message_GLL[] = { 0xF0, 0x01, // NMEA ID for GLL 0x00, // Rate for DDC 0x00, // Rate for UART1 @@ -217,7 +219,7 @@ const uint8_t GPS::_message_GLL[] = { // Disable GSA. GSA - GPS DOP and active satellites, used for detailing the satellites used in the positioning and // the DOP (Dilution of Precision) -const uint8_t GPS::_message_GSA[] = { +static const uint8_t _message_GSA[] = { 0xF0, 0x02, // NMEA ID for GSA 0x00, // Rate for DDC 0x00, // Rate for UART1 @@ -228,7 +230,7 @@ const uint8_t GPS::_message_GSA[] = { }; // Disable GSV. GSV - Satellites in view, details the number and location of satellites in view. -const uint8_t GPS::_message_GSV[] = { +static const uint8_t _message_GSV[] = { 0xF0, 0x03, // NMEA ID for GSV 0x00, // Rate for DDC 0x00, // Rate for UART1 @@ -240,7 +242,7 @@ const uint8_t GPS::_message_GSV[] = { // Disable VTG. VTG - Track made good and ground speed, which provides course and speed information relative to // the ground. -const uint8_t GPS::_message_VTG[] = { +static const uint8_t _message_VTG[] = { 0xF0, 0x05, // NMEA ID for VTG 0x00, // Rate for DDC 0x00, // Rate for UART1 @@ -251,7 +253,7 @@ const uint8_t GPS::_message_VTG[] = { }; // Enable RMC. RMC - Recommended Minimum data, the essential gps pvt (position, velocity, time) data. -const uint8_t GPS::_message_RMC[] = { +static const uint8_t _message_RMC[] = { 0xF0, 0x04, // NMEA ID for RMC 0x00, // Rate for DDC 0x01, // Rate for UART1 @@ -262,7 +264,7 @@ const uint8_t GPS::_message_RMC[] = { }; // Enable GGA. GGA - Global Positioning System Fix Data, which provides 3D location and accuracy data. -const uint8_t GPS::_message_GGA[] = { +static const uint8_t _message_GGA[] = { 0xF0, 0x00, // NMEA ID for GGA 0x00, // Rate for DDC 0x01, // Rate for UART1 @@ -274,7 +276,7 @@ const uint8_t GPS::_message_GGA[] = { // Disable UBX-AID-ALPSRV as it may confuse TinyGPS. The Neo-6 seems to send this message // whether the AID Autonomous is enabled or not -const uint8_t GPS::_message_AID[] = { +static const uint8_t _message_AID[] = { 0x0B, 0x32, // NMEA ID for UBX-AID-ALPSRV 0x00, // Rate for DDC 0x00, // Rate for UART1 @@ -287,7 +289,7 @@ const uint8_t GPS::_message_AID[] = { // Turn off TEXT INFO Messages for all but M10 series // B5 62 06 02 0A 00 01 00 00 00 03 03 00 03 03 00 1F 20 -const uint8_t GPS::_message_DISABLE_TXT_INFO[] = { +static const uint8_t _message_DISABLE_TXT_INFO[] = { 0x01, // Protocol ID for NMEA 0x00, 0x00, 0x00, // Reserved 0x03, // I2C @@ -310,7 +312,7 @@ const uint8_t GPS::_message_DISABLE_TXT_INFO[] = { // and must be smaller than the period. It is only valid when the powerSetupValue is set to Interval; otherwise, // it must be set to '0'. // This command applies to M8 products -const uint8_t GPS::_message_PMS[] = { +static const uint8_t _message_PMS[] = { 0x00, // Version (0) 0x03, // Power setup value 3 = Agresssive 1Hz 0x00, 0x00, // period: not applicable, set to 0 @@ -318,14 +320,14 @@ const uint8_t GPS::_message_PMS[] = { 0x00, 0x00 // reserved, generated by u-center }; -const uint8_t GPS::_message_SAVE[] = { +static const uint8_t _message_SAVE[] = { 0x00, 0x00, 0x00, 0x00, // clearMask: no sections cleared 0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections 0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded 0x17 // deviceMask: BBR, Flash, EEPROM, and SPI Flash }; -const uint8_t GPS::_message_SAVE_10[] = { +static const uint8_t _message_SAVE_10[] = { 0x00, 0x00, 0x00, 0x00, // clearMask: no sections cleared 0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections 0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded @@ -375,12 +377,12 @@ LIMITPEAKCURRENT L 1 // b5 62 06 8a 26 00 00 02 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01 10 00 d0 // 10 01 8c 03 */ -const uint8_t GPS::_message_VALSET_PM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, - 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0, - 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01}; -const uint8_t GPS::_message_VALSET_PM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, - 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0, - 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01}; +static const uint8_t _message_VALSET_PM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0, + 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01}; +static const uint8_t _message_VALSET_PM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0, + 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01}; /* CFG-ITFM replaced by 5 valset messages which can be combined into one for RAM and one for BBR @@ -394,10 +396,10 @@ CFG-ITFM replaced by 5 valset messages which can be combined into one for RAM an b5 62 06 8a 0e 00 00 01 00 00 0d 00 41 10 01 13 00 41 10 01 63 c6 */ -const uint8_t GPS::_message_VALSET_ITFM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x41, - 0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01}; -const uint8_t GPS::_message_VALSET_ITFM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x0d, 0x00, 0x41, - 0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01}; +static const uint8_t _message_VALSET_ITFM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x41, + 0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01}; +static const uint8_t _message_VALSET_ITFM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x0d, 0x00, 0x41, + 0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01}; // Turn off all NMEA messages: // Ram layer config message: @@ -407,13 +409,13 @@ const uint8_t GPS::_message_VALSET_ITFM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x0d, 0 // BBR layer config message: // b5 62 06 8a 13 00 00 02 00 00 ca 00 91 20 00 c5 00 91 20 00 b1 00 91 20 00 f8 4e -const uint8_t GPS::_message_VALSET_DISABLE_NMEA_RAM[] = { +static const uint8_t _message_VALSET_DISABLE_NMEA_RAM[] = { /*0x00, 0x01, 0x00, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00 */ 0x00, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x91, 0x20, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, 0x00, 0x91, 0x20, 0x00, 0xac, 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00, 0xbb, 0x00, 0x91, 0x20, 0x00}; -const uint8_t GPS::_message_VALSET_DISABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, - 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00}; +static const uint8_t _message_VALSET_DISABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, + 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00}; // Turn off text info messages: // Ram layer config message: @@ -432,17 +434,17 @@ const uint8_t GPS::_message_VALSET_DISABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, // b5 62 06 8a 0e 00 00 04 00 00 bb 00 91 20 01 ac 00 91 20 01 6d b6 // Doing this for the FLASH layer isn't really required since we save the config to flash later -const uint8_t GPS::_message_VALSET_DISABLE_TXT_INFO_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03}; -const uint8_t GPS::_message_VALSET_DISABLE_TXT_INFO_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03}; +static const uint8_t _message_VALSET_DISABLE_TXT_INFO_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03}; +static const uint8_t _message_VALSET_DISABLE_TXT_INFO_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03}; -const uint8_t GPS::_message_VALSET_ENABLE_NMEA_RAM[] = {0x00, 0x01, 0x00, 0x00, 0xbb, 0x00, 0x91, - 0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01}; -const uint8_t GPS::_message_VALSET_ENABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, 0xbb, 0x00, 0x91, - 0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01}; -const uint8_t GPS::_message_VALSET_DISABLE_SBAS_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x31, - 0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00}; -const uint8_t GPS::_message_VALSET_DISABLE_SBAS_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x31, - 0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00}; +static const uint8_t _message_VALSET_ENABLE_NMEA_RAM[] = {0x00, 0x01, 0x00, 0x00, 0xbb, 0x00, 0x91, + 0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01}; +static const uint8_t _message_VALSET_ENABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, 0xbb, 0x00, 0x91, + 0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01}; +static const uint8_t _message_VALSET_DISABLE_SBAS_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x31, + 0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00}; +static const uint8_t _message_VALSET_DISABLE_SBAS_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x31, + 0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00}; /* Operational issues with the M10: @@ -475,4 +477,4 @@ b5 62 06 8a 0e 00 00 01 00 00 20 00 31 10 00 05 00 31 10 00 46 87 BBR layer config message: b5 62 06 8a 0e 00 00 02 00 00 20 00 31 10 00 05 00 31 10 00 47 94 -*/ \ No newline at end of file +*/ From 7c2b6778cb427ed434d1c79241a0084e13c470a3 Mon Sep 17 00:00:00 2001 From: Tomas Dubec Date: Mon, 25 Nov 2024 13:12:19 +0100 Subject: [PATCH 004/132] enable MQTT with TLS on RPi picow (#5442) Co-authored-by: Ben Meadors --- src/mqtt/MQTT.cpp | 2 +- src/mqtt/MQTT.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 967b7ba50..967db04d6 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -345,7 +345,7 @@ void MQTT::reconnect() mqttPassword = moduleConfig.mqtt.password; } #if HAS_WIFI && !defined(ARCH_PORTDUINO) -#if !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(RPI_PICO) +#if !defined(CONFIG_IDF_TARGET_ESP32C6) if (moduleConfig.mqtt.tls_enabled) { // change default for encrypted to 8883 try { diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index fcefb94a2..7e0378238 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -35,7 +35,7 @@ class MQTT : private concurrency::OSThread #if HAS_WIFI WiFiClient mqttClient; #if !defined(ARCH_PORTDUINO) -#if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR < 3 +#if (defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR < 3) || defined(RPI_PICO) WiFiClientSecure wifiSecureClient; #endif #endif @@ -130,4 +130,4 @@ class MQTT : private concurrency::OSThread void mqttInit(); -extern MQTT *mqtt; \ No newline at end of file +extern MQTT *mqtt; From 58c957f2c7e73c8f6ab77a12520918900f52a2d6 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 25 Nov 2024 06:53:05 -0600 Subject: [PATCH 005/132] Don't powersave on Wifi (#5443) * Don't go into light sleep with wifi enabled * Move * Trunk --- src/PowerFSM.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 00dd6c942..4c4d203c2 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -19,6 +19,10 @@ #include "sleep.h" #include "target_specific.h" +#if HAS_WIFI && !defined(ARCH_PORTDUINO) +#include "mesh/wifi/WiFiAPClient.h" +#endif + #ifndef SLEEP_TIME #define SLEEP_TIME 30 #endif @@ -377,9 +381,9 @@ void PowerFSM_setup() // We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally) #ifdef ARCH_ESP32 // See: https://github.com/meshtastic/firmware/issues/1071 - // Don't add power saving transitions if we are a power saving tracker or sensor. Sleep will be initiated through the - // modules - if ((isRouter || config.power.is_power_saving) && !isTrackerOrSensor) { + // Don't add power saving transitions if we are a power saving tracker or sensor or have Wifi enabled. Sleep will be initiated + // through the modules + if ((isRouter || config.power.is_power_saving) && !isWifiAvailable() && !isTrackerOrSensor) { powerFSM.add_timed_transition(&stateNB, &stateLS, Default::getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL, "Min wake timeout"); From d5af8f0a9714a6f3193b2a90e2ee30e9f49ce94e Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 25 Nov 2024 21:09:55 -0600 Subject: [PATCH 006/132] Revert "Seems like the last DIY board that's not "extra" (#5420)" (#5446) This reverts commit e6fb6b115aebb12b31fb93ed9d1508a6109b2f03. --- variants/diy/platformio.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index 83e2175c8..00ff88da2 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -2,7 +2,6 @@ [env:meshtastic-diy-v1] extends = esp32_base board = esp32doit-devkit-v1 -board_level = extra board_check = true build_flags = ${esp32_base.build_flags} From ae4f54224ed21ca5d2a7b9c8706d74c9a6b955e5 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 25 Nov 2024 22:49:13 -0600 Subject: [PATCH 007/132] Actually gunzip all the files when building a .deb (#5449) --- .github/workflows/package_amd64.yml | 2 +- .github/workflows/package_raspbian.yml | 2 +- .github/workflows/package_raspbian_armv7l.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml index 4f6636712..c11566deb 100644 --- a/.github/workflows/package_amd64.yml +++ b/.github/workflows/package_amd64.yml @@ -58,7 +58,7 @@ jobs: if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz + gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r cp release/meshtasticd_linux_x86_64 .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml index d9b12d6da..56b683fdd 100644 --- a/.github/workflows/package_raspbian.yml +++ b/.github/workflows/package_raspbian.yml @@ -58,7 +58,7 @@ jobs: if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz + gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r cp release/meshtasticd_linux_aarch64 .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml index e19df9d17..663903e10 100644 --- a/.github/workflows/package_raspbian_armv7l.yml +++ b/.github/workflows/package_raspbian_armv7l.yml @@ -58,7 +58,7 @@ jobs: if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz + gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r cp release/meshtasticd_linux_armv7l .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ From 09286a3beb42ef1484e2a5759780a8410def46fc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 08:32:02 -0600 Subject: [PATCH 008/132] [create-pull-request] automated change (#5457) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 12 ++++++------ src/mesh/generated/meshtastic/storeforward.pb.h | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/protobufs b/protobufs index c952f8a4c..02e6576ef 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c952f8a4c1c30f724743ee322dd3ec3ec2f934c4 +Subproject commit 02e6576efaa2f691be9504b8c1c6261703f7a277 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 3e195e7f5..a173adb4d 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -229,7 +229,7 @@ typedef enum _meshtastic_Constants { /* From mesh.options note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is outside of this envelope */ - meshtastic_Constants_DATA_PAYLOAD_LEN = 237 + meshtastic_Constants_DATA_PAYLOAD_LEN = 233 } meshtastic_Constants; /* Error codes for critical errors @@ -603,7 +603,7 @@ typedef struct _meshtastic_Routing { }; } meshtastic_Routing; -typedef PB_BYTES_ARRAY_T(237) meshtastic_Data_payload_t; +typedef PB_BYTES_ARRAY_T(233) meshtastic_Data_payload_t; /* (Formerly called SubPacket) The payload portion fo a packet, this is the actual bytes that are sent inside a radio packet (because from/to are broken out by the comms library) */ @@ -882,7 +882,7 @@ typedef struct _meshtastic_FileInfo { uint32_t size_bytes; } meshtastic_FileInfo; -typedef PB_BYTES_ARRAY_T(237) meshtastic_Compressed_data_t; +typedef PB_BYTES_ARRAY_T(233) meshtastic_Compressed_data_t; /* Compressed message payload */ typedef struct _meshtastic_Compressed { /* PortNum to determine the how to handle the compressed payload. */ @@ -1730,14 +1730,14 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; #define MESHTASTIC_MESHTASTIC_MESH_PB_H_MAX_SIZE meshtastic_FromRadio_size #define meshtastic_ChunkedPayload_size 245 #define meshtastic_ClientNotification_size 415 -#define meshtastic_Compressed_size 243 -#define meshtastic_Data_size 273 +#define meshtastic_Compressed_size 239 +#define meshtastic_Data_size 269 #define meshtastic_DeviceMetadata_size 54 #define meshtastic_FileInfo_size 236 #define meshtastic_FromRadio_size 510 #define meshtastic_Heartbeat_size 0 #define meshtastic_LogRecord_size 426 -#define meshtastic_MeshPacket_size 375 +#define meshtastic_MeshPacket_size 371 #define meshtastic_MqttClientProxyMessage_size 501 #define meshtastic_MyNodeInfo_size 77 #define meshtastic_NeighborInfo_size 258 diff --git a/src/mesh/generated/meshtastic/storeforward.pb.h b/src/mesh/generated/meshtastic/storeforward.pb.h index 71f2fcad5..44ffd098c 100644 --- a/src/mesh/generated/meshtastic/storeforward.pb.h +++ b/src/mesh/generated/meshtastic/storeforward.pb.h @@ -91,7 +91,7 @@ typedef struct _meshtastic_StoreAndForward_Heartbeat { uint32_t secondary; } meshtastic_StoreAndForward_Heartbeat; -typedef PB_BYTES_ARRAY_T(237) meshtastic_StoreAndForward_text_t; +typedef PB_BYTES_ARRAY_T(233) meshtastic_StoreAndForward_text_t; /* TODO: REPLACE */ typedef struct _meshtastic_StoreAndForward { /* TODO: REPLACE */ @@ -211,7 +211,7 @@ extern const pb_msgdesc_t meshtastic_StoreAndForward_Heartbeat_msg; #define meshtastic_StoreAndForward_Heartbeat_size 12 #define meshtastic_StoreAndForward_History_size 18 #define meshtastic_StoreAndForward_Statistics_size 50 -#define meshtastic_StoreAndForward_size 242 +#define meshtastic_StoreAndForward_size 238 #ifdef __cplusplus } /* extern "C" */ From fe86c40145cf1fa5314e2d16d3d872cfbbd05dcb Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 26 Nov 2024 13:59:50 -0600 Subject: [PATCH 009/132] 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 --- src/detect/ScanI2CTwoWire.cpp | 135 +++++++++++++++++----------------- src/detect/ScanI2CTwoWire.h | 2 + src/main.cpp | 87 +++++++++++----------- src/main.h | 7 ++ 4 files changed, 121 insertions(+), 110 deletions(-) diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 55f13c5a0..cd0c3d144 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -72,10 +72,10 @@ ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const r &= 0x0f; if (r == 0x08 || r == 0x00) { - LOG_INFO("sh1106 display found"); + logFoundDevice("SH1106", (uint8_t)addr.address); o_probe = SCREEN_SH1106; // SH1106 } else if (r == 0x03 || r == 0x04 || r == 0x06 || r == 0x07) { - LOG_INFO("ssd1306 display found"); + logFoundDevice("SSD1306", (uint8_t)addr.address); o_probe = SCREEN_SSD1306; // SSD1306 } c++; @@ -129,7 +129,6 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation i2cBus->endTransmission(); delay(20); i2cBus->requestFrom(registerLocation.i2cAddress.address, responseWidth); - LOG_DEBUG("Wire.available() = %d", i2cBus->available()); if (i2cBus->available() == 2) { // Read MSB, then LSB value = (uint16_t)i2cBus->read() << 8; @@ -142,7 +141,7 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation #define SCAN_SIMPLE_CASE(ADDR, T, ...) \ case ADDR: \ - LOG_INFO(__VA_ARGS__); \ + logFoundDevice(__VA_ARGS__); \ type = T; \ break; @@ -184,9 +183,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) for (addr.address = 8; addr.address < 120; addr.address++) { if (asize != 0) { - if (!in_array(address, asize, addr.address)) + if (!in_array(address, asize, (uint8_t)addr.address)) continue; - LOG_DEBUG("Scan address 0x%x", addr.address); + LOG_DEBUG("Scan address 0x%x", (uint8_t)addr.address); } i2cBus->beginTransmission(addr.address); #ifdef ARCH_PORTDUINO @@ -199,8 +198,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) #endif type = NONE; if (err == 0) { - LOG_DEBUG("I2C device found at address 0x%x", addr.address); - switch (addr.address) { case SSD1306_ADDRESS: type = probeOLED(addr); @@ -227,7 +224,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) case RV3028_RTC: // foundDevices[addr] = RTC_RV3028; type = RTC_RV3028; - LOG_INFO("RV3028 RTC found"); + logFoundDevice("RV3028", (uint8_t)addr.address); rtc.initI2C(*i2cBus); rtc.writeToRegister(0x35, 0x07); // no Clkout rtc.writeToRegister(0x37, 0xB4); @@ -235,7 +232,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) #endif #ifdef PCF8563_RTC - SCAN_SIMPLE_CASE(PCF8563_RTC, RTC_PCF8563, "PCF8563 RTC found") + SCAN_SIMPLE_CASE(PCF8563_RTC, RTC_PCF8563, "PCF8563", (uint8_t)addr.address) #endif case CARDKB_ADDR: @@ -243,50 +240,50 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x04), 1); if (registerValue == 0x02) { // KEYPAD_VERSION - LOG_INFO("RAK14004 found"); + logFoundDevice("RAK14004", (uint8_t)addr.address); type = RAK14004; } else { - LOG_INFO("m5 cardKB found"); + logFoundDevice("M5 cardKB", (uint8_t)addr.address); type = CARDKB; } break; - SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard found"); - SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10 keyboard found"); + SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10", (uint8_t)addr.address); - SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "st7567 display found"); + SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "ST7567", (uint8_t)addr.address); #ifdef HAS_NCP5623 - SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623 RGB LED found"); + SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623", (uint8_t)addr.address); #endif #ifdef HAS_PMU - SCAN_SIMPLE_CASE(XPOWERS_AXP192_AXP2101_ADDRESS, PMU_AXP192_AXP2101, "axp192/axp2101 PMU found") + SCAN_SIMPLE_CASE(XPOWERS_AXP192_AXP2101_ADDRESS, PMU_AXP192_AXP2101, "AXP192/AXP2101", (uint8_t)addr.address) #endif case BME_ADDR: case BME_ADDR_ALTERNATE: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xD0), 1); // GET_ID switch (registerValue) { case 0x61: - LOG_INFO("BME-680 sensor found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("BME680", (uint8_t)addr.address); type = BME_680; break; case 0x60: - LOG_INFO("BME-280 sensor found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("BME280", (uint8_t)addr.address); type = BME_280; break; case 0x55: - LOG_INFO("BMP-085 or BMP-180 sensor found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("BMP085/BMP180", (uint8_t)addr.address); type = BMP_085; break; default: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); // GET_ID switch (registerValue) { case 0x50: // BMP-388 should be 0x50 - LOG_INFO("BMP-388 sensor found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("BMP-388", (uint8_t)addr.address); type = BMP_3XX; break; case 0x58: // BMP-280 should be 0x58 default: - LOG_INFO("BMP-280 sensor found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("BMP-280", (uint8_t)addr.address); type = BMP_280; break; } @@ -295,7 +292,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) break; #ifndef HAS_NCP5623 case AHT10_ADDR: - LOG_INFO("AHT10 sensor found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("AHT10", (uint8_t)addr.address); type = AHT10; break; #endif @@ -305,10 +302,10 @@ 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) { - LOG_INFO("INA260 sensor found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("INA260", (uint8_t)addr.address); type = INA260; } else { // Assume INA219 if INA260 ID is not found - LOG_INFO("INA219 sensor found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("INA219", (uint8_t)addr.address); type = INA219; } break; @@ -316,7 +313,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2); LOG_DEBUG("Register MFG_UID FE: 0x%x", registerValue); if (registerValue == 0x5449) { - LOG_INFO("INA3221 sensor found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("INA3221", (uint8_t)addr.address); type = INA3221; } else { /* check the first 2 bytes of the 6 byte response register @@ -331,7 +328,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x05), 2); LOG_DEBUG("Register MFG_UID 05: 0x%x", registerValue); if (registerValue == 0x5305) { - LOG_INFO("DFRobot Lark weather station found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("DFRobot Lark", (uint8_t)addr.address); type = DFROBOT_LARK; } // else: probably a RAK12500/UBLOX GPS on I2C @@ -346,7 +343,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 2); if (registerValue == 0x8700) { type = STK8BAXX; - LOG_INFO("STK8BAXX accelerometer found"); + logFoundDevice("STK8BAXX", (uint8_t)addr.address); break; } #endif @@ -355,7 +352,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x07), 2); if (registerValue == 0x0400) { type = MCP9808; - LOG_INFO("MCP9808 sensor found"); + logFoundDevice("MCP9808", (uint8_t)addr.address); break; } @@ -363,7 +360,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 2); if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333 type = LIS3DH; - LOG_INFO("LIS3DH accelerometer found"); + logFoundDevice("LIS3DH", (uint8_t)addr.address); } break; } @@ -371,93 +368,92 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2); if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c) { type = SHT4X; - LOG_INFO("SHT4X sensor found"); + logFoundDevice("SHT4X", (uint8_t)addr.address); } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) { type = OPT3001; - LOG_INFO("OPT3001 light sensor found"); + logFoundDevice("OPT3001", (uint8_t)addr.address); } else { type = SHT31; - LOG_INFO("SHT31 sensor found"); + logFoundDevice("SHT31", (uint8_t)addr.address); } break; - SCAN_SIMPLE_CASE(SHTC3_ADDR, SHTC3, "SHTC3 sensor found") + SCAN_SIMPLE_CASE(SHTC3_ADDR, SHTC3, "SHTC3", (uint8_t)addr.address) case RCWL9620_ADDR: // get MAX30102 PARTID registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFF), 1); if (registerValue == 0x15) { type = MAX30102; - LOG_INFO("MAX30102 Health sensor found"); + logFoundDevice("MAX30102", (uint8_t)addr.address); break; } else { type = RCWL9620; - LOG_INFO("RCWL9620 sensor found"); + logFoundDevice("RCWL9620", (uint8_t)addr.address); } break; case LPS22HB_ADDR_ALT: - SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB sensor found") - - SCAN_SIMPLE_CASE(QMC6310_ADDR, QMC6310, "QMC6310 Highrate 3-Axis magnetic sensor found") + SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB", (uint8_t)addr.address) + SCAN_SIMPLE_CASE(QMC6310_ADDR, QMC6310, "QMC6310", (uint8_t)addr.address) case QMI8658_ADDR: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0A), 1); // get ID if (registerValue == 0xC0) { type = BQ24295; - LOG_INFO("BQ24295 PMU found"); + logFoundDevice("BQ24295", (uint8_t)addr.address); break; } registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 1); // get ID if (registerValue == 0x6A) { type = LSM6DS3; - LOG_INFO("LSM6DS3 accelerometer found at address 0x%x", (uint8_t)addr.address); + logFoundDevice("LSM6DS3", (uint8_t)addr.address); } else { type = QMI8658; - LOG_INFO("QMI8658 Highrate 6-Axis inertial measurement sensor found"); + logFoundDevice("QMI8658", (uint8_t)addr.address); } break; - SCAN_SIMPLE_CASE(QMC5883L_ADDR, QMC5883L, "QMC5883L Highrate 3-Axis magnetic sensor found") - SCAN_SIMPLE_CASE(HMC5883L_ADDR, HMC5883L, "HMC5883L 3-Axis digital compass found") + SCAN_SIMPLE_CASE(QMC5883L_ADDR, QMC5883L, "QMC5883L", (uint8_t)addr.address) + SCAN_SIMPLE_CASE(HMC5883L_ADDR, HMC5883L, "HMC5883L", (uint8_t)addr.address) #ifdef HAS_QMA6100P - SCAN_SIMPLE_CASE(QMA6100P_ADDR, QMA6100P, "QMA6100P accelerometer found") + SCAN_SIMPLE_CASE(QMA6100P_ADDR, QMA6100P, "QMA6100P", (uint8_t)addr.address) #else - SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031 air quality sensor found") + SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031", (uint8_t)addr.address) #endif case BMA423_ADDR: // this can also be LIS3DH_ADDR_ALT registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 2); if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333 type = LIS3DH; - LOG_INFO("LIS3DH accelerometer found"); + logFoundDevice("LIS3DH", (uint8_t)addr.address); } else { type = BMA423; - LOG_INFO("BMA423 accelerometer found"); + logFoundDevice("BMA423", (uint8_t)addr.address); } break; - SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3 accelerometer found at address 0x%x", (uint8_t)addr.address); - SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535 I2C expander found"); - SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555 I2C expander found"); - SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700 light sensor found"); - SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591 light sensor found"); - SCAN_SIMPLE_CASE(OPT3001_ADDR, OPT3001, "OPT3001 light sensor found"); - SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632 IR temp sensor found"); - SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802 based scale found"); - SCAN_SIMPLE_CASE(FT6336U_ADDR, FT6336U, "FT6336U touchscreen found"); - SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048 lipo fuel gauge found"); + SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(OPT3001_ADDR, OPT3001, "OPT3001", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(FT6336U_ADDR, FT6336U, "FT6336U", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address); #ifdef HAS_TPS65233 - SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233 BIAS-T found"); + SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233", (uint8_t)addr.address); #endif case MLX90614_ADDR_DEF: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1); if (registerValue == 0x5a) { type = MLX90614; - LOG_INFO("MLX90614 IR temp sensor found"); + logFoundDevice("MLX90614", (uint8_t)addr.address); } else { type = MPR121KB; - LOG_INFO("MPR121KB keyboard found"); + logFoundDevice("MPR121KB", (uint8_t)addr.address); } break; @@ -466,15 +462,15 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); if (registerValue == 0xEA) { type = ICM20948; - LOG_INFO("ICM20948 9-dof motion processor found"); + logFoundDevice("ICM20948", (uint8_t)addr.address); break; } else if (addr.address == BMX160_ADDR) { type = BMX160; - LOG_INFO("BMX160 accelerometer found"); + logFoundDevice("BMX160", (uint8_t)addr.address); break; } else { type = MPU6050; - LOG_INFO("MPU6050 accelerometer found"); + logFoundDevice("MPU6050", (uint8_t)addr.address); break; } break; @@ -484,16 +480,16 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); if (registerValue == 0x7D) { type = CGRADSENS; - LOG_INFO("ClimateGuard RadSens Geiger-Muller Sensor found"); + logFoundDevice("ClimateGuard RadSens", (uint8_t)addr.address); break; } break; default: - LOG_INFO("Device found at address 0x%x was not able to be enumerated", addr.address); + LOG_INFO("Device found at address 0x%x was not able to be enumerated", (uint8_t)addr.address); } } else if (err == 4) { - LOG_ERROR("Unknown error at address 0x%x", addr.address); + LOG_ERROR("Unknown error at address 0x%x", (uint8_t)addr.address); } // Check if a type was found for the enumerated device - save, if so @@ -526,4 +522,9 @@ size_t ScanI2CTwoWire::countDevices() const { return foundDevices.size(); } + +void ScanI2CTwoWire::logFoundDevice(const char *device, uint8_t address) +{ + LOG_INFO("%s found at address 0x%x", device, address); +} #endif \ No newline at end of file diff --git a/src/detect/ScanI2CTwoWire.h b/src/detect/ScanI2CTwoWire.h index c8dd96469..e9f242512 100644 --- a/src/detect/ScanI2CTwoWire.h +++ b/src/detect/ScanI2CTwoWire.h @@ -58,5 +58,7 @@ class ScanI2CTwoWire : public ScanI2C uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const; DeviceType probeOLED(ScanI2C::DeviceAddress) const; + + static void logFoundDevice(const char *device, uint8_t address); }; #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index df18dae98..63c7fd29b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -572,49 +572,37 @@ void setup() LOG_DEBUG("acc_info = %i", acc_info.type); #endif -#define STRING(S) #S - -#define SCANNER_TO_SENSORS_MAP(SCANNER_T, PB_T) \ - { \ - auto found = i2cScanner->find(SCANNER_T); \ - if (found.type != ScanI2C::DeviceType::NONE) { \ - nodeTelemetrySensorsMap[PB_T].first = found.address.address; \ - nodeTelemetrySensorsMap[PB_T].second = i2cScanner->fetchI2CBus(found.address); \ - LOG_DEBUG("found i2c sensor %s", STRING(PB_T)); \ - } \ - } - - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BME_680, meshtastic_TelemetrySensorType_BME680) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BME_280, meshtastic_TelemetrySensorType_BME280) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_280, meshtastic_TelemetrySensorType_BMP280) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT31, meshtastic_TelemetrySensorType_SHT31) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHTC3, meshtastic_TelemetrySensorType_SHTC3) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::LPS22HB, meshtastic_TelemetrySensorType_LPS22) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMC6310, meshtastic_TelemetrySensorType_QMC6310) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::TSL2591, meshtastic_TelemetrySensorType_TSL25911FN) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::OPT3001, meshtastic_TelemetrySensorType_OPT3001) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::CGRADSENS, meshtastic_TelemetrySensorType_RADSENS) + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BME_680, meshtastic_TelemetrySensorType_BME680); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BME_280, meshtastic_TelemetrySensorType_BME280); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_280, meshtastic_TelemetrySensorType_BMP280); + 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::INA219, meshtastic_TelemetrySensorType_INA219); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHT31, meshtastic_TelemetrySensorType_SHT31); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHTC3, meshtastic_TelemetrySensorType_SHTC3); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::LPS22HB, meshtastic_TelemetrySensorType_LPS22); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC6310, meshtastic_TelemetrySensorType_QMC6310); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::TSL2591, meshtastic_TelemetrySensorType_TSL25911FN); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::OPT3001, meshtastic_TelemetrySensorType_OPT3001); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::CGRADSENS, meshtastic_TelemetrySensorType_RADSENS); i2cScanner.reset(); #endif @@ -1192,6 +1180,19 @@ extern meshtastic_DeviceMetadata getDeviceMetadata() #endif return deviceMetadata; } + +#if !MESHTASTIC_EXCLUDE_I2C +void scannerToSensorsMap(const std::unique_ptr &i2cScanner, ScanI2C::DeviceType deviceType, + meshtastic_TelemetrySensorType sensorType) +{ + auto found = i2cScanner->find(deviceType); + if (found.type != ScanI2C::DeviceType::NONE) { + nodeTelemetrySensorsMap[sensorType].first = found.address.address; + nodeTelemetrySensorsMap[sensorType].second = i2cScanner->fetchI2CBus(found.address); + } +} +#endif + #ifndef PIO_UNIT_TESTING void loop() { diff --git a/src/main.h b/src/main.h index 5722f7cf0..1816aef53 100644 --- a/src/main.h +++ b/src/main.h @@ -21,6 +21,9 @@ extern NimbleBluetooth *nimbleBluetooth; #include "NRF52Bluetooth.h" extern NRF52Bluetooth *nrf52Bluetooth; #endif +#if !MESHTASTIC_EXCLUDE_I2C +#include "detect/ScanI2CTwoWire.h" +#endif #if ARCH_PORTDUINO extern HardwareSPI *DisplaySPI; @@ -84,6 +87,10 @@ extern bool pauseBluetoothLogging; void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearBonds(), enterDfuMode(); meshtastic_DeviceMetadata getDeviceMetadata(); +#if !MESHTASTIC_EXCLUDE_I2C +void scannerToSensorsMap(const std::unique_ptr &i2cScanner, ScanI2C::DeviceType deviceType, + meshtastic_TelemetrySensorType sensorType); +#endif // We default to 4MHz SPI, SPI mode 0 extern SPISettings spiSettings; \ No newline at end of file From 474f9b5bfbf53be509a5bf8b0e8609979a1103c8 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 26 Nov 2024 14:00:10 -0600 Subject: [PATCH 010/132] Clean up some inline functions (#5454) --- src/gps/GeoCoord.cpp | 20 ++++++++++++++++++++ src/gps/GeoCoord.h | 29 ++++++----------------------- src/graphics/Screen.cpp | 6 +++--- src/mesh/MeshService.cpp | 2 +- src/mesh/NodeDB.cpp | 7 +++++++ src/mesh/NodeDB.h | 9 ++------- src/mesh/aes-ccm.cpp | 2 +- src/modules/PositionModule.cpp | 8 ++++---- src/modules/SerialModule.cpp | 6 +++--- src/modules/WaypointModule.cpp | 2 +- 10 files changed, 48 insertions(+), 43 deletions(-) diff --git a/src/gps/GeoCoord.cpp b/src/gps/GeoCoord.cpp index 5abb25a06..6d1f2da6d 100644 --- a/src/gps/GeoCoord.cpp +++ b/src/gps/GeoCoord.cpp @@ -574,3 +574,23 @@ const char *GeoCoord::degreesToBearing(unsigned int degrees) else return "N"; } + +double GeoCoord::pow_neg(double base, double exponent) +{ + if (exponent == 0) { + return 1; + } else if (exponent > 0) { + return pow(base, exponent); + } + return 1 / pow(base, -exponent); +} + +double GeoCoord::toRadians(double deg) +{ + return deg * PI / 180; +} + +double GeoCoord::toDegrees(double r) +{ + return r * 180 / PI; +} \ No newline at end of file diff --git a/src/gps/GeoCoord.h b/src/gps/GeoCoord.h index ecdaf0ec7..658c177b3 100644 --- a/src/gps/GeoCoord.h +++ b/src/gps/GeoCoord.h @@ -13,28 +13,6 @@ #define OLC_CODE_LEN 11 #define DEG_CONVERT (180 / PI) -// Helper functions -// Raises a number to an exponent, handling negative exponents. -static inline double pow_neg(double base, double exponent) -{ - if (exponent == 0) { - return 1; - } else if (exponent > 0) { - return pow(base, exponent); - } - return 1 / pow(base, -exponent); -} - -static inline double toRadians(double deg) -{ - return deg * PI / 180; -} - -static inline double toDegrees(double r) -{ - return r * 180 / PI; -} - // GeoCoord structs/classes // A struct to hold the data for a DMS coordinate. struct DMS { @@ -120,6 +98,11 @@ class GeoCoord static unsigned int bearingToDegrees(const char *bearing); static const char *degreesToBearing(unsigned int degrees); + // Raises a number to an exponent, handling negative exponents. + static double pow_neg(double base, double exponent); + static double toRadians(double deg); + static double toDegrees(double r); + // Point to point conversions int32_t distanceTo(const GeoCoord &pointB); int32_t bearingTo(const GeoCoord &pointB); @@ -162,4 +145,4 @@ class GeoCoord // OLC getter void getOLCCode(char *code) { strncpy(code, _olc.code, OLC_CODE_LEN + 1); } // +1 for null termination -}; +}; \ No newline at end of file diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index a875c11d6..dfef162ba 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1420,7 +1420,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ } bool hasNodeHeading = false; - if (ourNode && (hasValidPosition(ourNode) || screen->hasHeading())) { + if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading())) { const meshtastic_PositionLite &op = ourNode->position; float myHeading; if (screen->hasHeading()) @@ -1429,7 +1429,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); screen->drawCompassNorth(display, compassX, compassY, myHeading); - if (hasValidPosition(node)) { + if (nodeDB->hasValidPosition(node)) { // display direction toward node hasNodeHeading = true; const meshtastic_PositionLite &p = node->position; @@ -2757,4 +2757,4 @@ int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg) } // namespace graphics #else graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} -#endif // HAS_SCREEN +#endif // HAS_SCREEN \ No newline at end of file diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 50a13da6a..8f7717585 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -271,7 +271,7 @@ bool MeshService::trySendPosition(NodeNum dest, bool wantReplies) assert(node); - if (hasValidPosition(node)) { + if (nodeDB->hasValidPosition(node)) { #if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS if (positionModule) { LOG_INFO("Send position ping to 0x%x, wantReplies=%d, channel=%d", dest, wantReplies, node->channel); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 44a28eea2..22c32d98d 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1387,6 +1387,13 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) return lite; } +/// Sometimes we will have Position objects that only have a time, so check for +/// valid lat/lon +bool NodeDB::hasValidPosition(const meshtastic_NodeInfoLite *n) +{ + return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0); +} + /// Record an error that should be reported via analytics void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, const char *filename) { diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 2f9980f14..7e51a1240 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -165,6 +165,8 @@ class NodeDB localPosition = position; } + bool hasValidPosition(const meshtastic_NodeInfoLite *n); + private: uint32_t lastNodeDbSave = 0; // when we last saved our db to flash /// Find a node in our DB, create an empty NodeInfoLite if missing @@ -217,13 +219,6 @@ extern NodeDB *nodeDB; prefs.is_power_saving = True */ -/// Sometimes we will have Position objects that only have a time, so check for -/// valid lat/lon -static inline bool hasValidPosition(const meshtastic_NodeInfoLite *n) -{ - return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0); -} - /** The current change # for radio settings. Starts at 0 on boot and any time the radio settings * might have changed is incremented. Allows others to detect they might now be on a new channel. */ diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index b9af14fdb..8bc2989bf 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -10,7 +10,7 @@ #include "aes-ccm.h" #if !MESHTASTIC_EXCLUDE_PKI -static inline void WPA_PUT_BE16(uint8_t *a, uint16_t val) +static void WPA_PUT_BE16(uint8_t *a, uint16_t val) { a[0] = val >> 8; a[1] = val & 0xff; diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index d977cfdec..6285d7aa5 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -389,7 +389,7 @@ int32_t PositionModule::runOnce() } if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) { - if (hasValidPosition(node)) { + if (nodeDB->hasValidPosition(node)) { lastGpsSend = now; lastGpsLatitude = node->position.latitude_i; @@ -403,7 +403,7 @@ int32_t PositionModule::runOnce() } else if (config.position.position_broadcast_smart_enabled) { const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position - if (hasValidPosition(node2)) { + if (nodeDB->hasValidPosition(node2)) { // The minimum time (in seconds) that would pass before we are able to send a new position packet. auto smartPosition = getDistanceTraveledSinceLastSend(node->position); @@ -464,7 +464,7 @@ void PositionModule::handleNewPosition() meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position // We limit our GPS broadcasts to a max rate - if (hasValidPosition(node2)) { + if (nodeDB->hasValidPosition(node2)) { auto smartPosition = getDistanceTraveledSinceLastSend(node->position); uint32_t msSinceLastSend = millis() - lastGpsSend; if (smartPosition.hasTraveledOverThreshold && @@ -483,4 +483,4 @@ void PositionModule::handleNewPosition() } } -#endif +#endif \ No newline at end of file diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index 531be274e..bf53b1748 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -205,7 +205,7 @@ int32_t SerialModule::runOnce() uint32_t readIndex = 0; const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex); while (tempNodeInfo != NULL) { - if (tempNodeInfo->has_user && hasValidPosition(tempNodeInfo)) { + if (tempNodeInfo->has_user && nodeDB->hasValidPosition(tempNodeInfo)) { printWPL(outbuf, sizeof(outbuf), tempNodeInfo->position, tempNodeInfo->user.long_name, true); serialPrint->printf("%s", outbuf); } @@ -474,7 +474,7 @@ void SerialModule::processWXSerial() if (windDirPos != NULL) { // Extract data after "=" for WindDir strcpy(windDir, windDirPos + 15); // Add 15 to skip "WindDir = " - double radians = toRadians(strtof(windDir, nullptr)); + double radians = GeoCoord::toRadians(strtof(windDir, nullptr)); dir_sum_sin += sin(radians); dir_sum_cos += cos(radians); dirCount++; @@ -541,7 +541,7 @@ void SerialModule::processWXSerial() double avgCos = dir_sum_cos / dirCount; double avgRadians = atan2(avgSin, avgCos); - float dirAvg = toDegrees(avgRadians); + float dirAvg = GeoCoord::toDegrees(avgRadians); if (dirAvg < 0) { dirAvg += 360.0; diff --git a/src/modules/WaypointModule.cpp b/src/modules/WaypointModule.cpp index 48e0c4242..b8b738309 100644 --- a/src/modules/WaypointModule.cpp +++ b/src/modules/WaypointModule.cpp @@ -126,7 +126,7 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, } // If our node has a position: - if (ourNode && (hasValidPosition(ourNode) || screen->hasHeading())) { + if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading())) { const meshtastic_PositionLite &op = ourNode->position; float myHeading; if (screen->hasHeading()) From 502a83bb8abd5bc2098aa6c1c940f9b705c8c816 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 26 Nov 2024 16:39:16 -0600 Subject: [PATCH 011/132] Use isWithinTimespanMs to avoid refererence to NodeDb instance inside of NodeDb (#5453) --- src/mesh/NodeDB.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 22c32d98d..b529fa934 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1275,10 +1275,14 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde powerFSM.trigger(EVENT_NODEDB_UPDATED); notifyObservers(true); // Force an update whether or not our node counts have changed - // We just changed something about the user, store our DB - Throttle::execute( - &lastNodeDbSave, ONE_MINUTE_MS, []() { nodeDB->saveToDisk(SEGMENT_DEVICESTATE); }, - []() { LOG_DEBUG("Defer NodeDB saveToDisk for now"); }); // since we saved less than a minute ago + // We just changed something about a User, + // store our DB unless we just did so less than a minute ago + if (!Throttle::isWithinTimespanMs(lastNodeDbSave, ONE_MINUTE_MS)) { + saveToDisk(SEGMENT_DEVICESTATE); + lastNodeDbSave = millis(); + } else { + LOG_DEBUG("Defer NodeDB saveToDisk for now"); + } } return changed; From 601d912c6f554fd3c7f23017a9eb1362de2bbf8a Mon Sep 17 00:00:00 2001 From: Liam Cottle Date: Wed, 27 Nov 2024 21:45:31 +1300 Subject: [PATCH 012/132] fix cors for meshtasticd to allow use of cross origin clients (#5463) --- src/mesh/raspihttp/PiWebServer.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mesh/raspihttp/PiWebServer.cpp b/src/mesh/raspihttp/PiWebServer.cpp index f9ba9c235..846d70723 100644 --- a/src/mesh/raspihttp/PiWebServer.cpp +++ b/src/mesh/raspihttp/PiWebServer.cpp @@ -232,9 +232,9 @@ int handleAPIv1ToRadio(const struct _u_request *req, struct _u_response *res, vo ulfius_add_header_to_response(res, "X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto"); - if (req->http_verb == "OPTIONS") { + if (strcmp(req->http_verb, "OPTIONS") == 0) { ulfius_set_response_properties(res, U_OPT_STATUS, 204); - return U_CALLBACK_CONTINUE; + return U_CALLBACK_COMPLETE; } byte buffer[MAX_TO_FROM_RADIO_SIZE]; @@ -269,6 +269,11 @@ int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res, ulfius_add_header_to_response(res, "X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto"); + if (strcmp(req->http_verb, "OPTIONS") == 0) { + ulfius_set_response_properties(res, U_OPT_STATUS, 204); + return U_CALLBACK_COMPLETE; + } + uint8_t txBuf[MAX_STREAM_BUF_SIZE]; uint32_t len = 1; @@ -493,7 +498,9 @@ PiWebServerThread::PiWebServerThread() // Maximum body size sent by the client is 1 Kb instanceWeb.max_post_body_size = 1024; ulfius_add_endpoint_by_val(&instanceWeb, "GET", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, NULL); + ulfius_add_endpoint_by_val(&instanceWeb, "OPTIONS", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, NULL); ulfius_add_endpoint_by_val(&instanceWeb, "PUT", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, configWeb.rootPath); + ulfius_add_endpoint_by_val(&instanceWeb, "OPTIONS", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, NULL); // Add callback function to all endpoints for the Web Server ulfius_add_endpoint_by_val(&instanceWeb, "GET", NULL, "/*", 2, &callback_static_file, &configWeb); From 08323884825add46505fad2e6eebcfe69e452d17 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 27 Nov 2024 02:45:54 -0600 Subject: [PATCH 013/132] Remove ATECC crypto chip placeholder code (#5461) --- platformio.ini | 3 +- src/configuration.h | 3 +- src/detect/ScanI2C.h | 1 - src/detect/ScanI2CTwoWire.cpp | 52 ----------------------------------- src/detect/ScanI2CTwoWire.h | 2 -- src/main.cpp | 4 --- src/main.h | 7 ----- 7 files changed, 2 insertions(+), 70 deletions(-) diff --git a/platformio.ini b/platformio.ini index 7f4fbc3a5..cc08b33a0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -109,7 +109,6 @@ framework = arduino lib_deps = ${env.lib_deps} end2endzone/NonBlockingRTTTL@1.3.0 - https://github.com/meshtastic/SparkFun_ATECCX08a_Arduino_Library.git#5cf62b36c6f30bc72a07bdb2c11fc9a22d1e31da build_flags = ${env.build_flags} -Os build_src_filter = ${env.build_src_filter} - @@ -160,4 +159,4 @@ 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 + https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d \ No newline at end of file diff --git a/src/configuration.h b/src/configuration.h index 2e81557b1..b5727508d 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -171,7 +171,6 @@ along with this program. If not, see . // ----------------------------------------------------------------------------- // Security // ----------------------------------------------------------------------------- -#define ATECC608B_ADDR 0x35 // ----------------------------------------------------------------------------- // IO Expander @@ -362,4 +361,4 @@ along with this program. If not, see . #endif #include "DebugConfiguration.h" -#include "RF95Configuration.h" +#include "RF95Configuration.h" \ No newline at end of file diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index f4516458b..7fe3aac89 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -12,7 +12,6 @@ class ScanI2C SCREEN_SH1106, SCREEN_UNKNOWN, // has the same address as the two above but does not respond to the same commands SCREEN_ST7567, - ATECC608B, RTC_RV3028, RTC_PCF8563, CARDKB, diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index cd0c3d144..d29e5be8e 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -7,7 +7,6 @@ #include "linux/LinuxHardwareI2C.h" #endif #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) -#include "main.h" // atecc #include "meshUtils.h" // vformat #endif @@ -84,40 +83,6 @@ ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const return o_probe; } -void ScanI2CTwoWire::printATECCInfo() const -{ -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) - atecc.readConfigZone(false); - - std::string atecc_numbers = "ATECC608B Serial Number: "; - for (int i = 0; i < 9; i++) { - atecc_numbers += vformat("%02x", atecc.serialNumber[i]); - } - - atecc_numbers += ", Rev Number: "; - for (int i = 0; i < 4; i++) { - atecc_numbers += vformat("%02x", atecc.revisionNumber[i]); - } - LOG_DEBUG(atecc_numbers.c_str()); - - LOG_DEBUG("ATECC608B Config %s, Data %s, Slot 0 %s", atecc.configLockStatus ? "Locked" : "Unlocked", - atecc.dataOTPLockStatus ? "Locked" : "Unlocked", atecc.slot0LockStatus ? "Locked" : "Unlocked"); - - std::string atecc_publickey = ""; - if (atecc.configLockStatus && atecc.dataOTPLockStatus && atecc.slot0LockStatus) { - if (atecc.generatePublicKey() == false) { - atecc_publickey += "ATECC608B Error generating public key"; - } else { - atecc_publickey += "ATECC608B Public Key: "; - for (int i = 0; i < 64; i++) { - atecc_publickey += vformat("%02x", atecc.publicKey64Bytes[i]); - } - } - LOG_DEBUG(atecc_publickey.c_str()); - } -#endif -} - uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation ®isterLocation, ScanI2CTwoWire::ResponseWidth responseWidth) const { @@ -203,23 +168,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) type = probeOLED(addr); break; -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) - case ATECC608B_ADDR: -#ifdef RP2040_SLOW_CLOCK - if (atecc.begin(addr.address, Wire, Serial2) == true) -#else - if (atecc.begin(addr.address) == true) -#endif - - { - LOG_INFO("ATECC608B initialized"); - } else { - LOG_WARN("ATECC608B initialization failed"); - } - printATECCInfo(); - break; -#endif - #ifdef RV3028_RTC case RV3028_RTC: // foundDevices[addr] = RTC_RV3028; diff --git a/src/detect/ScanI2CTwoWire.h b/src/detect/ScanI2CTwoWire.h index e9f242512..d0af7cde6 100644 --- a/src/detect/ScanI2CTwoWire.h +++ b/src/detect/ScanI2CTwoWire.h @@ -53,8 +53,6 @@ class ScanI2CTwoWire : public ScanI2C concurrency::Lock lock; - void printATECCInfo() const; - uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const; DeviceType probeOLED(ScanI2C::DeviceAddress) const; diff --git a/src/main.cpp b/src/main.cpp index 63c7fd29b..9036cd59c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -150,10 +150,6 @@ ScanI2C::DeviceAddress accelerometer_found = ScanI2C::ADDRESS_NONE; // The I2C address of the RGB LED (if found) ScanI2C::FoundDevice rgb_found = ScanI2C::FoundDevice(ScanI2C::DeviceType::NONE, ScanI2C::ADDRESS_NONE); -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) -ATECCX08A atecc; -#endif - #ifdef T_WATCH_S3 Adafruit_DRV2605 drv; #endif diff --git a/src/main.h b/src/main.h index 1816aef53..b3f58ae4b 100644 --- a/src/main.h +++ b/src/main.h @@ -10,9 +10,6 @@ #include "mesh/generated/meshtastic/telemetry.pb.h" #include #include -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) -#include -#endif #if defined(ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) #include "nimble/NimbleBluetooth.h" extern NimbleBluetooth *nimbleBluetooth; @@ -42,10 +39,6 @@ extern bool pmu_found; extern bool isCharging; extern bool isUSBPowered; -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) -extern ATECCX08A atecc; -#endif - #ifdef T_WATCH_S3 #include extern Adafruit_DRV2605 drv; From b00c05012d70e4a0233f5caa749abe5aa975313c Mon Sep 17 00:00:00 2001 From: Christopher Hoover Date: Wed, 27 Nov 2024 02:56:25 -0800 Subject: [PATCH 014/132] 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 --- src/gps/GPS.cpp | 81 +++++++++++++++++++++++++++++++------------------ src/gps/GPS.h | 24 ++------------- 2 files changed, 54 insertions(+), 51 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index d49092fff..dcece305a 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -28,6 +28,12 @@ #define GPS_RESET_MODE HIGH #endif +// Not all platforms have std::size(). +template std::size_t array_count(const T (&)[N]) +{ + return N; +} + #if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) HardwareSerial *GPS::_serial_gps = &Serial1; #elif defined(ARCH_RP2040) @@ -46,8 +52,14 @@ static GPSUpdateScheduling scheduling; /// only init that port once. static bool didSerialInit; -static struct uBloxGnssModelInfo info; -static uint8_t uBloxProtocolVersion; +static struct uBloxGnssModelInfo { + char swVersion[30]; + char hwVersion[10]; + uint8_t extensionNo; + char extension[10][30]; + uint8_t protocol_version; +} ublox_info; + #define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway #define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc) @@ -412,6 +424,15 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t return 0; } +#if GPS_BAUDRATE_FIXED +// if GPS_BAUDRATE is specified in variant, only try that. +static const int serialSpeeds[1] = {GPS_BAUDRATE}; +static const int rareSerialSpeeds[1] = {GPS_BAUDRATE}; +#else +static const int serialSpeeds[3] = {9600, 115200, 38400}; +static const int rareSerialSpeeds[3] = {4800, 57600, GPS_BAUDRATE}; +#endif + /** * @brief Setup the GPS based on the model detected. * We detect the GPS by cycling through a set of baud rates, first common then rare. @@ -428,7 +449,7 @@ bool GPS::setup() LOG_DEBUG("Probe for GPS at %d", serialSpeeds[speedSelect]); gnssModel = probe(serialSpeeds[speedSelect]); if (gnssModel == GNSS_MODEL_UNKNOWN) { - if (++speedSelect == sizeof(serialSpeeds) / sizeof(int)) { + if (++speedSelect == array_count(serialSpeeds)) { speedSelect = 0; ++probeTries; } @@ -439,7 +460,7 @@ bool GPS::setup() LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]); gnssModel = probe(rareSerialSpeeds[speedSelect]); if (gnssModel == GNSS_MODEL_UNKNOWN) { - if (++speedSelect == sizeof(rareSerialSpeeds) / sizeof(int)) { + if (++speedSelect == array_count(rareSerialSpeeds)) { LOG_WARN("Give up on GPS probe and set to %d", GPS_BAUDRATE); return true; } @@ -635,7 +656,7 @@ bool GPS::setup() SEND_UBX_PACKET(0x06, 0x01, _message_RMC, "enable NMEA RMC", 500); SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "enable NMEA GGA", 500); - if (uBloxProtocolVersion >= 18) { + if (ublox_info.protocol_version >= 18) { clearBuffer(); SEND_UBX_PACKET(0x06, 0x86, _message_PMS, "enable powersave for GPS", 500); SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500); @@ -1131,7 +1152,7 @@ GnssModel_t GPS::probe(int serialSpeed) } #endif - memset(&info, 0, sizeof(struct uBloxGnssModelInfo)); + memset(&ublox_info, 0, sizeof(ublox_info)); uint8_t buffer[768] = {0}; delay(100); @@ -1198,64 +1219,64 @@ GnssModel_t GPS::probe(int serialSpeed) if (len) { uint16_t position = 0; for (int i = 0; i < 30; i++) { - info.swVersion[i] = buffer[position]; + ublox_info.swVersion[i] = buffer[position]; position++; } for (int i = 0; i < 10; i++) { - info.hwVersion[i] = buffer[position]; + ublox_info.hwVersion[i] = buffer[position]; position++; } while (len >= position + 30) { for (int i = 0; i < 30; i++) { - info.extension[info.extensionNo][i] = buffer[position]; + ublox_info.extension[ublox_info.extensionNo][i] = buffer[position]; position++; } - info.extensionNo++; - if (info.extensionNo > 9) + ublox_info.extensionNo++; + if (ublox_info.extensionNo > 9) break; } LOG_DEBUG("Module Info : "); - LOG_DEBUG("Soft version: %s", info.swVersion); - LOG_DEBUG("Hard version: %s", info.hwVersion); - LOG_DEBUG("Extensions:%d", info.extensionNo); - for (int i = 0; i < info.extensionNo; i++) { - LOG_DEBUG(" %s", info.extension[i]); + LOG_DEBUG("Soft version: %s", ublox_info.swVersion); + LOG_DEBUG("Hard version: %s", ublox_info.hwVersion); + LOG_DEBUG("Extensions:%d", ublox_info.extensionNo); + for (int i = 0; i < ublox_info.extensionNo; i++) { + LOG_DEBUG(" %s", ublox_info.extension[i]); } memset(buffer, 0, sizeof(buffer)); // tips: extensionNo field is 0 on some 6M GNSS modules - for (int i = 0; i < info.extensionNo; ++i) { - if (!strncmp(info.extension[i], "MOD=", 4)) { - strncpy((char *)buffer, &(info.extension[i][4]), sizeof(buffer)); - } else if (!strncmp(info.extension[i], "PROTVER", 7)) { + for (int i = 0; i < ublox_info.extensionNo; ++i) { + if (!strncmp(ublox_info.extension[i], "MOD=", 4)) { + strncpy((char *)buffer, &(ublox_info.extension[i][4]), sizeof(buffer)); + } else if (!strncmp(ublox_info.extension[i], "PROTVER", 7)) { char *ptr = nullptr; memset(buffer, 0, sizeof(buffer)); - strncpy((char *)buffer, &(info.extension[i][8]), sizeof(buffer)); + strncpy((char *)buffer, &(ublox_info.extension[i][8]), sizeof(buffer)); LOG_DEBUG("Protocol Version:%s", (char *)buffer); if (strlen((char *)buffer)) { - uBloxProtocolVersion = strtoul((char *)buffer, &ptr, 10); - LOG_DEBUG("ProtVer=%d", uBloxProtocolVersion); + ublox_info.protocol_version = strtoul((char *)buffer, &ptr, 10); + LOG_DEBUG("ProtVer=%d", ublox_info.protocol_version); } else { - uBloxProtocolVersion = 0; + ublox_info.protocol_version = 0; } } } - if (strncmp(info.hwVersion, "00040007", 8) == 0) { + if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) { LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6"); return GNSS_MODEL_UBLOX6; - } else if (strncmp(info.hwVersion, "00070000", 8) == 0) { + } else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) { LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7"); return GNSS_MODEL_UBLOX7; - } else if (strncmp(info.hwVersion, "00080000", 8) == 0) { + } else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) { LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8"); return GNSS_MODEL_UBLOX8; - } else if (strncmp(info.hwVersion, "00190000", 8) == 0) { + } else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) { LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9"); return GNSS_MODEL_UBLOX9; - } else if (strncmp(info.hwVersion, "000A0000", 8) == 0) { + } else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) { LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10"); return GNSS_MODEL_UBLOX10; } @@ -1729,4 +1750,4 @@ void GPS::toggleGpsMode() enable(); } } -#endif // Exclude GPS +#endif // Exclude GPS \ No newline at end of file diff --git a/src/gps/GPS.h b/src/gps/GPS.h index cc72350d2..74d73e39a 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -16,13 +16,6 @@ #define GPS_EN_ACTIVE 1 #endif -struct uBloxGnssModelInfo { - char swVersion[30]; - char hwVersion[10]; - uint8_t extensionNo; - char extension[10][30]; -}; - typedef enum { GNSS_MODEL_ATGM336H, GNSS_MODEL_MTK, @@ -119,7 +112,9 @@ class GPS : private concurrency::OSThread // Let the GPS hardware save power between updates void down(); - protected: + private: + GPS() : concurrency::OSThread("GPS") {} + /// Record that we have a GPS void setConnected(); @@ -147,9 +142,6 @@ class GPS : private concurrency::OSThread GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN; - private: - GPS() : concurrency::OSThread("GPS") {} - TinyGPSPlus reader; uint8_t fixQual = 0; // fix quality from GPGGA uint32_t lastChecksumFailCount = 0; @@ -161,14 +153,6 @@ class GPS : private concurrency::OSThread TinyGPSCustom gsapdop; // custom extract PDOP from GPGSA uint8_t fixType = 0; // fix type from GPGSA #endif -#if GPS_BAUDRATE_FIXED - // if GPS_BAUDRATE is specified in variant, only try that. - const int serialSpeeds[1] = {GPS_BAUDRATE}; - const int rareSerialSpeeds[1] = {GPS_BAUDRATE}; -#else - const int serialSpeeds[3] = {9600, 115200, 38400}; - const int rareSerialSpeeds[3] = {4800, 57600, GPS_BAUDRATE}; -#endif uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0; uint32_t rx_gpio = 0; @@ -252,8 +236,6 @@ class GPS : private concurrency::OSThread // delay counter to allow more sats before fixed position stops GPS thread uint8_t fixeddelayCtr = 0; - - const char *powerStateToString(); }; extern GPS *gps; From 8df7a035e2939fcdc5eaac90e85da02d7d1f8187 Mon Sep 17 00:00:00 2001 From: panaceya Date: Thu, 28 Nov 2024 13:34:09 +0200 Subject: [PATCH 015/132] Fix ukrainian fonts (#5468) * FIX: rollback to !4624 * UPDATE: new 16 and 24 UA Fonts and fixes --- src/graphics/Screen.cpp | 3 +- src/graphics/Screen.h | 1 + src/graphics/ScreenFonts.h | 12 +- src/graphics/fonts/OLEDDisplayFontsUA.cpp | 1759 ++++++++++++++++++-- src/graphics/fonts/OLEDDisplayFontsUA.h | 2 + src/modules/ExternalNotificationModule.cpp | 3 +- 6 files changed, 1645 insertions(+), 135 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index dfef162ba..2ab413bc5 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -20,7 +20,6 @@ along with this program. If not, see . */ #include "Screen.h" -#include "../userPrefs.h" #include "PowerMon.h" #include "Throttle.h" #include "configuration.h" @@ -2757,4 +2756,4 @@ int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg) } // namespace graphics #else graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} -#endif // HAS_SCREEN \ No newline at end of file +#endif // HAS_SCREEN diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 2a77ca575..41c90ca9a 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -1,5 +1,6 @@ #pragma once +#include "../userPrefs.h" #include "configuration.h" #include "detect/ScanI2C.h" diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index c9ce961fa..032348d54 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -27,14 +27,22 @@ #define FONT_SMALL ArialMT_Plain_10_RU #else #ifdef OLED_UA -#define FONT_SMALL ArialMT_Plain_10_UA +#define FONT_SMALL ArialMT_Plain_10_UA // Height: 13 #else #define FONT_SMALL ArialMT_Plain_10 // Height: 13 #endif #endif #endif +#ifdef OLED_UA +#define FONT_MEDIUM ArialMT_Plain_16_UA // Height: 19 +#else #define FONT_MEDIUM ArialMT_Plain_16 // Height: 19 -#define FONT_LARGE ArialMT_Plain_24 // Height: 28 +#endif +#ifdef OLED_UA +#define FONT_LARGE ArialMT_Plain_24_UA // Height: 28 +#else +#define FONT_LARGE ArialMT_Plain_24 // Height: 28 +#endif #endif #define _fontHeight(font) ((font)[1] + 1) // height is position 1 diff --git a/src/graphics/fonts/OLEDDisplayFontsUA.cpp b/src/graphics/fonts/OLEDDisplayFontsUA.cpp index 0295ee6ba..2a97526ef 100644 --- a/src/graphics/fonts/OLEDDisplayFontsUA.cpp +++ b/src/graphics/fonts/OLEDDisplayFontsUA.cpp @@ -166,71 +166,71 @@ const uint8_t ArialMT_Plain_10_UA[] PROGMEM = { 0x04, 0x6F, 0x10, 0x09, // 188 0x04, 0x7F, 0x10, 0x09, // 189 0x04, 0x8F, 0x10, 0x09, // 190 - 0x04, 0x9F, 0x0A, 0x06, // 191 - 0x04, 0xA9, 0x0C, 0x07, // 192 - 0x04, 0xB5, 0x0C, 0x07, // 193 - 0x04, 0xC1, 0x0C, 0x07, // 194 - 0x04, 0xCD, 0x0B, 0x07, // 195 - 0x04, 0xD8, 0x0C, 0x07, // 196 - 0x04, 0xE4, 0x0C, 0x07, // 197 - 0x04, 0xF0, 0x0C, 0x07, // 198 - 0x04, 0xFC, 0x0C, 0x07, // 199 - 0x05, 0x08, 0x0C, 0x07, // 200 - 0x05, 0x14, 0x0C, 0x07, // 201 - 0x05, 0x20, 0x0C, 0x07, // 202 - 0x05, 0x2C, 0x0C, 0x07, // 203 - 0x05, 0x38, 0x0C, 0x07, // 204 - 0x05, 0x44, 0x0C, 0x07, // 205 - 0x05, 0x50, 0x0C, 0x07, // 206 - 0x05, 0x5C, 0x0C, 0x07, // 207 - 0x05, 0x68, 0x0B, 0x07, // 208 - 0x05, 0x73, 0x0C, 0x07, // 209 - 0x05, 0x7F, 0x0B, 0x07, // 210 - 0x05, 0x8A, 0x0C, 0x07, // 211 - 0x05, 0x96, 0x0B, 0x07, // 212 - 0x05, 0xA1, 0x0C, 0x07, // 213 - 0x05, 0xAD, 0x0C, 0x07, // 214 - 0x05, 0xB9, 0x0C, 0x07, // 215 - 0x05, 0xC5, 0x0C, 0x07, // 216 - 0x05, 0xD1, 0x0E, 0x08, // 217 - 0x05, 0xDF, 0x0C, 0x07, // 218 - 0x05, 0xEB, 0x0C, 0x07, // 219 - 0x05, 0xF7, 0x0C, 0x07, // 220 - 0x06, 0x03, 0x0C, 0x07, // 221 - 0x06, 0x0F, 0x0C, 0x07, // 222 - 0x06, 0x1B, 0x0C, 0x07, // 223 - 0x06, 0x27, 0x0C, 0x07, // 224 - 0x06, 0x33, 0x0C, 0x07, // 225 - 0x06, 0x3F, 0x0C, 0x07, // 226 - 0x06, 0x4B, 0x0B, 0x07, // 227 - 0x06, 0x56, 0x0C, 0x07, // 228 - 0x06, 0x62, 0x0B, 0x07, // 229 - 0x06, 0x6D, 0x0C, 0x07, // 230 - 0x06, 0x79, 0x0C, 0x07, // 231 - 0x06, 0x85, 0x0C, 0x07, // 232 - 0x06, 0x91, 0x0C, 0x07, // 233 - 0x06, 0x9D, 0x0C, 0x07, // 234 - 0x06, 0xA9, 0x0C, 0x07, // 235 - 0x06, 0xB5, 0x0C, 0x07, // 236 - 0x06, 0xC1, 0x0C, 0x07, // 237 - 0x06, 0xCD, 0x0C, 0x07, // 238 - 0x06, 0xD9, 0x0C, 0x07, // 239 - 0x06, 0xE5, 0x0B, 0x07, // 240 - 0x06, 0xF0, 0x0C, 0x07, // 241 - 0x06, 0xFC, 0x0B, 0x07, // 242 - 0x07, 0x07, 0x0C, 0x07, // 243 - 0x07, 0x13, 0x0B, 0x07, // 244 - 0x07, 0x1E, 0x0C, 0x07, // 245 - 0x07, 0x2A, 0x0C, 0x07, // 246 - 0x07, 0x36, 0x0C, 0x07, // 247 - 0x07, 0x42, 0x0C, 0x07, // 248 - 0x07, 0x4E, 0x0E, 0x08, // 249 - 0x07, 0x5C, 0x0C, 0x07, // 250 - 0x07, 0x68, 0x0C, 0x07, // 251 - 0x07, 0x74, 0x0C, 0x07, // 252 - 0x07, 0x80, 0x0C, 0x07, // 253 - 0x07, 0x8C, 0x0C, 0x07, // 254 - 0x07, 0x98, 0x0C, 0x07, // 255 + 0x04, 0x9F, 0x06, 0x04, // 191 + 0x04, 0xA5, 0x0A, 0x06, // 192 + 0x04, 0xAF, 0x0A, 0x06, // 193 + 0x04, 0xB9, 0x0A, 0x06, // 194 + 0x04, 0xC3, 0x09, 0x06, // 195 + 0x04, 0xCC, 0x0A, 0x06, // 196 + 0x04, 0xD6, 0x0A, 0x06, // 197 + 0x04, 0xE0, 0x0A, 0x06, // 198 + 0x04, 0xEA, 0x08, 0x05, // 199 + 0x04, 0xF2, 0x0A, 0x06, // 200 + 0x04, 0xFC, 0x0A, 0x06, // 201 + 0x05, 0x06, 0x0A, 0x06, // 202 + 0x05, 0x10, 0x0A, 0x06, // 203 + 0x05, 0x1A, 0x0A, 0x06, // 204 + 0x05, 0x24, 0x0A, 0x06, // 205 + 0x05, 0x2E, 0x0A, 0x06, // 206 + 0x05, 0x38, 0x0A, 0x06, // 207 + 0x05, 0x42, 0x09, 0x06, // 208 + 0x05, 0x4B, 0x0A, 0x06, // 209 + 0x05, 0x55, 0x09, 0x06, // 210 + 0x05, 0x5E, 0x0A, 0x06, // 211 + 0x05, 0x68, 0x09, 0x06, // 212 + 0x05, 0x71, 0x0A, 0x06, // 213 + 0x05, 0x7B, 0x0A, 0x06, // 214 + 0x05, 0x85, 0x08, 0x05, // 215 + 0x05, 0x8D, 0x0A, 0x06, // 216 + 0x05, 0x97, 0x0C, 0x07, // 217 + 0x05, 0xA3, 0x0A, 0x06, // 218 + 0x05, 0xAD, 0x0A, 0x06, // 219 + 0x05, 0xB7, 0x08, 0x05, // 220 + 0x05, 0xBF, 0x0A, 0x06, // 221 + 0x05, 0xC9, 0x0A, 0x06, // 222 + 0x05, 0xD3, 0x0A, 0x06, // 223 + 0x05, 0xDD, 0x08, 0x05, // 224 + 0x05, 0xE5, 0x08, 0x05, // 225 + 0x05, 0xED, 0x08, 0x05, // 226 + 0x05, 0xF5, 0x07, 0x05, // 227 + 0x05, 0xFC, 0x0A, 0x06, // 228 + 0x06, 0x06, 0x09, 0x06, // 229 + 0x06, 0x0F, 0x0A, 0x06, // 230 + 0x06, 0x19, 0x08, 0x05, // 231 + 0x06, 0x21, 0x0A, 0x06, // 232 + 0x06, 0x2B, 0x0A, 0x06, // 233 + 0x06, 0x35, 0x06, 0x04, // 234 + 0x06, 0x3B, 0x08, 0x05, // 235 + 0x06, 0x43, 0x0A, 0x06, // 236 + 0x06, 0x4D, 0x08, 0x05, // 237 + 0x06, 0x55, 0x08, 0x05, // 238 + 0x06, 0x5D, 0x08, 0x05, // 239 + 0x06, 0x65, 0x05, 0x04, // 240 + 0x06, 0x6A, 0x08, 0x05, // 241 + 0x06, 0x72, 0x05, 0x04, // 242 + 0x06, 0x77, 0x08, 0x05, // 243 + 0x06, 0x7F, 0x09, 0x06, // 244 + 0x06, 0x88, 0x0A, 0x06, // 245 + 0x06, 0x92, 0x08, 0x05, // 246 + 0x06, 0x9A, 0x08, 0x05, // 247 + 0x06, 0xA2, 0x0A, 0x06, // 248 + 0x06, 0xAC, 0x0C, 0x07, // 249 + 0x06, 0xB8, 0x08, 0x05, // 250 + 0x06, 0xC0, 0x08, 0x05, // 251 + 0x06, 0xC8, 0x08, 0x05, // 252 + 0x06, 0xD0, 0x08, 0x05, // 253 + 0x06, 0xD8, 0x0A, 0x06, // 254 + 0x06, 0xE2, 0x08, 0x05, // 255 // Font Data: 0x00, 0x00, 0xF8, 0x02, // 33 0x38, 0x00, 0x00, 0x00, 0x38, // 34 @@ -356,69 +356,1568 @@ const uint8_t ArialMT_Plain_10_UA[] PROGMEM = { 0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0xC0, 0x00, 0x20, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 188 0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0x80, 0x00, 0x60, 0x00, 0x50, 0x02, 0x48, 0x03, 0xC0, 0x02, // 189 0x48, 0x00, 0x58, 0x00, 0x68, 0x03, 0x80, 0x00, 0x60, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 190 - 0x00, 0x00, 0x00, 0x00, 0x28, 0x02, 0xE0, 0x03, 0x28, 0x02, // 191 - 0x00, 0x00, 0xF0, 0x03, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xF0, 0x03, // 192 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x88, 0x01, // 193 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 194 - 0x00, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x18, // 195 - 0x00, 0x00, 0x00, 0x02, 0xFC, 0x03, 0x04, 0x02, 0xFC, 0x03, 0x00, 0x02, // 196 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x08, 0x02, // 197 - 0x00, 0x00, 0xB8, 0x03, 0x40, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xB8, 0x03, // 198 - 0x00, 0x00, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 199 - 0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0xF8, 0x03, // 200 - 0x00, 0x00, 0xE0, 0x03, 0x08, 0x01, 0x90, 0x00, 0x48, 0x00, 0xE0, 0x03, // 201 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xA0, 0x00, 0x10, 0x01, 0x08, 0x02, // 202 - 0x00, 0x00, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 203 - 0x00, 0x00, 0xF8, 0x03, 0x10, 0x00, 0x60, 0x00, 0x10, 0x00, 0xF8, 0x03, // 204 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 205 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 206 - 0x00, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 207 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 208 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 209 - 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 210 - 0x00, 0x00, 0x38, 0x00, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0xF8, 0x01, // 211 - 0x00, 0x00, 0x70, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x88, 0x00, 0x70, // 212 - 0x00, 0x00, 0x18, 0x03, 0xA0, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x18, 0x03, // 213 - 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, // 214 - 0x00, 0x00, 0x38, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 215 - 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, // 216 - 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x06, // 217 - 0x00, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 218 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, 0xF8, 0x03, // 219 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 220 - 0x00, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 221 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xF0, 0x01, 0x08, 0x02, 0xF0, 0x01, // 222 - 0x00, 0x00, 0x30, 0x02, 0x48, 0x01, 0xC8, 0x00, 0x48, 0x00, 0xF8, 0x03, // 223 - 0x00, 0x00, 0x00, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x03, // 224 - 0x00, 0x00, 0xE0, 0x01, 0x50, 0x02, 0x50, 0x02, 0x48, 0x02, 0x88, 0x01, // 225 - 0x00, 0x00, 0xE0, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 226 - 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, // 227 - 0x00, 0x00, 0x00, 0x02, 0xC0, 0x03, 0x20, 0x02, 0xE0, 0x03, 0x00, 0x02, // 228 - 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, // 229 - 0x00, 0x00, 0x60, 0x03, 0x80, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x60, 0x03, // 230 - 0x00, 0x00, 0x20, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 231 - 0x00, 0x00, 0xE0, 0x03, 0x00, 0x01, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 232 - 0x00, 0x00, 0xE0, 0x03, 0x00, 0x01, 0x98, 0x00, 0x40, 0x00, 0xE0, 0x03, // 233 - 0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 234 - 0x00, 0x00, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, // 235 - 0x00, 0x00, 0xE0, 0x03, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 236 - 0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 237 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 238 - 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, // 239 - 0x00, 0x00, 0xE0, 0x03, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 240 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x40, 0x02, // 241 - 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, // 242 - 0x00, 0x00, 0x60, 0x00, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0xE0, 0x01, // 243 - 0x00, 0x00, 0xC0, 0x00, 0x20, 0x01, 0xE0, 0x03, 0x20, 0x01, 0xC0, // 244 - 0x00, 0x00, 0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 245 - 0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, // 246 - 0x00, 0x00, 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 247 - 0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, // 248 - 0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x06, // 249 - 0x00, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, // 250 - 0x00, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, 0xE0, 0x03, // 251 - 0x00, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, // 252 - 0x00, 0x00, 0x40, 0x01, 0x20, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, // 253 - 0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, 0xC0, 0x01, // 254 - 0x00, 0x00, 0x40, 0x02, 0xA0, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0xE0, 0x03, // 255 + 0x28, 0x02, 0xE0, 0x03, 0x28, 0x02, // 191 + 0xF0, 0x03, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xF0, 0x03, // 192 + 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x88, 0x01, // 193 + 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 194 + 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x18, // 195 + 0x00, 0x02, 0xFC, 0x03, 0x04, 0x02, 0xFC, 0x03, 0x00, 0x02, // 196 + 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x08, 0x02, // 197 + 0xB8, 0x03, 0x40, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xB8, 0x03, // 198 + 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 199 + 0xF8, 0x03, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0xF8, 0x03, // 200 + 0xE0, 0x03, 0x08, 0x01, 0x90, 0x00, 0x48, 0x00, 0xE0, 0x03, // 201 + 0xF8, 0x03, 0x40, 0x00, 0xA0, 0x00, 0x10, 0x01, 0x08, 0x02, // 202 + 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 203 + 0xF8, 0x03, 0x10, 0x00, 0x60, 0x00, 0x10, 0x00, 0xF8, 0x03, // 204 + 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 205 + 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 206 + 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 207 + 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 208 + 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 209 + 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 210 + 0x38, 0x00, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0xF8, 0x01, // 211 + 0x70, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x88, 0x00, 0x70, // 212 + 0x18, 0x03, 0xA0, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x18, 0x03, // 213 + 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, // 214 + 0x38, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 215 + 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, // 216 + 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x06, // 217 + 0x08, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 218 + 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, 0xF8, 0x03, // 219 + 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 220 + 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 221 + 0xF8, 0x03, 0x40, 0x00, 0xF0, 0x01, 0x08, 0x02, 0xF0, 0x01, // 222 + 0x30, 0x02, 0x48, 0x01, 0xC8, 0x00, 0x48, 0x00, 0xF8, 0x03, // 223 + 0x00, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x01, // 224 + 0xE0, 0x01, 0x50, 0x02, 0x48, 0x02, 0x88, 0x01, // 225 + 0xE0, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 226 + 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0x60, // 227 + 0x00, 0x02, 0xC0, 0x03, 0x20, 0x02, 0xE0, 0x03, 0x00, 0x02, // 228 + 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, // 229 + 0x60, 0x03, 0x80, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x60, 0x03, // 230 + 0x20, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 231 + 0xE0, 0x03, 0x00, 0x01, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 232 + 0xE0, 0x03, 0x08, 0x01, 0x88, 0x00, 0x48, 0x00, 0xE0, 0x03, // 233 + 0xE0, 0x03, 0x80, 0x00, 0x60, 0x03, // 234 + 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xE0, 0x03, // 235 + 0xE0, 0x03, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 236 + 0xE0, 0x03, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 237 + 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 238 + 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, // 239 + 0xE0, 0x03, 0xA0, 0x00, 0x40, // 240 + 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 241 + 0x20, 0x00, 0xE0, 0x03, 0x20, // 242 + 0x60, 0x00, 0x80, 0x02, 0x80, 0x02, 0xE0, 0x01, // 243 + 0xC0, 0x00, 0x20, 0x01, 0xE0, 0x03, 0x20, 0x01, 0xC0, // 244 + 0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 245 + 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x06, // 246 + 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 247 + 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, // 248 + 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x06, // 249 + 0x20, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x00, 0x01, // 250 + 0xE0, 0x03, 0x80, 0x02, 0x00, 0x01, 0xE0, 0x03, // 251 + 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, // 252 + 0x40, 0x01, 0x20, 0x02, 0xA0, 0x02, 0xC0, 0x01, // 253 + 0xE0, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, 0xC0, 0x01, // 254 + 0x40, 0x02, 0xA0, 0x01, 0xA0, 0x00, 0xE0, 0x03, // 255 +}; + +const uint8_t ArialMT_Plain_16_UA[] PROGMEM = { + 0x10, // Width: 16 + 0x13, // Height: 19 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + // Jump Table: + 0xFF, 0xFF, 0x00, 0x04, // 32= :65535 + 0x00, 0x00, 0x08, 0x05, // 33=!:0 + 0x00, 0x08, 0x0D, 0x06, // 34=":8 + 0x00, 0x15, 0x1A, 0x09, // 35=#:21 + 0x00, 0x2F, 0x17, 0x09, // 36=$:47 + 0x00, 0x46, 0x26, 0x0E, // 37=%:70 + 0x00, 0x6C, 0x1D, 0x0B, // 38=&:108 + 0x00, 0x89, 0x04, 0x03, // 39=':137 + 0x00, 0x8D, 0x0C, 0x05, // 40=(:141 + 0x00, 0x99, 0x0B, 0x05, // 41=):153 + 0x00, 0xA4, 0x0D, 0x06, // 42=*:164 + 0x00, 0xB1, 0x17, 0x09, // 43=+:177 + 0x00, 0xC8, 0x09, 0x04, // 44=,:200 + 0x00, 0xD1, 0x0B, 0x05, // 45=-:209 + 0x00, 0xDC, 0x08, 0x04, // 46=.:220 + 0x00, 0xE4, 0x0A, 0x04, // 47=/:228 + 0x00, 0xEE, 0x17, 0x09, // 48=0:238 + 0x01, 0x05, 0x11, 0x09, // 49=1:261 + 0x01, 0x16, 0x17, 0x09, // 50=2:278 + 0x01, 0x2D, 0x17, 0x09, // 51=3:301 + 0x01, 0x44, 0x17, 0x09, // 52=4:324 + 0x01, 0x5B, 0x17, 0x09, // 53=5:347 + 0x01, 0x72, 0x17, 0x09, // 54=6:370 + 0x01, 0x89, 0x16, 0x09, // 55=7:393 + 0x01, 0x9F, 0x17, 0x09, // 56=8:415 + 0x01, 0xB6, 0x17, 0x09, // 57=9:438 + 0x01, 0xCD, 0x05, 0x04, // 58=::461 + 0x01, 0xD2, 0x06, 0x04, // 59=;:466 + 0x01, 0xD8, 0x17, 0x09, // 60=<:472 + 0x01, 0xEF, 0x17, 0x09, // 61==:495 + 0x02, 0x06, 0x17, 0x09, // 62=>:518 + 0x02, 0x1D, 0x16, 0x09, // 63=?:541 + 0x02, 0x33, 0x2F, 0x10, // 64=@:563 + 0x02, 0x62, 0x1D, 0x0B, // 65=A:610 + 0x02, 0x7F, 0x1D, 0x0B, // 66=B:639 + 0x02, 0x9C, 0x20, 0x0C, // 67=C:668 + 0x02, 0xBC, 0x20, 0x0C, // 68=D:700 + 0x02, 0xDC, 0x1D, 0x0B, // 69=E:732 + 0x02, 0xF9, 0x19, 0x0A, // 70=F:761 + 0x03, 0x12, 0x20, 0x0C, // 71=G:786 + 0x03, 0x32, 0x1D, 0x0B, // 72=H:818 + 0x03, 0x4F, 0x05, 0x03, // 73=I:847 + 0x03, 0x54, 0x14, 0x08, // 74=J:852 + 0x03, 0x68, 0x1D, 0x0B, // 75=K:872 + 0x03, 0x85, 0x17, 0x09, // 76=L:901 + 0x03, 0x9C, 0x23, 0x0D, // 77=M:924 + 0x03, 0xBF, 0x1D, 0x0B, // 78=N:959 + 0x03, 0xDC, 0x20, 0x0C, // 79=O:988 + 0x03, 0xFC, 0x1C, 0x0B, // 80=P:1020 + 0x04, 0x18, 0x20, 0x0C, // 81=Q:1048 + 0x04, 0x38, 0x1D, 0x0B, // 82=R:1080 + 0x04, 0x55, 0x1D, 0x0B, // 83=S:1109 + 0x04, 0x72, 0x19, 0x09, // 84=T:1138 + 0x04, 0x8B, 0x1D, 0x0B, // 85=U:1163 + 0x04, 0xA8, 0x1C, 0x0B, // 86=V:1192 + 0x04, 0xC4, 0x2B, 0x0F, // 87=W:1220 + 0x04, 0xEF, 0x20, 0x0B, // 88=X:1263 + 0x05, 0x0F, 0x19, 0x09, // 89=Y:1295 + 0x05, 0x28, 0x1A, 0x09, // 90=Z:1320 + 0x05, 0x42, 0x0C, 0x04, // 91=[:1346 + 0x05, 0x4E, 0x0B, 0x04, // 92=\:1358 + 0x05, 0x59, 0x09, 0x04, // 93=]:1369 + 0x05, 0x62, 0x14, 0x07, // 94=^:1378 + 0x05, 0x76, 0x1B, 0x09, // 95=_:1398 + 0x05, 0x91, 0x07, 0x05, // 96=`:1425 + 0x05, 0x98, 0x17, 0x09, // 97=a:1432 + 0x05, 0xAF, 0x17, 0x09, // 98=b:1455 + 0x05, 0xC6, 0x14, 0x08, // 99=c:1478 + 0x05, 0xDA, 0x17, 0x09, // 100=d:1498 + 0x05, 0xF1, 0x17, 0x09, // 101=e:1521 + 0x06, 0x08, 0x0A, 0x04, // 102=f:1544 + 0x06, 0x12, 0x17, 0x09, // 103=g:1554 + 0x06, 0x29, 0x14, 0x08, // 104=h:1577 + 0x06, 0x3D, 0x05, 0x04, // 105=i:1597 + 0x06, 0x42, 0x06, 0x03, // 106=j:1602 + 0x06, 0x48, 0x17, 0x08, // 107=k:1608 + 0x06, 0x5F, 0x05, 0x03, // 108=l:1631 + 0x06, 0x64, 0x23, 0x0D, // 109=m:1636 + 0x06, 0x87, 0x14, 0x08, // 110=n:1671 + 0x06, 0x9B, 0x17, 0x09, // 111=o:1691 + 0x06, 0xB2, 0x17, 0x09, // 112=p:1714 + 0x06, 0xC9, 0x18, 0x09, // 113=q:1737 + 0x06, 0xE1, 0x0D, 0x05, // 114=r:1761 + 0x06, 0xEE, 0x14, 0x08, // 115=s:1774 + 0x07, 0x02, 0x0B, 0x04, // 116=t:1794 + 0x07, 0x0D, 0x14, 0x08, // 117=u:1805 + 0x07, 0x21, 0x13, 0x07, // 118=v:1825 + 0x07, 0x34, 0x1F, 0x0B, // 119=w:1844 + 0x07, 0x53, 0x14, 0x07, // 120=x:1875 + 0x07, 0x67, 0x13, 0x07, // 121=y:1895 + 0x07, 0x7A, 0x14, 0x07, // 122=z:1914 + 0x07, 0x8E, 0x0F, 0x05, // 123={:1934 + 0x07, 0x9D, 0x06, 0x03, // 124=|:1949 + 0x07, 0xA3, 0x0E, 0x05, // 125=}:1955 + 0x07, 0xB1, 0x17, 0x09, // 126=~:1969 + 0x07, 0xC8, 0x1D, 0x0C, // 127=:1992 + 0x07, 0xE5, 0x26, 0x0E, // 1026=Ђ:2021 + 0x08, 0x0B, 0x19, 0x09, // 1027=Ѓ:2059 + 0x08, 0x24, 0x06, 0x04, // 8218=‚:2084 + 0x08, 0x2A, 0x10, 0x06, // 1107=ѓ:2090 + 0x08, 0x3A, 0x09, 0x05, // 8222=„:2106 + 0x08, 0x43, 0x26, 0x10, // 8230=…:2115 + 0x08, 0x69, 0x16, 0x09, // 8224=†:2153 + 0x08, 0x7F, 0x17, 0x09, // 8225=‡:2175 + 0x08, 0x96, 0x17, 0x09, // 8364=€:2198 + 0x08, 0xAD, 0x32, 0x11, // 8240=‰:2221 + 0x08, 0xDF, 0x2F, 0x11, // 1033=Љ:2271 + 0x09, 0x0E, 0x0B, 0x05, // 8249=‹:2318 + 0x09, 0x19, 0x2C, 0x10, // 1034=Њ:2329 + 0x09, 0x45, 0x1A, 0x09, // 1036=Ќ:2373 + 0x09, 0x5F, 0x23, 0x0D, // 1035=Ћ:2399 + 0x09, 0x82, 0x1D, 0x0C, // 1039=Џ:2434 + 0x09, 0x9F, 0x15, 0x08, // 1106=ђ:2463 + 0x09, 0xB4, 0x04, 0x04, // 8216=‘:2484 + 0x09, 0xB8, 0x04, 0x04, // 8217=’:2488 + 0x09, 0xBC, 0x07, 0x05, // 8220=“:2492 + 0x09, 0xC3, 0x0A, 0x05, // 8221=”:2499 + 0x09, 0xCD, 0x0E, 0x06, // 8226=•:2509 + 0x09, 0xDB, 0x1A, 0x09, // 8211=–:2523 + 0x09, 0xF5, 0x2F, 0x10, // 8212=—:2549 + 0x0A, 0x24, 0x1D, 0x0C, // 65533=�:2596 + 0x0A, 0x41, 0x2C, 0x10, // 8482=™:2625 + 0x0A, 0x6D, 0x29, 0x0F, // 1113=љ:2669 + 0x0A, 0x96, 0x0B, 0x05, // 8250=›:2710 + 0x0A, 0xA1, 0x23, 0x0D, // 1114=њ:2721 + 0x0A, 0xC4, 0x14, 0x07, // 1116=ќ:2756 + 0x0A, 0xD8, 0x14, 0x08, // 1115=ћ:2776 + 0x0A, 0xEC, 0x14, 0x08, // 1119=џ:2796 + 0xFF, 0xFF, 0x00, 0x04, // 160= :65535 + 0x0B, 0x00, 0x1C, 0x0A, // 1038=Ў:2816 + 0x0B, 0x1C, 0x13, 0x07, // 1118=ў:2844 + 0x0B, 0x2F, 0x14, 0x08, // 1032=Ј:2863 + 0x0B, 0x43, 0x14, 0x09, // 164=¤:2883 + 0x0B, 0x57, 0x16, 0x08, // 1168=Ґ:2903 + 0x0B, 0x6D, 0x06, 0x03, // 166=¦:2925 + 0x0B, 0x73, 0x17, 0x09, // 167=§:2931 + 0x0B, 0x8A, 0x1D, 0x0B, // 1025=Ё:2954 + 0x0B, 0xA7, 0x23, 0x0C, // 169=©:2983 + 0x0B, 0xCA, 0x20, 0x0C, // 1028=Є:3018 + 0x0B, 0xEA, 0x14, 0x09, // 171=«:3050 + 0x0B, 0xFE, 0x17, 0x09, // 172=¬:3070 + 0x0C, 0x15, 0x0B, 0x05, // 173=­:3093 + 0x0C, 0x20, 0x23, 0x0C, // 174=®:3104 + 0x0C, 0x43, 0x07, 0x03, // 1031=Ї:3139 + 0x0C, 0x4A, 0x0D, 0x06, // 176=°:3146 + 0x0C, 0x57, 0x17, 0x09, // 177=±:3159 + 0x0C, 0x6E, 0x05, 0x03, // 1030=І:3182 + 0x0C, 0x73, 0x05, 0x04, // 1110=і:3187 + 0x0C, 0x78, 0x10, 0x07, // 1169=ґ:3192 + 0x0C, 0x88, 0x17, 0x09, // 181=µ:3208 + 0x0C, 0x9F, 0x19, 0x09, // 182=¶:3231 + 0x0C, 0xB8, 0x08, 0x05, // 183=·:3256 + 0x0C, 0xC0, 0x17, 0x09, // 1105=ё:3264 + 0x0C, 0xD7, 0x2F, 0x11, // 8470=№:3287 + 0x0D, 0x06, 0x14, 0x08, // 1108=є:3334 + 0x0D, 0x1A, 0x17, 0x09, // 187=»:3354 + 0x0D, 0x31, 0x06, 0x03, // 1112=ј:3377 + 0x0D, 0x37, 0x1D, 0x0B, // 1029=Ѕ:3383 + 0x0D, 0x54, 0x14, 0x08, // 1109=ѕ:3412 + 0x0D, 0x68, 0x07, 0x03, // 1111=ї:3432 + 0x0D, 0x6F, 0x1D, 0x0B, // 1040=А:3439 + 0x0D, 0x8C, 0x1D, 0x0B, // 1041=Б:3468 + 0x0D, 0xA9, 0x1D, 0x0B, // 1042=В:3497 + 0x0D, 0xC6, 0x19, 0x09, // 1043=Г:3526 + 0x0D, 0xDF, 0x1E, 0x0B, // 1044=Д:3551 + 0x0D, 0xFD, 0x1D, 0x0B, // 1045=Е:3581 + 0x0E, 0x1A, 0x29, 0x0E, // 1046=Ж:3610 + 0x0E, 0x43, 0x1A, 0x0A, // 1047=З:3651 + 0x0E, 0x5D, 0x20, 0x0C, // 1048=И:3677 + 0x0E, 0x7D, 0x20, 0x0C, // 1049=Й:3709 + 0x0E, 0x9D, 0x1A, 0x09, // 1050=К:3741 + 0x0E, 0xB7, 0x1D, 0x0B, // 1051=Л:3767 + 0x0E, 0xD4, 0x23, 0x0D, // 1052=М:3796 + 0x0E, 0xF7, 0x1D, 0x0B, // 1053=Н:3831 + 0x0F, 0x14, 0x20, 0x0C, // 1054=О:3860 + 0x0F, 0x34, 0x1D, 0x0B, // 1055=П:3892 + 0x0F, 0x51, 0x1C, 0x0B, // 1056=Р:3921 + 0x0F, 0x6D, 0x20, 0x0C, // 1057=С:3949 + 0x0F, 0x8D, 0x19, 0x09, // 1058=Т:3981 + 0x0F, 0xA6, 0x1C, 0x0A, // 1059=У:4006 + 0x0F, 0xC2, 0x1D, 0x0B, // 1060=Ф:4034 + 0x0F, 0xDF, 0x20, 0x0B, // 1061=Х:4063 + 0x0F, 0xFF, 0x21, 0x0C, // 1062=Ц:4095 + 0x10, 0x20, 0x1A, 0x0A, // 1063=Ч:4128 + 0x10, 0x3A, 0x23, 0x0D, // 1064=Ш:4154 + 0x10, 0x5D, 0x27, 0x0E, // 1065=Щ:4189 + 0x10, 0x84, 0x23, 0x0D, // 1066=Ъ:4228 + 0x10, 0xA7, 0x26, 0x0E, // 1067=Ы:4263 + 0x10, 0xCD, 0x1D, 0x0B, // 1068=Ь:4301 + 0x10, 0xEA, 0x20, 0x0C, // 1069=Э:4330 + 0x11, 0x0A, 0x2C, 0x10, // 1070=Ю:4362 + 0x11, 0x36, 0x20, 0x0C, // 1071=Я:4406 + 0x11, 0x56, 0x17, 0x09, // 1072=а:4438 + 0x11, 0x6D, 0x17, 0x09, // 1073=б:4461 + 0x11, 0x84, 0x17, 0x09, // 1074=в:4484 + 0x11, 0x9B, 0x10, 0x06, // 1075=г:4507 + 0x11, 0xAB, 0x18, 0x09, // 1076=д:4523 + 0x11, 0xC3, 0x17, 0x09, // 1077=е:4547 + 0x11, 0xDA, 0x1D, 0x0A, // 1078=ж:4570 + 0x11, 0xF7, 0x11, 0x07, // 1079=з:4599 + 0x12, 0x08, 0x14, 0x08, // 1080=и:4616 + 0x12, 0x1C, 0x14, 0x08, // 1081=й:4636 + 0x12, 0x30, 0x14, 0x07, // 1082=к:4656 + 0x12, 0x44, 0x14, 0x08, // 1083=л:4676 + 0x12, 0x58, 0x1D, 0x0B, // 1084=м:4696 + 0x12, 0x75, 0x14, 0x08, // 1085=н:4725 + 0x12, 0x89, 0x17, 0x09, // 1086=о:4745 + 0x12, 0xA0, 0x14, 0x08, // 1087=п:4768 + 0x12, 0xB4, 0x17, 0x09, // 1088=р:4788 + 0x12, 0xCB, 0x14, 0x08, // 1089=с:4811 + 0x12, 0xDF, 0x13, 0x07, // 1090=т:4831 + 0x12, 0xF2, 0x13, 0x07, // 1091=у:4850 + 0x13, 0x05, 0x23, 0x0D, // 1092=ф:4869 + 0x13, 0x28, 0x14, 0x07, // 1093=х:4904 + 0x13, 0x3C, 0x1B, 0x09, // 1094=ц:4924 + 0x13, 0x57, 0x14, 0x08, // 1095=ч:4951 + 0x13, 0x6B, 0x1D, 0x0B, // 1096=ш:4971 + 0x13, 0x88, 0x21, 0x0B, // 1097=щ:5000 + 0x13, 0xA9, 0x1A, 0x0A, // 1098=ъ:5033 + 0x13, 0xC3, 0x20, 0x0C, // 1099=ы:5059 + 0x13, 0xE3, 0x17, 0x09, // 1100=ь:5091 + 0x13, 0xFA, 0x14, 0x08, // 1101=э:5114 + 0x14, 0x0E, 0x20, 0x0C, // 1102=ю:5134 + 0x14, 0x2E, 0x17, 0x09, // 1103=я:5166 + // Font Data: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x5F, // 33 + 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, // 34 + 0x80, 0x08, 0x00, 0x80, 0x78, 0x00, 0xC0, 0x0F, 0x00, 0xB8, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x78, 0x00, 0xC0, 0x0F, 0x00, + 0xB8, 0x08, 0x00, 0x80, 0x08, // 35 + 0x00, 0x00, 0x00, 0xE0, 0x10, 0x00, 0x10, 0x21, 0x00, 0x08, 0x43, 0x00, 0xFC, 0xFF, 0x00, 0x08, 0x42, 0x00, 0x18, 0x22, 0x00, + 0x20, 0x1C, // 36 + 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x61, 0x00, 0xF0, 0x18, 0x00, 0x00, 0x06, 0x00, + 0xC0, 0x01, 0x00, 0x30, 0x3C, 0x00, 0x08, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3C, // 37 + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x22, 0x00, 0x88, 0x41, 0x00, 0x08, 0x43, 0x00, 0x88, 0x44, 0x00, 0x70, 0x28, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x28, 0x00, 0x00, 0x44, // 38 + 0x00, 0x00, 0x00, 0x78, // 39 + 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x70, 0xC0, 0x01, 0x08, 0x00, 0x02, // 40 + 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x70, 0xC0, 0x01, 0x80, 0x3F, // 41 + 0x10, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x38, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x10, // 42 + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, // 43 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, // 44 + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, // 45 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, // 46 + 0x00, 0x60, 0x00, 0x00, 0x1E, 0x00, 0xE0, 0x01, 0x00, 0x18, // 47 + 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, + 0xE0, 0x1F, // 48 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF8, 0x7F, // 49 + 0x00, 0x00, 0x00, 0x20, 0x40, 0x00, 0x10, 0x60, 0x00, 0x08, 0x50, 0x00, 0x08, 0x48, 0x00, 0x08, 0x44, 0x00, 0x10, 0x43, 0x00, + 0xE0, 0x40, // 50 + 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x88, 0x41, 0x00, 0xF0, 0x22, 0x00, + 0x00, 0x1C, // 51 + 0x00, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x09, 0x00, 0xC0, 0x08, 0x00, 0x20, 0x08, 0x00, 0x10, 0x08, 0x00, 0xF8, 0x7F, 0x00, + 0x00, 0x08, // 52 + 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0xB8, 0x20, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x08, 0x21, 0x00, + 0x08, 0x1E, // 53 + 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x10, 0x21, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x10, 0x21, 0x00, + 0x20, 0x1E, // 54 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x78, 0x00, 0x88, 0x07, 0x00, 0x68, 0x00, 0x00, + 0x18, // 55 + 0x00, 0x00, 0x00, 0x60, 0x1C, 0x00, 0x90, 0x22, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x90, 0x22, 0x00, + 0x60, 0x1C, // 56 + 0x00, 0x00, 0x00, 0xE0, 0x11, 0x00, 0x10, 0x22, 0x00, 0x08, 0x44, 0x00, 0x08, 0x44, 0x00, 0x08, 0x44, 0x00, 0x10, 0x22, 0x00, + 0xE0, 0x1F, // 57 + 0x00, 0x00, 0x00, 0x40, 0x40, // 58 + 0x00, 0x00, 0x00, 0x40, 0xC0, 0x01, // 59 + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, + 0x40, 0x10, // 60 + 0x00, 0x00, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, + 0x80, 0x08, // 61 + 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x02, // 62 + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x5C, 0x00, 0x08, 0x02, 0x00, 0x10, 0x01, 0x00, + 0xE0, // 63 + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0xC0, 0x40, 0x00, 0x20, 0x80, 0x00, 0x10, 0x1E, 0x01, 0x10, 0x21, 0x01, 0x88, 0x40, 0x02, + 0x48, 0x40, 0x02, 0x48, 0x40, 0x02, 0x48, 0x20, 0x02, 0x88, 0x7C, 0x02, 0xC8, 0x43, 0x02, 0x10, 0x40, 0x02, 0x10, 0x20, 0x01, + 0x60, 0x10, 0x01, 0x80, 0x8F, // 64 + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x70, 0x04, 0x00, 0x08, 0x04, 0x00, 0x70, 0x04, 0x00, + 0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 65 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x90, 0x22, 0x00, 0x60, 0x1C, // 66 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 67 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 68 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 69 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, + 0x08, 0x02, 0x00, 0x08, // 70 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x42, 0x00, + 0x08, 0x42, 0x00, 0x10, 0x22, 0x00, 0x20, 0x12, 0x00, 0x00, 0x0E, // 71 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xF8, 0x7F, // 72 + 0x00, 0x00, 0x00, 0xF8, 0x7F, // 73 + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x3F, // 74 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x40, 0x04, 0x00, + 0x20, 0x18, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, // 75 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, // 76 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, 0x7F, // 77 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x10, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x7F, // 78 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 79 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, + 0x08, 0x02, 0x00, 0x10, 0x01, 0x00, 0xE0, // 80 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x00, 0x10, 0x20, 0x00, 0x20, 0x70, 0x00, 0xC0, 0x4F, // 81 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x0E, 0x00, + 0x08, 0x1A, 0x00, 0x10, 0x21, 0x00, 0xE0, 0x40, // 82 + 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x90, 0x20, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x42, 0x00, + 0x08, 0x42, 0x00, 0x10, 0x22, 0x00, 0x20, 0x1C, // 83 + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, // 84 + 0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 85 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x07, 0x00, 0xE0, 0x00, 0x00, 0x18, // 86 + 0x18, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x03, 0x00, 0x70, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x70, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1E, 0x00, 0xE0, 0x01, 0x00, + 0x18, // 87 + 0x00, 0x40, 0x00, 0x08, 0x20, 0x00, 0x10, 0x18, 0x00, 0x60, 0x04, 0x00, 0x80, 0x02, 0x00, 0x00, 0x01, 0x00, 0x80, 0x02, 0x00, + 0x60, 0x0C, 0x00, 0x10, 0x10, 0x00, 0x08, 0x20, 0x00, 0x00, 0x40, // 88 + 0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x7E, 0x00, 0x80, 0x01, 0x00, 0x40, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x08, // 89 + 0x00, 0x40, 0x00, 0x08, 0x60, 0x00, 0x08, 0x58, 0x00, 0x08, 0x44, 0x00, 0x08, 0x43, 0x00, 0x88, 0x40, 0x00, 0x68, 0x40, 0x00, + 0x18, 0x40, 0x00, 0x08, 0x40, // 90 + 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x08, 0x00, 0x02, 0x08, 0x00, 0x02, // 91 + 0x18, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x60, // 92 + 0x08, 0x00, 0x02, 0x08, 0x00, 0x02, 0xF8, 0xFF, 0x03, // 93 + 0x00, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00, 0x00, 0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x01, // 94 + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, // 95 + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, // 96 + 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x42, 0x00, 0x40, 0x22, 0x00, + 0x80, 0x7F, // 97 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0x00, 0x1F, // 98 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, // 99 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0xF8, 0x7F, // 100 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00, + 0x00, 0x17, // 101 + 0x40, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x48, 0x00, 0x00, 0x48, // 102 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x80, 0x20, 0x02, 0x40, 0x40, 0x02, 0x40, 0x40, 0x02, 0x40, 0x40, 0x02, 0x80, 0x20, 0x01, + 0xC0, 0xFF, // 103 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, // 104 + 0x00, 0x00, 0x00, 0xC8, 0x7F, // 105 + 0x00, 0x00, 0x02, 0xC8, 0xFF, 0x01, // 106 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x19, 0x00, 0x80, 0x20, 0x00, + 0x40, 0x40, // 107 + 0x00, 0x00, 0x00, 0xF8, 0x7F, // 108 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, 0x00, + 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, // 109 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, // 110 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0x00, 0x1F, // 111 + 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0x00, 0x1F, // 112 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0xC0, 0xFF, 0x03, // 113 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, // 114 + 0x00, 0x00, 0x00, 0x80, 0x23, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x38, // 115 + 0x40, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, // 116 + 0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xC0, 0x7F, // 117 + 0xC0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0, // 118 + 0xC0, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1F, 0x00, 0xC0, // 119 + 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, // 120 + 0xC0, 0x01, 0x00, 0x00, 0x06, 0x02, 0x00, 0x38, 0x02, 0x00, 0xC0, 0x01, 0x00, 0x38, 0x00, 0x00, 0x07, 0x00, 0xC0, // 121 + 0x40, 0x40, 0x00, 0x40, 0x60, 0x00, 0x40, 0x58, 0x00, 0x40, 0x44, 0x00, 0x40, 0x43, 0x00, 0xC0, 0x40, 0x00, 0x40, 0x40, // 122 + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xF0, 0xFB, 0x01, 0x08, 0x00, 0x02, 0x08, 0x00, 0x02, // 123 + 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, // 124 + 0x08, 0x00, 0x02, 0x08, 0x00, 0x02, 0xF0, 0xFB, 0x01, 0x00, 0x04, 0x00, 0x00, 0x04, // 125 + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, // 126 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, + 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0xE0, 0x7F, // 127 + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x01, 0x00, + 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3E, // 1026 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, // 1027 + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, // 8218 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x00, 0x00, 0x50, 0x00, 0x00, 0x48, 0x00, 0x00, 0x40, // 1107 + 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, // 8222 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, // 8230 + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x40, // 8224 + 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0xF8, 0xFF, 0x03, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, + 0x40, 0x40, // 8225 + 0x00, 0x05, 0x00, 0xC0, 0x1F, 0x00, 0x20, 0x25, 0x00, 0x10, 0x45, 0x00, 0x08, 0x45, 0x00, 0x08, 0x45, 0x00, 0x08, 0x45, 0x00, + 0x10, 0x20, // 8364 + 0xF0, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x71, 0x00, 0xF0, 0x0C, 0x00, 0x00, 0x03, 0x00, 0xE0, 0x3C, 0x00, + 0x18, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x42, 0x00, + 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3C, // 8240 + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x3F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x3E, // 1033 + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, // 8249 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0xF8, 0x7F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x3E, // 1034 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x82, 0x02, 0x00, 0x61, 0x04, 0x00, 0x10, 0x08, 0x00, + 0x08, 0x30, 0x00, 0x08, 0x40, // 1036 + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x01, 0x00, + 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x7E, // 1035 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, // 1039 + 0x10, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x90, 0x00, 0x00, 0x50, 0x00, 0x00, 0x50, 0x00, 0x00, 0x40, 0x00, 0x02, 0x80, 0xFF, + 0x03, // 1106 + 0x00, 0x00, 0x00, 0x38, // 8216 + 0x00, 0x00, 0x00, 0x38, // 8217 + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, // 8220 + 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, // 8221 + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x80, 0x07, 0x00, 0x80, 0x07, 0x00, 0x00, 0x03, // 8226 + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, // 8211 + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, // 8212 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, + 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0xE0, 0x7F, // 65533 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x18, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x01, 0x00, 0xE0, 0x00, 0x00, 0x18, 0x00, 0x00, + 0xF8, 0x01, // 8482 + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xC0, 0x3F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0xC0, 0x7F, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x64, 0x00, 0x00, + 0x38, // 1113 + 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, // 8250 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xC0, 0x7F, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x38, // 1114 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x10, 0x04, 0x00, 0x08, 0x1B, 0x00, 0xC0, 0x20, 0x00, 0x40, + 0x40, // 1116 + 0x10, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x90, 0x00, 0x00, 0x50, 0x00, 0x00, 0x50, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, + 0x7F, // 1115 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x40, 0x00, 0xC0, + 0x7F, // 1119 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x40, 0x00, 0x81, 0x41, 0x00, 0x02, 0x66, 0x00, 0x02, 0x18, 0x00, 0x02, 0x06, 0x00, + 0x81, 0x01, 0x00, 0x60, 0x00, 0x00, 0x18, // 1038 + 0xC0, 0x01, 0x00, 0x08, 0x06, 0x02, 0x10, 0x38, 0x02, 0x10, 0xC0, 0x01, 0x10, 0x38, 0x00, 0x08, 0x07, 0x00, 0xC0, // 1118 + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, + 0x3F, // 1032 + 0x00, 0x00, 0x00, 0x40, 0x0B, 0x00, 0x80, 0x04, 0x00, 0x40, 0x08, 0x00, 0x40, 0x08, 0x00, 0x80, 0x04, 0x00, 0x40, 0x0B, // 164 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x0F, // 1168 + 0x00, 0x00, 0x00, 0xF8, 0xF1, 0x03, // 166 + 0x00, 0x86, 0x00, 0x70, 0x09, 0x01, 0xC8, 0x10, 0x02, 0x88, 0x10, 0x02, 0x08, 0x21, 0x02, 0x08, 0x61, 0x02, 0x30, 0xD2, 0x01, + 0x00, 0x0C, // 167 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 1025 + 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0xC8, 0x47, 0x00, 0x28, 0x48, 0x00, 0x28, 0x48, 0x00, 0x28, 0x48, 0x00, + 0x28, 0x48, 0x00, 0x48, 0x44, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 169 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x11, 0x00, 0x10, 0x21, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 1028 + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, // 171 + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x80, 0x0F, // 172 + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, // 173 + 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0xE8, 0x4F, 0x00, 0x28, 0x41, 0x00, 0x28, 0x41, 0x00, 0x28, 0x43, 0x00, + 0x28, 0x45, 0x00, 0xC8, 0x48, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 174 + 0x02, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x02, // 1031 + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x30, // 176 + 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0xE0, 0x4F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, // 177 + 0x00, 0x00, 0x00, 0xF8, 0x7F, // 1030 + 0x00, 0x00, 0x00, 0xC8, 0x7F, // 1110 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x78, // 1169 + 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, + 0xC0, 0x7F, // 181 + 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x01, 0x00, 0xF8, 0x01, 0x00, 0xF8, 0xFF, 0x03, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0xF8, 0xFF, 0x03, 0x08, // 182 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // 183 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x50, 0x44, 0x00, 0x40, 0x44, 0x00, 0x50, 0x44, 0x00, 0x80, 0x24, 0x00, + 0x00, 0x17, // 1105 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x20, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x10, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x80, 0x27, 0x00, 0x40, 0x28, 0x00, 0x40, 0x28, 0x00, 0x40, 0x28, 0x00, + 0x40, 0x28, 0x00, 0x80, 0x27, // 8470 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x20, 0x00, 0x00, + 0x11, // 1108 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, + 0x00, 0x04, // 187 + 0x00, 0x00, 0x02, 0xC8, 0xFF, 0x01, // 1112 + 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x90, 0x20, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x42, 0x00, + 0x08, 0x42, 0x00, 0x10, 0x22, 0x00, 0x20, 0x1C, // 1029 + 0x00, 0x00, 0x00, 0x80, 0x23, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, + 0x38, // 1109 + 0x10, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x10, // 1111 + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x70, 0x04, 0x00, 0x08, 0x04, 0x00, 0x70, 0x04, 0x00, + 0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 1040 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x00, 0x3E, // 1041 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x90, 0x22, 0x00, 0x60, 0x1C, // 1042 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, // 1043 + 0x00, 0xC0, 0x03, 0x00, 0x60, 0x00, 0x00, 0x5C, 0x00, 0xF8, 0x43, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xC0, 0x03, // 1044 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 1045 + 0x08, 0x30, 0x00, 0x10, 0x08, 0x00, 0x60, 0x04, 0x00, 0x80, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xF8, 0x7F, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x80, 0x02, 0x00, 0x60, 0x04, 0x00, 0x10, 0x08, 0x00, 0x08, 0x30, 0x00, 0x08, + 0x40, // 1046 + 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x88, 0x42, 0x00, + 0x70, 0x42, 0x00, 0x00, 0x3C, // 1047 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x30, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, 0x7F, // 1048 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x30, 0x00, 0x00, 0x08, 0x00, 0x01, 0x04, 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, + 0x82, 0x00, 0x00, 0x41, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, 0x7F, // 1049 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x80, 0x02, 0x00, 0x60, 0x04, 0x00, 0x10, 0x08, 0x00, + 0x08, 0x30, 0x00, 0x08, 0x40, // 1050 + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x3F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, // 1051 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, 0x7F, // 1052 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xF8, 0x7F, // 1053 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 1054 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, // 1055 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, + 0x08, 0x02, 0x00, 0x10, 0x01, 0x00, 0xE0, // 1056 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 1057 + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, // 1058 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x40, 0x00, 0x80, 0x41, 0x00, 0x00, 0x66, 0x00, 0x00, 0x18, 0x00, 0x00, 0x06, 0x00, + 0x80, 0x01, 0x00, 0x60, 0x00, 0x00, 0x18, // 1059 + 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x40, 0x08, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00, 0xF8, 0x7F, 0x00, 0x20, 0x10, 0x00, + 0x20, 0x10, 0x00, 0x40, 0x08, 0x00, 0x80, 0x07, // 1060 + 0x00, 0x40, 0x00, 0x08, 0x20, 0x00, 0x10, 0x18, 0x00, 0x60, 0x04, 0x00, 0x80, 0x02, 0x00, 0x00, 0x01, 0x00, 0x80, 0x02, 0x00, + 0x60, 0x0C, 0x00, 0x10, 0x10, 0x00, 0x08, 0x20, 0x00, 0x00, 0x40, // 1061 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xC0, 0x03, // 1062 + 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0x00, 0xF8, 0x7F, // 1063 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, // 1064 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xC0, 0x03, // 1065 + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3E, // 1066 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x7F, // 1067 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3E, // 1068 + 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x10, 0x21, 0x00, 0x20, 0x11, 0x00, 0xC0, 0x0F, // 1069 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, + 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, + 0xC0, 0x0F, // 1070 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x40, 0x00, 0x10, 0x21, 0x00, 0x08, 0x1A, 0x00, 0x08, 0x0E, 0x00, 0x08, 0x02, 0x00, + 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0xF8, 0x7F, // 1071 + 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x42, 0x00, 0x40, 0x22, 0x00, + 0x80, 0x7F, // 1072 + 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x90, 0x20, 0x00, 0x48, 0x40, 0x00, 0x48, 0x40, 0x00, 0x48, 0x40, 0x00, 0x88, 0x20, 0x00, + 0x08, 0x1F, // 1073 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, + 0x80, 0x3B, // 1074 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, // 1075 + 0x00, 0xC0, 0x01, 0x00, 0x70, 0x00, 0xC0, 0x4F, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0xC0, 0x7F, 0x00, + 0x00, 0xC0, 0x01, // 1076 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00, + 0x00, 0x17, // 1077 + 0xC0, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x1B, 0x00, 0xC0, 0x20, 0x00, 0x40, 0x40, // 1078 + 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x3B, // 1079 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x30, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x80, 0x01, 0x00, 0xC0, + 0x7F, // 1080 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x08, 0x30, 0x00, 0x10, 0x0C, 0x00, 0x10, 0x02, 0x00, 0x90, 0x01, 0x00, 0xC8, + 0x7F, // 1081 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0xC0, 0x20, 0x00, 0x40, + 0x40, // 1082 + 0x00, 0x40, 0x00, 0xC0, 0x3F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, + 0x7F, // 1083 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x06, 0x00, 0xC0, 0x01, 0x00, 0xC0, 0x7F, // 1084 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xC0, + 0x7F, // 1085 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0x00, 0x1F, // 1086 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, + 0x7F, // 1087 + 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0x00, 0x1F, // 1088 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, + 0x20, // 1089 + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, // 1090 + 0xC0, 0x01, 0x00, 0x00, 0x06, 0x02, 0x00, 0x38, 0x02, 0x00, 0xC0, 0x01, 0x00, 0x38, 0x00, 0x00, 0x07, 0x00, 0xC0, // 1091 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0xF8, 0xFF, 0x03, + 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1F, // 1092 + 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, 0x00, 0x40, + 0x40, // 1093 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0xC0, 0x7F, 0x00, 0x00, 0xC0, 0x03, // 1094 + 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0xC0, + 0x7F, // 1095 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xC0, 0x7F, // 1096 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0xC0, 0x03, // 1097 + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x38, // 1098 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x7F, // 1099 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x38, // 1100 + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x80, 0x20, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00, 0x00, + 0x1F, // 1101 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, + 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1F, // 1102 + 0x00, 0x00, 0x00, 0x80, 0x63, 0x00, 0x40, 0x14, 0x00, 0x40, 0x0C, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, + 0xC0, 0x7F, // 1103 +}; + +const uint8_t ArialMT_Plain_24_UA[] PROGMEM = { + 0x18, // Width: 24 + 0x1C, // Height: 28 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + // Jump Table: + 0xFF, 0xFF, 0x00, 0x07, // 32= :65535 + 0x00, 0x00, 0x13, 0x08, // 33=!:0 + 0x00, 0x13, 0x1A, 0x09, // 34=":19 + 0x00, 0x2D, 0x33, 0x0D, // 35=#:45 + 0x00, 0x60, 0x2F, 0x0D, // 36=$:96 + 0x00, 0x8F, 0x4F, 0x15, // 37=%:143 + 0x00, 0xDE, 0x3B, 0x10, // 38=&:222 + 0x01, 0x19, 0x0A, 0x05, // 39=':281 + 0x01, 0x23, 0x1C, 0x08, // 40=(:291 + 0x01, 0x3F, 0x1B, 0x08, // 41=):319 + 0x01, 0x5A, 0x22, 0x09, // 42=*:346 + 0x01, 0x7C, 0x33, 0x0E, // 43=+:380 + 0x01, 0xAF, 0x10, 0x07, // 44=,:431 + 0x01, 0xBF, 0x1B, 0x08, // 45=-:447 + 0x01, 0xDA, 0x0F, 0x07, // 46=.:474 + 0x01, 0xE9, 0x1A, 0x07, // 47=/:489 + 0x02, 0x03, 0x2F, 0x0D, // 48=0:515 + 0x02, 0x32, 0x23, 0x0D, // 49=1:562 + 0x02, 0x55, 0x2F, 0x0D, // 50=2:597 + 0x02, 0x84, 0x2F, 0x0D, // 51=3:644 + 0x02, 0xB3, 0x2F, 0x0D, // 52=4:691 + 0x02, 0xE2, 0x2F, 0x0D, // 53=5:738 + 0x03, 0x11, 0x2F, 0x0D, // 54=6:785 + 0x03, 0x40, 0x2E, 0x0D, // 55=7:832 + 0x03, 0x6E, 0x2F, 0x0D, // 56=8:878 + 0x03, 0x9D, 0x2F, 0x0D, // 57=9:925 + 0x03, 0xCC, 0x0F, 0x07, // 58=::972 + 0x03, 0xDB, 0x10, 0x07, // 59=;:987 + 0x03, 0xEB, 0x2F, 0x0E, // 60=<:1003 + 0x04, 0x1A, 0x2F, 0x0E, // 61==:1050 + 0x04, 0x49, 0x2E, 0x0E, // 62=>:1097 + 0x04, 0x77, 0x2E, 0x0D, // 63=?:1143 + 0x04, 0xA5, 0x5C, 0x18, // 64=@:1189 + 0x05, 0x01, 0x3B, 0x0F, // 65=A:1281 + 0x05, 0x3C, 0x3B, 0x10, // 66=B:1340 + 0x05, 0x77, 0x3F, 0x11, // 67=C:1399 + 0x05, 0xB6, 0x3F, 0x11, // 68=D:1462 + 0x05, 0xF5, 0x3B, 0x10, // 69=E:1525 + 0x06, 0x30, 0x36, 0x0F, // 70=F:1584 + 0x06, 0x66, 0x43, 0x13, // 71=G:1638 + 0x06, 0xA9, 0x3B, 0x11, // 72=H:1705 + 0x06, 0xE4, 0x0F, 0x06, // 73=I:1764 + 0x06, 0xF3, 0x27, 0x0C, // 74=J:1779 + 0x07, 0x1A, 0x3F, 0x10, // 75=K:1818 + 0x07, 0x59, 0x2F, 0x0D, // 76=L:1881 + 0x07, 0x88, 0x43, 0x13, // 77=M:1928 + 0x07, 0xCB, 0x3B, 0x11, // 78=N:1995 + 0x08, 0x06, 0x47, 0x13, // 79=O:2054 + 0x08, 0x4D, 0x3A, 0x10, // 80=P:2125 + 0x08, 0x87, 0x48, 0x13, // 81=Q:2183 + 0x08, 0xCF, 0x3F, 0x11, // 82=R:2255 + 0x09, 0x0E, 0x3B, 0x10, // 83=S:2318 + 0x09, 0x49, 0x36, 0x0E, // 84=T:2377 + 0x09, 0x7F, 0x3B, 0x11, // 85=U:2431 + 0x09, 0xBA, 0x39, 0x0F, // 86=V:2490 + 0x09, 0xF3, 0x5A, 0x17, // 87=W:2547 + 0x0A, 0x4D, 0x3B, 0x0F, // 88=X:2637 + 0x0A, 0x88, 0x3D, 0x10, // 89=Y:2696 + 0x0A, 0xC5, 0x37, 0x0F, // 90=Z:2757 + 0x0A, 0xFC, 0x14, 0x07, // 91=[:2812 + 0x0B, 0x10, 0x1B, 0x07, // 92=\:2832 + 0x0B, 0x2B, 0x18, 0x07, // 93=]:2859 + 0x0B, 0x43, 0x2A, 0x0C, // 94=^:2883 + 0x0B, 0x6D, 0x34, 0x0D, // 95=_:2925 + 0x0B, 0xA1, 0x12, 0x08, // 96=`:2977 + 0x0B, 0xB3, 0x2F, 0x0D, // 97=a:2995 + 0x0B, 0xE2, 0x33, 0x0E, // 98=b:3042 + 0x0C, 0x15, 0x2B, 0x0C, // 99=c:3093 + 0x0C, 0x40, 0x2F, 0x0E, // 100=d:3136 + 0x0C, 0x6F, 0x2F, 0x0D, // 101=e:3183 + 0x0C, 0x9E, 0x1A, 0x07, // 102=f:3230 + 0x0C, 0xB8, 0x30, 0x0E, // 103=g:3256 + 0x0C, 0xE8, 0x2F, 0x0E, // 104=h:3304 + 0x0D, 0x17, 0x0F, 0x05, // 105=i:3351 + 0x0D, 0x26, 0x10, 0x06, // 106=j:3366 + 0x0D, 0x36, 0x2F, 0x0C, // 107=k:3382 + 0x0D, 0x65, 0x0F, 0x06, // 108=l:3429 + 0x0D, 0x74, 0x47, 0x14, // 109=m:3444 + 0x0D, 0xBB, 0x2F, 0x0E, // 110=n:3515 + 0x0D, 0xEA, 0x2F, 0x0D, // 111=o:3562 + 0x0E, 0x19, 0x33, 0x0E, // 112=p:3609 + 0x0E, 0x4C, 0x30, 0x0E, // 113=q:3660 + 0x0E, 0x7C, 0x1E, 0x08, // 114=r:3708 + 0x0E, 0x9A, 0x2B, 0x0C, // 115=s:3738 + 0x0E, 0xC5, 0x1B, 0x07, // 116=t:3781 + 0x0E, 0xE0, 0x2F, 0x0E, // 117=u:3808 + 0x0F, 0x0F, 0x2A, 0x0B, // 118=v:3855 + 0x0F, 0x39, 0x42, 0x11, // 119=w:3897 + 0x0F, 0x7B, 0x2B, 0x0B, // 120=x:3963 + 0x0F, 0xA6, 0x2A, 0x0C, // 121=y:4006 + 0x0F, 0xD0, 0x2B, 0x0C, // 122=z:4048 + 0x0F, 0xFB, 0x1C, 0x08, // 123={:4091 + 0x10, 0x17, 0x10, 0x06, // 124=|:4119 + 0x10, 0x27, 0x1B, 0x08, // 125=}:4135 + 0x10, 0x42, 0x33, 0x0E, // 126=~:4162 + 0xFF, 0xFF, 0x00, 0x12, // 127:65535 + 0x10, 0x75, 0x4F, 0x15, // 1026=� �..:4213 + 0x10, 0xC4, 0x32, 0x0D, // 1027=� �.:4292 + 0x10, 0xF6, 0x0C, 0x05, // 8218=��.�.:4342 + 0x11, 0x02, 0x22, 0x09, // 1107=�.�..:4354 + 0x11, 0x24, 0x1C, 0x08, // 8222=��.�.:4388 + 0x11, 0x40, 0x4B, 0x18, // 8230=��.�.:4416 + 0x11, 0x8B, 0x32, 0x0D, // 8224=��.� :4491 + 0x11, 0xBD, 0x33, 0x0D, // 8225=��.�.:4541 + 0x11, 0xF0, 0x2F, 0x0D, // 8364=��..�.:4592 + 0x12, 0x1F, 0x63, 0x1A, // 8240=��.��:4639 + 0x12, 0x82, 0x5F, 0x19, // 1033=� �.�:4738 + 0x12, 0xE1, 0x17, 0x08, // 8249=��.�..:4833 + 0x12, 0xF8, 0x5B, 0x18, // 1034=� �.:4856 + 0x13, 0x53, 0x37, 0x0E, // 1036=� �.:4947 + 0x13, 0x8A, 0x4F, 0x16, // 1035=� �..:5002 + 0x13, 0xD9, 0x3B, 0x11, // 1039=� �.:5081 + 0x14, 0x14, 0x30, 0x0E, // 1106=�.�..:5140 + 0x14, 0x44, 0x0A, 0x05, // 8216=��.Ч.:5188 + 0x14, 0x4E, 0x0A, 0x05, // 8217=��.�..:5198 + 0x14, 0x58, 0x1A, 0x08, // 8220=��.�.:5208 + 0x14, 0x72, 0x1A, 0x08, // 8221=��.�.:5234 + 0x14, 0x8C, 0x1B, 0x08, // 8226=��.�.:5260 + 0x14, 0xA7, 0x33, 0x0D, // 8211=��.�..:5287 + 0x14, 0xDA, 0x5F, 0x18, // 8212=��.�..:5338 + 0xFF, 0xFF, 0x00, 0x12, // 65533=��.�.:65535 + 0x15, 0x39, 0x5B, 0x18, // 8482=��..�.:5433 + 0x15, 0x94, 0x53, 0x16, // 1113=�.�..:5524 + 0x15, 0xE7, 0x1B, 0x08, // 8250=��.�.:5607 + 0x16, 0x02, 0x4B, 0x14, // 1114=�.�.:5634 + 0x16, 0x4D, 0x2B, 0x0B, // 1116=�.�.:5709 + 0x16, 0x78, 0x2F, 0x0E, // 1115=�.�.�:5752 + 0x16, 0xA7, 0x2F, 0x0D, // 1119=�.�.:5799 + 0xFF, 0xFF, 0x00, 0x07, // 160=�.� :65535 + 0x16, 0xD6, 0x36, 0x0F, // 1038=� �.:5846 + 0x17, 0x0C, 0x2A, 0x0C, // 1118=�.�.:5900 + 0x17, 0x36, 0x27, 0x0C, // 1032=� �..:5942 + 0x17, 0x5D, 0x33, 0x0D, // 164=�.�.:5981 + 0x17, 0x90, 0x2A, 0x0C, // 1168=�.�.:6032 + 0x17, 0xBA, 0x10, 0x06, // 166=�.�.:6074 + 0x17, 0xCA, 0x2F, 0x0D, // 167=�.�.:6090 + 0x17, 0xF9, 0x3B, 0x10, // 1025=� �.:6137 + 0x18, 0x34, 0x47, 0x12, // 169=�.��:6196 + 0x18, 0x7B, 0x3F, 0x11, // 1028=� �..:6267 + 0x18, 0xBA, 0x27, 0x0D, // 171=�.�.:6330 + 0x18, 0xE1, 0x2F, 0x0E, // 172=�.�.:6369 + 0x19, 0x10, 0x1B, 0x08, // 173=�.�:6416 + 0x19, 0x2B, 0x47, 0x12, // 174=�.�.:6443 + 0x19, 0x72, 0x15, 0x06, // 1031=� �..:6514 + 0x19, 0x87, 0x1E, 0x0A, // 176=�.��:6535 + 0x19, 0xA5, 0x33, 0x0D, // 177=�.�.:6565 + 0x19, 0xD8, 0x0F, 0x06, // 1030=� �. :6616 + 0x19, 0xE7, 0x0F, 0x05, // 1110=�.�..:6631 + 0x19, 0xF6, 0x22, 0x0A, // 1169=�.�.:6646 + 0x1A, 0x18, 0x2F, 0x0E, // 181=�.�.:6680 + 0x1A, 0x47, 0x32, 0x0D, // 182=�.�.:6727 + 0x1A, 0x79, 0x13, 0x08, // 183=�.��:6777 + 0x1A, 0x8C, 0x2F, 0x0D, // 1105=�.�.:6796 + 0x1A, 0xBB, 0x63, 0x1A, // 8470=��..�..:6843 + 0x1B, 0x1E, 0x2B, 0x0C, // 1108=�.�..:6942 + 0x1B, 0x49, 0x2F, 0x0D, // 187=�.�.:6985 + 0x1B, 0x78, 0x10, 0x06, // 1112=�.Ч.:7032 + 0x1B, 0x88, 0x3B, 0x10, // 1029=� �..:7048 + 0x1B, 0xC3, 0x2B, 0x0C, // 1109=�.�..:7107 + 0x1B, 0xEE, 0x16, 0x06, // 1111=�.�..:7150 + 0x1C, 0x04, 0x3B, 0x0F, // 1040=� �.:7172 + 0x1C, 0x3F, 0x3B, 0x10, // 1041=� �.:7231 + 0x1C, 0x7A, 0x3B, 0x10, // 1042=� �..:7290 + 0x1C, 0xB5, 0x32, 0x0D, // 1043=� �..:7349 + 0x1C, 0xE7, 0x40, 0x10, // 1044=� �..:7399 + 0x1D, 0x27, 0x3B, 0x10, // 1045=� �..:7463 + 0x1D, 0x62, 0x57, 0x16, // 1046=� �..:7522 + 0x1D, 0xB9, 0x37, 0x0F, // 1047=� �..:7609 + 0x1D, 0xF0, 0x3B, 0x11, // 1048=� Ч.:7664 + 0x1E, 0x2B, 0x3B, 0x11, // 1049=� �..:7723 + 0x1E, 0x66, 0x37, 0x0E, // 1050=� �.:7782 + 0x1E, 0x9D, 0x37, 0x10, // 1051=� �.�:7837 + 0x1E, 0xD4, 0x43, 0x13, // 1052=� �.:7892 + 0x1F, 0x17, 0x3B, 0x11, // 1053=� �.:7959 + 0x1F, 0x52, 0x47, 0x13, // 1054=� �.:8018 + 0x1F, 0x99, 0x3B, 0x11, // 1055=� �.:8089 + 0x1F, 0xD4, 0x3A, 0x10, // 1056=� � :8148 + 0x20, 0x0E, 0x3F, 0x11, // 1057=� �.:8206 + 0x20, 0x4D, 0x36, 0x0E, // 1058=� �.:8269 + 0x20, 0x83, 0x36, 0x0F, // 1059=� �.:8323 + 0x20, 0xB9, 0x43, 0x12, // 1060=� �.:8377 + 0x20, 0xFC, 0x3B, 0x0F, // 1061=� �.:8444 + 0x21, 0x37, 0x44, 0x12, // 1062=� �.:8503 + 0x21, 0x7B, 0x37, 0x10, // 1063=� �.:8571 + 0x21, 0xB2, 0x53, 0x16, // 1064=� �.:8626 + 0x22, 0x05, 0x5C, 0x17, // 1065=� ��:8709 + 0x22, 0x61, 0x47, 0x13, // 1066=� �.:8801 + 0x22, 0xA8, 0x4B, 0x15, // 1067=� �.:8872 + 0x22, 0xF3, 0x3B, 0x10, // 1068=� �.:8947 + 0x23, 0x2E, 0x3F, 0x11, // 1069=� �:9006 + 0x23, 0x6D, 0x5B, 0x18, // 1070=� �.:9069 + 0x23, 0xC8, 0x3B, 0x11, // 1071=� �.:9160 + 0x24, 0x03, 0x2F, 0x0D, // 1072=� ��:9219 + 0x24, 0x32, 0x33, 0x0E, // 1073=� �.:9266 + 0x24, 0x65, 0x2F, 0x0D, // 1074=� �.:9317 + 0x24, 0x94, 0x22, 0x09, // 1075=� �.:9364 + 0x24, 0xB6, 0x34, 0x0E, // 1076=� �.:9398 + 0x24, 0xEA, 0x2F, 0x0D, // 1077=� �.:9450 + 0x25, 0x19, 0x3B, 0x10, // 1078=� �.:9497 + 0x25, 0x54, 0x27, 0x0B, // 1079=� ��:9556 + 0x25, 0x7B, 0x2F, 0x0D, // 1080=� �.:9595 + 0x25, 0xAA, 0x2F, 0x0D, // 1081=� �..:9642 + 0x25, 0xD9, 0x2B, 0x0B, // 1082=� �.:9689 + 0x26, 0x04, 0x2F, 0x0E, // 1083=� �.:9732 + 0x26, 0x33, 0x3B, 0x11, // 1084=� �:9779 + 0x26, 0x6E, 0x2F, 0x0D, // 1085=� �.:9838 + 0x26, 0x9D, 0x2F, 0x0D, // 1086=� �.:9885 + 0x26, 0xCC, 0x2F, 0x0D, // 1087=� �.:9932 + 0x26, 0xFB, 0x33, 0x0E, // 1088=�.�.:9979 + 0x27, 0x2E, 0x2B, 0x0C, // 1089=�.�.:10030 + 0x27, 0x59, 0x26, 0x0B, // 1090=�.�..:10073 + 0x27, 0x7F, 0x2A, 0x0C, // 1091=�.�.:10111 + 0x27, 0xA9, 0x4B, 0x14, // 1092=�.�..:10153 + 0x27, 0xF4, 0x2B, 0x0B, // 1093=�.�..:10228 + 0x28, 0x1F, 0x34, 0x0E, // 1094=�.�. :10271 + 0x28, 0x53, 0x2B, 0x0D, // 1095=�.�..:10323 + 0x28, 0x7E, 0x47, 0x13, // 1096=�.�..:10366 + 0x28, 0xC5, 0x4C, 0x14, // 1097=�.�.�:10437 + 0x29, 0x11, 0x37, 0x0F, // 1098=�.�.:10513 + 0x29, 0x48, 0x3B, 0x11, // 1099=�.�..:10568 + 0x29, 0x83, 0x2F, 0x0D, // 1100=�.�.:10627 + 0x29, 0xB2, 0x2B, 0x0C, // 1101=�.�.:10674 + 0x29, 0xDD, 0x43, 0x12, // 1102=�.�.:10717 + 0x2A, 0x20, 0x2B, 0x0D, // 1103=�.�.:10784 + // Font Data: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xCF, 0x00, 0x80, 0xFF, 0xCF, // 33 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x1F, 0x00, 0x00, 0x80, 0x1F, // 34 + 0x00, 0x30, 0x0C, 0x00, 0x00, 0x30, 0xCC, 0x00, 0x00, 0x30, 0xFE, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x80, + 0x3F, 0x0C, 0x00, 0x80, 0x31, 0xCC, 0x00, 0x00, 0x30, 0xFE, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0xFE, 0x0D, 0x00, 0x80, 0x3F, + 0x0C, 0x00, 0x80, 0x31, 0x0C, 0x00, 0x00, 0x30, 0x0C, // 35 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x18, 0x00, 0x00, 0x3F, 0x78, 0x00, 0x00, 0x63, 0x70, 0x00, 0x80, 0x61, 0xE0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x81, 0xC1, 0x00, 0x00, 0x83, 0x61, 0x00, 0x00, 0x07, + 0x7F, 0x00, 0x00, 0x04, 0x1E, // 36 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, + 0x80, 0x80, 0x00, 0x80, 0xC1, 0xE0, 0x00, 0x00, 0x7F, 0x78, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xC0, + 0x03, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x3C, 0x3E, 0x00, 0x00, 0x0F, 0x7F, 0x00, 0x80, 0x83, 0xC1, 0x00, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x3E, // 37 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x8E, 0x73, 0x00, 0x00, 0xDF, 0xE1, 0x00, 0x80, + 0xF3, 0xC0, 0x00, 0x80, 0xE1, 0xC0, 0x00, 0x80, 0xE1, 0xC1, 0x00, 0x80, 0xB3, 0xE3, 0x00, 0x00, 0x3F, 0x6E, 0x00, 0x00, 0x0E, + 0x7C, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0x40, // 38 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x80, 0x1F, // 39 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x3E, 0xC0, 0x07, 0x00, 0x07, 0x00, 0x0E, 0x80, + 0x01, 0x00, 0x18, 0x80, 0x00, 0x00, 0x10, // 40 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x10, 0x80, 0x01, 0x00, 0x18, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x3E, 0xC0, 0x07, 0x00, + 0xF8, 0xFF, 0x01, 0x00, 0xC0, 0x3F, // 41 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x80, + 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, // 42 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 43 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0, 0x07, // 44 + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, // 45 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, // 46 + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, + 0x0F, 0x00, 0x00, 0x80, 0x01, // 47 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x07, 0x70, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x80, + 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0xFE, + 0x3F, 0x00, 0x00, 0xF8, 0x0F, // 48 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 49 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x80, 0x01, 0xD8, 0x00, 0x80, + 0x01, 0xCC, 0x00, 0x80, 0x01, 0xC6, 0x00, 0x80, 0x01, 0xC3, 0x00, 0x80, 0x81, 0xC1, 0x00, 0x00, 0xC3, 0xC0, 0x00, 0x00, 0x7F, + 0xC0, 0x00, 0x00, 0x3C, 0xC0, // 50 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x18, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x03, 0x70, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x00, 0xE3, 0xC0, 0x00, 0x00, 0xBF, 0x61, 0x00, 0x00, 0x1E, + 0x3F, 0x00, 0x00, 0x00, 0x1E, // 51 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF0, 0x0C, 0x00, 0x00, + 0x38, 0x0C, 0x00, 0x00, 0x1E, 0x0C, 0x00, 0x00, 0x07, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x0C, // 52 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x18, 0x00, 0x00, 0xFE, 0x38, 0x00, 0x80, 0x7F, 0x60, 0x00, 0x80, 0x21, 0xC0, 0x00, 0x80, + 0x31, 0xC0, 0x00, 0x80, 0x31, 0xC0, 0x00, 0x80, 0x31, 0xC0, 0x00, 0x80, 0x31, 0xC0, 0x00, 0x80, 0x61, 0x70, 0x00, 0x80, 0xC1, + 0x3F, 0x00, 0x00, 0x80, 0x0F, // 53 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x8F, 0x71, 0x00, 0x00, 0xC3, 0xE0, 0x00, 0x80, + 0x61, 0xC0, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x80, 0xC3, 0x60, 0x00, 0x00, 0xC7, + 0x3F, 0x00, 0x00, 0x06, 0x1F, // 54 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, + 0x01, 0xFE, 0x00, 0x80, 0xC1, 0x0F, 0x00, 0x80, 0xE1, 0x01, 0x00, 0x80, 0x39, 0x00, 0x00, 0x80, 0x0D, 0x00, 0x00, 0x80, 0x07, + 0x00, 0x00, 0x80, 0x01, // 55 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x7F, 0x00, 0x00, 0xBF, 0x61, 0x00, 0x80, 0xE3, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xE3, 0xC0, 0x00, 0x00, 0xBF, 0x61, 0x00, 0x00, 0x1E, + 0x7F, 0x00, 0x00, 0x00, 0x1E, // 56 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x30, 0x00, 0x00, 0xFE, 0x71, 0x00, 0x00, 0x87, 0xE1, 0x00, 0x80, 0x01, 0xC3, 0x00, 0x80, + 0x01, 0xC3, 0x00, 0x80, 0x01, 0xC3, 0x00, 0x80, 0x01, 0xC3, 0x00, 0x80, 0x81, 0x61, 0x00, 0x00, 0xC7, 0x78, 0x00, 0x00, 0xFE, + 0x3F, 0x00, 0x00, 0xF8, 0x07, // 57 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, // 58 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xC0, 0x0C, 0x00, 0x18, 0xC0, 0x07, // 59 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, + 0x60, 0x03, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x18, + 0x0C, 0x00, 0x00, 0x0C, 0x18, // 60 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, + 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, + 0x06, 0x00, 0x00, 0x30, 0x06, // 61 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, + 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0xC0, + 0x01, 0x00, 0x00, 0x80, // 62 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, + 0x01, 0xCE, 0x00, 0x80, 0x01, 0xCF, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x1C, // 63 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x78, 0xC0, 0x03, 0x00, 0x1C, 0x00, 0x07, 0x00, + 0x0E, 0x1F, 0x06, 0x00, 0xC7, 0x7F, 0x0E, 0x00, 0xE3, 0x60, 0x0C, 0x00, 0x33, 0xC0, 0x0C, 0x80, 0x39, 0xC0, 0x18, 0x80, 0x19, + 0xC0, 0x18, 0x80, 0x19, 0x60, 0x18, 0x80, 0x19, 0x30, 0x18, 0x80, 0x31, 0x78, 0x18, 0x80, 0xE1, 0xFF, 0x18, 0x80, 0xFB, 0xC7, + 0x18, 0x00, 0x3B, 0xC0, 0x18, 0x00, 0x07, 0x60, 0x0C, 0x00, 0x0E, 0x70, 0x0C, 0x00, 0x1C, 0x3C, 0x06, 0x00, 0xF8, 0x1F, 0x06, + 0x00, 0xE0, 0x07, 0x03, 0x00, 0x00, 0x00, 0x01, // 64 + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, + 0x3E, 0x06, 0x00, 0x80, 0x0F, 0x06, 0x00, 0x80, 0x01, 0x06, 0x00, 0x80, 0x0F, 0x06, 0x00, 0x00, 0x3E, 0x06, 0x00, 0x00, 0xF8, + 0x07, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xC0, // 65 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x00, 0xE3, 0xC1, 0x00, 0x00, 0xFF, 0x61, 0x00, 0x00, 0x1E, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 66 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x0E, 0x3C, 0x00, 0x00, 0x08, + 0x0C, // 67 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, + 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF0, + 0x07, // 68 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x01, 0xC0, // 69 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, + 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, + 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0x01, // 70 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x03, 0x60, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x81, + 0xC1, 0x00, 0x80, 0x81, 0xC1, 0x00, 0x80, 0x83, 0xC1, 0x00, 0x00, 0x83, 0x61, 0x00, 0x00, 0x87, 0x61, 0x00, 0x00, 0x8E, 0x3F, + 0x00, 0x00, 0x88, 0x3F, // 71 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 72 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 73 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x3F, // 74 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, + 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x9C, 0x07, 0x00, 0x00, 0x0E, + 0x1E, 0x00, 0x00, 0x07, 0x3C, 0x00, 0x80, 0x03, 0x78, 0x00, 0x80, 0x01, 0xE0, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x80, // 75 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, // 76 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0xFF, 0xFF, + 0x00, 0x80, 0xFF, 0xFF, // 77 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 78 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x1E, 0x3C, + 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF0, 0x07, // 79 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, + 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, + 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3C, // 80 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x03, 0x60, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xD8, 0x00, 0x80, 0x01, 0xD8, 0x00, 0x80, 0x03, 0xF0, 0x00, 0x00, 0x03, 0x70, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x1E, 0xFC, + 0x00, 0x00, 0xFC, 0xDF, 0x01, 0x00, 0xF0, 0x87, 0x01, // 81 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, + 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x01, 0x00, 0x80, 0xC1, 0x03, 0x00, 0x80, 0xC1, + 0x0F, 0x00, 0x80, 0xC1, 0x1E, 0x00, 0x80, 0x63, 0x7C, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0x3E, 0xC0, 0x00, 0x00, 0x00, + 0x80, // 82 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x3C, 0x00, 0x00, 0x7F, 0x70, 0x00, 0x00, 0x63, 0x60, 0x00, 0x80, + 0xE1, 0xE0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC1, 0x00, 0x80, 0x83, 0xE1, 0x00, 0x00, 0x87, 0x63, 0x00, 0x00, 0x0E, 0x3F, 0x00, 0x00, 0x0C, 0x1E, // 83 + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 84 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0x80, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x80, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, // 85 + 0x80, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xE0, + 0x07, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, // 86 + 0x80, 0x01, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x80, 0x0F, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x80, 0x3F, + 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xFE, 0x03, 0x00, + 0x80, 0x1F, 0x00, 0x00, 0x80, 0x01, // 87 + 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x07, 0x78, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x00, + 0x3C, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0x1E, 0x0F, 0x00, 0x00, 0x0F, + 0x1C, 0x00, 0x80, 0x07, 0x78, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x80, // 88 + 0x80, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, // 89 + 0x00, 0x00, 0xC0, 0x00, 0x80, 0x01, 0xE0, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x01, 0xDC, 0x00, 0x80, 0x01, 0xCE, 0x00, 0x80, + 0x01, 0xC7, 0x00, 0x80, 0x81, 0xC3, 0x00, 0x80, 0xE1, 0xC0, 0x00, 0x80, 0x71, 0xC0, 0x00, 0x80, 0x39, 0xC0, 0x00, 0x80, 0x1D, + 0xC0, 0x00, 0x80, 0x07, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x80, 0x01, 0xC0, // 90 + 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x1F, 0x80, 0xFF, 0xFF, 0x1F, 0x80, 0x01, 0x00, 0x18, 0x80, 0x01, 0x00, 0x18, // 91 + 0x80, 0x01, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x00, 0xC0, // 92 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x18, 0x80, 0x01, 0x00, 0x18, 0x80, 0xFF, 0xFF, 0x1F, 0x80, + 0xFF, 0xFF, 0x1F, // 93 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, + 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, + 0x80, // 94 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, // 95 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x02, // 96 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x38, 0x00, 0x00, 0x70, 0x7C, 0x00, 0x00, 0x30, 0xE6, 0x00, 0x00, 0x18, 0xC6, 0x00, 0x00, + 0x18, 0xC6, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0x63, 0x00, 0x00, 0x38, 0x63, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xE0, + 0xFF, 0x00, 0x00, 0x00, 0x80, // 97 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, + 0x30, 0x60, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, + 0x70, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x80, 0x0F, // 98 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x60, + 0x30, // 99 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x30, 0x60, 0x00, 0x00, 0x70, 0x30, 0x00, 0x80, 0xFF, + 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 100 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x73, 0x00, 0x00, 0x38, 0xE3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x38, 0xE3, 0x00, 0x00, 0x70, 0x63, 0x00, 0x00, 0xE0, + 0x33, 0x00, 0x00, 0xC0, 0x13, // 101 + 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x19, 0x00, 0x00, 0x80, + 0x19, 0x00, 0x00, 0x80, 0x19, // 102 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0F, 0x06, 0x00, 0xE0, 0x3F, 0x0E, 0x00, 0x70, 0x70, 0x1C, 0x00, 0x38, 0xE0, 0x18, 0x00, + 0x18, 0xC0, 0x18, 0x00, 0x18, 0xC0, 0x18, 0x00, 0x18, 0xC0, 0x18, 0x00, 0x30, 0x60, 0x1C, 0x00, 0x60, 0x30, 0x0E, 0x00, 0xF8, + 0xFF, 0x07, 0x00, 0xF8, 0xFF, 0x03, // 103 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, + 0xFF, 0x00, 0x00, 0xE0, 0xFF, // 104 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF9, 0xFF, 0x00, 0x80, 0xF9, 0xFF, // 105 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x80, 0xF9, 0xFF, 0x1F, 0x80, 0xF9, 0xFF, 0x0F, // 106 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x80, 0x03, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x60, 0x1E, 0x00, 0x00, 0x30, 0x38, 0x00, 0x00, 0x18, 0xF0, 0x00, 0x00, 0x08, + 0xC0, 0x00, 0x00, 0x00, 0x80, // 107 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 108 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xE0, + 0xFF, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xE0, 0xFF, // 109 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, + 0xFF, 0x00, 0x00, 0xE0, 0xFF, // 110 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, + 0x3F, 0x00, 0x00, 0xC0, 0x1F, // 111 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x60, 0x30, 0x00, 0x00, + 0x30, 0x60, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, + 0x70, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xC0, 0x0F, // 112 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x30, 0x60, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0xF8, + 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, // 113 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, // 114 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x30, 0x00, 0x00, 0xF0, 0x71, 0x00, 0x00, 0xB8, 0xE3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC7, 0x00, 0x00, 0x18, 0xC7, 0x00, 0x00, 0x38, 0xE6, 0x00, 0x00, 0x70, 0x7E, 0x00, 0x00, 0x60, + 0x3C, // 115 + 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, // 116 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 117 + 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, + 0x18, // 118 + 0x00, 0x38, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0x00, 0x7E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x80, + 0x0F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x01, + 0x00, 0x00, 0x38, // 119 + 0x00, 0x08, 0x80, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x78, 0xF0, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0x70, 0xF0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x08, + 0x80, // 120 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x18, 0x00, 0xC0, 0x07, 0x18, 0x00, 0x00, 0x3E, 0x1C, 0x00, + 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x7F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, + 0x18, // 121 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x18, 0xF0, 0x00, 0x00, 0x18, 0xF8, 0x00, 0x00, 0x18, 0xDC, 0x00, 0x00, + 0x18, 0xCF, 0x00, 0x00, 0x98, 0xC3, 0x00, 0x00, 0xD8, 0xC1, 0x00, 0x00, 0xF8, 0xC0, 0x00, 0x00, 0x78, 0xC0, 0x00, 0x00, 0x18, + 0xC0, // 122 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xFF, 0xF9, 0x0F, 0x80, 0xFF, 0xF0, 0x1F, 0x80, + 0x01, 0x00, 0x18, 0x80, 0x01, 0x00, 0x18, // 123 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x3F, 0x80, 0xFF, 0xFF, 0x3F, // 124 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x18, 0x80, 0x01, 0x00, 0x18, 0x80, 0xFF, 0xF0, 0x1F, 0x00, 0xFF, 0xFD, 0x0F, 0x00, + 0x00, 0x0F, 0x00, 0x00, 0x00, 0x06, // 125 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x01, // 126 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, + 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x60, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x00, 0x60, 0xC0, + 0x00, 0x00, 0xE0, 0xE0, 0x00, 0x00, 0xC0, 0x71, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x1F, // 1026 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0xA0, 0x01, 0x00, 0x00, 0xB8, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 1027 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0, 0x07, // 8218 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x80, + 0x1B, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x80, 0x18, 0x00, 0x00, 0x00, 0x18, // 1107 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0, 0x07, // 8222 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, // 8230 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, // 8224 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, + 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, // 8225 + 0x00, 0x30, 0x03, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x36, 0x3B, 0x00, 0x00, 0x33, 0x73, 0x00, 0x00, + 0x33, 0x63, 0x00, 0x80, 0x31, 0xC3, 0x00, 0x80, 0x31, 0xC3, 0x00, 0x80, 0x31, 0xC3, 0x00, 0x80, 0x31, 0xC3, 0x00, 0x80, 0x31, + 0xC0, 0x00, 0x00, 0x03, 0x60, // 8364 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, + 0xC1, 0xE0, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x3E, + 0x3E, 0x00, 0x80, 0x0F, 0x7F, 0x00, 0x80, 0x81, 0xC1, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x80, 0xC1, 0x00, + 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x3E, // 8240 + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x3F, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, + 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, + 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1033 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, + 0x10, 0x40, // 8249 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, + 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x80, 0x61, 0x00, + 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1034 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x20, 0x60, 0x03, 0x00, 0x38, 0x78, 0x07, 0x00, 0x18, 0x1E, 0x0E, 0x00, 0x08, 0x07, + 0x38, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0x80, // 1036 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, + 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0xFF, // 1035 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1039 + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x38, 0x00, 0x18, 0x00, 0xF0, + 0xFF, 0x1F, 0x00, 0xE0, 0xFF, 0x0F, // 1106 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, 0x19, // 8216 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x80, 0x0F, // 8217 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x80, 0x19, // 8220 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x19, 0x00, 0x00, 0x80, 0x0F, // 8221 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, + 0xE0, 0x07, 0x00, 0x00, 0xC0, 0x03, // 8226 + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, // 8211 + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, // 8212 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x1E, 0x00, + 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, + 0x80, 0xFF, 0x01, 0x00, 0x80, 0xFF, 0x01, // 8482 + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, + 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x3C, // 1113 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, + 0x80, 0x0F, 0x00, 0x00, 0x00, 0x02, // 8250 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, + 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x3C, // 1114 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x02, 0x02, 0x00, 0x80, + 0x03, 0x07, 0x00, 0x80, 0xC1, 0x0D, 0x00, 0x80, 0xF0, 0x38, 0x00, 0x00, 0x18, 0x70, 0x00, 0x00, 0x18, 0xE0, 0x00, 0x00, 0x00, + 0x80, // 1116 + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, + 0xFF, 0x00, 0x00, 0xE0, 0xFF, // 1115 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1119 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x1E, 0xC0, 0x00, 0x18, 0x78, 0xC0, 0x00, 0x30, + 0xE0, 0xC1, 0x00, 0x20, 0x80, 0x77, 0x00, 0x20, 0x00, 0x3E, 0x00, 0x20, 0x80, 0x07, 0x00, 0x30, 0xE0, 0x01, 0x00, 0x18, 0x78, + 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x01, // 1038 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x18, 0x80, 0xC1, 0x07, 0x18, 0x00, 0x03, 0x3E, 0x1C, 0x00, + 0x02, 0xF8, 0x0F, 0x00, 0x02, 0xF0, 0x03, 0x00, 0x02, 0x7F, 0x00, 0x00, 0xE3, 0x0F, 0x00, 0x80, 0xF9, 0x00, 0x00, 0x00, + 0x18, // 1118 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x3F, // 1032 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0xDC, 0x3B, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, 0xF8, + 0x1F, 0x00, 0x00, 0xDC, 0x3B, 0x00, 0x00, 0x08, 0x10, // 164 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFC, + 0x01, // 1168 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xE1, 0x3F, 0x80, 0xFF, 0xE1, 0x3F, // 166 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xCE, 0x07, 0x03, 0x00, 0x7F, 0x0C, 0x0F, 0x80, 0x33, 0x1C, 0x0C, 0x80, + 0x71, 0x18, 0x18, 0x80, 0x61, 0x30, 0x18, 0x80, 0xC1, 0x70, 0x18, 0x80, 0xC3, 0xE1, 0x1C, 0x00, 0x87, 0xD3, 0x0F, 0x00, 0x06, + 0x9F, 0x07, 0x00, 0x00, 0x0E, // 167 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0xB0, + 0xC1, 0xC0, 0x00, 0xB0, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0xB0, 0xC1, 0xC0, 0x00, 0xB0, 0xC1, + 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x01, 0xC0, // 1025 + 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0xE7, 0x71, 0x00, 0x00, + 0xFB, 0x67, 0x00, 0x80, 0x19, 0xC6, 0x00, 0x80, 0x0D, 0xCC, 0x00, 0x80, 0x0D, 0xCC, 0x00, 0x80, 0x0D, 0xCC, 0x00, 0x80, 0x0D, + 0xCC, 0x00, 0x80, 0x1D, 0xCE, 0x00, 0x00, 0x1B, 0x66, 0x00, 0x00, 0x17, 0x72, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x1C, 0x1C, + 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x03, // 169 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xCE, 0x3C, 0x00, 0x00, 0xC7, 0x70, 0x00, 0x00, + 0xC3, 0x60, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x0F, 0x78, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x08, + 0x08, // 1028 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, + 0x10, 0x42, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x10, 0x40, // 171 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xF0, + 0x07, 0x00, 0x00, 0xF0, 0x07, // 172 + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, // 173 + 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0xFB, 0x6F, 0x00, 0x80, 0xF9, 0xCF, 0x00, 0x80, 0x99, 0xC1, 0x00, 0x80, 0x99, 0xC1, 0x00, 0x80, 0x99, 0xC3, 0x00, 0x80, 0xF9, + 0xC7, 0x00, 0x80, 0xF1, 0xCC, 0x00, 0x00, 0x03, 0x68, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x1C, 0x1C, + 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x03, // 174 + 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x30, // 1031 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80, + 0x20, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0E, // 176 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, + 0x80, 0xC1, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, + 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, // 177 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1030 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF9, 0xFF, 0x00, 0x80, 0xF9, 0xFF, // 1110 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x80, 0x1F, // 1169 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 181 + 0x00, 0x3C, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, + 0xFF, 0xFF, 0x1F, 0x80, 0xFF, 0xFF, 0x1F, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x1F, 0x80, 0xFF, + 0xFF, 0x1F, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 182 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 183 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x73, 0x00, 0x00, 0x3B, 0xE3, 0x00, 0x00, + 0x1B, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x3B, 0xE3, 0x00, 0x00, 0x73, 0x63, 0x00, 0x00, 0xE0, + 0x33, 0x00, 0x00, 0xC0, 0x13, // 1105 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x63, 0x00, 0x00, 0xF0, 0x67, 0x00, 0x00, 0x38, 0x6E, 0x00, 0x00, 0x18, 0x6C, 0x00, + 0x00, 0x18, 0x6C, 0x00, 0x00, 0x38, 0x6E, 0x00, 0x00, 0xF0, 0x67, 0x00, 0x00, 0xE0, 0x63, // 8470 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0x70, 0x73, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0x60, 0x00, 0x00, 0x70, 0x78, 0x00, 0x00, 0x60, + 0x18, // 1108 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, + 0xE0, 0x3D, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x10, 0x42, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, 0x80, + 0x0F, 0x00, 0x00, 0x00, 0x02, // 187 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x80, 0xF9, 0xFF, 0x1F, 0x80, 0xF9, 0xFF, 0x0F, // 1112 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x3C, 0x00, 0x00, 0x7F, 0x70, 0x00, 0x00, 0x63, 0x60, 0x00, 0x80, + 0xE1, 0xE0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC1, 0x00, 0x80, 0x83, 0xE1, 0x00, 0x00, 0x87, 0x63, 0x00, 0x00, 0x0E, 0x3F, 0x00, 0x00, 0x0C, 0x1E, // 1029 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x30, 0x00, 0x00, 0xF0, 0x71, 0x00, 0x00, 0xB8, 0xE3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC7, 0x00, 0x00, 0x18, 0xC7, 0x00, 0x00, 0x38, 0xE6, 0x00, 0x00, 0x70, 0x7E, 0x00, 0x00, 0x60, + 0x3C, // 1109 + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, // 1111 + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, + 0x3E, 0x06, 0x00, 0x80, 0x0F, 0x06, 0x00, 0x80, 0x01, 0x06, 0x00, 0x80, 0x0F, 0x06, 0x00, 0x00, 0x3E, 0x06, 0x00, 0x00, 0xF8, + 0x07, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xC0, // 1040 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x81, 0x61, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1041 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x00, 0xE3, 0xC1, 0x00, 0x00, 0xFF, 0x61, 0x00, 0x00, 0x1E, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1042 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 1043 + 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x80, 0xFF, 0xC7, 0x00, 0x80, + 0x7F, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xC0, + 0x0F, // 1044 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x01, 0xC0, // 1045 + 0x80, 0x01, 0x80, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x00, 0x07, 0x3C, 0x00, 0x00, 0x1E, 0x0E, 0x00, 0x00, + 0x38, 0x07, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0xFF, + 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x03, + 0x00, 0x00, 0x38, 0x07, 0x00, 0x00, 0x1E, 0x0E, 0x00, 0x00, 0x07, 0x3C, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x01, 0xC0, 0x00, + 0x80, 0x01, 0x80, // 1046 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x78, 0x00, 0x80, 0x03, 0x60, 0x00, 0x80, + 0x01, 0xE0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x00, 0xE3, + 0xE1, 0x00, 0x00, 0xBF, 0x73, 0x00, 0x00, 0x1C, 0x3F, 0x00, 0x00, 0x00, 0x1E, // 1047 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1048 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x70, 0x00, 0x18, + 0x00, 0x3C, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x20, 0x80, 0x07, 0x00, 0x20, 0xC0, 0x01, 0x00, 0x20, 0xF0, 0x00, 0x00, 0x30, 0x38, + 0x00, 0x00, 0x18, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1049 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x78, 0x07, 0x00, 0x00, 0x1E, 0x0E, 0x00, 0x00, 0x07, + 0x38, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0x80, // 1050 + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x3F, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1051 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0xFF, 0xFF, + 0x00, 0x80, 0xFF, 0xFF, // 1052 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1053 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x1E, 0x3C, + 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF0, 0x07, // 1054 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1055 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, + 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, + 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3C, // 1056 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x0E, 0x3C, 0x00, 0x00, 0x08, + 0x0C, // 1057 + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 1058 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x1E, 0xC0, 0x00, 0x00, 0x78, 0xC0, 0x00, 0x00, + 0xE0, 0xC1, 0x00, 0x00, 0x80, 0x77, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x01, // 1059 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, + 0x0E, 0x38, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x06, + 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, + 0x00, 0x00, 0xE0, 0x03, // 1060 + 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x07, 0x78, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x00, + 0x3C, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0x1E, 0x0F, 0x00, 0x00, 0x0F, + 0x1C, 0x00, 0x80, 0x07, 0x78, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x80, // 1061 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, + 0x1F, 0x00, 0x00, 0xC0, 0x1F, // 1062 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1063 + 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, + 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, + 0xFF, // 1064 + 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, + 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x1F, // 1065 + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, + 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x80, 0x61, + 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1066 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, + 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, + 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1067 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, + 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, + 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1068 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x0F, 0x78, 0x00, 0x00, 0x03, 0x60, 0x00, 0x80, + 0x03, 0xE0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x00, 0xC3, 0x60, 0x00, 0x00, 0xC7, 0x70, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF0, + 0x07, // 1069 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, + 0x70, 0x00, 0x00, 0x03, 0x60, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, + 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x1E, 0x3C, 0x00, + 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF0, 0x07, // 1070 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x3E, 0xC0, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x80, 0x63, 0x7C, 0x00, 0x80, + 0xC1, 0x1E, 0x00, 0x80, 0xC1, 0x0F, 0x00, 0x80, 0xC1, 0x03, 0x00, 0x80, 0xC1, 0x01, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, + 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1071 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x38, 0x00, 0x00, 0x70, 0x7C, 0x00, 0x00, 0x30, 0xE6, 0x00, 0x00, 0x18, 0xC6, 0x00, 0x00, + 0x18, 0xC6, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0x63, 0x00, 0x00, 0x38, 0x63, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xE0, + 0xFF, 0x00, 0x00, 0x00, 0x80, // 1072 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x73, 0x70, 0x00, 0x00, 0x31, 0xE0, 0x00, 0x80, + 0x19, 0xC0, 0x00, 0x80, 0x19, 0xC0, 0x00, 0x80, 0x19, 0xC0, 0x00, 0x80, 0x19, 0xC0, 0x00, 0x80, 0x39, 0xE0, 0x00, 0x80, 0x71, + 0x70, 0x00, 0x80, 0xE1, 0x3F, 0x00, 0x80, 0x80, 0x0F, // 1073 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0xF0, 0xE7, 0x00, 0x00, 0xF0, + 0x7E, 0x00, 0x00, 0x00, 0x3C, // 1074 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, // 1075 + 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xF8, 0xDF, 0x00, 0x00, 0xF8, 0xC3, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x0F, // 1076 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x73, 0x00, 0x00, 0x38, 0xE3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x38, 0xE3, 0x00, 0x00, 0x70, 0x63, 0x00, 0x00, 0xE0, + 0x33, 0x00, 0x00, 0xC0, 0x13, // 1077 + 0x00, 0x18, 0xE0, 0x00, 0x00, 0x18, 0x70, 0x00, 0x00, 0xF8, 0x38, 0x00, 0x00, 0xE0, 0x0D, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, + 0x0D, 0x00, 0x00, 0xF8, 0x38, 0x00, 0x00, 0x18, 0x70, 0x00, 0x00, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x80, // 1078 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x30, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x38, 0xE7, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xE0, 0x3C, // 1079 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1080 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x00, + 0x03, 0x3C, 0x00, 0x00, 0x02, 0x0F, 0x00, 0x00, 0x82, 0x07, 0x00, 0x00, 0xE2, 0x01, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x80, 0xF9, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1081 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0xC0, 0x0D, 0x00, 0x00, 0xF0, 0x38, 0x00, 0x00, 0x18, 0x70, 0x00, 0x00, 0x18, 0xE0, 0x00, 0x00, 0x00, + 0x80, // 1082 + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1083 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0xF0, 0x03, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x80, + 0x1F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1084 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1085 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, + 0x3F, 0x00, 0x00, 0xC0, 0x1F, // 1086 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1087 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x60, 0x30, 0x00, 0x00, + 0x30, 0x60, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, + 0x70, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xC0, 0x0F, // 1088 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x60, + 0x30, // 1089 + 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, + 0xF8, 0xFF, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, // 1090 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x18, 0x00, 0xC0, 0x07, 0x18, 0x00, 0x00, 0x3E, 0x1C, 0x00, + 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x7F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, + 0x18, // 1091 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x70, 0x60, 0x00, 0x80, 0xFF, 0xFF, 0x1F, 0x80, 0xFF, + 0xFF, 0x1F, 0x00, 0x70, 0x60, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xE0, + 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xC0, 0x1F, // 1092 + 0x00, 0x08, 0x80, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x78, 0xF0, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0x70, 0xF0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x08, + 0x80, // 1093 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x00, 0xC0, 0x1F, // 1094 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, // 1095 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1096 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x00, 0xC0, 0x1F, // 1097 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, + 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, + 0xC3, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x3C, // 1098 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, + 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1099 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, + 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x00, 0x3C, // 1100 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0x70, 0x78, 0x00, 0x00, 0x38, 0x60, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x70, 0x73, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xC0, + 0x1F, // 1101 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x18, + 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, 0x3F, + 0x00, 0x00, 0xC0, 0x1F, // 1102 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC1, 0x00, 0x00, 0xF0, 0xE3, 0x00, 0x00, 0x38, 0x7B, 0x00, 0x00, 0x18, 0x1A, 0x00, 0x00, + 0x18, 0x0E, 0x00, 0x00, 0x18, 0x06, 0x00, 0x00, 0x18, 0x06, 0x00, 0x00, 0x18, 0x06, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, // 1103 }; \ No newline at end of file diff --git a/src/graphics/fonts/OLEDDisplayFontsUA.h b/src/graphics/fonts/OLEDDisplayFontsUA.h index 3bd9bb4ca..dc313aed2 100644 --- a/src/graphics/fonts/OLEDDisplayFontsUA.h +++ b/src/graphics/fonts/OLEDDisplayFontsUA.h @@ -8,4 +8,6 @@ #endif extern const uint8_t ArialMT_Plain_10_UA[] PROGMEM; +extern const uint8_t ArialMT_Plain_16_UA[] PROGMEM; +extern const uint8_t ArialMT_Plain_24_UA[] PROGMEM; #endif \ No newline at end of file diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 3ec6ff690..bbb3f90e0 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -124,7 +124,8 @@ int32_t ExternalNotificationModule::runOnce() if (externalTurnedOn[2] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) { - LOG_DEBUG("EXTERNAL 2 %d compared to %d", externalTurnedOn[2]+moduleConfig.external_notification.output_ms, millis()); + LOG_DEBUG("EXTERNAL 2 %d compared to %d", externalTurnedOn[2] + moduleConfig.external_notification.output_ms, + millis()); setExternalState(2, !getExternal(2)); } #if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE) From 060a3bde4d1d708ee42f91090e54198cc6eef177 Mon Sep 17 00:00:00 2001 From: virgil Date: Thu, 28 Nov 2024 20:19:18 +0800 Subject: [PATCH 016/132] fix: Solve the lightsleep crash problem via disable lightsleep for indicator. (#5470) --- src/mesh/NodeDB.cpp | 2 +- src/sleep.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index b529fa934..b38f55ae6 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -542,7 +542,7 @@ void NodeDB::initConfigIntervals() config.display.screen_on_secs = default_screen_on_secs; -#if defined(T_WATCH_S3) || defined(T_DECK) || defined(RAK14014) || defined(SENSECAP_INDICATOR) +#if defined(T_WATCH_S3) || defined(T_DECK) || defined(RAK14014) config.power.is_power_saving = true; config.display.screen_on_secs = 30; config.power.wait_bluetooth_secs = 30; diff --git a/src/sleep.cpp b/src/sleep.cpp index 05597cdfa..904bc3ab8 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -336,6 +336,11 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r { // LOG_DEBUG("Enter light sleep"); + //LORA_DIO1 is an extended IO pin. Setting it as a wake-up pin will cause problems, such as the indicator device not entering LightSleep. +#if defined(SENSECAP_INDICATOR) + return ESP_SLEEP_WAKEUP_TIMER; +#endif + waitEnterSleep(false); uint64_t sleepUsec = sleepMsec * 1000LL; From 5ad30a55ea3ec18ab81bed2db0d3a6c8fe8bf80b Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 28 Nov 2024 06:26:51 -0600 Subject: [PATCH 017/132] Trunk --- CODE_OF_CONDUCT.md | 2 +- src/sleep.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 1e23cdf4d..6843fc85d 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ # Contributor Covenant Code of Conduct -The Meshtastic Firmware project is subject to the code of conduct for the parent project, which can be found here: +The Meshtastic Firmware project is subject to the code of conduct for the parent project, which can be found here: https://meshtastic.org/docs/legal/conduct/ diff --git a/src/sleep.cpp b/src/sleep.cpp index 904bc3ab8..69eb0349a 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -336,7 +336,8 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r { // LOG_DEBUG("Enter light sleep"); - //LORA_DIO1 is an extended IO pin. Setting it as a wake-up pin will cause problems, such as the indicator device not entering LightSleep. + // LORA_DIO1 is an extended IO pin. Setting it as a wake-up pin will cause problems, such as the indicator device not entering + // LightSleep. #if defined(SENSECAP_INDICATOR) return ESP_SLEEP_WAKEUP_TIMER; #endif From b5777beb7d2dc3b835646128596642a8107b3523 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 28 Nov 2024 11:20:06 -0600 Subject: [PATCH 018/132] Warnings and log cleanup (#5472) * Don't log if keyboard not found * Signed comparison issue --- src/gps/GPS.h | 6 +++--- src/input/cardKbI2cImpl.cpp | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 74d73e39a..15fc50fe7 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -158,8 +158,8 @@ class GPS : private concurrency::OSThread uint32_t rx_gpio = 0; uint32_t tx_gpio = 0; - int speedSelect = 0; - int probeTries = 0; + uint8_t speedSelect = 0; + uint8_t probeTries = 0; /** * hasValidLocation - indicates that the position variables contain a complete @@ -239,4 +239,4 @@ class GPS : private concurrency::OSThread }; extern GPS *gps; -#endif // Exclude GPS +#endif // Exclude GPS \ No newline at end of file diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index b940f5448..eb9b07d6e 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -49,10 +49,11 @@ void CardKbI2cImpl::init() kb_model = 0x00; } } - LOG_DEBUG("Keyboard Type: 0x%02x Model: 0x%02x Address: 0x%02x", kb_info.type, kb_model, cardkb_found.address); if (cardkb_found.address == 0x00) { disable(); return; + } else { + LOG_DEBUG("Keyboard Type: 0x%02x Model: 0x%02x Address: 0x%02x", kb_info.type, kb_model, cardkb_found.address); } } #else From 79da2365f0ee900ca194db0c0828bca59140c0c4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 08:03:46 -0600 Subject: [PATCH 019/132] [create-pull-request] automated change (#5475) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index bed14bad5..a27fba5c0 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 14 +build = 15 From ac6b6c8d835bd16a0eeb7c1c88fdbe2687eabe65 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 29 Nov 2024 17:05:28 -0600 Subject: [PATCH 020/132] Adds libusb dev package to Raspbian build steps (#5480) --- .github/workflows/build_native.yml | 2 +- .github/workflows/build_raspbian.yml | 2 +- .github/workflows/build_raspbian_armv7l.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml index 1fb44a717..d4b0c8d58 100644 --- a/.github/workflows/build_native.yml +++ b/.github/workflows/build_native.yml @@ -14,7 +14,7 @@ jobs: 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 + 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 diff --git a/.github/workflows/build_raspbian.yml b/.github/workflows/build_raspbian.yml index e857ae635..1826504f0 100644 --- a/.github/workflows/build_raspbian.yml +++ b/.github/workflows/build_raspbian.yml @@ -14,7 +14,7 @@ jobs: shell: bash run: | sudo apt-get update -y --fix-missing - sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev + 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 diff --git a/.github/workflows/build_raspbian_armv7l.yml b/.github/workflows/build_raspbian_armv7l.yml index f7fddd038..fd53585a5 100644 --- a/.github/workflows/build_raspbian_armv7l.yml +++ b/.github/workflows/build_raspbian_armv7l.yml @@ -14,7 +14,7 @@ jobs: shell: bash run: | sudo apt-get update -y --fix-missing - sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev + 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 From fe8e0713cc9897fec2e66f134a878e0879a8c257 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 19:40:44 -0600 Subject: [PATCH 021/132] [create-pull-request] automated change (#5478) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index a27fba5c0..9d03516e4 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 15 +build = 16 From 43b897217199696daf16747112bb5272ccf0acc1 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 29 Nov 2024 21:29:45 -0600 Subject: [PATCH 022/132] Portduino fixes (#5479) * Set config.yaml defaults even if General is missing * Unsigned values should get %u in logging --- src/mesh/NodeDB.cpp | 4 ++-- src/platform/portduino/PortduinoGlue.cpp | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index b38f55ae6..0d63d3b9b 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1349,7 +1349,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) if (!lite) { if (isFull()) { - LOG_INFO("Node database full with %i nodes and %i bytes free. Erasing oldest entry", numMeshNodes, + LOG_INFO("Node database full with %i nodes and %u bytes free. Erasing oldest entry", numMeshNodes, memGet.getFreeHeap()); // look for oldest node and erase it uint32_t oldest = UINT32_MAX; @@ -1385,7 +1385,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) // everything is missing except the nodenum memset(lite, 0, sizeof(*lite)); lite->num = n; - LOG_INFO("Adding node to database with %i nodes and %i bytes free!", numMeshNodes, memGet.getFreeHeap()); + LOG_INFO("Adding node to database with %i nodes and %u bytes free!", numMeshNodes, memGet.getFreeHeap()); } return lite; diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 686564cc1..d53a5be94 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -400,11 +400,9 @@ bool loadConfig(const char *configPath) settingsStrings[webserverrootpath] = (yamlConfig["Webserver"]["RootPath"]).as(""); } - if (yamlConfig["General"]) { - settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as(200); - settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as(100); - settingsStrings[config_directory] = (yamlConfig["General"]["ConfigDirectory"]).as(""); - } + settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as(200); + settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as(100); + settingsStrings[config_directory] = (yamlConfig["General"]["ConfigDirectory"]).as(""); } catch (YAML::Exception &e) { std::cout << "*** Exception " << e.what() << std::endl; From 9f4c8a28043633fbeffc00da8c0ede02cebfdf9a Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sat, 30 Nov 2024 20:16:02 +0100 Subject: [PATCH 023/132] Update arduino-pico core and remove MDNS restriction (#5483) --- arch/rp2xx0/rp2040.ini | 4 ++-- arch/rp2xx0/rp2350.ini | 4 ++-- src/mesh/wifi/WiFiAPClient.cpp | 6 +----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/arch/rp2xx0/rp2040.ini b/arch/rp2xx0/rp2040.ini index 85efa583c..5cfa678d5 100644 --- a/arch/rp2xx0/rp2040.ini +++ b/arch/rp2xx0/rp2040.ini @@ -1,8 +1,8 @@ ; Common settings for rp2040 Processor based targets [rp2040_base] -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#19e30129fb1428b823be585c787dcb4ac0d9014c ; For arduino-pico 4.2.1 +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#19e30129fb1428b823be585c787dcb4ac0d9014c ; For arduino-pico >=4.2.1 extends = arduino_base -platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#996c3bfab9758f12c07aa20cc6d352e630c16987 ; 4.2.1 with fix for sporadic hangs +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#6024e9a7e82a72e38dd90f42029ba3748835eb2e ; 4.3.0 with fix MDNS board_build.core = earlephilhower board_build.filesystem_size = 0.5m diff --git a/arch/rp2xx0/rp2350.ini b/arch/rp2xx0/rp2350.ini index 6daf59bdf..c5849ff2a 100644 --- a/arch/rp2xx0/rp2350.ini +++ b/arch/rp2xx0/rp2350.ini @@ -1,8 +1,8 @@ ; Common settings for rp2040 Processor based targets [rp2350_base] -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#19e30129fb1428b823be585c787dcb4ac0d9014c ; For arduino-pico 4.2.1 +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#19e30129fb1428b823be585c787dcb4ac0d9014c ; For arduino-pico >=4.2.1 extends = arduino_base -platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#96c3bfab9758f12c07aa20cc6d352e630c16987 ; 4.2.1 with fix for sporadic hangs +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#6024e9a7e82a72e38dd90f42029ba3748835eb2e ; 4.3.0 with fix MDNS board_build.core = earlephilhower board_build.filesystem_size = 0.5m diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index 779576d64..911a47093 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -62,11 +62,7 @@ static void onNetworkConnected() LOG_INFO("Start WiFi network services"); // start mdns - if ( -#ifdef ARCH_RP2040 - !moduleConfig.mqtt.enabled && // MDNS is not supported when MQTT is enabled on ARCH_RP2040 -#endif - !MDNS.begin("Meshtastic")) { + if (!MDNS.begin("Meshtastic")) { LOG_ERROR("Error setting up MDNS responder!"); } else { LOG_INFO("mDNS Host: Meshtastic.local"); From 594af0cacd68885aa34192778717b7ca2f5515f3 Mon Sep 17 00:00:00 2001 From: dylanli Date: Mon, 2 Dec 2024 16:59:34 +0800 Subject: [PATCH 024/132] Update xiao_esp32 fully support L67K (#5488) L67K module hardware changed --- variants/seeed_xiao_s3/variant.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variants/seeed_xiao_s3/variant.h b/variants/seeed_xiao_s3/variant.h index ab886d354..f854ba38a 100644 --- a/variants/seeed_xiao_s3/variant.h +++ b/variants/seeed_xiao_s3/variant.h @@ -41,7 +41,7 @@ L76K GPS Module Information : https://www.seeedstudio.com/L76K-GNSS-Module-for-S L76K Expansion Board can not directly used, L76K Reset Pin needs to override or physically remove it, otherwise it will conflict with the SPI pins */ -// #define GPS_L76K +#define GPS_L76K #ifdef GPS_L76K #define GPS_RX_PIN 44 #define GPS_TX_PIN 43 @@ -81,4 +81,4 @@ L76K GPS Module Information : https://www.seeedstudio.com/L76K-GNSS-Module-for-S // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 #define SX126X_DIO2_AS_RF_SWITCH #define SX126X_DIO3_TCXO_VOLTAGE 1.8 -#endif \ No newline at end of file +#endif From d00e0f6911005b2904bed64adcbdc6f0103336ee Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 3 Dec 2024 06:17:25 -0600 Subject: [PATCH 025/132] 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 --- .trunk/trunk.yaml | 4 +- bin/build-userprefs-json.py | 2 +- bin/platformio-custom.py | 36 ++++++++++-- src/ButtonThread.cpp | 2 +- src/graphics/Screen.h | 3 +- src/main.cpp | 1 - src/mesh/Channels.cpp | 2 +- src/mesh/Default.cpp | 2 +- src/mesh/FloodingRouter.cpp | 2 +- src/mesh/NodeDB.cpp | 1 - src/mesh/Router.cpp | 1 - src/modules/AdminModule.cpp | 4 +- userPrefs.h | 107 ------------------------------------ userPrefs.json | 16 ------ userPrefs.jsonc | 37 +++++++++++++ 15 files changed, 79 insertions(+), 141 deletions(-) delete mode 100644 userPrefs.h delete mode 100644 userPrefs.json create mode 100644 userPrefs.jsonc diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 040712e1d..743f4214d 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -30,7 +30,7 @@ lint: - git-diff-check - gitleaks@8.21.1 - clang-format@16.0.3 - - prettier@3.3.3 + #- prettier@3.3.3 ignore: - linters: [ALL] paths: @@ -46,4 +46,4 @@ actions: enabled: - trunk-fmt-pre-commit - trunk-check-pre-push - - trunk-upgrade-available + - trunk-upgrade-available \ No newline at end of file diff --git a/bin/build-userprefs-json.py b/bin/build-userprefs-json.py index 58f460bcf..d155bae01 100644 --- a/bin/build-userprefs-json.py +++ b/bin/build-userprefs-json.py @@ -24,7 +24,7 @@ def write_macros_to_json(macros, output_file): def main(): header_file = 'userPrefs.h' - output_file = 'userPrefs.json' + output_file = 'userPrefs.jsonc' # Uncomment all macros in the header file with open(header_file, 'r') as file: lines = file.readlines() diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index 701f6b5d8..acfeae10c 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -3,6 +3,8 @@ # trunk-ignore-all(flake8/F821): For SConstruct imports import sys from os.path import join +import json +import re from readprops import readProps @@ -90,11 +92,37 @@ prefsLoc = projenv["PROJECT_DIR"] + "/version.properties" verObj = readProps(prefsLoc) print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV")) +jsonLoc = env["PROJECT_DIR"] + "/userPrefs.jsonc" +with open(jsonLoc) as f: + jsonStr = re.sub("//.*","", f.read(), flags=re.MULTILINE) + userPrefs = json.loads(jsonStr) + +pref_flags = [] +# Pre-process the userPrefs +for pref in userPrefs: + if userPrefs[pref].startswith("{"): + pref_flags.append("-D" + pref + "=" + userPrefs[pref]) + elif userPrefs[pref].replace(".", "").isdigit(): + pref_flags.append("-D" + pref + "=" + userPrefs[pref]) + elif userPrefs[pref] == "true" or userPrefs[pref] == "false": + pref_flags.append("-D" + pref + "=" + userPrefs[pref]) + elif userPrefs[pref].startswith("meshtastic_"): + pref_flags.append("-D" + pref + "=" + userPrefs[pref]) + # If the value is a string, we need to wrap it in quotes + else: + pref_flags.append("-D" + pref + "=" + env.StringifyMacro(userPrefs[pref]) + "") + # General options that are passed to the C and C++ compilers -projenv.Append( - CCFLAGS=[ +flags = [ "-DAPP_VERSION=" + verObj["long"], "-DAPP_VERSION_SHORT=" + verObj["short"], "-DAPP_ENV=" + env.get("PIOENV"), - ] -) + ] + pref_flags + +print ("Using flags:") +for flag in flags: + print(flag) + +projenv.Append( + CCFLAGS=flags, +) \ No newline at end of file diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index 238359952..3f64b3b3e 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -1,5 +1,5 @@ #include "ButtonThread.h" -#include "../userPrefs.h" + #include "configuration.h" #if !MESHTASTIC_EXCLUDE_GPS #include "GPS.h" diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 41c90ca9a..00884c5af 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -1,6 +1,5 @@ #pragma once -#include "../userPrefs.h" #include "configuration.h" #include "detect/ScanI2C.h" @@ -606,4 +605,4 @@ class Screen : public concurrency::OSThread } // namespace graphics -#endif +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 9036cd59c..902668d23 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,3 @@ -#include "../userPrefs.h" #include "configuration.h" #if !MESHTASTIC_EXCLUDE_GPS #include "GPS.h" diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index a516268eb..cfaff7640 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -1,5 +1,5 @@ #include "Channels.h" -#include "../userPrefs.h" + #include "CryptoEngine.h" #include "Default.h" #include "DisplayFormatters.h" diff --git a/src/mesh/Default.cpp b/src/mesh/Default.cpp index ba1dafe70..1bd0340f8 100644 --- a/src/mesh/Default.cpp +++ b/src/mesh/Default.cpp @@ -1,5 +1,5 @@ #include "Default.h" -#include "../userPrefs.h" + #include "meshUtils.h" uint32_t Default::getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval) diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index 81ea72381..e959297bf 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -1,5 +1,5 @@ #include "FloodingRouter.h" -#include "../userPrefs.h" + #include "configuration.h" #include "mesh-pb-constants.h" diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 0d63d3b9b..8935e1215 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1,4 +1,3 @@ -#include "../userPrefs.h" #include "configuration.h" #if !MESHTASTIC_EXCLUDE_GPS #include "GPS.h" diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 7b792db30..1303c5caa 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -20,7 +20,6 @@ #if ENABLE_JSON_LOGGING || ARCH_PORTDUINO #include "serialization/MeshPacketSerializer.h" #endif -#include "../userPrefs.h" #define MAX_RX_FROMRADIO \ 4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index c81b6ede4..2d33b723d 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -18,7 +18,7 @@ #ifdef ARCH_PORTDUINO #include "unistd.h" #endif -#include "../userPrefs.h" + #include "Default.h" #include "TypeConversions.h" @@ -1123,4 +1123,4 @@ void disableBluetooth() nrf52Bluetooth->shutdown(); #endif #endif -} +} \ No newline at end of file diff --git a/userPrefs.h b/userPrefs.h deleted file mode 100644 index 00f04149a..000000000 --- a/userPrefs.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef _USERPREFS_ -#define _USERPREFS_ - -// Slipstream values: - -#define USERPREFS_TZ_STRING "tzplaceholder " - -// Uncomment and modify to set device defaults - -// #define USERPREFS_EVENT_MODE 1 - -// #define USERPREFS_CONFIG_LORA_REGION meshtastic_Config_LoRaConfig_RegionCode_US -// #define USERPREFS_LORACONFIG_MODEM_PRESET meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST -// #define USERPREFS_LORACONFIG_CHANNEL_NUM 31 -// #define USERPREFS_CONFIG_LORA_IGNORE_MQTT true - -// #define USERPREFS_CONFIG_GPS_MODE meshtastic_Config_PositionConfig_GpsMode_ENABLED - -// #define USERPREFS_CHANNELS_TO_WRITE 3 -/* -#define USERPREFS_CHANNEL_0_PSK \ - { \ - 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, \ - 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 \ - } -*/ -// #define USERPREFS_CHANNEL_0_NAME "DEFCONnect" -// #define USERPREFS_CHANNEL_0_PRECISION 14 -// #define USERPREFS_CHANNEL_0_UPLINK_ENABLED true -// #define USERPREFS_CHANNEL_0_DOWNLINK_ENABLED true -/* -#define USERPREFS_CHANNEL_1_PSK \ - { \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 \ - } -*/ -// #define USERPREFS_CHANNEL_1_NAME "REPLACEME" -// #define USERPREFS_CHANNEL_1_PRECISION 14 -// #define USERPREFS_CHANNEL_1_UPLINK_ENABLED true -// #define USERPREFS_CHANNEL_1_DOWNLINK_ENABLED true -/* -#define USERPREFS_CHANNEL_2_PSK \ - { \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 \ - } -*/ -// #define USERPREFS_CHANNEL_2_NAME "REPLACEME" -// #define USERPREFS_CHANNEL_2_PRECISION 14 -// #define USERPREFS_CHANNEL_2_UPLINK_ENABLED true -// #define USERPREFS_CHANNEL_2_DOWNLINK_ENABLED true - -// #define USERPREFS_CONFIG_OWNER_LONG_NAME "My Long Name" -// #define USERPREFS_CONFIG_OWNER_SHORT_NAME "MLN" - -// #define USERPREFS_SPLASH_TITLE "DEFCONtastic" -// #define icon_width 34 -// #define icon_height 29 -// #define USERPREFS_HAS_SPLASH -/* -static unsigned char icon_bits[] = { - 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, - 0x9E, 0xE7, 0x00, 0x00, 0x00, 0x0E, 0xC7, 0x01, 0x00, 0x1C, 0x0F, 0xC7, 0x01, 0x00, 0x1C, 0xDF, 0xE7, 0x63, 0x00, 0x1C, 0xFF, - 0xBF, 0xE1, 0x00, 0x3C, 0xF3, 0xBF, 0xE3, 0x00, 0x7F, 0xF7, 0xBF, 0xF1, 0x00, 0xFF, 0xF7, 0xBF, 0xF9, 0x03, 0xFF, 0xE7, 0x9F, - 0xFF, 0x03, 0xC0, 0xCF, 0xEF, 0xDF, 0x03, 0x00, 0xDF, 0xE3, 0x8F, 0x00, 0x00, 0x7C, 0xFB, 0x03, 0x00, 0x00, 0xF8, 0xFF, 0x00, - 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x78, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0x00, 0x00, - 0x98, 0x3F, 0xF0, 0x23, 0x00, 0xFC, 0x0F, 0xE0, 0x7F, 0x00, 0xFC, 0x03, 0x80, 0xFF, 0x01, 0xFC, 0x00, 0x00, 0x3E, 0x00, 0x70, - 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00}; -*/ - -/* - * PKI Admin keys. - * If a Admin key is set with '{};' - * then it will be ignored, a PKI key must have a size of 32 byte. - */ -/* -#define USERPREFS_USE_ADMIN_KEY_0 \ - { \ - 0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, \ - 0x04, 0x1a, 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c \ - }; -*/ -// #define USERPREFS_USE_ADMIN_KEY_1 {}; -// #define USERPREFS_USE_ADMIN_KEY_2 {}; - -/* - * USERPREF_FIXED_GPS_LAT and USERPREF_FIXED_GPS_LON must be set, USERPREF_FIXED_GPS_ALT is optional - * - * Fixed GPS is Eiffel Tower, Paris, France - */ -// #define USERPREFS_FIXED_GPS -// #define USERPREFS_FIXED_GPS_LAT 48.85873920 -// #define USERPREFS_FIXED_GPS_LON 2.294508368 -// #define USERPREFS_FIXED_GPS_ALT 0 - -/* - * Set Fixed Bluetooth paring code - */ -// #define USERPREFS_FIXED_BLUETOOTH 121212 - -/* - * Will overwrite BUTTON_PIN if set - */ -// #define USERPREFS_BUTTON_PIN 36 - -#endif \ No newline at end of file diff --git a/userPrefs.json b/userPrefs.json deleted file mode 100644 index bc62602be..000000000 --- a/userPrefs.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "USERPREFS_CHANNEL_0_NAME": "\"DEFCONnect\"", - "USERPREFS_CHANNEL_0_PRECISION": "14", - "USERPREFS_CHANNEL_0_PSK": "{ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 }", - "USERPREFS_CONFIG_LORA_IGNORE_MQTT": "true", - "USERPREFS_CONFIG_LORA_REGION": "meshtastic_Config_LoRaConfig_RegionCode_US", - "USERPREFS_CONFIG_OWNER_LONG_NAME": "\"My Long Name\"", - "USERPREFS_CONFIG_OWNER_SHORT_NAME": "\"MLN\"", - "USERPREFS_EVENT_MODE": "1", - "USERPREFS_HAS_SPLASH": "", - "USERPREFS_LORACONFIG_CHANNEL_NUM": "31", - "USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST", - "USERPREFS_SPLASH_TITLE": "\"DEFCONtastic\"", - "USERPREFS_TZ_STRING": "\"tzplaceholder \"", - "USERPREFS_USE_ADMIN_KEY": "1" -} diff --git a/userPrefs.jsonc b/userPrefs.jsonc new file mode 100644 index 000000000..055f59273 --- /dev/null +++ b/userPrefs.jsonc @@ -0,0 +1,37 @@ +{ + // "USERPREFS_BUTTON_PIN": "36", + // "USERPREFS_CHANNELS_TO_WRITE": "3", + // "USERPREFS_CHANNEL_0_DOWNLINK_ENABLED": "true", + // "USERPREFS_CHANNEL_0_NAME": "DEFCONnect", + // "USERPREFS_CHANNEL_0_PRECISION": "14", + // "USERPREFS_CHANNEL_0_PSK": "{ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 }", + // "USERPREFS_CHANNEL_0_UPLINK_ENABLED": "true", + // "USERPREFS_CHANNEL_1_DOWNLINK_ENABLED": "true", + // "USERPREFS_CHANNEL_1_NAME": "REPLACEME", + // "USERPREFS_CHANNEL_1_PRECISION": "14", + // "USERPREFS_CHANNEL_1_PSK": "{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }", + // "USERPREFS_CHANNEL_1_UPLINK_ENABLED": "true", + // "USERPREFS_CHANNEL_2_DOWNLINK_ENABLED": "true", + // "USERPREFS_CHANNEL_2_NAME": "REPLACEME", + // "USERPREFS_CHANNEL_2_PRECISION": "14", + // "USERPREFS_CHANNEL_2_PSK": "{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }", + // "USERPREFS_CHANNEL_2_UPLINK_ENABLED": "true", + // "USERPREFS_CONFIG_GPS_MODE": "meshtastic_Config_PositionConfig_GpsMode_ENABLED", + // "USERPREFS_CONFIG_LORA_IGNORE_MQTT": "true", + // "USERPREFS_CONFIG_LORA_REGION": "meshtastic_Config_LoRaConfig_RegionCode_US", + // "USERPREFS_CONFIG_OWNER_LONG_NAME": "My Long Name", + // "USERPREFS_CONFIG_OWNER_SHORT_NAME": "MLN", + // "USERPREFS_EVENT_MODE": "1", + // "USERPREFS_FIXED_BLUETOOTH": "121212", + // "USERPREFS_FIXED_GPS": "", + // "USERPREFS_FIXED_GPS_ALT": "0", + // "USERPREFS_FIXED_GPS_LAT": "48.85873920", + // "USERPREFS_FIXED_GPS_LON": "2.294508368", + // "USERPREFS_LORACONFIG_CHANNEL_NUM": "31", + // "USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST", + // "USERPREFS_SPLASH_TITLE": "DEFCONtastic", + "USERPREFS_TZ_STRING": "tzplaceholder " + // "USERPREFS_USE_ADMIN_KEY_0": "{ 0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c }", + // "USERPREFS_USE_ADMIN_KEY_1": "{}", + // "USERPREFS_USE_ADMIN_KEY_2": "{}" +} \ No newline at end of file From 57ea6a265e6e35d7ee36d50497dafdca120ea6f3 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:21:24 +0100 Subject: [PATCH 026/132] 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 --- src/main.cpp | 4 +- src/mesh/MeshService.cpp | 25 +----- src/mesh/MeshService.h | 4 +- src/mesh/PhoneAPI.cpp | 15 ++-- src/mesh/Router.cpp | 3 + src/modules/Telemetry/DeviceTelemetry.cpp | 8 ++ src/platform/portduino/SimRadio.cpp | 97 ++++++++++++++++++----- src/platform/portduino/SimRadio.h | 19 +++-- src/platform/portduino/architecture.h | 3 + 9 files changed, 121 insertions(+), 57 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 902668d23..33eaa131e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -82,7 +82,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr; #include "STM32WLE5JCInterface.h" #endif -#if !HAS_RADIO && defined(ARCH_PORTDUINO) +#if defined(ARCH_PORTDUINO) #include "platform/portduino/SimRadio.h" #endif @@ -896,7 +896,7 @@ void setup() } #endif -#if !HAS_RADIO && defined(ARCH_PORTDUINO) +#if defined(ARCH_PORTDUINO) if (!rIf) { rIf = new SimRadio; if (!rIf->init()) { diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 8f7717585..773ab7053 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -166,27 +166,10 @@ NodeNum MeshService::getNodenumFromRequestId(uint32_t request_id) */ void MeshService::handleToRadio(meshtastic_MeshPacket &p) { -#if defined(ARCH_PORTDUINO) && !HAS_RADIO - // Simulates device received a packet via the LoRa chip - if (p.decoded.portnum == meshtastic_PortNum_SIMULATOR_APP) { - // Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first - meshtastic_Compressed scratch; - meshtastic_Compressed *decoded = NULL; - if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { - memset(&scratch, 0, sizeof(scratch)); - p.decoded.payload.size = - pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_Compressed_msg, &scratch); - if (p.decoded.payload.size) { - decoded = &scratch; - // Extract the original payload and replace - memcpy(&p.decoded.payload, &decoded->data, sizeof(decoded->data)); - // Switch the port from PortNum_SIMULATOR_APP back to the original PortNum - p.decoded.portnum = decoded->portnum; - } else - LOG_ERROR("Error decoding proto for simulator message!"); - } - // Let SimRadio receive as if it did via its LoRa chip - SimRadio::instance->startReceive(&p); +#if defined(ARCH_PORTDUINO) + if (SimRadio::instance && p.decoded.portnum == meshtastic_PortNum_SIMULATOR_APP) { + // Simulates device received a packet via the LoRa chip + SimRadio::instance->unpackAndReceive(p); return; } #endif diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index 1ccca4e6d..268c4308f 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -10,7 +10,7 @@ #include "MeshTypes.h" #include "Observer.h" #include "PointerQueue.h" -#if defined(ARCH_PORTDUINO) && !HAS_RADIO +#if defined(ARCH_PORTDUINO) #include "../platform/portduino/SimRadio.h" #endif #if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) @@ -165,4 +165,4 @@ class MeshService friend class RoutingModule; }; -extern MeshService *service; +extern MeshService *service; \ No newline at end of file diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 20421e73e..f49718c5e 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -613,13 +613,14 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p) { printPacket("PACKET FROM PHONE", &p); -// For use with the simulator, we should not ignore duplicate packets -#if !(defined(ARCH_PORTDUINO) && !HAS_RADIO) - if (p.id > 0 && wasSeenRecently(p.id)) { - LOG_DEBUG("Ignore packet from phone, already seen recently"); - return false; - } +#if defined(ARCH_PORTDUINO) + // For use with the simulator, we should not ignore duplicate packets from the phone + if (SimRadio::instance == nullptr) #endif + if (p.id > 0 && wasSeenRecently(p.id)) { + LOG_DEBUG("Ignore packet from phone, already seen recently"); + return false; + } if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] && Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) { @@ -656,4 +657,4 @@ int PhoneAPI::onNotify(uint32_t newValue) } return timeout ? -1 : 0; // If we timed out, MeshService should stop iterating through observers as we just removed one -} +} \ No newline at end of file diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 1303c5caa..e9c62ff27 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -6,6 +6,7 @@ #include "NodeDB.h" #include "RTC.h" #include "configuration.h" +#include "detect/LoRaRadioType.h" #include "main.h" #include "mesh-pb-constants.h" #include "meshUtils.h" @@ -491,6 +492,8 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // is not in the local nodedb // First, only PKC encrypt packets we are originating if (isFromUs(p) && + // Don't use PKC with simulator + radioType != SIM_RADIO && // Don't use PKC with Ham mode !owner.is_licensed && // Don't use PKC if it's not explicitly requested and a non-primary channel is requested diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 4989b88e2..192754e09 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -130,6 +130,14 @@ meshtastic_Telemetry DeviceTelemetryModule::getLocalStatsTelemetry() telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad; telemetry.variant.local_stats.num_tx_relay = RadioLibInterface::instance->txRelay; } +#ifdef ARCH_PORTDUINO + if (SimRadio::instance) { + telemetry.variant.local_stats.num_packets_tx = SimRadio::instance->txGood; + telemetry.variant.local_stats.num_packets_rx = SimRadio::instance->rxGood + SimRadio::instance->rxBad; + telemetry.variant.local_stats.num_packets_rx_bad = SimRadio::instance->rxBad; + telemetry.variant.local_stats.num_tx_relay = SimRadio::instance->txRelay; + } +#endif if (router) { telemetry.variant.local_stats.num_rx_dupe = router->rxDupe; telemetry.variant.local_stats.num_tx_relay_canceled = router->txRelayCanceled; diff --git a/src/platform/portduino/SimRadio.cpp b/src/platform/portduino/SimRadio.cpp index 0a77b6088..7e63b995e 100644 --- a/src/platform/portduino/SimRadio.cpp +++ b/src/platform/portduino/SimRadio.cpp @@ -73,6 +73,10 @@ void SimRadio::handleTransmitInterrupt() // ignore the transmit interrupt if (sendingPacket) completeSending(); + + isReceiving = true; + if (receivingPacket) // This happens when we don't consider something a collision if we weren't sending long enough + handleReceiveInterrupt(); } void SimRadio::completeSending() @@ -84,6 +88,8 @@ void SimRadio::completeSending() if (p) { txGood++; + if (!isFromUs(p)) + txRelay++; printPacket("Completed sending", p); // We are done sending that packet, release it @@ -113,12 +119,12 @@ bool SimRadio::canSendImmediately() bool SimRadio::isActivelyReceiving() { - return false; // TODO check how this should be simulated + return receivingPacket != nullptr; } bool SimRadio::isChannelActive() { - return false; // TODO ask simulator + return receivingPacket != nullptr; } /** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */ @@ -142,10 +148,16 @@ void SimRadio::onNotify(uint32_t notification) startTransmitTimer(); break; case ISR_RX: + handleReceiveInterrupt(); // LOG_DEBUG("rx complete - starting timer"); startTransmitTimer(); break; case TRANSMIT_DELAY_COMPLETED: + if (receivingPacket) { // This happens when we had a timer pending and we started receiving + handleReceiveInterrupt(); + startTransmitTimer(); + break; + } LOG_DEBUG("delay done"); // If we are not currently in receive mode, then restart the random delay (this can happen if the main thread @@ -183,6 +195,7 @@ void SimRadio::onNotify(uint32_t notification) void SimRadio::startSend(meshtastic_MeshPacket *txp) { printPacket("Start low level send", txp); + isReceiving = false; size_t numbytes = beginSending(txp); meshtastic_MeshPacket *p = packetPool.allocCopy(*txp); perhapsDecode(p); @@ -201,15 +214,64 @@ void SimRadio::startSend(meshtastic_MeshPacket *txp) service->sendQueueStatusToPhone(router->getQueueStatus(), 0, p->id); service->sendToPhone(p); // Sending back to simulator + service->loop(); // Process the send immediately +} + +// Simulates device received a packet via the LoRa chip +void SimRadio::unpackAndReceive(meshtastic_MeshPacket &p) +{ + // Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first + meshtastic_Compressed scratch; + meshtastic_Compressed *decoded = NULL; + if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + memset(&scratch, 0, sizeof(scratch)); + p.decoded.payload.size = + pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_Compressed_msg, &scratch); + if (p.decoded.payload.size) { + decoded = &scratch; + // Extract the original payload and replace + memcpy(&p.decoded.payload, &decoded->data, sizeof(decoded->data)); + // Switch the port from PortNum_SIMULATOR_APP back to the original PortNum + p.decoded.portnum = decoded->portnum; + } else + LOG_ERROR("Error decoding proto for simulator message!"); + } + // Let SimRadio receive as if it did via its LoRa chip + startReceive(&p); } void SimRadio::startReceive(meshtastic_MeshPacket *p) { +#ifdef USERPREFS_SIMRADIO_EMULATE_COLLISIONS + if (isActivelyReceiving()) { + LOG_WARN("Collision detected, dropping current and previous packet!"); + rxBad++; + airTime->logAirtime(RX_ALL_LOG, getPacketTime(receivingPacket)); + packetPool.release(receivingPacket); + receivingPacket = nullptr; + return; + } else if (sendingPacket) { + uint32_t airtimeLeft = tillRun(millis()); + if (airtimeLeft <= 0) { + LOG_WARN("Transmitting packet was already done"); + handleTransmitInterrupt(); // Finish sending first + } else if ((interval - airtimeLeft) > preambleTimeMsec) { + // Only if transmitting for longer than preamble there is a collision + // (channel should actually be detected as active otherwise) + LOG_WARN("Collision detected during transmission!"); + return; + } + } isReceiving = true; - size_t length = getPacketLength(p); - uint32_t xmitMsec = getPacketTime(length); - delay(xmitMsec); // Model the time it is busy receiving - handleReceiveInterrupt(p); + receivingPacket = packetPool.allocCopy(*p); + uint32_t airtimeMsec = getPacketTime(p); + notifyLater(airtimeMsec, ISR_RX, false); // Model the time it is busy receiving +#else + isReceiving = true; + receivingPacket = packetPool.allocCopy(*p); + handleReceiveInterrupt(); // Simulate receiving the packet immediately + startTransmitTimer(); +#endif } meshtastic_QueueStatus SimRadio::getQueueStatus() @@ -223,28 +285,27 @@ meshtastic_QueueStatus SimRadio::getQueueStatus() return qs; } -void SimRadio::handleReceiveInterrupt(meshtastic_MeshPacket *p) +void SimRadio::handleReceiveInterrupt() { - LOG_DEBUG("HANDLE RECEIVE INTERRUPT"); - uint32_t xmitMsec; + if (receivingPacket == nullptr) { + return; + } if (!isReceiving) { LOG_DEBUG("*** WAS_ASSERT *** handleReceiveInterrupt called when not in receive mode"); return; } - isReceiving = false; + LOG_DEBUG("HANDLE RECEIVE INTERRUPT"); + rxGood++; - // read the number of actually received bytes - size_t length = getPacketLength(p); - xmitMsec = getPacketTime(length); - // LOG_DEBUG("Payload size %d vs length (includes header) %d", p->decoded.payload.size, length); - - meshtastic_MeshPacket *mp = packetPool.allocCopy(*p); // keep a copy in packetPool + meshtastic_MeshPacket *mp = packetPool.allocCopy(*receivingPacket); // keep a copy in packetPool + packetPool.release(receivingPacket); // release the original + receivingPacket = nullptr; printPacket("Lora RX", mp); - airTime->logAirtime(RX_LOG, xmitMsec); + airTime->logAirtime(RX_LOG, getPacketTime(mp)); deliverToReceiver(mp); } @@ -265,4 +326,4 @@ int16_t SimRadio::readData(uint8_t *data, size_t len) } return state; -} +} \ No newline at end of file diff --git a/src/platform/portduino/SimRadio.h b/src/platform/portduino/SimRadio.h index 1edb4963b..c082444e5 100644 --- a/src/platform/portduino/SimRadio.h +++ b/src/platform/portduino/SimRadio.h @@ -11,11 +11,6 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr { enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED }; - /** - * Debugging counts - */ - uint32_t rxBad = 0, rxGood = 0, txGood = 0; - MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE); public: @@ -47,9 +42,17 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr meshtastic_QueueStatus getQueueStatus() override; + // Convert Compressed_msg to normal msg and receive it + void unpackAndReceive(meshtastic_MeshPacket &p); + + /** + * Debugging counts + */ + uint32_t rxBad = 0, rxGood = 0, txGood = 0, txRelay = 0; + protected: /// are _trying_ to receive a packet currently (note - we might just be waiting for one) - bool isReceiving = false; + bool isReceiving = true; private: void setTransmitDelay(); @@ -61,7 +64,7 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr void startTransmitTimerSNR(float snr); void handleTransmitInterrupt(); - void handleReceiveInterrupt(meshtastic_MeshPacket *p); + void handleReceiveInterrupt(); void onNotify(uint32_t notification); @@ -73,6 +76,8 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr int16_t readData(uint8_t *str, size_t len); + meshtastic_MeshPacket *receivingPacket = nullptr; // The packet we are currently receiving + protected: /** Could we send right now (i.e. either not actively receiving or transmitting)? */ virtual bool canSendImmediately(); diff --git a/src/platform/portduino/architecture.h b/src/platform/portduino/architecture.h index 321949226..3dde87199 100644 --- a/src/platform/portduino/architecture.h +++ b/src/platform/portduino/architecture.h @@ -11,6 +11,9 @@ #ifndef HAS_WIFI #define HAS_WIFI 1 #endif +#ifndef HAS_RADIO +#define HAS_RADIO 1 +#endif #ifndef HAS_RTC #define HAS_RTC 1 #endif From 7ad137b56a3fb25606b944de659839ede67269e0 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 3 Dec 2024 06:28:46 -0600 Subject: [PATCH 027/132] add nodeId to nodeinfo update log lines and removed redundant nodeinfo update log line (#5493) --- 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 8935e1215..cd5011ea2 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1242,7 +1242,6 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde return false; } - LOG_DEBUG("old user %s/%s, channel=%d", info->user.long_name, info->user.short_name, info->channel); #if !(MESHTASTIC_EXCLUDE_PKI) if (p.public_key.size > 0) { printBytes("Incoming Pubkey: ", p.public_key.bytes, 32); @@ -1266,7 +1265,8 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde } if (nodeId != getNodeNum()) info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel) - LOG_DEBUG("Update changed=%d user %s/%s, channel=%d", changed, info->user.long_name, info->user.short_name, info->channel); + LOG_DEBUG("Update changed=%d user %s/%s, id=0x%08x, channel=%d", changed, info->user.long_name, info->user.short_name, nodeId, + info->channel); info->has_user = true; if (changed) { From 85b2bad2753db5db5d67b0b3e4f1dcb04b581cfe Mon Sep 17 00:00:00 2001 From: dylanli Date: Tue, 3 Dec 2024 20:29:33 +0800 Subject: [PATCH 028/132] Refact the macro definition of GPS initialization of GPSDEFAULTD_NOT_PRESENT and added seeeed Indicator to this sequence (#5494) Co-authored-by: Ben Meadors --- src/mesh/NodeDB.cpp | 2 +- variants/seeed-sensecap-indicator/variant.h | 1 + variants/t-deck/variant.h | 2 +- variants/tlora_t3s3_epaper/variant.h | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index cd5011ea2..4bb9e68dd 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -464,7 +464,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) #endif #if defined(USERPREFS_CONFIG_GPS_MODE) config.position.gps_mode = USERPREFS_CONFIG_GPS_MODE; -#elif !HAS_GPS || defined(T_DECK) || defined(TLORA_T3S3_EPAPER) +#elif !HAS_GPS || GPS_DEFAULT_NOT_PRESENT config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT; #elif !defined(GPS_RX_PIN) if (config.position.rx_gpio == 0) diff --git a/variants/seeed-sensecap-indicator/variant.h b/variants/seeed-sensecap-indicator/variant.h index 55895f353..6028a3ffa 100644 --- a/variants/seeed-sensecap-indicator/variant.h +++ b/variants/seeed-sensecap-indicator/variant.h @@ -40,6 +40,7 @@ // // Buzzer // #define PIN_BUZZER 19 +#define GPS_DEFAULT_NOT_PRESENT 1 #define GPS_RX_PIN 20 #define GPS_TX_PIN 19 #define HAS_GPS 1 diff --git a/variants/t-deck/variant.h b/variants/t-deck/variant.h index 6d398391e..91c12ab3d 100644 --- a/variants/t-deck/variant.h +++ b/variants/t-deck/variant.h @@ -29,7 +29,7 @@ #define BUTTON_PIN 0 // #define BUTTON_NEED_PULLUP - +#define GPS_DEFAULT_NOT_PRESENT 1 #define GPS_RX_PIN 44 #define GPS_TX_PIN 43 diff --git a/variants/tlora_t3s3_epaper/variant.h b/variants/tlora_t3s3_epaper/variant.h index 461ce0c31..732869b20 100644 --- a/variants/tlora_t3s3_epaper/variant.h +++ b/variants/tlora_t3s3_epaper/variant.h @@ -19,6 +19,7 @@ #define I2C_SCL SCL // external qwiic connector +#define GPS_DEFAULT_NOT_PRESENT 1 #define GPS_RX_PIN 44 #define GPS_TX_PIN 43 From f846503cbfa55edccdb8ec38b8f6e7b362b7bc53 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 3 Dec 2024 06:30:19 -0600 Subject: [PATCH 029/132] 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 --- src/mesh/RadioInterface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 53b66ff0a..5161ac41f 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -284,8 +284,8 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr) void printPacket(const char *prefix, const meshtastic_MeshPacket *p) { #ifdef DEBUG_PORT - std::string out = DEBUG_PORT.mt_sprintf("%s (id=0x%08x fr=0x%02x to=0x%02x, WantAck=%d, HopLim=%d Ch=0x%x", prefix, p->id, - p->from & 0xff, p->to & 0xff, p->want_ack, p->hop_limit, p->channel); + std::string out = DEBUG_PORT.mt_sprintf("%s (id=0x%08x fr=0x%08x to=0x%08x, WantAck=%d, HopLim=%d Ch=0x%x", prefix, p->id, + p->from, p->to, p->want_ack, p->hop_limit, p->channel); if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { auto &s = p->decoded; @@ -622,4 +622,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); -} +} \ No newline at end of file From 10e10450cfb8f8a36fa0934ea38bbdcab15b01ce Mon Sep 17 00:00:00 2001 From: noon92 <40807970+noon92@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:33:27 +0200 Subject: [PATCH 030/132] 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 --- ...ofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml | 16 ++++++++++++++++ .../femtofox/femtofox_EByte-E22-900MM22S.yaml | 16 ++++++++++++++++ ...femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml | 14 ++++++++++++++ ...Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml | 13 +++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 bin/config.d/femtofox/femtofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml create mode 100644 bin/config.d/femtofox/femtofox_EByte-E22-900MM22S.yaml create mode 100644 bin/config.d/femtofox/femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml create 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 new file mode 100644 index 000000000..6c88b1eb2 --- /dev/null +++ b/bin/config.d/femtofox/femtofox_EByte-E22-900M30S_Ebyte-E22-900M22S.yaml @@ -0,0 +1,16 @@ +--- +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 new file mode 100644 index 000000000..451d5d3f4 --- /dev/null +++ b/bin/config.d/femtofox/femtofox_EByte-E22-900MM22S.yaml @@ -0,0 +1,16 @@ +--- +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 new file mode 100644 index 000000000..d5f02b42c --- /dev/null +++ b/bin/config.d/femtofox/femtofox_Heltec-HT-RA62_Seeed-WIO-SX1262.yaml @@ -0,0 +1,14 @@ +--- +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_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml b/bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml new file mode 100644 index 000000000..23834adec --- /dev/null +++ b/bin/config.d/femtofox/femtofox_Waveshare-SX126X-XXXM_AI-Thinker-RA-01SH.yaml @@ -0,0 +1,13 @@ +--- +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 e4f53677fca9ad718706a471b4926e24b7dba2cc Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Wed, 4 Dec 2024 13:39:02 +0200 Subject: [PATCH 031/132] [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 --- src/main.cpp | 47 +++++++++++++++++++++++- src/platform/portduino/PortduinoGlue.cpp | 11 +++++- src/platform/portduino/PortduinoGlue.h | 5 ++- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 33eaa131e..53a662272 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -858,6 +858,51 @@ void setup() LOG_INFO("SX1280 init success"); } } + } else if (settingsMap[use_lr1110]) { + if (!rIf) { + LOG_DEBUG("Activate lr1110 radio on SPI port %s", settingsStrings[spidev].c_str()); + LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + rIf = new LR1110Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], + settingsMap[busy]); + if (!rIf->init()) { + LOG_WARN("No LR1110 radio"); + delete rIf; + rIf = NULL; + exit(EXIT_FAILURE); + } else { + LOG_INFO("LR1110 init success"); + } + } + } else if (settingsMap[use_lr1120]) { + if (!rIf) { + LOG_DEBUG("Activate lr1120 radio on SPI port %s", settingsStrings[spidev].c_str()); + LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + rIf = new LR1120Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], + settingsMap[busy]); + if (!rIf->init()) { + LOG_WARN("No LR1120 radio"); + delete rIf; + rIf = NULL; + exit(EXIT_FAILURE); + } else { + LOG_INFO("LR1120 init success"); + } + } + } else if (settingsMap[use_lr1121]) { + if (!rIf) { + LOG_DEBUG("Activate lr1121 radio on SPI port %s", settingsStrings[spidev].c_str()); + LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); + rIf = new LR1121Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], + settingsMap[busy]); + if (!rIf->init()) { + LOG_WARN("No LR1121 radio"); + delete rIf; + rIf = NULL; + exit(EXIT_FAILURE); + } else { + LOG_INFO("LR1121 init success"); + } + } } else if (settingsMap[use_sx1268]) { if (!rIf) { LOG_DEBUG("Activate sx1268 radio on SPI port %s", settingsStrings[spidev].c_str()); @@ -1218,4 +1263,4 @@ void loop() mainDelay.delay(delayMsec); } } -#endif \ No newline at end of file +#endif diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index d53a5be94..a4485e91f 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -272,6 +272,9 @@ bool loadConfig(const char *configPath) settingsMap[use_sx1262] = false; settingsMap[use_rf95] = false; settingsMap[use_sx1280] = false; + settingsMap[use_lr1110] = false; + settingsMap[use_lr1120] = false; + settingsMap[use_lr1121] = false; settingsMap[use_sx1268] = false; if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1262") { @@ -280,6 +283,12 @@ bool loadConfig(const char *configPath) settingsMap[use_rf95] = true; } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1280") { settingsMap[use_sx1280] = true; + } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "lr1110") { + settingsMap[use_lr1110] = true; + } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "lr1120") { + settingsMap[use_lr1120] = true; + } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "lr1121") { + settingsMap[use_lr1121] = true; } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1268") { settingsMap[use_sx1268] = true; } @@ -415,4 +424,4 @@ bool loadConfig(const char *configPath) static bool ends_with(std::string_view str, std::string_view suffix) { return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; -} \ No newline at end of file +} diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 95d82c1a2..1e0223c48 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -16,6 +16,9 @@ enum configNames { ch341Quirk, use_rf95, use_sx1280, + use_lr1110, + use_lr1120, + use_lr1121, use_sx1268, user, gpiochip, @@ -67,4 +70,4 @@ extern std::map settingsStrings; extern std::ofstream traceFile; int initGPIOPin(int pinNum, std::string gpioChipname); bool loadConfig(const char *configPath); -static bool ends_with(std::string_view str, std::string_view suffix); \ No newline at end of file +static bool ends_with(std::string_view str, std::string_view suffix); From 8eca6a2df8698196ae98b319000386df0be9a2d3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 08:15:50 -0600 Subject: [PATCH 032/132] [create-pull-request] automated change (#5500) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/protobufs b/protobufs index 02e6576ef..00c9c9932 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 02e6576efaa2f691be9504b8c1c6261703f7a277 +Subproject commit 00c9c9932ea50c14cdc44d497d2672a0031641ce diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index a173adb4d..da439c375 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -215,6 +215,11 @@ typedef enum _meshtastic_HardwareModel { /* WisMesh Tap RAK-4631 w/ TFT in injection modled case */ meshtastic_HardwareModel_WISMESH_TAP = 84, + /* Similar to PORTDUINO but used by Routastic devices, this is not any + particular device and does not run Meshtastic's code but supports + the same frame format. + Runs on linux, see https://github.com/Jorropo/routastic */ + meshtastic_HardwareModel_ROUTASTIC = 85, /* ------------------------------------------------------------------------------------------------------------------------------------------ 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. ------------------------------------------------------------------------------------------------------------------------------------------ */ From d3e3985e397e539417d0cd5ebbcf3081aacf8a91 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 4 Dec 2024 12:15:17 -0600 Subject: [PATCH 033/132] Fix minor typos in package workflows (#5505) --- .github/workflows/package_amd64.yml | 6 +++--- .github/workflows/package_raspbian.yml | 6 +++--- .github/workflows/package_raspbian_armv7l.yml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml index c11566deb..ce2c85d54 100644 --- a/.github/workflows/package_amd64.yml +++ b/.github/workflows/package_amd64.yml @@ -57,11 +57,11 @@ jobs: shopt -s dotglob nullglob if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi + if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r cp release/meshtasticd_linux_x86_64 .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml - cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ + cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ -r chmod +x .debpkg/usr/sbin/meshtasticd cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles @@ -82,4 +82,4 @@ jobs: name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb overwrite: true path: | - ./*.deb + ./*.deb \ No newline at end of file diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml index 56b683fdd..46039b6ea 100644 --- a/.github/workflows/package_raspbian.yml +++ b/.github/workflows/package_raspbian.yml @@ -57,11 +57,11 @@ jobs: shopt -s dotglob nullglob if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi + if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r cp release/meshtasticd_linux_aarch64 .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml - cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ + cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ -r chmod +x .debpkg/usr/sbin/meshtasticd cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles @@ -82,4 +82,4 @@ jobs: name: meshtasticd_${{ steps.version.outputs.version }}_arm64.deb overwrite: true path: | - ./*.deb + ./*.deb \ No newline at end of file diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml index 663903e10..2eda103ca 100644 --- a/.github/workflows/package_raspbian_armv7l.yml +++ b/.github/workflows/package_raspbian_armv7l.yml @@ -57,11 +57,11 @@ jobs: shopt -s dotglob nullglob if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi + if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r cp release/meshtasticd_linux_armv7l .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml - cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ + cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ -r chmod +x .debpkg/usr/sbin/meshtasticd cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles @@ -82,4 +82,4 @@ jobs: name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb overwrite: true path: | - ./*.deb + ./*.deb \ No newline at end of file From c3d60342f443d1980dca86a22d703b9370cdd702 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Thu, 5 Dec 2024 03:00:19 +0100 Subject: [PATCH 034/132] 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 --- src/mesh/Channels.cpp | 3 +-- src/mesh/FloodingRouter.cpp | 3 ++- src/mesh/Router.cpp | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index cfaff7640..4bdd9e674 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -178,12 +178,11 @@ CryptoKey Channels::getKey(ChannelIndex chIndex) { meshtastic_Channel &ch = getByIndex(chIndex); const meshtastic_ChannelSettings &channelSettings = ch.settings; - assert(ch.has_settings); CryptoKey k; memset(k.bytes, 0, sizeof(k.bytes)); // In case the user provided a short key, we want to pad the rest with zeros - if (ch.role == meshtastic_Channel_Role_DISABLED) { + if (!ch.has_settings || ch.role == meshtastic_Channel_Role_DISABLED) { k.length = -1; // invalid } else { memcpy(k.bytes, channelSettings.psk.bytes, channelSettings.psk.size); diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index e959297bf..e29c596df 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -36,7 +36,8 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) if (isRepeated) { LOG_DEBUG("Repeated reliable tx"); if (!perhapsRebroadcast(p) && isToUs(p) && p->want_ack) { - sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0); + // FIXME - channel index should be used, but the packet is still encrypted here + sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, 0, 0); } } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index e9c62ff27..e714ef215 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -271,6 +271,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) auto encodeResult = perhapsEncode(p); if (encodeResult != meshtastic_Routing_Error_NONE) { packetPool.release(p_decoded); + p->channel = 0; // Reset the channel to 0, so we don't use the failing hash again abortSendAndNak(encodeResult, p); return encodeResult; // FIXME - this isn't a valid ErrorCode } From de774188c99ba94c8e857b984ab42db802d48771 Mon Sep 17 00:00:00 2001 From: broglep <20624281+broglep@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:02:10 +0100 Subject: [PATCH 035/132] Always Announce MDNS meshtastic service (#5503) * refactor server api port into define * always announce MDNS meshtastic service --- src/mesh/api/ServerAPI.h | 2 ++ src/mesh/api/WiFiServerAPI.h | 2 +- src/mesh/api/ethServerAPI.h | 2 +- src/mesh/wifi/WiFiAPClient.cpp | 6 +++--- src/platform/portduino/PortduinoGlue.cpp | 3 ++- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/mesh/api/ServerAPI.h b/src/mesh/api/ServerAPI.h index 5b84fddd7..fe6a733a7 100644 --- a/src/mesh/api/ServerAPI.h +++ b/src/mesh/api/ServerAPI.h @@ -2,6 +2,8 @@ #include "StreamAPI.h" +#define SERVER_API_DEFAULT_PORT 4403 + /** * Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs * (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs). diff --git a/src/mesh/api/WiFiServerAPI.h b/src/mesh/api/WiFiServerAPI.h index 7a3d2967f..6e60bb678 100644 --- a/src/mesh/api/WiFiServerAPI.h +++ b/src/mesh/api/WiFiServerAPI.h @@ -22,5 +22,5 @@ class WiFiServerPort : public APIServerPort explicit WiFiServerPort(int port); }; -void initApiServer(int port = 4403); +void initApiServer(int port = SERVER_API_DEFAULT_PORT); void deInitApiServer(); \ No newline at end of file diff --git a/src/mesh/api/ethServerAPI.h b/src/mesh/api/ethServerAPI.h index 6f214c75a..9d25a2fc1 100644 --- a/src/mesh/api/ethServerAPI.h +++ b/src/mesh/api/ethServerAPI.h @@ -22,4 +22,4 @@ class ethServerPort : public APIServerPort explicit ethServerPort(int port); }; -void initApiServer(int port = 4403); +void initApiServer(int port = SERVER_API_DEFAULT_PORT); diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index 911a47093..f9e5d1cc9 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -66,13 +66,13 @@ static void onNetworkConnected() LOG_ERROR("Error setting up MDNS responder!"); } else { LOG_INFO("mDNS Host: Meshtastic.local"); + MDNS.addService("meshtastic", "tcp", SERVER_API_DEFAULT_PORT); #ifdef ARCH_ESP32 MDNS.addService("http", "tcp", 80); MDNS.addService("https", "tcp", 443); + // ESP32 prints obtained IP address in WiFiEvent #elif defined(ARCH_RP2040) - // ARCH_RP2040 does not support HTTPS, create a "meshtastic" service - MDNS.addService("meshtastic", "tcp", 4403); - // ESP32 handles this in WiFiEvent + // ARCH_RP2040 does not support HTTPS LOG_INFO("Obtained IP address: %s", WiFi.localIP().toString().c_str()); #endif } diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index a4485e91f..2aa6c9054 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -9,6 +9,7 @@ #include #include "PortduinoGlue.h" +#include "api/ServerAPI.h" #include "linux/gpio/LinuxGPIOPin.h" #include "yaml-cpp/yaml.h" #include @@ -34,7 +35,7 @@ void cpuDeepSleep(uint32_t msecs) void updateBatteryLevel(uint8_t level) NOT_IMPLEMENTED("updateBatteryLevel"); -int TCPPort = 4403; +int TCPPort = SERVER_API_DEFAULT_PORT; static error_t parse_opt(int key, char *arg, struct argp_state *state) { From bac9fec17f0c23e33a0e02bc9067aeaed68c9ef8 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Sat, 7 Dec 2024 11:39:30 +1100 Subject: [PATCH 036/132] fix nodeDB erase loop when free mem returns invalid value (0, -1). (#5519) Co-authored-by: mverch67 --- src/mesh/NodeDB.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 4bb9e68dd..90b3e0747 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1372,11 +1372,14 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) if (oldestBoringIndex != -1) { oldestIndex = oldestBoringIndex; } - // Shove the remaining nodes down the chain - for (int i = oldestIndex; i < numMeshNodes - 1; i++) { - meshNodes->at(i) = meshNodes->at(i + 1); + + if (oldestIndex != -1) { + // Shove the remaining nodes down the chain + for (int i = oldestIndex; i < numMeshNodes - 1; i++) { + meshNodes->at(i) = meshNodes->at(i + 1); + } + (numMeshNodes)--; } - (numMeshNodes)--; } // add the node at the end lite = &meshNodes->at((numMeshNodes)++); From fc16d9342116235fa86cf6ac163b17125bb4b50e Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 6 Dec 2024 20:01:47 -0600 Subject: [PATCH 037/132] Add heltec capsule back --- lib/device-ui | 1 + .../heltec_capsule_sensor_v3/platformio.ini | 10 ++++ variants/heltec_capsule_sensor_v3/variant.h | 53 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 160000 lib/device-ui create mode 100644 variants/heltec_capsule_sensor_v3/platformio.ini create mode 100644 variants/heltec_capsule_sensor_v3/variant.h diff --git a/lib/device-ui b/lib/device-ui new file mode 160000 index 000000000..6b760f537 --- /dev/null +++ b/lib/device-ui @@ -0,0 +1 @@ +Subproject commit 6b760f5373b91de1d8234922a446f7232d5247d0 diff --git a/variants/heltec_capsule_sensor_v3/platformio.ini b/variants/heltec_capsule_sensor_v3/platformio.ini new file mode 100644 index 000000000..b5ffb65c2 --- /dev/null +++ b/variants/heltec_capsule_sensor_v3/platformio.ini @@ -0,0 +1,10 @@ +[env:heltec_capsule_sensor_v3] +extends = esp32s3_base +board = heltec_wifi_lora_32_V3 +board_check = true + +build_flags = + ${esp32s3_base.build_flags} -I variants/heltec_capsule_sensor_v3 + -D HELTEC_CAPSULE_SENSOR_V3 + -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output \ No newline at end of file diff --git a/variants/heltec_capsule_sensor_v3/variant.h b/variants/heltec_capsule_sensor_v3/variant.h new file mode 100644 index 000000000..415de0559 --- /dev/null +++ b/variants/heltec_capsule_sensor_v3/variant.h @@ -0,0 +1,53 @@ +#define LED_PIN 33 +#define LED_PIN2 34 +#define EXT_PWR_DETECT 35 + +#define BUTTON_PIN 18 + +#define BATTERY_PIN 7 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +#define ADC_CHANNEL ADC1_GPIO7_CHANNEL +#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider +#define ADC_MULTIPLIER (4.9 * 1.045) +#define ADC_CTRL 36 // active HIGH, powers the voltage divider. Only on 1.1 +#define ADC_CTRL_ENABLED HIGH + +#undef GPS_RX_PIN +#undef GPS_TX_PIN +#define GPS_RX_PIN 5 +#define GPS_TX_PIN 4 +#define PIN_GPS_RESET 3 +#define GPS_RESET_MODE LOW +#define PIN_GPS_PPS 1 +#define PIN_GPS_EN 21 +#define GPS_EN_ACTIVE HIGH + +#define USE_SX1262 +#define LORA_DIO0 -1 // a No connect on the SX1262 module +#define LORA_RESET 12 +#define LORA_DIO1 14 // SX1262 IRQ +#define LORA_DIO2 13 // SX1262 BUSY +#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled + +#define LORA_SCK 9 +#define LORA_MISO 11 +#define LORA_MOSI 10 +#define LORA_CS 8 + +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET + +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +#define I2C_SDA 1 +#define I2C_SCL 2 +#define HAS_SCREEN 0 +#define SENSOR_POWER_CTRL_PIN 21 +#define SENSOR_POWER_ON 1 + +#define PERIPHERAL_WARMUP_MS 100 +#define SENSOR_GPS_CONFLICT + +#define ESP32S3_WAKE_TYPE ESP_EXT1_WAKEUP_ANY_HIGH \ No newline at end of file From 39b5fb041e2423ed5b4d4d67b51be2feeb2a7e4b Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 7 Dec 2024 05:29:13 -0600 Subject: [PATCH 038/132] Revert "Add heltec capsule back" This reverts commit fc16d9342116235fa86cf6ac163b17125bb4b50e. --- lib/device-ui | 1 - .../heltec_capsule_sensor_v3/platformio.ini | 10 ---- variants/heltec_capsule_sensor_v3/variant.h | 53 ------------------- 3 files changed, 64 deletions(-) delete mode 160000 lib/device-ui delete mode 100644 variants/heltec_capsule_sensor_v3/platformio.ini delete mode 100644 variants/heltec_capsule_sensor_v3/variant.h diff --git a/lib/device-ui b/lib/device-ui deleted file mode 160000 index 6b760f537..000000000 --- a/lib/device-ui +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6b760f5373b91de1d8234922a446f7232d5247d0 diff --git a/variants/heltec_capsule_sensor_v3/platformio.ini b/variants/heltec_capsule_sensor_v3/platformio.ini deleted file mode 100644 index b5ffb65c2..000000000 --- a/variants/heltec_capsule_sensor_v3/platformio.ini +++ /dev/null @@ -1,10 +0,0 @@ -[env:heltec_capsule_sensor_v3] -extends = esp32s3_base -board = heltec_wifi_lora_32_V3 -board_check = true - -build_flags = - ${esp32s3_base.build_flags} -I variants/heltec_capsule_sensor_v3 - -D HELTEC_CAPSULE_SENSOR_V3 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. - ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output \ No newline at end of file diff --git a/variants/heltec_capsule_sensor_v3/variant.h b/variants/heltec_capsule_sensor_v3/variant.h deleted file mode 100644 index 415de0559..000000000 --- a/variants/heltec_capsule_sensor_v3/variant.h +++ /dev/null @@ -1,53 +0,0 @@ -#define LED_PIN 33 -#define LED_PIN2 34 -#define EXT_PWR_DETECT 35 - -#define BUTTON_PIN 18 - -#define BATTERY_PIN 7 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage -#define ADC_CHANNEL ADC1_GPIO7_CHANNEL -#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider -#define ADC_MULTIPLIER (4.9 * 1.045) -#define ADC_CTRL 36 // active HIGH, powers the voltage divider. Only on 1.1 -#define ADC_CTRL_ENABLED HIGH - -#undef GPS_RX_PIN -#undef GPS_TX_PIN -#define GPS_RX_PIN 5 -#define GPS_TX_PIN 4 -#define PIN_GPS_RESET 3 -#define GPS_RESET_MODE LOW -#define PIN_GPS_PPS 1 -#define PIN_GPS_EN 21 -#define GPS_EN_ACTIVE HIGH - -#define USE_SX1262 -#define LORA_DIO0 -1 // a No connect on the SX1262 module -#define LORA_RESET 12 -#define LORA_DIO1 14 // SX1262 IRQ -#define LORA_DIO2 13 // SX1262 BUSY -#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled - -#define LORA_SCK 9 -#define LORA_MISO 11 -#define LORA_MOSI 10 -#define LORA_CS 8 - -#define SX126X_CS LORA_CS -#define SX126X_DIO1 LORA_DIO1 -#define SX126X_BUSY LORA_DIO2 -#define SX126X_RESET LORA_RESET - -#define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - -#define I2C_SDA 1 -#define I2C_SCL 2 -#define HAS_SCREEN 0 -#define SENSOR_POWER_CTRL_PIN 21 -#define SENSOR_POWER_ON 1 - -#define PERIPHERAL_WARMUP_MS 100 -#define SENSOR_GPS_CONFLICT - -#define ESP32S3_WAKE_TYPE ESP_EXT1_WAKEUP_ANY_HIGH \ No newline at end of file From 46eab20a9025d22d97b84eeb9ad96f7e5170e43c Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 7 Dec 2024 05:30:59 -0600 Subject: [PATCH 039/132] Lets try this again minus device ui --- .../heltec_capsule_sensor_v3/platformio.ini | 10 ++++ variants/heltec_capsule_sensor_v3/variant.h | 53 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 variants/heltec_capsule_sensor_v3/platformio.ini create mode 100644 variants/heltec_capsule_sensor_v3/variant.h diff --git a/variants/heltec_capsule_sensor_v3/platformio.ini b/variants/heltec_capsule_sensor_v3/platformio.ini new file mode 100644 index 000000000..b5ffb65c2 --- /dev/null +++ b/variants/heltec_capsule_sensor_v3/platformio.ini @@ -0,0 +1,10 @@ +[env:heltec_capsule_sensor_v3] +extends = esp32s3_base +board = heltec_wifi_lora_32_V3 +board_check = true + +build_flags = + ${esp32s3_base.build_flags} -I variants/heltec_capsule_sensor_v3 + -D HELTEC_CAPSULE_SENSOR_V3 + -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output \ No newline at end of file diff --git a/variants/heltec_capsule_sensor_v3/variant.h b/variants/heltec_capsule_sensor_v3/variant.h new file mode 100644 index 000000000..415de0559 --- /dev/null +++ b/variants/heltec_capsule_sensor_v3/variant.h @@ -0,0 +1,53 @@ +#define LED_PIN 33 +#define LED_PIN2 34 +#define EXT_PWR_DETECT 35 + +#define BUTTON_PIN 18 + +#define BATTERY_PIN 7 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +#define ADC_CHANNEL ADC1_GPIO7_CHANNEL +#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider +#define ADC_MULTIPLIER (4.9 * 1.045) +#define ADC_CTRL 36 // active HIGH, powers the voltage divider. Only on 1.1 +#define ADC_CTRL_ENABLED HIGH + +#undef GPS_RX_PIN +#undef GPS_TX_PIN +#define GPS_RX_PIN 5 +#define GPS_TX_PIN 4 +#define PIN_GPS_RESET 3 +#define GPS_RESET_MODE LOW +#define PIN_GPS_PPS 1 +#define PIN_GPS_EN 21 +#define GPS_EN_ACTIVE HIGH + +#define USE_SX1262 +#define LORA_DIO0 -1 // a No connect on the SX1262 module +#define LORA_RESET 12 +#define LORA_DIO1 14 // SX1262 IRQ +#define LORA_DIO2 13 // SX1262 BUSY +#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled + +#define LORA_SCK 9 +#define LORA_MISO 11 +#define LORA_MOSI 10 +#define LORA_CS 8 + +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET + +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +#define I2C_SDA 1 +#define I2C_SCL 2 +#define HAS_SCREEN 0 +#define SENSOR_POWER_CTRL_PIN 21 +#define SENSOR_POWER_ON 1 + +#define PERIPHERAL_WARMUP_MS 100 +#define SENSOR_GPS_CONFLICT + +#define ESP32S3_WAKE_TYPE ESP_EXT1_WAKEUP_ANY_HIGH \ No newline at end of file From b99e57a6fa3ade8cc75c080cfaadb0ad221290ba Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 7 Dec 2024 07:03:58 -0600 Subject: [PATCH 040/132] Add popular nrf52 pro micro to the builds (#5523) --- variants/diy/platformio.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index 00ff88da2..b60d46996 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -61,7 +61,6 @@ debug_tool = jlink [env:nrf52_promicro_diy_tcxo] extends = nrf52840_base board = promicro-nrf52840 -board_level = extra build_flags = ${nrf52840_base.build_flags} -I variants/diy/nrf52_promicro_diy_tcxo -D NRF52_PROMICRO_DIY @@ -157,4 +156,4 @@ build_src_filter = ${esp32_base.build_src_filter} lib_deps = ${esp32_base.lib_deps} lovyan03/LovyanGFX@^1.1.16 earlephilhower/ESP8266Audio@^1.9.7 - earlephilhower/ESP8266SAM@^1.0.1 + earlephilhower/ESP8266SAM@^1.0.1 \ No newline at end of file From 4a34bf648f40e2e70f92000598dff32f8e78cfdf Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 7 Dec 2024 10:29:49 -0600 Subject: [PATCH 041/132] 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 --- arch/portduino/portduino.ini | 2 +- bin/config-dist.yaml | 4 +- src/platform/portduino/PortduinoGlue.cpp | 126 +++++++++++++++++++++-- src/platform/portduino/PortduinoGlue.h | 5 +- 4 files changed, 124 insertions(+), 13 deletions(-) diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 04fd6db09..946a1489b 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -1,6 +1,6 @@ ; The Portduino based sim environment on top of any host OS, all hardware will be simulated [portduino_base] -platform = https://github.com/meshtastic/platform-native.git#bcd02436cfca91f7d28ad0f7dab977c6aaa781af +platform = https://github.com/meshtastic/platform-native.git#7fcee253a928535ff8b142704035b4b982f7e2d2 framework = arduino build_src_filter = diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index 77680cc63..cb25b36e7 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -160,4 +160,6 @@ Webserver: General: MaxNodes: 200 MaxMessageQueue: 100 - ConfigDirectory: /etc/meshtasticd/config.d/ \ No newline at end of file + ConfigDirectory: /etc/meshtasticd/config.d/ +# MACAddress: AA:BB:CC:DD:EE:FF +# MACAddressSource: eth0 \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 2aa6c9054..b6a017d9f 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -5,22 +5,27 @@ #include "sleep.h" #include "target_specific.h" -#include -#include - #include "PortduinoGlue.h" #include "api/ServerAPI.h" #include "linux/gpio/LinuxGPIOPin.h" +#include "meshUtils.h" #include "yaml-cpp/yaml.h" +#include +#include +#include +#include #include +#include #include #include +#include #include std::map settingsMap; std::map settingsStrings; std::ofstream traceFile; char *configPath = nullptr; +char *optionMac = nullptr; // FIXME - move setBluetoothEnable into a HALPlatform class void setBluetoothEnable(bool enable) @@ -49,6 +54,10 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) case 'c': configPath = arg; break; + case 'h': + optionMac = arg; + break; + case ARGP_KEY_ARG: return 0; default: @@ -61,6 +70,7 @@ void portduinoCustomInit() { static struct argp_option options[] = {{"port", 'p', "PORT", 0, "The TCP port to use."}, {"config", 'c', "CONFIG_PATH", 0, "Full path of the .yaml config file to use."}, + {"hwid", 'h', "HWID", 0, "The mac address to assign to this virtual machine"}, {0}}; static void *childArguments; static char doc[] = "Meshtastic native build."; @@ -70,6 +80,51 @@ void portduinoCustomInit() portduinoAddArguments(child, childArguments); } +void getMacAddr(uint8_t *dmac) +{ + // We should store this value, and short-circuit all this if it's already been set. + if (optionMac != nullptr && strlen(optionMac) > 0) { + if (strlen(optionMac) >= 12) { + MAC_from_string(optionMac, dmac); + std::cout << optionMac << std::endl; + } else { + uint32_t hwId = sscanf(optionMac, "%u", &hwId); + dmac[0] = 0x80; + dmac[1] = 0; + dmac[2] = hwId >> 24; + dmac[3] = hwId >> 16; + dmac[4] = hwId >> 8; + dmac[5] = hwId & 0xff; + } + } else if (settingsStrings[mac_address].length() > 11) { + MAC_from_string(settingsStrings[mac_address], dmac); + std::cout << settingsStrings[mac_address] << std::endl; + exit; + } else { + + 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 + return; + } + + if (ioctl(btsock, HCIGETDEVINFO, (void *)&di)) { + return; + } + + dmac[0] = di.bdaddr.b[5]; + dmac[1] = di.bdaddr.b[4]; + dmac[2] = di.bdaddr.b[3]; + dmac[3] = di.bdaddr.b[2]; + dmac[4] = di.bdaddr.b[1]; + dmac[5] = di.bdaddr.b[0]; + } +} + /** apps run under portduino can optionally define a portduinoSetup() to * use portduino specific init code (such as gpioBind) to setup portduino on their host machine, * before running 'arduino' code. @@ -114,10 +169,20 @@ void portduinoSetup() std::cout << "Unable to use " << configPath << " as config file" << std::endl; exit(EXIT_FAILURE); } - } else if (access("config.yaml", R_OK) == 0 && loadConfig("config.yaml")) { - std::cout << "Using local config.yaml as config file" << std::endl; - } else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0 && loadConfig("/etc/meshtasticd/config.yaml")) { - std::cout << "Using /etc/meshtasticd/config.yaml as config file" << std::endl; + } else if (access("config.yaml", R_OK) == 0) { + if (loadConfig("config.yaml")) { + std::cout << "Using local config.yaml as config file" << std::endl; + } else { + std::cout << "Unable to use local config.yaml as config file" << std::endl; + exit(EXIT_FAILURE); + } + } else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0) { + if (loadConfig("/etc/meshtasticd/config.yaml")) { + std::cout << "Using /etc/meshtasticd/config.yaml as config file" << std::endl; + } else { + std::cout << "Unable to use /etc/meshtasticd/config.yaml as config file" << std::endl; + exit(EXIT_FAILURE); + } } else { std::cout << "No 'config.yaml' found, running simulated." << std::endl; settingsMap[maxnodes] = 200; // Default to 200 nodes @@ -138,6 +203,14 @@ void portduinoSetup() } } + uint8_t dmac[6]; + 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; + std::cout << "Please set a MAC Address in config.yaml using either MACAddress or MACAddressSource." << std::endl; + exit(EXIT_FAILURE); + } + printBytes("MAC Address: ", dmac, 6); // Rather important to set this, if not running simulated. randomSeed(time(NULL)); @@ -410,10 +483,27 @@ bool loadConfig(const char *configPath) settingsStrings[webserverrootpath] = (yamlConfig["Webserver"]["RootPath"]).as(""); } - settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as(200); - settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as(100); - settingsStrings[config_directory] = (yamlConfig["General"]["ConfigDirectory"]).as(""); + if (yamlConfig["General"]) { + settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as(200); + settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as(100); + settingsStrings[config_directory] = (yamlConfig["General"]["ConfigDirectory"]).as(""); + if ((yamlConfig["General"]["MACAddress"]).as("") != "" && + (yamlConfig["General"]["MACAddressSource"]).as("") != "") { + std::cout << "Cannot set both MACAddress and MACAddressSource!" << std::endl; + exit(EXIT_FAILURE); + } + settingsStrings[mac_address] = (yamlConfig["General"]["MACAddress"]).as(""); + if ((yamlConfig["General"]["MACAddressSource"]).as("") != "") { + std::ifstream infile("/sys/class/net/" + (yamlConfig["General"]["MACAddressSource"]).as("") + + "/address"); + std::getline(infile, settingsStrings[mac_address]); + } + // https://stackoverflow.com/a/20326454 + settingsStrings[mac_address].erase( + std::remove(settingsStrings[mac_address].begin(), settingsStrings[mac_address].end(), ':'), + settingsStrings[mac_address].end()); + } } catch (YAML::Exception &e) { std::cout << "*** Exception " << e.what() << std::endl; return false; @@ -426,3 +516,19 @@ static bool ends_with(std::string_view str, std::string_view suffix) { return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; } + +bool MAC_from_string(std::string mac_str, uint8_t *dmac) +{ + mac_str.erase(std::remove(mac_str.begin(), mac_str.end(), ':'), mac_str.end()); + if (mac_str.length() == 12) { + dmac[0] = std::stoi(settingsStrings[mac_address].substr(0, 2), nullptr, 16); + dmac[1] = std::stoi(settingsStrings[mac_address].substr(2, 2), nullptr, 16); + dmac[2] = std::stoi(settingsStrings[mac_address].substr(4, 2), nullptr, 16); + dmac[3] = std::stoi(settingsStrings[mac_address].substr(6, 2), nullptr, 16); + dmac[4] = std::stoi(settingsStrings[mac_address].substr(8, 2), nullptr, 16); + dmac[5] = std::stoi(settingsStrings[mac_address].substr(10, 2), nullptr, 16); + return true; + } else { + return false; + } +} \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 1e0223c48..01541eeed 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -59,7 +59,8 @@ enum configNames { maxtophone, maxnodes, ascii_logs, - config_directory + config_directory, + mac_address }; enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9488, hx8357d }; enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 }; @@ -71,3 +72,5 @@ extern std::ofstream traceFile; 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 From 59ed5c90491ced8c88d348a25456224fc3116dde Mon Sep 17 00:00:00 2001 From: Matthias Granberry Date: Sat, 7 Dec 2024 14:32:49 -0600 Subject: [PATCH 042/132] Configure Seeed Xiao S3 RX enable pin (#5517) --- variants/seeed_xiao_s3/variant.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/variants/seeed_xiao_s3/variant.h b/variants/seeed_xiao_s3/variant.h index f854ba38a..8f9282a7a 100644 --- a/variants/seeed_xiao_s3/variant.h +++ b/variants/seeed_xiao_s3/variant.h @@ -80,5 +80,7 @@ L76K GPS Module Information : https://www.seeedstudio.com/L76K-GNSS-Module-for-S // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 #define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_RXEN 38 +#define SX126X_TXEN RADIOLIB_NC #define SX126X_DIO3_TCXO_VOLTAGE 1.8 #endif From f81d3b045dd1b7e3ca7870af3da915ff4399ea98 Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Sun, 8 Dec 2024 12:06:45 +0200 Subject: [PATCH 043/132] Create OpenWRT_One_mikroBUS_sx1262.yaml (#5529) --- bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml diff --git a/bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml b/bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml new file mode 100644 index 000000000..f31411a51 --- /dev/null +++ b/bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml @@ -0,0 +1,8 @@ +Lora: + Module: sx1262 + IRQ: 10 + Busy: 12 + Reset: 2 + spidev: spidev2.0 + DIO2_AS_RF_SWITCH: true + DIO3_TCXO_VOLTAGE: true From 3ae85e2c829427b3a6a64506afa9b6ecdf3ad081 Mon Sep 17 00:00:00 2001 From: Andrew Yong Date: Mon, 9 Dec 2024 19:38:51 +0800 Subject: [PATCH 044/132] 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 --- variants/tlora_v2_1_16/variant.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/variants/tlora_v2_1_16/variant.h b/variants/tlora_v2_1_16/variant.h index 8bb5ce3b1..48c069ab7 100644 --- a/variants/tlora_v2_1_16/variant.h +++ b/variants/tlora_v2_1_16/variant.h @@ -8,10 +8,7 @@ #define I2C_SDA 21 // I2C pins for this board #define I2C_SCL 22 -#define LED_PIN 25 // If defined we will blink this LED -#define BUTTON_PIN 12 // If defined, this will be used for user button presses, - -#define BUTTON_NEED_PULLUP +#define LED_PIN 25 // If defined we will blink this LED #define USE_RF95 #define LORA_DIO0 26 // a No connect on the SX1262 module From f3850ee73b686a4bb2f5675a73283e9e42ef391d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 06:50:30 -0600 Subject: [PATCH 045/132] [create-pull-request] automated change (#5530) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 9d03516e4..addcab501 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 16 +build = 17 From d0e3427ec742223df6338c70b00fcc745bdfc53d Mon Sep 17 00:00:00 2001 From: jake-b <1012393+jake-b@users.noreply.github.com> Date: Mon, 9 Dec 2024 20:46:13 -0500 Subject: [PATCH 046/132] Fix detection for some RadSens hardware versions (#5542) Co-authored-by: Jake-B --- src/detect/ScanI2CTwoWire.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index d29e5be8e..551b87d27 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -425,11 +425,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) case CGRADSENS_ADDR: // Register 0x00 of the RadSens sensor contains is product identifier 0x7D + // Undocumented, but some devices return a product identifier of 0x7A registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); - if (registerValue == 0x7D) { + if (registerValue == 0x7D || registerValue == 0x7A) { type = CGRADSENS; logFoundDevice("ClimateGuard RadSens", (uint8_t)addr.address); break; + } else { + LOG_DEBUG("Unexpected Device ID for RadSense: addr=0x%x id=0x%x", CGRADSENS_ADDR, registerValue); } break; From 0e3dae4fec297b459da157872961fef506b808d0 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 9 Dec 2024 21:51:55 -0600 Subject: [PATCH 047/132] Initialize dmac array to nulls (#5538) * Initialize dmac array to nulls * Use std::cout for print before console is init. --- src/platform/portduino/PortduinoGlue.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index b6a017d9f..50b5c5b7b 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -86,7 +86,6 @@ void getMacAddr(uint8_t *dmac) if (optionMac != nullptr && strlen(optionMac) > 0) { if (strlen(optionMac) >= 12) { MAC_from_string(optionMac, dmac); - std::cout << optionMac << std::endl; } else { uint32_t hwId = sscanf(optionMac, "%u", &hwId); dmac[0] = 0x80; @@ -98,7 +97,6 @@ void getMacAddr(uint8_t *dmac) } } else if (settingsStrings[mac_address].length() > 11) { MAC_from_string(settingsStrings[mac_address], dmac); - std::cout << settingsStrings[mac_address] << std::endl; exit; } else { @@ -203,14 +201,14 @@ void portduinoSetup() } } - uint8_t dmac[6]; + uint8_t dmac[6] = {0}; 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; std::cout << "Please set a MAC Address in config.yaml using either MACAddress or MACAddressSource." << std::endl; exit(EXIT_FAILURE); } - printBytes("MAC Address: ", dmac, 6); + std::cout << "MAC Address: " << std::hex << +dmac[0] << +dmac[1] << +dmac[2] << +dmac[3] << +dmac[4] << +dmac[5] << std::endl; // Rather important to set this, if not running simulated. randomSeed(time(NULL)); @@ -531,4 +529,4 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac) } else { return false; } -} \ No newline at end of file +} From 438f627e9b856e20f0b537c5133f8cea5c4e795f Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Tue, 10 Dec 2024 09:46:50 +0200 Subject: [PATCH 048/132] Update OpenWRT_One_mikroBUS_sx1262.yaml (#5544) --- bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml b/bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml index f31411a51..6dc1e870d 100644 --- a/bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml +++ b/bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml @@ -2,7 +2,7 @@ Lora: Module: sx1262 IRQ: 10 Busy: 12 - Reset: 2 +# Reset: 2 spidev: spidev2.0 DIO2_AS_RF_SWITCH: true DIO3_TCXO_VOLTAGE: true From cf46e675caf241994c0cf030848bcb2ac561d767 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 10 Dec 2024 10:14:52 -0500 Subject: [PATCH 049/132] Add portduino-buildroot variant (#5540) * Add portduino-buildroot variant * Update platform-native for platform-buildroot --- arch/portduino/portduino.ini | 4 ++-- variants/portduino-buildroot/platformio.ini | 10 ++++++++++ variants/portduino-buildroot/variant.h | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 variants/portduino-buildroot/platformio.ini create mode 100644 variants/portduino-buildroot/variant.h diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 946a1489b..bbafef4da 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -1,6 +1,6 @@ -; The Portduino based sim environment on top of any host OS, all hardware will be simulated +; The Portduino based 'native' environment. Currently supported on Linux targets with real LoRa hardware (or simulated). [portduino_base] -platform = https://github.com/meshtastic/platform-native.git#7fcee253a928535ff8b142704035b4b982f7e2d2 +platform = https://github.com/meshtastic/platform-native.git#73bd1a21183ca8b00c4ea58bb21315df31a50dff framework = arduino build_src_filter = diff --git a/variants/portduino-buildroot/platformio.ini b/variants/portduino-buildroot/platformio.ini new file mode 100644 index 000000000..8ef183ef3 --- /dev/null +++ b/variants/portduino-buildroot/platformio.ini @@ -0,0 +1,10 @@ +[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 +build_flags = ${portduino_base.build_flags} -O0 -I variants/portduino-buildroot + !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} diff --git a/variants/portduino-buildroot/variant.h b/variants/portduino-buildroot/variant.h new file mode 100644 index 000000000..b7b39d6e8 --- /dev/null +++ b/variants/portduino-buildroot/variant.h @@ -0,0 +1,5 @@ +#define HAS_SCREEN 1 +#define CANNED_MESSAGE_MODULE_ENABLE 1 +#define HAS_GPS 1 +#define MAX_RX_TOPHONE settingsMap[maxtophone] +#define MAX_NUM_NODES settingsMap[maxnodes] \ No newline at end of file From 761a99d2411093f8902d23e513c13ffc429b49e2 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 10 Dec 2024 11:09:54 -0500 Subject: [PATCH 050/132] portduino-buildroot: Define c standard (#5547) --- variants/portduino-buildroot/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/portduino-buildroot/platformio.ini b/variants/portduino-buildroot/platformio.ini index 8ef183ef3..3c8f21537 100644 --- a/variants/portduino-buildroot/platformio.ini +++ b/variants/portduino-buildroot/platformio.ini @@ -3,6 +3,7 @@ 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 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 From cabeb40c303fb8fc4da2b477c75c7d7a2b9201b9 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 10 Dec 2024 14:58:16 -0500 Subject: [PATCH 051/132] Portduino: Move meshtasticd/web out of /usr/share/doc/ (#5548) --- .github/workflows/package_amd64.yml | 17 +++++++++++------ .github/workflows/package_raspbian.yml | 17 +++++++++++------ .github/workflows/package_raspbian_armv7l.yml | 17 +++++++++++------ bin/config-dist.yaml | 2 +- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml index ce2c85d54..aec3bca30 100644 --- a/.github/workflows/package_amd64.yml +++ b/.github/workflows/package_amd64.yml @@ -47,18 +47,18 @@ jobs: - name: build .debpkg run: | mkdir -p .debpkg/DEBIAN - mkdir -p .debpkg/usr/share/doc/meshtasticd/web + mkdir -p .debpkg/usr/share/meshtasticd/web mkdir -p .debpkg/usr/sbin mkdir -p .debpkg/etc/meshtasticd mkdir -p .debpkg/etc/meshtasticd/config.d mkdir -p .debpkg/etc/meshtasticd/available.d mkdir -p .debpkg/usr/lib/systemd/system/ - tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web + tar -xf build.tar -C .debpkg/usr/share/meshtasticd/web shopt -s dotglob nullglob - if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi - if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r + if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then mv .debpkg/usr/share/meshtasticd/web/build/* .debpkg/usr/share/meshtasticd/web/; fi + if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/meshtasticd/web/build; fi + if [ -d .debpkg/usr/share/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/meshtasticd/web/.DS_Store; fi + gunzip .debpkg/usr/share/meshtasticd/web/ -r cp release/meshtasticd_linux_x86_64 .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ -r @@ -66,6 +66,11 @@ jobs: cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles chmod +x .debpkg/DEBIAN/conffiles + # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd + echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst + chmod +x .debpkg/DEBIAN/preinst + echo "/usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/meshtasticd.links + chmod +x .debpkg/DEBIAN/meshtasticd.links - uses: jiro4989/build-deb-action@v3 with: diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml index 46039b6ea..b0a4a40d7 100644 --- a/.github/workflows/package_raspbian.yml +++ b/.github/workflows/package_raspbian.yml @@ -47,18 +47,18 @@ jobs: - name: build .debpkg run: | mkdir -p .debpkg/DEBIAN - mkdir -p .debpkg/usr/share/doc/meshtasticd/web + mkdir -p .debpkg/usr/share/meshtasticd/web mkdir -p .debpkg/usr/sbin mkdir -p .debpkg/etc/meshtasticd mkdir -p .debpkg/etc/meshtasticd/config.d mkdir -p .debpkg/etc/meshtasticd/available.d mkdir -p .debpkg/usr/lib/systemd/system/ - tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web + tar -xf build.tar -C .debpkg/usr/share/meshtasticd/web shopt -s dotglob nullglob - if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi - if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r + if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then mv .debpkg/usr/share/meshtasticd/web/build/* .debpkg/usr/share/meshtasticd/web/; fi + if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/meshtasticd/web/build; fi + if [ -d .debpkg/usr/share/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/meshtasticd/web/.DS_Store; fi + gunzip .debpkg/usr/share/meshtasticd/web/ -r cp release/meshtasticd_linux_aarch64 .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ -r @@ -66,6 +66,11 @@ jobs: cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles chmod +x .debpkg/DEBIAN/conffiles + # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd + echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst + chmod +x .debpkg/DEBIAN/preinst + echo "/usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/meshtasticd.links + chmod +x .debpkg/DEBIAN/meshtasticd.links - uses: jiro4989/build-deb-action@v3 with: diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml index 2eda103ca..d66b46dc2 100644 --- a/.github/workflows/package_raspbian_armv7l.yml +++ b/.github/workflows/package_raspbian_armv7l.yml @@ -47,18 +47,18 @@ jobs: - name: build .debpkg run: | mkdir -p .debpkg/DEBIAN - mkdir -p .debpkg/usr/share/doc/meshtasticd/web + mkdir -p .debpkg/usr/share/meshtasticd/web mkdir -p .debpkg/usr/sbin mkdir -p .debpkg/etc/meshtasticd mkdir -p .debpkg/etc/meshtasticd/config.d mkdir -p .debpkg/etc/meshtasticd/available.d mkdir -p .debpkg/usr/lib/systemd/system/ - tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web + tar -xf build.tar -C .debpkg/usr/share/meshtasticd/web shopt -s dotglob nullglob - if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi - if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r + if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then mv .debpkg/usr/share/meshtasticd/web/build/* .debpkg/usr/share/meshtasticd/web/; fi + if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/meshtasticd/web/build; fi + if [ -d .debpkg/usr/share/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/meshtasticd/web/.DS_Store; fi + gunzip .debpkg/usr/share/meshtasticd/web/ -r cp release/meshtasticd_linux_armv7l .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ -r @@ -66,6 +66,11 @@ jobs: cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles chmod +x .debpkg/DEBIAN/conffiles + # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd + echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst + chmod +x .debpkg/DEBIAN/preinst + echo "/usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/meshtasticd.links + chmod +x .debpkg/DEBIAN/meshtasticd.links - uses: jiro4989/build-deb-action@v3 with: diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index cb25b36e7..ec262536f 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -155,7 +155,7 @@ Logging: Webserver: # Port: 443 # Port for Webserver & Webservices -# RootPath: /usr/share/doc/meshtasticd/web # Root Dir of WebServer +# RootPath: /usr/share/meshtasticd/web # Root Dir of WebServer General: MaxNodes: 200 From 7dd362950111fbf6ca134b678ec1975d3b6eb1fd Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 10 Dec 2024 16:02:38 -0500 Subject: [PATCH 052/132] Portduino: fix transitional symlinks (#5550) --- .github/workflows/package_amd64.yml | 4 ++-- .github/workflows/package_raspbian.yml | 4 ++-- .github/workflows/package_raspbian_armv7l.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml index aec3bca30..782ef479b 100644 --- a/.github/workflows/package_amd64.yml +++ b/.github/workflows/package_amd64.yml @@ -69,8 +69,8 @@ jobs: # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst chmod +x .debpkg/DEBIAN/preinst - echo "/usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/meshtasticd.links - chmod +x .debpkg/DEBIAN/meshtasticd.links + echo "ln -sf /usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/postinst + chmod +x .debpkg/DEBIAN/postinst - uses: jiro4989/build-deb-action@v3 with: diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml index b0a4a40d7..aef8905f8 100644 --- a/.github/workflows/package_raspbian.yml +++ b/.github/workflows/package_raspbian.yml @@ -69,8 +69,8 @@ jobs: # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst chmod +x .debpkg/DEBIAN/preinst - echo "/usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/meshtasticd.links - chmod +x .debpkg/DEBIAN/meshtasticd.links + echo "ln -sf /usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/postinst + chmod +x .debpkg/DEBIAN/postinst - uses: jiro4989/build-deb-action@v3 with: diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml index d66b46dc2..ddb84d4a7 100644 --- a/.github/workflows/package_raspbian_armv7l.yml +++ b/.github/workflows/package_raspbian_armv7l.yml @@ -69,8 +69,8 @@ jobs: # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst chmod +x .debpkg/DEBIAN/preinst - echo "/usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/meshtasticd.links - chmod +x .debpkg/DEBIAN/meshtasticd.links + echo "ln -sf /usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/postinst + chmod +x .debpkg/DEBIAN/postinst - uses: jiro4989/build-deb-action@v3 with: From 1790407078c4648f5fd15a7e6938e3b2b405cb32 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 13 Dec 2024 02:58:19 +1100 Subject: [PATCH 053/132] 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> --- .devcontainer/99-platformio-udev.rules | 183 +++++++++++++++++++++++++ .devcontainer/Dockerfile | 17 ++- .devcontainer/devcontainer.json | 13 +- 3 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 .devcontainer/99-platformio-udev.rules diff --git a/.devcontainer/99-platformio-udev.rules b/.devcontainer/99-platformio-udev.rules new file mode 100644 index 000000000..83c7b8731 --- /dev/null +++ b/.devcontainer/99-platformio-udev.rules @@ -0,0 +1,183 @@ +# Copyright (c) 2014-present PlatformIO +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +##################################################################################### +# +# INSTALLATION +# +# Please visit > https://docs.platformio.org/en/latest/core/installation/udev-rules.html +# +##################################################################################### + +# +# Boards +# + +# CP210X USB UART +ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea[67][013]", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" +ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="80a9", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# FT231XS USB UART +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Prolific Technology, Inc. PL2303 Serial Port +ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# QinHeng Electronics HL-340 USB-Serial adapter +ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" +# QinHeng Electronics CH343 USB-Serial adapter +ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d3", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" +# QinHeng Electronics CH9102 USB-Serial adapter +ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d4", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Arduino boards +ATTRS{idVendor}=="2341", ATTRS{idProduct}=="[08][023]*", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" +ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="[08][02]*", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Arduino SAM-BA +ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="6124", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{MTP_NO_PROBE}="1" + +# Digistump boards +ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0753", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Maple with DFU +ATTRS{idVendor}=="1eaf", ATTRS{idProduct}=="000[34]", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# USBtiny +ATTRS{idProduct}=="0c9f", ATTRS{idVendor}=="1781", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# USBasp V2.0 +ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05dc", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Teensy boards +ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" +ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", MODE:="0666" +KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", MODE:="0666" + +# TI Stellaris Launchpad +ATTRS{idVendor}=="1cbe", ATTRS{idProduct}=="00fd", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# TI MSP430 Launchpad +ATTRS{idVendor}=="0451", ATTRS{idProduct}=="f432", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# GD32V DFU Bootloader +ATTRS{idVendor}=="28e9", ATTRS{idProduct}=="0189", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# FireBeetle-ESP32 +ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7522", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Wio Terminal +ATTRS{idVendor}=="2886", ATTRS{idProduct}=="[08]02d", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Raspberry Pi Pico +ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="[01]*", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# AIR32F103 +ATTRS{idVendor}=="0d28", ATTRS{idProduct}=="0204", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# STM32 virtual COM port +ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# +# Debuggers +# + +# Black Magic Probe +SUBSYSTEM=="tty", ATTRS{interface}=="Black Magic GDB Server", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" +SUBSYSTEM=="tty", ATTRS{interface}=="Black Magic UART Port", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# opendous and estick +ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="204f", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Original FT232/FT245/FT2232/FT232H/FT4232 +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="60[01][104]", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# DISTORTEC JTAG-lock-pick Tiny 2 +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8220", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# TUMPA, TUMPA Lite +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8a9[89]", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# XDS100v2 +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="a6d0", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Xverve Signalyzer Tool (DT-USB-ST), Signalyzer LITE (DT-USB-SLITE) +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="bca[01]", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# TI/Luminary Stellaris Evaluation Board FTDI (several) +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="bcd[9a]", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# egnite Turtelizer 2 +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="bdc8", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Section5 ICEbear +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="c14[01]", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Amontec JTAGkey and JTAGkey-tiny +ATTRS{idVendor}=="0403", ATTRS{idProduct}=="cff8", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# TI ICDI +ATTRS{idVendor}=="0451", ATTRS{idProduct}=="c32a", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# STLink probes +ATTRS{idVendor}=="0483", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Hilscher NXHX Boards +ATTRS{idVendor}=="0640", ATTRS{idProduct}=="0028", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Hitex probes +ATTRS{idVendor}=="0640", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Altera USB Blaster +ATTRS{idVendor}=="09fb", ATTRS{idProduct}=="6001", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Amontec JTAGkey-HiSpeed +ATTRS{idVendor}=="0fbb", ATTRS{idProduct}=="1000", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# SEGGER J-Link +ATTRS{idVendor}=="1366", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Raisonance RLink +ATTRS{idVendor}=="138e", ATTRS{idProduct}=="9000", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Debug Board for Neo1973 +ATTRS{idVendor}=="1457", ATTRS{idProduct}=="5118", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Olimex probes +ATTRS{idVendor}=="15ba", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# USBprog with OpenOCD firmware +ATTRS{idVendor}=="1781", ATTRS{idProduct}=="0c63", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# TI/Luminary Stellaris In-Circuit Debug Interface (ICDI) Board +ATTRS{idVendor}=="1cbe", ATTRS{idProduct}=="00fd", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Marvell Sheevaplug +ATTRS{idVendor}=="9e88", ATTRS{idProduct}=="9e8f", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Keil Software, Inc. ULink +ATTRS{idVendor}=="c251", ATTRS{idProduct}=="2710", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# CMSIS-DAP compatible adapters +ATTRS{product}=="*CMSIS-DAP*", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Atmel AVR Dragon +ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2107", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Espressif USB JTAG/serial debug unit +ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1001", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" + +# Zephyr framework USB CDC-ACM +ATTRS{idVendor}=="2fe3", ATTRS{idProduct}=="0100", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1" \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index c21564491..62f0b7ead 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,9 @@ FROM mcr.microsoft.com/devcontainers/cpp:1-debian-12 -# [Optional] Uncomment this section to install additional packages. +USER root + +# trunk-ignore(terrascan/AC_DOCKER_0002): Known terrascan issue +# trunk-ignore(hadolint/DL3008): Use latest version of packages RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ && apt-get -y install --no-install-recommends \ ca-certificates \ @@ -20,6 +23,16 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ python3-wheel \ wget \ zip \ + usbutils \ + hwdata \ + gpg \ + gnupg2 \ && apt-get clean && rm -rf /var/lib/apt/lists/* -RUN pipx install platformio==6.1.15 \ No newline at end of file +RUN pipx install platformio==6.1.15 + +COPY 99-platformio-udev.rules /etc/udev/rules.d/99-platformio-udev.rules + +USER vscode + +HEALTHCHECK NONE \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d83d052b0..bf1c50982 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,13 +13,24 @@ }, "customizations": { "vscode": { - "extensions": ["ms-vscode.cpptools", "platformio.platformio-ide"] + "extensions": [ + "ms-vscode.cpptools", + "platformio.platformio-ide", + "Trunk.io" + ], + "unwantedRecommendations": ["ms-azuretools.vscode-docker"], + "settings": { + "extensions.ignoreRecommendations": true + } } }, // Use 'forwardPorts' to make a list of ports inside the container available locally. "forwardPorts": [4403], + // Use "--device=" to make a local device available inside the container. + // "runArgs": ["--device=/dev/ttyACM0"], + // Run commands to prepare the container for use "postCreateCommand": ".devcontainer/setup.sh" } From 03770b799f7821275945ba70896012ec60bfea85 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 13 Dec 2024 03:42:41 +1100 Subject: [PATCH 054/132] 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 --- arch/nrf52/nrf52.ini | 3 ++- src/main.cpp | 5 ++--- src/mesh/NodeDB.cpp | 4 ++-- src/modules/Modules.cpp | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index d75f86306..778be5523 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -1,10 +1,11 @@ [nrf52_base] ; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files -platform = platformio/nordicnrf52@^10.5.0 +platform = platformio/nordicnrf52@^10.6.0 extends = arduino_base platform_packages = ; our custom Git version until they merge our PR framework-arduinoadafruitnrf52 @ https://github.com/geeksville/Adafruit_nRF52_Arduino.git + toolchain-gccarmnoneeabi@~1.90301.0 build_type = debug build_flags = diff --git a/src/main.cpp b/src/main.cpp index 53a662272..2357a00de 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -409,13 +409,13 @@ void setup() digitalWrite(AQ_SET_PIN, HIGH); #endif -#ifdef T_DECK +#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(800); + delay(200); #endif // Currently only the tbeam has a PMU @@ -577,7 +577,6 @@ void setup() scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808); - scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHT31, meshtastic_TelemetrySensorType_SHT31); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHTC3, meshtastic_TelemetrySensorType_SHTC3); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::LPS22HB, meshtastic_TelemetrySensorType_LPS22); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 90b3e0747..6ad1a953d 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -541,7 +541,7 @@ void NodeDB::initConfigIntervals() config.display.screen_on_secs = default_screen_on_secs; -#if defined(T_WATCH_S3) || defined(T_DECK) || defined(RAK14014) +#if defined(T_WATCH_S3) || defined(T_DECK) || defined(MESH_TAB) || defined(RAK14014) config.power.is_power_saving = true; config.display.screen_on_secs = 30; config.power.wait_bluetooth_secs = 30; @@ -1422,4 +1422,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting"); exit(2); #endif -} \ No newline at end of file +} diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index ad3f0ace4..9baed824c 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -74,7 +74,7 @@ #include "modules/StoreForwardModule.h" #endif #endif -#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) +#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO) #if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION #include "modules/ExternalNotificationModule.h" #endif @@ -223,7 +223,7 @@ void setupModules() storeForwardModule = new StoreForwardModule(); #endif #endif -#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) +#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO) #if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION externalNotificationModule = new ExternalNotificationModule(); #endif @@ -245,4 +245,4 @@ void setupModules() // NOTE! This module must be added LAST because it likes to check for replies from other modules and avoid sending extra // acks routingModule = new RoutingModule(); -} \ No newline at end of file +} From 92225eb6c3ac60a12995432a4bfac0ee3641f093 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 13 Dec 2024 11:48:27 -0600 Subject: [PATCH 055/132] 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 056/132] 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 057/132] 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 058/132] 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 059/132] 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 060/132] 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 061/132] 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 062/132] 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 063/132] [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 064/132] 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 065/132] 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 066/132] 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 067/132] 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 068/132] 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 069/132] 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 070/132] 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 071/132] [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 072/132] 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 073/132] 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 074/132] 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 075/132] 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 076/132] 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 077/132] [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 078/132] 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 079/132] 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 080/132] 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 081/132] 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 082/132] 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 083/132] 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 084/132] 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 085/132] 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 086/132] 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 087/132] 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 088/132] 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 089/132] 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 090/132] 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 091/132] 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 092/132] 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 093/132] 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 094/132] 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 095/132] 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 096/132] 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 097/132] 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;

From 6a2a4ffa2a89b531963072d1123d9b99c5645aad Mon Sep 17 00:00:00 2001
From: Jonathan Bennett 
Date: Sun, 22 Dec 2024 21:25:48 -0600
Subject: [PATCH 098/132] Add libi2c-dev to native builds

---
 .github/workflows/build_native.yml          | 2 +-
 .github/workflows/build_raspbian.yml        | 2 +-
 .github/workflows/build_raspbian_armv7l.yml | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml
index a57da5dfb..74bc074aa 100644
--- a/.github/workflows/build_native.yml
+++ b/.github/workflows/build_native.yml
@@ -14,7 +14,7 @@ jobs:
         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
+          sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev
 
       - name: Checkout code
         uses: actions/checkout@v4
diff --git a/.github/workflows/build_raspbian.yml b/.github/workflows/build_raspbian.yml
index 1826504f0..ac63dfea4 100644
--- a/.github/workflows/build_raspbian.yml
+++ b/.github/workflows/build_raspbian.yml
@@ -14,7 +14,7 @@ jobs:
         shell: bash
         run: |
           sudo apt-get update -y --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
+          sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev
 
       - name: Checkout code
         uses: actions/checkout@v4
diff --git a/.github/workflows/build_raspbian_armv7l.yml b/.github/workflows/build_raspbian_armv7l.yml
index fd53585a5..565d9a0dc 100644
--- a/.github/workflows/build_raspbian_armv7l.yml
+++ b/.github/workflows/build_raspbian_armv7l.yml
@@ -14,7 +14,7 @@ jobs:
         shell: bash
         run: |
           sudo apt-get update -y --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
+          sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev
 
       - name: Checkout code
         uses: actions/checkout@v4

From 32719f69c9d733d329eb29e55e0722a26fbbc4e0 Mon Sep 17 00:00:00 2001
From: Jonathan Bennett 
Date: Sun, 22 Dec 2024 22:53:54 -0600
Subject: [PATCH 099/132] Add NXP_SE050 detection (#5651)

* Add NXP_SE050 detection

* Put the flag in the right place

* Include libi2c0 dependency in .deb packages
---
 .github/workflows/package_amd64.yml           |  4 +--
 .github/workflows/package_raspbian.yml        |  4 +--
 .github/workflows/package_raspbian_armv7l.yml |  4 +--
 arch/portduino/portduino.ini                  |  3 +-
 src/detect/ScanI2C.h                          |  3 +-
 src/detect/ScanI2CTwoWire.cpp                 | 32 ++++++++++++++++---
 6 files changed, 38 insertions(+), 12 deletions(-)

diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml
index 782ef479b..c6e82e1be 100644
--- a/.github/workflows/package_amd64.yml
+++ b/.github/workflows/package_amd64.yml
@@ -79,7 +79,7 @@ jobs:
           maintainer: Jonathan Bennett
           version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.*
           arch: amd64
-          depends: libyaml-cpp0.7, openssl, libulfius2.7
+          depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0
           desc: Native Linux Meshtastic binary.
 
       - uses: actions/upload-artifact@v4
@@ -87,4 +87,4 @@ jobs:
           name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
           overwrite: true
           path: |
-            ./*.deb
\ No newline at end of file
+            ./*.deb
diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml
index aef8905f8..a4cd49573 100644
--- a/.github/workflows/package_raspbian.yml
+++ b/.github/workflows/package_raspbian.yml
@@ -79,7 +79,7 @@ jobs:
           maintainer: Jonathan Bennett
           version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.*
           arch: arm64
-          depends: libyaml-cpp0.7, openssl, libulfius2.7
+          depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0
           desc: Native Linux Meshtastic binary.
 
       - uses: actions/upload-artifact@v4
@@ -87,4 +87,4 @@ jobs:
           name: meshtasticd_${{ steps.version.outputs.version }}_arm64.deb
           overwrite: true
           path: |
-            ./*.deb
\ No newline at end of file
+            ./*.deb
diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml
index ddb84d4a7..c4cc5c673 100644
--- a/.github/workflows/package_raspbian_armv7l.yml
+++ b/.github/workflows/package_raspbian_armv7l.yml
@@ -79,7 +79,7 @@ jobs:
           maintainer: Jonathan Bennett
           version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.*
           arch: armhf
-          depends: libyaml-cpp0.7, openssl, libulfius2.7
+          depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0
           desc: Native Linux Meshtastic binary.
 
       - uses: actions/upload-artifact@v4
@@ -87,4 +87,4 @@ jobs:
           name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb
           overwrite: true
           path: |
-            ./*.deb
\ No newline at end of file
+            ./*.deb
diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini
index e4e32693c..777ce1e02 100644
--- a/arch/portduino/portduino.ini
+++ b/arch/portduino/portduino.ini
@@ -1,6 +1,6 @@
 ; The Portduino based 'native' environment. Currently supported on Linux targets with real LoRa hardware (or simulated).
 [portduino_base]
-platform = https://github.com/meshtastic/platform-native.git#73bd1a21183ca8b00c4ea58bb21315df31a50dff
+platform = https://github.com/meshtastic/platform-native.git#562d189828f09fbf4c4093b3c0104bae9d8e9ff9
 framework = arduino
 
 build_src_filter = 
@@ -39,4 +39,5 @@ build_flags =
   -lbluetooth
   -lgpiod
   -lyaml-cpp
+  -li2c
   -std=c++17
\ No newline at end of file
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index 2473a6573..2561a8e17 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -64,7 +64,8 @@ class ScanI2C
         TPS65233,
         MPR121KB,
         CGRADSENS,
-        INA226
+        INA226,
+        NXP_SE050,
     } DeviceType;
 
     // typedef uint8_t DeviceAddress;
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 79c0deccf..6e695c22f 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -154,9 +154,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
         }
         i2cBus->beginTransmission(addr.address);
 #ifdef ARCH_PORTDUINO
-        if (i2cBus->read() != -1)
-            err = 0;
-        else
+        err = 2;
+        if ((addr.address >= 0x30 && addr.address <= 0x37) || (addr.address >= 0x50 && addr.address <= 0x5F)) {
+            if (i2cBus->read() != -1)
+                err = 0;
+        } else {
+            err = i2cBus->writeQuick((uint8_t)0);
+        }
+        if (err != 0)
             err = 2;
 #else
         err = i2cBus->endTransmission();
@@ -396,7 +401,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
                 SCAN_SIMPLE_CASE(OPT3001_ADDR, OPT3001, "OPT3001", (uint8_t)addr.address);
                 SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632", (uint8_t)addr.address);
                 SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802", (uint8_t)addr.address);
-                SCAN_SIMPLE_CASE(FT6336U_ADDR, FT6336U, "FT6336U", (uint8_t)addr.address);
                 SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address);
 #ifdef HAS_TPS65233
                 SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233", (uint8_t)addr.address);
@@ -444,6 +448,26 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
                 }
                 break;
 
+            case 0x48: {
+                i2cBus->beginTransmission(addr.address);
+                uint8_t getInfo[] = {0x5A, 0xC0, 0x00, 0xFF, 0xFC};
+                uint8_t expectedInfo[] = {0xa5, 0xE0, 0x00, 0x3F, 0x19};
+                uint8_t info[5];
+                size_t len = 0;
+                i2cBus->write(getInfo, 5);
+                i2cBus->endTransmission();
+                len = i2cBus->readBytes(info, 5);
+                if (len == 5 && memcmp(expectedInfo, info, len) == 0) {
+                    LOG_INFO("NXP SE050 crypto chip found\n");
+                    type = NXP_SE050;
+
+                } else {
+                    LOG_INFO("FT6336U touchscreen found\n");
+                    type = FT6336U;
+                }
+                break;
+            }
+
             default:
                 LOG_INFO("Device found at address 0x%x was not able to be enumerated", (uint8_t)addr.address);
             }

From 143e1d1a0d0014332b1aa44222329f74a2df2455 Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Mon, 23 Dec 2024 07:48:07 -0800
Subject: [PATCH 100/132] Check if MQTT remote IP is private (#5647)

Co-authored-by: Ben Meadors 
---
 src/mqtt/MQTT.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index d61e87855..ba47e26e3 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -457,6 +457,7 @@ void MQTT::reconnect()
             enabled = true; // Start running background process again
             runASAP = true;
             reconnectCount = 0;
+            isMqttServerAddressPrivate = isPrivateIpAddress(mqttClient.remoteIP());
 
             publishNodeInfo();
             sendSubscriptions();

From b4b2fd6122e965adf69839a69b75df391436248e Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Tue, 24 Dec 2024 10:47:27 +1100
Subject: [PATCH 101/132] LIS3DH (WisMesh Pocket) - Honor Wake On Tap Or Motion
 (#5625)

As reported by @Mason10198, the WisMesh Pocket was always waking
on accelerometer motion.

This change gates the LIS3DH sensor's call to wakeScreen based on
config.display.wake_on_tap_or_motion .

fixes https://github.com/meshtastic/firmware/issues/5579
---
 src/motion/LIS3DHSensor.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/motion/LIS3DHSensor.cpp b/src/motion/LIS3DHSensor.cpp
index f3f5a62d1..995f74abe 100755
--- a/src/motion/LIS3DHSensor.cpp
+++ b/src/motion/LIS3DHSensor.cpp
@@ -22,7 +22,7 @@ int32_t LIS3DHSensor::runOnce()
 {
     if (sensor.getClick() > 0) {
         uint8_t click = sensor.getClick();
-        if (!config.device.double_tap_as_button_press) {
+        if (!config.device.double_tap_as_button_press && config.display.wake_on_tap_or_motion) {
             wakeScreen();
         }
 
@@ -34,4 +34,4 @@ int32_t LIS3DHSensor::runOnce()
     return MOTION_SENSOR_CHECK_INTERVAL_MS;
 }
 
-#endif
\ No newline at end of file
+#endif

From 57af51cc1822f03dbffdb0c5deae07f62171ee95 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= 
Date: Tue, 24 Dec 2024 09:04:57 +0100
Subject: [PATCH 102/132] fix typo in nugget radio def

---
 variants/nibble_rp2040/variant.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/variants/nibble_rp2040/variant.h b/variants/nibble_rp2040/variant.h
index ac105b439..0f71b98e9 100644
--- a/variants/nibble_rp2040/variant.h
+++ b/variants/nibble_rp2040/variant.h
@@ -6,7 +6,7 @@
 
 #define HAS_CPU_SHUTDOWN 1
 
-#define USE_RFM95
+#define USE_RF95
 #define LORA_SCK 10
 #define LORA_MISO 12
 #define LORA_MOSI 11

From 175ff218f1e8830b501dab26f8f2aa9e40d55051 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 24 Dec 2024 05:54:20 -0600
Subject: [PATCH 103/132] [create-pull-request] automated change (#5658)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
---
 version.properties | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/version.properties b/version.properties
index addcab501..ba7d7fe6f 100644
--- a/version.properties
+++ b/version.properties
@@ -1,4 +1,4 @@
 [VERSION]  
 major = 2
 minor = 5
-build = 17
+build = 18

From fbdd6e72237b53d660b4405ce672f2d2f4f26a11 Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Tue, 24 Dec 2024 21:31:35 -0800
Subject: [PATCH 104/132] Synchronize test workflow packages with native
 (#5664)

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

diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 241598fd0..25987fab0 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -9,11 +9,11 @@ jobs:
   test-simulator:
     runs-on: ubuntu-latest
     steps:
-      - name: Install libbluetooth
+      - 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
+          sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev
 
       - name: Checkout code
         uses: actions/checkout@v4

From a7d9e8107ac9f16575e349503ac5ce1c3a866ec4 Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Wed, 25 Dec 2024 06:33:53 -0800
Subject: [PATCH 105/132] More accurately determine if MQTT uses the default
 server (#5663)

* More accurately determine if MQTT uses the default server

* Channels::anyMqttEnabled() uses same logic

* Remove previous static bool
---
 src/mesh/Channels.cpp |  2 +-
 src/mqtt/MQTT.cpp     | 10 +++++-----
 src/mqtt/MQTT.h       |  3 +++
 3 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp
index 4bdd9e674..4bc91ce4e 100644
--- a/src/mesh/Channels.cpp
+++ b/src/mesh/Channels.cpp
@@ -318,7 +318,7 @@ bool Channels::anyMqttEnabled()
 {
 #if USERPREFS_EVENT_MODE
     // Don't publish messages on the public MQTT broker if we are in event mode
-    if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0) {
+    if (mqtt && mqtt.isUsingDefaultServer()) {
         return false;
     }
 #endif
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index ba47e26e3..a8a3e49ea 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -324,9 +324,10 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
                 moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs);
         }
 
+        String host = parseHostAndPort(moduleConfig.mqtt.address).first;
+        isConfiguredForDefaultServer = host.length() == 0 || host == default_mqtt_address;
         IPAddress ip;
-        isMqttServerAddressPrivate =
-            ip.fromString(parseHostAndPort(moduleConfig.mqtt.address).first.c_str()) && isPrivateIpAddress(ip);
+        isMqttServerAddressPrivate = ip.fromString(host.c_str()) && isPrivateIpAddress(ip);
 
 #if HAS_NETWORKING
         if (!moduleConfig.mqtt.proxy_to_client_enabled)
@@ -633,9 +634,8 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me
             return;
         }
 
-        if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 &&
-            (mp_decoded.decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP ||
-             mp_decoded.decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP)) {
+        if (isConfiguredForDefaultServer && (mp_decoded.decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP ||
+                                             mp_decoded.decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP)) {
             LOG_DEBUG("MQTT onSend - Ignoring range test or detection sensor message on public mqtt");
             return;
         }
diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h
index 81892f6f3..cb1fffcc9 100644
--- a/src/mqtt/MQTT.h
+++ b/src/mqtt/MQTT.h
@@ -79,6 +79,8 @@ class MQTT : private concurrency::OSThread
 
     void start() { setIntervalFromNow(0); };
 
+    bool isUsingDefaultServer() { return isConfiguredForDefaultServer; }
+
   protected:
     struct QueueEntry {
         std::string topic;
@@ -87,6 +89,7 @@ class MQTT : private concurrency::OSThread
     PointerQueue mqttQueue;
 
     int reconnectCount = 0;
+    bool isConfiguredForDefaultServer = true;
 
     virtual int32_t runOnce() override;
 

From 13960874ae5701003d074cbc624bc6dab5b267c3 Mon Sep 17 00:00:00 2001
From: Jonathan Bennett 
Date: Wed, 25 Dec 2024 16:47:00 -0600
Subject: [PATCH 106/132] Bump libch341 userspace to dev branch

---
 arch/portduino/portduino.ini | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini
index 777ce1e02..aa1150e9a 100644
--- a/arch/portduino/portduino.ini
+++ b/arch/portduino/portduino.ini
@@ -26,7 +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
+  https://github.com/pine64/libch341-spi-userspace#a9b17e3452f7fb747000d9b4ad4409155b39f6ef
 
 build_flags =
   ${arduino_base.build_flags}

From 835344074ca6653d37d3b1464847301dae289337 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 25 Dec 2024 19:31:46 -0600
Subject: [PATCH 107/132] [create-pull-request] automated change (#5660)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
---
 protobufs                                 |  2 +-
 src/mesh/generated/meshtastic/config.pb.h | 12 +++++++++---
 src/mesh/generated/meshtastic/mesh.pb.h   | 14 ++++++++++----
 3 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/protobufs b/protobufs
index 2cffaf53e..c55f120a9 160000
--- a/protobufs
+++ b/protobufs
@@ -1 +1 @@
-Subproject commit 2cffaf53e3faf1b6e41a8b8f05312f2f893be413
+Subproject commit c55f120a9c1ce90c85e4826907a0b9bcb2d5f5a2
diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h
index 8e2264e93..5e105ab17 100644
--- a/src/mesh/generated/meshtastic/config.pb.h
+++ b/src/mesh/generated/meshtastic/config.pb.h
@@ -58,7 +58,13 @@ typedef enum _meshtastic_Config_DeviceConfig_Role {
  Technical Details: Turns off many of the routine broadcasts to favor ATAK CoT packet stream
     and automatic TAK PLI (position location information) broadcasts.
     Uses position module configuration to determine TAK PLI broadcast interval. */
-    meshtastic_Config_DeviceConfig_Role_TAK_TRACKER = 10
+    meshtastic_Config_DeviceConfig_Role_TAK_TRACKER = 10,
+    /* Description: Will always rebroadcast packets, but will do so after all other modes.
+ Technical Details: Used for router nodes that are intended to provide additional coverage
+    in areas not already covered by other routers, or to bridge around problematic terrain,
+    but should not be given priority over other routers in order to avoid unnecessaraily
+    consuming hops. */
+    meshtastic_Config_DeviceConfig_Role_ROUTER_LATE = 11
 } meshtastic_Config_DeviceConfig_Role;
 
 /* Defines the device's behavior for how messages are rebroadcast */
@@ -588,8 +594,8 @@ extern "C" {
 
 /* Helper constants for enums */
 #define _meshtastic_Config_DeviceConfig_Role_MIN meshtastic_Config_DeviceConfig_Role_CLIENT
-#define _meshtastic_Config_DeviceConfig_Role_MAX meshtastic_Config_DeviceConfig_Role_TAK_TRACKER
-#define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_TAK_TRACKER+1))
+#define _meshtastic_Config_DeviceConfig_Role_MAX meshtastic_Config_DeviceConfig_Role_ROUTER_LATE
+#define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_ROUTER_LATE+1))
 
 #define _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN meshtastic_Config_DeviceConfig_RebroadcastMode_ALL
 #define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY
diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h
index 14ed76f70..5cd23c8e3 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.h
+++ b/src/mesh/generated/meshtastic/mesh.pb.h
@@ -770,6 +770,10 @@ typedef struct _meshtastic_MeshPacket {
     /* Last byte of the node number of the node that will relay/relayed this packet.
  Set by the firmware internally, clients are not supposed to set this. */
     uint8_t relay_node;
+    /* *Never* sent over the radio links.
+ Timestamp after which this packet may be sent.
+ Set by the firmware internally, clients are not supposed to set this. */
+    uint32_t tx_after;
 } meshtastic_MeshPacket;
 
 /* The bluetooth to device link:
@@ -1178,7 +1182,7 @@ extern "C" {
 #define meshtastic_Data_init_default             {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
 #define meshtastic_Waypoint_init_default         {0, false, 0, false, 0, 0, 0, "", "", 0}
 #define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
-#define meshtastic_MeshPacket_init_default       {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0}
+#define meshtastic_MeshPacket_init_default       {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0}
 #define meshtastic_NodeInfo_init_default         {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0, 0}
 #define meshtastic_MyNodeInfo_init_default       {0, 0, 0, {0, {0}}, ""}
 #define meshtastic_LogRecord_init_default        {"", 0, "", _meshtastic_LogRecord_Level_MIN}
@@ -1203,7 +1207,7 @@ extern "C" {
 #define meshtastic_Data_init_zero                {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
 #define meshtastic_Waypoint_init_zero            {0, false, 0, false, 0, 0, 0, "", "", 0}
 #define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
-#define meshtastic_MeshPacket_init_zero          {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0}
+#define meshtastic_MeshPacket_init_zero          {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0}
 #define meshtastic_NodeInfo_init_zero            {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0, 0}
 #define meshtastic_MyNodeInfo_init_zero          {0, 0, 0, {0, {0}}, ""}
 #define meshtastic_LogRecord_init_zero           {"", 0, "", _meshtastic_LogRecord_Level_MIN}
@@ -1301,6 +1305,7 @@ extern "C" {
 #define meshtastic_MeshPacket_pki_encrypted_tag  17
 #define meshtastic_MeshPacket_next_hop_tag       18
 #define meshtastic_MeshPacket_relay_node_tag     19
+#define meshtastic_MeshPacket_tx_after_tag       20
 #define meshtastic_NodeInfo_num_tag              1
 #define meshtastic_NodeInfo_user_tag             2
 #define meshtastic_NodeInfo_position_tag         3
@@ -1497,7 +1502,8 @@ X(a, STATIC,   SINGULAR, UINT32,   hop_start,        15) \
 X(a, STATIC,   SINGULAR, BYTES,    public_key,       16) \
 X(a, STATIC,   SINGULAR, BOOL,     pki_encrypted,    17) \
 X(a, STATIC,   SINGULAR, UINT32,   next_hop,         18) \
-X(a, STATIC,   SINGULAR, UINT32,   relay_node,       19)
+X(a, STATIC,   SINGULAR, UINT32,   relay_node,       19) \
+X(a, STATIC,   SINGULAR, UINT32,   tx_after,         20)
 #define meshtastic_MeshPacket_CALLBACK NULL
 #define meshtastic_MeshPacket_DEFAULT NULL
 #define meshtastic_MeshPacket_payload_variant_decoded_MSGTYPE meshtastic_Data
@@ -1747,7 +1753,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
 #define meshtastic_FromRadio_size                510
 #define meshtastic_Heartbeat_size                0
 #define meshtastic_LogRecord_size                426
-#define meshtastic_MeshPacket_size               371
+#define meshtastic_MeshPacket_size               378
 #define meshtastic_MqttClientProxyMessage_size   501
 #define meshtastic_MyNodeInfo_size               77
 #define meshtastic_NeighborInfo_size             258

From 1281da627e9d67d83d99c066ea6e8bfd6450d299 Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Wed, 25 Dec 2024 17:47:03 -0800
Subject: [PATCH 108/132] Generate a coverage report for End to end tests
 (#5667)

* Generate coverage report after running tests

* Wait for integration program to stop/start
---
 .github/workflows/tests.yml       | 34 ++++++++++++++++++++++++++-----
 src/modules/AdminModule.cpp       |  2 +-
 test/test_crypto/test_main.cpp    |  6 ++----
 variants/portduino/platformio.ini |  4 ++++
 4 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 25987fab0..ae9f82543 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -8,12 +8,15 @@ on:
 jobs:
   test-simulator:
     runs-on: ubuntu-latest
+    env:
+      LCOV_CAPTURE_FLAGS: --quiet --capture --include "${PWD}/src/*" --exclude '*/src/mesh/generated/*' --directory .pio/build/coverage/src --base-directory "${PWD}"
     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 libi2c-dev
+          sudo apt-get install -y lcov
 
       - name: Checkout code
         uses: actions/checkout@v4
@@ -24,7 +27,7 @@ jobs:
         shell: bash
         run: |
           python -m pip install --upgrade pip
-          pip install -U platformio adafruit-nrfutil
+          pip install -U platformio adafruit-nrfutil dotmap
           pip install -U meshtastic --pre
 
       - name: Upgrade platformio
@@ -36,17 +39,25 @@ jobs:
         run: bin/build-native.sh
 
       # We now run integration test before other build steps (to quickly see runtime failures)
-      - name: Build for native
-        run: platformio run -e native
+      - name: Build for native/coverage
+        run: |
+          platformio run -e coverage
+          lcov ${{ env.LCOV_CAPTURE_FLAGS }} --initial --output-file coverage_base.info
 
       - name: Integration test
         run: |
-          .pio/build/native/program & sleep 10 # 5 seconds was not enough
+          .pio/build/coverage/program &
+          PID=$!
+          timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
           echo "Simulator started, launching python test..."
           python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
+          wait
+          lcov ${{ env.LCOV_CAPTURE_FLAGS }} --test-name integration --output-file coverage_integration.info
 
       - name: PlatformIO Tests
-        run: platformio test -e native --junit-output-path testreport.xml
+        run: |
+          platformio test -e coverage --junit-output-path testreport.xml
+          lcov ${{ env.LCOV_CAPTURE_FLAGS }} --test-name tests --output-file coverage_tests.info
 
       - name: Test Report
         uses: dorny/test-reporter@v1.9.1
@@ -56,6 +67,19 @@ jobs:
           path: testreport.xml
           reporter: java-junit
 
+      - name: Generate Code Coverage Report
+        run: |
+          lcov --quiet --add-tracefile coverage_base.info --add-tracefile coverage_integration.info --add-tracefile coverage_tests.info --output-file coverage_src.info
+          mkdir code-coverage-report
+          genhtml --quiet --legend --prefix "${PWD}" coverage_src.info --output-directory code-coverage-report
+          mv coverage_*.info code-coverage-report
+
+      - name: Save Code Coverage Report
+        uses: actions/upload-artifact@v4
+        with:
+          name: code-coverage-report
+          path: code-coverage-report
+
   hardware-tests:
     runs-on: test-runner
     steps:
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index 2d33b723d..69b2c0a38 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -358,7 +358,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
 #ifdef ARCH_PORTDUINO
     case meshtastic_AdminMessage_exit_simulator_tag:
         LOG_INFO("Exiting simulator");
-        _exit(0);
+        exit(0);
         break;
 #endif
 
diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp
index 652d5dbcb..91e8331d5 100644
--- a/test/test_crypto/test_main.cpp
+++ b/test/test_crypto/test_main.cpp
@@ -176,9 +176,7 @@ void setup()
     RUN_TEST(test_DH25519);
     RUN_TEST(test_AES_CTR);
     RUN_TEST(test_PKC_Decrypt);
+    exit(UNITY_END()); // stop unit testing
 }
 
-void loop()
-{
-    UNITY_END(); // stop unit testing
-}
\ No newline at end of file
+void loop() {}
\ No newline at end of file
diff --git a/variants/portduino/platformio.ini b/variants/portduino/platformio.ini
index aa11142f7..cad87ea8c 100644
--- a/variants/portduino/platformio.ini
+++ b/variants/portduino/platformio.ini
@@ -9,3 +9,7 @@ build_flags = ${portduino_base.build_flags} -O0 -I variants/portduino
 board = cross_platform
 lib_deps = ${portduino_base.lib_deps}
 build_src_filter = ${portduino_base.build_src_filter}
+
+[env:coverage]
+extends = env:native
+build_flags = -lgcov --coverage -fprofile-abs-path ${env:native.build_flags}

From cc357df4897693aaa5105157e752e9ebc07533b5 Mon Sep 17 00:00:00 2001
From: Eric Severance 
Date: Wed, 25 Dec 2024 18:42:15 -0800
Subject: [PATCH 109/132] Include log messages in unit tests (#5666)

* Include log messages in unit tests

* Provide an initial time value

---------

Co-authored-by: Ben Meadors 
---
 src/DebugConfiguration.h       |  2 +-
 test/TestUtil.cpp              | 18 ++++++++++++++++++
 test/TestUtil.h                |  4 ++++
 test/test_crypto/test_main.cpp |  2 ++
 4 files changed, 25 insertions(+), 1 deletion(-)
 create mode 100644 test/TestUtil.cpp
 create mode 100644 test/TestUtil.h

diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h
index 55453ea1e..7987e7fa1 100644
--- a/src/DebugConfiguration.h
+++ b/src/DebugConfiguration.h
@@ -45,7 +45,7 @@
 #define LOG_CRIT(...) SEGGER_RTT_printf(0, __VA_ARGS__)
 #define LOG_TRACE(...) SEGGER_RTT_printf(0, __VA_ARGS__)
 #else
-#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) && !defined(PIO_UNIT_TESTING)
+#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
 #define LOG_DEBUG(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_DEBUG, __VA_ARGS__)
 #define LOG_INFO(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_INFO, __VA_ARGS__)
 #define LOG_WARN(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_WARN, __VA_ARGS__)
diff --git a/test/TestUtil.cpp b/test/TestUtil.cpp
new file mode 100644
index 000000000..b470b8ce8
--- /dev/null
+++ b/test/TestUtil.cpp
@@ -0,0 +1,18 @@
+#include "SerialConsole.h"
+#include "concurrency/OSThread.h"
+#include "gps/RTC.h"
+
+#include "TestUtil.h"
+
+void initializeTestEnvironment()
+{
+    concurrency::hasBeenSetup = true;
+    consoleInit();
+#if ARCH_PORTDUINO
+    struct timeval tv;
+    tv.tv_sec = time(NULL);
+    tv.tv_usec = 0;
+    perhapsSetRTC(RTCQualityNTP, &tv);
+#endif
+    concurrency::OSThread::setup();
+}
\ No newline at end of file
diff --git a/test/TestUtil.h b/test/TestUtil.h
new file mode 100644
index 000000000..ce021e459
--- /dev/null
+++ b/test/TestUtil.h
@@ -0,0 +1,4 @@
+#pragma once
+
+// Initialize testing environment.
+void initializeTestEnvironment();
\ No newline at end of file
diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp
index 91e8331d5..fd7706e6e 100644
--- a/test/test_crypto/test_main.cpp
+++ b/test/test_crypto/test_main.cpp
@@ -1,5 +1,6 @@
 #include "CryptoEngine.h"
 
+#include "TestUtil.h"
 #include 
 
 void HexToBytes(uint8_t *result, const std::string hex, size_t len = 0)
@@ -170,6 +171,7 @@ void setup()
     delay(10);
     delay(2000);
 
+    initializeTestEnvironment();
     UNITY_BEGIN(); // IMPORTANT LINE!
     RUN_TEST(test_SHA256);
     RUN_TEST(test_ECB_AES256);

From d87b7e49e4750d1f952ca08f72b69ce97382e424 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?V=C3=ADt=20Hol=C3=A1sek?=
 
Date: Thu, 26 Dec 2024 10:08:23 +0100
Subject: [PATCH 110/132] Add czech oled localization (#5661)

* Added support for Czech and Slovak glyphs localization

* Remove accidental commit

* Fix typo

* Fixed formatting
---
 src/graphics/Screen.cpp                   |    2 +-
 src/graphics/Screen.h                     |   80 +
 src/graphics/ScreenFonts.h                |   16 +
 src/graphics/fonts/OLEDDisplayFontsCS.cpp | 1863 +++++++++++++++++++++
 src/graphics/fonts/OLEDDisplayFontsCS.h   |   16 +
 5 files changed, 1976 insertions(+), 1 deletion(-)
 create mode 100644 src/graphics/fonts/OLEDDisplayFontsCS.cpp
 create mode 100644 src/graphics/fonts/OLEDDisplayFontsCS.h

diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 2ab413bc5..27ea6f414 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -126,7 +126,7 @@ static bool heartbeat = false;
 /// Check if the display can render a string (detect special chars; emoji)
 static bool haveGlyphs(const char *str)
 {
-#if defined(OLED_PL) || defined(OLED_UA) || defined(OLED_RU)
+#if defined(OLED_PL) || defined(OLED_UA) || defined(OLED_RU) || defined(OLED_CS)
     // Don't want to make any assumptions about custom language support
     return true;
 #endif
diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h
index 00884c5af..3cb39e8ec 100644
--- a/src/graphics/Screen.h
+++ b/src/graphics/Screen.h
@@ -427,6 +427,86 @@ class Screen : public concurrency::OSThread
         if (ch == 0xC2 || ch == 0xC3 || ch == 0x82 || ch == 0xD0 || ch == 0xD1)
             return (uint8_t)0;
 
+#endif
+
+#if defined(OLED_CS)
+
+        switch (last) {
+        case 0xC2: {
+            SKIPREST = false;
+            return (uint8_t)ch;
+        }
+
+        case 0xC3: {
+            SKIPREST = false;
+            return (uint8_t)(ch | 0xC0);
+        }
+
+        case 0xC4: {
+            SKIPREST = false;
+            if (ch == 140)
+                return (uint8_t)(129); // Č
+            if (ch == 141)
+                return (uint8_t)(138); // č
+            if (ch == 142)
+                return (uint8_t)(130); // Ď
+            if (ch == 143)
+                return (uint8_t)(139); // ď
+            if (ch == 154)
+                return (uint8_t)(131); // Ě
+            if (ch == 155)
+                return (uint8_t)(140); // ě
+            // Slovak specific glyphs
+            if (ch == 185)
+                return (uint8_t)(147); // Ĺ
+            if (ch == 186)
+                return (uint8_t)(148); // ĺ
+            if (ch == 189)
+                return (uint8_t)(149); // Ľ
+            if (ch == 190)
+                return (uint8_t)(150); // ľ
+            break;
+        }
+
+        case 0xC5: {
+            SKIPREST = false;
+            if (ch == 135)
+                return (uint8_t)(132); // Ň
+            if (ch == 136)
+                return (uint8_t)(141); // ň
+            if (ch == 152)
+                return (uint8_t)(133); // Ř
+            if (ch == 153)
+                return (uint8_t)(142); // ř
+            if (ch == 160)
+                return (uint8_t)(134); // Š
+            if (ch == 161)
+                return (uint8_t)(143); // š
+            if (ch == 164)
+                return (uint8_t)(135); // Ť
+            if (ch == 165)
+                return (uint8_t)(144); // ť
+            if (ch == 174)
+                return (uint8_t)(136); // Ů
+            if (ch == 175)
+                return (uint8_t)(145); // ů
+            if (ch == 189)
+                return (uint8_t)(137); // Ž
+            if (ch == 190)
+                return (uint8_t)(146); // ž
+            // Slovak specific glyphs
+            if (ch == 148)
+                return (uint8_t)(151); // Ŕ
+            if (ch == 149)
+                return (uint8_t)(152); // ŕ
+            break;
+        }
+        }
+
+        // We want to strip out prefix chars for two-byte char formats
+        if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5)
+            return (uint8_t)0;
+
 #endif
 
         // If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the
diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h
index 032348d54..81eb717cd 100644
--- a/src/graphics/ScreenFonts.h
+++ b/src/graphics/ScreenFonts.h
@@ -12,6 +12,10 @@
 #include "graphics/fonts/OLEDDisplayFontsUA.h"
 #endif
 
+#ifdef OLED_CS
+#include "graphics/fonts/OLEDDisplayFontsCS.h"
+#endif
+
 #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) ||      \
      defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS)) &&                                                         \
     !defined(DISPLAY_FORCE_SMALL_FONTS)
@@ -29,21 +33,33 @@
 #ifdef OLED_UA
 #define FONT_SMALL ArialMT_Plain_10_UA // Height: 13
 #else
+#ifdef OLED_CS
+#define FONT_SMALL ArialMT_Plain_10_CS
+#else
 #define FONT_SMALL ArialMT_Plain_10 // Height: 13
 #endif
 #endif
 #endif
+#endif
 #ifdef OLED_UA
 #define FONT_MEDIUM ArialMT_Plain_16_UA // Height: 19
 #else
+#ifdef OLED_CS
+#define FONT_MEDIUM ArialMT_Plain_16_CS
+#else
 #define FONT_MEDIUM ArialMT_Plain_16 // Height: 19
 #endif
+#endif
 #ifdef OLED_UA
 #define FONT_LARGE ArialMT_Plain_24_UA // Height: 28
 #else
+#ifdef OLED_CS
+#define FONT_LARGE ArialMT_Plain_24_CS // Height: 28
+#else
 #define FONT_LARGE ArialMT_Plain_24 // Height: 28
 #endif
 #endif
+#endif
 
 #define _fontHeight(font) ((font)[1] + 1) // height is position 1
 
diff --git a/src/graphics/fonts/OLEDDisplayFontsCS.cpp b/src/graphics/fonts/OLEDDisplayFontsCS.cpp
new file mode 100644
index 000000000..5c17e9177
--- /dev/null
+++ b/src/graphics/fonts/OLEDDisplayFontsCS.cpp
@@ -0,0 +1,1863 @@
+#include "OLEDDisplayFontsCS.h"
+
+// Font generated or edited with the glyphEditor
+const uint8_t ArialMT_Plain_10_CS[] PROGMEM = {
+    0x0A, // Width: 10
+    0x0D, // Height: 13
+    0x20, // First char: 32
+    0xE0, // Number of chars: 224
+    // Jump Table:
+    0xFF, 0xFF, 0x00, 0x0A, // 32
+    0x00, 0x00, 0x04, 0x03, // 33
+    0x00, 0x04, 0x05, 0x04, // 34
+    0x00, 0x09, 0x09, 0x06, // 35
+    0x00, 0x12, 0x0A, 0x06, // 36
+    0x00, 0x1C, 0x10, 0x09, // 37
+    0x00, 0x2C, 0x0E, 0x08, // 38
+    0x00, 0x3A, 0x01, 0x02, // 39
+    0x00, 0x3B, 0x06, 0x04, // 40
+    0x00, 0x41, 0x06, 0x04, // 41
+    0x00, 0x47, 0x05, 0x04, // 42
+    0x00, 0x4C, 0x09, 0x06, // 43
+    0x00, 0x55, 0x04, 0x03, // 44
+    0x00, 0x59, 0x03, 0x03, // 45
+    0x00, 0x5C, 0x04, 0x03, // 46
+    0x00, 0x60, 0x05, 0x04, // 47
+    0x00, 0x65, 0x0A, 0x06, // 48
+    0x00, 0x6F, 0x08, 0x05, // 49
+    0x00, 0x77, 0x0A, 0x06, // 50
+    0x00, 0x81, 0x0A, 0x06, // 51
+    0x00, 0x8B, 0x0B, 0x07, // 52
+    0x00, 0x96, 0x0A, 0x06, // 53
+    0x00, 0xA0, 0x0A, 0x06, // 54
+    0x00, 0xAA, 0x09, 0x06, // 55
+    0x00, 0xB3, 0x0A, 0x06, // 56
+    0x00, 0xBD, 0x0A, 0x06, // 57
+    0x00, 0xC7, 0x04, 0x03, // 58
+    0x00, 0xCB, 0x04, 0x03, // 59
+    0x00, 0xCF, 0x0A, 0x06, // 60
+    0x00, 0xD9, 0x09, 0x06, // 61
+    0x00, 0xE2, 0x09, 0x06, // 62
+    0x00, 0xEB, 0x0B, 0x07, // 63
+    0x00, 0xF6, 0x14, 0x0B, // 64
+    0x01, 0x0A, 0x0E, 0x08, // 65
+    0x01, 0x18, 0x0C, 0x07, // 66
+    0x01, 0x24, 0x0C, 0x07, // 67
+    0x01, 0x30, 0x0B, 0x07, // 68
+    0x01, 0x3B, 0x0C, 0x07, // 69
+    0x01, 0x47, 0x09, 0x06, // 70
+    0x01, 0x50, 0x0D, 0x08, // 71
+    0x01, 0x5D, 0x0C, 0x07, // 72
+    0x01, 0x69, 0x04, 0x03, // 73
+    0x01, 0x6D, 0x08, 0x05, // 74
+    0x01, 0x75, 0x0E, 0x08, // 75
+    0x01, 0x83, 0x0C, 0x07, // 76
+    0x01, 0x8F, 0x10, 0x09, // 77
+    0x01, 0x9F, 0x0C, 0x07, // 78
+    0x01, 0xAB, 0x0E, 0x08, // 79
+    0x01, 0xB9, 0x0B, 0x07, // 80
+    0x01, 0xC4, 0x0E, 0x08, // 81
+    0x01, 0xD2, 0x0C, 0x07, // 82
+    0x01, 0xDE, 0x0C, 0x07, // 83
+    0x01, 0xEA, 0x0B, 0x07, // 84
+    0x01, 0xF5, 0x0C, 0x07, // 85
+    0x02, 0x01, 0x0D, 0x08, // 86
+    0x02, 0x0E, 0x11, 0x0A, // 87
+    0x02, 0x1F, 0x0E, 0x08, // 88
+    0x02, 0x2D, 0x0D, 0x08, // 89
+    0x02, 0x3A, 0x0C, 0x07, // 90
+    0x02, 0x46, 0x06, 0x04, // 91
+    0x02, 0x4C, 0x06, 0x04, // 92
+    0x02, 0x52, 0x04, 0x03, // 93
+    0x02, 0x56, 0x09, 0x06, // 94
+    0x02, 0x5F, 0x0C, 0x07, // 95
+    0x02, 0x6B, 0x03, 0x03, // 96
+    0x02, 0x6E, 0x0A, 0x06, // 97
+    0x02, 0x78, 0x0A, 0x06, // 98
+    0x02, 0x82, 0x0A, 0x06, // 99
+    0x02, 0x8C, 0x0A, 0x06, // 100
+    0x02, 0x96, 0x0A, 0x06, // 101
+    0x02, 0xA0, 0x05, 0x04, // 102
+    0x02, 0xA5, 0x0A, 0x06, // 103
+    0x02, 0xAF, 0x0A, 0x06, // 104
+    0x02, 0xB9, 0x04, 0x03, // 105
+    0x02, 0xBD, 0x04, 0x03, // 106
+    0x02, 0xC1, 0x08, 0x05, // 107
+    0x02, 0xC9, 0x04, 0x03, // 108
+    0x02, 0xCD, 0x10, 0x09, // 109
+    0x02, 0xDD, 0x0A, 0x06, // 110
+    0x02, 0xE7, 0x0A, 0x06, // 111
+    0x02, 0xF1, 0x0A, 0x06, // 112
+    0x02, 0xFB, 0x0A, 0x06, // 113
+    0x03, 0x05, 0x05, 0x04, // 114
+    0x03, 0x0A, 0x08, 0x05, // 115
+    0x03, 0x12, 0x06, 0x04, // 116
+    0x03, 0x18, 0x0A, 0x06, // 117
+    0x03, 0x22, 0x09, 0x06, // 118
+    0x03, 0x2B, 0x0E, 0x08, // 119
+    0x03, 0x39, 0x0A, 0x06, // 120
+    0x03, 0x43, 0x09, 0x06, // 121
+    0x03, 0x4C, 0x0A, 0x06, // 122
+    0x03, 0x56, 0x06, 0x04, // 123
+    0x03, 0x5C, 0x04, 0x03, // 124
+    0x03, 0x60, 0x05, 0x04, // 125
+    0x03, 0x65, 0x09, 0x06, // 126
+    0xFF, 0xFF, 0x00, 0x0A, // 127
+    0xFF, 0xFF, 0x00, 0x0A, // 128
+    0x03, 0x6E, 0x0C, 0x07, // 129
+    0x03, 0x7A, 0x0B, 0x07, // 130
+    0x03, 0x85, 0x0C, 0x07, // 131
+    0x03, 0x91, 0x0C, 0x07, // 132
+    0x03, 0x9D, 0x0C, 0x07, // 133
+    0x03, 0xA9, 0x0C, 0x07, // 134
+    0x03, 0xB5, 0x0B, 0x07, // 135
+    0x03, 0xC0, 0x0C, 0x07, // 136
+    0x03, 0xCC, 0x0C, 0x07, // 137
+    0x03, 0xD8, 0x0A, 0x06, // 138
+    0x03, 0xE2, 0x0D, 0x08, // 139
+    0x03, 0xEF, 0x0A, 0x06, // 140
+    0x03, 0xF9, 0x0A, 0x06, // 141
+    0x04, 0x03, 0x07, 0x05, // 142
+    0x04, 0x0A, 0x08, 0x05, // 143
+    0x04, 0x12, 0x07, 0x05, // 144
+    0x04, 0x19, 0x0A, 0x06, // 145
+    0x04, 0x23, 0x0A, 0x06, // 146
+    0x04, 0x2D, 0x0C, 0x07, // 147
+    0x04, 0x39, 0x05, 0x04, // 148
+    0x04, 0x3E, 0x0C, 0x07, // 149
+    0x04, 0x4A, 0x07, 0x05, // 150
+    0x04, 0x51, 0x0C, 0x07, // 151
+    0x04, 0x5D, 0x07, 0x05, // 152
+    0xFF, 0xFF, 0x00, 0x0A, // 153
+    0xFF, 0xFF, 0x00, 0x0A, // 154
+    0xFF, 0xFF, 0x00, 0x0A, // 155
+    0xFF, 0xFF, 0x00, 0x0A, // 156
+    0xFF, 0xFF, 0x00, 0x0A, // 157
+    0xFF, 0xFF, 0x00, 0x0A, // 158
+    0xFF, 0xFF, 0x00, 0x0A, // 159
+    0xFF, 0xFF, 0x00, 0x0A, // 160
+    0x04, 0x64, 0x04, 0x03, // 161
+    0x04, 0x68, 0x0A, 0x06, // 162
+    0x04, 0x72, 0x0C, 0x07, // 163
+    0x04, 0x7E, 0x0A, 0x06, // 164
+    0x04, 0x88, 0x0A, 0x06, // 165
+    0x04, 0x92, 0x04, 0x03, // 166
+    0x04, 0x96, 0x0A, 0x06, // 167
+    0x04, 0xA0, 0x05, 0x04, // 168
+    0x04, 0xA5, 0x0D, 0x08, // 169
+    0x04, 0xB2, 0x07, 0x05, // 170
+    0x04, 0xB9, 0x0A, 0x06, // 171
+    0x04, 0xC3, 0x09, 0x06, // 172
+    0x04, 0xCC, 0x03, 0x03, // 173
+    0x04, 0xCF, 0x0D, 0x08, // 174
+    0x04, 0xDC, 0x0B, 0x07, // 175
+    0x04, 0xE7, 0x07, 0x05, // 176
+    0x04, 0xEE, 0x0A, 0x06, // 177
+    0x04, 0xF8, 0x05, 0x04, // 178
+    0x04, 0xFD, 0x05, 0x04, // 179
+    0x05, 0x02, 0x05, 0x04, // 180
+    0x05, 0x07, 0x0A, 0x06, // 181
+    0x05, 0x11, 0x09, 0x06, // 182
+    0x05, 0x1A, 0x03, 0x03, // 183
+    0x05, 0x1D, 0x06, 0x04, // 184
+    0x05, 0x23, 0x05, 0x04, // 185
+    0x05, 0x28, 0x07, 0x05, // 186
+    0x05, 0x2F, 0x0A, 0x06, // 187
+    0x05, 0x39, 0x10, 0x09, // 188
+    0x05, 0x49, 0x10, 0x09, // 189
+    0x05, 0x59, 0x10, 0x09, // 190
+    0x05, 0x69, 0x0A, 0x06, // 191
+    0x05, 0x73, 0x0E, 0x08, // 192
+    0x05, 0x81, 0x0E, 0x08, // 193
+    0x05, 0x8F, 0x0E, 0x08, // 194
+    0x05, 0x9D, 0x0E, 0x08, // 195
+    0x05, 0xAB, 0x0E, 0x08, // 196
+    0x05, 0xB9, 0x0E, 0x08, // 197
+    0x05, 0xC7, 0x12, 0x0A, // 198
+    0x05, 0xD9, 0x0C, 0x07, // 199
+    0x05, 0xE5, 0x0C, 0x07, // 200
+    0x05, 0xF1, 0x0C, 0x07, // 201
+    0x05, 0xFD, 0x0C, 0x07, // 202
+    0x06, 0x09, 0x0C, 0x07, // 203
+    0x06, 0x15, 0x05, 0x04, // 204
+    0x06, 0x1A, 0x04, 0x03, // 205
+    0x06, 0x1E, 0x04, 0x03, // 206
+    0x06, 0x22, 0x05, 0x04, // 207
+    0x06, 0x27, 0x0B, 0x07, // 208
+    0x06, 0x32, 0x0C, 0x07, // 209
+    0x06, 0x3E, 0x0E, 0x08, // 210
+    0x06, 0x4C, 0x0E, 0x08, // 211
+    0x06, 0x5A, 0x0E, 0x08, // 212
+    0x06, 0x68, 0x0E, 0x08, // 213
+    0x06, 0x76, 0x0E, 0x08, // 214
+    0x06, 0x84, 0x0A, 0x06, // 215
+    0x06, 0x8E, 0x0D, 0x08, // 216
+    0x06, 0x9B, 0x0C, 0x07, // 217
+    0x06, 0xA7, 0x0C, 0x07, // 218
+    0x06, 0xB3, 0x0C, 0x07, // 219
+    0x06, 0xBF, 0x0C, 0x07, // 220
+    0x06, 0xCB, 0x0D, 0x08, // 221
+    0x06, 0xD8, 0x0B, 0x07, // 222
+    0x06, 0xE3, 0x0C, 0x07, // 223
+    0x06, 0xEF, 0x0A, 0x06, // 224
+    0x06, 0xF9, 0x0A, 0x06, // 225
+    0x07, 0x03, 0x0A, 0x06, // 226
+    0x07, 0x0D, 0x0A, 0x06, // 227
+    0x07, 0x17, 0x0A, 0x06, // 228
+    0x07, 0x21, 0x0A, 0x06, // 229
+    0x07, 0x2B, 0x10, 0x09, // 230
+    0x07, 0x3B, 0x0A, 0x06, // 231
+    0x07, 0x45, 0x0A, 0x06, // 232
+    0x07, 0x4F, 0x0A, 0x06, // 233
+    0x07, 0x59, 0x0A, 0x06, // 234
+    0x07, 0x63, 0x0A, 0x06, // 235
+    0x07, 0x6D, 0x05, 0x04, // 236
+    0x07, 0x72, 0x04, 0x03, // 237
+    0x07, 0x76, 0x05, 0x04, // 238
+    0x07, 0x7B, 0x05, 0x04, // 239
+    0x07, 0x80, 0x0A, 0x06, // 240
+    0x07, 0x8A, 0x0A, 0x06, // 241
+    0x07, 0x94, 0x0A, 0x06, // 242
+    0x07, 0x9E, 0x0A, 0x06, // 243
+    0x07, 0xA8, 0x0A, 0x06, // 244
+    0x07, 0xB2, 0x0A, 0x06, // 245
+    0x07, 0xBC, 0x0A, 0x06, // 246
+    0x07, 0xC6, 0x09, 0x06, // 247
+    0x07, 0xCF, 0x0A, 0x06, // 248
+    0x07, 0xD9, 0x0A, 0x06, // 249
+    0x07, 0xE3, 0x0A, 0x06, // 250
+    0x07, 0xED, 0x0A, 0x06, // 251
+    0x07, 0xF7, 0x0A, 0x06, // 252
+    0x08, 0x01, 0x09, 0x06, // 253
+    0x08, 0x0A, 0x0A, 0x06, // 254
+    0x08, 0x14, 0x09, 0x06, // 255
+    // Font Data:
+    0x00, 0x00, 0xF8, 0x02,                                                                                                 // 33
+    0x38, 0x00, 0x00, 0x00, 0x38,                                                                                           // 34
+    0xA0, 0x03, 0xE0, 0x00, 0xB8, 0x03, 0xE0, 0x00, 0xB8,                                                                   // 35
+    0x30, 0x01, 0x28, 0x02, 0xF8, 0x07, 0x48, 0x02, 0x90, 0x01,                                                             // 36
+    0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x30, 0x03, 0xC0, 0x00, 0xB0, 0x01, 0x48, 0x02, 0x80, 0x01,                         // 37
+    0x80, 0x01, 0x50, 0x02, 0x68, 0x02, 0xA8, 0x02, 0x18, 0x01, 0x80, 0x03, 0x80, 0x02,                                     // 38
+    0x38,                                                                                                                   // 39
+    0xE0, 0x03, 0x10, 0x04, 0x08, 0x08,                                                                                     // 40
+    0x08, 0x08, 0x10, 0x04, 0xE0, 0x03,                                                                                     // 41
+    0x28, 0x00, 0x18, 0x00, 0x28,                                                                                           // 42
+    0x40, 0x00, 0x40, 0x00, 0xF0, 0x01, 0x40, 0x00, 0x40,                                                                   // 43
+    0x00, 0x00, 0x00, 0x06,                                                                                                 // 44
+    0x80, 0x00, 0x80,                                                                                                       // 45
+    0x00, 0x00, 0x00, 0x02,                                                                                                 // 46
+    0x00, 0x03, 0xE0, 0x00, 0x18,                                                                                           // 47
+    0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01,                                                             // 48
+    0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0xF8, 0x03,                                                                         // 49
+    0x10, 0x02, 0x08, 0x03, 0x88, 0x02, 0x48, 0x02, 0x30, 0x02,                                                             // 50
+    0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01,                                                             // 51
+    0xC0, 0x00, 0xA0, 0x00, 0x90, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x80,                                                       // 52
+    0x60, 0x01, 0x38, 0x02, 0x28, 0x02, 0x28, 0x02, 0xC8, 0x01,                                                             // 53
+    0xF0, 0x01, 0x28, 0x02, 0x28, 0x02, 0x28, 0x02, 0xD0, 0x01,                                                             // 54
+    0x08, 0x00, 0x08, 0x03, 0xC8, 0x00, 0x38, 0x00, 0x08,                                                                   // 55
+    0xB0, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01,                                                             // 56
+    0x70, 0x01, 0x88, 0x02, 0x88, 0x02, 0x88, 0x02, 0xF0, 0x01,                                                             // 57
+    0x00, 0x00, 0x20, 0x02,                                                                                                 // 58
+    0x00, 0x00, 0x20, 0x06,                                                                                                 // 59
+    0x00, 0x00, 0x40, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x10, 0x01,                                                             // 60
+    0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0,                                                                   // 61
+    0x00, 0x00, 0x10, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0x40,                                                                   // 62
+    0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC8, 0x02, 0x48, 0x00, 0x30,                                                       // 63
+    0x00, 0x00, 0xC0, 0x03, 0x30, 0x04, 0xD0, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0xC8, 0x0B, 0x68, 0x0A, 0x10, 0x05, 0xE0, 0x04, // 64
+    0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x01, 0x00, 0x02,                                     // 65
+    0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01,                                                 // 66
+    0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01,                                                 // 67
+    0x00, 0x00, 0xF8, 0x03, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, 0xE0,                                                       // 68
+    0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02,                                                 // 69
+    0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x08,                                                                   // 70
+    0x00, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x50, 0x01, 0xC0,                                           // 71
+    0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03,                                                 // 72
+    0x00, 0x00, 0xF8, 0x03,                                                                                                 // 73
+    0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01,                                                                         // 74
+    0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02,                                     // 75
+    0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,                                                 // 76
+    0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x30, 0x00, 0xF8, 0x03,                         // 77
+    0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x40, 0x00, 0x80, 0x01, 0xF8, 0x03,                                                 // 78
+    0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01,                                     // 79
+    0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30,                                                       // 80
+    0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x03, 0x08, 0x03, 0xF0, 0x02,                                     // 81
+    0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0xC8, 0x00, 0x30, 0x03,                                                 // 82
+    0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x90, 0x01,                                                 // 83
+    0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08,                                                       // 84
+    0x00, 0x00, 0xF8, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01,                                                 // 85
+    0x08, 0x00, 0x70, 0x00, 0x80, 0x01, 0x00, 0x02, 0x80, 0x01, 0x70, 0x00, 0x08,                                           // 86
+    0x18, 0x00, 0xE0, 0x01, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x02, 0xE0, 0x01, 0x18,                   // 87
+    0x00, 0x02, 0x08, 0x01, 0x90, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02,                                     // 88
+    0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x20, 0x00, 0x10, 0x00, 0x08,                                           // 89
+    0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x68, 0x02, 0x38, 0x02, 0x18, 0x02,                                                 // 90
+    0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08,                                                                                     // 91
+    0x18, 0x00, 0xE0, 0x00, 0x00, 0x03,                                                                                     // 92
+    0x08, 0x08, 0xF8, 0x0F,                                                                                                 // 93
+    0x40, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40,                                                                   // 94
+    0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08,                                                 // 95
+    0x08, 0x00, 0x10,                                                                                                       // 96
+    0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x03,                                                             // 97
+    0x00, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01,                                                             // 98
+    0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01,                                                             // 99
+    0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03,                                                             // 100
+    0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02,                                                             // 101
+    0x20, 0x00, 0xF0, 0x03, 0x28,                                                                                           // 102
+    0x00, 0x00, 0xC0, 0x05, 0x20, 0x0A, 0x20, 0x0A, 0xE0, 0x07,                                                             // 103
+    0x00, 0x00, 0xF8, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03,                                                             // 104
+    0x00, 0x00, 0xE8, 0x03,                                                                                                 // 105
+    0x00, 0x08, 0xE8, 0x07,                                                                                                 // 106
+    0xF8, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02,                                                                         // 107
+    0x00, 0x00, 0xF8, 0x03,                                                                                                 // 108
+    0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03,                         // 109
+    0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03,                                                             // 110
+    0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01,                                                             // 111
+    0x00, 0x00, 0xE0, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01,                                                             // 112
+    0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xE0, 0x0F,                                                             // 113
+    0x00, 0x00, 0xE0, 0x03, 0x20,                                                                                           // 114
+    0x40, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x20, 0x01,                                                                         // 115
+    0x20, 0x00, 0xF8, 0x03, 0x20, 0x02,                                                                                     // 116
+    0x00, 0x00, 0xE0, 0x01, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03,                                                             // 117
+    0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20,                                                                   // 118
+    0xE0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xE0, 0x01,                                     // 119
+    0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02,                                                             // 120
+    0x20, 0x00, 0xC0, 0x09, 0x00, 0x06, 0xC0, 0x01, 0x20,                                                                   // 121
+    0x20, 0x02, 0x20, 0x03, 0xA0, 0x02, 0x60, 0x02, 0x20, 0x02,                                                             // 122
+    0x80, 0x00, 0x78, 0x0F, 0x08, 0x08,                                                                                     // 123
+    0x00, 0x00, 0xF8, 0x0F,                                                                                                 // 124
+    0x08, 0x08, 0x78, 0x0F, 0x80,                                                                                           // 125
+    0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0xC0,                                                                   // 126
+    0x00, 0x00, 0xF0, 0x01, 0x09, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x10, 0x01,                                                 // 129
+    0x00, 0x00, 0xF8, 0x03, 0x09, 0x02, 0x0A, 0x02, 0x11, 0x01, 0xE0,                                                       // 130
+    0x00, 0x00, 0xF8, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x48, 0x02,                                                 // 131
+    0x00, 0x00, 0xF8, 0x03, 0x31, 0x00, 0x42, 0x00, 0x81, 0x01, 0xF8, 0x03,                                                 // 132
+    0x00, 0x00, 0xF8, 0x03, 0x49, 0x00, 0x4A, 0x00, 0xC9, 0x00, 0x30, 0x03,                                                 // 133
+    0x00, 0x00, 0x30, 0x01, 0x49, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x90, 0x01,                                                 // 134
+    0x00, 0x00, 0x08, 0x00, 0x09, 0x00, 0xFA, 0x03, 0x09, 0x00, 0x08,                                                       // 135
+    0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x05, 0x02, 0x02, 0x02, 0xF8, 0x01,                                                 // 136
+    0x08, 0x03, 0x88, 0x02, 0xC9, 0x02, 0x6A, 0x02, 0x39, 0x02, 0x18, 0x02,                                                 // 137
+    0x00, 0x00, 0xC0, 0x01, 0x24, 0x02, 0x28, 0x02, 0x44, 0x01,                                                             // 138
+    0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03, 0x00, 0x00, 0x18,                                           // 139
+    0x00, 0x00, 0xC0, 0x01, 0xA4, 0x02, 0xA8, 0x02, 0xC4, 0x02,                                                             // 140
+    0x00, 0x00, 0xE0, 0x03, 0x24, 0x00, 0x28, 0x00, 0xC4, 0x03,                                                             // 141
+    0x00, 0x00, 0xE4, 0x03, 0x28, 0x00, 0x04,                                                                               // 142
+    0x40, 0x02, 0xA4, 0x02, 0xA8, 0x02, 0x24, 0x01,                                                                         // 143
+    0x20, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x18,                                                                               // 144
+    0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x14, 0x02, 0xE8, 0x03,                                                             // 145
+    0x20, 0x02, 0x24, 0x03, 0xA8, 0x02, 0x64, 0x02, 0x20, 0x02,                                                             // 146
+    0x00, 0x00, 0xFA, 0x03, 0x01, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,                                                 // 147
+    0x00, 0x00, 0xFA, 0x03, 0x01,                                                                                           // 148
+    0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x18, 0x02, 0x00, 0x02,                                                 // 149
+    0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x18,                                                                               // 150
+    0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x4A, 0x00, 0xC9, 0x00, 0x30, 0x03,                                                 // 151
+    0x00, 0x00, 0xE0, 0x03, 0x28, 0x00, 0x04,                                                                               // 152
+    0x00, 0x00, 0xA0, 0x0F,                                                                                                 // 161
+    0x00, 0x00, 0xC0, 0x01, 0xA0, 0x0F, 0x78, 0x02, 0x40, 0x01,                                                             // 162
+    0x40, 0x02, 0x70, 0x03, 0xC8, 0x02, 0x48, 0x02, 0x08, 0x02, 0x10, 0x02,                                                 // 163
+    0x00, 0x00, 0xE0, 0x01, 0x20, 0x01, 0x20, 0x01, 0xE0, 0x01,                                                             // 164
+    0x48, 0x01, 0x70, 0x01, 0xC0, 0x03, 0x70, 0x01, 0x48, 0x01,                                                             // 165
+    0x00, 0x00, 0x38, 0x0F,                                                                                                 // 166
+    0xD0, 0x04, 0x28, 0x09, 0x48, 0x09, 0x48, 0x0A, 0x90, 0x05,                                                             // 167
+    0x08, 0x00, 0x00, 0x00, 0x08,                                                                                           // 168
+    0xE0, 0x00, 0x10, 0x01, 0x48, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0x10, 0x01, 0xE0,                                           // 169
+    0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x78,                                                                               // 170
+    0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02,                                                             // 171
+    0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0,                                                                   // 172
+    0x80, 0x00, 0x80,                                                                                                       // 173
+    0xE0, 0x00, 0x10, 0x01, 0xE8, 0x02, 0x68, 0x02, 0xC8, 0x02, 0x10, 0x01, 0xE0,                                           // 174
+    0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02,                                                       // 175
+    0x00, 0x00, 0x38, 0x00, 0x28, 0x00, 0x38,                                                                               // 176
+    0x40, 0x02, 0x40, 0x02, 0xF0, 0x03, 0x40, 0x02, 0x40, 0x02,                                                             // 177
+    0x48, 0x00, 0x68, 0x00, 0x58,                                                                                           // 178
+    0x48, 0x00, 0x58, 0x00, 0x68,                                                                                           // 179
+    0x00, 0x00, 0x10, 0x00, 0x08,                                                                                           // 180
+    0x00, 0x00, 0xE0, 0x0F, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03,                                                             // 181
+    0x70, 0x00, 0xF8, 0x0F, 0x08, 0x00, 0xF8, 0x0F, 0x08,                                                                   // 182
+    0x00, 0x00, 0x40,                                                                                                       // 183
+    0x00, 0x00, 0x00, 0x14, 0x00, 0x18,                                                                                     // 184
+    0x00, 0x00, 0x10, 0x00, 0x78,                                                                                           // 185
+    0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30,                                                                               // 186
+    0x00, 0x00, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01,                                                             // 187
+    0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0xC0, 0x00, 0x20, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01,                         // 188
+    0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0x80, 0x00, 0x60, 0x00, 0x50, 0x02, 0x48, 0x03, 0xC0, 0x02,                         // 189
+    0x48, 0x00, 0x58, 0x00, 0x68, 0x03, 0x80, 0x00, 0x60, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01,                         // 190
+    0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0xA0, 0x09, 0x00, 0x04,                                                             // 191
+    0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02,                                     // 192
+    0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02,                                     // 193
+    0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02,                                     // 194
+    0x00, 0x02, 0xC2, 0x01, 0xB1, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02,                                     // 195
+    0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x88, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02,                                     // 196
+    0x00, 0x02, 0xC0, 0x01, 0xBE, 0x00, 0x8A, 0x00, 0xBE, 0x00, 0xC0, 0x01, 0x00, 0x02,                                     // 197
+    0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x98, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02,             // 198
+    0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x16, 0x08, 0x1A, 0x10, 0x01,                                                 // 199
+    0x00, 0x00, 0xF8, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02,                                                 // 200
+    0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x48, 0x02,                                                 // 201
+    0x00, 0x00, 0xFA, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02,                                                 // 202
+    0x00, 0x00, 0xF8, 0x03, 0x4A, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x48, 0x02,                                                 // 203
+    0x00, 0x00, 0xF9, 0x03, 0x02,                                                                                           // 204
+    0x02, 0x00, 0xF9, 0x03,                                                                                                 // 205
+    0x01, 0x00, 0xFA, 0x03,                                                                                                 // 206
+    0x02, 0x00, 0xF8, 0x03, 0x02,                                                                                           // 207
+    0x40, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x10, 0x01, 0xE0,                                                       // 208
+    0x00, 0x00, 0xFA, 0x03, 0x31, 0x00, 0x42, 0x00, 0x81, 0x01, 0xF8, 0x03,                                                 // 209
+    0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01,                                     // 210
+    0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x08, 0x02, 0xF0, 0x01,                                     // 211
+    0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0xF0, 0x01,                                     // 212
+    0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01,                                     // 213
+    0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01,                                     // 214
+    0x10, 0x01, 0xA0, 0x00, 0xE0, 0x00, 0xA0, 0x00, 0x10, 0x01,                                                             // 215
+    0x00, 0x00, 0xF0, 0x02, 0x08, 0x03, 0xC8, 0x02, 0x28, 0x02, 0x18, 0x03, 0xE8,                                           // 216
+    0x00, 0x00, 0xF8, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0xF8, 0x01,                                                 // 217
+    0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0xF8, 0x01,                                                 // 218
+    0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0xF8, 0x01,                                                 // 219
+    0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0xF8, 0x01,                                                 // 220
+    0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC2, 0x03, 0x21, 0x00, 0x10, 0x00, 0x08,                                           // 221
+    0x00, 0x00, 0xF8, 0x03, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xE0,                                                       // 222
+    0x00, 0x00, 0xF0, 0x03, 0x08, 0x01, 0x48, 0x02, 0xB0, 0x02, 0x80, 0x01,                                                 // 223
+    0x00, 0x00, 0x00, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE0, 0x03,                                                             // 224
+    0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE0, 0x03,                                                             // 225
+    0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE8, 0x03,                                                             // 226
+    0x00, 0x00, 0x08, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE4, 0x03,                                                             // 227
+    0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA0, 0x02, 0xE8, 0x03,                                                             // 228
+    0x00, 0x00, 0x00, 0x03, 0xAE, 0x02, 0xAA, 0x02, 0xEE, 0x03,                                                             // 229
+    0x00, 0x00, 0x40, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02,                         // 230
+    0x00, 0x00, 0xC0, 0x01, 0x20, 0x16, 0x20, 0x1A, 0x40, 0x01,                                                             // 231
+    0x00, 0x00, 0xC0, 0x01, 0xA4, 0x02, 0xA8, 0x02, 0xC0, 0x02,                                                             // 232
+    0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC0, 0x02,                                                             // 233
+    0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC8, 0x02,                                                             // 234
+    0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA0, 0x02, 0xC8, 0x02,                                                             // 235
+    0x00, 0x00, 0xE4, 0x03, 0x08,                                                                                           // 236
+    0x08, 0x00, 0xE4, 0x03,                                                                                                 // 237
+    0x08, 0x00, 0xE4, 0x03, 0x08,                                                                                           // 238
+    0x08, 0x00, 0xE0, 0x03, 0x08,                                                                                           // 239
+    0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x38, 0x02, 0xE0, 0x01,                                                             // 240
+    0x00, 0x00, 0xE8, 0x03, 0x24, 0x00, 0x28, 0x00, 0xC4, 0x03,                                                             // 241
+    0x00, 0x00, 0xC0, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC0, 0x01,                                                             // 242
+    0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC0, 0x01,                                                             // 243
+    0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC8, 0x01,                                                             // 244
+    0x00, 0x00, 0xC8, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC4, 0x01,                                                             // 245
+    0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x20, 0x02, 0xC8, 0x01,                                                             // 246
+    0x40, 0x00, 0x40, 0x00, 0x50, 0x01, 0x40, 0x00, 0x40,                                                                   // 247
+    0x00, 0x00, 0xC0, 0x02, 0xA0, 0x03, 0x60, 0x02, 0xA0, 0x01,                                                             // 248
+    0x00, 0x00, 0xE0, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03,                                                             // 249
+    0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x04, 0x02, 0xE0, 0x03,                                                             // 250
+    0x00, 0x00, 0xE8, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03,                                                             // 251
+    0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x00, 0x02, 0xE8, 0x03,                                                             // 252
+    0x20, 0x00, 0xC0, 0x09, 0x08, 0x06, 0xC4, 0x01, 0x20,                                                                   // 253
+    0x00, 0x00, 0xF8, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01,                                                             // 254
+    0x20, 0x00, 0xC8, 0x09, 0x00, 0x06, 0xC8, 0x01, 0x20,                                                                   // 255
+};
+
+const uint8_t ArialMT_Plain_16_CS[] PROGMEM = {
+    0x10, // Width: 16
+    0x13, // Height: 19
+    0x20, // First char: 32
+    0xE0, // Number of chars: 224
+    // Jump Table:
+    0xFF, 0xFF, 0x00, 0x10, // 32
+    0x00, 0x00, 0x08, 0x04, // 33
+    0x00, 0x08, 0x0D, 0x06, // 34
+    0x00, 0x15, 0x1A, 0x0A, // 35
+    0x00, 0x2F, 0x17, 0x09, // 36
+    0x00, 0x46, 0x26, 0x0E, // 37
+    0x00, 0x6C, 0x1D, 0x0B, // 38
+    0x00, 0x89, 0x04, 0x03, // 39
+    0x00, 0x8D, 0x0C, 0x05, // 40
+    0x00, 0x99, 0x0B, 0x05, // 41
+    0x00, 0xA4, 0x0D, 0x06, // 42
+    0x00, 0xB1, 0x17, 0x09, // 43
+    0x00, 0xC8, 0x09, 0x04, // 44
+    0x00, 0xD1, 0x0B, 0x05, // 45
+    0x00, 0xDC, 0x08, 0x04, // 46
+    0x00, 0xE4, 0x0A, 0x05, // 47
+    0x00, 0xEE, 0x17, 0x09, // 48
+    0x01, 0x05, 0x11, 0x07, // 49
+    0x01, 0x16, 0x17, 0x09, // 50
+    0x01, 0x2D, 0x17, 0x09, // 51
+    0x01, 0x44, 0x17, 0x09, // 52
+    0x01, 0x5B, 0x17, 0x09, // 53
+    0x01, 0x72, 0x17, 0x09, // 54
+    0x01, 0x89, 0x16, 0x09, // 55
+    0x01, 0x9F, 0x17, 0x09, // 56
+    0x01, 0xB6, 0x17, 0x09, // 57
+    0x01, 0xCD, 0x05, 0x03, // 58
+    0x01, 0xD2, 0x06, 0x03, // 59
+    0x01, 0xD8, 0x17, 0x09, // 60
+    0x01, 0xEF, 0x17, 0x09, // 61
+    0x02, 0x06, 0x17, 0x09, // 62
+    0x02, 0x1D, 0x16, 0x09, // 63
+    0x02, 0x33, 0x2F, 0x11, // 64
+    0x02, 0x62, 0x1D, 0x0B, // 65
+    0x02, 0x7F, 0x1D, 0x0B, // 66
+    0x02, 0x9C, 0x20, 0x0C, // 67
+    0x02, 0xBC, 0x20, 0x0C, // 68
+    0x02, 0xDC, 0x1D, 0x0B, // 69
+    0x02, 0xF9, 0x19, 0x0A, // 70
+    0x03, 0x12, 0x20, 0x0C, // 71
+    0x03, 0x32, 0x1D, 0x0B, // 72
+    0x03, 0x4F, 0x05, 0x03, // 73
+    0x03, 0x54, 0x14, 0x08, // 74
+    0x03, 0x68, 0x1D, 0x0B, // 75
+    0x03, 0x85, 0x17, 0x09, // 76
+    0x03, 0x9C, 0x23, 0x0D, // 77
+    0x03, 0xBF, 0x1D, 0x0B, // 78
+    0x03, 0xDC, 0x20, 0x0C, // 79
+    0x03, 0xFC, 0x1C, 0x0B, // 80
+    0x04, 0x18, 0x20, 0x0C, // 81
+    0x04, 0x38, 0x1D, 0x0B, // 82
+    0x04, 0x55, 0x1D, 0x0B, // 83
+    0x04, 0x72, 0x19, 0x0A, // 84
+    0x04, 0x8B, 0x1D, 0x0B, // 85
+    0x04, 0xA8, 0x1C, 0x0B, // 86
+    0x04, 0xC4, 0x2B, 0x10, // 87
+    0x04, 0xEF, 0x20, 0x0C, // 88
+    0x05, 0x0F, 0x19, 0x0A, // 89
+    0x05, 0x28, 0x1A, 0x0A, // 90
+    0x05, 0x42, 0x0C, 0x05, // 91
+    0x05, 0x4E, 0x0B, 0x05, // 92
+    0x05, 0x59, 0x09, 0x04, // 93
+    0x05, 0x62, 0x14, 0x08, // 94
+    0x05, 0x76, 0x1B, 0x0A, // 95
+    0x05, 0x91, 0x07, 0x04, // 96
+    0x05, 0x98, 0x17, 0x09, // 97
+    0x05, 0xAF, 0x17, 0x09, // 98
+    0x05, 0xC6, 0x14, 0x08, // 99
+    0x05, 0xDA, 0x17, 0x09, // 100
+    0x05, 0xF1, 0x17, 0x09, // 101
+    0x06, 0x08, 0x0A, 0x05, // 102
+    0x06, 0x12, 0x17, 0x09, // 103
+    0x06, 0x29, 0x14, 0x08, // 104
+    0x06, 0x3D, 0x05, 0x03, // 105
+    0x06, 0x42, 0x06, 0x03, // 106
+    0x06, 0x48, 0x17, 0x09, // 107
+    0x06, 0x5F, 0x05, 0x03, // 108
+    0x06, 0x64, 0x23, 0x0D, // 109
+    0x06, 0x87, 0x14, 0x08, // 110
+    0x06, 0x9B, 0x17, 0x09, // 111
+    0x06, 0xB2, 0x17, 0x09, // 112
+    0x06, 0xC9, 0x18, 0x09, // 113
+    0x06, 0xE1, 0x0D, 0x06, // 114
+    0x06, 0xEE, 0x14, 0x08, // 115
+    0x07, 0x02, 0x0B, 0x05, // 116
+    0x07, 0x0D, 0x14, 0x08, // 117
+    0x07, 0x21, 0x13, 0x08, // 118
+    0x07, 0x34, 0x1F, 0x0C, // 119
+    0x07, 0x53, 0x14, 0x08, // 120
+    0x07, 0x67, 0x13, 0x08, // 121
+    0x07, 0x7A, 0x14, 0x08, // 122
+    0x07, 0x8E, 0x0F, 0x06, // 123
+    0x07, 0x9D, 0x06, 0x03, // 124
+    0x07, 0xA3, 0x0E, 0x06, // 125
+    0x07, 0xB1, 0x17, 0x09, // 126
+    0xFF, 0xFF, 0x00, 0x10, // 127
+    0xFF, 0xFF, 0x00, 0x10, // 128
+    0x07, 0xC8, 0x20, 0x0C, // 129
+    0x07, 0xE8, 0x20, 0x0C, // 130
+    0x08, 0x08, 0x1D, 0x0B, // 131
+    0x08, 0x25, 0x1D, 0x0B, // 132
+    0x08, 0x42, 0x1D, 0x0B, // 133
+    0x08, 0x5F, 0x1D, 0x0B, // 134
+    0x08, 0x7C, 0x19, 0x0A, // 135
+    0x08, 0x95, 0x1D, 0x0B, // 136
+    0x08, 0xB2, 0x1A, 0x0A, // 137
+    0x08, 0xCC, 0x14, 0x08, // 138
+    0x08, 0xE0, 0x1C, 0x0B, // 139
+    0x08, 0xFC, 0x17, 0x09, // 140
+    0x09, 0x13, 0x14, 0x08, // 141
+    0x09, 0x27, 0x0D, 0x06, // 142
+    0x09, 0x34, 0x14, 0x08, // 143
+    0x09, 0x48, 0x10, 0x07, // 144
+    0x09, 0x58, 0x14, 0x08, // 145
+    0x09, 0x6C, 0x14, 0x08, // 146
+    0x09, 0x80, 0x17, 0x09, // 147
+    0x09, 0x97, 0x07, 0x04, // 148
+    0x09, 0x9E, 0x17, 0x09, // 149
+    0x09, 0xB5, 0x0A, 0x05, // 150
+    0x09, 0xBF, 0x1D, 0x0B, // 151
+    0x09, 0xDC, 0x0D, 0x06, // 152
+    0xFF, 0xFF, 0x00, 0x10, // 153
+    0xFF, 0xFF, 0x00, 0x10, // 154
+    0xFF, 0xFF, 0x00, 0x10, // 155
+    0xFF, 0xFF, 0x00, 0x10, // 156
+    0xFF, 0xFF, 0x00, 0x10, // 157
+    0xFF, 0xFF, 0x00, 0x10, // 158
+    0xFF, 0xFF, 0x00, 0x10, // 159
+    0xFF, 0xFF, 0x00, 0x10, // 160
+    0x09, 0xE9, 0x09, 0x04, // 161
+    0x09, 0xF2, 0x17, 0x09, // 162
+    0x0A, 0x09, 0x17, 0x09, // 163
+    0x0A, 0x20, 0x14, 0x08, // 164
+    0x0A, 0x34, 0x1A, 0x0A, // 165
+    0x0A, 0x4E, 0x06, 0x03, // 166
+    0x0A, 0x54, 0x17, 0x09, // 167
+    0x0A, 0x6B, 0x07, 0x04, // 168
+    0x0A, 0x72, 0x23, 0x0D, // 169
+    0x0A, 0x95, 0x0E, 0x06, // 170
+    0x0A, 0xA3, 0x14, 0x08, // 171
+    0x0A, 0xB7, 0x17, 0x09, // 172
+    0x0A, 0xCE, 0x0B, 0x05, // 173
+    0x0A, 0xD9, 0x23, 0x0D, // 174
+    0x0A, 0xFC, 0x19, 0x0A, // 175
+    0x0B, 0x15, 0x0D, 0x06, // 176
+    0x0B, 0x22, 0x17, 0x09, // 177
+    0x0B, 0x39, 0x0E, 0x06, // 178
+    0x0B, 0x47, 0x0D, 0x06, // 179
+    0x0B, 0x54, 0x0A, 0x05, // 180
+    0x0B, 0x5E, 0x17, 0x09, // 181
+    0x0B, 0x75, 0x19, 0x0A, // 182
+    0x0B, 0x8E, 0x08, 0x04, // 183
+    0x0B, 0x96, 0x0C, 0x05, // 184
+    0x0B, 0xA2, 0x0B, 0x05, // 185
+    0x0B, 0xAD, 0x0D, 0x06, // 186
+    0x0B, 0xBA, 0x17, 0x09, // 187
+    0x0B, 0xD1, 0x26, 0x0E, // 188
+    0x0B, 0xF7, 0x26, 0x0E, // 189
+    0x0C, 0x1D, 0x26, 0x0E, // 190
+    0x0C, 0x43, 0x1A, 0x0A, // 191
+    0x0C, 0x5D, 0x1D, 0x0B, // 192
+    0x0C, 0x7A, 0x1D, 0x0B, // 193
+    0x0C, 0x97, 0x1D, 0x0B, // 194
+    0x0C, 0xB4, 0x1D, 0x0B, // 195
+    0x0C, 0xD1, 0x1D, 0x0B, // 196
+    0x0C, 0xEE, 0x1D, 0x0B, // 197
+    0x0D, 0x0B, 0x2C, 0x10, // 198
+    0x0D, 0x37, 0x20, 0x0C, // 199
+    0x0D, 0x57, 0x1D, 0x0B, // 200
+    0x0D, 0x74, 0x1D, 0x0B, // 201
+    0x0D, 0x91, 0x1D, 0x0B, // 202
+    0x0D, 0xAE, 0x1D, 0x0B, // 203
+    0x0D, 0xCB, 0x05, 0x03, // 204
+    0x0D, 0xD0, 0x07, 0x04, // 205
+    0x0D, 0xD7, 0x0A, 0x05, // 206
+    0x0D, 0xE1, 0x07, 0x04, // 207
+    0x0D, 0xE8, 0x20, 0x0C, // 208
+    0x0E, 0x08, 0x1D, 0x0B, // 209
+    0x0E, 0x25, 0x20, 0x0C, // 210
+    0x0E, 0x45, 0x20, 0x0C, // 211
+    0x0E, 0x65, 0x20, 0x0C, // 212
+    0x0E, 0x85, 0x20, 0x0C, // 213
+    0x0E, 0xA5, 0x20, 0x0C, // 214
+    0x0E, 0xC5, 0x17, 0x09, // 215
+    0x0E, 0xDC, 0x20, 0x0C, // 216
+    0x0E, 0xFC, 0x1D, 0x0B, // 217
+    0x0F, 0x19, 0x1D, 0x0B, // 218
+    0x0F, 0x36, 0x1D, 0x0B, // 219
+    0x0F, 0x53, 0x1D, 0x0B, // 220
+    0x0F, 0x70, 0x19, 0x0A, // 221
+    0x0F, 0x89, 0x1D, 0x0B, // 222
+    0x0F, 0xA6, 0x17, 0x09, // 223
+    0x0F, 0xBD, 0x17, 0x09, // 224
+    0x0F, 0xD4, 0x17, 0x09, // 225
+    0x0F, 0xEB, 0x17, 0x09, // 226
+    0x10, 0x02, 0x17, 0x09, // 227
+    0x10, 0x19, 0x17, 0x09, // 228
+    0x10, 0x30, 0x17, 0x09, // 229
+    0x10, 0x47, 0x29, 0x0F, // 230
+    0x10, 0x70, 0x14, 0x08, // 231
+    0x10, 0x84, 0x17, 0x09, // 232
+    0x10, 0x9B, 0x17, 0x09, // 233
+    0x10, 0xB2, 0x17, 0x09, // 234
+    0x10, 0xC9, 0x17, 0x09, // 235
+    0x10, 0xE0, 0x05, 0x03, // 236
+    0x10, 0xE5, 0x07, 0x04, // 237
+    0x10, 0xEC, 0x0A, 0x05, // 238
+    0x10, 0xF6, 0x07, 0x04, // 239
+    0x10, 0xFD, 0x17, 0x09, // 240
+    0x11, 0x14, 0x14, 0x08, // 241
+    0x11, 0x28, 0x17, 0x09, // 242
+    0x11, 0x3F, 0x17, 0x09, // 243
+    0x11, 0x56, 0x17, 0x09, // 244
+    0x11, 0x6D, 0x17, 0x09, // 245
+    0x11, 0x84, 0x17, 0x09, // 246
+    0x11, 0x9B, 0x17, 0x09, // 247
+    0x11, 0xB2, 0x17, 0x09, // 248
+    0x11, 0xC9, 0x14, 0x08, // 249
+    0x11, 0xDD, 0x14, 0x08, // 250
+    0x11, 0xF1, 0x14, 0x08, // 251
+    0x12, 0x05, 0x14, 0x08, // 252
+    0x12, 0x19, 0x13, 0x08, // 253
+    0x12, 0x2C, 0x17, 0x09, // 254
+    0x12, 0x43, 0x13, 0x08, // 255
+    // Font Data:
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x5F,                               // 33
+    0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, // 34
+    0x80, 0x08, 0x00, 0x80, 0x78, 0x00, 0xC0, 0x0F, 0x00, 0xB8, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x78, 0x00, 0xC0, 0x0F, 0x00,
+    0xB8, 0x08, 0x00, 0x80, 0x08, // 35
+    0x00, 0x00, 0x00, 0xE0, 0x10, 0x00, 0x10, 0x21, 0x00, 0x08, 0x41, 0x00, 0xFC, 0xFF, 0x00, 0x08, 0x42, 0x00, 0x10, 0x22, 0x00,
+    0x20, 0x1C, // 36
+    0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x61, 0x00, 0xF0, 0x18, 0x00, 0x00, 0x06, 0x00,
+    0xC0, 0x01, 0x00, 0x30, 0x3C, 0x00, 0x08, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3C, // 37
+    0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x22, 0x00, 0x88, 0x41, 0x00, 0x08, 0x43, 0x00, 0x88, 0x44, 0x00, 0x70, 0x28, 0x00,
+    0x00, 0x10, 0x00, 0x00, 0x28, 0x00, 0x00, 0x44,                               // 38
+    0x00, 0x00, 0x00, 0x78,                                                       // 39
+    0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x70, 0xC0, 0x01, 0x08, 0x00, 0x02,       // 40
+    0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x70, 0xC0, 0x01, 0x80, 0x3F,             // 41
+    0x10, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x38, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x10, // 42
+    0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00,
+    0x00, 0x02,                                                       // 43
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01,             // 44
+    0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, // 45
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,                   // 46
+    0x00, 0x60, 0x00, 0x00, 0x1E, 0x00, 0xE0, 0x01, 0x00, 0x18,       // 47
+    0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00,
+    0xE0, 0x1F,                                                                                           // 48
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF8, 0x7F, // 49
+    0x00, 0x00, 0x00, 0x20, 0x40, 0x00, 0x10, 0x60, 0x00, 0x08, 0x50, 0x00, 0x08, 0x48, 0x00, 0x08, 0x44, 0x00, 0x10, 0x43, 0x00,
+    0xE0, 0x40, // 50
+    0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x88, 0x41, 0x00, 0xF0, 0x22, 0x00,
+    0x00, 0x1C, // 51
+    0x00, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x09, 0x00, 0xC0, 0x08, 0x00, 0x20, 0x08, 0x00, 0x10, 0x08, 0x00, 0xF8, 0x7F, 0x00,
+    0x00, 0x08, // 52
+    0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0xB8, 0x20, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x08, 0x21, 0x00,
+    0x08, 0x1E, // 53
+    0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x10, 0x21, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x10, 0x21, 0x00,
+    0x20, 0x1E, // 54
+    0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x78, 0x00, 0x08, 0x07, 0x00, 0xC8, 0x00, 0x00, 0x28, 0x00, 0x00,
+    0x18, // 55
+    0x00, 0x00, 0x00, 0x60, 0x1C, 0x00, 0x90, 0x22, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x90, 0x22, 0x00,
+    0x60, 0x1C, // 56
+    0x00, 0x00, 0x00, 0xE0, 0x11, 0x00, 0x10, 0x22, 0x00, 0x08, 0x44, 0x00, 0x08, 0x44, 0x00, 0x08, 0x44, 0x00, 0x10, 0x22, 0x00,
+    0xE0, 0x1F,                         // 57
+    0x00, 0x00, 0x00, 0x40, 0x40,       // 58
+    0x00, 0x00, 0x00, 0x40, 0xC0, 0x01, // 59
+    0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00,
+    0x40, 0x10, // 60
+    0x00, 0x00, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00,
+    0x80, 0x08, // 61
+    0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00,
+    0x00, 0x02, // 62
+    0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x5C, 0x00, 0x08, 0x02, 0x00, 0x10, 0x01, 0x00,
+    0xE0, // 63
+    0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0xC0, 0x40, 0x00, 0x20, 0x80, 0x00, 0x10, 0x1E, 0x01, 0x10, 0x21, 0x01, 0x88, 0x40, 0x02,
+    0x48, 0x40, 0x02, 0x48, 0x40, 0x02, 0x48, 0x20, 0x02, 0x88, 0x7C, 0x02, 0xC8, 0x43, 0x02, 0x10, 0x40, 0x02, 0x10, 0x20, 0x01,
+    0x60, 0x10, 0x01, 0x80, 0x8F, // 64
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x70, 0x04, 0x00, 0x08, 0x04, 0x00, 0x70, 0x04, 0x00,
+    0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 65
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00,
+    0x08, 0x41, 0x00, 0x90, 0x22, 0x00, 0x60, 0x1C, // 66
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00,
+    0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 67
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00,
+    0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 68
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00,
+    0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 69
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00,
+    0x08, 0x02, 0x00, 0x08, // 70
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x42, 0x00,
+    0x08, 0x42, 0x00, 0x10, 0x22, 0x00, 0x20, 0x12, 0x00, 0x00, 0x0E, // 71
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00,
+    0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xF8, 0x7F,                                                                         // 72
+    0x00, 0x00, 0x00, 0xF8, 0x7F,                                                                                           // 73
+    0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x3F, // 74
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x40, 0x04, 0x00,
+    0x20, 0x18, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, // 75
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00,
+    0x00, 0x40, // 76
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00,
+    0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, 0x7F, // 77
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x10, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00,
+    0x00, 0x18, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x7F, // 78
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00,
+    0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 79
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00,
+    0x08, 0x02, 0x00, 0x10, 0x01, 0x00, 0xE0, // 80
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x50, 0x00,
+    0x08, 0x50, 0x00, 0x10, 0x20, 0x00, 0x20, 0x70, 0x00, 0xC0, 0x4F, // 81
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x06, 0x00,
+    0x08, 0x1A, 0x00, 0x10, 0x21, 0x00, 0xE0, 0x40, // 82
+    0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x90, 0x20, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x42, 0x00,
+    0x08, 0x42, 0x00, 0x10, 0x22, 0x00, 0x20, 0x1C, // 83
+    0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00,
+    0x08, 0x00, 0x00, 0x08, // 84
+    0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00,
+    0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 85
+    0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0x00, 0x18, 0x00,
+    0x00, 0x07, 0x00, 0xE0, 0x00, 0x00, 0x18, // 86
+    0x18, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x03, 0x00, 0x70, 0x00, 0x00,
+    0x08, 0x00, 0x00, 0x70, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1E, 0x00, 0xE0, 0x01, 0x00,
+    0x18, // 87
+    0x00, 0x40, 0x00, 0x08, 0x20, 0x00, 0x10, 0x10, 0x00, 0x60, 0x0C, 0x00, 0x80, 0x02, 0x00, 0x00, 0x01, 0x00, 0x80, 0x02, 0x00,
+    0x60, 0x0C, 0x00, 0x10, 0x10, 0x00, 0x08, 0x20, 0x00, 0x00, 0x40, // 88
+    0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x7E, 0x00, 0x80, 0x01, 0x00, 0x40, 0x00, 0x00,
+    0x30, 0x00, 0x00, 0x08, // 89
+    0x00, 0x40, 0x00, 0x08, 0x60, 0x00, 0x08, 0x58, 0x00, 0x08, 0x44, 0x00, 0x08, 0x43, 0x00, 0x88, 0x40, 0x00, 0x68, 0x40, 0x00,
+    0x18, 0x40, 0x00, 0x08, 0x40,                                                                                           // 90
+    0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x08, 0x00, 0x02, 0x08, 0x00, 0x02,                                                 // 91
+    0x18, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x60,                                                       // 92
+    0x08, 0x00, 0x02, 0x08, 0x00, 0x02, 0xF8, 0xFF, 0x03,                                                                   // 93
+    0x00, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00, 0x00, 0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x01, // 94
+    0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02,
+    0x00, 0x00, 0x02, 0x00, 0x00, 0x02,       // 95
+    0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, // 96
+    0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x42, 0x00, 0x40, 0x22, 0x00,
+    0x80, 0x7F, // 97
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00,
+    0x00, 0x1F,                                                                                                             // 98
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, // 99
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00,
+    0xF8, 0x7F, // 100
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00,
+    0x00, 0x17,                                                 // 101
+    0x40, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x48, 0x00, 0x00, 0x48, // 102
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x80, 0x20, 0x02, 0x40, 0x40, 0x02, 0x40, 0x40, 0x02, 0x40, 0x40, 0x02, 0x80, 0x20, 0x01,
+    0xC0, 0xFF,                                                                                                             // 103
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, // 104
+    0x00, 0x00, 0x00, 0xC8, 0x7F,                                                                                           // 105
+    0x00, 0x00, 0x02, 0xC8, 0xFF, 0x01,                                                                                     // 106
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x19, 0x00, 0x80, 0x20, 0x00,
+    0x40, 0x40,                   // 107
+    0x00, 0x00, 0x00, 0xF8, 0x7F, // 108
+    0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, 0x00,
+    0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F,                                     // 109
+    0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, // 110
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00,
+    0x00, 0x1F, // 111
+    0x00, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00,
+    0x00, 0x1F, // 112
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00,
+    0xC0, 0xFF, 0x03,                                                                                                       // 113
+    0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40,                                           // 114
+    0x00, 0x00, 0x00, 0x80, 0x23, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x38, // 115
+    0x40, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40,                                                       // 116
+    0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xC0, 0x7F, // 117
+    0xC0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0,       // 118
+    0xC0, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x03, 0x00,
+    0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1F, 0x00, 0xC0,                                                             // 119
+    0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, // 120
+    0xC0, 0x01, 0x00, 0x00, 0x06, 0x02, 0x00, 0x38, 0x02, 0x00, 0xE0, 0x01, 0x00, 0x38, 0x00, 0x00, 0x07, 0x00, 0xC0,       // 121
+    0x40, 0x40, 0x00, 0x40, 0x60, 0x00, 0x40, 0x58, 0x00, 0x40, 0x44, 0x00, 0x40, 0x43, 0x00, 0xC0, 0x40, 0x00, 0x40, 0x40, // 122
+    0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xF0, 0xFB, 0x01, 0x08, 0x00, 0x02, 0x08, 0x00, 0x02,                               // 123
+    0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03,                                                                                     // 124
+    0x08, 0x00, 0x02, 0x08, 0x00, 0x02, 0xF0, 0xFB, 0x01, 0x00, 0x04, 0x00, 0x00, 0x04,                                     // 125
+    0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00,
+    0x00, 0x01, // 126
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x09, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x0A, 0x40, 0x00,
+    0x09, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 129
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x09, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x0A, 0x40, 0x00,
+    0x09, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 130
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x09, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x0A, 0x41, 0x00,
+    0x09, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 131
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x10, 0x00, 0x00, 0x60, 0x00, 0x00, 0x81, 0x00, 0x00, 0x02, 0x03, 0x00, 0x02, 0x04, 0x00,
+    0x01, 0x18, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x7F, // 132
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x09, 0x02, 0x00, 0x0A, 0x02, 0x00, 0x0A, 0x06, 0x00,
+    0x09, 0x1A, 0x00, 0x10, 0x21, 0x00, 0xE0, 0x40, // 133
+    0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x90, 0x20, 0x00, 0x08, 0x41, 0x00, 0x09, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x0A, 0x42, 0x00,
+    0x09, 0x42, 0x00, 0x10, 0x22, 0x00, 0x20, 0x1C, // 134
+    0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x00, 0xFA, 0x7F, 0x00, 0x0A, 0x00, 0x00, 0x09, 0x00, 0x00,
+    0x08, 0x00, 0x00, 0x08, // 135
+    0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x06, 0x40, 0x00, 0x09, 0x40, 0x00, 0x09, 0x40, 0x00,
+    0x06, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 136
+    0x00, 0x40, 0x00, 0x08, 0x60, 0x00, 0x08, 0x58, 0x00, 0x09, 0x44, 0x00, 0x0A, 0x43, 0x00, 0x8A, 0x40, 0x00, 0x69, 0x40, 0x00,
+    0x18, 0x40, 0x00, 0x08, 0x40,                                                                                           // 137
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x48, 0x40, 0x00, 0x50, 0x40, 0x00, 0x50, 0x40, 0x00, 0x88, 0x20, // 138
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00,
+    0xF8, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x38, // 139
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x48, 0x44, 0x00, 0x50, 0x44, 0x00, 0x50, 0x44, 0x00, 0x88, 0x24, 0x00,
+    0x00, 0x17,                                                                                                             // 140
+    0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x88, 0x00, 0x00, 0x50, 0x00, 0x00, 0x50, 0x00, 0x00, 0x48, 0x00, 0x00, 0x80, 0x7F, // 141
+    0x00, 0x00, 0x00, 0xC8, 0x7F, 0x00, 0x90, 0x00, 0x00, 0x50, 0x00, 0x00, 0x48,                                           // 142
+    0x00, 0x00, 0x00, 0x80, 0x23, 0x00, 0x48, 0x44, 0x00, 0x50, 0x44, 0x00, 0x50, 0x44, 0x00, 0x48, 0x44, 0x00, 0x80, 0x38, // 143
+    0x40, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x70,                         // 144
+    0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x0C, 0x40, 0x00, 0x12, 0x40, 0x00, 0x12, 0x40, 0x00, 0x0C, 0x20, 0x00, 0xC0, 0x7F, // 145
+    0x40, 0x40, 0x00, 0x40, 0x60, 0x00, 0x48, 0x58, 0x00, 0x50, 0x44, 0x00, 0x50, 0x43, 0x00, 0xC8, 0x40, 0x00, 0x40, 0x40, // 146
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x02, 0x40, 0x00, 0x01, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00,
+    0x00, 0x40,                               // 147
+    0x00, 0x00, 0x00, 0xFA, 0x7F, 0x00, 0x01, // 148
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x38, 0x40, 0x00,
+    0x00, 0x40,                                                 // 149
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x38, // 150
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x0A, 0x02, 0x00, 0x09, 0x06, 0x00,
+    0x08, 0x1A, 0x00, 0x10, 0x21, 0x00, 0xE0, 0x40,                               // 151
+    0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x50, 0x00, 0x00, 0x48, // 152
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xFF, 0x03,                         // 161
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x03, 0x40, 0xF0, 0x00, 0x40, 0x4E, 0x00, 0xC0, 0x41, 0x00, 0xB8, 0x20, 0x00,
+    0x00, 0x11, // 162
+    0x00, 0x41, 0x00, 0xE0, 0x31, 0x00, 0x10, 0x2F, 0x00, 0x08, 0x21, 0x00, 0x08, 0x21, 0x00, 0x08, 0x40, 0x00, 0x10, 0x40, 0x00,
+    0x20, 0x20,                                                                                                             // 163
+    0x00, 0x00, 0x00, 0x40, 0x0B, 0x00, 0x80, 0x04, 0x00, 0x40, 0x08, 0x00, 0x40, 0x08, 0x00, 0x80, 0x04, 0x00, 0x40, 0x0B, // 164
+    0x08, 0x0A, 0x00, 0x10, 0x0A, 0x00, 0x60, 0x0A, 0x00, 0x80, 0x0B, 0x00, 0x00, 0x7E, 0x00, 0x80, 0x0B, 0x00, 0x60, 0x0A, 0x00,
+    0x10, 0x0A, 0x00, 0x08, 0x0A,       // 165
+    0x00, 0x00, 0x00, 0xF8, 0xF1, 0x03, // 166
+    0x00, 0x86, 0x00, 0x70, 0x09, 0x01, 0xC8, 0x10, 0x02, 0x88, 0x10, 0x02, 0x08, 0x21, 0x02, 0x08, 0x61, 0x02, 0x30, 0xD2, 0x01,
+    0x00, 0x0C,                               // 167
+    0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, // 168
+    0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0xC8, 0x47, 0x00, 0x28, 0x48, 0x00, 0x28, 0x48, 0x00, 0x28, 0x48, 0x00,
+    0x28, 0x48, 0x00, 0x48, 0x44, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F,                                     // 169
+    0xD0, 0x00, 0x00, 0x48, 0x01, 0x00, 0x28, 0x01, 0x00, 0x28, 0x01, 0x00, 0xF0, 0x01,                                     // 170
+    0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, // 171
+    0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00,
+    0x80, 0x0F,                                                       // 172
+    0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, // 173
+    0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0xE8, 0x4F, 0x00, 0x28, 0x41, 0x00, 0x28, 0x41, 0x00, 0x28, 0x43, 0x00,
+    0x28, 0x45, 0x00, 0xC8, 0x48, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 174
+    0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00,
+    0x04, 0x00, 0x00, 0x04,                                                       // 175
+    0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x30, // 176
+    0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0xE0, 0x4F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00,
+    0x00, 0x41,                                                                         // 177
+    0x10, 0x01, 0x00, 0x88, 0x01, 0x00, 0x48, 0x01, 0x00, 0x48, 0x01, 0x00, 0x30, 0x01, // 178
+    0x90, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x28, 0x01, 0x00, 0xD8,       // 179
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08,                         // 180
+    0x00, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00,
+    0xC0, 0x7F, // 181
+    0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x01, 0x00, 0xF8, 0x01, 0x00, 0xF8, 0xFF, 0x03, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00,
+    0xF8, 0xFF, 0x03, 0x08,                                                       // 182
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,                               // 183
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x02, 0x00, 0x00, 0x03,       // 184
+    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x01,             // 185
+    0xF0, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0xF0, // 186
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00,
+    0x00, 0x04, // 187
+    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x40, 0x00, 0xF8, 0x21, 0x00, 0x00, 0x10, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x02, 0x00,
+    0x80, 0x01, 0x00, 0x40, 0x30, 0x00, 0x30, 0x28, 0x00, 0x08, 0x24, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x20, // 188
+    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x40, 0x00, 0xF8, 0x31, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00,
+    0x80, 0x00, 0x00, 0x60, 0x44, 0x00, 0x10, 0x62, 0x00, 0x08, 0x52, 0x00, 0x00, 0x52, 0x00, 0x00, 0x4C, // 189
+    0x90, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x41, 0x00, 0x28, 0x21, 0x00, 0xD8, 0x18, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00,
+    0x80, 0x00, 0x00, 0x40, 0x30, 0x00, 0x30, 0x28, 0x00, 0x08, 0x24, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x20, // 190
+    0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x10, 0x01, 0x00, 0x08, 0x02, 0x40, 0x07, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02,
+    0x00, 0x00, 0x01, 0x00, 0xC0, // 191
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x71, 0x04, 0x00, 0x0A, 0x04, 0x00, 0x70, 0x04, 0x00,
+    0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 192
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x70, 0x04, 0x00, 0x0A, 0x04, 0x00, 0x71, 0x04, 0x00,
+    0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 193
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x72, 0x04, 0x00, 0x09, 0x04, 0x00, 0x71, 0x04, 0x00,
+    0x82, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 194
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x72, 0x04, 0x00, 0x09, 0x04, 0x00, 0x72, 0x04, 0x00,
+    0x81, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 195
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x72, 0x04, 0x00, 0x08, 0x04, 0x00, 0x72, 0x04, 0x00,
+    0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 196
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x7E, 0x04, 0x00, 0x0A, 0x04, 0x00, 0x7E, 0x04, 0x00,
+    0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 197
+    0x00, 0x60, 0x00, 0x00, 0x18, 0x00, 0x00, 0x06, 0x00, 0x80, 0x05, 0x00, 0x60, 0x04, 0x00, 0x18, 0x04, 0x00, 0x08, 0x04, 0x00,
+    0x08, 0x04, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00,
+    0x08, 0x41, // 198
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x02, 0x08, 0xC0, 0x02,
+    0x08, 0x40, 0x03, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 199
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x09, 0x41, 0x00, 0x0A, 0x41, 0x00,
+    0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 200
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x09, 0x41, 0x00,
+    0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 201
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x09, 0x41, 0x00, 0x09, 0x41, 0x00,
+    0x0A, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 202
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00,
+    0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40,             // 203
+    0x01, 0x00, 0x00, 0xFA, 0x7F,                               // 204
+    0x00, 0x00, 0x00, 0xFA, 0x7F, 0x00, 0x01,                   // 205
+    0x02, 0x00, 0x00, 0xF9, 0x7F, 0x00, 0x01, 0x00, 0x00, 0x02, // 206
+    0x02, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x02,                   // 207
+    0x00, 0x02, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x42, 0x00, 0x08, 0x42, 0x00, 0x08, 0x42, 0x00, 0x08, 0x42, 0x00, 0x08, 0x40, 0x00,
+    0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 208
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x10, 0x00, 0x00, 0x60, 0x00, 0x00, 0x82, 0x00, 0x00, 0x01, 0x03, 0x00, 0x02, 0x04, 0x00,
+    0x01, 0x18, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x7F, // 209
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x09, 0x40, 0x00, 0x0A, 0x40, 0x00,
+    0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 210
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x09, 0x40, 0x00,
+    0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 211
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x0A, 0x40, 0x00, 0x09, 0x40, 0x00, 0x09, 0x40, 0x00,
+    0x0A, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 212
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x0A, 0x40, 0x00, 0x09, 0x40, 0x00, 0x0A, 0x40, 0x00,
+    0x09, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 213
+    0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x08, 0x40, 0x00,
+    0x0A, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 214
+    0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x80, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x07, 0x00, 0x00, 0x05, 0x00, 0x80, 0x08, 0x00,
+    0x40, 0x10, // 215
+    0x00, 0x00, 0x00, 0xC0, 0x4F, 0x00, 0x20, 0x30, 0x00, 0x10, 0x30, 0x00, 0x08, 0x4C, 0x00, 0x08, 0x42, 0x00, 0x08, 0x41, 0x00,
+    0xC8, 0x40, 0x00, 0x30, 0x20, 0x00, 0x30, 0x10, 0x00, 0xC8, 0x0F, // 216
+    0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x01, 0x40, 0x00, 0x02, 0x40, 0x00, 0x00, 0x40, 0x00,
+    0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 217
+    0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x02, 0x40, 0x00, 0x01, 0x40, 0x00,
+    0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 218
+    0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x02, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00,
+    0x02, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 219
+    0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x02, 0x40, 0x00, 0x00, 0x40, 0x00, 0x02, 0x40, 0x00,
+    0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 220
+    0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x01, 0x00, 0x02, 0x7E, 0x00, 0x81, 0x01, 0x00, 0x40, 0x00, 0x00,
+    0x30, 0x00, 0x00, 0x08, // 221
+    0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00,
+    0x20, 0x10, 0x00, 0x40, 0x08, 0x00, 0x80, 0x07, // 222
+    0x00, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x10, 0x00, 0x00, 0x08, 0x20, 0x00, 0x88, 0x43, 0x00, 0x70, 0x42, 0x00, 0x00, 0x44, 0x00,
+    0x00, 0x38, // 223
+    0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x48, 0x44, 0x00, 0x50, 0x42, 0x00, 0x40, 0x22, 0x00,
+    0x80, 0x7F, // 224
+    0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x50, 0x44, 0x00, 0x48, 0x42, 0x00, 0x40, 0x22, 0x00,
+    0x80, 0x7F, // 225
+    0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x50, 0x44, 0x00, 0x48, 0x44, 0x00, 0x48, 0x42, 0x00, 0x50, 0x22, 0x00,
+    0x80, 0x7F, // 226
+    0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x50, 0x44, 0x00, 0x48, 0x44, 0x00, 0x50, 0x42, 0x00, 0x48, 0x22, 0x00,
+    0x80, 0x7F, // 227
+    0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x50, 0x44, 0x00, 0x40, 0x44, 0x00, 0x50, 0x42, 0x00, 0x40, 0x22, 0x00,
+    0x80, 0x7F, // 228
+    0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x5C, 0x44, 0x00, 0x54, 0x44, 0x00, 0x5C, 0x42, 0x00, 0x40, 0x22, 0x00,
+    0x80, 0x7F, // 229
+    0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x42, 0x00, 0x40, 0x22, 0x00,
+    0x80, 0x3F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00, 0x00, 0x17, // 230
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x02, 0x40, 0xC0, 0x02, 0x40, 0x40, 0x03, 0x80, 0x20, // 231
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x48, 0x44, 0x00, 0x50, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00,
+    0x00, 0x17, // 232
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x50, 0x44, 0x00, 0x48, 0x44, 0x00, 0x80, 0x24, 0x00,
+    0x00, 0x17, // 233
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x50, 0x44, 0x00, 0x48, 0x44, 0x00, 0x48, 0x44, 0x00, 0x90, 0x24, 0x00,
+    0x00, 0x17, // 234
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x50, 0x44, 0x00, 0x40, 0x44, 0x00, 0x50, 0x44, 0x00, 0x80, 0x24, 0x00,
+    0x00, 0x17,                                                 // 235
+    0x08, 0x00, 0x00, 0xD0, 0x7F,                               // 236
+    0x00, 0x00, 0x00, 0xD0, 0x7F, 0x00, 0x08,                   // 237
+    0x10, 0x00, 0x00, 0xC8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x10, // 238
+    0x10, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x10,                   // 239
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0xA0, 0x20, 0x00, 0x68, 0x40, 0x00, 0x58, 0x40, 0x00, 0x70, 0x40, 0x00, 0xE8, 0x20, 0x00,
+    0x00, 0x1F,                                                                                                             // 240
+    0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x90, 0x00, 0x00, 0x48, 0x00, 0x00, 0x50, 0x00, 0x00, 0x48, 0x00, 0x00, 0x80, 0x7F, // 241
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x48, 0x40, 0x00, 0x50, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00,
+    0x00, 0x1F, // 242
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x50, 0x40, 0x00, 0x48, 0x40, 0x00, 0x80, 0x20, 0x00,
+    0x00, 0x1F, // 243
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x50, 0x40, 0x00, 0x48, 0x40, 0x00, 0x48, 0x40, 0x00, 0x90, 0x20, 0x00,
+    0x00, 0x1F, // 244
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x50, 0x40, 0x00, 0x48, 0x40, 0x00, 0x50, 0x40, 0x00, 0x88, 0x20, 0x00,
+    0x00, 0x1F, // 245
+    0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x50, 0x40, 0x00, 0x40, 0x40, 0x00, 0x50, 0x40, 0x00, 0x80, 0x20, 0x00,
+    0x00, 0x1F, // 246
+    0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x80, 0x0A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00,
+    0x00, 0x02, // 247
+    0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x80, 0x30, 0x00, 0x40, 0x48, 0x00, 0x40, 0x44, 0x00, 0x40, 0x42, 0x00, 0x80, 0x21, 0x00,
+    0x40, 0x1F,                                                                                                             // 248
+    0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x40, 0x00, 0x00, 0x20, 0x00, 0xC0, 0x7F, // 249
+    0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x10, 0x40, 0x00, 0x08, 0x20, 0x00, 0xC0, 0x7F, // 250
+    0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x10, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0xC0, 0x7F, // 251
+    0x00, 0x00, 0x00, 0xD0, 0x3F, 0x00, 0x00, 0x40, 0x00, 0x10, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xC0, 0x7F, // 252
+    0xC0, 0x01, 0x00, 0x00, 0x06, 0x02, 0x00, 0x38, 0x02, 0x10, 0xE0, 0x01, 0x08, 0x38, 0x00, 0x00, 0x07, 0x00, 0xC0,       // 253
+    0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00,
+    0x00, 0x1F,                                                                                                       // 254
+    0xC0, 0x01, 0x00, 0x00, 0x06, 0x02, 0x10, 0x38, 0x02, 0x00, 0xE0, 0x01, 0x10, 0x38, 0x00, 0x00, 0x07, 0x00, 0xC0, // 255
+};
+
+const uint8_t ArialMT_Plain_24_CS[] PROGMEM = {
+    0x18, // Width: 24
+    0x1C, // Height: 28
+    0x20, // First char: 32
+    0xE0, // Number of chars: 224
+    // Jump Table:
+    0xFF, 0xFF, 0x00, 0x18, // 32
+    0x00, 0x00, 0x13, 0x06, // 33
+    0x00, 0x13, 0x1A, 0x08, // 34
+    0x00, 0x2D, 0x33, 0x0E, // 35
+    0x00, 0x60, 0x2F, 0x0D, // 36
+    0x00, 0x8F, 0x4F, 0x15, // 37
+    0x00, 0xDE, 0x3B, 0x10, // 38
+    0x01, 0x19, 0x0A, 0x04, // 39
+    0x01, 0x23, 0x1C, 0x08, // 40
+    0x01, 0x3F, 0x1B, 0x08, // 41
+    0x01, 0x5A, 0x21, 0x0A, // 42
+    0x01, 0x7B, 0x32, 0x0E, // 43
+    0x01, 0xAD, 0x10, 0x05, // 44
+    0x01, 0xBD, 0x1B, 0x08, // 45
+    0x01, 0xD8, 0x0F, 0x05, // 46
+    0x01, 0xE7, 0x19, 0x08, // 47
+    0x02, 0x00, 0x2F, 0x0D, // 48
+    0x02, 0x2F, 0x23, 0x0A, // 49
+    0x02, 0x52, 0x2F, 0x0D, // 50
+    0x02, 0x81, 0x2F, 0x0D, // 51
+    0x02, 0xB0, 0x2F, 0x0D, // 52
+    0x02, 0xDF, 0x2F, 0x0D, // 53
+    0x03, 0x0E, 0x2F, 0x0D, // 54
+    0x03, 0x3D, 0x2D, 0x0D, // 55
+    0x03, 0x6A, 0x2F, 0x0D, // 56
+    0x03, 0x99, 0x2F, 0x0D, // 57
+    0x03, 0xC8, 0x0F, 0x05, // 58
+    0x03, 0xD7, 0x10, 0x05, // 59
+    0x03, 0xE7, 0x2F, 0x0D, // 60
+    0x04, 0x16, 0x2F, 0x0D, // 61
+    0x04, 0x45, 0x2E, 0x0D, // 62
+    0x04, 0x73, 0x2E, 0x0D, // 63
+    0x04, 0xA1, 0x5B, 0x18, // 64
+    0x04, 0xFC, 0x3B, 0x10, // 65
+    0x05, 0x37, 0x3B, 0x10, // 66
+    0x05, 0x72, 0x3F, 0x11, // 67
+    0x05, 0xB1, 0x3F, 0x11, // 68
+    0x05, 0xF0, 0x3B, 0x10, // 69
+    0x06, 0x2B, 0x35, 0x0F, // 70
+    0x06, 0x60, 0x43, 0x12, // 71
+    0x06, 0xA3, 0x3B, 0x10, // 72
+    0x06, 0xDE, 0x0F, 0x05, // 73
+    0x06, 0xED, 0x27, 0x0B, // 74
+    0x07, 0x14, 0x3F, 0x11, // 75
+    0x07, 0x53, 0x2F, 0x0D, // 76
+    0x07, 0x82, 0x43, 0x12, // 77
+    0x07, 0xC5, 0x3B, 0x10, // 78
+    0x08, 0x00, 0x47, 0x13, // 79
+    0x08, 0x47, 0x3A, 0x10, // 80
+    0x08, 0x81, 0x47, 0x13, // 81
+    0x08, 0xC8, 0x3F, 0x11, // 82
+    0x09, 0x07, 0x3B, 0x10, // 83
+    0x09, 0x42, 0x35, 0x0F, // 84
+    0x09, 0x77, 0x3B, 0x10, // 85
+    0x09, 0xB2, 0x39, 0x10, // 86
+    0x09, 0xEB, 0x59, 0x18, // 87
+    0x0A, 0x44, 0x3B, 0x10, // 88
+    0x0A, 0x7F, 0x3D, 0x11, // 89
+    0x0A, 0xBC, 0x37, 0x0F, // 90
+    0x0A, 0xF3, 0x14, 0x06, // 91
+    0x0B, 0x07, 0x1B, 0x08, // 92
+    0x0B, 0x22, 0x18, 0x07, // 93
+    0x0B, 0x3A, 0x2A, 0x0C, // 94
+    0x0B, 0x64, 0x34, 0x0E, // 95
+    0x0B, 0x98, 0x11, 0x06, // 96
+    0x0B, 0xA9, 0x2F, 0x0D, // 97
+    0x0B, 0xD8, 0x33, 0x0E, // 98
+    0x0C, 0x0B, 0x2B, 0x0C, // 99
+    0x0C, 0x36, 0x2F, 0x0D, // 100
+    0x0C, 0x65, 0x2F, 0x0D, // 101
+    0x0C, 0x94, 0x1A, 0x08, // 102
+    0x0C, 0xAE, 0x2F, 0x0D, // 103
+    0x0C, 0xDD, 0x2F, 0x0D, // 104
+    0x0D, 0x0C, 0x0F, 0x05, // 105
+    0x0D, 0x1B, 0x10, 0x05, // 106
+    0x0D, 0x2B, 0x2F, 0x0D, // 107
+    0x0D, 0x5A, 0x0F, 0x05, // 108
+    0x0D, 0x69, 0x47, 0x13, // 109
+    0x0D, 0xB0, 0x2F, 0x0D, // 110
+    0x0D, 0xDF, 0x2F, 0x0D, // 111
+    0x0E, 0x0E, 0x33, 0x0E, // 112
+    0x0E, 0x41, 0x30, 0x0D, // 113
+    0x0E, 0x71, 0x1E, 0x09, // 114
+    0x0E, 0x8F, 0x2B, 0x0C, // 115
+    0x0E, 0xBA, 0x1B, 0x08, // 116
+    0x0E, 0xD5, 0x2F, 0x0D, // 117
+    0x0F, 0x04, 0x2A, 0x0C, // 118
+    0x0F, 0x2E, 0x42, 0x12, // 119
+    0x0F, 0x70, 0x2B, 0x0C, // 120
+    0x0F, 0x9B, 0x2A, 0x0C, // 121
+    0x0F, 0xC5, 0x2B, 0x0C, // 122
+    0x0F, 0xF0, 0x1C, 0x08, // 123
+    0x10, 0x0C, 0x10, 0x05, // 124
+    0x10, 0x1C, 0x1B, 0x08, // 125
+    0x10, 0x37, 0x32, 0x0E, // 126
+    0xFF, 0xFF, 0x00, 0x18, // 127
+    0xFF, 0xFF, 0x00, 0x18, // 128
+    0x10, 0x69, 0x3F, 0x11, // 129
+    0x10, 0xA8, 0x3F, 0x11, // 130
+    0x10, 0xE7, 0x3B, 0x10, // 131
+    0x11, 0x22, 0x3B, 0x10, // 132
+    0x11, 0x5D, 0x3F, 0x11, // 133
+    0x11, 0x9C, 0x3B, 0x10, // 134
+    0x11, 0xD7, 0x35, 0x0F, // 135
+    0x12, 0x0C, 0x3B, 0x10, // 136
+    0x12, 0x47, 0x37, 0x0F, // 137
+    0x12, 0x7E, 0x2B, 0x0C, // 138
+    0x12, 0xA9, 0x3A, 0x10, // 139
+    0x12, 0xE3, 0x2F, 0x0D, // 140
+    0x13, 0x12, 0x2F, 0x0D, // 141
+    0x13, 0x41, 0x1E, 0x09, // 142
+    0x13, 0x5F, 0x2B, 0x0C, // 143
+    0x13, 0x8A, 0x26, 0x0B, // 144
+    0x13, 0xB0, 0x2F, 0x0D, // 145
+    0x13, 0xDF, 0x2B, 0x0C, // 146
+    0x14, 0x0A, 0x2F, 0x0D, // 147
+    0x14, 0x39, 0x15, 0x07, // 148
+    0x14, 0x4E, 0x2F, 0x0D, // 149
+    0x14, 0x7D, 0x1A, 0x08, // 150
+    0x14, 0x97, 0x3F, 0x11, // 151
+    0x14, 0xD6, 0x1E, 0x09, // 152
+    0xFF, 0xFF, 0x00, 0x18, // 153
+    0xFF, 0xFF, 0x00, 0x18, // 154
+    0xFF, 0xFF, 0x00, 0x18, // 155
+    0xFF, 0xFF, 0x00, 0x18, // 156
+    0xFF, 0xFF, 0x00, 0x18, // 157
+    0xFF, 0xFF, 0x00, 0x18, // 158
+    0xFF, 0xFF, 0x00, 0x18, // 159
+    0xFF, 0xFF, 0x00, 0x18, // 160
+    0x14, 0xF4, 0x14, 0x06, // 161
+    0x15, 0x08, 0x2B, 0x0C, // 162
+    0x15, 0x33, 0x2F, 0x0D, // 163
+    0x15, 0x62, 0x33, 0x0E, // 164
+    0x15, 0x95, 0x31, 0x0E, // 165
+    0x15, 0xC6, 0x10, 0x05, // 166
+    0x15, 0xD6, 0x2F, 0x0D, // 167
+    0x16, 0x05, 0x19, 0x08, // 168
+    0x16, 0x1E, 0x46, 0x13, // 169
+    0x16, 0x64, 0x1A, 0x08, // 170
+    0x16, 0x7E, 0x27, 0x0B, // 171
+    0x16, 0xA5, 0x2F, 0x0D, // 172
+    0x16, 0xD4, 0x1B, 0x08, // 173
+    0x16, 0xEF, 0x46, 0x13, // 174
+    0x17, 0x35, 0x31, 0x0E, // 175
+    0x17, 0x66, 0x1E, 0x09, // 176
+    0x17, 0x84, 0x33, 0x0E, // 177
+    0x17, 0xB7, 0x1A, 0x08, // 178
+    0x17, 0xD1, 0x1A, 0x08, // 179
+    0x17, 0xEB, 0x19, 0x08, // 180
+    0x18, 0x04, 0x2F, 0x0D, // 181
+    0x18, 0x33, 0x31, 0x0E, // 182
+    0x18, 0x64, 0x12, 0x06, // 183
+    0x18, 0x76, 0x18, 0x07, // 184
+    0x18, 0x8E, 0x16, 0x07, // 185
+    0x18, 0xA4, 0x1E, 0x09, // 186
+    0x18, 0xC2, 0x2E, 0x0D, // 187
+    0x18, 0xF0, 0x4F, 0x15, // 188
+    0x19, 0x3F, 0x4B, 0x14, // 189
+    0x19, 0x8A, 0x4B, 0x14, // 190
+    0x19, 0xD5, 0x33, 0x0E, // 191
+    0x1A, 0x08, 0x3B, 0x10, // 192
+    0x1A, 0x43, 0x3B, 0x10, // 193
+    0x1A, 0x7E, 0x3B, 0x10, // 194
+    0x1A, 0xB9, 0x3B, 0x10, // 195
+    0x1A, 0xF4, 0x3B, 0x10, // 196
+    0x1B, 0x2F, 0x3B, 0x10, // 197
+    0x1B, 0x6A, 0x5B, 0x18, // 198
+    0x1B, 0xC5, 0x3F, 0x11, // 199
+    0x1C, 0x04, 0x3B, 0x10, // 200
+    0x1C, 0x3F, 0x3B, 0x10, // 201
+    0x1C, 0x7A, 0x3B, 0x10, // 202
+    0x1C, 0xB5, 0x3B, 0x10, // 203
+    0x1C, 0xF0, 0x11, 0x06, // 204
+    0x1D, 0x01, 0x11, 0x06, // 205
+    0x1D, 0x12, 0x15, 0x07, // 206
+    0x1D, 0x27, 0x15, 0x07, // 207
+    0x1D, 0x3C, 0x3F, 0x11, // 208
+    0x1D, 0x7B, 0x3B, 0x10, // 209
+    0x1D, 0xB6, 0x47, 0x13, // 210
+    0x1D, 0xFD, 0x47, 0x13, // 211
+    0x1E, 0x44, 0x47, 0x13, // 212
+    0x1E, 0x8B, 0x47, 0x13, // 213
+    0x1E, 0xD2, 0x47, 0x13, // 214
+    0x1F, 0x19, 0x2B, 0x0C, // 215
+    0x1F, 0x44, 0x47, 0x13, // 216
+    0x1F, 0x8B, 0x3B, 0x10, // 217
+    0x1F, 0xC6, 0x3B, 0x10, // 218
+    0x20, 0x01, 0x3B, 0x10, // 219
+    0x20, 0x3C, 0x3B, 0x10, // 220
+    0x20, 0x77, 0x3D, 0x11, // 221
+    0x20, 0xB4, 0x3A, 0x10, // 222
+    0x20, 0xEE, 0x37, 0x0F, // 223
+    0x21, 0x25, 0x2F, 0x0D, // 224
+    0x21, 0x54, 0x2F, 0x0D, // 225
+    0x21, 0x83, 0x2F, 0x0D, // 226
+    0x21, 0xB2, 0x2F, 0x0D, // 227
+    0x21, 0xE1, 0x2F, 0x0D, // 228
+    0x22, 0x10, 0x2F, 0x0D, // 229
+    0x22, 0x3F, 0x53, 0x16, // 230
+    0x22, 0x92, 0x2B, 0x0C, // 231
+    0x22, 0xBD, 0x2F, 0x0D, // 232
+    0x22, 0xEC, 0x2F, 0x0D, // 233
+    0x23, 0x1B, 0x2F, 0x0D, // 234
+    0x23, 0x4A, 0x2F, 0x0D, // 235
+    0x23, 0x79, 0x11, 0x06, // 236
+    0x23, 0x8A, 0x11, 0x06, // 237
+    0x23, 0x9B, 0x15, 0x07, // 238
+    0x23, 0xB0, 0x15, 0x07, // 239
+    0x23, 0xC5, 0x2F, 0x0D, // 240
+    0x23, 0xF4, 0x2F, 0x0D, // 241
+    0x24, 0x23, 0x2F, 0x0D, // 242
+    0x24, 0x52, 0x2F, 0x0D, // 243
+    0x24, 0x81, 0x2F, 0x0D, // 244
+    0x24, 0xB0, 0x2F, 0x0D, // 245
+    0x24, 0xDF, 0x2F, 0x0D, // 246
+    0x25, 0x0E, 0x32, 0x0E, // 247
+    0x25, 0x40, 0x33, 0x0E, // 248
+    0x25, 0x73, 0x2F, 0x0D, // 249
+    0x25, 0xA2, 0x2F, 0x0D, // 250
+    0x25, 0xD1, 0x2F, 0x0D, // 251
+    0x26, 0x00, 0x2F, 0x0D, // 252
+    0x26, 0x2F, 0x2A, 0x0C, // 253
+    0x26, 0x59, 0x2F, 0x0D, // 254
+    0x26, 0x88, 0x2A, 0x0C, // 255
+    // Font Data:
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x33, 0x00, 0xE0, 0xFF, 0x33, // 33
+    0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0,
+    0x07, 0x00, 0x00, 0xE0, 0x07, // 34
+    0x00, 0x0C, 0x03, 0x00, 0x00, 0x0C, 0x33, 0x00, 0x00, 0x0C, 0x3F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x80, 0xFF, 0x03, 0x00, 0xE0,
+    0x0F, 0x03, 0x00, 0x60, 0x0C, 0x33, 0x00, 0x00, 0x0C, 0x3F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x80, 0xFF, 0x03, 0x00, 0xE0, 0x0F,
+    0x03, 0x00, 0x60, 0x0C, 0x03, 0x00, 0x00, 0x0C, 0x03, // 35
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x06, 0x00, 0xC0, 0x0F, 0x1E, 0x00, 0xC0, 0x18, 0x1C, 0x00, 0x60, 0x18, 0x38, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0xF0, 0xFF, 0xFF, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x60, 0x38, 0x00, 0xC0, 0x60, 0x18, 0x00, 0xC0, 0xC1,
+    0x1F, 0x00, 0x00, 0x81, 0x07, // 36
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x20,
+    0x20, 0x20, 0x00, 0x60, 0x30, 0x38, 0x00, 0xC0, 0x1F, 0x1E, 0x00, 0x80, 0x8F, 0x0F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0,
+    0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x8F, 0x0F, 0x00, 0xC0, 0xC3, 0x1F, 0x00, 0xE0, 0x60, 0x30, 0x00, 0x20, 0x20, 0x20,
+    0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x80, 0x0F, // 37
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x80, 0xE3, 0x1C, 0x00, 0xC0, 0x77, 0x38, 0x00, 0xE0,
+    0x3C, 0x30, 0x00, 0x60, 0x38, 0x30, 0x00, 0x60, 0x78, 0x30, 0x00, 0xE0, 0xEC, 0x38, 0x00, 0xC0, 0x8F, 0x1B, 0x00, 0x80, 0x03,
+    0x1F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x38, 0x00, 0x00, 0x00, 0x10, // 38
+    0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xE0, 0x07,                                           // 39
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x80, 0x0F, 0xF0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0x60,
+    0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x04, // 40
+    0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x04, 0x60, 0x00, 0x00, 0x06, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x0F, 0xF0, 0x01, 0x00,
+    0xFE, 0x7F, 0x00, 0x00, 0xF0, 0x0F, // 41
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xE0,
+    0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x80, // 42
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+    0x60, 0x00, 0x00, 0x00, 0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x0F, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60,
+    0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60,                                                 // 43
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0xF0, 0x01, // 44
+    0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
+    0x80, 0x01, 0x00, 0x00, 0x80, 0x01,                                                       // 45
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, // 46
+    0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0,
+    0x03, 0x00, 0x00, 0x60, // 47
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x60,
+    0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x38, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0xFF,
+    0x0F, 0x00, 0x00, 0xFE, 0x03, // 48
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+    0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 49
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x00, 0xC0, 0x03, 0x38, 0x00, 0xC0, 0x00, 0x3C, 0x00, 0x60, 0x00, 0x36, 0x00, 0x60,
+    0x00, 0x33, 0x00, 0x60, 0x80, 0x31, 0x00, 0x60, 0xC0, 0x30, 0x00, 0x60, 0x60, 0x30, 0x00, 0xC0, 0x30, 0x30, 0x00, 0xC0, 0x1F,
+    0x30, 0x00, 0x00, 0x0F, 0x30, // 50
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xC0, 0x01, 0x0E, 0x00, 0xC0, 0x00, 0x1C, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0xC0, 0x38, 0x30, 0x00, 0xC0, 0x6F, 0x18, 0x00, 0x80, 0xC7,
+    0x0F, 0x00, 0x00, 0x80, 0x07, // 51
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x3C, 0x03, 0x00, 0x00,
+    0x0E, 0x03, 0x00, 0x80, 0x07, 0x03, 0x00, 0xC0, 0x01, 0x03, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x00,
+    0x03, 0x00, 0x00, 0x00, 0x03, // 52
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x80, 0x3F, 0x0E, 0x00, 0xE0, 0x1F, 0x18, 0x00, 0x60, 0x08, 0x30, 0x00, 0x60,
+    0x0C, 0x30, 0x00, 0x60, 0x0C, 0x30, 0x00, 0x60, 0x0C, 0x30, 0x00, 0x60, 0x0C, 0x30, 0x00, 0x60, 0x18, 0x1C, 0x00, 0x60, 0xF0,
+    0x0F, 0x00, 0x00, 0xE0, 0x03, // 53
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0x63, 0x1C, 0x00, 0xC0, 0x30, 0x38, 0x00, 0x60,
+    0x18, 0x30, 0x00, 0x60, 0x18, 0x30, 0x00, 0x60, 0x18, 0x30, 0x00, 0x60, 0x18, 0x30, 0x00, 0xE0, 0x30, 0x18, 0x00, 0xC0, 0xF1,
+    0x0F, 0x00, 0x80, 0xC1, 0x07, // 54
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x3C, 0x00, 0x60,
+    0x80, 0x3F, 0x00, 0x60, 0xE0, 0x03, 0x00, 0x60, 0x78, 0x00, 0x00, 0x60, 0x0E, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0xE0, 0x01,
+    0x00, 0x00, 0x60, // 55
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x80, 0xC7, 0x1F, 0x00, 0xC0, 0x6F, 0x18, 0x00, 0xE0, 0x38, 0x30, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0xE0, 0x38, 0x30, 0x00, 0xC0, 0x6F, 0x18, 0x00, 0x80, 0xC7,
+    0x1F, 0x00, 0x00, 0x80, 0x07, // 56
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x0C, 0x00, 0x80, 0x7F, 0x1C, 0x00, 0xC0, 0x61, 0x38, 0x00, 0x60, 0xC0, 0x30, 0x00, 0x60,
+    0xC0, 0x30, 0x00, 0x60, 0xC0, 0x30, 0x00, 0x60, 0xC0, 0x30, 0x00, 0x60, 0x60, 0x18, 0x00, 0xC0, 0x31, 0x1E, 0x00, 0x80, 0xFF,
+    0x0F, 0x00, 0x00, 0xFE, 0x01,                                                                   // 57
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30,       // 58
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, 0x03, 0x00, 0x06, 0xF0, 0x01, // 59
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00,
+    0xD8, 0x00, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06,
+    0x03, 0x00, 0x00, 0x03, 0x06, // 60
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00,
+    0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C,
+    0x01, 0x00, 0x00, 0x8C, 0x01, // 61
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00,
+    0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70,
+    0x00, 0x00, 0x00, 0x20, // 62
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x60,
+    0x80, 0x33, 0x00, 0x60, 0xC0, 0x33, 0x00, 0x60, 0xE0, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0xC0, 0x38, 0x00, 0x00, 0xC0, 0x1F,
+    0x00, 0x00, 0x00, 0x07, // 63
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x1E, 0xF0, 0x00, 0x00, 0x07, 0xC0, 0x01, 0x80,
+    0xC3, 0x87, 0x01, 0xC0, 0xF1, 0x9F, 0x03, 0xC0, 0x38, 0x18, 0x03, 0xC0, 0x0C, 0x30, 0x03, 0x60, 0x0E, 0x30, 0x06, 0x60, 0x06,
+    0x30, 0x06, 0x60, 0x06, 0x18, 0x06, 0x60, 0x06, 0x0C, 0x06, 0x60, 0x0C, 0x1E, 0x06, 0x60, 0xF8, 0x3F, 0x06, 0xE0, 0xFE, 0x31,
+    0x06, 0xC0, 0x0E, 0x30, 0x06, 0xC0, 0x01, 0x18, 0x03, 0x80, 0x03, 0x1C, 0x03, 0x00, 0x07, 0x8F, 0x01, 0x00, 0xFE, 0x87, 0x01,
+    0x00, 0xF8, 0xC1, 0x00, 0x00, 0x00, 0x40, // 64
+    0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x80,
+    0x8F, 0x01, 0x00, 0xE0, 0x83, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0xE0, 0x83, 0x01, 0x00, 0x80, 0x8F, 0x01, 0x00, 0x00, 0xFE,
+    0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 65
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30,
+    0x30, 0x00, 0xC0, 0x78, 0x30, 0x00, 0xC0, 0xFF, 0x18, 0x00, 0x80, 0xC7, 0x1F, 0x00, 0x00, 0x80, 0x07, // 66
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0,
+    0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00,
+    0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0F, 0x00, 0x00, 0x02,
+    0x03, // 67
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60,
+    0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00,
+    0x30, 0x00, 0xE0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0E, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC,
+    0x01, // 68
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30,
+    0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 69
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60,
+    0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30,
+    0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, // 70
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0,
+    0x00, 0x18, 0x00, 0xE0, 0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x60,
+    0x30, 0x00, 0x60, 0x60, 0x30, 0x00, 0xE0, 0x60, 0x38, 0x00, 0xC0, 0x60, 0x18, 0x00, 0xC0, 0x61, 0x18, 0x00, 0x80, 0xE3, 0x0F,
+    0x00, 0x00, 0xE2, 0x0F, // 71
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30,
+    0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 72
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F,             // 73
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+    0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0xE0, 0xFF, 0x0F, // 74
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00,
+    0x70, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xE7, 0x01, 0x00, 0x80, 0x83,
+    0x07, 0x00, 0xC0, 0x01, 0x0F, 0x00, 0xE0, 0x00, 0x1E, 0x00, 0x60, 0x00, 0x38, 0x00, 0x20, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x20, // 75
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+    0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x30, 0x00, 0x00, 0x00, 0x30, // 76
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0,
+    0x0F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x3F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0xFF, 0x3F,
+    0x00, 0xE0, 0xFF, 0x3F, // 77
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80,
+    0x03, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80,
+    0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 78
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0,
+    0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00,
+    0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F,
+    0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 79
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60,
+    0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60,
+    0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x00, 0x0F, // 80
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x0C, 0x00, 0xC0,
+    0x00, 0x18, 0x00, 0xE0, 0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00,
+    0x36, 0x00, 0x60, 0x00, 0x36, 0x00, 0xE0, 0x00, 0x3C, 0x00, 0xC0, 0x00, 0x1C, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x3F,
+    0x00, 0x00, 0xFF, 0x77, 0x00, 0x00, 0xFC, 0x61, // 81
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60,
+    0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x70, 0x00, 0x00, 0x60, 0xF0, 0x00, 0x00, 0x60, 0xF0,
+    0x03, 0x00, 0x60, 0xB0, 0x07, 0x00, 0xE0, 0x18, 0x1F, 0x00, 0xC0, 0x1F, 0x3C, 0x00, 0x80, 0x0F, 0x30, 0x00, 0x00, 0x00,
+    0x20, // 82
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x0F, 0x00, 0xC0, 0x1F, 0x1C, 0x00, 0xC0, 0x18, 0x18, 0x00, 0x60,
+    0x38, 0x38, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x70,
+    0x30, 0x00, 0xC0, 0x60, 0x18, 0x00, 0xC0, 0xE1, 0x18, 0x00, 0x80, 0xC3, 0x0F, 0x00, 0x00, 0x83, 0x07, // 83
+    0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60,
+    0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00,
+    0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, // 84
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
+    0x00, 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 85
+    0x20, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00,
+    0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8,
+    0x01, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x20, // 86
+    0x60, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x80, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00,
+    0x00, 0x30, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x03,
+    0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xE0, 0x0F,
+    0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x80, 0xFF, 0x00, 0x00,
+    0xE0, 0x07, 0x00, 0x00, 0x60, // 87
+    0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0x30, 0x00, 0x60, 0x00, 0x3C, 0x00, 0xE0, 0x01, 0x1E, 0x00, 0xC0, 0x83, 0x07, 0x00, 0x00,
+    0xCF, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xCF, 0x03, 0x00, 0xC0, 0x03,
+    0x07, 0x00, 0xE0, 0x01, 0x1E, 0x00, 0x60, 0x00, 0x3C, 0x00, 0x20, 0x00, 0x30, 0x00, 0x00, 0x00, 0x20, // 88
+    0x20, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+    0x1E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1E,
+    0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x20, // 89
+    0x00, 0x00, 0x30, 0x00, 0x60, 0x00, 0x38, 0x00, 0x60, 0x00, 0x3C, 0x00, 0x60, 0x00, 0x37, 0x00, 0x60, 0x80, 0x33, 0x00, 0x60,
+    0xC0, 0x31, 0x00, 0x60, 0xE0, 0x30, 0x00, 0x60, 0x38, 0x30, 0x00, 0x60, 0x1C, 0x30, 0x00, 0x60, 0x0E, 0x30, 0x00, 0x60, 0x07,
+    0x30, 0x00, 0xE0, 0x01, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30,                                           // 90
+    0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, // 91
+    0x60, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00,
+    0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 92
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0xE0, 0xFF, 0xFF, 0x07, 0xE0,
+    0xFF, 0xFF, 0x07, // 93
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xE0,
+    0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+    0x20, // 94
+    0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00,
+    0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+    0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06,                                           // 95
+    0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x80, // 96
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x00, 0x8C, 0x39, 0x00, 0x00, 0x86, 0x31, 0x00, 0x00,
+    0x86, 0x31, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x18, 0x00, 0x00, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8,
+    0x3F, 0x00, 0x00, 0x00, 0x20, // 97
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00,
+    0x0C, 0x18, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C,
+    0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x03, // 98
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00,
+    0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x18,
+    0x0C, // 99
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00,
+    0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0xE0, 0xFF,
+    0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 100
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00,
+    0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00, 0xDC, 0x18, 0x00, 0x00, 0xF8,
+    0x0C, 0x00, 0x00, 0xF0, 0x04, // 101
+    0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xC0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60,
+    0x06, 0x00, 0x00, 0x60, 0x06, // 102
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x01, 0x00, 0xF8, 0x8F, 0x03, 0x00, 0x1C, 0x1C, 0x07, 0x00, 0x0E, 0x38, 0x06, 0x00,
+    0x06, 0x30, 0x06, 0x00, 0x06, 0x30, 0x06, 0x00, 0x06, 0x30, 0x06, 0x00, 0x0C, 0x18, 0x07, 0x00, 0x18, 0x8C, 0x03, 0x00, 0xFE,
+    0xFF, 0x01, 0x00, 0xFE, 0xFF, // 103
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+    0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xFC,
+    0x3F, 0x00, 0x00, 0xF8, 0x3F,                                                                   // 104
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0x60, 0xFE, 0x3F,       // 105
+    0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x60, 0xFE, 0xFF, 0x07, 0x60, 0xFE, 0xFF, 0x03, // 106
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
+    0xE0, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x98, 0x07, 0x00, 0x00, 0x0C, 0x0E, 0x00, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x02,
+    0x30, 0x00, 0x00, 0x00, 0x20,                                                             // 107
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 108
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
+    0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF8,
+    0x3F, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00,
+    0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF8, 0x3F, // 109
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+    0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xFC,
+    0x3F, 0x00, 0x00, 0xF8, 0x3F, // 110
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00,
+    0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8,
+    0x0F, 0x00, 0x00, 0xF0, 0x07, // 111
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x07, 0x00, 0xFE, 0xFF, 0x07, 0x00, 0x18, 0x0C, 0x00, 0x00,
+    0x0C, 0x18, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C,
+    0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x03, // 112
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00,
+    0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0xFE,
+    0xFF, 0x07, 0x00, 0xFE, 0xFF, 0x07, // 113
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
+    0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, // 114
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x00, 0x00, 0x7C, 0x1C, 0x00, 0x00, 0xEE, 0x38, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00,
+    0xC6, 0x30, 0x00, 0x00, 0xC6, 0x31, 0x00, 0x00, 0xC6, 0x31, 0x00, 0x00, 0x8E, 0x39, 0x00, 0x00, 0x9C, 0x1F, 0x00, 0x00, 0x18,
+    0x0F, // 115
+    0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00,
+    0x06, 0x30, 0x00, 0x00, 0x06, 0x30, // 116
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00,
+    0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0xFE,
+    0x3F, 0x00, 0x00, 0xFE, 0x3F, // 117
+    0x00, 0x06, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00,
+    0x00, 0x38, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
+    0x06, // 118
+    0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00,
+    0x80, 0x1F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xE0,
+    0x03, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x7E, 0x00,
+    0x00, 0x00, 0x0E, // 119
+    0x00, 0x02, 0x20, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x38, 0x0E, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00,
+    0xC0, 0x01, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x38, 0x0E, 0x00, 0x00, 0x1C, 0x3C, 0x00, 0x00, 0x0E, 0x30, 0x00, 0x00, 0x02,
+    0x20, // 120
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0x00, 0xF0, 0x01, 0x06, 0x00, 0x80, 0x0F, 0x07, 0x00,
+    0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
+    0x06, // 121
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x06, 0x3E, 0x00, 0x00, 0x06, 0x37, 0x00, 0x00,
+    0xC6, 0x33, 0x00, 0x00, 0xE6, 0x30, 0x00, 0x00, 0x76, 0x30, 0x00, 0x00, 0x3E, 0x30, 0x00, 0x00, 0x1E, 0x30, 0x00, 0x00, 0x06,
+    0x30, // 122
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x03, 0x00, 0xC0, 0x7F, 0xFE, 0x03, 0xE0, 0x3F, 0xFC, 0x07, 0x60,
+    0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06,                                                       // 123
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0xFF, 0x0F, // 124
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0xE0, 0x3F, 0xFC, 0x07, 0xC0, 0x7F, 0xFF, 0x03, 0x00,
+    0xC0, 0x03, 0x00, 0x00, 0x80, 0x01, // 125
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0,
+    0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x60, // 126
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0,
+    0x00, 0x18, 0x00, 0x62, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x66, 0x00,
+    0x30, 0x00, 0x62, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0F, 0x00, 0x00, 0x02,
+    0x03, // 129
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60,
+    0x00, 0x30, 0x00, 0x62, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x66, 0x00,
+    0x30, 0x00, 0xE2, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0E, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC,
+    0x01, // 130
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0x62, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x66, 0x30,
+    0x30, 0x00, 0x62, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 131
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80,
+    0x03, 0x00, 0x00, 0x02, 0x0E, 0x00, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x0C, 0x70, 0x00, 0x00, 0x0C, 0xE0, 0x01, 0x00, 0x06, 0x80,
+    0x03, 0x00, 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 132
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60,
+    0x30, 0x00, 0x00, 0x62, 0x30, 0x00, 0x00, 0x66, 0x30, 0x00, 0x00, 0x6C, 0x70, 0x00, 0x00, 0x6C, 0xF0, 0x00, 0x00, 0x66, 0xF0,
+    0x03, 0x00, 0x62, 0xB0, 0x07, 0x00, 0xE0, 0x18, 0x1F, 0x00, 0xC0, 0x1F, 0x3C, 0x00, 0x80, 0x0F, 0x30, 0x00, 0x00, 0x00,
+    0x20, // 133
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x0F, 0x00, 0xC0, 0x1F, 0x1C, 0x00, 0xC0, 0x18, 0x18, 0x00, 0x62,
+    0x38, 0x38, 0x00, 0x66, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x62, 0x70,
+    0x30, 0x00, 0xC0, 0x60, 0x18, 0x00, 0xC0, 0xE1, 0x18, 0x00, 0x80, 0xC3, 0x0F, 0x00, 0x00, 0x83, 0x07, // 134
+    0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x66,
+    0x00, 0x00, 0x00, 0xEC, 0xFF, 0x3F, 0x00, 0xEC, 0xFF, 0x3F, 0x00, 0x66, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x60, 0x00,
+    0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, // 135
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
+    0x00, 0x38, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x1B, 0x00, 0x30, 0x00, 0x11, 0x00, 0x30, 0x00, 0x1B, 0x00, 0x30, 0x00, 0x0E, 0x00,
+    0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 136
+    0x00, 0x00, 0x30, 0x00, 0x60, 0x00, 0x38, 0x00, 0x60, 0x00, 0x3C, 0x00, 0x60, 0x00, 0x37, 0x00, 0x60, 0x80, 0x33, 0x00, 0x62,
+    0xC0, 0x31, 0x00, 0x66, 0xE0, 0x30, 0x00, 0x6C, 0x38, 0x30, 0x00, 0x6C, 0x1C, 0x30, 0x00, 0x66, 0x0E, 0x30, 0x00, 0x62, 0x07,
+    0x30, 0x00, 0xE0, 0x01, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, // 137
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x20, 0x0E, 0x38, 0x00, 0x60,
+    0x06, 0x30, 0x00, 0xC0, 0x06, 0x30, 0x00, 0xC0, 0x06, 0x30, 0x00, 0x60, 0x0E, 0x38, 0x00, 0x20, 0x1C, 0x1C, 0x00, 0x00, 0x18,
+    0x0C, // 138
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00,
+    0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0xE0, 0xFF,
+    0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0xE0, 0x01, // 139
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0x20, 0xCE, 0x38, 0x00, 0x60,
+    0xC6, 0x30, 0x00, 0xC0, 0xC6, 0x30, 0x00, 0xC0, 0xC6, 0x30, 0x00, 0x60, 0xCE, 0x38, 0x00, 0x20, 0xDC, 0x18, 0x00, 0x00, 0xF8,
+    0x0C, 0x00, 0x00, 0xF0, 0x04, // 140
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x20, 0x18, 0x00, 0x00, 0x60,
+    0x0C, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x20, 0x0E, 0x00, 0x00, 0x00, 0xFC,
+    0x3F, 0x00, 0x00, 0xF8, 0x3F, // 141
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xFE, 0x3F, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0,
+    0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x20, 0x06, // 142
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x00, 0x00, 0x7C, 0x1C, 0x00, 0x20, 0xEE, 0x38, 0x00, 0x60, 0xC6, 0x30, 0x00, 0xC0,
+    0xC6, 0x30, 0x00, 0xC0, 0xC6, 0x31, 0x00, 0x60, 0xC6, 0x31, 0x00, 0x20, 0x8E, 0x39, 0x00, 0x00, 0x9C, 0x1F, 0x00, 0x00, 0x18,
+    0x0F, // 143
+    0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00,
+    0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0xE0, 0x01, // 144
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x70,
+    0x00, 0x30, 0x00, 0xD8, 0x00, 0x30, 0x00, 0x88, 0x00, 0x30, 0x00, 0xD8, 0x00, 0x18, 0x00, 0x70, 0x00, 0x0C, 0x00, 0x00, 0xFE,
+    0x3F, 0x00, 0x00, 0xFE, 0x3F, // 145
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x06, 0x3E, 0x00, 0x20, 0x06, 0x37, 0x00, 0x60,
+    0xC6, 0x33, 0x00, 0xC0, 0xE6, 0x30, 0x00, 0xC0, 0x76, 0x30, 0x00, 0x60, 0x3E, 0x30, 0x00, 0x20, 0x1E, 0x30, 0x00, 0x00, 0x06,
+    0x30, // 146
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE8, 0xFF, 0x3F, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x06,
+    0x00, 0x30, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x30, 0x00, 0x00, 0x00, 0x30, // 147
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xFF, 0x3F, 0x00, 0xEE, 0xFF, 0x3F, 0x00, 0x06, 0x00, 0x00, 0x00,
+    0x02, // 148
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+    0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x60, 0x03, 0x30, 0x00, 0xE0, 0x01,
+    0x30, 0x00, 0x00, 0x00, 0x30, // 149
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
+    0x03, 0x00, 0x00, 0xE0, 0x01, // 150
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60,
+    0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x68, 0x70, 0x00, 0x00, 0x6E, 0xF0, 0x00, 0x00, 0x66, 0xF0,
+    0x03, 0x00, 0x62, 0xB0, 0x07, 0x00, 0xE0, 0x18, 0x1F, 0x00, 0xC0, 0x1F, 0x3C, 0x00, 0x80, 0x0F, 0x30, 0x00, 0x00, 0x00,
+    0x20, // 151
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x80, 0x0C, 0x00, 0x00, 0xE0,
+    0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x20, 0x06,                                                                   // 152
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x07, // 161
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x9C, 0x07, 0x00, 0x0E, 0x78, 0x00, 0x00,
+    0x06, 0x3F, 0x00, 0x00, 0xF6, 0x30, 0x00, 0x00, 0x0E, 0x30, 0x00, 0xE0, 0x0D, 0x1C, 0x00, 0x00, 0x1C, 0x0E, 0x00, 0x00, 0x10,
+    0x06, // 162
+    0x00, 0x60, 0x10, 0x00, 0x00, 0x60, 0x38, 0x00, 0x00, 0x7F, 0x1C, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0xE0, 0xE0, 0x19, 0x00, 0x60,
+    0x60, 0x18, 0x00, 0x60, 0x60, 0x18, 0x00, 0x60, 0x60, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0xC0, 0x01, 0x30, 0x00, 0x80, 0x01,
+    0x38, 0x00, 0x00, 0x00, 0x10, // 163
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0xF7, 0x0E, 0x00, 0x00, 0xFE, 0x07, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00,
+    0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0xFE,
+    0x07, 0x00, 0x00, 0xF7, 0x0E, 0x00, 0x00, 0x02, 0x04, // 164
+    0xE0, 0x60, 0x06, 0x00, 0xC0, 0x61, 0x06, 0x00, 0x80, 0x67, 0x06, 0x00, 0x00, 0x7E, 0x06, 0x00, 0x00, 0x7C, 0x06, 0x00, 0x00,
+    0xF0, 0x3F, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x7C, 0x06, 0x00, 0x00, 0x7E, 0x06, 0x00, 0x80, 0x67, 0x06, 0x00, 0xC0, 0x61,
+    0x06, 0x00, 0xE0, 0x60, 0x06, 0x00, 0x20,                                                       // 165
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x7F, 0xF8, 0x0F, 0xE0, 0x7F, 0xF8, 0x0F, // 166
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0xF3, 0xC1, 0x00, 0xC0, 0x1F, 0xC3, 0x03, 0xE0, 0x0C, 0x07, 0x03, 0x60,
+    0x1C, 0x06, 0x06, 0x60, 0x18, 0x0C, 0x06, 0x60, 0x30, 0x1C, 0x06, 0xE0, 0x70, 0x38, 0x07, 0xC0, 0xE1, 0xF4, 0x03, 0x80, 0xC1,
+    0xE7, 0x01, 0x00, 0x80, 0x03, // 167
+    0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
+    0x00, 0x00, 0x00, 0x60, // 168
+    0x00, 0xF8, 0x00, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0x07, 0x07, 0x00, 0x80, 0x01, 0x0C, 0x00, 0xC0, 0x79, 0x1C, 0x00, 0xC0,
+    0xFE, 0x19, 0x00, 0x60, 0x86, 0x31, 0x00, 0x60, 0x03, 0x33, 0x00, 0x60, 0x03, 0x33, 0x00, 0x60, 0x03, 0x33, 0x00, 0x60, 0x03,
+    0x33, 0x00, 0x60, 0x87, 0x33, 0x00, 0xC0, 0x86, 0x19, 0x00, 0xC0, 0x85, 0x1C, 0x00, 0x80, 0x01, 0x0C, 0x00, 0x00, 0x07, 0x07,
+    0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xF8, // 169
+    0x00, 0x00, 0x00, 0x00, 0xC0, 0x1C, 0x00, 0x00, 0xE0, 0x3E, 0x00, 0x00, 0x60, 0x32, 0x00, 0x00, 0x60, 0x32, 0x00, 0x00, 0xE0,
+    0x3F, 0x00, 0x00, 0xC0, 0x3F, // 170
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x78, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00,
+    0x84, 0x10, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x78, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x04, 0x10, // 171
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
+    0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xFC,
+    0x01, 0x00, 0x00, 0xFC, 0x01, // 172
+    0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
+    0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 173
+    0x00, 0xF8, 0x00, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0x07, 0x07, 0x00, 0x80, 0x01, 0x0C, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0,
+    0xFE, 0x1B, 0x00, 0x60, 0xFE, 0x33, 0x00, 0x60, 0x66, 0x30, 0x00, 0x60, 0x66, 0x30, 0x00, 0x60, 0xE6, 0x30, 0x00, 0x60, 0xFE,
+    0x31, 0x00, 0x60, 0x3C, 0x33, 0x00, 0xC0, 0x00, 0x1A, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x01, 0x0C, 0x00, 0x00, 0x07, 0x07,
+    0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xF8, // 174
+    0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C,
+    0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00,
+    0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, // 175
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x20,
+    0x08, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x80, 0x03, // 176
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00,
+    0x60, 0x30, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60,
+    0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, // 177
+    0x40, 0x20, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x20, 0x38, 0x00, 0x00, 0x20, 0x2C, 0x00, 0x00, 0x20, 0x26, 0x00, 0x00, 0xE0,
+    0x23, 0x00, 0x00, 0xC0, 0x21, // 178
+    0x40, 0x10, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x20, 0x22, 0x00, 0x00, 0x20, 0x22, 0x00, 0x00, 0xE0,
+    0x3D, 0x00, 0x00, 0xC0, 0x1D, // 179
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x60,
+    0x00, 0x00, 0x00, 0x20, // 180
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x07, 0x00, 0xFE, 0xFF, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00,
+    0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0xFE,
+    0x3F, 0x00, 0x00, 0xFE, 0x3F, // 181
+    0x00, 0x0F, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xE0,
+    0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF,
+    0xFF, 0x07, 0x60, 0x00, 0x00, 0x00, 0x60,                                                                   // 182
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, // 183
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x02, 0x00, 0x00, 0x80, 0x03, 0x00,
+    0x00, 0x00, 0x01, // 184
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xE0,
+    0x3F, // 185
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0xE0,
+    0x38, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x80, 0x0F, // 186
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00,
+    0x78, 0x0F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x78, 0x0F, 0x00, 0x00, 0xE0,
+    0x03, 0x00, 0x00, 0x80, // 187
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x20, 0x00, 0xE0, 0x3F, 0x38, 0x00, 0xE0,
+    0x3F, 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x38,
+    0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x0C, 0x00, 0xC0, 0x01, 0x0E, 0x00, 0xE0, 0x80, 0x0B,
+    0x00, 0x60, 0xC0, 0x08, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x00, 0x08, // 188
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x20, 0x00, 0xE0, 0x3F, 0x30, 0x00, 0xE0,
+    0x3F, 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x70,
+    0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x4E, 0x20, 0x00, 0x00, 0x67, 0x30, 0x00, 0xC0, 0x21, 0x38, 0x00, 0xE0, 0x20, 0x2C,
+    0x00, 0x60, 0x20, 0x26, 0x00, 0x00, 0xE0, 0x27, 0x00, 0x00, 0xC0, 0x21, // 189
+    0x40, 0x10, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x20, 0x22, 0x20, 0x00, 0x20, 0x22, 0x30, 0x00, 0xE0,
+    0x3D, 0x38, 0x00, 0xC0, 0x1D, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x70,
+    0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0E, 0x0C, 0x00, 0x00, 0x07, 0x0E, 0x00, 0x80, 0x83, 0x0B, 0x00, 0xE0, 0xC0, 0x08,
+    0x00, 0x60, 0xE0, 0x3F, 0x00, 0x20, 0xE0, 0x3F, 0x00, 0x00, 0x00, 0x08, // 190
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x1E, 0x03, 0x00,
+    0x00, 0x07, 0x07, 0x00, 0xE6, 0x03, 0x06, 0x00, 0xE6, 0x01, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+    0x80, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, // 191
+    0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x82,
+    0x8F, 0x01, 0x00, 0xE6, 0x83, 0x01, 0x00, 0x6E, 0x80, 0x01, 0x00, 0xE8, 0x83, 0x01, 0x00, 0x80, 0x8F, 0x01, 0x00, 0x00, 0xFE,
+    0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 192
+    0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x80,
+    0x8F, 0x01, 0x00, 0xE8, 0x83, 0x01, 0x00, 0x6E, 0x80, 0x01, 0x00, 0xE6, 0x83, 0x01, 0x00, 0x82, 0x8F, 0x01, 0x00, 0x00, 0xFE,
+    0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 193
+    0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x88,
+    0x8F, 0x01, 0x00, 0xEC, 0x83, 0x01, 0x00, 0x66, 0x80, 0x01, 0x00, 0xE6, 0x83, 0x01, 0x00, 0x8C, 0x8F, 0x01, 0x00, 0x08, 0xFE,
+    0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 194
+    0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x0C, 0xFE, 0x01, 0x00, 0x8E,
+    0x8F, 0x01, 0x00, 0xE6, 0x83, 0x01, 0x00, 0x66, 0x80, 0x01, 0x00, 0xEC, 0x83, 0x01, 0x00, 0x8C, 0x8F, 0x01, 0x00, 0x0E, 0xFE,
+    0x01, 0x00, 0x06, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 195
+    0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x8C,
+    0x8F, 0x01, 0x00, 0xEC, 0x83, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0xE0, 0x83, 0x01, 0x00, 0x8C, 0x8F, 0x01, 0x00, 0x0C, 0xFE,
+    0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 196
+    0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x9C,
+    0x8F, 0x01, 0x00, 0xE2, 0x83, 0x01, 0x00, 0x62, 0x80, 0x01, 0x00, 0xE2, 0x83, 0x01, 0x00, 0x9C, 0x8F, 0x01, 0x00, 0x00, 0xFE,
+    0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 197
+    0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00,
+    0xBC, 0x01, 0x00, 0x00, 0x8F, 0x01, 0x00, 0xC0, 0x83, 0x01, 0x00, 0xE0, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80,
+    0x01, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30,
+    0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00,
+    0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 198
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0,
+    0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x02, 0x60, 0x00, 0x30, 0x02, 0x60, 0x00, 0xF0, 0x02, 0x60, 0x00, 0xB0, 0x03, 0x60, 0x00,
+    0x30, 0x01, 0x60, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0F, 0x00, 0x00, 0x02,
+    0x03, // 199
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0x62, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x6E, 0x30, 0x30, 0x00, 0x68, 0x30, 0x30, 0x00, 0x60, 0x30,
+    0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 200
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x68, 0x30, 0x30, 0x00, 0x6E, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x62, 0x30,
+    0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 201
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0x68, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x6C, 0x30,
+    0x30, 0x00, 0x68, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 202
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x6C, 0x30,
+    0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 203
+    0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xE6, 0xFF, 0x3F, 0x00, 0xEE, 0xFF, 0x3F, 0x00, 0x08, // 204
+    0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xEE, 0xFF, 0x3F, 0x00, 0xE6, 0xFF, 0x3F, 0x00, 0x02, // 205
+    0x08, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xE6, 0xFF, 0x3F, 0x00, 0xE6, 0xFF, 0x3F, 0x00, 0x0C, 0x00, 0x00, 0x00,
+    0x08, // 206
+    0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x0C, 0x00, 0x00, 0x00,
+    0x0C, // 207
+    0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60,
+    0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00,
+    0x30, 0x00, 0xE0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0E, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC,
+    0x01, // 208
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x8C,
+    0x03, 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x06, 0x70, 0x00, 0x00, 0x0C, 0xE0, 0x01, 0x00, 0x0C, 0x80,
+    0x03, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 209
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0,
+    0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x62, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x6E, 0x00, 0x30, 0x00, 0x68, 0x00,
+    0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F,
+    0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 210
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0,
+    0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x60, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0x6E, 0x00, 0x30, 0x00, 0x66, 0x00,
+    0x30, 0x00, 0x62, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F,
+    0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 211
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0,
+    0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x68, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x66, 0x00,
+    0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0xE8, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F,
+    0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 212
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xCC,
+    0x00, 0x18, 0x00, 0xEE, 0x00, 0x38, 0x00, 0x66, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x6C, 0x00,
+    0x30, 0x00, 0x6E, 0x00, 0x30, 0x00, 0xE6, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F,
+    0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 213
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0,
+    0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00,
+    0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0xEC, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F,
+    0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 214
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x8E, 0x03, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00,
+    0xF8, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, 0x8E, 0x03, 0x00, 0x00, 0x06,
+    0x03, // 215
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x21, 0x00, 0x00, 0xFF, 0x77, 0x00, 0x80, 0x07, 0x3F, 0x00, 0xC0, 0x01, 0x1E, 0x00, 0xC0,
+    0x00, 0x1F, 0x00, 0xE0, 0x80, 0x3B, 0x00, 0x60, 0xC0, 0x31, 0x00, 0x60, 0xE0, 0x30, 0x00, 0x60, 0x70, 0x30, 0x00, 0x60, 0x38,
+    0x30, 0x00, 0x60, 0x1C, 0x30, 0x00, 0xE0, 0x0E, 0x38, 0x00, 0xC0, 0x07, 0x18, 0x00, 0xC0, 0x03, 0x1C, 0x00, 0xE0, 0x07, 0x0F,
+    0x00, 0x70, 0xFF, 0x07, 0x00, 0x20, 0xFC, 0x01, // 216
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
+    0x00, 0x38, 0x00, 0x02, 0x00, 0x30, 0x00, 0x06, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 217
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
+    0x00, 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x06, 0x00, 0x30, 0x00, 0x02, 0x00,
+    0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 218
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
+    0x00, 0x38, 0x00, 0x08, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x06, 0x00, 0x30, 0x00, 0x06, 0x00, 0x30, 0x00, 0x0C, 0x00,
+    0x30, 0x00, 0x08, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 219
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
+    0x00, 0x38, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x0C, 0x00,
+    0x30, 0x00, 0x0C, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 220
+    0x20, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+    0x1E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x08, 0xF0, 0x3F, 0x00, 0x0E, 0xF0, 0x3F, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x02, 0x1E,
+    0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x20, // 221
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00,
+    0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03,
+    0x06, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x86, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xF8, // 222
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x3F, 0x00, 0xC0, 0xFF, 0x3F, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x60,
+    0x00, 0x08, 0x00, 0x60, 0x00, 0x1C, 0x00, 0x60, 0x00, 0x38, 0x00, 0xE0, 0x78, 0x30, 0x00, 0xC0, 0x7F, 0x30, 0x00, 0x80, 0xC7,
+    0x30, 0x00, 0x00, 0x80, 0x39, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x0F, // 223
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x00, 0x8C, 0x39, 0x00, 0x20, 0x86, 0x31, 0x00, 0x60,
+    0x86, 0x31, 0x00, 0xE0, 0xC6, 0x30, 0x00, 0x80, 0xC6, 0x18, 0x00, 0x00, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8,
+    0x3F, 0x00, 0x00, 0x00, 0x20, // 224
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x00, 0x8C, 0x39, 0x00, 0x00, 0x86, 0x31, 0x00, 0x80,
+    0x86, 0x31, 0x00, 0xE0, 0xC6, 0x30, 0x00, 0x60, 0xC6, 0x18, 0x00, 0x20, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8,
+    0x3F, 0x00, 0x00, 0x00, 0x20, // 225
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x80, 0x8C, 0x39, 0x00, 0xC0, 0x86, 0x31, 0x00, 0x60,
+    0x86, 0x31, 0x00, 0x60, 0xC6, 0x30, 0x00, 0xC0, 0xC6, 0x18, 0x00, 0x80, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8,
+    0x3F, 0x00, 0x00, 0x00, 0x20, // 226
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0xC0, 0x1C, 0x1F, 0x00, 0xE0, 0x8C, 0x39, 0x00, 0x60, 0x86, 0x31, 0x00, 0x60,
+    0x86, 0x31, 0x00, 0xC0, 0xC6, 0x30, 0x00, 0xC0, 0xC6, 0x18, 0x00, 0xE0, 0xCE, 0x0C, 0x00, 0x60, 0xFC, 0x1F, 0x00, 0x00, 0xF8,
+    0x3F, 0x00, 0x00, 0x00, 0x20, // 227
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0xC0, 0x8C, 0x39, 0x00, 0xC0, 0x86, 0x31, 0x00, 0x00,
+    0x86, 0x31, 0x00, 0x00, 0xC6, 0x30, 0x00, 0xC0, 0xC6, 0x18, 0x00, 0xC0, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8,
+    0x3F, 0x00, 0x00, 0x00, 0x20, // 228
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x00, 0x8C, 0x39, 0x00, 0x70, 0x86, 0x31, 0x00, 0x88,
+    0x86, 0x31, 0x00, 0x88, 0xC6, 0x30, 0x00, 0x88, 0xC6, 0x18, 0x00, 0x70, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8,
+    0x3F, 0x00, 0x00, 0x00, 0x20, // 229
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0F, 0x00, 0x00, 0x9C, 0x1F, 0x00, 0x00, 0xCC, 0x39, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00,
+    0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0x66, 0x18, 0x00, 0x00, 0x6E, 0x1C, 0x00, 0x00, 0xFC,
+    0x0F, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xCC, 0x1C, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30,
+    0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xCC, 0x18, 0x00, 0x00, 0xF8, 0x0C, 0x00, 0x00, 0xE0, 0x04, // 230
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x02, 0x00,
+    0x06, 0x30, 0x02, 0x00, 0x06, 0xF0, 0x02, 0x00, 0x06, 0xB0, 0x03, 0x00, 0x0E, 0x38, 0x01, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x18,
+    0x0C, // 231
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0x20, 0xCE, 0x38, 0x00, 0x60,
+    0xC6, 0x30, 0x00, 0xE0, 0xC6, 0x30, 0x00, 0x80, 0xC6, 0x30, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00, 0xDC, 0x18, 0x00, 0x00, 0xF8,
+    0x0C, 0x00, 0x00, 0xF0, 0x04, // 232
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x80,
+    0xC6, 0x30, 0x00, 0xE0, 0xC6, 0x30, 0x00, 0x60, 0xC6, 0x30, 0x00, 0x20, 0xCE, 0x38, 0x00, 0x00, 0xDC, 0x18, 0x00, 0x00, 0xF8,
+    0x0C, 0x00, 0x00, 0xF0, 0x04, // 233
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0x80, 0xCE, 0x38, 0x00, 0xC0,
+    0xC6, 0x30, 0x00, 0x60, 0xC6, 0x30, 0x00, 0x60, 0xC6, 0x30, 0x00, 0xC0, 0xCE, 0x38, 0x00, 0x80, 0xDC, 0x18, 0x00, 0x00, 0xF8,
+    0x0C, 0x00, 0x00, 0xF0, 0x04, // 234
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0xC0, 0xCE, 0x38, 0x00, 0xC0,
+    0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0xC0, 0xCE, 0x38, 0x00, 0xC0, 0xDC, 0x18, 0x00, 0x00, 0xF8,
+    0x0C, 0x00, 0x00, 0xF0, 0x04,                                                                         // 235
+    0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0xE0, 0xFE, 0x3F, 0x00, 0x80, // 236
+    0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE0, 0xFE, 0x3F, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0x20, // 237
+    0x80, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0xC0, 0x00, 0x00, 0x00,
+    0x80, // 238
+    0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0xC0, 0x00, 0x00, 0x00,
+    0xC0, // 239
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1D, 0x1C, 0x00, 0xA0, 0x0F, 0x38, 0x00, 0xA0,
+    0x06, 0x30, 0x00, 0xE0, 0x06, 0x30, 0x00, 0xC0, 0x06, 0x30, 0x00, 0xC0, 0x0F, 0x38, 0x00, 0x20, 0x1F, 0x1C, 0x00, 0x00, 0xFC,
+    0x0F, 0x00, 0x00, 0xE0, 0x07, // 240
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0xC0, 0xFE, 0x3F, 0x00, 0xE0, 0x18, 0x00, 0x00, 0x60,
+    0x0C, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0xE0, 0x0E, 0x00, 0x00, 0x60, 0xFC,
+    0x3F, 0x00, 0x00, 0xF8, 0x3F, // 241
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x20, 0x0E, 0x38, 0x00, 0x60,
+    0x06, 0x30, 0x00, 0xE0, 0x06, 0x30, 0x00, 0x80, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8,
+    0x0F, 0x00, 0x00, 0xF0, 0x07, // 242
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x80,
+    0x06, 0x30, 0x00, 0xE0, 0x06, 0x30, 0x00, 0x60, 0x06, 0x30, 0x00, 0x20, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8,
+    0x0F, 0x00, 0x00, 0xF0, 0x07, // 243
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x80, 0x0E, 0x38, 0x00, 0xC0,
+    0x06, 0x30, 0x00, 0x60, 0x06, 0x30, 0x00, 0x60, 0x06, 0x30, 0x00, 0xC0, 0x0E, 0x38, 0x00, 0x80, 0x1C, 0x1C, 0x00, 0x00, 0xF8,
+    0x0F, 0x00, 0x00, 0xF0, 0x07, // 244
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0xC0, 0x1C, 0x1C, 0x00, 0xE0, 0x0E, 0x38, 0x00, 0x60,
+    0x06, 0x30, 0x00, 0x60, 0x06, 0x30, 0x00, 0xC0, 0x06, 0x30, 0x00, 0xC0, 0x0E, 0x38, 0x00, 0xE0, 0x1C, 0x1C, 0x00, 0x60, 0xF8,
+    0x0F, 0x00, 0x00, 0xF0, 0x07, // 245
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0xC0, 0x0E, 0x38, 0x00, 0xC0,
+    0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0xC0, 0x0E, 0x38, 0x00, 0xC0, 0x1C, 0x1C, 0x00, 0x00, 0xF8,
+    0x0F, 0x00, 0x00, 0xF0, 0x07, // 246
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x30, 0x00, 0x00, 0x00, 0xB6, 0x01, 0x00, 0x00, 0xB6, 0x01, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30,
+    0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, // 247
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x67, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00,
+    0x0E, 0x3F, 0x00, 0x00, 0x86, 0x33, 0x00, 0x00, 0xE6, 0x31, 0x00, 0x00, 0x76, 0x30, 0x00, 0x00, 0x3E, 0x38, 0x00, 0x00, 0x1C,
+    0x1C, 0x00, 0x00, 0xFF, 0x0F, 0x00, 0x00, 0xF3, 0x07, // 248
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x20, 0x00, 0x38, 0x00, 0x60,
+    0x00, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0x80, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0xFE,
+    0x3F, 0x00, 0x00, 0xFE, 0x3F, // 249
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00,
+    0x00, 0x30, 0x00, 0x80, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0x60, 0x00, 0x18, 0x00, 0x20, 0x00, 0x0C, 0x00, 0x00, 0xFE,
+    0x3F, 0x00, 0x00, 0xFE, 0x3F, // 250
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x80, 0x00, 0x38, 0x00, 0xC0,
+    0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x18, 0x00, 0x80, 0x00, 0x0C, 0x00, 0x00, 0xFE,
+    0x3F, 0x00, 0x00, 0xFE, 0x3F, // 251
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0xC0, 0x00, 0x38, 0x00, 0xC0,
+    0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x00, 0x0C, 0x00, 0x00, 0xFE,
+    0x3F, 0x00, 0x00, 0xFE, 0x3F, // 252
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0x00, 0xF0, 0x01, 0x06, 0x00, 0x80, 0x0F, 0x07, 0x80,
+    0x00, 0xFE, 0x03, 0xE0, 0x00, 0xFC, 0x00, 0x60, 0xC0, 0x1F, 0x00, 0x20, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
+    0x06, // 253
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, 0x00, 0x1C, 0x18, 0x00, 0x00,
+    0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8,
+    0x0F, 0x00, 0x00, 0xF0, 0x03, // 254
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0xC0, 0xF0, 0x01, 0x06, 0xC0, 0x80, 0x0F, 0x07, 0x00,
+    0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x00, 0xC0, 0xC0, 0x1F, 0x00, 0xC0, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
+    0x06, // 255
+};
\ No newline at end of file
diff --git a/src/graphics/fonts/OLEDDisplayFontsCS.h b/src/graphics/fonts/OLEDDisplayFontsCS.h
new file mode 100644
index 000000000..322fbc936
--- /dev/null
+++ b/src/graphics/fonts/OLEDDisplayFontsCS.h
@@ -0,0 +1,16 @@
+#ifndef OLEDDISPLAYFONTSCS_h
+#define OLEDDISPLAYFONTSCS_h
+
+#ifdef ARDUINO
+#include 
+#elif __MBED__
+#define PROGMEM
+#endif
+
+/**
+ * Localization for Czech and Slovak language containing glyphs with diacritic.
+ */
+extern const uint8_t ArialMT_Plain_10_CS[] PROGMEM;
+extern const uint8_t ArialMT_Plain_16_CS[] PROGMEM;
+extern const uint8_t ArialMT_Plain_24_CS[] PROGMEM;
+#endif
\ No newline at end of file

From b12aa3f3601430a7725f0fc95420a1e63bf7097d Mon Sep 17 00:00:00 2001
From: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
Date: Thu, 26 Dec 2024 10:49:06 +0100
Subject: [PATCH 111/132] Unset received SNR/RSSI values upon receiving packet
 via MQTT (#5668)

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

diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index a8a3e49ea..c91252231 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -96,6 +96,9 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
 
     UniquePacketPoolPacket p = packetPool.allocUniqueCopy(*e.packet);
     p->via_mqtt = true; // Mark that the packet was received via MQTT
+    // Unset received SNR/RSSI which might have been added by the MQTT gateway
+    p->rx_snr = 0;
+    p->rx_rssi = 0;
 
     if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
         if (moduleConfig.mqtt.encryption_enabled) {

From 33d2f78d21265cbf89386fe1bce306fb765479da Mon Sep 17 00:00:00 2001
From: Austin 
Date: Thu, 26 Dec 2024 13:59:26 -0500
Subject: [PATCH 112/132] meshtasticd-docker: simplify, add USB compose (#5662)

---
 .env.example                       |  4 ++
 .github/workflows/build_docker.yml | 21 ---------
 Dockerfile                         | 70 +++++++++++++++---------------
 docker-compose.yml                 | 29 +++++++++----
 4 files changed, 61 insertions(+), 63 deletions(-)
 create mode 100644 .env.example

diff --git a/.env.example b/.env.example
new file mode 100644
index 000000000..72d95970a
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,4 @@
+# Absolute path to the local meshtastic config.yaml file
+CONFIG_PATH=/path/to/meshtastic/config.yaml
+# USB device to passthrough (`lsusb -t`: look for `ch341`)
+USB_DEVICE=/dev/bus/usb/001/037
diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml
index bb5a394fd..13817a8cf 100644
--- a/.github/workflows/build_docker.yml
+++ b/.github/workflows/build_docker.yml
@@ -10,12 +10,6 @@ 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:
@@ -23,21 +17,6 @@ jobs:
           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: Get release version string
         run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
         id: version
diff --git a/Dockerfile b/Dockerfile
index ca216e04b..f3b294a5b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,32 +1,29 @@
-FROM debian:bookworm-slim AS builder
+# trunk-ignore-all(terrascan/AC_DOCKER_0002): Known terrascan issue
+# trunk-ignore-all(hadolint/DL3008): Use latest version of apt packages for buildchain
+# trunk-ignore-all(trivy/DS002): We must run as root for this container
+# trunk-ignore-all(checkov/CKV_DOCKER_8): We must run as root for this container
+# trunk-ignore-all(hadolint/DL3002): We must run as root for this container
 
+FROM python:3.12-bookworm AS builder
 ENV DEBIAN_FRONTEND=noninteractive
 ENV TZ=Etc/UTC
 
-# http://bugs.python.org/issue19846
-# > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK.
-ENV LANG C.UTF-8
-
-# Install build deps
-USER root
-
-# 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 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 \
-                           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
-USER mesh
+# Install Dependencies
+ENV PIP_ROOT_USER_ACTION=ignore
+RUN apt-get update && apt-get install --no-install-recommends -y wget g++ zip git ca-certificates \
+        libgpiod-dev libyaml-cpp-dev libbluetooth-dev libi2c-dev \
+        libusb-1.0-0-dev libulfius-dev liborcania-dev libssl-dev pkg-config && \
+    apt-get clean && rm -rf /var/lib/apt/lists/* && \
+    pip install --no-cache-dir -U platformio==6.1.16 && \
+    mkdir /tmp/firmware
 
+# Copy source code
 WORKDIR /tmp/firmware
-RUN python3 -m venv /tmp/firmware 
-RUN bash -o pipefail -c "source bin/activate; pip3 install --no-cache-dir -U platformio==6.1.15"
-# trunk-ignore(terrascan/AC_DOCKER_00024): We would actually like these files to be owned by mesh tyvm
-COPY --chown=mesh:mesh . /tmp/firmware
-RUN bash -o pipefail -c "source ./bin/activate && bash ./bin/build-native.sh"
-RUN cp "/tmp/firmware/release/meshtasticd_linux_$(uname -m)" "/tmp/firmware/release/meshtasticd"
+COPY . /tmp/firmware
+
+# Build
+RUN bash ./bin/build-native.sh && \
+    cp "/tmp/firmware/release/meshtasticd_linux_$(uname -m)" "/tmp/firmware/release/meshtasticd"
 
 
 ##### PRODUCTION BUILD #############
@@ -35,20 +32,25 @@ FROM debian:bookworm-slim
 ENV DEBIAN_FRONTEND=noninteractive
 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 libusb-1.0-0-dev liborcania2.3 libssl3 && \
-    apt-get clean && rm -rf /var/lib/apt/lists/*
+# nosemgrep: dockerfile.security.last-user-is-root.last-user-is-root
+USER root
 
-RUN groupadd -g 1000 mesh && useradd -ml -u 1000 -g 1000 mesh
-USER mesh
+RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libi2c0 libulfius2.7 libusb-1.0-0-dev liborcania2.3 libssl3 && \
+    apt-get clean && rm -rf /var/lib/apt/lists/* \
+    && mkdir -p /var/lib/meshtasticd \
+    && mkdir -p /etc/meshtasticd/config.d
 
-WORKDIR /home/mesh
-COPY --from=builder /tmp/firmware/release/meshtasticd /home/mesh/
+# Fetch compiled binary from the builder
+COPY --from=builder /tmp/firmware/release/meshtasticd /usr/sbin/
+# Copy config templates
+COPY ./bin/config.d /etc/meshtasticd/available.d
 
-RUN mkdir data
-VOLUME /home/mesh/data
+WORKDIR /var/lib/meshtasticd
+VOLUME /var/lib/meshtasticd
 
-CMD [ "sh",  "-cx", "./meshtasticd -d /home/mesh/data --hwid=${HWID:-$RANDOM}" ]
+# Expose Meshtastic TCP API port from the host
+EXPOSE 4403
+
+CMD [ "sh", "-cx", "meshtasticd -d /var/lib/meshtasticd" ]
 
 HEALTHCHECK NONE
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index 82f2647e8..4aac318c5 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,13 +1,26 @@
-version: "3.7"
+# USB-Based Meshtastic container-node!
+
+# Copy .env.example to .env and set the USB_DEVICE and CONFIG_PATH variables
 
 services:
   meshtastic-node:
     build: .
-    deploy:
-      mode: replicated
-      replicas: 4
-    networks:
-      - mesh
+    container_name: meshtasticd
 
-networks:
-  mesh:
+    # Pass USB device through to the container
+    devices:
+      - "${USB_DEVICE}"
+
+    # Mount local config file and named volume for data persistence
+    volumes:
+      - "${CONFIG_PATH}:/etc/meshtasticd/config.yaml:ro"
+      - meshtastic_data:/var/lib/meshtasticd
+
+    # Forward the container’s port 4403 to the host
+    ports:
+      - 4403:4403
+
+    restart: unless-stopped
+
+volumes:
+  meshtastic_data:

From b12ac6d564be4558047231d5ad946a908dfbbd7f Mon Sep 17 00:00:00 2001
From: Austin 
Date: Thu, 26 Dec 2024 14:00:50 -0500
Subject: [PATCH 113/132] meshtasticd-docker: Alpine container (#5659)

---
 alpine.Dockerfile | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 alpine.Dockerfile

diff --git a/alpine.Dockerfile b/alpine.Dockerfile
new file mode 100644
index 000000000..115602b3b
--- /dev/null
+++ b/alpine.Dockerfile
@@ -0,0 +1,42 @@
+# trunk-ignore-all(trivy/DS002): We must run as root for this container
+# trunk-ignore-all(checkov/CKV_DOCKER_8): We must run as root for this container
+# trunk-ignore-all(hadolint/DL3002): We must run as root for this container
+
+FROM python:3.12-alpine3.21 AS builder
+
+ENV PIP_ROOT_USER_ACTION=ignore
+RUN apk add bash g++ libstdc++-dev linux-headers zip git ca-certificates libgpiod-dev yaml-cpp-dev bluez-dev \
+        libusb-dev i2c-tools-dev openssl-dev pkgconf argp-standalone && \
+    pip install --no-cache-dir -U platformio==6.1.16 && \
+    mkdir /tmp/firmware
+
+WORKDIR /tmp/firmware
+COPY . /tmp/firmware
+
+# Create small package (no debugging symbols)
+# Add `argp` for musl
+ENV PLATFORMIO_BUILD_FLAGS="-Os -ffunction-sections -fdata-sections -Wl,--gc-sections -largp"
+
+RUN bash ./bin/build-native.sh && \
+    cp "/tmp/firmware/release/meshtasticd_linux_$(uname -m)" "/tmp/firmware/release/meshtasticd"
+
+# ##### PRODUCTION BUILD #############
+
+FROM alpine:3.21
+
+# nosemgrep: dockerfile.security.last-user-is-root.last-user-is-root
+USER root
+
+RUN apk add libstdc++ libgpiod yaml-cpp libusb i2c-tools \
+    && mkdir -p /var/lib/meshtasticd \
+    && mkdir -p /etc/meshtasticd/config.d
+COPY --from=builder /tmp/firmware/release/meshtasticd /usr/sbin/
+
+WORKDIR /var/lib/meshtasticd
+VOLUME /var/lib/meshtasticd
+
+EXPOSE 4403
+
+CMD [ "sh",  "-cx", "meshtasticd --fsdir=/var/lib/meshtasticd" ]
+
+HEALTHCHECK NONE
\ No newline at end of file

From b1d25ac7b72045a82c3c1820a4bc878e9c9e4032 Mon Sep 17 00:00:00 2001
From: Tavis 
Date: Thu, 26 Dec 2024 15:08:31 -0800
Subject: [PATCH 114/132] fix for nrf52 lfs assert boot loop (#5670)

* fix for nrf52 lfs assert boot loop

* guard format in ifdef FSCom block

* add ifndef portduino for format call
---
 src/FSCommon.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp
index 6cd17dac8..df46c1941 100644
--- a/src/FSCommon.cpp
+++ b/src/FSCommon.cpp
@@ -55,6 +55,15 @@ extern "C" void lfs_assert(const char *reason)
 {
     LOG_ERROR("LFS assert: %s", reason);
     lfs_assert_failed = true;
+
+#ifndef ARCH_PORTDUINO
+#ifdef FSCom
+    // CORRUPTED FILESYSTEM. This causes bootloop so
+    // might as well try formatting now.
+    LOG_ERROR("Trying FSCom.format()");
+    FSCom.format();
+#endif
+#endif
 }
 
 /**

From cd198fcf3f0f608cfbe2ee34ad08fba7c85a2af1 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Fri, 27 Dec 2024 10:46:21 +1100
Subject: [PATCH 115/132] cherry-pick: device-ui persistency (#5570)

* device-ui persistency

* review update

---------

Co-authored-by: mverch67 
---
 src/mesh/NodeDB.cpp         |  8 ++++++++
 src/mesh/NodeDB.h           |  1 +
 src/mesh/PhoneAPI.cpp       | 11 +++++++++--
 src/mesh/PhoneAPI.h         |  1 +
 src/modules/AdminModule.cpp | 30 ++++++++++++++++++++++++++++--
 src/modules/AdminModule.h   |  2 ++
 6 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 2af85e4f5..54ea570ff 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -57,6 +57,7 @@ NodeDB *nodeDB = nullptr;
 EXT_RAM_BSS_ATTR meshtastic_DeviceState devicestate;
 meshtastic_MyNodeInfo &myNodeInfo = devicestate.my_node;
 meshtastic_LocalConfig config;
+meshtastic_DeviceUIConfig uiconfig{.screen_brightness = 153, .screen_timeout = 30};
 meshtastic_LocalModuleConfig moduleConfig;
 meshtastic_ChannelFile channelFile;
 
@@ -895,6 +896,7 @@ void NodeDB::pickNewNodeNum()
 
 static const char *prefFileName = "/prefs/db.proto";
 static const char *configFileName = "/prefs/config.proto";
+static const char *uiconfigFileName = "/prefs/uiconfig.proto";
 static const char *moduleConfigFileName = "/prefs/module.proto";
 static const char *channelFileName = "/prefs/channels.proto";
 
@@ -1054,6 +1056,12 @@ void NodeDB::loadFromDisk()
         }
     }
 
+    state = loadProto(uiconfigFileName, meshtastic_DeviceUIConfig_size, sizeof(meshtastic_DeviceUIConfig),
+                      &meshtastic_DeviceUIConfig_msg, &uiconfig);
+    if (state == LoadFileResult::LOAD_SUCCESS) {
+        LOG_INFO("Loaded UIConfig\n");
+    }
+
     // 2.4.X - configuration migration to update new default intervals
     if (moduleConfig.version < 23) {
         LOG_DEBUG("ModuleConfig version %d is stale, upgrading to new default intervals", moduleConfig.version);
diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h
index 7e51a1240..c8c0d3170 100644
--- a/src/mesh/NodeDB.h
+++ b/src/mesh/NodeDB.h
@@ -29,6 +29,7 @@ extern meshtastic_DeviceState devicestate;
 extern meshtastic_ChannelFile channelFile;
 extern meshtastic_MyNodeInfo &myNodeInfo;
 extern meshtastic_LocalConfig config;
+extern meshtastic_DeviceUIConfig uiconfig;
 extern meshtastic_LocalModuleConfig moduleConfig;
 extern meshtastic_User &owner;
 extern meshtastic_Position localPosition;
diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp
index f49718c5e..c665c60bb 100644
--- a/src/mesh/PhoneAPI.cpp
+++ b/src/mesh/PhoneAPI.cpp
@@ -188,7 +188,6 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
     case STATE_SEND_NOTHING:
         LOG_DEBUG("FromRadio=STATE_SEND_NOTHING");
         break;
-
     case STATE_SEND_MY_INFO:
         LOG_DEBUG("FromRadio=STATE_SEND_MY_INFO");
         // If the user has specified they don't want our node to share its location, make sure to tell the phone
@@ -196,11 +195,18 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
         fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag;
         strncpy(myNodeInfo.pio_env, optstr(APP_ENV), sizeof(myNodeInfo.pio_env));
         fromRadioScratch.my_info = myNodeInfo;
-        state = STATE_SEND_OWN_NODEINFO;
+        state = STATE_SEND_UIDATA;
 
         service->refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon.
         break;
 
+    case STATE_SEND_UIDATA:
+        LOG_INFO("getFromRadio=STATE_SEND_UIDATA\n");
+        fromRadioScratch.which_payload_variant = meshtastic_FromRadio_deviceuiConfig_tag;
+        fromRadioScratch.deviceuiConfig = uiconfig;
+        state = STATE_SEND_OWN_NODEINFO;
+        break;
+
     case STATE_SEND_OWN_NODEINFO: {
         LOG_DEBUG("Send My NodeInfo");
         auto us = nodeDB->readNextMeshNode(readIndex);
@@ -518,6 +524,7 @@ bool PhoneAPI::available()
     case STATE_SEND_NOTHING:
         return false;
     case STATE_SEND_MY_INFO:
+    case STATE_SEND_UIDATA:
     case STATE_SEND_CHANNELS:
     case STATE_SEND_CONFIG:
     case STATE_SEND_MODULECONFIG:
diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h
index 3247fee5c..31538a0ab 100644
--- a/src/mesh/PhoneAPI.h
+++ b/src/mesh/PhoneAPI.h
@@ -34,6 +34,7 @@ class PhoneAPI
 {
     enum State {
         STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config
+        STATE_SEND_UIDATA,  // send stored data for device-ui
         STATE_SEND_MY_INFO, // send our my info record
         STATE_SEND_OWN_NODEINFO,
         STATE_SEND_METADATA,
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index 69b2c0a38..7f737a205 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -175,6 +175,12 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
         LOG_INFO("Client set ham mode");
         handleSetHamMode(r->set_ham_mode);
         break;
+    case meshtastic_AdminMessage_get_ui_config_request_tag: {
+        LOG_INFO("Client is getting device-ui config\n");
+        handleGetDeviceUIConfig(mp);
+        handled = true;
+        break;
+    }
 
     /**
      * Other
@@ -234,6 +240,11 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
         reboot(DEFAULT_REBOOT_SECONDS);
         break;
     }
+    case meshtastic_AdminMessage_store_ui_config_tag: {
+        LOG_INFO("Storing device-ui config\n");
+        handleStoreDeviceUIConfig(r->store_ui_config);
+        break;
+    }
     case meshtastic_AdminMessage_begin_edit_settings_tag: {
         LOG_INFO("Begin transaction for editing settings");
         hasOpenEditTransaction = true;
@@ -995,6 +1006,14 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch
     }
 }
 
+void AdminModule::handleGetDeviceUIConfig(const meshtastic_MeshPacket &req)
+{
+    meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default;
+    r.which_payload_variant = meshtastic_AdminMessage_get_ui_config_response_tag;
+    r.store_ui_config = uiconfig;
+    myReply = allocDataProtobuf(r);
+}
+
 void AdminModule::reboot(int32_t seconds)
 {
     LOG_INFO("Reboot in %d seconds", seconds);
@@ -1015,6 +1034,11 @@ void AdminModule::saveChanges(int saveWhat, bool shouldReboot)
     }
 }
 
+void AdminModule::handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg)
+{
+    nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uicfg);
+}
+
 void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p)
 {
     // Set call sign and override lora limitations for licensed use
@@ -1081,7 +1105,8 @@ bool AdminModule::messageIsResponse(const meshtastic_AdminMessage *r)
         r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag ||
         r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag ||
         r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag ||
-        r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag)
+        r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag ||
+        r->which_payload_variant == meshtastic_AdminMessage_get_ui_config_response_tag)
         return true;
     else
         return false;
@@ -1097,7 +1122,8 @@ bool AdminModule::messageIsRequest(const meshtastic_AdminMessage *r)
         r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_request_tag ||
         r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_request_tag ||
         r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_request_tag ||
-        r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_request_tag)
+        r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_request_tag ||
+        r->which_payload_variant == meshtastic_AdminMessage_get_ui_config_request_tag)
         return true;
     else
         return false;
diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h
index b99e86707..ee2ebfd96 100644
--- a/src/modules/AdminModule.h
+++ b/src/modules/AdminModule.h
@@ -43,6 +43,7 @@ class AdminModule : public ProtobufModule, public Obser
     void handleGetDeviceMetadata(const meshtastic_MeshPacket &req);
     void handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &req);
     void handleGetNodeRemoteHardwarePins(const meshtastic_MeshPacket &req);
+    void handleGetDeviceUIConfig(const meshtastic_MeshPacket &req);
     /**
      * Setters
      */
@@ -52,6 +53,7 @@ class AdminModule : public ProtobufModule, public Obser
     void handleSetModuleConfig(const meshtastic_ModuleConfig &c);
     void handleSetChannel();
     void handleSetHamMode(const meshtastic_HamParameters &req);
+    void handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg);
     void reboot(int32_t seconds);
 
     void setPassKey(meshtastic_AdminMessage *res);

From 8f8e304216c3b1af259066805f3b4337ca213d68 Mon Sep 17 00:00:00 2001
From: Jonathan Bennett 
Date: Thu, 26 Dec 2024 18:58:26 -0600
Subject: [PATCH 116/132] Add packet length to printPacket() (#5672)

---
 src/mesh/RadioInterface.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp
index 5161ac41f..5a18ab0c0 100644
--- a/src/mesh/RadioInterface.cpp
+++ b/src/mesh/RadioInterface.cpp
@@ -313,6 +313,7 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p)
             out += DEBUG_PORT.mt_sprintf(" failId=%08x", s.ackVariant.fail_id); */
     } else {
         out += " encrypted";
+        out += DEBUG_PORT.mt_sprintf(" len=%d", p->encrypted.size + sizeof(PacketHeader));
     }
 
     if (p->rx_time != 0)
@@ -622,4 +623,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p)
 
     sendingPacket = p;
     return p->encrypted.size + sizeof(PacketHeader);
-}
\ No newline at end of file
+}

From ed39d14c8525130b9ef86cae03c575484a18e6cf Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Fri, 27 Dec 2024 18:01:02 +1100
Subject: [PATCH 117/132] Remove remaining \n from log lines. (#5675)

---
 src/detect/ScanI2CTwoWire.cpp      |  6 +++---
 src/input/MPR121Keyboard.cpp       |  2 +-
 src/input/TouchScreenBase.cpp      |  6 +++---
 src/mesh/NodeDB.cpp                |  2 +-
 src/mesh/PhoneAPI.cpp              |  4 ++--
 src/modules/AdminModule.cpp        |  8 ++++----
 src/motion/QMA6100PSensor.cpp      |  6 +++---
 variants/rak2560/RAK9154Sensor.cpp | 18 +++++++++---------
 8 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 6e695c22f..a786f874d 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -458,11 +458,11 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
                 i2cBus->endTransmission();
                 len = i2cBus->readBytes(info, 5);
                 if (len == 5 && memcmp(expectedInfo, info, len) == 0) {
-                    LOG_INFO("NXP SE050 crypto chip found\n");
+                    LOG_INFO("NXP SE050 crypto chip found");
                     type = NXP_SE050;
 
                 } else {
-                    LOG_INFO("FT6336U touchscreen found\n");
+                    LOG_INFO("FT6336U touchscreen found");
                     type = FT6336U;
                 }
                 break;
@@ -510,4 +510,4 @@ void ScanI2CTwoWire::logFoundDevice(const char *device, uint8_t address)
 {
     LOG_INFO("%s found at address 0x%x", device, address);
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/src/input/MPR121Keyboard.cpp b/src/input/MPR121Keyboard.cpp
index 078d80272..f35b942b1 100644
--- a/src/input/MPR121Keyboard.cpp
+++ b/src/input/MPR121Keyboard.cpp
@@ -87,7 +87,7 @@ uint8_t MPR121_KeyMap[12] = {2, 5, 8, 11, 1, 4, 7, 10, 0, 3, 6, 9};
 
 MPR121Keyboard::MPR121Keyboard() : m_wire(nullptr), m_addr(0), readCallback(nullptr), writeCallback(nullptr)
 {
-    // LOG_DEBUG("MPR121 @ %02x\n", m_addr);
+    // LOG_DEBUG("MPR121 @ %02x", m_addr);
     state = Init;
     last_key = -1;
     last_tap = 0L;
diff --git a/src/input/TouchScreenBase.cpp b/src/input/TouchScreenBase.cpp
index a63203362..d2f7b54f8 100644
--- a/src/input/TouchScreenBase.cpp
+++ b/src/input/TouchScreenBase.cpp
@@ -113,13 +113,13 @@ int32_t TouchScreenBase::runOnce()
         if (_tapped) {
             _tapped = false;
             e.touchEvent = static_cast(TOUCH_ACTION_TAP);
-            LOG_DEBUG("action TAP(%d/%d)\n", _last_x, _last_y);
+            LOG_DEBUG("action TAP(%d/%d)", _last_x, _last_y);
         }
     } else {
         if (_tapped && (time_t(millis()) - _start) > TIME_LONG_PRESS - 50) {
             _tapped = false;
             e.touchEvent = static_cast(TOUCH_ACTION_TAP);
-            LOG_DEBUG("action TAP(%d/%d)\n", _last_x, _last_y);
+            LOG_DEBUG("action TAP(%d/%d)", _last_x, _last_y);
         }
     }
 #else
@@ -156,4 +156,4 @@ void TouchScreenBase::hapticFeedback()
     drv.setWaveform(1, 0); // end waveform
     drv.go();
 #endif
-}
\ No newline at end of file
+}
diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 54ea570ff..9dbe92b7c 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -1059,7 +1059,7 @@ void NodeDB::loadFromDisk()
     state = loadProto(uiconfigFileName, meshtastic_DeviceUIConfig_size, sizeof(meshtastic_DeviceUIConfig),
                       &meshtastic_DeviceUIConfig_msg, &uiconfig);
     if (state == LoadFileResult::LOAD_SUCCESS) {
-        LOG_INFO("Loaded UIConfig\n");
+        LOG_INFO("Loaded UIConfig");
     }
 
     // 2.4.X - configuration migration to update new default intervals
diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp
index c665c60bb..36045bcf9 100644
--- a/src/mesh/PhoneAPI.cpp
+++ b/src/mesh/PhoneAPI.cpp
@@ -201,7 +201,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
         break;
 
     case STATE_SEND_UIDATA:
-        LOG_INFO("getFromRadio=STATE_SEND_UIDATA\n");
+        LOG_INFO("getFromRadio=STATE_SEND_UIDATA");
         fromRadioScratch.which_payload_variant = meshtastic_FromRadio_deviceuiConfig_tag;
         fromRadioScratch.deviceuiConfig = uiconfig;
         state = STATE_SEND_OWN_NODEINFO;
@@ -664,4 +664,4 @@ int PhoneAPI::onNotify(uint32_t newValue)
     }
 
     return timeout ? -1 : 0; // If we timed out, MeshService should stop iterating through observers as we just removed one
-}
\ No newline at end of file
+}
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index 7f737a205..6fd2952c0 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -176,7 +176,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
         handleSetHamMode(r->set_ham_mode);
         break;
     case meshtastic_AdminMessage_get_ui_config_request_tag: {
-        LOG_INFO("Client is getting device-ui config\n");
+        LOG_INFO("Client is getting device-ui config");
         handleGetDeviceUIConfig(mp);
         handled = true;
         break;
@@ -241,7 +241,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
         break;
     }
     case meshtastic_AdminMessage_store_ui_config_tag: {
-        LOG_INFO("Storing device-ui config\n");
+        LOG_INFO("Storing device-ui config");
         handleStoreDeviceUIConfig(r->store_ui_config);
         break;
     }
@@ -488,7 +488,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
             IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_ROUTER,
                       meshtastic_Config_DeviceConfig_Role_REPEATER)) {
             config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL;
-            const char *warning = "Rebroadcast mode can't be set to NONE for a router or repeater\n";
+            const char *warning = "Rebroadcast mode can't be set to NONE for a router or repeater";
             LOG_WARN(warning);
             sendWarning(warning);
         }
@@ -1149,4 +1149,4 @@ void disableBluetooth()
         nrf52Bluetooth->shutdown();
 #endif
 #endif
-}
\ No newline at end of file
+}
diff --git a/src/motion/QMA6100PSensor.cpp b/src/motion/QMA6100PSensor.cpp
index 4c5bc14d2..eb81e16c7 100644
--- a/src/motion/QMA6100PSensor.cpp
+++ b/src/motion/QMA6100PSensor.cpp
@@ -88,13 +88,13 @@ bool QMA6100PSingleton::init(ScanI2C::FoundDevice device)
     bool status = begin(device.address.address, &Wire);
 #endif
     if (status != true) {
-        LOG_WARN("QMA6100P init begin failed\n");
+        LOG_WARN("QMA6100P init begin failed");
         return false;
     }
     delay(20);
     // SW reset to make sure the device starts in a known state
     if (softwareReset() != true) {
-        LOG_WARN("QMA6100P init reset failed\n");
+        LOG_WARN("QMA6100P init reset failed");
         return false;
     }
     delay(20);
@@ -180,4 +180,4 @@ bool QMA6100PSingleton::setWakeOnMotion()
     return true;
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/rak2560/RAK9154Sensor.cpp b/variants/rak2560/RAK9154Sensor.cpp
index 9f660947e..43affe581 100644
--- a/variants/rak2560/RAK9154Sensor.cpp
+++ b/variants/rak2560/RAK9154Sensor.cpp
@@ -37,11 +37,11 @@ static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT
         break;
 
     case SNHUBAPI_EVT_ADD_SID:
-        // LOG_INFO("+ADD:SID:[%02x]\r\n", msg[0]);
+        // LOG_INFO("+ADD:SID:[%02x]", msg[0]);
         break;
 
     case SNHUBAPI_EVT_ADD_PID:
-        // LOG_INFO("+ADD:PID:[%02x]\r\n", msg[0]);
+        // LOG_INFO("+ADD:PID:[%02x]", msg[0]);
 #ifdef BOOT_DATA_REQ
         provision = msg[0];
 #endif
@@ -55,12 +55,12 @@ static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT
 
     case SNHUBAPI_EVT_SDATA_REQ:
 
-        // LOG_INFO("+EVT:PID[%02x],IPSO[%02x]\r\n",pid,msg[0]);
+        // LOG_INFO("+EVT:PID[%02x],IPSO[%02x]",pid,msg[0]);
         // for( uint16_t i=1; i 0) ? true : false;
 }
-#endif // HAS_RAKPROT
\ No newline at end of file
+#endif // HAS_RAKPROT

From ae93f3fa3f617dcffa118a930fcac2d106b466b6 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Fri, 27 Dec 2024 22:12:33 +1100
Subject: [PATCH 118/132] TFT branch - minor cherry picks (#5676)

* fix missing include

* fix request, add handled

---------

Co-authored-by: mverch67 
---
 src/mesh/http/ContentHelper.h | 1 +
 src/modules/AdminModule.cpp   | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/mesh/http/ContentHelper.h b/src/mesh/http/ContentHelper.h
index a80c39f47..e5d3a2f57 100644
--- a/src/mesh/http/ContentHelper.h
+++ b/src/mesh/http/ContentHelper.h
@@ -1,5 +1,6 @@
 #include 
 #include 
+#include 
 
 #define BoolToString(x) ((x) ? "true" : "false")
 
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index 6fd2952c0..6b19f04e3 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -243,6 +243,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
     case meshtastic_AdminMessage_store_ui_config_tag: {
         LOG_INFO("Storing device-ui config");
         handleStoreDeviceUIConfig(r->store_ui_config);
+        handled = true;
         break;
     }
     case meshtastic_AdminMessage_begin_edit_settings_tag: {
@@ -1010,7 +1011,7 @@ void AdminModule::handleGetDeviceUIConfig(const meshtastic_MeshPacket &req)
 {
     meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default;
     r.which_payload_variant = meshtastic_AdminMessage_get_ui_config_response_tag;
-    r.store_ui_config = uiconfig;
+    r.get_ui_config_response = uiconfig;
     myReply = allocDataProtobuf(r);
 }
 

From 26a4d6c87a961aae5d5a1d9c3c2d2700dc248547 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Fri, 27 Dec 2024 22:13:45 +1100
Subject: [PATCH 119/132] Cherry-pick: Mesh-tab (#5674)

* mesh-tab targets

* update meshtab voltage divider

* fix DIO1 wakeup

* update mesh-tab tft configs

* update mesh-tab ini

* rotation fix; added ST7789 3.2" display

* mesh-tab touch updates

* mesh-tab: enable alert message menu

* mesh-tab rotation upside-down

* use MESH_TAB hardware model definition

* use board definition for mesh-tab

---------

Co-authored-by: mverch67 
---
 src/platform/esp32/architecture.h     |   2 +
 variants/diy/platformio.ini           |  70 --------
 variants/mesh-tab/pins_arduino.h      |  65 +++++++
 variants/mesh-tab/platformio.ini      | 233 ++++++++++++++++++++++++++
 variants/{diy => }/mesh-tab/variant.h |  23 ++-
 5 files changed, 316 insertions(+), 77 deletions(-)
 create mode 100644 variants/mesh-tab/pins_arduino.h
 create mode 100644 variants/mesh-tab/platformio.ini
 rename variants/{diy => }/mesh-tab/variant.h (67%)

diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h
index 1a274aa28..742b295b5 100644
--- a/src/platform/esp32/architecture.h
+++ b/src/platform/esp32/architecture.h
@@ -174,6 +174,8 @@
 #define HW_VENDOR meshtastic_HardwareModel_SENSECAP_INDICATOR
 #elif defined(SEEED_XIAO_S3)
 #define HW_VENDOR meshtastic_HardwareModel_SEEED_XIAO_S3
+#elif defined(MESH_TAB)
+#define HW_VENDOR meshtastic_HardwareModel_MESH_TAB
 #endif
 
 // -----------------------------------------------------------------------------
diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini
index b60d46996..b7f3f6a92 100644
--- a/variants/diy/platformio.ini
+++ b/variants/diy/platformio.ini
@@ -87,73 +87,3 @@ build_flags =
   -D ARDUINO_USB_MODE=0
   -D ARDUINO_USB_CDC_ON_BOOT=1
   -I variants/diy/t-energy-s3_e22
-
-; esp32-s3 + ra-sh01 lora + 3.2" ILI9143
-[env:mesh-tab]
-extends = esp32s3_base
-board = um_feathers3
-board_level = extra
-board_upload.flash_size = 16MB
-board_build.partitions = default_16MB.csv
-upload_protocol = esptool
-build_flags = ${esp32s3_base.build_flags}
-  -D MESH_TAB
-  -D PRIVATE_HW
-  -D CONFIG_ARDUHAL_ESP_LOG
-  -D CONFIG_ARDUHAL_LOG_COLORS=1
-  -D CONFIG_DISABLE_HAL_LOCKS=1 ; "feels" to be a bit more stable without locks
-  -D MESHTASTIC_EXCLUDE_CANNEDMESSAGES=1
-  -D MESHTASTIC_EXCLUDE_INPUTBROKER=1
-  -D MESHTASTIC_EXCLUDE_BLUETOOTH=1
-  -D MESHTASTIC_EXCLUDE_WEBSERVER=1
-  -D LV_LVGL_H_INCLUDE_SIMPLE
-  -D LV_CONF_INCLUDE_SIMPLE
-  -D LV_COMP_CONF_INCLUDE_SIMPLE
-  -D LV_USE_SYSMON=0
-  -D LV_USE_PROFILER=0
-  -D LV_USE_PERF_MONITOR=0
-  -D LV_USE_MEM_MONITOR=0
-  -D LV_USE_LOG=0
-  -D LV_BUILD_TEST=0
-  -D USE_LOG_DEBUG
-  -D LOG_DEBUG_INC=\"DebugConfiguration.h\"
-  -D RADIOLIB_SPI_PARANOID=0
-  -D MAX_NUM_NODES=250
-  -D MAX_THREADS=40
-  -D HAS_SCREEN=0
-  -D HAS_TFT=1
-  -D RAM_SIZE=1024
-  -D LGFX_DRIVER_TEMPLATE
-  -D LGFX_DRIVER=LGFX_GENERIC
-  -D LGFX_PANEL=ILI9341
-  -D LGFX_OFFSET_ROTATION=1
-  -D LGFX_TOUCH=XPT2046
-  -D LGFX_PIN_SCK=12
-  -D LGFX_PIN_MOSI=13
-  -D LGFX_PIN_MISO=11
-  -D LGFX_PIN_DC=16
-  -D LGFX_PIN_CS=10
-  -D LGFX_PIN_RST=-1
-  -D LGFX_PIN_BL=42
-  -D LGFX_TOUCH_INT=41
-  -D LGFX_TOUCH_CS=7
-  -D LGFX_TOUCH_CLK=12
-  -D LGFX_TOUCH_DO=11
-  -D LGFX_TOUCH_DIN=13
-  -D LGFX_TOUCH_X_MIN=300
-  -D LGFX_TOUCH_X_MAX=3900
-  -D LGFX_TOUCH_Y_MIN=400
-  -D LGFX_TOUCH_Y_MAX=3900
-  -D VIEW_320x240
-  -D USE_PACKET_API
-  -I lib/device-ui/generated/ui_320x240
-  -I variants/diy/mesh-tab
-build_src_filter = ${esp32_base.build_src_filter}
-  +<../lib/device-ui/generated/ui_320x240>
-  +<../lib/device-ui/resources>
-  +<../lib/device-ui/locale>
-  +<../lib/device-ui/source>
-lib_deps = ${esp32_base.lib_deps}
-  lovyan03/LovyanGFX@^1.1.16
-  earlephilhower/ESP8266Audio@^1.9.7
-  earlephilhower/ESP8266SAM@^1.0.1
\ No newline at end of file
diff --git a/variants/mesh-tab/pins_arduino.h b/variants/mesh-tab/pins_arduino.h
new file mode 100644
index 000000000..c995f638c
--- /dev/null
+++ b/variants/mesh-tab/pins_arduino.h
@@ -0,0 +1,65 @@
+#ifndef Pins_Arduino_h
+#define Pins_Arduino_h
+
+#include "soc/soc_caps.h"
+#include 
+
+#define USB_VID 0x303A
+#define USB_PID 0x80D6
+
+static const uint8_t TX = 43;
+static const uint8_t RX = 44;
+
+static const uint8_t SDA = 8;
+static const uint8_t SCL = 9;
+
+static const uint8_t SS = 5;
+static const uint8_t MOSI = 35;
+static const uint8_t MISO = 37;
+static const uint8_t SDO = 35;
+static const uint8_t SDI = 37;
+static const uint8_t SCK = 36;
+
+static const uint8_t A0 = 1;
+static const uint8_t A1 = 2;
+static const uint8_t A2 = 3;
+static const uint8_t A3 = 4;
+static const uint8_t A4 = 5;
+static const uint8_t A5 = 6;
+static const uint8_t A6 = 7;
+static const uint8_t A7 = 8;
+static const uint8_t A8 = 9;
+static const uint8_t A9 = 10;
+static const uint8_t A10 = 11;
+static const uint8_t A11 = 12;
+static const uint8_t A12 = 13;
+
+static const uint8_t T1 = 1;
+static const uint8_t T3 = 3;
+static const uint8_t T5 = 5;
+static const uint8_t T6 = 6;
+static const uint8_t T7 = 7;
+static const uint8_t T8 = 8;
+static const uint8_t T9 = 9;
+static const uint8_t T10 = 10;
+static const uint8_t T11 = 11;
+static const uint8_t T12 = 12;
+static const uint8_t T14 = 14;
+
+static const uint8_t VBAT_SENSE = 2;
+static const uint8_t VBUS_SENSE = 34;
+
+// User LED
+#define LED_BUILTIN 13
+#define BUILTIN_LED LED_BUILTIN // backward compatibility
+
+static const uint8_t RGB_DATA = 40;
+// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API neopixelWrite()
+#define RGB_BUILTIN (RGB_DATA + SOC_GPIO_PIN_COUNT)
+#define RGB_BRIGHTNESS 64
+
+static const uint8_t RGB_PWR = 39;
+static const uint8_t LDO2 = 39;
+static const uint8_t LED = 13;
+
+#endif /* Pins_Arduino_h */
diff --git a/variants/mesh-tab/platformio.ini b/variants/mesh-tab/platformio.ini
new file mode 100644
index 000000000..26b072cde
--- /dev/null
+++ b/variants/mesh-tab/platformio.ini
@@ -0,0 +1,233 @@
+; Base for Mesh-Tab device (esp32-s3 N16R2 + ra-sh01 lora)
+; https://github.com/valzzu/Mesh-Tab
+[mesh_tab_base]
+extends = esp32s3_base
+board = mesh-tab
+board_level = extra
+board_upload.flash_size = 16MB
+board_build.partitions = default_16MB.csv
+upload_protocol = esptool
+build_flags = ${esp32s3_base.build_flags}
+  -D MESH_TAB
+  -D CONFIG_ARDUHAL_ESP_LOG
+  -D CONFIG_ARDUHAL_LOG_COLORS=1
+  -D CONFIG_DISABLE_HAL_LOCKS=1
+  -D MESHTASTIC_EXCLUDE_CANNEDMESSAGES=1
+  -D MESHTASTIC_EXCLUDE_INPUTBROKER=1
+  -D MESHTASTIC_EXCLUDE_BLUETOOTH=1
+  -D MESHTASTIC_EXCLUDE_WEBSERVER=1
+  -D LV_LVGL_H_INCLUDE_SIMPLE
+  -D LV_CONF_INCLUDE_SIMPLE
+  -D LV_COMP_CONF_INCLUDE_SIMPLE
+  -D LV_USE_SYSMON=0
+  -D LV_USE_PROFILER=0
+  -D LV_USE_PERF_MONITOR=0
+  -D LV_USE_MEM_MONITOR=0
+  -D LV_USE_LOG=0
+  -D LV_BUILD_TEST=0
+  -D USE_LOG_DEBUG
+  -D LOG_DEBUG_INC=\"DebugConfiguration.h\"
+  -D RADIOLIB_SPI_PARANOID=0
+  -D MAX_NUM_NODES=250
+  -D MAX_THREADS=40
+  -D HAS_SCREEN=0
+  -D HAS_TFT=1
+  -D USE_PIN_BUZZER
+  -D RAM_SIZE=1024
+  -D LGFX_DRIVER_TEMPLATE
+  -D LGFX_DRIVER=LGFX_GENERIC
+  -D LGFX_PIN_SCK=12
+  -D LGFX_PIN_MOSI=13
+  -D LGFX_PIN_MISO=11
+  -D LGFX_PIN_DC=16
+  -D LGFX_PIN_CS=10
+  -D LGFX_PIN_RST=-1
+  -D LGFX_PIN_BL=42
+  -D LGFX_TOUCH_INT=41
+  -D VIEW_320x240
+  -D USE_PACKET_API
+  -I lib/device-ui/generated/ui_320x240
+  -I variants/mesh-tab
+build_src_filter = ${esp32_base.build_src_filter}
+  +<../lib/device-ui/generated/ui_320x240>
+  +<../lib/device-ui/resources>
+  +<../lib/device-ui/locale>
+  +<../lib/device-ui/source>
+lib_deps = ${esp32_base.lib_deps}
+  lovyan03/LovyanGFX@^1.1.16
+
+; 3.2" TN TFT ST7789 / XPT2046: https://vi.aliexpress.com/item/1005005933490544.html
+[env:mesh-tab-3-2-TN-resistive]
+extends = mesh_tab_base
+build_flags = ${mesh_tab_base.build_flags}
+  -D LGFX_SCREEN_WIDTH=240
+  -D LGFX_SCREEN_HEIGHT=320
+  -D LGFX_PANEL=ST7789
+  -D LGFX_INVERT_COLOR=false
+  -D LGFX_RGB_ORDER=false
+  -D LGFX_ROTATION=3
+  -D LGFX_TOUCH=XPT2046
+  -D SPI_FREQUENCY=60000000
+  -D LGFX_TOUCH_SPI_FREQ=2500000
+  -D LGFX_TOUCH_SPI_HOST=2
+  -D LGFX_TOUCH_CS=7
+  -D LGFX_TOUCH_CLK=12
+  -D LGFX_TOUCH_DO=11
+  -D LGFX_TOUCH_DIN=13
+  -D LGFX_TOUCH_X_MIN=300
+  -D LGFX_TOUCH_X_MAX=3900
+  -D LGFX_TOUCH_Y_MIN=400
+  -D LGFX_TOUCH_Y_MAX=3900
+  -D LGFX_TOUCH_ROTATION=4
+
+; 3.2" IPS TFT ILI9341 / XPT2046: https://www.aliexpress.com/item/1005006258575617.html
+[env:mesh-tab-3-2-IPS-resistive]
+extends = mesh_tab_base
+build_flags = ${mesh_tab_base.build_flags}
+  -D LGFX_SCREEN_WIDTH=240
+  -D LGFX_SCREEN_HEIGHT=320
+  -D LGFX_PANEL=ILI9341
+  -D LGFX_INVERT_COLOR=true
+  -D LGFX_RGB_ORDER=false
+  -D LGFX_ROTATION=1
+  -D LGFX_TOUCH=XPT2046
+  -D SPI_FREQUENCY=60000000 ; if image is distorted then lower to 40 MHz
+  -D LGFX_TOUCH_SPI_FREQ=2500000
+  -D LGFX_TOUCH_SPI_HOST=2
+  -D LGFX_TOUCH_CS=7
+  -D LGFX_TOUCH_CLK=12
+  -D LGFX_TOUCH_DO=11
+  -D LGFX_TOUCH_DIN=13
+  -D LGFX_TOUCH_X_MIN=300
+  -D LGFX_TOUCH_X_MAX=3900
+  -D LGFX_TOUCH_Y_MIN=400
+  -D LGFX_TOUCH_Y_MAX=3900
+  -D LGFX_TOUCH_ROTATION=4
+
+; 3.5" IPS TFT ILI9488 / XPT2046: https://vi.aliexpress.com/item/1005006333922639.html
+[env:mesh-tab-3-5-IPS-resistive]
+extends = mesh_tab_base
+build_flags = ${mesh_tab_base.build_flags}
+  -D DISPLAY_SET_RESOLUTION
+  -D LGFX_SCREEN_WIDTH=320
+  -D LGFX_SCREEN_HEIGHT=480
+  -D LGFX_PANEL=ILI9488
+  -D LGFX_INVERT_COLOR=true
+  -D LGFX_RGB_ORDER=false
+  -D LGFX_DLEN_16BITS=false
+  -D LGFX_ROTATION=0
+  -D LGFX_TOUCH=XPT2046
+  -D SPI_FREQUENCY=40000000 ; may go higher upto 40/60/80 MHz
+  -D LGFX_TOUCH_SPI_FREQ=2500000
+  -D LGFX_TOUCH_SPI_HOST=2
+  -D LGFX_TOUCH_CS=7
+  -D LGFX_TOUCH_CLK=12
+  -D LGFX_TOUCH_DO=11
+  -D LGFX_TOUCH_DIN=13
+  -D LGFX_TOUCH_X_MIN=300
+  -D LGFX_TOUCH_X_MAX=3900
+  -D LGFX_TOUCH_Y_MIN=400
+  -D LGFX_TOUCH_Y_MAX=3900
+  -D LGFX_TOUCH_ROTATION=0
+
+; 3.5" TN TFT ILI9488 / XPT2046: https://vi.aliexpress.com/item/32985467436.html
+[env:mesh-tab-3-5-TN-resistive]
+extends = mesh_tab_base
+build_flags = ${mesh_tab_base.build_flags}
+  -D DISPLAY_SET_RESOLUTION
+  -D LGFX_SCREEN_WIDTH=320
+  -D LGFX_SCREEN_HEIGHT=480
+  -D LGFX_PANEL=HX8357B
+  -D SPI_FREQUENCY=60000000
+  -D LGFX_INVERT_COLOR=false
+  -D LGFX_RGB_ORDER=false
+  -D LGFX_DLEN_16BITS=false
+  -D LGFX_ROTATION=4
+  -D LGFX_TOUCH=XPT2046
+  -D LGFX_TOUCH_SPI_FREQ=2500000
+  -D LGFX_TOUCH_SPI_HOST=2
+  -D LGFX_TOUCH_CS=7
+  -D LGFX_TOUCH_CLK=12
+  -D LGFX_TOUCH_DO=11
+  -D LGFX_TOUCH_DIN=13
+  -D LGFX_TOUCH_X_MIN=300
+  -D LGFX_TOUCH_X_MAX=3900
+  -D LGFX_TOUCH_Y_MIN=400
+  -D LGFX_TOUCH_Y_MAX=3900
+  -D LGFX_TOUCH_ROTATION=2
+
+; 3.2" IPS TFT ILI9341 / FT6236: https://vi.aliexpress.com/item/1005006624072350.html
+[env:mesh-tab-3-2-IPS-capacitive]
+extends = mesh_tab_base
+build_flags = ${mesh_tab_base.build_flags}
+  -D LGFX_SCREEN_WIDTH=240
+  -D LGFX_SCREEN_HEIGHT=320
+  -D LGFX_PANEL=ILI9341
+  -D LGFX_INVERT_COLOR=true
+  -D LGFX_RGB_ORDER=false
+  -D LGFX_ROTATION=1
+  -D LGFX_TOUCH=FT5x06
+  -D SPI_FREQUENCY=40000000 ; may go higher upto 60/80 MHz
+  -D LGFX_TOUCH_I2C_PORT=0
+  -D LGFX_TOUCH_I2C_ADDR=0x38
+  -D LGFX_TOUCH_I2C_SDA=8
+  -D LGFX_TOUCH_I2C_SCL=9
+  -D LGFX_TOUCH_RST=7
+  -D LGFX_TOUCH_X_MIN=0
+  -D LGFX_TOUCH_X_MAX=319
+  -D LGFX_TOUCH_Y_MIN=0
+  -D LGFX_TOUCH_Y_MAX=479
+  -D LGFX_TOUCH_ROTATION=0
+  -D LGFX_TOUCH_I2C_FREQ=1000000
+
+; 3.5" IPS TFT ILI9488 / FT6236: https://vi.aliexpress.com/item/1005006893699919.html
+[env:mesh-tab-3-5-IPS-capacitive]
+extends = mesh_tab_base
+build_flags = ${mesh_tab_base.build_flags}
+  -D DISPLAY_SET_RESOLUTION
+  -D LGFX_SCREEN_WIDTH=320
+  -D LGFX_SCREEN_HEIGHT=480
+  -D LGFX_PANEL=ILI9488
+  -D LGFX_INVERT_COLOR=true
+  -D LGFX_RGB_ORDER=false
+  -D LGFX_DLEN_16BITS=false
+  -D LGFX_ROTATION=1
+  -D LGFX_TOUCH=FT5x06
+  -D SPI_FREQUENCY=30000000 ; may go higher upto 40/60/80 MHz
+  -D LGFX_TOUCH_I2C_PORT=0
+  -D LGFX_TOUCH_I2C_ADDR=0x38
+  -D LGFX_TOUCH_I2C_SDA=8
+  -D LGFX_TOUCH_I2C_SCL=9
+  -D LGFX_TOUCH_RST=7
+  -D LGFX_TOUCH_X_MIN=0
+  -D LGFX_TOUCH_X_MAX=319
+  -D LGFX_TOUCH_Y_MIN=0
+  -D LGFX_TOUCH_Y_MAX=479
+  -D LGFX_TOUCH_ROTATION=1
+  -D LGFX_TOUCH_I2C_FREQ=1000000
+
+; 4.0" IPS TFT ILI9488 / FT6236: https://vi.aliexpress.com/item/1005007082906950.html
+[env:mesh-tab-4-0-IPS-capacitive]
+extends = mesh_tab_base
+build_flags = ${mesh_tab_base.build_flags}
+  -D DISPLAY_SET_RESOLUTION
+  -D LGFX_SCREEN_WIDTH=320
+  -D LGFX_SCREEN_HEIGHT=480
+  -D LGFX_PANEL=HX8357B
+  -D LGFX_INVERT_COLOR=true
+  -D LGFX_RGB_ORDER=false
+  -D LGFX_DLEN_16BITS=false
+  -D LGFX_ROTATION=4
+  -D LGFX_TOUCH=FT5x06
+  -D SPI_FREQUENCY=30000000 ; may go higher upto 40/60/80 MHz
+  -D LGFX_TOUCH_I2C_PORT=0
+  -D LGFX_TOUCH_I2C_ADDR=0x38
+  -D LGFX_TOUCH_I2C_SDA=8
+  -D LGFX_TOUCH_I2C_SCL=9
+  -D LGFX_TOUCH_RST=7
+  -D LGFX_TOUCH_X_MIN=0
+  -D LGFX_TOUCH_X_MAX=319
+  -D LGFX_TOUCH_Y_MIN=0
+  -D LGFX_TOUCH_Y_MAX=479
+  -D LGFX_TOUCH_ROTATION=1
+  -D LGFX_TOUCH_I2C_FREQ=1000000
\ No newline at end of file
diff --git a/variants/diy/mesh-tab/variant.h b/variants/mesh-tab/variant.h
similarity index 67%
rename from variants/diy/mesh-tab/variant.h
rename to variants/mesh-tab/variant.h
index 0a23a3c36..533c931bc 100644
--- a/variants/diy/mesh-tab/variant.h
+++ b/variants/mesh-tab/variant.h
@@ -7,8 +7,8 @@
 
 // Analog pins
 #define BATTERY_PIN 4 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
-// ratio of voltage divider = 2.0
-#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage.
+// ratio of voltage divider (100k, 220k)
+#define ADC_MULTIPLIER 1.6 // 1.45 + 10% for correction of display undervoltage.
 #define ADC_CHANNEL ADC1_GPIO4_CHANNEL
 
 // LED
@@ -17,6 +17,9 @@
 // Button
 #define BUTTON_PIN 0
 
+// Button
+#define PIN_BUZZER 5
+
 // GPS
 #define GPS_RX_PIN 18
 #define GPS_TX_PIN 17
@@ -28,20 +31,26 @@
 #define SPI_CS 10
 #define SDCARD_CS 6
 
+// LORA MODULES
+#define USE_SX1262
+
 // LORA SPI
 #define LORA_SCK 36
 #define LORA_MISO 37
 #define LORA_MOSI 35
 #define LORA_CS 39
 
-// LORA MODULES
-#define USE_SX1262
-
 // LORA CONFIG
+#define LORA_DIO0 -1 // a No connect on the SX1262 module
+#define LORA_RESET 14
+#define LORA_DIO1 15 // SX1262 IRQ
+#define LORA_DIO2 40 // SX1262 BUSY
+#define LORA_DIO3    // Not connected on PCB, but internally on the TTGO SX1262
+
 #define SX126X_CS LORA_CS
-#define SX126X_DIO1 15
+#define SX126X_DIO1 LORA_DIO1
 #define SX126X_DIO2_AS_RF_SWITCH
-#define SX126X_BUSY 40
+#define SX126X_BUSY LORA_DIO2
 #define SX126X_RESET 14
 #define SX126X_RXEN 47
 #define SX126X_TXEN RADIOLIB_NC // Assuming that DIO2 is connected to TXEN pin

From e5accf4e1da0bc95f13a3af98caf74d434b75fab Mon Sep 17 00:00:00 2001
From: aussieklutz 
Date: Fri, 27 Dec 2024 23:16:08 +1000
Subject: [PATCH 120/132] Enable the autoconf settings for MPR121 based
 keyboards, to make it more flexible for varying implementations. (#5680)

Co-authored-by: Ben Meadors 
---
 src/input/MPR121Keyboard.cpp | 29 ++++++++++++++++-------------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/src/input/MPR121Keyboard.cpp b/src/input/MPR121Keyboard.cpp
index f35b942b1..9bca6801d 100644
--- a/src/input/MPR121Keyboard.cpp
+++ b/src/input/MPR121Keyboard.cpp
@@ -29,6 +29,8 @@
 #define _MPR121_REG_CONFIG1 0x5C
 #define _MPR121_REG_CONFIG2 0x5D
 #define _MPR121_REG_ELECTRODE_CONFIG 0x5E
+#define _MPR121_REG_AUTOCONF_CTRL0 0x7B
+#define _MPR121_REG_AUTOCONF_CTRL1 0x7C
 #define _MPR121_REG_SOFT_RESET 0x80
 
 #define _KEY_MASK 0x0FFF // Key mask for the first 12 bits
@@ -132,18 +134,18 @@ void MPR121Keyboard::reset()
     writeRegister(_MPR121_REG_ELECTRODE_CONFIG, 0x00);
     delay(100);
 
-    LOG_DEBUG("MPR121 Configure");
+    LOG_DEBUG("MPR121 Configuring");
     // Set touch release thresholds
     for (uint8_t i = 0; i < 12; i++) {
         // Set touch threshold
-        writeRegister(_MPR121_REG_TOUCH_THRESHOLD + (i * 2), 15);
+        writeRegister(_MPR121_REG_TOUCH_THRESHOLD + (i * 2), 10);
         delay(20);
         // Set release threshold
-        writeRegister(_MPR121_REG_RELEASE_THRESHOLD + (i * 2), 7);
+        writeRegister(_MPR121_REG_RELEASE_THRESHOLD + (i * 2), 5);
         delay(20);
     }
     // Configure filtering and baseline registers
-    writeRegister(_MPR121_REG_MAX_HALF_DELTA_RISING, 0x01);
+    writeRegister(_MPR121_REG_MAX_HALF_DELTA_RISING, 0x05);
     delay(20);
     writeRegister(_MPR121_REG_MAX_HALF_DELTA_FALLING, 0x01);
     delay(20);
@@ -153,7 +155,7 @@ void MPR121Keyboard::reset()
     delay(20);
     writeRegister(_MPR121_REG_NOISE_HALF_DELTA_TOUCHED, 0x00);
     delay(20);
-    writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_RISING, 0x0e);
+    writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_RISING, 0x05);
     delay(20);
     writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_FALLING, 0x01);
     delay(20);
@@ -165,18 +167,19 @@ void MPR121Keyboard::reset()
     delay(20);
     writeRegister(_MPR121_REG_FILTER_DELAY_COUNT_TOUCHED, 0x00);
     delay(20);
-    // Set Debounce to 0x02
-    writeRegister(_MPR121_REG_DEBOUNCE, 0x00);
+    writeRegister(_MPR121_REG_AUTOCONF_CTRL0, 0x04); // Auto-config enable
     delay(20);
-    // Set Filter1 itterations and discharge current 6x and 16uA respectively (0x10)
-    writeRegister(_MPR121_REG_CONFIG1, 0x10);
+    writeRegister(_MPR121_REG_AUTOCONF_CTRL1, 0x00); // Ensure no auto-config interrupt
     delay(20);
-    // Set CDT to 0.5us, Filter2 itterations to 4x, and Sample interval = 0 (0x20)
-    writeRegister(_MPR121_REG_CONFIG2, 0x20);
+    writeRegister(_MPR121_REG_DEBOUNCE, 0x02);
+    delay(20);
+    writeRegister(_MPR121_REG_CONFIG1, 0x20);
+    delay(20);
+    writeRegister(_MPR121_REG_CONFIG2, 0x21);
     delay(20);
     // Enter run mode by Seting partial filter calibration tracking, disable proximity detection, enable 12 channels
     writeRegister(_MPR121_REG_ELECTRODE_CONFIG,
-                  ECR_CALIBRATION_TRACK_FROM_PARTIAL_FILTER | ECR_PROXIMITY_DETECTION_OFF | ECR_TOUCH_DETECTION_12CH);
+                  ECR_CALIBRATION_TRACK_FROM_FULL_FILTER | ECR_PROXIMITY_DETECTION_OFF | ECR_TOUCH_DETECTION_12CH);
     delay(100);
     LOG_DEBUG("MPR121 Run");
     state = Idle;
@@ -427,4 +430,4 @@ void MPR121Keyboard::writeRegister(uint8_t reg, uint8_t value)
     if (writeCallback) {
         writeCallback(m_addr, data[0], &(data[1]), 1);
     }
-}
+}
\ No newline at end of file

From 51331179361f225d52fcdc60bc026fbb45bac459 Mon Sep 17 00:00:00 2001
From: Mictronics 
Date: Fri, 27 Dec 2024 16:12:26 +0100
Subject: [PATCH 121/132] Fix issue #5665. (#5678)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Fix issue #5665.

---------

Co-authored-by: Ben Meadors 
Co-authored-by: Thomas Göttgens 
Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
---
 src/mesh/aes-ccm.cpp | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp
index 8bc2989bf..a650ba2fc 100644
--- a/src/mesh/aes-ccm.cpp
+++ b/src/mesh/aes-ccm.cpp
@@ -18,12 +18,9 @@ static void WPA_PUT_BE16(uint8_t *a, uint16_t val)
 
 static void xor_aes_block(uint8_t *dst, const uint8_t *src)
 {
-    uint32_t *d = (uint32_t *)dst;
-    uint32_t *s = (uint32_t *)src;
-    *d++ ^= *s++;
-    *d++ ^= *s++;
-    *d++ ^= *s++;
-    *d++ ^= *s++;
+    for (uint8_t i = 0; i < AES_BLOCK_SIZE; i++) {
+        dst[i] ^= src[i];
+    }
 }
 static void aes_ccm_auth_start(size_t M, size_t L, const uint8_t *nonce, const uint8_t *aad, size_t aad_len, size_t plain_len,
                                uint8_t *x)

From 2b33be2feaa0ef1f79a72a51d779dc6eca563b69 Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Fri, 27 Dec 2024 15:49:24 -0600
Subject: [PATCH 122/132] Exclude health telemetry by macro (#5679)

---
 platformio.ini                                  | 9 ++++++---
 src/configuration.h                             | 1 +
 src/modules/Modules.cpp                         | 4 +++-
 src/modules/Telemetry/HealthTelemetry.cpp       | 4 ++--
 src/modules/Telemetry/HealthTelemetry.h         | 4 ++--
 src/modules/Telemetry/Sensor/MAX30102Sensor.cpp | 4 ++--
 src/modules/Telemetry/Sensor/MAX30102Sensor.h   | 4 ++--
 7 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/platformio.ini b/platformio.ini
index 41f1ca764..bf50b7646 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -81,7 +81,8 @@ build_flags = -Wno-missing-field-initializers
 	-DRADIOLIB_EXCLUDE_LORAWAN=1
 	-DMESHTASTIC_EXCLUDE_DROPZONE=1
 	-DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
-  -DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
+	-DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1
+  	-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
         #-DBUILD_EPOCH=$UNIX_TIME
         ;-D OLED_PL
 
@@ -153,7 +154,6 @@ lib_deps =
 	sparkfun/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library@1.2.13
 	ClosedCube OPT3001@1.1.2
 	emotibit/EmotiBit MLX90632@1.0.8
-	sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2
 	adafruit/Adafruit MLX90614 Library@2.1.5
 	https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502
 	boschsensortec/BME68x Sensor Library@1.1.40407
@@ -161,4 +161,7 @@ lib_deps =
 	mprograms/QMC5883LCompass@1.2.3
 	dfrobot/DFRobot_RTU@1.0.3
 	https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d
-        robtillaart/INA226@0.6.0
+	robtillaart/INA226@0.6.0
+
+	; Health Sensor Libraries
+	sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2
\ No newline at end of file
diff --git a/src/configuration.h b/src/configuration.h
index b5727508d..fbac2c1f6 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -312,6 +312,7 @@ along with this program.  If not, see .
 #define MESHTASTIC_EXCLUDE_AUDIO 1
 #define MESHTASTIC_EXCLUDE_DETECTIONSENSOR 1
 #define MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR 1
+#define MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY 1
 #define MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION 1
 #define MESHTASTIC_EXCLUDE_PAXCOUNTER 1
 #define MESHTASTIC_EXCLUDE_POWER_TELEMETRY 1
diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp
index 9baed824c..58158ad3c 100644
--- a/src/modules/Modules.cpp
+++ b/src/modules/Modules.cpp
@@ -195,11 +195,13 @@ void setupModules()
         if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first > 0) {
             new AirQualityTelemetryModule();
         }
+#if !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY
         if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX30102].first > 0 ||
             nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MLX90614].first > 0) {
             new HealthTelemetryModule();
         }
 #endif
+#endif
 #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
         new PowerTelemetryModule();
 #endif
@@ -245,4 +247,4 @@ void setupModules()
     // NOTE! This module must be added LAST because it likes to check for replies from other modules and avoid sending extra
     // acks
     routingModule = new RoutingModule();
-}
+}
\ No newline at end of file
diff --git a/src/modules/Telemetry/HealthTelemetry.cpp b/src/modules/Telemetry/HealthTelemetry.cpp
index 22534e9f5..1b9b49813 100644
--- a/src/modules/Telemetry/HealthTelemetry.cpp
+++ b/src/modules/Telemetry/HealthTelemetry.cpp
@@ -1,6 +1,6 @@
 #include "configuration.h"
 
-#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY && !defined(ARCH_PORTDUINO)
 
 #include "../mesh/generated/meshtastic/telemetry.pb.h"
 #include "Default.h"
@@ -246,4 +246,4 @@ bool HealthTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
     return false;
 }
 
-#endif
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/HealthTelemetry.h b/src/modules/Telemetry/HealthTelemetry.h
index fe84f2d27..01e4c2372 100644
--- a/src/modules/Telemetry/HealthTelemetry.h
+++ b/src/modules/Telemetry/HealthTelemetry.h
@@ -1,6 +1,6 @@
 #include "configuration.h"
 
-#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY && !defined(ARCH_PORTDUINO)
 
 #pragma once
 #include "../mesh/generated/meshtastic/telemetry.pb.h"
@@ -57,4 +57,4 @@ class HealthTelemetryModule : private concurrency::OSThread, public ProtobufModu
     uint32_t sensor_read_error_count = 0;
 };
 
-#endif
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp b/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp
index 88128a6db..f99956925 100644
--- a/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp
@@ -1,6 +1,6 @@
 #include "configuration.h"
 
-#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY && !defined(ARCH_PORTDUINO)
 
 #include "../mesh/generated/meshtastic/telemetry.pb.h"
 #include "MAX30102Sensor.h"
@@ -80,4 +80,4 @@ bool MAX30102Sensor::getMetrics(meshtastic_Telemetry *measurement)
     return true;
 }
 
-#endif
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/MAX30102Sensor.h b/src/modules/Telemetry/Sensor/MAX30102Sensor.h
index 426d9d365..026e30ed0 100644
--- a/src/modules/Telemetry/Sensor/MAX30102Sensor.h
+++ b/src/modules/Telemetry/Sensor/MAX30102Sensor.h
@@ -1,6 +1,6 @@
 #include "configuration.h"
 
-#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY && !defined(ARCH_PORTDUINO)
 
 #include "../mesh/generated/meshtastic/telemetry.pb.h"
 #include "TelemetrySensor.h"
@@ -23,4 +23,4 @@ class MAX30102Sensor : public TelemetrySensor
     virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
 };
 
-#endif
+#endif
\ No newline at end of file

From b2808063d0488bcd2a2c3b17aa134ecf865513eb Mon Sep 17 00:00:00 2001
From: Erayd 
Date: Sat, 28 Dec 2024 11:52:18 +1300
Subject: [PATCH 123/132] Add new ROUTER_LATE role (#5528)

Will always rebroadcast packets, but will do so after all other modes.
Intended for router nodes that are there to provide additional coverage
in areas not already covered by other routers, or to bridge around
problematic terrain, but should not be given priority over other routers
in order to avoid unnecessaraily consuming hops.

By default, this role will rebroadcast during the normal client window.
However, if another node is overheard rebroadcasting the packet, then it
will be moved to a second window *after* the normal client one, with the
same timing behaviour.
---
 src/mesh/FloodingRouter.cpp    |  6 ++-
 src/mesh/MeshPacketQueue.cpp   | 26 +++++++++++--
 src/mesh/MeshPacketQueue.h     |  4 +-
 src/mesh/RadioInterface.cpp    | 20 ++++++++--
 src/mesh/RadioInterface.h      |  9 +++++
 src/mesh/RadioLibInterface.cpp | 70 +++++++++++++++++++++++++---------
 src/mesh/RadioLibInterface.h   | 15 +++++++-
 7 files changed, 120 insertions(+), 30 deletions(-)

diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp
index e29c596df..f94540905 100644
--- a/src/mesh/FloodingRouter.cpp
+++ b/src/mesh/FloodingRouter.cpp
@@ -24,11 +24,15 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
         printPacket("Ignore dupe incoming msg", p);
         rxDupe++;
         if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
-            config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
+            config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
+            config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_LATE) {
             // cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
             if (Router::cancelSending(p->from, p->id))
                 txRelayCanceled++;
         }
+        if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE && iface) {
+            iface->clampToLateRebroadcastWindow(getFrom(p), p->id);
+        }
 
         /* If the original transmitter is doing retransmissions (hopStart equals hopLimit) for a reliable transmission, e.g., when
         the ACK got lost, we will handle the packet again to make sure it gets an ACK to its packet. */
diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp
index 99ef41c1e..d7ee65800 100644
--- a/src/mesh/MeshPacketQueue.cpp
+++ b/src/mesh/MeshPacketQueue.cpp
@@ -16,6 +16,12 @@ inline uint32_t getPriority(const meshtastic_MeshPacket *p)
 bool CompareMeshPacketFunc(const meshtastic_MeshPacket *p1, const meshtastic_MeshPacket *p2)
 {
     assert(p1 && p2);
+
+    // If one packet is in the late transmit window, prefer the other one
+    if ((bool)p1->tx_after != (bool)p2->tx_after) {
+        return !p1->tx_after;
+    }
+
     auto p1p = getPriority(p1), p2p = getPriority(p2);
     // If priorities differ, use that
     // for equal priorities, prefer packets already on mesh.
@@ -94,11 +100,11 @@ meshtastic_MeshPacket *MeshPacketQueue::getFront()
 }
 
 /** Attempt to find and remove a packet from this queue.  Returns a pointer to the removed packet, or NULL if not found */
-meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
+meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id, bool tx_normal, bool tx_late)
 {
     for (auto it = queue.begin(); it != queue.end(); it++) {
         auto p = (*it);
-        if (getFrom(p) == from && p->id == id) {
+        if (getFrom(p) == from && p->id == id && ((tx_normal && !p->tx_after) || (tx_late && p->tx_after))) {
             queue.erase(it);
             return p;
         }
@@ -114,9 +120,10 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p)
     if (queue.empty()) {
         return false; // No packets to replace
     }
+
     // Check if the packet at the back has a lower priority than the new packet
     auto &backPacket = queue.back();
-    if (backPacket->priority < p->priority) {
+    if (!backPacket->tx_after && backPacket->priority < p->priority) {
         // Remove the back packet
         packetPool.release(backPacket);
         queue.pop_back();
@@ -125,6 +132,19 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p)
         return true;
     }
 
+    if (backPacket->tx_after) {
+        // Check if there's a non-late packet with lower priority
+        auto it = queue.end();
+        auto refPacket = *--it;
+        for (; refPacket->tx_after && it != queue.begin(); refPacket = *--it)
+            ;
+        if (!refPacket->tx_after && refPacket->priority < p->priority) {
+            packetPool.release(refPacket);
+            enqueue(refPacket);
+            return true;
+        }
+    }
+
     // If the back packet's priority is not lower, no replacement occurs
     return false;
 }
\ No newline at end of file
diff --git a/src/mesh/MeshPacketQueue.h b/src/mesh/MeshPacketQueue.h
index 3c28fc5ce..b41a214b9 100644
--- a/src/mesh/MeshPacketQueue.h
+++ b/src/mesh/MeshPacketQueue.h
@@ -36,5 +36,5 @@ class MeshPacketQueue
     meshtastic_MeshPacket *getFront();
 
     /** Attempt to find and remove a packet from this queue.  Returns the packet which was removed from the queue */
-    meshtastic_MeshPacket *remove(NodeNum from, PacketId id);
-};
+    meshtastic_MeshPacket *remove(NodeNum from, PacketId id, bool tx_normal = true, bool tx_late = true);
+};
\ No newline at end of file
diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp
index 5a18ab0c0..b1403f3b6 100644
--- a/src/mesh/RadioInterface.cpp
+++ b/src/mesh/RadioInterface.cpp
@@ -254,8 +254,8 @@ uint32_t RadioInterface::getTxDelayMsec()
     return random(0, pow(2, CWsize)) * slotTimeMsec;
 }
 
-/** The delay to use when we want to flood a message */
-uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
+/** The CW size to use when calculating SNR_based delays */
+uint8_t RadioInterface::getCWsize(float snr)
 {
     // The minimum value for a LoRa SNR
     const uint32_t SNR_MIN = -20;
@@ -263,10 +263,24 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
     // The maximum value for a LoRa SNR
     const uint32_t SNR_MAX = 15;
 
+    return map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax);
+}
+
+/** The worst-case SNR_based packet delay */
+uint32_t RadioInterface::getTxDelayMsecWeightedWorst(float snr)
+{
+    uint8_t CWsize = getCWsize(snr);
+    // offset the maximum delay for routers: (2 * CWmax * slotTimeMsec)
+    return (2 * CWmax * slotTimeMsec) + pow(2, CWsize) * slotTimeMsec;
+}
+
+/** The delay to use when we want to flood a message */
+uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
+{
     //  high SNR = large CW size (Long Delay)
     //  low SNR = small CW size (Short Delay)
     uint32_t delay = 0;
-    uint8_t CWsize = map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax);
+    uint8_t CWsize = getCWsize(snr);
     // LOG_DEBUG("rx_snr of %f so setting CWsize to:%d", snr, CWsize);
     if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
         config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h
index 89a4c7087..652b2269c 100644
--- a/src/mesh/RadioInterface.h
+++ b/src/mesh/RadioInterface.h
@@ -173,9 +173,18 @@ class RadioInterface
     /** The delay to use when we want to send something */
     uint32_t getTxDelayMsec();
 
+    /** The CW to use when calculating SNR_based delays */
+    uint8_t getCWsize(float snr);
+
+    /** The worst-case SNR_based packet delay */
+    uint32_t getTxDelayMsecWeightedWorst(float snr);
+
     /** The delay to use when we want to flood a message. Use a weighted scale based on SNR */
     uint32_t getTxDelayMsecWeighted(float snr);
 
+    /** If the packet is not already in the late rebroadcast window, move it there */
+    virtual void clampToLateRebroadcastWindow(NodeNum from, PacketId id) { return; }
+
     /**
      * Calculate airtime per
      * https://www.rs-online.com/designspark/rel-assets/ds-assets/uploads/knowledge-items/application-notes-for-the-internet-of-things/LoRa%20Design%20Guide.pdf
diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp
index e416160eb..997b1d6fe 100644
--- a/src/mesh/RadioLibInterface.cpp
+++ b/src/mesh/RadioLibInterface.cpp
@@ -235,12 +235,12 @@ void RadioLibInterface::onNotify(uint32_t notification)
     case ISR_TX:
         handleTransmitInterrupt();
         startReceive();
-        startTransmitTimer();
+        setTransmitDelay();
         break;
     case ISR_RX:
         handleReceiveInterrupt();
         startReceive();
-        startTransmitTimer();
+        setTransmitDelay();
         break;
     case TRANSMIT_DELAY_COMPLETED:
 
@@ -250,23 +250,32 @@ void RadioLibInterface::onNotify(uint32_t notification)
             if (!canSendImmediately()) {
                 setTransmitDelay(); // currently Rx/Tx-ing: reset random delay
             } else {
-                if (isChannelActive()) { // check if there is currently a LoRa packet on the channel
-                    startReceive();      // try receiving this packet, afterwards we'll be trying to transmit again
-                    setTransmitDelay();
+                meshtastic_MeshPacket *txp = txQueue.getFront();
+                assert(txp);
+                long delay_remaining = txp->tx_after ? txp->tx_after - millis() : 0;
+                if (delay_remaining > 0) {
+                    // There's still some delay pending on this packet, so resume waiting for it to elapse
+                    notifyLater(delay_remaining, TRANSMIT_DELAY_COMPLETED, false);
                 } else {
-                    // Send any outgoing packets we have ready as fast as possible to keep the time between channel scan and
-                    // actual transmission as short as possible
-                    meshtastic_MeshPacket *txp = txQueue.dequeue();
-                    assert(txp);
-                    bool sent = startSend(txp);
-                    if (sent) {
-                        // Packet has been sent, count it toward our TX airtime utilization.
-                        uint32_t xmitMsec = getPacketTime(txp);
-                        airTime->logAirtime(TX_LOG, xmitMsec);
+                    if (isChannelActive()) { // check if there is currently a LoRa packet on the channel
+                        startReceive();      // try receiving this packet, afterwards we'll be trying to transmit again
+                        setTransmitDelay();
+                    } else {
+                        // Send any outgoing packets we have ready as fast as possible to keep the time between channel scan and
+                        // actual transmission as short as possible
+                        txp = txQueue.dequeue();
+                        assert(txp);
+                        bool sent = startSend(txp);
+                        if (sent) {
+                            // Packet has been sent, count it toward our TX airtime utilization.
+                            uint32_t xmitMsec = getPacketTime(txp);
+                            airTime->logAirtime(TX_LOG, xmitMsec);
+                        }
                     }
                 }
             }
         } else {
+            // Do nothing, because the queue is empty
         }
         break;
     default:
@@ -277,15 +286,24 @@ void RadioLibInterface::onNotify(uint32_t notification)
 void RadioLibInterface::setTransmitDelay()
 {
     meshtastic_MeshPacket *p = txQueue.getFront();
+    if (!p) {
+        return; // noop if there's nothing in the queue
+    }
+
     // We want all sending/receiving to be done by our daemon thread.
     // We use a delay here because this packet might have been sent in response to a packet we just received.
     // So we want to make sure the other side has had a chance to reconfigure its radio.
 
-    /* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally.
-     *   This assumption is valid because of the offset generated by the radio to account for the noise
-     *   floor.
-     */
-    if (p->rx_snr == 0 && p->rx_rssi == 0) {
+    if (p->tx_after) {
+        unsigned long add_delay = p->rx_rssi ? getTxDelayMsecWeighted(p->rx_snr) : getTxDelayMsec();
+        unsigned long now = millis();
+        p->tx_after = max(p->tx_after + add_delay, now + add_delay);
+        notifyLater(now - p->tx_after, TRANSMIT_DELAY_COMPLETED, false);
+    } else if (p->rx_snr == 0 && p->rx_rssi == 0) {
+        /* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally.
+         *   This assumption is valid because of the offset generated by the radio to account for the noise
+         *   floor.
+         */
         startTransmitTimer(true);
     } else {
         // If there is a SNR, start a timer scaled based on that SNR.
@@ -312,6 +330,20 @@ void RadioLibInterface::startTransmitTimerSNR(float snr)
     }
 }
 
+/**
+ * If the packet is not already in the late rebroadcast window, move it there
+ */
+void RadioLibInterface::clampToLateRebroadcastWindow(NodeNum from, PacketId id)
+{
+    // Look for non-late packets only, so we don't do this twice!
+    meshtastic_MeshPacket *p = txQueue.remove(from, id, true, false);
+    if (p) {
+        p->tx_after = millis() + getTxDelayMsecWeightedWorst(p->rx_snr);
+        txQueue.enqueue(p);
+        LOG_DEBUG("Move existing queued packet to the late rebroadcast window %dms from now", p->tx_after - millis());
+    }
+}
+
 void RadioLibInterface::handleTransmitInterrupt()
 {
     // This can be null if we forced the device to enter standby mode.  In that case
diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h
index d6101ae37..dff58c9ad 100644
--- a/src/mesh/RadioLibInterface.h
+++ b/src/mesh/RadioLibInterface.h
@@ -140,10 +140,16 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
      * doing the transmit */
     void setTransmitDelay();
 
-    /** random timer with certain min. and max. settings */
+    /**
+     * random timer with certain min. and max. settings
+     * @return Timestamp after which the packet may be sent
+     */
     void startTransmitTimer(bool withDelay = true);
 
-    /** timer scaled to SNR of to be flooded packet */
+    /**
+     * timer scaled to SNR of to be flooded packet
+     * @return Timestamp after which the packet may be sent
+     */
     void startTransmitTimerSNR(float snr);
 
     void handleTransmitInterrupt();
@@ -193,4 +199,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
     virtual void setStandby();
 
     const char *radioLibErr = "RadioLib err=";
+
+    /**
+     * If the packet is not already in the late rebroadcast window, move it there
+     */
+    void clampToLateRebroadcastWindow(NodeNum from, PacketId id);
 };
\ No newline at end of file

From ad726ad684d1e608b9428c5f72e4785f958eb2e2 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Sun, 29 Dec 2024 01:29:58 +1100
Subject: [PATCH 124/132] More meshtab cherry-pick (#5681)

* board definition for mesh-tab (not yet used)

* use board definition for mesh-tab

---------

Co-authored-by: mverch67 
---
 boards/mesh-tab.json | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 boards/mesh-tab.json

diff --git a/boards/mesh-tab.json b/boards/mesh-tab.json
new file mode 100644
index 000000000..52c65bf77
--- /dev/null
+++ b/boards/mesh-tab.json
@@ -0,0 +1,42 @@
+{
+  "build": {
+    "arduino": {
+      "ldscript": "esp32s3_out.ld",
+      "partitions": "default_16MB.csv",
+      "memory_type": "qio_qspi"
+    },
+    "core": "esp32",
+    "extra_flags": [
+      "-DBOARD_HAS_PSRAM",
+      "-DARDUINO_USB_CDC_ON_BOOT=1",
+      "-DARDUINO_USB_MODE=0",
+      "-DARDUINO_RUNNING_CORE=1",
+      "-DARDUINO_EVENT_RUNNING_CORE=1"
+    ],
+    "f_cpu": "240000000L",
+    "f_flash": "80000000L",
+    "flash_mode": "qio",
+    "hwids": [["0x303A", "0x80D6"]],
+    "mcu": "esp32s3",
+    "variant": "mesh-tab"
+  },
+  "connectivity": ["wifi", "bluetooth", "lora"],
+  "debug": {
+    "default_tool": "esp-builtin",
+    "onboard_tools": ["esp-builtin"],
+    "openocd_target": "esp32s3.cfg"
+  },
+  "frameworks": ["arduino", "espidf"],
+  "name": "ESP32-S3 WROOM-1 N16R2 (16 MB FLASH, 2 MB PSRAM)",
+  "upload": {
+    "flash_size": "16MB",
+    "maximum_ram_size": 327680,
+    "maximum_size": 16777216,
+    "use_1200bps_touch": true,
+    "wait_for_upload_port": true,
+    "require_upload_port": true,
+    "speed": 460800
+  },
+  "url": "https://github.com/valzzu/Mesh-Tab",
+  "vendor": "Espressif"
+}

From 31a5b9c122ee29c20330b8c71e25e693ab6c7aeb Mon Sep 17 00:00:00 2001
From: Ben Meadors 
Date: Sat, 28 Dec 2024 08:30:53 -0600
Subject: [PATCH 125/132] Cleanup and exclude external sensor macro to make
 T1000-E binaries much smaller

---
 src/modules/AdminModule.cpp                    |  1 -
 src/modules/Telemetry/EnvironmentTelemetry.cpp | 15 ++++++++++-----
 variants/tracker-t1000-e/platformio.ini        |  3 ++-
 3 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index 6b19f04e3..6ca362061 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -1106,7 +1106,6 @@ bool AdminModule::messageIsResponse(const meshtastic_AdminMessage *r)
         r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag ||
         r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag ||
         r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag ||
-        r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag ||
         r->which_payload_variant == meshtastic_AdminMessage_get_ui_config_response_tag)
         return true;
     else
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index 92d964f7d..008da5c71 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -18,6 +18,7 @@
 #include 
 #include 
 
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
 // Sensors
 #include "Sensor/AHT10.h"
 #include "Sensor/BME280Sensor.h"
@@ -36,7 +37,6 @@
 #include "Sensor/SHT31Sensor.h"
 #include "Sensor/SHT4XSensor.h"
 #include "Sensor/SHTC3Sensor.h"
-#include "Sensor/T1000xSensor.h"
 #include "Sensor/TSL2591Sensor.h"
 #include "Sensor/VEML7700Sensor.h"
 
@@ -58,11 +58,12 @@ MLX90632Sensor mlx90632Sensor;
 DFRobotLarkSensor dfRobotLarkSensor;
 NAU7802Sensor nau7802Sensor;
 BMP3XXSensor bmp3xxSensor;
+CGRadSensSensor cgRadSens;
+#endif
 #ifdef T1000X_SENSOR_EN
+#include "Sensor/T1000xSensor.h"
 T1000xSensor t1000xSensor;
 #endif
-CGRadSensSensor cgRadSens;
-
 #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
 
@@ -104,7 +105,7 @@ int32_t EnvironmentTelemetryModule::runOnce()
             // therefore, we should only enable the sensor loop if measurement is also enabled
 #ifdef T1000X_SENSOR_EN
             result = t1000xSensor.runOnce();
-#else
+#elif !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
             if (dfRobotLarkSensor.hasSensor())
                 result = dfRobotLarkSensor.runOnce();
             if (bmp085Sensor.hasSensor())
@@ -159,8 +160,10 @@ int32_t EnvironmentTelemetryModule::runOnce()
         if (!moduleConfig.telemetry.environment_measurement_enabled) {
             return disable();
         } else {
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
             if (bme680Sensor.hasSensor())
                 result = bme680Sensor.runTrigger();
+#endif
         }
 
         if (((lastSentToMesh == 0) ||
@@ -499,6 +502,7 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
                                                                                  meshtastic_AdminMessage *response)
 {
     AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED;
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
     if (dfRobotLarkSensor.hasSensor()) {
         result = dfRobotLarkSensor.handleAdminMessage(mp, request, response);
         if (result != AdminMessageHandleResult::NOT_HANDLED)
@@ -609,7 +613,8 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
         if (result != AdminMessageHandleResult::NOT_HANDLED)
             return result;
     }
+#endif
     return result;
 }
 
-#endif
+#endif
\ No newline at end of file
diff --git a/variants/tracker-t1000-e/platformio.ini b/variants/tracker-t1000-e/platformio.ini
index 075811610..0bce9fbb5 100644
--- a/variants/tracker-t1000-e/platformio.ini
+++ b/variants/tracker-t1000-e/platformio.ini
@@ -4,6 +4,7 @@ board = tracker-t1000-e
 build_flags = ${nrf52840_base.build_flags} -Ivariants/tracker-t1000-e -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DTRACKER_T1000_E
   -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard"
   -DGPS_POWER_TOGGLE
+  -DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL=1
 board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld
 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/tracker-t1000-e>
 lib_deps = 
@@ -11,4 +12,4 @@ lib_deps =
   https://github.com/meshtastic/QMA6100P_Arduino_Library.git#14c900b8b2e4feaac5007a7e41e0c1b7f0841136
 debug_tool = jlink
 ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
-upload_protocol = nrfutil
+upload_protocol = nrfutil
\ No newline at end of file

From 43d6b31603691e8fec01ade16c7b292bb07b59f2 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Sun, 29 Dec 2024 01:31:54 +1100
Subject: [PATCH 126/132] TFT branch grab-bag (#5683)

Selection of minor edits from the TFT branch that are too
challenging to cherry-pick cleanly, including:
* introducing the HAS_TFT flag
* fixing pins in unphone
* Adding pinterdevice to portduino settings
---
 src/configuration.h                      | 5 ++++-
 src/graphics/Screen.cpp                  | 2 +-
 src/graphics/TFTDisplay.cpp              | 2 +-
 src/main.cpp                             | 5 ++++-
 src/mesh/PhoneAPI.h                      | 8 ++++----
 src/mesh/wifi/WiFiAPClient.cpp           | 2 +-
 src/modules/CannedMessageModule.h        | 6 ++++--
 src/modules/Modules.cpp                  | 2 +-
 src/platform/portduino/PortduinoGlue.cpp | 6 +++++-
 src/platform/portduino/PortduinoGlue.h   | 3 ++-
 variants/unphone/pins_arduino.h          | 8 --------
 11 files changed, 27 insertions(+), 22 deletions(-)

diff --git a/src/configuration.h b/src/configuration.h
index fbac2c1f6..994f1e72e 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -250,6 +250,9 @@ along with this program.  If not, see .
 #ifndef HAS_SCREEN
 #define HAS_SCREEN 0
 #endif
+#ifndef HAS_TFT
+#define HAS_TFT 0
+#endif
 #ifndef HAS_WIRE
 #define HAS_WIRE 0
 #endif
@@ -362,4 +365,4 @@ along with this program.  If not, see .
 #endif
 
 #include "DebugConfiguration.h"
-#include "RF95Configuration.h"
\ No newline at end of file
+#include "RF95Configuration.h"
diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 27ea6f414..4cfb8701e 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -1718,7 +1718,7 @@ void Screen::setup()
 #endif
     serialSinceMsec = millis();
 
-#if ARCH_PORTDUINO
+#if ARCH_PORTDUINO && !HAS_TFT
     if (settingsMap[touchscreenModule]) {
         touchScreenImpl1 =
             new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast(dispdev)->getTouch);
diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp
index 87c3f7de9..4f2af670b 100644
--- a/src/graphics/TFTDisplay.cpp
+++ b/src/graphics/TFTDisplay.cpp
@@ -347,7 +347,7 @@ static LGFX *tft = nullptr;
 #include  // Graphics and font library for ILI9342 driver chip
 
 static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
-#elif ARCH_PORTDUINO && HAS_SCREEN != 0
+#elif ARCH_PORTDUINO && HAS_SCREEN != 0 && !HAS_TFT
 #include  // Graphics and font library for ST7735 driver chip
 
 class LGFX : public lgfx::LGFX_Device
diff --git a/src/main.cpp b/src/main.cpp
index f4bb11535..5982e709d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -40,6 +40,7 @@
 #include 
 
 #ifdef ARCH_ESP32
+#include "freertosinc.h"
 #if !MESHTASTIC_EXCLUDE_WEBSERVER
 #include "mesh/http/WebServer.h"
 #endif
@@ -173,6 +174,8 @@ std::pair nodeTelemetrySensorsMap[_meshtastic_TelemetrySenso
 
 Router *router = NULL; // Users of router don't care what sort of subclass implements that API
 
+const char *firmware_version = optstr(APP_VERSION_SHORT);
+
 const char *getDeviceName()
 {
     uint8_t dmac[6];
@@ -1275,4 +1278,4 @@ void loop()
         mainDelay.delay(delayMsec);
     }
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h
index 31538a0ab..681b244c8 100644
--- a/src/mesh/PhoneAPI.h
+++ b/src/mesh/PhoneAPI.h
@@ -149,6 +149,9 @@ class PhoneAPI
      */
     virtual void onNowHasData(uint32_t fromRadioNum) {}
 
+    /// begin a new connection
+    void handleStartConfig();
+
   private:
     void releasePhonePacket();
 
@@ -158,9 +161,6 @@ class PhoneAPI
 
     void releaseClientNotification();
 
-    /// begin a new connection
-    void handleStartConfig();
-
     bool wasSeenRecently(uint32_t packetId);
 
     /**
@@ -171,4 +171,4 @@ class PhoneAPI
 
     /// If the mesh service tells us fromNum has changed, tell the phone
     virtual int onNotify(uint32_t newValue) override;
-};
\ No newline at end of file
+};
diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp
index f9e5d1cc9..38aa2e2a2 100644
--- a/src/mesh/wifi/WiFiAPClient.cpp
+++ b/src/mesh/wifi/WiFiAPClient.cpp
@@ -430,4 +430,4 @@ uint8_t getWifiDisconnectReason()
 {
     return wifiDisconnectReason;
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h
index fd9ffc9b6..a91933a0f 100644
--- a/src/modules/CannedMessageModule.h
+++ b/src/modules/CannedMessageModule.h
@@ -117,8 +117,10 @@ class CannedMessageModule : public SinglePortModule, public ObservableshouldDraw(); }
     virtual Observable *getUIFrameObservable() override { return this; }
-    virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
     virtual bool interceptingKeyboardInput() override;
+#if !HAS_TFT
+    virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
+#endif
     virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
                                                                  meshtastic_AdminMessage *request,
                                                                  meshtastic_AdminMessage *response) override;
@@ -228,4 +230,4 @@ class CannedMessageModule : public SinglePortModule, public Observableinit();
 #endif // INPUTBROKER_MATRIX_TYPE
 #endif // HAS_BUTTON
-#if ARCH_PORTDUINO
+#if ARCH_PORTDUINO && !HAS_TFT
         aLinuxInputImpl = new LinuxInputImpl();
         aLinuxInputImpl->init();
 #endif
diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index 82fd8de66..4fadc3ca9 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -153,6 +153,7 @@ void portduinoSetup()
     std::string gpioChipName = "gpiochip";
     settingsStrings[i2cdev] = "";
     settingsStrings[keyboardDevice] = "";
+    settingsStrings[pointerDevice] = "";
     settingsStrings[webserverrootpath] = "";
     settingsStrings[spidev] = "";
     settingsStrings[displayspidev] = "";
@@ -455,6 +456,8 @@ bool loadConfig(const char *configPath)
                 settingsMap[displayPanel] = ili9341;
             else if (yamlConfig["Display"]["Panel"].as("") == "ILI9342")
                 settingsMap[displayPanel] = ili9342;
+            else if (yamlConfig["Display"]["Panel"].as("") == "ILI9486")
+                settingsMap[displayPanel] = ili9486;
             else if (yamlConfig["Display"]["Panel"].as("") == "ILI9488")
                 settingsMap[displayPanel] = ili9488;
             else if (yamlConfig["Display"]["Panel"].as("") == "HX8357D")
@@ -515,6 +518,7 @@ bool loadConfig(const char *configPath)
         }
         if (yamlConfig["Input"]) {
             settingsStrings[keyboardDevice] = (yamlConfig["Input"]["KeyboardDevice"]).as("");
+            settingsStrings[pointerDevice] = (yamlConfig["Input"]["PointerDevice"]).as("");
         }
 
         if (yamlConfig["Webserver"]) {
@@ -570,4 +574,4 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac)
     } else {
         return false;
     }
-}
\ No newline at end of file
+}
diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h
index 9cf9b6678..5bc07df6a 100644
--- a/src/platform/portduino/PortduinoGlue.h
+++ b/src/platform/portduino/PortduinoGlue.h
@@ -55,6 +55,7 @@ enum configNames {
     displayOffsetY,
     displayInvert,
     keyboardDevice,
+    pointerDevice,
     logoutputlevel,
     traceFilename,
     webserver,
@@ -66,7 +67,7 @@ enum configNames {
     config_directory,
     mac_address
 };
-enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9488, hx8357d };
+enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9486, ili9488, hx8357d };
 enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 };
 enum { level_error, level_warn, level_info, level_debug, level_trace };
 
diff --git a/variants/unphone/pins_arduino.h b/variants/unphone/pins_arduino.h
index c4e9add1c..74067359f 100644
--- a/variants/unphone/pins_arduino.h
+++ b/variants/unphone/pins_arduino.h
@@ -6,14 +6,6 @@
 #define USB_VID 0x16D0
 #define USB_PID 0x1178
 
-#define EXTERNAL_NUM_INTERRUPTS 46
-#define NUM_DIGITAL_PINS 48
-#define NUM_ANALOG_INPUTS 20
-
-#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1)
-#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1)
-#define digitalPinHasPWM(p) (p < 46)
-
 #define LED_BUILTIN 13
 #define BUILTIN_LED LED_BUILTIN // backward compatibility
 

From 89ebafc8b8d85ee12002b9c574f8472d99e9ec50 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Sun, 29 Dec 2024 01:32:24 +1100
Subject: [PATCH 127/132] Minor TFT branch cherry-picks (#5682)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* update indicator board

* Fixed the issue that indicator device uploads via rp2040 serial port in some cases.

* esp debug logs

* USB mode=1 messed up the debug log

* dummy for config transfer (#5154)

---------

Co-authored-by: mverch67 
Co-authored-by: virgil 
Co-authored-by: Thomas Göttgens 
---
 boards/seeed-sensecap-indicator.json | 8 +++++---
 boards/t-deck.json                   | 2 +-
 src/mesh/PhoneAPI.cpp                | 4 ++++
 src/modules/AdminModule.cpp          | 7 +++++++
 4 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/boards/seeed-sensecap-indicator.json b/boards/seeed-sensecap-indicator.json
index 3fc57126f..0a02fc882 100644
--- a/boards/seeed-sensecap-indicator.json
+++ b/boards/seeed-sensecap-indicator.json
@@ -15,10 +15,12 @@
     ],
     "f_cpu": "240000000L",
     "f_flash": "80000000L",
+    "f_boot": "120000000L",
+    "boot": "qio",
     "flash_mode": "qio",
     "hwids": [["0x1A86", "0x7523"]],
     "mcu": "esp32s3",
-    "variant": "esp32s3r8"
+    "variant": "esp32s3"
   },
   "connectivity": ["wifi", "bluetooth", "lora"],
   "debug": {
@@ -32,9 +34,9 @@
     "flash_size": "8MB",
     "maximum_ram_size": 327680,
     "maximum_size": 8388608,
-    "require_upload_port": true,
+    "require_upload_port": false,
     "use_1200bps_touch": true,
-    "wait_for_upload_port": true,
+    "wait_for_upload_port": false,
     "speed": 921600
   },
   "url": "https://www.seeedstudio.com/Indicator-for-Meshtastic.html",
diff --git a/boards/t-deck.json b/boards/t-deck.json
index d62ec48e6..b112921b9 100644
--- a/boards/t-deck.json
+++ b/boards/t-deck.json
@@ -10,7 +10,7 @@
       "-DARDUINO_USB_CDC_ON_BOOT=1",
       "-DARDUINO_USB_MODE=0",
       "-DARDUINO_RUNNING_CORE=1",
-      "-DARDUINO_EVENT_RUNNING_CORE=0"
+      "-DARDUINO_EVENT_RUNNING_CORE=1"
     ],
     "f_cpu": "240000000L",
     "f_flash": "80000000L",
diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp
index 36045bcf9..8c1ba74c7 100644
--- a/src/mesh/PhoneAPI.cpp
+++ b/src/mesh/PhoneAPI.cpp
@@ -164,6 +164,7 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
  *
  * Our sending states progress in the following sequence (the client apps ASSUME THIS SEQUENCE, DO NOT CHANGE IT):
     STATE_SEND_MY_INFO, // send our my info record
+    STATE_SEND_UIDATA,
     STATE_SEND_OWN_NODEINFO,
     STATE_SEND_METADATA,
     STATE_SEND_CHANNELS
@@ -290,6 +291,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
             LOG_DEBUG("Send config: sessionkey");
             fromRadioScratch.config.which_payload_variant = meshtastic_Config_sessionkey_tag;
             break;
+        case meshtastic_Config_device_ui_tag: // NOOP!
+            fromRadioScratch.config.which_payload_variant = meshtastic_Config_device_ui_tag;
+            break;
         default:
             LOG_ERROR("Unknown config type %d", config_state);
         }
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index 6ca362061..fc3b914e5 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -633,6 +633,9 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
             requiresReboot = false;
 
         break;
+    case meshtastic_Config_device_ui_tag:
+        // NOOP! This is handled by handleStoreDeviceUIConfig
+        break;
     }
     if (requiresReboot && !hasOpenEditTransaction) {
         disableBluetooth();
@@ -795,6 +798,10 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32
             LOG_INFO("Get config: Sessionkey");
             res.get_config_response.which_payload_variant = meshtastic_Config_sessionkey_tag;
             break;
+        case meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG:
+            // NOOP! This is handled by handleGetDeviceUIConfig
+            res.get_config_response.which_payload_variant = meshtastic_Config_device_ui_tag;
+            break;
         }
         // NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior.
         // So even if we internally use 0 to represent 'use default' we still need to send the value we are

From a8e2446f000e32b66e4808e974c059f7b84f7c60 Mon Sep 17 00:00:00 2001
From: Jonathan Bennett 
Date: Sat, 28 Dec 2024 18:05:25 -0600
Subject: [PATCH 128/132] Initialize array to 0s (#5688)

---
 src/platform/portduino/PortduinoGlue.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index 4fadc3ca9..fd489829b 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -90,7 +90,7 @@ void getMacAddr(uint8_t *dmac)
         if (strlen(optionMac) >= 12) {
             MAC_from_string(optionMac, dmac);
         } else {
-            uint32_t hwId;
+            uint32_t hwId = {0};
             sscanf(optionMac, "%u", &hwId);
             dmac[0] = 0x80;
             dmac[1] = 0;

From 6749367a73134d9b0e524bb12701105e7d254064 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Sat, 28 Dec 2024 18:23:56 -0600
Subject: [PATCH 129/132] [create-pull-request] automated change (#5686)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
---
 version.properties | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/version.properties b/version.properties
index ba7d7fe6f..800529e39 100644
--- a/version.properties
+++ b/version.properties
@@ -1,4 +1,4 @@
 [VERSION]  
 major = 2
 minor = 5
-build = 18
+build = 19

From 57a9a5ca21a3e148151d60a33a111570f4795b89 Mon Sep 17 00:00:00 2001
From: Jonathan Bennett 
Date: Sat, 28 Dec 2024 18:48:54 -0600
Subject: [PATCH 130/132] Another Valgrind fix (#5690)

---
 src/platform/portduino/PortduinoGlue.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index fd489829b..0c981bf16 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -104,7 +104,7 @@ void getMacAddr(uint8_t *dmac)
         exit;
     } else {
 
-        struct hci_dev_info di;
+        struct hci_dev_info di = {0};
         di.dev_id = 0;
         bdaddr_t bdaddr;
         int btsock;

From e45c0e4d4082e3e78f9124381de01583314ecd59 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Sun, 29 Dec 2024 11:56:05 +1100
Subject: [PATCH 131/132] Minor cppcheck fixes (#5689)

* In graphics/Screen.cpp, a copy/paste error to do with hearts
* In mesh/http/ContentHandler.cpp, an unused variable
* in mqtt/MQTT.cpp, remove unneded logic " '!A || (A && B)' is equivalent to '!A || B'"
---
 src/graphics/Screen.cpp          | 4 ++--
 src/mesh/http/ContentHandler.cpp | 1 -
 src/mqtt/MQTT.cpp                | 3 +--
 3 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 4cfb8701e..31647c92d 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -1015,7 +1015,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
                          y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - devil_height) / 2 + 2 + 5, devil_width, devil_height, devil);
     } else if (strcmp(msg, "♥️") == 0 || strcmp(msg, "\U0001F9E1") == 0 || strcmp(msg, "\U00002763") == 0 ||
                strcmp(msg, "\U00002764") == 0 || strcmp(msg, "\U0001F495") == 0 || strcmp(msg, "\U0001F496") == 0 ||
-               strcmp(msg, "\U0001F497") == 0 || strcmp(msg, "\U0001F496") == 0) {
+               strcmp(msg, "\U0001F497") == 0 || strcmp(msg, "\U0001F498") == 0) {
         display->drawXbm(x + (SCREEN_WIDTH - heart_width) / 2,
                          y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - heart_height) / 2 + 2 + 5, heart_width, heart_height, heart);
     } else {
@@ -2756,4 +2756,4 @@ int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
 } // namespace graphics
 #else
 graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
-#endif // HAS_SCREEN
+#endif // HAS_SCREEN
\ No newline at end of file
diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp
index 2b88702ed..aa8a68f91 100644
--- a/src/mesh/http/ContentHandler.cpp
+++ b/src/mesh/http/ContentHandler.cpp
@@ -725,7 +725,6 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res)
                 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];
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index c91252231..4260ae201 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -627,8 +627,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me
     // mp_decoded will not be decoded when it's PKI encrypted and not directed to us
     if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
         // For uplinking other's packets, check if it's not OK to MQTT or if it's an older packet without the bitfield
-        bool dontUplink = !mp_decoded.decoded.has_bitfield ||
-                          (mp_decoded.decoded.has_bitfield && !(mp_decoded.decoded.bitfield & BITFIELD_OK_TO_MQTT_MASK));
+        bool dontUplink = !mp_decoded.decoded.has_bitfield || !(mp_decoded.decoded.bitfield & BITFIELD_OK_TO_MQTT_MASK);
         // check for the lowest bit of the data bitfield set false, and the use of one of the default keys.
         if (!isFromUs(&mp_decoded) && !isMqttServerAddressPrivate && dontUplink &&
             (ch.settings.psk.size < 2 || (ch.settings.psk.size == 16 && memcmp(ch.settings.psk.bytes, defaultpsk, 16)) ||

From 3c7053c66a16bdc6ee024d8cafffc8838e873417 Mon Sep 17 00:00:00 2001
From: Tom Fifield 
Date: Sun, 29 Dec 2024 15:23:46 +1100
Subject: [PATCH 132/132] reference seeed indicator fix commit arduino-esp32
 (#5692)

Co-authored-by: mverch67 
---
 variants/seeed-sensecap-indicator/platformio.ini | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/variants/seeed-sensecap-indicator/platformio.ini b/variants/seeed-sensecap-indicator/platformio.ini
index e6bb2145e..86d6d0412 100644
--- a/variants/seeed-sensecap-indicator/platformio.ini
+++ b/variants/seeed-sensecap-indicator/platformio.ini
@@ -2,7 +2,7 @@
 [env:seeed-sensecap-indicator]
 extends = esp32s3_base
 platform_packages =
-    platformio/framework-arduinoespressif32 @ https://github.com/mverch67/arduino-esp32.git#add_tca9535 ; based on 2.0.16
+    platformio/framework-arduinoespressif32 @ https://github.com/mverch67/arduino-esp32.git#aef7fef6de3329ed6f75512d46d63bba12b09bb5 ; add_tca9535 (based on 2.0.16)
 
 board = seeed-sensecap-indicator
 board_check = true