ZigBee 3.0教程-步骤6:物理接口-Light

所以现在是时候为我们的灯和开关制作物理接口了。虽然能够通过网络发送和查看消息很有趣,因为我们正在制作灯和开关,我们应该期望它们像灯一样打开和关闭光源和开关,从按下按钮或类似信号。

首先让我们把注意力转向灯光,因为灯光的开启和关闭是一个更容易观察的目标。正如我们在最后一步中注意到的那样,我们一直在切换我们的一个集群属性,特别是一个开关属性。我们想要的是根据这个属性的值来改变我们的设备。我们可以用来处理这个问题的一种机制是通过回调。

回调是 EmberZNet 处理整个堆栈代码和应用程序层事件的一种方式。如果单击回调选项卡,您将看到许多可在整个代码中使用的调用。这些通常分为应用程序回调、插件回调、堆栈回调、API 回调和集群回调。深入了解回调超出了本课的范围。

对于我们的轻量级代码,我们将需要处理两个事件。首先,在启动时我们需要启用我们的灯并配置它们。完成后,我们需要根据 On/Off 属性处理灯光的切换。

要设置我们的灯光,我们需要查看名为“非集群相关”的部分。所有这些回调都是有序的。当您查看列表时,您会遇到两个回调 Main Init 和 Main Start,这两个回调都在我们的 main() 函数的开头运行。Main Init 在 main 执行之后,但在网络设置之前运行,而 Main Start 在我们的主循环执行之前,在 HAL 完成硬件配置之后运行。在大多数情况下,我们在哪里设置 LED 接口并不重要,因此我们将使用 Main Start,选中它的框。

接下来,我们需要弄清楚如何打开和关闭我们的灯。因为这与 on/off 集群相关联,所以您希望转到 General 下的 On/Off 集群。查看列表,我们可以找到 Server Attribute Changed 回调。由于我们正在对属性值设置进行操作,这非常适合我们的需求,也请选中它的框。

在我们离开此页面之前,请确保在您的回调页面底部您已选中“生成特定于项目的回调文件”框。这将确保为您的应用程序生成新的回调文件。

在我们点击生成之前,我们还需要配置我们所有的 GPIO 以作为 LED 工作。Thunderboard 有 1 个绿色 LED、1 个红色 LED 和 4 个全彩 LED,对于这个项目,我们将使用全彩 LED。如果您阅读UG309第 3.4.8 节,您将看到所有引脚定义,以及 LED 设置说明。

接下来,我们需要启动 Hardware Configurator(简称 HWConf)。如果您查看您的灯光项目,您应该会看到一个名为 brd4166a_efr32mg12p332f1024gl125.hwconf 的文件,这是您的项目的头文件。双击它,它应该在 DefaultMode Port I/O 中的 HWConf 中打开。如果不是该格式,请单击 DefaultMode Peripherals 窗口底部的该选项卡。该视图为我们提供了引脚的图形表示,我们将在其中设置 GPIO,以便在我们的软件中对我们可用。

在 HWConf 中设置 GPIO 非常简单。首先,您需要转到 Studio 右上角的 Outline 视图并打开 Port I/O 树。然后单击您要设置的端口 (AK),这将使这些 GPIO 在图形引脚网格上变为活动状态。在网格上找到您要设置的引脚(小心,引脚字母很容易与它们的网格标识符混淆)并单击它们。然后,您将在 Studio 的中间右侧看到此图钉的属性。如果您单击自定义引脚名称中的值区域,您将能够为引脚命名,这将是您在程序中识别引脚的方式。

例如:PJ14 为 RGB_LED_ENABLE(注意这是默认配置的)

大纲视图:点击 PortJ

DefaultMode 端口 I/O:单击 PJ14(位于 B 行第 6 列)

属性视图:将其命名为 RGB_LED_ENABLE

对以下引脚执行相同操作 - 提供便于定位的位置:

PD11 (M10) – LEDS_RED

PD12 (N11) – LEDS_GREEN

PD13 (M11) – LEDS_BLUE

PI0 (G13) – LED0

PI1 (G12) – LED1

PI2 (G11) – LED2

PI3 (F13) – LED3

记住你给这些Pin 脚起的所有名字。

完成后,保存您的 HWConf 文件,这可能需要一些时间,因为它会生成一些新的硬件配置。然后返回 Simplicity IDE 视图并单击您的 MyLight.isc 文件。单击生成按钮。一段时间后,您将出现一个验证窗口。确保选中此窗口中的所有文件,Studio 会多次不选中 *_callbacks.c 文件以不覆盖此文件。选中所有框后,单击“确定”继续。

最后,是时候输入一些代码来打开和关闭我们的灯了。首先,在您的项目列表中找到回调文件。它将被称为 PROJECTNAME_callbacks.c,在我们的例子中,这将是 MyLight_callbacks.c,双击它会出现这个文件中的代码。您会注意到项目中应该有 2 个附加函数:emberAfMainStartCallback 和 emberAfOnOffClusterServerAttributeChangedCallback。

首先让我们启用我们的灯。由于我们需要在代码启动时启用 LED,因此我们将注意力集中在emberAfMainStartCallback()函数上。如前所述,此函数在我们的程序在运行时启动它的循环之前被调用。

现在我们需要一个函数来初始化我们的 GPIO,将它们配置为输出引脚。您可以浏览docs.silabs.com并找到EMLIB 部分以发现运行此设置的函数。为了节省您一些时间,您正在寻找的功能是:

void GPIO_PinModeSet (GPIO_Port_TypeDef port, unsigned int pin, GPIO_Mode_TypeDef mode, unsigned int out)
对此的函数调用在此处完全定义。但我们将引导您完成函数调用以便于参考。

前两个参数是我们要配置的端口和引脚。因为我们已经在 HWConf 中定义了引脚,所以您可以使用您在 GUI 中为这些引脚分配的名称。它们将类似于 PIN_NAME_PORT 和 PIN_NAME_PIN,分别对应端口字母和端口号。如果您在项目树中查看名为 hal-config 的文件夹,在该文件夹中您会找到一个 hal-config.h 文件,该文件包含端口和引脚的名称以便于参考,请找到该部分

// $[Custom pin names]
下一个参数是如何配置引脚。幸运的是,我们所有的 GPIO 都设置为输出,我们的 GPIO 模式将是gpioModePushPull。可以在此处找到完整的引脚选项列表。

最后,因为我们将这些用作输出引脚,所以最后一个参数是所需的引脚初始状态,0:关闭或 1:打开。

因此,要设置 RGB_LED_ENABLE,请使用以下命令:

GPIO_PinModeSet(RGB_LED_ENABLE_PORT, RGB_LED_ENABLE_PIN, gpioModePushPull, 1);
对所有端口和引脚执行相同操作。您当然应该确保 RGB_LED_ENABLE 在启动时处于打开状态。如果您想要特定颜色,则应根据您想要输出的颜色(红色、绿色或蓝色,或它们的某种组合)启用彩色 LED。LED 0-3 可以禁用或启用,我们将使用它们来打开 LED。

最后一个警告 LED 非常亮,特别是如果您将所有四个都打开为白色,因此在查看它们时要小心。这样做之后,您可能会决定要从它们开始。

此时您可以重新编译代码并测试以查看您的灯是否亮起。一旦您确认您的 LED 工作正常,您就可以实现开/关功能。

所以现在我们将下移到函数 emberAfOnOffClusterServerAttributeChangedCallback。每当灯光上的服务器端属性发生变化时,都会调用此函数。它的定义中有两个变量,一个是被更改操作的端点,另一个是被更改的属性本身。在我们的例子中,我们希望在任何时候看到对 on/off 属性上定义的端点的调用并更改为该属性中设置的状态。尽管我们的项目中只有一个端点,但您应该包装此调用以确保您在特定端点上调用它,因为您可以扩展它以用于其他端点。由于我们的轻量级功能是我们的主要端点,您应该将传递给此函数的端点与此进行比较。

if (endpoint == emberAfPrimaryEndpoint())
完成此操作后,我们需要确保我们正在对 on/off 属性进行操作。首先,您应该查看文件属性-id.h。该文件列出了您项目中的所有属性并为这些属性设置了许多定义,使用这些您的代码将更具可读性。如果您转到具有开/关集群的部分,您将看到与该属性 ID 匹配的 ZCL_ON_OFF_ATTRIBUTE_ID。然后,您可以进行一些比较或 switch 语句并将其用作函数的入口。

if(attributeId == ZCL_ON_OFF_ATTRIBUTE_ID)
现在,我们需要一种读取属性并根据该属性打开或关闭灯的方法。因为这个回调是在属性改变之后进行的,所以我们可以读取这个属性来获取它的状态。为此,我们需要查看 EmberZNet 的文档。如果你去Docs.silabs.com,你可以找到Ember 应用程序框架 API

在左侧,如果您单击 Ember 应用程序框架 API 参考,它将显示顶级 API 的部分。此时我们只需要通用应用程序框架接口。当您获得此页面时,您会发现最顶部列出的属性存储部分。我们正在寻找一些函数来读取属性,稍作搜索就会找到函数emberAfReadServerAttribute,该函数正在执行我们想要的操作,读取服务器属性,因此复制整个定义并将其粘贴到您的代码中。

EmberAfStatus emberAfReadServerAttribute (uint8_t endpoint, EmberAfClusterId cluster, EmberAfAttributeId attributeID, uint8_t *dataPtr, uint8_t readLength)
因为该函数返回一个 EmberAfStatus,所以创建一个来捕获返回值。如果读取成功,我们应该只更改我们的 LED。您可以返回 API 指南并查找EmberAfStatus,您会发现这是 EMBER_ZCL_STATUS_SUCCESS。

if(readStatus == EMBER_ZCL_STATUS_SUCCESS)
最后,我们需要确保正确读取属性并更改 LED,因此返回 emberAfReadServerAttribute 函数调用并正确设置它。我们知道我们的端点是传递给我们回调的端点,所以请确保它匹配。集群 ID 是开/关集群的 ID。如果您查看 cluster-id.h,您会看到它被枚举为 ZCL_ON_OFF_CLUSTER_ID,因此将其作为调用中的第二个变量。属性 ID 又是 ZCL_ON_OFF_ATTRIBUTE_ID,所以使用它。最后,我们需要一些东西来捕捉数据的价值。我创建了一个名为 onOff 的布尔类型来捕获它,签名的最后两个变量是指向该变量的指针和该变量的大小。最后,我们的完整调用看起来像这样:

boolean onOff;
EmberAfStatus readStatus;
readStatus = emberAfReadServerAttribute(endpoint,
ZCL_ON_OFF_CLUSTER_ID,
ZCL_ON_OFF_ATTRIBUTE_ID,
&onOff,
sizeof(onOff));
现在我们可以使用一个针对 onOff 调用的 if-else 来打开或关闭我们的 LED。再次检查 EMLIB,我们看到对此的函数调用是:

GPIO_PinOutSet(port, pin);
GPIO_PinOutClear(port, pin);
在代码块中对您想要打开和关闭的 LED 进行适当的调用,保存并重新编译您的代码。然后,您可以将其重新刷新到您的“Light”Thunderboard 并使用开关从 CLI 切换它。如果您能够打开和关闭灯,是时候转到我们的开关,让它从按钮切换。