NBIoT模擬CoAP協議將數據上傳到EMQ
關於UDP模擬CoAP協議可以參考:NBIoT模組NB35-A開發(3)— 使用UDP模擬CoAP協議連接到EMQ
一、NBIoT啓動後準備
新建【Application】文件,並在裏面新建app_nbiot.c
與app_nbiot.h
文件。
首先,在NBIoT模組啓動後後創建一個UDPcocket,在app_nbiot.c編寫啓動函數:
int udp_socket;
/* 描述:NBIoT啓動函數,在NBIoT啓動後,我們需要等待其駐網,然後查詢IP
得到IP後,申請一個UDPsocket
* 參數:無
* 返回值:0表示啓動成功,-1表示啓動失敗 */
int NB_Start(void)
{
char res_buf[256];
int cnt = 0, t = 0;
/* 查詢是否駐網,查詢15次*/
for(cnt = 0 ; cnt < 15; cnt++)
{
u1_printf("AT+CGATT?\r\n");
for(t = 0; t < 1000; t++)
{
if(USART1_RX_NUM > 0) break;
delay_1ms(1);
}
if(USART1_RX_NUM > 0)
{
USART1_RX_NUM = 0;
if(Find_string((char *)rx1_date_buf,"+CGATT:","\r\nOK\r\n",res_buf)>0)
if(res_buf[0] == '1') break;
}
delay_1ms(2000);
}
if(cnt == 15) return -1;
/* 查詢IP地址,查詢15次*/
for(cnt = 0 ; cnt < 15; cnt++)
{
u1_printf("AT+CGPADDR\r\n");
for(t = 0; t < 1000; t++)
{
if(USART1_RX_NUM > 0) break;
delay_1ms(1);
}
if(USART1_RX_NUM > 0)
{
USART1_RX_NUM = 0;
if(Find_string((char *)rx1_date_buf,"+CGPADDR:0,","\r\nOK",res_buf)>0)
{
printf("\r\nNBIoT IP addr: %s\r\n",res_buf);
break;
}
}
delay_1ms(1000);
}
if(cnt == 15) return -1;
/* 申請一個UDPsocket*/
for(cnt = 0 ; cnt < 15; cnt++)
{
u1_printf("AT+NSOCR=DGRAM,17,0\r\n");
for(t = 0; t < 1000; t++)
{
if(USART1_RX_NUM > 0) break;
delay_1ms(1);
}
if(USART1_RX_NUM > 0)
{
USART1_RX_NUM = 0;
if(Find_string((char *)rx1_date_buf,"\r\n","\r\nOK",res_buf)>0)
{
udp_socket = res_buf[0] - '0';
printf("\r\nudp_socket: %d\r\n",udp_socket);
break;
}
}
delay_1ms(1000);
}
if(cnt == 15) return -1;
return 0;
}
其中,用到的尋找字符串函數爲
/* 描述:尋找特定字符串
* 參數 *pcBuf:需要進行操作的字符串
* 參數 *left :需要搜索的字符串的左邊的標識符
* 參數 *right: 需要搜索的字符串的右邊的標識符
* 參數 *pcRes:輸出參數,尋找到的字符串
* 返回值:返回尋找到的字符串長度,0表示失敗 */
int Find_string(char *pcBuf,char *left,char *right, char *pcRes)
{
int str_len = 0;
char *pcBegin = NULL;
char *pcEnd = NULL;
pcBegin = strstr(pcBuf, left); /* 找到左側數據 */
pcEnd = strstr(pcBegin+strlen(left), right); /* 找到右側數據 */
if(pcBegin == NULL || pcEnd == NULL || pcBegin > pcEnd)
{
printf("string name not found!\n");
return 0;
}
else
{
pcBegin += strlen(left);
str_len = pcEnd-pcBegin;
memcpy(pcRes, pcBegin, str_len);
pcRes[str_len] = '\0';
return str_len;
}
}
然後我們在主函數中調用,編譯下載運行,可以看到,創建UDPsocket成功,從啓動到創建UDPsocket成功一共花了16秒
二、UDP模擬CoAP協議上報數據
在app_nbiot.c編寫上報函數:
/* 描述:NBIoT模擬CoAP上報數據
* 參數 *ipaddr :服務器的IP地址字符串
* 參數 *topic :EMQ轉換成MQTT協議後的主題,不能大於15個字符長度
* 參數 *payload:有效負載,即我們真正想要上傳的數據
* 返回值:無
*/
int NB_Send_data(char * ipaddr, char * topic, char * payload)
{
static uint16_t msg_id = 100;
static char udp_data[256];
static char palyload_hex[256];
static char topic_hex[256];
static char res_buf[256];
int t = 0;
msg_id++;
str2hex(topic,topic_hex);
str2hex(payload,palyload_hex);
sprintf(udp_data,"4203%04x5562b46d7174740%01x%s49633d636c69656e743105753d746f6d08703d736563726574ff%s",
msg_id,strlen(topic)%16,topic_hex,palyload_hex);
u1_printf("AT+NSOST=%d,%s,5683,%d,%s\r\n",udp_socket,ipaddr,strlen(udp_data)/2,udp_data);
}
裏面用到了字符串轉16進制格式函數
/* 描述:將字符轉換爲16進制格式
* 參數 *str:需要轉換的字符串
* 參數 *hex:轉換後的16進制格式
* 返回值:無 */
void str2hex(char* str, char* hex)
{
const char* cHex = "0123456789abcdef";
int i=0;
for(int j =0; str[j] != '\0'; j++)
{
unsigned int a = (unsigned int) str[j];
hex[i++] = cHex[(a & 0xf0) >> 4];
hex[i++] = cHex[(a & 0x0f)];
}
hex[i] = '\0';
}
三、每隔1秒上報一次數據
我們設置一個定時器,定時1秒,
timer5_init(10000,12000); /* 定時1000ms*/
然後在定時器中斷服務函數裏將上報標誌置1
uint8_t update_flag = 0;
void TIMER5_IRQHandler(void)
{
timer_flag_clear(TIMER5,TIMER_FLAG_UP); /* 定時器更新中斷的標識位需要手動清除 */
if (update_flag == 0) update_flag = 1;
}
然後再主函數中,首先調用啓動函數,若啓動超時,則復位
while(NB_Start())
{
u1_printf("AT+NRB\r\n"); /* 重啓*/
while(USART1_RX_NUM == 0)
;
if(USART1_RX_NUM >0)
{
printf("NBIoT: %s\r\n",rx1_date_buf);
USART1_RX_NUM = 0;
}
}
printf("start OK\r\n");
然後當update_flag置1的時候上報數據,並處理返回值
while(1)
{
if(update_flag == 1)
{
update_flag = 0;
times++;
LED(times%2);
sprintf(data_buf,"I am NBIoT , %d.",times);
NB_Send_data("39.96.35.207", "topic1", data_buf);
printf("Data send to EMQ...\r\n");
}
if(USART1_RX_NUM >0)
{
USART1_RX_NUM = 0;
if(Find_string((char *)rx1_date_buf,"+NSONMI:","\r\n",res_buf)>0)
{
u1_printf("AT+NSORF=%d,%d\r\n",udp_socket,res_buf[2]-'0');
}
printf("NBIOT: %s\r\n",rx1_date_buf);
}
if(USART_RX_NUM >0)
{
USART_RX_NUM = 0;
u1_printf("%s",rx0_date_buf);
}
}
編譯,運行程序,可以在EMQ平臺接收到數據:
四、代碼
完整代碼我存放在碼雲,可以查看:https://gitee.com/william_william/GD32.git