教程:http://geek-workshop.com/thread-37484-1-1.html
源码:链接:https://pan.baidu.com/s/1yuYYqsM-WSOb0AbyAT0qrQ 密码:d8e8
本帖最后由 zyzand 于 2018-5-10 16:00 编辑
前段时间,学校发了通知,说寝室要安智能电表了,由原来的每月底后付费改成预付费,说是没余额会自动断电,通过微信平台查询和缴纳电费。打开查电费的界面看了一下,发现缴费的界面是需要在微信里才能打开的,但查询的界面能直接在普通浏览器打开:
<ignore_js_op>
(最近刚安装上,学校实行保电模式,所以暂时没电费也能用哈)
正好实验室有几块ESP8266和几块OLED。我想能不能拿ESP8266来显示当前的实时电费信息呢?这样就避免舍友吃鸡的时候突然断电的尴尬了。
电费信息的解析
研究了一下查询电费的方式,发现只要更改地址就能查到不同楼层的电费信息,返回的html的源码如下
<ignore_js_op>
代码还是比较短的,有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
猜测是在编译固件时选择的Flash不对,我的ESP-01的flash型号是EN25Q80B-104,网上查了一下说是8Mbit(1M)的。于是在编译时选择Flash Size为1M(128k SPIFFS),这样再把编译好的固件上传到网站上就能空中升级了。
通过杭电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>
原文地址:https://www.cnblogs.com/kekeoutlook/p/9615089.html