NodeMCU(ESP8266)外部中断实现按键单击-双击-长按功能
2021-12-18 更新
B站网友 柳桥风起 分享了一个开源库使用效果更佳,OneButton 这个库功能更齐全,可直接使用,我个人分享的还存在bug,看看就好了,这里也贴出一段个人写的demo代码,当然更推荐的是到github上看原作者代码说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include <OneButton.h>
OneButton btn = OneButton(D3, false, false);
uint32_t clicktime = 0;
static void singleClick() { Serial.println("按键单击"); }
static void doubleClick() { Serial.println("按键双击"); }
static void longClickStart() { Serial.println("按键长按开始"); clicktime = millis(); }
static void longClick() { Serial.println("按键长按结束"); Serial.println("按键按下时间:"); Serial.print(millis()-clicktime+1000); clicktime = 0; }
void setup() { Serial.begin(115200); btn.attachClick(singleClick); btn.attachDoubleClick(doubleClick); btn.attachLongPressStop(longClick); btn.attachLongPressStart(longClickStart); }
void loop() { btn.tick(); }
|
背景
很多时候我们的设备就只有一个按键,但是我们需要的功能却比较多,所以就会围绕一个按键实现多种交互功能,单击,双击,长按可能就是最常见的几种交互了,所以我就想着用nodeMcu(esp8266)也搞一个出来,中途遇到很多的问题,为此便写下这篇笔记记录下来,分享给大家!
效果演示
实现方式
如果只是要实现按键单击功能是比较简单的,只需要读取对应的GPIO的电平信号即可,但是如果我们要实现案件双击,长按此时单纯靠读取电平信号则无法解决此问题。需要使用外部中断来处理按键的状态值。 大概思路就是根据按键按下的时间,和按键回弹的时间,来判断按键是否是第一次按压和按压两次或者长按(这里我也不想不出好的文字来描述此过程,大家看代码就懂了的)!
实现功能
- 单击切换LED显示状态
- 双击切换LED显示模式 (模式1:亮/灭 、模式2:闪烁/常亮)
- 长按超过3秒重启系统
电路图及原理
电路图和原理部分我直接搬运此文章的 ESP8266-12F 中断 ,有兴趣欢迎到原作者处查阅,我只是一个搬运工记录一下。
电路图
这里的电阻10K左右即可,大到20多K也没问题,切勿放一个小电阻,形成短路主板无法正常工作
外部中断
基于ESP8266的NodeMcu的数字IO的中断功能是通过attachInterrupt,detachInterrupt函数所支持的。
除了D0/GPIO16,中断可以绑定到任意GPIO的引脚上【D0-D10】。
所支持的标准中断类型有:
- CHANGE(改变沿,电平从低到高或者从高到低)
- RISING(上升沿,电平从低到高)
- FALLING(下降沿,电平从高到低)
attachInterrupt(pin, function, mode); 设置触发中断的引脚
- pin:要设置中断编号,注意,这里不是引脚编号
- function:中断发生时运行的函数, 这个函数不带任何参数,不返回任何内容
- Interrupt type/mode:它定义中断被触发的条件方式
- CHANGE:改变沿,引脚电平从低变为高或者从高变为低时触发中断。
- RISING:上升沿,引脚电平从低变为高时触发中断。
- FALLING:下降沿,引脚电平从高变为低时触发中断
- 返回值: 无
detachInterrupt(pin); 取消指定引脚的中断
digitalPinToInterrupt(pin);获取指定引脚的中断号
- pin:要获取中断号的GPIO引脚
- 返回值: 中断号
引脚对应的中断号:
- D1 -> 5
- D2 -> 4
- D4 -> 2
- D5 -> 14
- D6 -> 12
- D7 -> 13
- D8 -> 15
代码
代码部分参考自CSDN文章Arduino 触摸按键:实现单击,双击,长按功能,稳定无抖动。
感觉作者的代码分享,欢迎查阅原作者代码分享,我只是搬运了一下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
| #include <ESP8266WiFi.h>
int touchPin = D2;
int mode = 0;
bool isshow = false;
long touchDownTime = 0, touchUpTime = 0, firstTouchTime = 0;
bool isOne = 0, isDouble = 0;
int touchStatus = 0;
void powerMode() { if (isOne && millis() - firstTouchTime > 150) { isDouble = 0; isOne = 0; touchStatus = 1; } if (touchStatus == 1) { isshow = (isshow == 0) ? 1 : 0; } else if (touchStatus == 2) { mode = (mode == 0) ? 1 : 0; } else if (touchStatus == 3 && (touchUpTime - touchDownTime) >= 3000) { mode = 3; } touchStatus = 0; }
void lightning() { digitalWrite(LED_BUILTIN, HIGH); delay(300); digitalWrite(LED_BUILTIN, LOW); delay(300); }
void showMode0() { digitalWrite(LED_BUILTIN, isshow ? LOW : HIGH); }
void showMode1() { if (isshow == 1) { lightning(); } else { digitalWrite(LED_BUILTIN, LOW); } }
void setup() { Serial.begin(119200); pinMode(LED_BUILTIN, OUTPUT); attachInterrupt(digitalPinToInterrupt(touchPin), touchDownInterrupt, RISING); Serial.println("system is start"); }
void loop() { powerMode(); if (mode == 0) { showMode0(); } else if (mode == 1) { showMode1(); } else if (mode == 3) { ESP.restart(); } }
ICACHE_RAM_ATTR void touchDownInterrupt() { if (isOne && millis() - firstTouchTime <= 150) { isOne = 0; isDouble = 1; touchStatus = 2; } touchDownTime = millis(); attachInterrupt(digitalPinToInterrupt(touchPin), touchUpInterrupt, FALLING); }
ICACHE_RAM_ATTR void touchUpInterrupt() { touchUpTime = millis(); if ((touchUpTime - touchDownTime) > 700) { touchStatus = 3; } else if (isDouble) { isDouble = 0; } else { isOne = 1; firstTouchTime = millis(); } attachInterrupt(digitalPinToInterrupt(touchPin), touchDownInterrupt, RISING); }
|
参考文章
Arduino 触摸按键:实现单击,双击,长按功能,稳定无抖动。
ESP8266-12F 中断