ESP8266+Arduino实现控制【开关外设装置-记录二】

功能演示+讲解视频地址:【esp8266+arduino】WiFi+蓝牙实现无线开关功能演示(宿舍神器)_哔哩哔哩_bilibili

源码地址:基于esp8266的开关外设装置: 存放关于此装置的所有工程文件和教程资料,供大家一起学习

篇幅太长不好看,新开一篇接着记录

目录

9、按键 测试

10、EEPROM 测试

11、整合全部模块


9、按键 测试

功能:项目中作为舵机角度初始化设置的输入:+ 键 角度加,- 键 角度减;长按 +键 确认角度,长按 -键 重新进行角度设置。

TestKey:(中断方式,无法消抖)


#define PLUS 5         //D1 +
#define SUB 4          //D2 -

//声明中断服务函数   外部中断配置
void ICACHE_RAM_ATTR highInterrupt_plus(); 
void ICACHE_RAM_ATTR highInterrupt_sub(); 

//中断服务函数
void highInterrupt_plus() { 
  Serial.println(F("plus"));
  digitalWrite(D5,!digitalRead(D5));// 改变LED的点亮或者熄灭状态
}
void highInterrupt_sub() {  
  Serial.println(F("sub"));
  digitalWrite(D5,!digitalRead(D5));// 改变LED的点亮或者熄灭状态
}

void setup() {
  // Start Serial
  Serial.begin(115200);
  
  //prepare interupt  设置外置按钮管脚为上拉输入模式
  pinMode(PLUS,INPUT_PULLUP);
  pinMode(SUB,INPUT_PULLUP);
  pinMode(D5, OUTPUT); //设置D5引脚为输出模式以便控制LED

  // 设置外置按钮管脚中断为上升沿触发模式
  attachInterrupt(PLUS, highInterrupt_plus, RISING);
  attachInterrupt(SUB, highInterrupt_sub, RISING);
}

void loop() {
  
}

一般按键的消抖则是通过延时一定的时间来实现。但对于一个定时的系统来说,延时消抖的做法破坏了程序的同步性,是系统中的一个不确定因素。我找到下面的链接,里面提供了一个优化对按键消抖的设计,同时实现了短按、长按按键的功能。

长按,短按参考:《ESP8266学习笔记》之 采用定时器内的按键扫描方法,摒弃传统的延时按键消抖_慕容流年的博客-CSDN博客_esp8266sdk按键消抖程序

TestKeyTick:

本按键检测程序的优化是通过定时器来实现的。首先开启一个1毫秒的定时器,然后在1ms的定时周期内检测按键中断。当按键触发外部中断的时候,系统将按键的变量值加1。按键弹起后将检测变量值乘以1毫秒的定时周期,就能得到按键被按下的总时间,同时如果按键松开就清零按键的变量值。根据按的时间设置长按、短按的阈值,便可实现短按、长按的功能。同时可以设置一个抖动的时间阈值来通过程序消除按键的抖动。按键检测的整体实现流程图如下:

ESP8266+Arduino实现控制【开关外设装置-记录二】_第1张图片


#include 
#include 

#define PLUS D1         // +
#define SUB  D2         // -

//读按键值
#define KEYP      digitalRead(PLUS)
#define KEYS      digitalRead(SUB)

#define short_press_time 10     //短按时间
#define long_press_time 1000    //长按时间

#define NO_KEY_PRES     0   //无按键按下
 
#define PLUS_KEY_PRES     1
#define SUB_KEY_PRES      2
 
#define PLUS_KEY_LONG_PRES     11
#define SUB_KEY_LONG_PRES          22

bool KEYP_UP = 0;//按键状态,0为弹起,1为按下
bool KEYS_UP = 0;//按键状态,0为弹起,1为按下

uint16_t KEYP_PRESS_COUNT = 0;//按KEYU时间计数
uint16_t KEYS_PRESS_COUNT = 0;//按KEYM时间计数

uint8_t KEYP_KEY_READ = NO_KEY_PRES;//KEYU按键状态
uint8_t KEYS_KEY_READ = NO_KEY_PRES;//KEYM按键状态

bool PLUS_INIT = 0; //舵机角度调整,0为不工作,1为工作
bool SUB_INIT = 0;  //舵机角度调整,0为不工作,1为工作
bool ACK = 1;  //舵机角度确定,0为确定,1为不确定

Ticker key_tick;
Servo myServo;  // 定义Servo对象来控制
int pos = 90;    // 角度存储变量

void flip() {
  //PLUS_KEY
  if(KEYP==0){
    KEYP_PRESS_COUNT++;
    KEYP_UP = 1;
    if(KEYP_PRESS_COUNT<=short_press_time)
      KEYP_KEY_READ = NO_KEY_PRES;
    if(KEYP_PRESS_COUNT>=short_press_time&&KEYP_PRESS_COUNT<=long_press_time)
      KEYP_KEY_READ = PLUS_KEY_PRES;
    if(KEYP_PRESS_COUNT>=long_press_time)
      KEYP_KEY_READ = PLUS_KEY_LONG_PRES;
  }
  if(KEYP){
    KEYP_PRESS_COUNT = 0;
    KEYP_UP = 0;//按键状态,0为弹起,1为按下
  }
  //SUB_KEY
  if(KEYS==0){
    KEYS_PRESS_COUNT++;
    KEYS_UP = 1;
    if(KEYS_PRESS_COUNT<=short_press_time)
      KEYS_KEY_READ = NO_KEY_PRES;
    if(KEYS_PRESS_COUNT>=short_press_time&&KEYS_PRESS_COUNT<=long_press_time)
      KEYS_KEY_READ = SUB_KEY_PRES;
    if(KEYS_PRESS_COUNT>=long_press_time)
      KEYS_KEY_READ = SUB_KEY_LONG_PRES;
  }
  if(KEYS){
    KEYS_PRESS_COUNT = 0;
    KEYS_UP = 0;//按键状态,0为弹起,1为按下
  }


  if(KEYP_KEY_READ == PLUS_KEY_LONG_PRES && KEYP_UP == 0){
    Serial.println("--KEY_PLUS长按");
    ACK = 0; //确认舵机角度
    KEYP_KEY_READ = NO_KEY_PRES;
  }
  if(KEYP_KEY_READ == PLUS_KEY_PRES && KEYP_UP == 0){
    Serial.println("--KEY_PLUS短按");
    PLUS_INIT = 1; //舵机角度调整工作
    KEYP_KEY_READ = NO_KEY_PRES;
  }
 
  if(KEYS_KEY_READ == SUB_KEY_LONG_PRES && KEYS_UP == 0){
    Serial.println("--KEY_SUB长按");
    KEYS_KEY_READ = NO_KEY_PRES;
    ACK = 1;  //不确认舵机角度
    pos = 90;  //角度归中
    initServo();
  }
  if(KEYS_KEY_READ == SUB_KEY_PRES && KEYS_UP == 0){
    Serial.println("--KEY_SUB短按");
    SUB_INIT = 1; //舵机角度调整工作
    KEYS_KEY_READ = NO_KEY_PRES;
  }

}

void initServo(){
  Serial.println("舵机角度初始化");
  while(ACK){
    if(PLUS_INIT){
      pos += 10;
      PLUS_INIT = 0;
    }
    if(SUB_INIT){
      pos -= 10;
      SUB_INIT = 0;
    }
    myServo.write(pos);              // 舵机角度写入
    delay(100);
  }
  Serial.println("舵机角度调整结束");
  Serial.println(pos);
  myServo.write(90);              // 舵机角度写入
  delay(100);
}

void setup() {
  // Start Serial
  Serial.begin(115200);
  delay(100);

  myServo.attach(12); //D6 
  myServo.write(pos);              // 舵机角度写入
  delay(50);
  
  //prepare interupt  设置外置按钮管脚为上拉输入模式
  pinMode(PLUS,INPUT_PULLUP);
  pinMode(SUB,INPUT_PULLUP);
  pinMode(D5, OUTPUT); //设置D5引脚为输出模式以便控制LED

  key_tick.attach_ms(1, flip);//按键检测定时器(1ms)

  initServo();
}

void loop() {

}

10、EEPROM 测试

功能:实现舵机角度写入,断电不丢失数据

参考:(基于Ardunio)ESP8266之使用EEPROM_许沐白的博客-CSDN博客_esp8266 eeprom

writeEEPROM:(用来写0)

nclude  

byte byte1=0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200); 
  Serial.println("");   
  EEPROM.begin(1024);                  //开启EEPROM,开辟1024个位空间  

  //读取与保存byte类型
  EEPROM.write(0,byte1);              //给EEPROM 第0位,写入byte_1的值
  EEPROM.commit();
  byte byte2=EEPROM.read(0);
  Serial.print("byte2的值:"); 
  Serial.println(byte2);
}

void loop() {
  // put your main code here, to run repeatedly:

}

11、整合全部模块

整合起来的程序实现的就是演示视频里的功能,下图是整个开关外设装置的系统框图:

ESP8266+Arduino实现控制【开关外设装置-记录二】_第2张图片

下图是整个装置的系统流程图:

ESP8266+Arduino实现控制【开关外设装置-记录二】_第3张图片

除了软件还有硬件,下面是硬件电路图:

ESP8266+Arduino实现控制【开关外设装置-记录二】_第4张图片

关键是要找到对应的端口,根据需求,下表是本装置的引脚对应图,图上的线色是我连接使用的杜邦线的颜色(实际要看各位的需求!!!这个只是我的装置的线色)

这里也反应出一个问题就是之前说的引脚坑的问题,大家看着可能会比较乱

ESP8266+Arduino实现控制【开关外设装置-记录二】_第5张图片

testAll:


#include 
#include 
#include 
#include 
#include 
#include 
#include 

//-------------------------------- 关于按键的一些定义-----------------------------
#define PLUS D1         // +
#define SUB  D2         // -

//读按键值
#define KEYP      digitalRead(PLUS)
#define KEYS      digitalRead(SUB)

#define short_press_time 10     //短按时间
#define long_press_time 1000    //长按时间

#define NO_KEY_PRES       0   //无按键按下
 
#define PLUS_KEY_PRES     1
#define SUB_KEY_PRES      2
 
#define PLUS_KEY_LONG_PRES     11
#define SUB_KEY_LONG_PRES      22

//-------------------------------  页面代码  ----------------------------------------
// 首页
const char* page_html = "\
\r\n\
\r\n\
\r\n\
  \r\n\
  \r\n\
  Switch\r\n\
  \r\n\
\r\n\
\r\n\
  
\r\n\
\r\n\
\r\n\ \r\n\ \r\n\ "; // 操作成功页 const char* page_success_html = "\ \r\n\ \r\n\ \r\n\ \r\n\ \r\n\ Switch\r\n\ \r\n\ \r\n\ \r\n\
\r\n\
\r\n\
\r\n\ \r\n\ \r\n\ "; //------------------------------- 环境配置 ---------------------------------------- const char* AP_SSID = "Switch"; // 这里为AP名字--AP账号 const char* AP_PSW = "12345678"; // 这里为AP密码 8位以上 const byte DNS_PORT = 53; // DNS端口号 bool ledBlinkFlag = 0; // 低电压LED闪烁标志位 bool switchStatus = 0; // 当前开关转态,默认为0 bool KEYP_UP = 0; //按键状态,0为弹起,1为按下 bool KEYS_UP = 0; //按键状态,0为弹起,1为按下 uint16_t KEYP_PRESS_COUNT = 0; //按KEYU时间计数 uint16_t KEYS_PRESS_COUNT = 0; //按KEYM时间计数 uint8_t KEYP_KEY_READ = NO_KEY_PRES;//KEYU按键状态 uint8_t KEYS_KEY_READ = NO_KEY_PRES;//KEYM按键状态 bool PLUS_INIT = 0; //舵机角度调整,0为不工作,1为工作 bool SUB_INIT = 0; //舵机角度调整,0为不工作,1为工作 bool ACK = 1; //舵机角度确定,0为确定,1为不确定 IPAddress apIP(192, 168, 4, 1); // esp8266-AP-IP地址 DNSServer dnsServer; // 创建dnsServer实例 ESP8266WebServer server(80); // 创建WebServer SoftwareSerial btSerial(D7, D8); // 串口2: Rx,Tx Servo myServo; // 定义Servo对象 Ticker adcTick; // 电压检测定时器 Ticker ledTick; // LED闪烁定时器 Ticker keyTick; // 按键检测定时器 int pos = 90; // 角度存储变量 int posChange = 0; // 改变的角度值 //------------------------------- 网页请求处理 ---------------------------------------- void handleRoot() {//访问主页回调函数 server.send(200, "text/html", page_html); } //Post回调函数:处理LED控制请求 void handleRootPost() { //digitalWrite(D5,!digitalRead(D5));// 改变LED的点亮或者熄灭状态 servoService(); //server.sendHeader("Location",page_success_html);// 跳转回页面根目录 server.send(200, "text/html", page_success_html); server.send(303); // 发送Http相应代码303 跳转 Serial.println("/SWITCH 请求"); } void handleRootSuccess() {//访问主页回调函数 server.send(200, "text/html", page_html); Serial.println("/SUCCESS请求"); } //------------------------------- 配置函数 ---------------------------------------- //基础初始化 void initBasic(void){ delay(1000); Serial.begin(115200); // 串口波特率 btSerial.begin(9600); // 蓝牙波特率 EEPROM.begin(1024); // 开启EEPROM,开辟1024个位空间 pinMode(D5, OUTPUT); // 绿灯 pinMode(D3, OUTPUT); // 黄灯 Serial.println("Started..."); btSerial.write("start"); myServo.attach(12); // D6 myServo.write(pos); // 舵机角度写入 delay(50); //prepare interupt 设置外置按钮管脚为上拉输入模式 pinMode(PLUS,INPUT_PULLUP); pinMode(SUB,INPUT_PULLUP); digitalWrite(D5,LOW); // 绿灯熄灭 ledBlinkFlag = 1; // 黄灯闪烁 adcTick.attach(5, adcService); //电压检测定时器(每5秒检测一次) ledTick.attach_ms(250, ledBlink);//led闪烁定时器(每250毫秒闪烁一次) keyTick.attach_ms(1, flip); //按键检测定时器(每1毫秒检测一次) int eeprom_pos=EEPROM.read(0); Serial.print("eeprom_pos="); Serial.println(eeprom_pos); pos=eeprom_pos; posChange = abs(pos-90); // 舵机初始化,rom中有值就无需进行舵机初始化 if(eeprom_pos<60||eeprom_pos>130){ pos=90; initServo(); } } //初始化AP模式 void initSoftAP(void){ WiFi.mode(WIFI_AP); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); if(WiFi.softAP(AP_SSID,AP_PSW)){ Serial.println("ESP8266 SoftAP is right"); } } //初始化WebServer void initWebServer(void){ /*必须在第二个参数上添加上HTTP_GET才能不影响强制门户,防止有些设备无法弹出强制门户,要用域名访问, 如果不加第二个参数,就只能实现域名访问而无法强制门户 在无法响应的http请求响应回调设置为主页的回调函数,才可以强制门户*/ server.on("/", HTTP_GET, handleRoot);//设置主页回调函数 server.onNotFound(handleRoot);//设置无法响应的http请求的回调函数 server.on("/SWITCH", HTTP_POST, handleRootPost);//设置Post请求回调函数 server.on("/SUCCESS", HTTP_POST, handleRootSuccess);//设置Post请求回调函数 server.begin();//启动WebServer Serial.println("WebServer started!"); } //初始化DNS void initDNS(void){//初始化DNS服务器 if(dnsServer.start(DNS_PORT, "*", apIP)){//判断将所有地址映射到esp8266的ip上是否成功 Serial.println("start dnsserver success."); } else Serial.println("start dnsserver failed."); } //舵机角度初始化 void initServo(){ Serial.println("舵机角度初始化"); while(ACK){ if(PLUS_INIT){ pos += 10; PLUS_INIT = 0; } if(SUB_INIT){ pos -= 10; SUB_INIT = 0; } myServo.write(pos); // 舵机角度写入 delay(100); } //读取与保存byte类型 EEPROM.write(0,pos); //给EEPROM 第0位,写入byte_1的值 EEPROM.commit(); int eeprom_pos=EEPROM.read(0); digitalWrite(D5,HIGH); // 绿灯亮 Serial.println("舵机角度调整结束"); Serial.println(pos); Serial.print("new_eeprom_pos="); Serial.println(eeprom_pos); posChange = abs(pos-90); myServo.write(90); // 舵机角度写入 delay(100); } //WiFi环境初始化 void connectNewWifi(void){ WiFi.setAutoConnect(true); //设置自动连接(经过测试,强制门户必须要加上这一句) //WiFi.begin(); //(经过测试,强制门户必须要加上这一句,我并没有找到原因) int count = 0; while (WiFi.status() != WL_CONNECTED) { delay(500); count++; if(count > 10){//如果5秒内没有连上,就开启Web配网 可适当调整这个时间 initSoftAP(); initWebServer(); initDNS(); break;//跳出 防止无限初始化 } Serial.print("."); } ledBlinkFlag = 0; // 初始化结束,黄灯停止闪烁 digitalWrite(D3,LOW); // 黄灯灭 digitalWrite(D5,HIGH); // 绿灯亮 } //------------------------------- 服务函数 ---------------------------------------- //蓝牙服务 void bluetoothService(){ if (btSerial.available() > 0) { // check if bluetooth module sends some data to esp8266 char data = btSerial.read(); // read the data from bluetooth switch (data) { case 'C': // if receive data is 'C'(change) //digitalWrite(D5,!digitalRead(D5)); // 改变LED的点亮或者熄灭状态 servoService(); Serial.println("bluetooth chances the status"); break; default: break; } } } //LED闪烁服务 void ledBlink(){ if(ledBlinkFlag){ digitalWrite(D3,!digitalRead(D3));// 黄灯闪烁 } } //ADC服务 void adcService(){ //输出0-1023 对应 外部输入电压 0-1.0v int adcValues = analogRead(A0); Serial.print("ADC Value: "); Serial.println(adcValues); if (adcValues < 300){ ledBlinkFlag = 1; digitalWrite(D5,LOW); // 绿灯熄灭 } } //按键服务 void flip() { //PLUS_KEY if(KEYP==0){ KEYP_PRESS_COUNT++; KEYP_UP = 1; if(KEYP_PRESS_COUNT<=short_press_time) KEYP_KEY_READ = NO_KEY_PRES; if(KEYP_PRESS_COUNT>=short_press_time&&KEYP_PRESS_COUNT<=long_press_time) KEYP_KEY_READ = PLUS_KEY_PRES; if(KEYP_PRESS_COUNT>=long_press_time) KEYP_KEY_READ = PLUS_KEY_LONG_PRES; } if(KEYP){ KEYP_PRESS_COUNT = 0; KEYP_UP = 0;//按键状态,0为弹起,1为按下 } //SUB_KEY if(KEYS==0){ KEYS_PRESS_COUNT++; KEYS_UP = 1; if(KEYS_PRESS_COUNT<=short_press_time) KEYS_KEY_READ = NO_KEY_PRES; if(KEYS_PRESS_COUNT>=short_press_time&&KEYS_PRESS_COUNT<=long_press_time) KEYS_KEY_READ = SUB_KEY_PRES; if(KEYS_PRESS_COUNT>=long_press_time) KEYS_KEY_READ = SUB_KEY_LONG_PRES; } if(KEYS){ KEYS_PRESS_COUNT = 0; KEYS_UP = 0;//按键状态,0为弹起,1为按下 } if(KEYP_KEY_READ == PLUS_KEY_LONG_PRES && KEYP_UP == 0){ Serial.println("--KEY_PLUS长按"); ACK = 0; //确认舵机角度 KEYP_KEY_READ = NO_KEY_PRES; } if(KEYP_KEY_READ == PLUS_KEY_PRES && KEYP_UP == 0){ Serial.println("--KEY_PLUS短按"); PLUS_INIT = 1; //舵机角度调整工作 KEYP_KEY_READ = NO_KEY_PRES; } if(KEYS_KEY_READ == SUB_KEY_LONG_PRES && KEYS_UP == 0){ Serial.println("--KEY_SUB长按"); KEYS_KEY_READ = NO_KEY_PRES; ACK = 1; //不确认舵机角度 pos = 90; //角度归中 initServo(); } if(KEYS_KEY_READ == SUB_KEY_PRES && KEYS_UP == 0){ Serial.println("--KEY_SUB短按"); SUB_INIT = 1; //舵机角度调整工作 KEYS_KEY_READ = NO_KEY_PRES; } } //舵机服务 void servoService(){ if(switchStatus){ switchStatus = 0; myServo.write(90+posChange); // 舵机角度写入 delay(100); } else{ switchStatus = 1; myServo.write(90-posChange); // 舵机角度写入 delay(100); } Serial.println("开关操作完成"); myServo.write(90); // 舵机角度写入 delay(100); } //------------------------------- setup(配置函数) 只执行一次 ---------------------------------------- void setup() { initBasic(); connectNewWifi(); } //------------------------------- loop(主函数) 一直执行------------------------------------------- void loop() { server.handleClient(); //wifi服务 dnsServer.processNextRequest(); //DNS服务 bluetoothService(); //蓝牙服务 }

你可能感兴趣的:(折腾记录,物联网,arduino,嵌入式)