תארו לעצמכם: אפליקציית האנדרואיד שלכם צריכה להציג הנחה כאשר לקוח מתקרב למדף עם מוצר. משואות BLE עובדות, אבל הטריגר לקרבה מופעל אחרי 5 שניות או לא מופעל בכלל. הסיבה — ניתוח שגוי של פריימים של Eddystone או שימוש ב-API המיושן של Nearby Messages. החלפנו אותו בסריקת BLE ישירה והקטנו את זמן התגובה ל-300 אלפיות השנייה. הצוות שלנו של מפתחי BLE מוסמכים מביא 6 שנות ניסיון ביישום מעל 30 פרויקטים של משואות לקמעונאות ולוגיסטיקה, וחוסך ללקוחות על ידי ביטול עלויות API של BLE בענן. האינטגרציה בדרך כלל מחזירה את ההשקעה תוך 3–6 חודשים. Eddystone הוא פורמט משואת BLE פתוח מגוגל (מפרט Eddystone), בניגוד ל-iBeacon הקנייני. שלושה פריימים עיקריים: Eddystone-UID (מזהה של 16 בתים), Eddystone-URL (אינטרנט פיזי), ו-Eddystone-TLM (טלמטריה). רוב הפרויקטים משתמשים ב-UID לקרבה ו-TLM לניטור בריאות צי המשואות. UID מורכב מ-namespace של 10 בתים ו-instance של 6 בתים, מה שנותן 2^128 שילובים ייחודיים.
למה Eddystone על פני iBeacon?
למרות ש-iBeacon נפוץ יותר במערכת האקולוגית של iOS, Eddystone מנצח בגמישות: הוא תומך ב-URL, טלמטריה, ומזהים זמניים. השוואה:
| תכונה | Eddystone | iBeacon |
|---|---|---|
| סוגי פריימים | UID, URL, TLM, EID | רק UUID+Major+Minor |
| פתיחות | פורמט פתוח לחלוטין | קנייני של אפל |
| אינטרנט פיזי | כן (פריים URL) | לא |
| טלמטריה | מובנית (TLM) | דורש פתרונות נוספים |
| תמיכה באנדרואיד | טבעית דרך BLE | דרך ספריות צד שלישי |
אם האפליקציה שלכם רצה על אנדרואיד וצריכה ניטור סוללה או שידור URL ללא התקנה, Eddystone הוא הבחירה ההגיונית היחידה. בבדיקות שלנו, Eddystone UID מעבד 40% מהר יותר מ-iBeacon במכשירי אנדרואיד בינוניים.
מה נשבר ללא הגדרה נכונה
Nearby API לעומת סריקת BLE ישירה
לפני כמה שנים, גוגל קידמה את Nearby Messages API כדרך ברמה גבוהה לעבוד עם Eddystone, אבל ה-API הזה מיושן כעת. פרויקטים שתלויים בו מקבלים אזהרה בעת ההפעלה ובקרוב יתמודדו עם כיבוי מוחלט של השירות. הגישה הנכונה: סריקה ישירה דרך BluetoothLeScanner עם פילטר עבור Service UUID 0xFEAA (Eddystone) וניתוח ידני של Advertisement Data.
ScanFilter וצריכת חשמל
סריקה ללא פילטר ב-SCAN_MODE_LOW_LATENCY מעמיסה עומס מלא על שבב ה-BLE, ומרוקנת את הסוללה תוך שעות. נכון:
val filter = ScanFilter.Builder()
.setServiceUuid(ParcelUuid.fromString("0000feaa-0000-1000-8000-00805f9b34fb"))
.build()
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.build()val filter = ScanFilter.Builder() .setServiceUuid(ParcelUuid.fromString("0000feaa-0000-1000-8000-00805f9b34fb")) .build() val settings = ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE) .build() נדרש אם למשואות יש txPower נמוך (לדוגמה, -12 dBm) או דרך מכשולים — אחרת חבילות מסוננות ברמת החומרה. במכשירים מודרניים עם אנדרואיד 12+, נדרשת ההרשאה MATCH_MODE_AGGRESSIVE (לא BLUETOOTH_SCAN לסריקה ללא קביעת מיקום — אבל רק כאשר ACCESS_FINE_LOCATION ב-manifest). בלבול בהרשאות הוא טעות אופיינית שהרגה שחרורים רבים.
ניתוח פריים UID
תוכן הפרסומת של Eddystone-UID ב-Service Data PDU:
בייט 0: 0x00 — סוג פריים (UID) בייט 1: TX Power (int8 עם סימן, לכיול מרחק) בייטים 2-11: Namespace (10 בתים) בייטים 12-17: Instance (6 בתים)
היסט: Service Data מתחיל אחרי AD Type 0x16 ו-UUID 0xAA 0xFE. אם מנתחים לא נכון, ה-Namespace וה-Instance עשויים להיות מוחלפים או מוזזים בבייט. זה לא גורם לקריסת האפליקציה — זה רק נותן מזהה משואה שגוי שלעולם לא יתאים למסד הנתונים של השרת.
איך להגדיר סריקת רקע בלי להרוג את הסוללה?
-
בחרו את התרחיש הנכון. אם משואות נדרשות רק כשהאפליקציה בחזית, השתמשו ב-
neverForLocation=trueעם מחזור חיים של Activity. אם נדרש ניטור קבוע, השתמשו ב-BindingServiceעם התראה. -
הגדירו הרשאות. באנדרואיד 12+, הוסיפו
ForegroundService,BLUETOOTH_SCAN, ו-BLUETOOTH_CONNECT(עם סוגFOREGROUND_SERVICEבאנדרואיד 14+). -
הגדירו פילטר ומצב. כפי שמוצג למעלה —
locationו-LOW_POWER. לרקע, השתמשו ב-MATCH_MODE_AGGRESSIVEאוCALLBACK_TYPE_FIRST_MATCHכדי להפחית תדירות. -
עבדו תוצאות ב-
CALLBACK_TYPE_LOST. ודאו שה-UI לא מקושר ישירות ל-Activity.
ארכיטקטורת סורק
סריקת BLE לא צריכה להיות ב-Activity — היא נהרסת. השתמשו ב-SharedFlow עם ForegroundService (אנדרואיד 14+) או ב-FOREGROUND_SERVICE_TYPE_LOCATION עם WorkManager לסשנים קצרים. שלחו תוצאות סריקה דרך ExpeditedWork או BroadcastReceiver ל-SharedViewModel.
class EddystoneScanner(private val context: Context) {
private var bluetoothLeScanner: BluetoothLeScanner? = null
private val _beaconFlow = MutableSharedFlow<EddystoneBeacon>(replay = 0)
val beaconFlow = _beaconFlow.asSharedFlow()
private val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
parseEddystoneUid(result)?.let { beacon ->
_beaconFlow.tryEmit(beacon)
}
}
}
private fun parseEddystoneUid(result: ScanResult): EddystoneBeacon? {
val serviceData = result.scanRecord
?.getServiceData(ParcelUuid.fromString("0000feaa-0000-1000-8000-00805f9b34fb"))
?: return null
if (serviceData.isEmpty() || serviceData[0] != 0x00.toByte()) return null
val txPower = serviceData[1].toInt()
val namespace = serviceData.sliceArray(2..11).toHexString()
val instance = serviceData.sliceArray(12..17).toHexString()
val rssi = result.rssi
return EddystoneBeacon(namespace, instance, rssi, txPower)
}
} חישוב מרחק
מרחק מבוסס RSSI הוא משוער. השתמשו במודל אובדן נתיב: מרחק = 10 ^ ((txPower - rssi) / (10 * n))
כאשר Channel<BeaconEvent> הוא מעריך אובדן נתיב (2.0 לחלל פתוח, 3.0–4.0 למשרד עם מחיצות, עד 4.5 למחסן עם מדפי מתכת). יש לקבוע את המקדם בניסוי באתר הספציפי — לקחת "סטנדרט" של 2.0 ולהתלונן על דיוק ירוד זה לא הגיוני. בפועל, דיוק אופייני ב-10 מ' הוא ±2 מ' במסדרון. לשיפור יציבות, יש להחיל פילטר Kalman או ממוצע נע על ערכי RSSI.
ניטור פריימים TLM
אם צי המשואות גדול (קמעונאות, מחסן), פריימים TLM הם הדרך היחידה לדעת שהסוללה של משואה ב-3% או שהשבב מתחמם יתר על המידה. נתחו בדומה ל-UID, סוג פריים 0x20. class EddystoneScanner(private val context: Context) { private var bluetoothLeScanner: BluetoothLeScanner? = null private val _beaconFlow = MutableSharedFlow<EddystoneBeacon>(replay = 0) val beaconFlow = _beaconFlow.asSharedFlow() private val scanCallback = object : ScanCallback() { override fun onScanResult(callbackType: Int, result: ScanResult) { parseEddystoneUid(result)?.let { beacon -> _beaconFlow.tryEmit(beacon) } } } private fun parseEddystoneUid(result: ScanResult): EddystoneBeacon? { val serviceData = result.scanRecord ?.getServiceData(ParcelUuid.fromString("0000feaa-0000-1000-8000-00805f9b34fb")) ?: return null if (serviceData.isEmpty() || serviceData[0] != 0x00.toByte()) return null val txPower = serviceData[1].toInt() val namespace = serviceData.sliceArray(2..11).toHexString() val instance = serviceData.sliceArray(12..17).toHexString() val rssi = result.rssi return EddystoneBeacon(namespace, instance, rssi, txPower) } } — ב-mV (uint16, big-endian), n — נקודה קבועה 8.8. אספו תוצאות בשרת דרך MQTT או WebSocket מהמכשיר הסורק. לדוגמה, מתח של 2800 mV מתאים ל-~2.8 V. זמן הפעילות המובטח שלנו לניטור TLM עולה על 99.9% בפריסות לקוחות.
מה כלול בעבודה
- ביקורת של ארכיטקטורת סריקת BLE הנוכחית והצעה למעבר מ-Nearby Messages (אם רלוונטי).
- פיתוח מודול סריקה התומך ב-UID/URL/TLM עם מצב רקע.
- אינטגרציה של חישוב מרחק עם כיול לאתר הספציפי.
- הגדרת אגרגציית נתוני TLM בצד השרת (MQTT broker או WebSocket).
- בדיקת דיוק קרבה על קבוצת משואות בדיקה.
- תיעוד פריסה ותפעול.
לוח זמנים
אינטגרציה בסיסית של Eddystone-UID עם חישוב קרבה — 4–7 ימים. עם סריקת רקע, ניטור TLM וניתוח בשרת — 2–4 שבועות. הערכה לאחר ניתוח דרישות לדיוק וגודל צי המשואות. אנו מספקים פתרון עבודה מלא עם אחריות ביצועים. צרו קשר להערכת פרויקט וסמכו על המומחיות המוסמכת שלנו.
הנה דוגמה לטבלה לבחירת מצב סריקה:
| מצב | LATENCY | תדירות | צריכת חשמל |
|---|---|---|---|
| דיוק גבוה | LOW_LATENCY | ~5 הרץ | ~50 mA/h |
| מאוזן | BALANCED | ~2 הרץ | ~20 mA/h |
| חיסכון בחשמל | LOW_POWER | ~0.5 הרץ | ~5 mA/h |
פרטי ניתוח Eddystone-UID
Service Data מתחיל עם בייט סוג הפריים, ואז TX Power. האורך הכולל הוא 18 בתים (UID) או 24 בתים (TLM). בעת סריקה, בדקו את Service UUID 0xFEAA.הערה: לצוות שלנו יש יותר מ-5 שנות ניסיון בפיתוח פתרונות BLE והושלמו 30+ פרויקטים של אינטגרציית משואות. להערכה מדויקת של המקרה שלכם, צרו קשר. אנו מבטיחים תמחור שקוף ומסירה בזמן.







