Arduino教程传送门
关于巴法云
专注于开源,智造,创新,分享。关注硬件与创新,突破技术极限,面向未来,我们是认真的。
崇尚开源,发掘未知。 ————巴法科技
ESP8266-NodeMCU的环境配置已经在一篇文章有所交代,接下让我们开启巴法云的物联网开发吧!
设计目标
- 实现esp8266自动配网;
- 实现ESP8266通过TCP协议连接巴法云的TCP创客云,串口发送指令控制LED的亮灭;
- 实现ESP8266通过MQTT协议连接巴法云的MQTT设备云;串口发送指令控制LED的亮灭,还实现DHT11温湿度数据的获取,并将数据上发巴法云平台,在线显示数据;
- 实现巴法云平台对ESP8266的OTA指令升级,基于前面两个实验。
本次需要做三个实验,分别是TCP点灯实验,MQTT发送温湿度和OTA指令升级,准备工作包括硬件和软件两部分。
你要实现巴法云的三个实验,你需要准备以下材料✨✨✨
硬件连接:ESP8266-NodeMCU硬件连接非常简单,只需将DHT11数据线接入D6引脚就好,这套连线三个实验都通用,具体接线如下
ESP8266-NodeMCU -----> USB
VCC -----> 3.3V
GND -----> GND
out -----> D2
首先搭建巴法云平台,创建的产品相关主题,检测相关数据流,上传固件和发送指令,以便后期程序端口接入开发。
文件名 | 功能 |
---|---|
bemfa02 | 程序初始化调用和主程序调用; |
TCP | 发送数据到TCP服务器,初始化wifi连接,初始化和服务器建立连接; |
motion | 检查数据,发送心跳,检查WiFi; |
update | 固件升级函数。 |
bemfa02.ino
/*
* 智能语言控制控制,支持天猫、小爱、小度、google Assistent同时控制
* Time:20211127
* Author: 2345VOR
* 项目实例:发送on、off的指令开关灯
* 参考文献:https://bbs.bemfa.com/84/last
*/
#include
#include
#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;
}
}
实验效果:在订阅的“light002”主题下,发送on或者off,可见esp8266板载灯分别会亮灭。
登录巴法云物联网平台,选择控制台,点击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 //默认,加载WIFI头文件
#include "PubSubClient.h"//默认,加载MQTT库文件
#include //自动升级库 https://bbs.bemfa.com/84
#include //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
(略)
实验效果:在订阅的“led002”主题下,发送on或者off,可见esp8266板载灯分别会亮灭;然后点击temp004主题的更多设置,。
OTA配置好后,重新下载实验一的代码,然后再TCP创客云的light002主题测试on、off和update指令,输入update就会自动升级到实验二代码,可以到MQTT设备云的led002主题下,测试on、off和update指令,这就是实验一和实验二的梦幻操作!实验一效果如下: