用 HTTP 触发器为 Azure Functions 中的 IoT 继电器添加手动控制:IoT-For-Beginners 云端实战
2026/9/16 14:37:51 网站建设 项目流程

用 HTTP 触发器为 Azure Functions 中的 IoT 继电器添加手动控制:IoT-For-Beginners 云端实战

【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners

本文基于 IoT-For-Beginners 仓库"智能农场"项目的第 5 课作业(assignment.md)展开。该课讲解如何把控制继电器开合的应用逻辑从本地设备迁移到 Azure Functions 无服务器平台,而本作业则要求在此基础上,通过新增两个 HTTP 触发器,实现任何人用浏览器即可手动开启/关闭继电器的手动覆盖(manual override)能力。读完本文,你将掌握 Azure Functions 中 HTTP 触发器的创建、authLevel访问控制配置、本地与云端 URL 规则,以及如何在触发器内复用直接方法(direct method)调用向 IoT 设备下发命令。

作业背景:从云端自动化到手动覆盖

在第 5 课中,你构建了一个由 IoT Hub 事件触发器驱动的 Functions App(iot-hub-trigger),它订阅设备上报的土壤湿度遥测,并在云端自动计算何时开启/关闭水泵继电器。仓库中对应的完整示例代码位于 code/functions,核心逻辑在 iot-hub-trigger/init.py:

def main(event: func.EventHubEvent): body = json.loads(event.get_body().decode('utf-8')) device_id = event.iothub_metadata['connection-device-id'] soil_moisture = body['soil_moisture'] if soil_moisture > 450: direct_method = CloudToDeviceMethod(method_name='relay_on', payload='{}') else: direct_method = CloudToDeviceMethod(method_name='relay_off', payload='{}') registry_manager_connection_string = os.environ['REGISTRY_MANAGER_CONNECTION_STRING'] registry_manager = IoTHubRegistryManager(registry_manager_connection_string) registry_manager.invoke_device_method(device_id, direct_method)

这段代码演示了本课的关键模式:通过 Registry Manager 向具体设备发送relay_on/relay_off直接方法请求。自动化流程虽然方便,但在真实农业场景中,用户往往还需要一个"手动开关":例如自动浇水逻辑暂时关闭、或需要人工强制浇水时。这就是本作业的意义——用 HTTP 触发器把继电器控制暴露为一个 URL。

HTTP 触发器:把函数变成可访问的 URL

无服务器代码(serverless code)可以由多种事件触发,HTTP 请求就是其中最常见的一种。Azure Functions 中,一个 Functions App 可以包含多个触发器(trigger),彼此共享同一套配置(如host.jsonlocal.settings.json)。你现在已经有一个eventHubTrigger,本作业要再添加两个HttpTrigger

  • relay_on:向设备发送relay_on直接方法请求,打开继电器;
  • relay_off:向设备发送relay_off直接方法请求,关闭继电器。

与事件触发器"被动接收消息"不同,HTTP 触发器由调用方主动发起。默认情况下它同时支持GETPOST请求,而浏览器发起的就是GET请求,因此直接粘贴 URL 到浏览器地址栏即可触发函数——这是本作业实现"任何人通过网页请求控制继电器"的原理基础。

前置条件

开始前,请确认你已经完成第 5 课的全部步骤,即:

  1. 创建并本地运行过soil-moisture-triggerFunctions App(func init --worker-runtime python),包含host.jsonlocal.settings.jsonrequirements.txt三个基础文件;
  2. 创建了iot-hub-trigger事件触发器并成功处理遥测消息;
  3. 在 local.settings.json 中配置了IOT_HUB_CONNECTION_STRINGREGISTRY_MANAGER_CONNECTION_STRING两个连接字符串(后者用于调用直接方法,是 HTTP 触发器控制继电器的关键依赖);
  4. 设备端已实现relay_on/relay_off直接方法的响应处理。

步骤一:创建两个 HTTP 触发器

在 Functions App 项目根目录(含iot-hub-trigger子目录的那个文件夹)下,使用 Azure Functions Core Tools 的func new命令添加新函数:

func new --name <trigger name> --template "HTTP trigger"

<trigger name>替换为你的 HTTP 触发器名称。作业建议使用relay_onrelay_off,与直接方法名保持一致,便于后续代码维护。分别执行两次:

func new --name relay_on --template "HTTP trigger" func new --name relay_off --template "HTTP trigger"

每条命令都会在项目内新建一个同名文件夹,内含两个文件:

  • __init__.py:Python 函数体,即触发时执行的代码;
  • function.json:触发器绑定(binding)配置,指定了"type": "httpTrigger"、路由、authLevel等。

提示:HTTP 触发器与事件触发器在function.json上的差异是"绑定"层面的——事件触发器用eventHubTrigger作为输入绑定消费遥测,而 HTTP 触发器用httpTrigger作为入站绑定暴露端点。同一 Functions App 中可以并存多种触发器。

步骤二:移除访问限制(authLevel)

HTTP 触发器自带访问控制。默认情况下(authLevel: function),调用者必须随 URL 附带该函数专属的 API key 才能运行,这保证了云端函数不被匿名滥用。但对于本作业的演示目的,你可以在function.json中把authLevel改为anonymous,让任何人都能通过 URL 触发:

"authLevel": "anonymous"

修改后relay_onfunction.json大致如下:

{ "scriptFile": "__init__.py", "bindings": [ { "authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "methods": ["get", "post"] }, { "type": "http", "direction": "out", "name": "$return" } ] }

两点说明:

  • authLevel的可选值还包括function(默认,需函数级 API key)与admin(需主密钥),anonymous表示完全开放。生产环境务必保留鉴权,本作业仅为学习用途才放开限制;
  • 默认methods同时包含getpost,无需修改即可用浏览器触发。

步骤三:编写触发器内的继电器控制逻辑

两个 HTTP 触发器函数的__init__.py主体逻辑一致,都是"连接 Registry Manager → 下发直接方法请求",与事件触发器中复用同一套代码模式。可参考init.py 中的调用方式实现。核心代码如下:

import os import azure.functions as func from azure.iot.hub import IoTHubRegistryManager from azure.iot.hub.models import CloudToDeviceMethod def main(req: func.HttpRequest) -> func.HttpResponse: device_id = "<your-device-id>" direct_method = CloudToDeviceMethod(method_name='relay_on', payload='{}') registry_manager_connection_string = os.environ['REGISTRY_MANAGER_CONNECTION_STRING'] registry_manager = IoTHubRegistryManager(registry_manager_connection_string) registry_manager.invoke_device_method(device_id, direct_method) return func.HttpResponse("OK")

要点:

  • os.environ['REGISTRY_MANAGER_CONNECTION_STRING']从环境变量读取连接字符串;本地运行时,这些值来自 local.settings.json 的Values节(该文件不应提交到版本控制);
  • CloudToDeviceMethod(method_name='relay_on', payload='{}')构造直接方法请求,这里不需要负载,所以发送空 JSON;
  • invoke_device_method(device_id, direct_method)把命令只发给指定设备——这与早期基于 MQTT 的广播式控制不同,多设备场景下也能准确命中目标;
  • 别忘了在 requirements.txt 中保留azure-iot-hub依赖,并执行pip install -r requirements.txt

设备端(本仓库对应的 Wio Terminal / Raspberry Pi 示例)需已实现relay_on/relay_off直接方法处理器,才能响应这些云端命令。

步骤四:本地运行并验证

在项目目录启动 Functions App:

func start

运行成功后,终端会列出所有函数及其端点。作业给出了预期输出示例:

Functions: relay_off: [GET,POST] http://localhost:7071/api/relay_off relay_on: [GET,POST] http://localhost:7071/api/relay_on iot-hub-trigger: eventHubTrigger

请留意两个细节:

  • 事件触发器iot-hub-trigger的类型标注为eventHubTrigger,没有 HTTP 端点;只有新增的两个函数具备[GET,POST]URL;
  • 端点路径中包含/api前缀——HTTP 触发器默认位于api子路径下,这是 Azure Functions 的固定约定。

http://localhost:7071/api/relay_on粘贴到浏览器地址栏并按回车,或在 VS Code 终端中Ctrl+点击(macOS 为Cmd+点击)该链接,即可在默认浏览器中打开并触发函数,继电器应随之开启;再用relay_off验证关闭。若设备端已运行,可以实际观察继电器的通断变化。

步骤五:部署后的访问 URL

把 Functions App 部署到 Azure 云端后,HTTP 触发器的地址变为:

https://<functions app name>.azurewebsites.net/api/<trigger name>

其中<functions app name>是你的 Functions App 名称(部署命令为func azure functionapp publish <functions_app_name>),<trigger name>为触发器名。例如名称为soil-moisture-sensor时,两个端点分别为:

  • https://soil-moisture-sensor.azurewebsites.net/api/relay_on
  • https://soil-moisture-sensor.azurewebsites.net/api/relay_off

部署时还需注意:local.settings.json中的本地配置不会随代码部署,必须通过az functionapp config appsettings setREGISTRY_MANAGER_CONNECTION_STRING等值写入云端 Application Settings,代码中的os.environ才能在云端读到。部署成功后,在func azure functionapp publish的输出中应能看到新函数的Syncing triggers...列表。

结合仓库源码理解底层实现

本作业的"复用本课所学发送命令"部分,直接依赖仓库中已落地的直接方法调用链,可逐层拆解:

  1. 连接字符串策略:事件触发器消费遥测使用IOT_HUB_CONNECTION_STRING(来自 IoT Hub 的 Event Hub 兼容端点),而发送命令走REGISTRY_MANAGER_CONNECTION_STRING(来自--policy-name service的 ServiceConnect 策略)。两者职责分离,对应 local.settings.json 中两个独立的Values项;
  2. Registry Manager 角色IoTHubRegistryManager封装了与 IoT Hub 设备注册表的通信能力——查询设备、下发云到设备消息、更新设备孪生、以及调用直接方法;
  3. 绑定配置:事件触发器在 function.json 中通过"connection": "IOT_HUB_CONNECTION_STRING"引用设置项(而非直接存放连接字符串明文,避免泄露密钥);HTTP 触发器同样遵循"配置引用设置、敏感值放环境变量"的原则。

从代码结构看,relay_on/relay_off两个 HTTP 触发器与iot-hub-trigger共享同一套连接字符串配置,这种"一个 App 多触发器、共享配置"的设计正是 Functions App 的组织方式。

作业评分标准

完成本作业后,可对照官方 Rubric 自查(assignment.md):

标准优秀合格待改进
创建 HTTP 触发器创建了 2 个用于开启/关闭继电器的触发器,命名得当创建了 1 个触发器,命名得当未能创建任何触发器
从 HTTP 触发器控制继电器两个触发器都成功连接 IoT Hub 并正确控制继电器一个触发器成功连接 IoT Hub 并正确控制继电器触发器未能连接 IoT Hub

延伸思考:手动控制的工程化考量

  • 鉴权anonymous仅适用于演示。若要在公网长期开放手动控制端点,应改回function级 API key,或在前端套一层身份认证;密钥通过function.json之外的 Application Settings 管理。
  • 设备离线invoke_device_method在设备离线时可能失败或超时,建议在 HTTP 响应中捕获异常并返回明确的错误信息。
  • 幂等性:重复点击relay_on应是无害的,直接方法实现需保证设备状态处理的幂等。
  • 多设备:示例中device_id可硬编码为单个设备;若需控制多台设备,可把设备 ID 作为查询参数传入,例如/api/relay_on?deviceId=xxx

至此,你已拥有一个"自动化 + 手动覆盖"双通道的云端继电器控制系统:IoT Hub 事件触发器负责按土壤湿度自动决策,HTTP 触发器负责按人类指令手动干预,两者共享同一条直接方法调用链路,构成了完整、可扩展的农业 IoT 云端控制方案。

【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询