BC26 還有一組專用於 TCP 通訊的 AT 指令:《BC26_TCP/IP_AT_Commands_Manual_V1.1》,以前已經有了 Socket 能夠進行 TCP 通訊,如今又出一個 TCP/IP。或許就是 C# 中的 Socket 與 TCPClient 之間的關係吧,也有多是早期出了一個簡單版本的可用於 TCP 編程的 Socket,以後又出一個功能更爲強大的 TCP/IP,而爲了兼容老程序,保留了 Socket 而已。總之,前面弄過的東西須要再來一遍。固然 TCP/IP 指令同樣能夠用於 UDP 通訊,本文就再也不講解 UDP 了。編程
Quectel BC26 模塊嵌入了 TCP/IP 協議棧,它使得主機能夠直接經過 AT 指令訪問 Internet。這大大減小了對 PPP 和外部 TCP/IP 協議棧的依賴,從而將成本降到最低。c#
Quectel BC26 模塊提供瞭如下 socket 服務:TCP 客戶端和 UDP 客戶端。緩存
BC26 模塊支持如下兩種類型的數據訪問模式:服務器
當經過AT+QIOPEN
打開一個 socket 服務,可經過參數<access_mode>
來指定數據訪問模式。在 socket 服務開始後,AT+QISWTMD
可用於改變數據訪問模式。網絡
在緩存訪問模式中,數據可經過AT+QISEND/AT+QISENDEX
指令發送。當接收到數據時,模塊將緩存數據並報告一個 URC,格式爲:+QIURC:「recv」,<connectID>[,<current_recv_length>]
。主機可以使用AT+QIRD
讀取數據。併發
注意:在緩存訪問模式中,若是緩存不爲空,模塊將不會報告新的 URC,直到全部收到的數據被
AT+QIRD
從緩存中讀取。socket
AT+QISEND/AT+QISENDEX
指令發送。接收到的數據將直接經過如下 URC 輸出:+QIURC: 「recv」,<connectID>,<current_recv_length><CR><LF><data>
首先介紹本文所使用到的命令。工具
AT+QIOPEN=<contextID>,<connectID>,<service_type>,<IP_address>,<remote_port><local_port>,<access_mode>
打開一個 Socket 服務。編碼
<contextID>
:上下文 ID,範圍 1-3,用來幹啥的我也不懂,通常狀況下設爲 1 就好了。<connectID>
:Socket 服務編號,其實就是以前講過的,BC26 最多支持 5 個 Socket,編號 1-4。<service_type>
:協議類型,"TCP"或"UDP"。<IP_address>
:遠程服務器十進制點分隔 IP 地址。<remote_port>
:遠程服務器端口。<local_port>
:能夠指定本地通訊端口,通常設爲 0,表示讓程序自動分配。<access_mode>
:Socket 服務數據訪問模式,0 爲緩存訪問模式;1 爲直接推送模式。AT+QISTATE=<query_type>,<connectID>
檢查 Socket 服務的鏈接狀態。spa
<query_type>
:指是經過<contextID>
(0)仍是經過<connectID>
(1)來查詢鏈接狀態。通常狀況下都是用 1,即<connectID>
進行查詢。<connectID>
:選擇 5 個 socket 中的一個查詢,範圍 0-4。AT+QISEND=<connectID>,<send_length>,<data>
向服務器發送數據。
<send_length>
:發送數據的長度,以字節爲單位<data>
:發送的數據AT+QISEND=<connectID>
向服務器發送變長數據。發送此命令後,服務器會響應一個>
,此時輸入要發送的數據,並按快捷鍵【Ctrl + Z】便可發送給服務器。
AT+QISENDEX=<connectID>,<send_length>,<hex_string>
十六進制字符串格式發送數據,如AT+QISENDEX=0,5,3031323334
,是向 0 號 Socket 發送長度爲 5 的字符串「01234」。
AT+QIRD=<connectID>,<read_length>
從接收緩存中讀取數據。
<read_length>
:接收的長度,最大值爲 512 字節,通常設置爲 512 更方便,它會自動按緩存中的數據長度接收。AT+QICFG="showlength"[,<show_length_mode>]
設置在收到服務器信息時,顯示的 URC 中是否包含數據長度信息。
<show_length_mode>
:設爲 0 表示不顯示,設爲 1 表示顯示。AT+QICFG="viewmode"[,<view_mode>]
設置在讀取接收緩存中的數據時的顯示格式。
<view_mode>
:
AT+QICLOSE=<connectID>
關閉鏈接。
AT+QPING=<contextID>,<host>
Ping 一個遠程服務器。
<host>
:遠程主機域名或 IP 地址AT+QNTP=<contextID>,<server>
從遠程服務器同步時間。
<server>
:遠程時間服務器域名或 IP 地址。本文使用的例子較多,常常從新鏈接,不能再象上一個程序那樣,每個鏈接就要重啓一次程序。此次程序改成可接收多個鏈接。
using System; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; using System.Text; namespace TCPSocket { class Program { static void Main(string[] args) { //設置服務器 IP,若是是騰訊雲,必須使用內網地址,而不是公網 IP。 IPAddress ip = IPAddress.Parse("172.16.0.11"); IPEndPoint point = new IPEndPoint(ip, 5000); //端口指定爲 5000 Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); s.Bind(point); //開一個單獨的線程去偵聽客戶端鏈接 Task.Factory.StartNew(() => Listening(s), TaskCreationOptions.LongRunning); Console.ReadLine(); //按回車關閉程序 } //偵聽線程方法 static void Listening(Socket s) { s.Listen(5); Console.WriteLine("服務器開始偵聽..."); while (true) { Socket subSocket = s.Accept(); //等待新鏈接 Console.WriteLine("獲取一個來自{0}的鏈接", subSocket.RemoteEndPoint.ToString()); //建立線程接收客戶端的消息 Task.Factory.StartNew(() => ReceiveMessage(subSocket), TaskCreationOptions.LongRunning); } } //監聽客戶端鏈接的線程方法 static void ReceiveMessage(Socket subSocket) { byte[] buff = new byte[1024]; //建立一個接收緩衝區 try { while (true) { int count = subSocket.Receive(buff, buff.Length, SocketFlags.None); //下面這個判斷是很是必要的,不然有可能致使不停地接收到長度爲 0 的數據,致使 CPU 佔用率100% if (count == 0) { subSocket.Close(); return; } //將接收到的數據轉化爲 ASCII 字符 string recvStr = Encoding.ASCII.GetString(buff, 0, count); Console.WriteLine($"接收到數據:{recvStr}"); //將消息原樣返回 subSocket.Send(buff, count, SocketFlags.None); } } catch (Exception e) { Console.WriteLine(e.Message); } finally { subSocket.Close();//客戶端關閉時會引起異常,此時關閉此鏈接 Console.WriteLine($"客戶端{subSocket.RemoteEndPoint.ToString()}已退出鏈接。"); } } } }
啓動程序後,能夠最小化去放心作實驗了,重連多少次都不須要再回來看一眼。
BC26 支持兩種數據訪問模式:緩存訪問模式和直接推送模式。咱們首先介紹緩存訪問模式的操做。
發送數據使用AT+QISEND
和AT+QISENDEX
兩個命令。
//打開 socket 服務,並指定爲緩存訪問模式 >>>>>>>>>> AT+QIOPEN=1,0,"TCP","193.112.19.116",5000,0,0 OK +QIOPEN: 0,0 //URC:兩個參數分別表示 socket 編號和錯誤碼。 //查詢網絡狀態 >>>>>>>>>> AT+QISTATE=1,0 //倒數第三個參數 2 表示已經鏈接網絡 +QISTATE: 0,"TCP","193.112.19.116",5000,0,2,1,0 OK //發送長度爲10個字節的字符串「1234567890」 >>>>>>>>>> AT+QISEND=0,10,1234567890 OK SEND OK +QIURC: "recv",0 //URC:表示 0 號 socket 接收到數據 //發送長度爲5個字節的十六進制格式的字符串「01234」 >>>>>>>>>> AT+QISENDEX=0,5,3031323334 OK SEND OK >>>>>>>>>> AT+QICLOSE=0 //關閉 socket 服務 OK CLOSE OK
此例使用兩種方法向服務器發送數據,第一次是直接發送字符串,服務器返回數據,並報告 URC。第二次發送的是編碼形式的數據,服務器返回數據但沒有報告 URC,由於第一次接收的內容未接收,接收緩存未清空。
發送變長數據需使用AT+QISEND=0
命令,此時服務響應一個>
,表示等待用戶輸入,用戶在輸入數據後,在結尾添加 0x1A 便可向服務器發送無需標明長度的數據。弄懂這個命令費了一些周折。由於文檔寫的是輸入命令後,按下【Ctrl + Z】鍵,上帝啊!我想不出哪一個串口工具可使用【Ctrl + Z】來發送命令啊!全部編輯框裏的【Ctrl + Z】都是用來 Undo 的。後來發現只要在數據的結尾加上 0x1A 發送便可,0x1A 即表明【Ctrl + Z】鍵。毫不能結尾加上回車(0x0D,0x0A),必須是以 0x1A 結束。
本身寫工具的好處就在於自由,想加啥都行,立即加上此功能,版本改成 1.01。以下圖左邊【發送區設置】區域內添加了一個「自動添加【Ctrl+Z】」項,選擇此項後,再點綠色按鈕發送數據,就會自動添加 0x1A 併發送。
按上圖所示設置接收區和發送區選項,打開 TCP Client 腳本,全部命令須要使用右邊腳本面板中每條命令右邊的三角按鈕發送。僅在AT+QISEND=0
命令以後的輸入發送數據時使用發送區進行發送。在選擇「自動添加【Ctrl+Z】」項時,因爲沒法在命令後面添加回車,發送區不能發送命令。如下是完整命令腳本
>>>>>>>>>> AT+QIOPEN=1,0,"TCP","193.112.19.116",5000,0,0 OK +QIOPEN: 0,0 >>>>>>>>>> AT+QISEND=0 > >>>>>>>>>> www.iotxfd.cn //注意,這裏的結尾是 0x1A OK SEND OK +QIURC: "recv",0 >>>>>>>>>> AT+QICLOSE=0 OK CLOSE OK
在使用這種方式進行發送時,還能夠指定基最大發送長度,如AT+QISEND=0,10
,表示只發送 10 個字節。下例演示了這種狀況:
>>>>>>>>>> AT+QIOPEN=1,0,"TCP","193.112.19.116",5000,0,0 OK +QIOPEN: 0,0 >>>>>>>>>> AT+QISEND=0,10 > >>>>>>>>>> www.iotxfd.cn //注意,這裏的結尾是 0x1A www.iotxfd //多出的字符串被截斷 OK SEND OK +QIURC: "recv",0 >>>>>>>>>> AT+QICLOSE=0 OK CLOSE OK
能夠看到,因爲指定了最大長度,多出來的字符串未被髮送。
先來一個最簡單的接收示例:
>>>>>>>>>> AT+QIOPEN=1,0,"TCP","193.112.19.116",5000,0,0 //建立 Socket 服務 OK +QIOPEN: 0,0 //鏈接成功,使用的是 0 號 socket >>>>>>>>>> AT+QISEND=0,10,1234567890 //發送字符串「01234567890」 OK SEND OK +QIURC: "recv",0 //URC:0 號 socket 收到信息 >>>>>>>>>> AT+QIRD=0,512 //接收 0 號 socket 的接收緩衝區,長度 512 +QIRD: 10 //收到 10 個字節 1234567890 //數據爲:1234567890 OK >>>>>>>>>> AT+QIRD=0,512 //再次接收 +QIRD: 0 //指示接收緩衝已空 OK >>>>>>>>>> AT+QICLOSE=0 //關閉 socket 服務 OK CLOSE OK
這種接收方式應當是最經常使用的,每次按最大接收數 512 進行接收,最終只按實際數據長度進行接收,使用起來很是方便。你也能夠指定接收的長度,如:
>>>>>>>>>> AT+QIOPEN=1,0,"TCP","193.112.19.116",5000,0,0 OK +QIOPEN: 0,0 >>>>>>>>>> AT+QISEND=0,10,1234567890 OK SEND OK +QIURC: "recv",0 //URC:0 號 socket 收到信息 >>>>>>>>>> AT+QIRD=0,6 //指定接收長度爲 6 個字節 +QIRD: 6 //接收了 6 個字節 123456 OK >>>>>>>>>> AT+QIRD=0,6 //再次接收 +QIRD: 4 //接收了剩餘的 4 個字節 7890 OK >>>>>>>>>> AT+QICLOSE=0 OK CLOSE OK
可使用AT+QICFG="showlength",1
指令更改收到信息 URC 的顯示方式,讓其指示收到了多少個字節。過程以下圖所示:
可使用AT+QICFG="viewmode",1
更改接收信息的顯示方式:
>>>>>>>>>> AT+QICFG="viewmode",0 //將接收信息顯示方式改成 0 OK >>>>>>>>>> AT+QIOPEN=1,0,"TCP","193.112.19.116",5000,0,0 OK +QIOPEN: 0,0 >>>>>>>>>> AT+QISEND=0,10,1234567890 OK SEND OK +QIURC: "recv",0,10 >>>>>>>>>> AT+QIRD=0,4 //先接收 4 個字節 +QIRD: 4,6 //接收 4 個字節,剩餘 6 個字節 1234 //換行顯示 OK >>>>>>>>>> AT+QICFG="viewmode",1 //將接收信息顯示方式改成 1 OK >>>>>>>>>> AT+QIRD=0,4 //再次接收 4 個字節 +QIRD: 4,2,5678 //接收 4 個字節,剩餘 2 個字節,數據直接在逗號後面顯示 OK >>>>>>>>>> AT+QIRD=0,4 //再次接收 4 個字節 +QIRD: 2,0,90 //只收到了 2 個字節 OK >>>>>>>>>> AT+QIRD=0,4 +QIRD: 0 OK >>>>>>>>>> AT+QICLOSE=0 OK CLOSE OK
從上例可觀察到,在顯示接收數據時,viewmode=0,會換行顯示數據。viewmode=1 則直接在逗號後面顯示數據。
使用直接推送模式會在 URC 中直接顯示接收到的數據,以下圖所示:
很明顯,若是收到的數據量較小,使用直接推送模式會方便不少。
我這裏有兩塊開發板,一塊直接沒法 Ping,另外一塊能夠 Ping,但速度很慢。
>>>>>>>>>> AT+QPING=1,193.112.19.116 OK +QPING: 569 +QPING: 0,"193.112.19.116",32,990,52 +QPING: 0,"193.112.19.116",32,2060,52 +QPING: 0,"193.112.19.116",32,1040,52 +QPING: 0,4,3,1,990,2060,1363 >>>>>>>>>> AT+QPING=1,"www.baidu.com" OK +QPING: 0,"39.156.66.18",32,1560,52 +QPING: 569 +QPING: 0,"39.156.66.18",32,560,52 +QPING: 569 +QPING: 0,4,2,2,560,1560,1060
上述代碼中的 569 爲錯誤碼,表示超時。
時間同步也同樣,一塊開發板沒法用,另外一塊能夠:
>>>>>>>>>> AT+QNTP=1,"ntp5.aliyun.com" OK +QNTP: 0,"20/01/01,12:50:38+32"
新年第一天,泡製完 2020 年的第一篇文章。這個系列得停一段時間,想着仍是得先把 RFID 寫完了再回來繼續。