我的ESP32實做書籍:https://youyouyou.pixnet.net/blog/post/121105860
博客來網址:https://www.books.com.tw/products/0010901195
當我們從上一章學習如何利用ESP32所具備的WiFi上網能力,並到公開資訊網站上取得台灣各地PM2.5的數值,本章則要介紹如何讓ESP32成為一個小幫手,能幫我們監看倉庫的溫濕度,並在發生異常時發訊息通知我們,讓我們能立即處理異常,避免災害擴大,這樣是不是很方便呢?
而在台灣幾乎每個人都有手機,不管是Apple或是Android,也都會安裝Line通訊軟體來跟家人朋友聯繫,利用Line來打打字或是語音通訊都是免費的,而且還有可愛的貼圖可以用,所以發送Line訊息可以說是最方便即時的,不過ESP32要如何串接Line的訊息呢?ESP32又沒有手指是要如何打字呢?
Line有提供一個官方的網站工具,可以讓我們透過該網站傳遞參數,參數則包括訊息內容及權杖,權杖則是代表要傳遞的Line群組,當網站收到這些參數之後就會將訊息傳遞到指定的Line群組。也就是說,ESP32並不是直接把訊息傳到某個人手機的Line裡面,而是透過某個網站協助中間轉傳訊息,通常這類的網站就通稱為API(Application Programming Interface),使用這些API工具我們就可以省去學習Line APP內複雜的通訊協定、機制、格式、架構…等等,只需要把資料傳給API,而API就會處理所有的通訊過程,讓我們省下很多程式開發的時間。
整理上面的內容,也就是說首先我們利用ESP32及DHT11監看現場的溫濕度(也可以再加上氣體偵測MQ2、水位感測…等),當發現異常時(例如溫度>40或濕度>85)就發送到Line的API網站,Line API收到我們的訊息之後,就會轉傳到指定的Line群組中,讓相關的管理人員收到訊息,然後立即處理。
要完成以上的任務,本章分成四個步驟
一、申請Line Notify權杖
二、手冊測試Line Notify
三、ESP32+DHT11接線
四、程式設計
一、申請Line Notify權杖
Line Notify的網站申請開通服務,首先在瀏覽器中輸入網址:https://notify-bot.line.me/,並點選右上角的登入。
Line帳號密碼後,按下方的登入
完成登入,點選右上角的登入帳號/個人頁面,即可進入「已連動服務」管理頁面
Line群組通訊的密碼,若無此密碼,我們的Line可能每天都會收到一堆廣告訊息。
在設定權杖頁面分別輸入
1.名稱:未來發訊訊息通知時,會出現的名稱
2.對象:要發送訊息的群組,此處練習時先選擇「透過1對1聊天接收LINE Notify通知」,也就是傳訊給自己,另外這裡你會發現在清單中找不到你某個特定的朋友,而只能選「群組」來發送訊息,這是因為我們目前使用的Line通知是免費的Notify功能,若您要通知特定的「人」則必須申請Line Bot,這部份則較為複雜,本書先略過。
完成輸入後,按下方的發行即可獲得一組密碼,此即為權杖。
拿到權杖後,點選下方的複製按鈕將密碼複製起來,並貼在記事本上,避免遺失,若遺失也不用緊張,重新申請一次即可,不過就算申請的對象是同一個,每次的權杖密碼都是不相同的,但功能是一樣的,後面申請的也不會使之前申請的權杖失效。
確認申請完成,將會產生一個連動服務。
二、手動測試Line Notify
Line Notify權杖是否正常,也順便了解Line API的運作方式(如需要更詳細的Line Notify API可參閱網址:https://notify-bot.line.me/doc/)
首先我們先到連線到知名的API測試網址API Tester:https://apitester.com/(或者安裝POSTMAN API測試軟體),並依照規定輸入Line Notify的相關設定如下圖。
1. 修改傳遞方式POST
2. 輸入Line Notify API網站:https://notify-api.line.me/api/notify
3. 輸入傳遞的訊息內容:message=這是測試
4. 點選「+」號,增加一個表頭Request Header,我們會將密碼放在表頭與Line API做驗證
-輸入表頭名稱:Authorization
-輸入認證內容:Bearer 權杖密碼,要注意的是Bearer與權杖密碼中間有一個空白。
完成設定後後,按下下方的藍色Test按鈕,即可在手機收到LINE傳來的訊息。
其中前面【】內的名稱就是一開始申請時的名稱(圖X),而後方的訊息「這是測試」則是在API tester網站輸入的內容。
經由上面的測試可以發現,用ESP32傳LINE的方式就是將資料傳遞到LINE的API網站https://notify-api.line.me/api/notify,並將訊息內容及權杖密碼以參數方式夾帶給網站,網站收到後就會將訊息傳遞給權杖所對應的使用者。
下一節我們使用ESP32自動傳LINE時,也是一樣要指定這些訊息,就可以資訊傳遞給相關的人員。
另外如果您選擇傳訊的對象是一個群組,那麼要多一個動作:邀請LINE Notify進入群組內,否則會出現一個錯誤訊息是:此帳號尚未被邀請至已連動的群組。
三、ESP32 DHT11線路佈置
本部份參考前篇內容:第九篇-esp32-溫濕度顯示器(dht11+1602-lcd) ,完成線路佈置並測試讀取成功後,再進行下一個步驟。
四、程式撰寫
就如第二節所展示,這次我們的程式將使用wificlientsecure進行實做,wificlientsecure跟上一章的httpclient不同的地方在於wificlientsecure可以支援https加密協定,而httpclient只能用HTTP GET協定做參數傳遞,wificlientsecure及wificlient可以用來做POST協定。
程式主要分成幾4個流程
1. 初始化設定:設定無線網路SSID, Password, LINE API網址, 權杖密碼等資訊
2. 連線Wifi網路
3. 在Loop中重複監看DHT11溫濕度資料
4. 發現DHT11溫度或濕度過高,則利用wificlientsecure將資料POST到LINE網址
五、注意事項
最近很多夥伴說程式無法執行,會遇到「Connection Failed」的問題,主要是因為ESP32的1.0.6的核心會強迫使用SSL認證,此時可以先透過在Setup()內加上「client.setInsecure();」語法來解決,如下方紅色字的部份。
//請注意,ESP32 1.0.6版WiFiClientSecure有些問題,建議安裝1.0.4版
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <SimpleDHT.h>
//請修改以下參數--------------------------------------------
char SSID[] = "YourSSID";
char PASSWORD[] = "YourPassword";
String Linetoken = "Your Line Token";
int pinDHT11 = 14;//假設DHT11接在腳位GPIO14,麵包板左側序號8
//---------------------------------------------------------
SimpleDHT11 dht11(pinDHT11);//DHT11物件
WiFiClientSecure client;//網路連線物件
char host[] = "notify-api.line.me";//LINE Notify API網址
void setup() {
Serial.begin(115200);
//連線到指定的WiFi SSID
Serial.print("Connecting Wifi: ");
Serial.println(SSID);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
//連線成功,顯示取得的IP
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
IPAddress ip = WiFi.localIP();
Serial.println(ip);
//client.setInsecure();//ESP32核心 1.0.6以上
}
void loop() {
//嘗試讀取溫濕度內容
byte temperature = 0;
byte humidity = 0;
int err = SimpleDHTErrSuccess;
if ((err = dht11.read(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
Serial.print("Read DHT11 failed, err="); Serial.println(err); delay(1000);
return;
}
//讀取成功,將溫濕度顯示在序列視窗
Serial.print("Sample OK: ");
Serial.print((int)temperature); Serial.print(" *C, ");
Serial.print((int)humidity); Serial.println(" H");
//設定觸發LINE訊息條件為溫度超過30或濕度超過80
if ((int)temperature >= 30 || (int)humidity >= 80) {
//組成Line訊息內容
String message = "檢測環境發生異常,請協助儘速派人查看處理,目前環境狀態:";
message += "\n溫度=" + String(((int)temperature)) + " *C";
message += "\n濕度=" + String(((int)humidity)) + " H";
Serial.println(message);
if (client.connect(host, 443)) {
int LEN = message.length();
//傳遞POST表頭
String url = "/api/notify";
client.println("POST " + url + " HTTP/1.1");
client.print("Host: "); client.println(host);
//權杖
client.print("Authorization: Bearer "); client.println(Linetoken);
client.println("Content-Type: application/x-www-form-urlencoded");
client.print("Content-Length: "); client.println( String((LEN + 8)) );
client.println();
client.print("message="); client.println(message);
client.println();
//等候回應
delay(2000);
String response = client.readString();
//顯示傳遞結果
Serial.println(response);
client.stop(); //斷線,否則只能傳5次
}
else {
//傳送失敗
Serial.println("connected fail");
}
}
//每5秒讀取一次溫濕度
delay(5000);
}
LINE Notify訊息畫面
除了傳遞文字之外,也可以傳遞貼圖或照片喔,此外也可使用LINE Library的方式則更為簡單,使用者可自行Google 「Line-Notify-ESP32」就會有更多的資訊喔。

老師您好,請問連結資料庫的 WiFiClient client; 與 連結Line Notify的WiFiClientSecure client; 這兩者可以同時並用嗎?
理論是可以同時喔,只是我沒試過
*****
*****
*****
*****
如果一直顯示出connected failed 的話要如何解決?
//client.setInsecure();//ESP32核心 1.0.6以上
是esp 32的函式庫要安裝1.0.6以上的版本嗎? 也是第4樓的訪客
好像知道問題了,但溫度過高後顯示出 {"status":401,"message":"Invalid access token"} 這是甚麼意思呢 來自4樓
我成功用出來了,重新申請一個權杖就行了
ˊ讚讚讚
老師可以問一下Send failed而不能傳到Line notify該怎麼辦?
client.setInsecure();//ESP32核心 1.0.6以上
是哪一個部分要改啊?
老師你好 我照著你的步驟做,但我現在碰到了問題,我也改過client.setInsecure()了,但資料就是傳不到LINE上,請問該怎麼辦呢
要知道有什麼錯誤訊息
client.println( String((LEN + 8)) ); 老師你好 我想請問這段是什麼意思
資料長度,是post協定必備
老師你好,它還是一樣傳不到LINE上,我找不出問題,能請老師指教一下嗎 燒入結果: 草稿碼使用了 893078 bytes (68%) 的程式儲存空間。上限為 1310720 bytes。 全域變數使用了 39792 bytes (12%) 的動態記憶體,剩餘 287888 bytes 給區域變數。上限為 327680 bytes 。 esptool.py v2.6 Serial port COM3 Connecting.... Chip is ESP32D0WDQ5 (revision 1) Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None MAC: f0:08:d1:77:a3:44 Uploading stub... Running stub... Stub running... Changing baud rate to 921600 Changed. Configuring flash size... Auto-detected Flash size: 4MB Compressed 8192 bytes to 47... Writing at 0x0000e000... (100 %) Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.0 seconds (effective 5041.2 kbit/s)... Hash of data verified. Compressed 17392 bytes to 11186... Writing at 0x00001000... (100 %) Wrote 17392 bytes (11186 compressed) at 0x00001000 in 0.1 seconds (effective 940.1 kbit/s)... Hash of data verified. Compressed 893200 bytes to 492103... Writing at 0x00010000... (3 %) Writing at 0x00014000... (6 %) Writing at 0x00018000... (9 %) Writing at 0x0001c000... (12 %) Writing at 0x00020000... (16 %) Writing at 0x00024000... (19 %) Writing at 0x00028000... (22 %) Writing at 0x0002c000... (25 %) Writing at 0x00030000... (29 %) Writing at 0x00034000... (32 %) Writing at 0x00038000... (35 %) Writing at 0x0003c000... (38 %) Writing at 0x00040000... (41 %) Writing at 0x00044000... (45 %) Writing at 0x00048000... (48 %) Writing at 0x0004c000... (51 %) Writing at 0x00050000... (54 %) Writing at 0x00054000... (58 %) Writing at 0x00058000... (61 %) Writing at 0x0005c000... (64 %) Writing at 0x00060000... (67 %) Writing at 0x00064000... (70 %) Writing at 0x00068000... (74 %) Writing at 0x0006c000... (77 %) Writing at 0x00070000... (80 %) Writing at 0x00074000... (83 %) Writing at 0x00078000... (87 %) Writing at 0x0007c000... (90 %) Writing at 0x00080000... (93 %) Writing at 0x00084000... (96 %) Writing at 0x00088000... (100 %) Wrote 893200 bytes (492103 compressed) at 0x00010000 in 7.8 seconds (effective 914.9 kbit/s)... Hash of data verified. Compressed 3072 bytes to 128... Writing at 0x00008000... (100 %) Wrote 3072 bytes (128 compressed) at 0x00008000 in 0.0 seconds (effective 1890.5 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin... 序列埠顯示: Sample OK: 27 *C, 80H 檢測環境發生異常,請協助儘速派人查看處理,目前環境狀態: 溫度 =27C 濕度 =80H HTTP/1.0 400 Bad request Cache-Control: no-cache Connection: close Content-Type: text/html X-Line-Load-Balancer-Error: Yes
400 Bad request
Your browser sent an invalid request.Server: linelb
<body><h1>400 Bad request</h1>代表傳送的資料有問題,Line網有收到傳送的資料,是你程式有問題,貼來看看就知道了
老師你好,程式我都是照書上寫的,會不會可能是環境設定的問題阿 #include
#include
#include
// 請修改以下參數 --------------------------------------------
char SSID[] = "***";
char PASSWORD[] = "***";
String Linetoken = "***";
int pinDHT11 = 4; // 假設 DHT11 接在腳位 GPIO4,DHT11 接線圖請參考 P74
//---------------------------------------------------------
SimpleDHT11 dht11(pinDHT11);
WiFiClientSecure client; // 宣告一個加密的 WiFi 連線端
char host[] = "notify-api.line.me"; // 宣告 Line API 網站
void setup() {
Serial.begin(115200);
// 連線到指定的 WiFi SSID
Serial.print("Connecting Wifi: ");
Serial.println(SSID);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
// 連線成功,顯示取得的 IP
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
IPAddress ip = WiFi.localIP();
Serial.println(ip);
//client.setInsecure(); // 讀者若使用 ESP32 1.0.6 版核心須加上這句避免 SSL 問題
}
void loop() {
// 嘗試讀取溫濕度內容
byte temperature = 0;
byte humidity = 0;
int err = SimpleDHTErrSuccess;
if ((err = dht11.read(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
Serial.print("Read DHT11 failed, err="); Serial.println(err); delay(1000);
return;
}
// 讀取成功,將溫濕度顯示在序列視窗
Serial.print("Sample OK: ");
Serial.print((int)temperature); Serial.print(" *C, ");
Serial.print((int)humidity); Serial.println("H");
// 設定觸發 LINE 訊息條件為溫度超過 35 或濕度超過 80
if ((int)temperature >= 35 || (int)humidity >= 80) {
// 組成 Line 訊息
String message = "檢測環境發生異常,請協助儘速派人查看處理,目前環境狀態:";
message += "\r\n 溫度 =" + String((int)temperature) + "C";
message += "\r\n 濕度 =" + String((int)humidity) + "H";
Serial.println(message);
// 連線到 Line API 網站
if (client.connect(host, 443)) {
int LEN = message.length();
//(1) 傳遞網站
String url = "/api/notify"; // Line API 網址
client.println("POST " + url + "HTTP/1.1");
client.print("Host: "); client.println(host); // Line API 網站
//(2) 資料表頭
client.println("Authorization: Bearer " + Linetoken);
//(3) 內容格式
client.println("Content-Type: application/x-www-form-urlencoded");
//(4) 資料內容
client.println("Content-Length: " + String((LEN + 8)) ); // 訊息長度
client.println();
client.println("message=" + message); // 訊息內容
// 等候回應
delay(2000);
String response = client.readString();
// 顯示傳遞結果
Serial.println(response);
client.stop(); // 斷線,否則只能傳 5 次
}
else {
// 傳送失敗
Serial.println("connected fail");
}
}
// 每 5 秒讀取一次溫濕度
delay(5000);
}
您可以直接下載我的檔案,我覺得是您的程式少了一些控制用的空白符號之類。 https://t.ly/wed6
老師你好,我資料可以傳LINE,開心哈哈哈,然後我對比了一下網頁與您給的檔案,有幾個問題想請教您,請看下面的註解 if (client.connect(host, 443)) { int LEN = message.length(); String url = "/api/notify"; client.println("POST " + url + " HTTP/1.1"); client.print("Host: "); client.println(host); client.print("Authorization: Bearer "); client.println(Linetoken); //***client.println("Authorization: Bearer " + Linetoken);這兩個寫法有什麼不同,會不會問題出在這裡 client.println("Content-Type: application/x-www-form-urlencoded"); client.print("Content-Length: "); client.println( String((LEN + 8)) ); client.println(); client.print("message="); client.println(message); //***client.println();這行多加是指什麼意思 delay(2000); String response = client.readString(); Serial.println(response); client.stop(); }
client.println();是送出結尾,看來是我書上的程式少了這一行,非常感謝您的通知
老師您好,我是參照您的著作「IoT物聯網應用」來做的,可是不能傳送公告到Line Sketch uses 846949 bytes (64%) of program storage space. Maximum is 1310720 bytes. Global variables use 45048 bytes (13%) of dynamic memory, leaving 282632 bytes for local variables. Maximum is 327680 bytes. esptool.py v4.5.1 Serial port COM7 Connecting.... Chip is ESP32-D0WDQ6 (revision v1.0) Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None Crystal is 40MHz MAC: 7c:9e:bd:66:3a:9c Uploading stub... Running stub... Stub running... Configuring flash size... Flash will be erased from 0x00001000 to 0x00005fff... Flash will be erased from 0x00008000 to 0x00008fff... Flash will be erased from 0x0000e000 to 0x0000ffff... Flash will be erased from 0x00010000 to 0x000e0fff... Compressed 18960 bytes to 13073... Writing at 0x00001000... (100 %) Wrote 18960 bytes (13073 compressed) at 0x00001000 in 1.5 seconds (effective 98.2 kbit/s)... Hash of data verified. Compressed 3072 bytes to 146... Writing at 0x00008000... (100 %) Wrote 3072 bytes (146 compressed) at 0x00008000 in 0.1 seconds (effective 186.0 kbit/s)... Hash of data verified. Compressed 8192 bytes to 47... Writing at 0x0000e000... (100 %) Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.2 seconds (effective 335.7 kbit/s)... Hash of data verified. Compressed 852704 bytes to 549928... Writing at 0x00010000... (2 %) Writing at 0x0001badd... (5 %) Writing at 0x00026fb4... (8 %) Writing at 0x00036bfd... (11 %) Writing at 0x0003c3e6... (14 %) Writing at 0x00041dc9... (17 %) Writing at 0x000472fb... (20 %) Writing at 0x0004c952... (23 %) Writing at 0x00051f0e... (26 %) Writing at 0x000570dd... (29 %) Writing at 0x0005c35d... (32 %) Writing at 0x00061548... (35 %) Writing at 0x00066858... (38 %) Writing at 0x0006bb84... (41 %) Writing at 0x00070f66... (44 %) Writing at 0x0007615b... (47 %) Writing at 0x0007b2f8... (50 %) Writing at 0x0008059f... (52 %) Writing at 0x000860ce... (55 %) Writing at 0x0008b81a... (58 %) Writing at 0x00090e22... (61 %) Writing at 0x00096307... (64 %) Writing at 0x0009b472... (67 %) Writing at 0x000a0839... (70 %) Writing at 0x000a5ab0... (73 %) Writing at 0x000ab145... (76 %) Writing at 0x000b0a13... (79 %) Writing at 0x000b666e... (82 %) Writing at 0x000bc101... (85 %) Writing at 0x000c233e... (88 %) Writing at 0x000cc7b5... (91 %) Writing at 0x000d214f... (94 %) Writing at 0x000d784b... (97 %) Writing at 0x000dcf46... (100 %) Wrote 852704 bytes (549928 compressed) at 0x00010000 in 50.9 seconds (effective 133.9 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin... 序列埠顯示: 01:00:32.792 -> 檢測環境發生異常,請協助儘速派人查看處理,目前環境狀態: 01:00:32.792 -> 溫度=23 *C 01:00:32.792 -> 濕度=45 H 01:00:32.792 -> connected fail 01:00:37.795 -> Sample OK: 23 *C, 45 H 01:00:37.795 -> 檢測環境發生異常,請協助儘速派人查看處理,目前環境狀態: 01:00:37.843 -> 溫度=23 *C 01:00:37.843 -> 濕度=45 H 01:00:37.843 -> connected fail