[STM32+ESP8266] 基於STM32控制ESP8266向API接口發送GET請求並解析返回數據

前文寫道在使用STM32控制ESP8266通過TCP方式連接上服務器,然後可以和服務器進行通信,收發數據。本文將以蘇寧免費的時間接口爲例:(👈可以直接點擊查看返回結果)
http://quan.suning.com/getSysTime.do
演示如何向服務器的API接口發送GET請求報文,並且解析數據。(博主對網絡很不瞭解,如有錯誤的地方還請指正)

GET請求格式

具體GET請求的過程我不做多餘解釋,以免有錯誤誤導讀者,如想了解可以搜索一下請求頭內容以及解釋。
我們在連接服務器成功後可以向服務器發送以下內容,就可以獲得相應的結果。

GET http://quan.suning.com/getSysTime.do HTTP/1.1
Host: quan.suning.com


//結尾需要有一個空行並且回車換行,也就是說要有兩個換行回車

實際上GET請求全部內容有九行,這裏只要發送請求的資源名稱以及使用的協議版請求的服務器地址 就可以了。

使用TCP助手測試

在使用ESP8266連接之前,我們先用TCP助手連接服務器,併發送請求測試一下,以保證這個方案確實可行(TCP助手發送的數據和我們使用ESP8266發送的數據一模一樣。所以用它來測試向服務器發送GET請求的數據,可以不用改動內容直接使用)。

查看API接口的服務器地址和端口號

因爲TCP助手的侷限,我們連接時必須先知道API接口的服務器地址和端口號。但是正式使用ESP8266的時候就不必要了,直接使用域名就可以連接。
簡單的查看地址的方法有很多,在這裏博主介紹使用瀏覽器方法(測試IE瀏覽器好像不行,我是用的是QQ瀏覽器)。
1.直接在瀏覽器地址欄輸入蘇寧時間接口的URL,就會看到返回的數據內容(是一串json數據)
2.點擊鍵盤F12,選擇Network頁籤,然後刷新網頁,即可看到Name下有兩個文件
在這裏插入圖片描述
3.點擊第一個文件即可查看詳情。裏面包含了整個過程收發的內容。可以看到請求的URL,請求方式,和請求的地址(你看到的可能和我的不一樣,都可以使用)
在這裏插入圖片描述

使用TCP助手連接

打開TCP調試助手然後輸入地址和端口號,點擊連接網絡即可
在這裏插入圖片描述
至此,我們的準備工作就完成了。此時就相當於我們的ESP8266連接服務器成功。

使用TCP助手發送GET請求報文

下面將我前文說到的報文黏貼進去(請注意格式),點擊發送按鈕很快就會收到服務器發送回來的數據。
GET請求返回
數據包含響應報文和消息體。
第一行爲狀態行:200 OK 即是請求成功
空行後面緊接着的就是消息體,內容應該與瀏覽器顯示的結果一致。

如果返回結果有問題,多數是因爲複製的問題。刪除乾淨後,輸入請求報文內容至發送區。
至此,我們使用TCP調試助手測試成功。

使用ESP8266連接服務器併發送GET請求

如果你已經實現ESP8266連接服務器,並且能收發數據,難麼直接使用發送數據命令將請求報文發送出去就可以了。
如果還沒有實現,那麼可以按照我的博文基於STM32單片機控制ESP8266連接服務器(包含C源碼)中的方法來操作。

下面展示具體實現代碼:

//頭文件中宏定義內容
//時間地址和服務器
#define ESP_TIME_TCP_ADDRESS "quan.suning.com"
#define ESP_TIME_TCP_POINT "80"
//GET請求報文
#define ESP_TIME_INFO  "GET http://quan.suning.com/getSysTime.do HTTP/1.1\r\nHost: quan.suning.com\r\n\r\n" 

//開啓透傳模式後,直接通過串口向ESP8266發送報文
USART_Send_String(USART2, ESP_TIME_INFO); //發送GET請求報文,獲得返回結果
while(!(ESP_RX_STATE > 0));  //判斷接收標誌
/*
   此處寫收到結果後做的解析處理
*/
    ESP_RX_STATE --;//解析數據置位

如此下來,我們發送了GET請求並且還能接受到返回的結果,是不是超簡單。那麼如何處理結果呢?

解析返回結果

我們收到的數據格式爲json格式,這是一種廣泛使用數據交換格式。有專用的cJSON庫可以進行解析,具體這裏就不解釋怎麼使用了,博主使用另外的笨方法:暴!力!破!解!

解析數據

因爲我們的數據格式非常固定,我可以針對這個格式進行解析。再來回看下接收到的內容:
{“sysTime2”:“2019-11-02 22:38:52”,“sysTime1”:“20191102223852”}
使用了兩種時間表示方法,都可以使用。從"sysTime1"後的第三位開始就是我們需要的結果,因此我只要使用函數 strstr()函數定位到"sysTime1"的位置往後移動11位(strstr函數返回的是查找的字符串的首地址)就可以了。

 data_pt = strstr((const char *) ESP_RX_BUF.buf,(const char *)"sysTime1");   //尋找到時間結果的地址,首地址返回給字符類型的指針變量(char*) data_pt 

年的首地址移動11位;

 year_string = data_pt + YERA_ADD_DRES;   //年份地址

月份首地址移動15位;
日期首地址移動17位;
小時首地址移動19位;
分鐘首地址移動21位;
秒鐘首地址移動23位;
以此類推可以得到每個數據的位置(其實直接可以當一個字符串處理)。

處理數據

我們其實是得到了字符串類型的時間數據,已經可以直接使用了。博主爲了方便運算繼續進行了數據轉換,以便其他位置的使用。

獲得年:

//獲得年函數(因爲以年開始的字符串長度過長,所以使用不同的方法)
//輸入值是年位置的地址
//返回值是 整型的10進制四位數
int Get_Year(char *y)
{
   int year_return;
   char *year_temp;
   char year[5] = {0};
   char i;
//年的獲取需要提取一次字符串,否則無法讀取  
   year_temp = y;
   for(i = 0; i<4; i++)
   {
       year[i] = *year_temp;
       year_temp ++;
   }

   year_return =  atoi(&year[0]); 
   return year_return; 
}

獲得月份:

//獲得月份函數
//輸入值是月份位置的地址
//返回值是 整型的10進制兩位數
int Get_Moonth(char *m)
{
     int moonth_return;
     moonth_return = atoi(m)/100000000;   //取月份    
     return moonth_return;
}

獲得日期

//獲得日期函數
//輸入值是日期位置的地址
//返回值是 整型的10進制兩位數
int Get_Day(char *d)
{
    int day_return;
    day_return = atoi(d)/1000000;   //取日期
 
    return day_return;
}

獲得時間

//獲得時間
//輸入值是時間的位置的地址
//返回值是 整型的10進制的時間總秒數
int Get_Times(char *h, char *m, char *s)
{
    int time_return;
    int hour_return;
    int min_return;
    int sec_return; 

    hour_return = atoi(h)/10000;  //取小時
    min_return = atoi(m)/100;   //取分鐘
    sec_return = atoi(s);   //取秒數
 
    time_return = hour_return*3600 + min_return*60 + sec_return;   //轉換成總秒數
 
    return time_return;
}

完整解析過程

我的整個處理時間的代碼如下:

//定義時間存儲的全局變量(也可以定義成結構體)
__IO uint32_t TIMES = 0;
__IO uint32_t YEARS = 2019; 
__IO uint32_t MOONS = 1; 
__IO uint32_t DAYS = 1;
__IO uint32_t WEEKS = 0;

//獲取網絡時間並解析給系統
void GET_Net_Time(void)
{
    char *data_pt;
    char *day_string;
    char *moon_string;
    char *year_string;
    char *hour_string;
    char *minute_string;
    char *second_string;
    
    RT_Send_String(USART2, ESP_TIME_INFO);  //發送GET請求
    
    while(!(ESP_RX_STATE > 0));  //判斷接收標誌
    data_pt = strstr((const char *) ESP_RX_BUF.buf,(const char *)"sysTime1");   //尋找到時間結果的地址
    ESP_RX_STATE --;
    
   if(data_pt != NULL)
   {
         day_string = data_pt + DAYS_ADD_DRES;    //日期地址
         moon_string = data_pt + MOON_ADD_DRES;   //月份地址
         year_string = data_pt + YERA_ADD_DRES;   //年份地址
         hour_string = data_pt + HOURS_ADD_DRES;  //小時地址
         minute_string = data_pt + MINUTES_ADD_DRES;  //分鐘地址
         second_string = data_pt + SECONDS_ADD_DRES;   //秒中地址
   
    
   
//將時間信息傳遞給全局變量   
        DAYS = Get_Day(day_string);
        MOONS = Get_Moonth(moon_string);
        YEARS = Get_Year(year_string);
        TIMES = Get_Times(hour_string, minute_string, second_string);
   }
   else
   {
        printf("get net time failed!\r\n");
   }
}

//獲得年函數(以年開始的字符串長度過長,所以使用不同的方法)
//輸入值是年位置的地址
//返回值是 整型的10進制四位數
int Get_Year(char *y)
{
   int year_return;
   char *year_temp;
   char year[5] = {0};
   char i;
//年的獲取需要提取一次字符串,否則無法讀取  
   year_temp = y;
   for(i = 0; i<4; i++)
   {
       year[i] = *year_temp;
       year_temp ++;
   }

   year_return =  atoi(&year[0]); 
   return year_return; 
}

//獲得月份函數
//輸入值是月份位置的地址
//返回值是 整型的10進制兩位數
int Get_Moonth(char *m)
{
     int moonth_return;
     moonth_return = atoi(m)/100000000;   //取月份    
     return moonth_return;
}

//獲得日期函數
//輸入值是日期位置的地址
//返回值是 整型的10進制兩位數
int Get_Day(char *d)
{
    int day_return;
    day_return = atoi(d)/1000000;   //取日期
 
    return day_return;
}

//獲得時間
//輸入值是時間的位置的地址
//返回值是 整型的10進制的時間總秒數
int Get_Times(char *h, char *m, char *s)
{
    int time_return;
    int hour_return;
    int min_return;
    int sec_return; 

    hour_return = atoi(h)/10000;  //取小時
    min_return = atoi(m)/100;   //取分鐘
    sec_return = atoi(s);   //取秒數
 
    time_return = hour_return*3600 + min_return*60 + sec_return;   //轉換成總秒數
 
    return time_return;
}

如此,所有的數據解析完畢,剩下的就是使用啦,具體怎麼顯示根據自己的方法實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章