数码之家

 找回密码
 立即注册
搜索
查看: 4730|回复: 18

[Arduino] 基于ESP8266的WiFi排插接入贝壳互联实现天猫精灵控制

[复制链接]
发表于 2020-1-8 00:38:11 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 hzy3774 于 2020-1-8 00:45 编辑

* 实验的对象是一个基于ESP8266的WiFi排插,目标是通过Arduino编程实现天猫精灵语音控制排插每个插孔的通断电功能。
* 编写程序主要需关心的硬件配置是:ESP-12模块x1,轻触按键x1,220V插口x4,USB接口x2,Wifi信号LEDx1,电源LEDx1

* 侧面加装了USB接口方便下载调试程序,过程可以参考另一个帖子:基于ESP8266的WiFi排插拆机加装USB调试板,改造成Arduino开发板



* 实物如图

DSC04258.JPG


* 先来看看最终效果吧,一看就明白怎么回事了。点击观看视频


* 模块与IO连接如图:
微信图片编辑_20200107233554.jpg



* 下面开始写程序,首先是使用WifiManager库配网:
  1. // 设置Wifi
  2.   WiFiManager wifiManager;
  3.   wifiManager.autoConnect();
复制代码


* 之后是来初始化中断,因为插板上有一个按钮,使用中断来处理按钮事件,处理的逻辑是:如果插座全关的情况下按下按钮,则变为全开,否则就变为全关。
  1. // 中断响应
  2. ICACHE_RAM_ATTR void onKeyClick() {
  3.   // 全关的状态全部打开,否则都是全关闭
  4.   if ((portStatus & 0B1111000) == 0B1111000) {
  5.     portStatus &= 0B0000111;
  6.   } else {
  7.     portStatus |= 0B1111000;
  8.   }
  9.   refreshOutput();
  10. }

  11. void initPanelKey() {
  12.   attachInterrupt(digitalPinToInterrupt(5), onKeyClick, FALLING);
  13. }
复制代码


* 我们使用了一个变量portStatus来表示用到的所有插孔和LED的开关状态,这样我们只要修改portStatus,再调用refreshOutput()就可以改变所有输出IO状态:
  1. //7个端口分别对应 K1, K2, K3, K4,USB,LED_WIFI, LED_ROUND
  2. char swPorts[portNum] = {14, 12, 13, 4, 16, 0, 3};

  3. //除了USB充电口,其他都是低电平有效
  4. int portStatus = 0B0000110; //7个IO开关

  5. //======================================

  6. // 初始化插板
  7. void initOutputMode() {
  8.   for (int i = 0; i < portNum; i++) {
  9.     pinMode(swPorts[i], OUTPUT);
  10.   }
  11. }

  12. // 根据开关状态刷新IO电平
  13. void refreshOutput() {
  14.   int c = 1;
  15.   for (int i = portNum - 1; i >= 0; i--) {
  16.     digitalWrite(swPorts[i], (c & portStatus) ? HIGH : LOW);
  17.     c <<= 1;
  18.   }
  19. }
复制代码


* 有了中断,也可以修改IO输出状态,下面就是对接贝壳互联平台。

* 对接详情可以参考我的另一片帖子:
ESP8266基于Arduino接入贝壳物联,使用天猫精灵控制WS2812B灯带

* 但是与其他单设备不同,我们一个设备上有4个开关,就会麻烦一点,我的做法是使用子设备:

* 先添加一个设备,设备的类型必须要选择万能遥控器,万能遥控器的子设备才会生效:

微信截图_20200108000733.png


* 之后在子设备栏里添加4个子设备,当然,子设备的数量取决于主设备有多少路控制IO,这些子设备的ID会在之后编程中用到:
微信截图_20200108001109.png


* 然后我们在手机天猫精灵APP里绑定账号之后,设备里一下子就会多出来4个设备:

* 在天猫精灵里可以给这些子设备设置不同的类型,冰箱,电视,空调啊,可以随意设置,不过设置了什么类型,到时候和天猫精灵语音控制的时候就要说对应的设备类型,比如:“
天猫精灵,打开空调

* 但是如果我想4路开关一起控制,那我就说“天猫精灵,打开万能遥控器”即可。


设置完成后,我们来继续写云平台相关代码:

* 一样的套路,在loop循环中,主要做两件事,定期向服务端发送心跳包,每个循环中收一下消息,再处理一下消息。
  1. void loop() {
  2.   // 检测网络是否通畅
  3.   while (WiFi.status() != WL_CONNECTED) {
  4.     delay(1000);
  5.     Serial.print(".");
  6.   }
  7.   // 检测是否连接到服务器
  8.   if (!client.connected()) {
  9.     if (!client.connect(host, httpPort)) {
  10.       Serial.println("connection failed");
  11.       delay(5000);
  12.       return;
  13.     }
  14.   }
  15.   // 检测是否需要发送心跳包
  16.   if (millis() - lastCheckInTime > postingInterval || lastCheckInTime == 0) {
  17.     Serial.println("Send HeartBeat!!");
  18.     sendHeartBeat();
  19.   }
  20.   // 检测是否收到服务器推送
  21.   if (client.available()) {
  22.     String inputString = client.readStringUntil('\n');
  23.     onMessageReceive(inputString);
  24.   }
  25. }
复制代码


* 发送心跳包的代码:
  1. void sendHeartBeat() {
  2.   String msg = "{"M":"checkin","ID":"" + DEVICEID + "","K":"" + APIKEY + ""}\n";
  3.   client.print(msg);
  4.   lastCheckInTime = millis();
  5. }
复制代码


* 处理推送消息的代码:
  1. void onMessageReceive(String msg) {
  2.   msg.trim();
  3.   Serial.println(msg);
  4.   if (msg.startsWith("{") && msg.endsWith("}")) {
  5.     DynamicJsonDocument doc(1024);
  6.     deserializeJson(doc, msg);
  7.     JsonObject obj = doc.as<JsonObject>();
  8.     String M = obj["M"];
  9.     if (M == "say") {
  10.       String S = obj["S"];
  11.       String C = obj["C"];
  12.       int device = 0B1111000;
  13.       if (S == "D1160") {
  14.         device = 0B1000000;
  15.       } else if (S == "D1161") {
  16.         device = 0B0100000;
  17.       } else if (S == "D1162") {
  18.         device = 0B0010000;
  19.       } else if (S == "D1163") {
  20.         device = 0B0001000;
  21.       }
  22.       if (C == "play") {
  23.         portStatus &= ~device;
  24.       } else if (C == "stop") {
  25.         portStatus |= device;
  26.       }
  27.       refreshOutput();
  28.     } else if (M == "checkinok") {
  29.       portStatus &= 0B1111101;
  30.       refreshOutput();
  31.     }
  32.   }
  33. }
复制代码


解释一下消息处理部分的代码:我目前只处理了say和checkinok的消息。

* checkinok消息:
由于刚起机连上网的时候,设备并不能马上连接上推送服务,一般要等待几十秒到数分钟才能链接完成,连接成功后云平台会发送checkinok消息,所以收到checkinok消息时,我们把WIFI_LED点亮,表示设备联网成功了。

* say消息:表示设备需要响应命令,两个参数比较重要:C和S,

  S表示子设备的id,通过S便可以知道需要操作哪个子设备(为空的话表示操作所有设备)
  C表示动作,play表示打开设备,stop表示关闭设备。
当然这些都是我根据串口打印消息内容猜测的,具体情况还要参考天猫精灵和贝壳互联的说明才比较准确。


完整的代码如下,需对应修改DEVICE ID和API KEY:
  1. #include <ESP8266WiFi.h>
  2. #include <WiFiManager.h>
  3. #include <ArduinoJson.h>

  4. //=============  此处必须修该============
  5. String DEVICEID = "00000"; // 你的设备编号   ==
  6. String  APIKEY = "000000000"; // 设备密码==

  7. unsigned long lastCheckInTime = 0; //记录上次报到时间
  8. const unsigned long postingInterval = 40000; // 每隔40秒向服务器报到一次

  9. const char* host = "www.bigiot.net";
  10. const int httpPort = 8181;

  11. WiFiClient client;

  12. //======插座控制端口=======
  13. const int portNum = 7;

  14. //7个端口分别对应 K1, K2, K3, K4,USB,LED_WIFI, LED_ROUND
  15. char swPorts[portNum] = {14, 12, 13, 4, 16, 0, 3};

  16. //除了USB充电口,其他都是低电平有效
  17. int portStatus = 0B0000110; //7个IO开关

  18. //======================================

  19. // 初始化插板
  20. void initOutputMode() {
  21.   for (int i = 0; i < portNum; i++) {
  22.     pinMode(swPorts[i], OUTPUT);
  23.   }
  24. }

  25. // 根据开关状态刷新IO电平
  26. void refreshOutput() {
  27.   int c = 1;
  28.   for (int i = portNum - 1; i >= 0; i--) {
  29.     digitalWrite(swPorts[i], (c & portStatus) ? HIGH : LOW);
  30.     c <<= 1;
  31.   }
  32. }

  33. // 中断响应
  34. ICACHE_RAM_ATTR void onKeyClick() {
  35.   // 全关的状态全部打开,否则都是全关闭
  36.   if ((portStatus & 0B1111000) == 0B1111000) {
  37.     portStatus &= 0B0000111;
  38.   } else {
  39.     portStatus |= 0B1111000;
  40.   }
  41.   refreshOutput();
  42. }

  43. void initPanelKey() {
  44.   attachInterrupt(digitalPinToInterrupt(5), onKeyClick, FALLING);
  45. }

  46. void setup() {
  47.   // 设置串口
  48.   Serial.begin(115200);

  49.   //初始化输出端口
  50.   initOutputMode();
  51.   refreshOutput();

  52.   //初始化按键中断
  53.   initPanelKey();

  54.   // 设置Wifi
  55.   WiFiManager wifiManager;
  56.   wifiManager.autoConnect();
  57. }

  58. void loop() {
  59.   // 检测网络是否通畅
  60.   while (WiFi.status() != WL_CONNECTED) {
  61.     delay(1000);
  62.     Serial.print(".");
  63.   }
  64.   // 检测是否连接到服务器
  65.   if (!client.connected()) {
  66.     if (!client.connect(host, httpPort)) {
  67.       Serial.println("connection failed");
  68.       delay(5000);
  69.       return;
  70.     }
  71.   }
  72.   // 检测是否需要发送心跳包
  73.   if (millis() - lastCheckInTime > postingInterval || lastCheckInTime == 0) {
  74.     Serial.println("Send HeartBeat!!");
  75.     sendHeartBeat();
  76.   }
  77.   // 检测是否收到服务器推送
  78.   if (client.available()) {
  79.     String inputString = client.readStringUntil('\n');
  80.     onMessageReceive(inputString);
  81.   }
  82. }

  83. void onMessageReceive(String msg) {
  84.   msg.trim();
  85.   Serial.println(msg);
  86.   if (msg.startsWith("{") && msg.endsWith("}")) {
  87.     DynamicJsonDocument doc(1024);
  88.     deserializeJson(doc, msg);
  89.     JsonObject obj = doc.as<JsonObject>();
  90.     String M = obj["M"];
  91.     if (M == "say") {
  92.       String S = obj["S"];
  93.       String C = obj["C"];
  94.       int device = 0B1111000;
  95.       if (S == "D1160") {
  96.         device = 0B1000000;
  97.       } else if (S == "D1161") {
  98.         device = 0B0100000;
  99.       } else if (S == "D1162") {
  100.         device = 0B0010000;
  101.       } else if (S == "D1163") {
  102.         device = 0B0001000;
  103.       }
  104.       if (C == "play") {
  105.         portStatus &= ~device;
  106.       } else if (C == "stop") {
  107.         portStatus |= device;
  108.       }
  109.       refreshOutput();
  110.     } else if (M == "checkinok") {
  111.       portStatus &= 0B1111101;
  112.       refreshOutput();
  113.     }
  114.   }
  115. }

  116. void sendHeartBeat() {
  117.   String msg = "{"M":"checkin","ID":"" + DEVICEID + "","K":"" + APIKEY + ""}\n";
  118.   client.print(msg);
  119.   lastCheckInTime = millis();
  120. }
复制代码

基于ESP8266的WiFi排插拆机加装USB调试板,改造成Arduino开发板

结束




打赏

参与人数 3家元 +90 收起 理由
jpdd521 + 20 好评,刚好在弄一个4路的板子。。。.
家睦 + 60
数码家园 + 10

查看全部打赏

发表于 2020-1-8 11:35:58 | 显示全部楼层
看了下,你这没有WiFi断网自联吧,如果使用过程中WiFi断线在重新连接只能重启。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-8 16:06:20 | 显示全部楼层
水表君 发表于 2020-1-8 11:35
看了下,你这没有WiFi断网自联吧,如果使用过程中WiFi断线在重新连接只能重启。 ...

还没试过,有空试一下
回复 支持 反对

使用道具 举报

发表于 2020-1-9 11:53:01 | 显示全部楼层
最近在试着ESP8266 01S的开关,总是不得要领,头大。楼主好身手。
回复 支持 反对

使用道具 举报

发表于 2020-1-10 10:45:52 | 显示全部楼层
好技术,期待量产
回复 支持 反对

使用道具 举报

发表于 2020-1-10 14:48:19 | 显示全部楼层
要是能用四个开关分别控制四个插口,一个控制总开关那就完美了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-10 17:31:15 | 显示全部楼层
28674500 发表于 2020-1-10 14:48
要是能用四个开关分别控制四个插口,一个控制总开关那就完美了

和天猫精灵说“打开万能遥控器”就可以控制全部插孔,不过实际也是控制四个继电器,这个硬件上没有总继电器
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-10 20:14:51 | 显示全部楼层
天猫精灵的控制指令貌似是固定的,所以不容易定制化设计
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-10 20:19:25 | 显示全部楼层
z88088081 发表于 2020-1-10 10:45
好技术,期待量产

某宝上搜索Wifi智能排插应该有很多,不过是什么方案的就要拆机才知道,而且很多品牌本身就支持阿里智能和天猫精灵了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-10 20:21:45 | 显示全部楼层
hzw6834 发表于 2020-1-9 11:53
最近在试着ESP8266 01S的开关,总是不得要领,头大。楼主好身手。

期待有人搞个廉价的开源硬件产品出来玩
回复 支持 反对

使用道具 举报

发表于 2020-1-16 13:00:24 | 显示全部楼层
建议楼主可以试一下OTA升级,这样不用每次刷还要把排插拿到电脑边上,还要连线。
我玩8266大概2个多月,现在除了新买的模块要用传统方式刷一下以外正常的升级程序都是通过OTA,哪怕是实验板我都用OTA升级,方便的多了。
而且对于一些控制强电的地方通过OTA升级更安全,因为没有连线就不会有强电通过刷机线炸掉电脑的危险(我的一些DIY中8266模块和强电是不隔离的)
回复 支持 反对

使用道具 举报

发表于 2020-1-18 17:57:50 | 显示全部楼层
hzy3774 发表于 2020-1-10 20:14
天猫精灵的控制指令貌似是固定的,所以不容易定制化设计

所以我觉得天猫很傻,实际的物品和类型不一样很蛋疼。玩几下就懒得折腾了。
回复 支持 反对

使用道具 举报

发表于 2020-1-29 10:58:45 来自手机浏览器 | 显示全部楼层
学习了 准备入手一个
回复 支持 反对

使用道具 举报

发表于 2021-3-15 14:59:41 | 显示全部楼层
最近13包邮买了一个向日葵wifi插座,还不错,远程功能要服务器支持就算了,当本地定时插座倒计时也行
回复 支持 反对

使用道具 举报

发表于 2021-3-15 16:10:20 | 显示全部楼层
有干货啊,崇拜大佬。。。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2024-3-29 04:28 , Processed in 0.140400 second(s), 14 queries , Redis On.

Powered by Discuz!

© 2006-2023 smzj.net

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