Tüm Yazılar
KategoriiOS
Okuma Süresi
13 dk
Yayın Tarihi
...
Kelime Sayısı
652kelime

Kahveni hazırla - bu içerikli bir makale!

ESP32 device ↔ iOS app çift yönlü sync, BLE handshake + Firestore + Realm offline cache. 50ms handshake, 3-day offline survival, OTA firmware update. BLE state machine fragmentation gerçek konu.

Case Study: ESP-Point — IoT Hardware + iOS Sync, Offline-First Mimari

# Case Study: ESP-Point — IoT Hardware + iOS Sync, Offline-First Mimari

Problem: ESP32 microcontroller (DIY IoT cihaz) ile iOS app arasında çift yönlü realtime sync: cihaz sensor verisini yükler, app cihaza komut + firmware update gönderir. Offline-first zorunlu — cihaz internetsiz lokasyonlarda kullanılıyor.

Sonuç: 50ms BLE handshake median, 3-gün offline survival (local cache + delayed sync), OTA firmware update %99.2 success rate, 12K device sold.

1. Architecture

swift
1[ESP32 Device]
2 ↓ BLE (peripheral)
3[iOS App — central]
4 ↓ Wi-Fi when available
5[Firebase Realtime DB + Cloud Storage]
6 ↓ sync queue
7[Web Dashboard for fleet ops]

iOS app proxy / router rolünde: BLE \<-\> Cloud bridge.

2. BLE State Machine: Gerçek Konu

BLE'nin tek "easy demo"su olmaz. State machine fragmentation real production konu:

States:

  • unpairedScanning
  • pairing
  • paired (connected)
  • paired-disconnected (cached)
  • firmwareUpdating
  • error (her state'ten geçer)

Her transition için iOS CBCentralManager callback'lerinde 2-3 race condition çözdük:

  1. didDisconnect + didFailToConnect overlap — UUID-based dedup
  2. scanForPeripherals background restoration — CBCentralManagerOptionRestoreIdentifierKey set
  3. Bluetooth state .poweredOff.poweredOn transition'da scan otomatik resume etmiyor — explicit scanForPeripherals re-call

Result: 50ms handshake median. Connection drop rate %0.8 (industry standard %4-6).

3. Offline Storage: Realm + Sync Queue

3-day offline target. Her sensor reading (saat başı) device storage'a yazılır, sonra sync queue Realm'e push edilir:

swift
1class SyncQueue: Object {
2 @Persisted(primaryKey: true) var id: ObjectId
3 @Persisted var payload: Data
4 @Persisted var createdAt: Date
5 @Persisted var retryCount: Int = 0
6 @Persisted var lastError: String?
7}

Sync worker:

swift
1final class SyncWorker {
2 func runOnce() async {
3 let realm = try await Realm()
4 let pending = realm.objects(SyncQueue.self)
5 .where { $0.retryCount < 5 }
6 .sorted(by: \.createdAt)
7 .prefix(50)
8 
9 for item in pending {
10 do {
11 try await uploadToFirebase(item.payload)
12 try realm.write { realm.delete(item) }
13 } catch {
14 try realm.write {
15 item.retryCount += 1
16 item.lastError = error.localizedDescription
17 }
18 }
19 }
20 }
21}

Network reachability NWPathMonitor ile sync worker tetiklenir. App background'da BGAppRefreshTask ile day-1 retry.

4. OTA Firmware Update

8-12MB binary blob transfer over BLE:

  • Bluetooth chunk size: 244 bytes (BLE 5 max safe)
  • Sequence number + CRC32 her chunk
  • Acknowledgment per 10 chunk batch
  • Total transfer: 4-6 dakika (10MB firmware için)
  • Resume on disconnect support

Success rate: %99.2. Failure modes:

  • %0.5: connection drop son chunk öncesi
  • %0.2: device flash corrupt (re-upload OK)
  • %0.1: power loss critical moment (manuel recovery)

OTA UI önemli — progress bar + "do not close app" warning + battery indicator. App Store reviews'da +%18 satisfaction.

5. Test Strategy

  • Unit tests:: Sync queue state machine, BLE manager state transitions (mocked CBCentralManager)
  • Integration tests:: Realm migration, sync conflict resolution
  • Device farm:: 6 fiziksel ESP32 + 4 iPhone (iOS 16-18) — Cl pipeline'da physical test
  • Snapshot tests:: UI state for each BLE state

Coverage: Domain layer %92, BLE manager %78, UI %65. Total %78.

6. iOS App Architecture

  • SwiftUI iOS 16+
  • BLEManager: @MainActor actor (Bluetooth callbacks main thread)
  • DataLayer: Realm primary + Firebase secondary (offline-first)
  • Sync orchestrator: BG task + foreground refresh

Memory baseline: 105MB. Peak (during OTA): 220MB (firmware buffer).

7. Öğrenimler

1. BLE physical testing zorunlu. Simulator BLE simulate edemiyor. CI'da fiziksel cihaz şart.

2. `CBCentralManagerOptionRestoreIdentifierKey` background relaunch için kritik — yoksa app kill'den sonra connection kaybolur.

3. State machine documentation. 6-state machine production'da debug için her transition log'lanır.

4. Realm migration schema değişikliklerinde sync queue'yu boşaltır (test ile validate edilmedi → 200 user data loss yaşandı).

5. OTA "abort" button kullanıcıya verme — corrupt flash riski. Sadece pause/resume.

6. Firebase rules offline write throughput'ı kısıtlayabilir — requireAuth: false writeRule + custom token validation.

7. Battery profiling BLE constantly scan = 8 saat battery. Connection-on-demand pattern %35 tasarruf.

Sonuç: IoT + iOS sync, "happy path" gösterilenden 10x daha zor. State machine + offline storage + OTA = 3 ay teknik borç eğer baştan plan yapılmazsa.

Etiketler

#Case Study#IoT#BLE#Bluetooth#Offline-First#Realm#Firebase
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

İlgili İçerik