Skip to content

Repository files navigation

BLE Pressure Sensor Toolkit

Terminal utility for reading a Bluetooth LE pressure/force sensor module and visualizing the live value as a pressure bar.

This works with Bluetooth LE pressure/force sensors that expose a readable or notifiable GATT characteristic. Many simple firmwares return ASCII frames like:

115.01,0,0,0

The reader uses the first numeric field as value and keeps the full frame in the CSV raw column.

Quick Start On This PC

If you do not know the BLE address or advertised name yet, run one of the launchers without DEVICE_ADDRESS or DEVICE_NAME. It will install dependencies, scan nearby BLE devices, then print the target configuration to use.

Linux/macOS:

cd /home/krish/bluetooth-pressure-sensor
DEVICE_ADDRESS=AA:BB:CC:DD:EE:FF ./run_pressure.sh

Windows PowerShell:

cd path\to\bluetooth-pressure-sensor
$env:DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF"
.\run_pressure.ps1

The script:

  • creates .venv if needed
  • installs Python requirements
  • reconnects the configured paired device if possible on Linux
  • polls or subscribes to the selected BLE characteristic
  • renders a live terminal pressure bar
  • logs readings to readings.csv

Press Ctrl-C to stop.

Device Configuration

The launchers do not include a default device address. Set either DEVICE_ADDRESS or DEVICE_NAME before running.

Set CHARACTERISTIC_UUID only when you know which GATT characteristic should be used. If it is omitted, the reader skips standard GAP/GATT system characteristics, chooses a Nordic UART TX characteristic when present, then prefers readable data characteristics for read polling.

The launchers default to read polling because some pressure sensor firmwares advertise notifications but do not emit samples. For firmware that streams notifications, run the Python CLI with --notify.

The standard 00002a05-0000-1000-8000-00805f9b34fb Service Changed characteristic is not sensor data. If your firmware uses a known custom characteristic, pin it explicitly:

DEVICE_ADDRESS=AA:BB:CC:DD:EE:FF CHARACTERISTIC_UUID=00000002-0000-1000-8000-00805f9b34fb ./run_pressure.sh
$env:DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF"
$env:CHARACTERISTIC_UUID = "00000002-0000-1000-8000-00805f9b34fb"
.\run_pressure.ps1

Prerequisites

All platforms need:

  • Python 3.10+
  • Bluetooth adapter

Linux also needs:

  • BlueZ tools, especially bluetoothctl
  • GitHub CLI gh only if publishing changes

On Ubuntu/Debian:

sudo apt update
sudo apt install python3 python3-venv bluetooth bluez

Enable Bluetooth:

sudo systemctl enable --now bluetooth
bluetoothctl show

The controller should show Powered: yes.

On Windows:

  • Install Python from https://www.python.org/ or the Microsoft Store
  • Pair the BLE module in Windows Settings before running the reader
  • Run PowerShell from this project directory

If script execution is blocked by local policy, run:

powershell -ExecutionPolicy Bypass -File .\run_pressure.ps1

Pairing On A New Linux PC

Start bluetoothctl:

bluetoothctl

Inside the prompt:

power on
agent on
default-agent
scan on

Wait for the module to appear. It may show a friendly name or only a MAC-like address. Then:

pair AA:BB:CC:DD:EE:FF
trust AA:BB:CC:DD:EE:FF
connect AA:BB:CC:DD:EE:FF
info AA:BB:CC:DD:EE:FF
quit

On a different PC the address may change, especially for BLE random-address devices. If the address does not work, scan again:

source .venv/bin/activate
python pressure_ble_reader.py scan

Then inspect the candidate device:

python pressure_ble_reader.py read --address AA:BB:CC:DD:EE:FF --list-characteristics --duration 5

Use the characteristic that has read, notify, or indicate.

Pairing On Windows

Open Windows Settings, then go to Bluetooth & devices and pair the module. After pairing, scan from this project:

.\.venv\Scripts\python.exe pressure_ble_reader.py scan

If .venv does not exist yet, let one of the PowerShell launchers create it first:

.\run_pressure.ps1

Then inspect the candidate device:

.\.venv\Scripts\python.exe pressure_ble_reader.py read --address AA:BB:CC:DD:EE:FF --list-characteristics --duration 5

On Windows, Bleak uses the Windows Bluetooth stack directly. The Linux-only bluetoothctl reconnect fallback is skipped, so the device must be discoverable or already known to Windows.

Install Manually

run_pressure.sh and run_pressure.ps1 do this automatically, but the manual setup is:

Linux/macOS:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt

Windows PowerShell:

py -3 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt

Run The Visualizer

Linux/macOS:

DEVICE_ADDRESS=AA:BB:CC:DD:EE:FF ./run_pressure.sh

Windows PowerShell:

$env:DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF"
.\run_pressure.ps1

Override settings with environment variables:

Linux/macOS:

DEVICE_NAME=MySensor MIN_VALUE=28 MAX_VALUE=1728 INTERVAL=0.1 CSV_FILE=readings.csv ./run_pressure.sh

Windows PowerShell:

$env:DEVICE_NAME = "MySensor"
$env:MIN_VALUE = "28"
$env:MAX_VALUE = "1728"
$env:INTERVAL = "0.1"
$env:CSV_FILE = "readings.csv"
.\run_pressure.ps1

Equivalent direct command:

python pressure_ble_reader.py read \
  --address AA:BB:CC:DD:EE:FF \
  --prefer-read \
  --interval 0.25 \
  --decode auto \
  --visualize \
  --min 28 \
  --max 1728 \
  --csv readings.csv

Run The WebSocket Server

Start a WebSocket server for browser, Node, Python, or other clients:

Linux/macOS:

DEVICE_ADDRESS=AA:BB:CC:DD:EE:FF ./run_server.sh

Windows PowerShell:

$env:DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF"
.\run_server.ps1

Default server:

ws://0.0.0.0:8765

From the same PC, clients should connect to:

ws://localhost:8765

From another machine on the LAN, replace localhost with this PC's LAN IP address.

Each message is JSON:

{
  "timestamp": "2026-07-20T08:56:28.218+00:00",
  "value": 29.48,
  "raw": "29.48,0,0,0",
  "percent": 0.09,
  "min": 28.0,
  "max": 1728.0
}

Fields:

  • value: first numeric field decoded from the BLE frame
  • raw: complete raw BLE frame as printable text or hex
  • percent: clamped 0..100 display percentage using MIN_VALUE and MAX_VALUE
  • min / max: calibration bounds used by the server

Override server settings:

Linux/macOS:

DEVICE_NAME=MySensor HOST=127.0.0.1 PORT=9000 MIN_VALUE=30 MAX_VALUE=2500 INTERVAL=0.1 ./run_server.sh

Windows PowerShell:

$env:DEVICE_NAME = "MySensor"
$env:HOST = "127.0.0.1"
$env:PORT = "9000"
$env:MIN_VALUE = "30"
$env:MAX_VALUE = "2500"
$env:INTERVAL = "0.1"
.\run_server.ps1

For other firmware that actually streams notifications, run pressure_ws_server.py with --notify instead of the default read-polling mode.

Print samples on the server while clients receive them:

Linux/macOS:

DEVICE_ADDRESS=AA:BB:CC:DD:EE:FF PRINT_SAMPLES=1 ./run_server.sh

Windows PowerShell:

$env:DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF"
$env:PRINT_SAMPLES = "1"
.\run_server.ps1

Minimal Python client:

source .venv/bin/activate
python - <<'PY'
import asyncio
import websockets

async def main():
    async with websockets.connect("ws://localhost:8765") as ws:
        while True:
            print(await ws.recv())

asyncio.run(main())
PY

Percent Calibration

The terminal percentage is based on recent local readings in readings.csv.

Observed stats:

All samples:   min=10.57, median=27.95, p95=1463.74, max=5937.06
Recent tail:   min=26.73, median=27.95, p95=42.24,   max=1728.23

The current display calibration is:

0%   = 28 raw units, roughly the resting baseline
100% = 1728 raw units, the largest recent pressure event

Values below baseline clamp to 0%. Values above 1728 clamp to 100%. This is a relative pressure indicator, not a calibrated force/pressure unit.

To recalibrate after collecting better samples:

MIN_VALUE=<resting_value> MAX_VALUE=<pressed_value> ./run_pressure.sh

For example, if idle is around 30 and your desired full-scale press is 2500:

MIN_VALUE=30 MAX_VALUE=2500 ./run_pressure.sh

CLI Reference

Scan:

python pressure_ble_reader.py scan

Read by address:

python pressure_ble_reader.py read --address AA:BB:CC:DD:EE:FF

Read by advertised name:

python pressure_ble_reader.py read --name MySensor

List characteristics:

python pressure_ble_reader.py read --address AA:BB:CC:DD:EE:FF --list-characteristics --duration 5

Decode formats:

python pressure_ble_reader.py read --address AA:BB:CC:DD:EE:FF --decode auto
python pressure_ble_reader.py read --address AA:BB:CC:DD:EE:FF --decode u16le
python pressure_ble_reader.py read --address AA:BB:CC:DD:EE:FF --decode f32le
python pressure_ble_reader.py read --address AA:BB:CC:DD:EE:FF --decode rawhex

Apply simple numeric scaling:

python pressure_ble_reader.py read --address AA:BB:CC:DD:EE:FF --scale 0.001 --offset 0

Notes

The FSR402 is a force-sensing resistor. Its resistance decreases as force increases, but raw readings depend on the voltage divider, firmware, contact area, and mechanical setup. Treat the live percent as a practical relative pressure display unless you calibrate with known loads.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages