关于巴法云
专注于开源,智造,创新,分享。关注硬件与创新,突破技术极限,面向未来,我们是认真的。
崇尚开源,发掘未知。 ————巴法科技
1. 简介
ESP8266-NodeMCU的环境配置已经在一篇文章有所交代,接下让我们开启巴法云的物联网开发吧!😀😀😀
设计目标
实现esp8266自动配网;
实现ESP8266通过TCP协议连接巴法云的TCP创客云,串口发送指令控制LED的亮灭;
实现ESP8266通过MQTT协议连接巴法云的MQTT设备云;串口发送指令控制LED的亮灭,还实现DHT11温湿度数据的获取,并将数据上发巴法云平台,在线显示数据;
实现巴法云平台对ESP8266的OTA指令升级,基于前面两个实验。
2. 实验准备
本次需要做三个实验,分别是TCP点灯实验,MQTT发送温湿度和OTA指令升级,准备工作包括硬件💡💡💡和软件💻💻💻两部分。
2.1 硬件
你要实现巴法云的三个实验,你需要准备以下材料✨✨✨
- ESP8266-NodeMCU单片机(外加安卓数据线);
- DHT11温湿度传感器;
- 三根母母杜邦线;
硬件连接:ESP8266-NodeMCU硬件连接非常简单,只需将DHT11数据线接入D6引脚就好,这套连线三个实验都通用,具体接线如下🎀🎀🎀
ESP8266-NodeMCU -----> USB VCC -----> 3.3V GND -----> GND out -----> D2
2.2 软件
- Arduino IED 1.8.5以上(越高越好);
- 浏览器登录巴法云平台。
3. 实验步骤
首先搭建巴法云平台,创建的产品相关主题,检测相关数据流,上传固件和发送指令,以便后期程序端口接入开发。👩👩👩
3.1 TCP点灯实验
- 登录巴法云物联网平台,选择控制台,点击TCP创客云,然后新建light002主题,那么巴法云TCP就配置好了🎈
- 程序设计,程序有四部分bemfa02、TCP 、motion和update,其中:🎈🎈
文件名 | 功能 |
bemfa02 | 程序初始化调用和主程序调用; |
TCP | 发送数据到TCP服务器,初始化wifi连接,初始化和服务器建立连接; |
motion | 检查数据,发送心跳,检查WiFi; |
update | 固件升级函数。 |
bemfa02.ino
/* * 智能语言控制控制,支持天猫、小爱、小度、google Assistent同时控制 * Time:20211127 * Author: 2345VOR * 项目实例:发送on、off的指令开关灯 * 参考文献:https://bbs.bemfa.com/84/last */ #include <ESP8266WiFi.h> #include <ESP8266httpUpdate.h> #define server_ip "bemfa.com" //巴法云服务器地址默认即可 #define server_port "8344" //服务器端口,tcp创客云端口8344 //********************需要修改的部分*******************// #define wifi_name "J09 502" //WIFI名称,区分大小写,不要写错 #define wifi_password "qwertyuiop111" //WIFI密码 String UID = "e8882ae28d5bde39766c330ea913fd46"; //用户私钥,可在控制台获取,修改为自己的UID String TOPIC = "light002"; //主题名字,可在控制台新建 const int LED_Pin = D4; //单片机LED引脚值,D2是NodeMcu引脚命名方式,其他esp8266型号将D2改为自己的引脚 String upUrl = "http://bin.bemfa.com/b/3BcZTg4ODJhZTI4ZDViZGUzOTc2NmMzMzBlYTkxM2ZkNDY=light002.bin";//固件链接,在巴法云控制台复制、粘贴到这里即可 //**************************************************// //最大字节数 #define MAX_PACKETSIZE 512 //设置心跳值30s #define KEEPALIVEATIME 30*1000 //tcp客户端相关初始化,默认即可 WiFiClient TCPclient; String TcpClient_Buff = "";//初始化字符串,用于接收服务器发来的数据 unsigned int TcpClient_BuffIndex = 0; unsigned long TcpClient_preTick = 0; unsigned long preHeartTick = 0;//心跳 unsigned long preTCPStartTick = 0;//连接 bool preTCPConnected = false; //相关函数初始化 //连接WIFI void doWiFiTick(); void startSTA(); //TCP初始化连接 void doTCPClientTick(); void startTCPClient(); void sendtoTCPServer(String p); //led控制函数,具体函数内容见下方 #define turnOnLed() digitalWrite(LED_Pin,LOW); #define turnOffLed() digitalWrite(LED_Pin,HIGH); // 初始化,相当于main 函数 void setup() { Serial.begin(115200); pinMode(LED_Pin,OUTPUT); digitalWrite(LED_Pin,HIGH); Serial.println("Beginning..."); } //循环 void loop() { doWiFiTick(); doTCPClientTick(); }
TCP.ino
/* *发送数据到TCP服务器 */ void sendtoTCPServer(String p){ if (!TCPclient.connected()) { Serial.println("Client is not readly"); return; } TCPclient.print(p); } /* *初始化wifi连接 */ void startSTA(){ WiFi.disconnect(); WiFi.mode(WIFI_STA); WiFi.begin(wifi_name, wifi_password); } /* *初始化和服务器建立连接 :style="value.online?'订阅设备在线':'无订阅设备'" color:#9A9A9A; */ void startTCPClient(){ if(TCPclient.connect(server_ip, atoi(server_port))){ Serial.print("\nConnected to server:"); Serial.printf("%s:%d\r\n",server_ip,atoi(server_port)); String tcpTemp=""; //初始化字符串 tcpTemp = "cmd=1&uid="+UID+"&topic="+TOPIC+"\r\n"; //构建订阅指令 sendtoTCPServer(tcpTemp); //发送订阅指令 tcpTemp="";//清空 /* //如果需要订阅多个主题,可再次发送订阅指令 tcpTemp = "cmd=1&uid="+UID+"&topic="+主题2+"\r\n"; //构建订阅指令 sendtoTCPServer(tcpTemp); //发送订阅指令 tcpTemp="";//清空 */ preTCPConnected = true; preHeartTick = millis(); TCPclient.setNoDelay(true); } else{ Serial.print("Failed connected to server:"); Serial.println(server_ip); TCPclient.stop(); preTCPConnected = false; } preTCPStartTick = millis(); }
motion.ino
/* *检查数据,发送心跳 */ void doTCPClientTick(){ //检查是否断开,断开后重连 if(WiFi.status() != WL_CONNECTED) return; if (!TCPclient.connected()) {//断开重连 if(preTCPConnected == true){ preTCPConnected = false; preTCPStartTick = millis(); Serial.println(); Serial.println("TCP Client disconnected."); TCPclient.stop(); } else if(millis() - preTCPStartTick > 1*1000)//重新连接 startTCPClient(); } else { if (TCPclient.available()) {//收数据 char c =TCPclient.read(); TcpClient_Buff +=c; TcpClient_BuffIndex++; TcpClient_preTick = millis(); if(TcpClient_BuffIndex>=MAX_PACKETSIZE - 1){ TcpClient_BuffIndex = MAX_PACKETSIZE-2; TcpClient_preTick = TcpClient_preTick - 200; } preHeartTick = millis(); } if(millis() - preHeartTick >= KEEPALIVEATIME){//保持心跳 preHeartTick = millis(); Serial.println("--Keep alive:"); sendtoTCPServer("ping\r\n"); //发送心跳,指令需\r\n结尾,详见接入文档介绍 } } if((TcpClient_Buff.length() >= 1) && (millis() - TcpClient_preTick>=200)) { TCPclient.flush(); Serial.print("Rev string: "); TcpClient_Buff.trim(); //去掉首位空格 Serial.println(TcpClient_Buff); //打印接收到的消息 String getTopic = ""; String getMsg = ""; if(TcpClient_Buff.length() > 15){//注意TcpClient_Buff只是个字符串,在上面开头做了初始化 String TcpClient_Buff = ""; //此时会收到推送的指令,指令大概为 cmd=2&uid=xxx&topic=light002&msg=off int topicIndex = TcpClient_Buff.indexOf("&topic=")+7; //c语言字符串查找,查找&topic=位置,并移动7位,不懂的可百度c语言字符串查找 int msgIndex = TcpClient_Buff.indexOf("&msg=");//c语言字符串查找,查找&msg=位置 getTopic = TcpClient_Buff.substring(topicIndex,msgIndex);//c语言字符串截取,截取到topic,不懂的可百度c语言字符串截取 getMsg = TcpClient_Buff.substring(msgIndex+5);//c语言字符串截取,截取到消息 Serial.print("topic:------"); Serial.println(getTopic); //打印截取到的主题值 Serial.print("msg:--------"); Serial.println(getMsg); //打印截取到的消息值 } if(getMsg == "on"){ //如果收到指令on==打开灯 turnOnLed(); }else if(getMsg == "off"){ //如果收到指令off==关闭灯 turnOffLed(); }else if(getMsg == "update"){ //如果收到指令update updateBin();//执行升级函数 } TcpClient_Buff=""; TcpClient_BuffIndex = 0; } } /************************************************************************** WIFI ***************************************************************************/ /* WiFiTick 检查是否需要初始化WiFi 检查WiFi是否连接上,若连接成功启动TCP Client 控制指示灯 */ void doWiFiTick(){ static bool startSTAFlag = false; static bool taskStarted = false; static uint32_t lastWiFiCheckTick = 0; if (!startSTAFlag) { startSTAFlag = true; startSTA(); } //未连接1s重连 if ( WiFi.status() != WL_CONNECTED ) { if (millis() - lastWiFiCheckTick > 1000) { lastWiFiCheckTick = millis(); } } //连接成功建立 else { if (taskStarted == false) { taskStarted = true; Serial.print("\r\nGet IP Address: "); Serial.println(WiFi.localIP()); startTCPClient(); } } }
update.ino
/** * 固件升级函数 * 在需要升级的地方,加上这个函数即可,例如setup中加的updateBin(); * 原理:通过http请求获取远程固件,实现升级 */ void updateBin(){ Serial.println("start update"); WiFiClient UpdateClient; t_httpUpdate_return ret = ESPhttpUpdate.update(UpdateClient, upUrl); switch(ret) { case HTTP_UPDATE_FAILED: //当升级失败 Serial.println("[update] Update failed."); break; case HTTP_UPDATE_NO_UPDATES: //当无升级 Serial.println("[update] Update no Update."); break; case HTTP_UPDATE_OK: //当升级成功 Serial.println("[update] Update ok."); break; } }
修改bemfa02主程序对应wifi和巴法云参数,upUrl参数可以暂时不修改,实验三再修改🎈🎈🎈
编译程序,然后上传esp8266,观察esp8266和巴法云TCP控制台🎈🎈🎈🎈
- 实验效果:在订阅的“light002”主题下,发送on或者off,可见esp8266板载灯分别会亮灭。🎈🎈🎈🎈🎈
3.2 MQTT发送温湿度
- 登录巴法云物联网平台,选择控制台,点击MQTT设备云,然后新建led002控制端和temp004状态端主题,那么巴法云MQTT设备云就配置好了🎈
- 程序设计,程序有四部分dht11_led_OTA1.0、OTA(update)PubSubClient.cpp和PubSubClient.h,其中:🎈🎈
文件名 | 功能 |
dht11_led_OTA1.0 | 程序初始化调用和主程序调用,自动连接目标wifi,重新连接,led002的回调函数处理; |
OTA(update) | 固件升级函数。 |
dht11_led_OTA1.0.ino
/* Time:20211127 Author: 2345VOR 项目示例:通过MQTT协议发送on或off控制开关,温湿度上传巴法云 参考文献:https://www.cnblogs.com/bemfa/p/14590133.html */ #include <ESP8266WiFi.h>//默认,加载WIFI头文件 #include "PubSubClient.h"//默认,加载MQTT库文件 #include <ESP8266httpUpdate.h>//自动升级库 https://bbs.bemfa.com/84 #include <SimpleDHT.h>//dht11库文件 String upUrl = "http://bin.bemfa.com/b/1BcZTg4ODJhZTI4ZDViZGUzOTc2NmMzMzBlYTkxM2ZkNDY=led002.bin"; const char* ssid = "J09 502"; //修改,修改为你的路由的WIFI名字 const char* password = "qwertyuiop111"; //修改为你的WIFI密码 const char* mqtt_server = "bemfa.com"; //默认,MQTT服务器地址 const int mqtt_server_port = 9501; //默认,MQTT服务器端口 #define ID_MQTT "e8882ae28d5bde39766c330ea913fd46" //mqtt客户端ID,修改为你的开发者密钥 const char* topic = "led002"; //Led主题名字,可在巴法云控制台自行创建,名称随意 const char * dhttopic = "temp004"; //温湿度主题名字,可在巴法云mqtt控制台创建 int pinDHT11 = D2; //dht11传感器引脚输入 int B_led = D4; //控制的led引脚 long timeval = 3 * 1000; //上传的传感器时间间隔,默认3秒 #define ledstatus !digitalRead(B_led);//led状态默认0 long lastMsg = 0;//时间戳 SimpleDHT11 dht11(pinDHT11);//dht11初始化 WiFiClient espClient; PubSubClient client(espClient);//mqtt初始化 //灯光函数及引脚定义 #define turnOnLed() digitalWrite(B_led, LOW); #define turnOffLed() digitalWrite(B_led, HIGH); //自动连接目标wifi void setup_wifi() { delay(10); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } //重新连接 void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect(ID_MQTT)) {//连接mqtt Serial.println("connected"); client.subscribe(topic);//修改,修改为你的主题 } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } void setup() { pinMode(B_led, OUTPUT); Serial.begin(115200); setup_wifi(); client.setServer(mqtt_server, mqtt_server_port); client.setCallback(callback); digitalWrite(B_led, HIGH); } void loop() { if (!client.connected()) {//判断mqtt是否连接 reconnect(); } client.loop();//mqtt客户端 long now = millis();//获取当前时间戳 if (now - lastMsg > timeval) {//如果达到3s,进行数据上传 lastMsg = now; // read without samples. byte temperature = 0; byte humidity = 0; int err = SimpleDHTErrSuccess; if ((err = dht11.read(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) { Serial.print("Read DHT11 failed, err="); Serial.println(err); delay(1000); return; } String msg = "#" + (String)temperature + "#" + (String)humidity + "#" + ledstatus; //数据封装#温度#湿度#开关状态# client.publish(dhttopic, msg.c_str());//数据上传 } } //topic = "led002"的回调函数处理 void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); String Mqtt_Buff = ""; for (int i = 0; i < length; i++) { Mqtt_Buff += (char)payload[i]; } Serial.print(Mqtt_Buff); Serial.println(); // Switch on the LED if an 1 was received as first character if (Mqtt_Buff == "on") {//如果接收字符on,亮灯 turnOnLed();//开灯函数 } else if (Mqtt_Buff == "off") {//如果接收字符off,亮灯 turnOffLed();//关灯函数 }else if (Mqtt_Buff == "update") {//如果接收字符off,亮灯 client.disconnect(); //关闭mqtt delay(100); updateBin(); //开始升级 } Mqtt_Buff = ""; }
OTA(update).ino
/** * 固件升级函数 * 在需要升级的地方,加上这个函数即可,例如setup中加的updateBin(); * 原理:通过http请求获取远程固件,实现升级 */ void updateBin(){ Serial.println("start update"); WiFiClient UpdateClient; t_httpUpdate_return ret = ESPhttpUpdate.update(UpdateClient, upUrl); switch(ret) { case HTTP_UPDATE_FAILED: //当升级失败 Serial.println("[update] Update failed."); break; case HTTP_UPDATE_NO_UPDATES: //当无升级 Serial.println("[update] Update no Update."); break; case HTTP_UPDATE_OK: //当升级成功 Serial.println("[update] Update ok."); break; } }
PubSubClient.cpp
PubSubClient.h
修改dht11_led_OTA1.0主程序对应wifi和巴法云参数,upUrl参数可以暂时不修改,实验三再修改🎈🎈🎈
编译程序,然后上传esp8266,观察esp8266和巴法云MQTT控制台🎈🎈🎈🎈
- 实验效果:在订阅的“led002”主题下,发送on或者off,可见esp8266板载灯分别会亮灭;然后点击temp004主题的更多设置,。🎈🎈🎈🎈🎈
3.3 OTA指令升级
- OTA升级控制图
将刚刚两组实验联系起来,实现远程升级固件,通俗讲就是当你配置好实验一,在lighr002输入update指令就可以远程升级到实验二,反而言之也可以。🤩🤩🤩 - 关于上文实验一中的upUrl填写,先生成要升级(实验二)的bin文件,导入到TCP创客云的light002的OTA固件;关于上文实验二中的upUrl填写,先生成要升级(实验一)的bin文件,导入到MQTT设备云的led002的OTA固件,把生成的固件地址复制到对应的实验一的upUrl位置(地址不会变,固件会自动迭代更新)
- OTA配置好后,重新下载实验一的代码,然后再TCP创客云的light002主题测试on、off和update指令,输入update就会自动升级到实验二代码,可以到MQTT设备云的led002主题下,测试on、off和update指令,这就是实验一和实验二的梦幻操作!😁😀😂实验一效果如下:
4. 总结
- 期待大家可以消化代码并且半天实现OTA升级**本文是一个难度中等的物联网项目,实现E实现esp8266自动配网;TCP协议连接巴法云的TCP创客云,串口发送指令控制LED的亮灭;实现ESP8266通过MQTT协议连接巴法云的MQTT设备云;串口发送指令控制LED的亮灭,还实现DHT11温湿度数据的获取,并将数据上发巴法云平台,在线显示数据;实现巴法云平台对ESP8266的OTA指令升级,基于前面两个实验** 😁😁😁
- 在以后的博文中我们将学会用ESP8266做常用的物联网开发,从而实现对外部世界进行感知,充分认识这个有机与无机的环境,科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。