Điều khiển thiết bị từ xa qua internet luôn là một đề tài thú vị, đặc biệt với ESP8266. Tôi có đọc qua một số bài hướng dẫn điều khiển ESP8266 qua internet tuy nhiên một số cần mở Port modem, dùng ESP8266 làm webserver,…nhưng khả năng xử lý của ESP rất hạn chế nếu dùng làm webserver.
Bài viết này tiếp tục với công nghệ Web, nhưng sẽ giải quyết được những vấn đề trên chỉ với một hosting free. Chúng ta sẽ dễ dàng điều khiển ESP8266/ESP32 ở bất cứ đâu miễn là ESP8266 và thiết bị thông minh của bạn có kết nối internet.
Nguyên lý:
Mô hình Client- Server là mô hình một chiều, hay nói cách khác, chỉ khi nào Client gửi Request thì Server mới phản hồi, chúng ta không thể bắt Server gửi command để điều khiển ESP được.
Vậy việc cần làm là gì?
Theo sơ đồ trên các bạn có thể thấy có 2 việc chính:
- Thiết lập Server nhận request từ điện thoại, máy tính. Sau đó trả về giao diện điều khiển. Đồng thời khi thao tác trên giao diện điều khiển đó, điện thoại có thể gửi được command đến Server. Thiết lập một cơ cấu trung gian, nhận command điều khiển từ điện thoại của người dùng.
- Thiết lập ESP request theo chu kì, và response nhận được từ server sẽ có chưa kèm command. Tất nhiên chúng ta nhận ra ngay đây là phương pháp polling, nếu như tăng số lượng ESP8266/32 lên số lượng lớn khoảng vài chục hoặc vài trăm thì sẽ khiến server xử lý request nhiều hơn, gần giống với tấn công DDOS. 😊, nhưng cái đó tính sau, chúng ta sẽ khắc phục vấn đề này trong một bài viết khác.
Chi tiết cách làm:
- Thiết lập server để vừa cung cấp giao diện, vừa cung cấp chức năng trung gian. Giao diện ở đây chúng ta có thể dùng HTML để thiết kế các nút nhấn, dùng Javascript để hiển thị trạng thái on/off. Còn chức năng trung gian, rất đơn giản đó là một file json.
1.1 Chúng ta tạo ra một folder có tên là “test” và tạo ra một file test.json trong floder này luôn.
1.2 Tạo trong thư mục gốc của hosting /public_html 1 file có tên esp_control.php
1.3 Chúng ta sẽ viết script PHP để đọc dữ liệu trong file test.json. Mục đích?. Hiểu đơn giản là khi bạn tắt trình duyệt điều khiển đi ngủ, sáng hôm sau mở trình duyệt lên thì nó sẽ đọc file json này, hiển thị lên trạng thái mà mình đã đặt trước đó cho ESP từ tối hôm qua.
1 2 |
$jsonString = file_get_contents("test/test.json"); $data = json_decode($jsonString, true); |
Đoạn mã trên sẽ kết hợp với đoạn mã ở 1.7 sẽ hiển thị ra icon on/off trên giao diện
1.4 Sau đó là viết script cho việc nhận bản tim request từ máy điện thoại của người dùng. ,Cái này giống với việc các bạn làm trong bài gửi email bằng ESP8266. Ở đây là bản tin POST Request sinh ra khi chúng ta nhấn nút trên giao diện điều khiển, và bản tin này được gửi tới chính file esp_control.php ( có thể thấy nó trong đoạn mã sau: <form action=”esp_control.php” method=”post”> )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$user='abcd_ef'; if(isset($_POST['LED1_ON'])) { if($user==$_POST['LED1_ON']) { $data['led1'] = "on"; } } if(isset($_POST['LED1_OFF'])) { if($user==$_POST['LED1_OFF']) { $data['led1'] = "off"; } } |
1.5 Sau khi đã nhận được POST Request, chúng ta sẽ tách được data trong bản tin POST này để gán cho biến $data. Việc tiếp theo là lấy biến $data này, ghi vào file test.json. Mục đích? Để khi ESP gửi request, bản tin nó nhận được về chính là file test.json này, và kết quả là ESP8266 sau khi đọc sẽ thực thi command chúng ta cần.
1 2 |
$newJsonString = json_encode($data); file_put_contents("test/test.json", $newJsonString); |
1.6 Tiếp theo chúng ta dùng HTML tạo ra giao diện điều khiển và nút nhấn.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<form action="home.php" method="post"> <table border="2" width=100% height="400px" align="center"> <tr class="indam"> <td bgcolor="#FFCC00">TÊN THIẾT BỊ</td> <td bgcolor="#FFCC00">TRẠNG THÁI</td> <td bgcolor="#FFCC00"> ĐIỀU KHIỂN</td> </tr> <tr> <td><h2>Thiết bị 1</h2></td> <td> <img id="myImage1" src="pic_bulboff10.png" width="60" height="60"> </td> <td> <p> <?php $user='abcd_ef'; echo " <button style='height:60px; width:60px;' type='submit' name='LED1_ON' value='$user'>ON</button> "; echo " <button style='height:60px; width:60px;' type='submit' name='LED1_OFF' value='$user'>OFF</button> "; ?> </p> </td> </tr> </table> </form> |
Trong đoạn code trên, chúng ta thấy một Form được tạo ra, và trong form đó sẽ có một action đó là gửi bản tin post đến file esp_control.php khi có một click và nút nhấn.
1.7 Đoạn script dưới đây cho phép chúng ta thay đổi icon của bàn điều khiển tương đương với trạng thái điều khiển. Và đoạn mã này dùng Javascript
1 2 3 4 5 6 7 8 9 10 |
<?php $jsonString = file_get_contents("test/test.json"); $data = json_decode($jsonString, true); if ($data['led1'] == 'on') { echo " <script>"; echo " document.getElementById('myImage1').src = 'pic_bulbon10.png' "; echo " </script> "; } ?> |
1.8 Chúng ta chú ý trong đoan mã của 1.7 có dùng javascript để hiển thị icon on/off, vậy icon on/off này cần được tải lên thư mục gốc để có đoạn script trên có thể sử dụng. Các bạn upload lên như trong hình.
Chú ý ta chỉ cần 3 file thôi, đó là esp_control.php và pic_bulboff10.png, pic_bulbon10.png
Toàn bộ mã nguồn esp_control.php hoàn chỉnh.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="refresh" content="3"> <title>He Thong Dieu Khien</title> <link rel = "stylesheet" type="text/css" href="style.css"/> </head> <body align="center"> <br /> <header> <h2> <span style="color:#00F">HỆ THỐNG ĐIỀU KHIỂN THIẾT BỊ TỪ XA QUA MẠNG </span><br /> </h2> </header> <?php $jsonString = file_get_contents("test/test.json"); $data = json_decode($jsonString, true); $user='abcd_ef'; if(isset($_POST['LED1_ON'])) { if($user==$_POST['LED1_ON']) { $data['led1'] = "on"; } } if(isset($_POST['LED1_OFF'])) { if($user==$_POST['LED1_OFF']) { $data['led1'] = "off"; } } $newJsonString = json_encode($data); file_put_contents("test/test.json", $newJsonString); ?> <form action="esp_control.php" method="post"> <table border="2" width=100% height="400px" align="center"> <tr class="indam"> <td bgcolor="#FFCC00">TÊN THIẾT BỊ</td> <td bgcolor="#FFCC00">TRẠNG THÁI</td> <td bgcolor="#FFCC00"> ĐIỀU KHIỂN</td> </tr> <tr> <td><h2>Thiết bị 1</h2></td> <td> <img id="myImage1" src="pic_bulboff10.png" width="60" height="60"> </td> <td> <p> <?php $user='abcd_ef'; echo " <button style='height:60px; width:60px;' type='submit' name='LED1_ON' value='$user'>ON</button> "; echo " <button style='height:60px; width:60px;' type='submit' name='LED1_OFF' value='$user'>OFF</button> "; ?> </p> </td> </tr> </table> </form> <?php $jsonString = file_get_contents("test/test.json"); $data = json_decode($jsonString, true); if ($data['led1'] == 'on') { echo " <script>"; echo " document.getElementById('myImage1').src = 'pic_bulbon10.png' "; echo " </script> "; } ?> |
* Kiểm tra hoạt động tại hosting đã ok chưa
Các bạn vào đường dẫn của trang web, ở đây của tôi là: https://luuvachiase.000webhostapp.com/esp_control.php , xem đã có giao diện điều khiển chưa.
Sau khi có giao diện điều khiển rồi, hãy bấm vào bật/tắt để xem icon có thay đổi theo ko.
Nếu mọi thứ ok, hãy kiểm tra xem file test.json có data tương ứng bên trong không, nếu chúng ta click on thì test.json sẽ có dạng
Nếu chúng ta click off thì test.json sẽ có dạng
Đến đây coi như đã xong phần webserver.
- Lập trình ESP8266/32 kết nối internet, gửi Request đến server. Xử lý response là data dạng json trả về. Cuối cùng là thực hiện command theo data đã đọc được trong file json. Vậy file.json là gì, cấu trúc thế nào, tôi sẽ viết chi tiết trong một bài viết khác.
Mã nguồn ESP.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
// luuvachiase.net- ngoctuansqtt@gmail.com #include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h> #include <WiFiClient.h> #include <ArduinoJson.h> //=========================NGƯỜI DÙNG TỰ ĐỊNH NGHĨA==========================// const char* ssid = "xxx"; const char* password = "xxx"; const char* host = "xxxxx"; //luuvachiase.000webhostapp.com //=========================KHAI BÁO CÁC BIẾN TOÀN CỤC===========================// String path = "/test/test.json"; String line; int count=0; bool json_ok =0,en=0; String tuan; String section="message"; //====================================SETUP BAN ĐẦU, CHẠY 1 LẦN DUY NHẤT============// void setup() { WiFi.softAPdisconnect (true); // Tắt chức năng quảng bá SSID của AP. pinMode(2, OUTPUT); // Led bao trang thai ket noi server ok pinMode(10, OUTPUT); // Led bao trang thai ket noi wifi ok pinMode(12, OUTPUT); // chan dieu khien relay digitalWrite(2, HIGH); // Mức logic 1 là tắt led, logic 0 là sáng led digitalWrite(12, HIGH); digitalWrite(10, HIGH); Serial.begin(9600); delay(10); Serial.print("Connecting to Wifi: "); Serial.println(ssid); WiFi.begin(ssid, password); int wifi_ctr = 0; while (WiFi.status() != WL_CONNECTED) { digitalWrite(10, HIGH); delay(500); Serial.print("."); digitalWrite(10, LOW); delay(500); } Serial.println("Connected!"); } //====================================================================// void loop() { Serial.print("Connect to IoT-server"); Serial.println(host); WiFiClient client; const int httpPort = 80; if (!client.connect(host, httpPort)) { Serial.println("Connect to server fail!"); if(WiFi.status() != WL_CONNECTED) { digitalWrite(10, HIGH); // Nếu mất kết nối với wifi thì Led 10 sẽ tắt delay(500); Serial.print("."); digitalWrite(10, LOW); delay(300); } return; } digitalWrite(10, LOW); //ok Nếu kết nối với Server thành công LED 10 sẽ sáng while(1) { if(count==1) { Serial.print("No response:"); digitalWrite(2, HIGH); count=0; break; } // Nếu lần kết nối trước không thành công thì kết nối lại Serial.println("Send request"); client.print(String("GET ") + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: keep-alive\r\n\r\n"); digitalWrite(2, HIGH); delay(1000); // wait for server to respond count=1; //============================================= read response================================= while(client.available()){ count=2; // Serial.println("Data available!"); // chi để kiểm tra có nhận dc data ko digitalWrite(2, LOW); //ok //===================================== if(json_ok==false) { line = client.readStringUntil('\r'); //Serial.println(line); en=false; } else { line = client.readStringUntil('}'); int find_json = line.indexOf('{'); if(find_json<0) Serial.println("NOT JSON==>SKIP DATA"); else {tuan=line+"}"; en=true;Serial.println(tuan);} json_ok=false; } //======================================= if (line=="\n") { section="json"; json_ok=true; } else { section="header"; } //========================================= if(en==true) // Nếu response là json { String result = tuan.substring(1); line=""; // ===============Parse JSON=============== int size = result.length()+1; char json[size]; result.toCharArray(json, size); DynamicJsonBuffer jsonBuffer(size); JsonObject& json_parsed = jsonBuffer.parseObject(json); if (!json_parsed.success()) { Serial.println("parseObject() failed"); break; } else //=========================================================== if (strcmp(json_parsed["led1"], "on") == 0) { digitalWrite(12,LOW); } else { digitalWrite(12,HIGH); } }//end if(en==true) } // end while client_available }// while }// loop |
- Việc tiếp theo là kiểm tra hoạt động, nạp mã nguồn vào ESP, mở Serial Terminal ( tốc độ baud 9600) để xem kết quả.
Nếu chúng ta thấy phản hồi như trên thì chúng ta đã thành công rồi.
Các bạn có thể tham khảo video kết quả.
Mọi thắc mắc các bạn có thể comment phía dưới và đừng quên rate và like nhé. Thanks
Bạn ơi mình đã làm thành công đến bước 2 rồi, cả việc upgrade json 5 > json 6, mà khi mình đến bước 3 thì nó báo No Response, Not Json, mình có thế xin mail của bạn để gửi hình và code qua được không? Mong bạn giúp đỡ
– Đầu tiên bạn kiểm tra về địa chỉ của server đưa vào code arduino xem đúng chưa
– Bạn có thể #count=1; đi để nó không báo lỗi response nữa, cái đó dùng cho trường hợp lỗi kết nối đến server, esp sẽ kết nối lại.
Bạn ơi, mình chạy code báo là “NOT JSON==>SKIP DATA” mình không biết xử lý sao cả. Đã up lên json 6 rồi
Cái này có nghĩa là định dạng json gửi từ server về esp8266 của bạn chưa đúng. Mình giải thích tại sao nó lại có dòng đó:
+ Định dạng json:
{“led1″:”off”}
+ Mã nguồn xác định xem có phải là json ko ( vì nếu là json thì có cấu trúc “{…}”:
int find_json = line.indexOf(‘{‘); // kiểm tra xem trong data nhận về có dấu “{” không,
if(find_json<0) Serial.println("NOT JSON==>SKIP DATA”); // nếu không thì bỏ qua để đỡ mất thời gian xử lý
==> vì vậy bạn nên check xem phải hồi của server về có đúng là định dạng Json hay không đã nhé.
Bạn ơi, mình kiểm tra đúng hết tất cả. {“led1″:”on”} -> đây là định dạng file test.json trong folder test.
Minh đã kiểm tra lại đường dẫn cũng như hạ bản Arduinojson xuống 5 mà vẫn bị như vậy
https://hocathongminh.xyz/test/test.json
đây là đường dẫn chứa data của mình
Bạn có thể in ra toàn bộ response từ server trả về mà bạn bắt được trên ESP8266 được ko? Dùng Serial in ra, nếu bạn chưa chỉnh sửa gì code mình viết thì sửa như sau:
Bỏ ba dòng dưới đây:
if(find_json<0) Serial.println("NOT JSON==>SKIP DATA”);
else {tuan=line+”}”; en=true;Serial.println(tuan);}
json_ok=false;
Bằng:
{tuan=line+”}”; en=true;Serial.println(tuan);}
400 Bad Request
400 Bad Request
openresty
}
parseObject() failed
Send request
No response:Connect to IoT-serverhttp://thangtestweb1.000webhostapp.com
Send request
Bạn ơi, sao mình làm đúng như bạn mà nó cứ hiện vầy
Bạn kiểm tra lại cấu trúc “request” của bạn có đúng chưa, thường lỗi này do server ko hiểu hoặc cấu trúc request sai.
Còn phần thông báo “parseObject() failed” là do ESP8266 nó nhận bản tin ko phải Json, nên nó ko parse được.
bạn ơi, mình muốn làm 2 thiết bị trở lên thì phải sửa lại chổ nào nhỉ? Bạn giúp mình với
Cái này đơn giản thôi bạn,
Phía server: Bạn thêm các đoạn code tương tự để thêm biến cho json.
if(isset($_POST[‘LED2_ON’]))
{
if($user==$_POST[‘LED2_ON’])
{
$data[‘led2’] = “on”;
}
}
Dưới arduino: Bạn chỉ cần tăng số đoạn code đọc led1 lên thành led2
Anh ơi có thể cập nhật code làm theo GET để mọi người cùng tham khảo thêm được không ạ?
uh, cái này cũng đơn giản thôi, chỉnh lại phía code php tí là được, anh sẽ cố gắng update sớm nhất có thể, bận nhiều việc quá.
Em đang cần làm luận văn. Đề tài là thu thập dữ liệu và điều khiển thiết bị qua web. Anh có thể giúp em ko. – 0989724144
Bạn email cho mình cụ thể hơn chút nhé, ngoctuansqtt@gmail.com
Mới lấn thử qua mấy cái tự động này, gặp bài của bạn như bắt được vàng, cảm ơn rất nhiều.
Đã test ok trên relay 8 kênh
Cảm ơn bạn đã ủng hộ, chúc bạn thành công với dự án của mình.
Em muốn thay đổi trạng thái cả bằng nút bấm vật lí. Mà nó vẫn hiển thị trạng thái on/off lên web thì làm như nào ạ. Mang anh giúp
Cái này cũng nhiều cách nhưng chắc đơn giản nhất thì anh nghĩ như sau:
– Mỗi khi em nhấn nút, thì đồng thời em gửi một bản tin GET/POST lên server có kèm giá trị của nút nhấn ( 0 or 1)
– Tại server, em viết code để đọc bản tin GET/POST và tách lấy giá trị của nút nhấn, lưu vào file json.
– Việc cuối cùng là, cũng ở trên server, em thêm một đoạn code liên tục cập nhật giá trị của file json nói trên, đồng thời viết code cho hiển thị icon của nút nhấn tương ứng với giá trị trên file json. ( Nếu đọc json=1 thì icon= đỏ, json=0 thì icon= xanh, ví dụ vậy)
Anh có thể viết một bài hướng dẫn được không ạ
Em đang dùng cả nút vật lí và nút trên web. Nhưng khi em dùng nút bấm vật lí thì không được vì trên web trạng thái không đổi. Anh có thể giúp em vấn đề này được không ạ
úp ảnh lên như thế nào v ạ
vẫn chưa hiểu chổ này, làm ơn chỉ giúp:
1.2 Tạo trong thư mục gốc của hosting /public_html 1 file có tên esp_control.php
1.3 Chúng ta sẽ viết script PHP để đọc dữ liệu trong file test.json. Mục đích?. Hiểu đơn giản là khi bạn tắt trình duyệt điều khiển đi ngủ, sáng hôm sau mở trình duyệt lên thì nó sẽ đọc file json này, hiển thị lên trạng thái mà mình đã đặt trước đó cho ESP từ tối hôm qua.
$jsonString = file_get_contents(“test/test.json”);
$data = json_decode($jsonString, true);
1
2
$jsonString = file_get_contents(“test/test.json”);
$data = json_decode($jsonString, true);
Đoạn mã trên sẽ kết hợp với đoạn mã ở 1.7 sẽ hiển thị ra icon on/off trên giao diện
cảm ơn
Ok, để mình nói nhanh qua.
1. Đầu tiên là bạn sẽ viết code PhP lưu tại file esp_control.php, trong cái thư mục trên host của bạn có chữa sẵn cả 2 cái file hình ảnh hiển thị cái đèn bật và đèn tắt (pic_bulbon10.png, pic_bulboff10.png)
2. Bạn tạo ra một cái file có đuôi là .json, mục đichs là lưu dữ liệu theo cấu trúc json.
3. Khi bắt đầu chạy, cái code php sẽ đọc file json. lưu cái nó đọc được vào biến $data.
4. Tiếp theo, trong esp_control.php có một đoạn mã viết bằng javascript, nó sẽ đọc biến $data, nếu $data[‘led1’] == ‘on’ thì nó sẽ lấy cái hình pic_bulbon10.png (ON) ra để hiển thị. Ngược lại thì lấy hình pic_bulboff10.png (OFF) ra để hiển thị.
Cái này thì bạn cần biết qua một chút về php, javascript sẽ hiểu ngay thôi.
Thanks Ad,
mình down và cài XAMPP rồi thử.
Ok, nếu cần hỗ trợ bạn cứ comment.
Chào bạn, Bài viết của bạn rất hữu ích. Bạn cho hỏi:
Nếu mình Kết nối ESP8266 V1 với Arduino Uno R3 bằng cách: RX của EPS nối với chân số 11 của Uno, TX của ESP nối với chân 10 của Uno (dùng thư viện SoftwareSerial.h để tạo chân TX, RX ảo cho Arduino).
Thì Code của mình nên thêm những dòng code nào? Thêm ở đâu cho phù hợp. Cảm ơn bạn.
Bạn có thể nói rõ mục đích của việc nối UART của ESP8266 với Arduino không? nếu như có chức năng gì cụ thể thì mình sẽ hiểu để tư vấn, còn nếu trường hợp bạn dùng Arduino để điều khiển ESP8266 bằng AT commands thì chắc phải viết lại toàn bộ code 🙂
302 Found
Found
The document has moved here.
}
Mình in ra nó báo lỗi này ah! đó là lỗi j bạn. Minh vẫn có thư mục test/test.json
Mình cũng chưa định hình được lỗi này của bạn.