Raspberry PI传感器数据上云:Python IoT集成

Raspberry PI传感器数据上云:Python IoT集成

  • [Raspberry PI传感器数据上云:Python IoT集成](#Raspberry PI传感器数据上云:Python IoT集成)
    • [第一章 引言:物联网时代的边缘计算与云端融合](#第一章 引言:物联网时代的边缘计算与云端融合)
      • [1.1 项目背景与意义](#1.1 项目背景与意义)
      • [1.2 ThingSpeak平台简介](#1.2 ThingSpeak平台简介)
      • [1.3 项目整体架构设计](#1.3 项目整体架构设计)
    • [第二章 硬件基础:构建传感器网络](#第二章 硬件基础:构建传感器网络)
      • [2.1 核心控制器:Raspberry Pi 4B详解](#2.1 核心控制器:Raspberry Pi 4B详解)
      • [2.2 环境感知层:DHT22温湿度传感器原理](#2.2 环境感知层:DHT22温湿度传感器原理)
      • [2.3 光照感知层:光敏电阻与模数转换](#2.3 光照感知层:光敏电阻与模数转换)
      • [2.4 电路连接指南与GPIO引脚映射](#2.4 电路连接指南与GPIO引脚映射)
    • [第三章 软件环境搭建:从操作系统到Python库](#第三章 软件环境搭建:从操作系统到Python库)
      • [3.1 Raspberry Pi OS的安装与配置](#3.1 Raspberry Pi OS的安装与配置)
      • [3.2 Python虚拟环境的最佳实践](#3.2 Python虚拟环境的最佳实践)
      • [3.3 核心依赖库的安装与测试](#3.3 核心依赖库的安装与测试)
    • [第四章 Python实战:传感器数据驱动开发](#第四章 Python实战:传感器数据驱动开发)
      • [4.1 GPIO编程基础:RPi.GPIO库深度解析](#4.1 GPIO编程基础:RPi.GPIO库深度解析)
      • [4.2 驱动DHT22:时序信号的捕获与解码](#4.2 驱动DHT22:时序信号的捕获与解码)
      • [4.3 模拟信号数字化:MCP3008与SPI通信](#4.3 模拟信号数字化:MCP3008与SPI通信)
      • [4.4 代码重构:构建可复用的传感器类](#4.4 代码重构:构建可复用的传感器类)
    • [第五章 云端对接:ThingSpeak API集成](#第五章 云端对接:ThingSpeak API集成)
      • [5.1 ThingSpeak账户注册与频道配置](#5.1 ThingSpeak账户注册与频道配置)
      • [5.2 REST API协议详解:HTTP GET与POST](#5.2 REST API协议详解:HTTP GET与POST)
      • [5.3 Python `requests`库实现数据上传](#5.3 Python requests库实现数据上传)
      • [5.4 异常处理与网络重连机制设计](#5.4 异常处理与网络重连机制设计)
    • [第六章 数据可视化:构建在线仪表盘](#第六章 数据可视化:构建在线仪表盘)
      • [6.1 ThingSpeak内置图表插件应用](#6.1 ThingSpeak内置图表插件应用)
      • [6.2 私有与公开视图的配置策略](#6.2 私有与公开视图的配置策略)
      • [6.3 MATLAB分析与可视化小部件](#6.3 MATLAB分析与可视化小部件)
    • [第七章 系统进阶:自动化、多线程与低功耗优化](#第七章 系统进阶:自动化、多线程与低功耗优化)
      • [7.1 Linux Daemon进程与systemd服务配置](#7.1 Linux Daemon进程与systemd服务配置)
      • [7.2 多线程并发采集与上传架构](#7.2 多线程并发采集与上传架构)
      • [7.3 数据本地缓存与断点续传机制](#7.3 数据本地缓存与断点续传机制)
    • [第八章 总结与展望](#第八章 总结与展望)

Raspberry PI传感器数据上云:Python IoT集成

第一章 引言:物联网时代的边缘计算与云端融合

1.1 项目背景与意义

在当今数字化转型的浪潮中,物联网技术已成为连接物理世界与数字世界的桥梁。从智能家居的温控系统到工业产线的设备监控,数据的实时采集、传输与分析构成了智能决策的基础。然而,对于许多开发者和爱好者而言,如何构建一个稳定、高效且可视化的物联网系统,仍然是一个充满挑战的课题。

本项目"Raspberry Pi传感器数据上云"旨在通过软硬件结合的方式,打造一个完整的IoT闭环系统。我们不仅仅满足于点亮一个LED灯或是在终端打印出一行温度读数,我们的目标是让数据"流动"起来------从树莓派的GPIO引脚出发,穿越本地网络,跨越互联网协议,最终在云端汇聚成可视化的图表。这一过程涵盖了嵌入式开发、网络编程、API交互以及数据可视化等核心技能,是Python在工程应用领域的典型实践。

1.2 ThingSpeak平台简介

在众多IoT云平台中,ThingSpeak 以其专注于数据采集与分析的特性脱颖而出。作为MathWorks旗下的产品,它不仅提供了免费的REST API接口,允许设备轻松上传JSON格式的数据,还内置了强大的MATLAB分析引擎。ThingSpeak的核心优势在于:

  1. 开箱即用:无需复杂的后端开发,即可创建数据频道。
  2. 实时可视化:支持自定义图表、仪表盘,数据更新即时可见。
  3. 开放协议:支持HTTP、MQTT等多种协议,兼容性极强。

1.3 项目整体架构设计

本系统的架构遵循典型的"端-管-云"模型:

  • 感知层(端):由DHT22温湿度传感器和光敏电阻组成,负责采集环境数据。
  • 传输层(管):树莓派作为网关,通过Wi-Fi或有线网络连接路由器,利用HTTP协议将数据打包发送。
  • 应用层(云):ThingSpeak服务器接收数据,存储至时序数据库,并通过Web Dashboard展示给用户。

第二章 硬件基础:构建传感器网络

在编写代码之前,我们需要搭建一个可靠的硬件电路。硬件的稳定性直接决定了数据采集的准确性。

2.1 核心控制器:Raspberry Pi 4B详解

本项目采用Raspberry Pi 4 Model B作为核心控制器。它搭载了博通BCM2711四核Cortex-A72处理器,性能强劲。对于IoT项目而言,最重要的是其40针的GPIO(General Purpose Input/Output)扩展接口。

我们需要重点关注以下几类引脚:

  • 电源引脚:3.3V (Pin 1, 17) 和 5V (Pin 2, 4)。大多数传感器工作在3.3V或5V逻辑电平,树莓派的GPIO逻辑电平为3.3V,切勿直接输入5V电压,否则会烧毁芯片。
  • 接地引脚:GND (Pin 6, 9, 14, 20, 25, 30, 34, 39)。电路回路的公共端。
  • 通用IO引脚:可编程的数字引脚,如GPIO4 (Pin 7)等。

2.2 环境感知层:DHT22温湿度传感器原理

DHT22(也称为AM2302)是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有较高的可靠性与卓越的长期稳定性。

技术参数:

  • 湿度测量范围:0-100% RH
  • 温度测量范围:-40~80℃
  • 分辨率:温度0.1℃,湿度0.1% RH

通信协议:

DHT22使用的是单总线通信协议。这种协议对时序要求极其严格。传感器在通电后,需要等待1秒以越过不稳定状态。主机(树莓派)发送一个至少500微秒的低电平信号作为起始信号,然后释放总线等待DHT响应。DHT接收到信号后会回复80微秒低电平,再发送80微秒高电平,随后开始传输40位数据(湿度高位、湿度低位、温度高位、温度低位、校验和)。

2.3 光照感知层:光敏电阻与模数转换

光敏电阻是基于半导体光电导效应的元件,其阻值随光照强度的增加而减小。然而,树莓派的GPIO引脚只能读取数字信号(高或低),无法直接测量电阻的变化。因此,我们需要引入模数转换器(ADC)

本项目选用MCP3008芯片。它是一款10位精度的8通道ADC,采用SPI接口通信。

  • 分辨率 :10位,即可以将电压划分为 2 10 = 1024 2^{10}=1024 210=1024个等级。对于3.3V参考电压,精度约为3.3mV。
  • 电路设计:我们将光敏电阻与一个固定电阻组成分压电路。光照增强时,光敏电阻阻值降低,分压点电压升高。MCP3008读取该电压值并转换为数字量传输给树莓派。

2.4 电路连接指南与GPIO引脚映射

为了确保项目可复现,以下是详细的接线表:

DHT22接线:

  • VCC -> 3.3V (Pin 1)
  • Data -> GPIO4 (Pin 7) (注意:Data引脚与VCC之间通常需要接一个4.7K-10K的上拉电阻,部分DHT22模块已自带)
  • GND -> GND (Pin 6)

MCP3008接线(SPI接口):

  • MCP3008 Pin 16 (VDD) -> 3.3V
  • MCP3008 Pin 15 (VREF) -> 3.3V
  • MCP3008 Pin 14 (AGND) -> GND
  • MCP3008 Pin 13 (CLK) -> GPIO11 (SCLK) (Pin 23)
  • MCP3008 Pin 12 (DOUT) -> GPIO9 (MISO) (Pin 21)
  • MCP3008 Pin 11 (DIN) -> GPIO10 (MOSI) (Pin 19)
  • MCP3008 Pin 10 (CS/SHDN) -> GPIO8 (CE0) (Pin 24)
  • MCP3008 Pin 9 (DGND) -> GND
  • 光敏电阻分压输出 -> MCP3008 Channel 0 (Pin 1)

在连接电路时,请务必断电操作,检查线路无误后再上电,避免短路。


第三章 软件环境搭建:从操作系统到Python库

硬件搭建完毕后,我们需要为树莓派注入灵魂------配置软件环境。

3.1 Raspberry Pi OS的安装与配置

推荐使用Raspberry Pi Imager工具将最新的Raspberry Pi OS(64位版本)烧录至SD卡。在烧录前,可以通过设置界面开启SSH服务并配置Wi-Fi用户名密码,这样首次启动即可远程连接。

启动后,执行系统更新:

bash 复制代码
sudo apt-get update
sudo apt-get upgrade -y

这一步至关重要,它能修复内核漏洞并更新驱动程序。

3.2 Python虚拟环境的最佳实践

虽然树莓派自带Python,但直接在系统全局环境中安装大量第三方库可能会导致依赖冲突。作为专业的开发者,我们应使用venv模块创建隔离的虚拟环境。

bash 复制代码
# 创建项目目录
mkdir ~/iot_project
cd ~/iot_project

# 创建虚拟环境
python3 -m venv venv

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

激活后,终端提示符前会出现(venv)字样,此时所有的pip install操作都将在该隔离环境中进行。

3.3 核心依赖库的安装与测试

本项目需要以下核心库:

  1. Adafruit_CircuitPython_DHT:Adafruit提供的DHT传感器驱动库,封装了复杂的时序逻辑,支持Python纯实现,无需编译C扩展,兼容性好。
  2. Adafruit_MCP3008:用于驱动MCP3008 ADC芯片的库。
  3. Requests:Python最著名的HTTP库,用于调用ThingSpeak API。

安装命令:

bash 复制代码
pip install adafruit-circuitpython-dht
pip install adafruit-circuitpython-mcp3008
pip install requests

此外,为了使用SPI接口(MCP3008需要),需要在树莓派配置中开启SPI:

bash 复制代码
sudo raspi-config
# 导航至 Interface Options -> SPI -> Yes
# 重启生效
sudo reboot

第四章 Python实战:传感器数据驱动开发

环境配置完毕,现在我们开始编写核心代码。我们将采用面向对象编程(OOP)的思想,提高代码的可维护性。

4.1 GPIO编程基础:RPi.GPIO库深度解析

虽然Adafruit库内部已经处理了GPIO操作,但理解底层原理依然重要。RPi.GPIO库提供了两种引脚编号模式:BOARD(物理引脚编号)和BCM(Broadcom芯片引脚编号)。我们通常使用BCM模式,因为它更符合硬件定义。

python 复制代码
import RPi.GPIO as GPIO
import time

# 设置引脚编号模式
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False) # 关闭警告

# 示例:控制LED闪烁
LED_PIN = 18
GPIO.setup(LED_PIN, GPIO.OUT)

try:
    while True:
        GPIO.output(LED_PIN, GPIO.HIGH)
        time.sleep(1)
        GPIO.output(LED_PIN, GPIO.LOW)
        time.sleep(1)
except KeyboardInterrupt:
    GPIO.cleanup() # 清理资源

4.2 驱动DHT22:时序信号的捕获与解码

直接操作GPIO读取DHT22极其困难,因为Linux并非实时操作系统,进程调度可能导致微秒级的时序延迟。因此,我们使用Adafruit库来简化这一过程。

python 复制代码
import adafruit_dht
import board
import time

# 初始化DHT22传感器,连接到GPIO4
# board.D4 对应 BCM GPIO4
dht_device = adafruit_dht.DHT22(board.D4)

def read_dht_data():
    """
    读取温湿度数据
    返回: (温度, 湿度) 元组,失败返回
    """
    try:
        temperature = dht_device.temperature
        humidity = dht_device.humidity
        return temperature, humidity
    except RuntimeError as error:
        # DHT传感器经常读取失败,需要重试机制
        print(f"读取DHT传感器失败: {error.args[0]}")
        return None, None
    except Exception as error:
        dht_device.exit()
        raise error

注意:DHT22的采样周期约为2秒,过于频繁的读取会导致传感器发热,数据不准。

4.3 模拟信号数字化:MCP3008与SPI通信

MCP3008通过SPI协议传输数据。SPI是一种全双工同步串行接口,包含四根线:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。

python 复制代码
import busio
import digitalio
import board
import adafruit_mcp3008.mcp3008 as MCP
from adafruit_mcp3008.analog_in import AnalogIn

# 创建SPI总线接口
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)

# 创建片选信号
cs = digitalio.DigitalInOut(board.D8) # CE0 对应 GPIO8

# 初始化MCP3008
mcp = MCP.MCP3008(spi, cs)

# 创建模拟输入通道0
chan0 = AnalogIn(mcp, MCP.P0)

def read_light_intensity():
    """
    读取光照强度
    返回: 电压值 (0-3.3V) 和 原始值 (0-1023)
    """
    voltage = chan0.voltage
    raw_value = chan0.value
    return voltage, raw_value

4.4 代码重构:构建可复用的传感器类

为了主程序整洁,我们将上述功能封装成一个类。

python 复制代码
import time
import board
import adafruit_dht
import busio
import digitalio
import adafruit_mcp3008.mcp3008 as MCP
from adafruit_mcp3008.analog_in import AnalogIn

class EnvironmentSensor:
    def __init__(self, dht_pin=board.D4, mcp_cs_pin=board.D8):
        # 初始化DHT
        self.dht_device = adafruit_dht.DHT22(dht_pin)

        # 初始化MCP3008
        spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
        cs = digitalio.DigitalInOut(mcp_cs_pin)
        self.mcp = MCP.MCP3008(spi, cs)
        self.light_channel = AnalogIn(self.mcp, MCP.P0)

    def get_readings(self):
        data = {
            "temperature": None,
            "humidity": None,
            "light_raw": None,
            "light_voltage": None
        }

        # 读取温湿度
        try:
            data["temperature"] = self.dht_device.temperature
            data["humidity"] = self.dht_device.humidity
        except RuntimeError:
            print("DHT读取错误,将在下次循环重试")

        # 读取光照
        data["light_voltage"] = self.light_channel.voltage
        data["light_raw"] = self.light_channel.value

        return data

    def cleanup(self):
        self.dht_device.exit()

第五章 云端对接:ThingSpeak API集成

数据已在手中,现在我们要让它"飞"上云端。

5.1 ThingSpeak账户注册与频道配置

  1. 访问 ThingSpeak官网 并使用MathWorks账户登录。
  2. 点击 "New Channel" 创建新频道。
  3. 配置频道字段:
    • Name: Home_Environment_Monitor
    • Field 1: Temperature (C)
    • Field 2: Humidity (%)
    • Field 3: Light Voltage (V)
  4. 保存频道后,记录下两个关键ID:
    • Channel ID:频道的唯一标识。
    • Write API Key:写入数据的密钥(非常重要,不可泄露)。

5.2 REST API协议详解:HTTP GET与POST

ThingSpeak主要通过REST API进行交互。最简单的方式是使用HTTP GET请求更新数据。URL格式如下:

https://api.thingspeak.com/update?api_key=YOUR_WRITE_API_KEY&field1=0

虽然GET请求简单,但为了安全性和规范性,推荐使用HTTP POST方法,将数据放在请求体中发送。请求体格式通常为JSON:

json 复制代码
{
    "api_key": "YOUR_WRITE_API_KEY",
    "field1": "25.5",
    "field2": "60.2"
}

5.3 Python requests库实现数据上传

我们编写一个专门处理云端通信的类。

python 复制代码
import requests
import json
import time

class ThingSpeakUploader:
    def __init__(self, write_api_key, channel_id):
        self.base_url = "https://api.thingspeak.com/update.json"
        self.write_api_key = write_api_key
        self.channel_id = channel_id

    def upload_data(self, temp, hum, light_v):
        """
        上传数据到ThingSpeak
        """
        payload = {
            "api_key": self.write_api_key,
            "field1": temp,
            "field2": hum,
            "field3": light_v
        }

        try:
            response = requests.post(self.base_url, json=payload, timeout=10)

            if response.status_code == 200:
                # ThingSpeak返回条目ID
                print(f"数据上传成功! Entry ID: {response.text}")
                return True
            else:
                print(f"上传失败,状态码: {response.status_code}")
                return False

        except requests.exceptions.RequestException as e:
            print(f"网络错误: {e}")
            return False

重要限制:ThingSpeak免费版限制更新频率为每15秒一次。如果发送过快,服务器会拒绝请求。因此,我们的主循环必须包含延时控制。

5.4 异常处理与网络重连机制设计

在物联网应用中,网络波动是常态。我们不能因为一次上传失败就让程序崩溃。我们需要引入重试机制。

python 复制代码
def robust_upload(uploader, temp, hum, light_v, max_retries=3):
    for attempt in range(max_retries):
        if uploader.upload_data(temp, hum, light_v):
            return True
        print(f"第 {attempt + 1} 次重试...")
        time.sleep(5) # 等待5秒后重试
    print("达到最大重试次数,放弃本次上传。")
    return False

第六章 数据可视化:构建在线仪表盘

数据上传成功后,我们登录ThingSpeak网站查看结果。

6.1 ThingSpeak内置图表插件应用

ThingSpeak为每个字段自动生成了折线图。

  1. 进入频道详情页,点击 "Private View" 或 "Public View"。
  2. 你会看到三个图表窗口,分别对应Field 1, 2, 3。
  3. 点击图表上的铅笔图标,可以自定义图表样式:
    • Timescale:选择显示最近10分钟、1小时或1天的数据。
    • Type:折线图、柱状图、Spline等。
    • Axis:设置Y轴范围(例如温度范围0-40度)。

6.2 私有与公开视图的配置策略

  • Private View:只有登录的用户能看到,包含所有API Key信息,适合调试和管理。
  • Public View:对外展示的界面。你可以在 "Sharing" 选项卡中设置频道为公开,并生成嵌入代码(iframe),将仪表盘嵌入到个人博客或其他网页中。

6.3 MATLAB分析与可视化小部件

ThingSpeak的杀手锏是MATLAB Analysis插件。我们可以编写MATLAB代码对数据进行高级处理。

例如,计算温度的平均值并绘制图表:

matlab 复制代码
% 读取数据
readChannelID = YOUR_CHANNEL_ID;
readAPIKey = 'YOUR_READ_API_KEY';
data = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'NumPoints', 100);

% 计算均值
avgTemp = mean(data.Temperature);

% 绘图
thingSpeakPlot(data.Timestamps, data.Temperature, 'Marker', 'o');
title('Temperature Trend');
xlabel('Time');
ylabel('Temp (C)');

通过 "Apps" -> "TimeControl",我们可以定时运行这段MATLAB代码,实现数据的自动化分析。


第七章 系统进阶:自动化、多线程与低功耗优化

一个成熟的IoT系统应当是无人值守、自动运行的。

7.1 Linux Daemon进程与systemd服务配置

直接在SSH终端运行Python脚本,一旦断开SSH连接,进程就会被杀死。我们需要将脚本注册为系统服务。

  1. 创建服务文件:

    bash 复制代码
    sudo nano /etc/systemd/system/iot_monitor.service
  2. 写入以下内容:

    ini 复制代码
    [Unit]
    Description=Raspberry Pi IoT Monitor Service
    After=network.target
    
    [Service]
    Type=simple
    User=pi
    WorkingDirectory=/home/pi/iot_project
    ExecStart=/home/pi/iot_project/venv/bin/python main.py
    Restart=on-failure
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
  3. 启动服务:

    bash 复制代码
    sudo systemctl daemon-reload
    sudo systemctl enable iot_monitor.service
    sudo systemctl start iot_monitor.service
  4. 查看状态:

    bash 复制代码
    sudo systemctl status iot_monitor.service

    现在,你的程序会在开机自启动,崩溃后自动重启,实现了真正的无人值守。

7.2 多线程并发采集与上传架构

目前的程序是单线程阻塞的:读取传感器 -> 延时 -> 上传 -> 延时。如果网络上传耗时较长,会阻塞传感器读取。我们可以使用Python的threading模块优化。

  • 线程A(采集线程):每隔2秒读取一次传感器数据,存入全局缓冲区。
  • 线程B(上传线程):每隔15秒读取缓冲区最新数据,上传至云端。
python 复制代码
import threading
import time

# 全局数据缓冲
data_buffer = {
    "temp": 0,
    "hum": 0,
    "light": 0
}

def sensor_thread_func(sensor):
    while True:
        readings = sensor.get_readings()
        if readings["temperature"] is not None:
            data_buffer["temp"] = readings["temperature"]
            data_buffer["hum"] = readings["humidity"]
        data_buffer["light"] = readings["light_voltage"]
        time.sleep(2) # DHT22采样间隔

def upload_thread_func(uploader):
    while True:
        robust_upload(uploader, data_buffer["temp"], data_buffer["hum"], data_buffer["light"])
        time.sleep(15) # ThingSpeak限制间隔

# 主程序中启动线程
t1 = threading.Thread(target=sensor_thread_func, args=(sensor,))
t2 = threading.Thread(target=upload_thread_func, args=(uploader,))
t1.start()
t2.start()

7.3 数据本地缓存与断点续传机制

如果家中断网了怎么办?数据会丢失。我们可以引入SQLite数据库进行本地缓存。

  1. 建表

    sql 复制代码
    CREATE TABLE sensor_log (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
        temperature REAL,
        humidity REAL,
        light REAL,
        uploaded INTEGER DEFAULT 0
    );
  2. 逻辑修改

    • 上传线程优先尝试上传数据库中uploaded=0的最早一条记录。
    • 如果上传成功,标记uploaded=1
    • 如果网络断开,新采集的数据直接写入数据库,不进行上传尝试。
    • 网络恢复后,积压的数据会自动补发。

这种"先存后发"的机制极大地提高了系统的鲁棒性,确保数据零丢失。


第八章 总结与展望

经过八个章节的详细阐述,我们完成了一个从硬件底层到云端应用的全栈IoT项目。我们利用Raspberry Pi强大的计算能力和Python丰富的生态,解决了传感器驱动、网络通信、API集成以及系统运维等一系列工程问题。

相关推荐
橙子199110161 小时前
Java/Kotlin 与并发
java·python·kotlin
etcix1 小时前
go cli translator that use bing api and youdao api
开发语言·elasticsearch·golang
亚历山大海1 小时前
AiPPT接口文件PHP版本全,智能生成PPT文件并下载
开发语言·ai·php
ノBye~1 小时前
Spring的IOC详解
java·开发语言
willhuo1 小时前
全能文档解析服务doc-mcp-server
python·ai
147API1 小时前
Claude 模型选型:Opus/Sonnet/Haiku + 成本/限速预算(Kotlin)
android·开发语言·kotlin·147api
mr_LuoWei20092 小时前
自定义的中文脚本解释器来实现对excel自动化处理(二)
运维·python·自动化
MoSTChillax2 小时前
新手 3 个文件跑通前端 + Flask + MySQL(最小可行 CRUD)
数据库·python·mysql·flask