תרחיש טיפוסי: יש לכם מכשיר BLE—מד דופק, מנורה חכמה או חיישן תעשייתי. היצרן מספק רק אפליקציה ניידת. אתם רוצים שמשתמשים יוכלו לשלוט במכשיר ישירות מהאתר שלכם ללא התקנת תוכנה. Web Bluetooth API פותר זאת, אך דורש טיפול קפדני בפרוטוקול, לוגיקת התחברות מחדש וניהול שגיאות. במשך 5 שנים השלמנו יותר מ-20 אינטגרציות BLE, מצמידי כושר ועד מוניטורים רפואיים. באמצעות הדפדפן תוכלו לחסוך עד 70% בפיתוח אפליקציות ניידות ולהפחית את זמן היציאה לשוק פי שלושה. עלות אינטגרציה טיפוסית היא $500–$2,000, וחוסכת אלפים בהשוואה לפיתוח מקורי. רוצים לחבר מכשיר BLE לאתר שלכם? נוכל להעריך את הפרויקט שלכם ביום אחד—פשוט צרו קשר.
אילו מכשירי BLE ניתן לחבר לאתר?
אנחנו מתחברים למדדי דופק, מדחומים, מנורות, בקרים—כל מכשיר עם Bluetooth Low Energy. המשתמש פותח דף, לוחץ על "התחבר", בוחר מכשיר מהרשימה, והאתר מתחיל לקרוא נתוני חיישנים, לשנות בהירות או להפעיל מנוע. המפתח הוא הבנת שירותי GATT ומאפיינים. אם קיימת תיעוד, האינטגרציה אורכת ימים. אם לא, אנחנו מבצעים הנדסה לאחור של הפרוטוקול באמצעות חיישן רשת.
מדוע Web Bluetooth API היא חלופה לאפליקציה ניידת
יישום מבוסס דפדפן מהיר פי שלושה מפיתוח מקורי ואינו דורש פרסום בחנות אפליקציות. המשתמש לא מוריד אפליקציה—הוא פשוט עוקב אחר קישור. עדכונים מוחלים מיידית בשרת, ללא ניהול גרסאות. עבור עסקים, זה מוריד את חסם הכניסה וחוסך עד 70% בתחזוקת שתי פלטפורמות.
כיצד להבטיח חיבור יציב לאחר ניתוק
זה קריטי למכשירים שדורשים זרם נתונים רציף. אנו מיישמים התחברות מחדש אוטומטית עם השהיה אקספוננציאלית: ניסיון ראשון לאחר שנייה אחת, שני לאחר 2, שלישי לאחר 4. לאחר שלושה כשלונות, אנו מציגים הודעה. להלן דוגמת קוד ב-TypeScript.
התחברות למכשיר
interface BluetoothDevice {
name: string
gatt: BluetoothRemoteGATTServer
}
async function connectToDevice(serviceUUID: string): Promise<BluetoothRemoteGATTServer> {
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: [serviceUUID] }],
})
console.log(`Подключаемся к: ${device.name}`)
device.addEventListener('gattserverdisconnected', () => {
console.log('Устройство отключилось')
})
const server = await device.gatt!.connect()
return server
} קריאת נתונים: מד דופק
class HeartRateMonitor {
private server: BluetoothRemoteGATTServer | null = null
private characteristic: BluetoothRemoteGATTCharacteristic | null = null
async connect() {
this.server = await connectToDevice('heart_rate')
const service = await this.server.getPrimaryService('heart_rate')
this.characteristic = await service.getCharacteristic('heart_rate_measurement')
await this.characteristic.startNotifications()
this.characteristic.addEventListener(
'characteristicvaluechanged',
this.handleHeartRateMeasurement.bind(this)
)
}
private handleHeartRateMeasurement(event: Event) {
const value = (event.target as BluetoothRemoteGATTCharacteristic).value!
const flags = value.getUint8(0)
let heartRate: number
if (flags & 0x01) {
heartRate = value.getUint16(1, true)
} else {
heartRate = value.getUint8(1)
}
const rrIntervals: number[] = []
if (flags & 0x10) {
for (let i = 2; i + 1 < value.byteLength; i += 2) {
rrIntervals.push(value.getUint16(i, true) / 1024 * 1000)
}
}
console.log(`ЧСС: ${heartRate} уд/мин, RR: [${rrIntervals.join(', ')}] мс`)
}
async disconnect() {
await this.characteristic?.stopNotifications()
this.server?.disconnect()
}
} כתיבת נתונים: שליטה במנורה חכמה
class SmartLightController {
private server: BluetoothRemoteGATTServer | null = null
private controlChar: BluetoothRemoteGATTCharacteristic | null = null
private readonly SERVICE_UUID = '00010203-0405-0607-0809-0a0b0c0d1910'
private readonly CONTROL_UUID = '00010203-0405-0607-0809-0a0b0c0d2b11'
async connect() {
this.server = await connectToDevice(this.SERVICE_UUID)
const service = await this.server.getPrimaryService(this.SERVICE_UUID)
this.controlChar = await service.getCharacteristic(this.CONTROL_UUID)
}
async setColor(r: number, g: number, b: number) {
const command = new Uint8Array([
0x33, 0x05, 0x02, r, g, b, 0x00, 0x00, 0x00, r ^ g ^ b,
])
await this.controlChar!.writeValueWithResponse(command)
}
async setBrightness(level: number) {
const command = new Uint8Array([
0x33, 0x04, Math.round(level * 2.55), 0x00,
])
await this.controlChar!.writeValueWithoutResponse(command)
}
async readBatteryLevel(): Promise<number> {
const service = await this.server!.getPrimaryService('battery_service')
const char = await service.getCharacteristic('battery_level')
const value = await char.readValue()
return value.getUint8(0)
}
} React Hook לניהול חיבורים
function useBluetooth() {
const [device, setDevice] = useState<BluetoothRemoteGATTServer | null>(null)
const [isConnected, setIsConnected] = useState(false)
const [isSupported] = useState(() => 'bluetooth' in navigator)
const [error, setError] = useState<string | null>(null)
const connect = useCallback(async (serviceUUID: string) => {
try {
setError(null)
const server = await connectToDevice(serviceUUID)
setDevice(server)
setIsConnected(true)
} catch (err) {
if ((err as Error).name === 'NotFoundError') {
setError('Устройство не выбрано')
} else {
setError((err as Error).message)
}
}
}, [])
const disconnect = useCallback(() => {
device?.disconnect()
setDevice(null)
setIsConnected(false)
}, [device])
return { device, isConnected, isSupported, error, connect, disconnect }
} התחברות מחדש לאחר ניתוק
device.addEventListener('gattserverdisconnected', async () => {
let retries = 0
while (retries < 3) {
await new Promise((r) => setTimeout(r, 1000 * (retries + 1)))
try {
await device.gatt!.connect()
await resubscribeCharacteristics()
console.log('Переподключено')
return
} catch {
retries++
}
}
console.error('Не удалось переподключиться')
}) מקרה אמיתי: אינטגרציה בפלטפורמת כושר
בפרויקט אחד עבור סטארטאפ למעקב כושר, שילבנו מד דופק BLE ששידר נתונים כל שנייה. היישום המקורי היה בעל עיכוב של 4 שניות עקב גילוי שירות GATT לא יעיל. על ידי שמירת מזהי שירות במטמון ואופטימיזציה של הרשמות להתראות מאפיינים, צמצמנו את זמן החיבור הראשוני ל-0.5 שניות וביטלנו את עיכוב הנתונים. הפלטפורמה תומכת כעת בתצוגת דופק בזמן אמת עם התחברות מחדש אוטומטית, ומשרתת למעלה מ-10,000 משתמשים פעילים יומית.
השוואה: Web Bluetooth API לעומת אפליקציה מקורית
| קריטריון | Web Bluetooth API | אפליקציה מקורית |
|---|---|---|
| זמן פיתוח | 1–2 שבועות | 2–3 חודשים |
| אספקה | קישור | חנויות אפליקציות |
| עדכונים | מיידיים | ידניים, דרך החנות |
| פלטפורמות | Chrome/Edge | iOS, Android |
| מורכבות | בינונית | גבוהה |
| עלות | החל מ-$500 | החל מ-$5,000 |
טעויות נפוצות באינטגרציית BLE
המכשיר לא מופיע במהלך הסריקה. ודאו שהאתר מוגש באמצעות HTTPS ובקשו הרשאת Bluetooth. לעיתים קרובות הסיבה היא חסר interface BluetoothDevice { name: string gatt: BluetoothRemoteGATTServer } async function connectToDevice(serviceUUID: string): Promise<BluetoothRemoteGATTServer> { const device = await navigator.bluetooth.requestDevice({ filters: [{ services: [serviceUUID] }], }) console.log(`Подключаемся к: ${device.name}`) device.addEventListener('gattserverdisconnected', () => { console.log('Устройство отключилось') }) const server = await device.gatt!.connect() return server } . החיבור נופל במעבר לרקע. השתמשו ב-class HeartRateMonitor { private server: BluetoothRemoteGATTServer | null = null private characteristic: BluetoothRemoteGATTCharacteristic | null = null async connect() { this.server = await connectToDevice('heart_rate') const service = await this.server.getPrimaryService('heart_rate') this.characteristic = await service.getCharacteristic('heart_rate_measurement') await this.characteristic.startNotifications() this.characteristic.addEventListener( 'characteristicvaluechanged', this.handleHeartRateMeasurement.bind(this) ) } private handleHeartRateMeasurement(event: Event) { const value = (event.target as BluetoothRemoteGATTCharacteristic).value! const flags = value.getUint8(0) let heartRate: number if (flags & 0x01) { heartRate = value.getUint16(1, true) } else { heartRate = value.getUint8(1) } const rrIntervals: number[] = [] if (flags & 0x10) { for (let i = 2; i + 1 < value.byteLength; i += 2) { rrIntervals.push(value.getUint16(i, true) / 1024 * 1000) } } console.log(`ЧСС: ${heartRate} уд/мин, RR: [${rrIntervals.join(', ')}] мс`) } async disconnect() { await this.characteristic?.stopNotifications() this.server?.disconnect() } } או יישמו התחברות מחדש אוטומטית. פענוח נתונים שגוי. בדקו דגלי סיביות וסדר בתים (little-endian).
אילו דפדפנים תומכים ב-Web Bluetooth?
כיום, ה-API עובד ב-Chrome 70+, Edge 79+ ו-Chrome Android. Firefox ו-Safari אינם תומכים בו. אם הקהל שלכם משתמש בדפדפנים אלה, נוכל להציע פתרונות חלופיים: Native Messaging, PWA עם Service Worker, או אפליקציה היברידית. עבור רוב התרחישים העסקיים, Chrome הוא הבחירה הסטנדרטית.
תוכנית עבודה ולוח זמנים
| שלב | משך |
|---|---|
| ניתוח מסמכים והנדסה לאחור | 1–3 ימים |
| עיצוב ארכיטקטורה וממשק משתמש | 1–2 ימים |
| יישום חיבורים וניהול שגיאות | 2–4 ימים |
| בדיקה על מכשיר אמיתי | 1–2 ימים |
| פריסה וניטור | 0.5–1 יום |
אינטגרציה עם מכשיר מתועד: 3–5 ימים. עבור מכשירים לא מתועדים (הנדסה לאחור של פרוטוקול): 7–12 ימים. העלות נעה בין $500 ל-$2,000 בהתאם למורכבות ולצרכי ממשק המשתמש. נוכל להעריך את הפרויקט שלכם בתוך יום עסקים אחד—פשוט צרו קשר. קבלו ייעוץ על אינטגרציית BLE עוד היום.
מה כלול
- תיעוד מלא של הפרוטוקול וקוד האינטגרציה.
- גישה למאגר עם קוד מקור והוראות פריסה.
- הדרכה לצוות שלכם על תחזוקה והרחבת האינטגרציה.
- תמיכה ל-30 יום לאחר ההשקה (תיקוני באגים, ייעוץ).
- זמינות מובטחת עם התחברות מחדש אוטומטית.
- מהנדסים מוסמכים עם ניסיון של 5+ שנים ב-BLE.
עם ניסיון של 5+ שנים ב-BLE ו-20+ אינטגרציות מוצלחות, הצוות שלנו מספק פתרונות אמינים.
מדריך שלב אחר שלב
- בדקו תמיכת דפדפן:
class SmartLightController { private server: BluetoothRemoteGATTServer | null = null private controlChar: BluetoothRemoteGATTCharacteristic | null = null private readonly SERVICE_UUID = '00010203-0405-0607-0809-0a0b0c0d1910' private readonly CONTROL_UUID = '00010203-0405-0607-0809-0a0b0c0d2b11' async connect() { this.server = await connectToDevice(this.SERVICE_UUID) const service = await this.server.getPrimaryService(this.SERVICE_UUID) this.controlChar = await service.getCharacteristic(this.CONTROL_UUID) } async setColor(r: number, g: number, b: number) { const command = new Uint8Array([ 0x33, 0x05, 0x02, r, g, b, 0x00, 0x00, 0x00, r ^ g ^ b, ]) await this.controlChar!.writeValueWithResponse(command) } async setBrightness(level: number) { const command = new Uint8Array([ 0x33, 0x04, Math.round(level * 2.55), 0x00, ]) await this.controlChar!.writeValueWithoutResponse(command) } async readBatteryLevel(): Promise<number> { const service = await this.server!.getPrimaryService('battery_service') const char = await service.getCharacteristic('battery_level') const value = await char.readValue() return value.getUint8(0) } }. - בקשו את המכשיר:
function useBluetooth() { const [device, setDevice] = useState<BluetoothRemoteGATTServer | null>(null) const [isConnected, setIsConnected] = useState(false) const [isSupported] = useState(() => 'bluetooth' in navigator) const [error, setError] = useState<string | null>(null) const connect = useCallback(async (serviceUUID: string) => { try { setError(null) const server = await connectToDevice(serviceUUID) setDevice(server) setIsConnected(true) } catch (err) { if ((err as Error).name === 'NotFoundError') { setError('Устройство не выбрано') } else { setError((err as Error).message) } } }, []) const disconnect = useCallback(() => { device?.disconnect() setDevice(null) setIsConnected(false) }, [device]) return { device, isConnected, isSupported, error, connect, disconnect } }. - התחברו לשרת GATT:
device.addEventListener('gattserverdisconnected', async () => { let retries = 0 while (retries < 3) { await new Promise((r) => setTimeout(r, 1000 * (retries + 1))) try { await device.gatt!.connect() await resubscribeCharacteristics() console.log('Переподключено') return } catch { retries++ } } console.error('Не удалось переподключиться') }). - קבלו את השירות והמאפיין הרצויים.
- התחילו לקרוא או לכתוב נתונים.
- טפלו בניתוק: הירשמו לאירוע
optionalServices.
מהם שירותי ומאפייני GATT?
GATT (Generic Attribute Profile) הוא פרוטוקול המגדיר את מבנה הנתונים של מכשיר BLE. שירות הוא קבוצת מאפיינים (לדוגמה, שירות "סוללה"). מאפיין הוא פרמטר ספציפי (לדוגמה, רמת סוללה). לכל שירות ומאפיין יש UUID ייחודי.מידע נוסף על הטכנולוגיה: תיעוד רשמי ו-מאמר בוויקיפדיה.







