文章目录
-
- 项目概要
-
相关代码
-
SpringBoot代码
- controller
- mapper
- pojo
-
service
- pagerService
- pagerServiceImpl
- application.yml
-
pages
- MQTT
- HTTP
- 完整代码
- Mysql表结构
- 项目部署
- 接口测试
-
SpringBoot代码
- 小结
项目概要
自制一款可以联网操作的呼叫器正向手机发送ID可以控制响起,反向温度过高进行报警,手机网页端可以通过MQTT协议和ESP8266模块通信,ESP8266再通过串口和51单片机进行通信。添加温度报警功能,C51单片机通过获取到温度利用串口和ESP8266通信,将温度数据传递到ESP8266模块上。ESP8266模块再通过HTTP请求向后台Java接口进行数据的上传,保存到数据库中,并且实现数据的实时显示。该项目,不是特别的完美可以有很多创新和改进的地方,但主要实现了ESP8266的MQTT、HTTP网络通信协议应用,还有串口通信的使用,大家可以当做例子进行学习。
相关代码
SpringBoot代码
首先先看一下项目的目录结构,将从以下6个红色标记的目录依次展开讲解,都是比较简单的内容。
controller
controller中有俩个方法一个是UpdateTemperatureById(通过id上传更新数据库中所存储的代码),SelectTemperatureById(通过id获取当前数据库中所存储的温度),分别都是调用pagerService中的getRoomTemperature和updateTemperature后续会介绍。
package ***.example.pager.controller;
import ***.example.pager.pojo.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/pager")
public class pagerController {
@Autowired
private ***.example.pager.service.pagerService pagerService;
/**
* 上传温度到数据库
* @param id
* @param Temperature
* @return
*/
@GetMapping("/{id}/{Temperature}")
public R UpdateTemperatureById(@PathVariable Integer id,@PathVariable Integer Temperature){
try {
pagerService.updateTemperature(id,Temperature);
return new R(true,"200","null");
}catch (Exception e){
e.printStackTrace();
return new R(false,"500","null");
}
}
/**
* 获取温度通过id
* @param id
* @return
*/
@GetMapping("/{id}")
public R SelectTemperatureById(@PathVariable Integer id){
try {
int roomTemperature = pagerService.getRoomTemperature(id);
return new R(true,"200",roomTemperature);
}catch (Exception e){
e.printStackTrace();
return new R(false,"500","null");
}
}
}
mapper
这里使用了mybatis,使用@Mapper实现了自动代理,通过使用注解的方式进行SQL语句的书写,@Select是查询语句,@Update是更新数据语句,分别实现了温度的获取和温度的上传。
package ***.example.pager.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
@Mapper
public interface Temperature {
@Select("select Temperature from pagerRt where id=#{id}")
int getTemperature(int id);
@Update("UPDATE pagerRt set Temperature = #{Temperature} where id = #{id}")
void updateTemperature(int id,int Temperature);
}
pojo
为了统一响应数据结构,因此自定义了一个名为R的类,规定了response所响应的数据结构,有三个字段:flag(业务执行结果是否成功),message(返回结果的信息),data(返回的数据)。这样的好处是,方便前端对返回返回数据的获取,固定了获取格式,使得代码整洁统一,便于维护。
package ***.example.pager.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class R {
private boolean flag;//执行结果,true为执行成功 false为执行失败
private String message;//返回结果信息
private Object data;//返回数据
}
service
pagerService
pagerService接口中有俩个方法前面也有所提到一个是getRoomTemperature,一个是updateTemperature。
package ***.example.pager.service;
import org.springframework.stereotype.Service;
@Service
public interface pagerService {
int getRoomTemperature(Integer id);
void updateTemperature(int id,int Temperature);
}
pagerServiceImpl
pargerServiceImpl为pagerService接口的实现类,实现了接口中的两个方法,并进行了相关业务的操作,具体操作也是通过之前提到的Mapper中的两个数据库操作的语句进行实现。
package ***.example.pager.service.impl;
import ***.example.pager.mapper.Temperature;
import ***.example.pager.service.pagerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.***ponent;
@***ponent
public class pagerServiceImpl implements pagerService {
@Autowired
private Temperature temperature;
@Override
public int getRoomTemperature(Integer id) {
return temperature.getTemperature(id);
}
@Override
public void updateTemperature(int id, int Temperature) {
temperature.updateTemperature(id,Temperature);
}
}
application.yml
该文件是配置文件,由于项目的简单,配置内容也相对较少,就是关于端口号的配置,Mysql数据库的配置,还有mybatis-plus的配置。
server:
port: 81
spring:
datasource:
druid:
driver-class-name: ***.mysql.cj.jdbc.Driver
url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/pager?serverTimezone=UTC
username: root
password: 6d35ddeaddf92a38
mybatis-plus:
global-config:
db-config:
id-type: auto
pages
该目录下存放的是前端页面的HTML文件,这里面我们使用了element-ui框架进行设计,主要的重点在于MQTT协议的使用,还用对后端接口请求的发送。
MQTT
下面的代码是关于MQTT的一些配置和发送消息代码,以及MQTT的初始化,options中的URL是MQTT服务器地址,Topic以及clientid要和ESP8266中设置的一致,不要按照我的来,根据自己的情况进行设置即可。
options: {
url: 'ws://test.ranye-iot.***:8080',
// 需要修改
topic: 'connect_all_esp8266_mqtt_shanguqinliu_yinshuiji_topic',
connectTimeout: 5000,
clientId: 'connect_all_esp8266_mqtt_shanguqinliu_yinshuiji_' + new Date().getTime(),
clean: false,
keepAliveInterval: 30
}
/**
* 发送消息
*/
mqttPublish(status) {
// 向指定topic发送消息,topic要保持一致
this.client.publish(this.options.topic, status.toString(), {
qos: 1
});
},
/**
* mqtt初始化
*/
mqttConf() {
// 链接mqtt
this.client = mqtt.connect(this.options.url, this.options);
// 链接成功回调方法
this.client.on('connect', function() {
myVue.client.subscribe(myVue.options.topic, function(err) {
if (!err) {
console.log('订阅成功:', myVue.options.topic)
} else {
console.log('订阅失败:', myVue.options.topic, err)
}
})
}).on('reconnect', function(error) {
}).on('error', function(error) {
}).on('end', function() {
}).on('message', function(topic, message) {
// 接收到消息
myVue.status = Number(message);
myVue.descTextStyle = {
color: myVue.color[Number(message)]
};
});
}
}
HTTP
下面的代码是HTTP请求的代码,以获取温度的代码为例,使用的是axios构造请求体和设置请求方式,发送异步请求。并且多响应的数据进行获取,加载到前端页面上。
/**
* 获取温度
* @constructor
*/
getRoomTemperature(){
var that=this;
axios.get("/pager/1",{
id: 1
}).then((resp)=>{
if (resp.data.flag){
console.log(resp)
that.RoomTemperature = resp.data.data;
}
})
const element = document.querySelector('.dishext');
if (this.RoomTemperature>70){
this.openAudio(1);
element.style.color = 'red';
}else if (this.RoomTemperature>40&&this.RoomTemperature<70){
this.openAudio(0);
element.style.color = 'yellow';
}else {
this.openAudio(0);
element.style.color = 'blue';
}
},
openAudio(Start){
const audio = document.getElementById('myAudio');
if (Start ===1){
audio.play();
}else {
audio.pause();
}
}
完整代码
下面是前端页面的完整代码,里面的难度不是特别大,有问题可以信息我或者留言我们讨论。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-***patible" content="IE=edge">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui">
<meta name="description" content="">
<meta name="author" content="">
<title>寻呼手机端</title>
<script type="text/javascript" src="../plugins/js/vue.min.js"></script>
<script type="text/javascript" src="../plugins/js/iview.min.js"></script>
<script src="../plugins/js/mqtt.js"></script>
<link rel="stylesheet" type="text/css" href="../plugins/elementui/index.css">
<script type="text/javascript" src="../plugins/vue/vue.js"></script>
<script type="text/javascript" src="../plugins/elementui/index.js"></script>
<script type="text/javascript" src="../plugins/elementui/axios.min.js"></script>
<script type="text/javascript" src="https://cdn.bootcdn.***/ajax/libs/qs/6.11.0/qs.js"></script>
</head>
<div class="app" id="dishbody" style="background-image: url('https://shanguqinliu.oss-***-beijing.aliyuncs.***/49e24e79a9432c8e0632ad8477***735.jpg')">
<audio muted="muted" id="myAudio" src="../Ring/Warn.mp3"></audio>
<el-container>
<el-main>
<h2 class="dishext" style="font-size:30px;font-family:'华文宋体'">{{'当前温度:'+RoomTemperature+'摄氏度'}}</h2>
</el-main>
<el-footer>
<el-input class="input" type="text" placeholder="请输入ID" v-model="form.id" clearable>
<template slot="prepend" style="height: 60px;" >ID</template>
</el-input>
<div @click="SendId()" style="text-align: center; width: 330px;height:200px; line-height:600px;">
<img :src="showImg[0]" class="img" tyle="display: inline-block; vertical-align: middle;"/>
</div>
</el-footer>
</el-container>
</div>
<script>
const myVue =new Vue({
el: "#dishbody",
props: {
msg: String
},
data() {
return {
form: {
id: '',
other: ''
},
color: ['#E9E9E9', '#19BF6C'],
showImg: ['../img/Call.png'],
descTextStyle: {
color: '#E9E9E9'
},
RoomTemperature:23,
audioStart:0,
client: null,
options: {
url: 'ws://test.ranye-iot.***:8080',
// 需要修改
topic: 'connect_all_esp8266_mqtt_shanguqinliu_yinshuiji_topic',
connectTimeout: 5000,
clientId: 'connect_all_esp8266_mqtt_shanguqinliu_yinshuiji_' + new Date().getTime(),
clean: false,
keepAliveInterval: 30
}
}
},
methods: {
/**
* 获取温度
* @constructor
*/
getRoomTemperature(){
var that=this;
axios.get("/pager/1",{
id: 1
}).then((resp)=>{
if (resp.data.flag){
console.log(resp)
that.RoomTemperature = resp.data.data;
}
})
const element = document.querySelector('.dishext');
if (this.RoomTemperature>70){
this.openAudio(1);
element.style.color = 'red';
}else if (this.RoomTemperature>40&&this.RoomTemperature<70){
this.openAudio(0);
element.style.color = 'yellow';
}else {
this.openAudio(0);
element.style.color = 'blue';
}
},
openAudio(Start){
const audio = document.getElementById('myAudio');
if (Start ===1){
audio.play();
}else {
audio.pause();
}
}
,
/**
* 呼叫ID
*/
SendId(){
this.$message({
type:"su***ess",
message:"呼叫请求已发送"
})
let Id = '$'+this.form.id;
console.log(Id);
this.mqttPublish(Id);
},
/**
* 发送消息
*/
mqttPublish(status) {
// 向指定topic发送消息,topic要保持一致
this.client.publish(this.options.topic, status.toString(), {
qos: 1
});
},
/**
* mqtt初始化
*/
mqttConf() {
// 链接mqtt
this.client = mqtt.connect(this.options.url, this.options);
// 链接成功回调方法
this.client.on('connect', function() {
myVue.client.subscribe(myVue.options.topic, function(err) {
if (!err) {
console.log('订阅成功:', myVue.options.topic)
} else {
console.log('订阅失败:', myVue.options.topic, err)
}
})
}).on('reconnect', function(error) {
}).on('error', function(error) {
}).on('end', function() {
}).on('message', function(topic, message) {
// 接收到消息
myVue.status = Number(message);
myVue.descTextStyle = {
color: myVue.color[Number(message)]
};
});
}
},
mounted() {
that = this;
var t2 = window.setInterval("that.getRoomTemperature()",3000);
this.mqttConf();
}
})
</script>
<style scoped>
#dishbody {
width: 100%;
height: 100%;
min-width: 50px;
background-size: 100% 100%;
background-position: center center;
overflow: auto;
background-repeat: no-repeat;
position: fixed;
line-height: 100%;
padding-top: 0px;
}
.input {
margin-top: 60px;
width: 343px;
}
.dishext {
margin-bottom: 20px;
line-height: 50px;
text-align: center;
font-size: 30px;
font-weight: bolder;
color: rgb(150, 164, 232);
text-shadow: 2px 2px 4px #000000;
}
.dishdata {
width: 320px;
height: 300px;
transform: translate(-50%);
margin-left: 50%;
}
.tool {
display: flex;
justify-content: space-between;
color: #606266;
}
.rem{
margin-left: 40px;
}
.remleft{
margin-left: -30px;
}
.shou {
cursor: pointer;
color: #606266;
}
.butt {
margin-top: 10px;
text-align: center;
}
.app {
max-width: 750px;
margin: 0 auto;
}
</style>
</body>
</html>
下面是前段页面的效果图展示,当获取到不同的温度时候字体的颜色有所不同,0~40度为蓝色;41~69为黄色;大于70度为红色,并且伴随有警报声。
小结
代码和文件比较多,如果想参考可以到评论区留言,我私发给你。文中有不清楚的地方也可以私信我,或者评论区留言。本文关注度高的话会更新最终部分的内容,分享给大家,制作不易,请大家多多评论,多多点赞转发!!!