篇一主要是ESP8266相关的内容,这里主要涉及MQTT服务器和温度数据显示页面。
Express是一个基于Node.js平台的web开发框架,主要是提供route服务,有很多中间件可用,在到达路由之前进行一些请求处理,本项目其实没有利用多少他的优势,只是一个简单的示范。
使用Express生成器即可快速创建一个应用的骨架,通过下面的命令
express --view=ejs myapp
由此创建了一个名称为 myapp 的 Express 应用。此应用将在当前目录下的 myapp 目录中创建,并且设置为使用ejs模板引擎(view engine),通过下面的命令安装所有依赖包,将所有package.json中所有依赖安装在node_modules文件夹中。
npm install
通过下面的命令启动
DEBUG=myapp:* npm start
本项目中,首先修改app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
//*********mqtt server******************************
var mqtt_srever = require('./routes/mqtt_server');
//*********mqtt server******************************
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
在这里新增的内容主要是Mqtt服务器,即mqtt_server.js,如下所述
输入http://localhost:3000/后,首先通过路由为/routes/index,其中的内容为
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: '温度动态采集显示' });
});
module.exports = router;
进一步对index进行渲染,index.ejs内容如后面的内容所示。
关于MQTT服务器端,当时在这里也卡壳了很久,主要是开头的那个服务器建立格式不对,到目前还是不知道什么原因。。。
mqtt_server.js
var mosca = require("mosca");
var MqttServer = new mosca.Server({
http: {
port: 8083,
bundle: true,
static: './'
}
});
MqttServer.on('ready', function(){
console.log('mqtt is running...');
//MqttServer.authenticate = authenticate;
});
MqttServer.on('clientConnected', function(client){
console.log('client connected', client.id);
});
MqttServer.on('clientDisconnected', function (client) {
console.log('Client Disconnected := ', client.id);
});
MqttServer.on('subscribed', function (topic, client) {
console.log("Subscribed :=", topic, client.id);
});
MqttServer.on('unsubscribed', function (topic, client) {
console.log('unsubscribed := ', topic, client.id);
});
MqttServer.on('published', function(packet, client) {
if (typeof (client) == "undefined")
return;
else
console.log('client ', client.id, ' publish :', 'topic ='+packet.topic+ ',message = '+ packet.payload.toString());
});
MqttServer.on("error", function (err) {
console.log(err);
});
module.exports = MqttServer;
这里是被index.js渲染的内容,即网页呈现的内容,这里面主要完成mqtt数据的接收和显示,同时通过按钮控制ESP8266是否采集并发送温度数据
index.ejs
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
<!- 注释的内容*************************************************************************************** ->
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js"></script>
<script src="https://cdn.bootcss.com/echarts/4.2.1-rc1/echarts.min.js"></script>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/moment.js/2.20.1/moment.min.js"></script>
<!- 注释的内容*************************************************************************************** ->
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
<button onclick="opentemp()">open tmp transfer!</button>
<button onclick="closetemp()">close tmp transfer!</button>
<!- 注释的内容*************************************************************************************** ->
<form id="form">
Message: <input size="50" id="msg"> <input type="submit" value="Submit">
</form>
<div id="chart" style="width: 600px;height:400px;"></div>
<div id="main" style="width: 600px;height:400px;overflow:auto;"></div>
<!- 注释的内容*************************************************************************************** ->
<script>
var url = location.hostname;
var port = 8083;
client = new Paho.MQTT.Client(url, Number(port), "websockets-client");//建立客户端实例
client.connect({onSuccess:onConnect});//连接服务器并注册连接成功处理事件
function onConnect() {
console.log("onConnected");
topic = '/tempinfo'; //订阅的主题
client.subscribe(topic);//订阅主题
console.log("subscribed topic:"+topic);
//发送消息
}
client.onConnectionLost = onConnectionLost;//注册连接断开处理事件
client.onMessageArrived = onMessageArrived;//注册消息接收处理事件
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0) {
// console.log("onConnectionLost:"+responseObject.errorMessage);
console.log("连接已断开");
}
}
function onMessageArrived(message) {
console.log("收到消息:"+message.payloadString);
console.log("主题:"+message.destinationName);
tempinfo = message.payloadString + ".... ...."+moment().format('YYYY-MM-DD HH:mm:ss')
var div = document.createElement("div")
div.textContent = tempinfo
var board = document.getElementById("main")
var object = board.insertBefore(div,board.childNodes[0])
if (tempinfo.includes("DHT Temperature:")){
redraw(tempinfo)}
}
function mqttsend(msg){
if(msg.length > 0){
topic2 = '/topic'; //订阅的主题
message = new Paho.MQTT.Message(msg);
message.destinationName = topic2;
client.send(message);
console.log("消息发送成功"+message);
}
else {
alert("发送的消息不能为空");
}
}
//***************************************************************************************************
document.getElementById("form").onsubmit = function (event) {
var msg = document.getElementById("msg")
if (msg.value)
mqttsend(msg.value)
msg.value = ""
event.preventDefault()
}
function opentemp()
{
mqttsend("temp send open");
console.log("open");
}
function closetemp()
{
mqttsend("temp send close")
console.log("close");
}
//***************************************************************************************************
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('chart'));
function checkTime(i)
{
if (i<10)
{i="0" + i}
return i
}
function getnewData(tempinfo) {
now = tempinfo.split(".... ....")[1];
value = tempinfo.split("DHT Temperature:")[1].split(";Humidity")[0];
return {
name: now,
value: [
now,
Math.round(value)
]
};
}
var data = [];
var tempinfo_init = "[abc] DHT Temperature:22.000000;Humidity:28.000000 .... ....2020-03-27 23:03:50";
for (var i = 0; i < 90; i++) {
// data.push(getnewData(tempinfo_init));
}
option = {
title: {
text: '实时温度显示'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'line' // 默认为直线,可选为:'line' | 'shadow'
},
formatter: function(params) {
//console.log(params)
return params[0].name + '<br/>' +
params[0].seriesName + ' : ' + params[0].value[1] + ' <span>℃<SUP> </SUP></span>'
}
},
xAxis: {
type: 'time',
splitNumber: 8,
splitLine: {
show: false
}
},
yAxis: {
type: 'value',
boundaryGap: [0, '100%'],
splitLine: {
show: false
}
},
series: [{
name: '实时温度',
type: 'line',
smooth: true,
showSymbol: true,
symbolSize: 10,
hoverAnimation: false,
data: data
}]
};
function redraw(i)
{
if (data.length<100)
{
s = getnewData(i)
data.push(s);
}
else
{
data.shift();
s = getnewData(i)
data.push(s);
}
myChart.setOption(option);
}
//***************************************************************************************************
</script>
<!- 注释的内容*************************************************************************************** ->
<!- 注释的内容*************************************************************************************** ->
</body>
</html>
首先创建数据库和数据表:
create database ***;
create table esptemp(temp float,datatime TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);
在app.js中加入mysql数据库代码,将ESP8266采集得到的温度信息存储,并在前端页面中进行历史数据显示,写入代码如下:
if (packet.topic == "/tempinfo")
{
tempinfo = packet.payload.toString();
if ((tempinfo.search("Temperature:"))&&(tempinfo.search(";Humidity"))){
temp_char = tempinfo.split(";Humidity")[0];
temp_char = temp_char.split("Temperature:")[1];
if (temp_char!=null)
{
addSql = 'INSERT INTO esptemp (temp) VALUES (?)';
addSqlParams = parseFloat(temp_char);
connection.query(addSql,addSqlParams,function (err, result) {
if(err){
console.log('[INSERT ERROR] - ',err.message);
return;
}
console.log('-----------INSERT-------------');
//console.log('INSERT ID:',result.insertId);
console.log('INSERT ID:',result);
//console.log('-----\n\n');
});
}
服务器端查询代码如下:
var mysql = require('mysql');
var express = require('express');
var router = express.Router();
/* POST home page. */
router.post('/', function(req, res, next) {
console.log(req.body.time_begin);
console.log(req.body.time_end);
date_temp(req.body.time_begin,req.body.time_end,function(temp_result){
//console.log(temp_result);
res.send(temp_result);
});
});
function date_temp(time_begin,time_end,callback) //callback function
{
var connection = mysql.createConnection({
host : 'host',
user : '***',
password : '***',
port: '***',
database: '***'
});
connection.connect();
var temp_text;
var sql = 'select datatime, temp from esptemp where datatime >? and datatime<?';
var addSqlParams = [time_begin, time_end];
connection.query(sql,addSqlParams,function (err, result) {
if(err){
console.log('[SELECT ERROR] - ',err.message);
return;
}
var dataString = JSON.stringify(result);
var temp_json = JSON.parse(dataString);
//temp_text = JSON.stringify(temp_json);
console.log('-----------SELECT-------------');
console.log(temp_json);
console.log('\n\n');
callback(temp_json);
});
connection.end();
}
module.exports = router;
需要注意的是,在数据库查询中需要用到回调函数的返回值,需要特别处理。
项目路径和启动
cd /var/www/html/node_websocket/expresstest/exp_mqtt/myapp
DEBUG=myapp:* npm start