Skip to content

Latest commit

 

History

18 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GattForm

Tests

A BLE GATT server for ESP32 (Arduino framework) that exposes a dynamic form schema and receives submissions as JSON — pairs with a Web Bluetooth client that reads the schema, renders the matching form, and writes back the submitted values. No app install, no WiFi network required upfront.

Web Bluetooth client
        │
        ▼
ESP32  ──  BLE GATT Server
  ├── Field N schema (READ)         → one field's definition, sent as JSON
  ├── Field N value  (WRITE)        → that field's submitted value, sent as JSON
  ├── Commit         (WRITE)        → triggers assembling + dispatching the submission
  └── Status         (READ / NOTIFY) → ACK/NACK after submission

GattFormData (bundled in this same package) wraps the submitted JSON payload with typed getters. It has no BLE dependency, so it's covered by fast native unit tests — see test/test_native.

Usage

#include <GattForm.h>

GattForm form;

void setup() {
    form.setName("MyDevice")
        .addText("ssid", "SSID")
        .addPassword("pass", "Password")
        .addRange("bright", "Brightness", 0, 255)
        .onSubmit([](GattFormData& d) {
            Serial.println(d.getString("ssid"));
            form.ack();
        })
        .begin();
}

void loop() {}

Installation

Add this repo to your project's platformio.ini:

lib_deps =
    https://github.com/virgileMaon/GattForm.git#v0.1.0

Pin to a tag (as above) rather than tracking the default branch, so an upstream change never silently breaks your build.

Web client

docs/index.html is a generic Web Bluetooth client for any GattForm device — it doesn't hardcode any field, it just reads the Schema characteristic and renders whatever fields the firmware defined (addText, addRange, addSelect, ...). Point it at any board running this library and it works, no per-project customization needed.

Requirements: Google Chrome or another browser with Web Bluetooth support (Firefox and Safari don't have it).

To try it locally, just open the file in Chrome — Web Bluetooth works from file://. A hosted copy is also kept live at https://virgilemaon.github.io/GattForm/, so most projects using this library don't need to publish their own.

It talks to the device using the fixed UUIDs declared in src/GattForm.h (SERVICE_UUID, COMMIT_UUID, STATUS_UUID, plus a per-field schema/value characteristic pair derived from FIELD_SCHEMA_UUID_BASE/FIELD_VALUE_UUID_BASE) — any firmware built with this library exposes exactly those, so the same page works across every project using it, with no changes.

Encryption

BLE characteristics are unencrypted by default — anyone within radio range running a BLE sniffer can passively capture whatever's written to them, including a password field's submitted value. Call .requireEncryption() before .begin() to require BLE bonding on every characteristic (schema, value, commit, status):

form.setName("MyDevice")
    .addPassword("pass", "Password")
    .requireEncryption()
    .onSubmit([](GattFormData& d) { /* ... */ form.ack(); })
    .begin();

What it does and doesn't protect against:

  • Stops passive eavesdropping — a nearby sniffer only ever sees encrypted packets, never the plaintext values.
  • Doesn't stop an active man-in-the-middle during the very first pairing. Pairing uses "Just Works" (no PIN prompt) — the only mode a generic board without a display or keyboard can offer — so there's no out-of-band step to authenticate that first pairing against.

Trade-offs to know before turning it on:

  • The web client's first connection now triggers an OS-level Bluetooth pairing prompt (Windows/Android's own dialog, outside the page's control) instead of the plain "click to connect" of the unencrypted default. Reconnecting afterward doesn't prompt again — the bond is remembered on both sides.
  • The ESP32 stores bonded-device keys in NVS with limited slots — a device paired with many different phones over its lifetime (e.g. a shared/public provisioning kiosk) can run out and need its bonds cleared.
  • Neither test env can exercise this: pio test -e native has no real BLE stack, and pio test -e esp32dev would need a peer that actually pairs. See examples/demo_encryption for a manual check instead.
  • Interop varies by OS/BLE stack — pairing is handled by the underlying BLE library, not this one, and its own logging never says whether pairing actually succeeded. requireEncryption() registers a security callback that does: watch Serial for GattForm: BLE pairing succeeded. or GattForm: BLE pairing FAILED (HCI reason 0x..) when debugging a connection that pairs but then won't connect.
  • Tested on Android and Windows 11 — works on both, but pairing can be a bit finicky on Windows (may take a retry or two on first pairing).

Testing

# Fast unit tests (JSON parsing logic), no hardware required
pio test -e native

# BLE lifecycle tests, run on an actual ESP32 board
pio test -e esp32dev

CI (.github/workflows/tests.yml) runs the native unit tests on every push/PR, since those need no hardware. The esp32dev tests need a real board to execute (Unity results come back over serial), so CI only compiles that env and both examples/ projects instead — enough to catch a build break, not a substitute for actually running pio test -e esp32dev on hardware before a release.

Manual check: field defaults

examples/demo_defaults is a self-contained PlatformIO project (its own platformio.ini, depending on this repo via symlink://../..) that sets a default value on every field type, so you can eyeball that docs/index.html pre-fills them correctly — this isn't covered by the automated tests since it's a visual/UX check on the actual web form.

cd examples/demo_defaults
pio run -t upload --upload-port COMx   # flash the demo to your board

Then open docs/index.html in Chrome, connect to "GattForm Demo", and check the form against the expected values (also logged over Serial at 115200 baud on boot):

Field Expected pre-filled value
SSID (text) MyWifi
Password (empty — no default, by design)
Email user@example.com
Enable feature (bool) checked
Count (number) 4
Brightness (range) 128
Mode (select) normal

Manual check: encryption

examples/demo_encryption is the same kind of self-contained project, with .requireEncryption() turned on — see its src/main.cpp for what to expect (an OS-level pairing prompt on first connect) and how to confirm reads/writes are actually rejected without it.

Project structure

src/GattForm.h/.cpp             BLE GATT server: builds the form schema, handles submissions
src/GattFormData.h/.cpp         JSON payload wrapper, hardware-independent
docs/index.html                 Generic Web Bluetooth client (can be served via GitHub Pages)
test/test_native/               Unit tests, run on the host
test/test_embedded/             Integration tests, run on an ESP32 board
examples/demo_defaults/         Manual/visual check for field default values (see Testing)
examples/demo_encryption/       Manual check for requireEncryption() (see Testing)

About

Configure a device over Bluetooth from a web page, no app install and no WiFi network required upfront.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages