基于 WEB 的室内温湿度监测系统(树莓派)

2023-10-30

一、项目介绍

    本实验通过 DHT11 模块测量大气中温湿度,利用树莓派多功能的用途,将温湿度测量结果显示到网页,并包含历史数据、实时折线图表示。

二、 实验介绍

1. 实验原材料

树莓派 Raspberry 4 Model B,DHT11 v1.3,电阻,杜邦线,面包板。

2. 流程图

在这里插入图片描述

三、硬件部分

DHT11 传感器实验相关知识

1.1 DHT11引脚说明
下图为GPIO引脚对照表
GPIO引脚对照表
本次实验使用的DHT11有3个引脚:VCC、DATA、GND。
VCC接 5V PIN 02;DATA接 PIN 08;GND接地 PIN 06。
硬件连接图如下:右上角40引脚的2、6、8所连的三条线为DHT11所连的线。下面的13、14引脚所连的线为温度阈值报警小灯所用(此为附加功能所用)。
在这里插入图片描述
1.2 外设读取步骤
主机和从机之间的通信可通过如下几个步骤完成((外设(如微处理器)读取DHT11的数据的步骤)。
步骤一:
DHT11上电后(DHT11上电后要等待1S以越过不稳定状态在此期间不能发送任何指令),测试环境温湿度数据,并记录数据,同时DHT11的DATA数据线由上拉电阻拉高一直保持高电平;此时DHT11的DATA引脚处于输入状态,时刻检测外部信号。
步骤二:
微处理器的I/O设置为输出同时输出低电平,且低电平保持时间不能小于18ms(最大不得超过30ms),然后微处理器的I/O设置为输入状态,由于上拉电阻,微处理器的I/O即DHT11的DATA数据线也随之变高,等待DHT11作出回答信号。发送信号如下图所示: 在这里插入图片描述
步骤三:
DHT11的DATA引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后DHT11的 DATA引脚处于输出状态,输出83微秒的低电平作为应答信号,紧接着输出87微秒的高电平通知 外设准备接收数据,微处理器的I/O此时处于输入状态,检测到I/O有低电平(DHT11回应信号) 后,等待87微秒的高电平后的数据接收,发送信号下图所示:
在这里插入图片描述
步骤四:
由DHT11的DATA引脚输出40位数据,微处理器根据I/O电平的变化接收40位数据,位数据 “0”的格式为:54微秒的低电平和23-27微秒的高电平,位数据“1”的格式为:54微秒的低 电平加68-74微秒的高电平。位数据“0”、“1”格式信号如下图所示:
在这里插入图片描述
结束信号:
DHT11的DATA引脚输出40位数据后,继续输出低电平54微秒后转为输入状态,由于上拉电 阻随之变为高电平。但DHT11内部重测环境温湿度数据,并记录数据,等待外部信号的到来。

1.3. DHT11单总线传送数据位定义
DATA 用于微处理器与 DHT11 之间的通讯和同步,采用单总线数据格式,一次传送 40 位数据,高位先出。
数据格式:

8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验位,其中湿度小数部分为 0

示例一:接收到的40位数据为:
0011 0101 / 0000 0000 / 0001 1000 / 0000 0100 / 0101 0001
湿度高8位 / 湿度低8位 / 温度高8位 / 温度低8位 / 校验位
计算:
00110101+00000000+00011000+00000100=01010001
接收数据正确:
湿度:00110101(整数)=35H=53%RH00000000(小数)=00H=0.0%RH=>53%RH+0.0%RH=53.0%RH
温度:00011000(整数)=18H=24℃00000100(小数)=04H=0.4℃=>24℃+0.4℃=24.4℃

实验中dht11.py所读取的温度显示为整数,欲将其变为带有一位小数的输出,只需将return值处理一下,将温度的小数部分the_bytes[3]加上就可以了

return DHT11Result(DHT11Result.ERR_NO_ERROR, the_bytes[2]+float(the_bytes[3])/10, the_bytes[0])

四、软件部分

1. 设计流程

在这里插入图片描述

相关参数调用关系

本实验中参数调用主要围绕app.py文件进行,app.py运行后,首先读取dht11读取到的当前温湿度参数,把参数存到数据库中,创建数组调用数据生成折线图。生成的图片通过存到对应路径上。html文件调用路径上存的文件显示到网页上,同时调用style.css文件进行网页美化。

文件夹路径关系:
在这里插入图片描述
主文件app.py位于WebServer文件夹
第三级目录中:
templates用于放html文件;
statics用于存放,css文件和其他的图片(可自选,例如背景图、温度湿度曲线图等便于调用);
history用于放dht11.py文件(因为我的树莓派B4无法使用Adafruit库,所以是使用硬件直接读取温湿度数据的。)

2. 树莓派学习笔记

树莓派相关下载过程此处不再赘述,这里只总结一些我在实验中遇到的问题以及解决的方法。

  1. SSH 远程连接树莓派 使用SSH远程连接后,可以在另外一台PC机上操控树莓派
    在这里插入图片描述
    这个问题的出现的很普遍,因为大家对这个操作平台都还不是很熟悉,这个问题出现的原因是树莓派 SSH 服务没有打开,在树莓派桌面左上角树莓派图标上单击-> Preferences-> Raspberry Pi Configuration-> Interfaces -> SSH Enabled 选中 -> OK,重启PUTTY就可以解决了。
  2. 树莓派远程桌面 在实现远程连接时,要保证两个设备处在同一个网络中。 而且要确认好目前设备开的端口是哪一个,树莓派的IP地址是哪一个,确定好了才可以连接。

3. 代码程序报错记录

在运行这几个py文件时,遇到的几个主要的报错以及出现原因、处理方法如下:

  1. 报错:SyntaxError: invalid syntax
    这句话的意思是Python代码语法有问题。Python对语法的要求非常严格,有时候代码是网上直接找来复制粘贴的,就有可能导致明明看上去没错,但就一直报语法错误,可以尝试清空格式后重新输入一遍。
    另外需要注意的是Python2和Python3的语法有区别,比如Python2的print后面不用加括号,而Python3中就会报错。
  2. 报错:ModuleNotFoundError: No module named 'Adafruit_DHT’
    “import Adafruit_DHT ”语句错误,提示找不到Adafruit_DHT模块,但是在terminal里输入“import Adafruit_DHT”不报错,并且已经下载了相关库。可能是python版本的问题,先明确你运行程序时使用的是哪个Python版本。
    使用的是Python2使用的语句是:sudo pip install Adafruit_Python_DHT; sudo python setup.py install,
    但使用的是Python3使用的语句是:sudo pip3 install Adafruit_Python_DHT; sudo python3 setup.py install
    注意一定要用pip3
  3. 报错:ImportError: cannot import name ‘Beaglebone_Black_Driver’ from 'Adafruit_DHT’
    通过报错信息,发现是库中的common.py里引用get_platform有问题,再去platform_detect.py里查看发现树莓派的版本定义里没有4B,由于我们使用的是4B树莓派,所以导致返回了None
    解决:出现这个原因是AdaFruit不再更新这个旧的Adafruit_DHT库,所以里面没有更新树莓派4B对应的cpu。有两种解决方法,使用这个传感器的新库“Adafruit_CircuitPython_DHT”,或者在你原来的Adadruit_DHT库中自己手动更新,具体操作如下:
    (1)找到Adafruit_DHT文件夹, “/usr/local/lib/python3.7/dist-packages/Adafruit_DHT/”,
    (2)打开文件夹里的platform_detect.py文件,可以看到把下面这两行代码添加到最后。
    在这里插入图片描述
    其中BCM2711是树莓派4B的CPU,它是四核Cortex-A72 64位。其它的BCMxxxx是其它树莓派版本的CPU。
  4. Adafruit库无法使用
    在根据讲义下载DHT库Adafruit库时,下载到的库中缺少Raspberry_Pi_2driver.py文件,导致执行DHT11_test测试文件时就没能读出数据,经过原因排查和多次下载,我们推断猜测可能是因为树莓派的问题,因为我们使用的是树莓派B4,可能是版本更新了的问题。
    对此我们的解决方案是:不使用Adafruit读取温湿度数据,而是用我们之前用过的DHT11.py文件进行读取,这个文件是通过GPIO端口直接读取数据的,并具有CRC纠检错功能,足够实验的功能实现。
    实际操作:WebServer文件夹下添加新文件夹:library,将dht11.py文件放在其中作调用功能,并在app.py文件加调用语言from library import dht11,将接下来的变量名重置。

4. 代码

基础功能的代码:使用dht11.py读取温度后,将温湿度显示到网页上。

(1)数据读取部分dht11.py

用于通过传感器读取数据的文件

import time
import RPi


class DHT11Result:
    'DHT11 sensor result returned by DHT11.read() method'

    ERR_NO_ERROR = 0
    ERR_MISSING_DATA = 1
    ERR_CRC = 2

    error_code = ERR_NO_ERROR
    temperature = -1
    humidity = -1

    def __init__(self, error_code, temperature, humidity):
        self.error_code = error_code
        self.temperature = temperature
        self.humidity = humidity

    def is_valid(self):
        return self.error_code == DHT11Result.ERR_NO_ERROR


class DHT11:
    'DHT11 sensor reader class for Raspberry'

    __pin = 0

    def __init__(self, pin):
        self.__pin = pin

    def read(self):
        RPi.GPIO.setup(self.__pin, RPi.GPIO.OUT)

        # send initial high
        self.__send_and_sleep(RPi.GPIO.HIGH, 0.05)

        # pull down to low
        self.__send_and_sleep(RPi.GPIO.LOW, 0.02)

        # change to input using pull up
        RPi.GPIO.setup(self.__pin, RPi.GPIO.IN, RPi.GPIO.PUD_UP)

        # collect data into an array
        data = self.__collect_input()

        # parse lengths of all data pull up periods
        pull_up_lengths = self.__parse_data_pull_up_lengths(data)

        # if bit count mismatch, return error (4 byte data + 1 byte checksum)
        if len(pull_up_lengths) != 40:
            return DHT11Result(DHT11Result.ERR_MISSING_DATA, 0, 0)

        # calculate bits from lengths of the pull up periods
        bits = self.__calculate_bits(pull_up_lengths)

        # we have the bits, calculate bytes
        the_bytes = self.__bits_to_bytes(bits)

        # calculate checksum and check
        checksum = self.__calculate_checksum(the_bytes)
        if the_bytes[4] != checksum:
            return DHT11Result(DHT11Result.ERR_CRC, 0, 0)

        # ok, we have valid data, return it
        return DHT11Result(DHT11Result.ERR_NO_ERROR, the_bytes[2]+float(the_bytes[3])/10, the_bytes[0]+float(the_bytes[1])/10)

    def __send_and_sleep(self, output, sleep):
        RPi.GPIO.output(self.__pin, output)
        time.sleep(sleep)

    def __collect_input(self):
        # collect the data while unchanged found
        unchanged_count = 0

        # this is used to determine where is the end of the data
        max_unchanged_count = 100

        last = -1
        data = []
        while True:
            current = RPi.GPIO.input(self.__pin)
            data.append(current)
            if last != current:
                unchanged_count = 0
                last = current
            else:
                unchanged_count += 1
                if unchanged_count > max_unchanged_count:
                    break

        return data

    def __parse_data_pull_up_lengths(self, data):
        STATE_INIT_PULL_DOWN = 1
        STATE_INIT_PULL_UP = 2
        STATE_DATA_FIRST_PULL_DOWN = 3
        STATE_DATA_PULL_UP = 4
        STATE_DATA_PULL_DOWN = 5

        state = STATE_INIT_PULL_DOWN

        lengths = [] # will contain the lengths of data pull up periods
        current_length = 0 # will contain the length of the previous period

        for i in range(len(data)):

            current = data[i]
            current_length += 1

            if state == STATE_INIT_PULL_DOWN:
                if current == RPi.GPIO.LOW:
                    # ok, we got the initial pull down
                    state = STATE_INIT_PULL_UP
                    continue
                else:
                    continue
            if state == STATE_INIT_PULL_UP:
                if current == RPi.GPIO.HIGH:
                    # ok, we got the initial pull up
                    state = STATE_DATA_FIRST_PULL_DOWN
                    continue
                else:
                    continue
            if state == STATE_DATA_FIRST_PULL_DOWN:
                if current == RPi.GPIO.LOW:
                    # we have the initial pull down, the next will be the data pull up
                    state = STATE_DATA_PULL_UP
                    continue
                else:
                    continue
            if state == STATE_DATA_PULL_UP:
                if current == RPi.GPIO.HIGH:
                    # data pulled up, the length of this pull up will determine whether it is 0 or 1
                    current_length = 0
                    state = STATE_DATA_PULL_DOWN
                    continue
                else:
                    continue
            if state == STATE_DATA_PULL_DOWN:
                if current == RPi.GPIO.LOW:
                    # pulled down, we store the length of the previous pull up period
                    lengths.append(current_length)
                    state = STATE_DATA_PULL_UP
                    continue
                else:
                    continue

        return lengths

    def __calculate_bits(self, pull_up_lengths):
        # find shortest and longest period
        shortest_pull_up = 1000
        longest_pull_up = 0

        for i in range(0, len(pull_up_lengths)):
            length = pull_up_lengths[i]
            if length < shortest_pull_up:
                shortest_pull_up = length
            if length > longest_pull_up:
                longest_pull_up = length

        # use the halfway to determine whether the period it is long or short
        halfway = shortest_pull_up + (longest_pull_up - shortest_pull_up) / 2
        bits = []

        for i in range(0, len(pull_up_lengths)):
            bit = False
            if pull_up_lengths[i] > halfway:
                bit = True
            bits.append(bit)

        return bits

    def __bits_to_bytes(self, bits):
        the_bytes = []
        byte = 0

        for i in range(0, len(bits)):
            byte = byte << 1
            if (bits[i]):
                byte = byte | 1
            else:
                byte = byte | 0
            if ((i + 1) % 8 == 0):
                the_bytes.append(byte)
                byte = 0

        return the_bytes

    def __calculate_checksum(self, the_bytes):
        return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255

(2)主文件app.py
  1. 这里要注意地址需要修改成自己主机的地址,查看树莓派地址的方式:在终端输入ifconfig然后回车。
  2. from-import是用来引用文件中的文件内容的。 title中的内容"Sensor Data"和html中的代码</>Sensor Data</>是对应的,如果想要修改,需要两边同时修改。
  3. time.sleep(1)的用处是让程序间隔一秒读取数据。这里的timesleep是python自带的一个函数,在编写程序时候,我们有时需要将程序短暂的停顿一下,这个时候就需要用到time包下面的sleep。
  4. 有的时候运行报错可能是因为端口的问题,尝试把port=50改成80/5080/8080之类的就好了。
from flask import Flask ,render_template
from library import dht11
import RPi.GPIO as GPIO


app = Flask(__name__)
# initialize GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()

#read data using pin 14
instance = dht11.DHT11(pin=14)
    
    
@app.route("/")
def hello():
    result = instance.read()
    time.sleep(1)
    temperature = result.temperature
    humidity = result.humidity
    templateData={
        'title': "Sensor Data",
        'temp': temperature,
        'hum': humidity,
    }       
    return render_template("index.html", **templateData)
    
if __name__ == "__main__":
     app.run(host="192.168.1.xxx", port=50, debug=True)


(3)index.html
<!DOCTYPE html>
   <head>
      <html>
   <head>
   <title>{{title}}</title>

   <link rel="stylesheet" href='../static/style.css'/>
   </head>
   <body>
       <h1>Sensor Data</h1>
       <h2>TEMPERATURE ==> {{ temp }} oC</h2>
       <h3>HUMIDITY (Rel.) ==> {{ hum }} %</h3>
        
        </body>
       </html>

(4)style.css:用于美化html背景和字体颜色
body{
 	background: blue;
 	color: yellow;
	 padding:1%
}
.button {
 	font: bold 15px Arial;
 	text-decoration: none;
 	background-color: #EEEEEE;
 	color: #333333;
 	padding: 2px 6px 2px 6px;
 	border-top: 1px solid #CCCCCC;
 	border-right: 1px solid #333333;
 	border-bottom: 1px solid #333333;
 	border-left: 1px solid #CCCCCC;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于 WEB 的室内温湿度监测系统(树莓派) 的相关文章

随机推荐

  • 宝宝智能起名,免费起名实现方案,带源码

    宝宝智能起名 免费起名实现方案 带源码 作为一个coder 没有什么问题是代码解决不了的 哈哈哈 调皮一下 一个好的名字可以潜移默化的改变人的一生 很多人从来没有考虑到如何起一个好听的有文化的名字 比如周星驰的名字 来自王勃的 滕王阁序 中
  • Linux杀僵尸进程

    linux杀死僵尸进程https www cnblogs com mayhh p 9968428 html ps A o stat ppid pid cmd grep e Zz awk print 2 xargs kill 9 1 查看系统
  • 【灵敏度分析】应用莫里斯方法降低因素低估的风险(Matlab代码实现)

    个人主页 研学社的博客 欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 灵敏度分析用于估计
  • VennDiagram包绘制Venn图

    1 创建绘图测试数据 Venn data lt data frame A sample 1 100 80 replace T B sample 1 100 80 replace T C sample 1 100 80 replace T D
  • 解决Win找不到msvcp140_codecvt_ids.dll

    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题 如果是新手第一时间会认为是软件或游戏出错了 其实并不是这样 其主要原因就是你电脑系统的该dll文件丢失了或者损坏了 这时你只需下载这个msvcp140 codecvt ids dll
  • Activiti流程定义缓存源码分析7-流程缓存自定义

    接下来 重点看一下Activiti中如何自定义流程缓存 上面我们提到过自定义类首先需要继承StandaloneProcessEngineConfiguration类并通过动态属性注入方式为引擎配置类注入processDefinitionIn
  • 数据结构小白之冒泡排序算法

    1 冒泡排序 1 1 思路 冒泡排序的每一轮从杂乱无章的数组头部开始 每两个元素之间进行交换 直到这一轮当中最大或者最小的元素放在数组的尾部 然后去不断重复这个过程 ps 冒泡排序的核心在于双循环的编写 外层循环用来进行数组的遍历 内层循环
  • 创建利润中心在利润中心组选择时提示不存在利润中心组

    已经KCH1创建好利润中心组的情况下KE51选刚才创建好的利润中心组依然会有如题这样的提示 原因是没有0KE5进行成本控制范围设置 如图下图 设置好后再KE51创建利润中心就可以了
  • 《代码大全2》第4章 关键的“构建“决策

    目录 前言 本章主题 4 1 选择编程语言 4 2 编程约定 4 3 你在技术浪潮中的位置 4 3 1 深入一种语言去编程 的例子 4 3 2 在一种语言上编程 和 深入一种语言去编程 的区别 4 4 选择主要的构建实践方法 核对表 主要的
  • Mac使用国内源安装homebrew

    记录一下使用国内镜像源重装homebrew 在终端输入以下命令 bin bash c curl fsSL https gitee com cunkai HomebrewCN raw master Homebrew sh 会有如下提示 开始执
  • Android_AlarmManager(全局定时器)

    一 简介 AlarmManage有一个AlarmManagerServie服务程序 该服务程序才是正真提供闹铃服务的 它主要维护应用程序注册下来的各类闹铃并适时的设置即将触发的闹铃给闹铃设备 在系统中 linux实现的设备名 为 dev a
  • 【 NLP】如何减小预训练语言模型?

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • 【SCOI2010】股票交易

    Description SCOI2010 股票交易 在T天时间内 第 i 天股票购入价为 ap i 出售价为 bp i 每天最多购入 as i 股 最多出售 bs i 股 任意时刻手中的股票数不能超过 Maxp 且两次交易至少间隔 W 天
  • tomcat为什么把那个文件取名为catalina

    内部寓意就是tomcat的脚本文件 寄寓 是个小岛的名字 开发者曾在岛上生活过 Tomcat的这个单词的意思是 公猫 因为它的开发者姆斯 邓肯 戴维森希望用一种能够自己照顾自己的动物代表这个软件 于是命名为tomcat 它的Logo兼吉祥物
  • 分布式环境下的数据一致性

    1 互联网技术的四大法宝 多线程 分布式 异步 缓存 2 事务 编程式事务 3乐观锁 悲观锁 基于状态机的乐观锁
  • Android中引用sweet-alert-dialog以及遇到的问题解决

    概述 sweet alert dialog是一个带动画效果的自定义对话框样式 可以让你的Android程序的对话框优美很多 GitHub地址 Android Studio中引用 repositories mavenCentral depen
  • 【解决vscode终端输出中文乱码问题图文教程】

    全网最全解决vscode运行C C 终端输出乱码问题图文教程 解决vscode终端输出中文乱码问题 强推 方法二 少走几年弯路 乱码原因 方法一 永久性 方法二 永久性 强力推荐 附加 CMD修改成旧控制台 方法三 临时性 如果之前尝试了其
  • kafka入门,发送原理和生产者重要参数(三)

    发送原理 在消息发送过程中 涉及两个线程 main线程和Sender线程 在main线程中创建了一个双端队列 RecordAccumulator Sender过程不断从RecordAccumulator中拉取消息发送到Kafka Broke
  • Maven本地仓库安装jar包

    开发过程中的jar包依赖 通常是通过中央仓库 阿里云仓库等地方直接下载的 但是有一些jar包是不太容易下载 或者说对应的仓库中没有对应的jar包 比如ojdbc6 11 2 0 1 0 jar这个jar包 maven中央仓库中就没有这个版本
  • 基于 WEB 的室内温湿度监测系统(树莓派)

    一 项目介绍 本实验通过 DHT11 模块测量大气中温湿度 利用树莓派多功能的用途 将温湿度测量结果显示到网页 并包含历史数据 实时折线图表示 二 实验介绍 1 实验原材料 树莓派 Raspberry 4 Model B DHT11 v1