Edge AI on BLE Chips: What You Can Actually Run on 256KB of RAM

Running TinyML neural networks on memory-constrained BLE chips with just 256KB of RAM

Pull up any “TinyML on microcontrollers” tutorial and you’ll see a keyword spotter running on a Cortex-M4F, 256KB of RAM, problem solved. What the tutorial skips: that demo wasn’t running a BLE stack at the same time.

Your product probably is. That’s the whole point of picking an nRF52840 instead of a bare MCU. The second you load the SoftDevice and open a connection, a big chunk of that 256KB is gone before your model allocates a single byte.

The real question isn’t “can I run ML on a BLE SoC.” It’s “what’s left after the radio takes its cut, and what actually fits in that leftover.” Let’s budget it honestly.

What the BLE Stack Takes First

Take a concrete config so the numbers mean something: nRF52840, Nordic SoftDevice S140 v7.x, one central plus a few peripheral connections, default ATT MTU (23 bytes) bumped modestly. Your mileage shifts with every one of those knobs, so verify against your own memory.ld and the SoftDevice release notes rather than trusting a blog. But the pattern holds.

The SoftDevice itself reserves a fixed RAM region (__data_start__ gets pushed up by the value in your linker config). For S140 with a handful of connections, you’re looking at roughly 8-16KB of SoftDevice RAM plus connection buffers, and the number climbs fast if you raise MTU, add more links, or enable DLE (Data Length Extension). Call it ~10-24KB for the protocol stack’s own state.

Then your application needs a stack and heap: RTOS task stacks, the BLE event handler, your sensor drivers, logging, the GATT table. On a real product that’s easily 64-96KB once you stop pretending.

Here’s the budget I’d plan against:

[============ 256KB Total RAM ============]
[ BLE Stack ][ App Stack/Heap ][ ML Arena ]
   ~96KB         ~96KB            ~64KB
   |              |                |
   SoftDevice     buffers,         tensor arena +
   + conn         RTOS, app        model weights

That ~64KB ceiling for ML is generous. Real designs often land closer to 32-48KB. You have to account for double buffering sensor data, and leave headroom so you don’t fault under a connection event storm.

If you genuinely need more, the nRF5340 gives you 512KB across a dual-core split: the network core runs the radio stack in isolation, and your application core keeps its full RAM for the model. That’s the cleanest way to stop fighting the co-residency problem, and it’s why I’d reach for it on anything ML-heavy.

What Actually Fits: A TinyML 256KB Workload Tier List

With 32-64KB of working RAM, your model weights and tensor arena both live in that space. That rules out most of what people picture when they hear “AI.”

WORKLOAD                  | MODEL SIZE | FITS?
--------------------------|------------|--------
Anomaly detection         | <10KB      | YES
Sensor fusion classifier  | 10-25KB    | YES
Keyword spotting (int8)   | 20-40KB    | TIGHT
Simple gesture recog      | 25-50KB    | TIGHT
Vision / image class      | 100KB+     | NO
RNN / LSTM (meaningful)    | varies     | NO

Comfortable. Anomaly detection on a vibration or current sensor. A small autoencoder or a statistical model that flags “this doesn’t look like normal.” Often under 10KB of weights, microsecond-to-millisecond inference, and it leaves plenty of arena. Threshold logic plus a tiny classifier on extracted features sits here too.

Tight but doable. Keyword spotting and simple gesture recognition, quantized to int8, with a tensor arena under ~40KB. Take the classic DS-CNN keyword spotter from the MLPerf Tiny benchmarks. It lands around 25-40KB of weights and runs in tens of milliseconds on an M4F. It fits, but you’ll be watching every kilobyte, and you won’t have much room for a fat application alongside it.

Don’t bother. Image classification, anything with meaningful recurrent layers, or any model you’re tempted to leave in float32. A small vision model is 100KB+ before activations. An LSTM with enough hidden units to be useful blows the arena on its state buffers. These aren’t “optimize harder” problems on this class of part. They’re wrong-silicon problems.

The pattern: a neural network on a small MCU earns its keep when the input is already low-dimensional. Feed it raw audio or pixels and you lose. Feed it a handful of features and a 5KB model does real work.

Toolchain Reality: TFLite Micro + CMSIS-NN

For ML on a BLE SoC, the pragmatic default is TensorFlow Lite for Microcontrollers with CMSIS-NN kernels underneath. It’s boring and it works, which is what you want in firmware.

TFLite Micro gives you a static interpreter with no malloc and no OS dependency. You hand it a tensor arena (one fixed buffer you size yourself) and a flatbuffer model. CMSIS-NN provides the optimized kernels: convolution, fully-connected, pooling, written to hit the SIMD and DSP instructions on the Cortex-M4F. The speedup over reference kernels is real, often 4-5x on the int8 conv layers, which is the difference between a 10ms and a 50ms inference.

Two non-negotiables.

First, int8 quantization. Float32 is a luxury you can’t afford here. Quantizing cuts weights to a quarter the size and lets CMSIS-NN use the fast integer paths. Do it as post-training quantization with a representative dataset, or quantization-aware training if accuracy slips. Either way, quantize early so you find out your model doesn’t fit before you’ve tuned it for three weeks.

Second, size the tensor arena empirically. There’s no clean formula. Allocate a guess, call arena_used_bytes() after setup, then trim to the actual peak plus a small margin. Over-allocate and you steal RAM you don’t have; under-allocate and the interpreter fails to initialize at runtime.

Watch operator support. TFLite Micro implements a subset of ops, and you register only the ones your model uses to save flash. If your model has an op the runtime doesn’t support, you find out at conversion or first inference, never at a convenient time.

Edge Impulse wraps this whole flow nicely and is worth using for the data pipeline. microTVM and vendor SDKs exist too. But under the hood on Cortex-M4F, you’re almost always landing on CMSIS-NN regardless.

Light Sensor Fusion: The Realistic Win

Take an accelerometer and a temperature sensor. Don’t feed raw samples into a network. Compute a feature vector first: RMS, peak, zero-crossing rate, dominant frequency from a small FFT, mean and slope on temperature. Maybe 12-20 features.

Then run a tiny classifier (a 2-3 layer dense net, often under 10KB int8) on that vector. This beats a bigger network on raw data on every axis that matters for a BLE SoC: smaller model, smaller arena, faster inference, lower power.

Why it wins: the feature extraction does the dimensionality reduction that convolutional front-ends would otherwise burn RAM and cycles doing. You’re handing the network a problem that’s already mostly solved.

Power matters as much as memory here. You’re likely duty-cycling: wake on a sensor interrupt or a timer, grab a window of samples, extract features, infer, then sleep. The inference itself might cost a few millijoules. The radio costs far more, so the design goal is to infer locally and transmit rarely. A classifier that only wakes the BLE link when it sees something interesting can stretch a coin cell from weeks to years.

When the Edge Shouldn’t Do It All

The framing that the edge must do everything is where teams overspend. You don’t need full inference on the SoC. You need enough on-device intelligence to decide what’s worth sending.

Run lightweight detection or triage locally. When the model is confident, act on it. When it’s uncertain, or when the event matters enough to warrant a second opinion, stream the feature vector (or the flagged window) over BLE to something with real compute.

[ BLE SoC ]                    [ Gateway / Upstream ]
  sensor ──> feature extract
            ──> tiny classifier
                  |
                  ├─ confident? -> act locally
                  └─ uncertain? -> stream features
                                   over BLE ─────────> heavy model
                                                       (full inference)

The edge filters and upstream decides. You ship feature vectors, not raw streams, so the radio cost stays tiny and your battery survives. The heavy model (the vision net, the deep classifier, the thing that needs 100MB of RAM) lives where RAM is cheap.

The catch is usually the upstream link. A gateway in radio range works, but blanketing a deployment with gateways is its own cost and headache. A network like Hubble’s BLE backhaul changes the math here: your device advertises a small encrypted payload, the network carries it, and you run the heavy inference in the cloud without deploying a single gateway yourself. The edge stays a cheap BLE SoC running a 10KB classifier. The hard model runs where compute is unconstrained.

Design for this from the start. Decide which inferences must be local (latency, privacy, or the link might be down) and which can travel. Build the payload format around features, not raw data. Getting this split wrong means either an overbuilt edge node or a radio bill that eats your power budget.

Set Your Constraints Before You Pick a Model

Budget the RAM first. Open your memory.ld, subtract the SoftDevice region and your application’s real stack and heap, and treat what’s left as your hard ceiling. That number, not a tutorial’s, is what your model has to live inside.

Then quantize to int8 from day one so you’re never surprised by a model that fit in float on your laptop and faults on the device. Size the tensor arena empirically and leave margin. Pick workloads from the comfortable tier unless you have a specific reason to push into tight territory.

And architect the offload path before you write the first inference. The cheapest, most reliable edge ML on a BLE SoC is the model you didn’t have to run locally, because a small classifier already decided it wasn’t worth the trouble.


Hubble Network lets your edge nodes offload inference and reach the cloud directly over BLE, without the gateway infrastructure that usually eats your power and bandwidth budget. See how it works →