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

news2024/9/21 5:45:44

实时温湿度监测系统

  • 前言
  • 项目目的
  • 项目材料
  • 项目步骤
    • 模拟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 pd

def line_plot():
    # Read the data from the file
    data = pd.read_csv('data.txt', sep='\t', header=None, names=['Timestamp', 'Temperature', 'Humidity'])
    print("Data loaded for plotting.")

    # Create the figure with a single subplot
    fig, ax = plt.subplots(figsize=(12, 6))

    # Plot the temperature
    temperature_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-40
    ax.set_ylabel('Temperature (°C)', color='blue')
    ax.tick_params('y', colors='blue')

    
    # Create a twin y-axis for the humidity
    ax2 = 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-80
    ax2.tick_params('y', colors='green')

    # Set the title and grid
    ax.set_title('Temperature and Humidity over Time')
    ax.grid()

    # Add a legend
    lines = [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 labels
    num_ticks = 20
    start = 0
    end = 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 plot
    return fig

class AutoHideWindow:
    def __init__(self, root):
        self.root = root
        self.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 = False

        self.screen_width = self.root.winfo_screenwidth()
        self.screen_height = self.root.winfo_screenheight()

        self.hidden_window = None
        self.line_chart_window = None
        self.line_chart_open = False  # Track if the line chart window is open

        self.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 = 0
        self.y_offset = 0

        self.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:
            pass

        self.current_frame = 0
        self.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 status
        self.line_chart_window.protocol("WM_DELETE_WINDOW", self.close_line_chart_window)

    def check_position(self, event=None):
        if self.is_hidden:
            return
        x, 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:
            return

        x, 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 = None
            self.root.deiconify()
            self.is_hidden = False

    def show_line_chart_window(self):
        if self.line_chart_open:
            self.line_chart_window.deiconify()  # Show existing window
            self.create_line_chart(self.line_chart_window)  # Redraw the chart
        else:
            self.create_line_chart(self.line_chart_window)
            self.line_chart_window.deiconify()
            self.line_chart_open = True  # Update the open status

    def close_line_chart_window(self):
        if self.line_chart_open:
            self.line_chart_window.withdraw()  # Hide the window
            self.line_chart_open = False  # Update the open status

    def start_move(self, event):
        self.x_offset = event.x
        self.y_offset = event.y

    def on_move(self, event):
        x = self.root.winfo_pointerx() - self.x_offset
        y = self.root.winfo_pointery() - self.y_offset
        self.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://www.coloradmin.cn/o/1911567.html

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

相关文章

物流工业三防平板实时跟踪货物位置和状态

在当今全球化和高度数字化的商业环境中&#xff0c;物流行业的高效运作对于企业的成功和经济的繁荣至关重要。货物的准确、实时跟踪不仅能提高物流效率&#xff0c;还能增强客户满意度&#xff0c;降低运营成本。物流工业三防平板的出现&#xff0c;为实现货物位置和状态的实时…

使用redis进行短信登录验证(验证码打印在控制台)

使用redis进行短信登录验证 一、流程1. 总体流程图2. 流程文字讲解&#xff1a;3.代码3.1 UserServiceImpl&#xff1a;&#xff08;难点&#xff09;3.2 拦截器LoginInterceptor&#xff1a;3.3 拦截器配置类&#xff1a; 4 功能实现&#xff0c;成功存入redis &#xff08;黑…

2017年,我成为了技术博主

2017年9月&#xff0c;我已经大三了。 >>上一篇&#xff08;爪哇&#xff0c;我初窥门径&#xff09; 我大二学了很多java技术&#xff0c;看似我一会就把javaweb/ssh/ssm这些技术栈给学了。 这些技术确实不难&#xff0c;即便是我&#xff0c;我都能学会&#xff0c;…

深入理解 LXC (Linux Containers)

目录 引言LXC 的定义LXC 的架构LXC 的工作原理LXC 的应用场景LXC 在 CentOS 上的常见命令实验场景模拟总结 1. 引言 在现代 IT 基础设施中&#xff0c;容器技术已经成为一种重要的应用和部署方式。与虚拟机相比&#xff0c;容器具有更高的效率、更轻量的特性和更快的启动速度…

MySQL GROUP_CONCAT 函数详解与实战应用

提示&#xff1a;在需要将多个值组合成一个列表时&#xff0c;GROUP_CONCAT() 函数为 MySQL 提供了一种强大的方式来处理数据 文章目录 前言什么是 GROUP_CONCAT()基本语法 示例使用 GROUP_CONCAT()去除重复值排序结果 前言 提示&#xff1a;这里可以添加本文要记录的大概内容…

第一次作业--数据库-搭建MySQL环境

一、下载 二、进入安装向导 1.选择Custom &#xff0c;然后点击next 2.选择安装地址 点击第一个MySQL Servers然后依次点击打开到MySQL Server 8.0.37-X64 点击向右的绿色箭头 点击MySQL Server 8.0.37-X64 然后看到下面的蓝色Advanced Options 更改安装路径 然后点击next …

刷题(day02)

1、leetcode136.删除链表的结点 给定单向链表的头指针和一个要删除的节点的值&#xff0c;定义一个函数删除该节点。 返回删除后的链表的头节点。 示例 1: 输入: head [4,5,1,9], val 5 输出: [4,1,9] 解释: 给定你链表中值为 5 的第二个节点&#xff0c;那么在调用了你的函数…

说说iOS苹果的“开发者模式”什么时候需要打开 需不需要提前打开

在 iOS 开发过程中&#xff0c;开发者模式&#xff08;Developer Mode&#xff09;是一个非常重要的功能&#xff0c;它允许开发者在设备上运行和调试自己的应用程序。 经常有人私信或在群里问到&#xff0c;“我没有开发者模式&#xff0c;怎么办”&#xff0c;“开发者模式是…

优秀策划人必逛的地方,你不会还不知道吧?

道叔今天依然记得当初刚入行的时候&#xff0c;每天为完成策划任务&#xff0c;焦虑的整晚睡不着觉的痛苦。 但其实……很多时候&#xff0c;选择比努力更重要 优秀的策划和文案&#xff0c;也从来不是天生&#xff0c;你要走的路&#xff0c;前人都已经走过,你要做的仅仅是整…

windows JDK11 与JDK1.8自动切换,以及切换后失效的问题

1.windows安装不同环境的jdk 2.切换jdk 3.切换失败 原因&#xff1a;这是因为当我们安装并配置好JDK11之后它会自动生成一个环境变量&#xff08;此变量我们看不到&#xff09;&#xff0c;此环境变量优先级较高&#xff0c;导致我们在切换回JDK8后系统会先读取到JDK11生成的…

Windows11配置WSL2支持代理上网

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装WSL2分发版二、配置步骤三、测试总结 前言 说起来本来这个功能我也不需要的&#xff0c;只是最近突然有个需求就顺便研究了下&#xff0c;WSL2默认的网…

【漏洞复现】29网课交单平台 SQL注入

声明&#xff1a;本文档或演示材料仅用于教育和教学目的。如果任何个人或组织利用本文档中的信息进行非法活动&#xff0c;将与本文档的作者或发布者无关。 一、漏洞描述 29网课交单平台是一个在线学习平台&#xff0c;用于帮助学生完成网络课程的学习任务。这个平台提供了包括…

泛微E9开发 控制Radio框字段打印是否仅显示选中项文字

控制Radio框字段打印是否仅显示选中项文字 1、需求说明2、实现方法3、扩展知识点控制Radio框字段打印是否仅显示选中项文字格式参数说明样例 1、需求说明 当我们对单选框进行打印时&#xff0c;往往会把所有的选项一起打印出来&#xff08;如下图所示&#xff09;&#xff0c;现…

【Linux进阶】文件系统4——文件系统特性

1.磁盘组成与分区的复习 首先说明一下磁盘的物理组成&#xff0c;整块磁盘的组成主要有&#xff1a; 圆形的碟片&#xff08;主要记录数据的部分&#xff09;&#xff1b;机械手臂&#xff0c;与在机械手臂上的磁头&#xff08;可擦写碟片上的数据);主轴马达&#xff0c;可以…

Redis学习 - 基础篇

Redis学习 - 基础篇 一. 简介 Redis 是一个高性能的key-value数据库&#xff0c;常用的数据类型如下&#xff1a;string&#xff0c;list&#xff0c;set&#xff0c;zset&#xff0c;hash 二. 安装 Widows和Linux下如何安装Redis-CSDN博客 三. 常用命令 配置及数据库操作…

[ TOOLS ] JFLASH 使用说明

一、使用everything查找JFLASH everything是指这个软件&#xff0c;使用这个方便查找想要的文件 二、创建一个工程并配置 创建完后进行配置&#xff1a; Target devic: 板子的芯片型号&#xff0c;比如R7FA6M4Target interface: 一般是SWDSpeed: 一般是4000kHz, 不能下载则将Sp…

数学建模美赛入门

数学建模需要的学科知识 高等数学线性代数 有很多算法的掌握是需要高等数学和线代的相关知识 如&#xff1a;灰色预测模型需要微积分知识&#xff1b;神经网络需要用到导数知识&#xff1b;图论和层次分析法等都需要用到矩阵计算的相关知识等&#xff1b; 概率论与数理统计&am…

基于SpringBoot构造超简易QQ邮件服务发送 第二版

目录 追加 邮箱附件 添加依赖 编码 测试 第二版的更新点是追加了 邮箱附件功能 ( 后期追加定时任务 ) 基于SpringBoot构造超简易QQ邮件服务发送(分离-图解-新手) 第一版 追加 邮箱附件 添加依赖 <!-- 电子邮件 --><dependency><groupId>org.spri…

后端登录校验——Filter过滤器和Interceptor拦截器

一、Filter过滤器 前面我们学会了最先进的会话跟踪技术jwt令牌&#xff0c;那么我们要让用户使用某些功能时就要根据jwt令牌来验证用户身份&#xff0c;来决定他是否登陆了、让不让用户访问这个页面&#xff08;或功能&#xff09; 但是这样一来&#xff0c;没发一个请求&…

解决打印PDF文本不清楚的处理办法

之前打印PDF格式的电子书&#xff0c;不清晰&#xff0c;影响看书的心情&#xff0c;有时看到打印的书的质量&#xff0c;根本不想看&#xff0c;今天在打印一本页数不多&#xff0c;但PDF格式的书感觉也不太清楚&#xff0c;我想应该有办法解决&#xff0c;我使用的是解决福昕…