Michał Oźmina
Published © CC BY-NC-ND

HMTT: Proof of Presence

HMTT: Zero-Interval Edge Computing for Reliable Carpooling Proof of Presence

BeginnerWork in progress8 hours25
HMTT: Proof of Presence

Things used in this project

Hardware components

XIAO ESP32-C6
×3
DS3231SN
×3
ESP32-S3 DevKitC-1 N16R8 (16MB Flash, 8MB PSRAM)
×3
ESP32-C6-DevKitC-1 N16 (16MB Flash, WiFi6/BLE/Zigbee)
×3
Raspberry Pi 5 (8 GB RAM)
×1

Software apps and online services

Arduino IDE
Arduino IDE
KiCad
KiCad
Copilot
Antigravity

Hand tools and fabrication machines

PCBWay CNC machining
PCBWay CNC machining

Story

Read more

Schematics

schematics

Code

Implementation: StatusAdv Packet Builder (Keychain Fob Firmware)

Arduino
The following snippet from the keychain fob's BLE communication layer shows how the 11-byte StatusAdv payload is assembled. Each wake cycle, the fob evaluates its internal state — pending logs, sync requirements, battery level — and encodes the result into a compact Manufacturer Data packet. The Hub parses this packet passively from the BLE scan response, enabling the Zero-Interval decision without ever establishing a connection.
// ============================================================
// StatusAdv: Keychain state flagging in BLE ADV packet
// Enables Hub to make connect/no-connect decisions WITHOUT connecting.
// Format: 11 bytes Manufacturer Data
//   [0-1]  Company ID: 0xFE, 0xFF
//   [2-5]  Protocol:   'H','M','T','T'
//   [6]    Type:       0x03 (StatusAdv)
//   [7]    Flags:      bitfield (HAS_LOGS, NEEDS_SYNC, SERVICE_MODE, LOW_BATTERY)
//   [8]    Battery %:  0-100
//   [9-10] BrelokHash: 16-bit FNV-1a hash of brelok_id
// ============================================================
#define STATUS_ADV_TYPE          0x03
#define STATUS_FLAG_HAS_LOGS     0x01
#define STATUS_FLAG_NEEDS_SYNC   0x02
#define STATUS_FLAG_SERVICE_MODE 0x04
#define STATUS_FLAG_LOW_BATTERY  0x08

// --- FNV-1a 16-bit hash of brelok_id ---
static uint16_t fnv1a16(const char* str) {
    uint16_t hash = 0x811C; // FNV offset basis (truncated to 16-bit)
    while (*str) {
        hash ^= (uint8_t)*str++;
        hash *= 0x0101; // FNV prime (truncated to 16-bit)
    }
    return hash;
}

// --- Build 11-byte StatusAdv payload ---
std::string buildStatusAdvPayload() {
    uint8_t flags = 0;
    if (getLogCount() > 0)                   flags |= STATUS_FLAG_HAS_LOGS;
    if (!g_syncCompleted || epochBase == 0)   flags |= STATUS_FLAG_NEEDS_SYNC;
    if (serviceMode || localServiceOverride)  flags |= STATUS_FLAG_SERVICE_MODE;
    uint8_t bat = getBatteryPercent();
    if (bat < 15 && bat > 0)                 flags |= STATUS_FLAG_LOW_BATTERY;

    uint16_t brelokHash = fnv1a16(assignedBrelokId);

    uint8_t payload[11] = {
        0xFE, 0xFF,             // Company ID
        'H', 'M', 'T', 'T',    // Protocol magic
        STATUS_ADV_TYPE,        // Type = StatusAdv
        flags,                  // Decision flags bitfield
        bat,                    // Battery percentage
        (uint8_t)(brelokHash & 0xFF),        // BrelokHash low byte
        (uint8_t)((brelokHash >> 8) & 0xFF)  // BrelokHash high byte
    };

    return std::string((char*)payload, 11);
}

Credits

Michał Oźmina
1 project • 0 followers
Working in precast concrete industry. Expert in AutoCAD, a lot of experience in Revit and Tekla Structures. Photography entusiast
Thanks to filippofilip95.

Comments