2020年12月19日 星期六

三、1-2 多重輸出控制


如果可以遠端遙控單一輸出那要擴成到多個就不是甚麼大問題了!在上一個範例中已經介紹過使用”dweet.io”這個雲端網站去控制網際網路上ESP8266的單一輸出腳的方法接下來就讓我們把輸出擴充到多個(在此設定為4個輸出點)

本範例所使用的電路如下圖所示,使用的實驗模組板是標準的NodeMcu,圖中使用了D6、D7和D8這三根接腳,如果以標準的ESP8266來說這三隻接腳分別為:

D6:GPIO12

D7:GPIO15

D8:GPIO13


而這三根輸出接腳分別接上了一顆LED,當輸出為高態時會點亮LED

,這樣我們便可以觀察到接腳的輸狀態了。至於第4顆LED,則是沿用前一個範例的GPIO2這根接腳,不過要這隻腳的LED要低態才會點亮。這樣的混搭有兩個原因,一是可以讓使用者依需要決定用低態或是高態驅動輸出電路,二是ESP8266有些接腳在開機時的狀態必須是低態或是高態,否則可能會無法開機或是進入一些特別的模式,以致無法正常的動作,其中的GPIO15就是這樣的一隻腳,它在開機時必須為LOW,否則會開機失敗!


在之前ESP8266d8單機Soft-AP模式的文章中(網址如後:”http://musthtc100.blogspot.com/2020/05/ap-5-2-esp8266.html”),曾介紹過一塊型號為ESP-202的ESP8266延伸實驗開發板(如下圖所示),圖中標記2的部分是一顆RGB三色的LED模組,它們跟本範例電路一樣分別接到了:


紅(R):GPIO12

綠(G):GPIO15

藍(B):GPIO13


這三根接腳上,而且都是用高態輸出點亮LED。至於標記1的部分則是ESP8266其他可用的輸出腳,也都分別接上了一顆藍色的LED,不過這幾根腳都是用低態來點亮LED。如果手上有這塊實驗版的話,就可以直接套用在本範例上,不用再另外接實驗電路了!



以下是本實習的完整範例程式列表



  1. // Libraries
  2. #include <ESP8266WiFi.h>
  3.  
  4. const char* ssid = "yourSSID"; // 換成你的WiFi SSID名稱
  5. const char* password = "yourPassword"; // 換成你的WiFi 密碼
  6.  
  7. /// Host
  8. const char* host = "dweet.io";
  9. String  dweeting="GET /get/latest/dweet/for/%dweets% HTTP/1.1\r\nHost:
  10. dweet.io\r\nConnection: close\r\n\r\n";
  11. String myThing= "myDweetGetTest2"; // 在Dweet.io所使用的IOT物件名稱
  12.  
  13. const byte  LED_Pin=2;
  14. const byte  LED1=12,LED2=13,LED3=15;
  15.  
  16. void setup() {
  17.  
  18.   // Start Serial
  19.   Serial.begin(115200);
  20.   pinMode(LED_Pin,OUTPUT);
  21.   pinMode(LED1,OUTPUT);
  22.   pinMode(LED2,OUTPUT);
  23.   pinMode(LED3,OUTPUT);
  24.   delay(10);
  25.   digitalWrite(LED_Pin,1);
  26.   Serial.println();
  27.   Serial.print("Connecting to ");
  28.   Serial.println(ssid);
  29.   WiFi.begin(ssid, password);
  30.   while (WiFi.status() != WL_CONNECTED) {
  31.     delay(500);
  32.     Serial.print(".");
  33.   }
  34.  
  35.   Serial.println("");
  36.   Serial.print("WiFi connected : ");
  37.   Serial.println(ssid);
  38.   Serial.println("IP address: ");
  39.   Serial.println(WiFi.localIP());
  40.   Serial.println("");
  41.  
  42. }
  43.  
  44. void loop() {
  45.  
  46.   Serial.print("Connecting to ");
  47.   Serial.print(host);
  48.   Serial.println(".......");
  49.   Serial.println();
  50.   
  51.   dweeting.replace("%dweets%",myThing);
  52. //  Serial.println(dweeting);
  53.  
  54.   // Use WiFiClient class to create TCP connections
  55.   WiFiClient client;
  56.   const int httpPort = 80;
  57.   if (!client.connect(host, httpPort)) {
  58.     Serial.println("connection failed");
  59.     return;
  60.   }
  61.   
  62.   client.print(dweeting);
  63.   delay(10);
  64.   String payLoad="";
  65.   Serial.println();
  66.   Serial.println("[dweet.io Response:]");
  67.   while (client.connected())
  68.   {
  69.     if (client.available())
  70.     {
  71.       String line = client.readStringUntil('\n');
  72.       payLoad+=line;
  73.       Serial.println(line);
  74.     }
  75.   }
  76.   Serial.println();
  77.     Serial.println("[dweet.io Response End!]");
  78.   client.stop();
  79.   Serial.println("\n[Disconnected]");
  80.   Serial.println();
  81.     String content=payLoad.substring(payLoad.indexOf(":{")+1,payLoad.indexOf("}")+1);
  82.       Serial.print("你的Iot ==> ");    
  83.       Serial.println(content);
  84.       Serial.println();
  85.       content.toUpperCase();
  86.       Serial.println(content);
  87.   if(content.indexOf("LED\":\"ON") >= 0) 
  88.   {
  89.      Serial.println("LED is on!");
  90.      digitalWrite(LED_Pin,0);
  91.   }
  92.   if(content.indexOf("LED\":\"OFF") >= 0) 
  93.   {
  94.      Serial.println("LED is off!");
  95.      digitalWrite(LED_Pin,1);
  96.   }
  97.       if(content.indexOf("LED1\":\"ON") >= 0) {
  98.         Serial.println("LED1 is on!");
  99.         digitalWrite(LED1,1);
  100.       }
  101.       if(content.indexOf("LED1\":\"OFF") >= 0) {
  102.         Serial.println("LED1 is off!");
  103.         digitalWrite(LED1,0);
  104.       }
  105.       if(content.indexOf("LED2\":\"ON") >= 0) {
  106.         Serial.println("LED2 is on!");
  107.         digitalWrite(LED2,1);
  108.       }
  109.       if(content.indexOf("LED2\":\"OFF") >= 0) {
  110.         Serial.println("LED2 is off!");
  111.         digitalWrite(LED2,0);
  112.       }
  113.       if(content.indexOf("LED3\":\"ON") >= 0) {
  114.         Serial.println("LED3 is on!");
  115.         digitalWrite(LED3,1);
  116.       }
  117.       if(content.indexOf("LED3\":\"OFF") >= 0) {
  118.         Serial.println("LED3 is off!");
  119.         digitalWrite(LED3,0);
  120.       }
  121.       
  122.   // Repeat every 10 seconds
  123.   delay(5000);
  124. }


在本小節的範例程式中在變數設定的部分和上一小節不同之處如下:


String myThing= "myDweetGetTest2"; // 在Dweet.io所使用的IOT物件名稱

 

const byte  LED_Pin=2;

const byte  LED1=12,LED2=13,LED3=15;


一是把上傳到Dweet.io的IOT物件名稱稍作修改(加了一個‘2’)以便和上一小節不同;二是新增三根輸出接腳分別是GPIO121315。當然在初始化(setup())程式區的地方就必須把這三隻接腳設定為輸出模式了!


  pinMode(LED1,OUTPUT);

  pinMode(LED2,OUTPUT);

  pinMode(LED3,OUTPUT);


至於主迴圈(loop())程式區部分和前一小節的差異在87~120行也就是輸出控制指令從一組增加為四組即增加了LED1~LED3這三隻接腳的測試與判斷由於語法非常類似在此就不多做贅述了。


  if(content.indexOf("LED\":\"ON") >= 0) 

  {

     Serial.println("LED is on!");

     digitalWrite(LED_Pin,0);

  }

  if(content.indexOf("LED\":\"OFF") >= 0) 

  {

     Serial.println("LED is off!");

     digitalWrite(LED_Pin,1);

  }

      if(content.indexOf("LED1\":\"ON") >= 0) {

        Serial.println("LED1 is on!");

        digitalWrite(LED1,1);

      }

      if(content.indexOf("LED1\":\"OFF") >= 0) {

        Serial.println("LED1 is off!");

        digitalWrite(LED1,0);

      }

      if(content.indexOf("LED2\":\"ON") >= 0) {

        Serial.println("LED2 is on!");

        digitalWrite(LED2,1);

      }

      if(content.indexOf("LED2\":\"OFF") >= 0) {

        Serial.println("LED2 is off!");

        digitalWrite(LED2,0);

      }

      if(content.indexOf("LED3\":\"ON") >= 0) {

        Serial.println("LED3 is on!");

        digitalWrite(LED3,1);

      }

      if(content.indexOf("LED3\":\"OFF") >= 0) {

        Serial.println("LED3 is off!");

        digitalWrite(LED3,0);

      }


下圖是使用Google Chrome測試的過程,其中標記1是在瀏覽器的網址欄一口氣輸入了四組輸出的控制指令,即”led=on&led1=on&led2=off&led3=off”,由其內容可以看出,這四組輸出的狀態為:


LED  🡪 On

LED1 🡪 On

LED2 🡪 Off

LED3 🡪 Off


而標記則3是從Arduino IDE監控視窗看到的提示訊息其中包括原來的IoT指令和由ESP8266所發出的輸出狀態訊息其中最下方的訊息就是被控制的四根接腳上所連接LED的亮滅狀態也就是上面指令所要控制的輸出狀態


至於下圖四組輸出的控制指令為”led=off&led1=on&led2=on&led3=o”,四組輸出的狀態為:


LED  🡪 Off

LED1 🡪 On

LED2 🡪 On

LED3 🡪 On


而標記則3是從Arduino IDE監控視窗看到的提示訊息四根接腳上所連接LED的亮滅狀態和上面指令所要控制的輸出狀態吻合



下圖是使用瀏覽器只輸出兩組輸出的控制指令的情形,即” led1=off&led2=off”,由其內容可以看出,這兩組輸出的狀態為:


LED1 🡪 Off

LED2 🡪 Off


而標記則2是從Arduino IDE監控視窗看到的提示訊息其中最下方的訊息是被控制的兩根接腳上所連接LED的亮滅狀態也就是說我們的系統可以分別且單獨的控制特定的輸出不一定要同時控制全部輸出



如果用前面介紹過使用AI2設計的Dweet.io雲端網站測試APP的話,其畫面與過程如下圖所示,在圖的右方是使用電腦AI2模擬器的畫面,我們先在最上方的IoT物件名稱文字盒中入"myDweetGetTest2"這個物件特徵名稱然後在標記1的Dweet IoT物件指令輸入區輸入相關的指令,在此為:


“Led=on&led1=off&led2=on&led3=off”


然後按下【送出】按鈕,便可以把這一串共4個輸出端控制指令一次上傳到Dweet.io雲端網站;標記3是由Dweet.io雲端網站所回傳的上傳成功訊息。至於標記2則是ESP8266定時從Dweet.io雲端網站讀取控制指令後,所萃取出的IoT物件內容,也就是由主控端所發出控制指令;我們的ESP8266系統在測試這些輸出控制指令之後,便會讓對應的輸出腳開啟或關閉。


至於下圖則是只送出兩個輸出腳控制指令的情形。



在這個AI2的APP中,實際上增加了3個可單獨控制新增的輸出腳按鈕,為了節省螢幕的空間,三個按鈕是使用切換式的方式來控制輸出。以下圖來說,標記1的按鈕本來是顯示【SW1開】,當我們按下它之後,按鈕的文字會切換成【SW1關】,為了讓使用者知道正在執行的動作,APP會在下方標記2之處顯示「開啟SW1」的訊息。標記3一樣是ESP8266定時從Dweet.io雲端網站讀取控制指令後,所萃取出的控制指令。



至於下圖,標記1的按鈕本來是顯示【SW3關】,當我們按下它之後,按鈕的文字會切換成【SW3開】,同樣的APP會在下方標記2之處顯示「關閉SW3」的訊息。標記3是ESP8266從Dweet.io雲端網站讀取控制指令後,所萃取出的控制指令最下方則是執行這個輸出指令(LED3=OFF)後的提示訊息。




如果想把這三個按鈕新增到之前的APP上的話,可照著下面的步驟進行;首先如下圖所示,先在AI2的螢幕布局設計(Designer)頁面中插空隙加入一個水平布局元件(HorizontalArrangement5),由於一般上傳的指令內容都不會太長,為了能看到更多的回應訊息,可以適當的把標記1這個文字輸入盒(txbDweets)的高度尺寸縮小,好讓最下面的回應訊息區能顯示更多的資料。此外這個水平布局元件的水平排列屬性設定為置中對齊這樣接下來要放進去的的3個按鈕元件便可以以置中對齊的方式排列讓畫面看起來比較美觀

接著的步驟就是在這個水平布局元件中放上3個按鈕元件在此它們的名稱分別為btnSW1~btnSW3而按鈕上初始的顯示文字則為SW1開】~【SW3開這樣APP螢幕版面的設計工作就完成了。



在手機螢幕上放好新增的3個按鈕元件後接下來就是幫這些按鈕加上程式的方塊;下圖是「btnSW1這個按鈕的方塊程式圖同樣的程式一開始先檢查文字盒「txbIoTName」中的IoT物件名稱是否為空白如果輸入是空白則程式會由Notifier1送出短暫的警告訊息[不要忘了輸入IoT名稱!]

接著檢查btnSW1這個按鈕上的顯示文字是否為SW1開如果是代表按鈕現在應該執行的功能是開啟開關1這根接腳此時程式會執行部分的功能;在此程式會先將要上傳到Dweet.io雲端網站的URL內容部份打包好並設定給Web1這個元件的Url接著btnSW1這個按鈕上的顯示文字改為SW1關最後在下方標記處顯示「開啟SW1」的提示訊息

如果「btnSW1這個按鈕上的顯示文字不是SW1開那麼程式會執行部分的功能也就是關閉開關1這根接腳這部份的程式動作和前面的類似只是由開啟改成關閉而已當然按鈕本身的顯示文字也會改為SW1開。最後呼叫Web1的Get】處理副程式執行上傳到Dweet.io雲端網站的動作。



下面的程式方塊是這個AI2的APP中另外兩個按鈕的程式方塊圖,和上面的內容是一模一樣,差別只是在於所使用的元件號碼不一樣而已,請讀者自行參考。由於主控端和被控端的ESP8266中間隔著中介的Dweet.io雲端網站,再加上上傳和下載都會花一點時間,因此這些輸出控制動作是沒有辦法及時或很快的反應,所以每次動作之間必須保留適當的間隔時間才能正確的動作。



沒有留言:

張貼留言