diff --git a/boards/wio-sdk-wm1110.json b/boards/wio-sdk-wm1110.json
index 04db52518..9db60e203 100644
--- a/boards/wio-sdk-wm1110.json
+++ b/boards/wio-sdk-wm1110.json
@@ -7,13 +7,6 @@
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA",
"f_cpu": "64000000L",
- "hwids": [
- ["0x239A", "0x8029"],
- ["0x239A", "0x0029"],
- ["0x239A", "0x002A"],
- ["0x239A", "0x802A"]
- ],
- "usb_product": "WIO-BOOT",
"mcu": "nrf52840",
"variant": "Seeed_WIO_WM1110",
"bsp": {
diff --git a/extra_scripts/README.md b/extra_scripts/README.md
new file mode 100644
index 000000000..4c797f49f
--- /dev/null
+++ b/extra_scripts/README.md
@@ -0,0 +1,3 @@
+# extra_scripts
+
+This directory contains special [scripts](https://docs.platformio.org/en/latest/scripting/index.html) that are used to modify the platformio environment in rare cases.
diff --git a/extra_scripts/disable_adafruit_usb.py b/extra_scripts/disable_adafruit_usb.py
new file mode 100644
index 000000000..fb391715c
--- /dev/null
+++ b/extra_scripts/disable_adafruit_usb.py
@@ -0,0 +1,20 @@
+# trunk-ignore-all(flake8/F821)
+# trunk-ignore-all(ruff/F821)
+
+Import("env")
+
+# NOTE: This is not currently used, but can serve as an example on how to write extra_scripts
+
+print("Current CLI targets", COMMAND_LINE_TARGETS)
+print("Current Build targets", BUILD_TARGETS)
+print("CPP defs", env.get("CPPDEFINES"))
+
+# Adafruit.py in the platformio build tree is a bit naive and always enables their USB stack for building. We don't want this.
+# So come in after that python script has run and disable it. This hack avoids us having to fork that big project and send in a PR
+# which might not be accepted. -@geeksville
+
+env["CPPDEFINES"].remove("USBCON")
+env["CPPDEFINES"].remove("USE_TINYUSB")
+
+# Custom actions when building program/firmware
+# env.AddPreAction("buildprog", callback...)
diff --git a/src/configuration.h b/src/configuration.h
index 1149f344c..3d10feeaa 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -75,11 +75,17 @@ along with this program. If not, see .
#endif
// -----------------------------------------------------------------------------
-// Regulatory overrides for producing regional builds
+// Regulatory overrides
// -----------------------------------------------------------------------------
-// Define if region should override user saved region
-// #define LORA_REGIONCODE meshtastic_Config_LoRaConfig_RegionCode_SG_923
+// Override user saved region, for producing region-locked builds
+// #define REGULATORY_LORA_REGIONCODE meshtastic_Config_LoRaConfig_RegionCode_SG_923
+
+// Total system gain in dBm to subtract from Tx power to remain within regulatory ERP limit for non-licensed operators
+// This value should be set in variant.h and is PA gain + antenna gain (if system ships with an antenna)
+#ifndef REGULATORY_GAIN_LORA
+#define REGULATORY_GAIN_LORA 0
+#endif
// -----------------------------------------------------------------------------
// Feature toggles
diff --git a/src/main.cpp b/src/main.cpp
index b5707c8de..ddb99568d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -242,7 +242,7 @@ void setup()
initDeepSleep();
// power on peripherals
-#if defined(TTGO_T_ECHO) && defined(PIN_POWER_EN)
+#if defined(PIN_POWER_EN)
pinMode(PIN_POWER_EN, OUTPUT);
digitalWrite(PIN_POWER_EN, HIGH);
// digitalWrite(PIN_POWER_EN1, INPUT);
diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp
index eb86f4267..78228c077 100644
--- a/src/mesh/RadioInterface.cpp
+++ b/src/mesh/RadioInterface.cpp
@@ -154,8 +154,8 @@ static uint8_t bytes[MAX_RHPACKETLEN];
void initRegion()
{
const RegionInfo *r = regions;
-#ifdef LORA_REGIONCODE
- for (; r->code != meshtastic_Config_LoRaConfig_RegionCode_UNSET && r->code != LORA_REGIONCODE; r++)
+#ifdef REGULATORY_LORA_REGIONCODE
+ for (; r->code != meshtastic_Config_LoRaConfig_RegionCode_UNSET && r->code != REGULATORY_LORA_REGIONCODE; r++)
;
LOG_INFO("Wanted region %d, regulatory override to %s\n", config.lora.region, r->name);
#else
@@ -478,8 +478,8 @@ void RadioInterface::applyModemConfig()
power = loraConfig.tx_power;
- if ((power == 0) || ((power > myRegion->powerLimit) && !devicestate.owner.is_licensed))
- power = myRegion->powerLimit;
+ if ((power == 0) || ((power + REGULATORY_GAIN_LORA > myRegion->powerLimit) && !devicestate.owner.is_licensed))
+ power = myRegion->powerLimit - REGULATORY_GAIN_LORA;
if (power == 0)
power = 17; // Default to this power level if we don't have a valid regional power limit (powerLimit of myRegion defaults
diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h
index c979d016c..5565b6468 100644
--- a/src/platform/esp32/architecture.h
+++ b/src/platform/esp32/architecture.h
@@ -101,6 +101,8 @@
#define HW_VENDOR meshtastic_HardwareModel_STATION_G1
#elif defined(DR_DEV)
#define HW_VENDOR meshtastic_HardwareModel_DR_DEV
+#elif defined(HELTEC_HRU_3601)
+#define HW_VENDOR meshtastic_HardwareModel_HELTEC_HRU_3601
#elif defined(HELTEC_V3)
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_V3
#elif defined(HELTEC_WSL_V3)
diff --git a/src/sleep.cpp b/src/sleep.cpp
index 0e7045323..55e70e7b8 100644
--- a/src/sleep.cpp
+++ b/src/sleep.cpp
@@ -231,12 +231,10 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false)
nodeDB->saveToDisk();
-#ifdef TTGO_T_ECHO
#ifdef PIN_POWER_EN
pinMode(PIN_POWER_EN, INPUT); // power off peripherals
// pinMode(PIN_POWER_EN1, INPUT_PULLDOWN);
#endif
-#endif
#if HAS_GPS
// Kill GPS power completely (even if previously we just had it in sleep mode)
if (gps)
diff --git a/variants/heltec_hru_3601/pins_arduino.h b/variants/heltec_hru_3601/pins_arduino.h
new file mode 100644
index 000000000..625c57ced
--- /dev/null
+++ b/variants/heltec_hru_3601/pins_arduino.h
@@ -0,0 +1,25 @@
+#ifndef Pins_Arduino_h
+#define Pins_Arduino_h
+
+#include
+#include
+
+static const uint8_t TX = UART_TX;
+static const uint8_t RX = UART_RX;
+
+static const uint8_t SDA = I2C_SDA;
+static const uint8_t SCL = I2C_SCL;
+
+static const uint8_t SS = 8;
+static const uint8_t MOSI = 7;
+static const uint8_t MISO = 6;
+static const uint8_t SCK = 10;
+
+static const uint8_t A0 = 0;
+static const uint8_t A1 = 1;
+static const uint8_t A2 = 2;
+static const uint8_t A3 = 3;
+static const uint8_t A4 = 4;
+static const uint8_t A5 = 5;
+
+#endif /* Pins_Arduino_h */
diff --git a/variants/heltec_hru_3601/platformio.ini b/variants/heltec_hru_3601/platformio.ini
new file mode 100644
index 000000000..3668e72b7
--- /dev/null
+++ b/variants/heltec_hru_3601/platformio.ini
@@ -0,0 +1,9 @@
+[env:heltec-hru-3601]
+extends = esp32c3_base
+board = adafruit_qtpy_esp32c3
+build_flags =
+ ${esp32_base.build_flags}
+ -D HELTEC_HRU_3601
+ -I variants/heltec_hru_3601
+lib_deps = ${esp32c3_base.lib_deps}
+ adafruit/Adafruit NeoPixel @ ^1.12.0
diff --git a/variants/heltec_hru_3601/variant.h b/variants/heltec_hru_3601/variant.h
new file mode 100644
index 000000000..31783ec35
--- /dev/null
+++ b/variants/heltec_hru_3601/variant.h
@@ -0,0 +1,40 @@
+#define BUTTON_PIN 9
+
+#define HAS_SCREEN 0
+#define HAS_GPS 0
+#undef GPS_RX_PIN
+#undef GPS_TX_PIN
+
+#define USE_SX1262
+#define LORA_SCK 10
+#define LORA_MISO 6
+#define LORA_MOSI 7
+#define LORA_CS 8
+#define LORA_DIO0 RADIOLIB_NC
+#define LORA_RESET 5
+#define LORA_DIO1 3
+#define LORA_DIO2 RADIOLIB_NC
+#define LORA_BUSY 4
+#define SX126X_CS LORA_CS
+#define SX126X_DIO1 LORA_DIO1
+#define SX126X_BUSY LORA_BUSY
+#define SX126X_RESET LORA_RESET
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+
+// Vext_Ctrl pin controls 3.3V LDO (U2) which provides power to I2C peripheral
+#define PIN_POWER_EN 1
+
+// Board has I2C connected to UART0 pins, and no other hardware serial port
+#define UART_TX -1
+#define UART_RX -1
+
+// Board has I2C connected to U0RXD and U0TXD
+#define I2C_SDA 21
+#define I2C_SCL 20
+
+// Board has RGB LED on GPIO2
+#define HAS_NEOPIXEL // Enable the use of neopixels
+#define NEOPIXEL_COUNT 1 // How many neopixels are connected
+#define NEOPIXEL_DATA 2 // gpio pin used to send data to the neopixels
+#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) // type of neopixels in use
diff --git a/variants/wio-sdk-wm1110/platformio.ini b/variants/wio-sdk-wm1110/platformio.ini
index 9d9ea4c29..cd3a76d02 100644
--- a/variants/wio-sdk-wm1110/platformio.ini
+++ b/variants/wio-sdk-wm1110/platformio.ini
@@ -2,6 +2,10 @@
[env:wio-sdk-wm1110]
extends = nrf52840_base
board = wio-sdk-wm1110
+
+# Remove adafruit USB serial from the build (it is incompatible with using the ch340 serial chip on this board)
+build_unflags = ${nrf52840_base:build_unflags} -DUSBCON -DUSE_TINYUSB
+
board_level = extra
build_flags = ${nrf52840_base.build_flags} -Ivariants/wio-sdk-wm1110 -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DWIO_WM1110
-L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard"
@@ -12,4 +16,4 @@ lib_deps =
${nrf52840_base.lib_deps}
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 = jlink
\ No newline at end of file
+upload_protocol = jlink
diff --git a/variants/wio-sdk-wm1110/variant.h b/variants/wio-sdk-wm1110/variant.h
index f027b469f..8ad8c769a 100644
--- a/variants/wio-sdk-wm1110/variant.h
+++ b/variants/wio-sdk-wm1110/variant.h
@@ -31,6 +31,13 @@
#include "WVariant.h"
+#ifdef USE_TINYUSB
+#error TinyUSB must be disabled by platformio before using this variant
+#endif
+
+// We use the hardware serial port for the serial console
+#define Serial Serial1
+
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
diff --git a/variants/xiao_ble/platformio.ini b/variants/xiao_ble/platformio.ini
index 156eba528..613fd3599 100644
--- a/variants/xiao_ble/platformio.ini
+++ b/variants/xiao_ble/platformio.ini
@@ -3,7 +3,7 @@
extends = nrf52840_base
board = xiao_ble_sense
board_level = extra
-build_flags = ${nrf52840_base.build_flags} -Ivariants/xiao_ble -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -D EBYTE_E22 -DPRIVATE_HW
+build_flags = ${nrf52840_base.build_flags} -Ivariants/xiao_ble -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -D EBYTE_E22 -DEBYTE_E22_900M30S -DPRIVATE_HW
-L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard"
board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/xiao_ble>
diff --git a/variants/xiao_ble/variant.h b/variants/xiao_ble/variant.h
index 77af08278..a86ddfde2 100644
--- a/variants/xiao_ble/variant.h
+++ b/variants/xiao_ble/variant.h
@@ -142,6 +142,16 @@ static const uint8_t SCK = PIN_SPI_SCK;
// (which is the default for the sx1262interface code)
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+#ifdef EBYTE_E22_900M30S
+// 10dB PA gain and 30dB rated output; based on PA output table from Ebyte Robin
+#define REGULATORY_GAIN_LORA 10
+#define SX126X_MAX_POWER 20
+#endif
+#ifdef EBYTE_E22_900M33S
+// 25dB PA gain and 33dB rated output; based on TX Power Curve from E22-900M33S_UserManual_EN_v1.0.pdf
+#define REGULATORY_GAIN_LORA 25
+#define SX126X_MAX_POWER 8
+#endif
#endif
/*