tpt-bluetooth-mesh
KotlinPrivacy-preserving crowd-sourced BLE tracking network. Beacons rotate their advertising ID every 5 minutes — no stable identifiers, no corporate silo. Subscribers pay per-device; receivers (ESP32 or Android) earn ERC-20 tokens on Polygon for every detection.
Languages
TPT Bluetooth Mesh
A crowd-sourced, privacy-preserving BLE tracking network. Subscribers attach small ESP32 or nRF52 beacons to valuables; a global mesh of receiver devices (phones, ESP32 nodes) detects them and earns micro-dollar rewards paid as ERC-20 tokens on Polygon.
No corporate data silo. Beacons rotate their advertising ID every 5 minutes using a local HMAC-SHA256 derivation — the network sees only ephemeral IDs that cannot be linked across detections without the device secret.
Licensed under the Apache License 2.0 — copyright 2026 TPT Solutions.
How It Works
[Beacon] → BLE advertisement (rotating ID, no stable address)
↓
[Receiver] → POST /api/receivers/detections (batch, every 30 s)
↓
[Backend] → Redis O(1) ID resolution → gap reward → PostgreSQL ledger
↓
[Monthly] → Hardhat script distributes TPT tokens on Polygon
↓
[Subscriber] → push notification when their device is detected after a silence period
Repository Layout
| Directory | Contents |
|---|---|
backend/ | Express + TypeScript API — the heart of the system |
dashboard/ | Next.js 16 subscriber web app |
android-receiver/ | Android receiver app (Kotlin, foreground BLE scan service) |
android-subscriber/ | Android subscriber app (Kotlin, beacon management + alerts) |
firmware/beacon/ | ESP32 Arduino sketch — BLE advertiser |
firmware/receiver/ | ESP32 Arduino sketch — BLE scanner + WiFi uploader |
crypto/ | Hardhat project — ERC-20 TPTToken on Polygon |
tools/beacon-gen/ | CLI to generate device_secret locally (no server needed) |
self-hosted/ | Docker Compose: one-command self-hosted node |
storefront/ | E-commerce page for kit sales + browser-based ESP32 web flasher |
hardware/ | KiCad schematics and Gerber files for custom receiver board |
Quick Start
Run a node (self-hosted)
cd self-hosted
cp .env.example .env # fill in JWT_SECRET, STRIPE keys
docker compose up -d
This starts PostgreSQL, Redis, and the API on port 3000. The schema is applied automatically on first boot.
Backend (development)
cd backend
cp .env.example .env
npm install
npm run db:migrate # requires a running PostgreSQL instance
npm run dev
Dashboard (development)
cd dashboard
npm install
npm run dev # http://localhost:3001
Flash a beacon
- Generate a device secret:
cd tools/beacon-gen && node index.js - Copy the secret into
firmware/beacon/beacon_config.h - Flash via the web flasher, Arduino IDE, or
idf.py - On first boot the beacon opens a captive portal (
TPT-Mesh-Setup) to configure WiFi and node URL
Full beacon setup guide: firmware/beacon/README.md
Privacy Architecture
Beacons derive their advertising ID as:
advertising_id = HMAC-SHA256(device_secret, floor(unix_ts / window_seconds))[0:16 hex]
The default window is 300 seconds (5 minutes). The device_secret never leaves the beacon. The backend pre-computes the next 48 hours of IDs per device and stores them in Redis; receivers submit opaque hex strings and the backend resolves them — without ever seeing a stable identifier.
This design means:
- A passive observer cannot link two detections of the same beacon across rotation windows
- Receivers earn rewards without learning which physical device they detected
- Nodes in a federation each resolve only IDs registered with them; unknown IDs are silently ignored
Receiver Rewards
Receivers earn a share of each subscriber's monthly payment proportional to detection time:
per_second_rate = (subscription_amount × 0.95) / 2_592_000 (seconds in 30 days)
gap_reward = gap_seconds × per_second_rate
The 5% platform fee (configurable via PLATFORM_FEE_PERCENT) covers infrastructure costs. Rewards accumulate in a PostgreSQL ledger and are settled monthly as TPT ERC-20 tokens on Polygon.
Federation
Multiple independently-operated nodes form a federation. Receivers can broadcast to several nodes simultaneously; the "first arrival wins" claim lock (Redis SET NX) is per-node, so duplicate submissions across nodes are handled naturally.
Configure peers in the backend .env:
NODE_NAME=my-node
NODE_URL=https://my-node.example.com
FEDERATION_PEERS=https://node1.example.com,https://node2.example.com
Crypto / Token Settlement
See crypto/README.md for contract deployment and monthly settlement scripts.
The TPTToken ERC-20 contract uses AccessControl roles (MINTER_ROLE, SETTLER_ROLE). The settlement script reads the off-chain PostgreSQL ledger and calls batchDistribute() on the contract, then marks rows settled.
Android Apps
- Receiver app (android-receiver/) — runs a persistent foreground BLE scan service, tags detections with GPS, uploads every 30 seconds
- Subscriber app (android-subscriber/) — manages beacons, shows last-known location on OpenStreetMap, configures FCM push alerts and Stripe subscriptions
Contributing
Pull requests are welcome. Open an issue first for significant changes.
This project is licensed under the Apache License 2.0. See LICENSE for the full text.