凡亿教育-莎莎
凡事用心,一起进步
打开APP
公司名片
凡亿专栏 | 从需求到状态机设计:3200字保姆式项目实例教程!
从需求到状态机设计:3200字保姆式项目实例教程!

之前直播,有人让我讲讲状态机,因为涉及内容比较多,当时一时半会没想好,口头表述不够系统。

          

 

而且要有具体需求去设计比较直观些,今天这篇文章,就以我们无际单片机的项目《WiFi 4G LoRa 防盗报警主机》为例,讲解下状态机的设计。

          

 

报警主机这个设备是智能家居和安防系统的核心,功能丰富、技术多样,非常适合用来学习嵌入式软件设计的全流程。

          

 

我们将从需求分析开始,一步步梳理功能、设计状态机,最后用代码实现,确保你看完就能明白整个过程,并能动手实践。文章会用通俗易懂的语言,配上代码和注释,全文3000字以上,耐心看完,受益匪浅。

          

 

1.项目背景与需求分析

防盗报警主机是安防系统的大脑,它的任务是检测非法入侵并及时通知用户。

          

 

我们这个项目的主机支持 WiFi、4G和 LoRa 三种通信方式,并带锂电池应急供电,确保无论环境如何都能保持可靠的警报传递。

          

 

WiFi 和 4G 负责连接云平台,实现远程监控和通知;LoRa 是一种低功耗广域网技术,用于和家里的本地设备(如门磁探测器、遥控器、红外、烟感等)组网通信。

          

 

在动手设计之前,我们先把需求理清楚,满血版的功能,实在太多了,像通信、防盗报警、探测器组网、电话、短信、OTA等等。    

          

 

所以,我只提取其中一个防盗报警模式功能,去教大家入门状态机设计,方便理解。

          

 

通信功能:主机需要通过 WiFi 和 4G 与云平台交互,发送警报或接收用户指令;通过 LoRa 与门磁探测器、遥控器通信,获取传感器信号或用户控制信号。

          

 

防盗报警模式:系统支持多种工作模式,包括“撤防”(不检测入侵)、“在家布防”(只检测外部入侵)、“离家布防”(检测所有区域入侵)和“报警中”(触发警报并通知用户)。

          

 

状态机是一种非常适合嵌入式系统的设计方法,能让复杂的逻辑变得有条理,代码也更容易维护。

 

 

2.为什么要用状态机?

在嵌入式开发中,像防盗报警主机这样的设备需要根据用户指令、传感器信号和通信状态不断切换工作模式。如果用一堆 if-else 判断来写代码,逻辑很快就会乱成一团,调试和维护都特别痛苦。状态机(State Machine)就是专门解决这种问题的利器。

          

 

状态机的核心思想是把系统的工作模式定义为有限的“状态”,然后通过“事件”触发状态之间的切换。这样,代码逻辑变得清晰,每次只处理当前状态下的事件,既直观又容易扩展。

          

 

比如,我们可以定义“撤防”和“报警中”两个状态,当收到“入侵检测”事件时,从“撤防”切换到“报警中”,同时触发蜂鸣器和发送通知。这样的设计是不是一目了然?

          

 

接下来,我们就从需求出发,设计这个防盗报警主机的状态机,包括状态定义、事件设计和转移规则,最后再用代码实现。    

          

 

3.状态机的设计过程

 

定义状态

根据需求,防盗报警主机有几种明显的工作模式,我们直接把它们定义为状态:

•Idle(撤防):系统不检测任何入侵,用户可以随意活动,家里有人时常用这个模式。

•Armed_Home(在家布防):只检测外部区域(比如门窗)的入侵,忽略内部活动,适合晚上睡觉时使用。

•Armed_Away(离家布防):检测所有区域的入侵,家里没人时用这个模式。

•Alarm(报警中):检测到入侵后进入这个状态,触发蜂鸣器并发送警报通知。

          

 

这样,系统的状态就定下来了:Idle、Armed_Home、Armed_Away 和 Alarm。这四个状态覆盖了所有核心功能。

          

 

设计事件

状态机靠事件驱动,事件就是触发状态转换的“信号”。根据需求,我们定义以下事件:

          

 

•Arm_Home_Command:用户通过遥控器或手机发送“在家布防”命令。

•Arm_Away_Command:用户发送“离家布防”命令。    

•Disarm_Command:用户发送“撤防”命令。

•Intrusion_Detected:门磁探测器等传感器检测到入侵。

•Timeout:警报持续一段时间后自动停止。

          

 

这些事件会推动系统在不同状态之间切换,接下来我们就定义这些切换规则。

          

 

状态转移规则   

 

状态机的核心逻辑是“当前状态   事件 = 下一个状态”。我们逐个状态来看看它们在不同事件下的反应。

          

 

Idle 状态

•用户发送 Arm_Home_Command,系统进入 Armed_Home,开始监控外部区域。

•用户发送 Arm_Away_Command,系统进入 Armed_Away,监控所有区域。

          

 

在这个状态下,系统不检测入侵,所以 Intrusion_Detected 不会触发任何动作。

          

 

Armed_Home 状态

•收到 Intrusion_Detected(仅外部区域),系统进入 Alarm,触发警报。

•收到 Disarm_Command,系统回到 Idle,停止监控。

          

 

在家布防模式下,内部活动不会触发警报,这是和离家布防的关键区别。

          

 

    

Armed_Away 状态

•收到 Intrusion_Detected(任何区域),系统进入 Alarm。

•收到 Disarm_Command,系统回到 Idle。

          

 

离家布防模式下,所有入侵都会触发警报,保护力度最大。

          

 

Alarm 状态

•收到 Disarm_Command,系统回到 Idle,关闭警报。

•收到 Timeout,警报持续一段时间后自动停止,系统回到 Idle。

          

 

在这个状态下,系统会持续触发蜂鸣器并尝试通过 WiFi、4G 发送警报通知。

          

 

动作与行为    

 

状态切换时,系统还需要执行一些动作,比如:

•进入 Alarm 时:启动蜂鸣器、发送警报通知到云平台。

•回到 Idle 时:关闭所有警报和指示灯。

          

 

这些动作可以在状态转换时执行,也可以在进入状态时处理,具体实现时可以根据硬件需求调整。

          

 

    

4.代码实现

设计好状态机后,我们用 C 语言把它实现出来。嵌入式开发中,状态机通常用 switch-case 或函数指针实现,这里我们选简单的 switch-case,方便理解。

          

 

定义状态和事件

先定义状态和事件的枚举类型:


#include // 定义状态typedef enum {    STATE_IDLE,        // 撤防    STATE_ARMED_HOME,  // 在家布防    STATE_ARMED_AWAY,  // 离家布防    STATE_ALARM        // 报警中} State;// 定义事件typedef enum {    EVENT_ARM_HOME,    // 在家布防命令    EVENT_ARM_AWAY,    // 离家布防命令    EVENT_DISARM,      // 撤防命令    EVENT_INTRUSION,   // 入侵检测    EVENT_TIMEOUT      // 超时} Event;// 当前状态,初始为撤防State current_state = STATE_IDLE;

          

 

    

实现状态机逻辑

          

 

接下来写一个函数 handle_event,根据当前状态和事件处理状态转换:


void handle_event(Event event) {    switch (current_state)     {        case STATE_IDLE:            if (event == EVENT_ARM_HOME)             {                printf("进入在家布防模式 ");                current_state = STATE_ARMED_HOME;            } else if (event == EVENT_ARM_AWAY)             {                printf("进入离家布防模式 ");                current_state = STATE_ARMED_AWAY;            }            break;        case STATE_ARMED_HOME:            if (event == EVENT_INTRUSION)             {                printf("外部入侵检测到,触发报警 ");                current_state = STATE_ALARM; // 假设外部入侵检测逻辑已区分            } else if (event == EVENT_DISARM)             {                printf("从在家布防撤防 ");                current_state = STATE_IDLE;            }            break;        case STATE_ARMED_AWAY:            if (event == EVENT_INTRUSION)             {                printf("入侵检测到,触发报警 ");                current_state = STATE_ALARM;            } else if (event == EVENT_DISARM)             {                printf("从离家布防撤防 ");                current_state = STATE_IDLE;            }            break;        case STATE_ALARM:            if (event == EVENT_DISARM)             {                printf("从报警状态撤防 ");                current_state = STATE_IDLE;            } else if (event == EVENT_TIMEOUT)             {                printf("报警超时,回到撤防 ");                current_state = STATE_IDLE;            }            break;        default:            printf("未知状态 ");            break;    }}

这段代码用 switch-case 实现了状态机的逻辑。每个状态下,根据传入的事件,系统会打印当前行为并更新 current_state。在实际项目中,printf 可以替换为硬件操作,比如控制蜂鸣器或发送网络数据。

          

 

测试状态机

我们来模拟一个使用场景:用户布防、检测到入侵、撤防、报警超时。主函数如下:










int main() {    printf("初始状态:撤防 ");    handle_event(EVENT_ARM_AWAY);     // 用户发送离家布防命令    handle_event(EVENT_INTRUSION);    // 传感器检测到入侵    handle_event(EVENT_DISARM);       // 用户撤防    handle_event(EVENT_TIMEOUT);      // 超时(此时无作用)    return 0;}

运行结果会是:

初始状态:撤防
进入离家布防模式
入侵检测到,触发报警
从报警状态撤防

这个输出清晰地展示了状态机的每一步转换,完全符合我们设计的需求。

          

 

当然,这个只是展示一个框架模型,无际单片机这个项目的功能,远远比这个复杂,细节很多,从头到尾开发的话,哪怕有经验的情况下,也要一年半载的。

ca08ee5b8c68789b8fcae0b484d3e6.jpg

          

 

光状态机还不够,需要多种架构的组合,比如表驱动法 状态机。

          

 

    

5.状态机的优势与优化

通过上面的设计和代码,我们可以看到状态机的几个优点:

•逻辑清晰:每个状态和事件都独立处理,代码结构一目了然,后面每个功能修改都互不影响。

•易于扩展:如果想加个“低电量警告”状态,只需在 State 枚举中加一项,再增加对应的 case 分支。

•调试方便:打印当前状态就能快速定位问题。

          

 

不过,在实际应用中,还有一些细节需要注意。比如,如果状态和事件太多,可能导致“状态爆炸”,这时可以考虑用子状态机,把复杂状态拆分成更小的模块。

          

 

另外,每个状态下的事件要尽量覆盖全面,避免遗漏;比如在 Alarm 状态下,可以加个定时器检查 Timeout,确保警报不会无限鸣响。

          

 

通过这个防盗报警主机的项目,我们从需求分析开始,梳理了防盗模式的功能需求,接着设计了状态机,定义了状态和事件,最后用 C 语言实现了完整的逻辑。这个过程不仅展示了状态机的强大之处,也提供了一个可以直接套用到其他嵌入式项目的模板。

          

 

在实际开发中,你可以根据硬件特性调整代码,比如用中断处理传感器信号,用 FreeRTOS 任务管理通信模块。状态机是个灵活的工具,只要掌握了它的设计思路,就能轻松应对各种复杂需求。希望这篇文章能给你带来启发,如果你有自己的项目想法,不妨试试用状态机设计一下,效果绝对超出预期!    

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表凡亿课堂立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。
相关阅读
进入分区查看更多精彩内容>
精彩评论

暂无评论