在LoRa实验练习1中,我们实现了NS的搭建以及使用AT命令配置LoRa节点的工作模式,在不同模式下与Lora网关进行上下行通信,接下来就要开始对Lorawan 基础通信实验和传感器采集和控制进行实验。
在这个实验中,我们通过程序设置LoRa 节点 OTAA 自动入网,10S 间隔,按确认上行方式发送固定数据包。
在这个实验中,实现的功能为OTAA 自动入网,10S 间隔,按确认上行方式发送数据,因此需要在程序中就已经写好DevEUI等参数信息,这些配置参数都在头文件“Comissioning.h”该头文件定义了 lorawan 的基础配置参数,如下:
因此修改DevEUI是修改程序中LORAWAN_DEVICE_EUI的参数:
程序的烧录可以参考NB-IOT中的程序烧录,在烧录完成后,我们进入NS按照之前修改程序中的参数进行OTAA的节点的添加。
在文件“Comissioning.h”中查看 DevEUI、AppEUI、AppKey 参数,并填入到相关位置。
最后打开串口调试助手,复位 LORA 节点后可以看到串口调试助手显示如下界面则表示节点入网成功。
在NS页面中的Received Frames也可以看到间隔固定10S会接收到一串16进制数据
在实现LoRa节点OTAA自动入网并定时传递数据后,我们就可以加入对应的传感器模块实现传感数据的定时传输更新,同时也可以通过下行发送控制命令对执行器进行响应的控制。为了节省篇幅,这里具体介绍一下温度、光敏、蜂鸣器模块,因为这个模块包含了数据的上传以及控制命令的下发。
为了实验的便利,该实验平台所提供的传感器采集和控制代码与NB-IOT中的传感器采集及控制实验中的代码相似,为每种传感器单独设置“.c”文件,对每种传感器进行底层硬件操作,如传感器类型识别,GPIO 初始化,IIC 操作等,同时为所有传感器操作建立结构体,位于“board.h”,便于模块插拔后实现传感器模块的类型自动识别,对所有传感器的功能函数统一写在了“sensor_main.c”中可以更加直观的看到各传感器模块的操作代码。
在“board.h”建立了所有传感器操作的结构体,可以规范且清晰的对每个传感器模块的各相参数进行配置和初始化,对应的结构体和实际定义的结构体变量如下两段代码所示。
typedef struct
{
uint16_t id; //传感器类型序号
uint16_t boardpa4voltage; //PA4引脚电压值
uint16_t boardpa6voltage; //PA6引脚电压值
char *boardtype; //传感器类型名
void (*func) (void); //传感器处理函数
}Sensortype_t;
Sensortype_t sensortype[SENSORTYPE_NUM] =
{
{1, 0 , 290, " 8x8Led ", Led8x8_main},
{2, 0 , 2200, " Infrared ", Infrared_main},
{3, 0 , 540, " 4Led ", Led4_main},
{4, 0 , 1620, " 2Realy ", Realy2_main},
{5, 3300, 1620, " Hall ", Hall_main},
{6, 3300, 1920, " LED-Light ", Ledlight_main},
{7, 0 , 1035, "BEEP-POR-TEM", BeepPortemp_main},
{8, 0 , 2440, " Shack ", Shack_main},
{9, 3300, 2245, " Flame ", Flame_main},
{10, 0 , 1310, " Pressure ", Pressure_main},
{11, 0 , 800, " Humidity ", SHT10_main},
{12, 3300, 290, " Gas ", Gas_main},
{13, 0 , 2690, " Acc ", Acc_main},
{14, 3300, 1035, " Ulrasonic ", Ultrasonic_main}
};
可以看到使用结构体定义的变量结构更加清晰(谁说面向过程的语言做不到类似对象实例化的操作)
在NB-IOT的实验中我们介绍过,对于每一个传感器模块都设计有一个对应的分压电路,通过不同电阻的组合,每个传感器模块都会有其独一无二的分压值,因此实现传感器类型的识别,就只需要MCU采集一下分压电路的电压即可,因此使用ADC采集函数BoardPAXMeasureVolage 采集PA4 PA6引脚电压,然后不断的和结构体变量sensortype中预存值做对比,对比成功后break跳出循环,从而得到传感器类型序号。
循环判断代码如下:
while(adccnt++ < 10)
{
adctotal1 += BoardPAXMeasureVolage(&hadc1, ADC_CHANNEL_4);
adctotal2 += BoardPAXMeasureVolage(&hadc1, ADC_CHANNEL_6);
}
BoardPA4Voltage = adctotal1 / 10;
BoardPA6Voltage = adctotal2 / 10;
{
uint8_t i = 0;
for(i = 0; i < SENSORTYPE_NUM; i++)
{
if(sensortype[i].boardpa4voltage == 0)
{
if((BoardPA4Voltage < sensortype[i].boardpa4voltage + 200) &&
(BoardPA6Voltage > sensortype[i].boardpa6voltage - 200) &&
(BoardPA6Voltage < sensortype[i].boardpa6voltage + 200))
{
break;
}
}
else if(sensortype[i].boardpa4voltage == 3300)
{
if((BoardPA4Voltage > sensortype[i].boardpa4voltage - 200) &&
(BoardPA6Voltage > sensortype[i].boardpa6voltage - 200) &&
(BoardPA6Voltage < sensortype[i].boardpa6voltage + 200))
{
break;
}
}
}
在获得传感器类型后,需要周期性的对插入的传感器进行数据处理。
数据处理一共分为两个部分:
//2.sensor setparam
switch( sensortype_index )
{
case 0:
p=strstr((char*)temp,":");
tempd[0]=*(p+1)-'0';
(*sensortype[sensortype_index].func_setparam)(&tempd[0]);
break;
case 1:
//Infrared no download data
break;
case 2:
p=strstr((char*)temp,":");
tempd[0]=*(p+1)-'0';
tempd[1]=*(p+2)-'0';
tempd[2]=*(p+3)-'0';
tempd[3]=*(p+4)-'0';
(*sensortype[sensortype_index].func_setparam)(&tempd);
break;
case 3:
p=strstr((char*)temp,":");
tempd[0]=*(p+1)-'0';
p=p+1;
if(p1=strstr((char*)p,":"))
{
tempd[1]=*(p1+1)-'0';
(*sensortype[sensortype_index].func_setparam)(&tempd);
}
break;
case 4:
//Hall no download data
break;
case 5:
p=strstr((char*)temp,":");
tempd[0]=*(p+1)-'0';
tempd[1]=*(p+2)-'0';
tempd[2]=*(p+3)-'0';
(*sensortype[sensortype_index].func_setparam)(&tempd);
break;
case 6:
p=strstr((char*)temp,":");
tempd[0]=*(p+1)-'0';
(*sensortype[sensortype_index].func_setparam)(&tempd);
break;
case 7:
//shack no download data
break;
case 8:
//flame no download data
break;
case 9:
//pressure no download data
break;
case 10:
//sht10 no download data
break;
case 11:
//gas no download data
break;
case 12:
//acc no download data
break;
case 13:
//Ultrasonic no download data
break;
default:
break;
}
}
}
其中需要注意的是,因为在传感器采集的代码中约定LoRa的协议工作与CLASS C模式下,在CLASS C模式下类似继电器模块这种没有上传的传感器模块为了保证通信的完成在继电器模块入网后需要上传固定的数据以保证通信的顺利完成。
对继电器模块的操作代码如下:
if(needsendsensor==0)
PrepareTxFrame( AppPort );
NextTx = SendFrame( );
和1.2章节类似,在本环节的代码中也有“Comissioning.h”用于存储和管理DevEUI、 AppEUI 、AppKey等用于完成节点创建的参数。将“Comissioning.h”获取的参数填写到对应的位置。
我们插入温度、光敏、蜂鸣器传感器模块并烧录程序复位后,传感器节点显示如下:
同时我们打开Websocket,输入/ws/uplink/对应DevEUI号,使用JSON格式连接后,串口调试助手和网页显示如下:
在接收到的数据中为对应的温度数据和光敏数据
接下来就是对蜂鸣器的控制操作,通过JSON数据格式编写的命令:
{“data”:“7b226c6f42656570506f7274656d705f42223a317d”,“port”:45,“time”:“immediately”}
可以打开蜂鸣器
关闭蜂鸣器的JSON格式数据命令:
{“data”:“7b226c6f42656570506f7274656d705f42223a307d”,“port”:45,“time”:“immediately”}
与温度、光敏、蜂鸣器模块相关的功能函数代码段如下:
//7.BeepPortemp
extern Adc_t Adc_pa0;
extern Gpio_t beep1;
extern void BeepPortempInit(void);
static uint8_t BeepPortemponoff=0;
static uint8_t BeepPortempdoactive=0;
extern uint8_t BeepPortemp[4];
void BeepPortemp_init(void)
{
BeepPortempInit();
}
void BeepPortemp_setparam(void *pbeep)
{
BeepPortemponoff=*(unsigned char *)pbeep;
combuflen=sprintf((char*)combuf,"BeepPortemp:%s\r\n\r\n",
BeepPortemponoff?"on":"off");
UartPutBuffer(&Uart1,combuf,combuflen);
BeepPortempdoactive=1;
}
void BeepPortemp_task(void)
{
int8 temperature;
int16 light;
char tempBuf[12];
static uint16_t count1=0;
static uint32_t count2=200000;
{
if( NextTx == true )
{
KeyScan();
DelayMs(10);
}
if(KeyValue!=HAL_KEY_NULL)
{
GpioToggle( &beep1);
}
if(BeepPortempdoactive==1)
{
BeepPortempdoactive=0;
GpioWrite( &beep1, BeepPortemponoff?0:1 );
}
//采集温度
temperature = ReadTc77Temp();
//采集光敏电阻
light = AdcReadChannel(&Adc_pa0,ADC_CHANNEL_0);
if(count1++>1000)
{
count1=0;
sprintf( (void *)tempBuf,"T:%2d L:%4d ",temperature,light);
LcdPutString16_8(0,0,(uint8_t*)tempBuf,12,1);//CENTER
}
if(count2++>20000)
{
count2=0;
memcpy(BeepPortemp+1,&temperature,1);
memcpy(BeepPortemp+2,&light,2);
BeepPortemp[0]=1;
}
}
}
使用LoRaWAN进行基础通信实验以及传感器采集和控制的实验就讲到这里,下一篇进行