第5章 树莓派物联网监测 树莓派物联 网监测系统 本章以第4章搭建的智能小车作为监测节点,在树莓派内部搭建LAMP(Linux+Apache+MySQL+PHP)服务器,编写Web端监控软件,软件前端发送指令至树莓派服务器后端进行解析,实现远程数据采集、传输以及对监测节点的控制。 5.1服务器环境搭建 LAMP架构包括Linux操作系统、Apache网络服务器、MySQL数据库以及PHP编程语言,是目前最为成熟的网站应用模式,能够提供动态Web站点服务及其应用开发环境,具有通用、跨平台、高性能的优势。本节使用LAMP来构建树莓派Web端监控界面,即在Linux下开发网站,由Apache HTTP服务器提供内容,在MySQL数据库中存储内容,用PHP来实现程序逻辑。 5.1.1安装Apache服务器 Apache是一个开源的网页服务器软件,可以通过HTTP协议提供HTML文件服务。Apache可以在大多数计算机操作系统中运行,也支持树莓派网页服务。由于其多平台和安全性而被广泛使用,并且可通过简单的API扩展,将Perl/Python等解释器编译到服务器中。树莓派安装Apache服务器的步骤如下: (1) 在终端输入sudo aptget update更新源列表,然后输入sudo aptget install apache2 y安装Apache。 (2) 安装完成后,需要测试Apache服务器是否安装成功。在树莓派浏览器的地址栏输入“127.0.0.1”或“localhost”访问Apache服务器,若出现如图51所示界面,则表示Apache服务器安装成功。 图51访问Apache服务器 在终端输入命令sudo service apache2 status可以查看Apache服务器的状态,分别输入sudo service apache2 start/stop/restart来控制Apache服务器的启动、停止或重启。 5.1.2安装MySQL数据库 数据库是按照数据结构来组织、存储和管理数据的仓库,用户可以对数据库中的数据进行增、删、查、改等操作。MySQL是最流行的关系型数据库管理系统应用软件之一,因其体积小、速度快、开源的特点,在中小型网站数据库开发中被广泛使用。在树莓派上安装MySQL数据库的步骤如下: (1) 在终端输入命令aptget install mariadbserver(mariadb是发展最快的MySQL分支版本),待安装完成后,输入sudo nano /etc/apache2/apache2.conf打开Apache服务器的配置文件,将文件中的“AllowOverride None”修改为“AllowOverride All”,如图52所示。 图52配置Apache服务器 (2) 在终端输入sudo mysql u root p命令后直接按 Enter键即可登录MySQL数据库(初始登录时的默认密码为空)。 (3) 在数据库命令行输入update mysql.user set plugin='mysql_native_password';重置加密方式,再输入update mysql.user set password=PASSWORD("12345678") where User='root'; 来更改数据库用户名和密码(本章案例使用的用户名/密码为root/12345678,读者可自行设定)。 (4) 最后再输入flush privileges;来刷新权限信息, 在键盘上按Ctrl+C组合键退出数据库。需要说明的是,对数据库进行操作的命令要以分号结尾。 注意: 如果出现MySQL登录ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password)问题,可以输入sudo nano /etc/mysql/mariadb.conf.d/50server.cnf打开配置文件,找到[mysqld]配置项并在该项下添加 skipgranttables,然后保存文件。输入sudo service mysql restart重启 MySQL,再次输入sudo mysql u root p命令,当需要输入密码时,直接按Enter键便可以不用密码登录到数据库中。 5.1.3安装PHP PHP是一种基于服务器端来创建动态网站的脚本语言,具有开源免费、简单易懂、跨平台性强的特点。PHP使用非常广泛,兼容几乎所有的Web服务器,支持几乎所有流行的数据库以及操作系统。当服务器通过浏览器收到网页请求时,PHP解析出需要在页面上显示的内容,然后将该页面发送到浏览器。PHP是将程序嵌入到HTML文档中去执行的,可以比CGI或者Perl更快速地执行动态网页。树莓派安装PHP的步骤如下: (1) 在终端输入sudo aptget install php phpmysql完成PHP和MySQL连接库的安装。 (2) 为了测试PHP,输入sudo nano /var/www/html/test.php,在文本编辑器中输入以下内容: (3) 在浏览器中访问“http://192.168.137.3(树莓派IP地址)/test.php”,若出现如图53所示界面则代表PHP测试成功。需要说明的是,后文中网站开发的源程序文件都必须存放到树莓派/var/www/html目录下才能正常访问。 图53PHP测试 5.1.4安装phpMyAdmin phpMyAdmin是一个以PHP为基础的MySQL数据库管理工具,让用户以Web接口管理MySQL数据库。通过phpMyAdmin 可以对数据库进行增、删、查、改等操作。树莓派安装phpMyAdmin的步骤如下: (1) 在终端输入sudo aptget install phpmyadmin安装phpMyAdmin,如图54所示,选择apache2后按空格键确定,随后选择默认选项并一直按Enter键直到界面关闭。 图54选择apache2 (2) 安装完成后,需要将phpmyadmin链接到/var/www/html目录下,命令为sudo ln s /usr/share/phpmyadmin /var/www/html。 (3) 登录phpmyadmin,在浏览器地址栏输入“树莓派IP地址/phpmyadmin”出现如图55所示的界面,输入之前在安装MySQL数据库时设置好的用户名和密码(root/12345678)登录即可,接下来即可在数据库管理界面中进行数据的相关操作,如图56所示。 图55phpMyAdmin登录界面 图56数据库管理界面 经过上述步骤,网站所需的开发环境LAMP就已搭建完成。 5.2树莓派状态读取 为了保证网站服务器稳定可靠地运行,有必要对树莓派的工作状态进行实时监测。Pi Dashboard(Pi 仪表盘)是一款开源的物联网设备监控工具,主要用于树莓派平台以及其他树莓派硬件产品。只需在树莓派上配置好 PHP环境,就可方便地部署Pi仪表盘,并通过WebUI来监控树莓派的状态。目前,Pi仪表盘的监测项包括CPU基本信息、状态和使用率,内存、缓存、SWAP分区、SD卡使用情况,网络接口的实时数据以及树莓派主机名、操作系统、IP地址、运行时间等信息。 要实现树莓派状态的读取,首先需要安装Apache和PHP,具体步骤已在5.1节中做了介绍, 此处不再赘述。然后,在终端输入cd /var/www/html命令切换到相应的目录,接着输入sudo git clone https://github.com/nxez/pidashboard.git直接通过GitHub下载Pi Dashboard项目的源码到树莓派的/var/www/html目录下。 在树莓派中部署好Pi Dashboard后,通过http://树莓派IP/pidashboard即可访问Pi仪表盘,如图57所示。如果页面无法正常显示,则可以尝试在树莓派终端进行如下操作: 先切换到/var/www/html目录,再执行命令sudo chown R wwwdata pidashboard。该命令会将pidashboard目录及其下面的所有文件与子目录的所有者更改为wwwdata(apache2的运行用户是wwwdata)。 图57Pi Dashboard界面 5.3内网穿透 LAMP服务器搭建好以后只能在局域网内才可以访问网站,如果需要实现外网访问,则可以使用内网穿透的方法来解决。内网穿透就是进行网络地址转换,将内部网络的私有IP地址转换为公网IP地址,让外网的终端设备能够访问本地的应用。内网穿透可以通过开放的第三方端口来实现。最常用的方法是安装花生壳内网穿透软件,再添加映射并配置映射端口的信息,外网地址是映射之后访问的域名,这样就可以像平常浏览网页一样通过域名来访问在树莓派上搭建的网站。 花生壳内网穿透的操作步骤如下: (1) 打开花生壳下载页面(https://hsk.oray.com/download/),选择并下载树莓派5.0版本(应用平台选择Raspberry Pi 32位),如图58所示。将下载的安装包通过FileZilla或Samba传输到树莓派中。 图58树莓派版花生壳 (2) 输入命令sudo s切换到管理员权限,使用cd命令切换到安装包所在的目录,输入安装命令dpkg i phtunnel_5_0_rapi_armhf.deb。安装成功后,会显示花生壳的SN码、默认密码(admin)以及远程管理地址http://b.oray.com,如图59所示。 图59安装树莓派版花生壳 (3) 输入命令phddns可以看到扩展功能,phddns start(启动)| status(状态)| stop(停止)| restart(重启)| reset(重置)|enable(开机自启动)|disable(关闭开机自启动)| version(版本),如图510所示,输入相应命令即可实现对应的功能。 (4) 添加内网穿透映射,具体过程如下: ① 花生壳安装完成后,复制生成的SN码,在浏览器中访问远程管理地址http://b.oray.com,在页面中输入SN码与默认密码(admin)登录,如图511所示。 ② 如果是首次登录,则需要通过扫码方式或者密码方式进行激活。前者是指使用花生壳管理APP或微信进行扫码激活; 后者是输入已注册的Oray账号和密码激活,如图512所示。 ③ 激活成功后,进入花生壳管理平台。若绑定SN码的账号只需要动态域名解析功能,直接单击页面中的“免费开通”按钮,如图513所示。还可以根据需要直接将账号升级为带有内网穿透功能的服务版本。 图510花生壳功能操作 图511登录花生壳 图512激活方式 图513开通内网穿透功能 ④ 开通内网穿透体验版后的界面如图514所示,单击页面上的“增加映射”按钮,根据页面提示填写映射所需的信息。 图514增加映射 下面以映射树莓派HTTP服务为例进行说明,如图515所示,自定义应用名称,自行选择应用图标,映射类型选择HTTP,外网域名是指用户申请用作外网访问的域名,外网端口选择默认的80,内网主机为树莓派的内网IP地址,内网端口是映射类型对应的80端口,带宽默认为1Mb/s(可购买额外的映射带宽)。确认映射内容无误后,单击“确定”按钮。 图515映射配置 ⑤ 添加映射完成后,生成格式为“域名+端口号”的外网访问地址,如图516所示。完成上述步骤后,在浏览器地址栏输入域名就可以实现外网访问树莓派了。 图516完成内网穿透 5.4Web软件开发 Web页面呈现给用户的画面称为前端,开发前端界面需要掌握HTML、CSS、JavaScript等基础编程语言,其中HTML 定义网页的内容,CSS 规定网页的布局与样式,JavaScript 对网页逻辑行为进行编程。在前端界面中单击按钮、新增/修改信息等操作需要后台解析,这些对用户是不可见的,称为后端。后端开发一般涉及数据库设计与PHP语言编程。前端、后端以及数据库设计完成后,需要将源代码部署到服务器才能正常运行和访问。 本案例中设计的Web软件包括实时监测图表、远程控制、树莓派运行状态3个功能模块,页面布局如图517所示。要实现这些功能需要在树莓派/var/www/html目录下创建html、css、js等必要的文件,文件目录如图518所示。此外,还需要创建Python脚本文件完成Web软件与树莓派之间的交互。所有文件的目录结构及其实现功能如图519所示。 图517页面布局 图518文件目录 图519(a)中的所有文件都在计算机端的VS Code环境下进行开发设计,如图520所示,然后再上传到树莓派/var/www/html 下对应的目录中; 图519(b)中Python脚本文件直接在Python3 IDLE中编写, 并保存在相应的目录中。如果读者自行修改了Python脚本存放的路径, 则需要在后面的代码中做相应的调整。 图519文件结构与功能 图520VS Code开发环境 5.4.1数据库设计 Web软件使用MySQL数据库存储、查看以及更新监测节点上传的数据,需要在数据库中创建信息表,具体过程如下: (1) 在终端输入sudo mysql u root p后按Enter键确认,会要求输入密码。前面MySQL安装过程中已将用户名root的密码设置为“12345678”,输入该密码登录成功后将会看到如图521所示的界面。 图521数据库登录成功 (2) 使用create database语句创建数据库,命令格式为create database 数据库名 (其他选项);,如图522所示,创建了一个名为demo的数据库。创建成功时,数据库终端会输出“Query OK,1 row affected”。在浏览器地址栏输入“http://树莓派IP地址/phpmyadmin”即可看到创建好的数据库,如图523所示。 图522创建数据库 图523phpMyAdmin显示创建的数据库 (3) 使用create table语句创建数据信息表,命令格式为create table 表名称 (列声明);,需要说明的是,在创建信息表前要先执行use 数据库名,然后再依次输入以下内容创建名为rt_node的信息表。 create table rt_node( id int(10) unsigned not null auto_increment primary key, node_id int(11) not null, lng decimal(9,6) not null, lat decimal(8,6) not null, temperature float(7,2) not null, humidity float(7,2) not null, co2 float(7,2) not null, tvoc float(7,2) not null, ch2o float(7,2) not null, pm25 float(7,2) not null, pm10 float(7,2) not null, heading_angle float(7,2) not null, time_node timestamp not null ); 在数据库终端执行以上命令的结果如图524所示,创建的信息表用来存储智能小车上传的各种数据信息,说明如表51所示。 图524创建信息表 表51实时数据信息表 字 段 名 称字 段 定 义数据类型及长度特 殊 要 求 id数据信息编号int(10)AUTO_INCREMENT node_id监测节点编号int(10)NOT NULL lng节点经度信息decimal(9,6)NOT NULL lat节点纬度信息decimal(8,6)NOT NULL temperature环境温度float(7,2)NOT NULL humidity环境湿度float(7,2)NOT NULL co2二氧化碳含量float(7,2)NOT NULL 续表 字 段 名 称字 段 定 义数据类型及长度特 殊 要 求 tvoc挥发性有机物信息float(7,2)NOT NULL ch2o甲醛含量float(7,2)NOT NULL pm25空气质量指数PM2.5float(7,2)NOT NULL pm10空气质量指数PM10float(7,2)NOT NULL heading_angle节点航向角(与正北方的角度)float(7,2)NOT NULL time_node信息写入时间timestampNOT NULL 5.4.2地图显示 在Web软件中插入百度地图,用气泡在地图中标注出监测节点的位置,当用户单击气泡时会弹出窗口并显示监测节点当前获取的各种数据信息。使用百度地图功能的步骤如下: (1) 在百度地图开发者平台(http://lbsyun.baidu.com/index.php)申请应用,首先登录百度账户(没有百度账户的用户需要先注册再登录),然后单击页面右上角的“控制台”菜单项,进入如图525(a)所示的界面,单击“创建应用”按钮,按照所需功能和要求依次填写相应内容,如图525(b)所示,其中应用名称可以随意填写,应用类型选择浏览器端,默认全选启动服务,Referer白名单填写网站的IP地址(即树莓派IP地址),最后单击“提交”按钮。 图525申请百度地图功能 (2) 应用创建成功后,就可以看到如图526所示的应用列表,单击“应用配置”栏中的“设置”按钮,可以对启用服务进行选择和修改,也可以修改和增删“Referer白名单”(只有在白名单上的域名和IP地址才能正常使用百度地图),如图527所示,其中的“应用AK”是在网站中嵌入百度地图功能最关键的密钥信息。 图526应用列表 图527设置应用 (3) 为了统一存放与管理树莓派脚本文件,在树莓派/home/pi目录下建立pi_code子目录,在其中创建脚本raspberrytest.py并输入以下代码,将智能小车采集的监测数据打包为JSON数据格式,通过HTTP协议传输至服务器解析。 import requests import json import time import random import gps #从监测节点读取GPS信息 import hmc5883l #从监测节点读取方向角 import air_quality_senor #从监测节点读取空气指标参数 url = 'http://192.168.137.3/server_demo/dataupload.php' headers = {'content-type' : "application/json", 'charset' : 'utf-8'} while True: '''读取智能小车传感器采集的数据,具体内容参见本书第3章的相关章节,传感器脚本 文件gps.py、hmc5883l.py以及air_quality_senor.py也都存放于pi_code目录下''' hmc = hmc5883l.HMC5883() sto = air_quality_senor.Multisensor() angle = hmc.read_HMC5883_data() sto_co2,sto_tvoc,sto_ch20,sto_pm25,sto_pm10,sto_humidity,sto_temp = sto.read_sensor_data() gps_lng,lng_EW,gps_lat,lat_NS = gps.GPS() body = {"node_id" : "1", "lng" : gps_lng, "lat" : gps_lat, "temperature" : sto_temp, "humidity" : sto_humidity, "co2" : sto_co2, "tvoc" : sto_tvoc, "ch2o" : sto_ch20, "pm25" : sto_pm25, "pm10" : sto_pm10, "heading_angle" : angle } response = requests.post(url, data = json.dumps(body), headers = headers) (4) 在树莓派/var/www/html/server_demo目录下创建dataupload.php文件,内容如下: 以上代码用于将智能小车上传的监测数据解析存储至数据库信息表中,可通过phpMyAdmin查看上传的数据信息,如图528所示。 图528查看监测数据 (5) 在树莓派/var/www/html目录下的index.html文件中(该文件较长,读者可以查看本书配套资源中的完整代码)添加密钥代码。 百度地图中数据显示的流程是: 先由Web前端向后端发起Ajax请求,获取数据库中实时数据信息表的监测节点数据; 然后,后端将数据以JSON字符串的格式打包传输至前端,前端对数据做解析渲染后在百度地图中显示,同时给气泡标注添加单击事件; 最后,设置定时器以固定的时间间隔来局部刷新地图界面,实现监测节点信息在地图中的实时显示。 在js_demo/app.js文件中对应的前端部分关键代码如下: //百度地图API功能 let map = new BMap.Map("allmap"); //创建Map实例 let point = new BMap.Point(114.404252,30.52761); map.centerAndZoom(point, 17); //初始化地图,设置中心点坐标和地图级别 let opts = { width : 100, //信息窗口宽度 height: 300, //信息窗口高度 title : ">>>节点信息如下:" , //信息窗口标题 enableMessage:true //设置允许信息窗发送短息 }; map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放 //添加地图类型控件 map.addControl(new BMap.MapTypeControl({ mapTypes:[ BMAP_NORMAL_MAP, BMAP_HYBRID_MAP ]})); map.setCurrentCity("武汉"); // 设置地图显示的城市,此项必须设置 对应的后端代码保存在server_demo/mapmarker.php文件中,内容如下: =5000){ mysqli_query($conn,'truncate table rt_node'); } //获取节点数据 $sql = 'SELECT node_id,lng,lat,temperature,humidity,co2,tvoc,ch2o,pm25,pm10,heading_angle FROM rt_node where node_id=1 ORDER BY id DESC LIMIT 0,1'; $result = mysqli_query($conn,$sql); $results = array(); $row = mysqli_fetch_assoc($result); $results[0] = $row; //将数组转成JSON格式 $json=json_encode($results); //关闭连接 mysqli_close($conn); echo $json; ?> 地图显示功能最终效果如图529所示,其中图529(a)标识监测节点在地图中的位置,图529(b)是单击气泡时在弹出窗口中显示节点的监测数据。 图529地图显示效果 5.4.3监测数据图表显示 通过在Web软件中加入监测数据实时图表显示的功能,可以直观地查看监测节点采集的数据及其变化趋势。ECharts是百度旗下的一个基于JavaScript 的开源数据可视化工具,能够在 计算机端和移动设备上流畅运行,可以提供直观、生动、可交互以及个性化定制的数据可视化图表(读者可以访问https://echarts.apache.org/zh/index.html查看使用教程和示例)。使用ECharts.js可以方便地将折线图嵌入到Web软件中。 实现监测数据折线图显示,需要在index.html文件
标签中添加来引入ECharts.js。实时数据图表显示的流程如下: 前端将节点编号与传感器数据类型通过Ajax请求传输至后端服务器; 后端接收数据后与数据库中实时数据信息表的最新监测数据进行匹配,并将监测数据取出打包为JSON字符串格式回传; 前端根据传感器数据种类在界面创建相应数量的div图表容器,将回传数据解析后分别写入容器中进行图表渲染。为了实时显示数据图表,设置了setInterval定时器,以500ms的时间间隔与后端建立长连接,不断获取最新的节点数据。此外,为了方便用户分析数据,在图表中标识出了数据的极值,图表更新过程中的最大值和最小值会以气泡的形式渲染。 图表显示功能在js_demo/app.js文件中对应的前端关键代码如下: let clear = setInterval(function(){ $.ajax({ //请求方式 type:"GET", //文件位置 url:"server_demo/rtchart.php?q=" + nodeid + "&v=" +ntype, //返回数据格式为JSON,也可以是其他格式 dataType: "json", //请求成功后要执行的函数 success:function(result){ if(result.length != 0){ $.each(result, function(i,item){ rt = item["time_node"].split(" ")[1]; if(rt != xdata[11]){ xdata.shift(); ydata.shift(); ydata.push(item[ntype]); xdata.push(rt); } }); }} }); myChart.setOption({ xAxis: {data: xdata}, series: [{data: ydata}] }); },500); 对应的后端代码保存在server_demo/server/rtchart.php文件中,内容如下: =5000){ mysqli_query($conn,'truncate table rt_node'); } //节点数据类型 $type = ["temperature","humidity","co2","tvoc","ch2o","pm25","pm10","heading_angle"]; $t_num = count($type); for($i=0;$i<$t_num;$i++){ if($v==$type[$i]){ $result = mysqli_query($conn,'SELECT '.$type[$i].', time_node FROM rt_node WHERE node_id="'.$q.'"'.'ORDER BY id DESC LIMIT 0,1'); $results = array(); while ($row = mysqli_fetch_assoc($result)) { $results[] = $row; } //将数组转成JSON格式 $data=json_encode($results); //关闭连接 mysqli_close($conn); echo $data; } } ?> 监测数据图表功能最终效果如图530所示,图中通过折线图实时显示了7种环境指标参数以及树莓派小车运动过程中的航向角。 图530监测数据图表显示 5.4.4节点远程控制 Web软件需要具有对监测节点进行远程控制的功能,通过网页上的按钮下达控制指令,例如,开启/关闭摄像头、拍照或录像以及控制智能小车运动。在界面中设置img标签来容纳视频监控部分,此外,小车运动控制按钮设置了两种单击事件: 按下按钮时触发mousedown事件开启节点移动,抬起按钮时触发mouseup事件停止节点移动。 监测节点远程控制功能在js_demo/app.js文件中对应的前端关键代码如下: //控制树莓派摄像头 function cameraControl(){ let picturenum = 1; let videonum = 1; $(".camera-trigger").click(function (){ let text = $(this).text().replace(/ /g, "").replace(/\n/g, "").replace(/\r/g, "").replace(/\t/g, ""); let cmd = ""; console.log(text); switch (text) { case "实时监控": #运行MJPG-streamer网络视频监控 cmd = "cd /home/pi/mjpg-streamer-master/mjpg-streamer-experimental && ./mjpg_streamer -i \"./input_raspicam.so\" -o \"./output_http.so -w ./www\""; break; case "拍照": #以test+序号.jpg的形式命名图片 cmd = "ps -ef | grep mjpg_streamer | grep -v grep | awk '{print $2}' | xargs kill -9 ; cd /home/pi/Desktop/img && raspistill -o test" + picturenum + ".jpg"; picturenum++; break; case "录像": #以test+序号.h264的形式命名视频 cmd = "ps -ef | grep mjpg_streamer | grep -v grep | awk '{print $2}' | xargs kill -9 ; cd /home/pi/Desktop/video && raspivid -o test" + videonum +".h264 -t 15000"; videonum++; break; case "关闭监控": #终止MJPG-streamer进程,退出视频监控 cmd = "ps -ef | grep mjpg_streamer | grep -v grep | awk '{print $2}' | xargs kill -9"; break;} let camerawindowhtml = "