Initial commit

This commit is contained in:
pe1hvh
2026-02-03 13:01:55 +01:00
commit e4b9947c1e
8 changed files with 2397 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
.venv/
venv/
__pycache__/
*.pyc
logs/*.log
logs/*.txt
.DS_Store
.idea/
.vscode/

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 PE1HVH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

341
README.md Normal file
View File

@@ -0,0 +1,341 @@
# MeshCore GUI
A graphical user interface for MeshCore mesh network devices via Bluetooth Low Energy (BLE).
![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)
![License](https://img.shields.io/badge/License-MIT-green.svg)
![Platform](https://img.shields.io/badge/Platform-Linux%20%7C%20macOS%20%7C%20Windows-orange.svg)
## Why This Project Exists
MeshCore devices like the SenseCAP T1000-E can be managed through two interfaces: USB serial and BLE (Bluetooth Low Energy). The official companion apps communicate with devices over BLE, but they are mobile-only. If you want to manage your MeshCore device from a desktop or laptop, the usual approach is to **flash USB-serial firmware** via the web flasher. However, this replaces the BLE Companion firmware, which means you can no longer use the device with mobile companion apps (Android/iOS).
This project provides a **native desktop GUI** that connects to your MeshCore device over BLE — no firmware changes required. Your device stays on BLE Companion firmware and remains fully compatible with the mobile apps. The application is written in Python using cross-platform libraries and runs on **Linux, macOS and Windows**.
> **Note:** This application has only been tested on Linux (Ubuntu 24.04). macOS and Windows should work since all dependencies (`bleak`, `nicegui`, `meshcore`) are cross-platform, but this has not been verified. Feedback and contributions for other platforms are welcome.
Under the hood it uses `bleak` for Bluetooth Low Energy (which talks to BlueZ on Linux, CoreBluetooth on macOS, and WinRT on Windows), `meshcore` as the protocol layer, and `NiceGUI` for the web-based interface.
> **Linux users:** BLE on Linux can be temperamental. BlueZ occasionally gets into a bad state, especially after repeated connect/disconnect cycles. If you run into connection issues, see the [Troubleshooting Guide](docs/TROUBLESHOOTING.md). On macOS and Windows, BLE is generally more stable out of the box.
## TODO
- **Message route visualization** — Display message paths on the map showing the route (hops) each message has taken through the mesh network
- **Message persistence** — Store sent and received messages to disk so chat history is preserved across sessions
## Features
- **Real-time Dashboard** - Device info, contacts, messages and RX log
- **Interactive Map** - Leaflet map with markers for own position and contacts
- **Channel Messages** - Send and receive messages on channels
- **Direct Messages** - Click on a contact to send a DM
- **Message Filtering** - Filter messages per channel via checkboxes
- **Threaded Architecture** - BLE communication in separate thread for stable UI
## Screenshot
<img width="1002" height="532" alt="Screenshot from 2026-02-03 10-23-25" src="https://github.com/user-attachments/assets/7bc56b94-407c-4e20-aa30-8b2e0f03f820" />
## Requirements
- Python 3.10+
- Bluetooth Low Energy compatible adapter (built-in or USB)
- MeshCore device with BLE Companion firmware
### Platform support
| Platform | BLE Backend | Status |
|---|---|---|
| Linux (Ubuntu/Debian) | BlueZ/D-Bus | ✅ Tested |
| macOS | CoreBluetooth | ⬜ Untested |
| Windows 10/11 | WinRT | ⬜ Untested |
## Installation
### 1. System dependencies
**Linux (Ubuntu/Debian):**
```bash
sudo apt update
sudo apt install python3-pip python3-venv bluetooth bluez
```
**macOS:**
```bash
# Python 3.10+ via Homebrew (if not already installed)
brew install python
```
No additional Bluetooth packages needed — macOS has CoreBluetooth built in.
**Windows:**
- Install [Python 3.10+](https://www.python.org/downloads/) (check "Add to PATH" during installation)
- No additional Bluetooth packages needed — Windows 10/11 has WinRT built in.
### 2. Clone the repository
```bash
git clone https://github.com/pe1hvh/meshcore-gui.git
cd meshcore-gui
```
### 3. Create virtual environment
**Linux / macOS:**
```bash
python3 -m venv venv
source venv/bin/activate
```
**Windows:**
```cmd
python -m venv venv
venv\Scripts\activate
```
### 4. Install Python packages
```bash
pip install nicegui meshcore bleak
```
## Usage
### 1. Activate the virtual environment
**Linux / macOS:**
```bash
cd meshcore-gui
source venv/bin/activate
```
**Windows:**
```cmd
cd meshcore-gui
venv\Scripts\activate
```
### 2. Find your BLE device address
**Linux:**
```bash
bluetoothctl scan on
```
Look for your MeshCore device and note the MAC address (e.g., `literal:AA:BB:CC:DD:EE:FF`).
**macOS / Windows:**
```bash
python -c "
import asyncio
from bleak import BleakScanner
async def scan():
devices = await BleakScanner.discover(5.0)
for d in devices:
if 'MeshCore' in (d.name or ''):
print(f'{d.address} {d.name}')
asyncio.run(scan())
"
```
On macOS the address will be a UUID (e.g., `12345678-ABCD-...`) rather than a MAC address.
### 3. Configure channels
Open `meshcore_gui.py` and adjust `CHANNELS_CONFIG` to your own channels:
```python
CHANNELS_CONFIG = [
{'idx': 0, 'name': 'Public'},
{'idx': 1, 'name': '#test'},
{'idx': 2, 'name': 'MyChannel'},
{'idx': 3, 'name': '#local'},
]
```
**Tip:** Use `meshcli` to determine your channels:
```bash
meshcli -d literal:AA:BB:CC:DD:EE:FF
> get_channel 0
> get_channel 1
# etc.
```
### 4. Start the GUI
```bash
python meshcore_gui.py literal:AA:BB:CC:DD:EE:FF
```
Replace `literal:AA:BB:CC:DD:EE:FF` with the MAC address of your device.
### 5. Open the interface
The GUI opens automatically in your browser at `http://localhost:8080`
## Configuration
| Setting | Description |
|---------|-------------|
| `DEBUG` | Set to `True` for verbose logging |
| `CHANNELS_CONFIG` | List of channels (hardcoded due to BLE timing issues) |
| BLE Address | Command line argument |
## Functionality
### Device Info
- Name, frequency, SF/BW, TX power, location, firmware version
### Contacts
- List of known nodes with type and location
- Click on a contact to send a DM
### Map
- OpenStreetMap with markers for own position and contacts
- Shows your own position (blue marker)
- Automatically centers on your own position
### Channel Messages
- Select a channel in the dropdown
- Type your message and click "Send"
- Received messages appear in the messages list
- Filter messages via the checkboxes
### Direct Messages (DM)
- Click on a contact in the contacts list
- A dialog opens where you can type your message
- Click "Send" to send the DM
### RX Log
- Received packets with SNR and type
### Actions
- Refresh data
- Send advertisement
## Architecture
```
┌─────────────────┐ ┌─────────────────┐
│ Main Thread │ │ BLE Thread │
│ (NiceGUI) │ │ (asyncio) │
│ │ │ │
│ ┌───────────┐ │ │ ┌───────────┐ │
│ │ GUI │◄─┼──┬──┼─►│ BLEWorker │ │
│ └───────────┘ │ │ │ └───────────┘ │
│ │ │ │ │ │ │
│ ▼ │ │ │ ▼ │
│ ┌───────────┐ │ │ │ ┌───────────┐ │
│ │ Timer │ │ │ │ │ MeshCore │ │
│ │ (500ms) │ │ │ │ │ BLE │ │
│ └───────────┘ │ │ │ └───────────┘ │
└─────────────────┘ │ └─────────────────┘
┌──────┴──────┐
│ SharedData │
│ (thread- │
│ safe) │
└─────────────┘
```
- **BLEWorker**: Runs in separate thread with its own asyncio loop
- **SharedData**: Thread-safe data sharing between BLE and GUI
- **MeshCoreGUI**: NiceGUI interface in main thread
- **Communication**: Via queue (GUI→BLE) and shared state with flags (BLE→GUI)
## Known Limitations
1. **Channels hardcoded** - The `get_channel()` function in meshcore-py is unreliable via BLE
2. **send_appstart() sometimes fails** - Device info may remain empty with connection problems
3. **Initial load time** - GUI waits for BLE data before the first render is complete
## Troubleshooting
### Linux
For comprehensive Linux BLE troubleshooting (including the `EOFError` / `start_notify` issue), see [TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md).
#### Quick fixes
##### GUI remains empty / BLE connection fails
1. First disconnect any existing BLE connections:
```bash
bluetoothctl disconnect literal:AA:BB:CC:DD:EE:FF
```
2. Wait 2 seconds:
```bash
sleep 2
```
3. Restart the GUI:
```bash
python meshcore_gui.py literal:AA:BB:CC:DD:EE:FF
```
##### Bluetooth permissions
```bash
sudo usermod -a -G bluetooth $USER
# Log out and back in
```
### macOS
- Make sure Bluetooth is enabled in System Settings
- Grant your terminal app Bluetooth access when prompted
- Use the UUID address from BleakScanner, not a MAC address
### Windows
- Make sure Bluetooth is enabled in Settings → Bluetooth & devices
- Run the terminal as a regular user (not as Administrator — WinRT BLE can behave unexpectedly with elevated privileges)
### All platforms
#### Device not found
Make sure the MeshCore device is powered on and in BLE Companion mode. Run the BleakScanner script from the Usage section to verify it is visible.
#### Messages not arriving
- Check if your channels are correctly configured
- Use `meshcli` to verify that messages are arriving
## Development
### Debug mode
Set `DEBUG = True` in the script for verbose logging:
```python
DEBUG = True
```
### Project structure
```
meshcore-gui/
├── meshcore_gui.py # Main application
├── README.md # This file
└── docs/
├── TROUBLESHOOTING.md # BLE troubleshooting guide (Linux)
└── MeshCore_GUI_Design.docx # Design document
```
## Disclaimer
This is an **independent community project** and is not affiliated with or endorsed by the official [MeshCore](https://github.com/meshcore-dev) development team. It is built on top of the open-source `meshcore` Python library and `bleak` BLE library.
## License
MIT License - see LICENSE file
## Author
**PE1HVH** - [GitHub](https://github.com/pe1hvh)
## Acknowledgments
- [MeshCore](https://github.com/meshcore-dev) - Mesh networking firmware and protocol
- [meshcore_py](https://github.com/meshcore-dev/meshcore_py) - Python bindings for MeshCore
- [meshcore-cli](https://github.com/meshcore-dev/meshcore-cli) - Command line interface
- [NiceGUI](https://nicegui.io/) - Python GUI framework
- [Bleak](https://github.com/hbldh/bleak) - Cross-platform Bluetooth Low Energy library

Binary file not shown.

182
docs/TROUBLESHOOTING.md Normal file
View File

@@ -0,0 +1,182 @@
# MeshCore GUI - BLE Troubleshooting Guide
## The Problem
BLE connection to MeshCore device fails with `EOFError` during `start_notify` on the UART TX characteristic. The error originates in `dbus_fast` (the D-Bus library used by `bleak`) and looks like this:
```
File "src/dbus_fast/_private/unmarshaller.py", line 395, in dbus_fast._private.unmarshaller.Unmarshaller._read_sock_with_fds
EOFError
```
Basic BLE connect works fine, but subscribing to notifications (`start_notify`) crashes.
## Diagnostic Steps
### 1. Check adapter status
```bash
hciconfig -a
```
Expected: `UP RUNNING`. If it shows `DOWN`, reset with:
```bash
sudo hciconfig hci0 down
sudo hciconfig hci0 up
```
### 2. Check if adapter is detected
```bash
lsusb | grep -i blue
```
### 3. Test basic BLE connection (without notify)
```bash
python -c "
import asyncio
from bleak import BleakClient
async def test():
async with BleakClient('literal:AA:BB:CC:DD:EE:FF') as c:
print('Connected:', c.is_connected)
asyncio.run(test())
"
```
If this works but meshcli/meshcore_gui fails, the problem is specifically `start_notify`.
### 4. Test start_notify in isolation
```bash
python -c "
import asyncio
from bleak import BleakClient
UART_TX = '6e400003-b5a3-f393-e0a9-e50e24dcca9e'
async def test():
async with BleakClient('literal:AA:BB:CC:DD:EE:FF') as c:
def cb(s, d): print(f'RX: {d.hex()}')
await c.start_notify(UART_TX, cb)
print('Notify OK!')
await asyncio.sleep(2)
asyncio.run(test())
"
```
If this also fails with `EOFError`, the issue is confirmed at the BlueZ/D-Bus level.
### 5. Test notifications via bluetoothctl (outside Python)
```bash
bluetoothctl
scan on
# Wait for device to appear
connect literal:AA:BB:CC:DD:EE:FF
# Wait for "Connection successful"
menu gatt
select-attribute 6e400003-b5a3-f393-e0a9-e50e24dcca9e
notify on
```
If `connect` fails with `le-connection-abort-by-local`, the problem is at the BlueZ or device level. No Python fix will help.
## The Solution
In our case, the root cause was a stale BLE pairing state between the Linux adapter and the T1000-e device. The fix requires a clean reconnect sequence:
### Step 1 - Remove the device from BlueZ
```bash
bluetoothctl
remove literal:AA:BB:CC:DD:EE:FF
exit
```
### Step 2 - Hard power cycle the MeshCore device
Physically power off the T1000-e (not just a software reset). Wait 10 seconds, then power it back on.
### Step 3 - Scan and reconnect from scratch
```bash
bluetoothctl
scan on
```
Wait until the device appears: `[NEW] Device literal:AA:BB:CC:DD:EE:FF MeshCore-PE1HVH T1000e`
Then immediately connect:
```
connect literal:AA:BB:CC:DD:EE:FF
```
### Step 4 - Verify notifications work
```
menu gatt
select-attribute 6e400003-b5a3-f393-e0a9-e50e24dcca9e
notify on
```
If this succeeds, disconnect cleanly:
```
notify off
back
disconnect literal:AA:BB:CC:DD:EE:FF
exit
```
### Step 5 - Verify channels with meshcli
```bash
meshcli -d literal:AA:BB:CC:DD:EE:FF
> get_channels
```
Confirm output matches `CHANNELS_CONFIG` in `meshcore_gui.py`, then:
```
> exit
```
### Step 6 - Start the GUI
```bash
cd ~/Documents/Share/Web/meshcore-linux
source venv/bin/activate
python meshcore_gui.py literal:AA:BB:CC:DD:EE:FF
```
## Things That Did NOT Help
| Action | Result |
|---|---|
| `sudo systemctl restart bluetooth` | No effect |
| `sudo hciconfig hci0 down/up` | No effect |
| `sudo rmmod btusb && sudo modprobe btusb` | No effect |
| `sudo usbreset "8087:0026"` | No effect |
| `sudo reboot` | No effect |
| Clearing BlueZ cache (`/var/lib/bluetooth/*/cache`) | No effect |
| Recreating Python venv | No effect |
| Downgrading `dbus_fast` / `bleak` | No effect |
| Downgrading `linux-firmware` | No effect |
## Key Takeaway
When `start_notify` fails with `EOFError` but basic BLE connect works, the issue is almost always a stale BLE state between the host adapter and the peripheral device. The fix is:
1. **Remove** the device from bluetoothctl
2. **Hard power cycle** the peripheral device
3. **Re-scan** and reconnect from scratch
## Recommended Startup Sequence
For the most reliable BLE connection, always follow this order:
1. Ensure no other application holds the BLE connection (BT manager, bluetoothctl, meshcli)
2. Verify the device is visible: `bluetoothctl scan on`
3. Check channels: `meshcli -d <BLE_ADDRESS>``get_channels``exit`
4. Start the GUI: `python meshcore_gui.py <BLE_ADDRESS>`

View File

@@ -0,0 +1,737 @@
# BLE Capture Workflow T1000-e — Uitleg & Achtergrond
> **Bron:** `ble_capture_workflow_t_1000_e.md`
>
> Dit document is een **verdiepingsdocument** bij het originele technische werkdocument. Het biedt:
> - Didactische uitleg van BLE-concepten en terminologie
> - Achtergrondkennis over GATT-services en hun werking
> - Context om toekomstige BLE-projecten beter te begrijpen
>
> **Doelgroep:** Mezelf, als referentie voor de lange termijn.
---
## 1. Waar gaat dit document over?
Het originele document beschrijft hoe je op een Linux-computer kunt "meeluisteren" naar de BLE-communicatie van een **MeshCore T1000-e** radio. Het legt uit:
- Welke stappen nodig zijn om een verbinding op te zetten
- Waarom het vaak misgaat (en hoe je dat voorkomt)
- Hoe je ruwe protocol-data kunt opslaan voor analyse
**De kernboodschap in één zin:**
> Er mag maar **één luisteraar tegelijk** verbonden zijn met de T1000-e. Als iets anders al verbonden is, faalt jouw capture.
---
## 2. Begrippen en afkortingen uitgelegd
### 2.1 BLE — Bluetooth Low Energy
BLE is een **zuinige variant van Bluetooth**, ontworpen voor apparaten die maanden of jaren op een batterij moeten werken.
| Eigenschap | Klassiek Bluetooth | BLE |
|------------|-------------------|-----|
| Stroomverbruik | Hoog | Zeer laag |
| Datasnelheid | Hoog | Laag |
| Typisch gebruik | Audio, bestanden | Sensoren, IoT, MeshCore |
**Analogie:** Klassiek Bluetooth is als een telefoongesprek (constant verbonden, veel energie). BLE is als SMS'jes sturen (kort contact wanneer nodig, weinig energie).
---
### 2.2 GATT — Generic Attribute Profile
GATT is de **structuur** waarmee BLE-apparaten hun data aanbieden. Zie het als een **digitaal prikbord** met een vaste indeling:
```
Service (categorie)
└── Characteristic (specifiek datapunt)
└── Descriptor (extra configuratie)
```
**Voorbeeld voor MeshCore:**
```
Nordic UART Service (NUS)
├── RX Characteristic → berichten van radio naar computer
└── TX Characteristic → berichten van computer naar radio
```
---
### 2.3 NUS — Nordic UART Service
NUS is een **standaard BLE-service** ontwikkeld door Nordic Semiconductor. Het simuleert een ouderwetse seriële poort (UART) over Bluetooth.
- **RX** (Receive): Data die je **ontvangt** van het apparaat
- **TX** (Transmit): Data die je **verstuurt** naar het apparaat
Let op: RX/TX zijn vanuit het perspectief van de computer, niet van de radio.
#### Is NUS een protocol?
**Nee.** NUS is een **servicespecificatie**, geen protocol. Dit is een belangrijk onderscheid:
| Niveau | Wat is het | Voorbeeld |
|--------|-----------|-----------|
| **Protocol** | Regels voor communicatie | BLE, ATT, GATT |
| **Service** | Verzameling van gerelateerde characteristics | NUS, Heart Rate Service |
| **Characteristic** | Specifiek datapunt binnen een service | RX, TX |
**Analogie met een restaurant:**
| Concept | Restaurant-analogie |
|---------|---------------------|
| **Protocol (GATT)** | De regels: je bestelt bij de ober, eten komt uit de keuken |
| **Service (NUS)** | Een specifieke menukaart (bijv. "ontbijtmenu") |
| **Characteristics** | De individuele gerechten op dat menu |
Mensen zeggen vaak "we gebruiken het NUS-protocol", maar strikt genomen is **GATT** het protocol en is **NUS** een service die via GATT wordt aangeboden.
---
### 2.4 Andere GATT-services (officieel en custom)
NUS is slechts één van vele BLE-services. De **Bluetooth SIG** (de organisatie achter Bluetooth) definieert tientallen officiële services. Daarnaast kunnen fabrikanten eigen (custom) services maken.
#### Officiële services (Bluetooth SIG)
Deze services hebben een **16-bit UUID** en zijn gestandaardiseerd voor interoperabiliteit:
| Service | UUID | Toepassing |
|---------|------|------------|
| **Heart Rate Service** | 0x180D | Hartslagmeters, fitnessapparaten |
| **Battery Service** | 0x180F | Batterijniveau rapporteren |
| **Device Information** | 0x180A | Fabrikant, modelnummer, firmwareversie |
| **Blood Pressure** | 0x1810 | Bloeddrukmeters |
| **Health Thermometer** | 0x1809 | Medische thermometers |
| **Cycling Speed and Cadence** | 0x1816 | Fietssensoren |
| **Environmental Sensing** | 0x181A | Temperatuur, luchtvochtigheid, druk |
| **Glucose** | 0x1808 | Bloedglucosemeters |
| **HID over GATT** | 0x1812 | Toetsenborden, muizen, gamepads |
| **Proximity** | 0x1802 | "Find My"-functionaliteit |
| **Generic Access** | 0x1800 | **Verplicht** — apparaatnaam en uiterlijk |
#### Custom/vendor-specific services
Fabrikanten kunnen eigen services definiëren met een **128-bit UUID**. Voorbeelden:
| Service | Fabrikant | Toepassing |
|---------|-----------|------------|
| **Nordic UART Service (NUS)** | Nordic Semiconductor | Seriële poort over BLE |
| **Apple Notification Center** | Apple | iPhone notificaties naar wearables |
| **Xiaomi Mi Band Service** | Xiaomi | Fitnesstracker communicatie |
| **MeshCore Companion** | MeshCore | Radio-communicatie (gebruikt NUS) |
#### Het verschil: 16-bit vs. 128-bit UUID
| Type | Lengte | Voorbeeld | Wie mag het maken? |
|------|--------|-----------|-------------------|
| **Officieel (SIG)** | 16-bit | `0x180D` | Alleen Bluetooth SIG |
| **Custom** | 128-bit | `6e400001-b5a3-f393-e0a9-e50e24dcca9e` | Iedereen |
De NUS-service gebruikt bijvoorbeeld deze 128-bit UUID:
```
6e400001-b5a3-f393-e0a9-e50e24dcca9e
```
#### Waarom dit relevant is
In het MeshCore-project gebruiken we **NUS** (een custom service) voor de communicatie. Maar als je met andere BLE-apparaten werkt — zoals een hartslagmeter of een slimme thermostaat — dan gebruiken die vaak **officiële SIG-services**.
Het principe blijft hetzelfde:
1. Ontdek welke services het apparaat aanbiedt
2. Zoek de juiste characteristic
3. Lees, schrijf, of abonneer op notify
---
### 2.5 Notify vs. Read
Er zijn twee manieren om data van een BLE-apparaat te krijgen:
| Methode | Werking | Wanneer gebruiken |
|---------|---------|-------------------|
| **Read** | Jij vraagt actief om data | Eenmalige waarden (bijv. batterijstatus) |
| **Notify** | Apparaat stuurt automatisch bij nieuwe data | Continue datastroom (bijv. berichten) |
**Analogie:**
- **Read** = Je belt iemand en vraagt "hoe gaat het?"
- **Notify** = Je krijgt automatisch een WhatsApp-bericht als er nieuws is
Voor MeshCore-captures gebruik je **Notify** — je wilt immers weten wanneer er een bericht binnenkomt.
---
### 2.6 CCCD — Client Characteristic Configuration Descriptor
De CCCD is de **aan/uit-schakelaar voor Notify**. Technisch gezien:
1. Jouw computer schrijft een `1` naar de CCCD
2. Het apparaat weet nu: "deze client wil notificaties"
3. Bij nieuwe data stuurt het apparaat automatisch een bericht
**Het cruciale punt:** Slechts **één client tegelijk** kan de CCCD activeren. Een tweede client krijgt de foutmelding:
```
Notify acquired
```
Dit betekent: "iemand anders heeft notify al ingeschakeld."
---
### 2.7 Pairing, Bonding en Trust
Dit zijn drie afzonderlijke stappen in het BLE-beveiligingsproces:
| Stap | Wat gebeurt er | Analogie |
|------|----------------|----------|
| **Pairing** | Apparaten wisselen cryptografische sleutels uit | Je maakt kennis en wisselt telefoonnummers |
| **Bonding** | De sleutels worden permanent opgeslagen | Je slaat het nummer op in je contacten |
| **Trust** | Het systeem vertrouwt het apparaat automatisch | Je zet iemand in je favorieten |
Na deze drie stappen hoef je niet elke keer opnieuw de pincode in te voeren.
**Controle in Linux:**
```bash
bluetoothctl info literal:AA:BB:CC:DD:EE:FF | egrep -i "Paired|Bonded|Trusted"
```
Verwachte output:
```
Paired: yes
Bonded: yes
Trusted: yes
```
---
### 2.8 Ownership — Het kernprobleem
**Ownership** is een informele term die aangeeft: "welke client heeft op dit moment de actieve GATT-sessie met notify?"
**Analogie:** Denk aan een walkietalkie waarbij maar één persoon tegelijk kan luisteren:
- Als GNOME Bluetooth Manager al verbonden is → die is de "eigenaar"
- Als jouw Python-script daarna probeert te verbinden → krijgt het geen toegang
**Typische "eigenaren" die problemen veroorzaken:**
- GNOME Bluetooth GUI (draait vaak op de achtergrond)
- `bluetoothctl connect` (maakt bluetoothctl de eigenaar)
- Telefoon met Bluetooth aan
- Andere BLE-apps
---
### 2.9 BlueZ
**BlueZ** is de officiële Bluetooth-stack voor Linux. Het is de software die alle Bluetooth-communicatie afhandelt tussen je applicaties en de hardware.
---
### 2.10 Bleak
**Bleak** is een Python-bibliotheek voor BLE-communicatie. Het bouwt voort op BlueZ (Linux), Core Bluetooth (macOS) of WinRT (Windows).
---
## 3. BLE versus Classic Bluetooth
Een veelvoorkomende vraag: zijn BLE en "gewone" Bluetooth hetzelfde? Het antwoord is **nee** — het zijn verschillende technologieën die wel dezelfde naam en frequentieband delen.
### 3.1 Twee smaken van Bluetooth
Sinds Bluetooth 4.0 (2010) zijn er **twee afzonderlijke radiosystemen** binnen de Bluetooth-standaard:
| Naam | Technische term | Kenmerken |
|------|-----------------|-----------|
| **Classic Bluetooth** | BR/EDR (Basic Rate / Enhanced Data Rate) | Hoge datasnelheid, continue verbinding, meer stroom |
| **Bluetooth Low Energy** | BLE (ook: Bluetooth Smart) | Lage datasnelheid, korte bursts, zeer zuinig |
**Cruciaal:** Dit zijn **verschillende radioprotocollen** die niet rechtstreeks met elkaar kunnen communiceren.
### 3.2 Protocol én hardware
Bluetooth (zowel Classic als BLE) omvat **meerdere lagen** — het is niet alleen een protocol, maar ook hardware:
```
┌─────────────────────────────────────────┐
│ SOFTWARE │
│ ┌───────────────────────────────────┐ │
│ │ Applicatie (jouw code) │ │
│ ├───────────────────────────────────┤ │
│ │ Profielen / GATT Services │ │
│ ├───────────────────────────────────┤ │
│ │ Protocollen (ATT, L2CAP, etc.) │ │
│ ├───────────────────────────────────┤ │
│ │ Host Controller Interface (HCI) │ │ ← Grens software/firmware
│ └───────────────────────────────────┘ │
├─────────────────────────────────────────┤
│ FIRMWARE │
│ ┌───────────────────────────────────┐ │
│ │ Link Layer / Controller │ │
│ └───────────────────────────────────┘ │
├─────────────────────────────────────────┤
│ HARDWARE │
│ ┌───────────────────────────────────┐ │
│ │ Radio (2.4 GHz transceiver) │ │
│ ├───────────────────────────────────┤ │
│ │ Antenne │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
### 3.3 Waar zit het verschil?
Het verschil zit op **meerdere lagen**, niet alleen protocol:
| Laag | Classic (BR/EDR) | BLE | Verschil in hardware? |
|------|------------------|-----|----------------------|
| **Radio** | GFSK, π/4-DQPSK, 8DPSK | GFSK | **Ja** — andere modulatie |
| **Kanalen** | 79 kanalen, 1 MHz breed | 40 kanalen, 2 MHz breed | **Ja** — andere indeling |
| **Link Layer** | LMP (Link Manager Protocol) | LL (Link Layer) | **Ja** — andere state machine |
| **Protocollen** | L2CAP, RFCOMM, SDP | L2CAP, ATT, GATT | Nee — software |
### 3.4 Dual-mode apparaten
De overlap zit in apparaten die **beide** ondersteunen:
| Apparaattype | Ondersteunt | Voorbeeld |
|--------------|-------------|-----------|
| **Classic-only** | Alleen BR/EDR | Oude headsets, auto-audio |
| **BLE-only** (Bluetooth Smart) | Alleen BLE | Fitnesstrackers, sensoren, T1000-e |
| **Dual-Mode** (Bluetooth Smart Ready) | Beide | Smartphones, laptops, ESP32 |
**Jouw smartphone** is dual-mode: hij kan praten met je klassieke Bluetooth-koptelefoon (BR/EDR) én met je MeshCore T1000-e (BLE).
### 3.5 Praktijkvoorbeelden
| Scenario | Wat wordt gebruikt |
|----------|-------------------|
| Muziek naar je koptelefoon | **Classic** (A2DP profiel) |
| Hartslag van je smartwatch | **BLE** (Heart Rate Service) |
| Bestand naar laptop sturen | **Classic** (OBEX/FTP profiel) |
| MeshCore T1000-e uitlezen | **BLE** (NUS service) |
| Handsfree bellen in auto | **Classic** (HFP profiel) |
| Slimme lamp bedienen | **BLE** (eigen GATT service) |
---
## 4. BLE kanaalindeling en frequency hopping
### 4.1 De 40 BLE-kanalen
De 2.4 GHz ISM-band loopt van **2400 MHz tot 2483.5 MHz** (83.5 MHz breed).
BLE verdeelt dit in **40 kanalen van elk 2 MHz**:
```
2400 MHz 2480 MHz
│ │
▼ ▼
┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
│00│01│02│03│04│05│06│07│08│09│10│11│12│13│14│15│16│17│18│19│...→ 39
└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
└──────────────────────────────────────────────────────┘
2 MHz per kanaal
```
**Totaal:** 40 × 2 MHz = **80 MHz** gebruikt
### 4.2 Advertising vs. data kanalen
De 40 kanalen zijn niet allemaal gelijk:
| Type | Kanalen | Functie |
|------|---------|---------|
| **Advertising** | 3 (nrs. 37, 38, 39) | Apparaten vinden, verbinding starten |
| **Data** | 37 (nrs. 0-36) | Daadwerkelijke communicatie na verbinding |
De advertising-kanalen zijn strategisch gekozen om **Wi-Fi-interferentie** te vermijden:
```
Wi-Fi kanaal 1 Wi-Fi kanaal 6 Wi-Fi kanaal 11
│ │ │
▼ ▼ ▼
────████████─────────────█████████────────────████████────
BLE: ▲ ▲ ▲
Ch.37 Ch.38 Ch.39
(advertising kanalen zitten tússen de Wi-Fi kanalen)
```
### 4.3 Vergelijking met Classic Bluetooth
| Aspect | Classic (BR/EDR) | BLE |
|--------|------------------|-----|
| **Aantal kanalen** | 79 | 40 |
| **Kanaalbreedte** | 1 MHz | 2 MHz |
| **Totale bandbreedte** | 79 MHz | 80 MHz |
| **Frequency hopping** | Ja, alle 79 | Ja, 37 datakanalen |
Classic heeft **meer maar smallere** kanalen, BLE heeft **minder maar bredere** kanalen.
### 4.4 Frequency hopping: één kanaal tegelijk
**Belangrijk inzicht:** Je gebruikt altijd maar **één kanaal tegelijk**. De 40 kanalen zijn er voor **frequency hopping** — het afwisselend wisselen van kanaal om interferentie te vermijden:
```
Tijd →
┌───┐ ┌───┐ ┌───┐ ┌───┐
Ch. 12 │ ▓ │ │ │ │ │ │ ▓ │
└───┘ └───┘ └───┘ └───┘
┌───┐ ┌───┐ ┌───┐ ┌───┐
Ch. 07 │ │ │ ▓ │ │ │ │ │
└───┘ └───┘ └───┘ └───┘
┌───┐ ┌───┐ ┌───┐ ┌───┐
Ch. 31 │ │ │ │ │ ▓ │ │ │
└───┘ └───┘ └───┘ └───┘
↑ ↑ ↑ ↑
Pakket 1 Pakket 2 Pakket 3 Pakket 4
```
Dit is **geen parallelle communicatie** — het is serieel met wisselende frequentie.
---
## 5. Twee betekenissen van "serieel"
Wanneer we zeggen "NUS is serieel", kan dit verwarring veroorzaken. Het woord "serieel" heeft namelijk **twee verschillende betekenissen** in deze context.
### 5.1 Radio-niveau: altijd serieel
**Alle** draadloze communicatie is serieel op fysiek niveau — je hebt maar **één radiokanaal tegelijk** en bits gaan **na elkaar** de lucht in:
```
Radiogolf: ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁▂▃▄▅▆▇█▇▆▅▄▃▂▁
Bits: 0 1 1 0 1 0 0 1 1 1 0 1 0 0 1 0 → één voor één
```
De 40 kanalen zijn voor **frequency hopping**, niet voor parallel versturen. Dit geldt voor **alle** BLE-services — NUS, Heart Rate, Battery, allemaal.
### 5.2 Data-niveau: NUS simuleert een seriële poort
Wanneer we zeggen "NUS is een seriële service", bedoelen we iets anders:
**NUS simuleert een oude seriële poort (RS-232/UART):**
```
Historisch (jaren '80-'00):
Computer Apparaat
┌──────┐ Seriële kabel ┌──────┐
│ COM1 │←────────────────→│ UART │
└──────┘ (RS-232) └──────┘
Bytes: 0x48 0x65 0x6C 0x6C 0x6F ("Hello")
└─────────────────────┘
Geen structuur, gewoon een stroom bytes
```
**NUS bootst dit na over BLE:**
```
Vandaag:
Computer Apparaat
┌──────┐ BLE (NUS) ┌──────┐
│ App │←~~~~~~~~~~~~~~~~~~~~→│ MCU │
└──────┘ (draadloos) └──────┘
Gedraagt zich alsof er een seriële kabel zit
```
### 5.3 Vergelijking: serieel vs. gestructureerd
| Aspect | NUS (serieel) | Heart Rate (gestructureerd) |
|--------|---------------|----------------------------|
| **Radio** | Serieel, frequency hopping | Serieel, frequency hopping |
| **Data** | Ongestructureerde bytestroom | Vaste velden met betekenis |
| **Wie bepaalt formaat?** | Jij (eigen protocol) | Bluetooth SIG (specificatie) |
### 5.4 Analogie: snelweg met rijstroken
Denk aan een **snelweg met 40 rijstroken** (de kanalen):
- Je mag maar **één rijstrook tegelijk** gebruiken
- Je wisselt regelmatig van rijstrook (frequency hopping, om botsingen te vermijden)
- De **vracht** die je vervoert kan verschillen:
| Service | Vracht-analogie |
|---------|-----------------|
| **NUS** | Losse spullen door elkaar (flexibel, maar jij moet uitzoeken wat wat is) |
| **Heart Rate** | Gestandaardiseerde pallets (iedereen weet wat waar zit) |
De **snelweg werkt hetzelfde** — het verschil zit in hoe je de vracht organiseert.
---
## 6. Seriële vs. gestructureerde services (verdieping)
Een belangrijk onderscheid dat vaak over het hoofd wordt gezien: **niet alle BLE-services werken hetzelfde**. Er zijn fundamenteel twee benaderingen.
### 6.1 Seriële services (stream-gebaseerd)
**NUS (Nordic UART Service)** is ontworpen om een **seriële poort te simuleren**:
- Continue datastroom van ruwe bytes
- Geen opgelegde structuur
- Jij bepaalt zelf het formaat en de betekenis
**Analogie:** Een seriële service is als een **telefoonlijn** — je kunt alles zeggen wat je wilt, in elke taal, zonder vaste regels.
```
Voorbeeld NUS-data (MeshCore):
0x01 0x0A 0x48 0x65 0x6C 0x6C 0x6F ...
└── Betekenis bepaald door MeshCore protocol, niet door BLE
```
### 6.2 Gestructureerde services (veld-gebaseerd)
De meeste officiële SIG-services werken **anders** — ze definiëren **exact** welke bytes wat betekenen:
**Analogie:** Een gestructureerde service is als een **belastingformulier** — elk vakje heeft een vaste betekenis en een voorgeschreven formaat.
#### Voorbeeld: Heart Rate Measurement
```
Byte 0: Flags (bitfield)
├── Bit 0: 0 = hartslag in 1 byte, 1 = hartslag in 2 bytes
├── Bit 1-2: Sensor contact status
├── Bit 3: Energy expended aanwezig?
└── Bit 4: RR-interval aanwezig?
Byte 1(-2): Heart rate waarde
Byte N...: Optionele extra velden (afhankelijk van flags)
```
**Concreet voorbeeld:**
```
Ontvangen bytes: 0x00 0x73
0x00 = Flags: 8-bit formaat, geen extra velden
0x73 = 115 decimaal → hartslag is 115 bpm
```
Je krijgt dus niet de tekst "115", maar een binair pakket dat je moet **parsen** volgens de specificatie.
#### Voorbeeld: Battery Level
Eenvoudiger — slechts **1 byte**:
```
Ontvangen byte: 0x5A
0x5A = 90 decimaal → batterij is 90%
```
#### Voorbeeld: Environmental Sensing (temperatuur)
```
Ontvangen bytes: 0x9C 0x08
Little-endian 16-bit signed integer: 0x089C = 2204
Resolutie: 0.01°C
Temperatuur: 2204 × 0.01 = 22.04°C
```
### 6.3 Vergelijkingstabel
| Aspect | Serieel (NUS) | Gestructureerd (SIG) |
|--------|---------------|----------------------|
| **Data-indeling** | Vrij, zelf bepalen | Vast, door specificatie |
| **Wie definieert het formaat?** | Jij / de fabrikant | Bluetooth SIG |
| **Waar vind je de specificatie?** | Eigen documentatie / broncode | bluetooth.com/specifications |
| **Parsing** | Eigen parser bouwen | Standaard parser mogelijk |
| **Interoperabiliteit** | Alleen eigen software | Elke conforme app/device |
| **Flexibiliteit** | Maximaal | Beperkt tot spec |
| **Complexiteit** | Eenvoudig te starten | Spec lezen vereist |
### 6.4 Voorbeelden van gestructureerde services
| Service | Characteristic | Data-formaat |
|---------|----------------|--------------|
| **Battery Service** | Battery Level | 1 byte: 0-100 (percentage) |
| **Heart Rate** | Heart Rate Measurement | Flags + 8/16-bit HR + optionele velden |
| **Health Thermometer** | Temperature Measurement | IEEE-11073 FLOAT (4 bytes) |
| **Blood Pressure** | Blood Pressure Measurement | Compound: systolisch, diastolisch, MAP, pulse |
| **Cycling Speed & Cadence** | CSC Measurement | 32-bit tellers + 16-bit tijd |
| **Environmental Sensing** | Temperature | 16-bit signed, resolutie 0.01°C |
| **Environmental Sensing** | Humidity | 16-bit unsigned, resolutie 0.01% |
| **Environmental Sensing** | Pressure | 32-bit unsigned, resolutie 0.1 Pa |
### 6.5 Wanneer welke aanpak?
| Situatie | Aanbevolen aanpak |
|----------|-------------------|
| Eigen protocol (MeshCore, custom IoT) | **Serieel** (NUS of eigen service) |
| Standaard use-case (hartslag, batterij) | **Gestructureerd** (SIG-service) |
| Interoperabiliteit met bestaande apps vereist | **Gestructureerd** (SIG-service) |
| Complexe, variabele datastructuren | **Serieel** met eigen protocol |
| Snel prototype zonder spec-studie | **Serieel** (NUS) |
### 6.6 Waarom MeshCore NUS gebruikt
MeshCore koos voor NUS (serieel) omdat:
1. **Flexibiliteit** — Het Companion Protocol heeft eigen framing nodig
2. **Geen passende SIG-service** — Er is geen "Mesh Radio Service" standaard
3. **Bidirectionele communicatie** — NUS biedt RX én TX characteristics
4. **Eenvoud** — Geen complexe SIG-specificatie implementeren
Het nadeel: je kunt niet zomaar een willekeurige BLE-app gebruiken om met MeshCore te praten — je hebt software nodig die het MeshCore Companion Protocol begrijpt.
---
## 7. Het OSI-model in context
Het document plaatst het probleem in een **lagenmodel**. Dit helpt begrijpen *waar* het probleem zit:
| Laag | Naam | In dit project | Probleem hier? |
|------|------|----------------|----------------|
| 7 | Applicatie | MeshCore Companion Protocol | Nee |
| 6 | Presentatie | Frame-encoding (hex) | Nee |
| **5** | **Sessie** | **GATT client ↔ server sessie** | **★ JA** |
| 4 | Transport | ATT / GATT | Nee |
| 2 | Data Link | BLE Link Layer | Nee |
| 1 | Fysiek | 2.4 GHz radio | Nee |
**Conclusie:** Het ownership-probleem zit op **laag 5 (sessie)**. De firmware en het protocol zijn niet het probleem — het gaat om wie de sessie "bezit".
---
## 8. De workflow samengevat
### 8.1 Eenmalige voorbereiding
```bash
# Start bluetoothctl
bluetoothctl
# Activeer Bluetooth
power on
agent on
default-agent
scan on
# Wacht tot device zichtbaar is, dan pairen
pair literal:AA:BB:CC:DD:EE:FF
# Voer pincode in (bijv. 123456)
# Vertrouw het apparaat
trust literal:AA:BB:CC:DD:EE:FF
quit
```
### 8.2 Vóór elke capture
1. **Telefoon Bluetooth UIT**
2. **GNOME Bluetooth GUI SLUITEN**
3. **Expliciet disconnecten:**
```bash
bluetoothctl disconnect literal:AA:BB:CC:DD:EE:FF
```
4. **Controleer dat niemand verbonden is:**
```bash
bluetoothctl info literal:AA:BB:CC:DD:EE:FF | egrep -i "Connected"
# Verwacht: Connected: no
```
### 8.3 Capture starten
```bash
python tools/ble_observe.py \
--address literal:AA:BB:CC:DD:EE:FF \
--pre-scan-seconds 10 \
--connect-timeout 30 \
--notify \
--notify-seconds 30 \
--notify-start-timeout 30 \
--disconnect-timeout 15 \
--capture captures/session_$(date -u +%Y-%m-%d_%H%M%S).raw
```
---
## 9. Veelgemaakte fouten
| Fout | Gevolg | Oplossing |
|------|--------|-----------|
| GNOME Bluetooth Manager open | `Notify acquired` | GUI sluiten |
| `bluetoothctl connect` gedaan | Tool faalt bij notify | Altijd `disconnect` eerst |
| Telefoon Bluetooth aan | Telefoon claimt verbinding | Telefoon BT uit |
| Meerdere scripts tegelijk | Eerste wint, rest faalt | Één tool tegelijk |
---
## 10. Visueel overzicht (sequentiediagram)
```
┌──────┐ ┌───────┐ ┌───────┐ ┌───────┐
│ User │ │ Linux │ │ BlueZ │ │ Radio │
└──┬───┘ └───┬───┘ └───┬───┘ └───┬───┘
│ │ │ │
│ pair/trust │ │ │
│────────────>│────────────────────────-->│ (eenmalig)
│ │ │ │
│ │ Geen actieve verbinding│
│ │ │ │
│ start tool │ │ │
│────────────>│ │ │
│ │ connect() │ │
│ │────────────>│────────────>│
│ │ │ │
│ │start_notify │ │
│ │────────────>│────────────>│
│ │ │ │
│ │ notify (data frames) │
│ │<────────────│<────────────│
│ │ │ │
│ │ stop_notify │ │
│ │────────────>│────────────>│
│ │ │ │
│ │ disconnect │ │
│ │────────────>│────────────>│
│ │ │ │
```
---
## 11. Conclusie
Het document bewijst dat:
- ✅ MeshCore BLE companion **werkt correct** op Linux
- ✅ De firmware **blokkeert notify niet**
- ✅ Het enige vereiste is: **exact één actieve BLE client per radio**
Wanneer je deze workflow volgt, zijn captures reproduceerbaar en is verdere protocol-analyse mogelijk.
---
## 12. Referenties
- MeshCore Companion Radio Protocol: [GitHub Wiki](https://github.com/meshcore-dev/MeshCore/wiki/Companion-Radio-Protocol)
- Bluetooth SIG Assigned Numbers (officiële services): [bluetooth.com/specifications/assigned-numbers](https://www.bluetooth.com/specifications/assigned-numbers/)
- Bluetooth SIG GATT Specifications: [bluetooth.com/specifications/specs](https://www.bluetooth.com/specifications/specs/)
- Nordic Bluetooth Numbers Database: [GitHub](https://github.com/NordicSemiconductor/bluetooth-numbers-database)
- GATT Uitleg (Adafruit): [learn.adafruit.com](https://learn.adafruit.com/introduction-to-bluetooth-low-energy/gatt)
- Bleak documentatie: [bleak.readthedocs.io](https://bleak.readthedocs.io/)
- BlueZ: [bluez.org](http://www.bluez.org/)
---
> **Document:** `ble_capture_workflow_t_1000_e_uitleg.md`
> **Gebaseerd op:** `ble_capture_workflow_t_1000_e.md`

1105
meshcore_gui.py Normal file

File diff suppressed because it is too large Load Diff