这是在大三冬季学期综合工程设计的一个项目,因为这个项目接触到了物联网,从而获得了之后的实习offer和物联网方向的秋招offer。物联网是通信行业的大的风口,我觉得可以学习一下。
项目介绍:使用STM32F407开发板作为主控面板,使用搭载MQTT协议的ESP8266 01S芯片作为物联网模块,使用微信小程序作为远程控制端,可以实时查看温度、水浑浊情况等信息,具有定时换水、供氧、喂食、灯照等功能,同时可在微信小程序上实时查看生态箱状态并远程控制。
项目原理:


项目分工:
- 使用AD设计搭载stm32的PCB板
- 使用Keli 5编写stm32固件
- 使用Arduino搭建ESP8266环境
- 使用微信开发者工具编写配套微信小程序
- 使用Solidworks设计主控板外壳并3D打印
项目展示:


项目实现(因为我负责项目分工的3、4,所以只可以共享该部分代码):
- 微信小程序index页前端源码:
<template>
<div class="wrapper">
<div class="header-wrapper">
<div class="header-title">
<span>当前气温</span>
<span>{{location}}</span>
</div>
<div class="header-text">
<span>{{wea_temp}}℃</span>
<span>{{weather}}</span>
</div>
<div class="weather-highlow">当日最高气温为{{high_temp}}℃,最低温为{{low_temp}}℃,{{wind_dire}}风{{wind_scale}}级</div>
</div>
<div class="body-wrapper">
<div class="body">
<div class="data-wrapper">
<div class="advice">
<img class="advice-logo" src="/static/images/advice.png">
<transition name="slide">
<div class="advice-text" :key="advdata.id">{{advdata.val}}</div>
</transition>
</div>
</div>
<div class="data-wrapper">
<div class="data">
<div v-if="bad[0]">
<img class="data-logo" src="/static/images/badtemperature.png">
</div>
<div v-else>
<img class="data-logo" src="/static/images/temperature.png">
</div>
<div class="data-text">
<div class="data-title">水温</div>
<div class="data-value">{{temp}}℃</div>
</div>
</div>
<div class="data">
<div v-if="bad[1]">
<img class="data-logo" src="/static/images/badwater-depth.png">
</div>
<div v-else>
<img class="data-logo" src="/static/images/water-depth.png">
</div>
<div class="data-text">
<div class="data-title">水量</div>
<div class="data-value">{{depth}}</div>
</div>
</div>
</div>
<div class="data-wrapper">
<div class="data">
<div v-if="bad[2]">
<img class="data-logo" src="/static/images/badturbidity.png">
</div>
<div v-else>
<img class="data-logo" src="/static/images/turbidity.png">
</div>
<div class="data-text">
<div class="data-title">水质</div>
<div class="data-value">{{turb}}</div>
</div>
</div>
<div class="data">
<img class="data-logo" src="/static/images/heating.png">
<div class="data-text">
<div class="data-title">恒温</div>
<div class="data-value">
<switch @change="onHeatingChange" :checked="heating" color="#3d7ef9" />
</div>
</div>
</div>
</div>
<div class="data-wrapper">
<div class="data">
<img class="data-logo" src="/static/images/light.png">
<div class="data-text">
<div class="data-title">灯光</div>
<div class="data-value">
<switch @change="onLightChange" :checked="light" color="#3d7ef9" />
</div>
</div>
</div>
<div class="data">
<img class="data-logo" src="/static/images/food.png">
<div class="data-text">
<div class="data-title">喂食</div>
<div class="data-value">
<switch @change="onFoodChange" :checked="food" :disabled="runningfood" color="#3d7ef9" />
</div>
</div>
</div>
</div>
<div class="data-wrapper">
<div class="data">
<img class="data-logo" src="/static/images/oxygen.png">
<div class="data-text">
<div class="data-title">供氧</div>
<div class="data-value">
<switch @change="onOxygenChange" :checked="oxygen" :disabled="runningoxygen" color="#3d7ef9" />
</div>
</div>
</div>
<div v-if="badturb">
<div class="data">
<img class="data-logo" src="/static/images/rewater.png">
<div class="data-text">
<div class="data-title">换水</div>
<div class="data-value">
<switch @change="onRewaterChange" :checked="rewater" :disabled="runningrewater" color="#3d7ef9" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { connect } from "mqtt/dist/mqtt.js";
const mqtturl = 'wxs://yourmqtturl'
export default {
data () {
return {
wea_temp: '-',
location: "获取中",
weather: "--",
high_temp: '-',
low_temp: '-',
wind_dire: "--",
wind_scale: '-',
//——————————————————————————————————————//
client:{
},
temp: '--',
depth: '--',
turb: '--',
advice: [
'未获取到水温数据,请检查网络连接',
'未获取到水量数据,请检查网络连接',
'未获取到水质数据,请检查网络连接'],
advnum: 0,
bad:[false,false,false],//水温、水量、水质
heating: false,
light: false,
food: false,
oxygen: false,
rewater: false,
runningfood: false,
runningoxygen: false,
runningrewater: false,
}
},
computed: {
advdata () {
return {
id: this.advnum,
val: this.advice[this.advnum]
}
}
},
mounted () {
this.startMove()
},
methods: {
startMove () {
// eslint-disable-next-line
let timer = setTimeout(() => {
if (this.advnum === 2) {
this.advnum = 0;
} else {
this.advnum += 1;
}
this.startMove();
}, 2000); // 滚动不需要停顿则将2000改成动画持续时间
},
Refresh(event){
var that = this;
that.client.publish("/aee/sub",'{"Refresh":1}', function(err){
if(!err){
console.log("成功下发命令——更新")
}
})
},
onHeatingChange(event){
var that = this;
let sw = event.mp.detail.value;
if(sw){
that.client.publish("/aee/sub",'{"Heating_SW":1}', function(err){
if(!err){
console.log("成功下发命令——恒温")
}
})
}
else{
that.client.publish("/aee/sub",'{"Heating_SW":0}', function(err){
if(!err){
console.log("成功下发命令——关恒温")
}
})
}
},
onLightChange(event){
var that = this;
let sw = event.mp.detail.value
if(sw){
that.client.publish("/aee/sub",'{"Light_SW":1}', function(err){
if(!err){
console.log("成功下发命令——开灯")
}
})
}
else{
that.client.publish("/aee/sub",'{"Light_SW":0}', function(err){
if(!err){
console.log("成功下发命令——关灯")
}
})
}
},
onFoodChange(event){
var that = this;
let sw = event.mp.detail.value;
if(sw){
that.client.publish("/aee/sub",'{"Food_SW":1}', function(err){
if(!err){
console.log("成功下发命令——喂食")
}
})
}
},
onOxygenChange(event){
var that = this;
let sw = event.mp.detail.value;
if(sw){
that.client.publish("/aee/sub",'{"Oxygen_SW":1}', function(err){
if(!err){
console.log("成功下发命令——供氧")
}
})
}
else{
that.client.publish("/aee/sub",'{"Oxygen_SW":0}', function(err){
if(!err){
console.log("成功下发命令——关氧")
}
})
}
},
onRewaterChange(event){
var that = this;
let sw = event.mp.detail.value;
if(sw){
that.client.publish("/aee/sub",'{"Rewater_SW":1}', function(err){
if(!err){
console.log("成功下发命令——换水")
}
})
}
},
},
onPullDownRefresh() {
var that = this;
that.Refresh();
wx.getLocation({
type: "wgs84",
success(res){
const latitude = res.latitude;
const longitude = res.longitude;
const key = "yourkey";
wx.request({
url: `https://api.seniverse.com/v3/weather/now.json?key=${key}&location=${latitude}:${longitude}`,
success(res) {
//console.log(res.data);
const {location,now} = res.data.results[0];
that.location = location.name;
that.wea_temp = now.temperature;
that.weather = now.text;
}
});
wx.request({
url: `https://api.seniverse.com/v3/weather/daily.json?&key=${key}&location=${latitude}:${longitude}&days=1`,
success(res) {
//console.log(res.data);
const {daily,last_update,location} = res.data.results[0];
that.high_temp = daily[0].high;
that.low_temp = daily[0].low;
that.wind_dire = daily[0].wind_direction;
that.wind_scale = daily[0].wind_scale;
}
});
}
});
wx.stopPullDownRefresh();
},
onShow(){
var that = this;
that.client = connect(mqtturl);
that.client.on("connect",function() {
that.client.subscribe("/aee/pub",function (err) {
if(!err){
console.log("成功订阅设备上行数据Topic!")
}
})
})
that.Refresh();
that.client.on("message",function (topic,message) {
console.log(message);
let dataFromDev = {};
dataFromDev = JSON.parse(message);
that.temp = dataFromDev.temp;
if(dataFromDev.depth==1) {that.depth = "正常"; that.bad[1] = false; that.advice[1] = "水量良好,暂无操作建议";}
if(dataFromDev.depth==2) {that.depth = "适中"; that.bad[1] = false; that.advice[1] = "水量良好,暂无操作建议";}
if(dataFromDev.depth==3) {that.depth = "缺水"; that.bad[1] = true; that.advice[1] = "水量低,请及时补水";}
if(dataFromDev.turb==1) {that.turb = "优"; that.bad[2] = false; that.advice[2] = "水质良好,暂无操作建议";}
if(dataFromDev.turb==2) {that.turb = "良"; that.bad[2] = false; that.advice[2] = "水质良好,暂无操作建议";}
if(dataFromDev.turb==3) {that.turb = "中"; that.bad[2] = false; that.advice[2] = "水质良好,暂无操作建议";}
if(dataFromDev.turb==4) {that.turb = "差"; that.bad[2] = true; that.advice[2] = "水质差,建议开启换水";}
if(dataFromDev.temp<=18) {that.bad[0] = true; that.advice[0] = "水温低,建议开启恒温";}
if(dataFromDev.temp>18) {that.bad[0] = false; that.advice[0] = "水温良好,暂无操作建议";}
that.heating = dataFromDev.heating;
that.light = dataFromDev.light;
that.food = dataFromDev.food;
that.runningfood = dataFromDev.runningfood;
that.oxygen = dataFromDev.oxygen;
that.runningoxygen = dataFromDev.runningoxygen;
that.rewater = dataFromDev.rewater;
that.runningrewater = dataFromDev.runningrewater;
})
wx.getLocation({
type: "wgs84",
success(res){
const latitude = res.latitude;
const longitude = res.longitude;
const key = "yourkey";
wx.request({
url: `https://api.seniverse.com/v3/weather/now.json?key=${key}&location=${latitude}:${longitude}`,
success(res) {
//console.log(res.data);
const {location,now} = res.data.results[0];
that.location = location.name;
that.wea_temp = now.temperature;
that.weather = now.text;
}
});
wx.request({
url: `https://api.seniverse.com/v3/weather/daily.json?&key=${key}&location=${latitude}:${longitude}&days=1`,
success(res) {
//console.log(res.data);
const {daily,last_update,location} = res.data.results[0];
that.high_temp = daily[0].high;
that.low_temp = daily[0].low;
that.wind_dire = daily[0].wind_direction;
that.wind_scale = daily[0].wind_scale;
}
});
}
});
}
};
</script>
<style lang="scss" scoped>
.wrapper{
padding:15px;
.header-wrapper{
background-color: #3d7ef9;
border-radius: 20px;
color: #fff;
box-shadow: #d6d6d6 0px 0px 5px;
padding: 15px 30px;
.header-title{
display: flex;
justify-content: space-between;
}
.header-text{
margin-top: 10px;
font-size: 32px;
font-weight: 400;
display: flex;
justify-content: space-between;
}
.weather-highlow{
margin-top: 10px;
font-size: 12px;
}
}
.data-wrapper{
margin-top: 20px;
display: flex;
justify-content: space-between;
.data{
background-color: #fff;
width: 150px;
height: 80px;
border-radius: 20px;
display: flex;
justify-content: space-between;
padding: 0 8px;
box-shadow: #d6d6d6 0px 0px 5px;
.data-logo{
height: 36px;
width: 36px;
padding: 20px 15px;
}
.data-text{
margin-top: 10px;
color: #7f7f7f;
.data-title{
font-size: 18px;
padding: 0px 10px;
}
.data-value{
font-size: 24px;
}
}
}
.advice{
background-color: #fff;
width: 330px;
height: 40px;
border-radius: 20px;
display: flex;
justify-content: space-between;
padding: 0 8px;
box-shadow: #d6d6d6 0px 0px 5px;
overflow: hidden;
.advice-logo{
height: 36px;
width: 36px;
padding: 3px 15px;
}
.advice-text{
padding: 10px 13px;
color: #7f7f7f;
font-size: 14px;
}
.slide-enter-active, .slide-leave-active {
transition: all 0.5s linear;
}
.slide-enter{
transform: translateY(20px) scale(1);
opacity: 1;
}
.slide-leave-to {
transform: translateY(-20px) scale(0.8);
opacity: 0;
}
}
}
}
</style>