当前位置: 首页 > news >正文

基于手势识别完成ESP32C3控制8位继电器实现智能鱼缸整体方案设计

具体项目代码已上传到gitee. 链接见文章结尾

1. 设计背景

在这里插入图片描述

题主有两个鱼缸一个龟缸, 三个缸每个缸包括一个灯, 一个过滤相当于六个设备
遇到的问题:

  1. 每次喂鱼时, 希望三个缸的过滤可以同时关闭, 等喂完鱼后, 能后在关闭20分钟后自动打开(喂鱼20分钟都没吃完, 那只能说鱼食喂多了)
  2. 每次停电在启动后, 能够自动打开过滤
  3. 每次人靠近能够自动亮灯, 人走后30分钟自动关灯(毕竟人走后可能一会又回来赏鱼了, 为了防止来来回回开关, 增加30分钟延长)
  4. 晚上有一个灯常亮 (鱼缸在客厅, 这样如果有需要到客厅办事, 有光的话就不会摸黑走路了)

2. 设计方案

对于上面遇到的四个问题, 给出设计方案

  1. 开始喂鱼时, 通过手势识别能够关闭所有的鱼缸过滤, 比如向上摆手
  2. 停电后自启动过滤: 设置一部分开关上电默认打开状态
  3. 增加人体感应模块, 做到人来人走感知, 控制灯光开闭
  4. 联网获取日出日落时间, 根据该时间实现夜晚自动亮灯的功能

2.1. 硬件相关

  1. 主控: esp32C3 XIAO, 优点: 小, 功耗低, WiFi, 蓝牙; 缺点: 贵, 引脚少
  2. 手势识别: PAJ7620, IIC通信
  3. 显示整体状态: oled0.9寸, IIC通信
  4. 继电器模块: 8位普通继电器模块
  5. 串行转并行控制芯片: HC595, 三个esp32的引脚即可控制8位继电器的输出
  6. 小爱音响: 控制8位继电器开关
  7. 米家人体存在传感器: 感知人体存在

2.2. 平台相关

  1. 米家: 接入米家设备(小爱音响, 人体存在传感器)和其他平台的设备
  2. 巴法云: mqtt的平台, 可以将本地设备状态关联联网, 在将巴法云接入到米家, 米家间接控制整个系统

2.3. 总结

整体的解决方案如下所示
在这里插入图片描述

3. 软件设计与实现

3.1. 整体规划 UML图

3.2. 不同器件控制的细节

3.2.1. 按键 短按, 长按, 双击设计

因为有八个开关, 需要使用按键来选择手势控制开关开闭时, 选择哪几个开关控制
即按键选择对应开关, 手势控制开关开闭状态

本项目只设计了一个开关, 需要将所有模式选择的功能都放到这个开关上, 所以在软件实现上增加了短按, 长按, 双击三个功能, 以此增加不同的场景控制

button软件设计

  1. 增加定时器 根据超时判断单击 双击 还是长按
  2. /* 读取事件(非阻塞,读取后自动清除) */ KeyEvent readKeyEvent(); 整个流程是往g_event写按键结果, 然后在非阻塞状态下, 获取这个按键结果, 处理对应的业务流
  3. 如果在主函数的loop中调用update_keyMode将结果取走, 无法在其他位置获取结果, 因此本项目中按键的控制部分放到 update_keyMode
#include <Arduino.h>
#include <Ticker.h>
#include "feature_control_switch.h"#define KEY_PIN          D8       // 按键引脚
#define DEBOUNCE_MS     20       // 消抖时间
#define LONG_TIME      800       // 长按判定
#define DOUBLE_GAP     300       // 双击间隔enum KeyEvent {KEY_NONE,KEY_SHORT,KEY_LONG,KEY_DOUBLE
};static volatile KeyEvent g_event = KEY_NONE;/*---------------- 内部状态机 ----------------*/
static volatile bool   key_raw = true;   // 原始引脚电平
static volatile bool   key_stable = true;// 消抖后的电平
static volatile uint32_t t_last = 0;     // 上次变化时刻
static volatile bool   pressed = false;  // 已确认按下
static volatile uint32_t down_ms = 0;    // 按下时间戳
static volatile uint8_t click_cnt = 0;   // 点击计数/* 软件定时器 */
Ticker scanTicker;   // 1 ms 周期扫描
Ticker longTicker;
Ticker doubleTicker;/* 设置事件给 loop() 取走 */
static void setEvent(KeyEvent e) {g_event = e;
}/* 长按超时 */
static void longTimeout() {if (pressed) {setEvent(KEY_LONG);/* 新增:清除后续可能的短按/双击 */click_cnt = 0;longTicker.detach();doubleTicker.detach();}
}/* 双击超时 */
static void doubleTimeout() {if (click_cnt == 1) {setEvent(KEY_SHORT);}click_cnt = 0;
}/* 1 ms 周期任务:消抖 + 状态机 */
static void scanTask() {bool now = digitalRead(KEY_PIN);          // 读原始电平if (now != key_raw) {                     // 电平变化key_raw = now;t_last = millis();}/* 消抖完成,检查稳定电平 */if ((millis() - t_last) >= DEBOUNCE_MS) {if (key_stable != key_raw) {key_stable = key_raw;/* 稳定为低:按下 */if (!key_stable && !pressed) {pressed = true;down_ms = millis();click_cnt++;longTicker.once_ms(LONG_TIME, longTimeout);}/* 稳定为高:松开 */if (key_stable && pressed) {pressed = false;longTicker.detach();/* 如果已经触发过长按,这里什么也不做 */if (click_cnt == 0) return;if (click_cnt == 1) {doubleTicker.once_ms(DOUBLE_GAP, doubleTimeout);} else if (click_cnt == 2) {doubleTicker.detach();setEvent(KEY_DOUBLE);click_cnt = 0;}}}}
}/* 读取事件(非阻塞,读取后自动清除) */
KeyEvent readKeyEvent() {noInterrupts();KeyEvent e = g_event;g_event = KEY_NONE;interrupts();return e;
}void update_keyMode() {KeyEvent e = readKeyEvent();switch (e) {case KEY_SHORT:  Serial.println("short"); incFeatureCtrSwMode(); break;case KEY_LONG:   Serial.println("long");  break;case KEY_DOUBLE: Serial.println("double");break;default: break;}
}/*----------------------------------*/
void button_init() {pinMode(KEY_PIN, INPUT_PULLUP);/* 启动 1 ms 周期性扫描 */scanTicker.attach_ms(1, scanTask);
}void button_loop() {KeyEvent e = readKeyEvent();switch (e) {case KEY_SHORT:  Serial.println("short"); break;case KEY_LONG:   Serial.println("long");  break;case KEY_DOUBLE: Serial.println("double");break;default: break;}
}

3.2.2. 手势识别paj7620 上下左右

直接使用淘宝买的模块对应的demo即可

3.2.3. OLED显示 图片, 文字

直接使用淘宝买的模块对应的demo即可

3.2.4. HC595 串行转并行

这里使用ESP32的普通引脚, 用于控制74HC595输出指定的并行结果 比如 shiftOutByte(0x0f) 即对应的八位继电器状态是 00001111

#include <Arduino.h>// 引脚定义
#define PIN_HC595_DS   D1   // 数据
#define PIN_HC595_SHCP D3   // 移位时钟
#define PIN_HC595_STCP D2   // 锁存时钟// 发送 1 字节到 74HC595
void shiftOutByte(uint8_t data) {digitalWrite(PIN_HC595_STCP, LOW);          // 准备锁存for (uint8_t i = 0; i < 8; i++) {digitalWrite(PIN_HC595_DS, (data & (0x80 >> i)) ? HIGH : LOW);digitalWrite(PIN_HC595_SHCP, LOW);digitalWrite(PIN_HC595_SHCP, HIGH);      // 上升沿移位}digitalWrite(PIN_HC595_STCP, HIGH);         // 上升沿锁存
}void hc595_init() {pinMode(PIN_HC595_DS, OUTPUT);pinMode(PIN_HC595_SHCP, OUTPUT);pinMode(PIN_HC595_STCP, OUTPUT);digitalWrite(PIN_HC595_STCP, LOW);
}

3.2.5. 消息队列mqtt

直接使用巴法云对应的demo即可

3.2.6. 巴法接入米家控制新增的多个插座设备

在这里插入图片描述

图片中左侧部分是巴法云页面, 具体怎么使用见巴法云的文档, 这里增加8个开关
关联米家里八个开关, 测试可通过米家的小爱音响控制, 看esp32收到的消息是否正确

具体代码在 include\mqtt.h

3.3. 整合上述器件, 完成预期设想

feature_control_switch.cpp

在这里插入图片描述

processon绘图链接
https://www.processon.com/view/link/68739a8e5f31900ebd96f314?cid=68738ef1d8962758cba631cd

4. 最终展示

在这里插入图片描述
通过按钮点按, 选择控制哪几个继电器, 如下所示, 比如模式FeatureCtrSwMode_S0S1 表示当前控制的是01两个开关
向左手势打开0开关, 向右打开1开关, 向上全打开, 向下全关比
以此类推, 比如 FeatureCtrSwMode_S01234567 表示当前控制01234567八个开关, 向左打开0123, 向右打开4567, 向上全开, 向下全关

enum FeatureCtrSwMode : uint8_t
{FeatureCtrSwMode_S0S1 = 0,FeatureCtrSwMode_S2S3 = 1,FeatureCtrSwMode_S4S5 = 2,FeatureCtrSwMode_S6S7 = 3,FeatureCtrSwMode_S0123,FeatureCtrSwMode_S4567,FeatureCtrSwMode_S01234567,FeatureCtrSwMode_BUTT
};const uint8_t GROUPS[7][8] = {{0, 1},{2, 3},{4, 5},{6, 7},{0, 1, 2, 3},{4, 5, 6, 7},{0, 1, 2, 3, 4, 5, 6, 7},
};String groupNames[] = {"Group0_01","Group1_23","Group2_45","Group3_67","Group4_0123","Group5_4567","Group6_01234567","Group_but"
};const uint8_t GROUP_LEN[7] = {2, 2, 2, 2, 4, 4, 8};

4.1. 实际功能描述

5. 后续计划

买一个米家的无线按钮开关
短按切换所有鱼缸过滤(当前是通过手势控制, 也准备长按设计中的按钮, 实现所有过滤的开闭, 这个很关键)

代码仓库:
https://gitee.com/nwu_zjq/iot-fish.git

http://www.dtcms.com/a/277953.html

相关文章:

  • “找到一个或多个多重定义的符号“(LNK2005 或 LNK1169)
  • JAVA 反射总结
  • 开源工具DeepFilterNet:实时语音降噪
  • SQL的初步学习(二)(以MySQL为例)
  • 第四章 城市给水排水处理厂站工程
  • 双向链表:前后遍历的艺术
  • 动态规划题解_将一个数字表示成幂的和的方案数【LeetCode】
  • 高压空气冲击炮cad【3张】三维图+设计说明书
  • Python 学习之路(十)--常见算法实现原理及解析
  • 智慧公安信息化建设解决方案PPT(63页)
  • Matlab的命令行窗口内容的记录-利用diary记录日志/保存命令窗口输出
  • 什么是 MVP?产品从0到1的关键一步
  • OSPF 基础实验
  • X00211-基于残差edge-graph注意力机制的深度强化学习优化车辆路径问题
  • HarmonyOS从入门到精通:动画设计与实现之八 - 高级动画技巧(二)
  • [Plecs基础知识系列]基于Plecs的半导体热仿真方法(实战篇)_1.建立电路模型
  • C语言基础知识--文件读写(一)
  • RAID磁盘冗余技术深度解析
  • WEB渗透
  • 【LeetCode100】--- 6.三叔之和【思维导图---复习回顾】
  • 基于Java日志平台的访问链路追踪实战
  • JAVA并发——synchronized的实现原理
  • C#特性:从元数据到框架基石的深度解析
  • 强化学习初探及OREAL实践
  • Word中的批注显示与修订显示
  • 【vs2022】 error C2338: Unicode support requires compiling with /utf-8
  • 时间的弧线,逻辑的航道——标准单元延迟(cell delay)的根与源
  • [附源码+数据库+毕业论文+答辩PPT+部署教程+配套软件]基于SpringBoot+MyBatis+MySQL+Maven+Vue实现的交流互动管理系统
  • 基于Springboot+UniApp+Ai实现模拟面试小工具四:后端项目基础框架搭建下
  • 长上下文能力:FlashAttention vs. RingAttention