1. 项目概述:打造你的专属气象站

你是否曾对天气预报的准确性感到好奇,或者想亲手搭建一个属于自己的环境监测系统?在智能家居、小型温室种植或是工作室环境监控等场景下,一个能够实时、精准反馈本地微气候的设备,其价值远超依赖宏观区域预报的通用天气应用。今天,我们就来动手实现一个基于树莓派(Raspberry Pi)和Flask框架的物联网气象监测系统。这个项目不仅成本低廉、易于构建,更能让你深入理解物联网(IoT)从数据采集、传输到可视化展示的完整链路。

整个系统的核心逻辑非常清晰:我们利用树莓派作为“大脑”和微型服务器,通过其通用输入输出(GPIO)引脚连接DHT11温湿度传感器,实时采集物理环境数据。随后,使用Python编写数据采集逻辑,并借助轻量级的Flask Web框架,将采集到的数据封装成RESTful API接口。最终,通过一个简洁的网页前端,你可以在任何连接同一网络的设备上,实时查看当前的温湿度读数,甚至回溯过去一段时间的数据变化曲线。这不仅仅是拼接硬件和复制代码,我们将深入探讨每一步背后的设计考量、参数选择以及实际部署中可能遇到的“坑”,确保你不仅能成功复现,更能透彻理解其工作原理。

2. 核心硬件选型与电路连接解析

2.1 硬件清单与选型理由

一份清晰的物料清单是项目成功的起点。以下是构建本系统所需的核心硬件及其选型背后的思考:

  • Raspberry Pi 3 Model B+ (或更新型号) :作为项目的主控制器和服务器。选择树莓派3B+而非更早型号,主要因其集成了Wi-Fi和蓝牙,免去了额外配置无线网卡的麻烦,部署位置更灵活。如果使用树莓派4,其更强的处理能力和双频Wi-Fi会带来更流畅的Web服务体验,但本项目对算力要求不高,三代及以上型号均完全胜任。
  • Micro SD卡 (建议16GB Class10及以上) :用于安装树莓派操作系统(Raspberry Pi OS)和项目代码。Class10及以上规格能保证系统运行和读写速度,避免因存储卡性能瓶颈导致系统卡顿。
  • DHT11温湿度传感器 :本项目的数据来源。选择DHT11而非更精确的DHT22或BME280,主要基于两点考虑:一是成本极低,非常适合入门和验证概念;二是其单总线(1-Wire)通信协议,仅需一个GPIO引脚进行数据读写,接线和编程都相对简单。需要明确的是,DHT11的精度(温度±2°C,湿度±5%RH)和响应速度对于家庭室内环境监测是完全足够的,它的局限性在于不适合需要高精度或快速变化的工业场景。
  • 杜邦线 (母对母,4根) :用于连接树莓派GPIO引脚与DHT11传感器。母对母接口可以直接插在引脚和传感器针脚上,无需焊接,非常适合原型搭建。
  • 5V Micro USB电源 (额定电流≥2.5A) :为树莓派供电。务必使用质量合格的电源,电流不足可能导致树莓派在高负载时重启,尤其是在连接了外设的情况下。
  • 可选配件
    • 面包板 :虽然不是必须,但使用面包板进行连接可以使接线更整洁,也方便后续扩展其他传感器(如光敏电阻、气压计)。
    • 外壳与散热片 :为树莓派安装外壳和散热片能提升其长期运行的稳定性,尤其是在密闭空间或夏季高温环境。
    • 显示器、键盘、鼠标 :用于初次设置树莓派。一旦完成基础系统配置并开启SSH,后续所有操作都可以通过远程终端完成。

注意 :DHT11传感器有模块化和封装体两种常见形态。建议使用自带板载上拉电阻和滤波电容的模块化版本(通常为蓝色PCB),它比三针的封装体版本更稳定,抗干扰能力更强,对新手更友好。

2.2 电路连接详解与原理

正确的硬件连接是数据采集的物理基础。DHT11与树莓派的连接看似简单,但理解其原理能有效避免后续的通信故障。

DHT11模块通常有三个引脚(有些是四个,其中一个是空脚):

  1. VCC (电源正极) :接树莓派的 3.3V 引脚(例如物理引脚1)。 这里有一个关键点 :虽然DHT11模块的工作电压范围是3.3V-5.5V,但树莓派的GPIO引脚逻辑电平是3.3V。如果将DHT11的VCC接到5V引脚,其数据引脚输出的高电平可能是5V,这有可能损坏树莓派的GPIO引脚。为安全起见,统一使用3.3V供电是最稳妥的方案。
  2. DATA (数据线) :接树莓派的任意一个 GPIO 引脚(例如我们选用 GPIO4 ,对应物理引脚7)。这条线负责双向通信。
  3. GND (地线) :接树莓派的 GND 引脚(例如物理引脚6)。

连接示意图(文字描述)

  • 将DHT11模块的 VCC 引脚(通常标记为‘+’或‘VDD’)用一根杜邦线连接到树莓派的 物理引脚1 (3.3V电源)
  • 将DHT11模块的 DATA 引脚(有时标记为‘OUT’或‘S’)用一根杜邦线连接到树莓派的 物理引脚7 (GPIO4)
  • 将DHT11模块的 GND 引脚(标记为‘-’或‘GND’)用一根杜邦线连接到树莓派的 物理引脚6 (GND)

为什么选择GPIO4? 树莓派的GPIO引脚在软件层面是通用的,选择GPIO4并没有特殊硬件原因,主要是因为它位置方便(靠近3.3V和GND引脚),且不易与其他系统功能(如UART、I2C)冲突。你完全可以选择其他可用的GPIO,如GPIO17、GPIO27等,只需在代码中相应修改即可。

实操心得 :连接时,最好在树莓派断电状态下进行。插拔杜邦线时,确保对准引脚,避免因短路或错位导致硬件损坏。连接完成后,可以轻轻拉扯一下线材,确认接触牢固。

3. 软件环境搭建与核心库剖析

硬件准备就绪后,我们需要为树莓派构建一个稳定的软件运行环境。这个过程包括操作系统安装、基础配置以及Python库的安装。

3.1 树莓派系统初始化与远程访问配置

首先,需要在SD卡上安装树莓派操作系统(Raspberry Pi OS Lite版本即可,无需桌面环境以节省资源)。使用官方工具Raspberry Pi Imager进行烧录是最简单的方式。在烧录前,Imager的高级设置(按Ctrl+Shift+X)中,务必预先完成以下几项关键配置,这能极大简化后续步骤:

  1. 启用SSH :勾选“Enable SSH”,并设置密码认证或公钥认证。这是实现无头(Headless,即无显示器)运行和远程管理的基石。
  2. 配置Wi-Fi :填入你的无线网络名称(SSID)和密码,并选择国家代码(如CN)。这样树莓派在首次启动时就能自动连接网络。
  3. 设置主机名与用户 :建议设置一个易记的主机名,如 weather-pi ,并创建你的用户名和密码。避免使用默认的 pi 用户以增强安全性。

烧录完成后,将SD卡插入树莓派并上电。等待几分钟后,你可以在路由器管理界面查找名为 weather-pi (或你设置的主机名)的设备,记下其IP地址。随后,即可在电脑上使用SSH客户端(如Windows的PuTTY或终端下的 ssh 命令)进行连接: ssh your_username@weather-pi.local ssh your_username@<树莓派IP地址>

3.2 Python虚拟环境与依赖库安装

连接到树莓派后,首先更新系统软件包列表: sudo apt update && sudo apt upgrade -y

接下来,我们为项目创建一个独立的Python虚拟环境。这能隔离项目依赖,避免与系统Python环境发生冲突,是Python项目开发的最佳实践。

# 安装python3虚拟环境工具(如果尚未安装)
sudo apt install python3-venv -y

# 创建一个项目目录并进入
mkdir ~/weather_station && cd ~/weather_station

# 创建名为‘venv’的虚拟环境
python3 -m venv venv

# 激活虚拟环境
source venv/bin/activate

激活后,命令行提示符前会出现 (venv) 标识。现在所有 pip 安装的包都将仅限于此环境内。

根据项目描述,我们需要安装以下Python库。我们来逐一解释每个库的作用:

pip install RPi.GPIO requests flask flask-restful flask-wtf wtforms
  • RPi.GPIO :这是树莓派GPIO编程的核心库。它提供了用Python控制GPIO引脚(如设置输入/输出模式、读取数字信号、输出PWM波等)的接口。我们的代码将通过它来与DHT11传感器通信。
  • Flask :一个极其轻量灵活的Python Web框架。它负责处理HTTP请求和响应。在我们的项目中,Flask将扮演Web服务器的角色,监听特定的网络端口,当用户通过浏览器访问时,返回包含温湿度数据的网页。
  • Flask-RESTful :Flask的一个扩展,用于快速构建RESTful API。它可以帮助我们以更结构化、更符合REST规范的方式定义数据接口(如 /api/temperature , /api/humidity ),虽然在本项目的简单表单交互中不是必须,但它让后端API设计更清晰,便于未来扩展为手机App提供数据。
  • Flask-WTF 与 WTForms :这两个库配合使用,用于在Web应用中安全地生成和处理表单。WTForms定义了表单的结构和验证规则,Flask-WTF则将其与Flask集成。我们将用它来创建网页上那个让用户选择查看“温度”、“湿度”或“图表”的复选框表单。
  • requests :一个优雅而简单的HTTP库。虽然在我们的服务器端代码中可能不直接使用,但在测试API接口,或者未来编写一个独立的客户端程序来从多个气象站聚合数据时,它会非常有用。先安装以备不时之需。

注意事项 :在树莓派上使用 pip 安装某些需要编译的库时,可能会因为内存不足而失败。如果遇到这种情况,可以尝试增加交换空间(swap),或者使用 pip install --no-cache-dir 选项。对于 RPi.GPIO ,也可以直接通过系统包管理器安装: sudo apt install python3-rpi.gpio ,但这样安装的库会在系统Python环境中,而非我们的虚拟环境内。

4. 核心代码实现与Flask应用架构

有了环境,我们就可以开始编写项目的核心代码了。我们将按照MVC(模型-视图-控制器)的简化思想来组织代码,但更贴切地说,是数据采集层、业务逻辑层和表现层的分离。

4.1 数据采集层:与DHT11传感器对话

首先,我们创建一个专门负责与传感器打交道的模块 sensor_reader.py 。DHT11的通信协议是单总线协议,时序要求严格。虽然我们可以用 RPi.GPIO 库手动实现时序,但使用一个成熟的第三方库 Adafruit_DHT 会更稳定、更简单。我们需要先安装它: pip install Adafruit_DHT

# sensor_reader.py
import Adafruit_DHT
import time
from datetime import datetime

# 定义传感器类型和GPIO引脚
DHT_SENSOR = Adafruit_DHT.DHT11
DHT_PIN = 4  # 对应我们之前连接的GPIO4

class SensorReader:
    def __init__(self, max_retries=3):
        self.max_retries = max_retries
        self.readings_cache = []  # 用于存储最近24次读数
        self.max_cache_size = 24

    def read_sensor(self):
        """尝试读取传感器数据,失败时重试"""
        for attempt in range(self.max_retries):
            humidity, temperature = Adafruit_DHT.read_retry(DHT_SENSOR, DHT_PIN)
            if humidity is not None and temperature is not None:
                # 读取成功,构建数据对象
                reading = {
                    'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                    'temperature': round(temperature, 1),
                    'humidity': round(humidity, 1)
                }
                # 更新缓存
                self._update_cache(reading)
                return reading
            else:
                print(f"传感器读取失败,第{attempt+1}次重试...")
                time.sleep(1)  # 等待1秒后重试
        # 所有重试都失败
        print("错误:无法从DHT11传感器获取数据。请检查连接。")
        return None

    def _update_cache(self, new_reading):
        """管理最近24次读数的缓存"""
        self.readings_cache.append(new_reading)
        # 如果超过最大缓存数,移除最旧的读数
        if len(self.readings_cache) > self.max_cache_size:
            self.readings_cache.pop(0)

    def get_recent_readings(self):
        """获取缓存的最近读数"""
        return self.readings_cache

# 单例模式,确保全局只有一个传感器读取实例
sensor_reader = SensorReader()

代码解析

  1. Adafruit_DHT.read_retry() 函数封装了DHT11通信的复杂时序和错误重试逻辑,比我们自己写轮询更可靠。
  2. 我们定义了一个 SensorReader 类,它内部维护了一个最多存储24次读数的缓存列表。每次成功读取后,新数据被追加,如果超过24条,最老的数据被移除(先进先出)。
  3. read_sensor 方法在读取失败时会自动重试最多3次,提高了在偶尔信号干扰下的鲁棒性。
  4. 数据中加入了时间戳,这对于绘制历史图表至关重要。

4.2 业务逻辑与API层:Flask应用核心

接下来,创建主应用文件 app.py ,这里将集成Flask服务器、表单处理和数据接口。

# app.py
from flask import Flask, render_template, request, jsonify
from flask_wtf import FlaskForm
from wtforms import BooleanField, SubmitField
from wtforms.validators import DataRequired
from sensor_reader import sensor_reader
import json

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-please-change-this'  # 务必修改!

# 定义Web表单
class WeatherRequestForm(FlaskForm):
    request_temp = BooleanField('温度')
    request_humid = BooleanField('湿度')
    request_graph = BooleanField('历史图表')
    submit = SubmitField('获取数据')

@app.route('/', methods=['GET', 'POST'])
def index():
    form = WeatherRequestForm()
    latest_reading = None
    recent_for_chart = []

    if form.validate_on_submit():
        # 用户提交了表单
        latest_reading = sensor_reader.read_sensor()  # 触发一次新的读取

        # 根据用户选择,准备要传递到模板的数据
        show_temp = form.request_temp.data
        show_humid = form.request_humid.data
        show_graph = form.request_graph.data

        if show_graph:
            recent_for_chart = sensor_reader.get_recent_readings()

        # 渲染模板,并传递用户需要的选项和对应的数据
        return render_template('display.html',
                               form=form,
                               show_temp=show_temp,
                               show_humid=show_humid,
                               show_graph=show_graph,
                               reading=latest_reading,
                               chart_data=recent_for_chart)

    # GET请求或表单未验证,只显示空表单
    return render_template('index.html', form=form)

# 新增:纯数据API接口,供其他程序调用(如手机App)
@app.route('/api/current', methods=['GET'])
def api_current():
    reading = sensor_reader.read_sensor()
    if reading:
        return jsonify(reading), 200
    else:
        return jsonify({'error': 'Sensor read failed'}), 500

@app.route('/api/history', methods=['GET'])
def api_history():
    return jsonify(sensor_reader.get_recent_readings()), 200

if __name__ == '__main__':
    # 在树莓派上运行时,host='0.0.0.0'允许同一网络内其他设备访问
    app.run(host='0.0.0.0', port=5000, debug=True)

设计思路解析

  1. 表单驱动交互 :我们没有设计复杂的多页面,而是用一个表单( WeatherRequestForm )控制所有交互。用户勾选需要的选项后提交,服务器根据勾选情况采集一次新数据,并只渲染用户关心的部分到结果页面( display.html )。这种设计非常简洁高效。
  2. RESTful API扩展 :除了表单页面,我们还增加了 /api/current /api/history 两个API端点。它们直接返回JSON格式的数据,不包含任何HTML。这使得我们的气象站数据可以被其他应用程序(比如用Python脚本定时记录、或用手机App框架开发的客户端)轻松消费,极大地提升了项目的可扩展性。
  3. 密钥安全 SECRET_KEY 用于保护表单免受跨站请求伪造(CSRF)攻击。 务必在生产环境中将其修改为一个强随机字符串 ,例如通过 os.urandom(24) 生成。

4.3 表现层:HTML模板构建

Flask使用Jinja2模板引擎。我们需要在项目根目录下创建一个 templates 文件夹,并在其中放置HTML文件。

index.html 是首页,仅包含表单:

<!DOCTYPE html>
<html>
<head>
    <title>我的个人气象站</title>
    <style>
        body { font-family: sans-serif; margin: 40px; }
        .form-group { margin-bottom: 15px; }
        label { margin-right: 10px; }
    </style>
</head>
<body>
    <h1>欢迎使用个人气象站</h1>
    <p>请选择您要查看的信息:</p>
    <form method="POST">
        {{ form.hidden_tag() }} <!-- 渲染CSRF令牌 -->
        <div class="form-group">
            {{ form.request_temp.label }} {{ form.request_temp() }}
        </div>
        <div class="form-group">
            {{ form.request_humid.label }} {{ form.request_humid() }}
        </div>
        <div class="form-group">
            {{ form.request_graph.label }} {{ form.request_graph() }}
        </div>
        <div class="form-group">
            {{ form.submit() }}
        </div>
    </form>
</body>
</html>

display.html 是结果显示页,动态渲染内容:

<!DOCTYPE html>
<html>
<head>
    <title>气象数据 - 我的个人气象站</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <!-- 引入Chart.js用于绘图 -->
    <style>
        body { font-family: sans-serif; margin: 40px; }
        .data-box { border: 1px solid #ccc; padding: 20px; margin: 20px 0; border-radius: 5px; }
        h2 { color: #333; }
    </style>
</head>
<body>
    <h1>气象数据</h1>
    <a href="/">返回首页</a>

    {% if show_temp or show_humid %}
    <div class="data-box">
        <h2>实时读数</h2>
        <p><strong>数据更新时间:</strong>{{ reading.timestamp if reading else 'N/A' }}</p>
        {% if show_temp %}
        <p><strong>温度:</strong>{{ reading.temperature if reading else 'N/A' }} °C</p>
        {% endif %}
        {% if show_humid %}
        <p><strong>湿度:</strong>{{ reading.humidity if reading else 'N/A' }} %</p>
        {% endif %}
    </div>
    {% endif %}

    {% if show_graph and chart_data %}
    <div class="data-box">
        <h2>最近24次读数趋势</h2>
        <canvas id="weatherChart" width="800" height="400"></canvas>
        <script>
            const chartData = {{ chart_data | tojson }};
            const labels = chartData.map(d => d.timestamp);
            const tempData = chartData.map(d => d.temperature);
            const humidData = chartData.map(d => d.humidity);

            const ctx = document.getElementById('weatherChart').getContext('2d');
            const weatherChart = new Chart(ctx, {
                type: 'line',
                data: {
                    labels: labels,
                    datasets: [{
                        label: '温度 (°C)',
                        data: tempData,
                        borderColor: 'rgb(255, 99, 132)',
                        tension: 0.1
                    }, {
                        label: '湿度 (%)',
                        data: humidData,
                        borderColor: 'rgb(54, 162, 235)',
                        tension: 0.1
                    }]
                },
                options: {
                    responsive: false,
                    scales: {
                        y: {
                            beginAtZero: false
                        }
                    }
                }
            });
        </script>
    </div>
    {% elif show_graph %}
    <p>暂无足够的历史数据绘制图表。</p>
    {% endif %}

    <hr>
    <h3>再次查询</h3>
    <!-- 复用同一个表单,方便用户继续查询 -->
    <form method="POST">
        {{ form.hidden_tag() }}
        <div>
            {{ form.request_temp.label }} {{ form.request_temp() }}
            {{ form.request_humid.label }} {{ form.request_humid() }}
            {{ form.request_graph.label }} {{ form.request_graph() }}
        </div>
        {{ form.submit() }}
    </form>
</body>
</html>

前端交互亮点

  1. 条件渲染 :使用Jinja2的 {% if %} 语句,只在用户勾选了相应选项时才渲染对应的数据区块(温度、湿度、图表)。这使得页面非常干净。
  2. 动态图表 :我们引入了强大的Chart.js库来绘制折线图。后端将缓存的24次数据以JSON格式传递给模板,前端JavaScript解析这些数据并动态生成交互式图表。这比让后端生成静态图片要灵活和美观得多。
  3. 用户体验 :在结果显示页底部再次放置了表单,用户无需返回首页即可进行下一次查询,操作流程更顺畅。

5. 系统部署、测试与优化

代码编写完成后,我们需要将其部署到树莓派上并确保其稳定、长期运行。

5.1 启动测试与问题排查

首先,在项目目录下激活虚拟环境并运行Flask开发服务器:

cd ~/weather_station
source venv/bin/activate
python app.py

如果一切正常,终端会输出类似 * Running on http://0.0.0.0:5000/ 的信息。

现在,从同一局域网内的另一台设备(如笔记本电脑或手机)的浏览器中,访问 http://<树莓派的IP地址>:5000 。你应该能看到表单页面。尝试勾选不同选项并提交,观察是否能正确显示数据。

常见问题与排查技巧实录

  1. 问题:页面显示“无法从DHT11传感器获取数据”或读数一直为None。

    • 排查思路
      • 检查硬件连接 :这是最常见的问题。务必确认VCC、DATA、GND三根线没有接错或接触不良。可以尝试重新插拔杜邦线。
      • 检查GPIO引脚号 :确认代码中的 DHT_PIN (如GPIO4)与实际物理连接(物理引脚7)对应。树莓派有BCM编号(GPIO编号)和物理引脚编号, RPi.GPIO Adafruit_DHT 库默认使用BCM编号。
      • 电源问题 :确保树莓派电源充足,且传感器由3.3V供电。尝试用万用表测量3.3V引脚和GND之间的电压是否稳定。
      • 软件库问题 :确认 Adafruit_DHT 库已正确安装在当前虚拟环境中。可以尝试在Python交互环境中手动导入并测试: import Adafruit_DHT; Adafruit_DHT.read_retry(Adafruit_DHT.DHT11, 4)
      • 传感器损坏 :DHT11是静电敏感器件,有极小的概率在操作中损坏。有条件的话可以换一个传感器测试。
  2. 问题:能读到数据,但数值明显不准(如温度30°C,实际只有25°C)。

    • ��查思路
      • 传感器位置 :DHT11对热源非常敏感。确保它没有靠近树莓派的CPU(树莓派运行时会有一定发热)、电源模块或其他热源。
      • 通风与遮蔽 :传感器不应被阳光直射,也不应被完全密闭在不透气的容器内。将其放置在能代表你希望监测环境空气的位置。
      • 理解精度 :回忆DHT11的精度指标(±2°C, ±5%RH)。对于室内环境监测,这个精度通常可以接受。如果要求更高,应考虑升级到DHT22或BME280。
  3. 问题:Web页面无法访问(连接被拒绝)。

    • 排查思路
      • 检查Flask是否运行 :确认在树莓派终端上,Flask应用正在运行且没有报错退出。
      • 检查IP地址和端口 :确认你输入的树莓派IP地址正确,并且Flask运行在 0.0.0.0:5000
      • 检查防火墙 :树莓派OS Lite版默认防火墙(UFW)可能是关闭的。如果开启了,需要允许5000端口: sudo ufw allow 5000
      • 检查网络 :确保测试设备和树莓派连接在同一个局域网内。

5.2 生产环境部署:使用Gunicorn与Nginx

Flask自带的开发服务器( app.run )性能较弱且不安全,不适合长期对外服务。我们需要使用专业的WSGI服务器和反向代理。

  1. 安装Gunicorn :在虚拟环境中安装生产级WSGI服务器。 pip install gunicorn

  2. 使用Gunicorn启动应用

    cd ~/weather_station
    source venv/bin/activate
    gunicorn -w 2 -b 0.0.0.0:8000 app:app
    
    • -w 2 :启动2个工作进程,处理并发请求。
    • -b 0.0.0.0:8000 :绑定到所有网络接口的8000端口。
    • app:app :冒号前的 app 是模块名(即我们的 app.py 文件),冒号后的 app 是Flask应用实例的名字。

    现在应用运行在8000端口。访问 http://<树莓派IP>:8000 进行测试。

  3. 配置系统服务(让应用开机自启) : 创建一个系统服务文件,让树莓派在启动时自动运行我们的气象站应用。

    sudo nano /etc/systemd/system/weather-station.service
    

    写入以下内容( 注意修改 User WorkingDirectory ExecStart 的路径为你的实际信息 ):

    [Unit]
    Description=Personal Weather Station Flask App
    After=network.target
    
    [Service]
    User=your_username  # 替换为你的树莓派用户名
    WorkingDirectory=/home/your_username/weather_station
    ExecStart=/home/your_username/weather_station/venv/bin/gunicorn -w 2 -b 0.0.0.0:8000 app:app
    Restart=always
    RestartSec=10
    StandardOutput=syslog
    StandardError=syslog
    SyslogIdentifier=weather-station
    
    [Install]
    WantedBy=multi-user.target
    

    保存退出后,启用并启动服务:

    sudo systemctl daemon-reload
    sudo systemctl enable weather-station.service
    sudo systemctl start weather-station.service
    sudo systemctl status weather-station.service # 检查状态,应为active (running)
    
  4. 配置Nginx作为反向代理(可选但推荐) : 使用Nginx可以将服务映射到标准的80端口(HTTP),并提供更好的静态文件处理和安全性。

    • 安装Nginx: sudo apt install nginx -y
    • 编辑默认站点配置: sudo nano /etc/nginx/sites-available/default
    • server 块内,找到 location / 部分,修改或添加如下配置:
      location / {
          proxy_pass http://127.0.0.1:8000; # 转发给Gunicorn
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
      }
      
    • 测试配置并重启Nginx:
      sudo nginx -t  # 测试配置文件语法
      sudo systemctl restart nginx
      

    现在,你可以直接通过 http://<树莓派IP> 访问你的气象站,无需输入端口号。

5.3 项目优化与扩展思路

一个基础系统搭建完成后,可以考虑以下方向进行优化和扩展,使其更实用、更强大:

  1. 数据持久化 :目前数据仅保存在内存列表中,树莓派重启后历史数据会丢失。可以集成SQLite或轻量级数据库TinyDB,将每次读数连同时间戳存入数据库,实现长期存储和更复杂的数据查询。
  2. 定时自动采集 :当前是“请求-响应”模式,数据只在用户访问时更新。可以编写一个后台定时任务(例如使用 cron schedule 库),每隔一定时间(如每5分钟)自动读取一次传感器数据并存储,无论用户是否访问。
  3. 多传感器集成 :树莓派的GPIO和I2C/SPI接口可以连接更多传感器。例如,添加一个BMP280气压传感器来获取气压和海拔数据;添加一个光敏电阻来监测光照强度;甚至连接一个雨滴传感器或风速计。
  4. 数据可视化增强 :使用更强大的前端图表库(如ECharts),展示更丰富的图表类型,如24小时温度变化曲线、温湿度关联散点图等。
  5. 报警功能 :在代码中添加逻辑,当温度或湿度超过设定的阈值时,通过发送电子邮件(使用 smtplib )、推送通知到手机(使用Pushover、Bark等服务)或点亮一个LED灯的方式进行报警。
  6. 远程访问(进阶) :通过内网穿透工具(如frp、ngrok)或配置路由器端口转发(需公网IP),可以在家庭网络之外访问你的气象站。 请注意,此举会暴露你的树莓派服务到公网,务必设置强密码、考虑使用HTTPS,并做好安全加固。
  7. 电源优化 :如果部署在远离电源插座的地方,可以考虑使用大容量充电宝供电,或者为树莓派配置一个安全的关机/唤醒电路,以节省电量。

通过以上步骤,你不仅完成了一个可工作的物联网气象监测系统,更掌握了从硬件连接到软件部署、从原型开发到生产环境配置的完整技能链。这个项目是一个绝佳的起点,你可以根据自己的兴趣和需求,不断为其添加新的功能和模块,真正打造一个完全个性化、功能强大的环境监测中心。

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐