从烧瓶谋杀案gpio中读取DHT22

时间:2020-07-11 21:26:40

标签: python flask raspberry-pi4 adafruit

我在这里遇到了障碍,需要一些帮助...

我有一个带有DHT22温度/湿度传感器和烧瓶的树莓派4,可以在一个简单的网页上显示数据(数据也已写入sqlite3数据库,但这似乎与问题无关)。 cron作业每15分钟触发一次传感器读取。

一切工作都很好(我已经成功收集了数月的数据),直到最近我试图在网页上添加html按钮以允许用户强制传感器立即读取而不是等待下一个时间间隔为止。它是第一次工作,但随后所有尝试读取传感器的尝试现在都会失败,并出现以下错误(请注意,我的dht22数据线位于gpio引脚4上):

Unable to set line 4 to input

根据我到目前为止的研究(以下链接),用于与gpio引脚 libgpiod_pulsein 进行交互的库似乎是一个问题,就像我在终端上查找并终止该过程时一样,正常工作,直到下一次我尝试触发从按钮读取的传感器。

https://github.com/adafruit/Adafruit_CircuitPython_DHT/issues/27

RPI dht22 with flask: Unable to set line 4 to input - Timed out waiting for PulseIn message

我已经尝试了以下两种常见的修复方法:(1)在烧瓶中设置Debug = False,(2)尝试使用其他GPIO引脚,但没有成功或行为上没有明显变化。唯一的解决方法似乎是杀死有问题的过程,但是显然这不是长期的解决方案。其中一些其他页面似乎暗示 libgpiod_pulsein 不能正确退出,但是没有为此提出解决方案,而且我也不敢自己尝试解决方案。

我的问题是:

  1. 我是从根本上错误的方式接近“读取按钮”吗? HTML POST方法是调用python脚本的一种坏方法吗?如果是的话,我该怎么办?我试图避免仅针对1个或2个功能向该项目(ajax等)添加新的语言,但是如果那是唯一的方法,那么我会对此开放...

  2. 这里是否存在错误,导致libgpiod_pulsein的不良行为可以修复?

我在下面添加了一些似乎相关的代码,完整的代码位于https://github.com/nathansibon/raspberry_pi_plant_datalogger

烧瓶服务器

from flask import Flask, render_template
from config import *
import csv
import collect_data
from forms import *
from time import sleep

app = Flask(__name__)
app.config['SECRET_KEY'] = 'sparklingcider' #TODO change to random number

@app.route('/', methods=['GET', 'POST'])
def index():

    f_name = name.replace('_', '')
    f_location = location.replace('_', '')

    button = collect_data_button()

    data = {
        'name': f_name,
        'location': f_location
    }

    # Get the current status variables from CSV
    reader = csv.reader(open('webpage_sensor_data.csv'))
    for row in reader:
        if row[0] == '':
            pass
        else:
            data[row[0]] = row[1]

    # Get the daily of the variables from CSV
    reader = csv.reader(open('webpage_daily_data.csv'))
    for row in reader:
        if row[0] == '':
            pass
        else:
            data[row[0]] = row[1]

    if button.is_submitted():

        print('submit detected')
        collect_data.do()
        sleep(2)

        return render_template('index.html', **data, button=button)

    return render_template('index.html', **data, button=button)

# this method sets the chart images to expire after 5 mins so the browser will fetch new images instead of using the cache
@app.after_request
def add_header(response):
    response.cache_control.max_age = 300
    response.cache_control.public = True
    return response

if __name__ == '__main__':
    app.run(host='0.0.0.0')

HTML主页面

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Current Data {{name}}</title>
    <!-- this next part disables browser caching, otherwise the charts won't update! -->
    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Expires" content="0" />
<style>
    th, td {padding: 5px;}
</style>
</head>

<body>

<h1>Data from {{name}} located at {{location}}</h1>
<h1>Current:</h1>
<h2>Last read at {{time}}</h2>
<form action="" method="post"> {{ button.submit() }} </form>
<table>
    <tr>
        <th></th>
        <th style='text-align:center'>Drybulb</th>
        <th style='text-align:center'>Relative Humidity</th>
        <th style='text-align:center'>RH for 1 kPa VPD</th>
        <th style='text-align:center'>Vapor Pressure Deficit</th>
        <th style='text-align:center'>Light</th>
    </tr>
    <tr>
        <td>Indoor</td>
        <td style='text-align:center'>{{indoor_drybulb}} °C</td>
        <td style='text-align:center'>{{indoor_rh}}%</td>
        <td style='text-align:center'>{{req_rh}}%</td>
        <td style='text-align:center'>{{indoor_vpd}} kPa</td>
        <td style='text-align:center'>{{indoor_lux}} lux</td>
    </tr>
    <tr>
        <td>Outdoor</td>
        <td style='text-align:center'>{{outdoor_drybulb}} °C</td>
        <td style='text-align:center'>{{outdoor_rh}}%</td>
        <td></td>
        <td style='text-align:center'>{{outdoor_vpd}} kPa</td>
        <td></td>
    </tr>
</table>

<h1>Yesterday's Indoor Conditions:</h1>
<table>
    <tr>
        <th></th>
        <th style='text-align:center'>Mean</th>
        <th style='text-align:center'>High</th>
        <th style='text-align:center'>High Time</th>
        <th style='text-align:center'>Low</th>
        <th style='text-align:center'>Low Time</th>
    </tr>
    <tr>
        <td>Drybulb</td>
        <td style='text-align:center'>{{yesterday_drybulb_mean}} °C</td>
        <td style='text-align:center'>{{yesterday_drybulb_max}} °C</td>
        <td style='text-align:center'>{{yesterday_drybulb_max_time}}</td>
        <td style='text-align:center'>{{yesterday_drybulb_min}} °C</td>
        <td style='text-align:center'>{{yesterday_drybulb_min_time}}</td>

    </tr>
    <tr>
        <td>Relative Humidity</td>
        <td style='text-align:center'>{{yesterday_rh_mean}}%</td>
        <td style='text-align:center'>{{yesterday_rh_max}}%</td>
        <td style='text-align:center'>{{yesterday_rh_max_time}}</td>
        <td style='text-align:center'>{{yesterday_rh_min}}%</td>
        <td style='text-align:center'>{{yesterday_rh_min_time}}</td>
    </tr>
    <tr>
        <td>Vapor Pressure Deficit</td>
        <td style='text-align:center'>{{yesterday_vpd_mean}} kPa</td>
        <td style='text-align:center'>{{yesterday_vpd_max}} kPa</td>
        <td style='text-align:center'>{{yesterday_vpd_max_time}}</td>
        <td style='text-align:center'>{{yesterday_vpd_min}} kPa</td>
        <td style='text-align:center'>{{yesterday_vpd_min_time}}</td>
    </tr>
    <tr>
        <td>Light</td>
        <td style='text-align:center'>{{yesterday_lux_mean}} lux</td>
        <td style='text-align:center'>{{yesterday_lux_max}} lux</td>
        <td style='text-align:center'>{{yesterday_lux_max_time}}</td>
        <td></td>
        <td></td>
    </tr>
</table>

<h1>Last Week</h1>
<table>
    <tr>
        <img src="/static/last_week_drybulb.png" alt="drybulb graph" width="80%" height="auto">
    </tr>
    <tr>
        <img src="/static/last_week_rh.png" alt="drybulb graph" width="80%" height="auto">
    </tr>
    <tr>
        <img src="/static/last_week_vpd.png" alt="drybulb graph" width="80%" height="auto">
    </tr>
    <tr>
        <img src="/static/last_week_lux.png" alt="lux graph" width="80%" height="auto">
    </tr>
</table>

</body>
</html>

按钮的烧瓶形式

from flask_wtf import FlaskForm
import wtforms as wt
from wtforms.fields import html5 as wt5

class collect_data_button(FlaskForm):

    submit = wt.SubmitField('Read Now')

收集数据脚本(这是cron每15分钟运行一次)

from function_library import *
import logging, time, os, datetime
from config import *


# This is where the main code is kept for this script.
# We enclose it in a function so we can wrap it in a general purpose error-handler as shown below
# This makes writing log files much simpler and will work whether it's run from the shell or a cron job
def do():

    # retrieve data from OpenWeatherMap API, then calculate additional metrics from retrieved data and append list
    outdoor = get_outdoor_weather()
    outdoor = calc_outdoor_weather(outdoor)

    # retrieve data from sensor(s), then calculate additional metrics from retrieved data and append list.
    indoor = get_indoor_all()

    # Full path of database: /home/pi/share/env_datalogger/pi_X_data.db
    update_db(name + '_data.db', indoor, outdoor)

    update_web_vars_sensor(indoor, outdoor)
    update_web_charts(name + '_data.db')


# Set all paths to the current directory so cron job will not crash, but code will still run if you move files later...
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)

logging.basicConfig(filename='logs/collect_data.log', level=logging.INFO)
logging.info('started @ ' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

# this 'if' is necessary to allow other scripts (such as buttons on the flask server) to trigger the system to read sensors, but prevent this from running on module import
if __name__ == "__main__":
    # general purpose error handling to log file. helpful when executing from cron since you don't see errors
    try:
        do()
    except Exception as e:
        logging.exception('Error in main')
        logging.info(e)

    logging.info('completed @ ' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + '\n')

    exit()

function_library的导入(包含以下功能)

import math, numpy, sqlite3, time, logging, pyowm
import pandas as pd
import numpy as np
from config import *
from datetime import datetime
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# These packages will raise an error if not running this code on a Pi
# Adding this error handling will allow coding and debugging on PC
try:
    import board, adafruit_dht
except Exception as e:
    logging.info('not connected to pi, problematic libraries not imported (1 of 2)')
    logging.info(e)
    pass

try:
    import busio, adafruit_veml7700
except Exception as e:
    logging.info('not connected to pi, problematic libraries not imported (2 of 2)')
    logging.info(e)
    pass

室内传感器读取功能(在function_library.py内部)

def get_indoor_all():

    output = [
        datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        pi_serial,
        location
    ]

    if sensors.get('temp_humid') != 'none':
        # output is [0] datetime [1] serial [2] location [3] drybulb [4] rh
        for i in get_indoor_weather():
            output.append(i)

        # output is now [0] datetime [1] serial [2] location [3] drybulb [4] rh [5] wet bulb [6] dew point, [7] vapor pressure deficit [8] rh for vpd [9] white_light [10] lux
        output = calc_indoor_weather(output)
    else:
        logging.info('No temp/humidity sensor listed, adding placeholders')
        for i in range(6):
            output.append(0)

    if sensors.get('light') != 'none':
        # output is [0] datetime [1] serial [2] location [3] drybulb [4] rh [5] wet bulb [6] dew point, [7] vapor pressure deficit [8] rh for vpd [9] white_light [10] lux
        for i in get_indoor_light():
            output.append(i)
    else:
        logging.info('No light sensor listed, adding placeholders')
        for i in range(2):
            output.append(0)

    # TODO add soil moisture sensor

    return output

DHT22传感器读取(由get_indoor_all调用)

def get_indoor_weather():

    # Initial the dht device, with data pin connected to:
    print('starting dht22')
    dhtDevice = adafruit_dht.DHT22(board.D4)

    # Unfortunately, this sensor doesn't always read correctly so error handling and noise reduction is included
    max = 5
    err_count = 0
    drybulb = []
    humid = []
    output = []

    while len(drybulb) < max and err_count < max :
        logging.info('read: '+str(len(drybulb))+', err: '+str(err_count))
        # The DHT22 is reportedly slow at reading, this wait hopefully prevents a bad read (i.e. wildly different than it should be) on fail
        time.sleep(2)

        try:
            drybulb.append(dhtDevice.temperature)
            humid.append(round(dhtDevice.humidity / 100, 2))  # convert RH from "50%" to "0.50" to match outdoor weather format and work in calculations
            logging.info('success')
        except Exception as ee:
            #logging.info('Error reading Temp-Humid Sensor, retrying...')
            logging.info(ee)
            err_count += 1

    output.append(std_filter(drybulb, 1, 2))
    output.append(std_filter(humid, 1, 2))

    return output

0 个答案:

没有答案