Z-Wave Plus应用程序的基本功能由设备类型和角色类型定义。为Z-Wave Plus应用程序确定设备类型和角色类型的正确组合是很重要的。
一旦确定了设备和角色类型,就可以开始应用程序开发。
1. 创建应用程序文件夹并设置构建环境
1.1 选择要创建的应用程序角色
选择一个现有的软件样本,并修改它以满足您的需求。主要根据角色类型进行选择:
-
总是工作终端设备: SwitchOnOff, WallController, PowerStrip
-
报告睡眠终端设备: SensorPIR
-
侦听睡眠终端装置: DoorLock
此外,还有一些其他的选择要考虑
-
Endpoint实现由Power Strip支持。
-
关联组在Wall Controller中实现。
1.2 创建一个新的Simplicity Studio项目
按照以下步骤设置应用程序的工作目录
1. 要对构建环境有一个大致的了解,请参阅[3]以获得更多信息。
2. 选择其中一个应用程序并在Simplicity studio中创建一个示例项目。
3. 通过重命名根文件夹,例如<MyApp>,将导入的应用程序重命名为适合应用程序的名称。
4. 导航到<MyApp>/src文件夹,将.c文件重命名为<MyApp>.c。
5. 对于任何其他示例,搜索APP_FREQ并将其替换为您正在使用的频率。要了解更多细节,请参考[3]中的第6章。
6. 构建应用程序以验证更改没有破坏任何内容。也可以将应用程序下载到芯片上,以检查应用程序是否运行无误。
2. 配置config_app.h
2.1 通用类型、特定类型和设备选项
一个Z-Wave Plus设备必须使用以下两个定义指定一个通用设备类和一个特定设备类:
-
GENERIC_TYPE
-
SPECIFIC_TYPE
请参考Z-WaveDevelopment Basics和Z-WavePlus v2 Device Type Specification为您的产品选择适当的通用和特定设备类。标识符值在ZW_classcmd.h中定义。
DEVICE_OPTIONS_MASK是一个位掩码,用于指定一组设备选项。有关详细信息,请参阅ZW_basis_api.h文件。
2.2 角色类型、节点类型、图标类型、用户图标类型(Z-Wave Plus Info CC)
角色类型、节点类型、图标类型和用户图标类型都是Z-Wave Plus命令类使用的值。它们使用以下定义进行设置:
-
APP_ROLE_TYPE
-
APP_NODE_TYPE
-
APP_ICON_TYPE
-
APP_USER_ICON_TYPE
关于在Z-Wave Plus Info CC中使用的图标类型的进一步信息,请参考Z-WavePlus Assigned Icon Types。联系Z-Wave联盟获取新的图标类型。
2.3 Manufacturer Specific CC / 固件更新
Manufacturer specific CC需要定义一些特定于产品的值。有关更多信息,请参阅Z-WaveManagement Command Class Specification中的制造商特定命令类。ZAF使用以下宏来处理这些值 :
-
APP_MANUFACTURER_ID
-
APP_PRODUCT_TYPE_ID
-
APP_PRODUCT_ID
固件更新命令类使用以下定义:
-
APP_FIRMWARE_ID: 固件ID由产品类型ID和产品ID组成。
制造商和产品ID用于从制造商的角度识别Z-Wave产品。其他应用程序也可以使用这些信息在用户界面上显示公司标识、产品类型、用户指南等。该信息通过Manufacturer Specific C交互,当前分配的制造商ID列在ZW_classcmd.h文件中。
2.4 关联组信息(AGI)
在应用程序中,AGI必须在config_app.h中配置。
对于Z-Wave Plus产品,生命线组是强制性的。它可以使用以下声明来定义:
#define AGITABLE_LIFELINE_GROUP \
{<command class X>, <command A>}, \
{<command class Y>, <command B>}, \
{<command class Z>, <command C>}
参考Z-WavePlus v2 Device Type Specification可以了解在生命线组中要使用哪些事件和命令类。命令类和命令的定义可以在ZW_classcmd.h中找到。
如果应用程序没有实现Multi Channel端点,其他组(除了生命线)必须使用以下格式定义:
#define AGITABLE_ROOTDEVICE_GROUPS \
{<profile 1 MSB>, <profile 1 LSB>, {<command class X, command A}, "<group 2 name>"}\
{<profile 2 MSB>, <profile 2 LSB>, {<command class X, command A}, "<group 3 name>"}
如果应用程序实现Multi Channel端点,则必须使用端点组填写AGI定义。多通道端点实现的一个例子是Power Strip应用。
#define AGITABLE_ROOTDEVICE_GROUPS \
AGITABLE_ENDPOINT_1_GROUPS, \
AGITABLE_ENDPOINT_2_GROUPS, \
AGITABLE_ENDPOINT_3_GROUPS, \
AGITABLE_ENDPOINT_4_GROUPS
AGITABLE_ENDPOINT_X_GROUPS表示端点x的非生命线组。每个端点组都被定义为由AGITABLE_ROOTDEVICE_GROUPS定义的根设备组,但有一个匹配的定义名、其他配置文件、命令类等。
#define AGITABLE_ENDPOINT_1_GROUPS \
{<profile X MSB>, <profile X LSB>, {<command class>, <command>}, "group name"}, \
{ASSOCIATION_GROUP_INFO_REPORT_PROFILE_CONTROL,
ASSOCIATION_GROUP_INFO_REPORT_PROFILE_CONTROL_KEY01, {COMMAND_CLASS_SWITCH_MULTILEVEL_V4,
SWITCH_MULTILEVEL_SET_V4}, "Button 1"}
当产品实现Multi Channel端点时,它必须映射到根关联组。根组映射提供向后兼容性,因为不支持Multi Channel的设备必须能够询问根设备支持哪些组。根组映射定义到端点组的根设备映射中的组。
Root Device Group | Endpoint ID | Endpoint Ggroup ID |
Root Device Group 2 | Endpoint 1 | Endpoint 1 Group 2 |
Root Device Group 3 | Endpoint 2 | Endpoint 2 Group 2 |
下面显示了如何在config_app.h中配置根组映射。根组映射设置的详细信息取决于应用程序。
#define ASSOCIATION_ROOT_GROUP_MAPPING_CONFIG \
{<ROOT DEVICE GROUP 2>, <ENDPOINT ID 1>, <ENDPOINT GROUP 2>}, \
{<ROOT DEVICE GROUP 3>, <ENDPOINT ID 2>, <ENDPOINT GROUP 2>}
请参考Wall Controller应用程序中的config_app.h文件获取组映射的示例。SensorPIR还显示了AGI是如何设置的。
组定义用于在应用程序的main源文件中初始化AGI。
2.5 安全
安全级别在config_app.h文件中配置,并定义了REQUESTED_SECURITY_KEYS标志。定义可以在ZW_security_api.h文件中找到。
开发产品时必须指定应用程序安全级别位。安全级别为默认值No-secure。
#define REQUESTED_SECURITY_KEYS \ (SECURITY_KEY_S2_UNAUTHENTICATED_BIT|SECURITY_KEY_S0_BIT)
添加功能:
ApplicationSecureKeysRequested()包含安全密钥标志REQUESTED_SECURITY_KEYS。
2.3 设置config_rf.h
Tx功率应该通过修改文件config_rf.h来设置,以符合当地监管机构的要求。
该文件包含2个定义:
#define APP_MAX_TX_POWER 0
#define APP_MEASURED_0DBM_TX_POWER 33
其中,APP_MEASURED_0DBM_TX_POWER用于在传导环境中将TX功率调谐到0dbm。在旋转过程中,‘APP_MAX_TX_POWER’应该为0 (0dBm)。
APP_MAX_TX_POWER取值范围为-100 ~ +100 deci dBm (-10dBm ~ +10dBm),用于Tx功率调优完成后,应用框架对Tx功率进行调整。
有效范围是:
100 >= (APP_MAX_TX_POWER + APP_MEASURED_0DBM_TX_POWER) >= -100
要设置最大功率,请使用
#define APP_MAX_TX_POWER 100
#define APP_MEASURED_0DBM_TX_POWER 0
设置TX功率的步骤
a) 将APP_MAX_TX_POWER设置为0,将APP_MEASURED_0DBM_TX_POWER设置为0,编译应用程序。
b) 使用产品上的这些设置对Tx功率进行测量。重要的是,这是在成品上完成的。
c) 将APP_MEASURED_0DBM_TX_POWER设置为测量的传导值。
d) 将“APP_MAX_TX_POWER”设置为产品使用区域所需/允许的Tx功率。
e) 编译固件。
f) 按照调节限值(步骤d)进行调节辐射测量,例如50deci dBm (5 dBm)。如果调节测量允许再增加1/2db,则将1/2dbm改为55并重新编译。如果一个方向的辐射太高,例如需要少1/2db,那么将其改为45deci dBm并重新编译。
2.4 设置非易失性存储器
所有应用程序都必须打开一个NVM3[18]文件系统,ZAF使用该文件系统在非易失性内存中存储文件。应用程序还可以使用文件系统来存储应用程序特定的文件。通过调用在ZAF_nvm3_app.c中找到的applicationfilesystemit(..)函数来打开文件系统。
应用文件系统在flash中占用12k字节。为了使文件系统在所有情况下都能正常工作,建议在其中存储的数据不要超过4k字节,包括应用程序特定的文件和ZAF使用的文件。应用程序开发人员不能增加应用程序文件系统的默认大小(NVM3_APP_NVM_SIZE),因为这会使它与用于存储Z-Wave协议文件的区域冲突。
NVM3文件系统使用RAM中的缓存在flash中存储文件的位置。默认情况下,缓存中的最大文件数设置为10。如果ZAF和应用程序使用的文件总数超过10,则必须相应地增加ZAF_nvm3_app.c中的NVM3_APP_CACHE_SIZE,否则文件系统将大大减慢。
必须为NVM3文件系统中的每个文件分配一个单独的20位对象密钥,作为文件标识[18]。整数范围0x51000-0x5FFFF已经被保留用作ZAF的文件标识符,并且不能用于特定于应用程序的文件。特定于应用程序的文件最好使用范围为0x00000-0x0FFFF的标识符。
每个示例应用程序都列出了它在一个名为g_aFileDescriptors[]的数组中使用的文件。该列表包含应用程序中ZAF使用的文件标识符和文件大小。一些示例应用程序在列表中还包含特定于应用程序的文件。在启动时,应用程序检查列表中的所有文件是否都在文件系统中。所有应用程序文件系统都必须有一个文件标识符ZAF_FILE_ID_APP_VERSION,该文件包含应用程序的版本号。如果该文件丢失,则认为该文件系统未写入或损坏,从而重新格式化该文件系统,并将所有文件设置为默认值。
要将特定于应用程序的文件引入到文件系统,只需定义一个尚未使用的新的20位对象键值。一旦使用函数nvm3_writeData()首次写入该文件,该文件将被添加到文件系统中。
#define FILE_ID_MYNEWFILE (0x00112)
typedef struct SMyAppData
{
int32_t status;
uint8_t vector[10];
} SMyAppData;
SMyAppData wrtData;
wrtData.status = -5;
memset(&(wrtData.vector), 0x55, sizeof(wrtData.vector));
//This call will create and write a file containing wrtData in the file system.
nvm3_writeData(pFileSystemApplication, FILE_ID_MYNEWFILE, &wrtData, sizeof(wrtData));
SMyAppData rdData;
//This call will read the content of the file to rdData. Please be warned that it is
//not possible to read part of the file. If sizeof(rdData) < sizeof(wrtData) above,
//the read function will return an error.
nvm3_readData(pFileSystemApplication, FILE_ID_MYNEWFILE, &rdData, sizeof(rdData));
建议将默认内容写入函数SetDefaultConfiguration()中特定于应用程序的文件,该函数将在格式化文件系统后调用。
执行固件更新时,应用程序文件系统的内容保持不变。如果文件标识符保持不变,则仍然可以使用以前固件使用的旧文件。当开发固件的新版本时,需要增加应用程序config_app.h中的APP_VERSION、APP_REVISION或APP_PATCH号,在其中添加新文件或以任何方式更改文件。引导加载程序不接受版本号较低或相同的文件。然后,在新固件的第一次启动时,应用程序将能够通过读取标识符ZAF_FILE_ID_APP_VERSION的文件来识别芯片上的文件系统属于旧版本的固件。
2.5 看门狗使能/禁用
看门狗定时器在应用程序中默认是启用的,如果应用程序任务运行超过1秒而不释放处理器,将重置设备。
为了在开发过程中禁用看门狗,注释掉ApplicationTask函数内部的WDOGn_Enable()函数调用,如下所示:
注意:看门狗应该总是在生产代码中启用,但可以在调试版本中禁用
static void
ApplicationTask(SApplicationHandles* pAppHandles)
{
// Init
DPRINT("Enabling watchdog\n");
WDOGn_Enable(DEFAULT_WDOG, true); <<-- Comment out this line to disable the watchdog
2.6 应用程序源文件
应用程序必须实现一个用于初始化的函数:ApplicationInit(..)。该函数在系统启动时被Z-Wave主函数调用,并获得唤醒原因作为输入参数,请参阅ZW_basis_api.h文件。应用程序特定的硬件初始化,如引脚配置,应该从这个函数完成。配置完成后,函数必须通过调用ZW_UserTask_ApplicationRegisterTask(..)来注册应用程序任务,以便应用程序可以开始运行。
应用程序任务函数ApplicationTask(..)被FreeRTOS注册在准备运行的任务列表中。在软件示例中,ApplicationTask(..)在启动时首先执行特定于应用程序的软件初始化。其中,它使用针对不同类型事件的事件处理程序来配置事件分发功能。随后,它进入无限循环,让事件分发程序处理任何事件。参见9.5节中的描述。FreeRTOS调度器将确保应用程序任务根据其优先级得到处理器时间。
2.6.1 配置命令类列表
应用程序使用3个命令类列表根据包含状态发送节点信息帧(NIF)。这三个列表定义了以下的
NIF:
- Not included
- Non-securely included
- Securely included
以下命令类列表用于节点未被包含或未被安全包含的情况。当包含设备时,Security和Security_2命令类将被ZAF删除。这个命令类列表出现在NIF中:
static code BYTE cmdClassListNonSecureNotIncluded[]
以下命令类列表在安全包含节点时使用。它表示不安全支持的命令类,即使设备已经安全地包含在内。
static code BYTE cmdClassListNonSecureIncludedSecure[]
以下命令类列表在安全包含节点时使用。它只包含安全支持的命令类。该命令类列表通过安全命令类、支持的安全命令报告命令发布。
static BYTE cmdClassListSecure[]
2.6.2 Endpoint配置
如果您的设备必须实现端点,那么必须在config_app.h文件中设置AGI和关联组映射。在AGI中配置每个端点之后(参见2.4节),必须调用AssociationInitEndpointSupport()而不是AssociationInit()来进行关联组映射。
最后,需要通过调用Transport_AddEndpointSupport()函数为端点初始化传输层。这个函数设置端点的功能和命令类列表。请阅读[13]以获得关于单个端点和聚合端点的更多信息。
void Transport_AddEndpointSupport( EP_FUNCTIONALITY_DATA* pFunctionality, EP_NIF* pList, BYTE sizeList);
2.6.3 多线程应用
Z-Wave应用程序默认运行以下线程:
-
Z-Wave协议线程,用于执行协议栈
-
主应用程序线程用于执行应用程序代码,并作为Z-Wave协议线程的接口。
一个应用程序可以扩展创建额外的FreeRTOS线程\任务。这些线程被称为用户任务,以区别于主应用程序线程(负责与Z-Wave协议线程通信)。
用户任务是使用ZW_UserTask模块(ZW_UserTask.h)创建的,该模块包含ZW_UserTask_t数据类型的定义和可用api用于创建任务。
ZW_UserTask_t定义如下:
typedef struct {
TaskFunction_t pTaskFunc;
char* pTaskName;
void* pUserTaskParam;
ZW_UserTask_Priority_t priority;
ZW_UserTask_Buffer_t* taskBuffer;
} ZW_UserTask_t;
-
pTaskFunc是任务执行的函数
-
pTaskName为任务名称
-
pUserTaskParam是传递给任务函数的参数指针
-
priority是任务的优先级。可用的优先级有:
> USERTASK_PRIORITY_BACKGROUND
> USERTASK_PRIORITY_NORMAL
> USERTASK_PRIORITY_HIGHEST
-
taskBuffer是FreeRTOS处理任务所需的静态分配内存。
以下是可用的api
ReturnCode_t ZW_UserTask_Init(void);
-
ZW_UserTask_Init() 函数用于初始化ZW_UserTask模块。不需要显式地调用它,因为它在引导时已经被调用了。
> 如果再次调用该函数,它将返回Code_Fail_InvalidState
ReturnCode_t ZW_UserTask_CreateTask(ZW_UserTask_t* task, TaskHandle_t* xHandle);
-
ZW_UserTask_CreateTask() 可以用于创建额外的任务。
> 此函数需要以下输入参数
task是指向ZW_UserTask任务参数的指针。
xHandle是FreeRTOS返回的任务句柄。
> 这个函数可以返回以下代码:
Code_Fail_InvalidState—如果模块还没有初始化。
Code_Fail_InvalidOperation—如果已经创建了最大的用户任务。
Code_Fail_InvalidParameter—如果优先级设置超出界限或任何输入参数为NULL。
Code_Fail_Unknown—如果FreeRTOS创建任务失败。
bool ZW_UserTask_ApplicationRegisterTask(
VOID_CALLBACKFUNC(appTaskFunc)(SApplicationHandles*),
uint8_t iZwRxQueueTaskNotificationBitNumber,
uint8_t iZwCommandStatusQueueTaskNotificationBitNumber,
const SProtocolConfig_t * pProtocolConfig
);
-
ZW_UserTask_ApplicationRegisterTask() 用于创建主应用程序线程。关于函数使用和行为的更多详细信息,请参见2.6节。
有关api的更多信息,请参阅ZW_UserTask.h文件。有关可用返回码的详细信息,请参阅ZW_global_definitions.h中定义的ReturnCode_t。
一个如何使用这些api的例子可以在SensorPIR应用程序中找到(参见SensorPIR.c中的ApplicationInit(…))
2.6.3.1 限制和建议
以下限制和建议适用于用户任务:
-
除了主应用程序线程之外,最多可以创建三个用户任务。
-
UserTask优先级的分配都低于Z-Wave协议线程的优先级(这在定义可用优先级级别的ZW_UserTask模块中强制执行)。
-
在FreeRTOS调度程序启动之前必须创建用户任务(使用函数ZW_UserTask_CreateTask),因此,它们的创建只能在ApplicationInit(…)中进行(参见7.6节)。
-
没有提供主应用程序线程和用户任务之间的通信。但是,建议使用FreeRTOS队列和事件。
-
用户任务不能直接与Z-Wave协议线程通信。它们需要使用主应用程序线程作为中介。
> 除了电源管理器api,它可以从所有任务中调用。