前言:封校期间闲来无事,分享一个大三做过的一个项目(信息系统设计课程设计),本篇文章将大致介绍其原理和系统实现流程。另外附演示视频:基于STM32和Web的蔬菜大棚设计https://www.bilibili.com/video/BV1cp4y1Q7jW/
1. 总体设计
本项目基于STM32开发板,通过云平台的应用,在前端Web实现了简易的农业大棚监控系统。提出并设计了一种通过对大棚内农作物生长的环境信息(温度,湿度,光照强度)实时监测可视化,并可通过发送相关指令达到调控的系统,旨在为农作物的生长创造最适宜其生长的环境。系统基于高性能、低功耗的STM32主控制芯片设计,主要由数据采集模块,WIFI模块,执行机构模块,云平台组成;前端定时采集各温湿度,光敏传感器节点信息,记录历史数据,通过云平台的交互,实现人为远程控制和对农业大棚环境信息的监控。
-
整体框架图如下:
-
系统流程图如下:
2. 开发环境
-
硬件环境:
开发板:智前沿-智能产品全栈开发板STM32F407 ZET6
调试器:ST-LINK V2(CN)
串口工具:TTL转USB模块
RS485工具:USB转RS485模块
-
Keil 5
官网下载链接https://www.keil.com/download/product/,开发工具Keil 5需要自行安装单片机的开发包,其开发包名称和单片机厂商的MCU型号对应(STM32F407ZET6),获取方式为MDK5 Software Packs,文件可直接下载;然后在网页中选择下载STM32F4,用于本项目的STM32F407ZET6 MCU开发,对应ARM Cortex-M4。安装路径不能带中文,必须是英文路径;安装目录不能跟之前的版本比如Keil 4冲突。安装完成后,安装MCU开发包,即Keil.STM32F4xx_DFP.pack,安装路径选择跟Keil 5相同。
-
Sublime Text 3
官方网站下载链接Download - Sublime Text,下载完成后按照教程(附链接sublime text 3下载与安装详细教程_凉红茶的博客-CSDN博客_sublime下载)进行安装,创建桌面快捷方式。
设置字体和字号:设置字体用"font_face":"字体名称",设置字体大小用"font_size":"字体大小",注意它们之间需要用逗号隔开。
安装Package Control,Package Control为插件管理包,使用其可以方便的浏览,安装和卸载位于Sublime Text 3中的插件。安装链接为Package Control - the Sublime Text package manager 具体的安装教程可以参考Sublime怎么安装Package control组件-sublime-PHP中文网。
ConvertToUTF8 插件安装,ConvertToUTF8 能将除UTF8编码之外的其他编码文件在 Sublime Text 中转换成UTF8编码,在打开文件的时候一开始会显示乱码,然后一刹那就自动显示出正常的字体,当然,在保存文件之后原文件的编码格式不会改变。
3. 模块介绍
本项目主要包括数据采集模块(温湿度,光照传感器模块)、处理器模块、WIFI模块、云平台模块,前端模块,执行结构模块(蜂鸣器模块,数码管,LED灯模块)等,实现了前端实时监测数据,发送指令并触发相关模块,及案件操控。
总体实现的功能及相关的流程有:
- 开发板通过温湿度,光敏传感器模块持续读取温湿度及光照强度数据,并将数据发送至云平台。
- 前端获取从云平台传来的数据,进行实时监测,当某一数据出现异常(低于正常区间的下限或高于正常区间的上限)会发送相应的指令。
- 当温度异常时,前端发送指令1。此时开发板进入蜂鸣器模块,同时数码管模块开始记录异常次数。
- 当湿度/光照强度异常时,云平台发送指令2/3。此时开发板进入LED模块,同时数码管异常次数相应增加。
- 当要查看总异常次数时,云平台发送指令4。此时进入数码管模块,数码管通过扫描显示总异常次数。
- 当要重置WIFI状态时,前端发送指令5。此时进入WIFI模块,此时会读取按键信息,可以通过按键选择重新连接服务器或者恢复出厂设置。
这些模块的代码实现均在main.c中的main()中实现,以下将一一进行说明,分别展示温湿度及光敏模块、前端指令模块、按键控制模块。其中由于LED模块、蜂鸣器模式与数码管模块均由前端指令控制,因此将在指令控制模块说明;WIFI模式通过按键控制,将在按键控制模块下说明。
-
温湿度及光敏模块
温湿度及光敏模块主要用于读取开发板所出环境的温湿度及光照值,一方面会在开发板的OLED显示屏实时显示,另一方面会将三个数据打包发送给云平台。
- OLED显示
利用oled库函数OLED_ShowString(u8 x, u8 y, const u8 *p, u8 size),其中x,y为起始坐标,size表示字体大小*p表示字符串起始位置。
- 获取温湿度进行转化
利用SHT11_Measure(u16 *p_value, u8 *p_checksum, u8 mode)测量温湿度;
并通过SHT11_Calculate(u16 t, u16 rh, float *p_temperature, float *p_humidity)转化成实际生活中常用的温湿度计量单位,最后上传至云平台时会重新转化回便于传输的数据格式。
- 获取光照强度
通过函数Lsens_Get_Val(void)读取光敏传感器值,0表示最暗,100表示最亮。
- 传输数据
三个数据保存在数组uint32_t sendData[3]中,首先通过int getData(unsigned char *TxBuffer, int *len, uint32_t sendData[], int dataLen)进行拼接发送的数据包,TxBuffer为发送的数组,sendData为待上传的数据,dataLen为上传的数据个数,随后通过E103_W02_SendData( uint8_t *Data, int l)发送数据包。
-
前端指令控制蜂鸣器与LED数码管模块
本模块中,我们实现了在前端网页发送数字0-5代表的指令,使得开发板进入不同的模式。利用switch语句实现。其中特别地,可以通过指令控制蜂鸣器与数码管。前端通过指令来控制硬件效果图如下。
指令保存在传输数据帧的dataFrame.mOrderConnect中,通过读取其中的值选择不同模式及操作。
以下为具体说明:
- 在整个指令模块中,参数count用于计算出现异常进行报警的次数;
- 温度异常时发送指令1,蜂鸣器鸣叫,计数器count自加1;
- 湿度异常时发送指令2,点亮LED0,即点亮红灯,计数器count自加1;
- 光照强度异常时发送指令3,点亮LED1,即点亮蓝灯,计数器count自加1;
- 想要查询出现异常并报警的次数时,发送指令4时。此时读取count的四位数分别保存在a,b,c,d中(这里默认报警次数小于一万次)。随后在一段时间内循环扫描数码管,并进行短时间的时延,以显示四位数。通过select(int id)选择第id个数码管。 通过show_num(num)函数使得数码管显示相应的数字。
- 指令为0时表示异常消除,此时将蜂鸣器、LED灯重置,停止报警。
-
按键控制WIFI模块
当前端传输指令为5时,为控制参数flag0进行翻转。当flag0值为0时不会进入按键控制模块,OLED显示“0-key:off”;当flag0值为1时且按下按键会进入WIFI模块,通过语句if(key &&flag0)进行判断。其中为保证烧录后可以连接WIFI模块,因此flag0的初始值设定为1,读取按键key值,当为WKUP_PRS时执行WIFI模块中的连接服务器操作,当按下KEY1即值为KEY1_PRES时,进行重置操作。
-
云平台模块
本模块实现硬件与云平台(厂家)的连接以及配置,使用云平台来获取STM32开发板温度传感器,湿度传感器和光敏传感器模块的数据。
- WIFI调试:通过配置使得WIFI模块连接到服务器,然后将读取到的板载温湿度传感器和光敏传感器模块的数据根据规定好的协议拼装好,然后发送到服务器。
- WIFI模块指令内容的修改:WIFI与服务器通讯最重要的是连接本地路由器获得可以访问的网络,然后创建TCP连接。根据本地的路由器网络ID,密码和要连接的服务器IP,修改要连接的WIFI名称和密码。其中服务器地址为192.168.31.162,服务器端的端口号为3002。
-
前端模块
本部分实现前端和云平台的连接以及Web页面,包括CSS架构和HTML网页及相关代码。
- XmlHttp.open函数建立http请求:XmlHttpRequest对象用于后台与服务器交换数据,它能够在不重新加载页面的情况下更新网页;在页面已加载后从服务器请求数据;在页面已加载后从服务器接收数据;在后台向服务器发送数据等。其语法为:open(method, url, async, username, password)。 method参数是用于请求的http方法,包括get, post和head,本项目使用get(用get发送数据,只能是256KB),原因是请求不带参数的数据。url参数是请求的主体,大多数浏览器实施了一个同源安全策略,并且要求这个与url包含脚本的文本具有相同的主机名和端口。async 参数指示请求使用应该异步地执行。username 和 password 参数是可选的,分别是设备ID和登陆密码,为 url 所需的授权提供认证资格。
- CSS框架:Base:全站的基础,规定了元素的显示方式。其中包括body和background等;Layout:Layout是页面上主要的布局,包括header,footer等。在Layout设置好基本的展示样式后,就可以进行相应内容和元素的填充;Module:Module是一些可以复用的独立组件,包括柱形图各个组的显示;State:显示当前元素的状态,会根据JavaScript的运行而发生变化。
- HTML网页(具体见最后的代码)
4. 产品展示
-
开发板的实际效果图:
-
前端网页的实现效果图:
5. 主要问题
-
CSS文件链接HTML问题
问题描述:CSS保存路径不对,使得HTML文档无法被链接,进而无法运行。
解决方法:CSS也叫层叠样式表单,是用于(增强)控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。设置外部CSS文件的链接,路径为href="css/style.css",即将CSS文件和HTML文档放在同一个路径下。如图:
-
编译出错Undefined symbol USART_Init
问题描述:编译时出现Undefined symbol USART_Init(referred from usart.o)
解决办法:检查头文件路径是否添加;检查相对应的库是否添加。本项目实现过程中遇见的错误是没有添加库文件stm32f4xx_usart.c,添加相应的库文件即可。如图:
-
出现Symbol xxx multiply defined(by usart.o and main.o)报错
问题描述:两个.c文件出现了相同的定义变量(其实是main.c与usart.c引用了usart.h,而变量是再usart.h中定义的,导致变量被重复定义)。如图所示:
解决办法:首先,不应该在头文件.h中定义变量,而是直接在.c中定义。然后在主程序.h中将相应的变量定义为extern。由于.h是被所有源文件包含的,因此相应的变量变为extern,便可以直接使用了。
-
出现“Invalid ROM Table”错误
问题描述:使用ST-Link下载程序时出现”Invalid ROM Table”错误,如图:
解决办法:先断电开发板,再把开发板上的BOOT0接电位1,接着keil下载配置修改一下:进入Debug--Settings--Flash Download页面,把Program、Verify、Reset and Run前面的勾干掉下载程序,即擦除。接下来把开发板上的BOOT0换回电位0,再恢复keil下载配置,即勾选上rogram、Verify、Reset and Run,然后即可正常下载。
注:这只是恢复下载的办法,然后可以下载原先正常的程序。导致的根本原因是最近的一次程序不正常,程序内部的晶振频率设置错误导致。那么,其实造成这个结果的程序并不是本次下载提示这个错误的程序,而是上次的下载导致,只不过是下载此程序的时候才发现而已。
解决根源的方法:找到上次下载的程序,其实是程序里的晶振配置与外部实际的晶振配置不匹配导致的。先修改此文件stm32f4xx.h,结合外部实际晶振,修改这个值,并不一定是25,只不过本项目的是25。如图:
然后修改system_stm32f4xx.c,修改其数值为25.如图:
6. 附代码
-
HTML代码
<!--标准网页声明-->
<!DOCTYPE html>
<!--!文档类型,它的目的是要告诉标准通用标记语言解析器,它应该使用什么样的文档类型定(DTD)来解析文档,在html5文档中,一般写为<!DOCTTYPE html> ;值得注意的是,<!DOCTTYPE>不属于html标签。-->
<html >
<!--html标签,是html文档的根标签,所有的网页标签都放在这对标签中-->
<head>
<meta charset="UTF-8">
<title>CSS3实现基于STM32的智能蔬菜大棚DEMO演示</title>
<!--需设置CSS文件路径和相关联的HTML文档-->
<link rel="stylesheet" href="css/style.css">
<!--设置网页自动刷新,来不断获取数据,模拟蔬菜大棚一周时间内的数据变化-->
<meta http-equiv="refresh" content="600">
<!--若需要跳转到指定页面
<meta http-equiv="refresh" content="10;url=http://www.51jfgou.com">
-->
</head>
<body>
<br />
<br />
<h1 style="text-align: center;font-size: 28px">CSS3实现基于STM32的智能蔬菜大棚设计</h1>
<h2 style="text-align: center;font-size: 22px;">温度,湿度,光照强度变化的柱形图表DEMO演示</h2>
<!--<p><button onclick="clickCounter()" type="button">请点击这里</button></p>
<div id="result"></div>-->
<div>
<!--输出前空两格-->
<p style="text-indent: 2em">
Current Data
</div>
<div>
Temperature:<span id="showserverstr"></span>
</div>
<div>
Humidity:<span id="showserverstr1"></span>
</div>
<div>
Light intensity:<span id="showserverstr2"></span>
</div>
<!--<button type="button" onclick="getStrFromServer()">Start</button>-->
<div id="bar-chart">
<div class="graph">
<ul class="x-axis">
<li><span>06-01</span></li>
<li><span>06-02</span></li>
<li><span>06-03</span></li>
<li><span>06-04</span></li>
<li><span>06-05</span></li>
<!--这是注释-->
<li><span>06-06</span></li>
<li><span>06-07</span></li>
</ul>
<ul class="y-axis">
<li><span>100</span></li>
<li><span>75</span></li>
<li><span>50</span></li>
<li><span>25</span></li>
<li><span>0</span></li>
</ul>
<div class="bars">
<div class="bar-group">
<!--sta-1表示第一种颜色-->
<!--bar-1表示图表中的第一个柱子-->
<!--bar表示柱子的所有属性-->
<div class="bar bar-1 stat-1" style="height: 30.63%;">
<p>30.63</p>
<span>4080</span>
</div>
<div class="bar bar-2 stat-2" style="height: 62.67%;">
<p>62.67</p>
<span>5680</span>
</div>
<div class="bar bar-3 stat-3" style="height: 2%;">
<p>2</p>
<span>1040</span>
</div>
</div>
<div class="bar-group">
<div class="bar bar-4 stat-1" style="height: 23.34%;">
<p>23.34</p>
<span>6080</span>
</div>
<div class="bar bar-5 stat-2" style="height: 53.56%;">
<p>53.56</p>
<span>6880</span>
</div>
<div class="bar bar-6 stat-3" style="height: 22%;">
<p>22</p>
<span>1760</span>
</div>
</div>
<div class="bar-group">
<div class="bar bar-7 stat-1" style="height: 35.14%;">
<p>35.14</p>
<span>6240</span>
</div>
<div class="bar bar-8 stat-2" style="height: 34.37%;">
<p>34.37</p>
<span>5760</span>
</div>
<div class="bar bar-9 stat-3" style="height: 65%;">
<p>65</p>
<span>2880</span>
</div>
</div>
<div class="bar-group">
<div class="bar bar-10 stat-1" style="height: 29.84%;">
<p>29.84</p>
<span>3520</span>
</div>
<div class="bar bar-11 stat-2" style="height: 87.45%;">
<p>87.45</p>
<span>5120</span>
</div>
<div class="bar bar-12 stat-3" style="height: 59%">
<p>59</p>
<span>4720</span>
</div>
</div>
<div class="bar-group">
<div class="bar bar-13 stat-1" style="height: 28.52%;">
<p>28.52</p>
<span>2240</span>
</div>
<div class="bar bar-14 stat-2" style="height: 97.45%;">
<p>97.45</p>
<span>2640</span>
</div>
<div class="bar bar-15 stat-3" style="height: 10%;">
<p>10</p>
<span>7520</span>
</div>
</div>
<div class="bar-group">
<div class="bar bar-16 stat-1" style="height: 29.83%;">
<p>29.83</p>
<span>2240</span>
</div>
<div class="bar bar-17 stat-2" style="height: 32.46%;">
<p>32.46</p>
<span>2640</span>
</div>
<div class="bar bar-18 stat-3" style="height: 4%;">
<p>4</p>
<span>7520</span>
</div>
</div>
<div class="bar-group">
<div class="bar bar-19 stat-1" style="height: 34.27%;">
<p>34.27</p>
<span>2240</span>
</div>
<div class="bar bar-20 stat-2" style="height: 56.48%;">
<p>56.48</p>
<span>2640</span>
</div>
<div class="bar bar-21 stat-3" style="height: 87%;">
<p>87</p>
<span>7520</span>
</div>
</div>
</div>
</div>
</div>
<div style="text-align:center;clear:both">
<script src="/gg_bd_ad_720x90.js" type="text/javascript"></script>
<script src="/follow.js" type="text/javascript"></script>
</div>
</body>
<script>
/*设置页面自动刷新*/
function myrefresh(){
window.location.reload();
}
setTimeout('myrefresh()',1000000); //指定1000秒刷新一次
/*设置按钮进行刷新*/
function clickCounter() {
if(typeof(Storage) !== "undefined") {
if (sessionStorage.clickcount) {
sessionStorage.clickcount = Number(sessionStorage.clickcount)+1;
} else {
sessionStorage.clickcount = 1;
}
document.getElementById("result").innerHTML = "在本 session 中,您已经点击这个按钮 " + sessionStorage.clickcount + " 次。";
} else {
document.getElementById("result").innerHTML = "抱歉!您的浏览器不支持 Web Storage ...";
}
}
function getStrFromServer() {
var judge = 1;/*判断是否运行了该函数*/
var xmlhttp;
if (window.XMLHttpRequest) {// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp = new XMLHttpRequest();
}
else {// IE6, IE5 浏览器执行代码
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function ()
{
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
var revtext = xmlhttp.responseText;
var revObj = JSON.parse(revtext);
var getdata = JSON.parse(revObj.data);
var message = revObj.message;
var result = revObj.result;
if (result != 0)
alert(message);
else
{
//alert(getdata);
var val1 = parseFloat(getdata.val1) >> 8 ;
var val2 = (parseFloat(getdata.val1) & 0xFF) / 100;
document.getElementById("showserverstr").innerHTML =val1+val2;
/*document.getElementById() 是根据标签的ID获取到DOM*/
var val3 = parseFloat(getdata.val2) >> 8 ;
var val4 = (parseFloat(getdata.val2) & 0xFF) / 100;
document.getElementById("showserverstr1").innerHTML =val3+val4;
document.getElementById("showserverstr2").innerHTML =getdata.val3;
}
}
}
xmlhttp.open("GET", "http://traindev.sinoioetech.com/DeviceCtrl/DevCtrl/GetDeviceCurState?devid=DC201001661CFAFF&devsecret=123456", true);
xmlhttp.send();
judge = 0;
}
window.onload = getStrFromServer;/*函数自动执行,不需要按键触发*/
function sendCmdFromServer() {
var va15 =document.getElementById("inputCmd").value
if(va15==""){
document.getElementById("sendCmdResult").innerHTML = "指令不能为空";
return;
}
if(parseInt(va15)>65535){
document.getElementById("sendCmdResult").innerHTML = "指令数值最大为65535";
return;
}
document.getElementById("sendCmdResult").innerHTML = "";
var xmlhttp;
if (window.XMLHttpRequest) {// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp = new XMLHttpRequest();
}
else {// IE6, IE5 浏览器执行代码
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var revtext = xmlhttp.responseText;
var revObj = JSON.parse(revtext);
var message = revObj.message;
var result = revObj.result;
if (result != 0)
alert(message);
else
{
document.getElementById("sendCmdResult").innerHTML = revtext;
}
}
}
xmlhttp.open("POST", "http://traindev.sinoioetech.com/DeviceCtrl/DevCtrl/OptDeviceCommand?devid=DC201001661CFAFF&devsecret=123456&cmdname=val1&cmdvalue="+ va15 , true);
xmlhttp.send();
}
</script>
</html>
-
CSS代码
/*@-webkit-keyframes以百分比来规定改变发生的时间,
或者通过关键词 "from" 和 "to",等价于 0% 和 100%。0% 是动画的开始时间,100% 动画的结束时间*/
@-webkit-keyframes animate-width
{
0% {
width: 0;
}
100% {
visibility: visible;
}
}
/*keyframes是动画*/
@-moz-keyframes animate-width {
0% {
width: 0;
}
100% {
visibility: visible;
}
}
@keyframes animate-width {
0% {
width: 0;
}
100% {
visibility: visible;
}
}
@-webkit-keyframes animate-height {
0% {
height: 0;
}
100% {
visibility: visible;
}
}
@-moz-keyframes animate-height {
0% {
height: 0;
}
100% {
visibility: visible;
}
}
@keyframes animate-height {
0% {
height: 0;/*动画效果:缓和代入感*/
}
100% {
visibility: visible;
}
}
body {
background-color: #3b4150;/*背景颜色*/
font-family: arial, sans-serif;
color: #cdcdcd;
}
/*设置整个图表的位置属性*/
#bar-chart
{
height: 380px;
width: 80%;/*整个图表宽度范围占屏幕的比例*/
position: relative;
margin: 50px auto 0;
/*margin表示各个方向的外边距,顺序为上,右,下,左。auto的意思是页面左右空白自适应并且相等。
表示top为50px,right为自适应,bottom为0;由于left缺省,所以自动将它设为自适应。*/
}
#bar-chart * {
box-sizing: border-box;
}
#bar-chart .graph {
height: 283px;/*px就是pixel的缩写,就是我们常说的像素*/
position: relative;/*生成相对定位的元素*/
}
/*定义各个小组的属性*/
#bar-chart .bars {
height: 253px;
padding: 0 2%;/*padding表示内边距,其中上下内边距为0,左右内边距为2%*/
position: absolute;/*生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。
元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。*/
width: 100%;
z-index: 10;/*z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。*/
}
/*定义各个大组的属性*/
#bar-chart .bar-group
{
display: block;
float: left;
height: 100%;
position: relative;
width: 10%;/*各个组之间的间距*/
margin-right: 5%;/*设置各个组的右外边距*/
}
#bar-chart .bar-group:last-child {
margin-right: 0;/*设置最后一个组的右外边距为0*/
}
/*定义bar标签的用法*/
#bar-chart .bar-group .bar
{
visibility: hidden;
height: 0;
-webkit-animation: animate-height;
-moz-animation: animate-height;
animation: animate-height;
/*指定切换效果的速度,
此为中间两个点(x1,y1,x2,y2)
贝塞尔曲线通过控制曲线上的四个点(起始点、终止点以及两个相互分离的中间点)来创造、编辑图形,
绘制出一条光滑曲线并以曲线的状态来反映动画过程中速度的变化。*/
animation-timing-function: cubic-bezier(0.35, 0.95, 0.67, 0.99);
-webkit-animation-timing-function: cubic-bezier(0.35, 0.95, 0.67, 0.99);
-moz-animation-timing-function: cubic-bezier(0.35, 0.95, 0.67, 0.99);
animation-duration: 0.4s;
-webkit-animation-duration: 0.4s;
-moz-animation-duration: 0.4s;
animation-fill-mode: forwards;
-webkit-animation-fill-mode: forwards;
box-shadow: 10px 0 2px rgba(0, 0, 0, 0.15);
/*box-shadow用来实现阴影效果,参数值分别为:阴影水平偏移值,垂直偏移值,阴影模糊值,rgba(R,G,B,透明度)*/
border: 1px solid #2d2d2d;
/*设置边框粗细为1px(粗细)、样式为实心的(样式)、颜色为红色(颜色)的边框*/
border-radius: 3px 3px 0 0;
/*向该元素添加圆角边框*/
bottom: 0;
cursor: pointer;
height: 0;
position: absolute;
text-align: center;
width: 25%;/*柱子的宽度*/
}
#bar-chart .bar-group .bar:nth-child(2) {
left: 35%;/*第一个柱子和第二个柱子之间的间隔*/
}
#bar-chart .bar-group .bar:nth-child(3) {
left: 70%;/*第一个柱子和第三个柱子之间的间隔*/
}
#bar-chart .bar-group .bar span /*定义span的用法:none没啥用*/
{
display: none;
}
#bar-chart .bar-group .bar p /*定义p的用法,其中p用来注释具体的温度数值*/
{
position:relative;
bottom:25px;/*距离柱子的距离*/
font-size:10px;/*字号大小*/
text-align:center;
}
#bar-chart .bar-group .bar q
{
display: none;
}
#bar-chart .bar-group .bar-1 /*第一个柱子*/
{
animation-delay: 0.3s;/*柱子显示出来的所用的时间*/
-webkit-animation-delay: 0.3s;
}
#bar-chart .bar-group .bar-2 /*第二个柱子*/
{
animation-delay: 0.4s;
-webkit-animation-delay: 0.4s;
}
#bar-chart .bar-group .bar-3 /*第三个柱子,以此类推*/
{
animation-delay: 0.5s;
-webkit-animation-delay: 0.5s;
}
#bar-chart .bar-group .bar-4 {
animation-delay: 0.6s;
-webkit-animation-delay: 0.6s;
}
#bar-chart .bar-group .bar-5 {
animation-delay: 0.7s;
-webkit-animation-delay: 0.7s;
}
#bar-chart .bar-group .bar-6 {
animation-delay: 0.8s;
-webkit-animation-delay: 0.8s;
}
#bar-chart .bar-group .bar-7 {
animation-delay: 0.9s;
-webkit-animation-delay: 0.9s;
}
#bar-chart .bar-group .bar-8 {
animation-delay: 1s;
-webkit-animation-delay: 1s;
}
#bar-chart .bar-group .bar-9 {
animation-delay: 1.1s;
-webkit-animation-delay: 1.1s;
}
#bar-chart .bar-group .bar-10 {
animation-delay: 1.2s;
-webkit-animation-delay: 1.2s;
}
#bar-chart .bar-group .bar-11 {
animation-delay: 1.3s;
-webkit-animation-delay: 1.3s;
}
#bar-chart .bar-group .bar-12 {
animation-delay: 1.4s;
-webkit-animation-delay: 1.4s;
}
#bar-chart .bar-group .bar-13 {
animation-delay: 1.5s;
-webkit-animation-delay: 1.5s;
}
#bar-chart .bar-group .bar-14 {
animation-delay: 1.6s;
-webkit-animation-delay: 1.6s;
}
#bar-chart .bar-group .bar-15 {
animation-delay: 1.7s;
-webkit-animation-delay: 1.7s;
}
#bar-chart .bar-group .bar-16 {
animation-delay: 1.8s;
-webkit-animation-delay: 1.8s;
}
#bar-chart .bar-group .bar-17 {
animation-delay: 1.9s;
-webkit-animation-delay: 1.9s;
}
#bar-chart .bar-group .bar-18 {
animation-delay: 2.0s;
-webkit-animation-delay: 2.0s;
}
#bar-chart .bar-group .bar-19 {
animation-delay: 2.1s;
-webkit-animation-delay: 2.1s;
}
#bar-chart .bar-group .bar-20 {
animation-delay: 2.2s;
-webkit-animation-delay: 2.2s;
}
#bar-chart .bar-group .bar-21 {
animation-delay: 2.3s;
-webkit-animation-delay: 2.3s;
}
#bar-chart ul {
list-style: none;
margin: 0;/*设置元素的外边距*/
padding: 0;/*padding表示内边距,其中上下内边距为0,左右内边距为0*/
}
/*定义x轴的属性*/
#bar-chart .x-axis {
bottom: 0;
position: absolute;
text-align: center;
width: 100%;
}
/*设置标签li的属性*/
#bar-chart .x-axis li {
float: left;
margin-right: 5%;
/*x轴横坐标间隔*/
/*设置与元素相关联的盒子模型的右外边距*/
font-size: 11px;/*x轴横坐标的显示大小*/
width: 10%;/*跟x轴的坐标间隔有关*/
}
#bar-chart .x-axis li:last-child {
margin-right: 0;
}
/*配置y轴属性*/
#bar-chart .y-axis {
position: absolute;
text-align: right;
width: 100%;/*线条显示长度*/
}
/*设置标签li的属性*/
#bar-chart .y-axis li {
border-top: 1px solid #4e5464;/*设置边框粗细为1px(粗细)、样式为实心的(样式)、颜色为**(颜色)的边框*/
display: block;
height: 63.25px;/*间隔*/
width: 100%;/*线条显示长度*/
}
#bar-chart .y-axis li span {
display: block;
font-size: 11px;
margin: -10px 0 0 -60px;
padding: 0 10px;/*padding表示内边距,其中上下内边距为0,左右内边距为10%*/
width: 40px;
}
#bar-chart .stat-1 {/*第一种颜色#ff4500*/
background-image: -webkit-linear-gradient(left, #ff4500 0%, #ff4500 47%, #cf3a02 50%, #cf3a02 100%);
background-image: linear-gradient(to right, #ff4500 0%, #ff4500 47%, #cf3a02 50%, #cf3a02 100%);
}
#bar-chart .stat-2 {/*第二种颜色*/
background-image: -webkit-linear-gradient(left, #b8f123 0%, #b8f123 47%, #79a602 50%, #79a602 100%);
background-image: linear-gradient(to right, #b8f123 0%, #b8f123 47%, #79a602 50%, #79a602 100%);
}
#bar-chart .stat-3 {/*第三种颜色*/
background-image: -webkit-linear-gradient(left, #00c5ff 0%, #00c5ff 47%, #0383a9 50%, #0383a9 100%);
background-image: linear-gradient(to right, #00c5ff 0%, #00c5ff 47%, #0383a9 50%, #0383a9 100%);
}
-
Keil代码(WIFI模块部分代码,其他太长不方便贴出来)
#include <string.h>
#include "WIFI_E103_W02.h"
#include "CommonUsart.h"
#include "led.h"
#include "delay.h"
#include "oled.h"
#include "USB_To_Usart.h"
#include "timer.h"
char *E103_W02_AT="+++";//开启AT指令
char *E103_W02_AT_EXIT="AT+EXIT\r\n";//退出AT指令
char *E103_W02_STA="AT+ROLE=STA\r\n";//设置WIFI模式AP/STA
//char *SET_="AT+STA=ysnhzk.cn,2,880115ysn\r\n";
//设置STATION 参数(重启生效),即要连接的WIFI
//char *E103_W02_SET="AT+STA=chipcloud,2,chipcloud2017\r\n";
//char *E103_W02_SET="AT+STA=zwy_hw,2,zwyiot2017\r\n";
char *E103_W02_SET="AT+STA=TP-LINK_3FA2,2,xwqxwn971\r\n";
//创建socket 客户端
//char *E103_W02_CLIENT="AT+SOCK=TCP,CLIENT,192.168.31.162,3002,8887\r\n";
char *E103_W02_CLIENT="AT+SOCK=TCP,CLIENT,123.57.255.14,3003,8887\r\n";
//char *E103_W02_CLIENT="AT+SOCK=TCP,CLIENT,192.168.0.104,3002,8887\r\n";
//复位指令
char *E103_W02_RST="AT+RST\r\n";
//设置模块的ip信息
char *E103_W02_APIP="AT+APIP=192.168.1.12,255.255.255.0,192.168.1.12,192.168.1.1\r\n";
//恢复出厂设置(复位有效)
char *E103_W02_RESTORE="AT+RESTORE\r\n";
//查询模块当前socket信息
char *E103_W02_CLIENT_="AT+SOCK=?\r\n";
//查询当前模块版本信息
char *E103_W02_VER="AT+VER=?\r\n";
//开启AT指令的回显信息
char *E103_W02_AT_RES = "Entered AT mode";
//退出AT指令的回显信息
char *E103_W02_AT_EXIT_RES = "Exited AT mode";
//设置STA的回显信息
char *E103_W02_STA_RES = "Set STA mode";
//STA链接wifi的回显信息
char *E103_W02_SET_RES = "STA update OK";
//设置socket客户端的回显信息
char *E103_W02_CLIENT_RES = "Socket update OK";
//复位回显信息
char *E103_W02_RST_RES = "Module Reboot";
//恢复出厂设置回显信息
char *E103_W02_RESTORE_RES = "Restore OK";
COMMON_USART_PARAMET E103_UsartParamet;
u8 getLinkStatus(void){
return GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_6);
}
u8 WIFI_USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收数据长度
u16 WIFI_USART_RX_STA=0;
void UART4_IRQHandler(void){
u8 res;
if(USART_GetITStatus(E103_UsartParamet.COMMON_USART, USART_IT_RXNE) != RESET)//接收到数据
{
res =USART_ReceiveData(E103_UsartParamet.COMMON_USART);
if((WIFI_USART_RX_STA&(1<<15))==0)//接收完的一批数据,还没有被处理,则不再接收其他数据
{
if(WIFI_USART_RX_STA<200) //还可以接收数据
{
TIM_SetCounter(TIM3,0);//计数器清空
if(WIFI_USART_RX_STA==0)
TIM_Cmd(TIM3, ENABLE); //使能定时器3
WIFI_USART_RX_BUF[WIFI_USART_RX_STA++]=res; //记录接收到的值
}else
{
WIFI_USART_RX_STA|=1<<15; //强制标记接收完成
}
}
}
}
//wifi 发送数据
void E103_W02_SendData( uint8_t *Data,int l){
//int l = sizeof(Data) ;
//int l=strlen((const char *)Data);
COMMON_USART_OUT(E103_UsartParamet.COMMON_USART,Data,l);
}
//wifi 发送数据
void E103_W02_SendAT( uint8_t *Data){
//int l = sizeof(Data) ;
int l=strlen((const char *)Data);
E103_W02_SendData(Data,l);
}
void init_E103_w02(void){
GPIO_InitTypeDef GPIO_InitStructure;
//wifi 的串口参数这是参考架构体 COMMON_USART_PARAMET
E103_UsartParamet.COMMON_USART=UART4;
E103_UsartParamet.COMMON_USART_CLK=RCC_APB1Periph_UART4;
E103_UsartParamet.COMMON_USART_BAUDRATE=115200;
E103_UsartParamet.COMMON_USART_RX_GPIO_CLK=RCC_AHB1Periph_GPIOC;
E103_UsartParamet.COMMON_USART_RX_GPIO_PORT=GPIOC;
E103_UsartParamet.COMMON_USART_RX_PIN=GPIO_Pin_11;
E103_UsartParamet.COMMON_USART_RX_SOURCE=GPIO_PinSource11;
E103_UsartParamet.COMMON_USART_TX_GPIO_CLK=RCC_AHB1Periph_GPIOC;
E103_UsartParamet.COMMON_USART_TX_GPIO_PORT=GPIOC;
E103_UsartParamet.COMMON_USART_TX_PIN=GPIO_Pin_10;
E103_UsartParamet.COMMON_USART_TX_SOURCE=GPIO_PinSource10;
E103_UsartParamet.COMMON_USART_AF=GPIO_AF_UART4;
E103_UsartParamet.COMMON_USART_IRQ=UART4_IRQn;
initialCOMMON_USART(E103_UsartParamet);
//SendDataToDevice("Test486", 7);
//使能GPIOD时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
//配置GPIOD7的参数
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //GPIOD7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上啦
//初始化GPIOD7
GPIO_Init(GPIOD,&GPIO_InitStructure);
//输出高电平,使此I/O脚的功能失效
GPIO_SetBits(GPIOD,GPIO_Pin_7);//GPIOD7设置高,
//配置GPIOC9的参数
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIOD6
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //输入
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
//GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上啦
//初始化GPIOD6
GPIO_Init(GPIOD,&GPIO_InitStructure);
//定时器时钟84M,分频系数8400,所以84M/8400=10Khz的计数频率,计数30次为3ms
TIM3_Int_Init(30-1,8400-1); //100ms中断
WIFI_USART_RX_STA=0; //清零
TIM_Cmd(TIM3, DISABLE); //关闭定时器3
}
//比对两个字符串是否一样,或者string1包含string2
unsigned char my_strncmp(unsigned char *string1,unsigned char *string2,unsigned int count)
{
unsigned char res=0,k=1;
while(*string1!='\0'&&*string2!='\0'&&k<=count)
{
k++;
if(*string1==*string2)
{
string1++;
string2++;
}
else{
res = *string1-*string2;
break;
}
}
return res;
}
//退出AT指令模式
void AT_Exit(){
E103_W02_SendAT((uint8_t*)E103_W02_AT_EXIT);
delay_ms(500);
if(WIFI_USART_RX_STA!=0){
// LCD_ShowString(30,240,210,48,12,USART_RX_BUF);
}
WIFI_USART_RX_STA=0;
}
//连接服务器(配置WIFI模式、要连接的wifi账号密码、服务器IP,端口号),复位后生效
char link_server(){
int AT_id=0;
char AT_res=0;
while(AT_res==0){
switch(AT_id){
case 0://发送开启指令的命令
E103_W02_SendAT((uint8_t*)E103_W02_AT);
delay_ms(500);//发送到接收回应有个间隔
SendDataToDevice_a(WIFI_USART_RX_BUF,WIFI_USART_RX_STA);
//LCD_ShowString(30,140,210,48,12,USART_RX_BUF);//显示回应信息
//判断是否接收到数据,和回应的信息是否是我们需要的(需要我们进行比对)
AT_res=WIFI_USART_RX_STA=0?1:my_strncmp(WIFI_USART_RX_BUF,(unsigned char *)E103_W02_AT_RES,strlen(E103_W02_AT_RES));
break;
case 1://发送设置wifi STA模式命令
E103_W02_SendAT((uint8_t*)E103_W02_STA);
delay_ms(500);
SendDataToDevice_a(WIFI_USART_RX_BUF,WIFI_USART_RX_STA^0x8000);
AT_res=WIFI_USART_RX_STA=0?1:my_strncmp(WIFI_USART_RX_BUF,(unsigned char *)E103_W02_STA_RES,strlen(E103_W02_STA_RES));
break;
case 2://发送 设置要连接wifi的信息(名字、加密方式、密码)
E103_W02_SendAT((uint8_t*)E103_W02_SET);
delay_ms(3000);
SendDataToDevice_a(WIFI_USART_RX_BUF,WIFI_USART_RX_STA^0x8000);
AT_res=WIFI_USART_RX_STA=0?1:my_strncmp(WIFI_USART_RX_BUF,(unsigned char *)E103_W02_SET_RES,strlen(E103_W02_SET_RES));
break;
case 3://发送 设置socket信息命令(模式、协议、IP、端口号)
E103_W02_SendAT((uint8_t*)E103_W02_CLIENT);
delay_ms(500);
SendDataToDevice_a(WIFI_USART_RX_BUF,WIFI_USART_RX_STA^0x8000);
AT_res=WIFI_USART_RX_STA=0?1:my_strncmp(WIFI_USART_RX_BUF,(unsigned char *)E103_W02_CLIENT_RES,strlen(E103_W02_CLIENT_RES));
break;
case 4://发送复位命令
E103_W02_SendAT((uint8_t*)E103_W02_RST);
delay_ms(500);
SendDataToDevice_a(WIFI_USART_RX_BUF,WIFI_USART_RX_STA^0x8000);
AT_res=WIFI_USART_RX_STA=0?1:my_strncmp(WIFI_USART_RX_BUF,(unsigned char *)E103_W02_RST_RES,strlen(E103_W02_RST_RES));
break;
}
AT_id++;
WIFI_USART_RX_STA=0;
if(AT_id==5){
break;
}
}
AT_Exit();//发送退出AT指令模式的命令
return AT_res;
}
char restE103_W02(void){
int AT_id=0;
char AT_res=0;
while(AT_res==0){
switch(AT_id){
case 0://发送开启指令的命令
E103_W02_SendAT((uint8_t*)E103_W02_AT);
delay_ms(1000);
SendDataToDevice_a(WIFI_USART_RX_BUF,WIFI_USART_RX_STA^0x8000);
//OLED_ShowString(0,50, USART_RX_BUF,12);OLED_Refresh_Gram();//更新显示到OLED
AT_res=WIFI_USART_RX_STA=0?1:my_strncmp(WIFI_USART_RX_BUF,(unsigned char *)E103_W02_AT_RES,strlen(E103_W02_AT_RES));
break;
case 1://发送恢复出厂设置的命令
E103_W02_SendAT((uint8_t*)E103_W02_RESTORE);
delay_ms(2000);
SendDataToDevice_a(WIFI_USART_RX_BUF,WIFI_USART_RX_STA^0x8000);
//OLED_ShowString(0,50, USART_RX_BUF,12);OLED_Refresh_Gram();//更新显示到OLED
AT_res=WIFI_USART_RX_STA=0?1:my_strncmp(WIFI_USART_RX_BUF,(unsigned char *)E103_W02_RESTORE_RES,strlen(E103_W02_RESTORE_RES));
break;
case 2://发送复位命令
E103_W02_SendAT((uint8_t*)E103_W02_RST);
delay_ms(1000);
SendDataToDevice_a(WIFI_USART_RX_BUF,WIFI_USART_RX_STA^0x8000);
//OLED_ShowString(0,50, USART_RX_BUF,12);OLED_Refresh_Gram();//更新显示到OLED
AT_res=WIFI_USART_RX_STA=0?1:my_strncmp(WIFI_USART_RX_BUF,(unsigned char *)E103_W02_RST_RES,strlen(E103_W02_RST_RES));
break;
}
AT_id++;
WIFI_USART_RX_STA=0;
if(AT_id==3){
break;
}
}
AT_Exit();//发送退出指令模式的命令
return AT_res;
}