1. 程式人生 > >W5100S——MQTT連線百度天工雲平臺

W5100S——MQTT連線百度天工雲平臺

上篇文章主要講解了W5100S特色功能——SOCKET - less PING功能。

本篇文章則是講解,如何讓W5100S 通過MQTT連線百度天工雲平臺。

STM32+W5500_百度雲天工MQTT 例程下載------------>www.w5100s.com

1. 準備

  • 軟體環境:Windows

  • 硬體環境:STM32F103+W5500

  • 開發工具:Keil uVision5

  • 除錯工具:串列埠除錯助手

2. MQTT簡介

2.1 MQTT協議特點

MQTT是一個基於客戶端-伺服器的訊息釋出/訂閱傳輸協議。MQTT協議是輕量、簡單、開放和易於實現的,這些特點使它適用範圍非常廣泛。在很多情況下,包括受限的環境中,如:機器與機器(M2M)通訊和物聯網(IoT)。其在,通過衛星鏈路通訊感測器、偶爾撥號的醫療裝置、智慧家居、及一些小型化裝置中已廣泛使用。

MQTT協議當前版本為,2014年釋出的MQTT v3.1.1。除標準版外,還有一個簡化版MQTT-SN,該協議主要針對嵌入式裝置,這些裝置一般工作於百TCP/IP網路,如:ZigBee。

MQTT協議執行在TCP/IP或其他網路協議,提供有序、無損、雙向連線。其特點包括:

  • 使用的釋出/訂閱訊息模式,它提供了一對多訊息分發,以實現與應用程式的解耦。
  • 對負載內容遮蔽的訊息傳輸機制。
  • 對傳輸訊息有三種服務質量(QoS):
  • 最多一次,這一級別會發生訊息丟失或重複,訊息釋出依賴於底層TCP/IP網路。即:<=1
  • 至多一次,這一級別會確保訊息到達,但訊息可能會重複。即:>=1
  • 只有一次,確保訊息只有一次到達。即:=1。在一些要求比較嚴格的計費系統中,可以使用此級別

資料傳輸和協議交換的最小化(協議頭部只有2位元組),以減少網路流量

通知機制,異常中斷時通知傳輸雙方

2.2 MQTT協議原理及實現方式

實現MQTT協議需要:客戶端和伺服器端

MQTT協議中有三種身份:釋出者(Publish)、代理(Broker)(伺服器)、訂閱者(Subscribe)。其中,訊息的釋出者和訂閱者都是客戶端,訊息代理是伺服器,訊息釋出者可以同時是訂閱者。

MQTT傳輸的訊息分為:主題(Topic)和訊息的內容(payload)兩部分

Topic,可以理解為訊息的型別,訂閱者訂閱(Subscribe)後,就會收到該主題的訊息內容(payload)

payload,可以理解為訊息的內容,是指訂閱者具體要使用的內容

2.3 百度雲天工MQTT連線步驟

2.3.1.進入管理控制檯請提前登入);

2.3.2. 選擇“物接入IoT Hub”板塊

2.3.3. 建立專案

(例程搭配資料型裝置使用,詳細資料型與裝置型差異,請前往百度雲天工查詢)

2.3.4. 專案提交後,進入專案建立子使用者

名稱:MQTT連線報文中的使用者名稱

身份:確定訪問形式,可以使用金鑰連線。

策略:繫結對應許可權與主題。

金鑰:請妥善保管,此為連線的密碼。(遺忘可在身份列表重置)

專案建立成功後,記錄下:名稱、身份、金鑰。即可通過例程修改對應字串連線自己所建立的裝置。

3. 例程實驗現象

首選我們檢視燒錄程式後,應有的實驗現象。

注:測試W5100S訂閱主題後的接收訊息和向主題釋出訊息,是在百度雲建立了第二個裝置B。B裝置釋出訊息和接收W5100S發的主題訊息。以此檢驗是否W5100S能收到主題訊息或傳送主題訊息。

3.1.串列埠列印資訊顯示“連線成功”或者“連線失敗的具體原因”。

3.2.訂閱主題

3.3.接收主題訊息

3.4.釋出主題訊息

4. 例程程式碼

由於程式碼內涉及MQTT的拆包組包部分,詳細講解太過冗雜。

故此次解析,僅從函式功能及程式碼框架解析,不對函式實現做過多拆解。

4.1.主函式整體框架流程

#include <stdio.h>
#include <string.h>  
#include "stm32f10x.h"

#include "bsp_usart1.h"
#include "bsp_fsmc.h"
#include "bsp_spi.h"

#include "w5100s.h"
#include "W5100s_conf.h"
#include "wizchip_conf.h"
#include "w5100s_conf.h"

#include "utility.h"
#include "tcp_demo.h"
#include "MqttKit.h"

int main(void)
{ 	 
  systick_init(72);                     // 初始化滴答定時器
  USART1_Config();                      // 初始化USART1:[email protected] 
  reset_break_gpio_init();              // 復位與中斷管腳初始化
#if (_WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_)
  spi_gpio_init();                      // SPI管腳初始化
  spiinitailize();                      // SPI配置初始化
  reg_wizchip_spi_cbfunc(spi_read_byte,spi_send_byte);          // SPI讀寫資料函式對映
  reg_wizchip_cs_cbfunc(cs_low,cs_high);                        // SPI片選控制函式對映
#elif (_WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_BUS_INDIR_)
  FSMC_gpio_init();                     // FSMC管腳初始化c
  FSMCInitialize();                     // FSMC配置初始化
#endif
  printf("\r\n 煒世科技--WIZnet W5100S官方代理商。全程技術支援,價格優勢大!\r\n\r\n");
  reset_w5100s();                       // W5100S硬體復位 
  PHY_check();                          // 網線檢測程式
  set_w5100s_mac();                     // 設定W5100SMAC地址
  set_w5100s_netinfo();                 // 設定W5100S網路引數
  wizchip_init(txsize,rxsize);          // 初始化4個Socket的傳送接收快取大小
  printf("\r\n W5100S網路引數配置完成,請使用電腦 'PING' W5100SIP地址,檢查網路是否暢通。");
	
  while(1)                              /*迴圈執行的函式*/ 
  {
    do_tcp_client();                    // TCP 客戶端資料迴環測試 
		
    delay_ms(100);

    if(User1 == 0)                      //訂閱
    {
       while(User1 == 0);
       MQTT_STATE = MQTT_PKT_SUBSCRIBE;
    }
//  if(KEY_5 == 0)                      //取消訂閱
//  {
//     MQTT_STATE = MQTT_PKT_UNSUBSCRIBE;
//  }
    else if(User0 == 0)                 //釋出
    {
       while(User0 == 0);
       MQTT_STATE =MQTT_PKT_PUBLISH;
    }	
  }
} 

主函式部分,程式碼整體框架還是十分明瞭的。

main函式之前是微控制器和W5100S使用前的初始化部分。

main內則是基本的TCP客戶端程式和按鍵檢測,不同按鍵改變MQTT_STATE的不同狀態。

接下來詳細進一步探究do_tcp_client()函式。

4.2.do_tcp_client()函式

uint8 buff[2048];                                   /*定義一個2KB的快取*/
uint8  BD_TG_server_ip[4] = {163,177,150,12};       //BD_TG伺服器IP地址
uint16 BD_TG_server_port  = 1883;                   //BD_TG伺服器埠號
  

int MQTT_STATE = MQTT_PKT_CONNECT;                  //連線
const char *topics[] = {"test"};                    //主題
uint8 BD_TG_ping_pak[2] = {0xC0,0x00};              //心跳報文

unsigned char *data_ptr = NULL;                     //指標指空
/**
*@brief    TCP Client迴環演示函式。
*@param    無
*@return   無
*/
void do_tcp_client(void)
{  
  uint16 len=0;  

  switch(getSn_SR(SOCK_TCPC))                                   /*獲取socket的狀態*/
  {
    case SOCK_CLOSED:                                           /*socket處於關閉狀態*/
      socket(SOCK_TCPC,Sn_MR_TCP,local_port++,Sn_MR_ND);
    break;

    case SOCK_INIT:                                             /*socket處於初始化狀態*/
      connect(SOCK_TCPC,BD_TG_server_ip,BD_TG_server_port);     /*socket連線伺服器*/ 
    break;

    case SOCK_ESTABLISHED:                                      /*socket處於連線建立狀態*/
      if(getSn_IR(SOCK_TCPC) & Sn_IR_CON)
      {
        setSn_IR(SOCK_TCPC, Sn_IR_CON);                         /*清除接收中斷標誌位*/
      }

      len=getSn_RX_RSR(SOCK_TCPC);                              /*定義len為已接收資料長度*/
      if(len>0)
      {
        recv(SOCK_TCPC,buff,len);                               /*接收來自Server的資料*/      
        data_ptr = buff;
        if(data_ptr != NULL)
          BD_TG_RevPro(data_ptr);
        if(publish_buf[0] == 0x31)
        {  
          LED_ALL_ON;
        }      
        else if(publish_buf[0] == 0x32)  
        {  
          LED_ALL_OFF;
        }  
      }  
    
      switch(MQTT_STATE)
      {
        /*MQTT協議連線BD_TG代理平臺*/
        case MQTT_PKT_CONNECT:
          BD_TG_DevLink();
          MQTT_STATE = MQTT_PKT_PINGREQ;
        break;
        /*訂閱主題*/
        case MQTT_PKT_SUBSCRIBE:
          BD_TG_Subscribe(topics,1);
          MQTT_STATE = MQTT_PKT_PINGREQ;
        break;  
        /*Qos2級別釋出訊息*/
        case MQTT_PKT_PUBLISH:      
          
          BD_TG_Publish(*topics, "MQTT Publish Test"); //釋出訊息
          delay_ms(300);                               //等待平臺響應    
          /*接收平臺傳送的PubRec並回復PubRel響應*/     
          len=getSn_RX_RSR(SOCK_TCPC); 
          recv(SOCK_TCPC,buff,len); 
          data_ptr = buff;
          if(data_ptr != NULL)
            BD_TG_RevPro(data_ptr);
          delay_ms(100);                               //PubRel響應等待平臺響應
          len=getSn_RX_RSR(SOCK_TCPC); 
          recv(SOCK_TCPC,buff,len); 
          data_ptr = buff;
          if(data_ptr != NULL)
            BD_TG_RevPro(data_ptr);
          MQTT_STATE = MQTT_PKT_PINGREQ;
          /*120秒傳送一次Ping響應保持長連線*/
        break;
        case MQTT_PKT_UNSUBSCRIBE:
          MQTT_UnSubscribe(topics,1);
          MQTT_STATE = MQTT_PKT_PINGREQ;
        break;
        case MQTT_PKT_PINGREQ:
          if(BD_TG_ping_time > 120)
          {
            send(SOCK_TCPC,BD_TG_ping_pak,2);
            BD_TG_ping_time = 0;
          }
        break;
      }  
    break;

    case SOCK_CLOSE_WAIT:                              /*socket處於等待關閉狀態*/
      close(SOCK_TCPC);
    break;
  }
}

主要是個狀態機的編寫格式,基本的TCP建立連線(SOCK_ESTABLISHED狀態)後,通過MQTT_STATE變數的狀態,判斷是傳送MQTT連線報文、MQTT訂閱報文、MQTT訊息釋出、MQTT取消訂閱、MQTT心跳報文等不同的case。

最開始會先預設進入MQTT_PKT_CONNECT,傳送連線報文,我們連線百度雲,主要有以下引數需要更改

#define PROID		"7321ph4/w5500"         // 裝置全稱

#define AUTH_INFO	"23h0wtzkmmti982q"      // API金鑰
	
#define DEVID		"qwe"                   // 身份

以上資訊,在百度雲建立裝置時,會讓客戶設定。我們只需要把設定的複製,替換上述字串即可。

而我們MQTT的功能實現,則替換以下函式相應的傳參字元即可。

const char *topics[] = {"test"};                    //主題

BD_TG_Subscribe(topics,1);                          //訂閱主題,數量 1

BD_TG_Publish(*topics, "MQTT Publish Test");        //釋出的主題,釋出的訊息

MQTT_UnSubscribe(topics,1);                         //取消訂閱的主題,數量 1

對於MQTT基本的訂閱、釋出,修改上述引數即可完成。

其他功能,則需要根據組包函式的說明,修改相應傳參。

上述的程式碼,均是www.w5100s.com內17.MQTT_BDTG例程內的,該例程已經過實測,可正常連線百度天工雲平臺,實現MQTT訂閱、釋出等相關功能。

W5100S目前已經到貨,煒世已經正式調通並將配套的資料例程補充完畢。

相關資料煒世已經整理上傳至www.w5100s.com網址內,歡迎各位前往瀏覽。

WIZnet W5100S技術交流群:579842114,群內有“大神”為大家答疑解惑。

後續我將會陸續釋出關於W5100S各類功能測試博文,有興趣的朋友歡迎關注。

煒世是WIZnet的的的官方代理商,W5100S可提供模組/評估板供客戶測試。

如需詳細資料/樣品申請/技術支援等,歡迎與我們聯絡。

煒世為客戶提供全程的技術支援與優異的價格優勢。

電話:0755-86568556

QQ:2571856470

郵箱:[email protected]