超圖解ESP32深度實作

作者:趙英傑
出版社:旗標科技股份有限公司
出版日期:2021.4.16
頁數:784頁(PDF索引檔下載
定價:NT$880
相關說明:《超圖解ESP32深度實作》新書預告
內容勘誤:《超圖解ESP32深度實作》勘誤以及補充內容
零件清單:《超圖解ESP32深度實作》零件清單

ESP32是一系列高效能雙核心、低功耗、整合Wi-Fi與藍牙的32位元微控器,適合物聯網、可穿戴設備與行動裝置應用。ESP32的功能強大,涉及的程式以及應用場域相關背景知識也較為廣泛,本書的目的是把晦澀的技術內容,用簡單可活用的形式傳達給讀者。

網路上可以看到許多有趣的Arduino電子互動創意作品,歸根究柢,這些電子專案的基本組成架構不外乎:

微電腦控制器

很多創意發想其實用Arduino Uno這塊微電腦控制板,搭配週邊電子模組就能實作出來,像電子看板、體感控制、智慧家電監控、無線遙控小車、物聯網Line訊息通知…等等。我剛好有一本適合電子電路和Arduino程式設計入門的書籍:超圖解Arduino互動設計入門

如果你對電子電路DIY有興趣,但是偏好Python程式語言,推薦這本書:超圖解 Python 物聯網實作入門:使用 ESP8266 與 MicroPython

ESP32支援使用Arduino程式開發,所以你也可以說ESP32開發板是一種「Arduino相容控制板」。在某些場合可以直接用ESP32取代Arduino Uno開發板,就好像逛菜市場的代步車,從電動自行車升級成特斯拉,馬上顯得氣宇非凡,但這兩者達成的結果都一樣。

不同應用有不同的需求,電動自行車不能行駛快車道;汽車無法穿梭小巷。《超圖解ESP32深度實作》這本書著重在ESP32的本質和特色功能,也就是那些用8位元開發板做不到或顯得力有未逮的應用場合,例如:雙核心程式設計FreeRTOS多執行緒程式無線藍牙低功耗(Bluetooth Low Energy,BLE)Mesh(網狀)聯網HTTPS安全加密連線…等。

Mesh網狀網路

ESP32和典型(如:Arduino Uno板)的Arduino程式開發的不同點以及實例說明,全都歸納在本書第二章。

ESP32 RTC GPIO

處理器晶片的功能升級了,程式設計的能力也要跟著提昇,才能發揮與靈活運用ESP32的機能。所以本書從第三章開始介紹程式設計師慣用的「模組化」程式設計手法:物件導向程式設計(OOP),並且應用在後續數個章節。此外,依照開發ESP32程式的需要,書中也以一貫的圖解風格,說明類別繼承虛擬(virtual)函式多載(overload)堆疊(stack)與遞迴記憶體管理回呼函式指標存取結構解析2進位檔…等C++程式設計技術。

ESP32 Arduino程式

在開發過程的每個嘗試、失敗、再嘗試的循環中,不能光靠向前衝的勇氣,還要有技術的底氣;創意很容易抄襲模仿,自己下功夫累積的實力和執行力是別人帶不走的。

另外,微電腦控制器只是整個物聯網應用當中的一個環節,以透過網頁瀏覽器控制某個裝置的應用來說,呈現在瀏覽器的內容是採用HTML和JavaScript 語言開發的互動網頁,和微控器的Arduino程式語言完全不同,所以這本書也圖解說明JavaScript與Web Bluetooth(Web藍牙)非同步程式設計技術(Promise與Async/await)WebSocket網頁UI(使用者介面)操控…等主題。

JavaScript Web Bluetooth

開發微電腦應用程式,偶爾會用到一些小工具程式,例如,呈現在OLED顯示器上的中英文字體與影像,都必須先經過「轉檔」才能嵌入Arduino程式碼,除了使用現成的工具軟體,筆者也示範採用廣受歡迎的Python語言編寫批次轉換字體和影像檔的工具程式。

附錄A的Python藍牙BLE控制程式,也說明了Python非同步(asyncio)多工處理的程式寫法。書中提及的 Python 程式屬於進階應用,筆者假設讀者閱讀過《超圖解Python程式設計入門》,具備 Python 語言的操作檔案目錄、解析命令行參數、 轉換影像、執行緒…等相關概念。

Python非同步、多工處理

《超圖解ESP32深度實作》章節大綱

第1章 32位元雙核心ESP32晶片以及軟體開發工具

1-1∣ESP32的特色與開發板介紹
1-2∣ESP-IDF程式開發框架及menuconfig工具簡介
1-3∣使用Arduino IDE開發ESP32程式
1-4∣在Arduino IDE中編譯ESP-IDF程式
1-5∣ESP32程式開發工具內含FreeRTOS(即時作業系統)

第2章 ESP32開發板與Arduino程式開發應用

2-1∣EP32開發板的接腳
2-2∣ESP32的3個UART序列通訊與printf()函式
2-3∣輸出核心除錯訊息
2-4∣數位輸出/入及電容觸控腳
2-5∣類比輸入埠:讀取MQ-2煙霧/可燃性氣體感測值
動手做2-1偵測煙霧濃度
2-6∣使用ESP32內建的霍爾效應感測器
動手做2-2磁石控制開關
2-7∣PWM輸出
動手做2-3調光器
2-8∣調控PWM訊號的頻率:發出聲音
動手做2-4發出聲音
動手做2-5控制伺服馬達
2-9∣字串處理:String與std::string類型

第3章 物件導向程式設計與自製Arduino程式庫

3-1∣模組化程式設計
動手做3-1可分辨「按一下」和「長按」動作的開關
3-2∣使用enum定義常數數字的集合
3-3∣物件導向程式設計:自己寫程式庫
動手做3-2使用自製的Switch程式庫製作調光器

第4章 中斷處理以及ESP32記憶體配置

4-1∣觸發中斷的時機與中斷服務常式
動手做4-1設定與取消硬體中斷
4-2∣volatile和主記憶體分區
4-3∣分時多工與執行緒
4-4∣解析ESP32的回溯(Backtrace)除錯訊息
4-5∣微波感應偵測物體移動
動手做4-2人體移動警報器
4-6∣計時器中斷
動手做4-3利用計時器定時閃爍LED
動手做4-4用計時器定時閃爍LED之後刪除計時器物件
4-7∣認識堆疊(stack)和堆積(heap)記憶體區域

第5章 OLED顯示器以及Python中文轉換工具程式設計

5-1∣使用OLED顯示器顯示文字訊息
動手做5-1使用U8g2程式庫操控OLED顯示器
動手做5-2在OLED顯示器呈現動態資料
5-2∣全畫面及分頁暫存區(buffer)
5-3∣產生顯示器用的點陣字體子集
5-4∣使用JavaScript和Python取得字元編碼
動手做5-3在OLED螢幕顯示中文
5-5∣點陣VS向量字體:使用FontForge軟體檢視
5-6∣透過Python程式一氣呵成文字編碼和程式輸出

第6章 Wi-Fi無線物聯網操控裝置

6-1∣認識Wi-Fi無線網路
6-2∣使用ESP32的WiFi程式庫連接無線網路
動手做6-1連線到Wi-Fi網路並顯示IP位址和電波訊號強度
動手做6-2建立Wi-Fi無線接入點(AP)
動手做6-3使用Web Server程式庫建立HTTP伺服器
動手做6-4處理GET或POST請求
6-3∣在ESP32的快閃記憶體中儲存網頁檔案
6-4∣透過JavaScript(jQuery程式庫)動態擷取ESP32資料
動手做6-5從ESP32輸出網頁的純文字更新資料
動手做6-6動態網頁調光器

第7章 擷取網路資料以及Python OLED圖像轉換工具

7-1∣網路應用程式訊息交換格式:XML與JSON
動手做7-1讀取JSON格式的世界各地天氣資料
動手做7-2從ESP32讀取氣象網站資料
7-2∣使用ArduinoJson程式庫處理JSON資料.
7-3∣在OLED螢幕顯示天氣概況
7-4∣在OLED螢幕顯示開機畫面(點陣圖)
7-5∣自動批次轉換點陣圖檔的Python程式

第8章 物聯網動態資料圖表網頁

8-1∣從ESP32網站伺服器輸出JSON資料
動手做8-1從ESP32伺服器輸出JSON文件
8-2∣使用chart.js在網頁繪製動態圖表
8-3∣動態新增圖表資料
8-4∣即時動態圖表
動手做8-2呈現即時動態數據圖表

第9章 使用WebSocket即時連線監控聯網裝置

9-1∣使用WebSocket建立即時連線
9-2∣使用JavaScript的WebSocket物件與ESP32伺服器連線
動手做9-1透過WebSocket從ESP32發送JSON資料

第10章 RTC即時鐘以及網路和GPS精確對時

10-1∣再談struct(結構)
10-2∣內建在ESP32晶片內部的月曆和時鐘:即時鐘(RTC)
動手做10-1透過網際網路更新時間
10-3∣在ESP32的Serial2序列埠連接GPS衛星定位模組
動手做10-2連接GPS模組
10-4∣認識NMEA標準格式與獲取GPS的經緯度值
10-5∣解析GPS訊號的經緯度和日期時間資料
動手做10-3使用TinyGPS++程式庫解析GPS訊號

第11章 ESP32的睡眠模式與喚醒方法

11-1∣超低功耗的深度睡眠模式
11-2∣定時喚醒微控器
動手做11-1觸控喚醒微控器
11-3∣搭配網路時間的定時喚醒程式
11-4∣認識ThingSpeak物聯網雲端平台
動手做11-2定時喚醒ESP32並上傳感測資料
11-5∣在深度睡眠中維持接腳的狀態:控制RTC_GPIO接腳
動手做11-3在深度睡眠時維持數位輸出狀態
11-6∣外部喚醒:透過GPIO腳
動手做11-4透過GPIO腳從外部喚醒ESP32

第12章 SPIFFS檔案系統與MicroSD記憶卡

12-1∣快閃記憶體的SPIFFS分區配置與操作
動手做12-1在SPIFFS中寫入與讀取檔案
動手做12-2使用SPIFFS紀錄執行狀態
12-2∣透過網頁表單上傳檔案到ESP32
12-3∣連接microSD記憶卡
動手做12-3使用SD記憶卡提供ESP32伺服器網頁
動手做12-4寫入DHT11溫濕度紀錄到MicroSD記憶卡
12-4∣重複利用既有的程式碼:父類別、子類別與繼承
12-5∣透過指標存取類別物件

第13章 設置區域網路域名、動態顯示QR Code以及OTA更新韌體

13-1∣設置區域網路域名
動手做13-1替ESP32伺服器設定本地域名
13-2∣用QR Code二維條碼呈現網址
動手做13-2在OLED螢幕顯示本機IP位址和QR碼
13-3∣透過OTA更新ESP32的韌體
動手做13-3透過Arduino IDE進行OTA更新
動手做13-4透過網頁表單上傳檔案更新ESP32韌體

第14章 網路收音機、文字轉語音播報裝置與音樂播放器

14-1∣I2S序列音訊介面
14-2∣製作網路收音機的前置作業
動手做14-1網路收音機/Podcast播放器
14-3∣使用Google文字轉語音服務
動手做14-2氣溫語音播報服務
14-4∣認識與解析WAV聲音檔案格式資料
14-5∣驅動I2S週邊播放WAV音檔
14-6∣兼具播放立體聲和單聲道WAV音源的程式
14-7∣使用自訂結構解析WAV音檔標頭

第15章 典型藍牙以及BLE藍牙應用實作

15-1∣藍牙立體聲接收器以及ESP32內部的DAC
動手做15-1 ESP32藍牙立體聲播放器
15-2∣ESP32經典藍牙序列埠通訊程式
動手做15-2 ESP32藍牙序列埠通訊
15-3∣使用Serial Bluetooth Terminal手機App連接藍牙
動手做15-3藍牙SPP一對一連線
15-4∣開發BLE藍牙裝置
15-5∣使用nRFConnect工具軟體檢測BLE藍牙裝置
15-6∣製作ESP32BLE藍牙序列通訊裝置
15-7∣特徵回呼虛擬類別
15-8∣提供BLE藍牙剩餘電量資訊服務
動手做15-4BLE藍牙通知電量
15-9∣偵測負載的電流量
動手做15-5測量負載的消耗電流

第16章 BLE藍牙人機輸入裝置應用實作

16-1∣旋轉編碼器
動手做16-1連接旋轉編碼器
動手做16-2結合Switch類別的旋轉編碼器程式
16-2∣整合BLE藍牙鍵盤與滑鼠的程式庫
動手做16-3BLE藍牙多媒體控制器旋鈕
16-3∣BLE藍牙多媒體鍵盤
動手做16-4連接ESP32與按鍵模組
動手做16-5整合滑鼠與多媒體鍵盤
16-4∣電腦桌面自動切換器
動手做16-6使用VL53L0X飛時測距模組測量距離
動手做16-7電腦桌面自動切換器
16-5∣人機介面裝置(HID)程式庫的原理說明

第17章 FreeRTOS即時系統核心入門

17-1∣認識FreeRTOS以及任務排程
動手做17-1第一個FreeRTOS程式
17-2∣FreeRTOS資料類型
17-3∣FreeRTOS任務的一生
17-4∣任務的優先權與看門狗
17-5∣動態調整任務優先權與刪除任務
17-6∣ESP32的可用記憶體容量以及任務的記憶體用量
17-7∣傳遞參數給任務函式

第18章 FreeRTOS即時系統核心應用

18-1∣ESP32Arduino程式的起始點:app_main()
動手做18-1OLED顯示器任務
18-2∣透過佇列傳遞任務資料
動手做18-2讀取類比值並顯示在OLED螢幕
18-3∣熱敏電阻
動手做18-3在佇列中傳遞結構資料
18-4∣使用旗號(Semaphore)鎖定資源
動手做18-4由多工任務和中斷常式構成的投籃機

第19章 採用HTTPS加密連線的前端與Web伺服器

19-1∣認識HTTPS加密連線
19-2∣檢視與匯出網站的憑證
19-3∣以HTTPS加密連線取得ThingSpeak資料
動手做19-1從ESP32以HTTPS加密連線ThingSpeak
動手做19-2在SPIFFS中存放CA憑證檔
19-4∣使用OpenSSL工具產生自簽的SSL/TLS憑證
19-5∣在ESP32建立HTTPS加密連線的Web伺服器
19-6∣在瀏覽器中匯入自簽憑證

第20章 使用JavaScript操控ESP32BLE藍牙裝置

20-1∣使用瀏覽器探索藍牙裝置
20-2∣JavaScript非同步程式設計
20-3∣替VS Code程式編輯器安裝Live Server伺服器
20-4∣透過navigator.bluetooth物件操控ESP32藍牙裝置
20-5∣讀取與寫入藍牙UART服務的TX和RX特徵值
20-6∣ArrayBuffer(位元組陣列)與DataView(資料視圖)
20-7∣使用async/await改寫藍牙網頁程式
20-8∣藍牙遙控車的雙馬達驅動與控制電路
動手做20-1編寫馬達驅動程式模組
動手做20-2網頁藍牙ESP32遙控車
20-9∣製作藍牙遙控車的互動網頁

第21章 建立無線Mesh(網狀)通訊網路

21-1∣認識與建立Mesh網路
21-2∣調配與執行多工任務的TaskScheduler程式庫
動手做21-1在Mesh網路中分享訊息
21-3∣組建Mesh感測器網路
動手做21-2 Mesh感測器網路的根節點程式
21-4∣在Mesh網路中一對一傳送資料
21-5∣連接Mesh網路與網際網路
21-6∣上傳Mesh網路資料到雲端

附錄A Python Asyncio(非同步IO)多工處理以及BLE藍牙連線程式設計

A-1∣Python多工處理程式
A-2∣使用Python Bleak程式庫連結BLE藍牙裝置

Posts created 483

63 thoughts on “超圖解ESP32深度實作

  1. 我也預購了~這次主要是要看FreeRTOS的部份~因為現在在用的esp8266做不到~
    預祝新書大賣~

    1. 台灣樹莓派的sosorry先生有跟我提到他們會推出零件包,但我不確定上市日期。

      thanks,
      jeffrey

  2. 说起来想问一下有计划推出电子书版本吗?通过正版阅读工具例如kinect购买阅读的那种?

  3. 昨天晚上拿到新書, 今天早上先看完了第一章. 雖然我原本就會使用ESP32, 但書本講的真的很詳細, 有很多原本不知道的內容. 果然這本參考書就是物超所值!!

    1. 目前沒有這個打算,如果要提到LoRa,可能是書本裡的某一個章節。

      thanks,
      jeffrey

  4. 20年前, 学习PRO/E, 是林清安老师的盗版视频. 现在学习Arduino, 看到你的书, 受益匪浅. 谢谢你.

  5. 很喜歡動手做3-1可分辨按一下 和長按 動作的開關 的實作,在3-7頁 的程式內容很經典 但是邏輯有點複雜 要反覆推敲才能大概了解內部連動的邏輯
    不知道能否有狀態流程圖可分享 能夠很清楚整個state machine 如何跑

  6. 3-24 完成switch.h 和 switch.opp 可順利執行 , 進一步3-25 copy .h , .opp 到建立函式庫路徑 \libraries\Switch 後
    .ino 檔 由#include “Switch.h”改 成#include , 原來.ino檔案夾裡的.h 和.opp 檔案要刪掉 不然編譯無法成功

  7. 動手做4-1 中斷練習 可編譯成功 但是load 到板子上後 中斷會成功觸發 但是會一直reboot ,所以內建LED及取消中斷 也無法照程式完整執行

    Guru Meditation Error: Core 1 panic’ed (Interrupt wdt timeout on CPU1).
    rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00

    我試了兩塊板子LoLin32 v1.0.0 及, 也用了您的code試. 錯誤代碼都一樣 不知道問題出在哪裡 ?麻煩幫忙一下

    1. 咦?我剛剛使用搭載ESP32-WROOM-32模組的開發板測試,同樣是LoLin32 v1.0.0 ,執行無誤。Arduino IDE的ESP32開發板管理員版本是1.0.6版。

      ESP32中斷測試

  8. 謝謝您的回覆

    我測試時 只要把這行code 不執行 就不會當機
    //Serial.println(“是在哈囉?”);

    且按10次後(開關並聯三顆27pF電容) ,也會正確執行,在電腦顯示出
    收工啦~

    後面也沒有產生錯誤訊息

  9. 因為現在有在switch 加電容 能較精準控制按ㄧ次
    按一次 會正常顯示 :是在哈囉?

    但再按一次就會顯示reboot 如下 , 最後還會顯示 是在哈囉?
    Guru Meditation Error: Core 1 panic’ed (Interrupt wdt timeout on CPU1).
    Core 1 register dump:
    PC : 0x4008a0fe PS : 0x00060835 A0 : 0x80089376 A1 : 0x3ffbed1c
    A2 : 0x3ffb8a00 A3 : 0x3ffb8890 A4 : 0x00000004 A5 : 0x00060823
    A6 : 0x00060823 A7 : 0x00000001 A8 : 0x3ffb8890 A9 : 0x00000018
    A10 : 0x3ffb8890 A11 : 0x00000018 A12 : 0x3ffc17fc A13 : 0x00060823
    A14 : 0x007bee78 A15 : 0x003fffff SAR : 0x0000001b EXCCAUSE: 0x00000006
    EXCVADDR: 0x00000000 LBEG : 0x40085fd9 LEND : 0x40085fe9 LCOUNT : 0xfffffffb
    Core 1 was running in ISR context:
    EPC1 : 0x400d8e93 EPC2 : 0x00000000 EPC3 : 0x00000000 EPC4 : 0x00000000
    Backtrace:0x4008a0fb:0x3ffbed1c |<-CORRUPTED
    Core 0 register dump:
    PC : 0x4008a27b PS : 0x00060035 A0 : 0x80088fa3 A1 : 0x3ffbe7cc
    A2 : 0x3ffbee78 A3 : 0xb33fffff A4 : 0x0000abab A5 : 0x00060023
    A6 : 0x00060021 A7 : 0x0000cdcd A8 : 0x0000abab A9 : 0xffffffff
    A10 : 0x3ffc1604 A11 : 0x00000000 A12 : 0x3ffc1600 A13 : 0x00000007
    A14 : 0x007bee78 A15 : 0x003fffff SAR : 0x0000001a EXCCAUSE: 0x00000006
    EXCVADDR: 0x00000000 LBEG : 0x00000000 LEND : 0x00000000 LCOUNT : 0x00000000
    Backtrace:0x4008a278:0x3ffbe7cc |<-CORRUPTED
    ELF file SHA256: 0000000000000000
    Rebooting…
    ets Jun 8 2016 00:22:57
    rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:1
    load:0x3fff0030,len:1184
    load:0x40078000,len:12804
    ho 0 tail 12 room 4
    load:0x40080400,len:3032
    entry 0x400805e4
    是在哈囉?

    1. 我沒有加電容,在兩個不同的ESP32板子上測試也沒問題,比較可能原因應該是編譯環境不一樣,請問你的ESP32開發板管理員是第幾版?請用1.0.6版編譯測試看看,謝謝!

  10. 我直接安裝 Espressif Systems 1.06版 問題解決了
    不好意思 我忘了看 之前是安裝那一個版本.
    謝謝你花時間分析問題的環節

  11. 赵老师,您好。
    最近买了您的书,在学习,发现一个问题
    第1个测验ESP32主板的小程序,有小问题,少了一行代码;

    int LED_BUILTIN = 2;

    在初始化小灯之前,必须 先定义小灯的 ID值 2,这是主板的上LED编号 ,所以必须 对LED_BUILTIN 这个变量进行赋值 ,否则编译报错“error: ‘LED_BUILTIN’ was not declared in this scope”

    debug之后的程序应如下:

    int LED_BUILTIN = 2;

    void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
    }

    void loop() {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(1000);
    digitalWrite(LED_BUILTIN, LOW);
    delay(1000);
    }

  12. 感谢赵老师如此快速回复。您的几本书我都在网上采购了原版,很喜欢这种图解的风格,这四本书的选材方向也很好,主攻IoT、智能硬件、人工智能方向,一方面是创客领域,另一方面是当下发展大趋势。我可以说是您大陆粉丝:),您讲的内容非常好,浅显易懂,举例也非常好,很实用,感谢赵老师。

  13. 趙 老師您好,
    很喜歡您的手繪風格,
    想請教是如何製作的,
    是用什麼軟體,

  14. 趙老師您好
    我做您的超圖解esp32深度實作實驗
    做到6-2 (WiFi)之後
    每一個程式編譯都出現了undefined reference to app_main錯誤訊息(原先沒有)
    程式也都無法燒錄進板子
    但是其他用Arduino Uno的實驗都不受影響
    想請教您這樣的問題該如何處理呢?
    以及為何會突然出現這樣的錯誤訊息呢?
    謝謝您

    1. 剛剛測試:

      Arduino IDE 1.8.19
      ESP32開發板編譯環境1.0.6
      開發板選用WEMOS LOLIN32

      測試無誤。

  15. 老師你好,謝謝你開發這本ESP32 深度實作!

    我在測試diy14-2.ino(有關把溫度用google 語音讀出溫度)的範例而遇到一些問題:

    1. audio.connecttospeech(msg, “zh”);
    During the compile in Arduino IDE, it found compiled error : no matching function for call to ‘Audio::connecttospeech(String&, const char [3])’
    So, I modified msg to msg.c_str() , ie audio.connecttospeech(msg.c_str(), “zh”); which complied without any issue finally.

    *Note: I downloaded the original source of ESP32-audioI2S-master.zip without any modification*
    Question: Why your code is used “msg” without c_str() which worked for you ? is it related to esp32’s compile version ?

    2.連上網絡後,第一次可聽到語音傳回來後,傳出以下錯誤訊息,請老師自指教問題出在那裡。
    [ 38351][E][WiFiGeneric.cpp:1438] hostByName(): DNS Failed for 現在溫度攝氏16.11度,真是的好熱啊!

  16. 你好编辑:
    F1794-master\ch06\doy6-6\data\www\index.html原始描述
    LED開關:


    其中的value=\”ON\”应改为\”on\” ;value=\”OFF\”应改为\”off\”。
    因为doy6-6.ino文件中的程序为:
    server.on(\”/sw\”, HTTP_GET, [](AsyncWebServerRequest * request) {
    if (request->hasParam(\”led\”)) {
    AsyncWebParameter* p = request->getParam(\”led\”);
    Serial.print(\”led:\”); Serial.println(p->value()); //加了这行代码才看出来的。
    if (p->value() == \”on\”) {
    digitalWrite(LED_BUILTIN, LOW);
    } else if (p->value() == \”off\”) {
    digitalWrite(LED_BUILTIN, HIGH);
    }
    }

  17. 一下买了四本,支持一下赵老师的良心之作。当作藏书也是可以的,hahahh

  18. 修改了”圖解ESP32″一書12-4,將溫溼度寫入SD卡的範例
    自己想要只在一開始使用ntp做網路校時,以後睡眠之後就直接使用localtime,不用再執行ntp
    假設第一筆是17:00,接下來時間會變成9:10, 9:20(剛好少了八小時)
    後來發現timezone的設定會跑掉,應該是睡眠之後這部分設定就不見了,看了configTime()的實作將

    code修改成
    configTime(0 , daylightOffset, ntpServer); //不做timezone修正

    void saveData() {
    float t = dht.readTemperature();
    float h = dht.readHumidity();

    setTimeZone(-utcOffest, daylightOffset);//在這裡做timezone校正

    time_t now = time(NULL);
    struct tm *localtm = localtime(&now);

    //這個function是從configTime()裡面搬過來的,最後執行了tzset
    static void setTimeZone(long offset, int daylight)
    {
    char cst[17] = {0};
    char cdt[17] = “DST”;
    char tz[33] = {0};

    if(offset % 3600){
    sprintf(cst, “UTC%ld:%02u:%02u”, offset / 3600, abs((offset % 3600) / 60), abs(offset % 60));
    } else {
    sprintf(cst, “UTC%ld”, offset / 3600);
    }
    if(daylight != 3600){
    long tz_dst = offset – daylight;
    if(tz_dst % 3600){
    sprintf(cdt, “DST%ld:%02u:%02u”, tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60));
    } else {
    sprintf(cdt, “DST%ld”, tz_dst / 3600);
    }
    }
    sprintf(tz, “%s%s”, cst, cdt);
    setenv(“TZ”, tz, 1);
    tzset();
    }

    這過程學到了,timezone的設定,會在進入深度睡眠之後消失(應該就是因為重新開機了吧)

  19. 我一下就买了赵老师的两本书,台版的电子IT书籍质量高,这两本书也不例外。我因为最近关注音频应用比较多,所以直接从chapter14开始,都是很好的素材。但14-37页有一个关于指针的tiny小问题,值得商榷:

    uint32_t* pcmSize;

    memcpy(&pcmSize,dawData+40,4);


    =========================
    这个pointer declare 可能有问题,据https://stackoverflow.com/questions/48833976/failing-to-understand-what-the-expression-uint32-t-does 指出

    uint32_t* ptr;
    declares a pointer of type uint32_t*, but the pointer is uninitialized, that is, the pointer does not point to anywhere in particular. Trying to access memory through that pointer will cause undefined behaviour and your program might crash.

    所以应改成
    “”
    uint32_t pcmSize;

    memcpy(&pcmSize,dawData+40,4);


    两个程序我都验证过了,第一个程序也可以得到正确的data size, 但对指针变量进行取地址的操作,让人困惑;第二个程序就比较明了,容易读懂。

  20. 老師您好,謝謝你開發這本ESP32 深度實作!
    我在測試diy21-5 mesh_hub.ino(連接Mesh網路與網際網路)的範例,測試很多天嘗試很多方法,都無法連接WiFi網路以及設定網路伺服器。畫面如下:不知那裏出問題。麻煩老師協助,謝謝!

    rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:1
    load:0x3fff0030,len:1184
    load:0x40078000,len:12804
    ho 0 tail 12 room 4
    load:0x40080400,len:3032
    entry 0x400805e4

    setLogLevel: ERROR | STARTUP | CONNECTION |
    STARTUP: init(): 0
    STARTUP: AP tcp server established on port 5555
    AP模式的IP位址:10.51.29.1
    我是小A,ID:3944100637
    CONNECTION: New AP connection incoming
    CONNECTION: painlessmesh::Connection: New connection established.
    CONNECTION: newConnectionTask():
    CONNECTION: newConnectionTask(): adding 2222538461 now= 16735952
    新的連線,nodeId: 2222538461
    新的連線,JSON結構:
    {
    “nodeId”: 3944100637,
    “root”: true,
    “subs”: [
    {
    “nodeId”: 2222538461,
    “subs”: [
    {
    “nodeId”: 2653399405
    }
    ]
    }
    ]
    }
    連線產生變化了
    節點數: 2
    連線列表: 2222538461 2653399405
    溫度:31°C, 濕度:72%, 節點:阿B, ID:2222538461
    亮度:1452, 節點:笑CC, ID:2653399405
    溫度:31°C, 濕度:72%, 節點:阿B, ID:2222538461
    亮度:1444, 節點:笑CC, ID:2653399405
    溫度:31°C, 濕度:72%, 節點:阿B, ID:2222538461
    CONNECTION: stationScan(): nr3469-note
    CONNECTION: eventScanDoneHandler: ARDUINO_EVENT_WIFI_SCAN_DONE
    CONNECTION: scanComplete(): Scan finished
    CONNECTION: scanComplete():– > Cleared old APs.
    CONNECTION: scanComplete(): num = 2
    CONNECTION: Found 0 nodes
    亮度:1452, 節點:笑CC, ID:2653399405
    溫度:31°C, 濕度:72%, 節點:阿B, ID:2222538461
    亮度:1456, 節點:笑CC, ID:2653399405
    溫度:31°C, 濕度:72%, 節點:阿B, ID:2222538461
    亮度:1448, 節點:笑CC, ID:2653399405
    溫度:31°C, 濕度:72%, 節點:阿B, ID:2222538461
    亮度:1452, 節點:笑CC, ID:2653399405
    溫度:31°C, 濕度:72%, 節點:阿B, ID:2222538461
    亮度:1452, 節點:笑CC, ID:2653399405
    溫度:31°C, 濕度:72%, 節點:阿B, ID:2222538461
    亮度:1452, 節點:笑CC, ID:2653399405
    溫度:31°C, 濕度:72%, 節點:阿B, ID:2222538461
    CONNECTION: stationScan(): nr3469-note
    CONNECTION: eventScanDoneHandler: ARDUINO_EVENT_WIFI_SCAN_DONE
    CONNECTION: scanComplete(): Scan finished
    CONNECTION: scanComplete():– > Cleared old APs.
    CONNECTION: scanComplete(): num = 2
    CONNECTION: Found 0 nodes
    亮度:1452, 節點:笑CC, ID:2653399405
    溫度:31°C, 濕度:72%, 節點:阿B, ID:2222538461

    1. 從訊息看來,你的ESP32 Mesh節點有相連。請問你有試過21-34頁說明的mesh_thingspeak資料夾裡的mesh_A.ino程式嗎,該程式可讓Mesh網路的根節點連接到你指定的Wi-Fi分享器。

  21. 執行mesh_thingspeak資料夾裡的mesh_A.ino程式,好像無法連上:
    setLogLevel: ERROR | STARTUP | CONNECTION |
    STARTUP: init(): 0
    STARTUP: AP tcp server established on port 5555
    AP模式的IP位址:10.51.29.1
    我是小A,ID:3944100637
    CONNECTION: New AP connection incoming
    CONNECTION: painlessmesh::Connection: New connection established.
    CONNECTION: newConnectionTask():
    CONNECTION: newConnectionTask(): adding 2222538461 now= 16136856
    新的連線,nodeId: 2222538461
    新的連線,JSON結構:
    {
    \”nodeId\”: 3944100637,
    \”root\”: true,
    \”subs\”: [
    {
    \”nodeId\”: 2222538461,
    \”subs\”: [
    {
    \”nodeId\”: 2653399405
    }
    ]
    }
    ]
    }
    連線產生變化了
    節點數: 2
    連線列表: 2222538461 2653399405
    亮度:1456, 節點:笑CC, ID:2653399405
    HTTP請求出錯啦~
    溫度:32°C, 濕度:75%, 節點:阿B, ID:2222538461
    亮度:1456, 節點:笑CC, ID:2653399405
    HTTP請求出錯啦~
    溫度:32°C, 濕度:75%, 節點:阿B, ID:2222538461
    亮度:1448, 節點:笑CC, ID:2653399405

    1. 我猜想你的ESP32開發板管理員是2.x版,Painless_Mesh程式庫是用舊版的ESP-IDF開發的,請把ESP32開發版管理員改成1.0.5或1.0.6再編譯上傳試試。

  22. 可以了!這一段改成:
    mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT, WIFI_AP_STA, 6 );
    結果如下:
    CONNECTION: eventSTADisconnectedHandler: ARDUINO_EVENT_WIFI_STA_DISCONNECTED
    CONNECTION: eraseClosedConnections():
    CONNECTION: eventSTADisconnectedHandler: ARDUINO_EVENT_WIFI_STA_DISCONNECTED
    CONNECTION: eraseClosedConnections():
    網站伺服器IP位址:192.168.231.76
    CONNECTION: eventSTAGotIPHandler: ARDUINO_EVENT_WIFI_STA_GOT_IP
    亮度:400, 節點:笑CC, ID:2653399405
    HTTP回應碼:200、回應本體:72
    溫度:32°C, 濕度:71%, 節點:阿B, ID:2222538461
    亮度:407, 節點:笑CC, ID:2653399405
    HTTP回應碼:200、回應本體:0
    溫度:32°C, 濕度:71%, 節點:阿B, ID:2222538461

    為什麼我也不知道,因為我google很久猜出來的。

    1. 感謝告知!如21-5頁的語法說明,WIFI_AP_STA參數代表「驗證模式」,可能是你的Wi-Fi路由器不支援預設的驗證模式。數字6是頻道號碼,可不填。

  23. 老師您好,想請教有關 13-3 OTA 更新的問題,我照著參考書的內容實作了一遍,但是在工具/序列埠功能表看不到網路連接埠,不知如何解決,麻煩老師協助,謝謝!
    (Arduinoi IDE 為 2.3.2 版,Python 為 3.12.3 版)

    1. 咦?我測試沒問題ㄟ,請問你的WiFi名稱和密碼有設置正確嗎?還有,IDE需要一點時間才會更新連線。

      ESP32 OTA

  24. WiFi 名稱和密應該是正確的,為了測試這點我開了一個 HTTP Server,可以正常連上網頁,上傳程式碼後也等了一些時間,但在功能表還是看不到網路連接埠。因為在我的使用情境中,網頁表單另有用途,所以暫不考慮 13-4 的作法,希望能實現透過 OTA 更新。
    (抱歉不知道怎麼上傳圖片,沒辦法截圖給老師看)

    1. ESP32的OTA功能支援mDNS協定,請嘗試在Windows上安裝蘋果的Bonjour Print Services看看。
      另外,應該不是防火牆的問題,但也請查看你的系統是否阻擋了3232埠的連線。

  25. 找到問題出在哪裡了,之前 ESP32 跟電腦連到不同的 WiFi,只要連相同 WiFi 就能看到網路連接埠了,也能順利更新,感謝老師的回覆。

  26. 老師您好,
    依照您第4章的學習, 寫了一個中斷的程式, 從霍爾流量模組執行中斷, 然後在執行 Sersial.print()是正常的, 可是我要將霍爾流量數據顯示在 I2C介面的LCD1602, 卻會出現問題.
    只要霍爾流量一有數據, 執行了中斷程序, 就會馬上重新啟動.
    我有把老師提醒的一些中斷要注意的事項都寫入程式, 但還是會一直重新啟動. 這個狀況找了快2個星期但都沒能解決, 可以請老師撥空解惑嗎?

    我的程式碼放在以下, 可以請老師參考嗎?
    ================================================================
    #include //導入I2C函式庫
    #include //導入LCD1602函式庫
    LiquidCrystal_I2C lcd(0x27); //宣告I2C位址

    float vol = 0.0,l_minute; //宣告變數-流量值
    unsigned char flowsensor = 15; //宣告變數-霍爾流量器腳位
    long currentTime; //宣告變數-目前時間
    unsigned long cloopTime; //宣告變數-開始有流量的時間值
    volatile float flow_frequency = 0; //宣告變數-霍爾感應的頻率變數, 加上volatile宣告

    void IRAM_ATTR flow() //中斷呼叫執行函式
    {
    flow_frequency++; //中斷發生代表有霍爾感應值, flow_frequency累加值
    }

    void setup()
    {
    Serial.begin(115200);
    pinMode(flowsensor, INPUT); //霍爾流量器腳位設為INPUT
    digitalWrite(flowsensor, HIGH); // Optional Internal Pull-Up
    attachInterrupt(flowsensor, flow, RISING); //中斷設置
    currentTime = millis();
    cloopTime = currentTime;

    //LCD測試顯示
    lcd.begin(16, 2);
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(“LCD SETUP-01”);
    lcd.setCursor(0,1);
    lcd.print(“LCD SETUP-02”);

    }

    void loop()
    {
    currentTime = millis();
    if(currentTime >= (cloopTime + 1000)) //設定每1秒鐘顯示1次流量數據
    {
    cloopTime = currentTime; //更新計時數值
    if(flow_frequency != 0) //如果有霍爾流量感測值, 就執行顯示數值工作
    {
    l_minute = (flow_frequency / 7.5); // (Pulse frequency x 60 min) / 7.5Q = flowrate in L/hour
    Serial.print(“Rate: “);
    Serial.print(l_minute);
    Serial.println(” L/M”);
    l_minute = l_minute/60;
    vol = vol +l_minute;
    Serial.print(“Vol:”);
    Serial.print(vol);
    Serial.println(” L”);
    flow_frequency = 0; // Reset Counter
    Serial.print(l_minute, DEC); // Print litres/hour
    Serial.println(” L/Sec”);

    //將霍爾感測數據顯示在LCD1602 (只要有以下lcd.xx 就會重新啟動程式)
    lcd.begin(16, 2);
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(“Esp32-FlowMeter”);
    lcd.setCursor(0,1);
    lcd.print(“spock v2.0″);
    }
    else //如果沒有霍爾流量感測值, 就執行無數值的顯示工作
    {
    Serial.println(” flow rate = 0 “);
    Serial.print(“Rate: “);
    Serial.print( flow_frequency );
    Serial.println(” L/M”);
    Serial.print(“Vol:”);
    Serial.print(vol);
    Serial.println(” L”);
    lcd.begin(16, 2);
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(“NO Flow Rate”);
    lcd.setCursor(0,1);
    lcd.print(“NO Flow Vol”);
    }
    }
    }

    1. 初始化LCD的begin()或init()方法,只需在setup()內執行一次即可。loop()函式程式碼改成:

      void loop() {
        currentTime = millis();
        if (currentTime >= (cloopTime + 1000))  //設定每1秒鐘顯示1次流量數據
        {
          cloopTime = currentTime;  //更新計時數值
          if (flow_frequency != 0)  //如果有霍爾流量感測值, 就執行顯示數值工作
          {
            l_minute = (flow_frequency / 7.5);  // (Pulse frequency x 60 min) / 7.5Q = flowrate in L/hour
            Serial.print("Rate: ");
            Serial.print(l_minute);
            Serial.println(" L / M");
            l_minute = l_minute / 60;
            vol = vol + l_minute;
            Serial.print("Vol:");
            Serial.print(vol);
            Serial.println(" L");
            flow_frequency = 0;           // Reset Counter
            Serial.print(l_minute, DEC);  // Print litres/hour
            Serial.println(" L / Sec");
      
            //將霍爾感測數據顯示在LCD1602 (只要有以下lcd.xx 就會重新啟動程式)
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Rate: ");
            lcd.print(l_minute);
            lcd.println(" L / M");
            lcd.setCursor(0, 1);
            lcd.print("Vol:");
            lcd.print(vol);
            lcd.println(" L");
          } else  //如果沒有霍爾流量感測值, 就執行無數值的顯示工作
          {
            Serial.println(" flow rate = 0 ");
            Serial.print("Rate: ");
            Serial.print(flow_frequency);
            Serial.println(" L / M");
            Serial.print("Vol:");
            Serial.print(vol);
            Serial.println(" L");
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("NO Flow Rate");
            lcd.setCursor(0, 1);
            lcd.print("NO Flow Vol");
          }
        }
      }
      

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top