[ESP32 系列] 初探 ESP32

前言

睽違了三四年沒有碰 Micro:bit 開發版直到去年九月回到學校進修才又開始翻箱倒櫃的找出這個小玩意來玩玩,上學期的課程使用的是專攻邊緣運算的 Jetson Nano,但有了之前買了一堆感應器,最後因為沒時間可以玩,又放著變成電子垃圾的教訓,因此上學期收起我衝動的心,就只有使用實驗室的設備玩玩而已(其實是怕被老婆唸 XD)。

這學期我修了一門資安課,內容相當豐富,不僅探討了網路、資訊安全,還涉及到了密碼學等多個領域,課程的重點放在研究 IoT 設備在資料傳輸上的安全問題,更酷的是,我們還使用 IoT 設備來進行了 RSA 加密的實驗。

老師非常慷慨地給了我們每人一套 ESP32 + OLED + 溫溼度感測器,讓我們可以實際動手操作,通過 RSA 加密來保護這些 IoT 設備在雲端傳輸資料的安全性,雖然課後老師沒有要求我們撰寫實驗報告,但我覺得這些實驗實在太有趣了,而且都花了時間了,怕自己過段時間就會忘記,所以我特在這個網誌把所有的實驗的重要步驟都記錄下來,方便自己以後查詢。


讓我們先談談什麼是 ESP32 吧

ESP32 是一種廣泛用於物聯網(IoT)應用的微控制器,由上海樂鑫(Espressif)公司開發製造,它擁有雙核處理器、Wi-Fi、藍牙等多種通訊功能,以及豐富的周邊接口,因此非常適合用於連接各種感測器、執行控制邏輯、和進行通訊。

ESP32 可以通過 Arduino IDE 等開發工具進行開發,除了 Arduino IDE 外,開發者也可以選擇使用自己熟悉的開發環境進行開發,同時,ESP32 晶片模組也是針對輕巧且適合移動的應用所設計,它擁有低功耗的優點,可以在耗電量很低的狀態下運作,所以很適合用來製作穿戴式裝置的應用。

ESP32 有很多不同版本,每種都各有特色,在我們的實驗中 ESP32-S 使用的是 Nodemcu-32 v1.3 模組的開發版,其硬體規格如下。

  • 模組型號:NodeMCU-32
  • 尺寸大小:25.4(w) x 48.3(h) mm ±0.2 mm
  • 封裝方式:DIP-38 (2.54 標準排針間距)
  • SPI FLASH:預設 32Mbits,最大支援128Mbits
  • 支援接口:UART / SPI / SDIO / I2C / PWM / I2S / IR / DAC / ADC
  • wifi頻率:2400 ~ 2483.5MHz
  • 鮑率速度:300 ~ 4608000 bps,預設 115200 bps
  • 藍牙規格:支援 4.2 BR / EDR 和 BLE 標準
  • 工作溫度:-20 ~ 70 ℃
  • 保存環境:-40 ~ 125 ℃、<90%RH
  • 供電範圍:Micro USB 供電電壓 4.75 ~ 5.25V,建議 5V
  • 供電電壓:3 ~ 3.6V,供電電流>500mA,建議 3.3V


開發板的接腳如下圖,但請注意,開發板的接腳數量與可用接腳可能會略有不同,簡單的說明如下,詳細可自行參考 此篇文章 的說明,或者這個網站 [ESP32] 輸入輸出接腳該如何使用呢?怎麼使用 GPIO 才不會踩雷? 的腳位作用也寫的蠻清楚的。

  • 特殊功能接腳(Flash/PSRAM通訊):GPIO6、7、8、9、10、11,不建議用於輸入或輸出
  • 輸入的接腳:GPIO34、35、36、39,僅能設定為輸入模式
  • USB to UART 通訊接腳:GPIO1(TX0)和GPIO3(RX0),用於與電腦 USB 通訊,也不建議使用
  • Strapping 接腳:GPIO0、2、12、15,不適合用於外部上拉或下拉
  • 其他接腳:包括 GPIO1、3、5、6 ~ 11、14、15,可能在晶片啟動或重置時產生輸出訊號
  • EN/RST 接腳非GPIO接腳):EN接腳與開發板上的EN按鈕相連,可控制ESP32的開啟或關閉。
  • 中斷接腳:所有的 GPIO 接腳都可設定為中斷。

圖片來源 : https://www.circuspi.com/index.php/2023/08/30/esp32-gpio-1/

  • SPI : 快閃記憶體,用於高速資料傳輸 SD Card、TFT、RFID,GPIO 6 ~ 11。
  • Touch Censor : 提供觸控電容。
  • ADC : 類比數位轉換器,可接類比或數位感測器。
  • DAC : 數位類比轉換器,不太需要用,GPIO 25、26。
  • I2C : LCD、氣壓、陀螺儀 ,GPIO 21、22。
  • UART : 藍芽、相機,不限腳位編號。


Arduino IDE 開發工具

Arduino IDE 是一款開源的集成開發環境,用於編寫和上傳程式碼到支持 Arduino 微控制器的硬體上,它提供了簡單易用的介面,並支持多種不同的開發板,包括 ESP32、Arduino Uno 等。使用 Arduino IDE,開發者可以輕鬆地編寫程式碼、編譯和上傳到目標裝置上,從而實現各種應用,包括感測器控制、數據處理、通訊等,是我們這堂課使用的開發工具,底下紀錄一下安裝步驟。

  • 此到 此處下載 並安裝 EPS32-Arduino 開發工具,根據自己的作業系統版本安裝即可,這篇文章記錄時我使用的是 Windows x64 v2.3.2 版。

  • (非必要) 下載 USB 介面轉換成序列通續介面 (COM) 的驅動,ESP32 大部分使用 CH340 或CP2102 的晶片,我們需要使用此 USB轉接口來將開發完成的程式上傳到開發版上 ,課程使用的是 CH340 的晶片,若你的電腦接上 USB 後找不到驅動在安裝即可。

  • 打開裝置管理員,目前的 Windows 或 MAC 都沒有內建 CH340 驅動,因此在你使用 USB 接上 ESP32 後,應該會在其他裝置的地方看到出現一個驚嘆號的 USB Serial,若你的電腦也是如此,請記得回頭安裝上一步驟的驅動程式。 

  • 安裝完驅動後,重新整理裝置管理員,可以在連接埠 (COM 和 LPT) 的地方找到剛剛安裝好的 CH340 晶片,已我的電腦為例,它目前是在 COM4 的 Port。

  • 也可以直接使用開發工具來驗證上驅動是否正確安裝,步驟如下,接上 USB ,打開 IDE → Tools → Port,若正確安裝驅動,則會出現新的 COM Port。

  • 安裝核心開發套件, ESP32 並非 Arduino 原廠產品,我們只是需要使用它的 IDE 來進行開發,因此必須先安裝相容套件後,才可以透過 Arduino IDE 來開發,其安裝步驟如下。
    • 先到 Arduino ESP32 官網找到安裝套件的連結,這裡我使用的是穩定版本 。
      https://espressif.github.io/arduino-esp32/package_esp32_index.json

    • 取得套件路徑後,開啟 IDE,選擇 File → Preferences → Settings → Addition boards manager URLs,輸入剛剛的 json 路徑後,按 OK 完成設定。

    • 接著透過開發版管理員進行核心套件安裝,選擇 Tools → Board → Boards Manager,此步驟會引導你切換到套件安裝的設定頁籤。

    • 輸入 ESP32 後,找到要使用的核心套件版本 (目前最新的版本為 2.0.14) ,按下 Install 後會開始安裝,這需要等候一段時間。

  • 完成上述步驟,再次選擇開發工具, Tools → Board → esp32,看你使用的是哪一種 ESP32 主板來選擇,我使用的版本是 ESP32 Dev Module。

  • 最後選擇開發版的序列埠,Tools → Port → 開發版的 COM Port,完成後就可以準備開始開發了。


來吧,第一個 ESP32 程式

在開始之前還是得先提一下,ESP32 使用 C++ 的語法來進行開發,如果你對該程式語言不熟,也沒有玩過單晶片系統 (System on a Chip,縮寫 SoC) 的經驗,但又想快速體驗的 ESP32 的樂趣的話,該核心套件很貼心的提供了大量的 Sample Code,你可以到 File → Examples 找到跟 ESP32 (請根據你的開發版來選擇) 相關的範例程式,下圖是課程中助教讓我們確認功能是否正常的一個取得晶片 ID 的範例。


回到這正題,許多的程式語言的學習都會以「Hello World」當作第一個範例程式,因此我也不免俗的要使用 Hello World 來當作練習的範例,首先打開 IDE,File →  New Sketch,會開啟一個空白的新專案,預設僅會包含兩個事件,這個跟先前另外一篇文章 Micro Bit 系列] 用積木堆個程式吧 的預設事件是一樣的。

  • setup:這是讓你初始化設定用的地方,例如設定輸出 Baud Rate、GPIO 的腳位等等。
  • loop : 迴圈,讓你寫程式要重複執行的地方。

請在 setup 上面補上下圖的語法後,選則上傳到開發版,對了左邊那個勾是在驗證程式碼是否正確,但其實它也是重新建置一次,但沒有上傳而已,所以後來我都不選擇驗證程式碼了。

如何查看結果

完成了上一個步驟的建置後,當然最期待的就是看到我們的 Hello World 字串了,在開發視窗的右上角有一個像放大鏡的圖示,點擊後會開啟輸出序列的 Monitor 視窗,設定好你的輸出 Baud Rate,然後就可以在這裡看到你程式的執行結果了,就像下面這個圖一樣。


對了,上面好像沒有提到,ESP32 開發版上有兩顆按鈕。如果你建置完成後沒有看到輸出視窗,別急著去找問題,先回頭看一下程式碼,我的寫法是在 setup 事件中設定的,對吧?也就是說,在你打開 Monitor 視窗後已經超過這個事件的執行點了。這時候只需要點選左下角的重新開機 (Reset 鈕),如果真的還是沒有看到輸出結果,再來進一步查問題吧。

圖片來源 : https://www.chosemaker.com/board/esp32/lesson-1/


腦補一下概念

如果你沒有程式開發經驗的話,讓容許我簡單的說明一下建置的原理,如果你是有經驗的高手請自行跳過以下說明。

上面那段程式碼只要寫兩行就能動了,單晶片控制的程式好像也挺簡單的對吧?不過,我們得回到這個開發板的初衷,它的目的是要讓國中、高中的學生也能夠嘗試單晶片控制的程式開發,所以會希望著重在 IO 的使用上。至於那些和硬體溝通的複雜事情,就交給底層去處理吧!記得之前提到的 ESP32 核心套件嗎?這就是它的功用,它幫你處理了大部分的事情,並且提供了方便的輸出函式給你使用。

如果你不相信的話,可以回到剛剛建置程式時的輸出視窗,你會找到一個像是 C:\Users\lawrence\AppData\Local\Temp\arduino\sketches\E4BAF3BCA4882C0101C0E6D26FD2C1C6 這樣的路徑,裡面有一個叫做 core 的資料夾,裡面包含了一些附檔名為 .o 的檔案。這些檔案是 C++ 編譯過程中所需要的。


防毒軟體的坑

完成了第一個範例程式的撰寫後,我開始執行建置,但同學們早就完成了,而我卻等了足足十分鐘才完成。一開始我以為是我的電腦太老舊了,但仔細想想好像不太對,這可是用 C++ 開發的,即便是使用開發套件提供的功能,建置效能也不至於這麼差吧!這可真是個笑話,連網頁前端的 npm install + build 都沒有這麼慢。 

最後,在網路上找到一篇類似的文章,我立刻試著把趨勢防毒的即時掃描關閉,果然問題就是防毒軟體搞的鬼!


改物件導向的寫法

身為軟體開發人員,看到自己的程式碼都寫在同一個迴圈裡面,想必一定很不好受。就算只是簡單的輸出,也應該要將程式碼封裝起來,這樣才能對得起自己的良心,但如果你沒有程式開發經驗,其實也可以直接跳過這一段,沒關係。

上面有提到 ESP32 使用 C++ 的程式語言來開發,因此這個階段的目標是將 HelloWorld 打包成一個類別,讓使用上可以類似底層提供的 Serial.println(""); 的方式來使用。

在後續的文章中,若有時間的話,會分享一下如何把開發好的類別庫打包給其他人使用,但這篇文章僅介紹一下如何改成物件導向的寫法就好,詳細的步驟如下(因為之前只有開發 C#,沒有 C++ 的經驗,若有觀念有錯誤,也歡迎留言跟我分享喔):

  • 在專案相同路徑中新增一個 HelloWorld.h 檔,這個檔案是 C++ 的標頭檔,它宣告了類別的結構,並且用來避免重複引用時的問題。詳細語法說明請參考下面的程式碼。
    // 底下這兩行這是一個預處理器指令,用於確保在同一個程式碼中這個頭文件不會被多次引用
    // 如果 LawrenceLib_HelloWorld_H 這個變數尚未被定義,則執行以下程式碼,否則會跳過
    #ifndef LawrenceLib_HelloWorld_H
    #define LawrenceLib_HelloWorld_H
     
    #include  // 包含 Arduino 的頭文件,這是為了使用 Arduino 平台的庫函式
    #endif
    
    // 這是一個類別的定義,名為 HelloWorld
    class HelloWorld 
    { 
      // Public 宣告,在這這個範圍下的成員會公開給外部呼叫時使用
      public:
        // 建構子,用來初始化 HelloWorld 類別的物件,並包含一個字串作為參數,用來設置預設的名字
        HelloWorld(const char* defaultName);
        
        // 這是一個公開的函式,用來向使用者打招呼
        // 它接受一個字串作為參數,如果未提供參數,則使用預設的名字打招呼
        void sayHello(const char* name = NULL);  
    
      // Private 宣告,在這這個範圍下的成員僅提供內部使用
      private:
        // 這是一個私有的成員變數,用來存儲預設的名字
        const char* _defaultName; 
    };
    
    // 這行聲明了一個 HelloWorld 類別的外部實例,名為 hello
    // 這樣可以在其他文件中使用這個實例,就是類似底層 Serial.println 的用法
    extern HelloWorld hello;
    
  • 在專案相同路徑中新增一個 HelloWorld.cpp 檔,這個檔案包含了實際的程式邏輯。詳細語法說明請參考下面的程式碼。
    // 需告要使用 HelloWorld 類別的標頭檔文件
    #include "HelloWorld.h" 
    
    // 這是 HelloWorld 類別建構子的實作部分
    // 當建立 HelloWorld 物件時,會將一個字串作為預設名字傳遞進來,然後將它儲存在私有成員 _defaultName
    HelloWorld::HelloWorld(const char* defaultName)
    {  
      _defaultName = defaultName;
    }
    
    // 這是 HelloWorld 類別中 sayHello 函式的實作部分
    void HelloWorld::sayHello(const char* name) 
    { 
      Serial.print("Hello, "); 
      Serial.println(name != NULL ? name : _defaultName); 
    } 
    
    // 這是在全域範圍內創建了一個 HelloWorld 類別的物件,名為 hello
    // 並使用 "Lawrence" 作為預設名字來初始化
    // 在程式的其它地方就直接使用 hello 這個物件了
    HelloWorld Hello("Lawrence"); 
  • 使用方式很簡單,在主程式中 include 自定義的標頭檔,然後就可以正常使用了,下面的範例提供了三種呼叫 sayHello 的方式。
    #include "HelloWorld.h" 
    
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(115200);  
      Serial.println("");
    
      // 方法一,直接使用全域 Hello 物件,會顯示預設名稱 Lawrence
      Hello.sayHello();
    
      // 方法二,自己 new HelloWorld 物件,會顯示自己宣告預設名稱 Shen
      HelloWorld Hello2("Shen");
      Hello2.sayHello();
    
      // 方法三,呼叫傳入參數,使用傳入的參數名稱 Lawrence Shen
      Hello.sayHello("Lawrence Shen");
    }
    
  • 在 IDE 開發工具中,除了原本的 Hello.ino(主程式)檔案外,你可以在上方頁籤看到這兩個新增的檔案,並且可以直接在這裡進行開發。

文末有我範例程式的 Git 路徑,若有需要可自行參考喔。


關於 Baud Rate 的小陷阱

Baud Rate 是序列通訊中很重要的東西,但我不打算使用專業術語來解釋,用白話來說,你可以把它想像成國家的語言,兩個不同國家的人如果要溝通,就必須說相同的語言,否則彼此就無法正常溝通。當雙方各自說著不同的語言時,對方就會完全聽不懂。

以 ESP32 的開發板輸出訊號為例子,這塊板子的 baud rate 預設為 115200,如果我們的 Serial Monitor 設定也是 115200,那麼我們就可以正常收到開機時系統拋出來的訊息,就像下面這個例子一樣。


但如果你不使用預設的設定,而是在 Setup 的時候改變了設定,比如說改成了 9600,那麼監控視窗一開始會先收到一堆亂碼,這堆亂碼其實就是上面提到的因為語言不通造成的。所以,如果你在開發時看到輸出視窗拋出一堆亂碼,先別緊張,不是你的程式壞了,只是你們彼此還沒說著相同語言罷了。



本文範例 : Github,使用 Arduino IDE 2.3.2 版本開發。

參考網站

NodeMCU-32 规格书 - 安信可科技

ESP32 教學系列(一):ESP32 簡介

ESP32 教學系列(三):數位輸入與輸出-原理篇

第一篇 ESP32 Arduino開發環境架設(取代Arduino UNO及ESP8266首選)

Getting Started About Arduino ESP32

Arduino IDE 多文件开发

留言