最近在准备电赛,训练的时候做到了2021年的A题。
这个题目硬件部分要求相对简单,但其中有一项要求让我头疼了很久。
就是在手机上显示测量装置测得并显示的输入信号THD值、一个周期波形、基波与谐波的归一化幅值。
在网上搜索了很久,也看了过去的获奖队伍,都没有提到相关的技术。
开始打算通过一些物联网应用实现,但在使用的过程中发现他们大多没有提供相关功能或者需要收费。其次,有些接口并不能实现实时接收数据。
最重要的是,需要联网。这在电赛是不允许的。
于是想到自己不久前的一个帖子。使用ESP8266+舵机,实现宿舍无线开关
这里同理,可以使用esp8266做网络客户端, 通过手机浏览器查看。
首先是HTML代码部分。(比较长,所以单独列出来)
<!DOCTYPE html>
<html>
<head>
<title>测试使用</title>
<meta http-equiv="refresh" content="1" charset="utf-8">
<!--为了保证数据更新,此页面每一秒刷新一次--->
<style type="text/css">
body
{
display: inline-flex;
}
.label1{
width: 210px;
text-align:center;
margin-top: 70px;
margin-left: 30px;
}
.label3{
font-size: 24px;
line-height: 60px;
text-align:left;
}
</style>
</head>
<body style="height:100% ;">
<div class="label1">
<div class="label3"><span>THD: </span><text id="THD"></text></div>
<div class="label3"><span>一次谐波: </span><text id="one"></text></div>
<div class="label3"><span>二次谐波: </span><text id="two"></text></div>
<div class="label3"><span>三次谐波: </span><text id="three"></text></div>
<div class="label3"><span>四次谐波: </span><text id="four"></text></div>
<div class="label3"><span>五次谐波: </span><text id="five"></text></div>
</div>
<div>
<h2 style="text-align:center;">折线图</h2>
<canvas id="mc" width="700" height="400" style="border:3px solid rgb(0, 0, 0)"></canvas>
<script type="text/javascript">
var canvas=document.getElementById('mc');
var ctx=canvas.getContext('2d');
ctx.lineWidth = 3;
ctx.beginPath();
var THD,one,two,three,four,five,x,y;
x= [30,40.32,50.65,60.97,71.29,81.61,91.94,102.26,112.58,122.9,
133.23,143.55,153.87,164.19,174.52,184.84,195.16,205.48,215.81,226.13,
236.45,246.77,257.1,267.42,277.74,288.06,298.39,308.71,319.03,329.35,339.68,
350,360.32,370.65,380.97,391.29,401.61,411.94,422.26,432.58,442.9,453.23,463.55,
473.87,484.19,494.52,504.84,515.16,525.48,535.81,546.13,556.45,566.77,577.1,587.42,
597.74,608.06,618.39,628.71,639.03,649.35,659.68,670];
y = [200,182.03,164.24,146.80,129.90,113.70,98.36,84.03,70.87,58.99,48.52,
39.57,32.22,26.55,22.61,20.44,20.06,21.49,24.69,29.65,36.31,44.61,54.46,65.76,
78.41,92.27,107.2,123.07,139.7,156.93,174.6,192.51,210.51,228.4,246,263.15,279.66,
295.38,310.14,323.81,336.23,347.3,356.9,364.92,371.3,375.97,378.88,380 ,379.32,
376.86,372.62,366.66,359.03,349.82,339.11,327.01,313.64,299.13,283.63,267.3,250.3,
232.79,214.96];
THD = 1;
one = 0;
two = 0;
three = 0;
four = 0;
five = 0;// 默认数据是0,画sin图像
document.getElementById("THD").innerHTML=THD; //id指向对应数据,实现数据修改
document.getElementById("one").innerHTML=one;
document.getElementById("two").innerHTML=two;
document.getElementById("three").innerHTML=three;
document.getElementById("four").innerHTML=four;
document.getElementById("five").innerHTML=five;
ctx.moveTo(x[0],y[0]);
for(var i=2;i<x.length;i++)
{
ctx.lineTo(x[i],y[i]);
}
ctx.stroke();
</script>
</div>
</body>
</html>
这个代码是可以直接运行的,默认画sin图像
虽然比较简陋,但要求的功能都有。
接下来便是ESP8266代码部分了。
上一次的作品尽管不需要网络,但仍然依赖于路由器 ,有些麻烦。由于上次是物联网项目,连接路由器无可厚非,但对于比赛,我觉得还是有些冗余了。
所以,这次使用ESP8266的AP热点模式,既可以做热点也可以做网络服务器。手机只需连接ESP8266建立的热点,并在浏览器中输入地址即可。
#include<stdio.h>
#include <ESP8266WiFi.h> // 本程序使用 ESP8266WiFi库
#include <ESP8266WiFiMulti.h> // ESP8266WiFiMulti库
#include <ESP8266WebServer.h> // ESP8266WebServer库
ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象,对象名称是 'wifiMulti'
ESP8266WebServer esp8266_server(80);// 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)
String html_code1 = " <!DOCTYPE html><html><head><meta http-equiv=\"refresh\" content=\"1\"></head> <style type=\"text/css\"> body{display: inline-flex;} .label1{width: 210px; text-align:center; margin-top: 70px; margin-left: 22px;} .label3{font-size: 24px; line-height: 60px; text-align:left;} </style> <body style=\"height:100% ;\"> <div class=\"label1\"> <div class=\"label3\"><span>THD: </span><text id=\"THD\"></text></div> <div class=\"label3\"><span>一次谐波: </span><text id=\"one\"></text></div> <div class=\"label3\"><span>二次谐波: </span><text id=\"two\"></text></div> <div class=\"label3\"><span>三次谐波: </span><text id=\"three\"></text></div> <div class=\"label3\"><span>四次谐波: </span><text id=\"four\"></text></div> <div class=\"label3\"><span>五次谐波: </span><text id=\"five\"></text></div> </div> <div><h2 style=\"text-align:center;\">波形图</h2> <canvas id=\"mc\" width=\"700\" height=\"400\" style=\"border:2px solid black\"></canvas><script type=\"text/javascript\"> var canvas=document.getElementById(\'mc\'); var ctx=canvas.getContext(\'2d\');ctx.beginPath();ctx.lineWidth = 3; var THD = 0;var one,two,three,four,five,x,y;";
String html_code3 = " document.getElementById(\"THD\").innerHTML=THD; document.getElementById(\"one\").innerHTML=one; document.getElementById(\"two\").innerHTML=two; document.getElementById(\"three\").innerHTML=three; document.getElementById(\"four\").innerHTML=four; document.getElementById(\"five\").innerHTML=five; ctx.moveTo(x[0],y[0]); for(var i=1;i<x.length;i++){ctx.lineTo(x[i],y[i]);}ctx.stroke();</script></div></body></html> ";
//默认图像,正弦函数
String html_code2 = \
" x = [30,40.32,50.65,60.97,71.29,81.61,91.94,102.26,112.58,122.9,\
133.23,143.55,153.87,164.19,174.52,184.84,195.16,205.48,215.81,226.13,\
236.45,246.77,257.1,267.42,277.74,288.06,298.39,308.71,319.03,329.35,339.68,\
350,360.32,370.65,380.97,391.29,401.61,411.94,422.26,432.58,442.9,453.23,463.55,\
473.87,484.19,494.52,504.84,515.16,525.48,535.81,546.13,556.45,566.77,577.1,587.42,\
597.74,608.06,618.39,628.71,639.03,649.35,659.68,670]; \
y = [200,182.03,164.24,146.80,129.90,113.70,98.36,84.03,70.87,58.99,48.52,\
39.57,32.22,26.55,22.61,20.44,20.06,21.49,24.69,29.65,36.31,44.61,54.46,65.76,\
78.41,92.27,107.2,123.07,139.7,156.93,174.6,192.51,210.51,228.4,246,263.15,279.66,\
295.38,310.14,323.81,336.23,347.3,356.9,364.92,371.3,375.97,378.88,380 ,379.32,\
376.86,372.62,366.66,359.03,349.82,339.11,327.01,313.64,299.13,283.63,267.3,250.3,\
232.79,214.96];\
THD = 0;\
one = 1;\
two = 0;\
three = 0;\
four = 0;\
five = 0; "; //正弦坐标值;
void setup() {
Serial.begin(9600);
WiFi.mode(WIFI_AP); //设置Wi-Fi为AP模式
IPAddress softLocal(192,168,1,1); //设置内网WIFI IP地址
IPAddress softGateway(192,168,1,1); //设置网关
IPAddress softSubnet(255,255,255,0);
WiFi.softAPConfig(softLocal, softGateway, softSubnet);
String apName = ((String)ESP.getChipId());
const char *softAPName = apName.c_str();
String WiFi_name = "your_wifi_name";
String pasword = "your_pasword";
WiFi.softAP(WiFi_name, pasword); // 创建wifi 名称 +密码 adminadmin
IPAddress myIP = WiFi.softAPIP(); // 输出创建的WIFI IP地址
Serial.println("");
Serial.println("AP热点模式 ");
Serial.print("网关IP: ");
Serial.println(myIP);
Serial.print("WiFi 名称: ");
Serial.println(WiFi_name);
Serial.print("WiFi 密码: ");
Serial.println(pasword);
esp8266_server.begin(); // 启动网站服务
esp8266_server.on("/", HTTP_GET, handleRoot); // 设置服务器根目录即'/'的函数'handleRoot'
esp8266_server.onNotFound(handleNotFound); // 设置处理404情况的函数'handleNotFound'
Serial.println("HTTP esp8266_server started");// 告知用户ESP8266网络服务功能已经启动
}
void loop() {
if(Serial.available()>0)
{
InformationGet();
}
// put your main code here, to run repeatedly:
esp8266_server.handleClient();
}
/*串口数据接收*/
void InformationGet(){
html_code2 = "";
while (Serial.available())//时刻读取硬件串口数据
{
html_code2 = Serial.readStringUntil('\n');//从串口缓存区读取字符到一个字符串型变量,直至读完或遇到某终止字符。
Serial.println("information recieved!");
}
while (Serial.read() >= 0){}//清除串口缓存
}
void handleRoot() {
String html_code = html_code1 + html_code2 + html_code3;
esp8266_server.send(200, "text/html;charset=utf-8", html_code);
}
// 设置处理404情况的函数'handleNotFound'
void handleNotFound()
{
esp8266_server.send(404, "text/plain", "404: Not found"); // 发送 HTTP 状态 404 (未找到页面) 并向浏览器发送文字 "404: Not found"
}
这个实现的本质就是通过ESP8266获取串口信息对HTML代码修改。
串口信息需要以特定格式输入,如下
x = [30,40.32,50.65]; //x坐标
y = [200,182.03,164.24]; //y坐标 两坐标数量要相等。否则绘图失败
THD = 0.3456;
one = 1;
two = 0.12;
three = 0.45;
four = 0.44;
five = 0.89; //其他参数 分号不可忽略
// 数据输入顺序无要求。未输入的保持默认值0(x,y除外)
坐标适应性变换
mid = (max(y)+min(y))/2;
temp = mid-y;
y = temp/max(temp)*180;
y = y + 200;
temp = x - min(x);
x = temp/max(temp)*640+30;
坐标适应性变换是因为浏览器窗口不会跟随图像变化。题目要求显示一个周期。
此外,数组在js里是弱类型,操作起来十分不方便。
所以直接让队友在单片机里处理好再传输了。
不知道有没有大神有更好的方法?