diff --git a/.github/actions/build-variant/action.yml b/.github/actions/build-variant/action.yml index 80d2a56bb..b3303d393 100644 --- a/.github/actions/build-variant/action.yml +++ b/.github/actions/build-variant/action.yml @@ -51,6 +51,7 @@ runs: file: build.tar target: build.tar token: ${{ inputs.github_token }} + version: tags/v2.5.3 - name: Unpack web ui if: inputs.include-web-ui == 'true' diff --git a/.github/workflows/build_raspbian.yml b/.github/workflows/build_raspbian.yml index 1fd8fad30..e857ae635 100644 --- a/.github/workflows/build_raspbian.yml +++ b/.github/workflows/build_raspbian.yml @@ -13,8 +13,8 @@ jobs: - name: Install libbluetooth shell: bash run: | - apt-get update -y --fix-missing - apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev + 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 - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index efdbd2637..37164b758 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -203,6 +203,7 @@ jobs: ./device-*.sh ./device-*.bat ./littlefs-*.bin + ./littlefswebui-*.bin ./bleota*bin ./Meshtastic_nRF52_factory_erase*.uf2 retention-days: 30 diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 99e8693dc..d75f86306 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -16,6 +16,7 @@ build_flags = -DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818 -DMESHTASTIC_EXCLUDE_AUDIO=1 -DMESHTASTIC_EXCLUDE_PAXCOUNTER=1 + -DMAX_NUM_NODES=80 build_src_filter = ${arduino_base.build_src_filter} - - - - - - - - - - diff --git a/arch/rp2xx0/rp2040.ini b/arch/rp2xx0/rp2040.ini index 17b5df618..57e5d7bc2 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#v1.2.0-gcc12 +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#a606be683748c73e9a0d46baf70163478d298f0f ; For arduino-pico 4.2.0 extends = arduino_base -platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#4.0.3 +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#4.2.1 board_build.core = earlephilhower board_build.filesystem_size = 0.5m @@ -23,4 +23,4 @@ lib_deps = ${arduino_base.lib_deps} ${environmental_base.lib_deps} ${radiolib_base.lib_deps} - rweather/Crypto \ No newline at end of file + rweather/Crypto diff --git a/arch/rp2xx0/rp2350.ini b/arch/rp2xx0/rp2350.ini index 33bb36ad1..58c5e5554 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#9e55f6db5c56b9867c69fe473f388beea4546672 +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#a606be683748c73e9a0d46baf70163478d298f0f ; For arduino-pico 4.2.0 extends = arduino_base -platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#a6ab6e1f95bc1428d667d55ea7173c0744acc03c ; 4.0.2+ +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#4.2.1 board_build.core = earlephilhower board_build.filesystem_size = 0.5m diff --git a/bin/base64_to_hex.py b/bin/base64_to_hex.py new file mode 100644 index 000000000..07c559b9e --- /dev/null +++ b/bin/base64_to_hex.py @@ -0,0 +1,33 @@ +import sys +import base64 + +def base64_to_hex_string(b64_string): + try: + # Decode the Base64 string to raw bytes + decoded_bytes = base64.b64decode(b64_string) + except Exception as e: + raise ValueError(f"Invalid Base64 input: {e}") + + # Check if the decoded result is exactly 32 bytes + if len(decoded_bytes) != 32: + raise ValueError("Decoded Base64 input must be exactly 32 bytes.") + + # Convert each byte to its hex representation + hex_values = [f"0x{byte:02x}" for byte in decoded_bytes] + + # Join the formatted hex values with commas + formatted_output = "{ " + ", ".join(hex_values) + " };" + return formatted_output + +if __name__ == "__main__": + # Check if a Base64 string was provided in command line arguments + if len(sys.argv) != 2: + print("Usage: python script.py ") + sys.exit(1) + + b64_string = sys.argv[1] + try: + formatted_hex = base64_to_hex_string(b64_string) + print(formatted_hex) + except ValueError as e: + print(e) diff --git a/bin/build-esp32.sh b/bin/build-esp32.sh index adb6ab12a..f8d808ced 100755 --- a/bin/build-esp32.sh +++ b/bin/build-esp32.sh @@ -35,6 +35,11 @@ cp $SRCBIN $OUTDIR/$basename-update.bin echo "Building Filesystem for ESP32 targets" pio run --environment $1 -t buildfs +cp .pio/build/$1/littlefs.bin $OUTDIR/littlefswebui-$VERSION.bin +# Remove webserver files from the filesystem and rebuild +ls -l data/static # Diagnostic list of files +rm -rf data/static +pio run --environment $1 -t buildfs cp .pio/build/$1/littlefs.bin $OUTDIR/littlefs-$VERSION.bin cp bin/device-install.* $OUTDIR cp bin/device-update.* $OUTDIR diff --git a/protobufs b/protobufs index af2fea10f..52688fdcc 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit af2fea10fe2eba5857fb8e27975bbcea9c10af8e +Subproject commit 52688fdccb8a40c23101cada3736b3b22c2b229e diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index 0ea1309cc..238359952 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -1,4 +1,5 @@ #include "ButtonThread.h" +#include "../userPrefs.h" #include "configuration.h" #if !MESHTASTIC_EXCLUDE_GPS #include "GPS.h" @@ -26,12 +27,12 @@ using namespace concurrency; ButtonThread *buttonThread; // Declared extern in header volatile ButtonThread::ButtonEventType ButtonThread::btnEvent = ButtonThread::BUTTON_EVENT_NONE; -#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) +#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN) OneButton ButtonThread::userButton; // Get reference to static member #endif ButtonThread::ButtonThread() : OSThread("Button") { -#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) +#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN) #if defined(ARCH_PORTDUINO) if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) { @@ -39,7 +40,12 @@ ButtonThread::ButtonThread() : OSThread("Button") LOG_DEBUG("Use GPIO%02d for button", settingsMap[user]); } #elif defined(BUTTON_PIN) - int pin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; // Resolved button pin +#if !defined(USERPREFS_BUTTON_PIN) + int pin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; // Resolved button pin +#endif +#ifdef USERPREFS_BUTTON_PIN + int pin = config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN; // Resolved button pin +#endif #if defined(HELTEC_CAPSULE_SENSOR_V3) this->userButton = OneButton(pin, false, false); #elif defined(BUTTON_ACTIVE_LOW) @@ -59,7 +65,7 @@ ButtonThread::ButtonThread() : OSThread("Button") #endif #endif -#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) +#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN) userButton.attachClick(userButtonPressed); userButton.setClickMs(BUTTON_CLICK_MS); userButton.setPressMs(BUTTON_LONGPRESS_MS); @@ -102,7 +108,7 @@ int32_t ButtonThread::runOnce() // If the button is pressed we suppress CPU sleep until release canSleep = true; // Assume we should not keep the board awake -#if defined(BUTTON_PIN) +#if defined(BUTTON_PIN) || defined(USERPREFS_BUTTON_PIN) userButton.tick(); canSleep &= userButton.isIdle(); #elif defined(ARCH_PORTDUINO) @@ -130,7 +136,12 @@ int32_t ButtonThread::runOnce() return 50; } #ifdef BUTTON_PIN +#if !defined(USERPREFS_BUTTON_PIN) if (((config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN) != +#endif +#if defined(USERPREFS_BUTTON_PIN) + if (((config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN) != +#endif moduleConfig.canned_message.inputbroker_pin_press) || !(moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.rotary1_enabled) || !moduleConfig.canned_message.enabled) { @@ -244,7 +255,12 @@ void ButtonThread::attachButtonInterrupts() #elif defined(BUTTON_PIN) // Interrupt for user button, during normal use. Improves responsiveness. attachInterrupt( +#if !defined(USERPREFS_BUTTON_PIN) config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, +#endif +#if defined(USERPREFS_BUTTON_PIN) + config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN, +#endif []() { ButtonThread::userButton.tick(); runASAP = true; @@ -273,8 +289,13 @@ void ButtonThread::detachButtonInterrupts() if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) detachInterrupt(settingsMap[user]); #elif defined(BUTTON_PIN) +#if !defined(USERPREFS_BUTTON_PIN) detachInterrupt(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN); #endif +#if defined(USERPREFS_BUTTON_PIN) + detachInterrupt(config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN); +#endif +#endif #ifdef BUTTON_PIN_ALT detachInterrupt(BUTTON_PIN_ALT); @@ -315,7 +336,7 @@ void ButtonThread::userButtonMultiPressed(void *callerThread) // Non-static method, runs during callback. Grabs info while still valid void ButtonThread::storeClickCount() { -#ifdef BUTTON_PIN +#if defined(BUTTON_PIN) || defined(USERPREFS_BUTTON_PIN) multipressClickCount = userButton.getNumberClicks(); #endif } diff --git a/src/ButtonThread.h b/src/ButtonThread.h index 9cd7b3dac..a01a1718f 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -38,7 +38,7 @@ class ButtonThread : public concurrency::OSThread void storeClickCount(); private: -#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) +#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN) static OneButton userButton; // Static - accessed from an interrupt #endif #ifdef BUTTON_PIN_ALT diff --git a/src/Power.cpp b/src/Power.cpp index d66214315..a354b74e2 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -251,7 +251,6 @@ class AnalogBatteryLevel : public HasBatteryLevel #if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(HAS_PMU) && \ !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR if (hasINA()) { - LOG_DEBUG("Use INA on I2C addr 0x%x for device battery voltage", config.power.device_battery_ina_address); return getINAVoltage(); } #endif diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index bd0133740..fb7edcbfd 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -966,6 +966,16 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height, thumbdown); + } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F60A") == 0 || + strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F600") == 0 || + strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F642") == 0 || + strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F609") == 0 || + strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F601") == + 0) { // matches 5 different common smileys, so that the phone user doesn't have to remember which one is + // compatible + display->drawXbm(x + (SCREEN_WIDTH - smiley_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - smiley_height) / 2 + 2 + 5, smiley_width, smiley_height, + smiley); } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "❓") == 0) { display->drawXbm(x + (SCREEN_WIDTH - question_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - question_height) / 2 + 2 + 5, question_width, question_height, @@ -2747,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 \ No newline at end of file +#endif // HAS_SCREEN diff --git a/src/graphics/images.h b/src/graphics/images.h index 2b0854a33..fb6b176fc 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -56,6 +56,16 @@ static unsigned char thumbdown[] PROGMEM = { 0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, }; +#define smiley_height 30 +#define smiley_width 30 +static unsigned char smiley[] PROGMEM = { + 0x00, 0xfe, 0x0f, 0x00, 0x80, 0x01, 0x30, 0x00, 0x40, 0x00, 0xc0, 0x00, 0x20, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x10, 0x02, 0x0e, 0x0e, 0x10, 0x02, 0x09, 0x12, 0x10, + 0x01, 0x09, 0x12, 0x20, 0x01, 0x0f, 0x1e, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, + 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x81, 0x00, 0x20, 0x20, + 0x82, 0x00, 0x20, 0x10, 0x02, 0x01, 0x10, 0x10, 0x04, 0x02, 0x08, 0x08, 0x04, 0xfc, 0x07, 0x08, 0x08, 0x00, 0x00, 0x04, + 0x10, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x01, 0x40, 0x00, 0xc0, 0x00, 0x80, 0x01, 0x30, 0x00, 0x00, 0xfe, 0x0f, 0x00}; + #define question_height 25 #define question_width 25 static unsigned char question[] PROGMEM = { diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 51469ddc0..55b8c0b4d 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -246,6 +246,31 @@ NodeDB::NodeDB() config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; config.position.gps_enabled = 0; } +#ifdef USERPREFS_FIXED_GPS + if (myNodeInfo.reboot_count == 1) { // Check if First boot ever or after Factory Reset. + meshtastic_Position fixedGPS = meshtastic_Position_init_default; +#ifdef USERPREFS_FIXED_GPS_LAT + fixedGPS.latitude_i = (int32_t)(USERPREFS_FIXED_GPS_LAT * 1e7); + fixedGPS.has_latitude_i = true; +#endif +#ifdef USERPREFS_FIXED_GPS_LON + fixedGPS.longitude_i = (int32_t)(USERPREFS_FIXED_GPS_LON * 1e7); + fixedGPS.has_longitude_i = true; +#endif +#ifdef USERPREFS_FIXED_GPS_ALT + fixedGPS.altitude = USERPREFS_FIXED_GPS_ALT; + fixedGPS.has_altitude = true; +#endif +#if defined(USERPREFS_FIXED_GPS_LAT) && defined(USERPREFS_FIXED_GPS_LON) + fixedGPS.location_source = meshtastic_Position_LocSource_LOC_MANUAL; + config.has_position = true; + info->has_position = true; + info->position = TypeConversions::ConvertToPositionLite(fixedGPS); + nodeDB->setLocalPosition(fixedGPS); + config.position.fixed_position = true; +#endif + } +#endif saveToDisk(saveWhat); } @@ -382,9 +407,30 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) config.lora.ignore_mqtt = false; #endif #ifdef USERPREFS_USE_ADMIN_KEY - memcpy(config.security.admin_key[0].bytes, USERPREFS_ADMIN_KEY, 32); - config.security.admin_key[0].size = 32; - config.security.admin_key_count = 1; + // Initialize admin_key_count to zero + byte numAdminKeys = 0; + + // Check if USERPREFS_ADMIN_KEY_0 is non-empty + if (sizeof(USERPREFS_ADMIN_KEY_0) > 0) { + memcpy(config.security.admin_key[numAdminKeys].bytes, USERPREFS_ADMIN_KEY_0, 32); + config.security.admin_key[numAdminKeys].size = 32; + numAdminKeys++; + } + + // Check if USERPREFS_ADMIN_KEY_1 is non-empty + if (sizeof(USERPREFS_ADMIN_KEY_1) > 0) { + memcpy(config.security.admin_key[numAdminKeys].bytes, USERPREFS_ADMIN_KEY_1, 32); + config.security.admin_key[numAdminKeys].size = 32; + numAdminKeys++; + } + + // Check if USERPREFS_ADMIN_KEY_2 is non-empty + if (sizeof(USERPREFS_ADMIN_KEY_2) > 0) { + memcpy(config.security.admin_key[config.security.admin_key_count].bytes, USERPREFS_ADMIN_KEY_2, 32); + config.security.admin_key[config.security.admin_key_count].size = 32; + numAdminKeys++; + } + config.security.admin_key_count = numAdminKeys; #endif if (shouldPreserveKey) { config.security.private_key.size = 32; @@ -438,8 +484,13 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) #else bool hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C; #endif +#ifdef USERPREFS_FIXED_BLUETOOTH + config.bluetooth.fixed_pin = USERPREFS_FIXED_BLUETOOTH; + config.bluetooth.mode = meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN; +#else config.bluetooth.mode = hasScreen ? meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN : meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN; +#endif // for backward compat, default position flags are ALT+MSL config.position.position_flags = (meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL | @@ -793,8 +844,13 @@ void NodeDB::loadFromDisk() 0; // Mark the current device state as completely unusable, so that if we fail reading the entire file from // disk we will still factoryReset to restore things. +#ifdef ARCH_ESP32 + if (FSCom.exists("/static/static")) + rmDir("/static/static"); // Remove bad static web files bundle from initial 2.5.13 release +#endif + // static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM - auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES * sizeof(meshtastic_NodeInfo), + auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES_FS * sizeof(meshtastic_NodeInfo), sizeof(meshtastic_DeviceState), &meshtastic_DeviceState_msg, &devicestate); // See https://github.com/meshtastic/firmware/issues/4184#issuecomment-2269390786 @@ -813,6 +869,10 @@ void NodeDB::loadFromDisk() meshNodes = &devicestate.node_db_lite; numMeshNodes = devicestate.node_db_lite.size(); } + if (numMeshNodes > MAX_NUM_NODES) { + LOG_WARN("Node count %d exceeds MAX_NUM_NODES %d, truncating", numMeshNodes, MAX_NUM_NODES); + numMeshNodes = MAX_NUM_NODES; + } meshNodes->resize(MAX_NUM_NODES); state = loadProto(configFileName, meshtastic_LocalConfig_size, sizeof(meshtastic_LocalConfig), &meshtastic_LocalConfig_msg, diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index f0d137e2c..53b66ff0a 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -601,8 +601,6 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) // LOG_DEBUG("Send queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)", rf95.txGood(), rf95.rxGood(), rf95.rxBad()); assert(p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag); // It should have already been encoded by now - lastTxStart = millis(); - radioBuffer.header.from = p->from; radioBuffer.header.to = p->to; radioBuffer.header.id = p->id; diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index a67114e12..d23f8ed62 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -278,7 +278,8 @@ void RadioLibInterface::onNotify(uint32_t notification) startReceive(); // try receiving this packet, afterwards we'll be trying to transmit again setTransmitDelay(); } else { - // Send any outgoing packets we have ready + // 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); @@ -470,7 +471,8 @@ void RadioLibInterface::setStandby() /** start an immediate transmit */ bool RadioLibInterface::startSend(meshtastic_MeshPacket *txp) { - printPacket("Start low level send", txp); + /* NOTE: Minimize the actions before startTransmit() to keep the time between + channel scan and actual transmit as low as possible to avoid collisions. */ if (disabled || !config.lora.tx_enabled) { LOG_WARN("Drop Tx packet because LoRa Tx disabled"); packetPool.release(txp); @@ -489,6 +491,9 @@ bool RadioLibInterface::startSend(meshtastic_MeshPacket *txp) completeSending(); powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // Transmitter off now startReceive(); // Restart receive mode (because startTransmit failed to put us in xmit mode) + } else { + lastTxStart = millis(); + printPacket("Started Tx", txp); } // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register diff --git a/src/mesh/generated/meshtastic/device_ui.pb.h b/src/mesh/generated/meshtastic/device_ui.pb.h index 5c1e067ab..107aa8846 100644 --- a/src/mesh/generated/meshtastic/device_ui.pb.h +++ b/src/mesh/generated/meshtastic/device_ui.pb.h @@ -49,6 +49,8 @@ typedef enum _meshtastic_Language { meshtastic_Language_DUTCH = 12, /* Greek */ meshtastic_Language_GREEK = 13, + /* Norwegian */ + meshtastic_Language_NORWEGIAN = 14, /* Simplified Chinese (experimental) */ meshtastic_Language_SIMPLIFIED_CHINESE = 30, /* Traditional Chinese (experimental) */ @@ -84,6 +86,7 @@ typedef struct _meshtastic_NodeHighlight { char node_name[16]; } meshtastic_NodeHighlight; +typedef PB_BYTES_ARRAY_T(16) meshtastic_DeviceUIConfig_calibration_data_t; typedef struct _meshtastic_DeviceUIConfig { /* A version integer used to invalidate saved files when we make incompatible changes. */ uint32_t version; @@ -109,6 +112,8 @@ typedef struct _meshtastic_DeviceUIConfig { /* Node list highlightening */ bool has_node_highlight; meshtastic_NodeHighlight node_highlight; + /* 8 integers for screen calibration data */ + meshtastic_DeviceUIConfig_calibration_data_t calibration_data; } meshtastic_DeviceUIConfig; @@ -132,10 +137,10 @@ extern "C" { /* Initializer values for message structs */ -#define meshtastic_DeviceUIConfig_init_default {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_default, false, meshtastic_NodeHighlight_init_default} +#define meshtastic_DeviceUIConfig_init_default {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_default, false, meshtastic_NodeHighlight_init_default, {0, {0}}} #define meshtastic_NodeFilter_init_default {0, 0, 0, 0, 0, ""} #define meshtastic_NodeHighlight_init_default {0, 0, 0, 0, ""} -#define meshtastic_DeviceUIConfig_init_zero {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_zero, false, meshtastic_NodeHighlight_init_zero} +#define meshtastic_DeviceUIConfig_init_zero {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_zero, false, meshtastic_NodeHighlight_init_zero, {0, {0}}} #define meshtastic_NodeFilter_init_zero {0, 0, 0, 0, 0, ""} #define meshtastic_NodeHighlight_init_zero {0, 0, 0, 0, ""} @@ -164,6 +169,7 @@ extern "C" { #define meshtastic_DeviceUIConfig_language_tag 11 #define meshtastic_DeviceUIConfig_node_filter_tag 12 #define meshtastic_DeviceUIConfig_node_highlight_tag 13 +#define meshtastic_DeviceUIConfig_calibration_data_tag 14 /* Struct field encoding specification for nanopb */ #define meshtastic_DeviceUIConfig_FIELDLIST(X, a) \ @@ -179,7 +185,8 @@ X(a, STATIC, SINGULAR, BOOL, banner_enabled, 9) \ X(a, STATIC, SINGULAR, UINT32, ring_tone_id, 10) \ X(a, STATIC, SINGULAR, UENUM, language, 11) \ X(a, STATIC, OPTIONAL, MESSAGE, node_filter, 12) \ -X(a, STATIC, OPTIONAL, MESSAGE, node_highlight, 13) +X(a, STATIC, OPTIONAL, MESSAGE, node_highlight, 13) \ +X(a, STATIC, SINGULAR, BYTES, calibration_data, 14) #define meshtastic_DeviceUIConfig_CALLBACK NULL #define meshtastic_DeviceUIConfig_DEFAULT NULL #define meshtastic_DeviceUIConfig_node_filter_MSGTYPE meshtastic_NodeFilter @@ -215,7 +222,7 @@ extern const pb_msgdesc_t meshtastic_NodeHighlight_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_MAX_SIZE meshtastic_DeviceUIConfig_size -#define meshtastic_DeviceUIConfig_size 99 +#define meshtastic_DeviceUIConfig_size 117 #define meshtastic_NodeFilter_size 36 #define meshtastic_NodeHighlight_size 25 diff --git a/src/mesh/mesh-pb-constants.h b/src/mesh/mesh-pb-constants.h index f91c48560..039b36d8d 100644 --- a/src/mesh/mesh-pb-constants.h +++ b/src/mesh/mesh-pb-constants.h @@ -23,6 +23,8 @@ #define MAX_NUM_NODES 100 #endif +#define MAX_NUM_NODES_FS 100 + /// Max number of channels allowed #define MAX_NUM_CHANNELS (member_size(meshtastic_ChannelFile, channels) / member_size(meshtastic_ChannelFile, channels[0])) diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index faf5ce3de..911a47093 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -20,6 +20,8 @@ #include #include static void WiFiEvent(WiFiEvent_t event); +#elif defined(ARCH_RP2040) +#include #endif #ifndef DISABLE_NTP @@ -59,19 +61,21 @@ static void onNetworkConnected() // Start web server LOG_INFO("Start WiFi network services"); -#ifdef ARCH_ESP32 // start mdns if (!MDNS.begin("Meshtastic")) { LOG_ERROR("Error setting up MDNS responder!"); } else { - LOG_INFO("mDNS responder started"); LOG_INFO("mDNS Host: Meshtastic.local"); +#ifdef ARCH_ESP32 MDNS.addService("http", "tcp", 80); MDNS.addService("https", "tcp", 443); - } -#else // ESP32 handles this in WiFiEvent - LOG_INFO("Obtained IP address: %s", WiFi.localIP().toString().c_str()); +#elif defined(ARCH_RP2040) + // ARCH_RP2040 does not support HTTPS, create a "meshtastic" service + MDNS.addService("meshtastic", "tcp", 4403); + // ESP32 handles this in WiFiEvent + LOG_INFO("Obtained IP address: %s", WiFi.localIP().toString().c_str()); #endif + } #ifndef DISABLE_NTP LOG_INFO("Start NTP time client"); @@ -129,7 +133,7 @@ static int32_t reconnectWiFi() // Make sure we clear old connection credentials #ifdef ARCH_ESP32 WiFi.disconnect(false, true); -#else +#elif defined(ARCH_RP2040) WiFi.disconnect(false); #endif LOG_INFO("Reconnecting to WiFi access point %s", wifiName); @@ -193,7 +197,7 @@ void deinitWifi() if (isWifiAvailable()) { #ifdef ARCH_ESP32 WiFi.disconnect(true, false); -#else +#elif defined(ARCH_RP2040) WiFi.disconnect(true); #endif WiFi.mode(WIFI_OFF); @@ -229,15 +233,15 @@ bool initWifi() if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_STATIC && config.network.ipv4_config.ip != 0) { -#ifndef ARCH_RP2040 +#ifdef ARCH_ESP32 WiFi.config(config.network.ipv4_config.ip, config.network.ipv4_config.gateway, config.network.ipv4_config.subnet, config.network.ipv4_config.dns); -#else +#elif defined(ARCH_RP2040) WiFi.config(config.network.ipv4_config.ip, config.network.ipv4_config.dns, config.network.ipv4_config.gateway, config.network.ipv4_config.subnet); #endif } -#ifndef ARCH_RP2040 +#ifdef ARCH_ESP32 WiFi.onEvent(WiFiEvent); WiFi.setAutoReconnect(true); WiFi.setSleep(false); diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 5f1c1b284..6dca59570 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -526,6 +526,11 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) requiresReboot = false; } config.power = c.payload_variant.power; + if (c.payload_variant.power.on_battery_shutdown_after_secs > 0 && + c.payload_variant.power.on_battery_shutdown_after_secs < 30) { + LOG_WARN("Tried to set on_battery_shutdown_after_secs too low, set to min 30 seconds"); + config.power.on_battery_shutdown_after_secs = 30; + } break; case meshtastic_Config_network_tag: LOG_INFO("Set config: WiFi"); diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index d88f275b9..3ec6ff690 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -119,12 +119,13 @@ int32_t ExternalNotificationModule::runOnce() if (externalTurnedOn[1] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) { - setExternalState(0, !getExternal(1)); + setExternalState(1, !getExternal(1)); } if (externalTurnedOn[2] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) { - setExternalState(0, !getExternal(2)); + 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) red = (colorState & 4) ? brightnessValues[brightnessIndex] : 0; // Red enabled on colorState = 4,5,6,7 diff --git a/userPrefs.h b/userPrefs.h index 58a44fef0..622a491c3 100644 --- a/userPrefs.h +++ b/userPrefs.h @@ -68,10 +68,39 @@ static unsigned char icon_bits[] = { 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. + */ /* #define USERPREFS_USE_ADMIN_KEY 1 -static unsigned char USERPREFS_ADMIN_KEY[] = {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}; +static unsigned char USERPREFS_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}; +static unsigned char USERPREFS_ADMIN_KEY_1[] = {}; +static unsigned char USERPREFS_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/variants/rak11310/platformio.ini b/variants/rak11310/platformio.ini index c7b3504fe..923cedaa3 100644 --- a/variants/rak11310/platformio.ini +++ b/variants/rak11310/platformio.ini @@ -3,6 +3,7 @@ extends = rp2040_base board = wiscore_rak11300 upload_protocol = picotool # keep an old SDK to use less memory. +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#v1.2.0-gcc12 platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.7.2 # add our variants files to the include and src paths @@ -13,5 +14,5 @@ build_flags = ${rp2040_base.build_flags} -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" lib_deps = ${rp2040_base.lib_deps} -debug_build_flags = ${rp2040_base.build_flags} +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/rak_wismeshtap/platformio.ini b/variants/rak_wismeshtap/platformio.ini index 461696d29..277fdbe24 100644 --- a/variants/rak_wismeshtap/platformio.ini +++ b/variants/rak_wismeshtap/platformio.ini @@ -9,7 +9,6 @@ build_flags = ${nrf52840_base.build_flags} -Ivariants/rak_wismeshtap -D RAK_4631 -DEINK_WIDTH=250 -DEINK_HEIGHT=122 -DMESHTASTIC_EXCLUDE_WIFI=1 - -DMESHTASTIC_EXCLUDE_WAYPOINT=1 -DMESHTASTIC_EXCLUDE_DETECTIONSENSOR=1 -DMESHTASTIC_EXCLUDE_STOREFORWARD=1 -DMESHTASTIC_EXCLUDE_POWER_TELEMETRY=1 diff --git a/variants/rp2040-lora/platformio.ini b/variants/rp2040-lora/platformio.ini index 8499f6f3c..4c578fb2b 100644 --- a/variants/rp2040-lora/platformio.ini +++ b/variants/rp2040-lora/platformio.ini @@ -12,5 +12,5 @@ build_flags = ${rp2040_base.build_flags} -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" lib_deps = ${rp2040_base.lib_deps} -debug_build_flags = ${rp2040_base.build_flags} +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/rpipico/platformio.ini b/variants/rpipico/platformio.ini index e4b9e479f..9c62ebcb5 100644 --- a/variants/rpipico/platformio.ini +++ b/variants/rpipico/platformio.ini @@ -12,5 +12,5 @@ build_flags = ${rp2040_base.build_flags} -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" lib_deps = ${rp2040_base.lib_deps} -debug_build_flags = ${rp2040_base.build_flags} +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/rpipico2/platformio.ini b/variants/rpipico2/platformio.ini index a63414418..24714efd5 100644 --- a/variants/rpipico2/platformio.ini +++ b/variants/rpipico2/platformio.ini @@ -12,5 +12,5 @@ build_flags = ${rp2350_base.build_flags} -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" lib_deps = ${rp2350_base.lib_deps} -debug_build_flags = ${rp2350_base.build_flags} +debug_build_flags = ${rp2350_base.build_flags}, -g debug_tool = cmsis-dap ; for e.g. Picotool \ No newline at end of file diff --git a/variants/rpipicow/platformio.ini b/variants/rpipicow/platformio.ini index 2600b4b38..7a43ece3b 100644 --- a/variants/rpipicow/platformio.ini +++ b/variants/rpipicow/platformio.ini @@ -14,5 +14,5 @@ build_src_filter = ${rp2040_base.build_src_filter} + lib_deps = ${rp2040_base.lib_deps} ${networking_base.lib_deps} -debug_build_flags = ${rp2040_base.build_flags} +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/version.properties b/version.properties index 91c1f4823..bed14bad5 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 12 +build = 14