当前位置: 首页 > news >正文

实时温湿度监测系统:Micropython编码ESP32与DHT22模块的无线数据传输与PC端接收项目

实时温湿度监测系统

  • 前言
  • 项目目的
  • 项目材料
  • 项目步骤
    • 模拟ESP32接线连接测试
    • 搭建PC端ESP32拷录环境
    • 对ESP32进行拷录
    • PC端搭建桌面组件
      • 本地数据接收
      • 桌面小组件部分
  • 实验总结

前言

人生苦短,我用Python。

由于我在日常工作中经常使用Python,因此在进行该项目时,我首先考虑使用Python进行实现。在搜索电路板编程相关内容时,我发现Micropython是一个非常好的选择,因为它使用简单的语法能够帮助新手快速掌握。因此,我决定使用Micropython来实现该项目。
请添加图片描述

项目目的

实时监控房间温度,可以将其用作实时温湿度查看的桌面插件,也可以将其用作温湿度监控装置。

要求ESP32所处房间需要有可连接的wifi。

项目材料

  1. ESP32 wifi 模块
  2. HDT22 温湿度传感器
  3. 母对母接头(买HDT22会送)

项目步骤

模拟ESP32接线连接测试

可使用我进行模拟的网站进行学习,点击boot.py再点击播放键即可运行:“Wokwi测试项目”

这个测试网站可以使用“Wokwi-GUEST”开放式wifi进行测试,实际使用中将wifi改为房间中的wifi和密码即可。
并且该项目的两个py文件就是我本地拷录并且运行的代码,代码可以实现持续连接wifi和MQTT的功能,并且有呼吸灯和指示灯(这部分实际连接的时候可以注意到),还有一些数据传输的部分修饰。

网站的开放式wifi

能够看到当前的结果就是代码可以正常实现将温湿度以及时间数据传输到MQTT公共服务端:MQTT开放端口

测试结果

动手实践时可以按照模拟的方式进行实际连接:

模拟连接

搭建PC端ESP32拷录环境

安装tonny并且快速入门可看这个前几集和课件。
【Python+ESP32 快速上手(持续更新中)【 通俗易懂 】】 https://www.bilibili.com/video/BV1G34y1E7tE/?share_source=copy_web&vd_source=0d6fb1bf666097a8d32dc1f77cf20826

注意事项:

  1. 安装驱动之后连接ESP32到电脑可能不显示端口COM,可能是使用的数据线类型过旧,尽量更换数据线进行使用;
  2. Tonny运行的时候可能出现未连接情况,只需要点击重启后端,或者拔出等几秒重新插入即可。

在这里插入图片描述

对ESP32进行拷录

  1. 将模拟网站上的两个代码拷贝下来,修改TOPIC(尽量是唯一的,因为是公共端口,同时记得修改本地接收代码里面的信息)以及wifi部分,上传至ESP32中;
  2. 正确连接HDT22和ESP32;
  3. 给ESP32进行供电,当连接之后蓝灯闪烁就是在上传实时温湿度,蓝灯常亮就是MQTT端口暂时端口,蓝灯不亮就是wifi也没连上;

PC端搭建桌面组件

这部分是主要使用MQTTpython包进行本地数据接收以及tkinter创建桌面组件实现实时展示并且可以绘制折线图。

本地数据接收

MQTT本地包进行实时数据接收,保存到当前目录下的data.txt,可以自行修改,同时记得修改桌面组件读取路径。

import paho.mqtt.client as mqtt
import json# 当收到连接时的回调函数
def on_connect(client, userdata, flags, rc):print("Connected with result code " + str(rc))# 订阅主题client.subscribe(topic)# 当接收到消息时的回调函数
def on_message(client, userdata, msg):print("Received message: " + msg.payload.decode())dict = json.loads(msg.payload.decode())# 将消息保存到文件、数据库等with open("data.txt", "a") as file:file.write('\t'.join([dict["time"].replace("_"," "),str(dict["temp"]),str(dict["humidity"])])+"\n")# MQTT Broker的连接参数
broker = "broker.hivemq.com"
port = 1883  # 端口号
topic = "wokwi-weather"  # 订阅的主题,记得修改这里
# 创建一个MQTT客户端
client = mqtt.Client()# 设置回调函数
client.on_connect = on_connect
client.on_message = on_message# 连接到MQTT Broker
client.connect(broker, port, 60)# 开始循环,处理网络流量和调用回调函数
client.loop_forever()

桌面小组件部分

还在不断完善,因为也是刚学tkinter几天没有太掌握。
在这里插入图片描述

暂时可以实现实时读取data数据最后并读取全部数据绘制折线图。

import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import pandas as pddef line_plot():# Read the data from the filedata = pd.read_csv('data.txt', sep='\t', header=None, names=['Timestamp', 'Temperature', 'Humidity'])print("Data loaded for plotting.")# Create the figure with a single subplotfig, ax = plt.subplots(figsize=(12, 6))# Plot the temperaturetemperature_line, = ax.plot(data['Timestamp'], data['Temperature'], color='blue', label='Temperature')ax.set_xlabel('Timestamp')ax.set_ylim(20, 40)  # Set the y-axis limits for temperature to 20-40ax.set_ylabel('Temperature (°C)', color='blue')ax.tick_params('y', colors='blue')# Create a twin y-axis for the humidityax2 = ax.twinx()humidity_line, = ax2.plot(data['Timestamp'], data['Humidity'], color='green', label='Humidity')ax2.set_ylabel('Humidity (%)', color='green')ax2.set_ylim(20, 80)  # Set the y-axis limits for humidity to 20-80ax2.tick_params('y', colors='green')# Set the title and gridax.set_title('Temperature and Humidity over Time')ax.grid()# Add a legendlines = [temperature_line, humidity_line]labels = [l.get_label() for l in lines]ax.legend(lines, labels, loc='upper left')# Display 20 evenly spaced x-axis labelsnum_ticks = 20start = 0end = len(data['Timestamp'])tick_locations = [start + i * (end - start) / (num_ticks - 1) for i in range(num_ticks)]# def split_timestamp(ts):#     return "-".join(":".join(ts.split(":")[:-1]).split("-")[:])# tick_locations = tick_locations.apply(split_timestamp)tick_locations = [int(loc) for loc in tick_locations]ax.set_xticks(tick_locations)plt.setp(ax.get_xticklabels(), rotation=30)plt.tight_layout()# Display the plotreturn figclass AutoHideWindow:def __init__(self, root):self.root = rootself.root.geometry("320x130-100+100")self.root.overrideredirect(True)self.root.wm_attributes("-topmost", True)self.root.wm_attributes("-alpha", 0.9)self.is_hidden = Falseself.screen_width = self.root.winfo_screenwidth()self.screen_height = self.root.winfo_screenheight()self.hidden_window = Noneself.line_chart_window = Noneself.line_chart_open = False  # Track if the line chart window is openself.create_main_interface()self.create_line_chart_window()self.root.bind("<Configure>", self.check_position)self.root.bind("<Enter>", self.show_full_window)self.root.bind("<Escape>", self.hide_window)self.root.bind("<Return>", self.show_full_window)self.root.bind("<ButtonPress-1>", self.start_move)self.root.bind("<B1-Motion>", self.on_move)self.x_offset = 0self.y_offset = 0self.update_data()def create_main_interface(self):self.main_frame = ttk.Frame(self.root)self.main_frame.pack(fill=tk.BOTH, expand=True)self.gif_label = tk.Label(self.main_frame)self.gif_label.grid(row=0, column=1, rowspan=4, padx=5, pady=5, sticky=tk.W)self.load_gif("功德加一+(1).gif")self.numbers_label = ttk.Frame(self.main_frame)self.numbers_label.grid(row=0, column=0, rowspan=3, padx=10, pady=10)self.number0_label = tk.Label(self.numbers_label, width=20, height=1, bg='green', fg='white', font="Arial 10 bold", text=" ", relief=tk.FLAT, anchor=tk.W)self.number0_label.grid(column=0, row=0, sticky=tk.E)self.number1_label = tk.Label(self.numbers_label, width=20, height=1, bg='white', fg='black', font="Arial 10", text="温度:", relief=tk.FLAT, anchor=tk.W)self.number1_label.grid(column=0, row=1, sticky=tk.E, ipady=3)self.number2_label = tk.Label(self.numbers_label, width=20, height=1, bg='white', fg='black', font="Arial 10", text="湿度:", relief=tk.FLAT, anchor=tk.W)self.number2_label.grid(column=0, row=2, sticky=tk.E, ipady=3)self.button = ttk.Button(self.main_frame, text="温湿度折线图", command=self.show_line_chart_window)self.button.grid(column=0, row=3, sticky=tk.E)def load_gif(self, path):self.gif = Image.open(path)self.gif_frames = []try:while True:self.gif_frames.append(ImageTk.PhotoImage(self.gif.copy()))self.gif.seek(len(self.gif_frames))except EOFError:passself.current_frame = 0self.update_gif()def update_gif(self):self.gif_label.configure(image=self.gif_frames[self.current_frame])self.current_frame = (self.current_frame + 1) % len(self.gif_frames)self.root.after(100, self.update_gif)def create_line_chart_window(self):x, y = self.root.winfo_x(), self.root.winfo_y()width, height = 10, self.root.winfo_height()self.line_chart_window = tk.Toplevel(self.root)self.line_chart_window.geometry(f"320x500+{x}+{y}")self.line_chart_window.withdraw()# Bind the close event of the window to a method that resets the open statusself.line_chart_window.protocol("WM_DELETE_WINDOW", self.close_line_chart_window)def check_position(self, event=None):if self.is_hidden:returnx, y = self.root.winfo_x(), self.root.winfo_y()width, height = self.root.winfo_width(), self.root.winfo_height()if x <= 0 or x + width >= self.screen_width:self.hide_window()def hide_window(self, event=None):if self.hidden_window or self.is_hidden:returnx, y = self.root.winfo_x(), self.root.winfo_y()width, height = 10, self.root.winfo_height()self.hidden_window = tk.Toplevel(self.root)self.hidden_window.geometry(f"{width}x{height}+{x}+{y}")self.hidden_window.overrideredirect(True)self.hidden_window.bind("<Enter>", self.show_full_window)def show_full_window(self, event=None):if self.hidden_window:self.hidden_window.destroy()self.hidden_window = Noneself.root.deiconify()self.is_hidden = Falsedef show_line_chart_window(self):if self.line_chart_open:self.line_chart_window.deiconify()  # Show existing windowself.create_line_chart(self.line_chart_window)  # Redraw the chartelse:self.create_line_chart(self.line_chart_window)self.line_chart_window.deiconify()self.line_chart_open = True  # Update the open statusdef close_line_chart_window(self):if self.line_chart_open:self.line_chart_window.withdraw()  # Hide the windowself.line_chart_open = False  # Update the open statusdef start_move(self, event):self.x_offset = event.xself.y_offset = event.ydef on_move(self, event):x = self.root.winfo_pointerx() - self.x_offsety = self.root.winfo_pointery() - self.y_offsetself.root.geometry(f"+{x}+{y}")def update_data(self, file="data.txt"):try:with open(file, "r") as file:lines = file.readlines()if lines:last_line = lines[-1]lasttime, temperate0, humi = last_line.split('\t')temperate = temperate0.strip("℃ ")self.number0_label.config(text=f"时间:{' '.join(lasttime.split('_'))}")self.number1_label.config(text=f"温度:{temperate}℃")self.number2_label.config(text=f"湿度:{humi.strip()}%")except Exception as e:print(f"读取文件出错: {e}")self.root.after(10000, self.update_data)def create_line_chart(self, window):fig = line_plot()canvas = FigureCanvasTkAgg(fig, master=window)canvas.draw()canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)if __name__ == "__main__":root = tk.Tk()app = AutoHideWindow(root)root.mainloop()

这两个代码要同时运行就可以实现实时接收数据和实时组件展示,只开第一个就可以实时接收数据。

实验总结

在这里插入图片描述

是一次很好的学习电路板模块的小项目,也可作为中学生实践课程项目。
希望大家多多交流讨论啊,本人也是新手,希望有更简单高效的解决方案。

相关文章:

实时温湿度监测系统:Micropython编码ESP32与DHT22模块的无线数据传输与PC端接收项目

实时温湿度监测系统 前言项目目的项目材料项目步骤模拟ESP32接线连接测试搭建PC端ESP32拷录环境对ESP32进行拷录PC端搭建桌面组件本地数据接收桌面小组件部分 实验总结 前言 人生苦短&#xff0c;我用Python。 由于我在日常工作中经常使用Python&#xff0c;因此在进行该项目…...

CloudWatch Logs Insights 详解

CloudWatch Logs Insights 是 AWS 提供的强大日志分析工具,允许您快速、交互式地搜索和分析日志数据。本文将详细介绍使用 CloudWatch Logs Insights 所需的权限、常用查询方法,以及一些实用的查询示例。 1. 所需权限 要使用 CloudWatch Logs Insights,用户需要具备以下 I…...

Jmeter在信息头中设置Bearer与 token 的拼接值

思路&#xff1a;先获取token&#xff0c;将token设置成全局变量&#xff0c;再与Bearer拼接。 第一步&#xff1a;使用提取器将token值提取出来&#xff0c;使用setProperty函数将提取的token值设置成全局变量&#xff0c;在登录请求后面添加BeanShell取样器 或者 BeanShell后…...

C#程序调用Sql Server存储过程异常处理:调用存储过程后不返回、不抛异常的解决方案

目录 一、代码解析&#xff1a; 二、解决方案 1、增加日志记录 2、异步操作 注意事项 3、增加超时机制 4、使用线程池 5、使用信号量或事件 6、监控数据库连接状态 在C#程序操作Sql Server数据库的实际应用中&#xff0c;若异常就会抛出异常&#xff0c;我们还能找到异…...

数据统计与数据分组18-25题(30 天 Pandas 挑战)

数据统计与数据分组 1. 知识点1.18 分箱与统计个数1.19 分组与求和统计1.20 分组获取最小值1.21 分组获取值个数1.22 分组与条件查询1.23 分组与条件查询及获取最大值1.24 分组及自定义函数1.25 分组lambda函数统计 2. 题目2.18 按分类统计薪水&#xff08;数据统计&#xff09…...

Apache Seata应用侧启动过程剖析——注册中心与配置中心模块

本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Apache Seata应用侧启动过程剖析——注册中心与配置中心模块 前言 在Seata的应用侧&#xf…...

大话光学原理:1.“实体泛光说”、反射与折射

一、实体泛光说 在古希腊&#xff0c;那些喜好沉思的智者们中&#xff0c;曾流传着一个奇妙的设想&#xff1a;他们认为&#xff0c;我们的眼睛仿佛伸出无数触手般的光线&#xff0c;这些光线能向四面八方延伸&#xff0c;紧紧抓住周围的每一个物体。于是&#xff0c;当我们凝视…...

住宅代理、移动代理和数据中心代理之间的区别

如果您是一名认真的互联网用户&#xff0c;可能需要反复访问某个网站或服务器&#xff0c;可能是为了数据抓取、价格比较、SEO 监控等用例&#xff0c;而不会被 IP 列入黑名单或被 CAPTCHA 阻止。 代理的工作原理是将所有传出数据发送到代理服务器&#xff0c;然后代理服务器将…...

光学传感器图像处理流程(一)

光学传感器图像处理流程&#xff08;一&#xff09; 1. 处理流程总览2. 详细处理流程2.1. 图像预处理2.1.1. 降噪处理2.1.2. 薄云处理2.1.3. 阴影处理 2.2. 辐射校正2.2.1. 辐射定标2.2.2. 大气校正2.2.3. 地形校正 2.3. 几何校正2.3.1. 图像配准2.3.2. 几何粗校正2.3.3. 几何精…...

el-table 树状表格查询符合条件的数据

需要对el-table的树状表格根据输入机构名称&#xff0c;筛选出符合条件的数据&#xff0c;可用如下方法&#xff1a; 页面内容如下&#xff1a; <el-input v-model"ogeName" placeholder"请输入机构名称"><el-table :data"list" row…...

MQTT教程--服务器使用EMQX和客户端使用MQTTX

什么是MQTT MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级、基于发布-订阅模式的消息传输协议&#xff0c;适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境。它在物联网应用中广受欢迎&#xff0c;能够实现传感器、执行器和其它设备…...

326. 3 的幂

哈喽&#xff01;大家好&#xff0c;我是奇哥&#xff0c;一位专门给面试官添堵的职业面试员 文章持续更新&#xff0c;可以微信搜索【小奇JAVA面试】第一时间阅读&#xff0c;回复【资料】更有我为大家准备的福利哟&#xff01; 文章目录 一、题目二、答案三、总结 一、题目 …...

多标签问题

一、多标签问题与单标签问题的区别&#xff1a; 多标签问题是单标签问题的推广。 举个例子&#xff0c;同时识别图片中的小汽车&#xff0c;公交车&#xff0c;行人时&#xff0c;标签值有三个&#xff1a;小汽车&#xff0c;公交车&#xff0c;行人。 单标签问题仅对一个标签…...

suricata7 rule加载(三)加载options

suricata7.0.5 加载options (msg:“HTTP Request Example”; flow:established,to_server; http.method; content:“POST”; http.uri; content:“query.php”; bsize:>9; http.protocol; content:“HTTP/1.1”; bsize:8; http.host; content:“360”; bsize:>3; class…...

【电路笔记】-C类放大器

C类放大器 文章目录 C类放大器1、概述2、C类放大介绍3、C类放大器的功能4、C 类放大器的效率5、C类放大器的应用:倍频器6、总结1、概述 尽管存在差异,但我们在之前有关 A 类、B 类和 AB 类放大器的文章中已经看到,这三类放大器是线性或部分线性的,因为它们在放大过程中再现…...

c++语法之函数重载

引例 我们在C语言里面写add函数的时候&#xff0c;只能支持一种类型的相加&#xff0c;除非我们创建多个add函数&#xff1a; 但是这样写并不方便&#xff0c;于是就有了c的函数重载。 函数重载 函数重载就是可以将多个参数类型、顺序、数量不同&#xff0c;实现逻辑相同的函…...

EtherCAT主站IGH-- 11 -- IGH之fmmu_config.h/c文件解析

EtherCAT主站IGH-- 11 -- IGH之fmmu_config.h/c文件解析 0 预览一 该文件功能`fmmu_config.c` 文件功能函数预览二 函数功能介绍1. `ec_fmmu_config_init`2. `ec_fmmu_set_domain_offset_size`3. `ec_fmmu_config_page`示例用法示例详细说明三 h文件翻译四 c文件翻译该文档修改…...

如何使用IDEA快速清理无效代码(荣耀典藏版)

大家好&#xff0c;我是月夜枫。 今天分享一下IDEA中很有实用价值的Analyze&#xff0c;那么Analyze是用来做什么的呢&#xff1f; 主要用来清理没有引用的代码&#xff0c;包括方法、实体类以及没有使用的Mapper和Service等。 为了是项目更加整洁&#xff0c;可以使用Idea中…...

ELK优化之Filebeat部署

目录 1.安装配置Nginx 2.安装 Filebeat 3.设置 filebeat 的主配置文件 4.修改Logstash配置 5.启动配置 6.kibana验证 主机名ip地址主要软件es01192.168.9.114ElasticSearches02192.168.9.115ElasticSearches03192.168.9.116ElasticSearch、Kibananginx01192.168.9.113ng…...

蝙蝠优化算法(Bat Algorithm,BA)及其Python和MATLAB实现

蝙蝠优化算法&#xff08;Bat Algorithm&#xff0c;简称BA&#xff09;是一种基于蝙蝠群体行为的启发式优化算法&#xff0c;由Xin-She Yang于2010年提出。该算法模拟了蝙蝠捕食时在探测目标、适应环境和调整自身位置等过程中的行为&#xff0c;通过改进搜索过程来实现优化问题…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...