How to Use WendyOS as a BLE Gateway for Edge AI on Jetson and Raspberry Pi

Configuring WendyOS to bridge BLE sensors with edge AI on Jetson and Raspberry Pi

You’ve built the BLE gateway the hard way. A bleak loop, a few retries that mostly work, an MQTT bridge in a Python venv, an ONNX inference container next to it, and four systemd units pretending to be a system. It runs fine on the bench.

Then you put 50 of them in a steel mill. The BlueZ daemon starts dropping characteristic subscriptions at 3am. The model container drifts a version ahead of the preprocessor. Correlating an anomaly score back to the raw vibration packet that caused it means SSH-ing in and grepping across five journals.

This guide shows how to replace that whole stack with WendyOS, where BLE ingest and edge AI are peer subsystems in one declarative pipeline, running the same config on a Jetson Orin Nano or a Raspberry Pi 5.

Why BLE Gateways Are Harder Than They Look

The standard pattern (BlueZ + Python + MQTT + a separate inference runtime) has three failure modes that only show up at scale.

First, BLE state is fragile and nobody owns it. Your bleak loop reconnects, but BlueZ’s internal cache disagrees about which characteristic handles are valid. Subscriptions go silent without errors.

Second, the model and the data preprocessor version independently. The Python service does FFT in NumPy. The ONNX model expects a specific bin layout. One gets updated, the other doesn’t, and your anomaly scores are quietly garbage for two weeks.

Third, updates are all-or-nothing image reflashes, or worse, scp and pray.

Container-based fleet OSes solve the deployment problem but treat BLE as just another container with USB passthrough, which inherits every BlueZ pain point. Heavier edge frameworks add their own abstraction layer on top of the OS, which is more software to babysit, not less.

ConcernBlueZ + scriptsWendyOS
BLE ingestbleak loop + retriesdeclarative source
Inferenceseparate containerinline pipeline stage
Updatesimage reflashatomic wendyctl deploy
Observabilitygrep across servicescorrelated logs

What WendyOS Gives You Out of the Box

WendyOS ships a native BLE ingest layer that handles central role, multi-adapter scanning, GATT subscriptions, and exponential reconnect. The BLE state machine is owned by the runtime rather than a userspace script poking at bluetoothd.

On top of that sits the physical AI runtime: an inference engine that loads ONNX or TFLite models and auto-selects an execution provider based on the hardware it finds. CUDA on Jetson. NEON-optimized kernels on Pi. CPU fallback if neither is present.

The two subsystems talk through a declarative pipeline. A BLE source feeds a transform stage, the transform feeds inference, inference feeds a sink. You describe this in YAML. You don’t write the glue.

If you have assets where running a local gateway isn’t practical (mobile fleets, remote sensors, anything battery-powered at the edge of nowhere), Hubble can act as a managed global BLE backhaul into the same pipeline model, so the config shape stays the same whether the BLE traffic comes from a dongle on the gateway or from a satellite link.

Setting Up the Jetson BLE Gateway (or Raspberry Pi)

WendyOS supports Jetson Orin Nano and Orin NX, plus Raspberry Pi 4 and 5. The image and config are identical across them.

Flash the image:

# Jetson Orin Nano (via SDK Manager host)
wendyctl image flash --target jetson-orin-nano --device /dev/sdX

# Raspberry Pi 5
wendyctl image flash --target rpi5 --device /dev/sdX

After first boot, verify that the BLE adapter and any accelerator showed up:

wendyctl hw inspect

You should see your HCI adapter listed: the onboard controller on Pi 5, or a USB dongle on Jetson (which doesn’t ship with onboard BLE). The accelerator line should read cuda on Jetson or neon on Pi.

The topology you’re building:

   [BLE Sensors]          [WendyOS Gateway]           [Cloud / Hubble]
   vibration  ))         +------------------+        +---------------+
   temp       )))  --->  | BLE ingest       |        |               |
   current    ))         |   |              |  --->  |  MQTT broker  |
                         |   v              |        |   or Hubble   |
                         | edge inference   |        |   backhaul    |
                         |   |              |        |               |
                         |   v              |        +---------------+
                         | sink (mqtt/http) |
                         +------------------+

One note for Jetson: the Orin Nano dev kit has no onboard BLE. Plan for a USB dongle (and a second one for redundancy if this is production). The Pi 5’s onboard controller is fine for low-density deployments but will saturate around 8 to 10 concurrently subscribed peripherals in practice.

Defining the BLE Ingest Layer

Here’s a wendyos.yaml BLE source block declaring three characteristics across a fleet of advertised bearing-monitor sensors. The syntax below is representative; check the current WendyOS schema docs before copying into production.

sources:
  - id: bearing-fleet
    type: ble
    adapter: auto            # or hci0, hci1 for explicit pinning
    scan:
      mode: active
      filters:
        - manufacturer_id: 0x05A7
        - name_prefix: "BRG-"
    subscriptions:
      - characteristic: 0x2A56   # vibration RMS
        as: vibration_rms
        notify: true
      - characteristic: 0x2A6E   # temperature
        as: temp_c
        notify: true
      - characteristic: 0x2A66   # current draw
        as: current_a
        notify: true
    reconnect:
      backoff: exponential
      max_interval: 60s
    dedup: true
    timestamp: ingest

The runtime owns reconnect, dedup, and ingest-time timestamping. Each event that flows downstream carries the peripheral address, characteristic ID, timestamp, and raw payload. That’s roughly 300 lines of bleak plus retry plus dedup logic you don’t write or maintain.

Multiple adapters work the same way: declare adapter: hci0 and adapter: hci1 as separate sources and the runtime load-balances scan windows across them. That’s how you get adapter redundancy on a Pi without writing failover logic.

Wiring in Edge AI for Predictive Maintenance

Drop your ONNX model into /var/lib/wendyos/models/, then declare the pipeline. For bearing fault detection, a small autoencoder trained on healthy vibration spectra works well: input is a 64-bin FFT window, output is a reconstruction error you threshold for anomaly.

transforms:
  - id: vibration-window
    source: bearing-fleet
    select: vibration_rms
    window:
      size: 2s
      stride: 1s
    features:
      - fft:
          bins: 64
          sample_rate: 1000

inference:
  - id: bearing-anomaly
    model: /var/lib/wendyos/models/bearing-ae-v3.onnx
    accelerator: auto         # cuda on Jetson, neon on Pi
    input:
      spectrum: vibration-window.fft
    output:
      anomaly_score: reconstruction_error

sinks:
  - id: mqtt-out
    type: mqtt
    broker: tls://mqtt.internal:8883
    topic: "plant/{peripheral_address}/anomaly"
    from: bearing-anomaly

The pipeline reads as one graph:

   BLE source  -->  window/transform  -->  inference  -->  sink
   (GATT subs)      (RMS, FFT bins)        (ONNX)          (MQTT)
        |                  |                   |              |
        +--- timestamps ---+--- features ------+--- score ----+
                  (correlated in structured logs)

The transform stage lives inside the runtime, so the FFT layout and the model’s expected input layout ship together and can’t drift.

The same YAML runs on both targets. On Jetson Orin Nano, accelerator: auto resolves to the CUDA execution provider and a typical 64-bin window hits inference in under 15 ms. On a Pi 5, the NEON-optimized ONNX Runtime provider gets you to roughly 40 to 60 ms for the same model. End-to-end (BLE notify to MQTT publish) lands under 200 ms in both cases for normal window sizes.

Running, Observing, Updating

Standard systemd:

systemctl status wendyos
wendyctl pipeline tail --id bearing-anomaly

pipeline tail is the thing you’d otherwise spend a weekend building. It streams structured events that follow a single BLE notification through window, feature extraction, inference, and sink, with one correlation ID. When an anomaly score looks wrong, you can pull the exact 2-second FFT window and raw vibration samples that produced it without log archaeology.

For updates, ship the model and config as one transaction:

wendyctl deploy --config wendyos.yaml --models ./models/

The deploy is atomic, and a new pipeline that fails its health checks after activation rolls back automatically. No half-updated fleet.

For fleets where putting a gateway in the field is impractical (mobile assets, remote sensors, anywhere you don’t want hardware to maintain), Hubble can backhaul BLE traffic to a regional WendyOS instance over satellite or terrestrial coverage. The pipeline config is identical; only the source’s transport changes.

Production Checklist

Before you call this done:

  • Adapter redundancy. On Pi, add a USB dongle as hci1 alongside the onboard controller. On Jetson, run two dongles. Saturation on a single adapter is the most common silent failure.
  • Thermal headroom on Jetson. CUDA inference under sustained load will throttle the Orin Nano if it’s in a sealed enclosure without a fan. Budget for it.
  • Shadow inference for model rollouts. Run the new model in parallel with the old one as a separate inference block, compare outputs in the sink, and promote when you trust it.
  • Layered config. Keep a base wendyos.yaml and per-site overrides (different MQTT brokers, different scan filters per facility). wendyctl deploy merges them.
  • Decide what stays on-gateway. Raw spectra to the cloud is bandwidth suicide. Anomaly scores plus the offending window on threshold cross is usually the right cut.

Scaling the Fleet or Skipping the Gateway

You’ve got one runtime, one config, and BLE ingest sitting next to edge AI as peers rather than as duct-taped processes. The same artifact runs on Jetson and Pi, updates atomically, and produces logs you can actually trace.

If you’re scaling the gateway fleet itself, the fleet-scale IoT topology guide covers config layering and rollout patterns. If your assets are mobile or remote enough that a local gateway doesn’t pencil out, look at the Hubble device integration guide for backhauling BLE telemetry into the same pipeline model without putting hardware in the field.


Hubble Network reaches BLE devices directly from satellite, so you can stream telemetry from assets where deploying gateways isn’t practical. See how it works →