赞
踩
物联网的硬件已经越来越便宜了,ESP8266虽已被淘汰,但价格便宜,做智能开关还是非常不错的。中国移动旗下的ONENET物联网平台优点是服务器稳定快速,对个人爱好者很大方,注册后即可免费创建100个设备,如果个人实名认证可免费创建1000个设备。缺点是开发复杂,难度大。网上的教程版本落后,很多不能用,需要自己花大量时间去摸索。下面就自己做开关的经历简单记录下,也给后面想做远程控制定时开关的友友们一个少走弯路的参考。
由于手机端APP用ONENET的API控制的,场景控制里不能控制定时功能,所以本设备端包含内置的定时远程控制接口,便于手机端设置定时控制(设置后即使断网也能定时控制)。先说一下设备开发端比一般开关增加的功能:1.内置局域网服务控制;2.内置远程定时控制功能;3.设备端将WIFI信息上传。下面是设备代码部分,根据自己的修改即可使用:
#include <ESP8266WebServer.h>
#include <PubSubClient.h>
#include <WiFiUDP.h>
#include <NTPClient.h>
#include <Ticker.h>
const char *WiFi_SSID = "XXXXX";//连接的路由名称,替换成你的
const char *WiFi_Password = "XXXXX";//路由密码,替换成你的
const char *Device_ID = "KaiGuan";//设备名称id,替换成你的
const char *Product_ID = "XXXXXX"; //平台分配的产品ID ,替换成你的
const char *Api_KEY = "XXXXXXXXXXX";//填写经过 key 计算的 token
const char* mqttServer = "mqtts.heclouds.com";
const uint16_t mqttPort = 1883;
#define WIFI_NAME "OneNet_KG"//设备无线名称,可换
struct TimeTaskStruct
{
int on;//定时任务是否开启,0关1开
int min;//分钟,如20表示20分钟
int hour;//小时,如23表示23点
char week[7];//星期,7个字符分别表示每周的一天,0不执行1执行,如0111111表示周日不执行其它时间执行
int state;//执行动作,0断开,1闭合,2重启设备
};
TimeTaskStruct Timetask[4];//内部的定时器结构,可设置4个定时器
int num=0;
Ticker ticker;
#define ONENET_POST "$sys/产品ID/KaiGuan/thing/property/post"//产品ID,设备ID要换
#define ONENET_SCRIBE "$sys/产品ID/KaiGuan/thing/property/set"//产品ID,设备ID要换
#define ONENET_GETDESIRED "$sys/产品ID/KaiGuan/thing/property/desired/get"//产品ID,设备ID要换
#define ONENET_DESIRED "$sys/产品ID/KaiGuan/thing/property/desired/get/reply"//产品ID,设备ID要换
#define ONENET_FORMAT "{\"id\": \"1\",\"params\": {\"switch1\": {\"value\": %d}}}"//根据这个在ONENET平台设置设备的属性值
#define ONENET_FORMAT_WIFI "{\"id\": \"2\",\"params\": {\"wifi_ip\": {\"value\":{\"ip\":\"%s\",\"Signal\":\"%ld\",\"ssid\":\"%s\"}}}}"//上传WIFI信息,便于手机端显示
#define ONENET_FORMAT_TIMER "{\"id\":\"3\",\"params\":{\"timer\":{\"value\":[\"%s\",\"%s\",\"%s\",\"%s\"]}}}"//根据这个在ONENET平台设置定时器的属性
char Upload_Package[150];
uint32_t Last_Updata_Time = 0;
uint32_t Updata_Time = 0;
const int MQTTping = 58;
int stat=0;
int GPIO=0;
String WifiIP="";
ESP8266WebServer server(80);
String html="<!DOCTYPE html><html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'/><title>ESP8266 WEB控制</title><style type='text/css'>.login-box { width: 400px;margin: 0 auto;background-color: #F7F7F7; border-radius: 10px; padding-top: 5px; padding-right: 5px; padding-bottom: 5px; padding-left: 5px; }.login-box h2 { color: #444; text-align: center;}.login-box input[type='password']{ width: 50%; height:40px; padding: 2px; border: none; border-radius: 3px; background-color: #EFEFEF; font-size: 14px; color: #444; margin-left: 26px;}.login-box input[type='password']:focus{ outline: none; box-shadow: 0 0 5px #1E90FF;}.login-box button[type='button'] { width: 25%; border: none; border-radius: 3px; padding: 12px; background-color: #1E90FF; color: #FFF; cursor: pointer; transition: background-color 0.3s; margin-left: 20px;}.login-box button[type='button']:disabled{ color: #ccc; background-color: #f5f5f5; cursor: not-allowed; }.progress-bar { height: 15px; padding: 5px; border-radius: 5px; overflow: hidden; } .bar { height: 100%; background: linear-gradient(to right, red, yellow, green); width: 0; transition: width 0.5s ease-in-out; } input[type='checkbox']{appearance: none;width: 64px;height: 32px;position: relative;border-radius: 16px;cursor: pointer;background-color: #777;}input[type='checkbox']:before{ content: ''; position: absolute; width: 28px; height: 28px; background: white; left: 2px; top: 2px;border-radius:50%; transition: left cubic-bezier(0.3, 1.5, 0.7, 1) 0.3s; background: linear-gradient(#fdfdfd, #afaaaa);}input[type='checkbox']:after { content: '开 关'; text-indent: 12px; word-spacing: 4px; display: inline-block; white-space: nowrap;color:white; font: 14px/30px monospace; font-weight: bold; }input[type='checkbox']:checked {background-color:#FF3300;background:linear-gradient(to right,red, #fac000);}input[type='checkbox']:checked:before{left: 34px;}input[type='checkbox']:checked:after {color: black;}</style><script src='https://code.jquery.com/jquery-3.5.1.min.js'></script><script>function showBlock(){var password=$('#pass0').val();event.preventDefault();$.ajax({url: '/password',type:'POST',data:{pass0:password},success: function(result) {if(result=='1') {document.getElementById('stat').style.display = 'none';document.getElementById('hidden-switch').style.display = 'block';document.getElementById('hidden-update').style.display = 'block';}else alert('密码错误!请重新输入');}});}function checkbox_button(){var tmp_check=document.getElementById('checkbox1').checked; if(true==tmp_check) {document.getElementById('text_stat').innerHTML='已打开';$.ajax({url: '/led/on',success: function(result) {console.log(result);}});} else {document.getElementById('text_stat').innerHTML='已关闭';$.ajax({url: '/led/off',success: function(result) {console.log(result);}});}}$(document).ready(function(){var result;$.ajax({url: '/ledstate',success: function(result) {if(result==='state:on') {document.getElementById('text_stat').innerHTML='已打开';document.getElementById('checkbox1').checked=true;} if(result=== 'state:off') {console.log('off');document.getElementById('text_stat').innerHTML='已关闭';document.getElementById('checkbox1').checked=false;}}});})</script><script>var progress = 0; var xhr = new XMLHttpRequest();var state=document.getElementById('update_stat'); function updateFirmware(){ var formData = new FormData(); document.getElementById('upbutton').disabled = true; formData.append('firmware', document.getElementById('firmwareFile').files[0]); xhr.open('POST', '/update', true); xhr.upload.onprogress = function(e) { if (e.lengthComputable) { progress = (e.loaded / e.total) * 100; document.getElementById('bar').style.width = progress + '%'; if (progress === 100) { document.getElementById('update_stat').innerHTML = '已完成100%,正在重启中,1分钟后请刷新页面!'; document.getElementById('upbutton').disabled = false; } else {document.getElementById('update_stat').innerHTML = '已更新'+Math.round(progress) +'%'; } } }; xhr.send(formData); }</script></head><body><div class='login-box'><h2>EPS8266 WEB控制</h2><form id='stat' action='/password' method='post' ><input class='input-box' type='password' id='pass0' placeholder='请输入密码'><button type='button' onClick=\"showBlock();\">登录</button></form><div style='margin-top:40px;height:60;display:none' id='hidden-switch'><div style='position:absolute;margin-left:20px;width:80px'><p id='text_stat'>已关闭</p></div><div style='position:absolute;margin-left:130px; margin-top:10px'><input name='checkbox' type='checkbox' id='checkbox1' class='switch' οnchange=\"checkbox_button()\"></div></div> <div style='border-style:solid;border-width:1px;border-color:red; padding:10px;margin-top:120px;display:none' id='hidden-update' ><form method='POST' action='/update' enctype='multipart/form-data' id='upload_form'> <input type='file' id='firmwareFile'> <button type='button' id='upbutton' onClick=\"updateFirmware();\">更新</button></form><div class='progress-bar'><div class='bar' id='bar'></div></div><div style='text-align: center'><p id='update_stat'> </p></div> </div></div></body></html>";
WiFiClient espClient;
PubSubClient client(espClient);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "time1.cloud.tencent.com",60*60*8, 20*1000);
void WiFi_Init(void);
void MQTT_Init(void);
void MQTT_Reconnection(void);
void MQTT_Callback(char *MQTT_Topic, byte *MQTT_Payload, uint16_t MQTT_Payload_Len);
void CrontoDate(char *t);
void PayloadtotimeTask(char *t,int types);
void RESET();
void handleRoot()
{
server.send ( 200, "text/html", html);
}
void handleLedOn()
{
digitalWrite(GPIO,LOW);
stat=1;
Kaigun_stat(stat);
}
void handleLedOff()
{
digitalWrite(GPIO, HIGH);
stat=0;
Kaigun_stat(stat);
}
void handlestate()
{
char strtmp[4] = {0};
itoa(stat,strtmp,10);
String tmp="state:";
tmp+=strtmp;
tmp+="SSID:";
tmp+=WiFi.SSID().c_str();
tmp+="RSSI:"+WiFi.RSSI();
server.send(200,"text/state",tmp);
}
void handlepassword()
{
String password =server.arg("pass0");
if (password=="wang") {server.send(200,"text/plain","1");}
else { server.send(200,"text/plain","0");}
}
void setup()
{
Serial.begin(115200);
pinMode(GPIO, OUTPUT);
digitalWrite(GPIO, HIGH);
for(int i=0;i<4;i++)
{Timetask[i].on=Timetask[i].state=0;
Timetask[i].min=Timetask[i].hour=0;
for(int a=0;a<7;a++)
Timetask[i].week[a]='0';
}
memset(Upload_Package, 0, 150);
WiFi_Init();
timeClient.begin();
MQTT_Init();
server.on("/", handleRoot);
server.on("/led/on", handleLedOn);
server.on("/led/off", handleLedOff);
server.on("/state", handlestate);
server.on("/password", handlepassword);
server.on("/update", HTTP_POST, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
ESP.restart();
}, []() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.setDebugOutput(true);
WiFiUDP::stopAll();
Serial.printf("Update: %s\n", upload.filename.c_str());
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if (!Update.begin(maxSketchSpace)) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) {
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
} else {
Update.printError(Serial);
}
Serial.setDebugOutput(false);
}
yield();
});
server.begin();
}
void Kaigun_stat(int stat)
{
sprintf(Upload_Package,ONENET_FORMAT,stat);
client.publish(ONENET_POST,Upload_Package);
}
void loop()
{
server.handleClient();
if(WiFi.status() != WL_CONNECTED) { delay(500);}
if (!client.connected()) {client.disconnect();
delay(5000);MQTT_Init();}
if (millis() - Last_Updata_Time >= 60000)
{
Last_Updata_Time = millis();
WifiIP=WiFi.localIP().toString().c_str();
sprintf(Upload_Package,ONENET_FORMAT_WIFI,WifiIP.c_str(),WiFi.RSSI(),WiFi.SSID().c_str());
client.publish(ONENET_POST,Upload_Package);
}
client.loop();
if(millis() -Updata_Time>=3000)
{
Updata_Time = millis();
num++;
if(num<10) timeClient.update();
for(int i=0;i<4;i++)
{
if(Timetask[i].on==1)
{ int day=timeClient.getDay();
if(Timetask[i].week[day]=='1')
{
if(Timetask[i].hour==timeClient.getHours()&&Timetask[i].min==timeClient.getMinutes())
{
if(Timetask[i].state==1)
{if(stat==0)
{;stat=1;digitalWrite(GPIO,LOW);Kaigun_stat(stat);}
}
else if(Timetask[i].state==0)
{
if(stat==1) {stat=0;digitalWrite(GPIO,HIGH); Kaigun_stat(stat);}
}
else if(Timetask[i].state==2) {ticker.attach(59, RESET);}
}
}
}
}
}
wifi_set_sleep_type(LIGHT_SLEEP_T);
delay(100);
}
void WiFi_Init(void)
{
Serial.print("\r\n\r\nConnecting to ");
Serial.print(WiFi_SSID);
WiFi.mode(WIFI_STA);
WiFi.setHostname(WIFI_NAME);
WiFi.begin(WiFi_SSID, WiFi_Password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println(WiFi.localIP());
}
void MQTT_Init(void)
{
client.setServer(mqttServer, mqttPort);
client.setCallback(MQTT_Callback);
client.setKeepAlive(MQTTping);
if (client.connect(Device_ID, Product_ID, Api_KEY))
{
Serial.printf("MQTT link suss!\r\n");
client.subscribe(ONENET_SCRIBE);
client.subscribe(ONENET_DESIRED);
Kaigun_stat(stat);
client.publish(ONENET_GETDESIRED,"{\"id\":\"3\",\"params\":[\"switch1\",\"timer\"]}");
}
}
void MQTT_Callback(char *MQTT_Topic, byte *MQTT_Payload, uint16_t MQTT_Payload_Len)
{
Serial.printf("Topic: %s[%d]:\r\n", MQTT_Topic, MQTT_Payload_Len);
for(uint16_t i=0; i<MQTT_Payload_Len; i++) Serial.print((char)MQTT_Payload[i]);
MQTT_Payload[MQTT_Payload_Len]='\0';
char *payloadtmp=(char *)MQTT_Payload;
if(strstr(MQTT_Topic,"set")!= NULL) PayloadtotimeTask(payloadtmp,0);
if(strstr(MQTT_Topic,"get/reply")!= NULL) PayloadtotimeTask(payloadtmp,1);
}
void PayloadtotimeTask(char *t,int types)
{
char *strtmp=strstr(t,"switch1");
if(strtmp!=NULL)
{ char ctmp='0';
if(types==0)
{ctmp=*(strtmp+9); }
if(types==1)
{strtmp=strstr(strtmp,"value"); ctmp=*(strtmp+7);}
if(ctmp=='1') {stat=1;digitalWrite(GPIO,LOW);Kaigun_stat(stat);}
else if(ctmp=='2') {RESET();}
else {stat=0;digitalWrite(GPIO,HIGH); Kaigun_stat(stat);}
}
char *strtmp1=strstr(t,"[");
if(strtmp1!=NULL)
{
char* end = strstr(strtmp1 + 1, "]");
if (end != NULL)
{
char time[end - strtmp1 ];
strncpy(time, strtmp1 + 1, end - strtmp1-1);
time[end - strtmp1-1] = '\0';
CrontoDate(time);
}
}
}
void CrontoDate(char *t)
{
char *tmp=t;
char *strtmp;
char subtime[4][18];
for(int i=0;i<4;i++)
{ strtmp=strstr(tmp,"\"");
char* end = strstr(strtmp + 1, "\"");
if(end!=NULL){
char time[end - strtmp ];
strncpy(time, strtmp + 1, end - strtmp-1);
time[end - strtmp-1] = '\0';
int s=sscanf(time,"%1d%1d %d %d %s",&Timetask[i].on,&Timetask[i].state,&Timetask[i].min,&Timetask[i].hour,Timetask[i].week);
if(s<1) {Timetask[i].on=0;}
strcpy(subtime[i],time);
tmp=end+1;
}
}
sprintf(Upload_Package,ONENET_FORMAT_TIMER,subtime[0],subtime[1],subtime[2],subtime[3]);
client.publish(ONENET_POST,Upload_Package);
}
void RESET(){
ESP.reset();
}
后面有空继续完善ESP8266的开关教程。可加入QQ群:199183673,一起学习,共同提高!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。