相关信息
设备接入方式:CoAP非加密方式
应用开发
串口助手AT命令对接平台
这部分内容是通过电脑串口调试软件,手动发AT指令,直接发给NB-IoT模块。NB模块返回的信息,直接到电脑串口调试软件。一步一步动手操作,实现连接OceanConnect平台。采用的通讯协议是CoAP协议。
Step 1配置对接平台地址
指令:AT+NCDP=<ip_addr>[,<port>]
<ip_addr>:填写平台设备接入地址
<port>:填写接入方式的端口号
例:AT+NCDP= 139.159.140.34,5683
Step 2软件重启模组
指令:AT+NRB//配置完配置对接平台地址后需要重启模块才能生效
REBOOTING
0???
REBOOT_CAUSE_APPLICATION_AT
Neul
OK
Step 3检查是否入网成功
指令:AT+CGATT?
+CGATT:1 //返回1表示入网成功,返回0入网失败,300s后依然为0,则重启模组
OK
Step 4发送数据
指令:AT+NMGS=<length>,<data>
<length>:数据长度
<data>:数据(十六进制格式)
例:AT+NMGS=8,31322E3132332E34 //上报数据
OK
LiteOS API方式接入
这部分的通过单片机搭载华为LiteOS操作系统,并移植了可兼容所有AT指令型的AT框架程序,调用AT框架的API接口实现快速连接华为OceanConnect平台。并能实时接收平台下发的命令,实现对设备的控制,以下讲解调用API实现的方式。
Step 1分析主程序
int main(void){UINT32 uwRet = LOS_OK;HardWare_Init();uwRet = LOS_KernelInit();if (uwRet != LOS_OK){return LOS_NOK;}uwRet = creat_main_task();if (uwRet != LOS_OK){return LOS_NOK;}LOS_Start();}
主程序主要包括初始化硬件外设、初始化内核、创建传感器数据采集任务、创建数据上报任务,接下来讲详细讲解主要部分的实现方式。
Step 2初始化硬件
VOID HardWare_Init(VOID){HAL_Init();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */dwt_delay_init(SystemCoreClock);MX_GPIO_Init();MX_USART1_UART_Init();printf("Welcome to IoT-Club, This is EVB-M1 Board.\r\n");}
首先调用 HAL_Init() 初始化HAL库;SystemClock_Config(),用于系统时钟的配置;再调用MX_GPIO_Init()
初始化相应的GPIO;最后初始化单片机debug串口。
Step 3创建传感器采集任务
VOID data_collection_task(VOID){UINT32 uwRet = LOS_OK;DHT11_Init();//初始化传感器while (1){printf("This is data_collection_task !\r\n");if(DHT11_Read_TempAndHumidity(&DHT11_Data)==SUCCESS){printf("读取DHT11成功!-->湿度为%.1f %RH ,温度为 %.1f℃ \n",DHT11_Data.humidity,DHT11_Data.temperature);}else{printf("读取DHT11信息失败\n");DHT11_Init();}sprintf(DHT11_send.temp, "%.1f", DHT11_Data.temperature);sprintf(DHT11_send.hum, "%.1f", DHT11_Data.humidity);uwRet=LOS_TaskDelay(2000);if(uwRet !=LOS_OK)return;}}
这部分为传感器数据采集部分,首先是通过DHT11_Init();函数初始化温湿度传感器DHT11所对应的单片机管脚及传感器。
/*** 函数功能: DHT11 初始化函数* 输入参数: 无* 返 回 值: 无* 说 明:无*/void DHT11_Init ( void ){DHT11_Dout_GPIO_CLK_ENABLE();DHT11_Mode_Out_PP();DHT11_Dout_HIGH(); // 拉高GPIO}
通过 DHT11_Read_TempAndHumidity函数采集传感器数据
/*** 函数功能: 一次完整的数据传输为40bit,高位先出* 输入参数: DHT11_Data:DHT11数据类型* 返 回 值: ERROR: 读取出错* SUCCESS:读取成功* 说 明:8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和 */uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data){ uint8_t temp;uint16_t humi_temp;/*输出模式*/DHT11_Mode_Out_PP();/*主机拉低*/DHT11_Dout_LOW();/*延时18ms*/Delay_ms(18);/*总线拉高 主机延时30us*/DHT11_Dout_HIGH(); DHT11_Delay(30); //延时30us/*主机设为输入 判断从机响应信号*/ DHT11_Mode_IPU();/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/ if(DHT11_Data_IN()==GPIO_PIN_RESET){/*轮询直到从机发出 的80us 低电平 响应信号结束*/ while(DHT11_Data_IN()==GPIO_PIN_RESET);/*轮询直到从机发出的 80us 高电平 标置信号结束*/while(DHT11_Data_IN()==GPIO_PIN_SET);/*开始接收数据*/ DHT11_Data->humi_high8bit= DHT11_ReadByte();DHT11_Data->humi_low8bit = DHT11_ReadByte();DHT11_Data->temp_high8bit= DHT11_ReadByte();DHT11_Data->temp_low8bit = DHT11_ReadByte();DHT11_Data->check_sum = DHT11_ReadByte();/*读取结束,引脚改为输出模式*/DHT11_Mode_Out_PP();/*主机拉高*/DHT11_Dout_HIGH();/* 对数据进行处理 */humi_temp=DHT11_Data->humi_high8bit*100+DHT11_Data->humi_low8bit;DHT11_Data->humidity =(float)humi_temp/100;humi_temp=DHT11_Data->temp_high8bit*100+DHT11_Data->temp_low8bit;DHT11_Data->temperature=(float)humi_temp/100; /*检查读取的数据是否正确*/temp = DHT11_Data->humi_high8bit + DHT11_Data->humi_low8bit + DHT11_Data->temp_high8bit+ DHT11_Data->temp_low8bit;if(DHT11_Data->check_sum==temp){ return SUCCESS;}else return ERROR;}elsereturn ERROR;}
最后将传感器数据以字符串的形式存入到数组中,用于发送到平台上。
sprintf(DHT11_send.temp, "%.1f", DHT11_Data.temperature);sprintf(DHT11_send.hum, "%.1f", DHT11_Data.humidity);
Step 3创建数据上报任务
VOID data_report_task(VOID){UINT32 uwRet = LOS_OK;#define AT_DTLS 0#if AT_DTLSsec_param_s sec;sec.pskid = "868744031131026";sec.psk = "d1e1be0c05ac5b8c78ce196412f0cdb0";#endifprintf("\r\n=====================================================");printf("\r\nSTEP1: Init NB Module( NB Init )");printf("\r\n=====================================================\r\n");#if AT_DTLSlos_nb_init((const int8_t*)"180.101.147.115",(const int8_t*)"5684",&sec);#elselos_nb_init((const int8_t*)"180.101.147.115",(const int8_t*)"5683",NULL);#endifprintf("\r\n=====================================================");printf("\r\nSTEP2: Register Command( NB Notify )");printf("\r\n=====================================================\r\n");los_nb_notify("+NNMI:",strlen("+NNMI:"),nb_cmd_data_ioctl,OC_cmd_match);LOS_TaskDelay(3000);printf("\r\n=====================================================");printf("\r\nSTEP3: Report Data to Server( NB Report )");printf("\r\n=====================================================\r\n");while(1){if(los_nb_report((const char*)(&DHT11_send),sizeof(DHT11_send))>=0) //发送数据到平台 printf("ocean_send_data OK!\n");//发生成功else //发送失败{printf("ocean_send_data Fail!\n");}uwRet=LOS_TaskDelay(1000);if(uwRet !=LOS_OK)return;}}
这部分程序主要包括连接平台、注册下发命令的回调函数以及上报数据到平台,以下将讲解如何实现这三部分。
连接平台
连接平台的方式分为加密和非加密两种,通过#define AT_DTLS来实现连接方式的选择,定义为0时为非加密,此教程以非加密为例讲解,实现程序如下
#define AT_DTLS 0#if AT_DTLSsec_param_s sec;sec.pskid = "868744031131026";sec.psk = "d1e1be0c05ac5b8c78ce196412f0cdb0";#endifprintf("\r\n=====================================================");printf("\r\nSTEP1: Init NB Module( NB Init )");printf("\r\n=====================================================\r\n");#if AT_DTLSlos_nb_init((const int8_t*)"139.159.140.34",(const int8_t*)"5684",&sec);#elselos_nb_init((const int8_t*)"139.159.140.34",(const int8_t*)"5683",NULL);#endif
此部分通调用los_nb_init函数初始化模组,等待连接网络,设置平台对接地址实现对接平台。三个参数分别为设备对接IP、设备对接端口以及加密的PSK码。【注意】不同平台的设备对接地址不同,请以自己平台为准
int los_nb_init(const int8_t* host, const int8_t* port, sec_param_s* psk){int ret;int timecnt = 0;//if(port == NULL)//return -1;at.init();nb_reboot();LOS_TaskDelay(2000);if(psk != NULL)//encryption v1.9{if(psk->setpsk)nb_send_psk(psk->pskid, psk->psk);elsenb_set_no_encrypt();}while(1){ret = nb_hw_detect();printf("call nb_hw_detect,ret is %d\n",ret);if(ret == AT_OK)break;//LOS_TaskDelay(1000);}//nb_get_auto_connect();//nb_connect(NULL, NULL, NULL);while(timecnt < 120){ret = nb_get_netstat();nb_check_csq();if(ret != AT_FAILED){ret = nb_query_ip();break;}//LOS_TaskDelay(1000);timecnt++;}if(ret != AT_FAILED){nb_query_ip();}ret = nb_set_cdpserver((char *)host, (char *)port);return ret;}
注册下发命令回调函数
注册下发命令回调函数可实现对平台下发命令的快速相应并处理
los_nb_notify("+NNMI:",strlen("+NNMI:"),nb_cmd_data_ioctl,OC_cmd_match);
第一个参数为要匹配数据字段,第二个参数为匹配字段的长度、第三个参数为命令回调的执行函数、第四个为注册字段的匹配函数。
int32_t nb_cmd_data_ioctl(void* arg, int8_t * buf, int32_t len){int readlen = 0;char tmpbuf[1064] = {0};if (NULL == buf || len <= 0){AT_LOG("param invailed!");return -1;}sscanf((char *)buf,"\r\n+NNMI:%d,%s\r\n",&readlen,tmpbuf);memset(_nmgr, 0, 30);if (readlen > 0){HexStrToStr(tmpbuf, _nmgr,readlen*2);}AT_LOG("cmd is:%s\n",_nmgr);if(strcmp(_nmgr,"ON")==0){HAL_GPIO_WritePin(Light_GPIO_Port,Light_Pin,GPIO_PIN_RESET);}if(strcmp(_nmgr,"OFF")==0){HAL_GPIO_WritePin(Light_GPIO_Port,Light_Pin,GPIO_PIN_SET);}/*******************************END**********************************************/return 0;}
命令回调处理函数主要实现命令的解析以及实现控制相应的设备,此处以控制LED灯为例
int32_t OC_cmd_match(const char *buf, char* featurestr,int len){if(strstr(buf,featurestr) != NULL)return 0;elsereturn -1;}
注册字段匹配函数用于验证数据是否与注册字段匹配/
上报数据
while(1){if(los_nb_report((const char*)(&DHT11_send),sizeof(DHT11_send))>=0)//发送数据到平台printf("ocean_send_data OK!\n");//发生成功else //发送失败{printf("ocean_send_data Fail!\n"); }uwRet=LOS_TaskDelay(1000);if(uwRet !=LOS_OK)return;}
上报数据调用的API为los_nb_report填入的参数为数据与数据长度,这个函数里封装了发送coap消息的AT指令以及查询发送的消息量的AT指令
int32_t nb_send_payload(const char* buf, int len){char *cmd1 = "AT+NMGS=";char *cmd2 = "AT+NQMGS\r";int ret;char* str = NULL;int curcnt = 0;int rbuflen;static int sndcnt = 0;if(buf == NULL || len > AT_MAX_PAYLOADLEN){AT_LOG("payload too long");return -1;}memset(tmpbuf, 0, AT_DATA_LEN);memset(wbuf, 0, AT_DATA_LEN);str_to_hex(buf, len, tmpbuf);memset(rbuf, 0, AT_DATA_LEN);snprintf(wbuf, AT_DATA_LEN,"%s%d,%s%c",cmd1,(int)len,tmpbuf,'\r');ret = at.cmd((int8_t*)wbuf, strlen(wbuf), "OK", NULL,NULL);if(ret < 0)return -1;ret = at.cmd((int8_t*)cmd2, strlen(cmd2), "SENT=", rbuf,&rbuflen);if(ret < 0)return -1;str = strstr(rbuf,"SENT=");if(str == NULL)return -1;sscanf(str,"SENT=%d,%s",&curcnt,wbuf);if(curcnt == sndcnt)return -1;sndcnt = curcnt;return ret;}