实时温湿度监测系统: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()

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

实验总结

在这里插入图片描述

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3223876.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Excel第28享:如何新建一个Excel表格

一、背景需求 小姑电话说&#xff1a;要新建一个表格&#xff0c;并实现将几个单元格进行合并的需求。 二、解决方案 1、在电脑桌面上空白地方&#xff0c;点击鼠标右键&#xff0c;在下拉的功能框中选择“XLS工作表”或“XLSX工作表”都可以&#xff0c;如下图所示。 之后&…

C++基础知识:数组,数组是什么,数组的特点是什么?一维数组的三种定义方式,以及代码案例

1.数组的定义&#xff1a; 数组&#xff0c;就是一个集合&#xff0c;里面存放了相同类型的数据元素 2.数组的特点&#xff1a; 特点1:数组中的每个数据元素都是相同的数据类型 特点2:数组是由连续的内存位置组成的 3. 一维数组定义方式 维数组定义的三种方式: 1.数据类型 …

Studio One直播声音怎么调 Studio One直播没有声音输出怎么办 studio one如何设置声音变好听

Studio One做为新生代音乐工作站&#xff0c;凭借更低的价格和完备的功能&#xff0c;获得了音乐人和直播行业工作者的青睐&#xff0c;尤其是对硬件声卡的适配支持更好&#xff0c;特别适合用来配合线上教学和电商带货。 一、Studio One直播声音怎么调 在Studio One进行直播时…

linux使用chattr与lsattr设置文件/目录防串改

背景 linux服务器下,防止某个文件/目录被串改(增删改),可以使用chattr与lsattr设置,这是一种保护机制,用于防止意外地修改或删除重要的文件内容。 chattr与lsattr使用 1.设置目录 图中/tmp/zhk,设置目录属性文件可能被设置为不可更改(immutable)或者只追加(append …

0302GPIO外设输入功能

GPIO外设输入功能 输入部分硬件电路按键简介传感器模块简介按键和传感器模块的硬件电路 C语言的学习C语言数据类型宏定义typedef结构体枚举C语言知识总结 按键控制LED灯&光敏传感器蜂鸣器GPIO总结GPIO使用方法总结模块化编程的方法&#xff1a; 两个程序&#xff1a;按键控…

前端图表库G2快速上手

文档地址&#xff1a; https://g2-v3.antv.vision/zh/docs/manual/getting-started/ https://g2.antv.antgroup.com/ 安装&#xff1a; pnpm i antv/g2在vue3中使用&#xff1a; <script setup> import {Chart} from antv/g2; import {onMounted} from "vue"…

系统架构师考点--信息系统基础知识

大家好。今天我们来总结一下信息系统基础知识的相关考点&#xff0c;每年都会考&#xff0c;一般是在上午场选择题中&#xff0c;占3分左右&#xff0c;其次下午场论文也有可能会出相关的考题。 一、信息系统概述 信息系统&#xff1a; 是由计算机硬件、网络和通信设备、计算…

鸿蒙语言基础类库:【@ohos.util.Deque (线性容器Deque)】

线性容器Deque 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 Deque&#xff08;double ended queue&#xff09;根据循环队列的数据结构实现&#xff0c;符合先进先出以及先进后出的特点&…

ESP32的I2S引脚及支持的音频标准使用说明

ESP32 I2S 接口 ESP32 有 2 个标准 I2S 接口。这 2 个接口可以以主机或从机模式&#xff0c;在全双工或半双工模式下工作&#xff0c;并且可被配置为 8/16/32/48/64-bit 的输入输出通道&#xff0c;支持频率从 10 kHz 到 40 MHz 的 BCK 时钟。当 1 个或 2 个 被配置为主机模式…

nvm安装报错(镜像问题)

一、问题报错 安装的时候如果跟着网上早些时候的配置&#xff0c;调整了setting文件&#xff0c;配置镜像的话&#xff0c;可能报这个错误。 这个是因为他没检索到后面的链接地址&#xff0c;因为镜像的地址新的已经更换了。使用这个吧&#xff1a; node_mirror: https://npm…

华为、H3C、锐捷、思科四大设备厂商交换机配置命令总结合辑

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 下午好&#xff0c;我的网工朋友。 一直以来&#xff0c;对于华为、H3C、锐捷、思科交换机的命令配置&#xff0c;不断的有朋友留言&#xff0c;四…

解决本地操作云服务器上的Redis

方案一&#xff1a;开放Redis默认端口&#xff0c;本地通过公网IP端口号的形式访问。 方案二&#xff1a;每次将本地编写好的Java代码打包&#xff0c;放在云服务器上运行。 方案三&#xff1a;配置ssh端口转发&#xff0c;把云服务器的redis端口&#xff0c;映射到本地主机。 …

如何构建数据驱动的企业?爬虫管理平台是关键桥梁吗?

一、数据驱动时代&#xff1a;为何选择爬虫管理平台&#xff1f; 在信息爆炸的今天&#xff0c;数据驱动已成为企业发展的核心战略之一。爬虫管理平台&#xff0c;作为数据采集的第一站&#xff0c;它的重要性不言而喻。这类平台通过自动化手段&#xff0c;从互联网的各个角落…

CentOS 7:停止更新后如何下载软件?

引言 CentOS 7 是一个广受欢迎的 Linux 发行版&#xff0c;它为企业和开发者提供了一个稳定、安全、且免费的操作系统环境。然而&#xff0c;随着时间的推移&#xff0c;CentOS 7 的官方支持已经进入了维护阶段&#xff0c;这意味着它将不再收到常规的更新和新功能&#xff0c;…

Spring Boot集成grpc快速入门demo

1.什么是GRPC&#xff1f; gRPC 是一个高性能、开源、通用的RPC框架&#xff0c;由Google推出&#xff0c;基于HTTP2协议标准设计开发&#xff0c;默认采用Protocol Buffers数据序列化协议&#xff0c;支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务&#xff0c…

随笔(一)

1.即时通信软件原理&#xff08;发展&#xff09; 即时通信软件实现原理_即时通讯原理-CSDN博客 笔记&#xff1a; 2.泛洪算法&#xff1a; 算法介绍 | 泛洪算法&#xff08;Flood fill Algorithm&#xff09;-CSDN博客 漫水填充算法实现最常见有四邻域像素填充法&#xf…

ChatGPT提问提示指南PDF下载经典分享推荐书籍

ChatGPT提问提示指南PDF&#xff0c;在本书的帮助下&#xff0c;您将学习到如何有效地向 ChatGPT 提出问题&#xff0c;以获得更准确和有用的回答。我们希望这本书能够为您提供实用的指南和策略&#xff0c;帮助您更好地与 ChatGPT 交互。 ChatGPT提问提示指南PDF下载 无论您是…

ESLint: Delete `␍`(prettier/prettier)解决问题补充

如果你是克隆的&#xff0c;参考这位大佬的文章 vue.js - Delete ␍eslint(prettier/prettier) 错误的解决方案 - 个人文章 - SegmentFault 思否 如果你是个人在本地实现&#xff0c;且改为 仍旧报错&#xff0c;我解决的方案&#xff1a; 改为&#xff0c;同时勾选和我配置一…

【WebGIS平台】传统聚落建筑科普数字化建模平台

基于上述概括出建筑单体的特征部件&#xff0c;本文利用互联网、三维建模和地理信息等技术设计了基于浏览器/服务器&#xff08;B/S&#xff09;的传统聚落建筑科普数字化平台。该平台不仅实现了对传统聚落建筑风貌从基础到复杂的数字化再现&#xff0c;允许用户轻松在线构建从…

C++|异常

目录 一、异常概念 二、异常使用 2.1异常的抛出与捕获 2.2异常的重新抛出 2.3异常安全注意事项 2.4异常规范 三、自定义异常体系 四、C标准库的异常体系 五、异常的优缺点 对于传统的错误处理机制&#xff0c;例如c语言常用的&#xff1a; 1.assert&#xff0c;捕获到…