-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsupabase_bridge.py
More file actions
140 lines (116 loc) · 4.94 KB
/
supabase_bridge.py
File metadata and controls
140 lines (116 loc) · 4.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env python3
"""
supabase_bridge.py — WiFi multi-sensor RSSI bridge to Supabase.
Reads PKT lines from the ESP12F hub over serial and uploads them to Supabase
to trigger real-time triangulation and occupancy tracking.
PKT line format (matches correlator.py):
PKT,<src>,<mac>,<rssi>,<channel>,<timestamp_ms>,<report_ms>
src = 'L' (ESP12F local), '1' (ESP32 #1), '2' (ESP32 #2)
mac = 12 hex chars, e.g. AABBCCDDEEFF
timestamp_ms = board millis() when this MAC's best-RSSI packet was sniffed
report_ms = board millis() at the moment the report was transmitted
(same value for all entries in one burst)
Epoch correction:
sniff_unix = python_rx_time - (report_ms - timestamp_ms) / 1000.0
Usage:
python supabase_bridge.py [PORT] [--baud BAUD] [--interval SECS]
"""
import argparse
import serial
import sys
import time
import os
from dotenv import load_dotenv
from supabase import create_client, Client
# Mapping of sensor codes from firmware to Supabase board IDs
BOARD_MAP = {
"L": "board_east", # ESP12F Hub (local)
"1": "board_north", # ESP32 #1
"2": "board_south", # ESP32 #2
}
def parse_args():
p = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument("port", nargs="?", default="/dev/ttyUSB1",
help="Serial port (default: /dev/ttyUSB1)")
p.add_argument("--baud", type=int, default=115200)
p.add_argument("--interval", type=float, default=0.5,
help="Minimum seconds between uploads per MAC (rate limiting, default: 0.5s = 2/sec)")
return p.parse_args()
def main():
args = parse_args()
load_dotenv()
url = os.environ.get("VITE_SUPABASE_URL")
key = os.environ.get("VITE_SUPABASE_ANON_KEY")
if not url or not key:
print(f"Error: Supabase credentials not found")
sys.exit(1)
print(f"Connecting to Supabase at {url}...")
try:
supabase: Client = create_client(url, key)
except Exception as e:
sys.exit(f"Failed to connect: {e}")
print(f"Opening {args.port} at {args.baud} baud …")
try:
ser = serial.Serial(args.port, args.baud, timeout=1)
ser.reset_input_buffer()
time.sleep(0.1)
ser.reset_input_buffer()
except serial.SerialException as e:
sys.exit(f"Cannot open serial port: {e}")
print("Listening and bridging to Supabase. Press Ctrl-C to stop.\n")
last_upload: dict = {} # mac -> timestamp, for rate limiting
try:
while True:
raw = ser.readline()
if not raw:
continue
try:
line = raw.decode("ascii", errors="replace").strip()
except Exception:
continue
if line.startswith("PKT,"):
parts = line.split(",")
if len(parts) == 7:
_, src, mac, rssi_s, ch_s, ts_s, report_ms_s = parts
src = src.strip()
mac = mac.strip().upper()
board_id = BOARD_MAP.get(src)
if not board_id:
continue
# Rate-limit uploads per MAC to avoid slamming Supabase
now = time.time()
if mac in last_upload and now - last_upload[mac] < args.interval:
continue
try:
report_ms = int(report_ms_s)
ts_ms = int(ts_s)
# Corrected sniff time: shift board uptime back to Unix epoch
# using the report_ms anchor received at known wall-clock time.
sniff_unix = now - (report_ms - ts_ms) / 1000.0
payload = {
"packet_id": f"pkt_{mac}_{int(now)}",
"board_id": board_id,
"device_hash": mac,
"rssi": int(rssi_s),
"arrival_time_us": int(sniff_unix * 1_000_000),
"esp_timestamp_ms": ts_ms,
"esp_report_ms": report_ms,
}
supabase.table("packet_reports").insert(payload).execute()
print(f" [SUPA] {mac} from {src} ({int(rssi_s):+d} dBm ch{ch_s} sniff={sniff_unix:.3f})")
last_upload[mac] = now
except ValueError:
pass
except Exception as e:
print(f" [ERR] Supabase upload failed: {e}")
elif line.startswith("DBG,"):
print(f" [HUB] {line[4:]}")
elif line:
print(f" [???] {line}")
except KeyboardInterrupt:
print("\nStopped.")
finally:
ser.close()
if __name__ == "__main__":
main()