ESP8266利用Blinker、小爱同学和本地按钮控制4路开关-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 11844|回复: 26

[分享] ESP8266利用Blinker、小爱同学和本地按钮控制4路开关

[复制链接]
发表于 2021-3-22 16:08 | 显示全部楼层 |阅读模式
本帖最后由 Guofuqiang01 于 2021-3-22 16:34 编辑

案例介绍:小爱同学(安卓、苹果、小爱音响均可)控制4路开关
硬件需求:ESP8266 nodeMcu模块1个,4路继电器1个(可用固态继电器),4个自复位开关(自锁开关需修改代码)
功能介绍:1、Blinker APP端控制开关
                 2、小爱同学控制开关
                 3、本地开关控制开关
                 4、App端状态刷新
目前疑问:1、状态回调函数中void miotQuery(int32_t queryCode,uint8_t num),queryCode具体是什么值?温湿度还是开关状态都反馈的是0.通过串口监视器得知查询插孔几的状态反馈就是数字几,所以增加了uint8_t num来却别查询哪个插孔状态用于反馈状态。PS:查询、控制状态一定要说“插孔1”而不可以说插座;
                 2、本地按键有什么好的代码可以让程序在1秒内只检测是否按过而不是不停的反馈按键状态。
注意事项:1、一定选择阿里云Broker
                 2、小爱同学控制一定说插孔几(可以训练小爱自定义名称)
                 3、配网请微信扫二维码

一定选阿里云Broker

一定选阿里云Broker
界面.PNG

微信扫码配网

微信扫码配网


  1. /* *****************************************************************  
  2. * @author  GuoFuqiang
  3. * @version 1.0.0
  4. * @date    2021.03.22
  5. * @brief   主程序
  6. * 程序功能:blinker+小爱同学+本地按键(点触自复位)控制插座4路插孔/开关
  7. * 1.使用开发板:nodemcu-ESP8266(模块ESP-12F)
  8. * 2.程序功能1:使用Blinker 手机APP添加设备并绑定,通过Blinker APP或者小爱同学,
  9. *   远程控制开发板相连的继电器开关
  10. * 3.程序功能2:使用本地开关控制继电器开关,并反馈继电器状态到Blinker控制按钮
  11. * 4.例程文档:小爱类:https://www.diandeng.tech/doc/xiaoai
  12. *            SmartConfig自动配网:https://blog.csdn.net/u014091490/article/details/105178037
  13. *            Blinker心跳包:https://github.com/blinker-iot/blinker-doc/wiki/Blinker-Arduino-%E5%BA%93%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C
  14. * 5.程序参考:blinker库例程--blinker-library-master\examples\Blinker_MIOT\MIOT_MULTI_OUTLET
  15. * *****************************************************************/

  16. #define BLINKER_WIFI               //支持wifi
  17. #define BLINKER_ESP_SMARTCONFIG    //Smartconfig或者手机微信扫码自动配网,代码配网请注释此条
  18. #define BLINKER_MIOT_MULTI_OUTLET  //支持多路插座,最多4个插孔
  19. #define BLINKER_PRINT Serial       //串口协议库
  20. #define BLINKER_WITHOUT_SSL        //blinker默认使用加密方式进行远程通信,但通信加密会消耗大量的RAM,如果您对通信安全性无要求
  21.                      //可以添加宏BLINKER_WITHOUT_SSL用以获得更多可用RAM,BLINKER_WITHOUT_SSL目前仅可用于ESP8266,其他设备的RAM足以进行加密通信

  22. #include <Blinker.h>               //使用第三方Blinker库

  23. char auth[] = "746f9ac0b5b3";      //Blinker APP(连接小爱同学必须阿里云服务器)中获取到的Secret Key(密钥)
  24. //代码配网用这段
  25. //char ssid[] = "Your WIFI SSID";        //自己wifi名称
  26. //char pswd[] = "********";        //自己wifi密码

  27. //定义继电器信号
  28. #define Relay_1 5         //定义继电器1信号接入GPIO5,D1
  29. #define Relay_2 4         //定义继电器2信号接入GPIO4,D2
  30. #define Relay_3 0         //定义继电器3信号接入GPIO0,D3
  31. #define Relay_4 2         //定义继电器4信号接入GPIO2,D4

  32. //定义本地开关信号
  33. #define KG_1 14         //定义开关1信号接入GPIO14,D5
  34. #define KG_2 12         //定义开关2信号接入GPIO12,D6
  35. #define KG_3 13         //定义开关3信号接入GPIO13,D7
  36. #define KG_4 3          //定义开关4信号接入GPIO3,RX(GPIO15/D8持续低电平,后更改为GPIO3/RX接口)

  37. // 新建Blinker软件组件对象
  38. BlinkerButton Button("MainSwitch");     //组件对象,要和APP组件中的“数据键名”一致,总开关
  39. BlinkerButton Button1("Switch1");       //组件对象,要和APP组件中的“数据键名”一致,开关1
  40. BlinkerButton Button2("Switch2");       //组件对象,要和APP组件中的“数据键名”一致,开关2
  41. BlinkerButton Button3("Switch3");       //组件对象,要和APP组件中的“数据键名”一致,开关3
  42. BlinkerButton Button4("Switch4");       //组件对象,要和APP组件中的“数据键名”一致,开关4
  43. BlinkerButton Button5("Refresh");       //APP端按钮状态刷新

  44. //定义插座状态,用于小爱同学状态反馈
  45. bool oState[5] = { false };
  46. #define OUTLET_ALL   0  //所有插孔
  47. #define OUTLET_NO_1  1  //插座插孔一
  48. #define OUTLET_NO_2  2  //插座插孔二
  49. #define OUTLET_NO_3  3  //插座插孔三
  50. #define OUTLET_NO_4  4  //插座插孔四

  51. //反馈继电器状态函数
  52. void RelayState(int num)
  53. {
  54.   switch(num)
  55.   {
  56.     case 1:   //插座插孔一状态
  57.         if(digitalRead(Relay_1)==LOW)
  58.         {
  59.           Button1.color("#FFFF00");   //设置app按键是纯黄色,16进制颜色码
  60.           Button1.text("插座1开");          //设置app按键注释“开”
  61.           Button1.print("on");
  62.           oState[1] = true;
  63.         }
  64.         else if(digitalRead(Relay_1==HIGH))
  65.         {
  66.           Button1.color("#808080");   //设置app按键是灰色,16进制颜色码
  67.           Button1.text("插座1关");          //设置app按键注释“关”
  68.           Button1.print("off");
  69.           oState[1] = false;
  70.         }
  71.         break;
  72.     case  2:
  73.         if(digitalRead(Relay_2)==LOW)
  74.         {
  75.           Button2.color("#FFFF00");   //设置app按键是纯黄色,16进制颜色码
  76.           Button2.text("插座2开");          //设置app按键注释“开”
  77.           Button2.print("on");
  78.           oState[2] = true;
  79.         }
  80.         else if(digitalRead(Relay_2==HIGH))
  81.         {
  82.           Button2.color("#808080");   //设置app按键是灰色,16进制颜色码
  83.           Button2.text("插座2关");          //设置app按键注释“关”
  84.           Button2.print("off");
  85.           oState[2] = false;
  86.         }
  87.         break;
  88.     case  3:
  89.         if(digitalRead(Relay_3)==LOW)
  90.         {
  91.           Button3.color("#FFFF00");   //设置app按键是纯黄色,16进制颜色码
  92.           Button3.text("插座3开");          //设置app按键注释“开”
  93.           Button3.print("on");
  94.           oState[3] = true;
  95.         }
  96.         else if(digitalRead(Relay_3==HIGH))
  97.         {
  98.           Button3.color("#808080");   //设置app按键是灰色,16进制颜色码
  99.           Button3.text("插座3关");          //设置app按键注释“关”
  100.           Button3.print("off");
  101.           oState[3] = false;
  102.         }
  103.         break;
  104.     case  4:
  105.         if(digitalRead(Relay_4)==LOW)
  106.         {
  107.           Button4.color("#FFFF00");   //设置app按键是纯黄色,16进制颜色码
  108.           Button4.text("插座4开");          //设置app按键注释“开”
  109.           Button4.print("on");
  110.           oState[4] = true;
  111.         }
  112.         else if(digitalRead(Relay_4==HIGH))
  113.         {
  114.           Button4.color("#808080");   //设置app按键是灰色,16进制颜色码
  115.           Button4.text("插座4关");          //设置app按键注释“关”
  116.           Button4.print("off");
  117.           oState[4] = false;
  118.         }
  119.         break;
  120.         default:
  121.             break;
  122.   }
  123. }

  124. //小爱同学控制插座多个插孔
  125. void ctrl_multi_outlet(uint8_t num, uint8_t io_level)
  126. {
  127.     switch (num)
  128.         {
  129.             case 0:  //所有插孔
  130.                 digitalWrite(Relay_1, io_level);//控制继电器1
  131.                 digitalWrite(Relay_2, io_level);//控制继电器2
  132.                 digitalWrite(Relay_3, io_level);//控制继电器3
  133.                 digitalWrite(Relay_4, io_level);//控制继电器4
  134.                 break;
  135.             case 1:  //插座插孔一
  136.                 digitalWrite(Relay_1, io_level);//控制继电器1
  137.                 break;
  138.             case 2:  //插座插孔二
  139.                 digitalWrite(Relay_2, io_level);//控制继电器2
  140.                 break;
  141.             case 3:  //插座插孔三
  142.                 digitalWrite(Relay_3, io_level);//控制继电器3
  143.                 break;
  144.             case 4:  //插座插孔四
  145.                 digitalWrite(Relay_4, io_level);//控制继电器4
  146.                 break;
  147.             default:
  148.                 break;
  149.         }
  150. }

  151. //小爱电源类回调,例如:“打开插座”、“打开插座插孔一”、“打开插座插孔二”
  152. void miotPowerState(const String & state, uint8_t num)
  153. {
  154.     BLINKER_LOG("need set outlet: ", num, ", power state: ", state);

  155.     if (state == BLINKER_CMD_ON)
  156.     {
  157.         ctrl_multi_outlet(num, LOW);//打开继电器,num表示是多少路(继电器低电平出发)
  158.         BlinkerMIOT.powerState("on", num);
  159.         BlinkerMIOT.print();
  160.         RelayState(num);

  161.         oState[num] = true;
  162.     }
  163.    else if (state == BLINKER_CMD_OFF)
  164.     {
  165.         ctrl_multi_outlet(num, HIGH);//关闭继电器,num表示是多少路

  166.         BlinkerMIOT.powerState("off", num);
  167.         BlinkerMIOT.print();
  168.         RelayState(num);

  169.         oState[num] = false;
  170.     }
  171. }

  172. //小爱设备查询的回调函数,查询设备状态,例如:“插座插孔一状态”
  173. void miotQuery(int32_t queryCode,uint8_t num)
  174. {
  175.     BLINKER_LOG("插孔",num,"状态",",codes:", queryCode);

  176.     switch (num)
  177.     {
  178.          case 0 :
  179.             BLINKER_LOG("状态:");
  180.             BlinkerMIOT.powerState(oState[1] ? "on" : "off");
  181.             BlinkerMIOT.powerState(oState[2] ? "on" : "off");
  182.             BlinkerMIOT.powerState(oState[3] ? "on" : "off");
  183.             BlinkerMIOT.powerState(oState[4] ? "on" : "off");
  184.             BlinkerMIOT.print();
  185.             break;
  186.          case 1 :
  187.             BLINKER_LOG("插孔1状态:");
  188.             BlinkerMIOT.powerState(oState[1] ? "on" : "off");
  189.             BlinkerMIOT.print();
  190.             break;
  191.          case 2 :
  192.             BLINKER_LOG("插孔2状态:");
  193.             BlinkerMIOT.powerState(oState[2] ? "on" : "off");
  194.             BlinkerMIOT.print();
  195.             break;
  196.          case 3 :
  197.             BLINKER_LOG("插孔3状态:");
  198.             BlinkerMIOT.powerState(oState[3] ? "on" : "off");
  199.             BlinkerMIOT.print();
  200.             break;
  201.          case 4 :
  202.             BLINKER_LOG("插孔4状态:");
  203.             BlinkerMIOT.powerState(oState[4] ? "on" : "off");
  204.             BlinkerMIOT.print();
  205.             break;
  206.          default :
  207.             BlinkerMIOT.powerState(oState[1] ? "on" : "off");
  208.             BlinkerMIOT.powerState(oState[2] ? "on" : "off");
  209.             BlinkerMIOT.powerState(oState[3] ? "on" : "off");
  210.             BlinkerMIOT.powerState(oState[4] ? "on" : "off");
  211.             BlinkerMIOT.print();
  212.             break;
  213.     }
  214. }

  215. // 在APP控制,按下MainSwitch按键即会执行该函数
  216. void button_callback(const String & state)
  217. {
  218.     BLINKER_LOG("操作了MainSwitch: ", state);//APP中的Monitor控件打印的信息
  219.     if (state=="on")
  220.     {
  221.         ctrl_multi_outlet(OUTLET_ALL, LOW);//打开继电器--所有
  222.         // 反馈继电器状态
  223.         Button.color("#FFFF00");   //设置app按键是纯黄色,16进制颜色码
  224.         Button.text("开");          //设置app按键注释“开”
  225.         Button.print("on");
  226.     } else if(state=="off")
  227.     {
  228.         ctrl_multi_outlet(OUTLET_ALL, HIGH);//关闭继电器--所有
  229.         // 反馈继电器状态
  230.         Button.color("#808080");   //设置app按键是纯黄色,16进制颜色码
  231.         Button.text("关");          //设置app按键注释“开”
  232.         Button.print("off");
  233.     }
  234. }

  235. // 在APP控制,按下Switch1按键即会执行该函数--第1路开关
  236. void button1_callback(const String & state)
  237. {
  238.     BLINKER_LOG("get button state: ", state);//APP中的Monitor控件打印的信息
  239.     if (state=="on")
  240.     {
  241.         ctrl_multi_outlet(OUTLET_NO_1, LOW);//打开继电器--第1路
  242.         // 反馈继电器1状态
  243.         RelayState(1);    //调用继电器反馈程序
  244.     } else if(state=="off")
  245.     {
  246.         ctrl_multi_outlet(OUTLET_NO_1, HIGH);//关闭继电器--第1路
  247.         // 反馈继电器状态
  248.         RelayState(1);    //调用继电器反馈程序
  249.     }
  250. }

  251. // 在APP控制,按下Switch2按键即会执行该函数--第2路开关
  252. void button2_callback(const String & state)
  253. {
  254.     BLINKER_LOG("get button state: ", state);//APP中的Monitor控件打印的信息
  255.     if (state=="on")
  256.     {
  257.         ctrl_multi_outlet(OUTLET_NO_2, LOW);//打开继电器--第2路
  258.         // 反馈继电器状态
  259.         RelayState(2);    //调用继电器反馈程序
  260.     } else if(state=="off")
  261.     {
  262.         ctrl_multi_outlet(OUTLET_NO_2, HIGH);//关闭继电器--第2路
  263.         // 反馈继电器状态
  264.         RelayState(2);    //调用继电器反馈程序
  265.     }
  266. }

  267. // 在APP控制,按下Switch3按键即会执行该函数--第3路开关
  268. void button3_callback(const String & state)
  269. {
  270.     BLINKER_LOG("get button state: ", state);//APP中的Monitor控件打印的信息
  271.     if (state=="on")
  272.     {
  273.         ctrl_multi_outlet(OUTLET_NO_3, LOW);//打开继电器--第3路
  274.         // 反馈继电器状态
  275.         RelayState(3);    //调用继电器反馈程序
  276.     } else if(state=="off")
  277.     {
  278.         ctrl_multi_outlet(OUTLET_NO_3, HIGH);//关闭继电器--第3路
  279.         // 反馈继电器状态
  280.         RelayState(3);    //调用继电器反馈程序
  281.     }
  282. }

  283. // 在APP控制,按下Switch4按键即会执行该函数--第4路开关
  284. void button4_callback(const String & state)
  285. {
  286.     BLINKER_LOG("get button state: ", state);//APP中的Monitor控件打印的信息
  287.     if (state=="on")
  288.     {
  289.         ctrl_multi_outlet(OUTLET_NO_4, LOW);//打开继电器--第4路
  290.         // 反馈继电器状态
  291.         RelayState(4);    //调用继电器反馈程序
  292.     } else if(state=="off")
  293.     {
  294.         ctrl_multi_outlet(OUTLET_NO_4, HIGH);//关闭继电器--第4路
  295.         // 反馈继电器状态
  296.         RelayState(4);    //调用继电器反馈程序
  297.     }
  298. }

  299. //APP端状态手动刷新按钮
  300. void button5_callback(const String & state)
  301. {
  302.   for(int i=0;i<5;i++)
  303.   {
  304.     RelayState(i);
  305.   }
  306. }

  307. //心跳包刷新状态
  308. void heartbeat()
  309. {
  310.   for(int i=0;i<5;i++)
  311.   {
  312.     RelayState(i);
  313.   }
  314. }

  315. //本地开关控制继电器程序
  316. void bdms()
  317. {
  318.   if(digitalRead(KG_1)==LOW)
  319.   {
  320.     digitalWrite(Relay_1, !digitalRead(Relay_1));
  321.     //反馈继电器1状态
  322.     RelayState(1);    //调用继电器反馈程序
  323.     Blinker.delay(1000);    //必须加延时,否则灯一直切换状态
  324.   }
  325.   if(digitalRead(KG_2)==LOW)
  326.   {
  327.     digitalWrite(Relay_2, !digitalRead(Relay_2));
  328.     RelayState(2);    //调用继电器反馈程序
  329.     Blinker.delay(1000);
  330.   }
  331.   if(digitalRead(KG_3)==LOW)
  332.   {
  333.     digitalWrite(Relay_3, !digitalRead(Relay_3));
  334.     RelayState(3);    //调用继电器反馈程序
  335.     Blinker.delay(1000);
  336.   }
  337. if(digitalRead(KG_4)==LOW)
  338.   {
  339.     digitalWrite(Relay_4, !digitalRead(Relay_4));
  340.     RelayState(4);    //调用继电器反馈程序
  341.     Blinker.delay(1000);
  342.   }
  343. }

  344. void setup()
  345. {
  346.     // 初始化串口,用于调试,后期可删除
  347.     Serial.begin(115200);
  348.     BLINKER_DEBUG.stream(Serial);
  349.     BLINKER_DEBUG.debugAll();
  350.    
  351.     // 初始化有继电器的IO
  352.     pinMode(Relay_1, OUTPUT);
  353.     pinMode(Relay_2, OUTPUT);
  354.     pinMode(Relay_3, OUTPUT);
  355.     pinMode(Relay_4, OUTPUT);
  356.     //初始化继电器初始状态
  357.     digitalWrite(Relay_1, HIGH);       //继电器为低电平触发,初始化为HIGH
  358.     digitalWrite(Relay_2, HIGH);
  359.     digitalWrite(Relay_3, HIGH);
  360.     digitalWrite(Relay_4, HIGH);
  361.     //初始化本地按键状态
  362.     pinMode(KG_1, INPUT_PULLUP);   //本地开关输入上拉
  363.     pinMode(KG_2, INPUT_PULLUP);   //本地开关输入上拉
  364.     pinMode(KG_3, INPUT_PULLUP);   //本地开关输入上拉
  365.     pinMode(KG_4, INPUT_PULLUP);   //本地开关输入上拉
  366.    
  367.     //初始化blinker
  368.     Blinker.begin(auth);              //手机配网用这段
  369.     //Blinker.begin(auth, ssid, pswd);//代码配网用这段
  370.     Button.attach(button_callback);   //绑定按键回调
  371.     Button1.attach(button1_callback); //绑定按键回调
  372.     Button2.attach(button2_callback); //绑定按键回调
  373.     Button3.attach(button3_callback); //绑定按键回调
  374.     Button4.attach(button4_callback); //绑定按键回调
  375.     Button5.attach(button5_callback);
  376.    
  377.     //小爱同学注册回调
  378.     BlinkerMIOT.attachPowerState(miotPowerState); //注册小爱电源回调
  379.     BlinkerMIOT.attachQuery(miotQuery);           //小爱设备查询的回调函数
  380.    
  381.     //心跳包,初始化
  382.     Blinker.attachHeartbeat(heartbeat);           //app定时向设备发送心跳包, 设备收到心跳包后会返回设备当前状态
  383. }

  384. void loop()
  385. {
  386.   bdms();           //本地开关模式
  387.   Blinker.run();
  388. }
复制代码


发表于 2021-3-23 18:06 | 显示全部楼层
感谢楼主分享,很有作用
发表于 2021-3-24 09:18 | 显示全部楼层
学习了,感谢楼主的分享
 楼主| 发表于 2021-3-30 17:41 | 显示全部楼层
对手动模式内部程序做调整:
  1. digitalWrite(Relay_1, !digitalRead(Relay_1));
  2.     //反馈继电器1状态
  3.     RelayState(1);    //调用继电器反馈程序
  4.     Blinker.delay(1000);    //必须加延时,否则灯一直切换状态
复制代码


以上部分调整为
  1. digitalWrite(Relay_1, !digitalRead(Relay_1));
  2.      Blinker.delay(500);    //必须加延时,否则灯一直切换状态
  3.     //反馈继电器1状态
  4.     RelayState(1);    //调用继电器反馈程序
复制代码


经过测试,似乎这样更有效。


保持类型的开关,本地模式代码修改为
  1.         if(digitalRead(KG_1) != lastSate[1]){
  2.                         digitalWrite(Relay_1, !digitalRead(KG_1));
  3.                         RelayState(1);
  4.                 lastSate[1] = digitalRead(KG_1);
复制代码


其中lastSate[1]需要声明全局变量。bool lastSate[4] = { LOW };
发表于 2021-4-1 16:46 来自手机 | 显示全部楼层
试了一下,用总开关打开4个插座后,再用那4个单独的可以关闭,但是再用单独开不开。
发表于 2021-4-1 16:47 来自手机 | 显示全部楼层
试了一下,用总开关打开4个插座后,再用那4个单独的分别可以关闭,但是不能再单独开开。
 楼主| 发表于 2021-4-2 18:05 | 显示全部楼层
app-1 发表于 2021-4-1 16:47
试了一下,用总开关打开4个插座后,再用那4个单独的分别可以关闭,但是不能再单独开开。 ...

要刷新状态,查看状态必须保证状态和实际一样才可以。
发表于 2021-4-15 07:03 | 显示全部楼层
感谢楼主分享  能出一个最新版代码吗? 中间改的那些 有点搞不懂了
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 09:22 , Processed in 0.091484 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表