Arduino網路遠端遙控家電開關(二)

本文旨在補充《超圖解Arduino互動設計入門》第十六章「網路家電控制」單元,增加另一個網頁控制範例,以及說明使用指標(pointer)設定多重字串陣列的語法。

本文將把《Arduino網路遠端遙控家電開關(一)》製作的HTML頁面,放到Arduino控制板上執行。首先把網頁裡面,不會變動的HTML碼和CSS樣式,存入Arduino的程式記憶體(相關說明請參閱16-4頁):

底下是Webduino程式庫所需,處理預設命令(亦即:顯示首頁)的程式碼(相關說明請參閱16-4頁):

實際負責顯示網頁內容的程式碼,寫在名叫showPage()的自訂函數中,它接收一個WebServer類型的參數,透過這個參數,程式將能把網頁輸出給用戶端。

定義控制腳位編號及名稱

程式一開始,定義3個常數,儲存Arduino的控制輸出腳位,以及控制腳位的名字(如:”檯燈”和“熔岩燈”),好讓程式能產生動態頁面。

建立動態Arduino頁面

與控制腳位相關的3個常數定義如下:

Arduino官方的Ethernet以太網路控制板,當中的數位腳位編號4,保留給SD記憶卡的晶片選擇線使用,請不要挪做他用。

數位10~13腳是SPI介面的預設腳位,用於控制以太網路晶片,也請不要使用。

建立多重字串陣列指標

我們需要先定義好每個控制腳位的名稱,才能讓後續的迴圈程式取用。如5-15頁提到的,Arduino程式(C語言)採用陣列來存放字串,那麼,要儲存一組字串,就需要用到二維陣列了。

假設要在str陣列中儲存“str 1”, “str 2”和“str 3”這三個字串,底下兩種寫法都行:

儲存多重字串的二維陣列程式

假如每一組字串的長度不固定,元素的長度必須要確保能存入最長的字串,例如,以下的字串陣列中,“Android”佔7 個字元,因此元素的長度至少必須是8。

儲存多重字串的二維陣列程式

但是,那些用不滿8個字元空間的短字串(如:“iOS”),剩餘的空間就浪費掉了。

比較好的寫法是採用指標,如書本8-41頁提到,我們可以用指標變數來存取陣列元素。底下的敘述建立了一個包含3個指標元素的陣列,分別參照到3個字元陣列:

透過指標陣列存取多個字串

用指標記錄多個字串,就不會有空間被浪費掉。

因此,本程式透過一個名叫swNames的指標陣列,儲存6個控制腳位名稱。

動態顯示控制腳位狀態的網頁

讀取控制腳位值,並以HTML網頁形式輸出給用戶端的自訂函數,showPage()的程式原始碼如下,其中的for迴圈,將讀取6個控制腳位狀態,進而動態組成清單項目(<li>標籤)和超連結(<a>標籤)元素。

如果讀取到的數位腳是高電位,就在網頁上顯示ON;若是低電位,則顯示OFF:

動態顯示控制腳位狀態的自訂函數

接收並處理開關的查詢字串值

本單元的範例網頁裡的每個開關選項,都是超連結。每個選項連結都連到Arduino的自訂命令“sw”,並且附帶傳送id參數。例如,第一個選項的連結是“sw?id=0”,也就是以GET方式,傳送id為0的參數給sw頁面(請參閱16-31頁的「接收並處理透過GET方法傳送的查詢字串」單元):

處理查詢字串的流程

本程式的setup()函數,指定將sw頁面的請求,交給自訂函數getCmd處理:

自訂函數getCmd()的程式原始碼如下,因為預期的參數(亦即,id)和數值(0~5),都沒有超過兩個字,因此,筆者將接收URL參數的陣列長度設定成3(含字串結尾的Null字元)。

處理sw自訂命令的函數

下載Arduino網路遠端家電控制的原始碼

按此連結可下載本單元的Arduino原始碼。

Arduino官方的以太網路擴充板(Arduino Ethernet Shield),大約消耗180mA電流(W5100晶片本身約消耗150mA);Arduino UNO Rev 3控制板本身約消耗50mA電流(參閱《認識與實驗Arduino的睡眠模式》這篇貼文),兩者合計約230mA。

測試時如果發生不穩定的狀況,例如,顯示的網頁不完整,或者無法如預期般接收參數控制的情況,請將Arduino控制板接上外接電源再測試,不要接電腦的USB電源供電。

延伸閱讀

160 thoughts on “Arduino網路遠端遙控家電開關(二)

  1. 老師您好!WebServer的問題解決了,但我遇到新的問題是,因為我用的Ethernet版子是Seeed studio出的W200,我有找到他的資料庫,但他的資料庫名稱為EthernetV2_0.h,想請教老師,我只要將原本的Ethernet.h 改為 EthernetV2_0.h就可以嗎?因為我是過這方法後好像會有問題!還是是有需要改其他的地方呢?

    1. 剛剛查看了Seeed studio的W200乙太網路卡的規格,它採用的網路晶片是Wiz5200。Ardiuno原廠的乙太網路卡採用是Wiz5100晶片,而且Webduino程式庫是基於原廠的Ethernet程式庫,應該是這個原因,這個程式庫無法在你的網路卡上運作。

      thanks,
      jeffrey

  2. 老師你好 請問一下

    如果希望每個開關,能同時間定時控制(例如:第1個開關設定30秒開、第2個開關設定1分鐘開),是不是要再多增加timer1、timer2的物件是嗎? 這樣主記憶體吃的很大,還是說其實一個timer1就OK了。

  3. 老師您好!感謝老師的解說!
    想再請教一點,不論是Ethernet shield或是Wifi shield,都一定要在固定IP的環境下才能工作嗎?因為我在租屋處測試好像就已經無法連接網路!

  4. 老師您好
    剛學ARDUINO不久 買了老師的書來看
    想詢問一下
    書本中網路家電控制是使用以太網路卡
    手邊現在只有ESP8266
    請問老師程式需如何修改才能做到一樣透過網頁來控制家電呢?

    謝謝!

    1. hi chimin:

      ESP8266 WiFi無線網路控制模組,預設的韌體採用AT指令操作(如:查詢網路基地台、設定連線、查詢IP位址…等等)。

      目前也有許多自製的ESP8266韌體,像這個韌體支援採用Arduino程式開發,因為它本身內建微控制器,不需要搭配其他Arduino板,可獨立運作。

      但是,因為ESP8266的硬體結構,跟ATmega系列處理器以及本書使用的網路晶片不同,因此很多程式庫並不能直接在ESP8266上運作。

      所以很抱歉,本書並不支援ESP8266。

      Thanks,
      jeffrey

  5. 老師你好:

    之前你有教我使用”SimpleTimer程式庫”
    下面程式說是計時器編號

    // 每一秒執行一次repeatMe函式,並紀錄此計時器編號。
    timerId = timer.setInterval(1000, repeatMe);

    可是計時器編號到9次以上就沒法正常運作 只顯示timerId =-1,這樣往哪邊處理呢?

  6. 了解囉!謝謝老師

    不過增加了MAX_TIMERS常數,動態記憶體吃很大,這有辦法當MAX_TIMERS常數到達設定的數值後給他歸零,重新計數嗎?

    1. 當SimpleTimer的某個計時物件時間到時,它的callbacks陣列值會被歸零,以便讓出儲存空間給下一個計時程式物件;你可以縮小MAX_TIMERS常數值,或者重新設計程式庫。

      thanks,
      jeffrey

  7. 老師你好

    之後研究了一下SimpleTimer的程式庫,還是看不太懂要如何修改程式庫,MAX_TIMERS常數設定10,所以計次最多10次,而超過10次的值都顯示-1,增加MAX_TIMERS常數導致動態記憶體消耗很大,之後有想設條件事方式,當計次大於10後歸0計次,程式庫不能這樣寫….

  8. 請問 我有一個4~20 mA輸出的設備,使用arduino+W5100,簡單的將設備數值顯示在網頁上,可是想從外網去看這個網頁是不是自己一定要有固定IP去NAT才有辦法看,還是有其他方式可以去看?
    假如使用樹莓派+arduino也是一樣需要固定IP嗎?

    1. hi joskyo:

      不一定要有固定IP,但是有固定IP比較方便。只要在網路基地台上設定虛擬非軍事區或虛擬伺服器,即可從外部連入指定的設備。

      thanks,
      jeffrey

  9. 老師你好 我將這遙控家電改成接收數位跟類比的資料,網頁要怎麼去自動更新資料? 數位與類比腳假如沒有接上訊號是否可以判斷出來?

    1. 老師你好 網頁自動刷新有找到語法了,可是出現一個問題我有個點是開關控制,網頁刷新一次他就會跟著變動一次。這方面該怎麼處理?目前暫時google到處找資料 還請老師幫忙解惑。

    2. hi joskyo:

      沒有辦法判斷數位或類比腳是否有連接設備,但是你可以設計一個電路,讓它在週邊設備通電時,輸出一個高電位給Arduino。至於從網頁動態、持續傳遞訊息給伺服器端,需要用到JavaScript/AJAX或者WebSocket程式設計,這兩個主題並沒有包含在Arduino互動設計入門書籍。

      thanks,
      jeffrey

  10. 老師您好~我用您的方法後~~六顆繼電器都能控制(原始碼我只有改MAC與IP,其他都沒有變動)~短時間測試都正常~但是遇到一個問題~~我六個開關同時啟動時(RELAY上的指示燈都有亮,確定有控制到),短時間都正常,但是過了幾分鐘,UNO主板電源附近會變成非常的燙(輸入電源9V),有的時候會控制不了,RELAY上的LED指示燈也會變成比較暗,我是用UNO R3板(露天買的),我一共買三張UNO R3主板,有的會控制不了,有的主板會不斷重新啟動,目前RELAY都沒有接任何設備,純測試~~請問是不是主板上的5V電流不足呢?該怎樣解決呢?

  11. 老師您好~~請問要怎樣外接電源呢?書上有提到嗎?我找了好久都找不到~~我把RELAY 的VCC 與GND接到另外一顆5V變壓器,控制線接到Arduino上~但Relay都沒有反應呢~~

    1. 假設你採用的繼電器模組類似16-36頁(內建電晶體放大電路),請參考11-8頁的伺服馬達外接電源說明,外接電源的接地要和Arduino控制板的接地相連。

      thanks,
      jeffrey

  12. 老師您好~~照您的方法行了 唷!!感謝您!!還有我發現好像直接接行動電源也可以正常使用~~但想請問老師~~如果用行動電源供電會不會造成甚麼問題呢?
    另外我想要請問一下,如果想要在多控制幾組開關,除了1,2,可以用以外,還有別的腳可以用嗎?A0~A5可以使用嗎?是不是跟書上寫的一樣,A0=14 …直接使用就行了嗎?

    1. 外接電源的毫安數通常都夠大,所以可以驅動繼電器和模型馬達。沒錯,類比接腳也可以用。

      have fun!
      jeffrey

  13. 老師
    請問一下,目前測試程式時
    發現每次重整網頁
    就會重複開關最後一次按的按鈕,
    請問要如何修改呢?

    1. hi jack:

      上文範例的每個開關連結,傳送的資料只有開關編號,例如:

      sw?id=0

      根據getCmd()的處理邏輯,每次收到查詢字串時,它就會把該腳位的值反轉。若要確實指定開或關,首先要在查詢字串裡面加入數值,例如,底下的敘述可代表編號0的開關值為1。

      sw?id=0&v=1

      getCmd()也需要加入取出v值的敘述,相關說明和範例請參閱16-31頁。

      thanks,
      jeffrey

  14. 老師您好,本身是使用UNO R3 + W5100舊版的 套上本篇的程式碼 但是就無法顯示網頁(回應時間過長),不過ping倒是ping的到,如果使用clinect.print的話就沒有問題,目前就卡在webserver的方法行不通,測試後連defaultCmd的方法都沒有進來過,我也有使用範例程式碼還有書的光碟的程式碼,都是這個問題,還請老師幫麻解答 感恩!!

    1. hi kenny:

      請問你指的是,使用client.println()輸出網頁沒問題嗎?也就是上傳Arduino IDE軟體內建的「檔案→範例→Ethernet→WebServer」,可以順利執行,但是Webduino程式庫的程式卻不行嗎?

      thanks,
      jeffrey

  15. 老師您好
    書一開始是教使用client.print出html 那部分 是沒問題的
    但是本篇或光碟裡 使用webserver.h 我這裡會有問題
    我事先有安裝好函式庫了,所以編譯上傳都會顯示編譯成功
    (不過好像都會出現
    D:\Coding\Coding Tool\arduino-1.6.8\libraries\WiFi\src\utility\wifi_drv.cpp: In static member function ‘static uint8_t WiFiDrv::getEncTypeNetowrks(uint8_t)’:

    D:\Coding\Coding Tool\arduino-1.6.8\libraries\WiFi\src\utility\wifi_drv.cpp:451:10: warning: converting to non-pointer type ‘uint8_t {aka unsigned char}’ from NULL [-Wconversion-null]

    return NULL;

    ^

    D:\Coding\Coding Tool\arduino-1.6.8\libraries\WiFi\src\utility\wifi_drv.cpp: In static member function ‘static int32_t WiFiDrv::getRSSINetoworks(uint8_t)’:

    D:\Coding\Coding Tool\arduino-1.6.8\libraries\WiFi\src\utility\wifi_drv.cpp:476:10: warning: converting to non-pointer type ‘int32_t {aka long int}’ from NULL [-Wconversion-null]

    return NULL;
    ^)
    卡就卡在當我dhcp取得ip 192.168.1.105
    之後用這ip進入沒有任何回應,出現無法顯示網頁
    ping卻都完全正常可以連線,loop()有成功進入,但webserver.processConnection();似乎沒有動作,因為連httpSuccess這方法都沒有動作,所以沒送出200ok 這跟我設成dhcp有關聯嗎?還是w5100有問題?程式本用函式庫範例跟老師您的還有我自己寫的都沒辦法成功,都是同樣的問題, 感恩!!

    1. 可以達到相同效果,ESP8266使用Wi-Fi而非乙太網路卡,所以程式庫不同,你需要改寫程式碼。

      thanks,
      jeffrey

    2. 主要的原因是本文的網站伺服器採用Webduino程式庫建立,此程式庫是基於Ethernet程式庫,而Ehternet程式庫是基於W5100乙太網路晶片。

      ESP8266也有對應的Ethernet程式庫可用,可驅動W5100晶片並透過有線乙太網路連線,如果你的目的是要透過這種方式聯網,那也許可行。

      但如果你要直接運用ESP8266的Wi-Fi網路,那就得透過ESP8266WiFi和ESP8266WebServer等程式庫來撰寫網路程式,這些程式庫跟本文的Webduino不相容,並不是說不可行,只是你要用Wi-Fi程式庫的語法來重新改寫。

      have fun!
      jeffrey

    3. ” ESP8266也有對應的Ethernet程式庫可用,可驅動W5100晶片並透過有線乙太網路連線 ”
      老師這段的意思是吃WIFI網路再由W5100出去嗎?

      那如果我用ESP8266WebServer是用客戶端連到自己WIFI嗎?

      老師 因為我要把電表各個資料傳到網路上 , 然後能用手機觀看並且能夠操作開關

  16. 老師您好:
    感謝老師的幫助 砍掉原本 + 函式庫 換了1.6.5 從放函式庫回去 沒有問題了!
    謝謝老師!!

  17. 請問老師

    我用 w5100+arduino mega2560

    用老師給的範例 去做測試

    在電腦上網頁都可以正常的控制

    但是我用手機卻連不上網頁

    手機跟電腦是同一台無線AP出來的 電腦接網路線 手機連WIFI

    區段都一樣

    我後來測試 電腦開手機模擬器 可以正常使用

    最後我用arduino的範例 讀取5個類比值

    電腦跟手機 都可以正常使用

    請問是哪裡出了問題

    1. hi miku:

      如果你的手機系統是Android,建議安裝PingTools Network Utilities App,透過它可查看指定IP的裝置連線狀況。

      我覺得應該是HTML的問題,也許是某個標籤指令出錯,讓網頁無法順利呈現,

      thanks,
      jeffrey

  18. 您好,我最近買了一個Webduino,使用Webduino Blockly 編輯器,請問在JavaScript的部分該怎麼撰寫像這樣智慧家電控制系統?

    1. hi allison:

      Webduino是國內廠商的硬體產品,本文採用的Webduino則是Arduino的一個乙太網路程式庫,我沒有用過Webduino硬體產品,至於Google的Blockly,我只稍微玩過幾次,拍謝!

      thanks,
      jeffrey

  19. 老師您好 我有2塊乙太擴展版分別是
    舊版http://www.vetco.net/catalog/images/VUPN5784-1.jpg
    新版http://site.gravitech.us/Arduino/ARD-ETH/ARD-ETH_1.jpg

    不知道為什麼插上舊版就連程式都無法上傳
    新版使用上完全沒有任何問題

    請問舊版是不是有什麼硬體上的問題?

  20. 另外還有個問題是
    我想使用LCD顯示IP位址
    使用dtostrf顯示小數位時都是00 也就是只有整數部分有值
    並且請求網頁時(W5100擴展版會有紅色指示燈閃爍),有時候會自動reset 不用dtostrf就不會這樣
    想請教該如何解決?

    1. 可能是dtostrf函式的buffer不夠大,假設轉換後的字元數為5個字,buffer長度要設定成6,以便容納字串的null結尾。

      thanks,
      jeffrey

  21. “server.printP(htmlHead);” 每次驗證都是這裡的問題 “‘htmlHead’ was not declared in this scope”都會出現這行 想請問這會是哪邊錯了 我程式碼都沒改

  22. 你好,我也是在編譯時都會出現 “‘htmlHead’ was not declared in this scope”問題,我是使用1.6.12版本,請問這該怎麼解決呢?

  23. 不好意思請問一下,我要是單用arduino wifi shield可以直接做出上方的這個範例嗎?

    然後我之前在網路上做arduino+think speak的範例也都連接失敗

    這是因為我的arduino wifi shield不支援嗎?

    因為我之後專題可能會用到這塊板子上傳資料到資料庫中 然後是要透過網際網路來達成我的專題

    但是我目前這一塊arduino wifi shield幾乎網路上只有區域網路的例子

    有哪些是屬於網際網路的例子可以實際演練嗎?

    1. 本例的程式碼僅適用於W5100晶片的乙太網卡,
      使用Wi-Fi Shield,程式要重新改寫。
      至於think speak, 跟你用的網路卡無關,
      也不是區域網路或者網際網路的問題,
      它們都採用相同的乙太網路協定,
      只要了解其中幾個程式的運作原理,
      再延伸它應用,應該不難。

      thanks,
      jeffrey

  24. 老師你好請問一下,如果說我建構一個伺服器(固定IP),將網頁放在server端,Arduino 連線的是手機的Wifi ,請問這樣可以透過server端的網頁去控制Arduino嗎?它們是不同網段的

發表迴響

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