NB-IoT(8)---网络编程

NB-IoT网络编程

 

NB-IoT驱动开发一

1、驱动框架设计

2、AT指令发送

3、AT指令接收

驱动框架设计

数据结构设计

如何用数据结构来完成AT指令的发送、应答、超时、状态、重发:(新建 nbiotdriver.h 和 nbiotdriver.c 文件用于AT相关)

发送->其实就是发送字符串“AT\r\n”

解析->其实就是接收字符串“OK”

超时->其实就是超时时间

状态->其实就是 成功,超时,未收到

重发->其实就是 重发次数

 
  1. typedef enum //枚举类型:接收的状态

  2. {

  3. SUCCESS_REC = 0, //成功

  4. TIME_OUT, //超时

  5. NO_REC //未收到

  6. }teATStatus;

  7.  
  8. typedef struct //定义的数据结构,用数据结构来完成AT指令的发送、应答、超时、状态、重发

  9. {

  10. char *ATSendStr; //向NB-IOT发送字符串(AT命令)

  11. char *ATRecStr; //NB-IOT返回给MCU的字符串

  12. uint16_t TimeOut; //设置超时

  13. teATStatus ATStatus; //接收状态

  14. uint8_t RtyNum; //重发次数

  15. }tsATCmds;

AT指令

AT+CFUN=0

AT+CGSN=1

AT+NRB

AT+NCDP=180.101.147.115,5683

AT+CFUN=1

AT+CIMI

AT+CMEE=1

AT+CGDCONT=1,“IP”,“ctnb”

AT+NNMI=1

AT+CGATT=1

AT+CGPADDR

 
  1. tsATCmds ATCmds[] =

  2. {

  3. {"AT+CFUN=0\r\n","OK",2000,NO_REC,3},

  4. {"AT+CGSN=1\r\n","OK",2000,NO_REC,3},

  5. {"AT+NRB\r\n","OK",8000,NO_REC,3},//重启使用时间比较长,所以这里设置为8秒钟

  6. {"AT+NCDP=180.101.147.115,5683\r\n","OK",2000,NO_REC,3},

  7. {"AT+CFUN=1\r\n","OK",2000,NO_REC,3},

  8. {"AT+CIMI\r\n","OK",2000,NO_REC,3},

  9. {"AT+CMEE=1\r\n","OK",2000,NO_REC,3},

  10. {"AT+CGDCONT=1,\"IP\",\"ctnb\"\r\n","OK",2000,NO_REC,3},//注意AT命令中字符串的书写格式:\"IP\",\"ctnb\"(表示电信)

  11. {"AT+NNMI=1\r\n","OK",2000,NO_REC,3},

  12. {"AT+CGATT=1\r\n","OK",2000,NO_REC,3},

  13. {"AT+CGPADDR\r\n","+CGPADDR:1,1",2000,NO_REC,30},

  14. {"AT+NMGS=","OK",3000,NO_REC,3},

  15. };

AT指令发送

 
  1. typedef enum //枚举类型,用于与上方的数组 ATCmds 下标相对应

  2. {

  3. AT_CFUN0 = 0, //ATCmds[AT_CFUN0] 就是 ATCmds[0],代表数据结构 {"AT+CFUN=0\r\n","OK",2000,NO_REC,3} 以下类推

  4. AT_CGSN,

  5. AT_NRB,

  6. AT_NCDP,

  7. AT_CFUN1,

  8. AT_CIMI,

  9. AT_CMEE,

  10. AT_CGDCONT,

  11. AT_NNMI,

  12. AT_CGATT,

  13. AT_CGPADDR,

  14. AT_NMGS,

  15. AT_IDIE

  16. }teATCmdNum;

 
  1. #include "time.h"

  2. #include "led.h"

  3.  
  4. static tsTimeType TimeNB;//获取定时器的起始时间和时间间隔,具体见下面讲解

  5.  
  6. void ATSend(teATCmdNum ATCmdNum)

  7. {

  8. //清空接收缓存区

  9. memset(Usart2type.Usart2RecBuff,0,USART2_REC_SIZE);

  10. ATCmds[ATCmdNum].ATStatus = NO_REC;

  11.  
  12. ATRecCmdNum = ATCmdNum;

  13. if(ATCmdNum == AT_NMGS)

  14. {

  15. memset(NbSendData,0,100);

  16.  
  17. sprintf(NbSendData,"%s%d,%x%x\r\n",ATCmds[ATCmdNum].ATSendStr,2,0x10,0x10);

  18. HAL_UART_Transmit(&huart2,(uint8_t*)NbSendData,strlen(NbSendData),100);

  19. HAL_UART_Transmit(&huart1,(uint8_t*)NbSendData,strlen(NbSendData),100);

  20. }

  21. else

  22. {

  23. HAL_UART_Transmit(&huart2,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),100);

  24. HAL_UART_Transmit(&huart1,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),100);

  25. }

  26. //打开超时定时器,这里主要用来判断接收超时使用

  27. SetTime(&TimeNB,ATCmds[ATCmdNum].TimeOut);//获取定时器的起始时间和时间间隔,具体见下面讲解

  28.  
  29. //打开发送指示灯,配合LedTask函数的使用可以产生一个100毫秒的亮灯,具体函数下文有讲解

  30. //如果100毫秒之内又有数据发送,则定时器重新计时,LED灯继续延长点亮时间

  31. SetLedRun(LED_TX);

  32. }

AT指令接收

串口接收

串口回调函数

AT指令解析

 

 
  1. #define USART2_DMA_REC_SIZE 256

  2. #define USART2_REC_SIZE 1024

  3. typedef struct //用来处理DMA接收到的数据,解析缓存区的数据

  4. {

  5. uint8_t Usart2RecFlag;//数据接收到的标志位

  6. uint16_t Usart2DMARecLen;//获取接收DMA数据的长度

  7. uint16_t Usart2RecLen;//获取解析缓存区的长度

  8. uint8_t Usart2DMARecBuff[USART2_DMA_REC_SIZE];//DMA的缓冲区

  9. uint8_t Usart2RecBuff[USART2_REC_SIZE]; //用于解析接收数据的缓存区

  10. }tsUsart2type;

  11.  
  12. extern tsUsart2type Usart2type;//该变量在uart.c中声明,但是在其他文件中需要使用,所以需要外部声明

 
  1. //uint8_t Usart1Rx = 0;

  2. //uint8_t Usart2Rx = 0;

  3.  
  4. // __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);//打开UART1接收中断(接收寄存器不为空则产生中断)

  5. //

  6. // HAL_UART_Receive_IT(&huart1, &Usart1Rx, 1);//UART1接收使能,Usart1Rx:接收数据的缓存区

  7. //

  8. // __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);//打开UART2接收中断(接收寄存器不为空则产生中断)

  9. //

  10. // HAL_UART_Receive_IT(&huart2, &Usart2Rx, 1);//UART2接收使能,Usart2Rx:接收数据的缓存区

 
  1. uint8_t Usart1Rx = 0;

  2.  
  3. tsUsart2type Usart2type;//该数据类型用来设置DMA的缓冲区和解析接收数据的缓冲区,上面已经给出具体定义

  4.  
  5. void EnableUartIT(void)

  6. {

  7. //串口1

  8. __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);//打开UART1接收中断(接收寄存器不为空则产生中断)

  9.  
  10. HAL_UART_Receive_IT(&huart1, &Usart1Rx, 1);//UART1接收使能,Usart1Rx:接收数据的缓存区

  11.  
  12. //串口2

  13. //串口空闲中断:当连续接收字符时不会产生中断,但是如果中途出现一个字节的空闲则就产生中断(避免每接收一个字节就出现一次中断)

  14. __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//需要注意:每次设备一上电就会产生一次空闲中断

  15.  
  16. __HAL_UART_CLEAR_IDLEFLAG(&huart2);//清除UART2的空闲中断标志

  17. HAL_UART_Receive_DMA(&huart2,Usart2type.Usart2DMARecBuff,USART2_DMA_REC_SIZE);//开启DMA的接收

  18. /*(以上三行代码功能:将uart2接收到的数据通过DMA传递给Usart2type.Usart2DMARecBuff,然后产生串口空闲中断,

  19. 在中断中做进一步处理)*/

  20. }

extern void EnableUartIT(void);

 

EnableUartIT();

 

 

 
  1. extern DMA_HandleTypeDef hdma_usart2_rx;/*hdma_usart2_rx在CubeMX中添加UART2的DMA时创建的一个变量,在uart.c中定义,

  2. 所以需要外部声明之后才能在该文件中使用*/

  3.  
  4. void USART2_IRQHandler(void)

  5. {

  6. uint32_t temp;

  7.  
  8. if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) == SET)//判断UART2是否为空闲中断

  9. {

  10. __HAL_UART_CLEAR_IDLEFLAG(&huart2);//清除空闲中断标志位

  11. HAL_UART_DMAStop(&huart2);//停止DMA接收数据

  12. temp = huart2.Instance->ISR;

  13. temp = huart2.Instance->RDR;//以上两行代码用于清除DMA的接收中断(只需要读取一次ISR和RDR寄存器的值)

  14. temp = USART2_DMA_REC_SIZE - hdma_usart2_rx.Instance->CNDTR;/*CNDTR为DMA通道接收数据的计数器(注意是一个递减计数器,

  15. 所以需要将DMA的缓存区的总长度减去该计数器的值才是DMA通道接收数据的长度)*/

  16. Usart2type.Usart2DMARecLen = temp;//将DMA接收数据的长度赋值给上面定义的结构体变量

  17. HAL_UART_RxCpltCallback(&huart2);//串口中断的回调函数,具体函数见下方

  18. }

  19. HAL_UART_IRQHandler(&huart2);

  20. HAL_UART_Receive_DMA(&huart2,Usart2type.Usart2DMARecBuff,USART2_DMA_REC_SIZE);

  21. }

  22.  
  23.  
  24.  
  25. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //串口中断的回调函数

  26. {

  27. if(huart->Instance == USART2)

  28. {

  29. if(Usart2type.Usart2RecLen > 0)//判断解析缓存区是否有未处理的数据

  30. {

  31. memcpy(&Usart2type.Usart2RecBuff[Usart2type.Usart2RecLen],Usart2type.Usart2DMARecBuff,Usart2type.Usart2DMARecLen);

  32. Usart2type.Usart2RecLen += Usart2type.Usart2DMARecLen;

  33. }

  34. else

  35. {

  36. memcpy(Usart2type.Usart2RecBuff,Usart2type.Usart2DMARecBuff,Usart2type.Usart2DMARecLen);

  37. Usart2type.Usart2RecLen = Usart2type.Usart2DMARecLen;

  38. }

  39. memset(Usart2type.Usart2DMARecBuff,0,Usart2type.Usart2DMARecLen);//清空DMA的接收缓存区

  40. Usart2type.Usart2RecFlag = 1;//数据标志位的置位

  41. }

  42. }

 
  1. void ATRec(void)

  2. {

  3. if(Usart2type.Usart2RecFlag)//是否接收到一个完整的数据包

  4. {

  5. //判断解析缓存区中是否存在对应指令返回的正确参数(字符串),strstr的使用方法见下方

  6. if(strstr((const char*)Usart2type.Usart2RecBuff,ATCmds[ATRecCmdNum].ATRecStr) != NULL)

  7. {

  8. ATCmds[ATRecCmdNum].ATStatus = SUCCESS_REC;//接收状态赋值为成功

  9. }

  10.  
  11. SetLedRun(LED_RX);//打开接收指示灯,配合LedTask函数的使用可以产生一个100毫秒的亮灯,具体见下文

  12. HAL_UART_Transmit(&huart1,Usart2type.Usart2RecBuff,Usart2type.Usart2RecLen,100);//打印到串口

  13. Usart2type.Usart2RecFlag = 0;//清空标志位

  14. Usart2type.Usart2RecLen = 0;//设置解析缓存区字符串长度为0

  15. }

  16. }

  17.  
  18.  
  19.  
  20. strstr:

  21.  
  22. 原型:extern char *strstr(char *haystack, char *needle);

  23.  
  24. 用法:#include <string.h>

  25.  
  26. 功能:从字符串haystack中寻找needle第一次出现的位置(不比较结束符NULL)。

  27.  
  28. 说明:返回指向第一次出现needle位置的指针,如果没找到则返回NULL。

  29.  
  30.  
  31.  

NB-IoT驱动开发二

1、软件定时器设计

2、LED驱动设计

软件定时器设计

软件定时器需求

AT指令超时判断

定时采集传感器

定时上报数据

软件定时器设计:(新建两个文件 time.c 和 time.h 用于存储定时器相关)

设置定时器

比较定时器

参考HAL_Delay:

设置定时器

比较定时器

 
  1. #ifndef _TIME_H

  2. #define _TIME_H

  3.  
  4. #include "stm32f0xx.h"

  5.  
  6. typedef struct

  7. {

  8. uint32_t TimeStart;//获取起始时间

  9. uint32_t TimeInter;//间隔时间

  10. }tsTimeType;

  11.  
  12. void SetTime(tsTimeType *TimeType,uint32_t TimeInter);//打开超时定时器

  13. uint8_t CompareTime(tsTimeType *TimeType);//比较函数

  14. #endif

 
  1. #include "time.h"

  2.  
  3. void SetTime(tsTimeType *TimeType,uint32_t TimeInter)

  4. {

  5. TimeType->TimeStart = HAL_GetTick();//获取起始时间

  6. TimeType->TimeInter = TimeInter;//获取间隔时间

  7. }

  8. uint8_t CompareTime(tsTimeType *TimeType)//每隔1毫秒,计数器就会增加1

  9. {

  10. return ((HAL_GetTick()-TimeType->TimeStart) >= TimeType->TimeInter);

  11. }

LED驱动设计

LED需求

网络指示灯

接收指示灯

发送指示灯

LED设计:(新建两个文件 led.h 和 led.c 用于存储led相关)

LED打开

LED关闭

LED初始化

LED触发闪烁

LED闪烁任务

LED数据结构

LED数量(入网、发送、接收)

LED闪烁任务状态(运行、延时、停止)

LED GPIO封装(用数组表示LED IO信息)

LED打开/关闭/初始化

根据原理图,LED为低电平驱动,上电要全部关闭:

打开->HAL_GPIO_WritePin(X,X,RESET)

关闭-> HAL_GPIO_WritePin(X,X,SET)

LED触发闪烁

设置LED状态为运行

开启LED定时器

LED闪烁任务

 
  1. #ifndef _LED_H

  2. #define _LED_H

  3.  
  4. #include "stm32f0xx.h"

  5.  
  6. #define LED_NUMBER 3 //定义LED数量

  7.  
  8. typedef enum //枚举类型,LED对应功能

  9. {

  10. LED_NET = 0,

  11. LED_RX,

  12. LED_TX

  13. }teLedNums;

  14.  
  15. typedef enum//LED闪烁任务状态

  16. {

  17. LED_STOP = 0,

  18. LED_RUN,

  19. LED_DELAY

  20. }teLedTaskStatus;

  21.  
  22. void LedOn(teLedNums LedNums);//打开LED灯

  23. void LedOff(teLedNums LedNums);//关闭LED灯

  24. void LedInit(void);//LED灯的初始化

  25.  
  26. void SetLedRun(teLedNums LedNums);//设置LED为运行态

  27.  
  28. void LedTask(void);//指示灯如果在运行态在,则闪烁一次

  29.  
  30. #endif

 
  1. #include "led.h"

  2. #include "time.h"

  3.  
  4. const uint16_t LedPins[LED_NUMBER] =

  5. {

  6. GPIO_PIN_0, //对应LED_NET

  7. GPIO_PIN_1, //对应LED_RX

  8. GPIO_PIN_2 //对应LED_TX

  9. };

  10.  
  11.  
  12. static tsTimeType TimeLeds[LED_NUMBER];//获取起始时间和间隔时间

  13.  
  14. static teLedTaskStatus LedTaskStatus[LED_NUMBER];//LED任务状态

  15. void LedOn(teLedNums LedNums) //打开对应LED灯

  16. {

  17. HAL_GPIO_WritePin(GPIOB,LedPins[LedNums],GPIO_PIN_RESET);

  18. }

  19.  
  20. void LedOff(teLedNums LedNums) //关闭对应LED灯

  21. {

  22. HAL_GPIO_WritePin(GPIOB,LedPins[LedNums],GPIO_PIN_SET);

  23. }

  24.  
  25.  
  26. void LedInit(void)//LED灯初始化,关闭所有灯

  27. {

  28. int i;

  29. for(i = 0;i < LED_NUMBER;i++)

  30. {

  31. LedOff(i);

  32. }

  33. }

  34.  
  35. void SetLedRun(teLedNums LedNums)//设置对应LED为运行态

  36. {

  37. LedTaskStatus[LedNums] = LED_RUN;

  38. }

  39.  
  40.  
  41. void LedTask(void)//指示灯如果在运行态在,则闪烁一次

  42. {

  43. int i;

  44.  
  45. for(i = 0;i < LED_NUMBER;i++)

  46. {

  47. if(LedTaskStatus[i] == LED_RUN)

  48. {

  49. LedOn(i);

  50. SetTime(&TimeLeds[i],100);

  51. LedTaskStatus[i] = LED_DELAY;

  52. }

  53. else if(LedTaskStatus[i] == LED_DELAY)

  54. {

  55. if(CompareTime(&TimeLeds[i]))

  56. {

  57. LedOff(i);

  58. LedTaskStatus[i] = LED_STOP;

  59. }

  60. }

  61. }

  62. }

 

 
  1. #include "time.h"

  2. #include "led.h"

  3.  
  4. static tsTimeType TimeNB;//获取定时器的起始时间和时间间隔

  5.  
  6. void ATSend(teATCmdNum ATCmdNum)

  7. {

  8. //清空接收缓存区

  9. memset(Usart2type.Usart2RecBuff,0,USART2_REC_SIZE);

  10. ATCmds[ATCmdNum].ATStatus = NO_REC;

  11.  
  12. ATRecCmdNum = ATCmdNum;

  13. if(ATCmdNum == AT_NMGS)

  14. {

  15. memset(NbSendData,0,100);

  16.  
  17. sprintf(NbSendData,"%s%d,%x%x\r\n",ATCmds[ATCmdNum].ATSendStr,2,0x10,0x10);

  18. HAL_UART_Transmit(&huart2,(uint8_t*)NbSendData,strlen(NbSendData),100);

  19. HAL_UART_Transmit(&huart1,(uint8_t*)NbSendData,strlen(NbSendData),100);

  20. }

  21. else

  22. {

  23. HAL_UART_Transmit(&huart2,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),100);

  24. HAL_UART_Transmit(&huart1,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),100);

  25. }

  26. //打开超时定时器,这里主要用来判断接收超时使用

  27. SetTime(&TimeNB,ATCmds[ATCmdNum].TimeOut);//获取定时器的起始时间和时间间隔

  28.  
  29. //打开发送指示灯,配合LedTask函数的使用可以产生一个100毫秒的亮灯

  30. //如果100毫秒之内又有数据发送,则定时器重新计时,LED灯继续延长点亮时间

  31. SetLedRun(LED_TX);

  32. }

 
  1. void ATRec(void)//AT接收字符串的解析

  2. {

  3. if(Usart2type.Usart2RecFlag)//是否接收到一个完整的数据包

  4. {

  5. //判断解析缓存区中是否存在对应指令返回的正确参数(字符串),strstr的使用方法见下方

  6. if(strstr((const char*)Usart2type.Usart2RecBuff,ATCmds[ATRecCmdNum].ATRecStr) != NULL)

  7. {

  8. ATCmds[ATRecCmdNum].ATStatus = SUCCESS_REC;//接收状态赋值为成功

  9. }

  10.  
  11. SetLedRun(LED_RX);//打开接收指示灯,配合LedTask函数的使用可以产生一个100毫秒的亮灯

  12. HAL_UART_Transmit(&huart1,Usart2type.Usart2RecBuff,Usart2type.Usart2RecLen,100);//打印到串口

  13. Usart2type.Usart2RecFlag = 0;//清空标志位

  14. Usart2type.Usart2RecLen = 0;//设置解析缓存区字符串长度为0

  15. }

  16. }

  17.  
  18.  
  19.  
  20. strstr:

  21.  
  22. 原型:extern char *strstr(char *haystack, char *needle);

  23.  
  24. 用法:#include <string.h>

  25.  
  26. 功能:从字符串haystack中寻找needle第一次出现的位置(不比较结束符NULL)。

  27.  
  28. 说明:返回指向第一次出现needle位置的指针,如果没找到则返回NULL。

  29.  
 
  1. main函数中测试:

  2.  
  3. ...

  4.  
  5. LedInit();//初始化:所有灯关闭

  6. HAL_Delay(2000);//延时2秒

  7. SetLedRun(0);//设置入网指示灯为运行态

  8. SetLedRun(1);//设置接收指示灯为运行态

  9. SetLedRun(2);//设置发送指示灯为运行态

  10.  
  11. while(1)

  12. {

  13. LedTask();

  14. }

  15.  
  16. ...

 NB-IoT入网开发

1、NB-IoT入网流程

2、NB-IoT入网设计

NB-IoT入网流程

NB-IoT模块驱动流程

NB-IoT入网设计

NB-IoT入网任务算法

有限状态机编程

    -->裸机编程效率最高的编程模式

入网任务状态机(空闲、指令发送,等待响应、入网完成)

 
  1. typedef enum //NB任务的状态

  2. {

  3. NB_IDIE = 0, //NB空闲

  4. NB_SEND, //NB的发送

  5. NB_WAIT, //NB的等待

  6. NB_ACCESS //NB的入网完成

  7. }teNB_TaskStatus;

指令发送

等待响应

AT超时

NB初始化

入网完成

 
  1. static uint8_t CurrentRty;//当前重发的次数

  2. static teATCmdNum ATRecCmdNum;//

  3. static teATCmdNum ATCurrentCmdNum;//当前的指令

  4. static teATCmdNum ATNextCmdNum;//下一条指令

  5. static teNB_TaskStatus NB_TaskStatus;//声明任务的状态

  6. static tsTimeType TimeNBSendData;//NB发送数据时的起始时间获取 和 接收超时时间的设置

  7.  
  8. void NB_Task(void)//NB不同任务状态的处理程序,一般开始时NB_TaskStatus状态为NB_SEND,故可以从NB_SEND开始分析

  9. {

  10. while(1)//死循环,为了高效率处理

  11. {

  12. switch(NB_TaskStatus)

  13. {

  14. case NB_IDIE:

  15. //if(CompareTime(&TimeNBSendData))//判断发送指令是否超时

  16. //{

  17. // ATCurrentCmdNum = AT_NMGS;//当前指令设置为AT_NMGS

  18. // ATNextCmdNum = AT_IDIE;//下一条指令设置为空闲指令

  19. // NB_TaskStatus = NB_SEND;//任务状态设置为发送

  20. // SetTime(&TimeNBSendData,10000);//发送超时设置

  21. // break;//跳转到发送状态

  22. //}

  23. return;

  24. case NB_SEND:

  25. if(ATCurrentCmdNum != ATNextCmdNum)//如果当前指令不等于下一条指令,则该指令是第一次运行

  26. {

  27. CurrentRty = ATCmds[ATCurrentCmdNum].RtyNum;//获取当前指令的重发次数

  28. }

  29. ATSend(ATCurrentCmdNum);//发送指令

  30. NB_TaskStatus = NB_WAIT;//更改为等待态

  31. return;//因为有超时

  32. case NB_WAIT:

  33. ATRec();//AT接收字符串的解析

  34. if(ATCmds[ATCurrentCmdNum].ATStatus == SUCCESS_REC)//判断接收状态是否成功

  35. {

  36. if(ATCurrentCmdNum == AT_CGPADDR)//判断当前指令是否为入网指令

  37. {

  38. NB_TaskStatus = NB_ACCESS;//如果是则状态设置为入网完成

  39. break;//跳转指令

  40. }

  41. // else if(ATCurrentCmdNum == AT_NMGS)//判断当前指令是否为AT_NMGS指令

  42. // {

  43. // NB_TaskStatus = NB_IDIE;//设置任务状态为空闲状态

  44. // return;

  45. // }

  46. else

  47. {

  48. ATCurrentCmdNum += 1;//如果不是入网指令,则当前指令加1

  49.  
  50. ATNextCmdNum = ATCurrentCmdNum+1;//下一条指令在当前指令的基础上再加1

  51. NB_TaskStatus = NB_SEND;//设置为发送状态

  52. break;//跳转指令

  53. }

  54. }

  55. else if(CompareTime(&TimeNB))//判断发送指令之后接收是否超时

  56. {

  57. ATCmds[ATCurrentCmdNum].ATStatus = TIME_OUT;//改变当前指令的状态:设置超时

  58. if(CurrentRty > 0)//判断当前重发的次数是否大于零

  59. {

  60. CurrentRty--;

  61. ATNextCmdNum = ATCurrentCmdNum;//下一条指令等于当前指令

  62. NB_TaskStatus = NB_SEND;//改变任务状态为发送状态

  63. break;//跳转到发送状态的处理程序

  64. }

  65. else//否则重发次数已经达到最高的重发次数限制

  66. {

  67. NB_Init();//NB初始化,函数具体实现见下方

  68. return;

  69. }

  70. }

  71. return;

  72. case NB_ACCESS://如果是入网完成的状态

  73. LedOn(LED_NET);//打开入网完成的指示灯

  74. NB_TaskStatus = NB_IDIE;//任务状态设置为空闲状态

  75. break;//跳转到空闲状态

  76. // ATCurrentCmdNum = AT_NMGS;//当前指令设置为AT_NMGS

  77. // ATNextCmdNum = AT_IDIE;//下一条指令设置为空闲指令

  78. // NB_TaskStatus = NB_SEND;//任务状态设置为发送状态

  79. // SetTime(&TimeNBSendData,10000);//发送指令超时设置

  80. // break;//跳转到发送状态

  81. default:

  82. return;

  83. }

  84. }

  85. }

  86.  
  87.  
  88.  
  89.  
  90. void NB_Init(void)//NB初始化

  91. {

  92. NB_TaskStatus = NB_SEND;//任务状态设置为发送状态

  93. ATCurrentCmdNum = AT_CFUN0;//当前指令设置为第一条指令

  94. ATNextCmdNum = AT_CGSN;//下一条指令设置为第二条指令

  95. }

主程序:测试

 
  1. main函数中测试

  2.  
  3. #include "nbiotdriver.h"

  4.  
  5. ...

  6.  
  7. NB_Init();//NB初始化

  8.  
  9. printf("wait 5s NB Reset!\n");

  10. HAL_Delay(5000);//初始化时需等待NB芯片重启(这里设置5秒钟等待时间)

  11.  
  12. while (1)

  13. {

  14. LedTask();

  15. NB_Task();

  16. }

  17.  
  18. ...

NB-IoT网络CoAP通信

1、发展背景

2、HTTP协议

3、CoAP协议

4、NB-IoT CoAP通信开发

发展背景

互联网

移动互联网

物联网

新的协议

HTTP协议

HTTP介绍:

什么是超文本(HyperText)?

包含有超链接(Link)和各种多媒体元素标记(Markup)的文本。这些超文本文件彼此链接,形成网状(Web),因此又被称为网页(Web Page)。这些链接使用URL表示。最常见的超文本格式是超文本标记语言HTML。

什么是URL?

URL即统一资源定位符(Uniform Resource Locator),用来唯一地标识万维网中的某一个文档。URL由协议、主机和端口(默认为80)以及文件名三部分构成。如:

什么是超文本传输协议HTTP?

是一种按照URL指示,将超文本文档从一台主机(Web服务器)传输到另一台主机(浏览器)的应用层协议,以实现超链接的功能。

HTTP工作原理

请求/响应交互模型

在用户点击URL为http://www.makeru.com.cn//course/3172.html的链接后,浏览器和Web服务器执行以下动作:

1、浏览器分析超链接中的URL

2、浏览器向DNS请求解析www.makeru.com.cn的IP地址

3、DNS将解析出的IP地址202.2.16.21返回浏览器

4、浏览器与服务器建立TCP连接(80端口)

5、浏览器请求文档:GET /index.html

6、服务器给出响应,将文档 index.html发送给浏览器

7、释放TCP连接

8、浏览器显示index.html中的内容

HTTP报文结构

请求报文:即从客户端(浏览器)向Web服务器发送的请求报文。报文的所有字段都是ASCII码。

响应报文:即从Web服务器到客户机(浏览器)的应答。报文的所有字段都是ASCII码。

请求报文中的方法:方法(Method)是对所请求对象所进行的操作,也就是一些命令。请求报文中的操作有:

CoAP协议

CoAP是什么

CoAP是IETF为满足物联网,M2M场景制定的协议,特点如下:

类似HTTP,基于REST模型:Servers将Resource通过URI形式呈现,客户端可以通过诸如GET,PUT,POST,DELETE方法访问,但是相对HTTP简化实现降低复杂度(代码更小,封包更小)

应用于资源受限的环境(内存,存储,无良好的随机源),比如CPU为8-bit的单片机,内存32Kb,FLASH 256Kb

针对业务性能要求不高的应用:低速率(10s of kbit/s),低功耗

满足CoRE环境的HTTP简化增强版本

CoAP协议模型

逻辑上分为Message和Request/Response两层,Request/Response通过Message承载,从封包上不体现这种层次结构

基于UDP,支持组播

基于UDP的类似HTTP的Client/Server交互模型

Client发送Request(携带不同method)请求对资源(通过URI表示)的操作,Server返回Response(携带资源的representation)和状态码

在M2M应用场景,Endpoint实际同时是Server和Client

NB-IoT CoAP通信开发

NB-IoT CoAP通信开发

COAP数据收发:

CoAP 数据发送无需事先建立 socket(模组内部处理) , 直接发送数据:AT+NMGS=2,A1A2 发送 2 字节数据, 发送成功回复 OK, 否则 ERROR

读取 CoAP 数据:+NNMI:2,A1A2 收到 2 字节 CoAP 数据

NB-IoT CoAP通信开发流程

 
  1. tsATCmds ATCmds[] =

  2. {

  3. {"AT+CFUN=0\r\n","OK",2000,NO_REC,3},

  4. {"AT+CGSN=1\r\n","OK",2000,NO_REC,3},

  5. {"AT+NRB\r\n","OK",8000,NO_REC,3},

  6. {"AT+NCDP=180.101.147.115,5683\r\n","OK",2000,NO_REC,3},

  7. {"AT+CFUN=1\r\n","OK",2000,NO_REC,3},

  8. {"AT+CIMI\r\n","OK",2000,NO_REC,3},

  9. {"AT+CMEE=1\r\n","OK",2000,NO_REC,3},

  10. {"AT+CGDCONT=1,\"IP\",\"ctnb\"\r\n","OK",2000,NO_REC,3},

  11. {"AT+NNMI=1\r\n","OK",2000,NO_REC,3},

  12. {"AT+CGATT=1\r\n","OK",2000,NO_REC,3},

  13. {"AT+CGPADDR\r\n","+CGPADDR:1,1",2000,NO_REC,30},

  14. {"AT+NMGS=","OK",3000,NO_REC,3},//发送数据的指令,注意:发送数据是可变的,所以指令后面没有 "\r\t"

  15. };

 
  1. typedef enum

  2. {

  3. AT_CFUN0 = 0,

  4. AT_CGSN,

  5. AT_NRB,

  6. AT_NCDP,

  7. AT_CFUN1,

  8. AT_CIMI,

  9. AT_CMEE,

  10. AT_CGDCONT,

  11. AT_NNMI,

  12. AT_CGATT,

  13. AT_CGPADDR,

  14. AT_NMGS,

  15. AT_IDIE//因为AT_NMGS为最后一个指令,所以这里添加一个空闲指令标记

  16. }teATCmdNum;

 
  1. #include "time.h"

  2. #include "led.h"

  3.  
  4. static tsTimeType TimeNB;//获取定时器的起始时间和时间间隔

  5.  
  6. char NbSendData[100];//发送数据指令中数据的存储区

  7.  
  8. void ATSend(teATCmdNum ATCmdNum)

  9. {

  10. //清空接收缓存区

  11. memset(Usart2type.Usart2RecBuff,0,USART2_REC_SIZE);

  12. ATCmds[ATCmdNum].ATStatus = NO_REC;

  13.  
  14. ATRecCmdNum = ATCmdNum;

  15. if(ATCmdNum == AT_NMGS)//判断是否发送数据的指令

  16. {

  17. memset(NbSendData,0,100);//清空数据的存储区

  18.  
  19. //第一个%s为发送数据的指令:"AT+NMGS="

  20. //第二个%d为发送数据的个数是两个(字节的长度)

  21. //第三和第四个%x是两个要发送的16进制的数据

  22. //最终得到NbSendData的数据为:AT+NMGS=2,0x10,0x10\r\n

  23. sprintf(NbSendData,"%s%d,%x%x\r\n",ATCmds[ATCmdNum].ATSendStr,2,0x10,0x10);

  24. HAL_UART_Transmit(&huart2,(uint8_t*)NbSendData,strlen(NbSendData),100);//发送NbSendData到NB芯片

  25. HAL_UART_Transmit(&huart1,(uint8_t*)NbSendData,strlen(NbSendData),100);//发送NbSendData到串口1,用于调试

  26. }

  27. else

  28. {

  29. HAL_UART_Transmit(&huart2,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),100);

  30. HAL_UART_Transmit(&huart1,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),100);

  31. }

  32. //打开超时定时器,这里主要用来判断接收超时使用

  33. SetTime(&TimeNB,ATCmds[ATCmdNum].TimeOut);//获取定时器的起始时间和时间间隔,具体见下面讲解

  34.  
  35. //打开发送指示灯,配合LedTask函数的使用可以产生一个100毫秒的亮灯,具体函数下文有讲解

  36. //如果100毫秒之内又有数据发送,则定时器重新计时,LED灯继续延长点亮时间

  37. SetLedRun(LED_TX);

  38. }

 
  1. static uint8_t CurrentRty;//当前重发的次数

  2. static teATCmdNum ATRecCmdNum;//

  3. static teATCmdNum ATCurrentCmdNum;//当前的指令

  4. static teATCmdNum ATNextCmdNum;//下一条指令

  5. static teNB_TaskStatus NB_TaskStatus;//声明任务的状态

  6. static tsTimeType TimeNBSendData;//NB发送数据时的起始时间获取 和 接收超时时间的设置

  7.  
  8. void NB_Task(void)//NB不同任务状态的处理程序,一般开始时NB_TaskStatus状态为NB_SEND,故可以从NB_SEND开始分析

  9. {

  10. while(1)//死循环,为了高效率处理

  11. {

  12. switch(NB_TaskStatus)

  13. {

  14. case NB_IDIE:

  15. if(CompareTime(&TimeNBSendData))//判断发送指令是否超时

  16. {

  17. ATCurrentCmdNum = AT_NMGS;//当前指令设置为发送数据指令

  18. ATNextCmdNum = AT_IDIE;//下一条指令设置为空闲指令

  19. NB_TaskStatus = NB_SEND;//任务状态设置为发送

  20. SetTime(&TimeNBSendData,10000);//每隔10秒发送一次数据

  21. break;//跳转到发送状态

  22. }

  23. return;

  24. case NB_SEND:

  25. if(ATCurrentCmdNum != ATNextCmdNum)//如果当前指令不等于下一条指令,则该指令是第一次运行

  26. {

  27. CurrentRty = ATCmds[ATCurrentCmdNum].RtyNum;//获取当前指令的重发次数

  28. }

  29. ATSend(ATCurrentCmdNum);//发送指令

  30. NB_TaskStatus = NB_WAIT;//更改为等待态

  31. return;//因为有超时

  32. case NB_WAIT:

  33. ATRec();//AT接收字符串的解析

  34. if(ATCmds[ATCurrentCmdNum].ATStatus == SUCCESS_REC)//判断接收状态是否成功

  35. {

  36. if(ATCurrentCmdNum == AT_CGPADDR)//判断当前指令是否为入网指令

  37. {

  38. NB_TaskStatus = NB_ACCESS;//如果是则状态设置为入网完成

  39. break;//跳转指令

  40. }

  41. else if(ATCurrentCmdNum == AT_NMGS)//判断当前指令是否为发送数据指令

  42. {

  43. NB_TaskStatus = NB_IDIE;//设置任务状态为空闲状态

  44. return;

  45. }

  46. else

  47. {

  48. ATCurrentCmdNum += 1;//如果不是入网指令,则当前指令加1

  49.  
  50. ATNextCmdNum = ATCurrentCmdNum+1;//下一条指令在当前指令的基础上再加1

  51. NB_TaskStatus = NB_SEND;//设置为发送状态

  52. break;//跳转指令

  53. }

  54. }

  55. else if(CompareTime(&TimeNB))//判断发送指令之后接收是否超时

  56. {

  57. ATCmds[ATCurrentCmdNum].ATStatus = TIME_OUT;//改变当前指令的状态:设置超时

  58. if(CurrentRty > 0)//判断当前重发的次数是否大于零

  59. {

  60. CurrentRty--;

  61. ATNextCmdNum = ATCurrentCmdNum;//下一条指令等于当前指令

  62. NB_TaskStatus = NB_SEND;//改变任务状态为发送状态

  63. break;//跳转到发送状态的处理程序

  64. }

  65. else//否则重发次数已经达到最高的重发次数限制

  66. {

  67. NB_Init();//NB初始化,函数具体实现见下方

  68. return;

  69. }

  70. }

  71. return;

  72. case NB_ACCESS://如果是入网完成的状态

  73. LedOn(LED_NET);//打开入网完成的指示灯

  74. ATCurrentCmdNum = AT_NMGS;//当前指令设置为发送数据的指令

  75. ATNextCmdNum = AT_IDIE;//下一条指令设置为空闲指令

  76. NB_TaskStatus = NB_SEND;//任务状态设置为发送状态

  77. SetTime(&TimeNBSendData,10000);//发送指令超时设置

  78. break;//跳转到发送状态

  79. default:

  80. return;

  81. }

  82. }

  83. }

  84.  
  85.  
  86.  
  87.  
  88. void NB_Init(void)//NB初始化

  89. {

  90. NB_TaskStatus = NB_SEND;//任务状态设置为发送状态

  91. ATCurrentCmdNum = AT_CFUN0;//当前指令设置为第一条指令

  92. ATNextCmdNum = AT_CGSN;//下一条指令设置为第二条指令

  93. }

 

 

NB-IoT的中文指令集,基于BC95 657 SP2版本翻译的。 —————————————————————— AT 命令 描述 实现的状态 3GPP Commands (27.007) AT+CGMI 查询制造商 ID B350+ AT+CGMM 查询模块型号 B350+ AT+CGMR 查询固件版本 B350+ AT+CGSN 查询模块序列号 B350+ AT+CEREG 查询网络注册状态 B350+ AT+CSCON 查询信号连接状态 B350+ AT+CLAC 列出可用命令 B350+ AT+CSQ 获取信号强度 B350+ AT+CGPADDR 显示 PDP 地址 B350+ AT+COPS 选择接入的网络 B350+ AT+CGATT PS 连接或分离 B350+ AT+CGACT 激活或停用 PDP 上下文 B657SP1+ AT+CIMI 查询国际移动设备身份码 B350+ AT+CGDCONT 定义一个 PDP 上下文 B350+ AT+CFUN 设置终端功能 B350+ AT+CMEE 报告移动终端错误 B600+ AT+CCLK 返回当前日期和时间 B656+ AT+CPSMS 省电模式设置 B657SP1+ AT+CEDRXS eDRX 设置 B657SP1+ AT+CEER 扩展错误报告 B657SP1+ AT+CEDRXRDP eDRX 阅读动态参数 B657SP1+ AT+CTZR 时区报告 B657SP1+ ETSI Commands(正在开发中) AT+CSMS 选择短消息服务 B657SP1+ AT+CNMA 模块消息提醒 B657SP1+ AT+CSCA 服务中心地址 B657SP1+ AT+CMGS 发送短消息 B657SP1+ AT+CMGC 发送短信命令 B657SP1+ AT+CSODCP 通过控制层发送原始数据 B657SP1+ AT+CRTDCP 通过控制层传送终端数据 B657SP1+ General Commands 通用命令 AT+NMGS 发送消息到 CDP 服务器 B350+ AT+NMGR 接收 CDP 服务器消息 B350+ AT+NNMI 接收消息标志 B350+ AT+NSMI 发送消息的标志 B350+ AT+NQMGR 查询接收到的消息量 B350+ AT+NQMGS 查询发送的消息量 B350+ AT+NMSTATUS 信息注册状态 B657SP1+ AT+NRB 模块重启 B350+ AT+NCDP 配置和查询 CDP 服务器设置 B350+ AT+NUESTATS 获取的操作统计 B350+ AT+NEARFCN 指定搜素频率 B350+ AT+NSOCR 创建 Socket B350+ AT+NSOST 发送数据 B350+ AT+NSOSTF 发送有标记数据 B656+ AT+NSORF 接收命令 B350+ AT+NSOCL 关闭 Socket B350+ +NSONMI 指示 Socket 消息到达(只响应) B350+ AT+NPING 测试 IP 网络连接到远程主机 B350+ AT+NBAND 设置频段 B600+ AT+NLOGLEVEL 设置日志级别 B600+ AT+NCONFIG 配置模块的功能 B650+ AT+NATSPEED 配置 UART 端口波特率 B656+ AT+NCCID 卡片识别 B657SP1+ AT+NFWUPD 通过 UART 更新固件 B657SP1+ AT+NRDCTRL 控制无线配置 B657SP1+ AT+NCHIPINFO 读取系统信息 B657SP1+ Temporary Commands 临时命令
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值