diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini
index 7e3557475..351c7e65c 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#096b3c3e9c5c8e19d4c3b6cd803fffef2a9be4c5
+platform = https://github.com/meshtastic/platform-native.git#489ff929dca0bb768256ba2de45f95815111490f
framework = arduino
build_src_filter =
@@ -27,4 +27,4 @@ lib_deps =
build_flags =
${arduino_base.build_flags}
-fPIC
- -Isrc/platform/portduino
+ -Isrc/platform/portduino
\ No newline at end of file
diff --git a/arch/stm32/stm32wl5e.ini b/arch/stm32/stm32wl5e.ini
index b610bf40b..d3d577a9f 100644
--- a/arch/stm32/stm32wl5e.ini
+++ b/arch/stm32/stm32wl5e.ini
@@ -1,20 +1,29 @@
[stm32wl5e_base]
-platform = platformio/ststm32@^15.4.1
+platform_packages = platformio/framework-arduinoststm32 @ https://github.com/stm32duino/Arduino_Core_STM32.git#6e3f9910d0122e82a6c3438507dfac3d2fd80a39
+platform = ststm32
board = generic_wl5e
framework = arduino
build_type = debug
-build_flags =
+
+build_flags =
${arduino_base.build_flags}
-Isrc/platform/stm32wl -g
+ -DconfigUSE_CMSIS_RTOS_V2=1
+ -DVECT_TAB_OFFSET=0x08000000
+
+build_src_filter =
+ ${arduino_base.build_src_filter} - - - - - - - - - - - - -
-build_src_filter =
- ${arduino_base.build_src_filter} - - - - - - - - - - - - - -
+board_upload.offset_address = 0x08000000
+upload_protocol = stlink
lib_deps =
${env.lib_deps}
jgromes/RadioLib@^6.0.0
https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b
+ https://github.com/littlefs-project/littlefs.git#v2.5.1
+ https://github.com/stm32duino/STM32FreeRTOS.git#10.3.1
lib_ignore =
https://github.com/mathertel/OneButton#2.1.0
diff --git a/boards/generic_wl5e.json b/boards/generic_wl5e.json
index 433c55b59..5c4bc24a7 100644
--- a/boards/generic_wl5e.json
+++ b/boards/generic_wl5e.json
@@ -20,7 +20,7 @@
"maximum_ram_size": 65536,
"maximum_size": 262144,
"protocol": "cmsis-dap",
- "protocols": ["cmsis-dap"]
+ "protocols": ["cmsis-dap", "stlink"]
},
"url": "https://www.st.com/en/microcontrollers-microprocessors/stm32wl-series.html",
"vendor": "ST"
diff --git a/platformio.ini b/platformio.ini
index 156eb0076..9abb56b7a 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -2,7 +2,7 @@
; https://docs.platformio.org/page/projectconf.html
[platformio]
-default_envs = tbeam
+;default_envs = tbeam
;default_envs = pico
;default_envs = tbeam-s3-core
;default_envs = tbeam0.7
@@ -25,6 +25,7 @@ default_envs = tbeam
;default_envs = meshtastic-dr-dev
;default_envs = m5stack-coreink
;default_envs = rak4631
+default_envs = wio-e5
extra_configs =
arch/*/*.ini
diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp
index cc99caed8..150391237 100644
--- a/src/FSCommon.cpp
+++ b/src/FSCommon.cpp
@@ -57,7 +57,7 @@ bool renameFile(const char *pathFrom, const char *pathTo)
#endif
}
-void listDir(const char *dirname, uint8_t levels, boolean del = false)
+void listDir(const char *dirname, uint8_t levels, bool del = false)
{
#ifdef FSCom
#if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
diff --git a/src/FSCommon.h b/src/FSCommon.h
index 98d3911d7..ef1d3e4c1 100644
--- a/src/FSCommon.h
+++ b/src/FSCommon.h
@@ -13,6 +13,13 @@
#define FILE_O_READ "r"
#endif
+#if defined(ARCH_STM32WL)
+#include "platform/stm32wl/InternalFileSystem.h" // STM32WL version
+#define FSCom InternalFS
+#define FSBegin() FSCom.begin()
+using namespace LittleFS_Namespace;
+#endif
+
#if defined(ARCH_RP2040)
// RP2040
#include "LittleFS.h"
@@ -42,6 +49,6 @@ using namespace Adafruit_LittleFS_Namespace;
void fsInit();
bool copyFile(const char *from, const char *to);
bool renameFile(const char *pathFrom, const char *pathTo);
-void listDir(const char *dirname, uint8_t levels, boolean del);
+void listDir(const char *dirname, uint8_t levels, bool del);
void rmDir(const char *dirname);
void setupSDCard();
\ No newline at end of file
diff --git a/src/Power.cpp b/src/Power.cpp
index 1f4c3535a..37d80a31f 100644
--- a/src/Power.cpp
+++ b/src/Power.cpp
@@ -37,12 +37,18 @@ static const adc_atten_t atten = ADC_ATTENUATION;
#endif
#endif // BATTERY_PIN && ARCH_ESP32
+#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
+INA260Sensor ina260Sensor;
+INA219Sensor ina219Sensor;
+#endif
+
#ifdef HAS_PMU
#include "XPowersAXP192.tpp"
#include "XPowersAXP2101.tpp"
#include "XPowersLibInterface.hpp"
XPowersLibInterface *PMU = NULL;
#else
+
// Copy of the base class defined in axp20x.h.
// I'd rather not inlude axp20x.h as it brings Wire dependency.
class HasBatteryLevel
@@ -128,6 +134,13 @@ class AnalogBatteryLevel : public HasBatteryLevel
virtual uint16_t getBattVoltage() override
{
+#if defined(HAS_TELEMETRY) && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU)
+ if (hasINA()) {
+ LOG_DEBUG("Using INA on I2C addr 0x%x for device battery voltage\n", config.power.device_battery_ina_address);
+ return getINAVoltage();
+ }
+#endif
+
#ifndef ADC_MULTIPLIER
#define ADC_MULTIPLIER 2.0
#endif
@@ -246,6 +259,35 @@ class AnalogBatteryLevel : public HasBatteryLevel
const float fullVolt = BAT_FULLVOLT, emptyVolt = BAT_EMPTYVOLT, chargingVolt = BAT_CHARGINGVOLT, noBatVolt = BAT_NOBATVOLT;
float last_read_value = 0.0;
uint32_t last_read_time_ms = 0;
+
+#if defined(HAS_TELEMETRY) && !defined(ARCH_PORTDUINO)
+ uint16_t getINAVoltage()
+ {
+ if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219] == config.power.device_battery_ina_address) {
+ return ina219Sensor.getBusVoltageMv();
+ } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260] == config.power.device_battery_ina_address) {
+ return ina260Sensor.getBusVoltageMv();
+ }
+ return 0;
+ }
+
+ bool hasINA()
+ {
+ if (!config.power.device_battery_ina_address) {
+ return false;
+ }
+ if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219] == config.power.device_battery_ina_address) {
+ if (!ina219Sensor.isInitialized())
+ return ina219Sensor.runOnce() > 0;
+ return ina219Sensor.isRunning();
+ } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260] == config.power.device_battery_ina_address) {
+ if (!ina260Sensor.isInitialized())
+ return ina260Sensor.runOnce() > 0;
+ return ina260Sensor.isRunning();
+ }
+ return false;
+ }
+#endif
};
AnalogBatteryLevel analogLevel;
@@ -639,7 +681,6 @@ bool Power::axpChipInit()
// GNSS VDD 3300mV
PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
PMU->enablePowerOutput(XPOWERS_ALDO3);
-
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) {
// t-beam s3 core
/**
diff --git a/src/freertosinc.h b/src/freertosinc.h
index c5dfddc8e..dbe7c4001 100644
--- a/src/freertosinc.h
+++ b/src/freertosinc.h
@@ -12,7 +12,7 @@
#include
#endif
-#if defined(ARDUINO_NRF52_ADAFRUIT)
+#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_STM32)
#define HAS_FREE_RTOS
#include
diff --git a/src/main.cpp b/src/main.cpp
index 5120cb269..e7ce565c5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -59,6 +59,9 @@ NRF52Bluetooth *nrf52Bluetooth;
#include "SX1262Interface.h"
#include "SX1268Interface.h"
#include "SX1280Interface.h"
+#ifdef ARCH_STM32WL
+#include "STM32WLE5JCInterface.h"
+#endif
#if !HAS_RADIO && defined(ARCH_PORTDUINO)
#include "platform/portduino/SimRadio.h"
#endif
@@ -68,7 +71,7 @@ NRF52Bluetooth *nrf52Bluetooth;
#endif
#include "PowerFSMThread.h"
-#if !defined(ARCH_PORTDUINO)
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
#include "AccelerometerThread.h"
#endif
@@ -347,10 +350,11 @@ void setup()
* nodeTelemetrySensorsMap singleton. This wraps that logic in a temporary scope to declare the temporary field
* "found".
*/
+
// Only one supported RGB LED currently
rgb_found = i2cScanner->find(ScanI2C::DeviceType::NCP5623);
-#if !defined(ARCH_PORTDUINO)
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
auto acc_info = i2cScanner->firstAccelerometer();
accelerometer_found = acc_info.type != ScanI2C::DeviceType::NONE ? acc_info.address : accelerometer_found;
LOG_DEBUG("acc_info = %i\n", acc_info.type);
@@ -439,7 +443,7 @@ void setup()
screen_model = meshtastic_Config_DisplayConfig_OledType_OLED_SH1107; // keep dimension of 128x64
#endif
-#if !defined(ARCH_PORTDUINO)
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
if (acc_info.type != ScanI2C::DeviceType::NONE) {
accelerometerThread = new AccelerometerThread(acc_info.type);
}
@@ -530,6 +534,18 @@ void setup()
#endif
// radio init MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
+#if defined(USE_STM32WLx)
+ if (!rIf) {
+ rIf = new STM32WLE5JCInterface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
+ if (!rIf->init()) {
+ LOG_WARN("Failed to find STM32WL radio\n");
+ delete rIf;
+ rIf = NULL;
+ } else {
+ LOG_INFO("STM32WL Radio init succeeded, using STM32WL radio\n");
+ }
+ }
+#endif
#if !HAS_RADIO && defined(ARCH_PORTDUINO)
if (!rIf) {
diff --git a/src/main.h b/src/main.h
index 0d23dd10c..93baec590 100644
--- a/src/main.h
+++ b/src/main.h
@@ -38,8 +38,6 @@ extern bool isUSBPowered;
extern ATECCX08A atecc;
#endif
-extern uint8_t nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1];
-
extern int TCPPort; // set by Portduino
// Global Screen singleton.
@@ -69,4 +67,4 @@ void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), clearBonds();
meshtastic_DeviceMetadata getDeviceMetadata();
// FIXME, we default to 4MHz SPI, SPI mode 0, check if the datasheet says it can really do that
-extern SPISettings spiSettings;
+extern SPISettings spiSettings;
\ No newline at end of file
diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp
index d8861943e..171199277 100644
--- a/src/mesh/FloodingRouter.cpp
+++ b/src/mesh/FloodingRouter.cpp
@@ -21,6 +21,7 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
{
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
printPacket("Ignoring incoming msg, because we've already seen it", p);
+ Router::cancelSending(p->from, p->id); // cancel rebroadcast of this message *if* there was already one
return true;
}
@@ -62,4 +63,4 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
}
// handle the packet as normal
Router::sniffReceived(p, c);
-}
+}
\ No newline at end of file
diff --git a/src/mesh/InterfacesTemplates.cpp b/src/mesh/InterfacesTemplates.cpp
index 73b0bdfbc..c732829e9 100644
--- a/src/mesh/InterfacesTemplates.cpp
+++ b/src/mesh/InterfacesTemplates.cpp
@@ -10,6 +10,9 @@ template class SX126xInterface;
template class SX126xInterface;
template class SX126xInterface;
template class SX128xInterface;
+#ifdef ARCH_STM32WL
+template class SX126xInterface;
+#endif
#if HAS_ETHERNET
#include "api/ethServerAPI.h"
diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h
index 6635089e5..4634ca7ee 100644
--- a/src/mesh/RadioLibInterface.h
+++ b/src/mesh/RadioLibInterface.h
@@ -27,6 +27,19 @@ class LockingArduinoHal : public ArduinoHal
void spiEndTransaction() override;
};
+#if defined(USE_STM32WLx)
+/**
+ * A wrapper for the RadioLib STM32WLx_Module class, that doesn't connect any pins as they are virtual
+ */
+class STM32WLx_ModuleWrapper : public STM32WLx_Module
+{
+ public:
+ STM32WLx_ModuleWrapper(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
+ RADIOLIB_PIN_TYPE busy)
+ : STM32WLx_Module(){};
+};
+#endif
+
class RadioLibInterface : public RadioInterface, protected concurrency::NotifiedWorkerThread
{
/// Used as our notification from the ISR
@@ -56,7 +69,11 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
float currentLimit = 100; // 100mA OCP - Should be acceptable for RFM95/SX127x chipset.
+#if !defined(USE_STM32WLx)
Module module; // The HW interface to the radio
+#else
+ STM32WLx_ModuleWrapper module;
+#endif
/**
* provides lowest common denominator RadioLib API
diff --git a/src/mesh/STM32WLE5JCInterface.cpp b/src/mesh/STM32WLE5JCInterface.cpp
new file mode 100644
index 000000000..5b6fd0844
--- /dev/null
+++ b/src/mesh/STM32WLE5JCInterface.cpp
@@ -0,0 +1,45 @@
+#include "STM32WLE5JCInterface.h"
+#include "configuration.h"
+#include "error.h"
+
+#ifndef STM32WLx_MAX_POWER
+#define STM32WLx_MAX_POWER 22
+#endif
+
+#ifdef ARCH_STM32WL
+
+STM32WLE5JCInterface::STM32WLE5JCInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq,
+ RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy)
+ : SX126xInterface(hal, cs, irq, rst, busy)
+{
+}
+
+bool STM32WLE5JCInterface::init()
+{
+ RadioLibInterface::init();
+
+ lora.setRfSwitchTable(rfswitch_pins, rfswitch_table);
+
+ if (power == 0)
+ power = STM32WLx_MAX_POWER;
+
+ if (power > STM32WLx_MAX_POWER) // This chip has lower power limits than some
+ power = STM32WLx_MAX_POWER;
+
+ limitPower();
+
+ int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
+
+ LOG_INFO("STM32WLx init result %d\n", res);
+
+ LOG_INFO("Frequency set to %f\n", getFreq());
+ LOG_INFO("Bandwidth set to %f\n", bw);
+ LOG_INFO("Power output set to %d\n", power);
+
+ if (res == RADIOLIB_ERR_NONE)
+ startReceive(); // start receiving
+
+ return res == RADIOLIB_ERR_NONE;
+}
+
+#endif // ARCH_STM32WL
diff --git a/src/mesh/STM32WLE5JCInterface.h b/src/mesh/STM32WLE5JCInterface.h
new file mode 100644
index 000000000..73d53d92f
--- /dev/null
+++ b/src/mesh/STM32WLE5JCInterface.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "SX126xInterface.h"
+
+#ifdef ARCH_STM32WL
+
+/**
+ * Our adapter for STM32WLE5JC radios
+ */
+class STM32WLE5JCInterface : public SX126xInterface
+{
+ public:
+ STM32WLE5JCInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
+ RADIOLIB_PIN_TYPE busy);
+
+ virtual bool init() override;
+};
+
+// https://github.com/Seeed-Studio/LoRaWan-E5-Node/blob/main/Middlewares/Third_Party/SubGHz_Phy/stm32_radio_driver/radio_driver.c
+static const float tcxoVoltage = 1.7;
+
+/* https://wiki.seeedstudio.com/LoRa-E5_STM32WLE5JC_Module/
+ * Wio-E5 module ONLY transmits through RFO_HP
+ * Receive: PA4=1, PA5=0
+ * Transmit(high output power, SMPS mode): PA4=0, PA5=1 */
+static const RADIOLIB_PIN_TYPE rfswitch_pins[3] = {PA4, PA5, RADIOLIB_NC};
+
+static const Module::RfSwitchMode_t rfswitch_table[4] = {
+ {STM32WLx::MODE_IDLE, {LOW, LOW}}, {STM32WLx::MODE_RX, {HIGH, LOW}}, {STM32WLx::MODE_TX_HP, {LOW, HIGH}}, END_OF_MODE_TABLE};
+
+#endif // ARCH_STM32WL
\ No newline at end of file
diff --git a/src/modules/ExternalNotificationModule.h b/src/modules/ExternalNotificationModule.h
index 7d4f54c2f..f8ec053dd 100644
--- a/src/modules/ExternalNotificationModule.h
+++ b/src/modules/ExternalNotificationModule.h
@@ -3,7 +3,7 @@
#include "SinglePortModule.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
-#ifndef ARCH_PORTDUINO
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
#include
#else
// Noop class for portduino.
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index 76385197a..5cdc4bf4d 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -7,6 +7,7 @@
#include "Router.h"
#include "configuration.h"
#include "main.h"
+#include "power.h"
#include
#include
@@ -14,8 +15,6 @@
#include "Sensor/BME280Sensor.h"
#include "Sensor/BME680Sensor.h"
#include "Sensor/BMP280Sensor.h"
-#include "Sensor/INA219Sensor.h"
-#include "Sensor/INA260Sensor.h"
#include "Sensor/LPS22HBSensor.h"
#include "Sensor/MCP9808Sensor.h"
#include "Sensor/SHT31Sensor.h"
@@ -25,8 +24,6 @@ BMP280Sensor bmp280Sensor;
BME280Sensor bme280Sensor;
BME680Sensor bme680Sensor;
MCP9808Sensor mcp9808Sensor;
-INA260Sensor ina260Sensor;
-INA219Sensor ina219Sensor;
SHTC3Sensor shtc3Sensor;
LPS22HBSensor lps22hbSensor;
SHT31Sensor sht31Sensor;
@@ -83,15 +80,10 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = bme680Sensor.runOnce();
if (mcp9808Sensor.hasSensor())
result = mcp9808Sensor.runOnce();
- if (ina260Sensor.hasSensor())
- result = ina260Sensor.runOnce();
- if (ina219Sensor.hasSensor())
- result = ina219Sensor.runOnce();
if (shtc3Sensor.hasSensor())
result = shtc3Sensor.runOnce();
- if (lps22hbSensor.hasSensor()) {
+ if (lps22hbSensor.hasSensor())
result = lps22hbSensor.runOnce();
- }
if (sht31Sensor.hasSensor())
result = sht31Sensor.runOnce();
}
diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.cpp b/src/modules/Telemetry/Sensor/INA219Sensor.cpp
index bffa1e367..1dd7f7f2c 100644
--- a/src/modules/Telemetry/Sensor/INA219Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA219Sensor.cpp
@@ -12,8 +12,12 @@ int32_t INA219Sensor::runOnce()
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
- ina219 = Adafruit_INA219(nodeTelemetrySensorsMap[sensorType]);
- status = ina219.begin();
+ if (!ina219.success()) {
+ ina219 = Adafruit_INA219(nodeTelemetrySensorsMap[sensorType]);
+ status = ina219.begin();
+ } else {
+ status = ina219.success();
+ }
return initI2CSensor();
}
@@ -24,4 +28,9 @@ bool INA219Sensor::getMetrics(meshtastic_Telemetry *measurement)
measurement->variant.environment_metrics.voltage = ina219.getBusVoltage_V();
measurement->variant.environment_metrics.current = ina219.getCurrent_mA();
return true;
+}
+
+uint16_t INA219Sensor::getBusVoltageMv()
+{
+ return lround(ina219.getBusVoltage_V() * 1000);
}
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.h b/src/modules/Telemetry/Sensor/INA219Sensor.h
index dc6c0a5af..f11a571cc 100644
--- a/src/modules/Telemetry/Sensor/INA219Sensor.h
+++ b/src/modules/Telemetry/Sensor/INA219Sensor.h
@@ -1,8 +1,9 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
+#include "VoltageSensor.h"
#include
-class INA219Sensor : virtual public TelemetrySensor
+class INA219Sensor : virtual public TelemetrySensor, VoltageSensor
{
private:
Adafruit_INA219 ina219;
@@ -14,4 +15,5 @@ class INA219Sensor : virtual public TelemetrySensor
INA219Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+ virtual uint16_t getBusVoltageMv() override;
};
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA260Sensor.cpp b/src/modules/Telemetry/Sensor/INA260Sensor.cpp
index ee0256e62..034fecca0 100644
--- a/src/modules/Telemetry/Sensor/INA260Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA260Sensor.cpp
@@ -12,7 +12,10 @@ int32_t INA260Sensor::runOnce()
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
- status = ina260.begin(nodeTelemetrySensorsMap[sensorType]);
+
+ if (!status) {
+ status = ina260.begin(nodeTelemetrySensorsMap[sensorType]);
+ }
return initI2CSensor();
}
@@ -24,4 +27,9 @@ bool INA260Sensor::getMetrics(meshtastic_Telemetry *measurement)
measurement->variant.environment_metrics.voltage = ina260.readBusVoltage() / 1000;
measurement->variant.environment_metrics.current = ina260.readCurrent();
return true;
+}
+
+uint16_t INA260Sensor::getBusVoltageMv()
+{
+ return lround(ina260.readBusVoltage());
}
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/INA260Sensor.h b/src/modules/Telemetry/Sensor/INA260Sensor.h
index 1dcb596e6..8ea532697 100644
--- a/src/modules/Telemetry/Sensor/INA260Sensor.h
+++ b/src/modules/Telemetry/Sensor/INA260Sensor.h
@@ -1,8 +1,9 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
+#include "VoltageSensor.h"
#include
-class INA260Sensor : virtual public TelemetrySensor
+class INA260Sensor : virtual public TelemetrySensor, VoltageSensor
{
private:
Adafruit_INA260 ina260 = Adafruit_INA260();
@@ -14,4 +15,5 @@ class INA260Sensor : virtual public TelemetrySensor
INA260Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+ virtual uint16_t getBusVoltageMv() override;
};
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/TelemetrySensor.h b/src/modules/Telemetry/Sensor/TelemetrySensor.h
index 7a579ef5c..dec81e061 100644
--- a/src/modules/Telemetry/Sensor/TelemetrySensor.h
+++ b/src/modules/Telemetry/Sensor/TelemetrySensor.h
@@ -1,9 +1,9 @@
#pragma once
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "NodeDB.h"
-#include "main.h"
#define DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
+extern uint8_t nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1];
class TelemetrySensor
{
@@ -18,6 +18,7 @@ class TelemetrySensor
const char *sensorName;
meshtastic_TelemetrySensorType sensorType;
unsigned status;
+ bool initialized = false;
int32_t initI2CSensor()
{
@@ -28,6 +29,7 @@ class TelemetrySensor
LOG_INFO("Opened %s sensor on default i2c bus\n", sensorName);
setup();
}
+ initialized = true;
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
virtual void setup();
@@ -36,5 +38,8 @@ class TelemetrySensor
bool hasSensor() { return sensorType < sizeof(nodeTelemetrySensorsMap) && nodeTelemetrySensorsMap[sensorType] > 0; }
virtual int32_t runOnce() = 0;
+ virtual bool isInitialized() { return initialized; }
+ virtual bool isRunning() { return status > 0; }
+
virtual bool getMetrics(meshtastic_Telemetry *measurement) = 0;
-};
+};
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/VoltageSensor.h b/src/modules/Telemetry/Sensor/VoltageSensor.h
new file mode 100644
index 000000000..f2f28fb06
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/VoltageSensor.h
@@ -0,0 +1,7 @@
+#pragma once
+
+class VoltageSensor
+{
+ public:
+ virtual uint16_t getBusVoltageMv() = 0;
+};
\ No newline at end of file
diff --git a/src/platform/stm32wl/InternalFileSystem.cpp b/src/platform/stm32wl/InternalFileSystem.cpp
new file mode 100644
index 000000000..950ceb0cd
--- /dev/null
+++ b/src/platform/stm32wl/InternalFileSystem.cpp
@@ -0,0 +1,141 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 hathach for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "InternalFileSystem.h"
+#include
+
+//--------------------------------------------------------------------+
+// LFS Disk IO
+//--------------------------------------------------------------------+
+
+static inline uint32_t lba2addr(uint32_t block)
+{
+ return ((uint32_t)LFS_FLASH_ADDR) + block * LFS_BLOCK_SIZE;
+}
+
+static int _internal_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
+{
+ (void)c;
+
+ uint32_t addr = lba2addr(block) + off;
+ uint8_t prom;
+
+ for (int i = 0; i < size; i++) {
+ prom = EEPROM.read(addr + i);
+ memcpy((char *)buffer + i, &prom, 1);
+ }
+ return 0;
+}
+
+// Program a region in a block. The block must have previously
+// been erased. Negative error codes are propogated to the user.
+// May return LFS_ERR_CORRUPT if the block should be considered bad.
+static int _internal_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
+{
+ (void)c;
+
+ uint32_t addr = lba2addr(block) + off;
+ uint8_t prom;
+
+ for (int i = 0; i < size; i++) {
+ memcpy(&prom, (char *)buffer + i, 1);
+ EEPROM.update(addr + i, prom);
+ }
+ return 0;
+}
+
+// Erase a block. A block must be erased before being programmed.
+// The state of an erased block is undefined. Negative error codes
+// are propogated to the user.
+// May return LFS_ERR_CORRUPT if the block should be considered bad.
+static int _internal_flash_erase(const struct lfs_config *c, lfs_block_t block)
+{
+ (void)c;
+
+ uint32_t addr = lba2addr(block);
+
+ // implement as write 0xff to whole block address
+ for (int i = 0; i < LFS_BLOCK_SIZE; i++) {
+ EEPROM.update(addr, 0xff);
+ }
+
+ return 0;
+}
+
+// Sync the state of the underlying block device. Negative error codes
+// are propogated to the user.
+static int _internal_flash_sync(const struct lfs_config *c)
+{
+ // we don't use a ram cache, this is a noop
+ return 0;
+}
+
+static struct lfs_config _InternalFSConfig = {.context = NULL,
+
+ .read = _internal_flash_read,
+ .prog = _internal_flash_prog,
+ .erase = _internal_flash_erase,
+ .sync = _internal_flash_sync,
+
+ .read_size = LFS_CACHE_SIZE,
+ .prog_size = LFS_CACHE_SIZE,
+ .block_size = LFS_BLOCK_SIZE,
+ .block_count = LFS_FLASH_TOTAL_SIZE / LFS_BLOCK_SIZE,
+ .block_cycles =
+ 500, // protection against wear leveling (suggested values between 100-1000)
+ .cache_size = LFS_CACHE_SIZE,
+ .lookahead_size = LFS_CACHE_SIZE,
+
+ .read_buffer = lfs_read_buffer,
+ .prog_buffer = lfs_prog_buffer,
+ .lookahead_buffer = lfs_lookahead_buffer};
+
+InternalFileSystem InternalFS;
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+InternalFileSystem::InternalFileSystem(void) : LittleFS(&_InternalFSConfig) {}
+
+bool InternalFileSystem::begin(void)
+{
+ // failed to mount, erase all sector then format and mount again
+ if (!LittleFS::begin()) {
+ // Erase all sectors of internal flash region for Filesystem.
+ // implement as write 0xff to whole block address
+ for (uint32_t addr = LFS_FLASH_ADDR; addr < (LFS_FLASH_ADDR + LFS_FLASH_TOTAL_SIZE); addr++) {
+ EEPROM.update(addr, 0xff);
+ }
+
+ // lfs format
+ this->format();
+
+ // mount again if still failed, give up
+ if (!LittleFS::begin())
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/platform/stm32wl/InternalFileSystem.h b/src/platform/stm32wl/InternalFileSystem.h
new file mode 100644
index 000000000..66344194e
--- /dev/null
+++ b/src/platform/stm32wl/InternalFileSystem.h
@@ -0,0 +1,54 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 hathach for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef INTERNALFILESYSTEM_H_
+#define INTERNALFILESYSTEM_H_
+
+#include "LittleFS.h"
+
+// The EEPROM Library assumes our usable flash area starts at logical 0
+#define LFS_FLASH_ADDR 0
+
+// use the built in EEPROM emulation. Total Size is 2Kbyte
+#define LFS_BLOCK_SIZE 128 // min. block size is 128 to fit CTZ pointers
+#define LFS_CACHE_SIZE 16
+
+#define LFS_FLASH_TOTAL_SIZE FLASH_PAGE_SIZE
+
+static uint8_t lfs_read_buffer[LFS_CACHE_SIZE] = {0};
+static uint8_t lfs_prog_buffer[LFS_CACHE_SIZE] = {0};
+static uint8_t lfs_lookahead_buffer[LFS_CACHE_SIZE] = {0};
+
+class InternalFileSystem : public LittleFS
+{
+ public:
+ InternalFileSystem(void);
+
+ // overwrite to also perform low level format (sector erase of whole flash region)
+ bool begin(void);
+};
+
+extern InternalFileSystem InternalFS;
+
+#endif /* INTERNALFILESYSTEM_H_ */
diff --git a/src/platform/stm32wl/LittleFS.cpp b/src/platform/stm32wl/LittleFS.cpp
new file mode 100644
index 000000000..b1267d88a
--- /dev/null
+++ b/src/platform/stm32wl/LittleFS.cpp
@@ -0,0 +1,258 @@
+#include
+#include
+
+#include "LittleFS.h"
+
+using namespace LittleFS_Namespace;
+
+//--------------------------------------------------------------------+
+// Implementation
+//--------------------------------------------------------------------+
+
+LittleFS::LittleFS(void) : LittleFS(NULL) {}
+
+LittleFS::LittleFS(struct lfs_config *cfg)
+{
+ memset(&_lfs, 0, sizeof(_lfs));
+ _lfs_cfg = cfg;
+ _mounted = false;
+ _mutex = xSemaphoreCreateMutexStatic(&this->_MutexStorageSpace);
+}
+
+LittleFS::~LittleFS() {}
+
+// Initialize and mount the file system
+// Return true if mounted successfully else probably corrupted.
+// User should format the disk and try again
+bool LittleFS::begin(struct lfs_config *cfg)
+{
+ _lockFS();
+
+ bool ret;
+ // not a loop, just an quick way to short-circuit on error
+ do {
+ if (_mounted) {
+ ret = true;
+ break;
+ }
+ if (cfg) {
+ _lfs_cfg = cfg;
+ }
+ if (nullptr == _lfs_cfg) {
+ ret = false;
+ break;
+ }
+ // actually attempt to mount, and log error if one occurs
+ int err = lfs_mount(&_lfs, _lfs_cfg);
+ PRINT_LFS_ERR(err);
+ _mounted = (err == LFS_ERR_OK);
+ ret = _mounted;
+ } while (0);
+
+ _unlockFS();
+ return ret;
+}
+
+// Tear down and unmount file system
+void LittleFS::end(void)
+{
+ _lockFS();
+
+ if (_mounted) {
+ _mounted = false;
+ int err = lfs_unmount(&_lfs);
+ PRINT_LFS_ERR(err);
+ (void)err;
+ }
+
+ _unlockFS();
+}
+
+bool LittleFS::format(void)
+{
+ _lockFS();
+
+ int err = LFS_ERR_OK;
+ bool attemptMount = _mounted;
+ // not a loop, just an quick way to short-circuit on error
+ do {
+ // if already mounted: umount first -> format -> remount
+ if (_mounted) {
+ _mounted = false;
+ err = lfs_unmount(&_lfs);
+ if (LFS_ERR_OK != err) {
+ PRINT_LFS_ERR(err);
+ break;
+ }
+ }
+ err = lfs_format(&_lfs, _lfs_cfg);
+ if (LFS_ERR_OK != err) {
+ PRINT_LFS_ERR(err);
+ break;
+ }
+
+ if (attemptMount) {
+ err = lfs_mount(&_lfs, _lfs_cfg);
+ if (LFS_ERR_OK != err) {
+ PRINT_LFS_ERR(err);
+ break;
+ }
+ _mounted = true;
+ }
+ // success!
+ } while (0);
+
+ _unlockFS();
+ return LFS_ERR_OK == err;
+}
+
+// Open a file or folder
+LittleFS_Namespace::File LittleFS::open(char const *filepath, uint8_t mode)
+{
+ // No lock is required here ... the File() object will synchronize with the mutex provided
+ return LittleFS_Namespace::File(filepath, mode, *this);
+}
+
+// Check if file or folder exists
+bool LittleFS::exists(char const *filepath)
+{
+ struct lfs_info info;
+ _lockFS();
+
+ bool ret = (0 == lfs_stat(&_lfs, filepath, &info));
+
+ _unlockFS();
+ return ret;
+}
+
+// Create a directory, create intermediate parent if needed
+bool LittleFS::mkdir(char const *filepath)
+{
+ bool ret = true;
+ const char *slash = filepath;
+ if (slash[0] == '/')
+ slash++; // skip root '/'
+
+ _lockFS();
+
+ // make intermediate parent directory(ies)
+ while (NULL != (slash = strchr(slash, '/'))) {
+ char parent[slash - filepath + 1] = {0};
+ memcpy(parent, filepath, slash - filepath);
+
+ int rc = lfs_mkdir(&_lfs, parent);
+ if (rc != LFS_ERR_OK && rc != LFS_ERR_EXIST) {
+ PRINT_LFS_ERR(rc);
+ ret = false;
+ break;
+ }
+ slash++;
+ }
+ // make the final requested directory
+ if (ret) {
+ int rc = lfs_mkdir(&_lfs, filepath);
+ if (rc != LFS_ERR_OK && rc != LFS_ERR_EXIST) {
+ PRINT_LFS_ERR(rc);
+ ret = false;
+ }
+ }
+
+ _unlockFS();
+ return ret;
+}
+
+// Remove a file
+bool LittleFS::remove(char const *filepath)
+{
+ _lockFS();
+
+ int err = lfs_remove(&_lfs, filepath);
+ PRINT_LFS_ERR(err);
+
+ _unlockFS();
+ return LFS_ERR_OK == err;
+}
+
+// Rename a file
+bool LittleFS::rename(char const *oldfilepath, char const *newfilepath)
+{
+ _lockFS();
+
+ int err = lfs_rename(&_lfs, oldfilepath, newfilepath);
+ PRINT_LFS_ERR(err);
+
+ _unlockFS();
+ return LFS_ERR_OK == err;
+}
+
+// Remove a folder
+bool LittleFS::rmdir(char const *filepath)
+{
+ _lockFS();
+
+ int err = lfs_remove(&_lfs, filepath);
+ PRINT_LFS_ERR(err);
+
+ _unlockFS();
+ return LFS_ERR_OK == err;
+}
+
+// Remove a folder recursively
+bool LittleFS::rmdir_r(char const *filepath)
+{
+ /* adafruit: lfs is modified to remove non-empty folder,
+ According to below issue, comment these 2 line won't corrupt filesystem
+ at least when using LFS v1. If moving to LFS v2, see tracked issue
+ to see if issues (such as the orphans in threaded linked list) are resolved.
+ https://github.com/ARMmbed/littlefs/issues/43
+ */
+ _lockFS();
+
+ int err = lfs_remove(&_lfs, filepath);
+ PRINT_LFS_ERR(err);
+
+ _unlockFS();
+ return LFS_ERR_OK == err;
+}
+
+//------------- Debug -------------//
+#if CFG_DEBUG
+
+const char *dbg_strerr_lfs(int32_t err)
+{
+ switch (err) {
+ case LFS_ERR_OK:
+ return "LFS_ERR_OK";
+ case LFS_ERR_IO:
+ return "LFS_ERR_IO";
+ case LFS_ERR_CORRUPT:
+ return "LFS_ERR_CORRUPT";
+ case LFS_ERR_NOENT:
+ return "LFS_ERR_NOENT";
+ case LFS_ERR_EXIST:
+ return "LFS_ERR_EXIST";
+ case LFS_ERR_NOTDIR:
+ return "LFS_ERR_NOTDIR";
+ case LFS_ERR_ISDIR:
+ return "LFS_ERR_ISDIR";
+ case LFS_ERR_NOTEMPTY:
+ return "LFS_ERR_NOTEMPTY";
+ case LFS_ERR_BADF:
+ return "LFS_ERR_BADF";
+ case LFS_ERR_INVAL:
+ return "LFS_ERR_INVAL";
+ case LFS_ERR_NOSPC:
+ return "LFS_ERR_NOSPC";
+ case LFS_ERR_NOMEM:
+ return "LFS_ERR_NOMEM";
+
+ default:
+ static char errcode[10];
+ sprintf(errcode, "%ld", err);
+ return errcode;
+ }
+
+ return NULL;
+}
+
+#endif
diff --git a/src/platform/stm32wl/LittleFS.h b/src/platform/stm32wl/LittleFS.h
new file mode 100644
index 000000000..4a0b01af2
--- /dev/null
+++ b/src/platform/stm32wl/LittleFS.h
@@ -0,0 +1,85 @@
+
+#ifndef LITTLEFS_H_
+#define LITTLEFS_H_
+
+#include
+
+#include "lfs.h"
+
+#include "LittleFS_File.h"
+
+#include "FreeRTOS.h" // tied to FreeRTOS for serialization
+#include "semphr.h"
+
+class LittleFS
+{
+ public:
+ LittleFS(void);
+ explicit LittleFS(struct lfs_config *cfg);
+ virtual ~LittleFS();
+
+ bool begin(struct lfs_config *cfg = NULL);
+ void end(void);
+
+ // Open the specified file/directory with the supplied mode (e.g. read or
+ // write, etc). Returns a File object for interacting with the file.
+ // Note that currently only one file can be open at a time.
+ LittleFS_Namespace::File open(char const *filename, uint8_t mode = LittleFS_Namespace::FILE_O_READ);
+
+ // Methods to determine if the requested file path exists.
+ bool exists(char const *filepath);
+
+ // Create the requested directory hierarchy--if intermediate directories
+ // do not exist they will be created.
+ bool mkdir(char const *filepath);
+
+ // Delete the file.
+ bool remove(char const *filepath);
+
+ // Rename the file.
+ bool rename(char const *oldfilepath, char const *newfilepath);
+
+ // Delete a folder (must be empty)
+ bool rmdir(char const *filepath);
+
+ // Delete a folder (recursively)
+ bool rmdir_r(char const *filepath);
+
+ // format file system
+ bool format(void);
+
+ /*------------------------------------------------------------------*/
+ /* INTERNAL USAGE ONLY
+ * Although declare as public, it is meant to be invoked by internal
+ * code. User should not call these directly
+ *------------------------------------------------------------------*/
+ lfs_t *_getFS(void) { return &_lfs; }
+ void _lockFS(void) { xSemaphoreTake(_mutex, portMAX_DELAY); }
+ void _unlockFS(void) { xSemaphoreGive(_mutex); }
+
+ protected:
+ bool _mounted;
+ struct lfs_config *_lfs_cfg;
+ lfs_t _lfs;
+ SemaphoreHandle_t _mutex;
+
+ private:
+ StaticSemaphore_t _MutexStorageSpace;
+};
+
+#if !CFG_DEBUG
+#define VERIFY_LFS(...) _GET_3RD_ARG(__VA_ARGS__, VERIFY_ERR_2ARGS, VERIFY_ERR_1ARGS)(__VA_ARGS__, NULL)
+#define PRINT_LFS_ERR(_err)
+#else
+#define VERIFY_LFS(...) _GET_3RD_ARG(__VA_ARGS__, VERIFY_ERR_2ARGS, VERIFY_ERR_1ARGS)(__VA_ARGS__, dbg_strerr_lfs)
+#define PRINT_LFS_ERR(_err) \
+ do { \
+ if (_err) { \
+ VERIFY_MESS((long int)_err, dbg_strerr_lfs); \
+ } \
+ } while (0) // LFS_ERR are of type int, VERIFY_MESS expects long_int
+
+const char *dbg_strerr_lfs(int32_t err);
+#endif
+
+#endif /* LITTLEFS_H_ */
diff --git a/src/platform/stm32wl/LittleFS_File.cpp b/src/platform/stm32wl/LittleFS_File.cpp
new file mode 100644
index 000000000..cffb924e1
--- /dev/null
+++ b/src/platform/stm32wl/LittleFS_File.cpp
@@ -0,0 +1,362 @@
+#include
+
+#include "LittleFS.h"
+
+#include
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+using namespace LittleFS_Namespace;
+
+File::File(LittleFS &fs)
+{
+ _fs = &fs;
+ _is_dir = false;
+ _name[0] = 0;
+ _dir_path = NULL;
+
+ _dir = NULL;
+ _file = NULL;
+}
+
+File::File(char const *filename, uint8_t mode, LittleFS &fs) : File(fs)
+{
+ // public constructor calls public API open(), which will obtain the mutex
+ this->open(filename, mode);
+}
+
+bool File::_open_file(char const *filepath, uint8_t mode)
+{
+ int flags = (mode == FILE_O_READ) ? LFS_O_RDONLY : (mode == FILE_O_WRITE) ? (LFS_O_RDWR | LFS_O_CREAT) : 0;
+
+ if (flags) {
+ _file = (lfs_file_t *)malloc(sizeof(lfs_file_t));
+ if (!_file)
+ return false;
+
+ int rc = lfs_file_open(_fs->_getFS(), _file, filepath, flags);
+
+ if (rc) {
+ // failed to open
+ PRINT_LFS_ERR(rc);
+ return false;
+ }
+
+ // move to end of file
+ if (mode == FILE_O_WRITE)
+ lfs_file_seek(_fs->_getFS(), _file, 0, LFS_SEEK_END);
+
+ _is_dir = false;
+ }
+
+ return true;
+}
+
+bool File::_open_dir(char const *filepath)
+{
+ _dir = (lfs_dir_t *)malloc(sizeof(lfs_dir_t));
+ if (!_dir)
+ return false;
+
+ int rc = lfs_dir_open(_fs->_getFS(), _dir, filepath);
+
+ if (rc) {
+ // failed to open
+ PRINT_LFS_ERR(rc);
+ return false;
+ }
+
+ _is_dir = true;
+
+ _dir_path = (char *)malloc(strlen(filepath) + 1);
+ strcpy(_dir_path, filepath);
+
+ return true;
+}
+
+bool File::open(char const *filepath, uint8_t mode)
+{
+ bool ret = false;
+ _fs->_lockFS();
+
+ ret = this->_open(filepath, mode);
+
+ _fs->_unlockFS();
+ return ret;
+}
+
+bool File::_open(char const *filepath, uint8_t mode)
+{
+ bool ret = false;
+
+ // close if currently opened
+ if (this->isOpen())
+ _close();
+
+ struct lfs_info info;
+ int rc = lfs_stat(_fs->_getFS(), filepath, &info);
+
+ if (LFS_ERR_OK == rc) {
+ // file existed, open file or directory accordingly
+ ret = (info.type == LFS_TYPE_REG) ? _open_file(filepath, mode) : _open_dir(filepath);
+ } else if (LFS_ERR_NOENT == rc) {
+ // file not existed, only proceed with FILE_O_WRITE mode
+ if (mode == FILE_O_WRITE)
+ ret = _open_file(filepath, mode);
+ } else {
+ PRINT_LFS_ERR(rc);
+ }
+
+ // save bare file name
+ if (ret) {
+ char const *splash = strrchr(filepath, '/');
+ strncpy(_name, splash ? (splash + 1) : filepath, LFS_NAME_MAX);
+ }
+ return ret;
+}
+
+size_t File::write(uint8_t ch)
+{
+ return write(&ch, 1);
+}
+
+size_t File::write(uint8_t const *buf, size_t size)
+{
+ lfs_ssize_t wrcount = 0;
+ _fs->_lockFS();
+
+ if (!this->_is_dir) {
+ wrcount = lfs_file_write(_fs->_getFS(), _file, buf, size);
+ if (wrcount < 0) {
+ wrcount = 0;
+ }
+ }
+
+ _fs->_unlockFS();
+ return wrcount;
+}
+
+int File::read(void)
+{
+ // this thin wrapper relies on called function to synchronize
+ int ret = -1;
+ uint8_t ch;
+ if (read(&ch, 1) > 0) {
+ ret = static_cast(ch);
+ }
+ return ret;
+}
+
+int File::read(void *buf, uint16_t nbyte)
+{
+ int ret = 0;
+ _fs->_lockFS();
+
+ if (!this->_is_dir) {
+ ret = lfs_file_read(_fs->_getFS(), _file, buf, nbyte);
+ }
+
+ _fs->_unlockFS();
+ return ret;
+}
+
+int File::peek(void)
+{
+ int ret = -1;
+ _fs->_lockFS();
+
+ if (!this->_is_dir) {
+ uint32_t pos = lfs_file_tell(_fs->_getFS(), _file);
+ uint8_t ch = 0;
+ if (lfs_file_read(_fs->_getFS(), _file, &ch, 1) > 0) {
+ ret = static_cast(ch);
+ }
+ (void)lfs_file_seek(_fs->_getFS(), _file, pos, LFS_SEEK_SET);
+ }
+
+ _fs->_unlockFS();
+ return ret;
+}
+
+int File::available(void)
+{
+ int ret = 0;
+ _fs->_lockFS();
+
+ if (!this->_is_dir) {
+ uint32_t size = lfs_file_size(_fs->_getFS(), _file);
+ uint32_t pos = lfs_file_tell(_fs->_getFS(), _file);
+ ret = size - pos;
+ }
+
+ _fs->_unlockFS();
+ return ret;
+}
+
+bool File::seek(uint32_t pos)
+{
+ bool ret = false;
+ _fs->_lockFS();
+
+ if (!this->_is_dir) {
+ ret = lfs_file_seek(_fs->_getFS(), _file, pos, LFS_SEEK_SET) >= 0;
+ }
+
+ _fs->_unlockFS();
+ return ret;
+}
+
+uint32_t File::position(void)
+{
+ uint32_t ret = 0;
+ _fs->_lockFS();
+
+ if (!this->_is_dir) {
+ ret = lfs_file_tell(_fs->_getFS(), _file);
+ }
+
+ _fs->_unlockFS();
+ return ret;
+}
+
+uint32_t File::size(void)
+{
+ uint32_t ret = 0;
+ _fs->_lockFS();
+
+ if (!this->_is_dir) {
+ ret = lfs_file_size(_fs->_getFS(), _file);
+ }
+
+ _fs->_unlockFS();
+ return ret;
+}
+
+bool File::truncate(uint32_t pos)
+{
+ int32_t ret = LFS_ERR_ISDIR;
+ _fs->_lockFS();
+ if (!this->_is_dir) {
+ ret = lfs_file_truncate(_fs->_getFS(), _file, pos);
+ }
+ _fs->_unlockFS();
+ return (ret == 0);
+}
+
+bool File::truncate(void)
+{
+ int32_t ret = LFS_ERR_ISDIR;
+ _fs->_lockFS();
+ if (!this->_is_dir) {
+ uint32_t pos = lfs_file_tell(_fs->_getFS(), _file);
+ ret = lfs_file_truncate(_fs->_getFS(), _file, pos);
+ }
+ _fs->_unlockFS();
+ return (ret == 0);
+}
+
+void File::flush(void)
+{
+ _fs->_lockFS();
+
+ if (!this->_is_dir) {
+ lfs_file_sync(_fs->_getFS(), _file);
+ }
+
+ _fs->_unlockFS();
+ return;
+}
+
+void File::close(void)
+{
+ _fs->_lockFS();
+ this->_close();
+ _fs->_unlockFS();
+}
+
+void File::_close(void)
+{
+ if (this->isOpen()) {
+ if (this->_is_dir) {
+ lfs_dir_close(_fs->_getFS(), _dir);
+ free(_dir);
+ _dir = NULL;
+
+ if (this->_dir_path)
+ free(_dir_path);
+ _dir_path = NULL;
+ } else {
+ lfs_file_close(this->_fs->_getFS(), _file);
+ free(_file);
+ _file = NULL;
+ }
+ }
+}
+
+File::operator bool(void)
+{
+ return isOpen();
+}
+
+bool File::isOpen(void)
+{
+ return (_file != NULL) || (_dir != NULL);
+}
+
+// WARNING -- although marked as `const`, the values pointed
+// to may change. For example, if the same File
+// object has `open()` called with a different
+// file or directory name, this same pointer will
+// suddenly (unexpectedly?) have different values.
+char const *File::name(void)
+{
+ return this->_name;
+}
+
+bool File::isDirectory(void)
+{
+ return this->_is_dir;
+}
+
+File File::openNextFile(uint8_t mode)
+{
+ _fs->_lockFS();
+
+ File ret(*_fs);
+ if (this->_is_dir) {
+ struct lfs_info info;
+ int rc;
+
+ // lfs_dir_read returns 0 when reaching end of directory, 1 if found an entry
+ // Skip the "." and ".." entries ...
+ do {
+ rc = lfs_dir_read(_fs->_getFS(), _dir, &info);
+ } while (rc == 1 && (!strcmp(".", info.name) || !strcmp("..", info.name)));
+
+ if (rc == 1) {
+ // string cat name with current folder
+ char filepath[strlen(_dir_path) + 1 + strlen(info.name) + 1]; // potential for significant stack usage
+ strcpy(filepath, _dir_path);
+ if (!(_dir_path[0] == '/' && _dir_path[1] == 0))
+ strcat(filepath, "/"); // only add '/' if cwd is not root
+ strcat(filepath, info.name);
+
+ (void)ret._open(filepath, mode); // return value is ignored ... caller is expected to check isOpened()
+ } else if (rc < 0) {
+ PRINT_LFS_ERR(rc);
+ }
+ }
+ _fs->_unlockFS();
+ return ret;
+}
+
+void File::rewindDirectory(void)
+{
+ _fs->_lockFS();
+ if (this->_is_dir) {
+ lfs_dir_rewind(_fs->_getFS(), _dir);
+ }
+ _fs->_unlockFS();
+}
diff --git a/src/platform/stm32wl/LittleFS_File.h b/src/platform/stm32wl/LittleFS_File.h
new file mode 100644
index 000000000..e88a2790d
--- /dev/null
+++ b/src/platform/stm32wl/LittleFS_File.h
@@ -0,0 +1,82 @@
+#ifndef LITTLEFS_FILE_H_
+#define LITTLEFS_FILE_H_
+
+// Forward declaration
+class LittleFS;
+
+namespace LittleFS_Namespace
+{
+
+// avoid conflict with other FileSystem FILE_READ/FILE_WRITE
+enum {
+ FILE_O_READ = 0,
+ FILE_O_WRITE = 1,
+};
+
+class File : public Stream
+{
+ public:
+ explicit File(LittleFS &fs);
+ File(char const *filename, uint8_t mode, LittleFS &fs);
+
+ public:
+ bool open(char const *filename, uint8_t mode);
+
+ //------------- Stream API -------------//
+ virtual size_t write(uint8_t ch);
+ virtual size_t write(uint8_t const *buf, size_t size);
+ size_t write(const char *str)
+ {
+ if (str == NULL)
+ return 0;
+ return write((const uint8_t *)str, strlen(str));
+ }
+ size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); }
+
+ virtual int read(void);
+ int read(void *buf, uint16_t nbyte);
+
+ virtual int peek(void);
+ virtual int available(void);
+ virtual void flush(void);
+
+ bool seek(uint32_t pos);
+ uint32_t position(void);
+ uint32_t size(void);
+
+ bool truncate(uint32_t pos);
+ bool truncate(void);
+
+ void close(void);
+
+ operator bool(void);
+
+ bool isOpen(void);
+ char const *name(void);
+
+ bool isDirectory(void);
+ File openNextFile(uint8_t mode = FILE_O_READ);
+ void rewindDirectory(void);
+
+ private:
+ LittleFS *_fs;
+
+ bool _is_dir;
+
+ union {
+ lfs_file_t *_file;
+ lfs_dir_t *_dir;
+ };
+
+ char *_dir_path;
+ char _name[LFS_NAME_MAX + 1];
+
+ bool _open(char const *filepath, uint8_t mode);
+ bool _open_file(char const *filepath, uint8_t mode);
+ bool _open_dir(char const *filepath);
+ void _close(void);
+};
+
+} // namespace LittleFS_Namespace
+
+#endif /* LITTLEFS_FILE_H_ */
diff --git a/src/platform/stm32wl/architecture.h b/src/platform/stm32wl/architecture.h
index dc7adc5ff..1c021903a 100644
--- a/src/platform/stm32wl/architecture.h
+++ b/src/platform/stm32wl/architecture.h
@@ -6,24 +6,19 @@
// defaults for STM32WL architecture
//
+#ifndef HAS_RADIO
+#define HAS_RADIO 1
+#endif
+
//
// set HW_VENDOR
//
#ifndef HW_VENDOR
-#define HW_VENDOR HardwareModel_PRIVATE_HW
+#define HW_VENDOR meshtastic_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 */
+/* virtual pins */
#define SX126X_CS 1000
#define SX126X_DIO1 1001
#define SX126X_RESET 1003
diff --git a/src/platform/stm32wl/layer.c b/src/platform/stm32wl/layer.c
deleted file mode 100644
index d6f073200..000000000
--- a/src/platform/stm32wl/layer.c
+++ /dev/null
@@ -1,65 +0,0 @@
-#include "architecture.h"
-#include "stm32wlxx.h"
-#include "stm32wlxx_hal.h"
-#include
-
-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
index 8ebcb029e..f57928c60 100644
--- a/src/platform/stm32wl/main-stm32wl.cpp
+++ b/src/platform/stm32wl/main-stm32wl.cpp
@@ -11,8 +11,18 @@ void updateBatteryLevel(uint8_t level) {}
void getMacAddr(uint8_t *dmac)
{
- for (int i = 0; i < 6; i++)
- dmac[i] = i;
+ // https://flit.github.io/2020/06/06/mcu-unique-id-survey.html
+ const uint32_t uid0 = HAL_GetUIDw0(); // X/Y coordinate on wafer
+ const uint32_t uid1 = HAL_GetUIDw1(); // [31:8] Lot number (23:0), [7:0] Wafer number
+ const uint32_t uid2 = HAL_GetUIDw2(); // Lot number (55:24)
+
+ // Need to go from 96-bit to 48-bit unique ID
+ dmac[5] = (uint8_t)uid0;
+ dmac[4] = (uint8_t)(uid0 >> 16);
+ dmac[3] = (uint8_t)uid1;
+ dmac[2] = (uint8_t)(uid1 >> 8);
+ dmac[1] = (uint8_t)uid2;
+ dmac[0] = (uint8_t)(uid2 >> 8);
}
void cpuDeepSleep(uint32_t msecToWake) {}
diff --git a/src/power.h b/src/power.h
index c47e78466..e90e3f21b 100644
--- a/src/power.h
+++ b/src/power.h
@@ -22,6 +22,13 @@ extern RTC_NOINIT_ATTR uint64_t RTC_reg_b;
#include "soc/sens_reg.h" // needed for adc pin reset
#endif
+#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
+#include "modules/Telemetry/Sensor/INA219Sensor.h"
+#include "modules/Telemetry/Sensor/INA260Sensor.h"
+extern INA260Sensor ina260Sensor;
+extern INA219Sensor ina219Sensor;
+#endif
+
class Power : private concurrency::OSThread
{
@@ -41,7 +48,6 @@ class Power : private concurrency::OSThread
/// Setup a xpowers chip axp192/axp2101, return true if found
bool axpChipInit();
-
/// Setup a simple ADC input based battery sensor
bool analogInit();
diff --git a/src/shutdown.h b/src/shutdown.h
index b08478a1e..ee63422dd 100644
--- a/src/shutdown.h
+++ b/src/shutdown.h
@@ -26,8 +26,8 @@ void powerCommandsCheck()
if (shutdownAtMsec && millis() > shutdownAtMsec) {
LOG_INFO("Shutting down from admin command\n");
- playShutdownMelody();
#if defined(ARCH_NRF52) || defined(ARCH_ESP32)
+ playShutdownMelody();
power->shutdown();
#else
LOG_WARN("FIXME implement shutdown for this platform");
diff --git a/src/xmodem.h b/src/xmodem.h
index 848e926bd..2ba0bb39f 100644
--- a/src/xmodem.h
+++ b/src/xmodem.h
@@ -59,7 +59,7 @@ class XModemAdapter
uint16_t packetno = 0;
-#if defined(ARCH_NRF52)
+#if defined(ARCH_NRF52) || defined(ARCH_STM32WL)
File file = File(FSCom);
#else
File file;
diff --git a/variants/wio-e5/platformio.ini b/variants/wio-e5/platformio.ini
index 32436dae8..07f6efa6d 100644
--- a/variants/wio-e5/platformio.ini
+++ b/variants/wio-e5/platformio.ini
@@ -1,5 +1,11 @@
[env:wio-e5]
extends = stm32wl5e_base
board_level = extra
-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
+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
+upload_port = stlink
\ No newline at end of file
diff --git a/variants/wio-e5/variant.h b/variants/wio-e5/variant.h
index b1f7556b4..86e58bcb2 100644
--- a/variants/wio-e5/variant.h
+++ b/variants/wio-e5/variant.h
@@ -12,18 +12,6 @@ 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
+#define USE_STM32WLx
#endif
\ No newline at end of file