HarmonyOS APP开发—"智泊"智能停车App,需要用到这个库

停车场 200 个车位,每个装一个电池供电的 CoAP 传感器。用户打开 App 要立刻看到空位分布。逐个 HTTP 轮询?200 次请求、传感器电量撑不过一周。CoAP 组播一次请求,200 个传感器各自响应——这就是 @ohos/coap 的价值。

📦 仓库地址 | ohpm install @ohos/coap


场景

"智泊"要做的事:手机连上停车场 WiFi,扫一遍就知道哪些车位空着,点某个车位看详情(占用多久、能否预约),车位状态变化时地图实时更新。

硬件侧是低功耗 MCU + 地磁传感器,电池供电要用好几年。这决定了通信协议必须够轻:

  • HTTP:TCP 三次握手 + 几百字节 header,200 次轮询完全不现实
  • MQTT:够轻,但要部署 Broker,还得处理 200 个设备的订阅关系
  • CoAP:UDP + 4 字节头 + 原生组播,手机和传感器点对点,不需要任何中间件

为什么是 CoAP

维度HTTPMQTTCoAP
传输层TCPTCP✅ UDP
报文头几百字节2 字节✅ 4 字节
组播发现❌❌✅ 原生支持
中间服务器需要需要 Broker✅ 点对点直连
设备当服务端难难✅ 原生支持
传感器功耗高中✅ 低

组播是决定性优势:一次广播拿到全部车位状态,比 200 次单播省两个数量级的流量和电量。


关键实现

组播发现全部车位

import {
  CoapClient, CoapRequestMethod, CoapRequestType,
  CoapResponseCode, CoapBlockMode,
} from '@ohos/coap'

interface SpotInfo {
  id: string          // 车位号,如 A-023
  occupied: boolean
  section: string
  sensorIp: string    // 用于后续单播查询
}

async discoverSpots() {
  this.scanning = true
  const client = new CoapClient()   // 每次请求独立实例
  client.setBlockMode(CoapBlockMode.COAP_BLOCK_USE_LIBCOAP)

  try {
    const data = await client.request(
      'coap://224.0.1.187:5683/parking/spot',   // CoAP 组播地址
      CoapRequestMethod.GET,
      CoapRequestType.COAP_MESSAGE_NON,          // 组播用 NON,无需确认
    )
    if (data.code === CoapResponseCode.SUCCESS) {
      this.spots.push(JSON.parse(data.message))
    }
  } finally {
    this.scanning = false
  }
}

组播必须用 COAP_MESSAGE_NON(Non-confirmable)。如果用 CON,200 个传感器各回一个确认包,网络瞬间拥塞。

单播查详情:控制并发数

用户快速点多个车位时,为每个请求建独立实例,并限制并发:

async queryBatch(spots: SpotInfo[]): Promise<SpotInfo[]> {
  const results: SpotInfo[] = []
  const CONCURRENCY = 5

  for (let i = 0; i < spots.length; i += CONCURRENCY) {
    const batch = spots.slice(i, i + CONCURRENCY)
    const part = await Promise.all(batch.map(async (spot) => {
      const client = new CoapClient()   // ✅ 独立实例,不复用
      try {
        const data = await client.request(
          `coap://${spot.sensorIp}:5683/parking/spot/${spot.id}`,
          CoapRequestMethod.GET,
          CoapRequestType.COAP_MESSAGE_CON,   // 单播用 CON,保证可靠
        )
        return data.code === CoapResponseCode.SUCCESS ? JSON.parse(data.message) : null
      } catch (e) { return null }
    }))
    results.push(...part.filter((r) => r !== null))
  }
  return results
}

接收传感器主动推送

车位状态变化时传感器主动 POST,App 当服务端接:

import { CoapServer } from '@ohos/coap'

const server = new CoapServer()

server.handle('parking/update', (request) => {
  const update = JSON.parse(request.payload)
  // 更新本地状态,地图 UI 自动刷新
  this.spots = this.spots.map((s) =>
    s.id === update.id ? { ...s, occupied: update.occupied } : s
  )
  return { code: '2.04', payload: 'OK' }
})

server.start('coap://0.0.0.0:5683')

aboutToDisappear() { server.stop() }   // 别忘了停

两个容易踩的点

1. Native 日志会打印设备 IP

setNativeLogOpen(true) 调试协议问题很好用,但日志里含 IP 地址,隐私合规过不了。绑到构建配置上,别靠人记:

if (BuildProfile.DEBUG) CoapClient.setNativeLogOpen(true)
else CoapClient.setNativeLogOpen(false)

2. 地下车库信号差,要做退避重试

async requestWithRetry(url: string, maxRetry = 2) {
  for (let i = 0; i <= maxRetry; i++) {
    const client = new CoapClient()   // 每次重试新实例
    try {
      const data = await client.request(url, CoapRequestMethod.GET, CoapRequestType.COAP_MESSAGE_CON)
      if (data.code === CoapResponseCode.SUCCESS) return data
    } catch (err) {
      if (i < maxRetry) await new Promise((r) => setTimeout(r, 500 * (i + 1)))  // 退避
    }
  }
  return null
}

小结

"智泊"这个场景里,@ohos/coap 解决了三件事:组播一次发现 200 个车位、手机既能当客户端查询也能当服务端接推送、UDP 低功耗让电池供电的传感器能跑几年。

关键是不需要在停车场部署任何中间服务器——手机和传感器点对点通信就够了。这是 HTTP 和 MQTT 方案都做不到的一点。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐