基于Domoticz智能家居系统(十八)移植MySensors终端框架代码到Keil-MDK并用STM32+NRF24L01作个简单无线继电器

基于Domoticz智能家居系统(十八)移植MySensors终端框架代码到Keil-MDK并用STM32+NRF24L01作个简单无线继电器

  • 移植MySensors终端框架代码到Keil-MDK并用STM32+NRF24L01作个简单无线继电器
    • 一、硬件连接:
      • 1、开发板与无线模块的连接(使用了STM32F107VC的SPI1接口)
      • 2、开发板与继电器的连接
    • 二、关于MySensors代码移植
      • 1、本次移植做了很多精简
      • 2、代码中未实现的部分
      • 3、主要的移植部分简介
      • 4、MySensors框架运行的基本流程
        • (1)、初始化和对其他节点建立连接的大概流程
        • (2)、向网关注册节点ID的大概流程
        • (3)、终端节点与网关建立通信的时序图
          • A、如果网关先开机,终端节点后开机
          • B、如果终端节点设备在通信建立后重启
          • C、如果终端节点设备先开机,网关后开机
        • (4)、实际通信的打印信息
      • 5、关于移植过程中的重点和难点
        • (1)、无线模块驱动
        • (2)、指定节点ID和父节点ID
        • (3)、注册节点ID
      • 6、完整移植代码(带应用例程)和编译
        • (1)、源代码
        • (2)、实际例程

移植MySensors终端框架代码到Keil-MDK并用STM32+NRF24L01作个简单无线继电器

前面一篇博文介绍了用Arduino IDE上如何用ESP8266和NRF24L01+模块作为MySensors系统的终端,Arduino IDE有它的特点和好处,比如可以安装第三方提供的各种库,或者第三方提供的各种开发板的板级支持包(BSP),可以通过某些工具,比如串口等等自动烧录固件,但是它也有很大的缺点,比如说它的IDE严重占用系统资源,因为它是用java开发的,运行一个软件实例已经导致系统很卡,如果开多个实例,同时看代码,那会导致系统死机;还有它似乎也不支持文件同步修改,也就是说如果我用source Insight等代码编辑器写的代码,不能同步过来,每次都需要重新打开这个IDE加载整个工程才行;还有一个最严重的缺点,那就是:它编译速度实在令人难以忍受。。。作为项目开发,如果有其他的编译器,请果断抛弃Arduino IDE!

本文将在MySensors 2.3.1 版本源代码基础之上,做些修改,直接用在STM32的开发板上,并用 Keil-MDK作为IDE进行编译。
Keil-MDK(Keil uVision5)版本号: MDK-ARM Plus Version 5.23

本人使用的STM32开发板是比较老旧的一个金牛板,芯片是STM32F107VC.

RF模块:NRF24L01+ 无线模块 功率加强版 2.4G 无线收发通信模块。
基于Domoticz智能家居系统(十八)移植MySensors终端框架代码到Keil-MDK并用STM32+NRF24L01作个简单无线继电器_第1张图片
还有一个5V1路继电器模块 小体积 (高电平触发吸合)
基于Domoticz智能家居系统(十八)移植MySensors终端框架代码到Keil-MDK并用STM32+NRF24L01作个简单无线继电器_第2张图片

一、硬件连接:

1、开发板与无线模块的连接(使用了STM32F107VC的SPI1接口)

STM32F107VC NRF24L01+ 模块
3V3 VCC (NRF24L01+须3.3V供电)
GND GND
PA3 CE
PA4 (SPI1_NSS) CSN (CS)
PA5 (SPI1_SCK) SCK
PA6 (SPI1_MISO) MISO
PA7 (SPI1_MOSI) MOSI

注意:本文不使用NRF24L01+的中断控制功能,故它的IRQ脚不接

2、开发板与继电器的连接

STM32F107VC 继电器
3V3 VCC (改继电器模块可以接受3v到5v或者更宽的范围)
GND GND
PB0 S (或IN)(信号控制端)

注意:本人使用的是比较便宜的继电器模块,没有光耦,直接跟STM32芯片的IO连接有安全上的风险,建议使用带光耦的模块。

二、关于MySensors代码移植

具体的移植比较麻烦,就不一点点写上来了。
挑几个重要的地方说吧。

1、本次移植做了很多精简

具体来说,去掉了做网关部分的代码,去掉了OTA(固件在线升级)部分的代码,去掉了加密、签名,只留最基本的通讯协议。留下的这部分,我们可以学习和熟悉MySensors的整个框架。有兴趣的朋友可以自己移植加密等部分。

2、代码中未实现的部分

本来MySensors框架需要用到存储芯片,用来存取节点ID和父节点ID等信息。但是我们在这次移植中为了简便,就省略了这部分代码,但在应用程序部分直接给定节点ID和父节点ID(默认为MY_PARENT_NODE_ID,默认初始值是255,表示尚未连接到父节点).。只要在正常任务运行之前,指定这两个ID,就不影响整体的运行。

3、主要的移植部分简介

首先说明一点:本次移植基于开发板使用FreeRTOS操作系统。但是,整个MySensors框架本质上是独立于操作系统的,移植过程大部分跟操作系统无关。如无操作系统(No OS),只要实现hwMillis、hwSleep、delay(ms)、delayMicroseconds(ms)函数,并写好NRF24模块的底层驱动,能够顺利收发数据就可以正常使用MySensors框架

移植的文件 位置 功能
MyHwSTM32F1.cpp MySensors/hal/architecture/STM32F1/ 实现STM32芯片上的几个最基本的函数,例如hwMillis(获取系统运行毫秒节拍数)、hwSleep(任务睡眠函数)、hwDebugPrint(打印调试信息函数)。
RF24.cpp MySensors/hal/transport/RF24/driver/ 实现NRF24L01+ 无线模块和STM32芯片的底层驱动(板子使用的是25Mhz晶振,运行主频是72Mhz)。
RF24.cpp MySensors/hal/transport/RF24/ 这部分属于进一步封装无线模块收发数据接口的适配层(这部分主要是是因为MySensors系统不只是针对NRF24模块控制,而是面向更多的无线模块,利用这层封装来适配这些模块)。
MyMessage.cpp MySensors/core/ 定义节点消息数据包结构,以及对数据值的读取处理等。每个数据包最长32字节(Byte),最短7字节。其中包括最基本的7字节消息头。payload(有效载荷)部分长度是0~25字节,最长25字节。具体请参考(Serial Protocol - 2.x 协议):https://www.mysensors.org/download/serial_api_20
MyTransport.cpp MySensors/core/ 传输层的实现,MySensors框架核心部分之一,主要按照通信协议对消息进行解析,实现一个控制通信状态的状态机机制,并通过解析后的消息来控制状态机。它有六个状态:stInit(初始化,获取节点ID、父节点ID等信息)、 stParent(寻找父节点)、 stID(确认终端ID合法性或请求向MySensors网关请求注册一个ID)、stUplink(把本节点连接到MySensors网关进行级联)、stReady(连接到MySensors网关成功,为更进一步无线数据传输功能准备就绪)、stFailure(连接MySensors网关失败)。另外,实现了节点之间发送消息、节点向网关或路由(即MySensors中继器)发送消息的函数,以及控制无线模块(例如但不限于NRF24无线模块)发送消息数据的底层实现。
MySensorsCore.cpp MySensors/core/ MySensors框架核心部分之二,主要是管理MySensors系统的整体运行,并封装常用的API函数。

4、MySensors框架运行的基本流程

注意:下面的流程主要是对指定的动态ID进行注册的过程。
如果是指定静态ID,注册的过程则稍有不同。

(1)、初始化和对其他节点建立连接的大概流程

Created with Raphaël 2.2.0 应用程序调用进入_begin() ,初始化无线设备; transportInitialise():初始化传输层状态机,把状态机切换到stInit状态,获取/指定节点ID和父节点原始ID; 进入transportWaitUntilReady(): 进入transportProcess(),在此处理接收到的消息数据 调用transportUpdateSM(), 如果状态有变化则更新状态机(初始时,状态为stInit) 进入stInitUpdate(),对节点ID可用性进行确认 进入transportProcessFIFO(); transportAvailable() 判断是否接收到数据 指定的父节点ID是否可用? 切换状态机到stParent,运行stParentTransition(), 在该函数中会发送一个 I_FIND_PARENT_REQUEST 消息, 请求目标父节点回复; 然后进入stParentUpdate(),寻找父节点(等待连接目标返回信息) 是否已经找到父节点? 如果(parentNodeId != AUTO)为真, 则表示找到,否则没找到父节点 切换状态机到stID,进入stIDUpdate() 从目标节点接收到的父 节点ID是否合法(非原始ID) 更新父节点ID记录; 切换状态机到stUplink,运行stUplinkTransition(), 在该函数中发送一个 I_PING 消息到父节点; 进入stUplinkUpdate(),等待对方节点回复 判断是否受到对方回应I_PONG 消息 已经成功建立连接,基本通信功能就绪,切换状态机到stReady。 跳出transportWaitUntilReady()。 切换状态机到stFailure, 然后再切换状态机到stInit 跳出transportProcessFIFO() 跳出transportWaitUntilReady() yes no yes no yes no yes no yes no

(2)、向网关注册节点ID的大概流程

【注意】:框架使用了MY_REGISTRATION_FEATURE (注册特性),也就是说该节点在发送传感器数据前,必须向网关或控制器进行注册。控制器是指另外一个节点,它对传感器节点进行响应或其他操作。

Created with Raphaël 2.2.0 传输层状态机被置为stReady stReadyTransition() 调用_transportReady_cb()。 该函数中会回调_transportReady_cb函数指针, 这个函数指针默认在_begin()中, 被transportRegisterReadyCallback函数 设置为_callbackTransportReady(), 如果想设置为自定义函数, 可以在应用程序中的setup()中去设定 调用_callbackTransportReady() 进入presentNode() 调用present发送节点介绍信息到网关; 调用_sendRoute()向控制器发送 I_CONFIG 路由消息进行配置交换请求, 把前面得到的父节点ID发送出去。( 控制器以最新的节点配置回应) 运行wait()函数,该函数又调用_process(),等待配置回应; _process()会调用transportProcess(); transportProcess()则调用transportUpdateSM()和transportProcessFIFO(); transportUpdateSM()如前面流程图分析,它更新状态机的状态; transportProcessFIFO()主要是接收、解析消息,并调用合适的状态切换。 当网关接收到 I_CONFIG 配置交换请求后, 会把请求者的配置信息记录并保存到网关中,终端设备会等待2秒 应用程序自定义的presentation(),如果没有定义则跳过; 这里一般是应用程序调用sendSketchInfo()发送节点名称、版本号等信息, 调用present()向网关发送应用程序中的 C_PRESENTATION 命令消息。 这部分由终端应用程序自己去定义,没有硬性要求。 跳出presentNode(), 进入_registerNode()——真正注册节点ID的函数 调用_sendRoute()向网关发送 I_REGISTRATION_REQUEST (注册请求)消息 最多尝试MY_REGISTRATION_RETRIES(默认为3)次 调用wait(2000, C_INTERNAL, I_REGISTRATION_RESPONSE) 来 等待接收网关发来的 I_REGISTRATION_RESPONSE (注册响应)消息 wait会调用_process();_processd的调用过程如前几步所述 是否收到 I_REGISTRATION_RESPONSE 消息 _coreConfig.nodeRegistered根据消息数据进行赋值(真或假) _coreConfig.nodeRegistered是否为真? 真:注册成功; 假:注册失败 注册失败的话,会导致send发送消息失败。 _coreConfig.nodeRegistered直接赋值为假 yes no

(3)、终端节点与网关建立通信的时序图

以终端节点ID=3为例(网关ID默认为0,负责广播的节点ID=255)(D表示Destination——目标节点):

A、如果网关先开机,终端节点后开机
终端结点 网关 开机运行(过程省略) 父节点初始ID=255,D=255 ,Sensor Id= 255 向ID=255节点发送 I_FIND_PARENT_REQUEST 消息 向ID=3节点发送 I_FIND_PARENT_RESPONSE 消息 父节点ID=0,D=1 向网关ID=0节点发送 I_PING 消息 向ID=3节点发送 I_PONG 消息 确认ID=3,PAR(父节点)=0,DIS(距离)=1,准备就绪 发送 I_SIGNING_PRESENTATION (节点介绍信息)消息 发送 I_SIGNING_PRESENTATION (网关介绍信息)消息 发送S_ARDUINO_NODE消息 发送 I_CONFIG (配置)消息 发送 I_CONFIG (配置)消息 以下运行自定义 presentation函数 presentation()内容开始 发送I_SKETCH_NAME消息 发送I_SKETCH_VERSION消息 发送 类型为 S_DOOR 传感器 消息 (Sensor Id=CHILD_ID_DOOR的值) 发送 类型为 S_TEMP 传感器 消息 (Sensor Id=CHILD_ID_TEMP 的值) 网关会注册这两个ID presentation()内容结束 发送 I_REGISTRATION_REQUEST (节点注册请求) 消息 发送 I_REGISTRATION_RESPONSE (注册响应) 消息,Payload: 1为成功,0为失败 以上是建立基本通信 ; 下面是应用程序 跟网关的通信 应用程序调用 request( CHILD_ID_DOOR, V_STATUS, GATEWAY_ADDRESS) 发送V_STATUS (Id=CHILD_ID_DOOR的传感器状态值)消息(无Payload表示请求) 发送V_STATUS (Id=CHILD_ID_DOOR的传感器状态值)消息(有Payload表示应答) 通信结束! 终端结点 网关
B、如果终端节点设备在通信建立后重启
终端结点 网关 开机运行(过程省略) 运行到stReadyUpdate() 向广播节点发送 I_DISCOVER_REQUEST (查找终端节点)消息 广播节点ID= BROADCAST_ADDRESS 默认值为255 向网关发送 I_DISCOVER_RESPONSE 消息 网关节点ID=0 调用stReadyUpdate()结束 loop [ 网关一直循环 ] 终端结点 网关
C、如果终端节点设备先开机,网关后开机

这种情况实际是跟A情况过程一样的。

(4)、实际通信的打印信息

终端节点:

###############################################
##    hello! welcome to FreeRTOS v9.0.0      ##
###############################################


main.c,line:177,
RF24Task.cpp,line:122,vTaskRF24L01()
0 MCO:BGN:INIT NODE,REL=255,VER=2.3.1

1 MCO:BGN:BFR

1 TSM:INIT

1 TSF:WUR:MS=0

2 RF24:INIT

2 RF24:SBY

1003 RF24:FRX

1003 RF24:FTX

1004 TSM:INIT:TSP OK

1005 RF24:STL

1005 TSF:SID:OK,ID=3

1006 TSM:FPAR

MyTransport.cpp,line:942,totalMsgLength=7
1007 RF24:SPL

1008 RF24:OWP:RCPT=255

1009 RF24:TXM:TO=255,LEN=7

1009 RF24:FTX

1015 !RF24:TXM:MAX_RT

1015 RF24:FTX

1016 RF24:STL

1016 TSF:MSG:SEND,3-3-255-255,s=255,c=3,t=7,pt=0,l=0,sg=0,ft=0,st=OK:

1143 RF24:RXM:LEN=8

1144 TSF:MSG:READ,0-0-3,s=255,c=3,t=8,pt=1,l=1,sg=0:0

1145 TSF:MSG:FPAR OK,ID=0,D=1

3019 TSM:FPAR:OK

3019 TSM:ID

3020 TSM:ID:OK

3020 TSM:UPL

MyTransport.cpp,line:942,totalMsgLength=8
3021 RF24:SPL

3022 RF24:OWP:RCPT=0

3022 RF24:TXM:TO=0,LEN=8

3023 RF24:FTX

3024 RF24:STL

3025 TSF:MSG:SEND,3-3-0-0,s=255,c=3,t=24,pt=1,l=1,sg=0,ft=0,st=OK:1

3027 RF24:RXM:LEN=8

3028 TSF:MSG:READ,0-0-3,s=255,c=3,t=25,pt=1,l=1,sg=0:1

3029 TSF:MSG:PONG RECV,HP=1

3030 TSM:UPL:OK

3031 TSM:READY:ID=3,PAR=0,DIS=1

MyTransport.cpp,line:942,totalMsgLength=9
3032 RF24:SPL

3033 RF24:OWP:RCPT=0

3034 RF24:TXM:TO=0,LEN=9

3034 RF24:FTX

3035 RF24:STL

3036 TSF:MSG:SEND,3-3-0-0,s=255,c=3,t=15,pt=6,l=2,sg=0,ft=0,st=OK:0100

3038 RF24:RXM:LEN=9

3039 TSF:MSG:READ,0-0-3,s=255,c=3,t=15,pt=6,l=2,sg=0:0100

MyTransport.cpp,line:942,totalMsgLength=12
3042 RF24:SPL

3042 RF24:OWP:RCPT=0

3043 RF24:TXM:TO=0,LEN=12

3044 RF24:FTX

3045 RF24:STL

3045 TSF:MSG:SEND,3-3-0-0,s=255,c=0,t=17,pt=0,l=5,sg=0,ft=0,st=OK:2.3.1

MyTransport.cpp,line:942,totalMsgLength=8
3048 RF24:SPL

3049 RF24:OWP:RCPT=0

3049 RF24:TXM:TO=0,LEN=8

3050 RF24:FTX

3051 RF24:STL

3052 TSF:MSG:SEND,3-3-0-0,s=255,c=3,t=6,pt=1,l=1,sg=0,ft=0,st=OK:0

3063 RF24:RXM:LEN=8

3064 TSF:MSG:READ,0-0-3,s=255,c=3,t=6,pt=0,l=1,sg=0:M

RF24Task.cpp,line:40,presentation()
MyTransport.cpp,line:942,totalMsgLength=16
3067 RF24:SPL

3067 RF24:OWP:RCPT=0

3068 RF24:TXM:TO=0,LEN=16

3069 RF24:FTX

3070 RF24:STL

3071 TSF:MSG:SEND,3-3-0-0,s=255,c=3,t=11,pt=0,l=9,sg=0,ft=0,st=OK:RF24 test

MyTransport.cpp,line:942,totalMsgLength=10
3074 RF24:SPL

3074 RF24:OWP:RCPT=0

3075 RF24:TXM:TO=0,LEN=10

3076 RF24:FTX

3077 RF24:STL

3077 TSF:MSG:SEND,3-3-0-0,s=255,c=3,t=12,pt=0,l=3,sg=0,ft=0,st=OK:1.0

MyTransport.cpp,line:942,totalMsgLength=7
3080 RF24:SPL

3081 RF24:OWP:RCPT=0

3081 RF24:TXM:TO=0,LEN=7

3082 RF24:FTX

3083 RF24:STL

3084 TSF:MSG:SEND,3-3-0-0,s=1,c=0,t=0,pt=0,l=0,sg=0,ft=0,st=OK:

MyTransport.cpp,line:942,totalMsgLength=11
3086 RF24:SPL

3086 RF24:OWP:RCPT=0

3087 RF24:TXM:TO=0,LEN=11

3088 RF24:FTX

3089 RF24:STL

3090 TSF:MSG:SEND,3-3-0-0,s=3,c=0,t=6,pt=0,l=4,sg=0,ft=0,st=OK:Temp

3091 MCO:REG:REQ

MyTransport.cpp,line:942,totalMsgLength=8
3092 RF24:SPL

3093 RF24:OWP:RCPT=0

3094 RF24:TXM:TO=0,LEN=8

3094 RF24:FTX

3095 RF24:STL

3096 TSF:MSG:SEND,3-3-0-0,s=255,c=3,t=26,pt=1,l=1,sg=0,ft=0,st=OK:2

3098 RF24:RXM:LEN=8

3099 TSF:MSG:READ,0-0-3,s=255,c=3,t=27,pt=1,l=1,sg=0:1

3101 MCO:PIM:NODE REG=1

3101 MCO:BGN:STP

MyTransport.cpp,line:942,totalMsgLength=7
3103 RF24:SPL

3103 RF24:OWP:RCPT=0

3104 RF24:TXM:TO=0,LEN=7

3105 RF24:FTX

3106 RF24:STL

3106 TSF:MSG:SEND,3-3-0-0,s=1,c=2,t=2,pt=0,l=0,sg=0,ft=0,st=OK:

3108 MCO:BGN:INIT OK,TSP=1

3136 RF24:RXM:LEN=8

3137 TSF:MSG:READ,0-0-3,s=1,c=2,t=2,pt=0,l=1,sg=0:0

status=0

网关:

1901540 TSF:MSG:READ,3-3-255,s=255,c=3,t=7,pt=0,l=0,sg=0:
1901545 TSF:MSG:BC
1901547 TSF:MSG:FPAR REQ,ID=3
1901550 TSF:PNG:SEND,TO=0
1901552 TSF:CKU:OK
1901554 TSF:MSG:GWL OK
1902570 TSF:MSG:SEND,0-0-3-3,s=255,c=3,t=8,pt=1,l=1,sg=0,ft=0,st=OK:0
1917238 TSF:MSG:READ,3-3-0,s=255,c=3,t=24,pt=1,l=1,sg=0:1
1917243 TSF:MSG:PINGED,ID=3,HP=1
1917253 TSF:MSG:SEND,0-0-3-3,s=255,c=3,t=25,pt=1,l=1,sg=0,ft=0,st=OK:1
1917326 TSF:MSG:READ,3-3-0,s=255,c=3,t=15,pt=6,l=2,sg=0:0100
1917335 TSF:MSG:SEND,0-0-3-3,s=255,c=3,t=15,pt=6,l=2,sg=0,ft=0,st=OK:0100
1917398 TSF:MSG:READ,3-3-0,s=255,c=0,t=17,pt=0,l=5,sg=0:2.3.1
1917403 GWT:TPS:TOPIC=domoticz/in/MyMQTT/3/255/0/0/17,MSG SENT
PresentationMessage
1917449 TSF:MSG:READ,3-3-0,s=255,c=3,t=6,pt=1,l=1,sg=0:0
1917454 GWT:TPS:TOPIC=domoticz/in/MyMQTT/3/255/3/0/6,MSG SENT
1917531 GWT:IMQ:TOPIC=domoticz/out/MyMQTT/3/255/3/0/6, MSG RECEIVED
1917539 TSF:MSG:SEND,0-0-3-3,s=255,c=3,t=6,pt=0,l=1,sg=0,ft=0,st=OK:M
1917597 TSF:MSG:READ,3-3-0,s=255,c=3,t=11,pt=0,l=9,sg=0:RF24 test
1917603 GWT:TPS:TOPIC=domoticz/in/MyMQTT/3/255/3/0/11,MSG SENT
1917648 TSF:MSG:READ,3-3-0,s=255,c=3,t=12,pt=0,l=3,sg=0:1.0
1917653 GWT:TPS:TOPIC=domoticz/in/MyMQTT/3/255/3/0/12,MSG SENT
1917694 TSF:MSG:READ,3-3-0,s=1,c=0,t=0,pt=0,l=0,sg=0:
1917699 GWT:TPS:TOPIC=domoticz/in/MyMQTT/3/1/0/0/0,MSG SENT
PresentationMessage
1917741 TSF:MSG:READ,3-3-0,s=3,c=0,t=6,pt=0,l=4,sg=0:Temp
1917746 GWT:TPS:TOPIC=domoticz/in/MyMQTT/3/3/0/0/6,MSG SENT
PresentationMessage
1917792 TSF:MSG:READ,3-3-0,s=255,c=3,t=26,pt=1,l=1,sg=0:2
1917804 TSF:MSG:SEND,0-0-3-3,s=255,c=3,t=27,pt=1,l=1,sg=0,ft=0,st=OK:1
1917873 TSF:MSG:READ,3-3-0,s=1,c=2,t=2,pt=0,l=0,sg=0:
1917878 GWT:TPS:TOPIC=domoticz/in/MyMQTT/3/1/2/0/2,MSG SENT
1918102 GWT:IMQ:TOPIC=domoticz/out/MyMQTT/3/1/2/0/2, MSG RECEIVED
1918110 TSF:MSG:SEND,0-0-3-3,s=1,c=2,t=2,pt=0,l=1,sg=0,ft=0,st=OK:0
1944523 GWT:IMQ:TOPIC=domoticz/out/MyMQTT/0/0/3/0/18, MSG RECEIVED
1944529 GWT:TPS:TOPIC=domoticz/in/MyMQTT/0/255/3/0/22,MSG SENT

5、关于移植过程中的重点和难点

(1)、无线模块驱动

其实移植的大部分源代码不需要太多改动,主要是对NRF24L01模块的驱动做一些调整。
只要严格按照它手册上的时序来调,就比较容易调出来。
如果读者有条件,强烈建议使用示波器来测量时序。

调试的难点在于,如果NRF24驱动中的延迟不当,会造成模块与主控芯片基本通信失败。
应该在保证基本通信正常的情况下进行传输层的通信调试。

基本通信是指主控芯片STM32对NRF24L01模块寄存器的读写,以及由发射状态转入接收状态后能否及时收到数据。

(2)、指定节点ID和父节点ID

我们使用的是动态配置ID,所以在注册节点ID之前,需要在程序中初始化节点ID和父节点ID。
笔者已经添加了设置这两个ID的API函数:

    void setCFG_ParentNodeId(const uint8_t id);
    void setCFG_NodeId(const uint8_t id);

设置节点ID和父节点ID可以在应用程序中定义before函数,该函数体中实现:

void before()
{
	//设置父节点ID,默认为255
	setCFG_ParentNodeId(MY_PARENT_NODE_ID);
	
	//设置本NRF24模块节点ID为3
	setCFG_NodeId(3);
}

默认网关的ID为0,广播节点ID为255。
所有本无线网络内节点的父节点ID就默认设置为255。
节点ID可以视情况而定,可以是1~254中的任意值,但需要保证这个无线网络内的任意两个节点ID不要重复。
当节点发送消息时,最初是发给广播节点255,网关默认负责广播节点255的消息处理,连接成功后,网关发消息给终端节点。
之后终端节点会更改父节点ID为网关ID(默认为0)。

(3)、注册节点ID

传输层的通信一个重要点在于注册节点ID有个wait函数调用,默认超时设置为2000ms(即2秒),所以,需要在这个超时时间内完成注册的确认,如果驱动程序在基本通信延迟很严重的话,那么就会导致注册不成功。

6、完整移植代码(带应用例程)和编译

(1)、源代码

因为代码比较多,就不贴上来了。
提供一个下载地址:
移植MySensors终端框架代码到Keil-MDK

代码的编译使用到 Keil-MDK,需要加入 ARDUINO_ARCH_STM32F1 这个宏定义 和 --gnu,和设置如图:
基于Domoticz智能家居系统(十八)移植MySensors终端框架代码到Keil-MDK并用STM32+NRF24L01作个简单无线继电器_第3张图片

加入“–gnu”,是因为编译MyMessage这个代码模块的时候,这个类的数据结构中包含几个结构体,并且,代码使用中,采用了匿名方式访问,就是说,这些结构体并没有起名字,兼容GNU标准的编译器是允许这样做的,但是其他的编译器很难说。
就像Keil-MDK,需要加上“–gnu”这个参数来支持匿名访问特性。

(2)、实际例程

Relay1Driver.h :

/******************************************************************************
* ÎļþÃû: Relay1Driver.h
* ×¢:
* 
* ¼òÊö: A brief file description.
* ÏêÊö:
*     A more elaborated file description.
* ´´½¨Õß: 
* ´´½¨Ê±¼ä:2011-Apr-20 23:57:33
* ÐÞ¸ÄÕß: 
* ÐÞ¸Äʱ¼ä:2011-Apr-20 23:57:33
* °æ±¾¼Ç¼:
******************************************************************************/


#ifndef STEPMOTOR_DRIVER_H
#define STEPMOTOR_DRIVER_H

#ifdef __cplusplus
extern "C"{
#else
#include 	
#endif

void Relay1_Init();
void Relay1_SetOnOrOff(bool on);

#ifdef __cplusplus
}
#endif

#endif
/*-- File end --*/



Relay1Driver.c :

/******************************************************************************
* 文件名: Relay1Driver.c
* 注:
*
* 简述: A brief file description.
* 详述:
*     A more elaborated file description.
* 创建者: 
* 创建时间:2011-Apr-20 23:53:21
* 修改者: 
* 修改时间:2011-Apr-20 23:53:21
* 版本记录:
******************************************************************************/

#include "Relay1Driver.h"
#include "stm32f10x_gpio.h"

#define _DEBUG
#include "dprintf.h"	


/*-- #include --*/
#include "stm32f10x.h"

#define RELAY_1_PB_RCC				RCC_APB2Periph_GPIOB
#define RELAY_1_PB_GPIO				GPIOB
#define RELAY_1_PB_PIN				(GPIO_Pin_0)

	
typedef struct
{
	GPIO_TypeDef* GPIOx;
	uint16_t GPIO_Pin;
}Relay;

void Relay1_Init();
void Relay1_SetOnOrOff(bool on);


Relay relay1={RELAY_1_PB_GPIO,RELAY_1_PB_PIN};

void Relay1_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RELAY_1_PB_RCC,ENABLE); 
	
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
																
	GPIO_InitStructure.GPIO_Pin   = relay1.GPIO_Pin;
	GPIO_Init(relay1.GPIOx, &GPIO_InitStructure);

}


void Relay1_SetOnOrOff(bool on)
{
	if(on==true)
	{
		GPIO_SetBits(relay1.GPIOx, relay1.GPIO_Pin);

	}
	else
	{
		GPIO_ResetBits(relay1.GPIOx, relay1.GPIO_Pin);
	}
}
/*-- File end --*/


main.c 代码片段:

#include "RF24Task.h"
#define NRF24_TASK_PRIO    ( tskIDLE_PRIORITY )

static TaskHandle_t xHandleRF24Task=NULL;

int main(void)
{  
	__set_PRIMASK(1);//禁止全局中断
	prvSetupHardware();	
	
	FreeRTOS_printf_service_init();

	printf("###############################################\r\n");
	printf("##    hello! welcome to FreeRTOS v9.0.0      ##\r\n");
	printf("###############################################\r\n");

	printf("\r\n\r\n");
	
	dprintf("\n");
	
	xTaskCreate(vTaskLED,"vTaskLED",ledSTACK_SIZE,NULL,3,&xHandleTaskLED);

	xTaskCreate(vTaskRF24L01,"vTaskRF24L01",256,NULL,NRF24_TASK_PRIO,&xHandleRF24Task);
	
	vTaskStartScheduler();//启动任务调度器
}

#include "FreeRTOS.h"
#include "task.h"

#include "Relay1Driver.h"

#define _DEBUG
#include "dprintf.h"

#include "MySensors.h"
#include "MyMessage.h"

#define CHILD_ID_DOOR 1
#define CHILD_ID_TEMP 3

MyMessage msgTemp(CHILD_ID_TEMP,V_TEMP);

/**
 * For initialisations that needs to take place before MySensors transport has been setup (eg: SPI devices).
 */
 
//before函数在MySensors框架代码中被调用,而且是在执行setup()函数之前被调用
void before()
{
	//设置父节点ID,默认为255
	setCFG_ParentNodeId(MY_PARENT_NODE_ID);
	
	//设置本NRF24模块节点ID为3
	setCFG_NodeId(3);
}
/**
 * This allows controller to re-request presentation and do re-configuring node after startup.
 */

//presentation函数在MySensors框架代码中被调用,
//它允许控制器重新请求节点介绍信息,并在节点建立后重新进行配置
void presentation()
{dprintf("presentation()\n");
	// Present locally attached sensors here
  sendSketchInfo("RF24 test", "1.0",false);
	
	present(CHILD_ID_DOOR, S_DOOR,"",false); 
	present(CHILD_ID_TEMP, S_TEMP,"Temp",0);//S_TEMP
	
}

/**
 * Called once at startup, usually used to initialize sensors.
 */
void setup()
{
	Relay1_Init();//继电器1硬件初始化
	Relay1_SetOnOrOff(false);//断开继电器1
	
	//向网关请求传感器id=CHILD_ID_DOOR的状态值(V_STATUS)
	request(CHILD_ID_DOOR, V_STATUS,GATEWAY_ADDRESS);
	
	//request(MS_RELAY2_CHILD_ID, V_STATUS);
	//request(MS_LEDPWM_CHILD_ID, V_DIMMER);
}

void loop()
{//dprintf("loop()\n");	
    // Send  locally attached sensor data here
}

//receive函数在MySensors框架代码中被调用
void receive(const MyMessage &message)
{
	if( message.sensor == CHILD_ID_DOOR )
	{
		if( message.type == V_STATUS )
		{
			bool status = message.getBool();//从消息中获取状态值
			if( status )
			{
				printf("status=1\n");
				Relay1_SetOnOrOff(true);//吸合继电器1
			}
			else
			{
				printf("status=0\n");
				Relay1_SetOnOrOff(false);//断开继电器1
			}
		}  
	}
}

	
//无线模块任务
extern "C" void vTaskRF24L01(void* pvParameters)
{
	dprintf("vTaskRF24L01()\n");
	_begin(); //Startup MySensors library
	for(;;) 
	{
		_process(); //处理接收到的数据

		loop(); // 调用 sketch loop
	}    
}

实际效果如图:

你可能感兴趣的:(智能家居,FreeRTOS与物联网)