Skip to content

Commit e738a74

Browse files
author
Scott Powell
committed
Merge branch 'dev'
2 parents 6b52fb3 + 465776d commit e738a74

File tree

183 files changed

+8370
-2080
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

183 files changed

+8370
-2080
lines changed

.devcontainer/devcontainer.json

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "MeshCore",
3+
"image": "mcr.microsoft.com/devcontainers/python:3-bookworm",
4+
"features": {
5+
"ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
6+
"packages": [
7+
"sudo"
8+
]
9+
}
10+
},
11+
"runArgs": [
12+
"--privileged",
13+
"--network=host",
14+
"--volume=/dev/bus/usb:/dev/bus/usb:ro",
15+
// arch tty* is owned by uucp (986)
16+
// debian tty* is owned by dialout (20)
17+
"--group-add=20",
18+
"--group-add=986"
19+
],
20+
"postCreateCommand": {
21+
"platformio": "pipx install platformio"
22+
},
23+
"customizations": {
24+
"vscode": {
25+
"settings": {
26+
"platformio-ide.disablePIOHomeStartup": true,
27+
"editor.formatOnSave": false,
28+
"workbench.colorCustomizations": {
29+
"titleBar.activeBackground": "#0d1a2b",
30+
"titleBar.activeForeground": "#ffffff",
31+
"titleBar.inactiveBackground": "#0d1a2b99",
32+
"titleBar.inactiveForeground": "#ffffff99"
33+
}
34+
},
35+
"extensions": [
36+
"platformio.platformio-ide",
37+
"github.vscode-github-actions",
38+
"GitHub.vscode-pull-request-github"
39+
],
40+
"unwantedRecommendations": [
41+
"ms-vscode.cpptools-extension-pack"
42+
]
43+
}
44+
}
45+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ cmake-*
1414
.cache
1515
.ccls
1616
compile_commands.json
17+
.venv/
18+
venv/

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ Please submit PR's using 'dev' as the base branch!
8989
For minor changes just submit your PR and I'll try to review it, but for anything more 'impactful' please open an Issue first and start a discussion. Is better to sound out what it is you want to achieve first, and try to come to a consensus on what the best approach is, especially when it impacts the structure or architecture of this codebase.
9090

9191
Here are some general principals you should try to adhere to:
92-
* Keep it simple. Please, don't think like a high-level lang programmer. Think embedded, and keep code concise, without any unecessary layers.
92+
* Keep it simple. Please, don't think like a high-level lang programmer. Think embedded, and keep code concise, without any unnecessary layers.
9393
* No dynamic memory allocation, except during setup/begin functions.
9494
* Use the same brace and indenting style that's in the core source modules. (A .clang-format is prob going to be added soon, but please do NOT retroactively re-format existing code. This just creates unnecessary diffs that make finding problems harder)
9595

@@ -106,7 +106,7 @@ There are a number of fairly major features in the pipeline, with no particular
106106
- [ ] Core + Apps: support for LZW message compression
107107
- [ ] Core: dynamic CR (Coding Rate) for weak vs strong hops
108108
- [ ] Core: new framework for hosting multiple virtual nodes on one physical device
109-
- [ ] V2 protocol spec: discussion and concensus around V2 packet protocol, including path hashes, new encryption specs, etc
109+
- [ ] V2 protocol spec: discussion and consensus around V2 packet protocol, including path hashes, new encryption specs, etc
110110

111111
## 📞 Get Support
112112

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
"""
2+
Bluefruit BLE Patch Script
3+
4+
Patches Bluefruit library to fix semaphore leak bug that causes device lockup
5+
when BLE central disconnects unexpectedly (e.g., going out of range, supervision timeout).
6+
7+
Patches applied:
8+
1. BLEConnection.h: Add _hvn_qsize member to track semaphore queue size
9+
2. BLEConnection.cpp: Store hvn_qsize and restore semaphore on disconnect
10+
11+
Bug description:
12+
- When a BLE central disconnects unexpectedly (reason=8 supervision timeout),
13+
the BLE_GATTS_EVT_HVN_TX_COMPLETE event may never fire
14+
- This leaves the _hvn_sem counting semaphore in a decremented state
15+
- Since BLEConnection objects are reused (destructor never called), the
16+
semaphore count is never restored
17+
- Eventually all semaphore counts are exhausted and notify() blocks/fails
18+
19+
"""
20+
21+
from pathlib import Path
22+
23+
Import("env") # pylint: disable=undefined-variable
24+
25+
26+
def _patch_ble_connection_header(source: Path) -> bool:
27+
"""
28+
Add _hvn_qsize member variable to BLEConnection class.
29+
30+
This is needed to restore the semaphore to its correct count on disconnect.
31+
32+
Returns True if patch was applied or already applied, False on error.
33+
"""
34+
try:
35+
content = source.read_text()
36+
37+
# Check if already patched
38+
if "_hvn_qsize" in content:
39+
return True # Already patched
40+
41+
# Find the location to insert - after _phy declaration
42+
original_pattern = ''' uint8_t _phy;
43+
44+
uint8_t _role;'''
45+
46+
patched_pattern = ''' uint8_t _phy;
47+
uint8_t _hvn_qsize;
48+
49+
uint8_t _role;'''
50+
51+
if original_pattern not in content:
52+
print("Bluefruit patch: WARNING - BLEConnection.h pattern not found")
53+
return False
54+
55+
content = content.replace(original_pattern, patched_pattern)
56+
source.write_text(content)
57+
58+
# Verify
59+
if "_hvn_qsize" not in source.read_text():
60+
return False
61+
62+
return True
63+
except Exception as e:
64+
print(f"Bluefruit patch: ERROR patching BLEConnection.h: {e}")
65+
return False
66+
67+
68+
def _patch_ble_connection_source(source: Path) -> bool:
69+
"""
70+
Patch BLEConnection.cpp to:
71+
1. Store hvn_qsize in constructor
72+
2. Restore _hvn_sem semaphore to full count on disconnect
73+
74+
Returns True if patch was applied or already applied, False on error.
75+
"""
76+
try:
77+
content = source.read_text()
78+
79+
# Check if already patched (look for the restore loop)
80+
if "uxSemaphoreGetCount(_hvn_sem)" in content:
81+
return True # Already patched
82+
83+
# Patch 1: Store queue size in constructor
84+
constructor_original = ''' _hvn_sem = xSemaphoreCreateCounting(hvn_qsize, hvn_qsize);'''
85+
86+
constructor_patched = ''' _hvn_qsize = hvn_qsize;
87+
_hvn_sem = xSemaphoreCreateCounting(hvn_qsize, hvn_qsize);'''
88+
89+
if constructor_original not in content:
90+
print("Bluefruit patch: WARNING - BLEConnection.cpp constructor pattern not found")
91+
return False
92+
93+
content = content.replace(constructor_original, constructor_patched)
94+
95+
# Patch 2: Restore semaphore on disconnect
96+
disconnect_original = ''' case BLE_GAP_EVT_DISCONNECTED:
97+
// mark as disconnected
98+
_connected = false;
99+
break;'''
100+
101+
disconnect_patched = ''' case BLE_GAP_EVT_DISCONNECTED:
102+
// Restore notification semaphore to full count
103+
// This fixes lockup when disconnect occurs with notifications in flight
104+
while (uxSemaphoreGetCount(_hvn_sem) < _hvn_qsize) {
105+
xSemaphoreGive(_hvn_sem);
106+
}
107+
// Release indication semaphore if waiting
108+
if (_hvc_sem) {
109+
_hvc_received = false;
110+
xSemaphoreGive(_hvc_sem);
111+
}
112+
// mark as disconnected
113+
_connected = false;
114+
break;'''
115+
116+
if disconnect_original not in content:
117+
print("Bluefruit patch: WARNING - BLEConnection.cpp disconnect pattern not found")
118+
return False
119+
120+
content = content.replace(disconnect_original, disconnect_patched)
121+
source.write_text(content)
122+
123+
# Verify
124+
verify_content = source.read_text()
125+
if "uxSemaphoreGetCount(_hvn_sem)" not in verify_content:
126+
return False
127+
if "_hvn_qsize = hvn_qsize" not in verify_content:
128+
return False
129+
130+
return True
131+
except Exception as e:
132+
print(f"Bluefruit patch: ERROR patching BLEConnection.cpp: {e}")
133+
return False
134+
135+
136+
def _apply_bluefruit_patches(target, source, env): # pylint: disable=unused-argument
137+
framework_path = env.get("PLATFORMFW_DIR")
138+
if not framework_path:
139+
framework_path = env.PioPlatform().get_package_dir("framework-arduinoadafruitnrf52")
140+
141+
if not framework_path:
142+
print("Bluefruit patch: ERROR - framework directory not found")
143+
env.Exit(1)
144+
return
145+
146+
framework_dir = Path(framework_path)
147+
bluefruit_lib = framework_dir / "libraries" / "Bluefruit52Lib" / "src"
148+
patch_failed = False
149+
150+
# Patch BLEConnection.h
151+
conn_header = bluefruit_lib / "BLEConnection.h"
152+
if conn_header.exists():
153+
before = conn_header.read_text()
154+
success = _patch_ble_connection_header(conn_header)
155+
after = conn_header.read_text()
156+
157+
if success:
158+
if before != after:
159+
print("Bluefruit patch: OK - Applied BLEConnection.h fix (added _hvn_qsize member)")
160+
else:
161+
print("Bluefruit patch: OK - BLEConnection.h already patched")
162+
else:
163+
print("Bluefruit patch: FAILED - BLEConnection.h")
164+
patch_failed = True
165+
else:
166+
print(f"Bluefruit patch: ERROR - BLEConnection.h not found at {conn_header}")
167+
patch_failed = True
168+
169+
# Patch BLEConnection.cpp
170+
conn_source = bluefruit_lib / "BLEConnection.cpp"
171+
if conn_source.exists():
172+
before = conn_source.read_text()
173+
success = _patch_ble_connection_source(conn_source)
174+
after = conn_source.read_text()
175+
176+
if success:
177+
if before != after:
178+
print("Bluefruit patch: OK - Applied BLEConnection.cpp fix (restore semaphore on disconnect)")
179+
else:
180+
print("Bluefruit patch: OK - BLEConnection.cpp already patched")
181+
else:
182+
print("Bluefruit patch: FAILED - BLEConnection.cpp")
183+
patch_failed = True
184+
else:
185+
print(f"Bluefruit patch: ERROR - BLEConnection.cpp not found at {conn_source}")
186+
patch_failed = True
187+
188+
if patch_failed:
189+
print("Bluefruit patch: CRITICAL - Patch failed! Build aborted.")
190+
env.Exit(1)
191+
192+
193+
# Register the patch to run before build
194+
bluefruit_action = env.VerboseAction(_apply_bluefruit_patches, "Applying Bluefruit BLE patches...")
195+
env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", bluefruit_action)
196+
197+
# Also run immediately to patch before any compilation
198+
_apply_bluefruit_patches(None, None, env)

boards/esp32-s3-zero.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"build": {
3+
"arduino": {
4+
"ldscript": "esp32s3_out.ld"
5+
},
6+
"core": "esp32",
7+
"extra_flags": [
8+
"-D ARDUINO_USB_CDC_ON_BOOT=1",
9+
"-D ARDUINO_USB_MSC_ON_BOOT=0",
10+
"-D ARDUINO_USB_DFU_ON_BOOT=0",
11+
"-D ARDUINO_USB_MODE=1",
12+
"-D ARDUINO_RUNNING_CORE=1",
13+
"-D ARDUINO_EVENT_RUNNING_CORE=1"
14+
],
15+
"f_cpu": "240000000L",
16+
"f_flash": "80000000L",
17+
"flash_mode": "qio",
18+
"hwids": [["0x303A", "0x1001"]],
19+
"mcu": "esp32s3",
20+
"variant": "esp32s3"
21+
},
22+
"connectivity": ["wifi", "bluetooth"],
23+
"debug": {
24+
"default_tool": "esp-builtin",
25+
"onboard_tools": ["esp-builtin"],
26+
"openocd_target": "esp32s3.cfg"
27+
},
28+
"frameworks": ["arduino", "espidf"],
29+
"name": "ESP32-S3-Zero",
30+
"upload": {
31+
"flash_size": "4MB",
32+
"maximum_ram_size": 327680,
33+
"maximum_size": 4194304,
34+
"require_upload_port": true,
35+
"speed": 921600
36+
},
37+
"url": "https://www.espressif.com",
38+
"vendor": "Espressif"
39+
}
40+

boards/meshtiny.json

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"build": {
3+
"arduino": {
4+
"ldscript": "nrf52840_s140_v6.ld"
5+
},
6+
"core": "nRF5",
7+
"cpu": "cortex-m4",
8+
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
9+
"f_cpu": "64000000L",
10+
"hwids": [
11+
[
12+
"0x239A",
13+
"0x8029"
14+
],
15+
[
16+
"0x239A",
17+
"0x0029"
18+
],
19+
[
20+
"0x239A",
21+
"0x002A"
22+
],
23+
[
24+
"0x239A",
25+
"0x802A"
26+
]
27+
],
28+
"usb_product": "Meshtiny",
29+
"mcu": "nrf52840",
30+
"variant": "meshtiny",
31+
"bsp": {
32+
"name": "adafruit"
33+
},
34+
"softdevice": {
35+
"sd_flags": "-DS140",
36+
"sd_name": "s140",
37+
"sd_version": "6.1.1",
38+
"sd_fwid": "0x00B6"
39+
},
40+
"bootloader": {
41+
"settings_addr": "0xFF000"
42+
}
43+
},
44+
"connectivity": [
45+
"bluetooth"
46+
],
47+
"debug": {
48+
"jlink_device": "nRF52840_xxAA",
49+
"svd_path": "nrf52840.svd",
50+
"openocd_target": "nrf52840-mdk-rs"
51+
},
52+
"frameworks": [
53+
"arduino",
54+
"freertos"
55+
],
56+
"name": "Meshtiny",
57+
"upload": {
58+
"maximum_ram_size": 248832,
59+
"maximum_size": 815104,
60+
"speed": 115200,
61+
"protocol": "nrfutil",
62+
"protocols": [
63+
"jlink",
64+
"nrfjprog",
65+
"nrfutil",
66+
"stlink"
67+
],
68+
"use_1200bps_touch": true,
69+
"require_upload_port": true,
70+
"wait_for_upload_port": true
71+
},
72+
"url": "https://shop.mtoolstec.com/product/meshtiny",
73+
"vendor": "MTools Tec"
74+
}

0 commit comments

Comments
 (0)