乐于分享
好东西不私藏

第一篇:智能割草机 App 到底复杂在哪里?移动端业务全景拆解

第一篇:智能割草机 App 到底复杂在哪里?移动端业务全景拆解

很多人第一次接触智能割草机 App,可能会觉得它和扫地机器人 App 差不多:

  • 绑定设备

  • 点击开始

  • 查看运行状态

  • 没电自动回充

从界面上看,功能似乎并不复杂。

但真正进入业务之后就会发现,智能割草机 App 并不是一个简单的“设备遥控器”。

它需要同时连接用户、云端服务、IoT 平台、割草机设备、庭院地图以及 RTK 定位系统。用户在 App 中点击一次“开始割草”,背后可能会触发设备状态校验、地图校验、任务创建、指令下发、设备执行、实时位置上报和异常监控等一整套流程。

这一篇先不讨论具体代码,而是从移动端角度,完整拆解智能割草机业务。


一、割草机 App 不是普通的接口型应用

普通的商城、资讯或者内容类 App,核心数据通常来自服务端。

移动端发送一个 HTTP 请求:

App ↓服务器 ↓返回数据 ↓页面展示

只要接口返回成功,页面通常就可以认为本次操作已经完成。

例如用户修改昵称,服务器返回成功后,App 就可以更新用户信息。

但设备类 App 不一样。

用户在割草机 App 中点击“开始割草”后,服务器返回成功,只能说明服务器收到了请求,并不能证明割草机已经真正开始工作。

完整链路可能是:

用户点击开始割草        ↓App 检查当前设备状态        ↓App 向服务器发送控制指令        ↓服务器将指令发送给 IoT 平台        ↓IoT 平台将指令转发给割草机        ↓割草机接收并校验指令        ↓割草机开始执行任务        ↓割草机上报新的运行状态        ↓App 收到状态后更新页面

这条链路中任何一个环节出现问题,都可能导致操作失败。

例如:

  • 手机网络正常,但设备已经离线;

  • 服务器成功接收指令,但设备没有收到;

  • 设备收到指令,但当前存在故障,无法执行;

  • 设备开始执行,但 RTK 定位状态不满足要求;

  • App 没有及时收到最新状态,页面仍然显示“准备中”。

因此,在设备业务中:

接口调用成功,不等于设备执行成功。

这是理解整个割草机移动端架构的第一个关键点。


二、移动端连接的不是一台设备,而是一套系统

从用户界面来看,App 操作的是一台割草机。

但从技术架构来看,移动端实际连接的是一套完整系统。

用户 ↓割草机 App ↓业务服务器 ↓IoT 平台 ↓割草机设备 ↓RTK 基站与定位系统

除此之外,还包括:

  • 用户账号

  • 家庭或者庭院

  • 设备

  • 庭院地图

  • 割草区域

  • 禁区

  • 充电桩

  • 割草任务

  • 实时轨迹

  • 故障告警

  • 固件升级

这些对象之间并不是孤立的。

一台割草机通常属于某个家庭或者庭院,一个庭院下可能有一张或多张地图,一张地图中又可能包含多个工作区域、禁区和连接通道。

可以将核心业务关系理解为:

用户 ↓家庭 / 庭院 ↓割草机设备 ↓庭院地图 ↓工作区域 ↓割草任务

用户切换家庭后,设备列表会发生变化。

用户切换设备后,当前地图、任务状态、告警信息和设备设置也需要同时切换。

因此,移动端不能只维护一个简单的 deviceId,而是需要建立完整的业务上下文。

例如:

data class AppContextState(    val currentUser: User?,    val currentHome: Home?,    val currentDevice: MowerDevice?,    val currentMap: MowerMap?)

这些上下文发生变化时,页面中的其他业务模块也需要同步更新。


三、割草机业务可以拆成哪些模块

从移动端视角,智能割草机业务大致可以拆成以下几个核心模块:

智能割草机 App├── 用户与家庭├── 设备接入├── 设备通信├── 设备实时状态├── 地图与区域├── 割草任务├── RTK 与定位├── 告警与故障├── OTA 升级└── 数据缓存与同步

这些模块之间又存在大量交叉。

例如“开始割草”这个功能,就会同时依赖:

  • 当前用户是否拥有设备控制权限;

  • 设备是否在线;

  • 设备是否正在升级;

  • 当前地图是否有效;

  • 是否选择了工作区域;

  • RTK 状态是否正常;

  • 电量是否满足要求;

  • 是否存在未处理的严重故障;

  • 当前是否已经存在其他任务。

因此,一个按钮背后,往往不是一个接口,而是一组业务规则。


四、设备接入:先让 App 找到一台真实设备

普通 App 安装完成后,用户登录就可以使用。

但割草机 App 在登录之后,还需要完成设备接入。

常见流程包括:

扫描设备二维码 ↓获取设备编号 ↓通过蓝牙发现设备 ↓建立近场连接 ↓向设备发送 Wi-Fi 信息 ↓设备连接云端 ↓服务器确认设备在线 ↓账号绑定设备

这里至少涉及三种对象:

手机 App割草机设备业务服务器

配网失败时,也不能简单显示“连接失败”。

移动端需要判断失败发生在哪一步:

  • 没有扫描到设备;

  • 蓝牙权限未开启;

  • 手机与设备连接失败;

  • Wi-Fi 密码错误;

  • 设备无法连接路由器;

  • 设备已连接网络,但无法访问云端;

  • 设备已经被其他账号绑定;

  • 云端绑定接口失败。

这些失败原因对应的用户处理方式完全不同。

因此,设备接入本身就是一个完整的状态流程,而不是一个普通页面。


五、设备通信:不同协议负责不同事情

割草机 App 往往不会只使用 HTTP。

常见通信方式包括:

通信方式
主要用途
HTTP
查询设备、地图、历史任务和设备设置
MQTT
设备状态上报、消息订阅和控制指令
WebSocket
App 与服务端之间的实时数据同步
BLE
配网、近场控制和设备诊断
局域网通信
同一网络下的快速设备控制

不同通信方式解决的问题不同。

例如,设备列表可以通过 HTTP 查询,但设备当前位置和任务进度属于高频实时数据,如果完全依赖轮询接口,会产生较大的延迟和请求压力。

设备运行过程中可能持续上报:

  • 当前坐标

  • 当前电量

  • 工作状态

  • 任务进度

  • RTK 状态

  • 故障信息

  • 充电状态

这些数据更适合通过 MQTT 或 WebSocket 推送。

移动端架构需要把不同通信方式统一到业务层,而不是让页面直接操作 MQTT、蓝牙或者 WebSocket。

理想的数据流应该是:

MQTT / WebSocket / HTTP / BLE              ↓        DataSource              ↓         Repository              ↓         ViewModel              ↓             UI

这样页面只关心业务状态,而不需要知道数据来自哪个协议。


六、实时状态:页面不是接口结果,而是设备当前状态

设备类 App 最重要的数据之一,就是设备实时状态。

一台割草机可能处于:

离线在线空闲准备中割草中暂停中返回充电充电中升级中故障中

如果使用多个 Boolean 表示状态,很快就会出现冲突。

例如:

val isOnline: Booleanval isWorking: Booleanval isCharging: Booleanval isReturning: Booleanval hasError: Boolean

一旦同时出现:

isWorking = trueisCharging = true

业务就会变得难以判断。

因此,更合理的方式是将设备状态设计成明确的状态模型:

sealed interface DeviceWorkingState {    data object Offline : DeviceWorkingState    data object Idle : DeviceWorkingState    data object Preparing : DeviceWorkingState    data object Mowing : DeviceWorkingState    data object Paused : DeviceWorkingState    data object Returning : DeviceWorkingState    data object Charging : DeviceWorkingState    data object Updating : DeviceWorkingState    data class Fault(val code: String) : DeviceWorkingState}

页面上的按钮、提示文案和交互方式,都应该由当前状态决定。

例如:

空闲:显示“开始割草”割草中:显示“暂停”和“结束”暂停中:显示“继续”和“返回充电”升级中:禁止设备控制故障中:优先显示故障处理入口

这也是为什么设备业务非常依赖状态机设计。


七、割草任务不是一次简单的开始和结束

用户看到的割草任务,可能只是一次“开始割草”。

但设备真正执行时,任务会经历多个阶段:

任务创建 ↓设备准备 ↓离开充电桩 ↓前往工作区域 ↓开始割草 ↓电量不足 ↓返回充电 ↓充电完成 ↓继续割草 ↓任务完成

在这个过程中,还可能出现:

  • 用户主动暂停;

  • 用户主动结束;

  • 设备被抬起;

  • 刀盘堵转;

  • 车轮被卡住;

  • RTK 定位丢失;

  • 设备超出电子围栏;

  • 天气不适合作业;

  • 设备突然离线。

因此,割草任务本身也需要一个状态机。

移动端需要根据任务状态解决三个问题:

第一,当前页面应该展示什么。

第二,当前用户可以执行哪些操作。

第三,App 被关闭再打开后,如何恢复任务状态。

这意味着任务状态不能只保存在当前页面的 ViewModel 中,而应该由 Repository 根据服务器数据和设备实时上报统一维护。


八、割草机地图不是普通地图

普通地图类 App 主要展示道路、地点和导航路线。

割草机地图展示的却是设备可以在哪里工作。

一张庭院地图可能包含:

工作区域禁区连接通道充电桩RTK 基站割草机位置实时轨迹历史轨迹

例如,用户可能在同一个庭院中划分:

  • 前院

  • 后院

  • 草坪一区

  • 草坪二区

  • 花坛禁区

  • 水池禁区

  • 区域之间的连接通道

这些数据不是简单的地图覆盖物,而是实际参与设备工作决策的业务数据。

地图模块至少需要处理:

  • 区域边界绘制;

  • 禁区绘制;

  • 多边形闭合;

  • 边界点拖动;

  • 区域自相交校验;

  • 禁区是否位于工作区内部;

  • 通道是否有效连接两个区域;

  • 地图版本同步;

  • 本地编辑与云端地图冲突。

因此,地图在割草机 App 中不是一个 UI 组件,而是核心业务模块。


九、RTK 为什么会成为核心业务

传统 GPS 定位通常存在米级误差。

对于汽车导航来说,几米的误差大多数情况下可以接受,但对于割草机来说,几米误差可能意味着:

  • 割到花坛;

  • 冲出草坪;

  • 重复割草;

  • 漏掉大片区域;

  • 无法准确沿边;

  • 无法返回充电桩。

因此,智能割草机通常需要更高精度的定位能力,RTK 就成为其中非常关键的一部分。

移动端一般不会负责 RTK 定位算法本身,但需要负责:

  • 展示基站状态;

  • 展示设备定位状态;

  • 展示卫星或者信号信息;

  • 接收设备坐标;

  • 判断定位数据是否有效;

  • 在定位异常时引导用户处理。

RTK 状态也不是简单的“成功”和“失败”。

常见状态可能包括:

未连接初始化中单点解浮点解固定解信号弱定位丢失

当设备轨迹发生跳点时,移动端还需要结合:

  • 坐标时间戳;

  • 定位精度;

  • RTK 解状态;

  • 设备移动速度;

  • 前后坐标距离;

  • 当前任务状态;

共同判断这条坐标是否可信。


十、故障提示不能只显示错误码

实体设备运行在真实环境中,故障是不可避免的。

割草机常见异常包括:

  • 左轮或者右轮堵转;

  • 刀盘堵转;

  • 设备被抬起;

  • 设备倾斜;

  • 碰撞传感器异常;

  • 充电失败;

  • 电池温度异常;

  • RTK 信号弱;

  • 定位丢失;

  • 设备越界;

  • 设备离线。

设备上报的通常是一个错误码,例如:

ERROR_WHEEL_LEFT_BLOCKED

但普通用户无法理解这种错误。

移动端需要完成一层业务转换:

设备错误码 ↓业务错误类型 ↓用户可以理解的描述 ↓具体处理步骤

最终展示给用户的内容应该类似:

左轮可能被异物卡住。请关闭设备,并检查左轮周围是否存在树枝、石块或者缠绕的杂草。

同时还要区分:

  • 普通提示;

  • 可恢复告警;

  • 严重故障;

  • 必须立即停止任务的故障。

这部分不仅是文案工作,也是业务架构的一部分。


十一、最难的问题:App、服务器和设备状态不一致

割草机业务中,同时存在三份状态:

App 本地状态服务器记录状态设备真实状态

这三份状态可能并不一致。

例如:

App 显示设备正在割草,但设备实际已经离线。服务器显示指令发送成功,但设备没有真正执行。用户在本地修改了地图,但新地图还没有同步到设备。

因此,移动端必须明确不同数据的可信来源。

一般可以按照下面的原则处理:

账号、家庭和设备配置:以服务器为准设备实时运行状态:以设备最新上报为准地图编辑中的临时数据:以本地编辑副本为准历史任务和历史告警:以服务器记录为准

此外,还需要结合消息时间戳和数据版本,避免旧消息覆盖新状态。

例如设备先上报“割草中”,随后上报“已暂停”,但由于网络延迟,“割草中”的消息最后才到达 App。

如果移动端不校验消息时间,就可能把页面错误地恢复成“割草中”。


十二、从移动端角度看整体架构

将前面的业务放在一起,可以得到一套基本架构:

┌─────────────────────────────────────┐│               UI 层                ││ 首页、地图、任务、告警、设置、OTA   │├─────────────────────────────────────┤│            状态管理层               ││ 设备状态、任务状态、地图状态、连接状态│├─────────────────────────────────────┤│              业务层                 ││ 设备控制、任务管理、地图管理、RTK 管理│├─────────────────────────────────────┤│              数据层                 ││ Repository、Local、Remote、Device   │├─────────────────────────────────────┤│              通信层                 ││ HTTP、MQTT、WebSocket、BLE、局域网   │└─────────────────────────────────────┘

对应到常见的移动端代码结构,可以是:

UI ↓ViewModel ↓UseCase / Manager ↓Repository ↓RemoteDataSourceLocalDataSourceDeviceDataSource

其中:

  • UI 只负责展示和接收用户操作;

  • ViewModel 负责页面状态;

  • UseCase 负责完整业务流程;

  • Repository 负责统一数据来源;

  • DataSource 负责具体通信和存储;

  • MQTT、BLE、HTTP 等细节被隔离在数据层以下。

这样的设计能够避免页面和设备协议强耦合。


十三、割草机业务真正考验的是什么

从移动端开发者角度看,割草机业务真正复杂的地方,并不是页面数量,也不是某一个地图 SDK 或通信框架。

它真正考验的是以下几种能力:

1. 业务建模能力

能否把设备、地图、区域、任务、定位和故障抽象成清晰的业务模型。

2. 状态管理能力

能否处理设备在线、割草、暂停、回充、充电、故障等复杂状态。

3. 多通信链路管理能力

能否将 HTTP、MQTT、WebSocket 和 BLE 统一到一套业务架构中。

4. 数据一致性能力

能否处理 App、服务器和设备状态不一致的问题。

5. 异常恢复能力

能否在断网、设备离线、App 重启和指令超时后恢复正确状态。

6. 模块边界设计能力

能否让地图、设备、任务、定位和通信模块保持清晰边界,而不是全部堆在一个 ViewModel 中。


总结

智能割草机 App 看起来像一个设备控制工具,实际上却是一个典型的复杂 IoT 移动端系统。

它同时需要处理:

用户与家庭设备接入多协议通信实时状态任务状态机庭院地图RTK 定位异常告警OTA 升级多端数据一致性

普通接口型 App 关注的是:

接口返回了什么。

而设备型 App 更关注的是:

设备现在到底处于什么状态。

这也是整个割草机移动端架构的核心。

后续文章将从设备接入开始,逐步拆解蓝牙配网、设备绑定以及设备上线的完整链路。

下一篇预告

《割草机 App 如何连接一台真实设备?从扫码绑定到蓝牙配网》

下一篇将重点讨论:

  • 为什么扫码后还需要蓝牙;

  • 手机、服务器和设备分别承担什么职责;

  • 配网过程如何设计成状态机;

  • 手机连接成功为什么不等于设备上线;

  • 配网失败后如何定位具体问题。