Raspberry Pi的一个流行应用是构建Web服务器。为此,我们可以使用不同的技术,如Python、Node.JS甚至PHP。由于Raspberry Pi的绝大多数脚本都是用Python编写的,因此使用Python创建REST API接口也是合情合理的。然后,我们可以调用特定的函数,例如控制或读取GPIO。这可以方便地控制LED或其他传感器/模块。其美妙之处在于,我们可以使迄今为止为Raspberry Pi用Python编写的几乎所有代码都可以轻松通过REST API进行调用。
因此,在本教程中,我们将使用FastAPI创建这样一个接口,并探讨如何扩展和保障其安全。
所需硬件部件
原则上,本教程不需要太多的配件。但是,由于我们想测试我们的设置是否有效,我建议使用以下部件:
- Raspberry Pi
- LED灯
- 330Ω电阻
- 面包板
- 雌性-雌性跳线
当然,你可以根据自己的需求进行扩展,并连接传感器(如温度传感器等),我们可以通过API对其进行查询。
什么是REST API?
API(应用程序编程接口)是一种可以通过URL等调用的接口。REST(表述性状态转移)概括了一些原则,描述了接口应该如何表现,例如,GET请求应该是只读的,并且不应该更改服务器上的任何内容。另一方面,POST命令允许创建新实体(例如,书籍的新实例)。你可以在这里了解更多关于实现的信息。
https://en.wikipedia.org/wiki/REST
Raspberry Pi上的设置
在本教程中,Raspberry Pi上的设置非常简单,因为我们只使用一个LED和一个按钮。当然,你的场景可以(应该!)与此不同,因为它只是一个示例,因此也非常简单。
LED通过330Ω串联电阻连接到GPIO 17,按钮连接到3.3V和GPIO 21。
此外,我们在以下内容中使用GPIO的BCM编号,而不是板载编号:
Raspberry Pi GPIO引脚分配
Python REST API 的软件组件
现在,我们将逐步创建API。首先,我们准备所需的工具。之后,我们创建并扩展我们的REST API,以切换或读取GPIO。最后但同样重要的是,我们要保障API的安全,以免任何人都可以访问它。
顺便说一下:你也可以在Github上找到我们将逐步讲解的整个代码。
https://github.com/tutRPi/raspberry-pi-fastapi-gpio-rest-api
安装库
在开始之前,我们需要Python3和一些库,我们通过包安装器pip加载它们。
sudo apt-get install python3 python3-pip
之后,我们可以安装所需的Python库:
pip3 install fastapi uvicorn[standard] rpi.gpio
可以在各自的文档页面(fastapi、uvicorn、rpi.gpio)上找到更多信息。
https://sourceforge.net/p/raspberry-gpio-python/wiki/Examples/
入门:首先通过API读取状态
让我们开始第一次测试。为此,我们创建一个简单的Python脚本。
sudo nano main.py
脚本内容如下:
from fastapi import FastAPI
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
app = FastAPI()
@app.get("/read/{gpio}")
def read_root(gpio: int):
GPIO.setup(gpio, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
return {"gpio": gpio, "on": GPIO.input(gpio)}
使用CTRL+O保存文件,并使用CTRL+X关闭nano编辑器。之后,我们就可以启动程序了。
uvicorn main:app --reload
现在,你可以在Raspberry Pi的浏览器中打开以下URL:
如果按钮连接到不同的引脚,你可以更改GPIO编号。如果你没有按下按钮,结果将如下所示:
{"gpio":17,"on":false}
这是我们在此端点下定义的响应。如果你按下按钮并再次调用URL,结果将发生变化。仅用几行代码,我们就编写了第一个REST端点。但现在我们想对其进行扩展。
扩展我们的Python API——设置GPIO状态
纯读取有点无聊,所以我们当然还想控制和设置GPIO。因此,我们创建了另一个端点(这次是PATCH,因为我们要更改内容):
from fastapi import FastAPI
from pydantic import BaseModel
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
app = FastAPI()
class SetGPIO(BaseModel):
on: bool
@app.get("/read/{gpio}")
def read_root(gpio: int):
GPIO.setup(gpio, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
return {"gpio": gpio, "on": GPIO.input(gpio)}
@app.patch("/set/{gpio}")
def read_item(gpio: int, value: SetGPIO):
if value.on:
GPIO.setup(gpio, GPIO.OUT, initial=GPIO.HIGH)
else:
GPIO.setup(gpio, GPIO.OUT, initial=GPIO.LOW)
return {"gpio": gpio, "on": value.on}
如您所见,第一个参数再次是GPIO编号,
你可以使用Postman、浏览器扩展或cURL(sudo apt-get install curl)。我使用了后者:
curl -X PATCH http://127.0.0.1:8000/set/21 -H "Content-Type: application/json" -d '{"on": true}'
这样,LED就亮了!
顺便说一下,你可以在http://127.0.0.1:8000/docs找到API文档,这是使用Swagger/OpenAPI自动生成的。为了使响应更具可读性,我们定义了模型(response_model)。这只是一个具有属性的类,在端点的定义中,我们声明将返回此模型。
from fastapi import FastAPI
from pydantic import BaseModel
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
app = FastAPI()
class GpioStatusResponse(BaseModel):
gpio: int
on: bool
class SetGPIO(BaseModel):
on: bool
@app.get("/read/{gpio}", response_model=GpioStatusResponse)
def read_root(gpio: int):
GPIO.setup(gpio, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
return GpioStatusResponse(gpio=gpio, on=GPIO.input(gpio))
@app.patch("/set/{gpio}", response_model=GpioStatusResponse)
def read_item(gpio: int, value: SetGPIO):
if value.on:
GPIO.setup(gpio, GPIO.OUT, initial=GPIO.HIGH)
else:
GPIO.setup(gpio, GPIO.OUT, initial=GPIO.LOW)
return GpioStatusResponse(gpio=gpio, on=value.on)
最后一项练习:构建一个端点(POST),用于在GPIO上激活PWM,以便我们能够调节LED的亮度(点击此处访问PWM文档)。
https://sourceforge.net/p/raspberry-gpio-python/wiki/PWM/
此外,您现在拥有足够的资源来构建一个与用户界面交互的GPIO,例如:
https://github.com/tutRPi/Raspberry-Pi-Simple-Web-GPIO-GUI
安全性——基本认证和其他方法
如果我们开放Raspberry Pi的8000端口,任何人都可以访问该API。我们要防止这种情况发生,并加入认证机制。为此,我们有几种选择:
1.每次调用API时,都会在请求头中发送用户名和密码。为此,我们使用基本认证,并在调用时验证数据的正确性。
2.另外,我们还可以使用带有JWT(JSON Web Tokens)的oauth2,它们也是通过请求头发送的。为了简化起见,本教程中不会进行此操作。如果您仍然想实现它(作为练习),可以在此处了解更多相关信息。
https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/
现在,我们再次打开脚本并相应地进行调整:from fastapi.security import HTTPBasic, HTTPBasicCredentials
from pydantic import BaseModel
from fastapi import Depends, FastAPI, HTTPException, status
import RPi.GPIO as GPIO
import secrets
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
app = FastAPI()
security = HTTPBasic()
class GpioStatusResponse(BaseModel):
gpio: int
on: bool
class SetGPIO(BaseModel):
on: bool
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
correct_username = secrets.compare_digest(credentials.username, "admin")
correct_password = secrets.compare_digest(credentials.password, "passw0rd")
if not (correct_username and correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get("/read/{gpio}", response_model=GpioStatusResponse)
def read_root(gpio: int, username: str = Depends(get_current_username)):
GPIO.setup(gpio, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
return GpioStatusResponse(gpio=gpio, on=GPIO.input(gpio))
@app.patch("/set/{gpio}", response_model=GpioStatusResponse)
def read_item(gpio: int, value: SetGPIO, username: str = Depends(get_current_username)):
if value.on:
GPIO.setup(gpio, GPIO.OUT, initial=GPIO.HIGH)
else:
GPIO.setup(gpio, GPIO.OUT, initial=GPIO.LOW)
return GpioStatusResponse(gpio=gpio, on=value.on)
为了使查询能够成功进行,我们需要包含用户名和密码(第24/25行)。使用curl或Postman可以很容易地实现这一点:
curl -X PATCH http://127.0.0.1:8000/set/21 -H "Content-Type: application/json" -d '{"on": false}' -u "admin:passw0rd"
最后但同样重要的是:如果您希望通过互联网访问API(即向外部世界开放您的端口),建议使用SSL证书(如Let’s Encrypt),以确保连接加密。您可以在此页面上了解更多相关信息。
https://fastapi.tiangolo.com/deployment/https/
结论
使用FastAPI,我们可以非常轻松、快速地创建一个REST接口,并调用特定于Raspberry Pi的函数。通过它,我们可以控制GPIO、读取传感器数据等等。如果外部系统需要调用Raspberry Pi,这是一个简单且清晰的解决方案。然而,您应该注意适当的身份验证,以确保不是每个人都可以远程控制Raspberry Pi。
作为Python REST API的替代方案,有诸如MQTT之类的解决方案:如果只需要传输/接收数据,并且没有公共API,MQTT是一个不错的选择。如果我们仍然需要API,也可以使用Node.JS来切换GPIO。总的来说,我觉得通过Python的解决方案更加舒适,而且它拥有最多的兼容扩展。
原文地址: https://tutorials-raspberrypi.com/control-all-gpios-with-the-raspberry-pi-rest-api-via-python/