山姆的編程實作分享。。。

Sam Blog, Sam Sharing, Sam Studio

2020年3月23日

[Arduino][ESP8266] 有最佳化你的 ESP8266 ?


[Arduino][ESP8266]
有最佳化你的ESP8266 ?
前言
我寫了以上兩篇文章, 其最終的目的都是在提醒大家,在撰寫ESP8266的程式碼時,請以
Event-Driven 的思維以及callback function 來呼叫 WiFi Library。
但這呼籲也僅是建議,而非絕對,畢竟原廠(Espressif)的 WiFi Library提供了 call directly
以及 Call asynchronous 兩種方式讓大家來使用 WiFi Library,也就是說,你還是可以以直接
呼叫的方式來使用WiFi Library。
但是,就如同我在ESP8266 Event-Driven & Callback Method 這篇文章的文末,自問自答的
那個題目,
Question: 若我不用event-driven 或 callback function 來寫ESP8266的程式碼,不行嗎? 
而我的答案是 “ 可以的”
但同時,我也提出了警告,"若使用call directly”會有你意想不到的後果;所以,這篇就讓我
以實際範例來告訴你這個事實吧 !


範例:WiFi Scan – Call Directly 方式
底下的程式碼是來自ESP8266內建的 WiFi Scan 範例,而非我挖洞,故意設計的喔!
我確實有修改原廠範例的程式碼,但也僅僅加了 “取得系統時間”以及 “LED顯示”,
為了就是顯示call directly 和 call asynchronous 的差異!
首先是 Call directly的方式,相信大部分人都是用這方式在使用 WiFi Scan,因為這段程式碼
是原廠提供的範例啊!

  1. /* 
  2.     This sketch demonstrates how to scan WiFi networks. 
  3.     The API is almost the same as with the WiFi Shield library, 
  4.     the most obvious difference being the different file you need to include: 
  5. */  
  6. #include "ESP8266WiFi.h"  
  7.   
  8. unsigned long time1, time2;  
  9.   
  10. void setup() {  
  11.   
  12.     Serial.begin(115200);  
  13.     pinMode(LED_BUILTIN, OUTPUT);  
  14.     digitalWrite(LED_BUILTIN, HIGH);  
  15.   
  16.     // Set WiFi to station mode and disconnect from an AP if it was previously connected  
  17.     WiFi.mode(WIFI_STA);  
  18.     WiFi.disconnect();  
  19.     delay(100);  
  20. }  
  21.   
  22. void loop() {  
  23.   
  24.     time1 = millis();  
  25.   
  26.     // WiFi.scanNetworks will return the number of networks found  
  27.     int n = WiFi.scanNetworks();  
  28.   
  29.     if (n == 0) {  
  30.         Serial.println("no networks found");  
  31.     } else {  
  32.         Serial.println("Scan by call directly");  
  33.         Serial.printf(" %d network(s) found\n", n);  
  34.   
  35.         for (int i = 0; i < n; ++i) {  
  36.             // Print SSID and RSSI for each network found  
  37.             Serial.printf( "%d: %s, Channel: %d (%ddBm)\n", i + 1,  
  38.                                                             WiFi.SSID(i).c_str(),  
  39.                                                             WiFi.channel(i),  
  40.                                                             WiFi.RSSI(i) );  
  41.         }  
  42.     }  
  43.   
  44.     time2 = millis();  
  45.     Serial.print("Time: ");  
  46.     Serial.println( time2 - time1 );  
  47.   
  48.     digitalWrite(LED_BUILTIN, LOW);  
  49.     delay(100);  
  50.     digitalWrite(LED_BUILTIN, HIGH);  
  51.     delay(100);  
  52.  }  
程式碼重點說明:
Line 27 : 以 call directly 方式呼叫WiFi.scanNetworks。
Line 29 ~ 42 : 在loop()函數內列印scan network 結果。
Line 24, 44 ~46 : 列印出 呼叫WiFi.scanNetworks函數前後的時間差,也就是WiFi.scanNetworks這函數的
執行時間。
Line 48 ~51 : LED做 100ms 的閃爍。


下圖是 Call directly serial port 的列印輸出
從 serial port 的輸出,可看到 Scan network 花費了約2196ms,若你理解這程式碼或實際執行
這程式碼,你應該可以知道或觀看到,LED的燈號閃爍則是無法以100ms 的方式”連續” 閃爍。
因為在loop()迴圈裡,Scan network 大約花了2秒,致使LED 無法以100ms 的方式”連續” 閃
爍。
這樣一切合理 ? 
先不說call directly 方式哪裡有問題,我們先來看看 Call asynchronous方式的程式碼
及執行結果


  1. /* 
  2.     This sketch demonstrates how to scan WiFi networks. 
  3.     The API is almost the same as with the WiFi Shield library, 
  4.     the most obvious difference being the different file you need to include: 
  5. */  
  6. #include "ESP8266WiFi.h"  
  7.   
  8. unsigned long time1, time2;  
  9. bool scanDone;  
  10.   
  11. void scan_OnComplete(int n) {  
  12.   
  13.     scanDone = true;  
  14.   
  15.     if (n == 0) {  
  16.         Serial.println("no networks found");  
  17.     } else {  
  18.         Serial.println("Scan Async");  
  19.         Serial.printf(" %d network(s) found\n", n);  
  20.         for (int i = 0; i < n; ++i )  
  21.         {  
  22.             Serial.printf( "%d: %s, Channel: %d (%ddBm)\n", i + 1,  
  23.                                                       WiFi.SSID(i).c_str(),  
  24.                                                       WiFi.channel(i),  
  25.                                                       WiFi.RSSI(i) );  
  26.         }  
  27.     }  
  28. }  
  29.   
  30.   
  31. void setup() {  
  32.   
  33.     Serial.begin(115200);  
  34.     pinMode(LED_BUILTIN, OUTPUT);  
  35.     digitalWrite(LED_BUILTIN, HIGH);  
  36.   
  37.     // Set WiFi to station mode and disconnect from an AP if it was previously connected  
  38.     WiFi.mode(WIFI_STA);  
  39.     WiFi.disconnect();  
  40.     delay(100);  
  41.   
  42.     scanDone = false;  
  43.     WiFi.scanNetworksAsync(&scan_OnComplete);  
  44. }  
  45.   
  46. void loop() {  
  47.   
  48.     time1 = millis();  
  49.   
  50.      if ( scanDone )  
  51.      {  
  52.          scanDone = false;  
  53.          WiFi.scanNetworksAsync(scan_OnComplete);  
  54.      }  
  55.   
  56.      time2 = millis();  
  57.      Serial.print("Time: ");  
  58.      Serial.println( time2 - time1 );  
  59.   
  60.      digitalWrite(LED_BUILTIN, LOW);  
  61.      delay(100);  
  62.      digitalWrite(LED_BUILTIN, HIGH);  
  63.      delay(100);  
  64. }  
程式碼和call directly的差異說明 (二個差異點):
第一個差異點 :
Line 43 :以 call asynchronous 方式呼叫WiFi.scanNetworksAync,並註冊scan_OnComplete() callback
function
第二個差異點 :
Line 11 ~ 28 : scan_onComplete(),”列印scan network 結果”,從 loop()移到此處

下圖是 Call asynchronous serial port 的列印輸出
從 serial port 的輸出,可看到 Scan network Asynchronous花費了0ms,看到這輸出結果,
你應該猜測到,LED的燈號閃爍應該是以100ms 的方式”連續” 閃爍吧。


Why ?
為什麼 Call Asynchronous 方式, LED 可以連續閃爍,而 Call directly 卻要被 scan network
中斷,致使LED無法連續閃爍呢 ? 不是同一顆 CPU ?
首先,大家要有一個觀念, 晶片和外界溝通有很大部分時間是花費在等待,而且好的
(或說貴的)晶片通常會有獨立控制單元,也就不太需佔用CPU的資源,所以類似scan network
這種request 是可以快速把CPU的執行權交回到User -Task (也就是Loop() )。

第二點,ESP8266只是單核,也算不上有RTOS (雖然存在2個以上的Task),所以call directly
的方式,勢必一定要等到執行有結果;而CPU 這時在睡?
不,他是以最高速運算頻率在等待網路掃描結果喔,也就是說CPU一直在做虛功,
而且還不執行你User-Task內的程式碼!

第三,可以trace 一下 WiFi Library 的程式碼,因為Library 程式碼就已經告訴你,
call directly 的ScanNetwork() 會suspend User-Task,CPU執行權會交給WiFi-MAC,
必須要等到Scan Complete,這段時間 (以scan為例,就是2秒) CPU 是不會執行你的程式碼的。


下一篇,就讓我以來告訴你幾種trace “WiFi Library” 的方便方法吧 !
敬請期待!!!


延伸閱讀

熱門文章