从零开始·树莓派智慧灌溉系统——小白也能做(附完整代码+教程)
本文就来手把手教大家搭建一个 自动化智能灌溉系统。土干了,水泵自动开启;水够湿润,自动关停。而且,还带Web管理后台——手机打开浏览器就能看土壤湿度、光照状态、水泵运行日志。
完整代码及配套网页均已开源,文末会贴出所有代码,可直接复制使用。
一、项目预览
先来看一下最终效果(手机、电脑都能打开(统一局域网下)):
-
🌿 土壤湿度卡片:显示“湿润”或“干燥”,附带对应的表情符号
-
☀️ 光照状态卡片:显示“明亮”或“较暗”
-
💧 水泵状态卡片:显示“运行中”或“已关闭”
-
一个开关:可在 自动模式与 手动模式之间自由切换
-
两个手动按钮:手动开启/关闭水泵
-
系统日志区:自动记录每一次操作(开/关水泵、切换模式、储水报警等)
二、准备工作:硬件清单
| 硬件 | 型号/规格 | 参考价格 |
|---|---|---|
| 树莓派 | 3B / 4B 均可 | ¥300~600 |
| 土壤湿度传感器 | YL-69 或 FC-28 | ¥15~25 |
| 光敏传感器模块 | 数字型 DO 输出 | ¥8~15 |
| 继电器模块 | 一路 5V | ¥10~20 |
| 微型水泵 | 5V/12V直流 | ¥25~40 |
| 杜邦线 | 母对母 ×若干 | ¥5~10 |
| 面包板(可选) | 便于接线 | ¥8~15 |

还需要一个这个(里面可以放3个1.5V的干电池)
三、环境搭建:库的安装
3.1 安装 Python 库
首先是 RPi.GPIO,这个库是树莓派 GPIO 控制的核心模块。Raspberry Pi OS 通常已预装 RPi.GPIO。如未安装,可以使用以下命令:
bash
sudo apt updatesudo apt install python3-rpi.gpio


或使用 pip3:(这个第一个方法不行的的时候你们自己试一下,一般情况下都能下载好)
bash
sudo pip3 install RPi.GPIO
验证是否安装成功:
bash
python3 -c "import RPi.GPIO as GPIO; print(GPIO.VERSION)"
如果输出类似 0.7.1 的版本号,说明安装成功。
然后是 Flask(Python Web 框架):
bash
sudo apt install python3-flask

⚠️ 注意:RPi.GPIO 只能在树莓派上运行,在普通电脑(Windows/Mac)上无法安装。如果你在电脑上调试代码,需要先把所有 GPIO 相关的部分注释掉,只在树莓派上真正运行。
(运行代码必须在树莓派上运行)
四、硬件接线
4.1 传感器引脚说明
土壤湿度传感器(YL-69 或 FC-28)有 4 个引脚:
| 引脚 | 含义 |
|---|---|
| AO | 模拟信号输出(本项目未使用) |
| DO | 数字信号输出(本项目使用) |
| GND | 电源负极 |
| VCC | 电源正极 |
🌱 什么是 DO 引脚? DO 是数字量输出。传感器模块内部有一个比较器,它会将测量的湿度值与一个可调的“阈值”进行比较——湿度高于阈值输出低电平(表示湿润),低于阈值输出高电平(表示干燥)。传感器上的那个蓝色十字旋钮,就是用来调节这个阈值(灵敏度)的。
光敏传感器引脚类似。光照充足时输出低电平,光照不足(变暗)时输出高电平。
💡 传感器原理小科普:电阻式土壤湿度传感器通过两个金属探针检测土壤导电能力。土壤越湿→导电性越好→探针间电阻越小→输出电平发生变化。
继电器模块引脚一般标注为:VCC、GND、IN(信号输入)。
4.2 接线表
本项目中使用的 GPIO 引脚(BCM 编号方式):
| 硬件 | 硬件上的引脚 | 连接到树莓派的 BCM 编号 | 说明 |
|---|---|---|---|
| 继电器模块 | VCC | 5V(物理 2 号) | 给继电器内部电路供电 |
| GND | GND(任选) | 共地 | |
| IN | GPIO27 | 控制信号(树莓派发指令) | |
| 独立电源(给水泵供电) | 正极(+) | → 继电器的 COM 端子 | 不直接接树莓派 |
| 负极(-) | → 水泵的黑线(负极) | 独立回路,不与树莓派共地 | |
| 水泵 | 红线(正极) | → 继电器的 NO 端子 | 继电器吸合时水泵得电 |
| 黑线(负极) | → 独立电源的负极(-) | 形成回路 | |
| 土壤湿度传感器 | VCC | 3.3V(物理 1 号) | 给传感器供电 |
| GND | GND(任选,与继电器不同孔亦可) | 共地 | |
| DO | GPIO22 | 检测湿度(干燥=高电平) | |
| AO | 不接 | 本项目中不用 | |
| 光敏传感器 | VCC | 3.3V(物理 17 号) | 给传感器供电 |
| GND | GND(任选) | 共地 | |
| DO | GPIO26 | 检测亮度(暗=高电平) |
📋 分步详细说明(附文字图示)
1️⃣ 继电器 + 独立电源 + 水泵
text
独立电源(例如12V适配器): (+) 正极 ──────→ 继电器 COM 端子 (-) 负极 ──────→ 水泵 黑线 继电器: NO 端子 ──────→ 水泵 红线 VCC ──────→ 树莓派 物理2号(5V) GND ──────→ 树莓派 物理6号(GND) IN ──────→ 树莓派 物理13号(GPIO27) 水泵: 红线 → 继电器 NO 黑线 → 独立电源 负极
⚠️ 重要:水泵和树莓派之间没有直接的电连接,树莓派只控制继电器。水泵的电源来自独立电源,不是树莓派的5V。
2️⃣ 土壤湿度传感器
text
土壤传感器模块(通常有4个引脚:VCC、GND、DO、AO): VCC → 树莓派 物理1号(3.3V) GND → 树莓派 物理9号(GND) DO → 树莓派 物理15号(GPIO22) AO → 不接
💡 传感器上的蓝色电位器可以调节灵敏度:逆时针转到底(最灵敏),然后慢慢顺时针调,直到插入湿土时DO指示灯灭、插入干土时指示灯亮。
3️⃣ 光敏传感器
text
光敏传感器模块(通常3个引脚:VCC、GND、DO): VCC → 树莓派 物理17号(3.3V) GND → 树莓派 物理14号(GND) DO → 树莓派 物理37号(GPIO26)
💡 光敏模块也有蓝色电位器,调节光线阈值:用手遮住时DO指示灯亮(表示较暗),对着光时指示灯灭(表示明亮)。
五、代码详解(完整版)
5.1 主程序 app.py
python
"""
树莓派智慧灌溉系统 - Smart Irrigation System
Flask Web + RPi.GPIO 水泵控制 + 数字传感器
"""
import time
import smtplib
import threading
import logging
from datetime import datetime
from email.mime.text import MIMEText
from flask import Flask, render_template, jsonify, request
import RPi.GPIO as GPIO
# --------------- GPIO 初始化 ---------------
PUMP_PIN = 27 # 水泵控制引脚
MOISTURE_DO_PIN = 22 # 土壤湿度传感器 DO 引脚
LIGHT_DO_PIN = 26 # 光敏传感器 DO 引脚
GPIO.setmode(GPIO.BCM) # 使用 BCM 引脚编号
GPIO.setwarnings(False)
GPIO.setup(PUMP_PIN, GPIO.OUT)
GPIO.output(PUMP_PIN, GPIO.HIGH) # 默认关闭(HIGH=关)
GPIO.setup(MOISTURE_DO_PIN, GPIO.IN)
GPIO.setup(LIGHT_DO_PIN, GPIO.IN)
# --------------- Flask 应用 ---------------
app = Flask(__name__)
logging.basicConfig(level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
# =============== 全局状态 ===============
pump_status = False # 水泵当前状态
auto_mode = True # 自动/手动模式
soil_dry = True # 土壤是否干燥(True=干燥)
is_dark = False # 是否天黑(True=暗)
logs: list[str] = [] # 系统日志
MAX_LOGS = 20
# 储水报警相关
pump_on_time: float | None = None
soil_dry_at_pump_on: bool = True
last_alarm_email_time: float = 0
ALARM_EMAIL_COOLDOWN = 300 # 5分钟
def add_log(msg: str):
ts = datetime.now().strftime("%H:%M")
entry = f"[{ts}] {msg}"
logs.insert(0, entry)
if len(logs) > MAX_LOGS:
logs.pop()
logger.info(entry)
# =============== 水泵控制 ===============
PUMP_ON = GPIO.LOW
PUMP_OFF = GPIO.HIGH
def turn_on_pump():
global pump_status, pump_on_time, soil_dry_at_pump_on
if not pump_status:
GPIO.output(PUMP_PIN, PUMP_ON)
pump_status = True
pump_on_time = time.time()
soil_dry_at_pump_on = soil_dry
add_log("水泵开启")
def turn_off_pump():
global pump_status, pump_on_time
if pump_status:
GPIO.output(PUMP_PIN, PUMP_OFF)
pump_status = False
pump_on_time = None
add_log("水泵关闭")
# =============== 邮件报警 ===============
def send_alarm_email(reason: str):
smtp_user = "your_email@qq.com" # ⚠️ 改成你的QQ邮箱
smtp_pass = "your_authorization_code" # ⚠️ 改成授权码
receiver = "your_email@qq.com"
subject = "智慧灌溉系统报警"
body = f"报警时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n" \
f"报警原因:{reason}\n\n" \
f"系统已自动关闭水泵,请检查储水杯。"
msg = MIMEText(body, "plain", "utf-8")
msg["Subject"] = subject
msg["From"] = smtp_user
msg["To"] = receiver
try:
server = smtplib.SMTP_SSL("smtp.qq.com", 465)
server.login(smtp_user, smtp_pass)
server.sendmail(smtp_user, [receiver], msg.as_string())
server.quit()
add_log(f"报警邮件已发送: {reason}")
except Exception as e:
add_log(f"邮件发送失败: {e}")
python
# =============== 传感器读取 ===============
def read_sensors():
"""读取传感器数字引脚"""
global soil_dry, is_dark
while True:
soil_raw = GPIO.input(MOISTURE_DO_PIN)
light_raw = GPIO.input(LIGHT_DO_PIN)
# DO=HIGH 表示干燥,DO=LOW 表示湿润
soil_dry = (soil_raw == GPIO.HIGH)
# DO=HIGH 表示暗,DO=LOW 表示亮
is_dark = (light_raw == GPIO.HIGH)
if int(time.time()) % 10 == 0:
logger.info(f"传感器: 土壤DO={soil_raw}, 光照DO={light_raw}, soil_dry={soil_dry}, is_dark={is_dark}")
time.sleep(1)
# =============== 储水报警检测 ===============
def check_water_alarm():
global last_alarm_email_time, pump_on_time
if not pump_status or pump_on_time is None:
return
elapsed = time.time() - pump_on_time
if elapsed >= 30 and soil_dry and soil_dry_at_pump_on:
reason = f"水泵运行{elapsed:.0f}秒,土壤仍干燥,可能储水杯没水"
add_log(f"储水报警: {reason}")
if time.time() - last_alarm_email_time >= ALARM_EMAIL_COOLDOWN:
last_alarm_email_time = time.time()
threading.Thread(target=send_alarm_email,
args=(reason,), daemon=True).start()
else:
add_log("距上次报警不足5分钟,跳过邮件发送")
pump_on_time = time.time() # 重置计时
# =============== 自动灌溉主循环 ===============
def auto_irrigation_loop():
while True:
check_water_alarm()
if auto_mode:
now = datetime.now()
in_noon = 10 <= now.hour < 15
bright_in_noon = in_noon and not is_dark
if soil_dry and not bright_in_noon and not pump_status:
turn_on_pump()
add_log("自动开启水泵:土壤干燥")
elif not soil_dry and pump_status:
turn_off_pump()
add_log("土壤湿润,自动关闭水泵")
time.sleep(1)
# =============== Flask 路由 ===============
@app.route("/")
def index():
return render_template("index.html")
@app.route("/api/status")
def api_status():
return jsonify({
"soil_dry": soil_dry,
"is_dark": is_dark,
"pump_status": pump_status,
"auto_mode": auto_mode,
"logs": logs[:5],
})
@app.route("/api/pump", methods=["POST"])
def api_pump():
global auto_mode
action = request.json.get("action")
if action == "on":
auto_mode = False
turn_on_pump()
elif action == "off":
auto_mode = False
turn_off_pump()
return jsonify({"ok": True, "pump_status": pump_status, "auto_mode": auto_mode})
@app.route("/api/mode", methods=["POST"])
def api_mode():
global auto_mode
mode = request.json.get("mode")
auto_mode = (mode == "auto")
add_log(f"切换为{'自动' if auto_mode else '手动'}模式")
return jsonify({"ok": True, "auto_mode": auto_mode})
# =============== 启动 ===============
if __name__ == "__main__":
add_log("系统启动")
threading.Thread(target=read_sensors, daemon=True).start()
threading.Thread(target=auto_irrigation_loop, daemon=True).start()
app.run(host="0.0.0.0", port=5000, debug=False, use_reloader=False)
📧 如何获取QQ邮箱授权码:
-
登录 QQ 邮箱 → 设置 → 账户

-
向下滚动找到“POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务”

-
开启“IMAP/SMTP服务”,按提示发送短信验证
-
系统会生成一串授权码(例如
abcdefghijklmnop),保存好,作为代码中smtp_pass使用。
5.2 Web 页面 index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智慧灌溉系统</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
min-height: 100vh; color: #e0e0e0;
}
.container { max-width: 960px; margin: 0 auto; padding: 20px; }
h1 {
text-align: center; font-size: 1.8rem; padding: 18px 0 6px;
color: #4fc3f7; letter-spacing: 2px;
}
.subtitle { text-align: center; color: #90a4ae; font-size: .85rem; margin-bottom: 24px; }
/* 卡片样式 */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; margin-bottom: 20px; }
.card {
background: rgba(255,255,255,.07); border-radius: 14px;
padding: 20px; backdrop-filter: blur(6px);
border: 1px solid rgba(255,255,255,.1);
transition: transform .2s; text-align: center;
}
.card:hover { transform: translateY(-2px); }
.card-title { font-size: .8rem; color: #90a4ae; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 10px; }
.card-value { font-size: 1.6rem; font-weight: 700; }
.card-value.status-ok { color: #4caf50; }
.card-value.status-warn { color: #f44336; }
.card-value.pump-on { color: #4caf50; }
.card-value.pump-off { color: #f44336; }
.card-value.auto { color: #4fc3f7; }
.card-value.manual { color: #ff9800; }
.card-icon { font-size: 2.4rem; margin-bottom: 6px; }
/* 操作区 */
.controls {
background: rgba(255,255,255,.07); border-radius: 14px;
padding: 20px; margin-bottom: 20px;
border: 1px solid rgba(255,255,255,.1);
display: flex; flex-wrap: wrap; gap: 16px; align-items: center; justify-content: center;
}
.controls label { font-size: .85rem; color: #b0bec5; }
.switch { position: relative; display: inline-block; width: 56px; height: 28px; }
.switch input { opacity: 0; width: 0; height: 0; }
.slider {
position: absolute; cursor: pointer; inset: 0;
background: #ff9800; border-radius: 28px; transition: .3s;
}
.slider::before {
content: ""; position: absolute; height: 22px; width: 22px;
left: 3px; bottom: 3px; background: #fff; border-radius: 50%; transition: .3s;
}
.switch input:checked + .slider { background: #4fc3f7; }
.switch input:checked + .slider::before { transform: translateX(28px); }
.btn {
padding: 10px 24px; border: none; border-radius: 8px;
font-size: .9rem; font-weight: 600; cursor: pointer;
transition: .2s; color: #fff;
}
.btn:active { transform: scale(.96); }
.btn-on { background: #4caf50; }
.btn-on:hover { background: #388e3c; }
.btn-off { background: #f44336; }
.btn-off:hover { background: #c62828; }
/* 日志区 */
.log-section {
background: rgba(255,255,255,.07); border-radius: 14px;
padding: 20px; border: 1px solid rgba(255,255,255,.1);
}
.log-section h3 { font-size: .9rem; color: #90a4ae; margin-bottom: 12px; }
.log-list { list-style: none; }
.log-list li {
padding: 8px 12px; margin-bottom: 6px; border-radius: 8px;
background: rgba(255,255,255,.04); font-size: .85rem;
font-family: "Cascadia Code", "Fira Code", monospace;
border-left: 3px solid #4fc3f7;
}
.log-list li:first-child { animation: fadeIn .4s; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.no-log { color: #607d8b; font-size: .85rem; }
@media (max-width: 600px) {
.cards { grid-template-columns: 1fr 1fr; }
.controls { flex-direction: column; }
}
</style>
</head>
<body>
<div class="container">
<h1>智慧灌溉系统</h1>
<p class="subtitle">Raspberry Pi Smart Irrigation</p>
<div class="cards">
<div class="card">
<div class="card-title">土壤湿度</div>
<div class="card-icon" id="soilIcon">--</div>
<div class="card-value" id="soilVal">--</div>
</div>
<div class="card">
<div class="card-title">光照状态</div>
<div class="card-icon" id="lightIcon">--</div>
<div class="card-value" id="lightVal">--</div>
</div>
<div class="card">
<div class="card-title">水泵状态</div>
<div class="card-icon" id="pumpIcon">--</div>
<div class="card-value" id="pumpVal">--</div>
</div>
</div>
<div class="controls">
<label>模式切换</label>
<span id="modeLabel" style="min-width:36px; font-weight:600;">手动</span>
<label class="switch">
<input type="checkbox" id="modeSwitch" checked>
<span class="slider"></span>
</label>
<button class="btn btn-on" id="btnOn">开启水泵</button>
<button class="btn btn-off" id="btnOff">关闭水泵</button>
</div>
<div class="log-section">
<h3>系统日志(最近 5 条)</h3>
<ul class="log-list" id="logList">
<li class="no-log">暂无日志...</li>
</ul>
</div>
</div>
<script>
const $ = id => document.getElementById(id);
async function poll() {
try {
const r = await fetch("/api/status");
const d = await r.json();
// 土壤
if (d.soil_dry) {
$("soilIcon").textContent = "🚨";
$("soilVal").textContent = "干燥";
$("soilVal").className = "card-value status-warn";
} else {
$("soilIcon").textContent = "🌿";
$("soilVal").textContent = "湿润";
$("soilVal").className = "card-value status-ok";
}
// 光照
if (d.is_dark) {
$("lightIcon").textContent = "🌙";
$("lightVal").textContent = "较暗";
$("lightVal").className = "card-value status-warn";
} else {
$("lightIcon").textContent = "☀️";
$("lightVal").textContent = "明亮";
$("lightVal").className = "card-value status-ok";
}
// 水泵
if (d.pump_status) {
$("pumpIcon").textContent = "💧";
$("pumpVal").textContent = "运行中";
$("pumpVal").className = "card-value pump-on";
} else {
$("pumpIcon").textContent = "⛔";
$("pumpVal").textContent = "已关闭";
$("pumpVal").className = "card-value pump-off";
}
$("modeSwitch").checked = d.auto_mode;
updateModeLabel(d.auto_mode);
const logList = $("logList");
if (d.logs.length === 0) {
logList.innerHTML = '<li class="no-log">暂无日志...</li>';
} else {
logList.innerHTML = d.logs.map(l => `<li>${esc(l)}</li>`).join("");
}
} catch (e) {
console.error("poll error", e);
}
}
function esc(s) {
const d = document.createElement("div");
d.textContent = s;
return d.innerHTML;
}
function updateModeLabel(auto) {
$("modeLabel").textContent = auto ? "自动" : "手动";
$("modeLabel").style.color = auto ? "#4fc3f7" : "#ff9800";
}
$("modeSwitch").addEventListener("change", async function () {
const mode = this.checked ? "auto" : "manual";
await fetch("/api/mode", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({mode})
});
poll();
});
$("btnOn").addEventListener("click", async () => {
await fetch("/api/pump", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({action: "on"})
});
poll();
});
$("btnOff").addEventListener("click", async () => {
await fetch("/api/pump", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({action: "off"})
});
poll();
});
setInterval(poll, 1000);
poll();
</script>
</body>
</html>
5.3 代码逻辑解读
核心工作流程:
-
传感器线程(read_sensors) 每隔 1 秒读取土壤湿度和光照状态,更新全局变量
soil_dry和is_dark -
自动灌溉线程(auto_irrigation_loop) 循环判断自动模式下土壤状态和光照条件,决定水泵的开启或关闭
-
储水报警检测(check_water_alarm) 水泵持续运行 30 秒后如果土壤仍然干燥,触发邮件报警
-
Flask Web 服务 提供
/api/status和/api/pump、/api/mode三个 API,前端每秒轮询刷新数据
🧠 BCM 与 BOARD 的区别:BCM 编号对应 Broadcom 芯片的 GPIO 通道号(如 GPIO27),而 BOARD 编号对应物理引脚位置(如物理第 13 脚)。本项目使用 GPIO.BCM 模式。
六、项目文件部署
6.1 创建项目文件夹
bash
mkdir ~/smart_irrigation cd ~/smart_irrigation
6.2 创建文件
bash

nano python # 粘贴上面 Python 代码粘贴到这个里面,按ctrl o保存这个文件以及给这个文件起名,然后ctrl x退出文件 mkdir templates (创建名为templates的文件夹) nano templates/index.html # 粘贴上面 HTML 代码
复制粘贴需要一个工具(把自己电脑上的文件传输到树莓派本机)
这个是网址:https://filezilla-project.org/download.php?type=client

直接下载安装即可

直接打开的它里面的字体是乱码(就是别的格式),所以我们的要选格式(这个是我之前传输的文件,不是本次项目的)

这样这个文件好了

📁 注意:index.html 务必放在
templates文件夹中,因为 Flask 默认在这个位置查找模板文件。
6.3 修改邮箱配置
打开 app.py 找到以下两行:
python
smtp_user = "your_email@qq.com" smtp_pass = "your_authorization_code"
替换成你的 QQ 邮箱和授权码(不是 QQ 密码)。
6.4 运行
bash
python3 app.py
控制台会显示类似 Running on http://0.0.0.0:5000 的信息。
同一局域网下,手机或电脑浏览器打开 http://树莓派IP:5000 即可。
七、开机自启(systemd)[能运行且能看到数据下面不用看,但是不行的话试一下这些]
想要系统一启动就自动运行 Web 程序,通过 systemd 注册为 Linux 服务即可实现。树莓派重启后无需人工干预,灌溉系统自动启动。
7.1 创建工作目录并放置代码
bash
sudo mkdir -p /opt/smart_irrigation sudo cp ~/smart_irrigation/app.py /opt/smart_irrigation/ sudo cp -r ~/smart_irrigation/templates /opt/smart_irrigation/
7.2 创建服务文件
bash
sudo nano /etc/systemd/system/smart_irrigation.service
写入以下内容:
ini
[Unit] Description=Smart Irrigation System After=network.target [Service] Type=simple User=pi WorkingDirectory=/opt/smart_irrigation ExecStart=/usr/bin/python3 /opt/smart_irrigation/app.py Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
7.3 启用服务
bash
sudo systemctl daemon-reload sudo systemctl enable smart_irrigation sudo systemctl start smart_irrigation sudo systemctl status smart_irrigation # 检查状态
状态显示 active (running) 即成功。
查看实时日志:
bash
sudo journalctl -u smart_irrigation -f
重启树莓派验证:sudo reboot。
如果遇到其他问题,欢迎在评论区留言,我看到都会尽力解答!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
sudo apt install python3-rpi.gpio

粘贴到这个里面,按ctrl o保存这个文件以及给这个文件起名,然后ctrl x退出文件
mkdir templates (创建名为templates的文件夹)
nano templates/index.html # 粘贴上面 HTML 代码

所有评论(0)