城市排水系统中,井盖下方的积水深度是防汛巡检的关键指标。借助支持蓝牙低功耗(BLE)的水位传感器,巡检人员使用一台iPhone即可完成近距离读数、报警判断与历史数据查看,无需铺设线缆或携带专用终端。Core Bluetooth框架为iOS提供了完整的BLE中心设备管理接口,能够发现井盖内传感器广播、建立连接、读取积水深度特征值并在超出阈值时触发提醒。

蓝牙水位传感器的广播与连接建立
智能井盖中的水位传感器通常作为外围设备(peripheral)周期性发送广播包,包内带有设备名称、厂商自定义字段以及电池电量等信息。iOS端作为中心设备(central),需要先请求蓝牙权限并在CBCentralManager初始化完成后开始扫描。值得注意的是,从iOS 13起必须在Info.plist中声明NSBluetoothAlwaysUsageDescription,否则状态会停留在poweredOff或无法弹窗授权。
扫描到目标设备后,通过connect方法建立连接。由于井盖金属环境对信号有屏蔽,实际连接可能不稳定,因此需要实现断线重连逻辑。下面的代码展示了基本的中心管理器初始化与连接回调处理,其中用服务UUID过滤只连接水位传感器,避免误连其他蓝牙设备。
import CoreBluetooth
let waterServiceUUID = CBUUID(string: "1234")
let depthCharUUID = CBUUID(string: "5678")
class WaterSensorManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var central: CBCentralManager!
var sensor: CBPeripheral?
override init() {
super.init()
central = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
central.scanForPeripherals(withServices: [waterServiceUUID], options: nil)
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
sensor = peripheral
central.connect(peripheral, options: nil)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices([waterServiceUUID])
}
}
在didConnect之后应立刻发现服务与特征值,并把深度特征值设为通知模式。这样传感器在每次测量更新时就会主动推送,不需要iOS端轮询,既省电又降低延迟。若连接失败,可在didFailToConnect中等待数秒后重新调用connect,但需控制重试频率,防止蓝牙芯片过载。
积水深度读取与水位报警逻辑
水位传感器一般通过超声波或压力变送器获得毫米级深度,并以无符号整数形式存放在特征值里,单位是毫米。iOS端在didUpdateValueFor回调中解析字节,转换为浮点米数。报警判断应放在本地:当深度超过设定阈值(例如0.5米)且持续若干周期,就调用UNUserNotificationCenter发送本地通知,提醒巡检员井盖存在溢流风险。
为了避免单次抖动导致误报,推荐采用滑动窗口判断。下面示例在接收到新深度后加入队列,仅当最近五次读数全部超阈值才报警。同时把当前值存入SQLite,便于后续绘制曲线。这种本地决策不依赖网络,即使手机处于飞行模式也能完成报警。
var depthHistory: [Double] = []
let alarmThreshold = 0.5
func handleDepth(raw: Data) {
let mm = raw.withUnsafeBytes { $0.load(as: UInt16.self) }
let meters = Double(mm) / 1000.0
depthHistory.append(meters)
if depthHistory.count > 5 { depthHistory.removeFirst() }
if depthHistory.count == 5 && depthHistory.allSatisfy({ $0 > alarmThreshold }) {
sendAlarmNotification(depth: meters)
}
saveToDatabase(depth: meters, time: Date())
}
func sendAlarmNotification(depth: Double) {
let content = UNMutableNotificationContent()
content.title = "井盖积水报警"
content.body = "当前深度(depth)米,超过安全阈值"
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
报警阈值不应写死在代码中,而应由用户在设置页配置并持久化。对于不同区域的井盖(如低洼路段与高架排水),阈值差异很大。另外,iOS在前台时通知可能不会弹窗,因此界面上也要有红色警示条,保证巡检员在打开App时直观看到危险状态。
历史曲线记录与离线数据查看
每次测量的深度和时间戳写入本地数据库后,便可生成二十四小时水位变化曲线。使用SQLite轻量可靠,配合Chart类绘图库能在iPhone上流畅渲染。查询时按小时聚合,若某小时缺失数据则用前后点线性插值补齐,防止曲线断裂影响趋势判断。
下面代码演示从SQLite读取当日数据并做简单聚合。实际项目中可加入索引优化,使上千条记录查询仍在毫秒级。历史曲线不仅用于复盘某次暴雨的积水过程,也可导出为CSV供市政平台分析。由于iOS后台扫描受限,建议在App进入前台时自动补传未同步记录。
import SQLite3
func queryTodayDepths() -> [(Date, Double)] {
var result: [(Date, Double)] = []
var db: OpaquePointer?
if sqlite3_open("water.db", &db) == SQLITE_OK {
let sql = "SELECT time, depth FROM records WHERE time >= date('now','start of day')"
var stmt: OpaquePointer?
if sqlite3_prepare_v2(db, sql, -1, &stmt, nil) == SQLITE_OK {
while sqlite3_step(stmt) == SQLITE_ROW {
let time = Date(timeIntervalSince1970: sqlite3_column_double(stmt, 0))
let depth = sqlite3_column_double(stmt, 1)
result.append((time, depth))
}
}
sqlite3_finalize(stmt)
}
sqlite3_close(db)
return result
}
在展示曲线时,横坐标应使用本地时区,避免巡检员误读时间。若设备长期无人连接,传感器自身也应具备本地存储与断电保护,待手机连上后通过批量特征值或私有命令一次性回传,弥补iOS不能全天候后台采集的短板。这样整套方案既便宜又实用,特别适合城市老旧管网的渐进式智能化改造。
Core_Bluetooth iOS 水位传感器修改时间:2026-08-17 02:32:32