2018年8月1日 星期三

如何使用 MCU 建立與其他 ESP8266 的 UDP 透傳通訊

網頁最後修改時間:2018/08/01

延續前一篇說明如何以手動輸入的方式建立 ESP8266 的透傳模式後,這一篇將以此做為依據撰寫 MCU 程式操作 ESP8266 進入 UDP 透傳模式,並同時與電腦和另一顆 ESP8266 進行 UDP 透傳通訊。

*********************************************************************************
此網頁所用的材料可自行準備,或選用新版本的升級套件
更多 ESP8266 相關商品,請至分類賣場
*********************************************************************************

雖然 AT 指令透傳模式下,ESP8266 不支援多連接,但這是表示:
  • 做為 TCP Client 時,ESP8266 只能接收來自單一 TCP Server 的回傳的資料,但可以跳轉到其他同一區域網路下的其他 TCP Server。
    不過 ESP8266 處於 TCP 透傳模式下,必須要有 TCP Server 存在,否則 AT+CIPSTART 指令一下就會返回失敗;但 UDP 則不會。
  • UDP 透傳通訊下,只能接收來自指定的 RemoteIP 回傳的資料,但不限定要傳送資料給哪一個 UDP 網路裝置 ( 上述限定 AT+CIPSTART 最後一個參數設定為 0 )。
    用戶可以指定要傳送資料到任一個 UDP 裝置,但此時除非雙方裝置已互相設定對方,否則是收不到其回傳的資料的。
本篇接下來以 UDP 透傳通訊為主,通過 Arduino UNO + ESP8266 (WiFi) 開發板 接收兩組來自不同 IP 的 UDP 資料,並且將其回傳到指定的 UDP 裝置上。

架構說明:

整個 ESP8266 UDP 透傳模式的測試架構,如下圖所示。
ESP8266 UDP 透傳模式測試架構圖
最左邊的 "電腦" 是用來釋疑的!原先的測試設計是開發板和 ESP8266 (ESP-01S) 相互通訊 (由測試結構圖中可看出兩者互相通訊 IP 與 Port 相互綁定),但這會讓某些用戶產生 ESP8266 UDP 透傳模式下只能一對一的傳輸誤解。所以在原先設計的架構圖中,特別再利用電腦再創建一個 UDP 連線,說明即便是沒有特別在下 AT+CIPSTART 指令時指定 RemoteIP 地址與 Port 的情況下,只要其他 UDP 裝置知道開發板的 IP 位址與 Port 號碼,也一樣可以傳送資料給開發板;差別在於,開發板回傳的資料只會回傳到先前指令設定好的 UDP 裝置上。
電腦使用 TCP-UDP 軟體設置 UDP 通訊

TCP-UDP 軟體下載 (Windows Store),也可使用 USR-TCP232 (前一篇有下載網址)

上面簡單的說明了整個 ESP8266 UDP 透傳模式的測試架構,至於詳細的過程請參考下面各節的說明:
  • 電腦的 UDP 通訊設定
  • 電腦端的 ESP8266 (ESP-01S) UDP 透傳通訊 (AT 指令)
  • Arduino UNO + ESP8266 (WiFi) 開發板的程式撰寫與模式選擇開關操作說明

電腦的 UDP 通訊設定:

打開 TCP-UDP 軟體切換到 "Settings" 頁面。選擇 "UDP" 傳輸,並根據架構圖 "電腦" Remote 欄位的資訊,輸入資料到相對應 (紅色框框) 的欄位,最後按下 "Connect" 進行 UDP 連線;再提醒一次,這時被連線的裝置並不需要事先建立好 UDP 連線。畫紅線的部分則是說明此軟體 (TCP-UDP) 所代表的 IP 地址,是電腦網路介面卡的。
TCP-UDP Settings 頁面
這時就能切換到 "Terminal" 頁面準備發送資料。

不過有個東西可能需要注意一下!就是傳送的資料字串必須以換行符號 (LF, \n, ASII 10) 作結尾,所以 "Format of Send Data" 欄位必須選擇 "Mix" 才能輸入 16 進位的字元碼;如下圖輸入欄位所示。
TCP-UDP Terminal 頁面
前一個部分有說過,在 TCP-UDP 所發送的字串不會顯示在上方 "Received Mesasges" 的欄位中,而是會經由開發板轉發到架構圖中的中間偏左的 ESP8266 (ESP-01S) 的串口調試助手視窗裡;實際的情況到最後的測試就會看到!

/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
** 使用命令列變更電腦 IP:

有時會特別需要將電腦指定為特定的 IP 地址。以管理者權限開啟命令提示字元視窗,輸入畫面上的指令,就能手動變更。

首先,輸入 netsh int ip show interface 可得到網路介面卡的名稱。

由於電腦端使用無線網路,因此要變更的是網路介面卡是 "Wi-Fi"、IP 地址 192.168.11.3、Netmask 255.255.255.0 和 Getway 192.168.11.1,將這些資料引用到下一個指令變更 IP 地址

netsh int ip set address "WiFi" 192.168.11.3 255.255.255.0 192.168.11.1 1

參考網址

若之後要變回 DHCP,則改用下面指令恢復

netsh int ip set dns "Wi-Fi" dhcp

餐考網址

給特定網路介面指定靜態 IP

電腦端的 ESP8266 (ESP-01S) UDP 透傳通訊 (AT 指令):

這一部分主要是架構圖中間偏左的 ESP8266 (ESP-01S) 藉由 UART 通訊底接到電腦,使用串口調試助手發送 AT 指令建立與架構圖最右邊開發板的 UDP 透傳通訊。這也是最主要的通訊部分,所有傳送到開發板的資料都會轉發到此處。

下面是建立與開發板的 UDP 透傳通訊的 AT 指令集。大致與前一篇的指令差不多,只多了步驟 5 (下面會特別說明)。若之前已經恢復 ESP8266 (ESP-01S) 參數回預設值,則從步驟 3 開始執行即可,詳細的操作過程可參考前一篇網頁。
AT 指令集 - ESP8266 (station mode), UDP 透傳模式

/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 設定 Station 的 MAC 地址 :

AP 和 Station 的 MAC 地址在 ESP8266 無線模組中不可以設定相同,但是有可能兩個 ESP8266 擁有相同的 MAC 地址嗎 ? 這是有可能的,但箇中原由請自行 Google,這裡只討論怎麼解決!

當出現兩顆 ESP8266 擁有相同的 Station MAC 地址時,同時連線到同一個無線路由器時會產生衝突,連線就會產生問題,因此事先透過 AT 指令查詢是否測試的 ESP8266 無線模組擁有相同的 MAC 地址,就可事先預防此問題;別懷疑!就是會發生,所以才有這一小節。

修改 Station MAC 地址可使用下表的  AT 指令,但請提別留意注意事項裡面的最後一點。如此,才能事先確認,避免後面無謂的程式除錯與時間浪費!
AT+CIPSTAMAC

Arduino UNO + ESP8266 (WiFi) 開發板的程式撰寫與模式選擇開關操作說明:

這個網頁的 MCU 測試程式,除了可用所提到的二合一開發板之外,也可單獨使用 Arduino 開發板再自行加上 ESP8266 (ESP-01/01S) 無線模組也行。關於二合一開發板的部分,請繼續往下看各小節的說明。

/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* Arduino UNO + ESP8266 (WiFi) 開發板開關操作說明:

如下圖所示,各方塊內號碼代表開關,通訊元件 CH340 與 ATmega328 (MCU, 微控制器) 和 ESP8266 (WiFi) 經由不同的號碼組合,可使二合一開發板處於不同的開發或通訊模式下。
二合一開發板模式選擇開關示意圖
關於通訊與開發模式選擇就如下表格所示;其中,在中間的三種模式都會常用到。
二合一開發板接腳說明
下面列出兩個在開發時要注意的事項供用戶參考:
  • 插拔開發板上的 MicroUSB 埠時,要先用手按住MicroUSB 埠之後再插拔,避免施力方向或動作不對易造成損傷
  • 同時使用 Arduino UNO 和 ESP8266 功能時 ( 1-2 ON ),勿直接使用電腦 USB 供電 (電流不夠,開發板會一直重置),要使用大電流 ( 至少 1A 以上 ) 的 5V 電源供應器 ( MicroUSB 埠供電 ) 或外部電源供電
模式選擇的開關很小,使用一般前面是金屬的自動鉛筆就很容易撥動;往箭頭撥動就是 ON 的方向。

Arduino 程式要上傳的時候,3-4 ON 其餘 OFF。成功上傳之後,退出電腦 USB 埠,1-2 ON 其餘 OFF,接著變換使用外部電源供電;除非電腦 USB 埠能提供足夠的電流,否則請務必使用外部電源,再次提醒!
二合一開發板模式選擇開關實體圖

/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* Arduino 程式碼:

下面的程式碼與影片中使用相同,當中移除一些冗長的註解並採分段說明,用前請自行整理。

先從主程式的部分看起。

setup()
  • line  2: UART 通訊速率設為 115200 bps
  • line  3: 等待 ESP8266 開機穩定
  • line  5: 設定 Arduino 板載的 LED (D13, L) 為輸出
  • line  8 ~ 9: 單獨重置 Arduino 開發板時,若 ESP8266 沒有一起重置,會導致 ESP8266 一直處於 UDP 透傳狀態無法輸入 AT 指令,所以需在程式開始執行前讓其跳出透傳模式,也能同時確認 ESP8266 已連接且可接受 AT 指令。當此處出現錯誤時,板載 LED 每秒閃一次。
  • line 11 ~ 12: 建立 UDP 透傳通訊。當此處出現錯誤時,板載 LED 每秒閃二次。
  • line 15: 建立 UDP 透傳通訊若沒有出現任何問題,則讓板載 LED 常亮;否則在 line 8 - line 12 出現錯誤時,此燈會以每秒閃爍幾次來指示錯誤發生的地方。
ESP8266UDPPassthrough_Demo00, (01/07), setup()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void setup() {
  Serial.begin( 115200 );
  delay( 3000 );    // wait

  pinMode( _builtin_led, OUTPUT );

  // 退出透傳模式與 UDP 傳輸,回到 AT 指令下
  if( !exitPassthroughMode() )
    ledErrorIndicator( 1 );

  if( !station_createUDPPassthrough() )
    ledErrorIndicator( 2 );  
  
  digitalWrite( _builtin_led, HIGH );
}
** 行號只是為了說明用,不是程式碼實際的行號!

loop()
  • line  3 ~ 4:  接收來自 UART 的字元,並且將其存入到 rcv[] 字元陣列中
  • line  7: 接收到換行符號,表示已接收到一完整字串,可以準備回傳
  • line  8: 加入字串結束字元 
  • line  9 ~ 10: 回傳接收到的字串之前,先輸出頭行 ">>> reply from ESP8266" 再輸出接收到的字串;藉此可用來區分出是從哪一個裝置回傳的資料
  • line 12 ~ 16: 確認字元陣列範圍沒有溢出,如超出範圍則將設為 0。所以輸入的字元再多,也只會輸出字串最後面的部分
ESP8266UDPPassthrough_Demo00, (02/07), loop()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
void loop() {

  if( Serial.available() ) {
    rcv[ rcvIndex ] = Serial.read();

    //--** 收到換行字元,準備輸出**--//
    if( rcv[ rcvIndex ] == '\n' ) {
      rcv[ rcvIndex + 1 ] = 0;
      Serial.print( ">>> reply from ESP8266\n" );
      Serial.print( rcv );      
      rcvIndex = 0;
    } else {
      //--** 確認範圍,避免溢出 **--//
      if( rcvIndex < ( CHARBUFFER - 2 ) ) rcvIndex++;
      else rcvIndex = 0;
    }
  }
}

再來的是程式用到的標頭檔與變數宣告和定義。
ESP8266UDPPassthrough_Demo00, (03/07)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>

// 常亮表示 AT 指令執行正常,閃爍就是出現錯誤!
#define _builtin_led    13

#define CHARBUFFER      102
static char rcv[CHARBUFFER];         // 暫存接收到的字元
static uint8_t rcvIndex;             // 字元暫存位置

bool checkOK( uint16_t m = 0, uint8_t e = 0 );

最後是其他函式的部分。

checkOK 檢查 AT 指令回傳是否正確。
  • line  2: 等待 ms 時間之後再確認 AT 指令回傳的訊息
  • line  4: 確認 AT 指令回傳是否包含 "OK"
  • line  5: 若沒有回傳 "OK" 表示指令執行出現錯誤,依照參數 e 的設定,指定每秒 LED 閃爍的次數 (此函式不返回,會一直重複執行)
  • line  7: 若沒有出現錯誤,返回 true
ESP8266UDPPassthrough_Demo00, (04/07), checkOK()
 1
 2
 3
 4
 5
 6
 7
 8
bool checkOK( uint16_t ms, uint8_t e ) {
  if( ms > 0 ) delay(ms);

  if( !Serial.find( "OK" ) )
    if( e > 0 ) ledErrorIndicator( e );
  
  return true;
}

ledErrorIndicator 使用 LED 閃爍的方式顯示錯誤;當程式出現錯誤時,會一直留在此函式。
  • line  2: 儲存 LED OFF 的時間值
  • line  7 ~ 11:  重複執行 LED 閃爍的動作
ESP8266UDPPassthrough_Demo00, (05/07), ledErrorIndicator()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
void ledErrorIndicator( uint8_t times ) {
  static uint16_t offms[] = {900, 400, 250, 150, 100};
  while(1) {
    for( int i = 0; i < times; i++ ) {
      digitalWrite( _builtin_led, HIGH );
      delay( 100 );
      digitalWrite( _builtin_led, LOW );
      delay( offms[times - 1] );
    }
    delay( 1000 );
  }
}

exitPassthroughMode 避免 ESP8266 停留在透傳模式,使其回到可輸入 AT 指令的模式下。

ESP8266UDPPassthrough_Demo00, (06/07), exitPassthroughMode()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
bool exitPassthroughMode() {

  Serial.println( "AT" );
  if( !checkOK() ) {
    Serial.print( "+++" );    // 退出透傳傳送數據
    delay( 1000 );            // 延遲 1 秒後才能繼續傳送 AT 指令
    Serial.println( "AT" );
    if( !checkOK() ) return false;
  }

  return true;
}

station_createUDPPassthrough 建立 UDP 透傳通訊。

這部分參考前一篇 - ESP8266 (station mode), UDP 透傳模式這一小節裡的 AT 指令集。所不同的是,將所有會自動儲存到 flash user parameters 區域裡的 AT 指令,全部在指令尾部加上 _CUR 讓這些指令只生效在當下。

由於相關 AT 指令在前篇網頁與下面程式碼中都有說明,所以就不再贅述!

ESP8266UDPPassthrough_Demo00, (07/07), station_createUDPPassthrough()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool station_createUDPPassthrough() { // station

  // 建立 station
  Serial.println( "AT+CWMODE_CUR=1" );    // station mode
  if( !checkOK(0, 1) ) return false;
  // 連線到 ROUTER
  Serial.println( "AT+CWJAP_CUR=\"Proteus-WiFi\",\"asdfghjkl\"" );
  if( !checkOK( 5000, 3 ) ) return false;
  // 指定 station 的 IP、Gateway 和 Netmask
  Serial.println( "AT+CIPSTA_CUR=\"192.168.11.11\",\"192.168.11.1\",\"255.255.255.0\"" );
  if( !checkOK( 0, 3) ) return false;
  // Establish UDP Transmission
  Serial.println( "AT+CIPSTART=\"UDP\",\"192.168.11.12\",8888,8889,0" );
  if( !checkOK( 0, 4 ) ) return false;
  // 設定傳輸模式為透傳模式
  Serial.println( "AT+CIPMODE=1" );
  if( !checkOK() ) return false;
  // 開始傳送資料
  Serial.println( "AT+CIPSEND" );
  if( checkOK(0, 5) )
    if( !Serial.find( ">" ) ) return false;

  return true;
}

在程式上傳之前,請確認程式碼編譯沒有問題,以及至少執行過一次 ESP8266 AT 指令 AT+RESTORE,否則執行上很大可能會出現錯誤!

另外,有可能是因為 Arduino 關電的當下沒有以適當的方法退出透傳模式 ( +++ ) 和退出 UDP 傳輸 ( AT+CIPCLOSE ),因此系統重置之後要再馬上重新建立 UDP 透傳通訊,會連不上!若出現此問題時,先全部關電等待至少 15 ~ 30 秒後再重新開電即能正常運行。若還是不行,再延長時間至 1 分鐘,讓無線路由器反應過來再作連線,就會恢復正常運作!

照著網頁中的步驟逐步完成相關的設置之後,就能做到與下面影片相同展示的動作。


結論:

經過連續兩篇網頁說明 ESP8266 AT 指令下透傳通訊的介紹,對比於一般傳輸模式,透傳通訊有著如下的方便之處:

  • 資料傳送之前,( AT+CIPSEND ) 不需指定網路連接 ID 號碼 <link ID> 和字串長度 <length>
  • 資料接收時,不需處理 +IPD 指令
  • 連接斷開時,普通傳輸模式不會重連,直接提示斷開 [,<link ID>] CLOSE;透傳傳輸模式會一直嘗試重連,直到接收到 +++ 退出傳輸,停止重連

經過此篇網頁中實際範例的操作,展示了同樣使用 AT 指令操作的兩顆 ESP8266 無線模組的透傳通訊,也同時展示了如何在電腦端與 ESP8266 透傳通訊的方法,希望對於 ESP8266 透傳通訊說的還算清楚,也能起到幫助的作用!


<< 部落格相關文章 >>

沒有留言:

張貼留言