Skip to content
jasiszPublic

About

Two BLE links in a trench coat — a transparent BLE relay for stock Meshtastic nodes on nRF52-DK.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

Repository files navigation

NotANode

NotANode — two BLE links in a trench coat

Two BLE links in a trench coat.

NotANode is an experimental, transparent Bluetooth LE relay for stock Meshtastic nodes. It runs on a Nordic nRF52-DK and lets an official Meshtastic client talk to a real node through two independent BLE links.

No custom Meshtastic firmware. No custom phone app. No Wi-Fi, cloud, MQTT, or extra LoRa radio.

The balcony problem

LoRa wants height. BLE wants proximity.

Putting a Meshtastic node on a balcony, roof, mast, or attic can dramatically improve its radio position while making its phone connection dramatically worse. NotANode sits somewhere in the middle: close enough to the outdoor node and close enough to the people inside.

                         HIGH / OUTSIDE

                 ┌─────────────────────────┐
      LoRa mesh )))  stock Meshtastic node │  balcony / roof / mast
                 └────────────┬────────────┘
                              │
                           BLE link A
                              │
        ┌─────────────────────▼─────────────────────┐
        │  □  □  □  □  □                 ║ balcony │
        ├─────────────────────────────────╫─────────┤
        │  □  □  □  □  □                 ║ balcony │
        ├─────────────────────────────────╫─────────┤
        │       ┌─────────────────┐       ║ balcony │
        │       │ NotANode        │       ║         │
        │       │ Nordic nRF52-DK │       ║         │
        │       └────────┬────────┘       ║         │
        ├────────────────┼────────────────╫─────────┤
        │             BLE link B          ║ balcony │
        ├────────────────┼────────────────╫─────────┤
        │       ┌────────▼────────┐        ║         │
        │ sofa  │ official app    │        ║ inside  │
        │       └─────────────────┘        ║         │
        └───────────────────────────────────────────┘

                          INSIDE / COUCH

NotANode is not a BLE RF repeater. It terminates two real connections and proxies Meshtastic's GATT transport between them:

official app  ⇄  BLE  ⇄  NotANode  ⇄  BLE  ⇄  stock node  ⇄  LoRa mesh

The firmware understands BLE transport semantics, but it does not decode or recreate Meshtastic protobufs. The design goal is deliberately boring:

GATT in  →  bounded queues  →  GATT out

It actually works

The first successful end-to-end hardware session used a stock Meshtastic node, an nRF52-DK, and an unmodified official client. The app paired with NotANode, completed the normal PhoneAPI configuration and NodeDB handshake, and continued into normal traffic.

The captured session ended with:

upstream:             ready, MTU 247
downstream:           connected, MTU 247
ToRadio forwarded:    7
FromRadio forwarded:  75
FromNum received:     5
forward errors:       0
queue overflows:      0

The important discovery was a compatibility edge case: some released clients wait for a FromNum wake during configuration even though current clients proactively drain FromRadio. NotANode emits the smallest possible local wake when its downstream queue changes from empty to non-empty. Payloads remain opaque and unchanged.

Current status

NotANode is v0.1-alpha. The core path is running on real hardware; broader device and range testing is still wanted.

Capability Status
Upstream scan, connect, PIN pairing, bonding, discovery Hardware tested
Upstream FromNum subscription and reconnect Hardware tested
Official-client discovery, pairing, MTU 247 Hardware tested
PhoneAPI configuration and real NodeDB transfer Hardware tested
Normal bidirectional client traffic through the stock node Hardware tested
Long-duration balcony/roof range measurements Pending
Multiple remembered clients, used one at a time Implemented; broader A/B testing pending

Hardware and SDK

  • Nordic nRF52-DK / PCA10040
  • nRF52832: 512 KiB flash, 64 KiB RAM
  • Zephyr board target: nrf52dk/nrf52832
  • Tested with nRF Connect SDK v3.3.3
  • Nordic SoftDevice Controller in central + peripheral multirole mode

Build and flash

From an initialized nRF Connect SDK v3.3.3 workspace:

west build --pristine -b nrf52dk/nrf52832 /path/to/notanode
west flash

Battery-oriented build

The default image remains the development and first-setup image. A separate headless profile keeps the same GATT proxy but trades discovery/reconnection latency for lower average current:

west build --pristine -b nrf52dk/nrf52832 /path/to/notanode \
  -- -Dblema_EXTRA_CONF_FILE=battery.conf
west flash

Configure the upstream target and PIN with the normal image first. Flashing the battery image does not erase the NVS settings partition, so the target, PIN, and BLE bonds remain available. Do not perform a full chip erase between the two images.

Some drag-and-drop J-Link MSD firmware performs a full-chip erase despite the HEX file not containing the NVS address range. For that development-only case, create an ignored battery.local.conf containing a fallback identity:

CONFIG_MESHTASTIC_RELAY_FACTORY_TARGET_NAME="Meshtastic_abcd"
CONFIG_MESHTASTIC_RELAY_FACTORY_UPSTREAM_PIN=123456

Build with both private fragments:

west build --pristine -b nrf52dk/nrf52832 /path/to/notanode \
  -- -Dblema_EXTRA_CONF_FILE="battery.conf;battery.local.conf"

The public defaults contain no target or PIN. NVS always overrides these fallbacks when stored configuration exists.

The battery profile:

  • removes UART logging and the interactive shell;
  • pulses status LEDs for 20 ms instead of leaving LED1/LED2 lit;
  • uses 100–150 ms preferred connection intervals and downstream latency 4;
  • advertises every 400–500 ms;
  • scans upstream in eight-second bursts at 20% RX duty cycle, then backs off from 5 to 60 seconds while the node remains absent;
  • disables periodic RSSI polling.

The relay transport, buffers, security, bonds, and one-active-client policy are unchanged. A phone or upstream node may negotiate different legal connection parameters. After a long upstream outage, automatic reconnection can take up to about 68 seconds (the 60-second maximum sleep plus the next scan burst).

The current default/debug build reports:

Region Used Total Free Used
Flash application partition 297,616 B 499,712 B 202,096 B 59.56%
RAM 55,188 B 65,536 B 10,348 B 84.21%

The battery image reports 226,928 B flash (45.41%) and 47,508 B RAM (72.49%), leaving 272,784 B flash and 18,028 B RAM. These size differences do not directly predict battery life; the radio/LED duty-cycle changes above are the important runtime savings.

The last 24 KiB of flash is reserved for NVS settings and BLE bonds. Both the DTS overlay and pm_static.yml pin this partition to 0x7a000, so default and battery builds use the same persistent data. The image is intended for direct flashing, not MCUboot.

First setup

Connect to the DK USB UART at 115200 baud. Configure either the upstream BLE identity (preferred) or its exact advertised name:

notanode target AA:BB:CC:DD:EE:FF random
notanode target-name Meshtastic_abcd
notanode upstream-pin 123456
notanode status

Use public instead of random where appropriate. For a node configured in Meshtastic NO_PIN mode:

notanode upstream-pin clear

The upstream PIN is persisted but never printed. NotANode's downstream PIN defaults to 123456; change CONFIG_MESHTASTIC_RELAY_DOWNSTREAM_PIN in prj.conf before deployment if needed. It is compile-time because an nRF52-DK has no trustworthy PIN display.

Then:

  1. Wait for LED1 to become solid.
  2. Open the official Meshtastic app.
  3. Select NotANode_52D0 and enter NotANode's downstream PIN.
  4. Use Meshtastic normally. The configuration, NodeDB, and mesh state belong to the real upstream node.

NotANode advertises its Meshtastic-compatible service only after the upstream node is secured, discovered, and subscribed. Losing upstream terminates the active downstream session; recovery and advertising resume automatically.

UART commands

notanode status
notanode scan
notanode target <AA:BB:CC:DD:EE:FF> [random|public]
notanode target-name <name>
notanode upstream-pin <PIN|clear>
notanode reconnect
notanode clear-upstream-bond
notanode clear-downstream-bonds
notanode verbose on|off
notanode stats [reset]
notanode stacks

relay remains available as a legacy alias. Verbose mode hex-dumps PhoneAPI frames for development; default logs never print pairing keys or the configured PIN.

notanode stacks reports each thread's high-water mark and the lowest observed headroom. An upstream ToRadio write or FromRadio read that does not complete within 10 seconds is logged, counted as gatt_timeouts, and recovered by the normal upstream reconnect path instead of leaving the proxy permanently busy.

LEDs and buttons

Control Meaning
LED1 blinking Scanning, connecting, securing, or recovering upstream
LED1 solid Upstream Meshtastic service ready
LED2 solid Downstream client connected
LED3 pulse ToRadio forwarded upstream
LED4 pulse FromRadio consumed downstream
BTN1 Force upstream reconnect
BTN2 Clear upstream target, PIN, and bond
BTN3 Disconnect downstream and clear downstream bonds
BTN4 Toggle verbose diagnostics

In the battery image, LEDs are event pulses rather than persistent state: LED1 pulses on upstream search/ready transitions and LED2 on downstream connection. Traffic LEDs use 10 ms pulses. Buttons still perform their recovery actions, but the headless image has no UART feedback and BTN4 has no visible effect.

Design constraints

  • Exactly two BLE connection slots: one central plus one peripheral.
  • Up to eight stored bonds, but only one active downstream client.
  • Static four-entry queues in each direction, each entry bounded to 512 bytes.
  • Independent ATT MTUs and GATT long operations; no protobuf fragmentation.
  • FIFO ordering, explicit backpressure, and no silent truncation.
  • The upstream connection stays alive while personal downstream devices change.
  • 1M PHY and moderate 30–50 ms connection intervals favor reliability.

Known limitations

  • Only one downstream client can be active at a time. Multiple clients may be bonded and used sequentially.
  • Long-duration range and failure-injection testing is not complete.
  • LogRadio, Battery Service, Device Information Service, OTA, and DFU are not proxied.
  • Three ATT prepare buffers support a 512-byte downstream write at common 185/247-byte phone MTUs. A peer stuck at the default MTU 23 cannot prepare a full 512-byte write with this RAM-bounded configuration.
  • On controllers that reject standard HCI Read RSSI, the diagnostic output is explicitly labeled RSSI(last-scan) rather than pretending it is live.
  • Address targeting assumes a stable advertised identity. Exact-name targeting is available for nodes using a private address unknown before bonding.

Documentation

  • Protocol reconnaissance — official UUIDs, client discovery, security, PhoneAPI, queue, and MTU evidence
  • Architecture — modules, state transitions, buffering, and recovery design
  • Test checklist — compatibility, range, multi-client, and failure-injection acceptance tests

Name

The project requirements repeat one warning more than any other: this relay is not another Meshtastic node. The name simply makes that promise impossible to miss.

The architecture still answers to its internal codename, Dumbwaiter: it carries packets upstairs without looking inside.

License and trademark

Licensed under the Apache License 2.0.

NotANode is an independent community project. It is not affiliated with or endorsed by Meshtastic LLC. Meshtastic is a registered trademark of Meshtastic LLC. The relay does not include Meshtastic firmware or protobuf implementation; it interoperates with the public BLE GATT interface.

About

Two BLE links in a trench coat — a transparent BLE relay for stock Meshtastic nodes on nRF52-DK.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages