mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-12 06:41:17 +10:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7555242661 | ||
|
|
c5a8fbc157 | ||
|
|
fbccf910d4 | ||
|
|
b02193f341 | ||
|
|
097f77cbcf | ||
|
|
8995a68b07 | ||
|
|
ebe6ddc7aa | ||
|
|
b27f80131a | ||
|
|
e3d68645c1 |
@@ -8,7 +8,7 @@
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/python:1": {
|
||||
"installTools": true,
|
||||
"version": "3.13"
|
||||
"version": "3.14"
|
||||
}
|
||||
},
|
||||
"customizations": {
|
||||
|
||||
Vendored
+4
-4
@@ -1,10 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"Jason2866.esp-decoder",
|
||||
"pioarduino.pioarduino-ide"
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack",
|
||||
"platformio.platformio-ide"
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
|
||||
+1
-1
@@ -38,4 +38,4 @@ cp bin/device-install.* $OUTDIR/
|
||||
cp bin/device-update.* $OUTDIR/
|
||||
|
||||
echo "Copying manifest"
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json || true
|
||||
|
||||
@@ -293,12 +293,9 @@ if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
|
||||
board_arch = infer_architecture(env.BoardConfig())
|
||||
should_skip_manifest = board_arch is None
|
||||
|
||||
# Most platforms can generate the manifest as part of the default 'buildprog' target.
|
||||
# Typically this passes success/failure properly.
|
||||
mtjson_deps = ["buildprog"]
|
||||
if platform.name == "espressif32":
|
||||
# On ESP32, we need to explicitly depend upon the binary to prevent fake-success upon failure.
|
||||
mtjson_deps = ["$BUILD_DIR/${PROGNAME}.bin"]
|
||||
# For host/native envs, avoid depending on 'buildprog' (some targets don't define it)
|
||||
mtjson_deps = [] if should_skip_manifest else ["buildprog"]
|
||||
if not should_skip_manifest and platform.name == "espressif32":
|
||||
# Build littlefs image as part of mtjson target
|
||||
# Equivalent to `pio run -t buildfs`
|
||||
target_lfs = env.DataToBin(
|
||||
@@ -312,8 +309,7 @@ if should_skip_manifest:
|
||||
|
||||
env.AddCustomTarget(
|
||||
name="mtjson",
|
||||
# For host/native envs, avoid depending on 'buildprog' (some targets don't define it)
|
||||
dependencies=[],
|
||||
dependencies=mtjson_deps,
|
||||
actions=[skip_manifest],
|
||||
title="Meshtastic Manifest (skipped)",
|
||||
description="mtjson generation is skipped for native environments",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"extra_flags": [
|
||||
"-D CDEBYTE_EORA_S3",
|
||||
"-D ARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-D ARDUINO_USB_MODE=1",
|
||||
"-D ARDUINO_USB_MODE=0",
|
||||
"-D ARDUINO_RUNNING_CORE=1",
|
||||
"-D ARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-D BOARD_HAS_PSRAM"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-DBOARD_HAS_PSRAM"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DHELTEC_WIRELESS_TRACKER",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=0"
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DLILYGO_TBEAM_1W",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DLILYGO_TBEAM_S3_CORE",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"extra_flags": [
|
||||
"-DLILYGO_T3S3_V1",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-DBOARD_HAS_PSRAM"
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DUNPHONE_SPIN=9",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x640000,
|
||||
app1, app, ota_1, 0x650000,0x640000,
|
||||
spiffs, data, spiffs, 0xc90000,0x360000,
|
||||
coredump, data, coredump,0xFF0000,0x10000,
|
||||
|
@@ -1,7 +0,0 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x330000,
|
||||
app1, app, ota_1, 0x340000,0x330000,
|
||||
spiffs, data, spiffs, 0x670000,0x180000,
|
||||
coredump, data, coredump,0x7F0000,0x10000,
|
||||
|
@@ -70,6 +70,17 @@ def esp32_create_combined_bin(source, target, env):
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
|
||||
|
||||
# Enable Newlib Nano formatting to save space
|
||||
# ...but allow printf float support (compromise)
|
||||
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
||||
esp32_kind = env.GetProjectOption("custom_esp32_kind")
|
||||
if esp32_kind == "esp32":
|
||||
# Free up some IRAM by removing auxiliary SPI flash chip drivers.
|
||||
# Wrapped stub symbols are defined in src/platform/esp32/iram-quirk.c.
|
||||
env.Append(
|
||||
LINKFLAGS=[
|
||||
"-Wl,--wrap=esp_flash_chip_gd",
|
||||
"-Wl,--wrap=esp_flash_chip_issi",
|
||||
"-Wl,--wrap=esp_flash_chip_winbond",
|
||||
]
|
||||
)
|
||||
else:
|
||||
# For newer ESP32 targets, using newlib nano works better.
|
||||
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# trunk-ignore-all(ruff/F821)
|
||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||
|
||||
# force linker response file instead of command line arguments
|
||||
|
||||
Import("env")
|
||||
|
||||
|
||||
def wrap_with_tempfile(command_key):
|
||||
command = env.get(command_key)
|
||||
if not command or not isinstance(command, str):
|
||||
return
|
||||
if "TEMPFILE(" in command:
|
||||
return
|
||||
env.Replace(**{command_key: "${TEMPFILE('%s')}" % command})
|
||||
|
||||
|
||||
# Force SCons to spill long commands into response files on this target.
|
||||
env.Replace(MAXLINELENGTH=8192)
|
||||
|
||||
for key in ("LINKCOM", "CXXLINKCOM", "SHLINKCOM", "SHCXXLINKCOM"):
|
||||
wrap_with_tempfile(key)
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = heltec-v3
|
||||
default_envs = tbeam
|
||||
|
||||
extra_configs =
|
||||
variants/*/*.ini
|
||||
@@ -170,8 +170,8 @@ lib_deps =
|
||||
https://github.com/EmotiBit/EmotiBit_MLX90632/archive/refs/tags/v1.0.8.zip
|
||||
# renovate: datasource=github-tags depName=Adafruit MLX90614 packageName=adafruit/Adafruit_MLX90614
|
||||
https://github.com/adafruit/Adafruit-MLX90614-Library/archive/refs/tags/2.1.6.zip
|
||||
# renovate: datasource=github-tags depName=INA3221_RT packageName=RobTillaart/INA3221_RT
|
||||
https://github.com/RobTillaart/INA3221_RT/archive/refs/tags/0.4.2.zip
|
||||
# renovate: datasource=git-refs depName=INA3221 packageName=https://github.com/sgtwilko/INA3221 gitBranch=FixOverflow
|
||||
https://github.com/sgtwilko/INA3221/archive/bb03d7e9bfcc74fc798838a54f4f99738f29fc6a.zip
|
||||
# renovate: datasource=github-tags depName=QMC5883L Compass packageName=mprograms/QMC5883LCompass
|
||||
https://github.com/mprograms/QMC5883LCompass/archive/refs/tags/v1.2.3.zip
|
||||
# renovate: datasource=github-tags depName=DFRobot_RTU packageName=dfrobot/DFRobot_RTU
|
||||
|
||||
@@ -151,8 +151,9 @@ extern "C" void logLegacy(const char *level, const char *fmt, ...);
|
||||
#include <RAK13800_W5100S.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#if HAS_WIFI
|
||||
|
||||
+1
-1
@@ -277,7 +277,7 @@ void fsInit()
|
||||
*/
|
||||
void setupSDCard()
|
||||
{
|
||||
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI) && !defined(HAS_SD_MMC)
|
||||
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI)
|
||||
concurrency::LockGuard g(spiLock);
|
||||
SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
if (!SD.begin(SDCARD_CS, SDHandler, SD_SPI_FREQUENCY)) {
|
||||
|
||||
+992
-34
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@
|
||||
#include "memGet.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
#include <assert.h>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
@@ -20,6 +21,22 @@
|
||||
#if HAS_NETWORKING
|
||||
extern meshtastic::Syslog syslog;
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
std::atomic<bool> serialHalLogSuppressed{false};
|
||||
}
|
||||
|
||||
void RedirectablePrint::setSerialHalLogSuppressed(bool suppressed)
|
||||
{
|
||||
serialHalLogSuppressed.store(suppressed);
|
||||
}
|
||||
|
||||
bool RedirectablePrint::isSerialHalLogSuppressed()
|
||||
{
|
||||
return serialHalLogSuppressed.load();
|
||||
}
|
||||
|
||||
void RedirectablePrint::rpInit()
|
||||
{
|
||||
#ifdef HAS_FREE_RTOS
|
||||
@@ -277,6 +294,10 @@ meshtastic_LogRecord_Level RedirectablePrint::getLogLevel(const char *logLevel)
|
||||
void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
{
|
||||
|
||||
if (isSerialHalLogSuppressed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// append \n to format
|
||||
size_t len = strlen(format);
|
||||
auto newFormat = std::unique_ptr<char[]>(new char[len + 2]);
|
||||
|
||||
@@ -24,6 +24,11 @@ class RedirectablePrint : public Print
|
||||
public:
|
||||
explicit RedirectablePrint(Print *_dest) : dest(_dest) {}
|
||||
|
||||
/// Suppress all log output while a SerialHal transaction is in progress.
|
||||
// Unclear if this is necessary, but it seems to help with response speeds.
|
||||
static void setSerialHalLogSuppressed(bool suppressed);
|
||||
static bool isSerialHalLogSuppressed();
|
||||
|
||||
/**
|
||||
* Set a new destination
|
||||
*/
|
||||
|
||||
@@ -53,11 +53,10 @@ void InkHUD::MessageStore::saveToFlash()
|
||||
f.write(reinterpret_cast<const uint8_t *>(&m.timestamp), sizeof(m.timestamp)); // Write timestamp. 4 bytes
|
||||
f.write(reinterpret_cast<const uint8_t *>(&m.sender), sizeof(m.sender)); // Write sender NodeId. 4 Bytes
|
||||
f.write(reinterpret_cast<const uint8_t *>(&m.channelIndex), sizeof(m.channelIndex)); // Write channel index. 1 Byte
|
||||
f.write(reinterpret_cast<const uint8_t *>(m.text.c_str()),
|
||||
min((size_t)MAX_MESSAGE_SIZE, m.text.size())); // Write message text
|
||||
f.write('\0'); // Append null term
|
||||
LOG_DEBUG("Wrote message %u, length %u, text \"%s\"", static_cast<uint32_t>(i),
|
||||
min((size_t)MAX_MESSAGE_SIZE, m.text.size()), m.text.c_str());
|
||||
f.write(reinterpret_cast<const uint8_t *>(m.text.c_str()), min(MAX_MESSAGE_SIZE, m.text.size())); // Write message text
|
||||
f.write('\0'); // Append null term
|
||||
LOG_DEBUG("Wrote message %u, length %u, text \"%s\"", static_cast<uint32_t>(i), min(MAX_MESSAGE_SIZE, m.text.size()),
|
||||
m.text.c_str());
|
||||
}
|
||||
|
||||
// Release firmware's SPI lock, because SafeFile::close needs it
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
|
||||
#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE
|
||||
|
||||
// REVISIT esp_adc_cal.h
|
||||
// "legacy adc calibration driver is deprecated, please migrate to use esp_adc/adc_cali.h and esp_adc/adc_cali_scheme.h"
|
||||
#include <esp_adc_cal.h>
|
||||
#include <soc/adc_channel.h>
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ TLoraPagerKeyboard::TLoraPagerKeyboard()
|
||||
: TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(UINT8_MAX),
|
||||
next_key(UINT8_MAX), last_tap(0L), char_idx(0), tap_interval(0)
|
||||
{
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ledcAttach(KB_BL_PIN, LEDC_BACKLIGHT_FREQ, LEDC_BACKLIGHT_BIT_WIDTH);
|
||||
#else
|
||||
ledcSetup(LEDC_BACKLIGHT_CHANNEL, LEDC_BACKLIGHT_FREQ, LEDC_BACKLIGHT_BIT_WIDTH);
|
||||
@@ -108,7 +108,7 @@ void TLoraPagerKeyboard::setBacklight(bool on)
|
||||
uint32_t _brightness = 0;
|
||||
if (on)
|
||||
_brightness = brightness;
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ledcWrite(KB_BL_PIN, _brightness);
|
||||
#else
|
||||
ledcWrite(LEDC_BACKLIGHT_CHANNEL, _brightness);
|
||||
|
||||
+8
-5
@@ -951,8 +951,12 @@ void setup()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
auto rIf = initLoRa();
|
||||
|
||||
std::unique_ptr<RadioInterface> rIf;
|
||||
if (!config.lora.serial_hal_only) {
|
||||
rIf = initLoRa();
|
||||
} else {
|
||||
LOG_INFO("skipping LoRa radio init, for serialHal");
|
||||
}
|
||||
lateInitVariant(); // Do board specific init (see extra_variants/README.md for documentation)
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_MQTT
|
||||
@@ -996,10 +1000,9 @@ void setup()
|
||||
|
||||
// Start airtime logger thread.
|
||||
airTime = new AirTime();
|
||||
|
||||
if (!rIf)
|
||||
if (!rIf && !config.lora.serial_hal_only)
|
||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_NO_RADIO);
|
||||
else {
|
||||
else if (rIf) {
|
||||
// Log bit rate to debug output
|
||||
LOG_DEBUG("LoRA bitrate = %f bytes / sec", (float(meshtastic_Constants_DATA_PAYLOAD_LEN) /
|
||||
(float(rIf->getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN)))) *
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#ifdef ARCH_PORTDUINO
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#include "platform/portduino/SerialHal.h"
|
||||
#include "platform/portduino/SimRadio.h"
|
||||
#include "platform/portduino/USBHal.h"
|
||||
#endif
|
||||
@@ -292,6 +293,9 @@ std::unique_ptr<RadioInterface> initLoRa()
|
||||
portduino_config.lora_spi_dev.c_str());
|
||||
if (portduino_config.lora_spi_dev == "ch341") {
|
||||
RadioLibHAL = ch341Hal;
|
||||
} else if (portduino_config.lora_spi_dev == "serial") {
|
||||
RadioLibHAL = new SerialHal(portduino_config.lora_serial_device, portduino_config.lora_serial_baud,
|
||||
(uint32_t)portduino_config.lora_serial_timeout_ms);
|
||||
} else {
|
||||
if (RadioLibHAL != nullptr) {
|
||||
delete RadioLibHAL;
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
#include "mesh/SerialHalDevice.h"
|
||||
#include "NodeDB.h"
|
||||
#include "SPILock.h"
|
||||
#include "concurrency/Periodic.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh/StreamAPI.h"
|
||||
#include "mesh/generated/meshtastic/config.pb.h"
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(ARCH_ESP32)
|
||||
#if defined(HW_SPI1_DEVICE)
|
||||
extern SPIClass SPI1;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr uint32_t SERIAL_PI_RISING = 1;
|
||||
constexpr uint32_t SERIAL_PI_FALLING = 2;
|
||||
constexpr uint32_t SERIAL_PI_INPUT = 0;
|
||||
constexpr uint32_t SERIAL_PI_OUTPUT = 1;
|
||||
constexpr size_t MAX_INTERRUPT_SLOTS = 8;
|
||||
constexpr int32_t INTERRUPT_POLL_MS = 5;
|
||||
|
||||
struct InterruptSlot {
|
||||
bool used = false;
|
||||
uint32_t pin = 0;
|
||||
uint32_t mode = 0;
|
||||
volatile bool pending = false;
|
||||
};
|
||||
|
||||
concurrency::Lock interruptMutex;
|
||||
InterruptSlot interruptSlots[MAX_INTERRUPT_SLOTS];
|
||||
StreamAPI *interruptStreamApi = nullptr;
|
||||
concurrency::Periodic *interruptEmitter = nullptr;
|
||||
|
||||
int findSlotByPinLocked(uint32_t pin)
|
||||
{
|
||||
for (size_t i = 0; i < MAX_INTERRUPT_SLOTS; ++i) {
|
||||
if (interruptSlots[i].used && interruptSlots[i].pin == pin) {
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int allocateSlotLocked()
|
||||
{
|
||||
for (size_t i = 0; i < MAX_INTERRUPT_SLOTS; ++i) {
|
||||
if (!interruptSlots[i].used) {
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(ARCH_PORTDUINO) || defined(ARCH_RP2040)
|
||||
PinStatus toInterruptMode(uint32_t serialMode)
|
||||
{
|
||||
if (serialMode == SERIAL_PI_RISING) {
|
||||
return PinStatus::RISING;
|
||||
}
|
||||
if (serialMode == SERIAL_PI_FALLING) {
|
||||
return PinStatus::FALLING;
|
||||
}
|
||||
return PinStatus::CHANGE;
|
||||
}
|
||||
#else
|
||||
int toInterruptMode(uint32_t serialMode)
|
||||
{
|
||||
if (serialMode == SERIAL_PI_RISING) {
|
||||
return RISING;
|
||||
}
|
||||
if (serialMode == SERIAL_PI_FALLING) {
|
||||
return FALLING;
|
||||
}
|
||||
return CHANGE;
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t pumpInterruptEvents();
|
||||
|
||||
void ensureInterruptEmitter()
|
||||
{
|
||||
if (!interruptEmitter) {
|
||||
interruptEmitter = new concurrency::Periodic("SerialHalIrqEmitter", pumpInterruptEvents);
|
||||
}
|
||||
}
|
||||
|
||||
void emitInterruptEvent(uint32_t pin, StreamAPI *streamApi)
|
||||
{
|
||||
if (streamApi == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
meshtastic_SerialHalResponse event = meshtastic_SerialHalResponse_init_zero;
|
||||
event.transaction_id = 0; // asynchronous interrupt notification
|
||||
event.result = meshtastic_SerialHalResponse_Result_OK;
|
||||
event.value = pin; // host-side SerialHal treats value as interrupt pin
|
||||
SerialHalDevice::emitResponse(event, streamApi);
|
||||
}
|
||||
|
||||
void markPendingBySlot(uint8_t slot)
|
||||
{
|
||||
if (slot < MAX_INTERRUPT_SLOTS && interruptSlots[slot].used) {
|
||||
interruptSlots[slot].pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
void isr0()
|
||||
{
|
||||
markPendingBySlot(0);
|
||||
}
|
||||
void isr1()
|
||||
{
|
||||
markPendingBySlot(1);
|
||||
}
|
||||
void isr2()
|
||||
{
|
||||
markPendingBySlot(2);
|
||||
}
|
||||
void isr3()
|
||||
{
|
||||
markPendingBySlot(3);
|
||||
}
|
||||
void isr4()
|
||||
{
|
||||
markPendingBySlot(4);
|
||||
}
|
||||
void isr5()
|
||||
{
|
||||
markPendingBySlot(5);
|
||||
}
|
||||
void isr6()
|
||||
{
|
||||
markPendingBySlot(6);
|
||||
}
|
||||
void isr7()
|
||||
{
|
||||
markPendingBySlot(7);
|
||||
}
|
||||
|
||||
void (*const isrTable[MAX_INTERRUPT_SLOTS])() = {isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7};
|
||||
|
||||
int32_t pumpInterruptEvents()
|
||||
{
|
||||
uint32_t toEmit[MAX_INTERRUPT_SLOTS] = {0};
|
||||
size_t emitCount = 0;
|
||||
StreamAPI *streamApi = nullptr;
|
||||
|
||||
{
|
||||
concurrency::LockGuard lock(&interruptMutex);
|
||||
streamApi = interruptStreamApi;
|
||||
for (size_t i = 0; i < MAX_INTERRUPT_SLOTS; ++i) {
|
||||
if (interruptSlots[i].used && interruptSlots[i].pending) {
|
||||
interruptSlots[i].pending = false;
|
||||
toEmit[emitCount++] = interruptSlots[i].pin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < emitCount; ++i) {
|
||||
emitInterruptEvent(toEmit[i], streamApi);
|
||||
}
|
||||
|
||||
return INTERRUPT_POLL_MS;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Helper to safely set response result
|
||||
static inline void setResponseError(meshtastic_SerialHalResponse &response, meshtastic_SerialHalResponse_Result result,
|
||||
const char *error = nullptr)
|
||||
{
|
||||
response.result = result;
|
||||
if (error != nullptr) {
|
||||
snprintf(response.error, sizeof(response.error), "%s", error);
|
||||
}
|
||||
}
|
||||
|
||||
void SerialHalDevice::handleCommand(const uint8_t *buf, size_t len, StreamAPI *streamApi)
|
||||
{
|
||||
if (buf == nullptr || streamApi == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate role - SerialHal commands only handled when config.lora.serial_hal_only
|
||||
if (!config.lora.serial_hal_only) {
|
||||
meshtastic_SerialHalResponse response = meshtastic_SerialHalResponse_init_zero;
|
||||
response.result = meshtastic_SerialHalResponse_Result_UNSUPPORTED;
|
||||
snprintf(response.error, sizeof(response.error), "SerialHal not enabled for this role");
|
||||
emitResponse(response, streamApi);
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode the command
|
||||
meshtastic_SerialHalCommand cmd = meshtastic_SerialHalCommand_init_zero;
|
||||
if (!pb_decode_from_bytes(buf, len, &meshtastic_SerialHalCommand_msg, &cmd)) {
|
||||
meshtastic_SerialHalResponse response = meshtastic_SerialHalResponse_init_zero;
|
||||
response.result = meshtastic_SerialHalResponse_Result_BAD_REQUEST;
|
||||
snprintf(response.error, sizeof(response.error), "Failed to decode SerialHalCommand");
|
||||
emitResponse(response, streamApi);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize response with matching transaction_id
|
||||
meshtastic_SerialHalResponse response = meshtastic_SerialHalResponse_init_zero;
|
||||
response.transaction_id = cmd.transaction_id;
|
||||
response.result = meshtastic_SerialHalResponse_Result_OK;
|
||||
|
||||
// Dispatch to operation handler
|
||||
switch (cmd.type) {
|
||||
case meshtastic_SerialHalCommand_Type_PIN_MODE:
|
||||
handlePinMode(cmd, response);
|
||||
break;
|
||||
case meshtastic_SerialHalCommand_Type_DIGITAL_WRITE:
|
||||
handleDigitalWrite(cmd, response);
|
||||
break;
|
||||
case meshtastic_SerialHalCommand_Type_DIGITAL_READ:
|
||||
handleDigitalRead(cmd, response);
|
||||
break;
|
||||
case meshtastic_SerialHalCommand_Type_ATTACH_INTERRUPT:
|
||||
handleAttachInterrupt(cmd, response);
|
||||
break;
|
||||
case meshtastic_SerialHalCommand_Type_DETACH_INTERRUPT:
|
||||
handleDetachInterrupt(cmd, response);
|
||||
break;
|
||||
case meshtastic_SerialHalCommand_Type_SPI_TRANSFER:
|
||||
handleSpiTransfer(cmd, response);
|
||||
break;
|
||||
case meshtastic_SerialHalCommand_Type_NOOP:
|
||||
// NOOP: just return OK
|
||||
break;
|
||||
default:
|
||||
response.result = meshtastic_SerialHalResponse_Result_UNSUPPORTED;
|
||||
snprintf(response.error, sizeof(response.error), "Unknown SerialHal operation type");
|
||||
break;
|
||||
}
|
||||
|
||||
emitResponse(response, streamApi);
|
||||
}
|
||||
|
||||
void SerialHalDevice::handlePinMode(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response)
|
||||
{
|
||||
// LOG_DEBUG("SerialHalDevice: pinMode pin=%u mode=%u", cmd.pin, cmd.mode);
|
||||
if (cmd.mode == SERIAL_PI_INPUT) {
|
||||
pinMode((int)cmd.pin, INPUT);
|
||||
} else if (cmd.mode == SERIAL_PI_OUTPUT) {
|
||||
pinMode((int)cmd.pin, OUTPUT);
|
||||
} else {
|
||||
setResponseError(response, meshtastic_SerialHalResponse_Result_BAD_REQUEST, "Unsupported pin mode");
|
||||
}
|
||||
}
|
||||
|
||||
void SerialHalDevice::handleDigitalWrite(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response)
|
||||
{
|
||||
// LOG_DEBUG("SerialHalDevice: digitalWrite pin=%u value=%u", cmd.pin, cmd.value);
|
||||
digitalWrite((int)cmd.pin, cmd.value ? HIGH : LOW);
|
||||
}
|
||||
|
||||
void SerialHalDevice::handleDigitalRead(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response)
|
||||
{
|
||||
// LOG_DEBUG("SerialHalDevice: digitalRead pin=%u", cmd.pin);
|
||||
response.value = (uint32_t)digitalRead((int)cmd.pin);
|
||||
}
|
||||
|
||||
void SerialHalDevice::handleAttachInterrupt(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response)
|
||||
{
|
||||
// LOG_DEBUG("SerialHalDevice: attachInterrupt pin=%u mode=%u", cmd.pin, cmd.mode);
|
||||
|
||||
ensureInterruptEmitter();
|
||||
|
||||
int slot = -1;
|
||||
{
|
||||
concurrency::LockGuard lock(&interruptMutex);
|
||||
slot = findSlotByPinLocked(cmd.pin);
|
||||
if (slot < 0) {
|
||||
slot = allocateSlotLocked();
|
||||
}
|
||||
|
||||
if (slot >= 0) {
|
||||
interruptSlots[slot].used = true;
|
||||
interruptSlots[slot].pin = cmd.pin;
|
||||
interruptSlots[slot].mode = cmd.mode;
|
||||
interruptSlots[slot].pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (slot < 0) {
|
||||
setResponseError(response, meshtastic_SerialHalResponse_Result_ERROR, "No interrupt slots available");
|
||||
return;
|
||||
}
|
||||
|
||||
::attachInterrupt((int)cmd.pin, isrTable[slot], toInterruptMode(cmd.mode));
|
||||
}
|
||||
|
||||
void SerialHalDevice::handleDetachInterrupt(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response)
|
||||
{
|
||||
// LOG_DEBUG("SerialHalDevice: detachInterrupt pin=%u", cmd.pin);
|
||||
|
||||
::detachInterrupt((int)cmd.pin);
|
||||
|
||||
{
|
||||
concurrency::LockGuard lock(&interruptMutex);
|
||||
const int slot = findSlotByPinLocked(cmd.pin);
|
||||
if (slot >= 0) {
|
||||
interruptSlots[slot] = InterruptSlot{};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SerialHalDevice::handleSpiTransfer(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response)
|
||||
{
|
||||
if (cmd.data.size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if !ARCH_PORTDUINO
|
||||
if (spiLock == nullptr) {
|
||||
setResponseError(response, meshtastic_SerialHalResponse_Result_ERROR, "SPI lock not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(HW_SPI1_DEVICE)
|
||||
SPIClass &spiBus = SPI1;
|
||||
#else
|
||||
SPIClass &spiBus = SPI;
|
||||
#endif
|
||||
|
||||
response.data.size = cmd.data.size;
|
||||
|
||||
{
|
||||
concurrency::LockGuard guard(spiLock);
|
||||
spiBus.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||
#ifdef ARCH_ESP32
|
||||
spiBus.transferBytes(cmd.data.bytes, response.data.bytes, cmd.data.size);
|
||||
#else
|
||||
spiBus.transfer(cmd.data.bytes, response.data.bytes, cmd.data.size);
|
||||
#endif
|
||||
spiBus.endTransaction();
|
||||
}
|
||||
#else
|
||||
// SPI wiring is board/radio-specific; keep this explicit for now.
|
||||
response.result = meshtastic_SerialHalResponse_Result_UNSUPPORTED;
|
||||
snprintf(response.error, sizeof(response.error), "SPI not supported on this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
void SerialHalDevice::emitResponse(const meshtastic_SerialHalResponse &response, StreamAPI *streamApi)
|
||||
{
|
||||
if (streamApi == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Encode the response
|
||||
uint8_t encoded[meshtastic_SerialHalResponse_size] = {0};
|
||||
const size_t responseLen =
|
||||
pb_encode_to_bytes(encoded, sizeof(encoded), &meshtastic_SerialHalResponse_msg, static_cast<const void *>(&response));
|
||||
|
||||
if (responseLen == 0 || responseLen > 0xFFFF) {
|
||||
LOG_ERROR("SerialHalDevice: Failed to encode response (len=%zu)", responseLen);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build frame with StreamAPI framing: START1 SERIALHAL_MAGIC LEN_H LEN_L [payload]
|
||||
constexpr uint8_t START1 = 0x94;
|
||||
constexpr uint8_t SERIALHAL_MAGIC = 0xA5;
|
||||
|
||||
uint8_t hdr[4];
|
||||
hdr[0] = START1;
|
||||
hdr[1] = SERIALHAL_MAGIC;
|
||||
hdr[2] = (uint8_t)((responseLen >> 8) & 0xFF); // LEN_H
|
||||
hdr[3] = (uint8_t)(responseLen & 0xFF); // LEN_L
|
||||
|
||||
// Emit via StreamAPI (this uses the internal txBuf + framing)
|
||||
streamApi->emitSerialHalResponse(hdr, sizeof(hdr), encoded, responseLen);
|
||||
|
||||
// Keep a recent stream instance so async interrupt events can be emitted.
|
||||
{
|
||||
concurrency::LockGuard lock(&interruptMutex);
|
||||
interruptStreamApi = streamApi;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include "mesh/generated/meshtastic/serial_hal.pb.h"
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief Device-side handler for SerialHal GPIO/SPI operations over StreamAPI framing.
|
||||
*
|
||||
* This module decodes SerialHalCommand protobufs received from a host and executes
|
||||
* the requested GPIO (pinMode, digitalWrite, digitalRead, attach/detachInterrupt) or
|
||||
* SPI operations, then returns results via SerialHalResponse.
|
||||
*
|
||||
* Usage:
|
||||
* 1. Override StreamAPI::handleSerialHalCommand() in a subclass
|
||||
* 2. Call SerialHalDevice::handleCommand(buf, len, streamApi)
|
||||
* 3. SerialHalDevice will decode, execute, and emit the response
|
||||
*
|
||||
* The handler is only active when config.lora.serial_hal_only is true.
|
||||
*/
|
||||
|
||||
class StreamAPI; // forward declaration
|
||||
|
||||
class SerialHalDevice
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Process a SerialHalCommand and emit a response.
|
||||
*
|
||||
* Decodes the protobuf, validates the operation, executes it on the device,
|
||||
* and writes the response back via the StreamAPI instance.
|
||||
*
|
||||
* @param buf Pointer to the encoded SerialHalCommand protobuf payload (not including framing)
|
||||
* @param len Length of the encoded payload
|
||||
* @param streamApi Pointer to the StreamAPI instance (used for emitting responses)
|
||||
*/
|
||||
static void handleCommand(const uint8_t *buf, size_t len, StreamAPI *streamApi);
|
||||
|
||||
/**
|
||||
* @brief Emit a SerialHalResponse back to the host via StreamAPI framing.
|
||||
*
|
||||
* Encodes the response protobuf and sends it with proper framing (START1 SERIALHAL_MAGIC LEN_H LEN_L payload).
|
||||
*
|
||||
* @param response The response to send
|
||||
* @param streamApi Pointer to the StreamAPI instance
|
||||
*/
|
||||
static void emitResponse(const meshtastic_SerialHalResponse &response, StreamAPI *streamApi);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Execute a GPIO pinMode operation.
|
||||
* @param cmd Decoded SerialHalCommand with PIN_MODE type
|
||||
* @param response Response object to fill with result
|
||||
*/
|
||||
static void handlePinMode(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Execute a GPIO digitalWrite operation.
|
||||
* @param cmd Decoded SerialHalCommand with DIGITAL_WRITE type
|
||||
* @param response Response object to fill with result
|
||||
*/
|
||||
static void handleDigitalWrite(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Execute a GPIO digitalRead operation.
|
||||
* @param cmd Decoded SerialHalCommand with DIGITAL_READ type
|
||||
* @param response Response object to fill with result (value field contains read result)
|
||||
*/
|
||||
static void handleDigitalRead(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Execute an attachInterrupt operation.
|
||||
* @param cmd Decoded SerialHalCommand with ATTACH_INTERRUPT type
|
||||
* @param response Response object to fill with result
|
||||
*/
|
||||
static void handleAttachInterrupt(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Execute a detachInterrupt operation.
|
||||
* @param cmd Decoded SerialHalCommand with DETACH_INTERRUPT type
|
||||
* @param response Response object to fill with result
|
||||
*/
|
||||
static void handleDetachInterrupt(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Execute an SPI transfer operation.
|
||||
* @param cmd Decoded SerialHalCommand with SPI_TRANSFER type and data to send
|
||||
* @param response Response object to fill with result (data field contains received bytes)
|
||||
*/
|
||||
static void handleSpiTransfer(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse &response);
|
||||
};
|
||||
+95
-17
@@ -1,12 +1,15 @@
|
||||
#include "StreamAPI.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "RTC.h"
|
||||
#include "RedirectablePrint.h"
|
||||
#include "SerialHalDevice.h"
|
||||
#include "Throttle.h"
|
||||
#include "concurrency/LockGuard.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#define START1 0x94
|
||||
#define START2 0xc3
|
||||
#define SERIALHAL_MAGIC 0xa5 // second framing byte for SerialHal frames (START1 SH_MAGIC LEN_H LEN_L PAYLOAD)
|
||||
#define HEADER_LEN 4
|
||||
|
||||
int32_t StreamAPI::runOncePart()
|
||||
@@ -79,18 +82,29 @@ int32_t StreamAPI::handleRecStream(const char *buf, uint16_t bufLen)
|
||||
if (ptr == 0) { // looking for START1
|
||||
if (c != START1)
|
||||
rxPtr = 0; // failed to find framing
|
||||
} else if (ptr == 1) { // looking for START2
|
||||
if (c != START2)
|
||||
rxPtr = 0; // failed to find framing
|
||||
} else if (ptr == 1) { // discriminate frame type on second byte
|
||||
if (c == START2) {
|
||||
rxIsSerialHal = false; // standard ToRadio frame
|
||||
serialHalRxActive.store(false);
|
||||
RedirectablePrint::setSerialHalLogSuppressed(false);
|
||||
} else if (c == SERIALHAL_MAGIC) {
|
||||
rxIsSerialHal = true; // SerialHal command frame
|
||||
serialHalRxActive.store(true);
|
||||
RedirectablePrint::setSerialHalLogSuppressed(true);
|
||||
} else {
|
||||
rxPtr = 0; // unrecognised second byte — not our frame
|
||||
serialHalRxActive.store(false);
|
||||
RedirectablePrint::setSerialHalLogSuppressed(false);
|
||||
}
|
||||
} else if (ptr >= HEADER_LEN - 1) { // we have at least read our 4 byte framing
|
||||
uint32_t len = (rxBuf[2] << 8) + rxBuf[3]; // big endian 16 bit length follows framing
|
||||
|
||||
// console->printf("len %d\n", len);
|
||||
|
||||
if (ptr == HEADER_LEN - 1) {
|
||||
// we _just_ finished our 4 byte header, validate length now (note: a length of zero is a valid
|
||||
// protobuf also)
|
||||
if (len > MAX_TO_FROM_RADIO_SIZE)
|
||||
// we _just_ finished our 4 byte header, validate length now
|
||||
uint32_t maxLen = rxIsSerialHal ? (uint32_t)meshtastic_SerialHalCommand_size : MAX_TO_FROM_RADIO_SIZE;
|
||||
if (len > maxLen)
|
||||
rxPtr = 0; // length is bogus, restart search for framing
|
||||
}
|
||||
|
||||
@@ -98,8 +112,16 @@ int32_t StreamAPI::handleRecStream(const char *buf, uint16_t bufLen)
|
||||
if (ptr + 1 >= len + HEADER_LEN) { // have we received all of the payload?
|
||||
rxPtr = 0; // start over again on the next packet
|
||||
|
||||
// If we didn't just fail the packet and we now have the right # of bytes, parse it
|
||||
handleToRadio(rxBuf + HEADER_LEN, len);
|
||||
// Dispatch based on which frame type we identified at byte 1
|
||||
if (rxIsSerialHal)
|
||||
handleSerialHalCommand(rxBuf + HEADER_LEN, len);
|
||||
else
|
||||
handleToRadio(rxBuf + HEADER_LEN, len);
|
||||
|
||||
if (rxIsSerialHal)
|
||||
serialHalRxActive.store(false);
|
||||
if (rxIsSerialHal)
|
||||
RedirectablePrint::setSerialHalLogSuppressed(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,7 +136,12 @@ int32_t StreamAPI::readStream()
|
||||
if (!stream->available()) {
|
||||
// Nothing available this time, if the computer has talked to us recently, poll often, otherwise let CPU sleep a long time
|
||||
bool recentRx = Throttle::isWithinTimespanMs(lastRxMsec, 2000);
|
||||
return recentRx ? 5 : 250;
|
||||
if (!recentRx)
|
||||
return 250; // Sleep a long time if we haven't heard from the computer in a while
|
||||
if (serialHalRxActive.load())
|
||||
return 0; // If we are in the middle of a SerialHal transaction, don't sleep at all because we want to be as
|
||||
// responsive as possible to incoming SerialHal bytes
|
||||
return 5; // Otherwise, poll frequently for new data
|
||||
} else {
|
||||
while (stream->available()) { // Currently we never want to block
|
||||
int cInt = stream->read();
|
||||
@@ -135,18 +162,30 @@ int32_t StreamAPI::readStream()
|
||||
if (ptr == 0) { // looking for START1
|
||||
if (c != START1)
|
||||
rxPtr = 0; // failed to find framing
|
||||
} else if (ptr == 1) { // looking for START2
|
||||
if (c != START2)
|
||||
rxPtr = 0; // failed to find framing
|
||||
} else if (ptr == 1) { // discriminate frame type on second byte
|
||||
if (c == START2) {
|
||||
rxIsSerialHal = false; // standard ToRadio frame
|
||||
serialHalRxActive.store(false);
|
||||
RedirectablePrint::setSerialHalLogSuppressed(false);
|
||||
} else if (c == SERIALHAL_MAGIC) {
|
||||
rxIsSerialHal = true; // SerialHal command frame
|
||||
serialHalRxActive.store(true);
|
||||
RedirectablePrint::setSerialHalLogSuppressed(true);
|
||||
LOG_WARN("StreamAPI: Detected SerialHal command frame");
|
||||
} else {
|
||||
rxPtr = 0; // unrecognised second byte — not our frame
|
||||
serialHalRxActive.store(false);
|
||||
RedirectablePrint::setSerialHalLogSuppressed(false);
|
||||
}
|
||||
} else if (ptr >= HEADER_LEN - 1) { // we have at least read our 4 byte framing
|
||||
uint32_t len = (rxBuf[2] << 8) + rxBuf[3]; // big endian 16 bit length follows framing
|
||||
|
||||
// console->printf("len %d\n", len);
|
||||
|
||||
if (ptr == HEADER_LEN - 1) {
|
||||
// we _just_ finished our 4 byte header, validate length now (note: a length of zero is a valid
|
||||
// protobuf also)
|
||||
if (len > MAX_TO_FROM_RADIO_SIZE)
|
||||
// we _just_ finished our 4 byte header, validate length now
|
||||
uint32_t maxLen = rxIsSerialHal ? (uint32_t)meshtastic_SerialHalCommand_size : MAX_TO_FROM_RADIO_SIZE;
|
||||
if (len > maxLen)
|
||||
rxPtr = 0; // length is bogus, restart search for framing
|
||||
}
|
||||
|
||||
@@ -154,8 +193,16 @@ int32_t StreamAPI::readStream()
|
||||
if (ptr + 1 >= len + HEADER_LEN) { // have we received all of the payload?
|
||||
rxPtr = 0; // start over again on the next packet
|
||||
|
||||
// If we didn't just fail the packet and we now have the right # of bytes, parse it
|
||||
handleToRadio(rxBuf + HEADER_LEN, len);
|
||||
// Dispatch based on which frame type we identified at byte 1
|
||||
if (rxIsSerialHal)
|
||||
handleSerialHalCommand(rxBuf + HEADER_LEN, len);
|
||||
else
|
||||
handleToRadio(rxBuf + HEADER_LEN, len);
|
||||
|
||||
if (rxIsSerialHal)
|
||||
serialHalRxActive.store(false);
|
||||
if (rxIsSerialHal)
|
||||
RedirectablePrint::setSerialHalLogSuppressed(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,6 +246,10 @@ void StreamAPI::emitRebooted()
|
||||
|
||||
void StreamAPI::emitLogRecord(meshtastic_LogRecord_Level level, const char *src, const char *format, va_list arg)
|
||||
{
|
||||
if (serialHalRxActive.load()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// IMPORTANT: do NOT touch `fromRadioScratch` or `txBuf` here — those
|
||||
// belong to the main packet-emission path and a LOG_ firing during
|
||||
// `writeStream()` would corrupt an in-flight encode. We keep a
|
||||
@@ -249,4 +300,31 @@ void StreamAPI::onConnectionChanged(bool connected)
|
||||
// received a packet in a while
|
||||
powerFSM.trigger(EVENT_SERIAL_DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
void StreamAPI::handleSerialHalCommand(const uint8_t *buf, size_t len)
|
||||
{
|
||||
// Default implementation: dispatch to SerialHalDevice for GPIO/SPI handling
|
||||
SerialHalDevice::handleCommand(buf, len, this);
|
||||
}
|
||||
|
||||
void StreamAPI::emitSerialHalResponse(const uint8_t *hdr, size_t hdrLen, const uint8_t *payload, size_t payloadLen)
|
||||
{
|
||||
if (hdr == nullptr || hdrLen != 4 || payload == nullptr || payloadLen > meshtastic_SerialHalResponse_size) {
|
||||
LOG_ERROR("StreamAPI: Invalid SerialHal response parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build complete frame in a temporary buffer
|
||||
uint8_t frame[4 + meshtastic_SerialHalResponse_size];
|
||||
memcpy(frame, hdr, hdrLen);
|
||||
memcpy(frame + hdrLen, payload, payloadLen);
|
||||
|
||||
size_t totalLen = hdrLen + payloadLen;
|
||||
|
||||
// Serialize stream writes against other emit operations via streamLock
|
||||
concurrency::LockGuard guard(&streamLock);
|
||||
stream->write(frame, totalLen);
|
||||
stream->flush();
|
||||
LOG_WARN("StreamAPI: Emitted SerialHal response frame (len=%zu)", totalLen);
|
||||
}
|
||||
+26
-2
@@ -4,10 +4,15 @@
|
||||
#include "Stream.h"
|
||||
#include "concurrency/Lock.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "generated/meshtastic/serial_hal.pb.h"
|
||||
#include <atomic>
|
||||
#include <cstdarg>
|
||||
|
||||
// A To/FromRadio packet + our 32 bit header
|
||||
#define MAX_STREAM_BUF_SIZE (MAX_TO_FROM_RADIO_SIZE + sizeof(uint32_t))
|
||||
// Buffer sized for the larger of a full ToRadio/FromRadio payload or a full SerialHalCommand payload, plus header.
|
||||
#define MAX_STREAM_PAYLOAD_SIZE \
|
||||
(MAX_TO_FROM_RADIO_SIZE > (int)meshtastic_SerialHalCommand_size ? MAX_TO_FROM_RADIO_SIZE \
|
||||
: (int)meshtastic_SerialHalCommand_size)
|
||||
#define MAX_STREAM_BUF_SIZE (MAX_STREAM_PAYLOAD_SIZE + (int)sizeof(uint32_t))
|
||||
|
||||
/**
|
||||
* A version of our 'phone' API that talks over a Stream. So therefore well suited to use with serial links
|
||||
@@ -39,6 +44,8 @@ class StreamAPI : public PhoneAPI
|
||||
|
||||
uint8_t rxBuf[MAX_STREAM_BUF_SIZE] = {0};
|
||||
size_t rxPtr = 0;
|
||||
bool rxIsSerialHal = false; ///< true when the current in-progress frame is a SerialHal frame (START1 SH_MAGIC ...)
|
||||
std::atomic<bool> serialHalRxActive{false};
|
||||
|
||||
/// time of last rx, used, to slow down our polling if we haven't heard from anyone
|
||||
uint32_t lastRxMsec = 0;
|
||||
@@ -56,6 +63,17 @@ class StreamAPI : public PhoneAPI
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
virtual bool checkIsConnected() override = 0;
|
||||
|
||||
/**
|
||||
* Emit a SerialHal response frame with proper framing (START1 SERIALHAL_MAGIC LEN_H LEN_L payload).
|
||||
* Called by SerialHalDevice to send responses back to the host.
|
||||
*
|
||||
* @param hdr 4-byte header (START1 SERIALHAL_MAGIC LEN_H LEN_L)
|
||||
* @param hdrLen Length of header (should be 4)
|
||||
* @param payload Encoded SerialHalResponse protobuf payload
|
||||
* @param payloadLen Length of payload
|
||||
*/
|
||||
void emitSerialHalResponse(const uint8_t *hdr, size_t hdrLen, const uint8_t *payload, size_t payloadLen);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Read any rx chars from the link and call handleToRadio
|
||||
@@ -75,6 +93,12 @@ class StreamAPI : public PhoneAPI
|
||||
*/
|
||||
void emitRebooted();
|
||||
|
||||
/**
|
||||
* Called when a complete SerialHal-framed packet has been received.
|
||||
* Default implementation dispatches to SerialHalDevice for GPIO/SPI handling.
|
||||
*/
|
||||
virtual void handleSerialHalCommand(const uint8_t *buf, size_t len);
|
||||
|
||||
virtual void onConnectionChanged(bool connected) override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
#include "ServerAPI.h"
|
||||
#include <WiFi.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
|
||||
#include <AsyncUDP.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#define UDP_MULTICAST_DEFAUL_PORT 4403 // Default port for UDP multicast is same as TCP api server
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
#include "target_specific.h"
|
||||
#include <WiFi.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
extern bool needReconnect;
|
||||
|
||||
@@ -262,7 +262,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
break;
|
||||
}
|
||||
case meshtastic_AdminMessage_ota_request_tag: {
|
||||
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
|
||||
#if defined(ARCH_ESP32)
|
||||
LOG_INFO("OTA Requested");
|
||||
|
||||
if (r->ota_request.ota_hash.size != 32) {
|
||||
|
||||
@@ -16,28 +16,10 @@ int32_t INA3221Sensor::runOnce()
|
||||
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||
}
|
||||
if (!status) {
|
||||
// Re-initialise with the address and Wire bus from the telemetry sensors map.
|
||||
// (Rob Tillaart INA3221_RT takes address + TwoWire*, unlike sgtwilko which took Wire in begin().)
|
||||
ina3221 = INA3221(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
|
||||
status = ina3221.begin();
|
||||
if (status) {
|
||||
// Default all three channels to a 0.1 Ω shunt resistor.
|
||||
// Override per-variant by defining INA3221_SHUNT_R_CH1/CH2/CH3 (in Ohms) in variant.h.
|
||||
#ifndef INA3221_SHUNT_R_CH1
|
||||
#define INA3221_SHUNT_R_CH1 0.1f
|
||||
#endif
|
||||
#ifndef INA3221_SHUNT_R_CH2
|
||||
#define INA3221_SHUNT_R_CH2 0.1f
|
||||
#endif
|
||||
#ifndef INA3221_SHUNT_R_CH3
|
||||
#define INA3221_SHUNT_R_CH3 0.1f
|
||||
#endif
|
||||
ina3221.setShuntR(0, INA3221_SHUNT_R_CH1);
|
||||
ina3221.setShuntR(1, INA3221_SHUNT_R_CH2);
|
||||
ina3221.setShuntR(2, INA3221_SHUNT_R_CH3);
|
||||
}
|
||||
ina3221.begin(nodeTelemetrySensorsMap[sensorType].second);
|
||||
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
|
||||
status = true;
|
||||
} else {
|
||||
// Already initialised; status stays true and initI2CSensor() returns next poll interval.
|
||||
status = true;
|
||||
}
|
||||
return initI2CSensor();
|
||||
@@ -45,14 +27,12 @@ int32_t INA3221Sensor::runOnce()
|
||||
|
||||
void INA3221Sensor::setup() {}
|
||||
|
||||
struct _INA3221Measurement INA3221Sensor::getMeasurement(uint8_t ch)
|
||||
struct _INA3221Measurement INA3221Sensor::getMeasurement(ina3221_ch_t ch)
|
||||
{
|
||||
struct _INA3221Measurement measurement;
|
||||
|
||||
measurement.voltage = ina3221.getBusVoltage(ch); // Volts
|
||||
// getCurrent_mA() is used instead of getCurrent() because Rob Tillaart's getCurrent()
|
||||
// returns Amperes; the telemetry proto and VoltageSensor/CurrentSensor interfaces expect mA.
|
||||
measurement.current = ina3221.getCurrent_mA(ch); // milliAmps
|
||||
measurement.voltage = ina3221.getVoltage(ch);
|
||||
measurement.current = ina3221.getCurrent(ch);
|
||||
|
||||
return measurement;
|
||||
}
|
||||
@@ -63,7 +43,7 @@ struct _INA3221Measurements INA3221Sensor::getMeasurements()
|
||||
|
||||
// INA3221 has 3 channels starting from 0
|
||||
for (int i = 0; i < 3; i++) {
|
||||
measurements.measurements[i] = getMeasurement((uint8_t)i);
|
||||
measurements.measurements[i] = getMeasurement((ina3221_ch_t)i);
|
||||
}
|
||||
|
||||
return measurements;
|
||||
@@ -107,41 +87,24 @@ bool INA3221Sensor::getPowerMetrics(meshtastic_Telemetry *measurement)
|
||||
measurement->variant.power_metrics.has_ch3_voltage = true;
|
||||
measurement->variant.power_metrics.has_ch3_current = true;
|
||||
|
||||
// INA3221 channel indices are zero-based (0=CH1, 1=CH2, 2=CH3).
|
||||
measurement->variant.power_metrics.ch1_voltage = m.measurements[0].voltage;
|
||||
measurement->variant.power_metrics.ch1_current = m.measurements[0].current;
|
||||
measurement->variant.power_metrics.ch2_voltage = m.measurements[1].voltage;
|
||||
measurement->variant.power_metrics.ch2_current = m.measurements[1].current;
|
||||
measurement->variant.power_metrics.ch3_voltage = m.measurements[2].voltage;
|
||||
measurement->variant.power_metrics.ch3_current = m.measurements[2].current;
|
||||
measurement->variant.power_metrics.ch1_voltage = m.measurements[INA3221_CH1].voltage;
|
||||
measurement->variant.power_metrics.ch1_current = m.measurements[INA3221_CH1].current;
|
||||
measurement->variant.power_metrics.ch2_voltage = m.measurements[INA3221_CH2].voltage;
|
||||
measurement->variant.power_metrics.ch2_current = m.measurements[INA3221_CH2].current;
|
||||
measurement->variant.power_metrics.ch3_voltage = m.measurements[INA3221_CH3].voltage;
|
||||
measurement->variant.power_metrics.ch3_current = m.measurements[INA3221_CH3].current;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t INA3221Sensor::getBusVoltageMv()
|
||||
{
|
||||
return lround(ina3221.getBusVoltage_mV(BAT_CH));
|
||||
return lround(ina3221.getVoltage(BAT_CH) * 1000);
|
||||
}
|
||||
|
||||
int16_t INA3221Sensor::getCurrentMa()
|
||||
{
|
||||
return lround(ina3221.getCurrent_mA(BAT_CH));
|
||||
}
|
||||
|
||||
// Bus voltage register (0x02 + ch*2): bits [15:3] unsigned, 1 LSB = 8 mV (datasheet p.6).
|
||||
// Voltage raw units: 1 count = 8 mV, so V_mV = raw * 8.
|
||||
int16_t INA3221Sensor::getRawBusVoltage(uint8_t ch)
|
||||
{
|
||||
return (int16_t)(ina3221.getRegister(0x02 + ch * 2) >> 3);
|
||||
}
|
||||
|
||||
// Shunt voltage register (0x01 + ch*2): bits [15:3] signed two's complement, 1 LSB = 40 µV (datasheet p.6).
|
||||
// Current raw units are shunt-voltage counts: 1 count = 40 uV, signed.
|
||||
// I_mA = (raw * 40 uV) / R_mOhm, because uV / mOhm = mA.
|
||||
// Example for 100 mOhm shunt: I_mA = raw * 40 / 100 = raw * 0.4.
|
||||
int16_t INA3221Sensor::getRawShuntCurrent(uint8_t ch)
|
||||
{
|
||||
return (int16_t)(ina3221.getRegister(0x01 + ch * 2) >> 3);
|
||||
return lround(ina3221.getCurrent(BAT_CH));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,9 +1,3 @@
|
||||
// INA3221 channel aliases (zero-based: 0 = CH1, 1 = CH2, 2 = CH3).
|
||||
// Defined before configuration.h so variant.h can use them in INA3221_ENV_CH / INA3221_BAT_CH.
|
||||
#define INA3221_CH1 0
|
||||
#define INA3221_CH2 1
|
||||
#define INA3221_CH3 2
|
||||
|
||||
#include "configuration.h"
|
||||
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<INA3221.h>)
|
||||
@@ -15,29 +9,26 @@
|
||||
#include <INA3221.h>
|
||||
|
||||
#ifndef INA3221_ENV_CH
|
||||
#define INA3221_ENV_CH INA3221_CH1 // channel to report in environment metrics (default: CH1)
|
||||
#define INA3221_ENV_CH INA3221_CH1
|
||||
#endif
|
||||
|
||||
#ifndef INA3221_BAT_CH
|
||||
#define INA3221_BAT_CH INA3221_CH1 // channel for device_battery_ina_address (default: CH1)
|
||||
#define INA3221_BAT_CH INA3221_CH1
|
||||
#endif
|
||||
|
||||
class INA3221Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
|
||||
{
|
||||
private:
|
||||
// Placeholder constructor; re-initialised with correct address and Wire in runOnce().
|
||||
INA3221 ina3221 = INA3221(INA3221_ADDR);
|
||||
INA3221 ina3221 = INA3221(INA3221_ADDR42_SDA);
|
||||
|
||||
// channel to report voltage/current for environment metrics
|
||||
static const uint8_t ENV_CH = INA3221_ENV_CH;
|
||||
static_assert(INA3221_ENV_CH >= 0 && INA3221_ENV_CH <= 2, "INA3221_ENV_CH must be 0, 1, or 2");
|
||||
static const ina3221_ch_t ENV_CH = INA3221_ENV_CH;
|
||||
|
||||
// channel to report battery voltage for device_battery_ina_address
|
||||
static const uint8_t BAT_CH = INA3221_BAT_CH;
|
||||
static_assert(INA3221_BAT_CH >= 0 && INA3221_BAT_CH <= 2, "INA3221_BAT_CH must be 0, 1, or 2");
|
||||
static const ina3221_ch_t BAT_CH = INA3221_BAT_CH;
|
||||
|
||||
// get a single measurement for a channel
|
||||
struct _INA3221Measurement getMeasurement(uint8_t ch);
|
||||
struct _INA3221Measurement getMeasurement(ina3221_ch_t ch);
|
||||
|
||||
// get all measurements for all channels
|
||||
struct _INA3221Measurements getMeasurements();
|
||||
@@ -54,10 +45,6 @@ class INA3221Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
|
||||
bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||
virtual uint16_t getBusVoltageMv() override;
|
||||
virtual int16_t getCurrentMa() override;
|
||||
|
||||
// Raw register reads (bits [15:3] right-shifted), no conversion applied.
|
||||
int16_t getRawBusVoltage(uint8_t ch);
|
||||
int16_t getRawShuntCurrent(uint8_t ch);
|
||||
};
|
||||
|
||||
struct _INA3221Measurement {
|
||||
|
||||
@@ -90,8 +90,8 @@ int32_t PaxcounterModule::runOnce()
|
||||
configuration.blecounter = 1;
|
||||
configuration.blescantime = 0; // infinite
|
||||
configuration.wificounter = 1;
|
||||
// configuration.wifi_channel_map = WIFI_CHANNEL_ALL;
|
||||
// configuration.wifi_channel_switch_interval = 50;
|
||||
configuration.wifi_channel_map = WIFI_CHANNEL_ALL;
|
||||
configuration.wifi_channel_switch_interval = 50;
|
||||
configuration.wifi_rssi_threshold = Default::getConfiguredOrDefault(moduleConfig.paxcounter.wifi_threshold, -80);
|
||||
configuration.ble_rssi_threshold = Default::getConfiguredOrDefault(moduleConfig.paxcounter.ble_threshold, -80);
|
||||
libpax_update_config(&configuration);
|
||||
|
||||
+3
-2
@@ -19,8 +19,9 @@
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#endif // HAS_ETHERNET
|
||||
#include "Default.h"
|
||||
#if !defined(ARCH_NRF52) || NRF52_USE_JSON
|
||||
|
||||
+243
-163
@@ -10,18 +10,23 @@
|
||||
#include "mesh/PhoneAPI.h"
|
||||
#include "mesh/mesh-pb-constants.h"
|
||||
#include "sleep.h"
|
||||
#include <BLE2904.h>
|
||||
#include <BLEAdvertising.h>
|
||||
#include <BLEDevice.h>
|
||||
#include <BLESecurity.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <NimBLEDevice.h>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
#include "NimBLEAdvertising.h"
|
||||
#include "NimBLEExtAdvertising.h"
|
||||
#include "PowerStatus.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_store.h"
|
||||
#else
|
||||
#include "nimble/nimble/host/include/host/ble_gap.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -29,6 +34,7 @@ constexpr uint16_t kPreferredBleMtu = 517;
|
||||
constexpr uint16_t kPreferredBleTxOctets = 251;
|
||||
constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
// Debugging options: careful, they slow things down quite a bit!
|
||||
// #define DEBUG_NIMBLE_ON_READ_TIMING // uncomment to time onRead duration
|
||||
@@ -38,10 +44,10 @@ constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
|
||||
#define NIMBLE_BLUETOOTH_TO_PHONE_QUEUE_SIZE 3
|
||||
#define NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE 3
|
||||
|
||||
BLECharacteristic *fromNumCharacteristic;
|
||||
BLECharacteristic *BatteryCharacteristic;
|
||||
BLECharacteristic *logRadioCharacteristic;
|
||||
BLEServer *bleServer;
|
||||
NimBLECharacteristic *fromNumCharacteristic;
|
||||
NimBLECharacteristic *BatteryCharacteristic;
|
||||
NimBLECharacteristic *logRadioCharacteristic;
|
||||
NimBLEServer *bleServer;
|
||||
|
||||
static bool passkeyShowing;
|
||||
static std::atomic<uint16_t> nimbleBluetoothConnHandle{BLE_HS_CONN_HANDLE_NONE}; // BLE_HS_CONN_HANDLE_NONE means "no connection"
|
||||
@@ -112,8 +118,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
- Yes, we have to do some copy operations on pop because of this, but it's worth it to avoid cross-task memory management.
|
||||
|
||||
NOTIFY IS BROKEN:
|
||||
- Adding BLECharacteristic::PROPERTY_NOTIFY to FromRadioCharacteristic appears to break things. It is NOT backwards
|
||||
compatible.
|
||||
- Adding NIMBLE_PROPERTY::NOTIFY to FromRadioCharacteristic appears to break things. It is NOT backwards compatible.
|
||||
|
||||
ZERO-SIZE READS:
|
||||
- Returning a zero-size read from onRead breaks some clients during the config phase. So we have to block onRead until we
|
||||
@@ -134,7 +139,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
std::mutex fromPhoneMutex;
|
||||
std::atomic<size_t> fromPhoneQueueSize{0};
|
||||
// We use array here (and pay the cost of memcpy) to avoid dynamic memory allocations and frees across FreeRTOS tasks.
|
||||
std::array<BLEValue, NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE> fromPhoneQueue{};
|
||||
std::array<NimBLEAttValue, NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE> fromPhoneQueue{};
|
||||
|
||||
/* Packets to phone (BLE onRead callback) */
|
||||
std::mutex toPhoneMutex;
|
||||
@@ -296,7 +301,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
LOG_DEBUG("NimbleBluetooth: handling ToRadio packet, fromPhoneQueueSize=%u", fromPhoneQueueSize.load());
|
||||
|
||||
// Pop the front of fromPhoneQueue, holding the mutex only briefly while we pop.
|
||||
BLEValue val;
|
||||
NimBLEAttValue val;
|
||||
{ // scope for fromPhoneMutex mutex
|
||||
std::lock_guard<std::mutex> guard(fromPhoneMutex);
|
||||
val = fromPhoneQueue[0];
|
||||
@@ -311,7 +316,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
fromPhoneQueueSize--;
|
||||
}
|
||||
|
||||
handleToRadio(val.getData(), val.getLength());
|
||||
handleToRadio(val.data(), val.length());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +328,9 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
#ifdef DEBUG_NIMBLE_NOTIFY
|
||||
|
||||
int currentNotifyCount = notifyCount.fetch_add(1);
|
||||
|
||||
uint8_t cc = bleServer->getConnectedCount();
|
||||
// This logging slows things down when there are lots of packets going to the phone, like initial connection:
|
||||
LOG_DEBUG("BLE notify(%d) fromNum: %d connections: %d", currentNotifyCount, fromRadioNum, cc);
|
||||
@@ -333,7 +340,13 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
put_le32(val, fromRadioNum);
|
||||
|
||||
fromNumCharacteristic->setValue(val, sizeof(val));
|
||||
#ifdef NIMBLE_TWO
|
||||
// NOTE: I don't have any NIMBLE_TWO devices, but this line makes me suspicious, and I suspect it needs to just be
|
||||
// notify().
|
||||
fromNumCharacteristic->notify(val, sizeof(val), BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
fromNumCharacteristic->notify();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
@@ -396,9 +409,14 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI;
|
||||
// Last ToRadio value received from the phone
|
||||
static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
|
||||
|
||||
class NimbleBluetoothToRadioCallback : public BLECharacteristicCallbacks
|
||||
class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
|
||||
{
|
||||
void onWrite(BLECharacteristic *pCharacteristic) override
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) override
|
||||
#else
|
||||
virtual void onWrite(NimBLECharacteristic *pCharacteristic) override
|
||||
|
||||
#endif
|
||||
{
|
||||
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
|
||||
// Assumption: onWrite is serialized by NimBLE, so we don't need to lock here against multiple concurrent onWrite calls.
|
||||
@@ -410,17 +428,15 @@ class NimbleBluetoothToRadioCallback : public BLECharacteristicCallbacks
|
||||
LOG_DEBUG("BLE onWrite(%d): start millis=%d", currentWriteCount, startMillis);
|
||||
#endif
|
||||
|
||||
// Create a BLEValue and populate it with the received data
|
||||
BLEValue val;
|
||||
val.setValue(pCharacteristic->getData(), pCharacteristic->getLength());
|
||||
auto val = pCharacteristic->getValue();
|
||||
|
||||
if (memcmp(lastToRadio, val.getData(), val.getLength()) != 0) {
|
||||
if (memcmp(lastToRadio, val.data(), val.length()) != 0) {
|
||||
if (bluetoothPhoneAPI->fromPhoneQueueSize < NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE) {
|
||||
// Note: the comparison above is safe without a mutex because we are the only method that *increases*
|
||||
// fromPhoneQueueSize. (It's okay if fromPhoneQueueSize *decreases* in the main task meanwhile.)
|
||||
memcpy(lastToRadio, val.getData(), val.getLength());
|
||||
memcpy(lastToRadio, val.data(), val.length());
|
||||
|
||||
{ // scope for fromPhoneMutex mutexv, pCharacteristic->getLen
|
||||
{ // scope for fromPhoneMutex mutex
|
||||
// Append to fromPhoneQueue, protected by fromPhoneMutex. Hold the mutex as briefly as possible.
|
||||
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->fromPhoneMutex);
|
||||
bluetoothPhoneAPI->fromPhoneQueue.at(bluetoothPhoneAPI->fromPhoneQueueSize) = val;
|
||||
@@ -434,21 +450,24 @@ class NimbleBluetoothToRadioCallback : public BLECharacteristicCallbacks
|
||||
#ifdef DEBUG_NIMBLE_ON_WRITE_TIMING
|
||||
int finishMillis = millis();
|
||||
LOG_DEBUG("BLE onWrite(%d): append to fromPhoneQueue took %u ms. numBytes=%d", currentWriteCount,
|
||||
finishMillis - startMillis, val.getLength());
|
||||
finishMillis - startMillis, val.length());
|
||||
#endif
|
||||
} else {
|
||||
LOG_WARN("BLE onWrite(%d): Drop ToRadio packet, fromPhoneQueue full (%u bytes)", currentWriteCount,
|
||||
val.getLength());
|
||||
LOG_WARN("BLE onWrite(%d): Drop ToRadio packet, fromPhoneQueue full (%u bytes)", currentWriteCount, val.length());
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG("BLE onWrite(%d): Drop duplicate ToRadio packet (%u bytes)", currentWriteCount, val.getLength());
|
||||
LOG_DEBUG("BLE onWrite(%d): Drop duplicate ToRadio packet (%u bytes)", currentWriteCount, val.length());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class NimbleBluetoothFromRadioCallback : public BLECharacteristicCallbacks
|
||||
class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
||||
{
|
||||
void onRead(BLECharacteristic *pCharacteristic) override
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) override
|
||||
#else
|
||||
virtual void onRead(NimBLECharacteristic *pCharacteristic) override
|
||||
#endif
|
||||
{
|
||||
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
|
||||
|
||||
@@ -554,15 +573,34 @@ class NimbleBluetoothFromRadioCallback : public BLECharacteristicCallbacks
|
||||
}
|
||||
};
|
||||
|
||||
class NimbleBluetoothSecurityCallback : public BLESecurityCallbacks
|
||||
class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
{
|
||||
void onPassKeyNotify(uint32_t passkey) override
|
||||
#ifdef NIMBLE_TWO
|
||||
public:
|
||||
NimbleBluetoothServerCallback(NimbleBluetooth *ble) { this->ble = ble; }
|
||||
|
||||
private:
|
||||
NimbleBluetooth *ble;
|
||||
|
||||
virtual uint32_t onPassKeyDisplay() override
|
||||
#else
|
||||
virtual uint32_t onPassKeyRequest() override
|
||||
#endif
|
||||
{
|
||||
LOG_INFO("*** Enter passkey %06u on the peer side ***", passkey);
|
||||
uint32_t passkey = config.bluetooth.fixed_pin;
|
||||
|
||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
|
||||
LOG_INFO("Use random passkey");
|
||||
// This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
|
||||
passkey = random(100000, 999999);
|
||||
}
|
||||
LOG_INFO("*** Enter passkey %d on the peer side ***", passkey);
|
||||
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
meshtastic::BluetoothStatus newStatus(std::to_string(passkey));
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
#if HAS_SCREEN
|
||||
|
||||
#if HAS_SCREEN // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
|
||||
if (screen) {
|
||||
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
char btPIN[16] = "888888";
|
||||
@@ -592,8 +630,15 @@ class NimbleBluetoothSecurityCallback : public BLESecurityCallbacks
|
||||
}
|
||||
#endif
|
||||
passkeyShowing = true;
|
||||
|
||||
return passkey;
|
||||
}
|
||||
void onAuthenticationComplete(ble_gap_conn_desc *desc) override
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onAuthenticationComplete(NimBLEConnInfo &connInfo) override
|
||||
#else
|
||||
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc) override
|
||||
#endif
|
||||
{
|
||||
LOG_INFO("BLE authentication complete");
|
||||
|
||||
@@ -601,47 +646,58 @@ class NimbleBluetoothSecurityCallback : public BLESecurityCallbacks
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
clearPairingDisplay();
|
||||
|
||||
// Store the connection handle for future use
|
||||
#ifdef NIMBLE_TWO
|
||||
nimbleBluetoothConnHandle = connInfo.getConnHandle();
|
||||
#else
|
||||
nimbleBluetoothConnHandle = desc->conn_handle;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class NimbleBluetoothServerCallback : public BLEServerCallbacks
|
||||
{
|
||||
public:
|
||||
explicit NimbleBluetoothServerCallback(NimbleBluetooth *ble) : ble(ble) {}
|
||||
|
||||
private:
|
||||
NimbleBluetooth *ble;
|
||||
|
||||
void onConnect(BLEServer *pServer, struct ble_gap_conn_desc *desc)
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override
|
||||
{
|
||||
BLEAddress peer_addr(desc->peer_id_addr);
|
||||
LOG_INFO("BLE incoming connection %s", peer_addr.toString().c_str());
|
||||
LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str());
|
||||
|
||||
const uint16_t connHandle = desc->conn_handle;
|
||||
const uint16_t connHandle = connInfo.getConnHandle();
|
||||
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
|
||||
int phyResult =
|
||||
ble_gap_set_prefered_le_phy(connHandle, BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_CODED_ANY);
|
||||
if (phyResult == 0) {
|
||||
LOG_INFO("BLE conn %u requested 2M PHY", connHandle);
|
||||
} else {
|
||||
LOG_WARN("Failed to prefer 2M PHY for conn %u, rc=%d", connHandle, phyResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
// With Google Pixel 8 Android devices, this causes ESP32 device crash
|
||||
// when phone reconnects. Disable this to make progress on the
|
||||
// Arduino v3 migration while we investigate the Android compatibility
|
||||
// issue.
|
||||
#if 0
|
||||
int dataLenResult = ble_gap_set_data_len(connHandle, kPreferredBleTxOctets, kPreferredBleTxTimeUs);
|
||||
if (dataLenResult == 0) {
|
||||
LOG_INFO("BLE conn %u requested data length %u bytes", connHandle, kPreferredBleTxOctets);
|
||||
} else {
|
||||
LOG_WARN("Failed to raise data length for conn %u, rc=%d", connHandle, dataLenResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_INFO("BLE conn %u peer MTU %u (target %u)", connHandle, pServer->getPeerMTU(connHandle), kPreferredBleMtu);
|
||||
LOG_INFO("BLE conn %u initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu);
|
||||
pServer->updateConnParams(connHandle, 6, 12, 0, 200);
|
||||
}
|
||||
#endif
|
||||
|
||||
void onDisconnect(BLEServer *pServer, struct ble_gap_conn_desc *desc)
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override
|
||||
{
|
||||
LOG_INFO("BLE disconnected");
|
||||
LOG_INFO("BLE disconnect reason: %d", reason);
|
||||
#else
|
||||
virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) override
|
||||
{
|
||||
LOG_INFO("BLE disconnect");
|
||||
#endif
|
||||
#ifdef NIMBLE_TWO
|
||||
if (ble->isDeInit)
|
||||
return;
|
||||
#else
|
||||
if (nimbleBluetooth && nimbleBluetooth->isDeInit)
|
||||
return;
|
||||
#endif
|
||||
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
@@ -666,51 +722,43 @@ class NimbleBluetoothServerCallback : public BLEServerCallbacks
|
||||
bluetoothPhoneAPI->writeCount = 0;
|
||||
}
|
||||
|
||||
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
|
||||
memset(lastToRadio, 0, sizeof(lastToRadio));
|
||||
|
||||
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE;
|
||||
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE; // BLE_HS_CONN_HANDLE_NONE means "no connection"
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
// Restart Advertising
|
||||
ble->startAdvertising();
|
||||
#else
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
if (!pAdvertising->start(0)) {
|
||||
if (pAdvertising->isAdvertising()) {
|
||||
LOG_DEBUG("BLE advertising already running");
|
||||
} else {
|
||||
LOG_ERROR("BLE failed to restart advertising");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static NimbleBluetoothToRadioCallback *toRadioCallbacks;
|
||||
static NimbleBluetoothFromRadioCallback *fromRadioCallbacks;
|
||||
|
||||
void NimbleBluetooth::startAdvertising()
|
||||
{
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->stop();
|
||||
pAdvertising->reset();
|
||||
|
||||
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
|
||||
// if (powerStatus->getHasBattery() == 1) {
|
||||
// pAdvertising->addServiceUUID(BLEUUID((uint16_t)0x180f));
|
||||
// }
|
||||
|
||||
BLEAdvertisementData scan = BLEAdvertisementData();
|
||||
scan.setName(getDeviceName());
|
||||
pAdvertising->setScanResponseData(scan);
|
||||
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
|
||||
pAdvertising->setMaxPreferred(0x12);
|
||||
|
||||
if (!pAdvertising->start(0)) {
|
||||
LOG_ERROR("BLE failed to start advertising");
|
||||
} else {
|
||||
LOG_DEBUG("BLE Advertising started");
|
||||
}
|
||||
}
|
||||
|
||||
void NimbleBluetooth::shutdown()
|
||||
{
|
||||
// No measurable power saving for ESP32 during light-sleep(?)
|
||||
#ifndef ARCH_ESP32
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
LOG_INFO("Disable bluetooth");
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->reset();
|
||||
pAdvertising->stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Proper shutdown for ESP32. Needs reboot to reverse.
|
||||
void NimbleBluetooth::deinit()
|
||||
{
|
||||
#ifdef ARCH_ESP32
|
||||
@@ -720,19 +768,21 @@ void NimbleBluetooth::deinit()
|
||||
#ifdef BLE_LED
|
||||
digitalWrite(BLE_LED, LED_STATE_OFF);
|
||||
#endif
|
||||
|
||||
BLEDevice::deinit(true);
|
||||
#ifndef NIMBLE_TWO
|
||||
NimBLEDevice::deinit();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Has initial setup been completed
|
||||
bool NimbleBluetooth::isActive()
|
||||
{
|
||||
return bleServer != nullptr;
|
||||
return bleServer;
|
||||
}
|
||||
|
||||
bool NimbleBluetooth::isConnected()
|
||||
{
|
||||
return bleServer && bleServer->getConnectedCount() > 0;
|
||||
return bleServer->getConnectedCount() > 0;
|
||||
}
|
||||
|
||||
int NimbleBluetooth::getRssi()
|
||||
@@ -745,9 +795,9 @@ int NimbleBluetooth::getRssi()
|
||||
uint16_t connHandle = nimbleBluetoothConnHandle.load();
|
||||
|
||||
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
|
||||
const auto peers = bleServer->getPeerDevices(true);
|
||||
const auto peers = bleServer->getPeerDevices();
|
||||
if (!peers.empty()) {
|
||||
connHandle = peers.begin()->first;
|
||||
connHandle = peers.front();
|
||||
nimbleBluetoothConnHandle = connHandle;
|
||||
}
|
||||
}
|
||||
@@ -775,84 +825,74 @@ void NimbleBluetooth::setup()
|
||||
|
||||
LOG_INFO("Init the NimBLE bluetooth module");
|
||||
|
||||
BLEDevice::init(getDeviceName());
|
||||
BLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
NimBLEDevice::init(getDeviceName());
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
|
||||
int mtuResult = BLEDevice::setMTU(kPreferredBleMtu);
|
||||
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
|
||||
int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu);
|
||||
if (mtuResult == 0) {
|
||||
LOG_INFO("BLE MTU request set to %u", kPreferredBleMtu);
|
||||
} else {
|
||||
LOG_WARN("Unable to request MTU %u, rc=%d", kPreferredBleMtu, mtuResult);
|
||||
}
|
||||
|
||||
BLESecurity *pSecurity = new BLESecurity();
|
||||
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
||||
pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
||||
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
// Set IO capability to DisplayOnly for MITM authentication
|
||||
pSecurity->setCapability(ESP_IO_CAP_OUT);
|
||||
// Set the passkey
|
||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
|
||||
LOG_INFO("Use random passkey");
|
||||
pSecurity->setPassKey(false); // generate a random passkey
|
||||
} else {
|
||||
LOG_INFO("Use fixed passkey");
|
||||
pSecurity->setPassKey(true, config.bluetooth.fixed_pin);
|
||||
}
|
||||
// Enable authorization requirements:
|
||||
// - bonding: true (for persistent storage of the keys)
|
||||
// - MITM: true (enables Man-In-The-Middle protection for password prompts)
|
||||
// - secure connection: true (enables secure connection for encryption)
|
||||
pSecurity->setAuthenticationMode(true, true, true);
|
||||
int phyResult = ble_gap_set_prefered_default_le_phy(BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_2M_MASK);
|
||||
if (phyResult == 0) {
|
||||
LOG_INFO("BLE default PHY preference set to 2M");
|
||||
} else {
|
||||
// No IO capability for no PIN mode
|
||||
pSecurity->setCapability(ESP_IO_CAP_NONE);
|
||||
// No PIN mode: no MITM protection
|
||||
pSecurity->setAuthenticationMode(true, false, false);
|
||||
}
|
||||
// Set the security callbacks
|
||||
BLEDevice::setSecurityCallbacks(new NimbleBluetoothSecurityCallback());
|
||||
bleServer = BLEDevice::createServer();
|
||||
|
||||
// BLEDevice::createServer calls ble_svc_gap_init, which resets the device
|
||||
// name to default, so set it again.
|
||||
int nameRc = ble_svc_gap_device_name_set(BLEDevice::getDeviceName().c_str());
|
||||
if (nameRc != 0) {
|
||||
LOG_ERROR("ble_svc_gap_device_name_set: rc=%d %s", nameRc, BLEUtils::returnCodeToString(nameRc));
|
||||
LOG_WARN("Failed to prefer 2M PHY by default, rc=%d", phyResult);
|
||||
}
|
||||
|
||||
bleServer->setCallbacks(new NimbleBluetoothServerCallback(this));
|
||||
int dataLenResult = ble_gap_write_sugg_def_data_len(kPreferredBleTxOctets, kPreferredBleTxTimeUs);
|
||||
if (dataLenResult == 0) {
|
||||
LOG_INFO("BLE suggested data length set to %u bytes", kPreferredBleTxOctets);
|
||||
} else {
|
||||
LOG_WARN("Failed to raise suggested data length (%u/%u), rc=%d", kPreferredBleTxOctets, kPreferredBleTxTimeUs,
|
||||
dataLenResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);
|
||||
NimBLEDevice::setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
|
||||
NimBLEDevice::setSecurityRespKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
|
||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
|
||||
}
|
||||
bleServer = NimBLEDevice::createServer();
|
||||
#ifdef NIMBLE_TWO
|
||||
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(this);
|
||||
#else
|
||||
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback();
|
||||
#endif
|
||||
bleServer->setCallbacks(serverCallbacks, true);
|
||||
setupService();
|
||||
startAdvertising();
|
||||
}
|
||||
|
||||
void NimbleBluetooth::setupService()
|
||||
{
|
||||
BLEService *bleService = bleServer->createService(MESH_SERVICE_UUID);
|
||||
BLECharacteristic *ToRadioCharacteristic;
|
||||
BLECharacteristic *FromRadioCharacteristic;
|
||||
NimBLEService *bleService = bleServer->createService(MESH_SERVICE_UUID);
|
||||
NimBLECharacteristic *ToRadioCharacteristic;
|
||||
NimBLECharacteristic *FromRadioCharacteristic;
|
||||
// Define the characteristics that the app is looking for
|
||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE);
|
||||
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
|
||||
// Allow notifications so phones can stream FromRadio without polling.
|
||||
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ);
|
||||
fromNumCharacteristic =
|
||||
bleService->createCharacteristic(FROMNUM_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ);
|
||||
logRadioCharacteristic = bleService->createCharacteristic(LOGRADIO_UUID, BLECharacteristic::PROPERTY_NOTIFY |
|
||||
BLECharacteristic::PROPERTY_READ);
|
||||
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ);
|
||||
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
|
||||
logRadioCharacteristic =
|
||||
bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ, 512U);
|
||||
} else {
|
||||
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_WRITE_AUTHEN |
|
||||
BLECharacteristic::PROPERTY_WRITE_ENC);
|
||||
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_READ_AUTHEN |
|
||||
BLECharacteristic::PROPERTY_READ_ENC);
|
||||
fromNumCharacteristic = bleService->createCharacteristic(
|
||||
FROMNUM_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_ENC);
|
||||
ToRadioCharacteristic = bleService->createCharacteristic(
|
||||
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
FromRadioCharacteristic = bleService->createCharacteristic(
|
||||
FROMRADIO_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
||||
fromNumCharacteristic =
|
||||
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
|
||||
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
||||
logRadioCharacteristic = bleService->createCharacteristic(
|
||||
LOGRADIO_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_ENC);
|
||||
LOGRADIO_UUID,
|
||||
NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC, 512U);
|
||||
}
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
|
||||
@@ -865,31 +905,76 @@ void NimbleBluetooth::setupService()
|
||||
bleService->start();
|
||||
|
||||
// Setup the battery service
|
||||
BLEService *batteryService = bleServer->createService(BLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
||||
BLE2904 *batteryLevelDescriptor = new BLE2904();
|
||||
batteryLevelDescriptor->setFormat(BLE2904::FORMAT_UINT8);
|
||||
NimBLEService *batteryService = bleServer->createService(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
||||
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
|
||||
(uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY, 1);
|
||||
#ifdef NIMBLE_TWO
|
||||
NimBLE2904 *batteryLevelDescriptor = BatteryCharacteristic->create2904();
|
||||
#else
|
||||
NimBLE2904 *batteryLevelDescriptor = (NimBLE2904 *)BatteryCharacteristic->createDescriptor((uint16_t)0x2904);
|
||||
#endif
|
||||
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
|
||||
batteryLevelDescriptor->setNamespace(1);
|
||||
batteryLevelDescriptor->setUnit(0x27ad);
|
||||
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
|
||||
(uint16_t)0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
|
||||
BatteryCharacteristic->addDescriptor(batteryLevelDescriptor);
|
||||
|
||||
batteryService->start();
|
||||
}
|
||||
|
||||
void NimbleBluetooth::startAdvertising()
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
NimBLEExtAdvertisement legacyAdvertising;
|
||||
|
||||
legacyAdvertising.setLegacyAdvertising(true);
|
||||
legacyAdvertising.setScannable(true);
|
||||
legacyAdvertising.setConnectable(true);
|
||||
legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN);
|
||||
if (powerStatus->getHasBattery() == 1) {
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f));
|
||||
}
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID));
|
||||
legacyAdvertising.setMinInterval(500);
|
||||
legacyAdvertising.setMaxInterval(1000);
|
||||
|
||||
NimBLEExtAdvertisement legacyScanResponse;
|
||||
legacyScanResponse.setLegacyAdvertising(true);
|
||||
legacyScanResponse.setConnectable(true);
|
||||
legacyScanResponse.setName(getDeviceName());
|
||||
|
||||
if (!pAdvertising->setInstanceData(0, legacyAdvertising)) {
|
||||
LOG_ERROR("BLE failed to set legacyAdvertising");
|
||||
} else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) {
|
||||
LOG_ERROR("BLE failed to set legacyScanResponse");
|
||||
} else if (!pAdvertising->start(0, 0, 0)) {
|
||||
LOG_ERROR("BLE failed to start legacyAdvertising");
|
||||
}
|
||||
#else
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->reset();
|
||||
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
|
||||
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
||||
pAdvertising->start(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
if ((config.bluetooth.enabled == true) && bleServer && nimbleBluetooth->isConnected()) {
|
||||
BatteryCharacteristic->setValue(&level, 1);
|
||||
#ifdef NIMBLE_TWO
|
||||
BatteryCharacteristic->notify(&level, 1, BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
BatteryCharacteristic->notify();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void NimbleBluetooth::clearBonds()
|
||||
{
|
||||
LOG_INFO("Clearing bluetooth bonds!");
|
||||
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_PEER_SEC, nullptr);
|
||||
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_CCCD, nullptr);
|
||||
NimBLEDevice::deleteAllBonds();
|
||||
}
|
||||
|
||||
void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
@@ -897,16 +982,11 @@ void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
if (!bleServer || !isConnected() || length > 512) {
|
||||
return;
|
||||
}
|
||||
logRadioCharacteristic->setValue(logMessage, length);
|
||||
logRadioCharacteristic->notify();
|
||||
#ifdef NIMBLE_TWO
|
||||
logRadioCharacteristic->notify(logMessage, length, BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
logRadioCharacteristic->notify(logMessage, length, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void clearNVS()
|
||||
{
|
||||
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_PEER_SEC, nullptr);
|
||||
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_CCCD, nullptr);
|
||||
#ifdef ARCH_ESP32
|
||||
ESP.restart();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -12,11 +12,16 @@ class NimbleBluetooth : BluetoothApi
|
||||
bool isConnected();
|
||||
int getRssi();
|
||||
void sendLog(const uint8_t *logMessage, size_t length);
|
||||
#if defined(NIMBLE_TWO)
|
||||
void startAdvertising();
|
||||
#endif
|
||||
bool isDeInit = false;
|
||||
|
||||
private:
|
||||
void setupService();
|
||||
#if !defined(NIMBLE_TWO)
|
||||
void startAdvertising();
|
||||
#endif
|
||||
};
|
||||
|
||||
void setBluetoothEnable(bool enable);
|
||||
@@ -1,42 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_attr.h"
|
||||
|
||||
#ifdef ESP32_FORCE_IRAM_MEMSET
|
||||
|
||||
/*
|
||||
* T-Beam/classic ESP32 boot workaround
|
||||
* ------------------------------------
|
||||
* During early flash operations the ESP32 disables cache, but some IRAM flash
|
||||
* code paths still reach libc memcpy/memset. If those resolve to flash-resident
|
||||
* implementations, startup can panic with cache-disabled access errors.
|
||||
*
|
||||
* We wrap memcpy/memset for the T-Beam environment. Fast path uses the
|
||||
* normal libc routines when cache is enabled; slow path uses IRAM-safe byte
|
||||
* loops when cache is disabled.
|
||||
*/
|
||||
|
||||
extern void *__real_memcpy(void *dst, const void *src, size_t n);
|
||||
|
||||
static inline bool IRAM_ATTR cache_is_enabled(void)
|
||||
{
|
||||
return (*(volatile uint32_t *)0x3FF00040u) & (1u << 3);
|
||||
}
|
||||
|
||||
extern void *IRAM_ATTR __wrap_memcpy(void *dst, const void *src, size_t n)
|
||||
{
|
||||
if (cache_is_enabled()) {
|
||||
return __real_memcpy(dst, src, n);
|
||||
}
|
||||
|
||||
uint8_t *d = (uint8_t *)dst;
|
||||
const uint8_t *s = (const uint8_t *)src;
|
||||
while (n--) {
|
||||
*d++ = *s++;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,42 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_attr.h"
|
||||
|
||||
#ifdef ESP32_FORCE_IRAM_MEMSET
|
||||
|
||||
/*
|
||||
* T-Beam/classic ESP32 boot workaround
|
||||
* ------------------------------------
|
||||
* During early flash operations the ESP32 disables cache, but some IRAM flash
|
||||
* code paths still reach libc memcpy/memset. If those resolve to flash-resident
|
||||
* implementations, startup can panic with cache-disabled access errors.
|
||||
*
|
||||
* We wrap memcpy/memset for the T-Beam environment. Fast path uses the
|
||||
* normal libc routines when cache is enabled; slow path uses IRAM-safe byte
|
||||
* loops when cache is disabled.
|
||||
*/
|
||||
|
||||
extern void *__real_memset(void *dst, int c, size_t n);
|
||||
|
||||
static inline bool IRAM_ATTR cache_is_enabled(void)
|
||||
{
|
||||
return (*(volatile uint32_t *)0x3FF00040u) & (1u << 3);
|
||||
}
|
||||
|
||||
extern void *IRAM_ATTR __wrap_memset(void *dst, int c, size_t n)
|
||||
{
|
||||
if (cache_is_enabled()) {
|
||||
return __real_memset(dst, c, n);
|
||||
}
|
||||
|
||||
uint8_t *ptr = (uint8_t *)dst;
|
||||
const uint8_t fill = (uint8_t)c;
|
||||
while (n--) {
|
||||
*ptr++ = fill;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
/* Arduino fix: catch esp_event's orphaned .text.handler_execute section and align to 4 bytes */
|
||||
SECTIONS
|
||||
{
|
||||
.text.handler_execute ALIGN(4) :
|
||||
{
|
||||
KEEP(*(.text.handler_execute))
|
||||
KEEP(*(.text.handler_execute.*))
|
||||
. = ALIGN(4);
|
||||
}
|
||||
}
|
||||
INSERT AFTER .flash.text;
|
||||
@@ -165,30 +165,17 @@ void esp32Setup()
|
||||
// #define APP_WATCHDOG_SECS 45
|
||||
#define APP_WATCHDOG_SECS 90
|
||||
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
const esp_task_wdt_config_t wdt_config = {
|
||||
.timeout_ms = APP_WATCHDOG_SECS * 1000,
|
||||
.idle_core_mask = (1U << CONFIG_FREERTOS_NUMBER_OF_CORES) - 1U,
|
||||
.trigger_panic = true,
|
||||
};
|
||||
res = esp_task_wdt_init(&wdt_config);
|
||||
if (res == ESP_ERR_INVALID_STATE) {
|
||||
LOG_WARN("Task watchdog already initialized, reconfiguring existing instance");
|
||||
res = esp_task_wdt_reconfigure(&wdt_config);
|
||||
}
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C6
|
||||
esp_task_wdt_config_t *wdt_config = (esp_task_wdt_config_t *)malloc(sizeof(esp_task_wdt_config_t));
|
||||
wdt_config->timeout_ms = APP_WATCHDOG_SECS * 1000;
|
||||
wdt_config->trigger_panic = true;
|
||||
res = esp_task_wdt_init(wdt_config);
|
||||
assert(res == ESP_OK);
|
||||
#else
|
||||
res = esp_task_wdt_init(APP_WATCHDOG_SECS, true);
|
||||
if (res == ESP_ERR_INVALID_STATE) {
|
||||
LOG_WARN("Task watchdog already initialized, reusing existing instance");
|
||||
res = ESP_OK;
|
||||
}
|
||||
assert(res == ESP_OK);
|
||||
#endif
|
||||
res = esp_task_wdt_status(NULL);
|
||||
if (res == ESP_ERR_NOT_FOUND) {
|
||||
res = esp_task_wdt_add(NULL);
|
||||
}
|
||||
res = esp_task_wdt_add(NULL);
|
||||
assert(res == ESP_OK);
|
||||
|
||||
#if HAS_32768HZ
|
||||
@@ -271,10 +258,8 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
#endif
|
||||
variant_shutdown();
|
||||
|
||||
#if SOC_PM_SUPPORT_RTC_PERIPH_PD
|
||||
// We want RTC peripherals to stay on
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
#endif
|
||||
|
||||
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
|
||||
esp_deep_sleep_start(); // TBD mA sleep current (battery)
|
||||
|
||||
@@ -598,7 +598,8 @@ void portduinoSetup()
|
||||
for (const auto *i : portduino_config.all_pins) {
|
||||
// In the case of a ch341 Lora device, we don't want to touch the system GPIO lines for Lora
|
||||
// Those GPIO are handled in our usermode driver instead.
|
||||
if (i->config_section == "Lora" && portduino_config.lora_spi_dev == "ch341") {
|
||||
if (i->config_section == "Lora" &&
|
||||
(portduino_config.lora_spi_dev == "ch341" || portduino_config.lora_spi_dev == "serial")) {
|
||||
continue;
|
||||
}
|
||||
if (i->enabled) {
|
||||
@@ -617,7 +618,8 @@ void portduinoSetup()
|
||||
for (auto i : portduino_config.extra_pins) {
|
||||
// In the case of a ch341 Lora device, we don't want to touch the system GPIO lines for Lora
|
||||
// Those GPIO are handled in our usermode driver instead.
|
||||
if (i.config_section == "Lora" && portduino_config.lora_spi_dev == "ch341") {
|
||||
if (i.config_section == "Lora" &&
|
||||
(portduino_config.lora_spi_dev == "ch341" || portduino_config.lora_spi_dev == "serial")) {
|
||||
continue;
|
||||
}
|
||||
if (i.enabled) {
|
||||
@@ -664,7 +666,8 @@ void portduinoSetup()
|
||||
for (auto i : portduino_config.extra_pins) {
|
||||
// In the case of a ch341 Lora device, we don't want to touch the system GPIO lines for Lora
|
||||
// Those GPIO are handled in our usermode driver instead.
|
||||
if (i.config_section == "Lora" && portduino_config.lora_spi_dev == "ch341") {
|
||||
if (i.config_section == "Lora" &&
|
||||
(portduino_config.lora_spi_dev == "ch341" || portduino_config.lora_spi_dev == "serial")) {
|
||||
continue;
|
||||
}
|
||||
if (i.enabled && i.default_high) {
|
||||
@@ -674,7 +677,8 @@ void portduinoSetup()
|
||||
}
|
||||
|
||||
// Only initialize the radio pins when dealing with real, kernel controlled SPI hardware
|
||||
if (portduino_config.lora_spi_dev != "" && portduino_config.lora_spi_dev != "ch341") {
|
||||
if (portduino_config.lora_spi_dev != "" && portduino_config.lora_spi_dev != "ch341" &&
|
||||
portduino_config.lora_spi_dev != "serial") {
|
||||
SPI.begin(portduino_config.lora_spi_dev.c_str());
|
||||
}
|
||||
|
||||
@@ -708,6 +712,7 @@ void portduinoSetup()
|
||||
}
|
||||
if (portduino_config.lora_spi_dev != "") {
|
||||
portduinoSetOptions({.realHardware = true});
|
||||
LOG_DEBUG("Running with real hardware SPI device %s", portduino_config.lora_spi_dev.c_str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -844,12 +849,15 @@ bool loadConfig(const char *configPath)
|
||||
}
|
||||
|
||||
portduino_config.spiSpeed = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000);
|
||||
portduino_config.lora_serial_device = yamlConfig["Lora"]["SerialDevice"].as<std::string>("");
|
||||
portduino_config.lora_serial_baud = yamlConfig["Lora"]["SerialBaud"].as<int>(115200);
|
||||
portduino_config.lora_serial_timeout_ms = yamlConfig["Lora"]["SerialTimeoutMs"].as<int>(500);
|
||||
portduino_config.lora_usb_serial_num = yamlConfig["Lora"]["USB_Serialnum"].as<std::string>("");
|
||||
portduino_config.lora_usb_pid = yamlConfig["Lora"]["USB_PID"].as<int>(0x5512);
|
||||
portduino_config.lora_usb_vid = yamlConfig["Lora"]["USB_VID"].as<int>(0x1A86);
|
||||
|
||||
portduino_config.lora_spi_dev = yamlConfig["Lora"]["spidev"].as<std::string>("spidev0.0");
|
||||
if (portduino_config.lora_spi_dev != "ch341") {
|
||||
if (portduino_config.lora_spi_dev != "ch341" && portduino_config.lora_spi_dev != "serial") {
|
||||
portduino_config.lora_spi_dev = "/dev/" + portduino_config.lora_spi_dev;
|
||||
if (portduino_config.lora_spi_dev.length() == 14) {
|
||||
int x = portduino_config.lora_spi_dev.at(11) - '0';
|
||||
|
||||
@@ -86,6 +86,9 @@ extern struct portduino_config_struct {
|
||||
bool has_device_id = false;
|
||||
uint8_t device_id[16] = {0};
|
||||
std::string lora_spi_dev = "";
|
||||
std::string lora_serial_device = "";
|
||||
int lora_serial_baud = 115200;
|
||||
int lora_serial_timeout_ms = 500;
|
||||
std::string lora_usb_serial_num = "";
|
||||
int lora_spi_dev_int = 0;
|
||||
int lora_default_gpiochip = 0;
|
||||
@@ -276,6 +279,12 @@ extern struct portduino_config_struct {
|
||||
}
|
||||
if (lora_usb_serial_num != "")
|
||||
out << YAML::Key << "USB_Serialnum" << YAML::Value << lora_usb_serial_num;
|
||||
if (lora_serial_device != "")
|
||||
out << YAML::Key << "SerialDevice" << YAML::Value << lora_serial_device;
|
||||
if (lora_serial_baud != 115200)
|
||||
out << YAML::Key << "SerialBaud" << YAML::Value << lora_serial_baud;
|
||||
if (lora_serial_timeout_ms != 500)
|
||||
out << YAML::Key << "SerialTimeoutMs" << YAML::Value << lora_serial_timeout_ms;
|
||||
if (spiSpeed != 2000000)
|
||||
out << YAML::Key << "spiSpeed" << YAML::Value << spiSpeed;
|
||||
if (rfswitch_dio_pins[0] != RADIOLIB_NC) {
|
||||
|
||||
@@ -0,0 +1,595 @@
|
||||
#include "platform/portduino/SerialHal.h"
|
||||
|
||||
#include "mesh/mesh-pb-constants.h"
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <sched.h>
|
||||
#include <sys/time.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr uint8_t START1 = 0x94;
|
||||
constexpr uint8_t SERIALHAL_MAGIC = 0xA5;
|
||||
constexpr size_t HEADER_SIZE = 4; // START1 + SERIALHAL_MAGIC + LEN_H + LEN_L
|
||||
constexpr uint8_t START2 = 0xC3; // second byte of a normal FromRadio frame
|
||||
|
||||
speed_t toTermiosBaud(uint32_t baud)
|
||||
{
|
||||
switch (baud) {
|
||||
case 9600:
|
||||
return B9600;
|
||||
case 19200:
|
||||
return B19200;
|
||||
case 38400:
|
||||
return B38400;
|
||||
case 57600:
|
||||
return B57600;
|
||||
case 115200:
|
||||
return B115200;
|
||||
case 230400:
|
||||
return B230400;
|
||||
case 460800:
|
||||
return B460800;
|
||||
case 921600:
|
||||
return B921600;
|
||||
default:
|
||||
return B115200;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SerialHal::SerialHal(const std::string &devicePath, uint32_t baudRate, uint32_t opTimeoutMs)
|
||||
: RadioLibHal(SERIAL_PI_INPUT, SERIAL_PI_OUTPUT, SERIAL_PI_LOW, SERIAL_PI_HIGH, SERIAL_PI_RISING, SERIAL_PI_FALLING),
|
||||
device(devicePath), baud(baudRate), timeoutMs(opTimeoutMs)
|
||||
{
|
||||
if (!openPort()) {
|
||||
setTransportError("unable to open serial device");
|
||||
}
|
||||
}
|
||||
|
||||
SerialHal::~SerialHal()
|
||||
{
|
||||
closePort();
|
||||
}
|
||||
|
||||
bool SerialHal::openPort()
|
||||
{
|
||||
closePort();
|
||||
fd = ::open(device.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
termios tty = {};
|
||||
if (tcgetattr(fd, &tty) != 0) {
|
||||
closePort();
|
||||
return false;
|
||||
}
|
||||
|
||||
cfsetospeed(&tty, toTermiosBaud(baud));
|
||||
cfsetispeed(&tty, toTermiosBaud(baud));
|
||||
|
||||
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
|
||||
tty.c_iflag &= ~(IGNBRK | IXON | IXOFF | IXANY);
|
||||
tty.c_lflag = 0;
|
||||
tty.c_oflag = 0;
|
||||
tty.c_cc[VMIN] = 0;
|
||||
tty.c_cc[VTIME] = 0;
|
||||
tty.c_cflag |= (CLOCAL | CREAD);
|
||||
tty.c_cflag &= ~(PARENB | PARODD);
|
||||
tty.c_cflag &= ~CSTOPB;
|
||||
tty.c_cflag &= ~CRTSCTS;
|
||||
|
||||
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
|
||||
closePort();
|
||||
return false;
|
||||
}
|
||||
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
inError = false;
|
||||
startReaderThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SerialHal::closePort()
|
||||
{
|
||||
stopReaderThread();
|
||||
if (fd >= 0) {
|
||||
::close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void SerialHal::setTransportError(const char *msg)
|
||||
{
|
||||
if (!inError.load() || !hasWarned) {
|
||||
LOG_ERROR("SerialHal: %s (%s)", msg, device.c_str());
|
||||
}
|
||||
inError = true;
|
||||
hasWarned = true;
|
||||
portduino_status.LoRa_in_error = true;
|
||||
}
|
||||
|
||||
bool SerialHal::waitForReadable(int timeout)
|
||||
{
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
pollfd pfd = {};
|
||||
pfd.fd = fd;
|
||||
pfd.events = POLLIN;
|
||||
int ret = poll(&pfd, 1, timeout);
|
||||
return ret > 0 && (pfd.revents & POLLIN);
|
||||
}
|
||||
|
||||
bool SerialHal::writeAll(const uint8_t *data, size_t len)
|
||||
{
|
||||
size_t off = 0;
|
||||
while (off < len) {
|
||||
ssize_t rc = ::write(fd, data + off, len - off);
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
off += (size_t)rc;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SerialHal::readExact(uint8_t *data, size_t len)
|
||||
{
|
||||
size_t off = 0;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (off < len) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
int elapsed = (int)std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count();
|
||||
int remaining = (int)timeoutMs - elapsed;
|
||||
if (remaining <= 0 || !waitForReadable(remaining)) {
|
||||
return false;
|
||||
}
|
||||
ssize_t rc = ::read(fd, data + off, len - off);
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (rc == 0) {
|
||||
return false;
|
||||
}
|
||||
off += (size_t)rc;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t SerialHal::crc16(const uint8_t *data, size_t len) const
|
||||
{
|
||||
uint16_t crc = 0xFFFF;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
crc ^= ((uint16_t)data[i] << 8);
|
||||
for (int bit = 0; bit < 8; ++bit) {
|
||||
if (crc & 0x8000) {
|
||||
crc = (uint16_t)((crc << 1) ^ 0x1021);
|
||||
} else {
|
||||
crc = (uint16_t)(crc << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool SerialHal::sendRequest(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse *response)
|
||||
{
|
||||
if (fd < 0 && !openPort()) {
|
||||
setTransportError("serial open failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t encoded[meshtastic_SerialHalCommand_size] = {0};
|
||||
const size_t payloadLen =
|
||||
pb_encode_to_bytes(encoded, sizeof(encoded), &meshtastic_SerialHalCommand_msg, static_cast<const void *>(&cmd));
|
||||
if (payloadLen == 0 || payloadLen > 0xFFFF) {
|
||||
setTransportError("serial command encode failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build frame with StreamAPI canonical framing: START1 SERIALHAL_MAGIC LEN_H LEN_L [payload]
|
||||
std::vector<uint8_t> frame;
|
||||
frame.resize(HEADER_SIZE + payloadLen);
|
||||
|
||||
frame[0] = START1;
|
||||
frame[1] = SERIALHAL_MAGIC;
|
||||
frame[2] = (uint8_t)((payloadLen >> 8) & 0xFF); // LEN_H (big-endian)
|
||||
frame[3] = (uint8_t)(payloadLen & 0xFF); // LEN_L
|
||||
memcpy(frame.data() + HEADER_SIZE, encoded, payloadLen);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> writeGuard(writeMutex);
|
||||
if (!writeAll(frame.data(), frame.size())) {
|
||||
setTransportError("serial write failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
meshtastic_SerialHalResponse got = meshtastic_SerialHalResponse_init_zero;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(stateMutex);
|
||||
const auto timeout = std::chrono::milliseconds(timeoutMs);
|
||||
const bool arrived = responseCv.wait_for(lock, timeout, [&]() { return pendingResponses.count(cmd.transaction_id) > 0; });
|
||||
if (!arrived) {
|
||||
setTransportError("serial response timeout");
|
||||
LOG_WARN("SerialHal: response timeout for transaction_id %u, cmd type %u", cmd.transaction_id, cmd.type);
|
||||
return false;
|
||||
}
|
||||
|
||||
got = pendingResponses[cmd.transaction_id];
|
||||
pendingResponses.erase(cmd.transaction_id);
|
||||
}
|
||||
|
||||
if (got.result != meshtastic_SerialHalResponse_Result_OK) {
|
||||
setTransportError("serial response reported error");
|
||||
LOG_WARN("SerialHal: response error: %s, %u, %u", got.error, cmd.type, cmd.data.size);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response != nullptr) {
|
||||
*response = got;
|
||||
}
|
||||
|
||||
inError = false;
|
||||
hasWarned = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SerialHal::pinMode(uint32_t pin, uint32_t mode)
|
||||
{
|
||||
if (checkError() || pin == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
meshtastic_SerialHalCommand cmd = meshtastic_SerialHalCommand_init_zero;
|
||||
cmd.transaction_id = txId.fetch_add(1);
|
||||
cmd.type = meshtastic_SerialHalCommand_Type_PIN_MODE;
|
||||
cmd.pin = pin;
|
||||
cmd.mode = mode;
|
||||
|
||||
meshtastic_SerialHalResponse response = meshtastic_SerialHalResponse_init_zero;
|
||||
sendRequest(cmd, &response);
|
||||
}
|
||||
|
||||
void SerialHal::digitalWrite(uint32_t pin, uint32_t value)
|
||||
{
|
||||
if (checkError() || pin == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
meshtastic_SerialHalCommand cmd = meshtastic_SerialHalCommand_init_zero;
|
||||
cmd.transaction_id = txId.fetch_add(1);
|
||||
cmd.type = meshtastic_SerialHalCommand_Type_DIGITAL_WRITE;
|
||||
cmd.pin = pin;
|
||||
cmd.value = value;
|
||||
|
||||
meshtastic_SerialHalResponse response = meshtastic_SerialHalResponse_init_zero;
|
||||
sendRequest(cmd, &response);
|
||||
}
|
||||
|
||||
uint32_t SerialHal::digitalRead(uint32_t pin)
|
||||
{
|
||||
if (checkError() || pin == RADIOLIB_NC) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
meshtastic_SerialHalCommand cmd = meshtastic_SerialHalCommand_init_zero;
|
||||
cmd.transaction_id = txId.fetch_add(1);
|
||||
cmd.type = meshtastic_SerialHalCommand_Type_DIGITAL_READ;
|
||||
cmd.pin = pin;
|
||||
|
||||
meshtastic_SerialHalResponse response = meshtastic_SerialHalResponse_init_zero;
|
||||
if (!sendRequest(cmd, &response)) {
|
||||
return 0;
|
||||
}
|
||||
return response.value;
|
||||
}
|
||||
|
||||
void SerialHal::attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode)
|
||||
{
|
||||
if (checkError() || interruptNum == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(stateMutex);
|
||||
interruptCallbacks[interruptNum] = interruptCb;
|
||||
}
|
||||
|
||||
meshtastic_SerialHalCommand cmd = meshtastic_SerialHalCommand_init_zero;
|
||||
cmd.transaction_id = txId.fetch_add(1);
|
||||
cmd.type = meshtastic_SerialHalCommand_Type_ATTACH_INTERRUPT;
|
||||
cmd.pin = interruptNum;
|
||||
cmd.mode = mode;
|
||||
|
||||
meshtastic_SerialHalResponse response = meshtastic_SerialHalResponse_init_zero;
|
||||
sendRequest(cmd, &response);
|
||||
}
|
||||
|
||||
void SerialHal::detachInterrupt(uint32_t interruptNum)
|
||||
{
|
||||
if (checkError() || interruptNum == RADIOLIB_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(stateMutex);
|
||||
interruptCallbacks.erase(interruptNum);
|
||||
}
|
||||
|
||||
meshtastic_SerialHalCommand cmd = meshtastic_SerialHalCommand_init_zero;
|
||||
cmd.transaction_id = txId.fetch_add(1);
|
||||
cmd.type = meshtastic_SerialHalCommand_Type_DETACH_INTERRUPT;
|
||||
cmd.pin = interruptNum;
|
||||
|
||||
meshtastic_SerialHalResponse response = meshtastic_SerialHalResponse_init_zero;
|
||||
sendRequest(cmd, &response);
|
||||
}
|
||||
|
||||
void SerialHal::delay(unsigned long ms)
|
||||
{
|
||||
delayMicroseconds(ms * 1000);
|
||||
}
|
||||
|
||||
void SerialHal::delayMicroseconds(unsigned long us)
|
||||
{
|
||||
if (us == 0) {
|
||||
sched_yield();
|
||||
return;
|
||||
}
|
||||
usleep(us);
|
||||
}
|
||||
|
||||
void SerialHal::yield()
|
||||
{
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
unsigned long SerialHal::millis()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000ULL);
|
||||
}
|
||||
|
||||
unsigned long SerialHal::micros()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
|
||||
}
|
||||
|
||||
long SerialHal::pulseIn(uint32_t pin, uint32_t state, unsigned long timeout)
|
||||
{
|
||||
(void)pin;
|
||||
(void)state;
|
||||
(void)timeout;
|
||||
LOG_WARN("SerialHal pulseIn is not supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SerialHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in)
|
||||
{
|
||||
if (checkError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
meshtastic_SerialHalCommand cmd = meshtastic_SerialHalCommand_init_zero;
|
||||
cmd.transaction_id = txId.fetch_add(1);
|
||||
cmd.type = meshtastic_SerialHalCommand_Type_SPI_TRANSFER;
|
||||
|
||||
const size_t maxTx = sizeof(cmd.data.bytes);
|
||||
const size_t txLen = len < maxTx ? len : maxTx;
|
||||
cmd.data.size = txLen;
|
||||
if (out != nullptr) {
|
||||
memcpy(cmd.data.bytes, out, txLen);
|
||||
} else {
|
||||
memset(cmd.data.bytes, 0, txLen);
|
||||
}
|
||||
|
||||
meshtastic_SerialHalResponse response = meshtastic_SerialHalResponse_init_zero;
|
||||
if (!sendRequest(cmd, &response)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (in != nullptr) {
|
||||
size_t copyLen = response.data.size < len ? response.data.size : len;
|
||||
memcpy(in, response.data.bytes, copyLen);
|
||||
if (copyLen < len) {
|
||||
memset(in + copyLen, 0, len - copyLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SerialHal::checkError()
|
||||
{
|
||||
if (inError.load()) {
|
||||
if (!hasWarned) {
|
||||
LOG_ERROR("SerialHal in_error detected");
|
||||
hasWarned = true;
|
||||
}
|
||||
portduino_status.LoRa_in_error = true;
|
||||
return true;
|
||||
}
|
||||
hasWarned = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SerialHal::readFrame(std::vector<uint8_t> &payload, int firstByteTimeoutMs)
|
||||
{
|
||||
payload.clear();
|
||||
|
||||
// Loop so that normal FromRadio frames (START1 START2 ...) emitted by the
|
||||
// device on the same serial port are drained and discarded rather than
|
||||
// causing the byte stream to desync.
|
||||
for (;;) {
|
||||
uint8_t hdr[HEADER_SIZE] = {0};
|
||||
for (;;) {
|
||||
|
||||
ssize_t rc = ::read(fd, &hdr[0], 1);
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (rc == 0) {
|
||||
return false;
|
||||
}
|
||||
if (hdr[0] == START1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!readExact(hdr + 1, HEADER_SIZE - 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t len = ((uint16_t)hdr[2] << 8) | (uint16_t)hdr[3];
|
||||
|
||||
if (hdr[1] == SERIALHAL_MAGIC) {
|
||||
// SerialHal response frame — this is what we want.
|
||||
if (len > meshtastic_SerialHalResponse_size) {
|
||||
return false;
|
||||
}
|
||||
payload.resize(len);
|
||||
if (len > 0 && !readExact(payload.data(), len)) {
|
||||
payload.clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (hdr[1] == START2) {
|
||||
// Normal FromRadio frame emitted by the device — drain and discard
|
||||
// its payload so we stay in sync, then loop to find a SerialHal frame.
|
||||
if (len > 0) {
|
||||
std::vector<uint8_t> discard(len);
|
||||
if (!readExact(discard.data(), len)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// continue looping, look for next frame
|
||||
} else {
|
||||
// Unknown second byte after START1 — restart search for framing.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SerialHal::readerLoop()
|
||||
{
|
||||
readerRunning = true;
|
||||
while (!readerStopRequested.load()) {
|
||||
if (fd < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!waitForReadable(100)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> payload;
|
||||
if (!readFrame(payload, 40)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
meshtastic_SerialHalResponse resp = meshtastic_SerialHalResponse_init_zero;
|
||||
if (payload.empty() || !pb_decode_from_bytes(payload.data(), payload.size(), &meshtastic_SerialHalResponse_msg, &resp)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resp.transaction_id == 0) {
|
||||
LOG_WARN("SerialHal: received unsolicited interrupt event: pin=%u", resp.value);
|
||||
// transaction_id 0 is reserved for unsolicited interrupt events.
|
||||
// The device reports the triggered pin in resp.value instead of
|
||||
// matching one of the synchronous request/response transactions.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(stateMutex);
|
||||
if (interruptCallbacks.count(resp.value) > 0) {
|
||||
pendingInterruptPins.push_back(resp.value);
|
||||
}
|
||||
}
|
||||
interruptCv.notify_one();
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(stateMutex);
|
||||
pendingResponses[resp.transaction_id] = resp;
|
||||
}
|
||||
responseCv.notify_all();
|
||||
}
|
||||
readerRunning = false;
|
||||
}
|
||||
|
||||
void SerialHal::interruptDispatchLoop()
|
||||
{
|
||||
interruptDispatcherRunning = true;
|
||||
while (!readerStopRequested.load()) {
|
||||
uint32_t pin = 0;
|
||||
void (*cb)(void) = nullptr;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(stateMutex);
|
||||
interruptCv.wait(lock, [&]() { return readerStopRequested.load() || !pendingInterruptPins.empty(); });
|
||||
if (readerStopRequested.load()) {
|
||||
break;
|
||||
}
|
||||
|
||||
pin = pendingInterruptPins.front();
|
||||
pendingInterruptPins.pop_front();
|
||||
|
||||
auto it = interruptCallbacks.find(pin);
|
||||
if (it != interruptCallbacks.end()) {
|
||||
cb = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (cb != nullptr) {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
interruptDispatcherRunning = false;
|
||||
}
|
||||
|
||||
void SerialHal::startReaderThread()
|
||||
{
|
||||
stopReaderThread();
|
||||
readerStopRequested = false;
|
||||
readerThread = std::thread(&SerialHal::readerLoop, this);
|
||||
interruptThread = std::thread(&SerialHal::interruptDispatchLoop, this);
|
||||
}
|
||||
|
||||
void SerialHal::stopReaderThread()
|
||||
{
|
||||
readerStopRequested = true;
|
||||
interruptCv.notify_all();
|
||||
if (readerThread.joinable()) {
|
||||
readerThread.join();
|
||||
}
|
||||
if (interruptThread.joinable()) {
|
||||
interruptThread.join();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(stateMutex);
|
||||
pendingInterruptPins.clear();
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifndef PI_HAL_SERIAL_H
|
||||
#define PI_HAL_SERIAL_H
|
||||
|
||||
#include <RadioLib.h>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <mesh/generated/meshtastic/serial_hal.pb.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#define SERIAL_PI_INPUT (0)
|
||||
#define SERIAL_PI_OUTPUT (1)
|
||||
#define SERIAL_PI_LOW (0)
|
||||
#define SERIAL_PI_HIGH (1)
|
||||
#define SERIAL_PI_RISING (1)
|
||||
#define SERIAL_PI_FALLING (2)
|
||||
|
||||
class SerialHal : public RadioLibHal
|
||||
{
|
||||
public:
|
||||
explicit SerialHal(const std::string &device, uint32_t baud = 115200, uint32_t timeoutMs = 500);
|
||||
~SerialHal() override;
|
||||
|
||||
void init() override {}
|
||||
void term() override {}
|
||||
|
||||
void pinMode(uint32_t pin, uint32_t mode) override;
|
||||
void digitalWrite(uint32_t pin, uint32_t value) override;
|
||||
uint32_t digitalRead(uint32_t pin) override;
|
||||
|
||||
void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override;
|
||||
void detachInterrupt(uint32_t interruptNum) override;
|
||||
|
||||
void delay(unsigned long ms) override;
|
||||
void delayMicroseconds(unsigned long us) override;
|
||||
void yield() override;
|
||||
|
||||
unsigned long millis() override;
|
||||
unsigned long micros() override;
|
||||
|
||||
long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override;
|
||||
|
||||
void spiBegin() override {}
|
||||
void spiBeginTransaction() override {}
|
||||
void spiTransfer(uint8_t *out, size_t len, uint8_t *in) override;
|
||||
void spiEndTransaction() override {}
|
||||
void spiEnd() override {}
|
||||
|
||||
bool checkError();
|
||||
|
||||
private:
|
||||
bool openPort();
|
||||
void closePort();
|
||||
bool sendRequest(const meshtastic_SerialHalCommand &cmd, meshtastic_SerialHalResponse *response);
|
||||
bool writeAll(const uint8_t *data, size_t len);
|
||||
bool readExact(uint8_t *data, size_t len);
|
||||
bool waitForReadable(int timeoutMs);
|
||||
bool readFrame(std::vector<uint8_t> &payload, int firstByteTimeoutMs);
|
||||
void readerLoop();
|
||||
void interruptDispatchLoop();
|
||||
void startReaderThread();
|
||||
void stopReaderThread();
|
||||
|
||||
uint16_t crc16(const uint8_t *data, size_t len) const;
|
||||
void setTransportError(const char *msg);
|
||||
|
||||
std::string device;
|
||||
uint32_t baud;
|
||||
uint32_t timeoutMs;
|
||||
int fd = -1;
|
||||
bool hasWarned = false;
|
||||
std::atomic<bool> inError{false};
|
||||
std::atomic<uint16_t> txId{1};
|
||||
|
||||
std::mutex fdMutex;
|
||||
std::mutex writeMutex;
|
||||
std::mutex stateMutex;
|
||||
std::condition_variable responseCv;
|
||||
|
||||
std::thread readerThread;
|
||||
std::thread interruptThread;
|
||||
std::atomic<bool> readerStopRequested{false};
|
||||
std::atomic<bool> readerRunning{false};
|
||||
std::atomic<bool> interruptDispatcherRunning{false};
|
||||
|
||||
std::condition_variable interruptCv;
|
||||
std::deque<uint32_t> pendingInterruptPins;
|
||||
|
||||
std::unordered_map<uint32_t, void (*)(void)> interruptCallbacks;
|
||||
std::unordered_map<uint16_t, meshtastic_SerialHalResponse> pendingResponses;
|
||||
};
|
||||
|
||||
#endif
|
||||
+7
-4
@@ -4,10 +4,8 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// #include <driver/adc.h>
|
||||
#include <esp_adc/adc_cali.h>
|
||||
#include <esp_adc/adc_cali_scheme.h>
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
// "legacy adc calibration driver is deprecated, please migrate to use esp_adc/adc_cali.h and esp_adc/adc_cali_scheme.h
|
||||
#include <esp_adc_cal.h>
|
||||
#include <soc/adc_channel.h>
|
||||
#endif
|
||||
|
||||
@@ -30,6 +28,11 @@
|
||||
#define NUM_CELLS 1
|
||||
#endif
|
||||
|
||||
#ifdef BAT_MEASURE_ADC_UNIT
|
||||
extern RTC_NOINIT_ATTR uint64_t RTC_reg_b;
|
||||
#include "soc/sens_reg.h" // needed for adc pin reset
|
||||
#endif
|
||||
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
#include "modules/Telemetry/Sensor/nullSensor.h"
|
||||
#if __has_include(<Adafruit_INA219.h>)
|
||||
|
||||
@@ -1,693 +0,0 @@
|
||||
#include "power/BatteryLevel.h"
|
||||
|
||||
#include "NodeDB.h"
|
||||
#include "Throttle.h"
|
||||
#include "meshUtils.h"
|
||||
#include "power/PowerHAL.h"
|
||||
|
||||
#if defined(ARCH_NRF52)
|
||||
#include "Nrf52SaadcLock.h"
|
||||
#include "concurrency/LockGuard.h"
|
||||
#endif
|
||||
|
||||
#if defined(HAS_BQ27220)
|
||||
#include "bq27220.h"
|
||||
#endif
|
||||
|
||||
#if defined(HAS_PPM) && HAS_PPM
|
||||
#include <XPowersLib.h>
|
||||
#endif
|
||||
|
||||
#ifdef HELTEC_MESH_SOLAR
|
||||
#include "meshSolarApp.h"
|
||||
#endif
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_I2C
|
||||
#include <Wire.h>
|
||||
#include <utility>
|
||||
extern std::pair<uint8_t, TwoWire *> nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1];
|
||||
#endif
|
||||
|
||||
#ifndef ADC_MULTIPLIER
|
||||
#define ADC_MULTIPLIER 2.0
|
||||
#endif
|
||||
|
||||
#ifndef BATTERY_SENSE_SAMPLES
|
||||
#define BATTERY_SENSE_SAMPLES 15
|
||||
#endif
|
||||
|
||||
#if !defined(AREF_VOLTAGE) && !defined(ARCH_NRF52)
|
||||
#define AREF_VOLTAGE 3.3
|
||||
#endif
|
||||
|
||||
#if defined(BATTERY_PIN) && defined(ARCH_ESP32)
|
||||
|
||||
#ifndef BAT_MEASURE_ADC_UNIT
|
||||
static const adc_channel_t adc_channel = ADC_CHANNEL;
|
||||
static const adc_unit_t unit = ADC_UNIT_1;
|
||||
#else
|
||||
static const adc_channel_t adc_channel = ADC_CHANNEL;
|
||||
static const adc_unit_t unit = ADC_UNIT_2;
|
||||
#endif
|
||||
|
||||
static adc_oneshot_unit_handle_t adc_handle = nullptr;
|
||||
static adc_cali_handle_t adc_cali_handle = nullptr;
|
||||
static bool adc_calibrated = false;
|
||||
|
||||
#ifndef ADC_ATTENUATION
|
||||
static const adc_atten_t atten = ADC_ATTEN_DB_12;
|
||||
#else
|
||||
static const adc_atten_t atten = ADC_ATTENUATION;
|
||||
#endif
|
||||
|
||||
#ifdef ADC_BITWIDTH
|
||||
static const adc_bitwidth_t adc_width = ADC_BITWIDTH;
|
||||
#else
|
||||
static const adc_bitwidth_t adc_width = ADC_BITWIDTH_DEFAULT;
|
||||
#endif
|
||||
|
||||
static int adcBitWidthToBits(adc_bitwidth_t width)
|
||||
{
|
||||
switch (width) {
|
||||
case ADC_BITWIDTH_9:
|
||||
return 9;
|
||||
case ADC_BITWIDTH_10:
|
||||
return 10;
|
||||
case ADC_BITWIDTH_11:
|
||||
return 11;
|
||||
case ADC_BITWIDTH_12:
|
||||
return 12;
|
||||
#ifdef ADC_BITWIDTH_13
|
||||
case ADC_BITWIDTH_13:
|
||||
return 13;
|
||||
#endif
|
||||
default:
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
|
||||
static bool initAdcCalibration()
|
||||
{
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = adc_width,
|
||||
};
|
||||
esp_err_t ret = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle);
|
||||
if (ret == ESP_OK) {
|
||||
LOG_INFO("ADC calibration: curve fitting enabled");
|
||||
return true;
|
||||
}
|
||||
if (ret != ESP_ERR_NOT_SUPPORTED) {
|
||||
LOG_WARN("ADC calibration: curve fitting failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = adc_width,
|
||||
.default_vref = DEFAULT_VREF,
|
||||
};
|
||||
esp_err_t ret = adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle);
|
||||
if (ret == ESP_OK) {
|
||||
LOG_INFO("ADC calibration: line fitting enabled");
|
||||
return true;
|
||||
}
|
||||
if (ret != ESP_ERR_NOT_SUPPORTED) {
|
||||
LOG_WARN("ADC calibration: line fitting failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_INFO("ADC calibration not supported; using approximate scaling");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void battery_adcEnable()
|
||||
{
|
||||
#ifdef ADC_CTRL
|
||||
#ifdef ADC_USE_PULLUP
|
||||
pinMode(ADC_CTRL, INPUT_PULLUP);
|
||||
#else
|
||||
#ifdef HELTEC_V3
|
||||
pinMode(ADC_CTRL, INPUT);
|
||||
uint8_t adc_ctl_enable_value = !(digitalRead(ADC_CTRL));
|
||||
pinMode(ADC_CTRL, OUTPUT);
|
||||
digitalWrite(ADC_CTRL, adc_ctl_enable_value);
|
||||
#else
|
||||
pinMode(ADC_CTRL, OUTPUT);
|
||||
digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED);
|
||||
#endif
|
||||
#endif
|
||||
delay(10);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void battery_adcDisable()
|
||||
{
|
||||
#ifdef ADC_CTRL
|
||||
#ifdef ADC_USE_PULLUP
|
||||
pinMode(ADC_CTRL, INPUT_PULLDOWN);
|
||||
#else
|
||||
#ifdef HELTEC_V3
|
||||
pinMode(ADC_CTRL, ANALOG);
|
||||
#else
|
||||
digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool battery_adcInit()
|
||||
{
|
||||
#if defined(BATTERY_PIN) && defined(ARCH_ESP32)
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = unit,
|
||||
};
|
||||
|
||||
if (!adc_handle) {
|
||||
esp_err_t err = adc_oneshot_new_unit(&init_config, &adc_handle);
|
||||
if (err != ESP_OK) {
|
||||
LOG_ERROR("ADC oneshot init failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
adc_oneshot_chan_cfg_t chan_cfg = {
|
||||
.atten = atten,
|
||||
.bitwidth = adc_width,
|
||||
};
|
||||
|
||||
esp_err_t err = adc_oneshot_config_channel(adc_handle, adc_channel, &chan_cfg);
|
||||
if (err != ESP_OK) {
|
||||
LOG_ERROR("ADC channel config failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
adc_calibrated = initAdcCalibration();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
int AnalogBatteryLevel::getBatteryPercent()
|
||||
{
|
||||
#if defined(HAS_RAKPROT) && !defined(HAS_PMU)
|
||||
if (hasRAK()) {
|
||||
return rak9154Sensor.getBusBatteryPercent();
|
||||
}
|
||||
#endif
|
||||
|
||||
float v = getBattVoltage();
|
||||
if (v < noBatVolt)
|
||||
return -1;
|
||||
|
||||
#ifdef NO_BATTERY_LEVEL_ON_CHARGE
|
||||
if (v > chargingVolt)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
float battery_SOC = 0.0;
|
||||
uint16_t voltage = v / NUM_CELLS;
|
||||
for (int i = 0; i < NUM_OCV_POINTS; i++) {
|
||||
if (OCV[i] <= voltage) {
|
||||
if (i == 0) {
|
||||
battery_SOC = 100.0;
|
||||
} else {
|
||||
battery_SOC = (float)100.0 / (NUM_OCV_POINTS - 1.0) *
|
||||
(NUM_OCV_POINTS - 1.0 - i + ((float)voltage - OCV[i]) / (OCV[i - 1] - OCV[i]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if defined(BATTERY_CHARGING_INV)
|
||||
if (!digitalRead(BATTERY_CHARGING_INV) && battery_SOC > 99)
|
||||
battery_SOC = 99;
|
||||
#endif
|
||||
return clamp((int)(battery_SOC), 0, 100);
|
||||
}
|
||||
|
||||
uint16_t AnalogBatteryLevel::getBattVoltage()
|
||||
{
|
||||
#if HAS_TELEMETRY && defined(HAS_RAKPROT) && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
if (hasRAK()) {
|
||||
return getRAKVoltage();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_TELEMETRY && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
if (hasINA()) {
|
||||
return getINAVoltage();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BATTERY_PIN
|
||||
float operativeAdcMultiplier =
|
||||
config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER;
|
||||
const uint32_t min_read_interval = 5000;
|
||||
if (!initial_read_done || !Throttle::isWithinTimespanMs(last_read_time_ms, min_read_interval)) {
|
||||
last_read_time_ms = millis();
|
||||
|
||||
uint32_t raw = 0;
|
||||
float scaled = 0;
|
||||
|
||||
battery_adcEnable();
|
||||
#ifdef ARCH_STM32WL
|
||||
Vref = __LL_ADC_CALC_VREFANALOG_VOLTAGE(analogRead(AVREF), LL_ADC_RESOLUTION);
|
||||
raw = analogRead(BATTERY_PIN);
|
||||
scaled = __LL_ADC_CALC_DATA_TO_VOLTAGE(Vref, raw, LL_ADC_RESOLUTION);
|
||||
scaled *= operativeAdcMultiplier;
|
||||
#elif defined(ARCH_ESP32)
|
||||
raw = espAdcRead();
|
||||
int voltage_mv = 0;
|
||||
if (adc_calibrated && adc_cali_handle) {
|
||||
if (adc_cali_raw_to_voltage(adc_cali_handle, raw, &voltage_mv) != ESP_OK) {
|
||||
LOG_WARN("ADC calibration read failed; using raw value");
|
||||
voltage_mv = 0;
|
||||
}
|
||||
}
|
||||
if (voltage_mv == 0) {
|
||||
const int bits = adcBitWidthToBits(adc_width);
|
||||
const float max_code = powf(2.0f, bits) - 1.0f;
|
||||
voltage_mv = (int)((raw / max_code) * DEFAULT_VREF);
|
||||
}
|
||||
scaled = voltage_mv * operativeAdcMultiplier;
|
||||
#else
|
||||
#ifdef ARCH_NRF52
|
||||
concurrency::LockGuard saadcGuard(concurrency::nrf52SaadcLock);
|
||||
#endif
|
||||
for (uint32_t i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
raw += analogRead(BATTERY_PIN);
|
||||
}
|
||||
raw = raw / BATTERY_SENSE_SAMPLES;
|
||||
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
|
||||
#endif
|
||||
battery_adcDisable();
|
||||
|
||||
if (!initial_read_done) {
|
||||
if (scaled > last_read_value)
|
||||
last_read_value = scaled;
|
||||
initial_read_done = true;
|
||||
} else {
|
||||
last_read_value += (scaled - last_read_value) * 0.5;
|
||||
}
|
||||
}
|
||||
return last_read_value;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(ARCH_ESP32) && !defined(HAS_PMU) && defined(BATTERY_PIN)
|
||||
uint32_t AnalogBatteryLevel::espAdcRead()
|
||||
{
|
||||
uint32_t raw = 0;
|
||||
uint8_t raw_c = 0;
|
||||
|
||||
if (!adc_handle) {
|
||||
LOG_ERROR("ADC oneshot handle not initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
int val = 0;
|
||||
esp_err_t err = adc_oneshot_read(adc_handle, adc_channel, &val);
|
||||
if (err == ESP_OK) {
|
||||
raw += val;
|
||||
raw_c++;
|
||||
} else {
|
||||
LOG_DEBUG("ADC read failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
return (raw / (raw_c < 1 ? 1 : raw_c));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool AnalogBatteryLevel::isBatteryConnect()
|
||||
{
|
||||
#ifdef BATTERY_IMMUTABLE
|
||||
return true;
|
||||
#elif defined(ADC_V)
|
||||
int lastReading = digitalRead(ADC_V);
|
||||
for (int i = 2; i < 500; i++) {
|
||||
int reading = digitalRead(ADC_V);
|
||||
if (reading != lastReading) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return getBatteryPercent() != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AnalogBatteryLevel::isVbusIn()
|
||||
{
|
||||
#ifdef EXT_PWR_DETECT
|
||||
return digitalRead(EXT_PWR_DETECT) == EXT_PWR_DETECT_VALUE;
|
||||
#elif defined(MUZI_BASE) || defined(PROMICRO_DIY_TCXO)
|
||||
return powerHAL_isVBUSConnected();
|
||||
#endif
|
||||
return getBattVoltage() > chargingVolt;
|
||||
}
|
||||
|
||||
bool AnalogBatteryLevel::isCharging()
|
||||
{
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) && !defined(HAS_PMU)
|
||||
if (hasRAK()) {
|
||||
return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse;
|
||||
}
|
||||
#endif
|
||||
#if defined(ELECROW_ThinkNode_M6)
|
||||
return digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE || isVbusIn();
|
||||
#elif defined(EXT_CHRG_DETECT)
|
||||
return digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE;
|
||||
#elif defined(BATTERY_CHARGING_INV)
|
||||
return !digitalRead(BATTERY_CHARGING_INV);
|
||||
#else
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
|
||||
if (hasINA()) {
|
||||
LOG_DEBUG("Using INA on I2C addr 0x%x for charging detection", config.power.device_battery_ina_address);
|
||||
#if defined(INA_CHARGING_DETECTION_INVERT)
|
||||
return getINACurrent() > 0;
|
||||
#else
|
||||
return getINACurrent() < 0;
|
||||
#endif
|
||||
}
|
||||
return isBatteryConnect() && isVbusIn();
|
||||
#endif
|
||||
#endif
|
||||
return isVbusIn();
|
||||
}
|
||||
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT)
|
||||
uint16_t AnalogBatteryLevel::getRAKVoltage()
|
||||
{
|
||||
return rak9154Sensor.getBusVoltageMv();
|
||||
}
|
||||
|
||||
bool AnalogBatteryLevel::hasRAK()
|
||||
{
|
||||
if (!rak9154Sensor.isInitialized())
|
||||
return rak9154Sensor.runOnce() > 0;
|
||||
return rak9154Sensor.isRunning();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
uint16_t AnalogBatteryLevel::getINAVoltage()
|
||||
{
|
||||
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
|
||||
return ina219Sensor.getBusVoltageMv();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == config.power.device_battery_ina_address) {
|
||||
return ina226Sensor.getBusVoltageMv();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == config.power.device_battery_ina_address) {
|
||||
return ina260Sensor.getBusVoltageMv();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == config.power.device_battery_ina_address) {
|
||||
return ina3221Sensor.getBusVoltageMv();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t AnalogBatteryLevel::getINACurrent()
|
||||
{
|
||||
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
|
||||
return ina219Sensor.getCurrentMa();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == config.power.device_battery_ina_address) {
|
||||
return ina226Sensor.getCurrentMa();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == config.power.device_battery_ina_address) {
|
||||
return ina3221Sensor.getCurrentMa();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AnalogBatteryLevel::hasINA()
|
||||
{
|
||||
if (!config.power.device_battery_ina_address) {
|
||||
return false;
|
||||
}
|
||||
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
|
||||
if (!ina219Sensor.isInitialized())
|
||||
return ina219Sensor.runOnce() > 0;
|
||||
return ina219Sensor.isRunning();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA226].first == config.power.device_battery_ina_address) {
|
||||
if (!ina226Sensor.isInitialized())
|
||||
return ina226Sensor.runOnce() > 0;
|
||||
return ina226Sensor.isRunning();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first == config.power.device_battery_ina_address) {
|
||||
if (!ina260Sensor.isInitialized())
|
||||
return ina260Sensor.runOnce() > 0;
|
||||
return ina260Sensor.isRunning();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first == config.power.device_battery_ina_address) {
|
||||
if (!ina3221Sensor.isInitialized())
|
||||
return ina3221Sensor.runOnce() > 0;
|
||||
return ina3221Sensor.isRunning();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_I2C && __has_include(<Adafruit_MAX1704X.h>)
|
||||
bool MAX17048BatteryLevel::runOnce()
|
||||
{
|
||||
if (max17048 == nullptr) {
|
||||
max17048 = MAX17048Singleton::GetInstance();
|
||||
}
|
||||
|
||||
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX17048].first != 0) {
|
||||
return max17048->runOnce(nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX17048].second);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int MAX17048BatteryLevel::getBatteryPercent()
|
||||
{
|
||||
return max17048->getBusBatteryPercent();
|
||||
}
|
||||
|
||||
uint16_t MAX17048BatteryLevel::getBattVoltage()
|
||||
{
|
||||
return max17048->getBusVoltageMv();
|
||||
}
|
||||
|
||||
bool MAX17048BatteryLevel::isBatteryConnect()
|
||||
{
|
||||
return max17048->isBatteryConnected();
|
||||
}
|
||||
|
||||
bool MAX17048BatteryLevel::isVbusIn()
|
||||
{
|
||||
return max17048->isExternallyPowered();
|
||||
}
|
||||
|
||||
bool MAX17048BatteryLevel::isCharging()
|
||||
{
|
||||
return max17048->isBatteryCharging();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_I2C && HAS_CW2015
|
||||
int CW2015BatteryLevel::getBatteryPercent()
|
||||
{
|
||||
int data = -1;
|
||||
Wire.beginTransmission(CW2015_ADDR);
|
||||
Wire.write(0x04);
|
||||
if (Wire.endTransmission() == 0) {
|
||||
if (Wire.requestFrom(CW2015_ADDR, (uint8_t)1)) {
|
||||
data = Wire.read();
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
uint16_t CW2015BatteryLevel::getBattVoltage()
|
||||
{
|
||||
uint16_t mv = 0;
|
||||
Wire.beginTransmission(CW2015_ADDR);
|
||||
Wire.write(0x02);
|
||||
if (Wire.endTransmission() == 0) {
|
||||
if (Wire.requestFrom(CW2015_ADDR, (uint8_t)2)) {
|
||||
mv = Wire.read();
|
||||
mv <<= 8;
|
||||
mv |= Wire.read();
|
||||
mv = mv * 305 / 1000;
|
||||
}
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAS_PPM) && HAS_PPM
|
||||
extern XPowersPPM *PPM;
|
||||
|
||||
bool LipoCharger::runOnce()
|
||||
{
|
||||
if (PPM == nullptr) {
|
||||
PPM = new XPowersPPM;
|
||||
bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR);
|
||||
if (result) {
|
||||
LOG_INFO("PPM BQ25896 init succeeded");
|
||||
PPM->setChargeTargetVoltage(4288);
|
||||
PPM->setChargerConstantCurr(1024);
|
||||
PPM->enableMeasure();
|
||||
PPM->enableCharge();
|
||||
} else {
|
||||
LOG_WARN("PPM BQ25896 init failed");
|
||||
delete PPM;
|
||||
PPM = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (bq == nullptr) {
|
||||
bq = new BQ27220;
|
||||
bq->setDefaultCapacity(BQ27220_DESIGN_CAPACITY);
|
||||
|
||||
bool result = bq->init();
|
||||
if (result) {
|
||||
LOG_DEBUG("BQ27220 design capacity: %d", bq->getDesignCapacity());
|
||||
LOG_DEBUG("BQ27220 fullCharge capacity: %d", bq->getFullChargeCapacity());
|
||||
LOG_DEBUG("BQ27220 remaining capacity: %d", bq->getRemainingCapacity());
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_WARN("BQ27220 init failed");
|
||||
delete bq;
|
||||
bq = nullptr;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int LipoCharger::getBatteryPercent()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint16_t LipoCharger::getBattVoltage()
|
||||
{
|
||||
return bq->getVoltage();
|
||||
}
|
||||
|
||||
bool LipoCharger::isBatteryConnect()
|
||||
{
|
||||
return PPM->getBattVoltage() > 0;
|
||||
}
|
||||
|
||||
bool LipoCharger::isVbusIn()
|
||||
{
|
||||
return PPM->isVbusIn();
|
||||
}
|
||||
|
||||
bool LipoCharger::isCharging()
|
||||
{
|
||||
bool charging = PPM->isCharging();
|
||||
if (charging) {
|
||||
LOG_DEBUG("BQ27220 time to full charge: %d min", bq->getTimeToFull());
|
||||
} else if (!PPM->isVbusIn()) {
|
||||
LOG_DEBUG("BQ27220 time to empty: %d min (%d mAh)", bq->getTimeToEmpty(), bq->getRemainingCapacity());
|
||||
}
|
||||
return charging;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HELTEC_MESH_SOLAR
|
||||
bool meshSolarBatteryLevel::runOnce()
|
||||
{
|
||||
meshSolarStart();
|
||||
return true;
|
||||
}
|
||||
|
||||
int meshSolarBatteryLevel::getBatteryPercent()
|
||||
{
|
||||
return meshSolarGetBatteryPercent();
|
||||
}
|
||||
|
||||
uint16_t meshSolarBatteryLevel::getBattVoltage()
|
||||
{
|
||||
return meshSolarGetBattVoltage();
|
||||
}
|
||||
|
||||
bool meshSolarBatteryLevel::isBatteryConnect()
|
||||
{
|
||||
return meshSolarIsBatteryConnect();
|
||||
}
|
||||
|
||||
bool meshSolarBatteryLevel::isVbusIn()
|
||||
{
|
||||
return meshSolarIsVbusIn();
|
||||
}
|
||||
|
||||
bool meshSolarBatteryLevel::isCharging()
|
||||
{
|
||||
return meshSolarIsCharging();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SERIAL_BATTERY_LEVEL
|
||||
bool SerialBatteryLevel::runOnce()
|
||||
{
|
||||
BatterySerial.begin(4800);
|
||||
return true;
|
||||
}
|
||||
|
||||
int SerialBatteryLevel::getBatteryPercent()
|
||||
{
|
||||
return v_percent;
|
||||
}
|
||||
|
||||
uint16_t SerialBatteryLevel::getBattVoltage()
|
||||
{
|
||||
return voltage * 1000;
|
||||
}
|
||||
|
||||
bool SerialBatteryLevel::isBatteryConnect()
|
||||
{
|
||||
if (BatterySerial.available() > 5) {
|
||||
while (BatterySerial.available() > 11) {
|
||||
BatterySerial.read();
|
||||
}
|
||||
int tries = 0;
|
||||
while (BatterySerial.read() != 0xFE) {
|
||||
tries++;
|
||||
if (tries > 10) {
|
||||
LOG_WARN("SerialBatteryLevel: no start byte found");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Data[1] = BatterySerial.read();
|
||||
Data[2] = BatterySerial.read();
|
||||
Data[3] = BatterySerial.read();
|
||||
Data[4] = BatterySerial.read();
|
||||
Data[5] = BatterySerial.read();
|
||||
if (Data[5] != 0xFD) {
|
||||
LOG_WARN("SerialBatteryLevel: invalid end byte %02x", Data[5]);
|
||||
return true;
|
||||
}
|
||||
v_percent = Data[1];
|
||||
voltage = Data[2] + (((float)Data[3]) / 100) + (((float)Data[4]) / 10000);
|
||||
voltage *= 2;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SerialBatteryLevel::isVbusIn()
|
||||
{
|
||||
#ifdef EXT_CHRG_DETECT
|
||||
return digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SerialBatteryLevel::isCharging()
|
||||
{
|
||||
#ifdef EXT_CHRG_DETECT
|
||||
return digitalRead(EXT_CHRG_DETECT) == EXT_CHRG_DETECT_VALUE;
|
||||
#endif
|
||||
return isVbusIn();
|
||||
}
|
||||
#endif
|
||||
@@ -1,175 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "power.h"
|
||||
|
||||
#ifdef HAS_SERIAL_BATTERY_LEVEL
|
||||
#include <SoftwareSerial.h>
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_STM32WL) && defined(BATTERY_PIN)
|
||||
#include "stm32yyxx_ll_adc.h"
|
||||
|
||||
/* Analog read resolution */
|
||||
#if defined(LL_ADC_RESOLUTION_12B)
|
||||
#define LL_ADC_RESOLUTION LL_ADC_RESOLUTION_12B
|
||||
#define BATTERY_SENSE_RESOLUTION_BITS 12
|
||||
#elif defined(LL_ADC_DS_DATA_WIDTH_12_BIT)
|
||||
#define LL_ADC_RESOLUTION LL_ADC_DS_DATA_WIDTH_12_BIT
|
||||
#define BATTERY_SENSE_RESOLUTION_BITS 12
|
||||
#else
|
||||
#error "ADC resolution could not be defined!"
|
||||
#endif
|
||||
#define ADC_RANGE (1 << BATTERY_SENSE_RESOLUTION_BITS)
|
||||
#endif
|
||||
|
||||
#ifdef EXT_PWR_DETECT
|
||||
#ifndef EXT_PWR_DETECT_MODE
|
||||
#define EXT_PWR_DETECT_MODE INPUT
|
||||
#elif EXT_PWR_DETECT_MODE == INPUT_PULLUP
|
||||
#define EXT_PWR_DETECT_VALUE LOW
|
||||
#elif EXT_PWR_DETECT_MODE == INPUT_PULLDOWN
|
||||
#define EXT_PWR_DETECT_VALUE HIGH
|
||||
#endif
|
||||
#ifndef EXT_PWR_DETECT_VALUE
|
||||
#define EXT_PWR_DETECT_VALUE HIGH
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef EXT_CHRG_DETECT
|
||||
#ifndef EXT_CHRG_DETECT_MODE
|
||||
#define EXT_CHRG_DETECT_MODE INPUT
|
||||
#elif EXT_CHRG_DETECT_MODE == INPUT_PULLUP
|
||||
#define EXT_CHRG_DETECT_VALUE LOW
|
||||
#elif EXT_CHRG_DETECT_MODE == INPUT_PULLDOWN
|
||||
#define EXT_CHRG_DETECT_VALUE HIGH
|
||||
#endif
|
||||
#ifndef EXT_CHRG_DETECT_VALUE
|
||||
#define EXT_CHRG_DETECT_VALUE HIGH
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAS_PMU
|
||||
class HasBatteryLevel
|
||||
{
|
||||
public:
|
||||
virtual int getBatteryPercent() { return -1; }
|
||||
virtual uint16_t getBattVoltage() { return 0; }
|
||||
virtual bool isBatteryConnect() { return false; }
|
||||
virtual bool isVbusIn() { return false; }
|
||||
virtual bool isCharging() { return false; }
|
||||
};
|
||||
#endif
|
||||
|
||||
class AnalogBatteryLevel : public HasBatteryLevel
|
||||
{
|
||||
public:
|
||||
int getBatteryPercent() override;
|
||||
uint16_t getBattVoltage() override;
|
||||
bool isBatteryConnect() override;
|
||||
bool isVbusIn() override;
|
||||
bool isCharging() override;
|
||||
|
||||
#if defined(ARCH_ESP32) && !defined(HAS_PMU) && defined(BATTERY_PIN)
|
||||
uint32_t espAdcRead();
|
||||
#endif
|
||||
|
||||
private:
|
||||
const uint16_t OCV[NUM_OCV_POINTS] = {OCV_ARRAY};
|
||||
const float chargingVolt = (OCV[0] + 10) * NUM_CELLS;
|
||||
const float noBatVolt = (OCV[NUM_OCV_POINTS - 1] - 500) * NUM_CELLS;
|
||||
bool initial_read_done = false;
|
||||
float last_read_value = (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS);
|
||||
uint32_t last_read_time_ms = 0;
|
||||
|
||||
#ifdef ARCH_STM32WL
|
||||
uint32_t Vref = 3300;
|
||||
#endif
|
||||
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT)
|
||||
uint16_t getRAKVoltage();
|
||||
bool hasRAK();
|
||||
#endif
|
||||
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
uint16_t getINAVoltage();
|
||||
int16_t getINACurrent();
|
||||
bool hasINA();
|
||||
#endif
|
||||
};
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_I2C && HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_MAX1704X.h>)
|
||||
class MAX17048BatteryLevel : public HasBatteryLevel
|
||||
{
|
||||
private:
|
||||
MAX17048Singleton *max17048 = nullptr;
|
||||
|
||||
public:
|
||||
bool runOnce();
|
||||
int getBatteryPercent() override;
|
||||
uint16_t getBattVoltage() override;
|
||||
bool isBatteryConnect() override;
|
||||
bool isVbusIn() override;
|
||||
bool isCharging() override;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_I2C && HAS_CW2015
|
||||
class CW2015BatteryLevel : public AnalogBatteryLevel
|
||||
{
|
||||
public:
|
||||
int getBatteryPercent() override;
|
||||
uint16_t getBattVoltage() override;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HAS_PPM) && HAS_PPM
|
||||
class BQ27220;
|
||||
|
||||
class LipoCharger : public HasBatteryLevel
|
||||
{
|
||||
private:
|
||||
BQ27220 *bq = nullptr;
|
||||
|
||||
public:
|
||||
bool runOnce();
|
||||
int getBatteryPercent() override;
|
||||
uint16_t getBattVoltage() override;
|
||||
bool isBatteryConnect() override;
|
||||
bool isVbusIn() override;
|
||||
bool isCharging() override;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef HELTEC_MESH_SOLAR
|
||||
class meshSolarBatteryLevel : public HasBatteryLevel
|
||||
{
|
||||
public:
|
||||
bool runOnce();
|
||||
int getBatteryPercent() override;
|
||||
uint16_t getBattVoltage() override;
|
||||
bool isBatteryConnect() override;
|
||||
bool isVbusIn() override;
|
||||
bool isCharging() override;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SERIAL_BATTERY_LEVEL
|
||||
class SerialBatteryLevel : public HasBatteryLevel
|
||||
{
|
||||
public:
|
||||
bool runOnce();
|
||||
int getBatteryPercent() override;
|
||||
uint16_t getBattVoltage() override;
|
||||
bool isBatteryConnect() override;
|
||||
bool isVbusIn() override;
|
||||
bool isCharging() override;
|
||||
|
||||
private:
|
||||
SoftwareSerial BatterySerial = SoftwareSerial(SERIAL_BATTERY_RX, SERIAL_BATTERY_TX);
|
||||
uint8_t Data[6] = {0};
|
||||
int v_percent = 0;
|
||||
float voltage = 0.0;
|
||||
};
|
||||
#endif
|
||||
|
||||
bool battery_adcInit();
|
||||
+12
-30
@@ -18,6 +18,8 @@
|
||||
#include "target_specific.h"
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// "esp_pm_config_esp32_t is deprecated, please include esp_pm.h and use esp_pm_config_t instead"
|
||||
#include "esp32/pm.h"
|
||||
#include "esp_pm.h"
|
||||
#if HAS_WIFI
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
@@ -144,31 +146,15 @@ void initDeepSleep()
|
||||
// If we booted because our timer ran out or the user pressed reset, send those as fake events
|
||||
RESET_REASON hwReason = rtc_get_reset_reason(0);
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||
if (hwReason == BROWN_OUT_RESET)
|
||||
reason = "brownout";
|
||||
else if (hwReason == HP_CORE_HP_WDT_RESET)
|
||||
reason = "taskWatchdog";
|
||||
else if (hwReason == HP_CORE_LP_WDT_RESET)
|
||||
reason = "intWatchdog";
|
||||
else if (hwReason == CHIP_LP_WDT_RESET)
|
||||
reason = "chipWatchdog";
|
||||
else if (hwReason == SUPER_WDT_RESET)
|
||||
reason = "superWatchdog";
|
||||
else if (hwReason == HP_SYS_HP_WDT_RESET)
|
||||
reason = "systemWatchdog";
|
||||
else if (hwReason == HP_SYS_LP_WDT_RESET)
|
||||
reason = "systemLowPowerWatchdog";
|
||||
#else
|
||||
if (hwReason == RTCWDT_BROWN_OUT_RESET)
|
||||
reason = "brownout";
|
||||
else if (hwReason == RTCWDT_RTC_RESET)
|
||||
reason = "rtcWatchdog";
|
||||
else if (hwReason == TG0WDT_SYS_RESET)
|
||||
|
||||
if (hwReason == TG0WDT_SYS_RESET)
|
||||
reason = "taskWatchdog";
|
||||
else if (hwReason == TG1WDT_SYS_RESET)
|
||||
|
||||
if (hwReason == TG1WDT_SYS_RESET)
|
||||
reason = "intWatchdog";
|
||||
#endif
|
||||
|
||||
LOG_INFO("Booted, wake cause %d (boot count %d), reset_reason=%s", wakeCause, bootCount, reason);
|
||||
#endif
|
||||
|
||||
@@ -411,10 +397,8 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
|
||||
|
||||
// NOTE! ESP docs say we must disable bluetooth and wifi before light sleep
|
||||
|
||||
#if SOC_PM_SUPPORT_RTC_PERIPH_PD
|
||||
// We want RTC peripherals to stay on
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
#endif
|
||||
|
||||
#if defined(BUTTON_PIN) && defined(BUTTON_NEED_PULLUP)
|
||||
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
||||
@@ -548,7 +532,11 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
|
||||
*/
|
||||
void enableModemSleep()
|
||||
{
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
static esp_pm_config_t esp32_config; // filled with zeros because bss
|
||||
#else
|
||||
static esp_pm_config_esp32_t esp32_config; // filled with zeros because bss
|
||||
#endif
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
@@ -557,12 +545,6 @@ void enableModemSleep()
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ;
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#if CONFIG_ESP32P4_REV_MIN_FULL < 300
|
||||
esp32_config.max_freq_mhz = 360;
|
||||
#else
|
||||
esp32_config.max_freq_mhz = 400;
|
||||
#endif
|
||||
#else
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
|
||||
#endif
|
||||
@@ -580,8 +562,8 @@ bool shouldLoraWake(uint32_t msecToWake)
|
||||
|
||||
void enableLoraInterrupt()
|
||||
{
|
||||
#if SOC_PM_SUPPORT_EXT_WAKEUP && defined(LORA_DIO1) && (LORA_DIO1 != RADIOLIB_NC)
|
||||
esp_err_t res;
|
||||
#if SOC_PM_SUPPORT_EXT_WAKEUP && defined(LORA_DIO1) && (LORA_DIO1 != RADIOLIB_NC)
|
||||
res = gpio_pulldown_en((gpio_num_t)LORA_DIO1);
|
||||
if (res != ESP_OK) {
|
||||
LOG_ERROR("gpio_pulldown_en(LORA_DIO1) result %d", res);
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
// Battery
|
||||
|
||||
#define BATTERY_PIN 34 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC_CHANNEL_6
|
||||
#define ADC_CHANNEL ADC1_GPIO34_CHANNEL
|
||||
#define ADC_ATTENUATION \
|
||||
ADC_ATTEN_DB_2_5 // 2_5-> 100mv-1250mv, 11-> 150mv-3100mv for ESP32
|
||||
// ESP32-S2/C3/S3 are different
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
// Battery sense
|
||||
#define BATTERY_PIN 35
|
||||
#define ADC_MULTIPLIER 2.01 // 100k + 100k, and add 1% tolerance
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
#define BATTERY_SENSE_RESOLUTION_BITS ADC_RESOLUTION
|
||||
|
||||
// SPI
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#define I2C_SDA 4
|
||||
#define I2C_SCL 5
|
||||
#define BATTERY_PIN 34
|
||||
#define ADC_CHANNEL ADC_CHANNEL_6
|
||||
#define ADC_CHANNEL ADC1_GPIO34_CHANNEL
|
||||
|
||||
// GPS
|
||||
#undef GPS_RX_PIN
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// Note: On the ESP32 base version, gpio34-39 are input-only, and do not have internal pull-ups.
|
||||
// If 39 is not being used for a button, it is suggested to remove the #define.
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
#define ADC_MULTIPLIER 1.85 // (R1 = 470k, R2 = 680k)
|
||||
#define EXT_PWR_DETECT 4 // Pin to detect connected external power source for LILYGO® TTGO T-Energy T18 and other DIY boards
|
||||
#define EXT_NOTIFY_OUT 12 // Overridden default pin to use for Ext Notify Module (#975).
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#define BUTTON_PIN 39 // The middle button GPIO on the T-Beam
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
#define ADC_MULTIPLIER 1.85 // (R1 = 470k, R2 = 680k)
|
||||
#define EXT_PWR_DETECT 4 // Pin to detect connected external power source for LILYGO® TTGO T-Energy T18 and other DIY boards
|
||||
#define EXT_NOTIFY_OUT 12 // Overridden default pin to use for Ext Notify Module (#975).
|
||||
|
||||
+15
-257
@@ -4,9 +4,8 @@ extends = arduino_base
|
||||
custom_esp32_kind =
|
||||
custom_mtjson_part =
|
||||
platform =
|
||||
# TODO renovate
|
||||
https://github.com/pioarduino/platform-espressif32/releases/download/55.03.38-1/platform-espressif32.zip
|
||||
; https://github.com/pioarduino/platform-espressif32.git#develop
|
||||
# renovate: datasource=custom.pio depName=platformio/espressif32 packageName=platformio/platform/espressif32
|
||||
platformio/espressif32@6.13.0
|
||||
platform_packages =
|
||||
# renovate: datasource=custom.pio depName=platformio/tool-mklittlefs packageName=platformio/tool/tool-mklittlefs
|
||||
platformio/tool-mklittlefs@1.203.210628
|
||||
@@ -36,18 +35,20 @@ build_unflags =
|
||||
-std=gnu++11
|
||||
build_flags =
|
||||
${arduino_base.build_flags}
|
||||
-flto
|
||||
-Wall
|
||||
-Wextra
|
||||
-Isrc/platform/esp32
|
||||
-std=gnu++17
|
||||
-DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG
|
||||
# DO NOT INCREASE THIS TO DEBUG. It appears to trigger a bug in ESP-IDF
|
||||
# Bluetooth stack with Pixel 8 Android devices:
|
||||
# https://github.com/espressif/esp-idf/issues/18126#issuecomment-4286197744
|
||||
# Once the bug is resolved, we can remove this warning.
|
||||
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO
|
||||
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
|
||||
-DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL
|
||||
-DAXP_DEBUG_PORT=Serial
|
||||
-DCONFIG_BT_NIMBLE_ENABLED
|
||||
-DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3
|
||||
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
|
||||
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
|
||||
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
||||
-DSERIAL_BUFFER_SIZE=4096
|
||||
-DSERIAL_HAS_ON_RECEIVE
|
||||
@@ -61,13 +62,16 @@ build_flags =
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
${networking_base.lib_deps}
|
||||
${networking_extra.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
${environmental_extra.lib_deps}
|
||||
${radiolib_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master
|
||||
https://github.com/meshtastic/esp32_https_server/archive/0c71f380390ad483ff134ad938e07f6cf1226c5b.zip
|
||||
# TODO renovate
|
||||
https://github.com/mverch67/libpax/archive/6f52ee989301cdabaeef00bcbf93bff55708ce2f.zip
|
||||
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
|
||||
h2zero/NimBLE-Arduino@1.4.3
|
||||
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
||||
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||
lewisxhe/XPowersLib@0.3.3
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
@@ -75,30 +79,7 @@ lib_deps =
|
||||
|
||||
lib_ignore =
|
||||
segger_rtt
|
||||
; ESP32 BLE Arduino
|
||||
; BLE
|
||||
; Ignore select Bluetooth libs
|
||||
NimBLE-Arduino
|
||||
BluetoothSerial
|
||||
SimpleBLE
|
||||
WiFiProv
|
||||
ArduinoOTA
|
||||
; Ignore pioarduino esp32 libs we don't use
|
||||
ESP_I2S
|
||||
ESP_NOW
|
||||
ESP_SR
|
||||
Insights
|
||||
Matter
|
||||
OpenThread
|
||||
RainMaker
|
||||
;SPIFFS
|
||||
USB
|
||||
;NetworkClientSecure
|
||||
Zigbee
|
||||
Micro-RTSP
|
||||
; testing below
|
||||
mqtt
|
||||
esp-mqtt
|
||||
ESP32 BLE Arduino
|
||||
|
||||
; leave this commented out to avoid breaking Windows
|
||||
;upload_port = /dev/ttyUSB0
|
||||
@@ -111,226 +92,3 @@ lib_ignore =
|
||||
; customize the partition table
|
||||
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
||||
board_build.partitions = partition-table.csv
|
||||
|
||||
custom_component_remove =
|
||||
espressif/esp_hosted
|
||||
espressif/esp_wifi_remote
|
||||
espressif/esp_modem
|
||||
espressif/esp-dsp
|
||||
espressif/esp32-camera
|
||||
espressif/libsodium
|
||||
espressif/esp-modbus
|
||||
espressif/qrcode
|
||||
espressif/esp_insights
|
||||
espressif/esp_diag_data_store
|
||||
espressif/esp_diagnostics
|
||||
espressif/esp_rainmaker
|
||||
espressif/rmaker_common
|
||||
espressif/network_provisioning
|
||||
chmorgan/esp-libhelix-mp3
|
||||
espressif/esp-tflite-micro
|
||||
espressif/esp-sr
|
||||
espressif/esp_matter
|
||||
espressif/esp-zboss-lib
|
||||
espressif/esp-zigbee-lib
|
||||
espressif/mqtt
|
||||
|
||||
custom_sdkconfig =
|
||||
; CONFIG_LOG_DEFAULT_LEVEL=4
|
||||
; CONFIG_LOG_MAXIMUM_LEVEL=4
|
||||
'# CONFIG_BT_NIMBLE_LOG_LEVEL_INFO is not set'
|
||||
CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR=y
|
||||
CONFIG_LOG_COLORS=y
|
||||
CONFIG_ARDUHAL_LOG_COLORS=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
;
|
||||
; Compiler options
|
||||
;
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=n
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=n
|
||||
CONFIG_COMPILER_STACK_CHECK_MODE_NORM=n
|
||||
CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y
|
||||
CONFIG_COMPILER_DISABLE_GCC12_WARNINGS=y
|
||||
CONFIG_COMPILER_DISABLE_GCC13_WARNINGS=y
|
||||
CONFIG_COMPILER_DISABLE_GCC14_WARNINGS=y
|
||||
CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=n
|
||||
CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE=y
|
||||
CONFIG_ESP_GDBSTUB_ENABLED=n
|
||||
CONFIG_ESP_TASK_WDT_INIT=n
|
||||
CONFIG_NEWLIB_NANO_FORMAT=y
|
||||
; LIBC_NEWLIB_NANO_FORMAT is enabled via 'esp32_extra.py' to allow float support
|
||||
CONFIG_ESP_COREDUMP_ENABLE=n
|
||||
CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=n
|
||||
CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y
|
||||
CONFIG_ESP32_ENABLE_COREDUMP=n
|
||||
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=n
|
||||
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=n
|
||||
CONFIG_FREERTOS_USE_TRACE_FACILITY=n
|
||||
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
|
||||
CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y
|
||||
CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH=y
|
||||
CONFIG_ESP_SYSTEM_ESP32_SRAM1_REGION_AS_IRAM=y
|
||||
CONFIG_ESP_WIFI_IRAM_OPT=n
|
||||
CONFIG_ESP32_WIFI_RX_IRAM_OPT=n
|
||||
CONFIG_SPIRAM_CACHE_LIBCHAR_IN_IRAM=n
|
||||
CONFIG_SPIRAM_CACHE_LIBSTR_IN_IRAM=n
|
||||
CONFIG_SPIRAM_CACHE_LIBMISC_IN_IRAM=n
|
||||
CONFIG_SPIRAM_CACHE_LIBTIME_IN_IRAM=n
|
||||
CONFIG_UNITY_ENABLE_FLOAT=n
|
||||
CONFIG_UNITY_ENABLE_DOUBLE=n
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
||||
CONFIG_SUPPORT_TERMIOS=n
|
||||
CONFIG_VFS_SUPPORT_TERMIOS=n
|
||||
CONFIG_VFS_SUPPORT_SELECT=n
|
||||
CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=n
|
||||
CONFIG_WS_TRANSPORT=n
|
||||
CONFIG_PPP_SUPPORT=n
|
||||
CONFIG_NETWORK_PROV_NETWORK_TYPE_WIFI=n
|
||||
; CONFIG_LWIP_DHCPS=n
|
||||
CONFIG_LWIP_PPP_SUPPORT=n
|
||||
CONFIG_LWIP_IP_FORWARD=n
|
||||
CONFIG_LWIP_IPV4_NAPT=n
|
||||
;
|
||||
; MBEDTLS
|
||||
;
|
||||
CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=8192
|
||||
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n
|
||||
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y
|
||||
; Switch to custom CA bundle (for Meshtastic MQTT/etc) in the future
|
||||
; https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/kconfig-reference.html#certificate-bundle-configuration
|
||||
; CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE=y
|
||||
; CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH=""
|
||||
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=1
|
||||
; #shame
|
||||
CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION=y
|
||||
CONFIG_MBEDTLS_SSL_RENEGOTIATION=n
|
||||
CONFIG_MBEDTLS_SSL_PROTO_DTLS=n
|
||||
CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=n
|
||||
CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=n
|
||||
CONFIG_MBEDTLS_PKCS7_C=n
|
||||
CONFIG_MBEDTLS_CAMELLIA_C=n
|
||||
CONFIG_MBEDTLS_CCM_C=n
|
||||
CONFIG_MBEDTLS_CMAC_C=n
|
||||
CONFIG_MBEDTLS_SHA512_C=n
|
||||
CONFIG_MBEDTLS_X509_CRL_PARSE_C=n
|
||||
CONFIG_MBEDTLS_X509_CSR_PARSE_C=n
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_PSK=n
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=n
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=n
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=n
|
||||
CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=n
|
||||
CONFIG_MDNS_ENABLE_CONSOLE_CLI=n
|
||||
CONFIG_MDNS_PREDEF_NETIF_AP=n
|
||||
; CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
|
||||
CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT=n
|
||||
CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=n
|
||||
CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA=n
|
||||
CONFIG_ESP_WIFI_ENABLE_SAE_H2E=n
|
||||
CONFIG_OPENTHREAD_ENABLED=n
|
||||
CONFIG_ZB_ENABLED=n
|
||||
CONFIG_IEEE802154_ENABLED=n
|
||||
CONFIG_ESP_INSIGHTS_ENABLED=n
|
||||
CONFIG_HTTPD_WS_SUPPORT=n
|
||||
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=n
|
||||
CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=n
|
||||
; using esp-idf5-https_server library instead, for now?
|
||||
CONFIG_ESP_HTTPS_SERVER_ENABLE=n
|
||||
;
|
||||
; Builtin ESP-MQTT, we don't use this (yet?)
|
||||
;
|
||||
CONFIG_MQTT_TRANSPORT_SSL=n
|
||||
CONFIG_MQTT_TRANSPORT_WEBSOCKET=n
|
||||
CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=n
|
||||
CONFIG_MQTT_PROTOCOL_311=n
|
||||
;
|
||||
; Ethernet
|
||||
; Disabled in esp32_common, re-enable on variants that need it
|
||||
;
|
||||
CONFIG_ETH_ENABLED=n
|
||||
CONFIG_ARDUINO_SELECTIVE_Ethernet=n
|
||||
;
|
||||
; Bluetooth
|
||||
;
|
||||
CONFIG_BLE_MESH=n
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_NVS_PERSIST=y
|
||||
CONFIG_BT_NIMBLE_ROLE_CENTRAL=n
|
||||
CONFIG_BT_NIMBLE_ROLE_OBSERVER=n
|
||||
CONFIG_BT_CONTROLLER_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
|
||||
CONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||
CONFIG_BT_NIMBLE_MAX_BONDS=6
|
||||
CONFIG_BT_NIMBLE_ENABLE_PERIODIC_SYNC=n
|
||||
CONFIG_BT_NIMBLE_ENABLE_PERIODIC_ADV=n
|
||||
CONFIG_BT_NIMBLE_EXT_SCAN=n
|
||||
CONFIG_BT_NIMBLE_EXT_ADV=n
|
||||
CONFIG_BT_NIMBLE_PERIODIC_ADV_SYNC_TRANSFER=n
|
||||
CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_2M_PHY=n
|
||||
CONFIG_BT_NIMBLE_BLUFI_ENABLE=n
|
||||
CONFIG_BT_NIMBLE_PROX_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_ANS_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_CTS_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_HTP_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_IPSS_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_TPS_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_IAS_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_LLS_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_SPS_SERVICE=y
|
||||
CONFIG_BT_NIMBLE_HR_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_HID_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_BAS_SERVICE=n
|
||||
CONFIG_BT_NIMBLE_DIS_SERVICE=n
|
||||
;
|
||||
; Arduino selective compilation
|
||||
; Disable unused Arduino libraries to save space
|
||||
;
|
||||
CONFIG_ARDUINO_SELECTIVE_COMPILATION=y
|
||||
CONFIG_ARDUINO_SELECTIVE_SPI=y
|
||||
CONFIG_ARDUINO_SELECTIVE_Wire=y
|
||||
CONFIG_ARDUINO_SELECTIVE_ESP_SR=n
|
||||
CONFIG_ARDUINO_SELECTIVE_EEPROM=y
|
||||
CONFIG_ARDUINO_SELECTIVE_Preferences=y
|
||||
CONFIG_ARDUINO_SELECTIVE_Ticker=y
|
||||
CONFIG_ARDUINO_SELECTIVE_Update=y
|
||||
CONFIG_ARDUINO_SELECTIVE_Zigbee=n
|
||||
CONFIG_ARDUINO_SELECTIVE_FS=y
|
||||
CONFIG_ARDUINO_SELECTIVE_SD=y
|
||||
CONFIG_ARDUINO_SELECTIVE_SD_MMC=y
|
||||
CONFIG_ARDUINO_SELECTIVE_SPIFFS=y
|
||||
CONFIG_ARDUINO_SELECTIVE_FFat=n
|
||||
CONFIG_ARDUINO_SELECTIVE_LittleFS=y
|
||||
CONFIG_ARDUINO_SELECTIVE_Network=y
|
||||
; CONFIG_ARDUINO_SELECTIVE_Ethernet=n
|
||||
CONFIG_ARDUINO_SELECTIVE_PPP=n
|
||||
CONFIG_ARDUINO_SELECTIVE_Hash=y
|
||||
CONFIG_ARDUINO_SELECTIVE_ArduinoOTA=n
|
||||
CONFIG_ARDUINO_SELECTIVE_AsyncUDP=y
|
||||
CONFIG_ARDUINO_SELECTIVE_DNSServer=n
|
||||
CONFIG_ARDUINO_SELECTIVE_ESPmDNS=y
|
||||
CONFIG_ARDUINO_SELECTIVE_HTTPClient=n
|
||||
CONFIG_ARDUINO_SELECTIVE_Matter=n
|
||||
CONFIG_ARDUINO_SELECTIVE_NetBIOS=n
|
||||
CONFIG_ARDUINO_SELECTIVE_WebServer=n
|
||||
CONFIG_ARDUINO_SELECTIVE_WiFi=y
|
||||
CONFIG_ARDUINO_SELECTIVE_NetworkClientSecure=y
|
||||
CONFIG_ARDUINO_SELECTIVE_WiFiProv=n
|
||||
CONFIG_ARDUINO_SELECTIVE_BLE=y
|
||||
CONFIG_ARDUINO_SELECTIVE_BluetoothSerial=n
|
||||
CONFIG_ARDUINO_SELECTIVE_SimpleBLE=n
|
||||
CONFIG_ARDUINO_SELECTIVE_RainMaker=n
|
||||
CONFIG_ARDUINO_SELECTIVE_OpenThread=n
|
||||
CONFIG_ARDUINO_SELECTIVE_Insights=n
|
||||
|
||||
@@ -11,39 +11,12 @@ build_src_filter =
|
||||
|
||||
build_flags =
|
||||
${esp32_common.build_flags}
|
||||
-mtext-section-literals
|
||||
-D ESP32_FORCE_IRAM_MEMSET
|
||||
-Wl,--wrap=memset
|
||||
-Wl,--wrap=memcpy
|
||||
-DMESHTASTIC_EXCLUDE_AUDIO=1
|
||||
-DMESHTASTIC_EXCLUDE_ACCELEROMETER=1
|
||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
|
||||
-DMESHTASTIC_EXCLUDE_WEBSERVER=1
|
||||
-DMESHTASTIC_EXCLUDE_RANGETEST=1
|
||||
; HACK HACK HACK this is just a proof of concept ESP32+NimBLE but these
|
||||
; includes need to be done properly somehow
|
||||
-I${platformio.packages_dir}/framework-espidf/components/bt/host/nimble/nimble/nimble/host/include
|
||||
-I${platformio.packages_dir}/framework-espidf/components/bt/host/nimble/nimble/nimble/include
|
||||
-I${platformio.packages_dir}/framework-espidf/components/bt/host/nimble/nimble/porting/nimble/include
|
||||
-I${platformio.packages_dir}/framework-espidf/components/bt/host/nimble/port/include
|
||||
-I${platformio.packages_dir}/framework-espidf/components/bt/host/nimble/nimble/porting/npl/freertos/include
|
||||
-I${platformio.packages_dir}/framework-espidf/components/bt/host/nimble/nimble/nimble/transport/include
|
||||
-I${platformio.packages_dir}/framework-espidf/components/bt/host/nimble/nimble/nimble/host/services/gap/include
|
||||
-I${platformio.packages_dir}/framework-espidf/components/bt/host/nimble/esp-hci/include
|
||||
-I${platformio.packages_dir}/framework-espidf/components/bt/host/nimble/nimble/nimble/host/services/gatt/include
|
||||
-I${platformio.packages_dir}/framework-espidf/components/bt/host/nimble/nimble/nimble/host/util/include
|
||||
|
||||
custom_sdkconfig =
|
||||
${esp32_common.custom_sdkconfig}
|
||||
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
|
||||
'# CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY is not set'
|
||||
'# CONFIG_BTDM_CTRL_MODE_BTDM is not set'
|
||||
'# CONFIG_BT_BLUEDROID_ENABLED is not set'
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP=y
|
||||
|
||||
; Override lib_deps to use environmental_extra_no_bsec instead of environmental_extra
|
||||
; BSEC library uses ~3.5KB DRAM which causes overflow on original ESP32 targets
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
${networking_base.lib_deps}
|
||||
@@ -51,8 +24,8 @@ lib_deps =
|
||||
${radiolib_base.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
${environmental_extra_no_bsec.lib_deps}
|
||||
# TODO renovate
|
||||
https://github.com/mverch67/libpax/archive/6f52ee989301cdabaeef00bcbf93bff55708ce2f.zip
|
||||
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
|
||||
h2zero/NimBLE-Arduino@1.4.3
|
||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||
lewisxhe/XPowersLib@0.3.3
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#define ADC_CTRL 21
|
||||
#define ADC_CTRL_ENABLED LOW
|
||||
#define BATTERY_PIN 37 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC_CHANNEL_1
|
||||
#define ADC_CHANNEL ADC1_CHANNEL_1
|
||||
// ratio of voltage divider = 3.20 (R1=100k, R2=220k)
|
||||
#define ADC_MULTIPLIER 3.2
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
#define PIN_EINK_MOSI 23 // EPD_MOSI
|
||||
|
||||
#define BATTERY_PIN 35
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
// https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/schematic/Core/m5paper/M5_PAPER_SCH.pdf
|
||||
// https://github.com/m5stack/M5Core-Ink/blob/master/examples/Basics/FactoryTest/FactoryTest.ino#L58
|
||||
// VBAT
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#endif
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
#define BATTERY_SENSE_SAMPLES 15 // Set the number of samples, It has an effect of increasing sensitivity.
|
||||
#define ADC_MULTIPLIER 2
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ build_flags =
|
||||
-DVTABLES_IN_FLASH=1
|
||||
-DCONFIG_DISABLE_HAL_LOCKS=1
|
||||
-DHAS_STK8XXX=1
|
||||
; -O2
|
||||
-O2
|
||||
-I variants/esp32/radiomaster_900_bandit
|
||||
board_build.f_cpu = 240000000L
|
||||
upload_protocol = esptool
|
||||
|
||||
@@ -47,7 +47,7 @@ static const uint8_t SCK = 33;
|
||||
|
||||
#define PIN_VBAT WB_A0
|
||||
#define BATTERY_PIN PIN_VBAT
|
||||
#define ADC_CHANNEL ADC_CHANNEL_0
|
||||
#define ADC_CHANNEL ADC1_GPIO36_CHANNEL
|
||||
|
||||
// https://docs.rakwireless.com/Product-Categories/WisBlock/RAK13300/
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#endif
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
#define BATTERY_SENSE_SAMPLES 30 // Set the number of samples, It has an effect of increasing sensitivity.
|
||||
#define ADC_MULTIPLIER 6.45
|
||||
#define CELL_TYPE_LION // same curve for liion/lipo
|
||||
|
||||
@@ -16,7 +16,6 @@ board_check = true
|
||||
build_flags = ${esp32_base.build_flags}
|
||||
-D TBEAM_V10
|
||||
-I variants/esp32/tbeam
|
||||
|
||||
upload_speed = 921600
|
||||
|
||||
[env:tbeam-displayshield]
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#define BUTTON_PIN 39
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
|
||||
#define I2C_SDA 21 // I2C pins for this board
|
||||
#define I2C_SCL 22
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
|
||||
#define I2C_SDA 21 // I2C pins for this board
|
||||
#define I2C_SCL 22
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#define BATTERY_PIN 35
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
#define BATTERY_SENSE_SAMPLES 30
|
||||
|
||||
// ratio of voltage divider = 2.0 (R42=100k, R43=100k)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
// ratio of voltage divider = 2.0 (R42=100k, R43=100k)
|
||||
#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage.
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
|
||||
#define I2C_SDA 21 // I2C pins for this board
|
||||
#define I2C_SCL 22
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#undef BAT_MEASURE_ADC_UNIT
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_MULTIPLIER 1.34 // tracked resistance divider is 100k+470k, so it can not fillfull well on esp32 adc
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
|
||||
#define ADC_ATTENUATION ADC_ATTEN_DB_12 // lower dB for high resistance voltage divider
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
|
||||
@@ -5,19 +5,6 @@ custom_esp32_kind = esp32c3
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_c3_exception_decoder
|
||||
|
||||
build_flags =
|
||||
${esp32_common.build_flags}
|
||||
; Linker script to align text.handler_execute section to 4 bytes
|
||||
-Wl,-Tsrc/platform/esp32/align-text.handler_execute-4.ld
|
||||
|
||||
custom_sdkconfig =
|
||||
${esp32_common.custom_sdkconfig}
|
||||
; ESP32c3 doesn't support SD_MMC
|
||||
CONFIG_ARDUINO_SELECTIVE_SD_MMC=n
|
||||
; CONFIG_BT_NIMBLE_EXT_ADV=y
|
||||
; CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=257
|
||||
; CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2
|
||||
|
||||
lib_deps =
|
||||
${esp32_common.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
|
||||
@@ -1,34 +1,50 @@
|
||||
[esp32c6_base]
|
||||
extends = esp32_common
|
||||
custom_esp32_kind = esp32c6
|
||||
|
||||
platform =
|
||||
# Do not renovate until we have switched to pioarduino tagged builds
|
||||
https://github.com/Jason2866/platform-espressif32/archive/22faa566df8c789000f8136cd8d0aca49617af55.zip
|
||||
platform_packages =
|
||||
# HACK: This release was automatically removed upstream
|
||||
framework-arduinoespressif32 @ https://github.com/vidplace7/platform-espressif32/releases/download/meshtastic-esp32c6/framework-arduinoespressif32-all-release_v5.1-124d64e.zip
|
||||
build_flags =
|
||||
${esp32_common.build_flags}
|
||||
; Linker script to align text.handler_execute section to 4 bytes
|
||||
-Wl,-Tsrc/platform/esp32/align-text.handler_execute-4.ld
|
||||
; Exclude Paxcounter, it uses 'esp_vhci_host_send_packet' whch is not available on ESP32-C6
|
||||
; https://github.com/espressif/arduino-esp32/issues/11716
|
||||
${arduino_base.build_flags}
|
||||
-Wall
|
||||
-Wextra
|
||||
-Isrc/platform/esp32
|
||||
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
||||
-DSERIAL_BUFFER_SIZE=4096
|
||||
-DLIBPAX_ARDUINO
|
||||
-DLIBPAX_WIFI
|
||||
-DLIBPAX_BLE
|
||||
-DMESHTASTIC_EXCLUDE_WEBSERVER
|
||||
;-DDEBUG_HEAP
|
||||
; TEMP
|
||||
-DHAS_BLUETOOTH=0
|
||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER
|
||||
-DMESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
${networking_base.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
${environmental_extra.lib_deps}
|
||||
${radiolib_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||
lewisxhe/XPowersLib@0.3.3
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
rweather/Crypto@0.4.0
|
||||
|
||||
build_src_filter =
|
||||
${esp32_common.build_src_filter}
|
||||
${esp32_common.build_src_filter} -<mesh/http>
|
||||
|
||||
monitor_speed = 460800
|
||||
monitor_filters = esp32_c3_exception_decoder
|
||||
|
||||
custom_sdkconfig =
|
||||
${esp32_common.custom_sdkconfig}
|
||||
; ESP32c6 doesn't support SD_MMC
|
||||
CONFIG_ARDUINO_SELECTIVE_SD_MMC=n
|
||||
; CONFIG_BT_NIMBLE_EXT_ADV=y
|
||||
; CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=257
|
||||
; CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2
|
||||
|
||||
lib_ignore =
|
||||
${esp32_common.lib_ignore}
|
||||
NonBlockingRTTTL
|
||||
NimBLE-Arduino
|
||||
libpax
|
||||
|
||||
lib_deps =
|
||||
${esp32_common.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
|
||||
|
||||
@@ -17,12 +17,28 @@ board_build.partitions = default_16MB.csv
|
||||
;Normal method
|
||||
upload_protocol = esptool
|
||||
;upload_port = /dev/ttyACM2
|
||||
build_unflags =
|
||||
-D HAS_BLUETOOTH
|
||||
-D MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
-D HAS_WIFI
|
||||
lib_deps =
|
||||
${esp32c6_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
|
||||
h2zero/NimBLE-Arduino@2.3.7
|
||||
build_flags =
|
||||
${esp32c6_base.build_flags}
|
||||
-D M5STACK_UNITC6L
|
||||
-I variants/esp32c6/m5stack_unitc6l
|
||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DARDUINO_USB_MODE=1
|
||||
-D HAS_BLUETOOTH=1
|
||||
-DCONFIG_BT_NIMBLE_EXT_ADV=1
|
||||
-DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2
|
||||
-D NIMBLE_TWO
|
||||
monitor_speed=115200
|
||||
lib_ignore =
|
||||
NonBlockingRTTTL
|
||||
libpax
|
||||
build_src_filter =
|
||||
${esp32c6_base.build_src_filter} +<../variants/esp32c6/m5stack_unitc6l>
|
||||
|
||||
@@ -8,11 +8,3 @@ build_flags =
|
||||
-I variants/esp32c6/tlora_c6
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DARDUINO_USB_MODE=1
|
||||
; Disable screen + audio.
|
||||
; ESP32-C6 with 4MB flash is TIGHT on space.
|
||||
-DMESHTASTIC_EXCLUDE_SCREEN=1
|
||||
-DMESHTASTIC_EXCLUDE_AUDIO=1
|
||||
|
||||
lib_ignore =
|
||||
${esp32c6_base.lib_ignore}
|
||||
ESP32 Codec2
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
[esp32p4_base]
|
||||
extends = esp32_common
|
||||
custom_esp32_kind = esp32p4
|
||||
board_build.mcu = esp32p4
|
||||
|
||||
build_unflags=
|
||||
-DHAS_UDP_MULTICAST=1
|
||||
|
||||
build_flags =
|
||||
${esp32_common.build_flags}
|
||||
-DMESHTASTIC_EXCLUDE_WIFI=1 ; TODO
|
||||
-DMESHTASTIC_EXCLUDE_MQTT=1
|
||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
|
||||
|
||||
build_src_filter =
|
||||
${esp32_common.build_src_filter} -<libpax/> -<platform/stm32wl> -<nimble> -<mesh/http/> -<platform/rp2xx0> -<mesh/raspihttp> -<serialization/>
|
||||
|
||||
extra_scripts =
|
||||
${esp32_common.extra_scripts}
|
||||
extra_scripts/ld_response_file.py
|
||||
|
||||
; Override esp32_common component pruning: keep esp_hosted + esp_wifi_remote for P4 hosted BT
|
||||
custom_component_remove =
|
||||
espressif/esp_modem
|
||||
espressif/esp-dsp
|
||||
espressif/esp32-camera
|
||||
espressif/libsodium
|
||||
espressif/esp-modbus
|
||||
espressif/qrcode
|
||||
espressif/esp_insights
|
||||
espressif/esp_diag_data_store
|
||||
espressif/esp_diagnostics
|
||||
espressif/esp_rainmaker
|
||||
espressif/rmaker_common
|
||||
espressif/network_provisioning
|
||||
chmorgan/esp-libhelix-mp3
|
||||
espressif/esp-tflite-micro
|
||||
espressif/esp-sr
|
||||
espressif/esp_matter
|
||||
espressif/esp-zboss-lib
|
||||
espressif/esp-zigbee-lib
|
||||
espressif/mqtt
|
||||
|
||||
custom_sdkconfig =
|
||||
${esp32_common.custom_sdkconfig}
|
||||
CONFIG_ARDUINO_SELECTIVE_SD_MMC=y
|
||||
CONFIG_BT_CONTROLLER_DISABLED=y
|
||||
CONFIG_ESP_WIFI_REMOTE_ENABLED=y
|
||||
# esp_hosted core
|
||||
CONFIG_ESP_HOSTED_ENABLED=y
|
||||
# Board: custom (not Espressif EV board)
|
||||
CONFIG_ESP_HOSTED_P4_DEV_BOARD_NONE=y
|
||||
# Delay after C6 reset to allow boot (e.g. old v2.3.0 may be slow)
|
||||
CONFIG_ESP_HOSTED_SDIO_RESET_DELAY_MS=1500
|
||||
CONFIG_ESP_HOSTED_SDIO_OPTIMIZATION_RX_STREAMING_MODE=y
|
||||
CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y
|
||||
CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET="esp32c6"
|
||||
CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6=y
|
||||
CONFIG_ESP_HOSTED_CP_TARGET_ESP32H2=n
|
||||
CONFIG_ESP_HOSTED_PRIV_SDIO_OPTION=y
|
||||
CONFIG_ESP_HOSTED_SPI_HOST_INTERFACE=n
|
||||
CONFIG_ESP_HOSTED_TRANSPORT_SDIO=y
|
||||
CONFIG_ESP_HOSTED_ENABLE_NIMBLE=y
|
||||
CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
CONFIG_ESP_HOSTED_NIMBLE_HCI_VHCI=y
|
||||
CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER=y
|
||||
CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS=3
|
||||
# OTA method: LittleFS
|
||||
CONFIG_OTA_METHOD_LITTLEFS=y
|
||||
|
||||
# Skip version check — we force OTA regardless
|
||||
# CONFIG_OTA_VERSION_CHECK_HOST_SLAVE is not set
|
||||
# CONFIG_OTA_VERSION_CHECK_SLAVEFW_SLAVE is not set
|
||||
|
||||
# RX streaming mode
|
||||
CONFIG_ESP_HOSTED_SLAVE_RESET_ON_EVERY_HOST_BOOTUP=y
|
||||
;CONFIG_ESP_HOSTED_SLAVE_RESET_ONLY_IF_NECESSARY=y
|
||||
# SOC_LCD (MUI / lovyanGFX)
|
||||
CONFIG_SOC_LCD_I80_SUPPORTED=y
|
||||
CONFIG_SOC_LCD_RGB_SUPPORTED=y
|
||||
CONFIG_SOC_MIPI_DSI_SUPPORTED=y
|
||||
# stack dump
|
||||
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y ; remove for production version
|
||||
;CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y; for production version
|
||||
;CONFIG_ESP_SYSTEM_PANIC_GDBSTUB=y ; for target debugging
|
||||
# Logger: verbose for experiment
|
||||
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
|
||||
CONFIG_LOG_MAXIMUM_LEVEL_DEBUG=y
|
||||
|
||||
|
||||
lib_ignore =
|
||||
${esp32_common.lib_ignore}
|
||||
libpax
|
||||
esp8266-oled-ssd1306
|
||||
bsec2
|
||||
esp32_idf5_https_server
|
||||
esp_driver_cam
|
||||
esp_http_server
|
||||
|
||||
; Override lib_deps to exclude environmental_extras
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
${networking_base.lib_deps}
|
||||
${networking_extra.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
${radiolib_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
rweather/Crypto@0.4.0
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
@@ -12,18 +12,13 @@ build_flags =
|
||||
-DHAS_BLUETOOTH=0
|
||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER
|
||||
-DMESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
-mtext-section-literals
|
||||
|
||||
custom_sdkconfig =
|
||||
${esp32_common.custom_sdkconfig}
|
||||
|
||||
lib_ignore =
|
||||
${esp32_common.lib_ignore}
|
||||
NimBLE-Arduino
|
||||
libpax
|
||||
|
||||
lib_deps =
|
||||
${esp32_common.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
|
||||
|
||||
lib_ignore =
|
||||
${esp32_common.lib_ignore}
|
||||
NimBLE-Arduino
|
||||
libpax
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define BUTTON_PIN 0 // BOOT button
|
||||
|
||||
#define BATTERY_PIN 1
|
||||
#define ADC_CHANNEL ADC_CHANNEL_0
|
||||
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
|
||||
#define ADC_MULTIPLIER 103.0 // Calibrated value
|
||||
#define ADC_ATTENUATION ADC_ATTEN_DB_0
|
||||
#define ADC_CTRL 37
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
// Battery voltage monitoring - TODO: test, currently untested, copied from T3S3 variant
|
||||
#define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC_CHANNEL_0
|
||||
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
|
||||
#define ADC_MULTIPLIER \
|
||||
2.11 // ratio of voltage divider = 2.0 (R10=1M, R13=1M), plus some undervoltage correction - TODO: this was carried over from
|
||||
// the T3S3, test to see if the undervoltage correction is needed.
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
// Battery
|
||||
// #define BATTERY_PIN 2
|
||||
#define BATTERY_PIN 17
|
||||
// #define ADC_CHANNEL ADC_CHANNEL_1
|
||||
#define ADC_CHANNEL ADC_CHANNEL_6
|
||||
// #define ADC_CHANNEL ADC1_GPIO2_CHANNEL
|
||||
#define ADC_CHANNEL ADC2_GPIO17_CHANNEL
|
||||
#define BATTERY_SENSE_RESOLUTION_BITS 12
|
||||
#define BATTERY_SENSE_RESOLUTION 4096.0
|
||||
#undef AREF_VOLTAGE
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
// USB_CHECK
|
||||
#define EXT_PWR_DETECT 12
|
||||
#define BATTERY_PIN 8
|
||||
#define ADC_CHANNEL ADC_CHANNEL_7
|
||||
#define ADC_CHANNEL ADC1_GPIO8_CHANNEL
|
||||
|
||||
#define ADC_MULTIPLIER 2.0 // 2.0 + 10% for correction of display undervoltage.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// #define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to
|
||||
// measure battery voltage ratio of voltage divider = 2.0 (assumption)
|
||||
// #define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage.
|
||||
// #define ADC_CHANNEL ADC_CHANNEL_0
|
||||
// #define ADC_CHANNEL ADC1_GPIO1_CHANNEL
|
||||
|
||||
#define I2C_SDA SDA // 21
|
||||
#define I2C_SCL SCL // 15
|
||||
|
||||
@@ -26,4 +26,3 @@ build_flags =
|
||||
-DBOARD_HAS_PSRAM
|
||||
-mfix-esp32-psram-cache-issue
|
||||
-DARDUINO_USB_MODE=0
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
|
||||
@@ -8,9 +8,13 @@ board_build.f_cpu = 240000000L
|
||||
upload_protocol = esptool
|
||||
;upload_port = /dev/ttyACM0
|
||||
upload_speed = 921600
|
||||
build_unflags =
|
||||
${esp32s3_base.build_unflags}
|
||||
-DARDUINO_USB_MODE=1
|
||||
build_flags =
|
||||
${esp32s3_base.build_flags}
|
||||
-D PRIVATE_HW
|
||||
-I variants/esp32s3/diy/my_esp32s3_diy_oled
|
||||
-DBOARD_HAS_PSRAM
|
||||
-mfix-esp32-psram-cache-issue
|
||||
-DARDUINO_USB_MODE=0
|
||||
|
||||
@@ -6,10 +6,13 @@ board_build.partitions = default_16MB.csv
|
||||
board_level = extra
|
||||
board_upload.flash_size = 16MB ;Specify the FLASH capacity as 16MB
|
||||
board_build.arduino.memory_type = qio_opi ;Enable internal PSRAM
|
||||
build_unflags =
|
||||
${esp32s3_base.build_unflags}
|
||||
-D ARDUINO_USB_MODE=1
|
||||
build_flags =
|
||||
${esp32s3_base.build_flags}
|
||||
-D EBYTE_ESP32_S3
|
||||
-D BOARD_HAS_PSRAM
|
||||
-D ARDUINO_USB_MODE=1
|
||||
-D ARDUINO_USB_MODE=0
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1
|
||||
-I variants/esp32s3/diy/t-energy-s3_e22
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// Battery
|
||||
#define BATTERY_PIN 3
|
||||
#define ADC_MULTIPLIER 2.0
|
||||
#define ADC_CHANNEL ADC_CHANNEL_2
|
||||
#define ADC_CHANNEL ADC1_GPIO3_CHANNEL
|
||||
|
||||
// Button on NanoVHF PCB
|
||||
#define BUTTON_PIN 39
|
||||
|
||||
@@ -15,9 +15,9 @@ static const uint8_t MOSI = 48;
|
||||
static const uint8_t MISO = 47;
|
||||
static const uint8_t SCK = 41;
|
||||
|
||||
// static const uint8_t SPI_MOSI = 6;
|
||||
// static const uint8_t SPI_SCK = 5;
|
||||
// static const uint8_t SPI_MISO = 4;
|
||||
static const uint8_t SPI_MOSI = 6;
|
||||
static const uint8_t SPI_SCK = 5;
|
||||
static const uint8_t SPI_MISO = 4;
|
||||
|
||||
static const uint8_t A0 = 1;
|
||||
static const uint8_t A1 = 2;
|
||||
|
||||
@@ -38,9 +38,6 @@ build_flags = ${esp32s3_base.build_flags} -Os
|
||||
-D USE_PACKET_API
|
||||
-D HAS_SDCARD
|
||||
-D SD_SPI_FREQUENCY=75000000
|
||||
-D SPI_MISO=4
|
||||
-D SPI_MOSI=6
|
||||
-D SPI_SCK=5
|
||||
|
||||
lib_deps = ${esp32s3_base.lib_deps}
|
||||
${device-ui_base.lib_deps}
|
||||
@@ -50,9 +47,7 @@ lib_deps = ${esp32s3_base.lib_deps}
|
||||
earlephilhower/ESP8266SAM@1.1.0
|
||||
# renovate: datasource=custom.pio depName=TCA9534 packageName=hideakitai/library/TCA9534
|
||||
hideakitai/TCA9534@0.1.1
|
||||
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
|
||||
lovyan03/LovyanGFX@1.2.19
|
||||
; REVISIT note: v1.2.7 breaks the elecrow 7" display functionality
|
||||
lovyan03/LovyanGFX@1.2.0 ; note: v1.2.7 breaks the elecrow 7" display functionality
|
||||
|
||||
[crowpanel_small_esp32s3_base] ; 2.4, 2.8, 3.5 inch
|
||||
extends = crowpanel_base
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
// https://www.waveshare.com/img/devkit/ESP32-S3-Pico/ESP32-S3-Pico-details-inter-1.jpg
|
||||
// digram is incorrect labeled as battery pin is getting readings on GPIO7_CH1?
|
||||
#define BATTERY_PIN 7
|
||||
#define ADC_CHANNEL ADC_CHANNEL_6
|
||||
// #define ADC_CHANNEL ADC_CHANNEL_5
|
||||
#define ADC_CHANNEL ADC1_GPIO7_CHANNEL
|
||||
// #define ADC_CHANNEL ADC1_GPIO6_CHANNEL
|
||||
// ratio of voltage divider = 3.0 (R17=200k, R18=100k)
|
||||
#define ADC_MULTIPLIER 3.1 // 3.0 + a bit for being optimistic
|
||||
|
||||
|
||||
@@ -4,16 +4,6 @@ custom_esp32_kind = esp32s3
|
||||
|
||||
monitor_speed = 115200
|
||||
|
||||
build_flags =
|
||||
${esp32_common.build_flags}
|
||||
-mtext-section-literals
|
||||
|
||||
custom_sdkconfig =
|
||||
${esp32_common.custom_sdkconfig}
|
||||
; CONFIG_BT_NIMBLE_EXT_ADV=y
|
||||
; CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=257
|
||||
; CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2
|
||||
|
||||
lib_deps =
|
||||
${esp32_common.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user