教程:http://geek-workshop.com/thread-37484-1-1.html
源碼:鏈接:https://pan.baidu.com/s/1yuYYqsM-WSOb0AbyAT0qrQ 密碼:d8e8
本帖最后由 zyzand 於 2018-5-10 16:00 編輯
前段時間,學校發了通知,說寢室要安智能電表了,由原來的每月底后付費改成預付費,說是沒余額會自動斷電,通過微信平台查詢和繳納電費。打開查電費的界面看了一下,發現繳費的界面是需要在微信里才能打開的,但查詢的界面能直接在普通瀏覽器打開:
(最近剛安裝上,學校實行保電模式,所以暫時沒電費也能用哈)
正好實驗室有幾塊ESP8266和幾塊OLED。我想能不能拿ESP8266來顯示當前的實時電費信息呢?這樣就避免舍友吃雞的時候突然斷電的尷尬了。
電費信息的解析
研究了一下查詢電費的方式,發現只要更改地址就能查到不同樓層的電費信息,返回的html的源碼如下
代碼還是比較短的,有1M內存的ESP8266完全能應付得來。
思路也很簡單,就只先用http庫獲取到網頁源碼,然后使用關鍵信息前后的字符截取出相應的信息。解析信息的方法如下。(代碼可能寫的不太規范,歡迎大神指點)
struct priceinf {
float price;
char timestr[20 * 7];
int year;
int month;
int day;
int hh;
int mm;
int ss;
char lou[20];
char qinshi[10];
};
/*
返回信息解析
傳入:html代碼
返回:解析出來的信息(priceinf 結構體)
未解析到則返回空的結構體
*/
priceinf getPrice(String s) {
priceinf dat;
int datStart = 0, datEnd = 0;
String datstr;
char buf[50];
char datsign[] = "<span class=\"price\"";
datStart = s.indexOf(datsign) + strlen(datsign) + 23;
if (datStart == strlen(datsign) + 23 - 1) { //沒有找到price
memset(&dat, 0, sizeof(dat));
return dat;
}
datEnd = s.indexOf("</span>", datStart) - 2; //減2是為了減去字符“元”
datstr = s.substring(datStart, datEnd);
dat.price = datstr.toFloat();
char timesign[] = "<font style=\"color:#2d9fd3\"><b>";
datStart = s.indexOf(timesign) + strlen(timesign);
datEnd = s.indexOf("</b></font>", datStart); //結尾
datstr = s.substring(datStart, datEnd);
datstr.toCharArray(dat.timestr, 20);
dat.timestr[19] = 0;
dat.year = datstr.substring(0, 4).toInt();
dat.month = datstr.substring(5, 7).toInt();
dat.day = datstr.substring(8, 10).toInt();
dat.hh = datstr.substring(11, 13).toInt();
dat.mm = datstr.substring(14, 16).toInt();
dat.ss = datstr.substring(17, 19).toInt();
char lousign[] = "樓幢:";
datStart = s.indexOf(lousign) + strlen(lousign);
datEnd = s.indexOf("</p>", datStart); //結尾
datstr = s.substring(datStart, datEnd);
datstr.toCharArray(dat.lou, 20);
dat.lou[19] = 0;
char qinshisign[] = "寢室號:";
datStart = s.indexOf(qinshisign) + strlen(qinshisign);
datEnd = s.indexOf("</p>", datStart); //結尾
datstr = s.substring(datStart, datEnd);
datstr.toCharArray(dat.qinshi, 10);
dat.qinshi[9] = 0;
//Serial.println(s);
Serial.print("time=");
Serial.println(dat.timestr);
Serial.print("price=");
Serial.println(dat.price);
Serial.print("lou=");
Serial.println(dat.lou);
Serial.print("qinshi=");
Serial.println(dat.qinshi);
Serial.println();
return dat;
}
這個函數能實現對截止時間,樓,寢室號,剩余電費的解析,並把解析到的信息儲存在結構體里。
然后把信息用OLED顯示出來,不過這塊OLED默認是SPI的,而我用的ESP-01最多只有3個能自定義的io,完全不夠用。所以要改成IIC接口。按照說明改一下背后的電阻,然后將RES接VCC,CS、DC接GND,這樣D0就是SCL,D1是SDA了。分別連接到ESP8266的GPOP0和GPIO2上。
把OLED 屏幕的軟件IIC庫導入到Arduino里,測試一下還挺好用。
其他功能
到這里這個程序就基本完成了,難度不是很大。但感覺這個程序好簡單啊,ESP8266的潛能還沒全發揮出來,於是,當做練習,在這個程序里加入了SmartConfig,用來應對應對沒有已知密碼的wifi的情況,然后讓他能開機時從指定網址先獲取查詢電費的網址,並能從指定網址進行OTA空中升級。這樣就不止能查詢自己的宿舍,並且就算送給別人用也能隨時對程序進行修改。
獲取查詢網址和升級信息
獲取查詢網址和升級信息的函數,為了方便可管理多個終端,在開機的時候會先獲取本芯片的唯一SN號,然后程序會訪問自己SN號對應的網址,這樣就不用為每一個芯片單獨燒寫固件了,同一個固件就能去訪問不同的網址:
int site_Get() {
int ret = 0;
if ((WiFiMulti.run() == WL_CONNECTED)) {
HTTPClient http;
Serial.printf("[www.zyzand.com]Connect to www.zyzand.com...\n");
http.begin("www.zyzand.com", 80, (String)"/IoT/clients/" + SN + "/index.php"); //HTTP
int httpCode = http.GET();
int i = 5;//重試次數
while (i-- > 0 && httpCode != 200) {
Serial.printf("[www.zyzand.com]code: %d Try again...", httpCode);
delay(1000);
httpCode = http.GET();
}
//從www.zyzand.com獲取信息失敗
if (httpCode != 200) {
Serial.printf("[www.zyzand.com] GET Fail!");
ret = 2;
}
//成功則進行數據解析
else {
Serial.println("[www.zyzand.com]GET data:");
String payload = http.getString();
Serial.println(payload);
int nflag = payload.indexOf("\n");
Serial.println(nflag);
Serial.println(payload.indexOf("\n", nflag));
dat1 = payload.substring(0, nflag);
dat2 = payload.substring(nflag + 1, payload.indexOf("\n", nflag + 1));
Serial.print("[www.zyzand.com]dat1 = ");
Serial.println(dat1);
Serial.print("[www.zyzand.com]dat2 = ");
Serial.println(dat2);
//OTA
if (strcmp(dat1.c_str(), "updata") == 0) {
OTA();
}
}
}
else {//WiFi連接失敗
Serial.print("[www.zyzand.com]WiFi failed\n");
ret = 1;
}
//失敗時的處理
if (ret != 0) {
dat1 = "zyzand.com";
dat2 = (String)"/IoT/clients/" + SN + "/error/index.php";
}
return ret;
}
在測試OTA空中升級的時候,遇到的問題值得提一下。開始時空中升級總是返回flash配置錯誤:
- Flash config wrong real
通過杭電i-hdu上網認證
我所在的學校有校園免費wifi,連接是不需要密碼的,但是連接后會自動跳轉到一個網頁,需要進行認證才能訪問外網。一直對他的認證方法感興趣,於是用Fiddler抓了一下包,發現用來認證信息的只是一個post請求,也不是很復雜。在電腦上模擬成功,用ESP8266試了一下,也成功通過。
/*
通過i-HDU認證,請自己修改postDate中的學號和密碼
*/
int hdulogin() {
const char * host = "2.2.2.2";
const int httpPort = 80;
WiFiClient client;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return 1;
}
delay(10);
String postDate = "opr=pwdLogin&userName=你的學號&pwd=學號對應的密碼&rememberPwd=1";//將從串口接收的數據發送到服務器,readLine()方法可以自行設計
if (postDate.length() && postDate != "0") {
String data = (String)postDate;
int length = data.length();
String postRequest = (String)("POST ") + "/ac_portal/login.php HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: Keep Alive\r\n" +
"Content-Length: " + length + "\r\n" +
"Accept: */*\r\n" +
"Origin: http://2.2.2.2\r\n" +
"Content-Type: application/x-www-form-urlencoded; charset=UTF-8" + "\r\n" +
"User-Agent: zyzandESP8266\r\n" +
"\r\n" +
data + "\r\n";
//Serial.println(postRequest);
Serial.println();
client.print(postRequest);
delay(600);
//處理返回信息
String line = client.readStringUntil('\n');
while (client.available() > 0) {
line += client.readStringUntil('\n');
}
// Serial.println(line);
client.stop();
if (line.indexOf("logon success") != -1 || line.indexOf("不需要") != -1) { //認證成功
return 0;
}
else {
return 2;
}
}
}
最后自己焊接了個小板子,用AMS1117-3.3進行降壓:
<ignore_js_op>
<ignore_js_op>
