案例成果
1.Ardino写入部分
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
const char *ssid = "601B";
const char *password = "12345678";
const char* mqtt_server = "bemfa.com";
const int mqtt_server_port = 9501;
#define ID_MQTT "0c876e1774cc3bd27de1d5d1bfc61c90"
WiFiClient espClient;
PubSubClient client(espClient);
#define ledpin 2
#define eqpin 3
#define beeppin 18
#define flapin 5
#define dhtpin 15
#define DHTTYPE DHT11
#define airpin 34
DHT dht(dhtpin, DHTTYPE);
bool val1 = 0;
bool val2 = 0;
const char* mqtt_switch_topic = "switch";
const char* mqtt_temper_topic = "temper";
const char* mqtt_humid_topic = "humid";
const char* mqtt_air_topic = "air";
const char* mqtt_earthquake_topic = "earthquake";
const char* mqtt_flame_topic = "flame";
const char* mqtt_beep_topic = "beep";
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("订阅的信息为 [");
Serial.print(topic);
Serial.print("] ");
String message;
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println(message);
if (strcmp(topic, mqtt_switch_topic) == 0) {
if (message == "on") {
digitalWrite(ledpin, HIGH);
Serial.println("打开 LED 灯");
} else if (message == "off") {
digitalWrite(ledpin, LOW);
Serial.println("关闭 LED 灯");
}
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("尝试连接MQTT...");
if (client.connect(ID_MQTT)) {
Serial.println("connected");
client.subscribe(mqtt_switch_topic);
Serial.print("已订阅主题: ");
Serial.println(mqtt_switch_topic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
Serial.println("初始化系统...");
dht.begin();
pinMode(ledpin, OUTPUT);
digitalWrite(ledpin, LOW);
pinMode(eqpin, INPUT);
pinMode(flapin, INPUT);
pinMode(beeppin, OUTPUT);
pinMode(airpin, INPUT);
setup_wifi();
client.setServer(mqtt_server, mqtt_server_port);
client.setCallback(callback);
reconnect();
Serial.println("系统初始化完成!");
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
} else {
Serial.print("湿度: ");
Serial.print(h);
Serial.print("%, 温度: ");
Serial.print(t);
Serial.println("°C");
String tempMsg = String(t);
String humidMsg = String(h);
client.publish(mqtt_temper_topic, tempMsg.c_str());
client.publish(mqtt_humid_topic, humidMsg.c_str());
}
int airval = analogRead(airpin);
Serial.print("空气质量: ");
Serial.println(airval);
String airMsg = String(airval);
client.publish(mqtt_air_topic, airMsg.c_str());
val1 = digitalRead(eqpin);
if (val1 == 0) {
client.publish(mqtt_earthquake_topic, "off");
Serial.println("无震感");
client.publish(mqtt_beep_topic, "off");
}
else {
client.publish(mqtt_earthquake_topic, "on");
Serial.println("有震感");
client.publish(mqtt_beep_topic, "on");
}
val2 = digitalRead(flapin);
Serial.print("火灾传感器状态: ");
Serial.println(val2);
if (val2 == 1) {
client.publish(mqtt_flame_topic, "off");
Serial.println("无火灾");
digitalWrite(beeppin, HIGH);
}
else {
client.publish(mqtt_flame_topic, "on");
Serial.println("有火灾");
digitalWrite(beeppin, LOW);
}
delay(100);
}
2.微信小程序JS部分
const app = getApp()
import mqtt from '../../utils/mqtt.min'
Page({
data: {
ledtopic: "switch",
eqtopic: "earthquake",
fhtopic: "flame",
wdtopic: "temper",
sdtopic: "humid",
kqtopic: "air",
beeptopic: "beep",
uid: "0c876e1774cc3bd27de1d5d1bfc61c90",
qustatus: "无震感",
flastatus: "无火灾",
wdval: "未知",
sdval: "未知",
airval: "未知",
beepstatus: "关闭",
isThemeOn: false,
device_status: "离线",
client: null
},
mqttConnect() {
var that = this;
var options = {
keepalive: 60,
clean: true,
protocolVersion: 4,
clientId: that.data.uid
};
that.data.client = mqtt.connect('wxs://bemfa.com:9504/wss', options);
that.data.client.on('connect', function() {
console.log("连接服务器成功");
that.setData({
device_status: "在线"
});
that.data.client.subscribe(that.data.eqtopic, function(err) {
if(err) console.log(err);
});
that.data.client.subscribe(that.data.fhtopic, function(err) {
if(err) console.log(err);
});
that.data.client.subscribe(that.data.wdtopic, function(err) {
if(err) console.log(err);
});
that.data.client.subscribe(that.data.sdtopic, function(err) {
if(err) console.log(err);
});
that.data.client.subscribe(that.data.kqtopic, function(err) {
if(err) console.log(err);
});
that.data.client.subscribe(that.data.beeptopic, function(err) {
if(err) console.log(err);
});
});
that.data.client.on('message', function(topic, message) {
var msg = message.toString();
if(topic === that.data.eqtopic) {
that.setData({
qustatus: msg === 'on' ? '有地震' : '无地震'
});
}
else if(topic === that.data.fhtopic) {
that.setData({
flastatus: msg === 'on' ? '有火灾' : '无火灾'
});
}
else if(topic === that.data.wdtopic) {
that.setData({
wdval: msg
});
}
else if(topic === that.data.sdtopic) {
that.setData({
sdval: msg
});
}
else if(topic === that.data.kqtopic) {
that.setData({
airval: msg
});
}
else if(topic === that.data.beeptopic) {
that.setData({
beepstatus: msg === 'on' ? '开启' : '关闭'
});
}
});
that.data.client.on('reconnect', function() {
console.log("重新连接");
that.setData({
device_status: "连接中"
});
});
that.data.client.on('error', function(error) {
console.log("连接失败", error);
that.setData({
device_status: "离线"
});
});
},
onLoad() {
this.mqttConnect()
},
onThemeSwitchTap: function() {
if(this.data.client && this.data.client.connected) {
const message = this.data.isThemeOn ? 'off' : 'on';
this.data.client.publish(this.data.ledtopic, message);
this.setData({
isThemeOn: !this.data.isThemeOn
});
} else {
console.log("MQTT未连接");
}
}
})
3.微信小程序xml部分
<!--pages/znjj/znjj.wxml-->
<view class="imag4_src"></view>
<image class="imag4" src="/img/znjj.png"></image>
<view class="spacer"></view>
<view class="container1">
<view class="view1">
<view class="img_wd">
<view class="content-row">
<image class="img1" src="/img/wd.png"></image>
<text>温度</text>
</view>
<view class="loading-container">
<!-- 修改:绑定温度数据 -->
<text class="loading-text">{{wdval}}</text>
</view>
</view>
</view>
<view class="view2">
<view class="img_wd">
<view class="content-row">
<image class="img1" src="/img/sd.png"></image>
<text>湿度</text>
</view>
<view class="loading-container">
<!-- 修改:绑定湿度数据 -->
<text class="loading-text">{{sdval}}</text>
</view>
</view>
</view>
<view class="view3">
<view class="img_wd">
<view class="content-row">
<image class="img1" src="/img/PM2.5.png"></image>
<text>空气</text>
</view>
<view class="loading-container">
<!-- 修改:绑定空气质量数据 -->
<text class="loading-text">{{airval}}</text>
</view>
</view>
</view>
</view>
<view class="spacer"></view>
<view class="container2">
<view class="view4">
<view class="img_wd">
<view class="content-row">
<image class="img1" src="/img/hy.png"></image>
<text>防火</text>
</view>
<view class="loading-container">
<!-- 修改:绑定火灾状态 -->
<text class="loading-text">{{flastatus}}</text>
</view>
</view>
</view>
<view class="view5">
<view class="img_wd">
<view class="content-row">
<image class="img1" src="/img/dz.png"></image>
<text>地震</text>
</view>
<view class="loading-container">
<text class="loading-text">{{qustatus}}</text>
</view>
</view>
</view>
<view class="view6">
<view class="img_wd">
<view class="content-row">
<image class="img1" src="/img/kt.png"></image>
<text>空调</text>
</view>
<view class="switch-container">
<button class="theme-switch-button" bindtap="onThemeSwitchTap">
{{isThemeOn ? 'on' : 'off'}}
</button>
</view>
</view>
</view>
</view>
<view class="spacer"></view>
<view class="container3">
<view class="view7">
<view class="img_wd">
<view class="content-text">
<text>设备状态</text>
<text class="module">{{device_status}}</text>
</view>
</view>
</view>
<view class="view8">
<view class="img_wd">
<view class="content-text">
<text>火灾告警</text>
<view wx:if="{{flastatus == '有火灾'}}">
<image class="img5" src="/img/hz_on.png"></image>
</view>
<view wx:else>
<image class="img5" src="/img/hz_off.png"></image>
</view>
</view>
</view>
</view>
<view class="view9">
<view class="img_wd">
<view class="content-text">
<text>地震告警</text>
<!-- 修改:使用qustatus而不是eqstatus -->
<view wx:if="{{qustatus == '有地震'}}">
<image class="img5" src="/img/dz_on.png"></image>
</view>
<view wx:else>
<image class="img5" src="/img/dz_off.png"></image>
</view>
</view>
</view>
</view>
</view>
4. 微信小程序CSS部分
.imag4_src{
display: block;
width: 100%;
}
.imag4{
width: 350px;
height: 200px;
justify-content: center;
border: 1px solid #000;
border-radius: 10px;
margin: 20px;
box-shadow: 5px 5px 15px rgba(0,0,0,0.3);
transition: transform 0.3s ease;
}
.imag4:hover{
transform: scale(1.05);
}
.spacer{
height: 10px;
}
.container1,
.container2{
display: flex;
flex-direction: row;
justify-content: center;
}
.container1 .view1,
.container1 .view2,
.container1 .view3,
.container2 .view4,
.container2 .view5,
.container2 .view6{
width: 90px;
height: 100px;
margin: 20px;
position: relative;
background-color: transparent;
display: flex;
align-items: lsft;
justify-content: center;
border: 2px solid rgb(291,214,214);
box-shadow: 0 0 10px rgba(37,37,37,0.5);
}
.container1 .view1 { background-color: #e3ece2; }
.container1 .view2 { background-color: #ccd4cb; }
.container1 .view3 { background-color: #e5f3ee; }
.container2 .view4 { background-color: #f7e7e9; }
.container2 .view5 { background-color: #d7eef7; }
.container2 .view6 { background-color: #e3e4f1; }
.img_wd{
justify-content: flex-start;
align-items: flex-start;
width: 100px;
height: 100px;
position: relative;
}
.content-row{
margin-top: 1px;
display: flex;
align-items: center;
margin-left: 3px;
}
.img1{
width: 30px;
height: 30px;
border: 1px solid rgb(211,209,209);
border-radius: 10px;
margin: 3px;
box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.3);
transition: transform 0.3s ease;
margin-right: 5px;
}
.loading-container{
margin-top: 20px;
display: flex;
justify-content: center;
align-items: center;
}
.loading-text{
justify-content: center;
align-items: center;
font-size: 16px;
color: #333;
height: 20px;
}
.container3{
display: flex;
flex-direction: row;
justify-content: center;
}
.container3 .view7,
.container3 .view8,
.container3 .view9{
width: 90px;
height: 100px;
margin: 20px;
position: relative;
background-color: transparent;
display: flex;
align-items: left;
justify-content: center;
border: 2px solid rgb(219, 214, 214);
box-shadow: 0 0 10px rgba(37, 37, 37, 0.5);
border-radius: 60px;
}
.container3 .view7 { background-color: #f1ecec; }
.container3 .view8 { background-color: #f3f8f6; }
.container3 .view9 { background-color: #ece6f5; }
.switch-container {
width: 60px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
margin-left: auto;
margin-right: auto;
}
.theme-switch-button {
height: 30px;
line-height: 30px;
background-color: rgb(146, 198, 247);
color: rgb(13, 13, 14);
border: none;
border-radius: 20px;
text-align: center;
font-size: 13px;
border: 1px solid rgb(241, 234, 234);
box-shadow: 0 0 10px rgba(2, 8, 27, 0.5);
padding: 0;
}
.content-text {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
margin-top: 15px;
width: 100%;
box-sizing: border-box;
}
.module {
margin-top: 10px;
color: #f74d4d;
font-weight: bold;
}
.img5{
margin-top: 5px;
width: 50px;
height: 40px;
}