很多人第一次接触智能割草机 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: Boolean
val isWorking: Boolean
val isCharging: Boolean
val isReturning: Boolean
val hasError: Boolean
一旦同时出现:
isWorking = true
isCharging = 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
↓
RemoteDataSource
LocalDataSource
DeviceDataSource
其中:
-
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 如何连接一台真实设备?从扫码绑定到蓝牙配网》
下一篇将重点讨论:
-
为什么扫码后还需要蓝牙;
-
手机、服务器和设备分别承担什么职责;
-
配网过程如何设计成状态机;
-
手机连接成功为什么不等于设备上线;
-
配网失败后如何定位具体问题。
网硕互联帮助中心

评论前必须登录!
注册