Compare commits

..
Author SHA1 Message Date
Thomas Göttgens 8b8d8938b9 Merge branch 'pioarduino' into crowpanel-p4 2026-05-07 16:40:44 +02:00
Austin 0bb89644f9 Merge branch 'develop' into pioarduino 2026-05-05 09:10:28 -04:00
Manuel aef13bef89 Merge branch 'develop' into pioarduino 2026-04-28 20:47:04 +01:00
Manuel 6d31915e57 use adc_channel_t in variant.h 2026-04-28 21:46:01 +02:00
Austin aa89faa619 Merge branch 'develop' into pioarduino 2026-04-27 20:15:22 -04:00
Manuel ee796c75c9 Refactor watchdog timer initialization and handling 2026-04-27 16:24:01 +02:00
ManuelandCopilot 918577b5b5 added maintainers note to bluetooth variants
Co-authored-by: Copilot <copilot@github.com>
2026-04-26 21:57:30 +02:00
Manuel c5a477f4c1 remove dummy implementation 2026-04-26 21:36:15 +02:00
Manuel fc79cf3971 fix guard 2026-04-26 21:35:43 +02:00
Manuel 0004616878 Merge branch 'pioarduino' into crowpanel-p4 2026-04-25 22:33:14 +02:00
Austin Lane a0a7209229 Use mverch's iram_memset hack for all OG-ESP32 2026-04-25 14:29:36 -04:00
Austin 16f2ff8c9e Merge branch 'develop' into pioarduino 2026-04-25 13:37:03 -04:00
Austin 894a2f43bd Merge branch 'develop' into pioarduino 2026-04-25 07:34:26 -04:00
Austin Lane e470723811 tlora-c6: Disable Screen
MESHTASTIC_EXCLUDE_SCREEN=1 on tlora-c6.

It doesn't have a screen, and this gets it compiling again (saving flash).
2026-04-24 22:44:11 -04:00
Austin LaneandCopilot 13ee648e3d Fix ESP32-C6 linker errors.
Align .text.handler_execute section to 4 bytes and update watchdog timer core mask configuration

Co-authored-by: Copilot <copilot@github.com>
2026-04-24 20:59:49 -04:00
AustinandCopilot bec00b0a57 platformio-custom: Modify mtjson target dependency to prevent fake-success. (#10291)
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 19:52:23 -04:00
ManuelandCopilot 025630937c BLEDevice::deinit() added
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 21:02:01 +02:00
Manuel 527e2dc7f7 ignore trunk 2026-04-24 19:24:55 +02:00
Manuel 095838f375 trunk fmt 2026-04-24 18:28:53 +02:00
ManuelandCopilot e66fcde7e3 robot tbeam cache error fix
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 16:14:58 +02:00
Manuel dc478d2163 hackaday fix 2026-04-24 09:46:48 +02:00
Manuel f85ed3e59e Merge branch 'develop' into pioarduino 2026-04-24 09:07:26 +02:00
Manuel acb7ac6e3d sensecap indicator fixes after upgrade arduino-esp & lovyanGFX libs 2026-04-23 23:21:01 +02:00
Manuel 91314c40f5 Merge branch 'pioarduino' into crowpanel-p4 2026-04-23 18:05:07 +02:00
Austin Lane c6282d3a3c Re-add tool-mklittlefs 2026-04-22 20:57:17 -04:00
Austin Lane 3f35f2c6d7 Re-enable littlefs json manifest
This works locally again :)
Not sure what changed
2026-04-22 19:12:56 -04:00
Manuel 407ab7abda Merge branch 'pioarduino' into crowpanel-p4 2026-04-23 00:34:57 +02:00
Catalin Patulea 6847f232a7 Build ESP32 original with NimBLE ('custom_sdkconfig' approach). (#10235) 2026-04-22 18:33:53 -04:00
Manuel e9cd77e758 Merge branch 'pioarduino' into crowpanel-p4 2026-04-22 23:42:31 +02:00
Austin Lane f586824435 Fix Power.cpp check warning
Local variable 'config' shadows outer variable [shadowVariable]
2026-04-22 17:25:34 -04:00
mverch67 b0d7440bad fix infinite loop 2026-04-22 22:45:53 +02:00
mverch67 8dc82c45b8 fix linker error using response file (p4 only) 2026-04-22 22:39:38 +02:00
Austin Lane 96538b492a I thought I fixed this 2026-04-22 16:39:32 -04:00
Manuel 023461cd92 Merge branch 'pioarduino' into crowpanel-p4 2026-04-22 22:28:41 +02:00
Austin 41df7ef0bf Merge branch 'develop' into pioarduino 2026-04-22 12:47:46 -04:00
mverch67 e49dab1f08 update p4 esp_hosted for BT 2026-04-22 14:04:12 +02:00
Manuel 74f7d9ce3c Merge branch 'develop' into pioarduino 2026-04-22 14:01:13 +02:00
Austin Lane 6c74052ec3 InkHUD: Fix type casting for message size in saveToFlash method
inkhud compiles again!
2026-04-21 17:56:35 -04:00
Austin Lane fb7d34afb9 Cleanup: Fix ADC channels on new variants 2026-04-21 17:08:29 -04:00
Austin Lane 814773f50e ESP32: Disable classic bluetooth 2026-04-21 16:17:15 -04:00
Austin Lane 621aeb29b5 Cleanup after merge 2026-04-21 11:23:47 -04:00
copilot-swe-agent[bot]andvidplace7 6f1d611c34 Merge branch 'develop' into pioarduino (resolve conflicts favoring pioarduino)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2026-04-21 13:40:59 +00:00
Austin Lane fc871f42e4 Add extension from pioarduino nag
"Jason2866.esp-decoder"
2026-04-21 09:15:09 -04:00
AustinandCatalin Patulea 9da0ee9c51 NimBLE-Arduino -> Arduino "BLE" (3.3.x) migration (#10164)
* NimBLE-Arduino -> Arduino "BLE" (3.3.x) migration

* More NimBLE

* Fix Device Name in ATT Read Request (0x2A00).

Device Name is exposed in two places:

- Advertisement data: this is set properly in startAdvertising.
- GATT attribute Device Name (0x2A00). This one is handled internally in NimBLE
  and comes from ble_svc_gap_device_name_set. This is set initially, but then
  BLEDevice::createServer calls ble_svc_gap_init which resets the device name.
  This causes the device to apparently "change name after pairing":

< ACL Data TX:... flags 0x00 dlen 7  #113 [hci0] 14.241149
      ATT: Read Request (0x0a) len 2
        Handle: 0x0003 Type: Device Name (0x2a00)
> ACL Data RX: Handle 2048 flags 0x02 dlen 11             #115 [hci0] 14.269050
      ATT: Read Response (0x0b) len 6
        Value[6]: 6e696d626c65   # "nimble"

Workaround this by setting the device name once again after
BLEDevice::createServer.

* Temporarily lower CORE_DEBUG_LEVEL to INFO to avoid triggering an apparent ESP-IDF Bluetooth bug when re-connecting to Pixel 8 Android devices.

Initial pairing works, but after ESP32 is rebooted, phone fails to reconnect. Meshtastic app shows it as disconnecting immediately. LightBlue shows a more detailed error "Peripheral Connection - Warning: onConnectionStatusChange: status 61" (0x3D - MIC Failure).

Bug report to Espresssif: https://github.com/espressif/esp-idf/issues/18126#issuecomment-4286197744

* Temporarily disable ble_gap_set_data_len, causes crash with Pixel 8 Android reconnect.

Crash looks like this:
  [ 11966][E][BLEAdvertising.cpp:341] setScanResponseData(): ble_gap_adv_rsp_set_data: 22
  [ 11975][E][BLEAdvertising.cpp:1554] start(): Host reset, wait for sync.
  ERROR | ??:??:?? 11 BLE failed to start advertising
  Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

  Core  0 register dump:
  PC      : 0x420e6190  PS      : 0x00060730  A0      : 0x820e158b  A1      : 0x3fce50c0
  A2      : 0x00000000  A3      : 0x3fcb8600  A4      : 0x3fcb85cc  A5      : 0x00000000
  A6      : 0x00000000  A7      : 0x00000c03  A8      : 0x00000000  A9      : 0x3fce50b0
  A10     : 0x0000000e  A11     : 0x00000000  A12     : 0x00000010  A13     : 0x3fce50e0
  A14     : 0x00000c03  A15     : 0x00000001  SAR     : 0x0000001e  EXCCAUSE: 0x0000001c
  EXCVADDR: 0x00000000  LBEG    : 0x400570e8  LEND    : 0x400570f3  LCOUNT  : 0x00000000

  Backtrace: 0x420e618d:0x3fce50c0 0x420e1588:0x3fce5110 0x420dfe87:0x3fce5200 0x420dfefb:0x3fce5220 0x420dff3f:0x3fce5240 0x4219602b:0x3fce5260 0x4037b0e5:0x3fce5280 0x4201edf3:0x3fce52a0

Connection seems fast enough even without this. We'll investigate the
reason for the crash and re-enable once it's safe.

---------

Co-authored-by: Catalin Patulea <cronos586@gmail.com>
2026-04-21 09:07:53 -04:00
Manuel Verch 76734f0067 fix chip variant 2026-04-20 16:00:18 +02:00
Manuel fd65de142b Fix copy&paste 2026-04-16 22:51:25 +02:00
Manuel 5ae50b607d add class HostedBluetooth 2026-04-16 22:38:42 +02:00
Manuel 20fb1b2fc2 fix class definition 2026-04-16 22:17:15 +02:00
Manuel a30ee4748a fix LDO warnings 2026-04-16 22:06:01 +02:00
Manuel 7726863043 update boards and pins_arduino.h definition 2026-04-16 19:56:25 +02:00
Austin Lane 61bab08d9e Pioarduino 55.03.38-1 2026-04-14 18:04:06 -04:00
Manuel da2cd98d98 update esp_hosted parameters for BT 2026-04-14 23:52:33 +02:00
Manuel 979a1a1554 exclude wifi for now 2026-04-13 23:32:54 +02:00
Manuel a9a655c8f3 Merge branch 'pioarduino' into crowpanel-p4 2026-04-13 21:52:23 +02:00
Manuel e85ebc6859 enable esp_hosted for esp32-p4 (experimental) 2026-04-13 21:31:45 +02:00
Manuel c9fc1edb4f revert a6f6175, update to 3.3.8 2026-04-13 21:30:32 +02:00
Manuel 483bb33749 Merge branch 'develop' into pioarduino 2026-04-13 21:27:36 +02:00
Manuel 62f62b4e8c update target 2026-04-13 21:15:19 +02:00
Manuel ad568021ba experimental esp_hosted implementation 2026-04-13 20:26:51 +02:00
Manuel ff8102e753 preliminary workaround setLDOPower() crash 2026-04-13 20:25:57 +02:00
Manuel 3c7d5435fe add targets 7", 9" and 10.1" 2026-04-13 20:24:51 +02:00
Manuel dd1184f7af update target name 2026-04-13 19:54:37 +02:00
Manuel 46c1230c39 remove I2C0, add GPS 2026-04-13 15:29:47 +02:00
Manuel fa8f0f0dd8 update nodes 2026-04-09 18:32:32 +02:00
Manuel e22a22b325 fix/workaround SDMMC 2026-04-09 17:19:49 +02:00
Manuel 35e4a877ee Merge branch 'develop' into pioarduino 2026-04-09 17:02:38 +02:00
Manuel a6f6175535 config for MUI 2026-04-09 00:05:54 +02:00
Manuel 3e5f95f8ed don't ignore esp_lcd 2026-04-09 00:02:26 +02:00
Manuel ca15aa86f2 MESHTASTIC_EXCLUDE_WEBSERVER 2026-04-09 00:00:41 +02:00
Manuel a3dcad08d4 MUI update (320x240 for tests) 2026-04-08 22:55:34 +02:00
Manuel 70caa76fa0 Merge branch 'pioarduino' into crowpanel-p4 2026-04-04 09:28:45 +02:00
Austin Lane edb975d886 Switch to meshtastic/esp32_https_server fork (idf5 branch) 2026-03-30 13:55:38 -04:00
Austin 8d649e9a0b Merge branch 'develop' into pioarduino 2026-03-30 13:42:13 -04:00
Austin 5c1e5f3dd6 Merge branch 'develop' into pioarduino 2026-03-02 09:54:36 -05:00
Manuel f1ffd05a55 initial setup for lora 2026-02-03 23:05:09 +01:00
Manuel 6bde1d5fbb esp32-p4 specific adaptations 2026-02-03 22:44:57 +01:00
Manuel bc311616a3 check for esp32 w/ wifi 2026-02-03 22:26:53 +01:00
Manuel 9fdeec173e fix esp32p4.ini 2026-02-03 22:26:20 +01:00
Austin Lane b24a6676a6 Update lovyangfx from develop commit to 1.2.19 2026-01-23 14:20:10 -05:00
Austin a3f39de4b9 Merge branch 'develop' into pioarduino 2026-01-23 14:10:12 -05:00
Austin d235d3f933 Merge branch 'develop' into pioarduino 2026-01-22 10:54:52 -05:00
Austin Lane 67726f9e3a pioarduino: use legacy esptoolpy naming (forward-compatible) 2026-01-21 21:50:25 -05:00
Austin Lane 3cbc9c91cb pioarduino: disable network provisioning (wifiprov) 2026-01-21 21:09:32 -05:00
Austin Lane 5c2afbf8ce pioarduino: T-Beam 1W CDC mode 2026-01-21 19:43:33 -05:00
Austin Lane f1ca363efe pioarduino: Fix OG ESP32 duplicate libs 2026-01-21 18:39:15 -05:00
Austin Lane 814dc2db1b pioarduino 3.3.6 *release*
chasing the release
2026-01-21 18:20:51 -05:00
Austin Lane fbeabe29ed pioarduino 3.3.6 2026-01-21 18:09:36 -05:00
Austin Lane 7235afec2f pioarduino: Update LovyanGFX
Includes Manuel's recent commit
2026-01-20 19:51:10 -05:00
mverch67 ef7036e9ed preliminary esp32p4.ini 2026-01-21 01:10:12 +01:00
Austin c997e3bb65 Merge branch 'develop' into pioarduino 2026-01-20 19:03:57 -05:00
Austin 028f781ea5 Merge branch 'develop' into pioarduino 2026-01-13 12:05:06 -05:00
Austin Lane 86cdff463b Use pioarduino develop
The latest fixes and the latest bugs!
2026-01-13 10:59:05 -05:00
Austin 778090a269 Merge branch 'develop' into pioarduino 2026-01-05 17:35:48 -05:00
Austin Lane 14e9cb0fc3 ESP32c6 align text.handler_execute same as C3 2026-01-02 10:17:41 -05:00
Austin Lane aa506ce4ab Migrate esp32 families to pioarduino platform 2025-12-30 10:37:19 -05:00
182 changed files with 2565 additions and 1394 deletions
+1 -1
View File
@@ -8,7 +8,7 @@
"features": {
"ghcr.io/devcontainers/features/python:1": {
"installTools": true,
"version": "3.14"
"version": "3.13"
}
},
"customizations": {
+4 -4
View File
@@ -1,10 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
"Jason2866.esp-decoder",
"pioarduino.pioarduino-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
"ms-vscode.cpptools-extension-pack",
"platformio.platformio-ide"
]
}
+1 -1
View File
@@ -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 || true
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
@@ -87,9 +87,6 @@
</screenshots>
<releases>
<release version="2.7.24" date="2026-05-08">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.24</url>
</release>
<release version="2.7.23" date="2026-04-14">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.23</url>
</release>
+8 -4
View File
@@ -293,9 +293,12 @@ if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
board_arch = infer_architecture(env.BoardConfig())
should_skip_manifest = board_arch is None
# 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":
# 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"]
# Build littlefs image as part of mtjson target
# Equivalent to `pio run -t buildfs`
target_lfs = env.DataToBin(
@@ -309,7 +312,8 @@ if should_skip_manifest:
env.AddCustomTarget(
name="mtjson",
dependencies=mtjson_deps,
# For host/native envs, avoid depending on 'buildprog' (some targets don't define it)
dependencies=[],
actions=[skip_manifest],
title="Meshtastic Manifest (skipped)",
description="mtjson generation is skipped for native environments",
+1 -1
View File
@@ -7,7 +7,7 @@
"extra_flags": [
"-D CDEBYTE_EORA_S3",
"-D ARDUINO_USB_CDC_ON_BOOT=1",
"-D ARDUINO_USB_MODE=0",
"-D ARDUINO_USB_MODE=1",
"-D ARDUINO_RUNNING_CORE=1",
"-D ARDUINO_EVENT_RUNNING_CORE=1",
"-D BOARD_HAS_PSRAM"
+1 -1
View File
@@ -6,7 +6,7 @@
"core": "esp32",
"extra_flags": [
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1",
"-DBOARD_HAS_PSRAM"
+32
View File
@@ -0,0 +1,32 @@
{
"build": {
"core": "esp32",
"extra_flags": ["-DBOARD_HAS_PSRAM"],
"f_cpu": "360000000L",
"f_flash": "80000000L",
"f_psram": "200000000L",
"flash_mode": "qio",
"hwids": [["0x1A86", "0x7522"]],
"mcu": "esp32p4",
"chip_variant": "esp32p4",
"variant": "esp32p4"
},
"arduino": {
"partitions": "default_16MB.csv"
},
"connectivity": ["bluetooth", "openthread"],
"debug": {
"openocd_target": "esp32p4.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "CrowPanel Advanced ESP32-P4 HMI AI Display",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 512000,
"maximum_size": 16777216,
"require_upload_port": true,
"speed": 1500000
},
"url": "https://www.elecrow.com/crowpanel-advanced-5inch-esp32-p4-hmi-ai-display-800x480-ips-touch-screen-with-wifi-6.html",
"vendor": "Elecrow"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
+1 -1
View File
@@ -9,7 +9,7 @@
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
+1 -1
View File
@@ -9,7 +9,7 @@
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
+1 -1
View File
@@ -9,7 +9,7 @@
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
+1 -1
View File
@@ -8,7 +8,7 @@
"extra_flags": [
"-DHELTEC_WIRELESS_TRACKER",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
+1 -1
View File
@@ -7,7 +7,7 @@
"core": "esp32",
"extra_flags": [
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
+1 -1
View File
@@ -8,7 +8,7 @@
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=0"
],
+1 -1
View File
@@ -9,7 +9,7 @@
"-DBOARD_HAS_PSRAM",
"-DLILYGO_TBEAM_1W",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
+1 -1
View File
@@ -8,7 +8,7 @@
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
+1 -1
View File
@@ -8,7 +8,7 @@
"-DBOARD_HAS_PSRAM",
"-DLILYGO_TBEAM_S3_CORE",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
+1 -1
View File
@@ -7,7 +7,7 @@
"extra_flags": [
"-DLILYGO_T3S3_V1",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1",
"-DBOARD_HAS_PSRAM"
+1 -1
View File
@@ -10,7 +10,7 @@
"-DBOARD_HAS_PSRAM",
"-DUNPHONE_SPIN=9",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
-6
View File
@@ -1,9 +1,3 @@
meshtasticd (2.7.24.0) unstable; urgency=medium
* Version 2.7.24
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Fri, 08 May 2026 10:44:12 +0000
meshtasticd (2.7.23.0) unstable; urgency=medium
* Version 2.7.23
+7
View File
@@ -0,0 +1,7 @@
# 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 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x640000
5 app1 app ota_1 0x650000 0x640000
6 spiffs data spiffs 0xc90000 0x360000
7 coredump data coredump 0xFF0000 0x10000
+7
View File
@@ -0,0 +1,7 @@
# 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,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x330000
5 app1 app ota_1 0x340000 0x330000
6 spiffs data spiffs 0x670000 0x180000
7 coredump data coredump 0x7F0000 0x10000
+3 -14
View File
@@ -70,17 +70,6 @@ def esp32_create_combined_bin(source, target, env):
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
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"])
# Enable Newlib Nano formatting to save space
# ...but allow printf float support (compromise)
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
+23
View File
@@ -0,0 +1,23 @@
#!/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)
+1 -1
View File
@@ -2,7 +2,7 @@
; https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = tbeam
default_envs = heltec-v3
extra_configs =
variants/*/*.ini
+25 -1
View File
@@ -14,4 +14,28 @@ const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70,
const uint8_t LEGACY_LOGRADIO_UUID_16[16u] = {0xe2, 0xf2, 0x1e, 0xbe, 0xc5, 0x15, 0xcf, 0xaa,
0x6b, 0x43, 0xfa, 0x78, 0x38, 0xd2, 0x6f, 0x6c};
const uint8_t LOGRADIO_UUID_16[16u] = {0x47, 0x95, 0xDF, 0x8C, 0xDE, 0xE9, 0x44, 0x99,
0x23, 0x44, 0xE6, 0x06, 0x49, 0x6E, 0x3D, 0x5A};
0x23, 0x44, 0xE6, 0x06, 0x49, 0x6E, 0x3D, 0x5A};
void BluetoothApi::setup() {}
void BluetoothApi::shutdown() {}
void BluetoothApi::deinit() {}
void BluetoothApi::clearBonds() {}
bool BluetoothApi::isActive()
{
return false;
}
bool BluetoothApi::isConnected()
{
return false;
}
void BluetoothApi::sendLog(const uint8_t *logMessage, size_t length)
{
(void)logMessage;
(void)length;
}
void updateBatteryLevel(uint8_t level) __attribute__((weak));
void updateBatteryLevel(uint8_t level)
{
(void)level;
}
+5
View File
@@ -24,9 +24,14 @@ void updateBatteryLevel(uint8_t level);
class BluetoothApi
{
public:
virtual ~BluetoothApi() = default;
virtual void setup();
virtual void shutdown();
virtual void deinit();
virtual void clearBonds();
virtual bool isActive();
virtual bool isConnected();
virtual int getRssi() = 0;
virtual void sendLog(const uint8_t *logMessage, size_t length);
};
+18 -15
View File
@@ -26,8 +26,6 @@ SOFTWARE.*/
#include "DebugConfiguration.h"
#include <memory>
#ifdef ARCH_PORTDUINO
#include "platform/portduino/PortduinoGlue.h"
#endif
@@ -121,22 +119,27 @@ bool Syslog::vlogf(uint16_t pri, const char *fmt, va_list args)
bool Syslog::vlogf(uint16_t pri, const char *appName, const char *fmt, va_list args)
{
// First measure the formatted length using a copy of args; passing args directly
// to vsnprintf consumes it, and reusing a consumed va_list is undefined behavior.
va_list args_measure;
va_copy(args_measure, args);
int needed = vsnprintf(nullptr, 0, fmt, args_measure);
va_end(args_measure);
char *message;
size_t initialLen;
size_t len;
bool result;
if (needed < 0)
return false; // encoding error
initialLen = strlen(fmt);
auto message = std::unique_ptr<char[]>(new char[static_cast<size_t>(needed) + 1]);
int written = vsnprintf(message.get(), static_cast<size_t>(needed) + 1, fmt, args);
if (written < 0)
return false;
message = new char[initialLen + 1];
return this->_sendLog(pri, appName, message.get());
len = vsnprintf(message, initialLen + 1, fmt, args);
if (len > initialLen) {
delete[] message;
message = new char[len + 1];
vsnprintf(message, len + 1, fmt, args);
}
result = this->_sendLog(pri, appName, message);
delete[] message;
return result;
}
inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *message)
+2 -3
View File
@@ -151,9 +151,8 @@ extern "C" void logLegacy(const char *level, const char *fmt, ...);
#include <RAK13800_W5100S.h>
#endif // HAS_ETHERNET
#if HAS_ETHERNET && defined(USE_WS5500)
#include <ETHClass2.h>
#define ETH ETH2
#if HAS_ETHERNET && defined(ARCH_ESP32)
#include <ETH.h>
#endif // HAS_ETHERNET
#if HAS_WIFI
+1 -1
View File
@@ -277,7 +277,7 @@ void fsInit()
*/
void setupSDCard()
{
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI)
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI) && !defined(HAS_SD_MMC)
concurrency::LockGuard g(spiLock);
SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
if (!SD.begin(SDCARD_CS, SDHandler, SD_SPI_FREQUENCY)) {
+126 -83
View File
@@ -24,6 +24,13 @@
#include "meshUtils.h"
#include "power/PowerHAL.h"
#include "sleep.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>
#include <esp_err.h>
#endif
#if defined(ARCH_PORTDUINO)
#include "api/WiFiServerAPI.h"
@@ -63,9 +70,8 @@
#include <WiFi.h>
#endif
#if HAS_ETHERNET && defined(USE_WS5500)
#include <ETHClass2.h>
#define ETH ETH2
#if HAS_ETHERNET && defined(ARCH_ESP32)
#include <ETH.h>
#endif // HAS_ETHERNET
#endif
@@ -77,21 +83,86 @@
#if defined(BATTERY_PIN) && defined(ARCH_ESP32)
#ifndef BAT_MEASURE_ADC_UNIT // ADC1 is default
static const adc1_channel_t adc_channel = ADC_CHANNEL;
static const adc_channel_t adc_channel = ADC_CHANNEL;
static const adc_unit_t unit = ADC_UNIT_1;
#else // ADC2
static const adc2_channel_t adc_channel = ADC_CHANNEL;
#else // ADC2
static const adc_channel_t adc_channel = ADC_CHANNEL;
static const adc_unit_t unit = ADC_UNIT_2;
RTC_NOINIT_ATTR uint64_t RTC_reg_b;
#endif // BAT_MEASURE_ADC_UNIT
esp_adc_cal_characteristics_t *adc_characs = (esp_adc_cal_characteristics_t *)calloc(1, sizeof(esp_adc_cal_characteristics_t));
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 // BATTERY_PIN && ARCH_ESP32
#ifdef EXT_PWR_DETECT
@@ -367,8 +438,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
scaled *= operativeAdcMultiplier;
#elif defined(ARCH_ESP32) // ADC block for espressif platforms
raw = espAdcRead();
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
scaled *= operativeAdcMultiplier;
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) {
// Fallback approximate conversion without calibration
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 // block for all other platforms
#ifdef ARCH_NRF52
concurrency::LockGuard saadcGuard(concurrency::nrf52SaadcLock);
@@ -410,51 +493,22 @@ class AnalogBatteryLevel : public HasBatteryLevel
uint32_t raw = 0;
uint8_t raw_c = 0; // raw reading counter
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
if (!adc_handle) {
LOG_ERROR("ADC oneshot handle not initialized");
return 0;
}
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
int val_ = adc1_get_raw(adc_channel);
if (val_ >= 0) { // save only valid readings
raw += val_;
int val = 0;
esp_err_t err = adc_oneshot_read(adc_handle, adc_channel, &val);
if (err == ESP_OK) {
raw += val;
raw_c++;
}
// delayMicroseconds(100);
}
#else // ADC2
#ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3
// ADC2 wifi bug workaround not required, breaks compile
// On ESP32S3, ADC2 can take turns with Wifi (?)
int32_t adc_buf;
esp_err_t read_result;
// Multiple samples
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
adc_buf = 0;
read_result = -1;
read_result = adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf);
if (read_result == ESP_OK) {
raw += adc_buf;
raw_c++; // Count valid samples
} else {
LOG_DEBUG("An attempt to sample ADC2 failed");
LOG_DEBUG("ADC read failed: %s", esp_err_to_name(err));
}
}
#else // Other ESP32
int32_t adc_buf = 0;
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
// ADC2 wifi bug workaround, see
// https://github.com/espressif/arduino-esp32/issues/102
WRITE_PERI_REG(SENS_SAR_READ_CTRL2_REG, RTC_reg_b);
SET_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DATA_INV);
adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf);
raw += adc_buf;
raw_c++;
}
#endif // BAT_MEASURE_ADC_UNIT
#endif // End BAT_MEASURE_ADC_UNIT
return (raw / (raw_c < 1 ? 1 : raw_c));
}
#endif
@@ -666,42 +720,31 @@ bool Power::analogInit()
#ifdef ARCH_STM32WL
analogReadResolution(BATTERY_SENSE_RESOLUTION_BITS);
#elif defined(ARCH_ESP32) // ESP32 needs special analog stuff
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = unit,
};
#ifndef ADC_WIDTH // max resolution by default
static const adc_bits_width_t width = ADC_WIDTH_BIT_12;
#else
static const adc_bits_width_t width = ADC_WIDTH;
#endif
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
adc1_config_width(width);
adc1_config_channel_atten(adc_channel, atten);
#else // ADC2
adc2_config_channel_atten(adc_channel, atten);
#ifndef CONFIG_IDF_TARGET_ESP32S3
// ADC2 wifi bug workaround
// Not required with ESP32S3, breaks compile
RTC_reg_b = READ_PERI_REG(SENS_SAR_READ_CTRL2_REG);
#endif
#endif
// calibrate ADC
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_characs);
// show ADC characterization base
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
LOG_INFO("ADC config based on Two Point values stored in eFuse");
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
LOG_INFO("ADC config based on reference voltage stored in eFuse");
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;
}
}
#ifdef CONFIG_IDF_TARGET_ESP32S3
// ESP32S3
else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP_FIT) {
LOG_INFO("ADC config based on Two Point values and fitting curve "
"coefficients stored in eFuse");
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;
}
#endif
else {
LOG_INFO("ADC config based on default reference voltage");
}
#endif // ARCH_ESP32
adc_calibrated = initAdcCalibration();
#endif // ARCH_ESP32
// NRF52 ADC init moved to powerHAL_init in nrf52 platform
+2 -2
View File
@@ -222,7 +222,7 @@ void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_
if (config.security.debug_log_api_enabled && !pauseBluetoothLogging) {
bool isBleConnected = false;
#ifdef ARCH_ESP32
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();
isBleConnected = bluetoothApi && bluetoothApi->isActive() && bluetoothApi->isConnected();
#elif defined(ARCH_NRF52)
isBleConnected = nrf52Bluetooth != nullptr && nrf52Bluetooth->isConnected();
#endif
@@ -238,7 +238,7 @@ void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[meshtastic_LogRecord_size]);
size_t size = pb_encode_to_bytes(buffer.get(), meshtastic_LogRecord_size, meshtastic_LogRecord_fields, &logRecord);
#ifdef ARCH_ESP32
nimbleBluetooth->sendLog(buffer.get(), size);
bluetoothApi->sendLog(buffer.get(), size);
#elif defined(ARCH_NRF52)
nrf52Bluetooth->sendLog(buffer.get(), size);
#endif
+762
View File
@@ -0,0 +1,762 @@
#include "configuration.h"
#if defined(CONFIG_IDF_TARGET_ESP32P4) && defined(CONFIG_ESP_HOSTED_ENABLED) && !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "BluetoothStatus.h"
#include "PowerFSM.h"
#include "bluetooth/HostedBluetooth.h"
#include "concurrency/OSThread.h"
#include "esp32-hal-hosted.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_hosted.h"
#include "esp_hosted_event.h"
#include "main.h"
#include "mesh/PhoneAPI.h"
#include "mesh/mesh-pb-constants.h"
#include <BLEAdvertising.h>
#include <BLEDevice.h>
#include <BLESecurity.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <array>
#include <atomic>
#include <climits>
#include <cstring>
#include <driver/gpio.h>
#include <mutex>
#include "host/ble_gap.h"
#include "host/ble_store.h"
namespace
{
/*
* Maintainer note: HostedBluetooth intentionally stays close to NimbleBluetooth
* but is not a strict drop-in equivalent.
*
* Intentional differences from NimbleBluetooth include:
* - ESP-Hosted transport lifecycle handling (event callbacks + CP reset GPIO control).
* - Data-length update behavior (Hosted currently requests ble_gap_set_data_len on connect).
* - No Battery Service exposure here.
*
* If you modify common BLE flow in one class, review and likely mirror in both:
* - PhoneAPI queueing/synchronization between BLE callbacks and runOnce().
* - Security/pairing config and passkey UX/status updates.
* - Mesh GATT characteristics, permissions, and advertising setup.
* - Connection parameter tuning and reconnect/disconnect cleanup paths.
*/
constexpr uint16_t kPreferredBleMtu = 517;
constexpr uint16_t kPreferredBleTxOctets = 251;
constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
constexpr size_t kBluetoothToPhoneQueueSize = 3;
constexpr size_t kBluetoothFromPhoneQueueSize = 3;
BLECharacteristic *fromNumCharacteristic = nullptr;
BLECharacteristic *logRadioCharacteristic = nullptr;
BLEServer *bleServer = nullptr;
static bool passkeyShowing = false;
std::atomic<uint16_t> hostedConnHandle{BLE_HS_CONN_HANDLE_NONE};
void clearPairingDisplay()
{
if (!passkeyShowing) {
return;
}
passkeyShowing = false;
#if HAS_SCREEN
if (screen) {
screen->endAlert();
}
#endif
}
class HostedBluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
{
public:
HostedBluetoothPhoneAPI() : concurrency::OSThread("HostedBluetooth") { api_type = TYPE_BLE; }
std::mutex fromPhoneMutex;
std::atomic<size_t> fromPhoneQueueSize{0};
std::array<BLEValue, kBluetoothFromPhoneQueueSize> fromPhoneQueue{};
std::mutex toPhoneMutex;
std::atomic<size_t> toPhoneQueueSize{0};
std::array<std::array<uint8_t, meshtastic_FromRadio_size>, kBluetoothToPhoneQueueSize> toPhoneQueue{};
std::array<size_t, kBluetoothToPhoneQueueSize> toPhoneQueueByteSizes{};
std::atomic<bool> onReadCallbackIsWaitingForData{false};
protected:
int32_t runOnce() override
{
while (fromPhoneQueueSize > 0 || onReadCallbackIsWaitingForData) {
runOnceHandleFromPhoneQueue();
runOnceHandleToPhoneQueue();
}
return INT32_MAX;
}
void onNowHasData(uint32_t fromRadioNum) override
{
PhoneAPI::onNowHasData(fromRadioNum);
if (!fromNumCharacteristic || !bleServer || bleServer->getConnectedCount() == 0) {
return;
}
uint8_t val[4];
put_le32(val, fromRadioNum);
fromNumCharacteristic->setValue(val, sizeof(val));
fromNumCharacteristic->notify();
}
bool checkIsConnected() override { return bleServer && bleServer->getConnectedCount() > 0; }
private:
void runOnceHandleToPhoneQueue()
{
if (!onReadCallbackIsWaitingForData) {
return;
}
uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0};
size_t numBytes = getFromRadio(fromRadioBytes);
if (numBytes > 0 && toPhoneQueueSize < kBluetoothToPhoneQueueSize) {
std::lock_guard<std::mutex> guard(toPhoneMutex);
const size_t storeAtIndex = toPhoneQueueSize.load();
memcpy(toPhoneQueue[storeAtIndex].data(), fromRadioBytes, numBytes);
toPhoneQueueByteSizes[storeAtIndex] = numBytes;
toPhoneQueueSize++;
}
onReadCallbackIsWaitingForData = false;
}
void runOnceHandleFromPhoneQueue()
{
if (fromPhoneQueueSize == 0) {
return;
}
BLEValue val;
{
std::lock_guard<std::mutex> guard(fromPhoneMutex);
val = fromPhoneQueue[0];
for (size_t i = 1; i < fromPhoneQueueSize; ++i) {
fromPhoneQueue[i - 1] = fromPhoneQueue[i];
}
fromPhoneQueueSize--;
}
handleToRadio(val.getData(), val.getLength());
}
};
static HostedBluetoothPhoneAPI *bluetoothPhoneAPI = nullptr;
uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE] = {0};
class HostedBluetoothToRadioCallback : public BLECharacteristicCallbacks
{
public:
void onWrite(BLECharacteristic *pCharacteristic) override
{
if (!bluetoothPhoneAPI) {
return;
}
BLEValue val;
val.setValue(pCharacteristic->getData(), pCharacteristic->getLength());
if (memcmp(lastToRadio, val.getData(), val.getLength()) == 0) {
return;
}
if (bluetoothPhoneAPI->fromPhoneQueueSize >= kBluetoothFromPhoneQueueSize) {
LOG_WARN("Hosted BLE onWrite drop: queue full (%u bytes)", val.getLength());
return;
}
memcpy(lastToRadio, val.getData(), val.getLength());
{
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->fromPhoneMutex);
bluetoothPhoneAPI->fromPhoneQueue.at(bluetoothPhoneAPI->fromPhoneQueueSize) = val;
bluetoothPhoneAPI->fromPhoneQueueSize++;
}
bluetoothPhoneAPI->setIntervalFromNow(0);
concurrency::mainDelay.interrupt();
}
};
class HostedBluetoothFromRadioCallback : public BLECharacteristicCallbacks
{
public:
void onRead(BLECharacteristic *pCharacteristic) override
{
if (!bluetoothPhoneAPI) {
return;
}
if (bluetoothPhoneAPI->toPhoneQueueSize == 0) {
bluetoothPhoneAPI->onReadCallbackIsWaitingForData = true;
bluetoothPhoneAPI->setIntervalFromNow(0);
concurrency::mainDelay.interrupt();
int tries = 0;
while (bluetoothPhoneAPI->onReadCallbackIsWaitingForData && tries < 4000) {
delay(tries < 20 ? 1 : 5);
tries++;
if (tries == 4000) {
LOG_WARN("BLE onRead: timeout waiting for data after %d tries, giving up and returning 0-size response",
tries);
}
}
}
uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0};
size_t numBytes = 0;
{
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->toPhoneMutex);
size_t pending = bluetoothPhoneAPI->toPhoneQueueSize.load();
if (pending > 0) {
numBytes = bluetoothPhoneAPI->toPhoneQueueByteSizes[0];
memcpy(fromRadioBytes, bluetoothPhoneAPI->toPhoneQueue[0].data(), numBytes);
for (size_t i = 1; i < pending; ++i) {
memcpy(bluetoothPhoneAPI->toPhoneQueue[i - 1].data(), bluetoothPhoneAPI->toPhoneQueue[i].data(),
bluetoothPhoneAPI->toPhoneQueueByteSizes[i]);
bluetoothPhoneAPI->toPhoneQueueByteSizes[i - 1] = bluetoothPhoneAPI->toPhoneQueueByteSizes[i];
}
bluetoothPhoneAPI->toPhoneQueueSize--;
}
}
pCharacteristic->setValue(fromRadioBytes, numBytes);
}
};
class HostedBluetoothServerCallback : public BLEServerCallbacks
{
public:
explicit HostedBluetoothServerCallback(HostedBluetooth *owner) : owner(owner) {}
private:
HostedBluetooth *owner;
void onConnect(BLEServer *pServer, struct ble_gap_conn_desc *desc) override
{
BLEAddress peerAddr(desc->peer_id_addr);
LOG_INFO("Hosted BLE incoming connection %s", peerAddr.toString().c_str());
owner->setConnected(true);
hostedConnHandle = desc->conn_handle;
const int dataLenResult = ble_gap_set_data_len(desc->conn_handle, kPreferredBleTxOctets, kPreferredBleTxTimeUs);
if (dataLenResult != 0) {
LOG_WARN("Hosted BLE failed to raise data length rc=%d", dataLenResult);
}
pServer->updateConnParams(desc->conn_handle, 6, 12, 0, 200);
}
void onDisconnect(BLEServer *pServer, struct ble_gap_conn_desc *desc) override
{
(void)pServer;
(void)desc;
LOG_INFO("Hosted BLE disconnected");
owner->setConnected(false);
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
bluetoothStatus->updateStatus(&newStatus);
clearPairingDisplay();
hostedConnHandle = BLE_HS_CONN_HANDLE_NONE;
memset(lastToRadio, 0, sizeof(lastToRadio));
if (bluetoothPhoneAPI) {
bluetoothPhoneAPI->close();
{
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->fromPhoneMutex);
bluetoothPhoneAPI->fromPhoneQueueSize = 0;
}
{
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->toPhoneMutex);
bluetoothPhoneAPI->toPhoneQueueSize = 0;
}
bluetoothPhoneAPI->onReadCallbackIsWaitingForData = false;
}
owner->startAdvertising();
}
};
class HostedBluetoothSecurityCallback : public BLESecurityCallbacks
{
public:
void onPassKeyNotify(uint32_t passkey) override
{
LOG_INFO("*** Enter passkey %06u on the peer side ***", passkey);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
meshtastic::BluetoothStatus newStatus(std::to_string(passkey));
bluetoothStatus->updateStatus(&newStatus);
#if HAS_SCREEN
if (screen) {
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
char btPIN[16] = "888888";
snprintf(btPIN, sizeof(btPIN), "%06u", passkey);
int x_offset = display->width() / 2;
int y_offset = display->height() <= 80 ? 0 : 12;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
#if !defined(M5STACK_UNITC6L)
display->setFont(FONT_SMALL);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
display->drawString(x_offset + x, y_offset + y, "Enter this code");
#endif
display->setFont(FONT_LARGE);
char pin[8];
snprintf(pin, sizeof(pin), "%.3s %.3s", btPIN, btPIN + 3);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
display->drawString(x_offset + x, y_offset + y, pin);
display->setFont(FONT_SMALL);
char deviceName[64];
snprintf(deviceName, sizeof(deviceName), "Name: %s", getDeviceName());
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
display->drawString(x_offset + x, y_offset + y, deviceName);
});
}
#endif
passkeyShowing = true;
}
void onAuthenticationComplete(ble_gap_conn_desc *desc) override
{
(void)desc;
LOG_INFO("Hosted BLE authentication complete");
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
bluetoothStatus->updateStatus(&newStatus);
clearPairingDisplay();
}
};
HostedBluetoothToRadioCallback *toRadioCallbacks = nullptr;
HostedBluetoothFromRadioCallback *fromRadioCallbacks = nullptr;
HostedBluetoothSecurityCallback *securityCallbacks = nullptr;
gpio_num_t getSlaveResetGpio()
{
#if defined(CONFIG_ESP_HOSTED_GPIO_SLAVE_RESET_SLAVE)
return static_cast<gpio_num_t>(CONFIG_ESP_HOSTED_GPIO_SLAVE_RESET_SLAVE);
#elif defined(CONFIG_ESP_HOSTED_SDIO_GPIO_RESET_SLAVE)
return static_cast<gpio_num_t>(CONFIG_ESP_HOSTED_SDIO_GPIO_RESET_SLAVE);
#else
return GPIO_NUM_NC;
#endif
}
void setSlaveResetLine(bool assertReset)
{
const gpio_num_t gpioNum = getSlaveResetGpio();
if (gpioNum == GPIO_NUM_NC || gpioNum < 0) {
return;
}
if (gpio_reset_pin(gpioNum) != ESP_OK) {
return;
}
if (gpio_set_direction(gpioNum, GPIO_MODE_OUTPUT) != ESP_OK) {
return;
}
const int activeLevel =
#if defined(CONFIG_ESP_HOSTED_SDIO_RESET_ACTIVE_HIGH)
1;
#else
0;
#endif
const int inactiveLevel = activeLevel ? 0 : 1;
gpio_set_level(gpioNum, assertReset ? activeLevel : inactiveLevel);
LOG_DEBUG("[HostedBluetooth] setSlaveResetLine: GPIO[%d] -> %d (assertReset=%d, activeLevel=%d)", gpioNum,
assertReset ? activeLevel : inactiveLevel, assertReset, activeLevel);
}
void hostedEventHandler(void *arg, esp_event_base_t eventBase, int32_t eventId, void *eventData)
{
(void)eventData;
auto *self = static_cast<HostedBluetooth *>(arg);
if (!self || eventBase != ESP_HOSTED_EVENT) {
return;
}
switch (eventId) {
case ESP_HOSTED_EVENT_TRANSPORT_UP:
LOG_INFO("ESP-Hosted transport is up");
self->setConnected(true);
break;
case ESP_HOSTED_EVENT_TRANSPORT_DOWN:
LOG_WARN("ESP-Hosted transport is down");
[[fallthrough]];
case ESP_HOSTED_EVENT_TRANSPORT_FAILURE:
if (eventId == ESP_HOSTED_EVENT_TRANSPORT_FAILURE) {
LOG_ERROR("ESP-Hosted transport failure");
}
self->setConnected(false);
break;
case ESP_HOSTED_EVENT_CP_INIT:
case ESP_HOSTED_EVENT_CP_HEARTBEAT:
default:
break;
}
}
} // namespace
HostedBluetooth::HostedBluetooth() {}
HostedBluetooth::~HostedBluetooth()
{
deinit();
}
bool HostedBluetooth::registerCallbacks()
{
if (callbacksRegistered) {
return true;
}
// Defensive cleanup in case setup() is called again after an incomplete/early previous init.
// esp_event_handler_unregister can safely fail if the handler wasn't present.
esp_event_handler_unregister(ESP_HOSTED_EVENT, ESP_EVENT_ANY_ID, hostedEventHandler);
esp_err_t err = esp_event_handler_register(ESP_HOSTED_EVENT, ESP_EVENT_ANY_ID, hostedEventHandler, this);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
LOG_ERROR("Failed to register hosted event handler: %s", esp_err_to_name(err));
return false;
}
if (err == ESP_ERR_INVALID_STATE) {
LOG_WARN("Hosted event handler already registered, continuing");
}
callbacksRegistered = true;
return true;
}
void HostedBluetooth::unregisterCallbacks()
{
if (!callbacksRegistered) {
return;
}
esp_event_handler_unregister(ESP_HOSTED_EVENT, ESP_EVENT_ANY_ID, hostedEventHandler);
callbacksRegistered = false;
}
void HostedBluetooth::setup()
{
if (active) {
return;
}
// Ensure co-processor is released from reset before bringing transport back up.
setSlaveResetLine(false);
LOG_DEBUG("[HostedBluetooth] setup(): Released co-processor from reset");
if (!registerCallbacks()) {
return;
}
active = setupGatt();
firstRssiLogged.store(false);
if (active) {
LOG_INFO("ESP-Hosted Bluetooth ready");
} else {
LOG_ERROR("Hosted BLE setup failed in hosted mode");
deinit();
}
}
void HostedBluetooth::shutdown()
{
deinit();
}
void HostedBluetooth::deinit()
{
if (!active && !callbacksRegistered) {
return;
}
shutdownGatt();
if (BLEDevice::getInitialized()) {
BLEDevice::deinit(false);
} else {
hostedDeinitBLE();
}
// Hold co-processor in reset when hosted transport is disabled to reduce idle draw.
setSlaveResetLine(true);
unregisterCallbacks();
connected.store(false);
active = false;
rssi.store(0);
firstRssiLogged.store(false);
}
void HostedBluetooth::clearBonds()
{
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_PEER_SEC, nullptr);
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_CCCD, nullptr);
}
bool HostedBluetooth::isActive()
{
return active;
}
bool HostedBluetooth::isConnected()
{
if (!connected.load()) {
return false;
}
return bleServer && bleServer->getConnectedCount() > 0;
}
int HostedBluetooth::getRssi()
{
if (!bleServer || !isConnected()) {
return 0;
}
uint16_t connHandle = hostedConnHandle.load();
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
const auto peers = bleServer->getPeerDevices(true);
if (!peers.empty()) {
connHandle = peers.begin()->first;
hostedConnHandle = connHandle;
}
}
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
return 0;
}
int8_t currentRssi = 0;
const int rc = ble_gap_conn_rssi(connHandle, &currentRssi);
if (rc == 0) {
setRssi(currentRssi);
return currentRssi;
}
return rssi.load();
}
void HostedBluetooth::sendLog(const uint8_t *logMessage, size_t length)
{
if (!logRadioCharacteristic || !isConnected() || length > 512) {
return;
}
logRadioCharacteristic->setValue(logMessage, length);
logRadioCharacteristic->notify();
}
void HostedBluetooth::setConnected(bool value)
{
connected.store(value);
}
void HostedBluetooth::setRssi(int value)
{
rssi.store(value);
maybeLogFirstRssi(value);
}
void HostedBluetooth::maybeLogFirstRssi(int value)
{
bool expected = false;
if (firstRssiLogged.compare_exchange_strong(expected, true)) {
LOG_INFO("ESP-Hosted first RSSI update: %d dBm", value);
}
}
bool HostedBluetooth::setupGatt()
{
memset(lastToRadio, 0, sizeof(lastToRadio));
hostedConnHandle = BLE_HS_CONN_HANDLE_NONE;
if (!BLEDevice::init(getDeviceName())) {
LOG_ERROR("Hosted BLE init failed");
return false;
}
#ifdef ESP_PWR_LVL_P9
BLEDevice::setPower(ESP_PWR_LVL_P9);
#endif
BLESecurity *security = new BLESecurity();
security->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
security->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
security->setCapability(ESP_IO_CAP_OUT);
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
LOG_INFO("Hosted BLE using random passkey");
security->setPassKey(false);
} else {
LOG_INFO("Hosted BLE using fixed passkey");
security->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)
security->setAuthenticationMode(true, true, true);
} else {
security->setCapability(ESP_IO_CAP_NONE);
security->setAuthenticationMode(true, false, false);
}
if (!securityCallbacks) {
securityCallbacks = new HostedBluetoothSecurityCallback();
}
BLEDevice::setSecurityCallbacks(securityCallbacks);
const int mtuResult = BLEDevice::setMTU(kPreferredBleMtu);
if (mtuResult == 0) {
LOG_INFO("Hosted BLE MTU request set to %u", kPreferredBleMtu);
} else {
LOG_WARN("Hosted BLE unable to request MTU %u, rc=%d", kPreferredBleMtu, mtuResult);
}
bleServer = BLEDevice::createServer();
if (!bleServer) {
LOG_ERROR("Hosted BLE createServer failed");
return false;
}
const int nameRc = ble_svc_gap_device_name_set(BLEDevice::getDeviceName().c_str());
if (nameRc != 0) {
LOG_WARN("Hosted BLE device_name_set rc=%d %s", nameRc, BLEUtils::returnCodeToString(nameRc));
}
bleServer->setCallbacks(new HostedBluetoothServerCallback(this));
BLEService *meshService = bleServer->createService(MESH_SERVICE_UUID);
if (!meshService) {
LOG_ERROR("Hosted BLE mesh service creation failed");
return false;
}
BLECharacteristic *toRadioCharacteristic = nullptr;
BLECharacteristic *fromRadioCharacteristic = nullptr;
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
toRadioCharacteristic = meshService->createCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE);
fromRadioCharacteristic = meshService->createCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ);
fromNumCharacteristic = meshService->createCharacteristic(FROMNUM_UUID, BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_READ);
logRadioCharacteristic = meshService->createCharacteristic(LOGRADIO_UUID, BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_READ);
} else {
toRadioCharacteristic = meshService->createCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_WRITE_AUTHEN |
BLECharacteristic::PROPERTY_WRITE_ENC);
fromRadioCharacteristic = meshService->createCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_READ_AUTHEN |
BLECharacteristic::PROPERTY_READ_ENC);
fromNumCharacteristic = meshService->createCharacteristic(
FROMNUM_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_ENC);
logRadioCharacteristic = meshService->createCharacteristic(
LOGRADIO_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_ENC);
}
if (!toRadioCharacteristic || !fromRadioCharacteristic || !fromNumCharacteristic || !logRadioCharacteristic) {
LOG_ERROR("Hosted BLE characteristic creation failed");
return false;
}
if (!bluetoothPhoneAPI) {
bluetoothPhoneAPI = new HostedBluetoothPhoneAPI();
}
if (!toRadioCallbacks) {
toRadioCallbacks = new HostedBluetoothToRadioCallback();
}
if (!fromRadioCallbacks) {
fromRadioCallbacks = new HostedBluetoothFromRadioCallback();
}
toRadioCharacteristic->setCallbacks(toRadioCallbacks);
fromRadioCharacteristic->setCallbacks(fromRadioCallbacks);
meshService->start();
startAdvertising();
return true;
}
void HostedBluetooth::shutdownGatt()
{
if (bluetoothPhoneAPI) {
bluetoothPhoneAPI->close();
delete bluetoothPhoneAPI;
bluetoothPhoneAPI = nullptr;
}
delete toRadioCallbacks;
toRadioCallbacks = nullptr;
delete fromRadioCallbacks;
fromRadioCallbacks = nullptr;
delete securityCallbacks;
securityCallbacks = nullptr;
if (bleServer) {
BLEAdvertising *advertising = BLEDevice::getAdvertising();
if (advertising) {
advertising->stop();
}
}
fromNumCharacteristic = nullptr;
logRadioCharacteristic = nullptr;
bleServer = nullptr;
hostedConnHandle = BLE_HS_CONN_HANDLE_NONE;
}
void HostedBluetooth::startAdvertising()
{
BLEAdvertising *advertising = BLEDevice::getAdvertising();
if (!advertising) {
LOG_ERROR("Hosted BLE getAdvertising failed");
return;
}
advertising->stop();
advertising->reset();
advertising->addServiceUUID(MESH_SERVICE_UUID);
BLEAdvertisementData scan;
scan.setName(getDeviceName());
advertising->setScanResponseData(scan);
advertising->setMinPreferred(0x06);
advertising->setMaxPreferred(0x12);
if (!advertising->start(0)) {
LOG_ERROR("Hosted BLE failed to start advertising");
} else {
LOG_INFO("Hosted BLE advertising started");
}
}
#endif
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include "BluetoothCommon.h"
#include <atomic>
/**
* Placeholder backend for ESP32-P4 + ESP-Hosted BLE transport.
*
* This intentionally mirrors the NimbleBluetooth surface used by the firmware,
* so the caller side can stay stable while we implement esp-hosted internals.
*/
class HostedBluetooth : public BluetoothApi
{
public:
HostedBluetooth();
~HostedBluetooth() override;
void setup() override;
void shutdown() override;
void deinit() override;
void clearBonds() override;
bool isActive() override;
bool isConnected() override;
int getRssi() override;
void sendLog(const uint8_t *logMessage, size_t length) override;
void startAdvertising();
void setConnected(bool value);
void setRssi(int value);
private:
bool setupGatt();
void shutdownGatt();
void maybeLogFirstRssi(int value);
bool registerCallbacks();
void unregisterCallbacks();
bool active = false;
std::atomic<bool> connected{false};
std::atomic<int> rssi{0};
std::atomic<bool> firstRssiLogged{false};
bool callbacksRegistered = false;
};
-7
View File
@@ -14,11 +14,6 @@ Lock::Lock() : handle(xSemaphoreCreateBinary())
}
}
Lock::~Lock()
{
vSemaphoreDelete(handle);
}
void Lock::lock()
{
if (xSemaphoreTake(handle, portMAX_DELAY) == false) {
@@ -35,8 +30,6 @@ void Lock::unlock()
#else
Lock::Lock() {}
Lock::~Lock() {}
void Lock::lock() {}
void Lock::unlock() {}
-1
View File
@@ -12,7 +12,6 @@ class Lock
{
public:
Lock();
~Lock();
Lock(const Lock &) = delete;
Lock &operator=(const Lock &) = delete;
+1 -2
View File
@@ -11,8 +11,7 @@ enum LoRaRadioType {
SX1280_RADIO,
LR1110_RADIO,
LR1120_RADIO,
LR1121_RADIO,
LR2021_RADIO
LR1121_RADIO
};
extern LoRaRadioType radioType;
+3 -11
View File
@@ -1025,13 +1025,10 @@ void GPS::up()
setPowerState(GPS_ACTIVE);
}
// We've finished a GPS search cycle (lock or timeout). Enter a low power state, potentially.
// We've got a GPS lock. Enter a low power state, potentially.
void GPS::down()
{
if (hasValidLocation)
scheduling.informGotLock();
else
scheduling.informSearchFailed();
scheduling.informGotLock();
uint32_t predictedSearchDuration = scheduling.predictedSearchDurationMs();
uint32_t sleepTime = scheduling.msUntilNextSearch();
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval);
@@ -1556,12 +1553,7 @@ std::unique_ptr<GPS> GPS::createGps()
_en_gpio = PIN_GPS_EN;
#endif
#ifdef ARCH_PORTDUINO
if (portduino_config.has_gps) {
// These need to set as flags so later checks will pass on native and GPS will work.
// They are not used for any hardware access.
_rx_gpio = 1;
_tx_gpio = 1;
} else
if (!portduino_config.has_gps)
return nullptr;
#endif
if (!_rx_gpio || !_serial_gps) // Configured to have no GPS at all
+2 -34
View File
@@ -15,19 +15,6 @@ void GPSUpdateScheduling::informGotLock()
searchEndedMs = millis();
LOG_DEBUG("Took %us to get lock", (searchEndedMs - searchStartedMs) / 1000);
updateLockTimePrediction();
consecutiveFailures = 0; // Drop back to fast cadence as soon as we acquire any fix
}
// Search finished without obtaining a fix. We still need to mark the end time so
// the next sleep is timed correctly, but we must not feed the timeout duration
// into predictedMsToGetLock — doing so poisons msUntilNextSearch() and causes
// down() to fall into GPS_IDLE, leaving the chip awake on subsequent indoor cycles.
void GPSUpdateScheduling::informSearchFailed()
{
searchEndedMs = millis();
consecutiveFailures++;
LOG_DEBUG("GPS search ended without fix after %us (consecutive failures: %u)", (searchEndedMs - searchStartedMs) / 1000,
consecutiveFailures);
}
// Clear old lock-time prediction data.
@@ -38,7 +25,6 @@ void GPSUpdateScheduling::reset()
searchEndedMs = 0;
searchCount = 0;
predictedMsToGetLock = 0;
consecutiveFailures = 0;
}
// How many milliseconds before we should next search for GPS position
@@ -50,20 +36,6 @@ uint32_t GPSUpdateScheduling::msUntilNextSearch()
// Target interval (seconds), between GPS updates
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval, default_gps_update_interval);
// After a failed search, back off: indoors / no-sky environments will keep failing,
// so wake at most once per broadcast interval rather than once per gps_update_interval.
// Capped at 1 hour so a user-configured very-long broadcast interval still retries
// periodically (in case conditions change). Reset on any successful lock.
if (consecutiveFailures > 0) {
constexpr uint32_t failureRetryCapMs = 60UL * 60UL * 1000UL; // 1 hour cap
uint32_t failureSleepMs =
Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs);
if (failureSleepMs > failureRetryCapMs)
failureSleepMs = failureRetryCapMs;
if (updateInterval < failureSleepMs)
updateInterval = failureSleepMs;
}
// Check how long until we should start searching, to hopefully hit our target interval
uint32_t dueAtMs = searchEndedMs + updateInterval;
uint32_t compensatedStart = dueAtMs - predictedMsToGetLock;
@@ -99,18 +71,14 @@ bool GPSUpdateScheduling::isUpdateDue()
bool GPSUpdateScheduling::searchedTooLong()
{
constexpr uint32_t oneMinuteMs = 60UL * 1000UL;
constexpr uint32_t maxSearchClampMs = 15UL * oneMinuteMs; // Hard cap: 15 minutes is always too long
constexpr uint32_t postFailureSearchMs = 5UL * oneMinuteMs; // Tighter dwell once we know the environment is hostile
constexpr uint32_t maxSearchClampMs = 15UL * oneMinuteMs; // Hard cap: 15 minutes is always too long
uint32_t elapsed = elapsedSearchMs();
// Anything over 15 minutes is too long, regardless of the broadcast interval.
// TODO: Make a smarter algorithm that backs off the search dwell time when not getting a lock.
if (elapsed > maxSearchClampMs)
return true;
// After a prior failed search, shorten the dwell
if (consecutiveFailures > 0 && elapsed > postFailureSearchMs)
return true;
uint32_t minimumOrConfiguredSecs =
Default::getConfiguredOrMinimumValue(config.position.position_broadcast_secs, default_broadcast_interval_secs);
uint32_t maxSearchMs = Default::getConfiguredOrDefaultMs(minimumOrConfiguredSecs, default_broadcast_interval_secs);
+1 -3
View File
@@ -8,8 +8,7 @@ class GPSUpdateScheduling
public:
// Marks the time of these events, for calculation use
void informSearching();
void informGotLock(); // Predicted lock-time is recalculated here
void informSearchFailed(); // Search ended without a fix; prediction is left untouched
void informGotLock(); // Predicted lock-time is recalculated here
void reset(); // Reset the prediction - after GPS::disable() / GPS::enable()
bool isUpdateDue(); // Is it time to begin searching for a GPS position?
@@ -25,7 +24,6 @@ class GPSUpdateScheduling
uint32_t searchEndedMs = 0;
uint32_t searchCount = 0;
uint32_t predictedMsToGetLock = 0;
uint32_t consecutiveFailures = 0; // Count of search cycles that ended without a fix; reset on lock
const float weighting = 0.2; // Controls exponential smoothing of lock-times prediction. 20% weighting of "latest lock-time".
};
+5 -4
View File
@@ -53,10 +53,11 @@ 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(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());
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());
}
// Release firmware's SPI lock, because SafeFile::close needs it
+2
View File
@@ -13,6 +13,8 @@
#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>
+8 -3
View File
@@ -13,6 +13,7 @@ RotaryEncoderImpl *rotaryEncoderImpl;
RotaryEncoderImpl::RotaryEncoderImpl()
{
rotary = nullptr;
#ifdef ARCH_ESP32
isFirstInit = true;
#endif
@@ -22,6 +23,11 @@ RotaryEncoderImpl::~RotaryEncoderImpl()
{
LOG_DEBUG("RotaryEncoderImpl destructor");
detachRotaryEncoderInterrupts();
if (rotary != nullptr) {
delete rotary;
rotary = nullptr;
}
}
bool RotaryEncoderImpl::init()
@@ -37,9 +43,8 @@ bool RotaryEncoderImpl::init()
eventPressed = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_press);
if (rotary == nullptr) {
rotary.reset(new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a,
moduleConfig.canned_message.inputbroker_pin_b,
moduleConfig.canned_message.inputbroker_pin_press));
rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b,
moduleConfig.canned_message.inputbroker_pin_press);
}
attachRotaryEncoderInterrupts();
+1 -2
View File
@@ -5,7 +5,6 @@
#include "InputBroker.h"
#include "concurrency/OSThread.h"
#include "mesh/NodeDB.h"
#include <memory>
class RotaryEncoder;
@@ -29,7 +28,7 @@ class RotaryEncoderImpl final : public InputPollable
input_broker_event eventCcw = INPUT_BROKER_NONE;
input_broker_event eventPressed = INPUT_BROKER_NONE;
std::unique_ptr<RotaryEncoder> rotary;
RotaryEncoder *rotary;
private:
#ifdef ARCH_ESP32
+2 -2
View File
@@ -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_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 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_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
ledcWrite(KB_BL_PIN, _brightness);
#else
ledcWrite(LEDC_BACKLIGHT_CHANNEL, _brightness);
+1 -2
View File
@@ -49,8 +49,7 @@
#include "mesh/http/WebServer.h"
#endif
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "nimble/NimbleBluetooth.h"
NimbleBluetooth *nimbleBluetooth = nullptr;
BluetoothApi *bluetoothApi = nullptr;
#endif
#endif
+2 -2
View File
@@ -12,8 +12,8 @@
#include <SPI.h>
#include <map>
#if defined(ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2)
#include "nimble/NimbleBluetooth.h"
extern NimbleBluetooth *nimbleBluetooth;
#include "BluetoothCommon.h"
extern BluetoothApi *bluetoothApi;
#endif
#ifdef ARCH_NRF52
#include "NRF52Bluetooth.h"
-7
View File
@@ -1,9 +1,5 @@
#include "configuration.h"
#include "LR11x0Interface.cpp"
#include "LR11x0Interface.h"
#include "LR20x0Interface.cpp"
#include "LR20x0Interface.h"
#include "SX126xInterface.cpp"
#include "SX126xInterface.h"
#include "SX128xInterface.cpp"
@@ -25,9 +21,6 @@ template class LR11x0Interface<LR1110>;
template class LR11x0Interface<LR1120>;
template class LR11x0Interface<LR1121>;
#endif
#if defined(USE_LR2021) && RADIOLIB_EXCLUDE_LR2021 != 1
template class LR20x0Interface<LR2021>;
#endif
#ifdef ARCH_STM32WL
template class SX126xInterface<STM32WLx>;
#endif
+6 -20
View File
@@ -57,19 +57,16 @@ template <typename T> bool LR11x0Interface<T>::init()
#if ARCH_PORTDUINO
float tcxoVoltage = (float)portduino_config.dio3_tcxo_voltage / 1000;
// FIXME: correct logic to default to not using TCXO if no voltage is specified for LR11x0_DIO3_TCXO_VOLTAGE
#elif defined(LR11X0_DIO3_TCXO_VOLTAGE)
float tcxoVoltage = LR11X0_DIO3_TCXO_VOLTAGE;
LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V", LR11X0_DIO3_TCXO_VOLTAGE);
// (DIO3 is not free to be used as an IRQ)
#elif defined(TCXO_OPTIONAL)
float tcxoVoltage = 1.6f; // TCXO_OPTIONAL: try default 1.6 V first, fall back to XTAL on failure
LOG_DEBUG("TCXO_OPTIONAL: no LR11X0_DIO3_TCXO_VOLTAGE defined, trying default TCXO Vref 1.6 V first");
#else
#elif !defined(LR11X0_DIO3_TCXO_VOLTAGE)
float tcxoVoltage =
0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per
// https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/LR11x0/LR11x0.h#L471C26-L471C104
// (DIO3 is free to be used as an IRQ)
LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage");
#else
float tcxoVoltage = LR11X0_DIO3_TCXO_VOLTAGE;
LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V", LR11X0_DIO3_TCXO_VOLTAGE);
// (DIO3 is not free to be used as an IRQ)
#endif
RadioLibInterface::init();
@@ -104,17 +101,6 @@ template <typename T> bool LR11x0Interface<T>::init()
res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
}
#if defined(TCXO_OPTIONAL)
// If init failed for any reason other than chip not found, retry without TCXO (XTAL mode)
if (res != RADIOLIB_ERR_NONE && res != RADIOLIB_ERR_CHIP_NOT_FOUND && tcxoVoltage > 0) {
LOG_WARN("LR11x0 init failed with TCXO Vref %f V (err %d), retrying without TCXO", tcxoVoltage, res);
tcxoVoltage = 0;
res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
if (res == RADIOLIB_ERR_NONE)
LOG_INFO("LR11x0 init success without TCXO (XTAL mode)");
}
#endif
// \todo Display actual typename of the adapter, not just `LR11x0`
LOG_INFO("LR11x0 init result %d", res);
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED)
@@ -212,7 +198,7 @@ template <typename T> bool LR11x0Interface<T>::reconfigure()
startReceive(); // restart receiving
return true;
return RADIOLIB_ERR_NONE;
}
template <typename T> void LR11x0Interface<T>::disableInterrupt()
-18
View File
@@ -1,18 +0,0 @@
#include "configuration.h"
#if defined(USE_LR2021) && RADIOLIB_EXCLUDE_LR2021 != 1
#include "LR2021Interface.h"
#include "error.h"
LR2021Interface::LR2021Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy)
: LR20x0Interface(hal, cs, irq, rst, busy)
{
}
bool LR2021Interface::wideLora()
{
return true;
}
#endif
-15
View File
@@ -1,15 +0,0 @@
#pragma once
#if RADIOLIB_EXCLUDE_LR2021 != 1
#include "LR20x0Interface.h"
/**
* Our adapter for LR2021 radios
*/
class LR2021Interface : public LR20x0Interface<LR2021>
{
public:
LR2021Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
bool wideLora() override;
};
#endif
-399
View File
@@ -1,399 +0,0 @@
#include "configuration.h"
#if defined(USE_LR2021) && RADIOLIB_EXCLUDE_LR2021 != 1
#include "LR20x0Interface.h"
#include "error.h"
#include "mesh/NodeDB.h"
// Keep LR20x0 naming while RadioLib exposes LR2021 symbols.
#ifndef LR20x0
#define LR20x0 LR2021
#endif
#ifdef LR2021_DIO_AS_RF_SWITCH
#include "rfswitch.h"
#elif ARCH_PORTDUINO
#include "PortduinoGlue.h"
#define lr20x0_rfswitch_dio_pins portduino_config.rfswitch_dio_pins
#define lr20x0_rfswitch_table portduino_config.rfswitch_table
#else
static const uint32_t lr20x0_rfswitch_dio_pins[] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
static const Module::RfSwitchMode_t lr20x0_rfswitch_table[] = {
{LR20x0::MODE_STBY, {}}, {LR20x0::MODE_RX, {}}, {LR20x0::MODE_TX, {}},
{LR20x0::MODE_RX_HF, {}}, {LR20x0::MODE_TX_HF, {}}, END_OF_MODE_TABLE,
};
#endif
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
// specified (may be dangerous if using external PA and LR20x0 power config forgotten)
#if ARCH_PORTDUINO
#define LR2021_MAX_POWER portduino_config.lr2021_max_power
#endif
#ifndef LR2021_MAX_POWER
#define LR2021_MAX_POWER 22
#endif
// the 2.4G part maxes at 12dBm
#if ARCH_PORTDUINO
#define LR2021_MAX_POWER_HF portduino_config.lr2021_max_power_hf
#endif
#ifndef LR2021_MAX_POWER_HF
#define LR2021_MAX_POWER_HF 12
#endif
template <typename T>
LR20x0Interface<T>::LR20x0Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy)
: RadioLibInterface(hal, cs, irq, rst, busy, &lora), lora(&module)
{
LOG_WARN("LR20x0Interface(cs=%d, irq=%d, rst=%d, busy=%d)", cs, irq, rst, busy);
}
/// Initialise the Driver transport hardware and software.
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
template <typename T> bool LR20x0Interface<T>::init()
{
#ifdef LR2021_POWER_EN
pinMode(LR2021_POWER_EN, OUTPUT);
digitalWrite(LR2021_POWER_EN, HIGH);
#endif
#if ARCH_PORTDUINO
float tcxoVoltage = (float)portduino_config.dio3_tcxo_voltage / 1000;
// FIXME: correct logic to default to not using TCXO if no voltage is specified for LR20x0_DIO3_TCXO_VOLTAGE
#elif defined(LR2021_DIO3_TCXO_VOLTAGE)
float tcxoVoltage = LR2021_DIO3_TCXO_VOLTAGE;
LOG_DEBUG("LR2021_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V", LR2021_DIO3_TCXO_VOLTAGE);
// (DIO3 is not free to be used as an IRQ)
#elif defined(TCXO_OPTIONAL)
float tcxoVoltage = 1.6f; // TCXO_OPTIONAL: try default 1.6 V first, fall back to XTAL on failure
LOG_DEBUG("TCXO_OPTIONAL: no LR2021_DIO3_TCXO_VOLTAGE defined, trying default TCXO Vref 1.6 V first");
#else
float tcxoVoltage =
0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per
// https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/LR11x0/LR11x0.h#L471C26-L471C104
// (DIO3 is free to be used as an IRQ)
LOG_DEBUG("LR2021_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage");
#endif
RadioLibInterface::init();
#ifdef LR2021_IRQ_DIO_NUM
lora.irqDioNum = LR2021_IRQ_DIO_NUM;
LOG_DEBUG("Set irqDioNum %d", lora.irqDioNum);
#elif defined(IRQ_DIO_NUM)
lora.irqDioNum = IRQ_DIO_NUM;
LOG_DEBUG("Set irqDioNum %d", lora.irqDioNum);
#else
LOG_DEBUG("Use default irqDioNum %d", lora.irqDioNum);
#endif
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) { // clamp if wide freq range
limitPower(LR2021_MAX_POWER_HF);
} else {
limitPower(LR2021_MAX_POWER); // default clamp for non-wide freq range
}
#ifdef LR2021_RF_SWITCH_SUBGHZ
pinMode(LR2021_RF_SWITCH_SUBGHZ, OUTPUT);
digitalWrite(LR2021_RF_SWITCH_SUBGHZ, getFreq() < 1e9 ? HIGH : LOW);
LOG_DEBUG("Set RF0 switch to %s", getFreq() < 1e9 ? "SubGHz" : "2.4GHz");
#endif
#ifdef LR2021_RF_SWITCH_2_4GHZ
pinMode(LR2021_RF_SWITCH_2_4GHZ, OUTPUT);
digitalWrite(LR2021_RF_SWITCH_2_4GHZ, getFreq() < 1e9 ? LOW : HIGH);
LOG_DEBUG("Set RF1 switch to %s", getFreq() < 1e9 ? "SubGHz" : "2.4GHz");
#endif
// Allow extra time for TCXO to stabilize after power-on
delay(10);
int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
// Retry if we get SPI command failed - some units need extra TCXO stabilization time
if (res == RADIOLIB_ERR_SPI_CMD_FAILED) {
LOG_WARN("LR20x0 init failed with %d (SPI_CMD_FAILED), retrying after delay...", res);
delay(100);
res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
}
#if defined(TCXO_OPTIONAL)
// If init failed for any reason other than chip not found, retry without TCXO (XTAL mode)
if (res != RADIOLIB_ERR_NONE && res != RADIOLIB_ERR_CHIP_NOT_FOUND && tcxoVoltage > 0) {
LOG_WARN("LR20x0 init failed with TCXO Vref %f V (err %d), retrying without TCXO", tcxoVoltage, res);
tcxoVoltage = 0;
res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
if (res == RADIOLIB_ERR_NONE)
LOG_INFO("LR20x0 init success without TCXO (XTAL mode)");
}
#endif
// \todo Display actual typename of the adapter, not just `LR20x0`
LOG_INFO("LR20x0 init result %d", res);
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED)
return false;
// Some basic info about the module's explicit firmware version - no other info available
// Currently requires radiolib godmode
#if RADIOLIB_GODMODE
uint8_t fwMajor = 0;
uint8_t fwMinor = 0;
int versionRes = lora.getVersion(&fwMajor, &fwMinor);
if (versionRes == RADIOLIB_ERR_NONE)
LOG_DEBUG("LR20x0 FW %d.%d", fwMajor, fwMinor);
#endif
LOG_INFO("Frequency set to %f", getFreq());
LOG_INFO("Bandwidth set to %f", bw);
LOG_INFO("Power output set to %d", power);
if (res == RADIOLIB_ERR_NONE)
res = lora.setCRC(2);
// Standard DCDC ramp timing from RadioLib workarounds (register 0x00F20024)
// Currently requires radiolib godmode
#if RADIOLIB_GODMODE
if (res == RADIOLIB_ERR_NONE) {
uint8_t rampTimes[4] = {15, 15, 15, 15}; // Standard case for all conditions
res = lora.setRegMode(RADIOLIB_LR2021_REG_MODE_SIMO_NORMAL, rampTimes);
if (res != RADIOLIB_ERR_NONE)
LOG_WARN("LR2021 setRegMode failed: %d", res);
}
#endif
#ifdef LR2021_DIO_AS_RF_SWITCH
bool dioAsRfSwitch = true;
#elif defined(ARCH_PORTDUINO)
bool dioAsRfSwitch = portduino_config.has_rfswitch_table;
#else
bool dioAsRfSwitch = false;
#endif
if (dioAsRfSwitch) {
lora.setRfSwitchTable(lr20x0_rfswitch_dio_pins, lr20x0_rfswitch_table);
LOG_DEBUG("Set DIO RF switch");
}
if (res == RADIOLIB_ERR_NONE) {
if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate
res = lora.setRxBoostedGainMode(true);
LOG_INFO("Set RX gain to boosted mode; result: %d", res);
} else {
res = lora.setRxBoostedGainMode(false);
LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d", res);
}
}
if (res == RADIOLIB_ERR_NONE)
startReceive(); // start receiving
return res == RADIOLIB_ERR_NONE;
}
template <typename T> bool LR20x0Interface<T>::reconfigure()
{
RadioLibInterface::reconfigure();
// set mode to standby
setStandby();
// configure publicly accessible settings
int err = lora.setSpreadingFactor(sf);
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
err = lora.setBandwidth(bw); // different form than LR11xx
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
err = lora.setCodingRate(cr, cr != 7); // use long interleaving except if CR is 4/7 which doesn't support it
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
err = lora.setSyncWord(syncWord);
assert(err == RADIOLIB_ERR_NONE);
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) { // clamp if wide freq range
limitPower(LR2021_MAX_POWER_HF);
} else {
limitPower(LR2021_MAX_POWER); // default clamp for non-wide freq range
}
err = lora.setPreambleLength(preambleLength);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setFrequency(getFreq());
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
err = lora.setOutputPower(power);
assert(err == RADIOLIB_ERR_NONE);
// Apply RX gain mode — valid in STDBY, matches resetAGC() pattern
err = lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);
if (err != RADIOLIB_ERR_NONE)
LOG_WARN("LR20x0 setRxBoostedGainMode %s%d", radioLibErr, err);
startReceive(); // restart receiving
return true;
}
template <typename T> void LR20x0Interface<T>::disableInterrupt()
{
lora.clearIrqAction();
}
template <typename T> void LR20x0Interface<T>::setStandby()
{
checkNotification(); // handle any pending interrupts before we force standby
int err = lora.standby();
if (err != RADIOLIB_ERR_NONE) {
LOG_DEBUG("LR20x0 standby failed with error %d", err);
}
assert(err == RADIOLIB_ERR_NONE);
isReceiving = false; // If we were receiving, not any more
activeReceiveStart = 0;
disableInterrupt();
completeSending(); // If we were sending, not anymore
RadioLibInterface::setStandby();
}
/**
* Add SNR data to received messages
*/
template <typename T> void LR20x0Interface<T>::addReceiveMetadata(meshtastic_MeshPacket *mp)
{
// LOG_DEBUG("PacketStatus %x", lora.getPacketStatus());
mp->rx_snr = lora.getSNR();
mp->rx_rssi = lround(lora.getRSSI());
// LOG_DEBUG("Corrected frequency offset: %f", lora.getFrequencyError()); // not implemented for LR20x0, but noop for LR11x0
// too(!)
}
/** We override to turn on transmitter power as needed.
*/
template <typename T> void LR20x0Interface<T>::configHardwareForSend()
{
RadioLibInterface::configHardwareForSend();
}
// For power draw measurements, helpful to force radio to stay sleeping
// #define SLEEP_ONLY
template <typename T> void LR20x0Interface<T>::startReceive()
{
#ifdef SLEEP_ONLY
sleep();
#else
setStandby();
lora.setPreambleLength(preambleLength); // Solve RX ack fail after direct message sent. Not sure why this is needed.
// We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly.
int err =
lora.startReceive(RADIOLIB_LR2021_RX_TIMEOUT_INF, MESHTASTIC_RADIOLIB_IRQ_RX_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0);
if (err)
LOG_ERROR("StartReceive error: %d", err);
assert(err == RADIOLIB_ERR_NONE);
RadioLibInterface::startReceive();
// Must be done AFTER starting receive, because startReceive clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0);
checkRxDoneIrqFlag();
#endif
}
/** Is the channel currently active? */
template <typename T> bool LR20x0Interface<T>::isChannelActive()
{
// check if we can detect a LoRa preamble on the current channel
ChannelScanConfig_t cfg = {.cad = {.symNum = NUM_SYM_CAD,
.detPeak = RADIOLIB_LR2021_CAD_PARAM_DEFAULT,
.detMin = RADIOLIB_LR2021_CAD_PARAM_DEFAULT,
.exitMode = RADIOLIB_LR2021_CAD_PARAM_DEFAULT,
.timeout = 0,
.irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS,
.irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK}};
int16_t result;
setStandby();
result = lora.scanChannel(cfg);
if (result == RADIOLIB_LORA_DETECTED)
return true;
assert(result != RADIOLIB_ERR_WRONG_MODEM);
return false;
}
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
template <typename T> bool LR20x0Interface<T>::isActivelyReceiving()
{
// The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
// received and handled the interrupt for reading the packet/handling errors.
return receiveDetected(lora.getIrqStatus(), RADIOLIB_LR2021_IRQ_LORA_HEADER_VALID, RADIOLIB_LR2021_IRQ_PREAMBLE_DETECTED);
}
#ifdef LR20X0_AGC_RESET
template <typename T> void LR20x0Interface<T>::resetAGC()
{
// Safety: don't reset mid-packet
if (sendingPacket != NULL || (isReceiving && isActivelyReceiving()))
return;
LOG_DEBUG("LR20x0 AGC reset: warm sleep + Calibrate(0x3F)");
// 1. Warm sleep — powers down the analog frontend, resetting AGC state
lora.sleep(true, 0);
// 2. Wake to RC standby for stable calibration
lora.standby(RADIOLIB_LR20X0_STANDBY_RC, true);
// 3. Calibrate all blocks (PLL, ADC, image, RC oscillators)
// calibrate() is protected on LR20x0, so use raw SPI (same as internal implementation)
uint8_t calData = RADIOLIB_LR20X0_CALIBRATE_ALL;
module.SPIwriteStream(RADIOLIB_LR20X0_CMD_CALIBRATE, &calData, 1, true, true);
// 4. Re-calibrate image rejection for actual operating frequency
// Calibrate(0x3F) defaults to 902-928 MHz which is wrong for other regions.
lora.calibrateImageRejection(getFreq() - 4.0f, getFreq() + 4.0f);
// 5. Re-apply RX boosted gain mode
lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);
// 6. Resume receiving
startReceive();
}
#endif
template <typename T> bool LR20x0Interface<T>::sleep()
{
// \todo Display actual typename of the adapter, not just `LR20x0`
LOG_DEBUG("LR20x0 entering sleep mode");
setStandby(); // Stop any pending operations
// turn off TCXO if it was powered
lora.setTCXO(0);
// put chipset into sleep mode (we've already disabled interrupts by now)
bool keepConfig = false;
lora.sleep(keepConfig, 0); // Note: we do not keep the config, full reinit will be needed
#ifdef LR2021_POWER_EN
digitalWrite(LR2021_POWER_EN, LOW);
#endif
return true;
}
#endif
-75
View File
@@ -1,75 +0,0 @@
#pragma once
#if RADIOLIB_EXCLUDE_LR2021 != 1
#include "RadioLibInterface.h"
/**
* \brief Adapter for LR20x0 radio family. Implements common logic for child classes.
* \tparam T RadioLib module type for LR20x0, e.g. LR2021.
*/
template <class T> class LR20x0Interface : public RadioLibInterface
{
public:
LR20x0Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
/// Initialise the Driver transport hardware and software.
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool init() override;
/// Apply any radio provisioning changes
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool reconfigure() override;
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
virtual bool sleep() override;
bool isIRQPending() override { return lora.getIrqFlags() != 0; }
#ifdef LR20X0_AGC_RESET
void resetAGC() override;
#endif
protected:
/**
* Specific module instance
*/
T lora;
/**
* Glue functions called from ISR land
*/
virtual void disableInterrupt() override;
/**
* Enable a particular ISR callback glue function
*/
virtual void enableInterrupt(void (*callback)()) { lora.setIrqAction(callback); }
/** can we detect a LoRa preamble on the current channel? */
virtual bool isChannelActive() override;
/** are we actively receiving a packet (only called during receiving state) */
virtual bool isActivelyReceiving() override;
/**
* Start waiting to receive a message
*/
virtual void startReceive() override;
/**
* We override to turn on transmitter power as needed.
*/
virtual void configHardwareForSend() override;
/**
* Add SNR data to received messages
*/
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override;
virtual void setStandby() override;
uint32_t getPacketTime(uint32_t pl, bool received) override { return computePacketTime(lora, pl, received); }
};
#endif
+41 -26
View File
@@ -17,7 +17,7 @@
#define VERBOSE_PACKET_HISTORY 0 // Set to 1 for verbose logging, 2 for heavy debugging
#define PACKET_HISTORY_TRACE_AGING 1 // Set to 1 to enable logging of the age of re/used history slots
PacketHistory::PacketHistory(uint32_t size) : recentPacketsCapacity(0) // Initialize members
PacketHistory::PacketHistory(uint32_t size) : recentPacketsCapacity(0), recentPackets(NULL) // Initialize members
{
if (size < 4 || size > PACKETHISTORY_MAX) { // Copilot suggested - makes sense
LOG_WARN("Packet History - Invalid size %d, using default %d", size, PACKETHISTORY_MAX);
@@ -34,7 +34,7 @@ PacketHistory::PacketHistory(uint32_t size) : recentPacketsCapacity(0) // Initia
// Allocate memory for the recent packets array
recentPacketsCapacity = size;
recentPackets.reset(new PacketRecord[recentPacketsCapacity]);
recentPackets = new PacketRecord[recentPacketsCapacity];
if (!recentPackets) { // No logging here, console/log probably uninitialized yet.
LOG_ERROR("Packet History - Memory allocation failed for size=%d entries / %d Bytes", size,
sizeof(PacketRecord) * recentPacketsCapacity);
@@ -43,20 +43,33 @@ PacketHistory::PacketHistory(uint32_t size) : recentPacketsCapacity(0) // Initia
}
// Initialize the recent packets array to zero
memset(recentPackets.get(), 0, sizeof(PacketRecord) * recentPacketsCapacity);
memset(recentPackets, 0, sizeof(PacketRecord) * recentPacketsCapacity);
#if !MESHTASTIC_EXCLUDE_PKT_HISTORY_HASH
// Allocate hash index with load factor <= 0.5 for short probe chains
hashCapacity = nextPowerOf2(recentPacketsCapacity * 2);
hashMask = hashCapacity - 1;
hashIndex.reset(new uint16_t[hashCapacity]);
hashIndex = new uint16_t[hashCapacity];
if (!hashIndex) {
LOG_ERROR("Packet History - Hash index allocation failed for %d entries", hashCapacity);
hashCapacity = 0;
hashMask = 0;
return;
}
memset(hashIndex.get(), 0xFF, sizeof(uint16_t) * hashCapacity); // Fill with HASH_EMPTY (0xFFFF)
memset(hashIndex, 0xFF, sizeof(uint16_t) * hashCapacity); // Fill with HASH_EMPTY (0xFFFF)
#endif
}
PacketHistory::~PacketHistory()
{
recentPacketsCapacity = 0;
delete[] recentPackets;
recentPackets = NULL;
#if !MESHTASTIC_EXCLUDE_PKT_HISTORY_HASH
delete[] hashIndex;
hashIndex = NULL;
hashCapacity = 0;
hashMask = 0;
#endif
}
@@ -272,7 +285,7 @@ void PacketHistory::hashRebuild()
{
if (!hashIndex)
return;
memset(hashIndex.get(), 0xFF, sizeof(uint16_t) * hashCapacity);
memset(hashIndex, 0xFF, sizeof(uint16_t) * hashCapacity);
for (uint32_t i = 0; i < recentPacketsCapacity; i++) {
if (recentPackets[i].rxTimeMsec != 0)
hashInsert(recentPackets[i].sender, recentPackets[i].id, (uint16_t)i);
@@ -319,8 +332,7 @@ PacketHistory::PacketRecord *PacketHistory::find(NodeNum sender, PacketId id)
#endif
// Linear scan (sole path when hash excluded, fallback when hash allocation failed)
PacketRecord *base = recentPackets.get();
for (PacketRecord *it = base; it < (base + recentPacketsCapacity); ++it) {
for (PacketRecord *it = recentPackets; it < (recentPackets + recentPacketsCapacity); ++it) {
if (it->id == id && it->sender == sender) {
return it;
}
@@ -334,38 +346,39 @@ void PacketHistory::insert(const PacketRecord &r)
{
uint32_t now_millis = millis(); // Should not jump with time changes
uint32_t OldtrxTimeMsec = 0;
PacketRecord *base = recentPackets.get();
PacketRecord *tu = NULL; // Will insert here.
PacketRecord *it = NULL;
// Find a free, matching or oldest used slot in the recentPackets array
for (it = base; it < (base + recentPacketsCapacity); ++it) {
for (it = recentPackets; it < (recentPackets + recentPacketsCapacity); ++it) {
if (it->id == 0 && it->sender == 0 /*&& rxTimeMsec == 0*/) { // Record is empty
tu = it; // Remember the free slot
#if VERBOSE_PACKET_HISTORY >= 2
LOG_DEBUG("Packet History - insert: Free slot@ %d/%d", tu - base, recentPacketsCapacity);
LOG_DEBUG("Packet History - insert: Free slot@ %d/%d", tu - recentPackets, recentPacketsCapacity);
#endif
// We have that, Exit the loop
it = (base + recentPacketsCapacity);
it = (recentPackets + recentPacketsCapacity);
} else if (it->id == r.id && it->sender == r.sender) { // Record matches the packet we want to insert
tu = it; // Remember the matching slot
OldtrxTimeMsec = now_millis - it->rxTimeMsec; // ..and save current entry's age
#if VERBOSE_PACKET_HISTORY >= 2
LOG_DEBUG("Packet History - insert: Matched slot@ %d/%d age=%d", tu - base, recentPacketsCapacity, OldtrxTimeMsec);
LOG_DEBUG("Packet History - insert: Matched slot@ %d/%d age=%d", tu - recentPackets, recentPacketsCapacity,
OldtrxTimeMsec);
#endif
// We have that, Exit the loop
it = (base + recentPacketsCapacity);
it = (recentPackets + recentPacketsCapacity);
} else {
if (it->rxTimeMsec == 0) {
LOG_WARN(
"Packet History - insert: Found packet s=%08x id=%08x with rxTimeMsec = 0, slot %d/%d. Should never happen!",
it->sender, it->id, it - base, recentPacketsCapacity);
it->sender, it->id, it - recentPackets, recentPacketsCapacity);
}
if ((now_millis - it->rxTimeMsec) > OldtrxTimeMsec) { // 49.7 days rollover friendly
OldtrxTimeMsec = now_millis - it->rxTimeMsec;
tu = it; // remember the oldest packet
#if VERBOSE_PACKET_HISTORY >= 2
LOG_DEBUG("Packet History - insert: Older slot@ %d/%d age=%d", tu - base, recentPacketsCapacity, OldtrxTimeMsec);
LOG_DEBUG("Packet History - insert: Older slot@ %d/%d age=%d", tu - recentPackets, recentPacketsCapacity,
OldtrxTimeMsec);
#endif
}
// keep looking for oldest till entire array is checked
@@ -380,11 +393,13 @@ void PacketHistory::insert(const PacketRecord &r)
#if VERBOSE_PACKET_HISTORY
if (tu->id == 0 && tu->sender == 0) {
LOG_DEBUG("Packet History - insert: slot@ %d/%d is NEW", tu - base, recentPacketsCapacity);
LOG_DEBUG("Packet History - insert: slot@ %d/%d is NEW", tu - recentPackets, recentPacketsCapacity);
} else if (tu->id == r.id && tu->sender == r.sender) {
LOG_DEBUG("Packet History - insert: slot@ %d/%d MATCHED, age=%d", tu - base, recentPacketsCapacity, OldtrxTimeMsec);
LOG_DEBUG("Packet History - insert: slot@ %d/%d MATCHED, age=%d", tu - recentPackets, recentPacketsCapacity,
OldtrxTimeMsec);
} else {
LOG_DEBUG("Packet History - insert: slot@ %d/%d REUSE OLDEST, age=%d", tu - base, recentPacketsCapacity, OldtrxTimeMsec);
LOG_DEBUG("Packet History - insert: slot@ %d/%d REUSE OLDEST, age=%d", tu - recentPackets, recentPacketsCapacity,
OldtrxTimeMsec);
}
#endif
@@ -417,9 +432,9 @@ void PacketHistory::insert(const PacketRecord &r)
#endif
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - insert: Store slot@ %d/%d s=%08x id=%08x nh=%02x rby=%02x %02x %02x rxT=%d BEFORE", tu - base,
recentPacketsCapacity, tu->sender, tu->id, tu->next_hop, tu->relayed_by[0], tu->relayed_by[1], tu->relayed_by[2],
tu->rxTimeMsec);
LOG_DEBUG("Packet History - insert: Store slot@ %d/%d s=%08x id=%08x nh=%02x rby=%02x %02x %02x rxT=%d BEFORE",
tu - recentPackets, recentPacketsCapacity, tu->sender, tu->id, tu->next_hop, tu->relayed_by[0], tu->relayed_by[1],
tu->relayed_by[2], tu->rxTimeMsec);
#endif
if (r.rxTimeMsec == 0) {
@@ -439,16 +454,16 @@ void PacketHistory::insert(const PacketRecord &r)
*tu = r; // store the packet
if (!isMatchingSlot) {
hashInsert(r.sender, r.id, (uint16_t)(tu - base));
hashInsert(r.sender, r.id, (uint16_t)(tu - recentPackets));
}
#else
*tu = r; // store the packet
#endif
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - insert: Store slot@ %d/%d s=%08x id=%08x nh=%02x rby=%02x %02x %02x rxT=%d AFTER", tu - base,
recentPacketsCapacity, tu->sender, tu->id, tu->next_hop, tu->relayed_by[0], tu->relayed_by[1], tu->relayed_by[2],
tu->rxTimeMsec);
LOG_DEBUG("Packet History - insert: Store slot@ %d/%d s=%08x id=%08x nh=%02x rby=%02x %02x %02x rxT=%d AFTER",
tu - recentPackets, recentPacketsCapacity, tu->sender, tu->id, tu->next_hop, tu->relayed_by[0], tu->relayed_by[1],
tu->relayed_by[2], tu->rxTimeMsec);
#endif
}
+6 -4
View File
@@ -1,7 +1,6 @@
#pragma once
#include "NodeDB.h"
#include <memory>
// Number of relayers we keep track of. Use 6 to be efficient with memory alignment of PacketRecord to 20 bytes
#define NUM_RELAYERS 6
@@ -27,7 +26,7 @@ class PacketHistory
uint32_t recentPacketsCapacity =
0; // Can be set in constructor, no need to recompile. Used to allocate memory for mx_recentPackets.
std::unique_ptr<PacketRecord[]> recentPackets; // Simple and fixed in size. Debloat.
PacketRecord *recentPackets = NULL; // Simple and fixed in size. Debloat.
#if !MESHTASTIC_EXCLUDE_PKT_HISTORY_HASH
// Open-addressing hash table for O(1) lookup in find(), replacing the O(N) linear scan.
@@ -35,7 +34,7 @@ class PacketHistory
// The load factor invariant holds permanently: hashCapacity = 2 * nextPowerOf2(recentPacketsCapacity),
// and at most recentPacketsCapacity entries can ever be live (one per recentPackets[] slot).
static constexpr uint16_t HASH_EMPTY = 0xFFFF;
std::unique_ptr<uint16_t[]> hashIndex;
uint16_t *hashIndex = NULL;
uint32_t hashCapacity = 0; // Always a power of 2
uint32_t hashMask = 0; // hashCapacity - 1, for fast modular indexing
@@ -65,8 +64,11 @@ class PacketHistory
uint8_t getOurTxHopLimit(const PacketRecord &r);
void setOurTxHopLimit(PacketRecord &r, uint8_t hopLimit);
PacketHistory(const PacketHistory &); // non construction-copyable
PacketHistory &operator=(const PacketHistory &); // non copyable
public:
explicit PacketHistory(uint32_t size = -1); // Constructor with size parameter, default is PACKETHISTORY_MAX
~PacketHistory();
/**
* Update recentBroadcasts and return true if we have already seen this packet
@@ -98,5 +100,5 @@ class PacketHistory
void removeRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender);
// To check if the PacketHistory was initialized correctly by constructor
bool initOk(void) { return recentPackets != nullptr && recentPacketsCapacity != 0; }
bool initOk(void) { return recentPackets != NULL && recentPacketsCapacity != 0; }
};
+1 -1
View File
@@ -252,7 +252,7 @@ bool RF95Interface::reconfigure()
startReceive(); // restart receiving
return true;
return RADIOLIB_ERR_NONE;
}
/**
-15
View File
@@ -5,7 +5,6 @@
#include "LR1110Interface.h"
#include "LR1120Interface.h"
#include "LR1121Interface.h"
#include "LR2021Interface.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
@@ -479,20 +478,6 @@ std::unique_ptr<RadioInterface> initLoRa()
}
#endif
#if defined(USE_LR2021) && RADIOLIB_EXCLUDE_LR2021 != 1
if (!rIf) {
rIf = std::unique_ptr<LR2021Interface>(
new LR2021Interface(loraHal, LR2021_SPI_NSS_PIN, LR2021_IRQ_PIN, LR2021_NRESET_PIN, LR2021_BUSY_PIN));
if (!rIf->init()) {
LOG_WARN("No LR2021 radio");
rIf = nullptr;
} else {
LOG_INFO("LR2021 init success");
radioType = LR2021_RADIO;
}
}
#endif
#if defined(USE_SX1280) && RADIOLIB_EXCLUDE_SX128X != 1
if (!rIf) {
rIf = std::unique_ptr<SX1280Interface>(new SX1280Interface(loraHal, SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY));
+1 -1
View File
@@ -262,7 +262,7 @@ template <typename T> bool SX126xInterface<T>::reconfigure()
startReceive(); // restart receiving
return true;
return RADIOLIB_ERR_NONE;
}
template <typename T> void SX126xInterface<T>::disableInterrupt()
+1 -1
View File
@@ -153,7 +153,7 @@ template <typename T> bool SX128xInterface<T>::reconfigure()
startReceive(); // restart receiving
return true;
return RADIOLIB_ERR_NONE;
}
template <typename T> void SX128xInterface<T>::disableInterrupt()
+2 -3
View File
@@ -3,9 +3,8 @@
#include "ServerAPI.h"
#include <WiFi.h>
#if HAS_ETHERNET && defined(USE_WS5500)
#include <ETHClass2.h>
#define ETH ETH2
#if HAS_ETHERNET && defined(ARCH_ESP32)
#include <ETH.h>
#endif // HAS_ETHERNET
/**
+2 -3
View File
@@ -13,9 +13,8 @@
#include <WebServer.h>
#include <WiFi.h>
#if HAS_ETHERNET && defined(USE_WS5500)
#include <ETHClass2.h>
#define ETH ETH2
#if HAS_ETHERNET && defined(ARCH_ESP32)
#include <ETH.h>
#endif // HAS_ETHERNET
#ifdef ARCH_ESP32
+2 -3
View File
@@ -12,9 +12,8 @@
#include <AsyncUDP.h>
#if HAS_ETHERNET && defined(USE_WS5500)
#include <ETHClass2.h>
#define ETH ETH2
#if HAS_ETHERNET && defined(ARCH_ESP32)
#include <ETH.h>
#endif // HAS_ETHERNET
#define UDP_MULTICAST_DEFAUL_PORT 4403 // Default port for UDP multicast is same as TCP api server
+2 -3
View File
@@ -10,9 +10,8 @@
#include "target_specific.h"
#include <WiFi.h>
#if HAS_ETHERNET && defined(USE_WS5500)
#include <ETHClass2.h>
#define ETH ETH2
#if HAS_ETHERNET && defined(ARCH_ESP32)
#include <ETH.h>
#endif // HAS_ETHERNET
#include <WiFiUdp.h>
+2 -3
View File
@@ -9,9 +9,8 @@
#include <WiFi.h>
#endif
#if HAS_ETHERNET && defined(USE_WS5500)
#include <ETHClass2.h>
#define ETH ETH2
#if HAS_ETHERNET && defined(ARCH_ESP32)
#include <ETH.h>
#endif // HAS_ETHERNET
extern bool needReconnect;
+6 -6
View File
@@ -262,7 +262,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
break;
}
case meshtastic_AdminMessage_ota_request_tag: {
#if defined(ARCH_ESP32)
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
LOG_INFO("OTA Requested");
if (r->ota_request.ota_hash.size != 32) {
@@ -1336,9 +1336,9 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
conn.has_bluetooth = true;
conn.bluetooth.pin = config.bluetooth.fixed_pin;
#ifdef ARCH_ESP32
if (config.bluetooth.enabled && nimbleBluetooth) {
conn.bluetooth.is_connected = nimbleBluetooth->isConnected();
conn.bluetooth.rssi = nimbleBluetooth->getRssi();
if (config.bluetooth.enabled && bluetoothApi) {
conn.bluetooth.is_connected = bluetoothApi->isConnected();
conn.bluetooth.rssi = bluetoothApi->getRssi();
}
#elif defined(ARCH_NRF52)
if (config.bluetooth.enabled && nrf52Bluetooth) {
@@ -1599,8 +1599,8 @@ void disableBluetooth()
{
#if HAS_BLUETOOTH
#ifdef ARCH_ESP32
if (nimbleBluetooth)
nimbleBluetooth->deinit();
if (bluetoothApi)
bluetoothApi->deinit();
#elif defined(ARCH_NRF52)
if (nrf52Bluetooth)
nrf52Bluetooth->shutdown();
+2 -2
View File
@@ -80,9 +80,9 @@ void StoreForwardModule::populatePSRAM()
(this->records ? this->records : (((memGet.getFreePsram() / 4) * 3) / sizeof(PacketHistoryStruct)));
this->records = numberOfPackets;
#if defined(ARCH_ESP32)
this->packetHistory.reset(static_cast<PacketHistoryStruct *>(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct))));
this->packetHistory = static_cast<PacketHistoryStruct *>(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
#elif defined(ARCH_PORTDUINO)
this->packetHistory.reset(static_cast<PacketHistoryStruct *>(calloc(numberOfPackets, sizeof(PacketHistoryStruct))));
this->packetHistory = static_cast<PacketHistoryStruct *>(calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
#endif
+1 -8
View File
@@ -7,7 +7,6 @@
#include "configuration.h"
#include <Arduino.h>
#include <functional>
#include <memory>
#include <unordered_map>
struct PacketHistoryStruct {
@@ -30,17 +29,11 @@ struct PacketHistoryStruct {
class StoreForwardModule : private concurrency::OSThread, public ProtobufModule<meshtastic_StoreAndForward>
{
// packetHistory is allocated with ps_calloc / calloc, so it must be released with free(),
// not delete[].
struct CFreeDeleter {
void operator()(PacketHistoryStruct *p) const noexcept { free(p); }
};
bool busy = 0;
uint32_t busyTo = 0;
char routerMessage[meshtastic_Constants_DATA_PAYLOAD_LEN] = {0};
std::unique_ptr<PacketHistoryStruct[], CFreeDeleter> packetHistory;
PacketHistoryStruct *packetHistory = 0;
uint32_t packetHistoryTotalCount = 0;
uint32_t last_time = 0;
uint32_t requestCount = 0;
+2 -2
View File
@@ -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);
+2 -6
View File
@@ -19,9 +19,8 @@
#include "mesh/wifi/WiFiAPClient.h"
#include <WiFi.h>
#endif
#if HAS_ETHERNET && defined(USE_WS5500)
#include <ETHClass2.h>
#define ETH ETH2
#if HAS_ETHERNET && defined(ARCH_ESP32)
#include <ETH.h>
#endif // HAS_ETHERNET
#include "Default.h"
#if !defined(ARCH_NRF52) || NRF52_USE_JSON
@@ -409,9 +408,6 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
void mqttInit()
{
if (!moduleConfig.mqtt.enabled)
return;
new MQTT();
}
+178 -245
View File
@@ -1,5 +1,5 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
#if !MESHTASTIC_EXCLUDE_BLUETOOTH && !defined(CONFIG_IDF_TARGET_ESP32P4)
#include "BluetoothCommon.h"
#include "NimbleBluetooth.h"
#include "PowerFSM.h"
@@ -10,31 +10,28 @@
#include "mesh/PhoneAPI.h"
#include "mesh/mesh-pb-constants.h"
#include "sleep.h"
#include <NimBLEDevice.h>
#include <BLE2904.h>
#include <BLEAdvertising.h>
#include <BLEDevice.h>
#include <BLESecurity.h>
#include <BLEUtils.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"
#else
#include "nimble/nimble/host/include/host/ble_gap.h"
#endif
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
#include "host/ble_store.h"
namespace
{
// Maintainer note: this backend intentionally diverges from HostedBluetooth in a few platform-specific areas.
// If you change shared BLE flow here (PhoneAPI queue/sync, security/pairing, mesh GATT/advertising,
// connect/disconnect handling), review and update HostedBluetooth.cpp as needed.
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
@@ -44,10 +41,10 @@ constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
#define NIMBLE_BLUETOOTH_TO_PHONE_QUEUE_SIZE 3
#define NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE 3
NimBLECharacteristic *fromNumCharacteristic;
NimBLECharacteristic *BatteryCharacteristic;
NimBLECharacteristic *logRadioCharacteristic;
NimBLEServer *bleServer;
BLECharacteristic *fromNumCharacteristic;
BLECharacteristic *BatteryCharacteristic;
BLECharacteristic *logRadioCharacteristic;
BLEServer *bleServer;
static bool passkeyShowing;
static std::atomic<uint16_t> nimbleBluetoothConnHandle{BLE_HS_CONN_HANDLE_NONE}; // BLE_HS_CONN_HANDLE_NONE means "no connection"
@@ -118,7 +115,8 @@ 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 NIMBLE_PROPERTY::NOTIFY to FromRadioCharacteristic appears to break things. It is NOT backwards compatible.
- Adding BLECharacteristic::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
@@ -139,7 +137,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<NimBLEAttValue, NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE> fromPhoneQueue{};
std::array<BLEValue, NIMBLE_BLUETOOTH_FROM_PHONE_QUEUE_SIZE> fromPhoneQueue{};
/* Packets to phone (BLE onRead callback) */
std::mutex toPhoneMutex;
@@ -301,7 +299,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.
NimBLEAttValue val;
BLEValue val;
{ // scope for fromPhoneMutex mutex
std::lock_guard<std::mutex> guard(fromPhoneMutex);
val = fromPhoneQueue[0];
@@ -316,7 +314,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
fromPhoneQueueSize--;
}
handleToRadio(val.data(), val.length());
handleToRadio(val.getData(), val.getLength());
}
}
@@ -328,9 +326,7 @@ 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);
@@ -340,13 +336,7 @@ 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
@@ -409,14 +399,9 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI;
// Last ToRadio value received from the phone
static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
class NimbleBluetoothToRadioCallback : public BLECharacteristicCallbacks
{
#ifdef NIMBLE_TWO
virtual void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) override
#else
virtual void onWrite(NimBLECharacteristic *pCharacteristic) override
#endif
void onWrite(BLECharacteristic *pCharacteristic) override
{
// 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.
@@ -428,15 +413,17 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
LOG_DEBUG("BLE onWrite(%d): start millis=%d", currentWriteCount, startMillis);
#endif
auto val = pCharacteristic->getValue();
// Create a BLEValue and populate it with the received data
BLEValue val;
val.setValue(pCharacteristic->getData(), pCharacteristic->getLength());
if (memcmp(lastToRadio, val.data(), val.length()) != 0) {
if (memcmp(lastToRadio, val.getData(), val.getLength()) != 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.data(), val.length());
memcpy(lastToRadio, val.getData(), val.getLength());
{ // scope for fromPhoneMutex mutex
{ // scope for fromPhoneMutex mutexv, pCharacteristic->getLen
// 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;
@@ -450,24 +437,21 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
#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.length());
finishMillis - startMillis, val.getLength());
#endif
} else {
LOG_WARN("BLE onWrite(%d): Drop ToRadio packet, fromPhoneQueue full (%u bytes)", currentWriteCount, val.length());
LOG_WARN("BLE onWrite(%d): Drop ToRadio packet, fromPhoneQueue full (%u bytes)", currentWriteCount,
val.getLength());
}
} else {
LOG_DEBUG("BLE onWrite(%d): Drop duplicate ToRadio packet (%u bytes)", currentWriteCount, val.length());
LOG_DEBUG("BLE onWrite(%d): Drop duplicate ToRadio packet (%u bytes)", currentWriteCount, val.getLength());
}
}
};
class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
class NimbleBluetoothFromRadioCallback : public BLECharacteristicCallbacks
{
#ifdef NIMBLE_TWO
virtual void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) override
#else
virtual void onRead(NimBLECharacteristic *pCharacteristic) override
#endif
void onRead(BLECharacteristic *pCharacteristic) override
{
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
@@ -573,34 +557,15 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
}
};
class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
class NimbleBluetoothSecurityCallback : public BLESecurityCallbacks
{
#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
void onPassKeyNotify(uint32_t passkey) override
{
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);
LOG_INFO("*** Enter passkey %06u on the peer side ***", passkey);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
meshtastic::BluetoothStatus newStatus(std::to_string(passkey));
bluetoothStatus->updateStatus(&newStatus);
#if HAS_SCREEN // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
#if HAS_SCREEN
if (screen) {
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
char btPIN[16] = "888888";
@@ -630,15 +595,8 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
}
#endif
passkeyShowing = true;
return passkey;
}
#ifdef NIMBLE_TWO
virtual void onAuthenticationComplete(NimBLEConnInfo &connInfo) override
#else
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc) override
#endif
void onAuthenticationComplete(ble_gap_conn_desc *desc) override
{
LOG_INFO("BLE authentication complete");
@@ -646,58 +604,47 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
bluetoothStatus->updateStatus(&newStatus);
clearPairingDisplay();
// Store the connection handle for future use
#ifdef NIMBLE_TWO
nimbleBluetoothConnHandle = connInfo.getConnHandle();
#else
nimbleBluetoothConnHandle = desc->conn_handle;
#endif
}
};
#ifdef NIMBLE_TWO
virtual void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override
class NimbleBluetoothServerCallback : public BLEServerCallbacks
{
public:
explicit NimbleBluetoothServerCallback(NimbleBluetooth *ble) : ble(ble) {}
private:
NimbleBluetooth *ble;
void onConnect(BLEServer *pServer, struct ble_gap_conn_desc *desc)
{
LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str());
BLEAddress peer_addr(desc->peer_id_addr);
LOG_INFO("BLE incoming connection %s", peer_addr.toString().c_str());
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
const uint16_t connHandle = desc->conn_handle;
// 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 initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu);
LOG_INFO("BLE conn %u peer MTU %u (target %u)", connHandle, pServer->getPeerMTU(connHandle), kPreferredBleMtu);
pServer->updateConnParams(connHandle, 6, 12, 0, 200);
}
#endif
#ifdef NIMBLE_TWO
virtual void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override
void onDisconnect(BLEServer *pServer, struct ble_gap_conn_desc *desc)
{
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
LOG_INFO("BLE disconnected");
if (ble->isDeInit)
return;
#else
if (nimbleBluetooth && nimbleBluetooth->isDeInit)
return;
#endif
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
bluetoothStatus->updateStatus(&newStatus);
@@ -722,43 +669,51 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
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; // BLE_HS_CONN_HANDLE_NONE means "no connection"
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE;
#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");
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->reset();
pAdvertising->stop();
#endif
}
// Proper shutdown for ESP32. Needs reboot to reverse.
void NimbleBluetooth::deinit()
{
#ifdef ARCH_ESP32
@@ -768,21 +723,19 @@ void NimbleBluetooth::deinit()
#ifdef BLE_LED
digitalWrite(BLE_LED, LED_STATE_OFF);
#endif
#ifndef NIMBLE_TWO
NimBLEDevice::deinit();
#endif
BLEDevice::deinit(true);
#endif
}
// Has initial setup been completed
bool NimbleBluetooth::isActive()
{
return bleServer;
return bleServer != nullptr;
}
bool NimbleBluetooth::isConnected()
{
return bleServer->getConnectedCount() > 0;
return bleServer && bleServer->getConnectedCount() > 0;
}
int NimbleBluetooth::getRssi()
@@ -795,9 +748,9 @@ int NimbleBluetooth::getRssi()
uint16_t connHandle = nimbleBluetoothConnHandle.load();
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
const auto peers = bleServer->getPeerDevices();
const auto peers = bleServer->getPeerDevices(true);
if (!peers.empty()) {
connHandle = peers.front();
connHandle = peers.begin()->first;
nimbleBluetoothConnHandle = connHandle;
}
}
@@ -825,74 +778,84 @@ void NimbleBluetooth::setup()
LOG_INFO("Init the NimBLE bluetooth module");
NimBLEDevice::init(getDeviceName());
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
BLEDevice::init(getDeviceName());
BLEDevice::setPower(ESP_PWR_LVL_P9);
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu);
int mtuResult = BLEDevice::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);
}
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 {
LOG_WARN("Failed to prefer 2M PHY by default, rc=%d", phyResult);
}
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
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) {
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);
// 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);
} 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);
}
bleServer = NimBLEDevice::createServer();
#ifdef NIMBLE_TWO
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(this);
#else
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback();
#endif
bleServer->setCallbacks(serverCallbacks, true);
// 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));
}
bleServer->setCallbacks(new NimbleBluetoothServerCallback(this));
setupService();
startAdvertising();
}
void NimbleBluetooth::setupService()
{
NimBLEService *bleService = bleServer->createService(MESH_SERVICE_UUID);
NimBLECharacteristic *ToRadioCharacteristic;
NimBLECharacteristic *FromRadioCharacteristic;
BLEService *bleService = bleServer->createService(MESH_SERVICE_UUID);
BLECharacteristic *ToRadioCharacteristic;
BLECharacteristic *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, NIMBLE_PROPERTY::WRITE);
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE);
// Allow notifications so phones can stream FromRadio without polling.
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, 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);
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ);
fromNumCharacteristic =
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
bleService->createCharacteristic(FROMNUM_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ);
logRadioCharacteristic = bleService->createCharacteristic(LOGRADIO_UUID, BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_READ);
} 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);
logRadioCharacteristic = bleService->createCharacteristic(
LOGRADIO_UUID,
NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC, 512U);
LOGRADIO_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_ENC);
}
bluetoothPhoneAPI = new BluetoothPhoneAPI();
@@ -905,76 +868,31 @@ void NimbleBluetooth::setupService()
bleService->start();
// Setup the battery service
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);
BLEService *batteryService = bleServer->createService(BLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
BLE2904 *batteryLevelDescriptor = new BLE2904();
batteryLevelDescriptor->setFormat(BLE2904::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()) {
if ((config.bluetooth.enabled == true) && bleServer && bluetoothApi && bluetoothApi->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!");
NimBLEDevice::deleteAllBonds();
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_PEER_SEC, nullptr);
ble_store_util_delete_all(BLE_STORE_OBJ_TYPE_CCCD, nullptr);
}
void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
@@ -982,11 +900,26 @@ void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
if (!bleServer || !isConnected() || length > 512) {
return;
}
#ifdef NIMBLE_TWO
logRadioCharacteristic->notify(logMessage, length, BLE_HS_CONN_HANDLE_NONE);
#else
logRadioCharacteristic->notify(logMessage, length, true);
logRadioCharacteristic->setValue(logMessage, length);
logRadioCharacteristic->notify();
}
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
}
#else
void updateBatteryLevel(uint8_t level)
{
(void)level;
}
void clearNVS() {}
#endif
+11 -14
View File
@@ -1,27 +1,24 @@
#pragma once
#include "BluetoothCommon.h"
class NimbleBluetooth : BluetoothApi
class NimbleBluetooth : public BluetoothApi
{
public:
void setup();
void shutdown();
void deinit();
void clearBonds();
bool isActive();
bool isConnected();
int getRssi();
void sendLog(const uint8_t *logMessage, size_t length);
#if defined(NIMBLE_TWO)
void setup() override;
void shutdown() override;
void deinit() override;
void clearBonds() override;
bool isActive() override;
bool isConnected() override;
int getRssi() override;
void sendLog(const uint8_t *logMessage, size_t length) override;
void startAdvertising();
#endif
virtual ~NimbleBluetooth() {}
bool isDeInit = false;
private:
void setupService();
#if !defined(NIMBLE_TWO)
void startAdvertising();
#endif
};
void setBluetoothEnable(bool enable);
+42
View File
@@ -0,0 +1,42 @@
#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
+42
View File
@@ -0,0 +1,42 @@
#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
@@ -0,0 +1,11 @@
/* 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;
+45 -12
View File
@@ -4,9 +4,13 @@
#include "esp_task_wdt.h"
#include "main.h"
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
#if defined(CONFIG_IDF_TARGET_ESP32P4)
#include "bluetooth/HostedBluetooth.h"
#elif !defined(CONFIG_IDF_TARGET_ESP32S2)
#include "nimble/NimbleBluetooth.h"
#endif
#endif
#include <MeshtasticOTA.h>
@@ -40,16 +44,30 @@ void setBluetoothEnable(bool enable)
if (config.bluetooth.enabled == true)
#endif
{
if (!nimbleBluetooth) {
nimbleBluetooth = new NimbleBluetooth();
#if defined(CONFIG_IDF_TARGET_ESP32P4)
if (!enable) {
if (bluetoothApi && bluetoothApi->isActive()) {
bluetoothApi->shutdown();
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
}
return;
}
if (enable && !nimbleBluetooth->isActive()) {
#endif
if (!bluetoothApi) {
#if defined(CONFIG_IDF_TARGET_ESP32P4)
bluetoothApi = new HostedBluetooth();
#else
bluetoothApi = new NimbleBluetooth();
#endif
}
if (enable && !bluetoothApi->isActive()) {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
nimbleBluetooth->setup();
bluetoothApi->setup();
}
// For ESP32, no way to recover from bluetooth shutdown without reboot
// BLE advertising automatically stops when MCU enters light-sleep(?)
// For deep-sleep, shutdown hardware with nimbleBluetooth->deinit(). Requires reboot to reverse
// For deep-sleep, shutdown hardware with bluetoothApi->deinit(). Requires reboot to reverse
}
}
#else
@@ -165,17 +183,30 @@ void esp32Setup()
// #define APP_WATCHDOG_SECS 45
#define APP_WATCHDOG_SECS 90
#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);
#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);
}
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_add(NULL);
res = esp_task_wdt_status(NULL);
if (res == ESP_ERR_NOT_FOUND) {
res = esp_task_wdt_add(NULL);
}
assert(res == ESP_OK);
#if HAS_32768HZ
@@ -258,8 +289,10 @@ 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)
+4 -4
View File
@@ -18,7 +18,6 @@
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <stdexcept>
#include <sys/ioctl.h>
@@ -302,9 +301,10 @@ void portduinoSetup()
// Try CH341
try {
std::cout << "autoconf: Looking for CH341 device..." << std::endl;
auto probe = std::unique_ptr<Ch341Hal>(new Ch341Hal(0, portduino_config.lora_usb_serial_num,
portduino_config.lora_usb_vid, portduino_config.lora_usb_pid));
probe->getProductString(autoconf_product, 95);
ch341Hal = new Ch341Hal(0, portduino_config.lora_usb_serial_num, portduino_config.lora_usb_vid,
portduino_config.lora_usb_pid);
ch341Hal->getProductString(autoconf_product, 95);
delete ch341Hal;
std::cout << "autoconf: Found CH341 device " << autoconf_product << std::endl;
found_ch341 = true;
+4 -7
View File
@@ -4,8 +4,10 @@
#include "configuration.h"
#ifdef ARCH_ESP32
// "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 <driver/adc.h>
#include <esp_adc/adc_cali.h>
#include <esp_adc/adc_cali_scheme.h>
#include <esp_adc/adc_oneshot.h>
#include <soc/adc_channel.h>
#endif
@@ -28,11 +30,6 @@
#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>)
+32 -14
View File
@@ -18,8 +18,6 @@
#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"
@@ -146,15 +144,31 @@ 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";
if (hwReason == TG0WDT_SYS_RESET)
else if (hwReason == RTCWDT_RTC_RESET)
reason = "rtcWatchdog";
else if (hwReason == TG0WDT_SYS_RESET)
reason = "taskWatchdog";
if (hwReason == TG1WDT_SYS_RESET)
else if (hwReason == TG1WDT_SYS_RESET)
reason = "intWatchdog";
#endif
LOG_INFO("Booted, wake cause %d (boot count %d), reset_reason=%s", wakeCause, bootCount, reason);
#endif
@@ -220,8 +234,8 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
// Full shutdown of bluetooth hardware
if (nimbleBluetooth)
nimbleBluetooth->deinit();
if (bluetoothApi)
bluetoothApi->deinit();
#endif
#ifdef ARCH_ESP32
@@ -397,8 +411,10 @@ 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);
@@ -532,11 +548,7 @@ 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
@@ -545,6 +557,12 @@ 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
@@ -562,8 +580,8 @@ bool shouldLoraWake(uint32_t msecToWake)
void enableLoraInterrupt()
{
esp_err_t res;
#if SOC_PM_SUPPORT_EXT_WAKEUP && defined(LORA_DIO1) && (LORA_DIO1 != RADIOLIB_NC)
esp_err_t res;
res = gpio_pulldown_en((gpio_num_t)LORA_DIO1);
if (res != ESP_OK) {
LOG_ERROR("gpio_pulldown_en(LORA_DIO1) result %d", res);
-12
View File
@@ -800,17 +800,6 @@ void test_disabled(void)
TEST_ASSERT_FALSE(mqtt->isEnabled());
}
void test_mqttInitSkipsAllocationWhenDisabled(void)
{
delete unitTest;
mqtt = unitTest = NULL;
moduleConfig.mqtt.enabled = false;
mqttInit();
TEST_ASSERT_NULL(mqtt);
}
// Subscriptions contain the moduleConfig.mqtt.root prefix.
void test_customMqttRoot(void)
{
@@ -923,7 +912,6 @@ void setup()
RUN_TEST(test_usingCustomServer);
RUN_TEST(test_enabled);
RUN_TEST(test_disabled);
RUN_TEST(test_mqttInitSkipsAllocationWhenDisabled);
RUN_TEST(test_customMqttRoot);
RUN_TEST(test_configEmptyIsValid);
RUN_TEST(test_configEnabledEmptyIsValid);
+1 -1
View File
@@ -12,4 +12,4 @@ build_flags =
lib_deps =
${esp32_base.lib_deps}
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
lovyan03/LovyanGFX@1.2.21
lovyan03/LovyanGFX@1.2.19
+1 -1
View File
@@ -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 ADC1_GPIO34_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_6
#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 ADC1_GPIO35_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_7
#define BATTERY_SENSE_RESOLUTION_BITS ADC_RESOLUTION
// SPI
+1 -1
View File
@@ -3,7 +3,7 @@
#define I2C_SDA 4
#define I2C_SCL 5
#define BATTERY_PIN 34
#define ADC_CHANNEL ADC1_GPIO34_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_6
// GPS
#undef GPS_RX_PIN
+1 -1
View File
@@ -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 ADC1_GPIO35_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_7
#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).
+1 -1
View File
@@ -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 ADC1_GPIO35_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_7
#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).
+257 -15
View File
@@ -4,8 +4,9 @@ extends = arduino_base
custom_esp32_kind =
custom_mtjson_part =
platform =
# renovate: datasource=custom.pio depName=platformio/espressif32 packageName=platformio/platform/espressif32
platformio/espressif32@6.13.0
# 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
platform_packages =
# renovate: datasource=custom.pio depName=platformio/tool-mklittlefs packageName=platformio/tool/tool-mklittlefs
platformio/tool-mklittlefs@1.203.210628
@@ -35,20 +36,18 @@ 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
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_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
-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
@@ -62,16 +61,13 @@ 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
# 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/df424747f9acb86ab07c5a206ded1e8e3650726a.zip
# TODO renovate
https://github.com/mverch67/libpax/archive/6f52ee989301cdabaeef00bcbf93bff55708ce2f.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
@@ -79,7 +75,30 @@ lib_deps =
lib_ignore =
segger_rtt
ESP32 BLE Arduino
; 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
; leave this commented out to avoid breaking Windows
;upload_port = /dev/ttyUSB0
@@ -92,3 +111,226 @@ 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
+29 -2
View File
@@ -11,12 +11,39 @@ 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}
@@ -24,8 +51,8 @@ lib_deps =
${radiolib_base.lib_deps}
${environmental_base.lib_deps}
${environmental_extra_no_bsec.lib_deps}
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
h2zero/NimBLE-Arduino@1.4.3
# TODO renovate
https://github.com/mverch67/libpax/archive/6f52ee989301cdabaeef00bcbf93bff55708ce2f.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
+1 -1
View File
@@ -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 ADC1_CHANNEL_1
#define ADC_CHANNEL ADC_CHANNEL_1
// ratio of voltage divider = 3.20 (R1=100k, R2=220k)
#define ADC_MULTIPLIER 3.2
+1 -1
View File
@@ -35,4 +35,4 @@ lib_ignore =
lib_deps =
${esp32_base.lib_deps}
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
lovyan03/LovyanGFX@1.2.21
lovyan03/LovyanGFX@1.2.19
+1 -1
View File
@@ -92,7 +92,7 @@
#define PIN_EINK_MOSI 23 // EPD_MOSI
#define BATTERY_PIN 35
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_7
// 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
+1 -1
View File
@@ -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 ADC1_GPIO35_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_7
#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
+1 -1
View File
@@ -47,7 +47,7 @@ static const uint8_t SCK = 33;
#define PIN_VBAT WB_A0
#define BATTERY_PIN PIN_VBAT
#define ADC_CHANNEL ADC1_GPIO36_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_0
// https://docs.rakwireless.com/Product-Categories/WisBlock/RAK13300/
+1 -1
View File
@@ -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 ADC1_GPIO35_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_7
#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
+1
View File
@@ -16,6 +16,7 @@ board_check = true
build_flags = ${esp32_base.build_flags}
-D TBEAM_V10
-I variants/esp32/tbeam
upload_speed = 921600
[env:tbeam-displayshield]
+1 -1
View File
@@ -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 ADC1_GPIO35_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_7
#define USE_RF95
#define LORA_DIO0 26 // a No connect on the SX1262 module
+1 -1
View File
@@ -1,5 +1,5 @@
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_7
#define I2C_SDA 21 // I2C pins for this board
#define I2C_SCL 22
+1 -1
View File
@@ -1,5 +1,5 @@
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_7
#define I2C_SDA 21 // I2C pins for this board
#define I2C_SCL 22
+1 -1
View File
@@ -1,5 +1,5 @@
#define BATTERY_PIN 35
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
#define ADC_CHANNEL ADC_CHANNEL_7
#define BATTERY_SENSE_SAMPLES 30
// ratio of voltage divider = 2.0 (R42=100k, R43=100k)

Some files were not shown because too many files have changed in this diff Show More