NodeMCU(ESP8266)按鍵中斷實(shí)現(xiàn)單擊-雙擊-長(zhǎng)按功能

2021-12-18 更新

B站網(wǎng)友 柳橋風(fēng)起 分享了一個(gè)開源庫(kù)使用效果更佳阅悍,OneButton 這個(gè)庫(kù)功能更齊全,可直接使用艇潭,我個(gè)人分享的還存在bug回还,看看就好了裆泳,這里也貼出一段個(gè)人寫的demo代碼,當(dāng)然更推薦的是到github上看原作者代碼說明


#include <OneButton.h>

OneButton btn = OneButton(D3, false, false);

// 記錄按鍵按下時(shí)間
uint32_t clicktime = 0;

/**
 * 處理單擊
 */
static void singleClick() {
  Serial.println("按鍵單擊");
}

/**
 * 處理雙擊
 */
static void doubleClick() {
  Serial.println("按鍵雙擊");
}

/**
 * 按鍵長(zhǎng)按開始做的事情
 */
static void longClickStart() {
  Serial.println("按鍵長(zhǎng)按開始");
  clicktime = millis();
}

/**
 * 處理按鍵長(zhǎng)按
 */
static void longClick() {
  Serial.println("按鍵長(zhǎng)按結(jié)束");
  Serial.println("按鍵按下時(shí)間:");
  // 這里為啥加1000柠硕? 因?yàn)榘存I長(zhǎng)按開始時(shí)間時(shí)按下一秒后開始計(jì)算的工禾,所以就要加上我們?cè)疽呀?jīng)按下的一秒種
  Serial.print(millis()-clicktime+1000);
  // 重置按下時(shí)間
  clicktime = 0;
}

void setup() {
  Serial.begin(115200);
  // 添加單擊事件函數(shù)
  btn.attachClick(singleClick);
  // 添加雙擊事件函數(shù)
  btn.attachDoubleClick(doubleClick);
  // 添加長(zhǎng)按事件函數(shù)
  btn.attachLongPressStop(longClick);
  // 添加按下事件函數(shù)
  btn.attachLongPressStart(longClickStart);
}

void loop() {
  btn.tick();
}

背景

很多時(shí)候我們的設(shè)備就只有一個(gè)按鍵运提,但是我們需要的功能卻比較多,所以就會(huì)圍繞一個(gè)按鍵實(shí)現(xiàn)多種交互功能闻葵,單擊民泵,雙擊,長(zhǎng)按可能就是最常見的幾種交互了槽畔,所以我就想著用nodeMcu(esp8266)也搞一個(gè)出來栈妆,中途遇到很多的問題,為此便寫下這篇筆記記錄下來厢钧,分享給大家鳞尔!

實(shí)現(xiàn)方式

如果只是要實(shí)現(xiàn)按鍵單擊功能是比較簡(jiǎn)單的,只需要讀取對(duì)應(yīng)的GPIO的電平信號(hào)即可早直,但是如果我們要實(shí)現(xiàn)案件雙擊寥假,長(zhǎng)按此時(shí)單純靠讀取電平信號(hào)則無法解決此問題。需要使用外部中斷來處理按鍵的狀態(tài)值霞扬。 大概思路就是根據(jù)按鍵按下的時(shí)間糕韧,和按鍵回彈的時(shí)間,來判斷按鍵是否是第一次按壓和按壓兩次或者長(zhǎng)按(這里我也不想不出好的文字來描述此過程喻圃,大家看代碼就懂了的)兔沃!

實(shí)現(xiàn)功能

  1. 單擊切換LED顯示狀態(tài)
  2. 雙擊切換LED顯示模式 (模式1:亮/滅 、模式2:閃爍/常亮)
  3. 長(zhǎng)按超過3秒重啟系統(tǒng)

電路圖及原理

電路圖和原理部分我直接搬運(yùn)此文章的 ESP8266-12F 中斷 级及,有興趣歡迎到原作者處查閱乒疏,我只是一個(gè)搬運(yùn)工記錄一下。

電路圖

電路圖

這里的電阻10K左右即可饮焦,大到20多K也沒問題怕吴,切勿放一個(gè)小電阻,形成短路主板無法正常工作

外部中斷

基于ESP8266的NodeMcu的數(shù)字IO的中斷功能是通過attachInterrupt县踢,detachInterrupt函數(shù)所支持的转绷。
除了D0/GPIO16,中斷可以綁定到任意GPIO的引腳上【D0-D10】硼啤。
所支持的標(biāo)準(zhǔn)中斷類型有:

  • CHANGE(改變沿议经,電平從低到高或者從高到低)
  • RISING(上升沿,電平從低到高)
  • FALLING(下降沿谴返,電平從高到低)

attachInterrupt(pin, function, mode); 設(shè)置觸發(fā)中斷的引腳

  • pin:要設(shè)置中斷編號(hào)煞肾,注意,這里不是引腳編號(hào)
  • function:中斷發(fā)生時(shí)運(yùn)行的函數(shù), 這個(gè)函數(shù)不帶任何參數(shù)嗓袱,不返回任何內(nèi)容
  • Interrupt type/mode:它定義中斷被觸發(fā)的條件方式
    • CHANGE:改變沿籍救,引腳電平從低變?yōu)楦呋蛘邚母咦優(yōu)榈蜁r(shí)觸發(fā)中斷。
    • RISING:上升沿渠抹,引腳電平從低變?yōu)楦邥r(shí)觸發(fā)中斷蝙昙。
    • FALLING:下降沿闪萄,引腳電平從高變?yōu)榈蜁r(shí)觸發(fā)中斷
  • 返回值: 無

detachInterrupt(pin); 取消指定引腳的中斷

  • pin:中斷號(hào)
  • 返回值: 無

digitalPinToInterrupt(pin)奇颠;獲取指定引腳的中斷號(hào)

  • pin:要獲取中斷號(hào)的GPIO引腳
  • 返回值: 中斷號(hào)

引腳對(duì)應(yīng)的中斷號(hào):

  • D1 -> 5
  • D2 -> 4
  • D4 -> 2
  • D5 -> 14
  • D6 -> 12
  • D7 -> 13
  • D8 -> 15

代碼

代碼部分參考自CSDN文章Arduino 觸摸按鍵:實(shí)現(xiàn)單擊败去,雙擊,長(zhǎng)按功能烈拒,穩(wěn)定無抖動(dòng)为迈。
感覺作者的代碼分享,歡迎查閱原作者代碼分享缺菌,我只是搬運(yùn)了一下代碼


#include <ESP8266WiFi.h>

// 按鍵設(shè)置在D8(gpio15)引腳
int touchPin = D2;
// 模式:0:LED亮和滅   1. LED常亮或閃爍    2. 重啟系統(tǒng)
int mode = 0;
// 模式0:亮/滅   模式1: 常亮/閃爍
bool isshow = false;
// 按鍵按下去的時(shí)間     按鍵按起來的時(shí)間    第一次按按鍵的時(shí)間
long touchDownTime = 0, touchUpTime = 0, firstTouchTime = 0;
// 是否單擊   是否雙擊
bool isOne = 0, isDouble = 0;
// 按鍵狀態(tài) 0:無任何操作葫辐,1:?jiǎn)螜C(jī)  2: 雙擊   3:長(zhǎng)按
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;
  }
  // 如果是長(zhǎng)按,且按下到按起時(shí)間超過五秒伴郁,則重啟系統(tǒng)
  else if (touchStatus == 3 && (touchUpTime - touchDownTime) >= 3000)
  {
    mode = 3;
  }
  // 重置按鍵狀態(tài)
  touchStatus = 0;
}

/**
 * LED閃爍
 */
void lightning()
{
  // 每三百毫秒閃一次LED
  digitalWrite(LED_BUILTIN, HIGH);
  delay(300);
  digitalWrite(LED_BUILTIN, LOW);
  delay(300);
}

/**
 * 顯示模式0
 */
void showMode0()
{
  digitalWrite(LED_BUILTIN, isshow ? LOW : HIGH);
}

/**
 * 顯示模式1
 */
void showMode1()
{
  if (isshow == 1)
  {
    lightning();
  }
  else
  {
    digitalWrite(LED_BUILTIN, LOW);
  }
}

void setup()
{
  //打開串口
  Serial.begin(119200);
  // 使用板載的LED燈來顯示
  pinMode(LED_BUILTIN, OUTPUT);
  // 設(shè)置按鍵中斷(上升沿耿战,引腳電平從低變?yōu)楦邥r(shí)觸發(fā)中斷。)
  attachInterrupt(digitalPinToInterrupt(touchPin), touchDownInterrupt, RISING);
  Serial.println("system is start");
}

void loop()
{
  powerMode();
  if (mode == 0)
  {
    // 模式0
    showMode0();
  }
  else if (mode == 1)
  {
    // 模式1
    showMode1();
  }
  else if (mode == 3)
  {
    // 重啟系統(tǒng)
    ESP.restart();
  }
}

/**
 * 按鍵按下去的中斷
 * 
 */
ICACHE_RAM_ATTR void touchDownInterrupt()
{
  // 如果已經(jīng)按下過一次焊傅,且第二次按下的時(shí)間不超過150ms則表示雙擊
  if (isOne && millis() - firstTouchTime <= 150)
  {
    isOne = 0;
    isDouble = 1;
    touchStatus = 2;
  }
  // 記錄按下的時(shí)間
  touchDownTime = millis();
  attachInterrupt(digitalPinToInterrupt(touchPin), touchUpInterrupt, FALLING);
}

/**
 * 按鍵彈起來的中斷
 * 
 */
ICACHE_RAM_ATTR void touchUpInterrupt()
{
  // 記錄按鍵按起時(shí)間
  touchUpTime = millis();
  // 按鍵按下到按起時(shí)間超過700毫秒則表示處于長(zhǎng)按狀態(tài)
  if ((touchUpTime - touchDownTime) > 700)
  {
    touchStatus = 3;
  }
  else if (isDouble)
  {
    isDouble = 0;
  }
  else
  {
    isOne = 1;
    firstTouchTime = millis();
  }
  attachInterrupt(digitalPinToInterrupt(touchPin), touchDownInterrupt, RISING);
}

參考文章

Arduino 觸摸按鍵:實(shí)現(xiàn)單擊剂陡,雙擊,長(zhǎng)按功能狐胎,穩(wěn)定無抖動(dòng)鸭栖。
ESP8266-12F 中斷

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市握巢,隨后出現(xiàn)的幾起案子晕鹊,更是在濱河造成了極大的恐慌,老刑警劉巖暴浦,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件溅话,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡歌焦,警方通過查閱死者的電腦和手機(jī)飞几,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來独撇,“玉大人屑墨,你說我怎么就攤上這事》紫常” “怎么了卵史?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)关炼。 經(jīng)常有香客問我程腹,道長(zhǎng),這世上最難降的妖魔是什么儒拂? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任寸潦,我火速辦了婚禮,結(jié)果婚禮上社痛,老公的妹妹穿的比我還像新娘见转。我一直安慰自己,他們只是感情好蒜哀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布斩箫。 她就那樣靜靜地躺著,像睡著了一般撵儿。 火紅的嫁衣襯著肌膚如雪乘客。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天淀歇,我揣著相機(jī)與錄音易核,去河邊找鬼。 笑死浪默,一個(gè)胖子當(dāng)著我的面吹牛牡直,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纳决,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼碰逸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了阔加?” 一聲冷哼從身側(cè)響起饵史,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胜榔,沒想到半個(gè)月后约急,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苗分,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年厌蔽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摔癣。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奴饮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出择浊,到底是詐尸還是另有隱情戴卜,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布琢岩,位于F島的核電站投剥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏担孔。R本人自食惡果不足惜江锨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一吃警、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧啄育,春花似錦酌心、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至氓英,卻和暖如春侯勉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铝阐。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工址貌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人饰迹。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓芳誓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親啊鸭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锹淌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容