一、概述
  
LoRa是semtech公司创建的低功耗局域网无线标准,我们知道,低功耗一般很难覆盖远距离,而远距离一般功耗高,LoRa的名字翻译就是远距离无线电(Long Range Radio),它最大特点就是在同样的功耗条件下比其他无线方式传播的距离更远,实现了低功耗和远距离的统一,它在同样的功耗下比传统的无线射频通信距离扩大3-5倍。

LoRa在物联网应用中的无线技术有多种,可组成局域网或广域网。LoRa网络主要由终端(可内置LoRa模块)、网关(或称基站)、Server和云四部分组成。

LoRaWAN的数据传输速率范围为0.3 kbps至37.5 kbps,为了最大化终端设备电池的寿命和整个网络容量,LoRaWAN网络服务器通过一种速率自适应(Adaptive Data Rate , ADR)方案来控制数据传输速率和每一终端设备的射频输出功率。

  二、方案设计
  
本案例将通过LoRa技术实现对机房的环境检测与控制。

一般情况下,一个企业的机房很少连接网络,即使连接网络了,也要花费不小的费用进行设备和线路的安装。例如使用宽带时要走网线,距离短,机房少可能还好,如果机房较多,距离较长,那么安装成本和人工成本就会迅速增加。如果安装无线路由器,方法虽然可行,但是穿透力差,信号有时连接不上,想要增强信号,就要增加设备。

至于NB模组,如果信号良好,会是不错之选。目前仍然有许多城市或者乡镇没有覆盖NB-IoT信号。所以NB模组适用于一些大城市。我们要想知道是否覆盖物联网信号,就看这个城市是否有共享单车。比如我所在的海南省,除了地级市,其他县级市基本没有覆盖物联网,因为那里没有共享单车。

LoRa有远距离、低功耗以及低成本等优势。LoRa的传输距离范围长达15至20公里,低功耗的特性延长了电池使用寿命,免牌照的频段、基础设施以及节点/终端的低成本,以上特性都使LoRa的使用成本大幅降低。

所以就机房或者整栋建筑大楼这个特定区域来讲,采用LoRa技术来实现对机房的环境参数的采集与控制是最节省资源的办法。它的穿透力适合布局到整个办公大楼。从而把它变成智慧建筑是可行的。

up-764ea90379de377793522e0808f71f98fe7.jpeg
LoRa网络拓补图

up-dd411ea35e56f7f54b83f4688079e021c4f.jpeg
设计框架

up-be45f5817c95b8614011b7e91a1fcdd1656.jpeg
智慧建筑

  三、方案实现
首先参考教程基于 TencentOS tiny 的 LoRaWAN 开发入门指南[1] 的介绍完成开发环境搭建,包括 MDK 软件的安装及配置、ST-Link 驱动安装、串口软件的安装。

  1. 硬件设计
  (1)LoRa套件
本方案采用P-NUCLEO-LRWAN3套件,包括网关和节点,可用于评估LoRaWAN网络。使用该套件,用户可以轻松设置LPWAN网络,帮助用户学习LoRaWAN技术,了解如何在自己的应用程序中使用LoRaWAN技术。LoRa网关套件由ST Nucleo-F746ZG底板和瑞兴恒方基于SX1301的LRWAN_GS模块组成。

ST Nucleo LoRa节点套件由LRWAN_NS1扩展板和ST Nucleo-L073底板组成。其中 LRWAN_NS1扩展板集成瑞兴恒方的RHF0M003 LoRaWAN模组,并集成了温湿度传感器HTS221、气压传感器LPS22HB、3轴磁力传感器LIS3MDL、6轴姿态传感器LSM6DS3共4个I2C传感器件。

LoRa节点采集的数据通过LoRa网关将数据上传到物联网云平台,实现对终端设备的控制和数据监控。

up-5a2abf1f48bed48f677762b85e238add77b.jpeg
LoRa套件

  (2)LCD显示
液晶屏是ST7735R,用于显示实时采集的数据。例如温度度,压强,海拔等,以及控制的状态指示。采用模拟I2C的方式来实现写指令。

up-0995661b0c7a7a34a4789c3db809e3d5b70.jpeg
LCD显示采集的数据和控制状态

  (3)继电器设计
继电器模块主要用于220V交流电的开关,实现对电机或电灯的电源控制。电路中采用的是光耦进行电气隔离,防止回流时对MCU的冲击。依据这个电路,可以扩展出多路继电器,实现控制各类设备。

up-b0a098e0460cb21335fc86518910e5cc9ec.jpeg
继电器电路

up-4fc4ebefb489d30d0f96851f6e994cb7319.jpeg
继电器电路实物

  (4)E53模块应用
采用E53 SC1模块,我是从小熊派物联网开发套件中拿来用的。这个扩展模块集成有LED路灯,光照强度传感器BH1750以及EEPROM芯片24Cxx。我的设计思想是,通过光照强度传感器检测机房亮暗度来决定是否开启应急灯的充电或开启机房灯光,保证应急抢修的需要。同时把相关控制信息存储到EEPROM,实现历史记录的查询。以下是模块原理图:

up-48f81447685f873fc99ea1f8cc10ebb5f3f.jpeg
扩展模块接口原理图

up-f56ff95d8159a20e6844b488ae5db5145af.jpeg
光照强度传感器原理图

up-f2ef518c05a30a6ee9705224f173e44e630.jpeg
EEPROM原理图

up-fa534b56da6cecae097be7733070ba8cb49.jpeg
E53智慧路灯扩展模块

  (5)LoRa节点整体外观
右边的两个黑色按键和开发板上的蓝色按键分别实现LED路灯、继电器和LCD背光的本地控制。设计思路是脱离网络方便本地控制。

up-ea60f16d0065412d2845b5fc03abd42d2c0.jpeg
各个模块拼凑起来的LoRa控制电路

  2. 软件设计
  (1)LoRa源码实现
该套件可以很快实现上云,通过官方提供的教程LoRa 温湿度传感器接入指引[2],打通数据连接,随后就是修改TencentOS tiny源码中的LoRa案例。

以下是需要要上报云端的参数,需要注意的是参数的顺序要和云端解析顺序一致,否则会解码失败而读到错误的数据。
  1. uint16_t report_period = 1;
  2. bool    report_power_switch=0;
  3. bool    report_motor_fan=0;
  4. float   report_pressure=0;
  5. float   report_height=0;
  6. float   first_pressure=0;
  7. float   first_height=0;
  8. extern float pressure_hPa;
  9. extern float temperature_degC;
  10. extern float height;
  11. ​ typedef struct device_data_st {   
  12.      uint8_t     temperature;
  13.      uint8_t     humidity;
  14.      uint16_t    period;
  15.      unsigned int quantity;
  16.      bool   power_switch;
  17.      bool   motor_fan;
  18.      float  pressure;
  19.      float  height;
  20. } __PACKED__ dev_data_t;
以下是按键任务,功能是进行普通的按键扫描,检测到相应按键后进行相应控制,同时把相关的控制状态通过LCD显示出来并反馈到云端。
  1. void key_task(void *arg){  
  2.    int lcd_back_flag=1;  
  3.    while(1)  {   
  4.         tos_task_delay(10);
  5.         if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET)   
  6.           {        
  7.                tos_task_delay(100);
  8.                if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET)
  9.                  {              
  10.                       lcd_back_flag=~lcd_back_flag;
  11.                       if(lcd_back_flag==1)         
  12.                          {                    
  13.                               LCD_LED_CLR;//关闭LCD背光
  14.                         }         
  15.                        else         
  16.                           {                  
  17.                               LCD_LED_SET;//开LCD背光  
  18.                          }      
  19.                    }   
  20.              }   
  21.           if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET)   
  22.              {        tos_task_delay(100);
  23.                       if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET)
  24.                         {                 
  25.                            if(report_power_switch==1)         
  26.                               {                        
  27.                                    report_power_switch=0;   
  28.                                    HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin,GPIO_PIN_RESET);
  29.                                    printf("LED OFF: %d\n",report_power_switch);
  30.                                    Gui_DrawFont_GBK16(5,125,RED,BLACK ,(uint8_t*)"LED:OFF");
  31.                                }           
  32.                             else            
  33.                                {                     
  34.                                     report_power_switch=1;
  35.                                     HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin,GPIO_PIN_SET);
  36.                                     printf("LED OFF: %d\n",report_power_switch);
  37.                                     Gui_DrawFont_GBK16(5,125,GREEN,BLACK ,(uint8_t*)"LED:ON");
  38.                                }        
  39.                          }   
  40.                }   
  41.          if(HAL_GPIO_ReadPin(KEY3_GPIO_Port,KEY3_Pin)==GPIO_PIN_RESET)
  42.                {        tos_task_delay(100);  
  43.                         if(HAL_GPIO_ReadPin(KEY3_GPIO_Port,KEY3_Pin)==GPIO_PIN_RESET)  
  44.                           {                 
  45.                               if(report_motor_fan==1)         
  46.                                 {                        
  47.                                      report_motor_fan=0;
  48.                                      HAL_GPIO_WritePin(MOTOR_GPIO_Port,MOTOR_Pin,GPIO_PIN_RESET);
  49.                                      printf("motor_fan OFF: %d\n", report_motor_fan);
  50.                                      Gui_DrawFont_GBK16(5,140,RED,BLACK,(uint8_t*)"Motor:OFF  ");
  51.                                 }         
  52.                               else           
  53.                                 {                    
  54.                                      report_motor_fan=1;
  55.                                      HAL_GPIO_WritePin(MOTOR_GPIO_Port,MOTOR_Pin,GPIO_PIN_SET);
  56.                                      printf("motor_fan ON: %d\n", report_motor_fan);  
  57.                                      Gui_DrawFont_GBK16(5,140,GREEN,BLACK,(uint8_t*)"Motor:ON  ");  
  58.                                  }      
  59.                            }     
  60.                    }  
  61.               }
  62.        }
主任务函数,该任务实现LoRa连接到网关,检测连接状态,定时上报数据以及一些需要换算的参数。
  1. void application_entry(void *arg)
  2.     {  
  3.         printf("APP RUNNING...\r\n");
  4.         float report_temperature;
  5.         int16_t  temperature;
  6.         int16_t report_humidity;
  7.         unsigned int quantity=0;
  8.         uint16_t sum1=0;
  9.         char temp[]={0};
  10.         Lcd_Init();
  11.         LCD_LED_SET;
  12.         Lcd_Clear(BLACK);
  13.         Gui_DrawFont_GBK16(10,5,RED,BLACK ,"TencentOS Tiny");
  14.         Gui_DrawFont_GBK16(10,20,YELLOW,BLACK,"LoRa Node NO.1");
  15.         LPS22HB_Init();
  16.         HTS221_Init();
  17.         #ifdef LORA_REPORT  Gui_DrawFont_GBK16(5,35,RED,BLACK,"LoRa Linking...");
  18.         rhf76_lora_init(HAL_UART_PORT_1);
  19.         tos_lora_module_recvcb_register(recv_callback);  
  20.         if(tos_lora_module_join_otaa("8cf957200000f87e", "8cf957200000f87e9239aaaaad204a72")==-1)
  21.            {         
  22.                 Gui_DrawFont_GBK16(5,35,RED,BLACK,  " Link GW Error!   ");
  23.                 report_period=60;      
  24.            }   
  25.         else
  26.            {         
  27.                 Gui_DrawFont_GBK16(5,35,GREEN,BLACK," Link GW OK!     ");
  28.            }   
  29.         Gui_DrawFont_GBK16(5,125,RED,BLACK,"LED:OFF        ");
  30.         Gui_DrawFont_GBK16(5,140,RED,BLACK,"Motor:OFF  ");
  31.         #endif   example_main_one_shot_lps22hb();
  32.         first_pressure=pressure_hPa;
  33.         first_height=height;
  34.         while (1)   
  35.            {
  36.                 printf("------LoRawan sensor board data------\n");
  37.                 Gui_DrawFont_GBK16(5,50,YELLOW,BLACK,"---------------");
  38.                 example_main_one_shot_lps22hb();
  39.                 HTS221_Get_Temperature(&temperature);
  40.                 HTS221_Get_Humidity(&report_humidity);
  41.                 report_temperature= temperature_degC;
  42.                 report_height=(height-first_height)*1000;
  43.                 if(pressure_hPa<0)      
  44.                 report_pressure=-pressure_hPa*100;
  45.                 else      
  46.                 report_pressure=pressure_hPa*100;
  47.                 sprintf(temp,"Temp:%2.1f   ",report_temperature);
  48.                 Gui_DrawFont_GBK16(5,65,WHITE,BLACK,temp);
  49.                 sprintf(temp,"Humi:%2.1f     ", report_humidity / 10.0);
  50.                 Gui_DrawFont_GBK16(5,80,WHITE,BLACK,temp);
  51.                 sprintf(temp,"Pa:%2.2f    ",pressure_hPa);
  52.                 Gui_DrawFont_GBK16(5,95,WHITE,BLACK,temp);
  53.                 sprintf(temp,"Height:%d    ", (int)report_height);
  54.                 Gui_DrawFont_GBK16(5,110,WHITE,BLACK,temp);
  55.                 printf("LPS22HB_pressure[hPa]:%0.2f,height[mm]:%d\r\n", pressure_hPa,(int)report_height);
  56.                 printf("LPS22HB_temperature [degC]:%0.2f\r\n",report_temperature);
  57.                 printf("HTS221_temperature : %2.1f\n", temperature/10.0);
  58.                 printf("HTS221_humidity    : %2.1f\n", report_humidity / 10.0);
  59.                 sum1++;
  60.                 printf("sum:%d\r\n",sum1);
  61.                 tos_task_delay(500);
  62.                 #ifdef LORA_REPORT       if(sum1>=report_period)
  63.                     {                        
  64.                         sum1=0;
  65.                         quantity++;  
  66.                         printf("quantity    : %d\n", quantity);
  67.                         printf("LED_Status  : %d\n",report_power_switch);
  68.                         printf("motor_Status: %d\n",report_motor_fan);
  69.                         dev_data_wrapper.u.dev_data.temperature = report_temperature;
  70.                         dev_data_wrapper.u.dev_data.humidity    = report_humidity / 10;
  71.                         dev_data_wrapper.u.dev_data.period      = report_period;
  72.                         dev_data_wrapper.u.dev_data.quantity    = quantity;
  73.                         dev_data_wrapper.u.dev_data.power_switch= report_power_switch;
  74.                         dev_data_wrapper.u.dev_data.motor_fan   = report_motor_fan;
  75.                         dev_data_wrapper.u.dev_data.pressure    = report_pressure;
  76.                         dev_data_wrapper.u.dev_data.height      = report_height;  
  77.                         tos_lora_module_send(dev_data_wrapper.u.serialize, sizeof(dev_data_t));
  78.                     }   
  79.               #endif     
  80.          }
  81.     }
(2)云平台实现 登陆iotexplorer平台即可快速创建LoRa项目
up-5e844f7b2fe0cb8ecc672f6a435f5e00ad5.jpeg
建立项目

up-57a7fed63f9ebae4321db1066a583512296.jpeg
定义属性

up-fe86eca450ae1039e2a134fdab6423c846d.jpeg
数据解析

up-ea0364e6c2dd7b55440b73142fb10d93fb1.jpeg
实时查看数据变化

  (3)微信小程序实现
小程序集成了LoRa设备和NB设备,这样就可以切换页面查看并控制不同终端设备。这个小程序是通过官方提供demo进行二次开发的。

up-579d8a7ccbc5cff7b18a63e0d9f32a567cf.jpeg
小程序主页面

up-fd69a24c961ae53d8fb5c94ec27d7084dee.png
LoRa模组小程序页面

up-20162b2f75e1e86d323f44ce3e822fbf0c1.jpeg
NB模组小程序页面

  四、总结
  
利用LoRa极强的穿透力,实现对楼层中的机房环境进行监控。它安装方便,节省了人力物力和财力。所以LoRa物联网应用于机房或建设智慧建筑有先天的优势。如果使用NB模组,每年都要换卡或者缴费。使用WIFI设备呢,穿透力不够强,距离没有LoRa传输得远。

本案例需要增强改进的地方是,检测机房断路器通断,交流接触器动作等器件的工作状态。也可以直接和PLC控制器进行通讯,直接读取PLC采集的数据,然后通过LoRa上传数据。

参考资料:
[1] 基于 TencentOS tiny 的 LoRaWAN 开发入门指南:
https://github.com/Tencent/TencentOS-tiny/blob/master/doc/16.TencentOS_tiny_LoRaWAN_Getting_Started_Guide.md

[2] LoRa 温湿度传感器接入指引:
https://cloud.tencent.com/document/product/1081/41112?from=10680

[3] LoRa节点源码和微信小程序源码及PPT链接:
https://share.weiyun.com/5u2vejl 密码:8szgyr  

[4] 源码说明:源码中需要搭配TencentOS tiny的框架,只需要覆盖原来原来的lorawan案例的源码和rhf76的头文件。