diff --git a/boards/generic_wl5e.json b/boards/generic_wl5e.json
new file mode 100644
index 00000000..87e14043
--- /dev/null
+++ b/boards/generic_wl5e.json
@@ -0,0 +1,31 @@
+{
+ "build": {
+ "core": "stm32",
+ "cpu": "cortex-m4",
+ "extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_GENERIC_WLE5CCUX",
+ "f_cpu": "48000000L",
+ "mcu": "stm32wle5ccu",
+ "variant": "STM32WLxx/WL54CCU_WL55CCU_WLE4C(8-B-C)U_WLE5C(8-B-C)U",
+ "product_line": "STM32WLE5xx"
+ },
+ "debug": {
+ "default_tools": [
+ "stlink"
+ ],
+ "jlink_device": "STM32WLE5CC",
+ "openocd_target": "stm32wlx",
+ "svd_path": "STM32WLE5_CM4.svd"
+ },
+ "frameworks": ["arduino"],
+ "name": "BB-STM32WL",
+ "upload": {
+ "maximum_ram_size": 65536,
+ "maximum_size": 262144,
+ "protocol": "cmsis-dap",
+ "protocols": [
+ "cmsis-dap"
+ ]
+ },
+ "url": "https://www.st.com/en/microcontrollers-microprocessors/stm32wl-series.html",
+ "vendor": "ST"
+}
diff --git a/platformio.ini b/platformio.ini
index 554645c4..f06c8cff 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -184,4 +184,24 @@ lib_ignore =
lib_deps =
${arduino_base.lib_deps}
${environmental_base.lib_deps}
- https://github.com/kokke/tiny-AES-c.git
\ No newline at end of file
+ https://github.com/kokke/tiny-AES-c.git
+
+[stm32wl5e_base]
+platform = ststm32
+board = generic_wl5e
+framework = arduino
+build_type = debug
+build_flags =
+ ${arduino_base.build_flags}
+ -Isrc/platform/stm32wl -g
+ -DHAL_SUBGHZ_MODULE_ENABLED
+# Arduino/PlatformIO framework-arduinoststm32 package does not presently have SUBGHZSPI support
+# -DPIN_SPI_MOSI=PINSUBGHZSPIMOSI -DPIN_SPI_MISO=PINSUBGHZSPIMISO -DPIN_SPI_SCK=PINSUBGHZSPISCK
+build_src_filter =
+ ${arduino_base.build_src_filter} - - - - - - - - - - - - -
+lib_deps =
+ ${env.lib_deps}
+ https://github.com/jgromes/RadioLib.git
+ https://github.com/kokke/tiny-AES-c.git
+lib_ignore =
+ mathertel/OneButton@^2.0.3
diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp
index 633a8dd5..e7073851 100644
--- a/src/mesh/RadioLibInterface.cpp
+++ b/src/mesh/RadioLibInterface.cpp
@@ -43,6 +43,10 @@ RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq
: NotifiedWorkerThread("RadioIf"), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
{
instance = this;
+#if defined(ARCH_STM32WL) && defined(USE_SX1262)
+ module.setCb_digitalWrite(stm32wl_emulate_digitalWrite);
+ module.setCb_digitalRead(stm32wl_emulate_digitalRead);
+#endif
}
#ifdef ARCH_ESP32
diff --git a/src/platform/stm32wl/STM32WLCryptoEngine.cpp b/src/platform/stm32wl/STM32WLCryptoEngine.cpp
new file mode 100644
index 00000000..535d88cf
--- /dev/null
+++ b/src/platform/stm32wl/STM32WLCryptoEngine.cpp
@@ -0,0 +1,36 @@
+#include "configuration.h"
+#include "CryptoEngine.h"
+#include "aes.hpp"
+
+class STM32WLCryptoEngine : public CryptoEngine
+{
+ public:
+ STM32WLCryptoEngine() {}
+
+ ~STM32WLCryptoEngine() {}
+
+ /**
+ * Encrypt a packet
+ *
+ * @param bytes is updated in place
+ */
+ virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) override
+ {
+ if (key.length > 0) {
+ AES_ctx ctx;
+ initNonce(fromNode, packetNum);
+ AES_init_ctx_iv(&ctx, key.bytes, nonce);
+ AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes);
+ }
+ }
+
+ virtual void decrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) override
+ {
+ // For CTR, the implementation is the same
+ encrypt(fromNode, packetNum, numBytes, bytes);
+ }
+
+ private:
+};
+
+CryptoEngine *crypto = new STM32WLCryptoEngine();
diff --git a/src/platform/stm32wl/architecture.h b/src/platform/stm32wl/architecture.h
new file mode 100644
index 00000000..02196eaf
--- /dev/null
+++ b/src/platform/stm32wl/architecture.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#define ARCH_STM32WL
+
+//
+// defaults for STM32WL architecture
+//
+
+//
+// set HW_VENDOR
+//
+
+#ifndef HW_VENDOR
+ #define HW_VENDOR HardwareModel_PRIVATE_HW
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void stm32wl_emulate_digitalWrite(long unsigned int pin, long unsigned int value);
+ int stm32wl_emulate_digitalRead(long unsigned int pin);
+#ifdef __cplusplus
+}
+#endif
+
+/* virtual pins for stm32wl_emulate_digitalWrite() / stm32wl_emulate_digitalRead() to recognize */
+#define SX126X_CS 1000
+#define SX126X_DIO1 1001
+#define SX126X_RESET 1003
+#define SX126X_BUSY 1004
+
diff --git a/src/platform/stm32wl/layer.c b/src/platform/stm32wl/layer.c
new file mode 100644
index 00000000..1352b391
--- /dev/null
+++ b/src/platform/stm32wl/layer.c
@@ -0,0 +1,67 @@
+#include
+#include "architecture.h"
+#include "stm32wlxx.h"
+#include "stm32wlxx_hal.h"
+
+void HardFault_Handler(void)
+{
+ asm("bkpt");
+}
+
+void stm32wl_emulate_digitalWrite(long unsigned int pin, long unsigned int value)
+{
+ switch (pin)
+ {
+ case SX126X_CS: /* active low */
+ if (value)
+ LL_PWR_UnselectSUBGHZSPI_NSS();
+ else
+ LL_PWR_SelectSUBGHZSPI_NSS();
+ break;
+ case SX126X_RESET: /* active low */
+ if (value)
+ LL_RCC_RF_DisableReset();
+ else
+ {
+ LL_RCC_RF_EnableReset();
+ LL_RCC_HSE_EnableTcxo();
+ LL_RCC_HSE_Enable();
+ while (!LL_RCC_HSE_IsReady());
+ }
+ break;
+ default:
+ asm("bkpt");
+ break;
+ }
+}
+
+static bool irq_happened;
+
+void SUBGHZ_Radio_IRQHandler(void)
+{
+ NVIC_DisableIRQ(SUBGHZ_Radio_IRQn);
+ irq_happened = true;
+}
+
+int stm32wl_emulate_digitalRead(long unsigned int pin)
+{
+ int outcome = 0;
+
+ switch (pin)
+ {
+ case SX126X_BUSY:
+// return ((LL_PWR_IsActiveFlag_RFBUSYMS() & LL_PWR_IsActiveFlag_RFBUSYS()) == 1UL);
+ outcome = LL_PWR_IsActiveFlag_RFBUSYS();
+ break;
+ case SX126X_DIO1:
+ default:
+ NVIC_ClearPendingIRQ(SUBGHZ_Radio_IRQn);
+ irq_happened = false;
+ NVIC_EnableIRQ(SUBGHZ_Radio_IRQn);
+ for (int i = 0; i < 64; i++) asm("nop");
+ outcome = irq_happened;
+ break;
+ }
+ return outcome;
+}
+
diff --git a/src/platform/stm32wl/main-stm32wl.cpp b/src/platform/stm32wl/main-stm32wl.cpp
new file mode 100644
index 00000000..f5b83a70
--- /dev/null
+++ b/src/platform/stm32wl/main-stm32wl.cpp
@@ -0,0 +1,27 @@
+#include
+#include
+#include "configuration.h"
+#include "RTC.h"
+
+void setBluetoothEnable(bool on) {}
+
+void playStartMelody() {}
+
+void updateBatteryLevel(uint8_t level) {}
+
+void getMacAddr(uint8_t *dmac)
+{
+ for (int i = 0; i < 6; i++)
+ dmac[i] = i;
+}
+
+void cpuDeepSleep(uint64_t msecToWake) {}
+
+/* pacify libc_nano */
+extern "C" {
+int _gettimeofday( struct timeval *tv, void *tzvp )
+{
+ return -1;
+}
+}
+
diff --git a/variants/wio-e5/platformio.ini b/variants/wio-e5/platformio.ini
new file mode 100644
index 00000000..0050d411
--- /dev/null
+++ b/variants/wio-e5/platformio.ini
@@ -0,0 +1,4 @@
+[env:wio-e5]
+extends = stm32wl5e_base
+build_flags = ${stm32wl5e_base.build_flags} -Ivariants/wio-e5 -DHAL_DAC_MODULE_ONLY
+ -DSERIAL_UART_INSTANCE=1 -DPIN_SERIAL_RX=PB7 -DPIN_SERIAL_TX=PB6
diff --git a/variants/wio-e5/variant.h b/variants/wio-e5/variant.h
new file mode 100644
index 00000000..3ef371e2
--- /dev/null
+++ b/variants/wio-e5/variant.h
@@ -0,0 +1,29 @@
+/*
+Wio-E5 mini (formerly LoRa-E5 mini)
+https://www.seeedstudio.com/LoRa-E5-mini-STM32WLE5JC-p-4869.html
+https://www.seeedstudio.com/LoRa-E5-Wireless-Module-p-4745.html
+*/
+
+/*
+This variant is a work in progress.
+Do not expect a working Meshtastic device with this target.
+*/
+
+#ifndef _VARIANT_WIOE5_
+#define _VARIANT_WIOE5_
+
+//Arduino/PlatformIO support for SUBGHZSPI is not currently available
+//#define USE_SX1262
+
+#ifdef USE_SX1262
+ #define HAS_RADIO 1
+
+ /* module only transmits through RFO_HP */
+ #define SX126X_RXEN PA5
+ #define SX126X_TXEN PA4
+
+ /* TCXO fed by internal LDO option behind PB0 pin */
+ #define SX126X_E22
+#endif
+
+#endif
\ No newline at end of file