建立Arduino的Socket即時通訊程式(二)

延續上一篇貼文,本單元的程式修改自Socket.io-v1.x-Library程式庫的“Hello_time”範例,Arduino和Node.js的socket.io建立連線之後,將每隔5秒發送一個事件訊息詢問Node.js目前的時間,Node.js將在收到訊息之後回覆一個事件訊息給Arduino。

Arduino與Node.js的socket訊息交換

實驗材料:

  • Arduino Uno板 × 1
  • 採用W5100晶片的乙太網路擴展板 × 1
  • 執行Node.js的電腦或控制板(如:樹梅派) × 1

Arduino Uno + W5100乙太網路卡 + Raspberry Pi

撰寫Arduino的Socket即時通訊程式

Socket.io-v1.x-Library程式庫支援三種網路晶片,每個晶片都需要引用不同的程式庫,因此,SocketIOClient.h檔案透過#ifdef(代表“if defined”,「若有定義」)和#endif(代表“end if”,「結束if區塊」)前置處理指令,來決定要引用哪些程式庫:

#ifdef和$endif處理指令

所以,Arduino程式必須依照網路晶片類型,在開頭使用#define指令定義W5100, ESP8266或者ENC28J60。本文採用W5100晶片的乙太網路擴展板,程式開頭需要先定義W5100(後面不需要設定任何值),再引用SocketIOClient.h程式庫:

使用#define指令定義W5100

接著宣告SocketIO的程式物件,以及一些變數:

使用extern關鍵字宣告的變數,代表該變數存在於外部程式檔,在程式編譯過程中,編譯器會自行找尋定義該變數的程式檔。

client物件的connect()方法用於連結遠端伺服器,在底下的setup()函式中,程式將在連結成功之後,發送一則socket訊息:

setup()函式

使用F()函式將字串存入Flash記憶體

上面程式裡的序列輸出字串敘述,採用F()函式包裝字串。F()函式能把字串存入Flash(程式記憶體)區域,底下兩個敘述都會在「序列埠監控視窗」輸出“Hello!”:

用F()函式包裝字串

差別在於左邊的寫法,字串會佔用主記憶體,右邊的寫法不會。

在loop()函式中監聽是否有新的Socket訊息

底下的程式將每隔5秒,透過socket送出“Time please?”(請問時間)訊息給Node.js伺服器程式,並且在每一次loop()迴圈中,執行client.monitor()看看是否有新的訊息:

loop()函式

如果有新的訊息,即可從RID, Rname和Rcontent這三個外在(extern)變數,取得socket的事件名稱、訊息名稱和訊息內容

完整的Arduino程式碼如下:

Node.js伺服器端的socket.io程式

本單元的Node.js伺服器端程式改自書本第5章的chat.js程式(位於ch5的socket資料夾),因為該專案路徑已經包含socket.io套件。完整的Node.js程式碼如下:

需要補充說明的是new Date().toJSON()這個敘述,toJSON()方法會把日期物件資料轉換成ISO標準日期格式的簡短字串:

new Date().toJSON()

筆者將此Node程式命名成arduinoSocket.js存入ch5的socket資料夾,先執行此Node程式,再執行Arduino的socket程式,Node伺服器端的命令列(終端機)視窗將出現如下的訊息:

Node伺服器端的命令列(終端機)視窗

Arduino的「序列埠監控視窗」將呈現如下的訊息:

序列埠監控視窗

延伸閱讀

20 thoughts on “建立Arduino的Socket即時通訊程式(二)

  1. 趙老師您好:

    書中5-30頁,Node.js是作網頁伺服器,瀏覽器為用戶端,兩者用socket.io建立即時連線。

    而在這個範例中,老師用Node.js是作網頁伺服器,Arduino UNO+W5100及ESP8266當作用戶端,兩者用Socket.io-v1.x-Library程式庫及socket.io建立即時連線。

    如果Arduino UNO+W5100及ESP8266當作網頁伺服器,瀏覽器為用戶端,兩者用Socket.io-v1.x-Library程式庫及socket.io建立即時連線,程式該如何撰寫呢?

    1. hi alex:

      Socket.io-v1.x-Library程式庫是前端,後端你可以用ESPSocket程式庫,我看了一下它的範例程式,有包含網頁檔案,也具備上傳檔案到快閃記憶體的功能,提供你參考,謝謝!

      thanks,
      jeffrey

  2. 趙老師你好:我不知道我哪裡做錯了,一直無法編譯過,以下是錯誤的訊息,是否能幫助我一下呢?

    In file included from /Users/eagle/Desktop/test_socket/test_socket.ino:2:0:
    /Users/eagle/Documents/Arduino/libraries/Socket.io-v1.x-Library-master/SocketIOClient.h:40:42: fatal error: ESP8266WiFi.h: No such file or directory
    #include //For ESP8266
    ^
    compilation terminated.
    exit status 1
    編譯時發生錯誤

    1. 本文的範例程式適用於W5100晶片的乙太網路卡,請問你有依照上文說明,修改SocketIOClient.h檔嗎?

      thanks,
      jeffrey

  3. Hello CUBIE!
    謝謝您精彩的補充教材!請問採用GPRS實現socket通訊合適嗎?Socket.io-v1.x-Library只支援三種晶片,如果想用Arduino+SIM900模組實現socket請問有推薦的library嗎?

    1. 拍謝,我沒用過GPRS模組。不過,GSM的通訊協定與Arduino程式庫都和乙太網路不同,即便是普通的Web程式也無法直接相互移植。

      thanks,
      jeffrey

  4. 老師您好 前篇的更改.h已經更改過了,但我編譯上面的ARDUINO程式,一直編譯失敗,會出現下面問題
    In file included from sketch_mar30a.ino:4:0:
    C:\Users\User1\Documents\Arduino\libraries\SocketIOClient/SocketIOClient.h:30:22: fatal error: Ethernet.h: No such file or directory
    #include
    ^
    compilation terminated.

    1. 請在你的程式碼開頭加入底下的敘述測試看看,我在1.8.1版編譯Arduino Uno程式沒問題。

      thanks,
      jeffrey

  5. 老師您好:
    文中未提到Ethernet模組連接電腦的方式,
    請問老師是利用什麼樣的環境連結的?node js伺服器的IP是利用固定IP來接收的?

    1. 通常用來當做伺服器端的設備, 都會設定成固定IP. 因為當你需要它的時候, 如果它在固定的位置, 你很容易的就能在同一個地方找到它.
      固定IP還可以分成二種類型, 一種是虛擬IP(常見的為:192.168.X.X, 用於區域網路), 另一種為實體IP(例如:61.219.146.X, 用於外部網路).
      區域網路的概念你可以把它想像成一棟大樓, 對大樓管理員而言, 假設你跟管理員說東西要交給B棟 9F(這個相當於虛擬IP)的潘先生, 他可以很輕易的知道東西要交到本大樓第二棟9F的住戶手裡; 但如果是跟外面的路人說要把東西交給B棟 9F的潘先生, 路人會一頭霧水, 是哪一棟大樓的B棟 9F?
      如果要讓路人也能知道正確的位置, 你必需要跟他講一個實際的地址(例如:XX市XX路378號9F, 這個就是實體IP), 這樣才能找到實際的位置.
      至於node.js伺服器要使用虛擬IP還是實體IP, 就要看你的需求而定了~~~

  6. 趙老師您好
    最近在作個實驗是想用EXCEL讀取馬達所轉的圈數
    並且由EXCEL傳送命令給馬達啟動,運轉至所指定圈數
    ESP8266與NODE.JS以socket.io照您書上的範例+已經完成彼此間的溝通
    問題卡在EXCEL能以甚麼方式與ESP8266或NODE.JS溝通?
    (PS EXCEL的VBA我還可以
    目前試過PLX-DAQ(須有線且一次只能一個COM不符合我的需求
    我的需求將來至少須一次用到5個ESP8266作末端,且須由EXCEL作運算SERVER端)
    苦惱中,想請問趙老師您有甚麼建議方向或思路嗎?
    感謝

  7. 趙老師您好,感謝您的回覆
    以HTTP通訊方式之前有實作過”可行” (EXCEL當client ESP8266當SERVER)
    但礙於
    1.HTTP是單向通訊(P5-27頁)無法”即時”回報SERVER當前的狀態
    (像是收到資料沒有?目前馬達已經轉到第幾圈?工作完成否?)給client端
    2.若以固定時間取查詢SERVER目前工作狀態
    會變成無法即時且EXCEL會在此查詢期間卡住,不符所需
    所以轉SOCKET.IO方式

    以SOCKET.IO作為EXCEL VBA與EPS8266之間的溝通方式
    (google不太到這樣的資料,或這是我google的面向完全不對@@)
    我這外行,除了EXCEL跟VBA及目前您目前兩本大作之外
    其他幾乎全外行

    不知趙老師能否指點一下方向?或我該去認識哪方面的知識來補足?
    感謝

    1. 我想到的方法有兩個,其一是採用雲端Excel方案。像微軟和Google都有推出雲端(在瀏覽器中運作)的試算表,我不知道它們的功能和「桌面」版的差別,如果它們的功能符合你的需求,應該也會提供JavaScript API,如此一來,就能運用WebSocket和其他HTML5的機能了。

      第二個想法是,假設有客戶提出類似的專案需求,我會用互動網頁來解決,因為我對Excel完全不熟,加上Excel的運算式、圖表和表格編輯等功能,JavaScript都做得到,像HandsonTable就是用JavaScript實作的試算表程式庫。

      或說回來,如果你的出發點,主要是因為熟悉Excel…那就是另一回事了。

      thanks,
      jeffrey

  8. 趙老師您好
    感謝您的回答
    您提的兩個方法我會去仔細研究研究
    PS 我的出發點確實是因為”相對熟悉”EXCEL,想要的東西能作出來夠用就好”
    但這次讀了您的兩本書,也google這方面的東西,才驚覺資訊的東西發展了非常多
    過去是夠用就好,現在要夠用要讀很多東西了

    您的兩本書讓我獲益良多,謝謝您

    PS 能否對 P3-41 Postman 這個軟體在您的網站上作些常用的實戰介紹 ?
    下載回來安裝後發現,英文介面加上網路協議不熟稔
    時在有點丈二金剛摸不著頭腦

  9. Google試算表(類似Excel的功能)和Google Docs(類似Word的功能)是採用Https的協定,你可以透過Google App Script來進行數據的傳輸。基於安全性理由,內容會被轉址到script.googleusercontent.com。所以如果ESP8266直接採用POST和GET的request會有問題。我之前是使用一個”HTTPSRedirect”的Library,你可以上Github搜尋”HTTPSRedirect”,內建的範例就是以Google雲端的服務做介紹。

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *