Subscribe Us

header ads

Bài 8. Hưỡng Dẫn Tự Học Esp8266 AJAX Web Server, Tự Động Cập Nhật Trạng Thái Trên Web Server


Esp8266 AJAX JavaScript. Update Server

Như ở bài 4 mình dùng web server và button kết hợp trên esp8266 để điều khiển led. Khi mình nhấn nút on off trên web thì sẽ gửi về esp8266 1 đường dẫn. lúc này esp8266 sẽ lấy uri để sử lý bất tắt led. Như vậy trên web sẽ phải tải đường dẫn mới.
Khi mình sử dụng nút nhấn trên mạch esp8266 thì trạng thái sẽ không được cập nhật, mà phải tự tải lại trang web thì mới cập nhật trạng thái được.
Ở bài này mình sẽ khác phục tình trạng này.
Những ở bài 5 mình đã kết hợp HTML và CSS để tạo ra một mẫu Web Server. ở mẫu này mình đã tạo ra 2 thẻ <H1>

<div class='bc'><h1 id='L1'>ON</h1></div>>
Và 2 button

<button onClick='FA("?Start=1")' class='button'>BUTTON 1</button>


Như vậy ở bài này mình sẽ dùng mẫu ở bài 5 kết hợp với JavaScript để bắt trạng thái button onClick. Và hiển thị trạng thái lên 2 thẻ H1 mà k cần phải tải lại trang web


Mẫu Dao Diện Web Server


JavaScript AJAX là gì

AJAX là một bộ công cụ cho phép load dữ liệu từ server mà không yêu cầu tải lại trang. Nó sử dụng chức năng sẵn có XMLHttpRequest(XHR) của trình duyệt để thực hiện một yêu cầu đến server và xử lý dữ liệu server trả về.

Cách Thức Hoạt Động ESP8266 AJAX

Mình sẽ giải thích cách thức hoạt động truyền nhận dữ liệu giữa web server và esp8266 nhờ AJAX trong JavaScript
Đối với esp8266
Đầu tiền ở chương trình esp8266 mình sẽ để một đường dẫn server.on trong dó sẽ gửi một mẫu dữ liệu trạng thái của led dưới dạng json. Và trong đó sẽ sử lí URI được ajax gửi về dưới dạng GET.
Ở JavaScript trên web.
Sau khi tải xong trang web thì AJAX sẽ thực hiện lấy dữ liệu trạng thái từ đường dẫn chứa trạng thái. Rồi sử lý và thay đổi nội dụng 2 thẻ H1 để hiển thị trạng thái, trạng thái sẽ tự cập nhật sau 1s.
Khi nhấn button thì ajax lấy thông tin button và gửi về đường dẫn trên esp8266 để esp8266 sử lý. Đồng thời cập nhật lại trạng thái hiển thị

Giải thích chương trình trên esp8266 AJAX

Thêm đường dẫn server.on


server.on("/AjaxUpdate",AjaxUpdate);


Thêm Hằng void sử lý và gửi trạng thái cho ajax


void AjaxUpdate(){
  String Ajax;
  Serial.print("URI là :");
  Serial.println(server.uri());
  String argAB = server.arg("AButton");
  Serial.println("argAB là ");
  Serial.println(argAB);
  if (argAB=="1"){
    if(status1=="OFF"){
      status1="ON";
    }else if(status1=="ON"){
      status1="OFF";
    }
    buildLed1();
  }
  if (argAB=="2"){
    if(status2=="OFF"){
      status2="ON";
    }else if(status2=="ON"){
      status2="OFF";
    }
    buildLed2();
  }
  Ajax = "{";
  Ajax += "\"led1\":\"";
  Ajax += status1;
  Ajax += "\",";
  Ajax += "\"led2\":\"";
  Ajax += status2;
  Ajax += "\"}";
  server.send(200,"text/xml",Ajax);

}


Lấy ARG khi button được onclick gửi về từ Ajax


String argAB = server.arg("AButton");
Trả về dưới dạng String được lưu vào biến argAB

Sử Lý Trạng Thái Led Nhận Về Từ AJAX


  if (argAB=="1"){
    if(status1=="OFF"){
      status1="ON";
    }else if(status1=="ON"){
      status1="OFF";
    }
    buildLed1();
  }
Khi nhấn button thì trạng thái được gửi về, ta sẽ thay đổi trạng thái led từ on sang off hoặc ngược lại từ off sang on.
Trạng thái gửi về argAB là 1 biểu hiện cho button 1 được nhấn sẽ thay đổi trạng thái led 1
Trạng thái gửi về argAB là 2 biểu hiện cho button 2 được nhấn sẽ thay đổi trạng thái led 2

Gửi Trạng Thái Led 1 Và 2 Đến AJAX Dưới Dạng Json

Kiểu dữ liệu json như đã được học từ bài 7

  Ajax = "{";
  Ajax += "\"led1\":\"";
  Ajax += status1;
  Ajax += "\",";
  Ajax += "\"led2\":\"";
  Ajax += status2;
  Ajax += "\"}";
Với Key led1 sẽ có value là status1
Với key led2 sẽ có value là status2

Gửi Dữ Liệu Trạng Thái lên AJAX


server.send(200,"text/xml",Ajax);


Giải Thích Chương Trình JavaScript AJAX

Chương trình websever AJAX Esp8288

Mở lên bằng sublime text

  
<!DOCTYPE html>
<html>
<head>
    <meta charset='UTF-8'><meta name='viewport' content='width=device-width,initial-scale=1,user-scalable=no'>
    <title>LEPA Trợ Lý Học Tập</title>
    <script>

    var updata,jsl1,jsl2;
    function FA(p){
        var x=null;
        var a='';
        if(FA.arguments.length==1){
            a=p;
            clearTimeout(updata);
        }
        if(x!=null){
            x.abort();
        }
        x=new XMLHttpRequest();
        x.onreadystatechange=function(){
            if(x.readyState==4&&x.status==200){
                var s=x.responseText;
                console.log(s);
                var js = JSON.parse(s);
                console.log(js.led1);
                console.log(js.led2);
                if(js.led1 == "ON"){
                    jsL1 = 'ON';
                }
                else{
                    jsL1 ='OFF';
                }

                if(js.led2 == "ON"){
                    jsL2 = 'ON';
                }
                else{
                    jsL2 ='OFF';
                }
                document.getElementById('L1').innerHTML=jsL1;
                document.getElementById('L2').innerHTML=jsL2;
            }
        };
        x.open('GET','AjaxUpdate'+a,true);
        x.send();
        updata=setTimeout(FA,1000);
    }
    window.onload=FA();
    </script>
    <style>
     h3 {
        text-align: center
    }
    .ctn, .ctn1, .ft{
        width: 350px;
        margin: 0 auto;
        padding: 0.3rem;
        background: #ffffff;
    }
    .ctn{
        border: 1px solid #ddd;
    }        
    .f-i{
        padding: 8px;
        width: 93%;
        margin-bottom: 1rem
    }
    input[type=submit]{
        width: 100%;
    }
    .button, input[type=submit]{
        background: #69d2e7;
        color: #ffffff;
        padding: 10px;
        border-radius: 10px;
    }
    .button:hover, input[type=submit]:hover{
        background: #088A85;
    }
    .bc, .button {
        display: inline-block;
        margin: 0;
        width: 40%;
    }
    #ct {
        text-align: center;
        }
    .ft{
        font-size: 75%;
        color: #838383;
        text-align: right;
    }
    </style>
</head>
<body>
    <div class='ctn'>
        
        <h3>MAIN MENU</h3>
        <div id=ct>
              <div class='bc'><h1 id='L1'>ON</h1></div>
              <div class='bc'><h1 id='L2'>OFF</h1></div>
          </div>
        <div id=ct>
            <button onClick='FA("?AButton=1")' class='button'>BUTTON 1</button>
            <button onClick='FA("?AButton=2")' class='button'>BUTTON 2</button>
        </div>
    </div>
    <div class='ctn1'>
            <form action='/swifi'>
                    <input type='submit' value='Configure Wifi'>
            </form>
            <form action='/smqtt'>
                    <input type='submit' value='Configure MQTT'>
            </form>
            <form action='/ssms'>
                    <input type='submit' value='Thông Tin'>
            </form>
    </div>
    <div class='ft'>
     <hr>
        <p>&copy; LEPA-Trợ Lý Học Tập</p>
    </div>
</body>
</html>



Trước khi đi vào JavaScript AJAX thì mình giới thiệu một chút về JavaScript DOM

JavaScript DOM là gì

Để thao tác được với các thẻ HTML thì nó phải thông qua một cơ chế ta gọi là DOM và ta hay gọi là Document Object Model
DOM sẽ có nhiệm vụ xử lý các vấn đề như đổi thuộc tính của thẻ, đổi cấu trúc HTML của thẻ,… dựa vào ID của thẻ
Ví dụ

document.getElementById(‘L1’).innerHTML=jsL1;
Phương thức getElementById(L1) trả về phần tử có thuộc tính ID là L1 với giá trị được chỉ định.
Thuộc tính innerHTML đặt hoặc trả về nội dung HTML (HTML bên trong) của một phần tử.
Vậy tập lệnh trên sẽ lấy thuộc tính của thẻ L1 trên HTML và thay nội dung trong thẻ HTML thành nội dung trong biến jsL1

<div class='bc'><h1
id='L1'>ON</h1></div>


Sự kiện onload trong Javascript


window.onload=FA();
Sự kiện này có ý nghĩa rằng khi trình duyệt đã load xong mọi thứ thì sự kiện nằm bên trong window.onload sẽ được chạy. vậy sự kiện này sẽ chạy sau cùng khi mọi thứ đã được tải xong
Vậy khi trang web được tải xong thì function FA sẽ được gọi, function FA Chứa lệnh AJAX

Function JavaScript Là Gì.

Function (hàm, chức năng), gọi chung là subprogram (chương trình con) có thể được gọi ở bên ngoài hoặc bên trong chính nó
Nó cũng giống như lệnh void trong Arduino IDE

Khai Báo Biến Trong JavaScritp


var x=null;


arguments Kiểm Tra Function Có Nhận Được Giá Trị Truyền Vào


if(FA.arguments.length==1)
lệnh arguments chứa các thông số truyền vào của function FA
nếu có thông số truyền vào thì gán giá trị truyền vào vào biến a.
sau đó xóa lệnh hàm thiết lập thời gian update

XMLHttpRequest  Phương thức request của HTTP


x=new XMLHttpRequest();
XMLHttpRequest  được thiết kế để đọc nguồn dữ liệu từ URL gán bằng giá trị x

onreadystatechange này sẽ được kích hoạt mỗi khi có sự kiện từ XMLHttpRequest


x.onreadystatechange=function()
Một Event Handler lắng nghe và xử lý một sự kiện khi có thay đổi về trạng thái nào diễn ra trên XMLHttpRequest

Kiểm tra trạng thái của XMLHttpRequest


if(x.readyState==4&&x.status==200)
readyState trả về gia trị bằng 4 có nghĩa là đã hoàn thành quá trình XMLHttpRequest
status trạng thái rả về từ máy chủ là đúng

responseText trả về văn bản nhận được từ máy chủ sau khi yêu cầu được gửi.


var s=x.responseText;
gán ban bản được nhận về từ XMLHttpRequest vào s

console.log() in giá trị ra cửa sổ console trên trình duyệt


console.log(s);
in giá trị ra cửa sổ console trên trình duyệt

JSON.parse chuyển đổi ra json


var js = JSON.parse(s);
chuyển tài liệu lưu trong s thành Json lưu vào js

lấy giá trị trong json


js.led1
Lấy value từ key led1 trong json js

if(js.led1 == "ON"){
lấy giá trịnh nhận được từ esp8266 so sánh để gán giá trị vào biến jsL1jsL2

document.getElementById('L1').innerHTML=jsL1;
gán giá trị của jsl1 vào thẻ có id L1 trên HTML

XMLHttpRequest.open() AJAX Gửi yêu cầu đến máy chủ

Mẫu open(method, url, async)

x.open('GET','AjaxUpdate'+a,true);
gửi giá trị về server với phương thức GET
địa chỉ URL là /AjaxUpdate + với giá trị được truyền vào trong function FA
URL EventEsp được tạo trên esp tới nới chứa chương trình sử lý giá trị truền về và gửi lên web chuỗi json chưa trạng thái led.
Async = true là không đồng bộ với server

x.send();
gửi yêu cầu tới url /AjaxUpdate

setTimeout gọi một hàm sau thơi gian chỉ đinh


updata=setTimeout(FA,1000);
Phương Thức setTimeout dùng để gọi hàm FA sau khoảng thời gian 1000ms = 1s

button onClick HTML


<button onClick='FA("?AButton=1")'
class='button'>BUTTON 1</button>
Trong HTML có thẻ Button với sự kiện onClick
Khi onclick vào button thì sẽ gửi AButoon=1 tới function FA. Sau đó giá trị sẽ được gửi đi tới url AjaxUpdate


Chương trình đầy đủ cho esp8266 trên Arduino IDE


  
/*  LePan Trợ Lý Học Tập
 *  WEB Button AJAX
 *  tự động cập nhật trạng thái mà k tải lại trang
 *  Tự học esp 8266
 *  https://trolyhoctap.blogspot.com/
*/
 
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
ESP8266WebServer server(80);
const char* ssid="HomeSmart";       //viết têm wifi muốn truy cập
const char* password="098847474567"; // viết mật khẩu wifi đo
String webSite;
int ledPin1=4;
int ledPin2=5;
int buttonPin1=12;
int buttonPin2=14;
String status1 = "OFF";
String status2 = "OFF";
int previous1;        //trạng thái trước khi an nút
int previous2;
int reading1;        // lưu lại trạng thái nút ấn ở cuối loop
int reading2;
long time1 = 0;       // dùng để lưu millis khi nhấn nút
long time2 = 0;
long debounce = 200;    // thời gian chờ chống rung nút nhấn
//===========================Khai Báo String Cho WEB==================
String CSS = "h3 { text-align: center } .ctn, .ctn1, .ft{ width: 350px; margin: 0 auto; padding: 0.3rem; background: #ffffff; } .ctn{ border: 1px solid #ddd; } .f-i{ padding: 8px; width: 93%; margin-bottom: 1rem } input[type=submit]{ width: 100%; } .button, input[type=submit]{ background: #69d2e7; color: #ffffff; padding: 10px; border-radius: 10px; } .button:hover, input[type=submit]:hover{ background: #088A85; } .bc, .button { display: inline-block; margin: 0; width: 40%; } #ct { text-align: center; } .ft{ font-size: 75%; color: #838383; text-align: right; }";
//trạng thái led
void buildLed1(){
  if (status1 == "ON"){
        Serial.print("status1 la ");
        Serial.println(status1);
        digitalWrite(ledPin1, LOW);       // Led Kich ở mức thấp
  }
  else {
        Serial.print("status1 la ");
        Serial.println(status1);
        digitalWrite(ledPin1, HIGH);
  }
}

void buildLed2(){
  if (status2 == "ON"){
        Serial.print("status2 la ");
        Serial.println(status2);
        digitalWrite(ledPin2, LOW);   //led bật ở mức thấp
  }
  else {
        Serial.print("status2 la ");
        Serial.println(status2);
        digitalWrite(ledPin2, HIGH);
  }
}
//Trang Chủ
void handleWebsite(){
  webSite = "<html lang=\"en\" class=\"\"><head>";
  webSite += "<meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1,user-scalable=no\">";
  webSite += "<title>LePa Trợ Lý Học Tập</title>";
  webSite += "<script>";
  webSite += "var updata,jsl1,jsl2;";
  webSite += "function FA(p){";
  webSite += "var x=null;";
  webSite += "var a='';";
  webSite += "if(FA.arguments.length==1){";
  webSite += "a=p;";
  webSite += "clearTimeout(updata);";
  webSite += "}";
  webSite += "if(x!=null){";
  webSite += "x.abort();";
  webSite += "}";
  webSite += "x=new XMLHttpRequest();";
  webSite += "x.onreadystatechange=function(){";
  webSite += "if(x.readyState==4&&x.status==200){";
  webSite += "var s=x.responseText;";
  webSite += "console.log(s);";
  webSite += "var js = JSON.parse(s);";
  webSite += "console.log(js.led1);";
  webSite += "console.log(js.led2);";
  webSite += "if(js.led1 == 'ON'){";
  webSite += "jsL1 = 'ON';";
  webSite += "}";
  webSite += "else{";
  webSite += "jsL1 ='OFF';";
  webSite += "}";
  webSite += "if(js.led2 == 'ON'){";
  webSite += "jsL2 = 'ON';";
  webSite += "}";
  webSite += "else{";
  webSite += "jsL2 ='OFF';";
  webSite += "}";
  webSite += "document.getElementById('L1').innerHTML=jsL1;";
  webSite += "document.getElementById('L2').innerHTML=jsL2;";
  webSite += "}";
  webSite += "};";
  webSite += "x.open('GET','EventEsp'+a,true);";
  webSite += "x.send();";
  webSite += "updata=setTimeout(FA,1000);";
  webSite += "}";
  webSite += "window.onload=FA();";
  webSite += "</script>";
  webSite += "<style>";
  webSite += CSS;
  webSite += "</style>";
  webSite += "</head>";
  webSite += "<body>";
  webSite += "<div class='ctn'>";
  webSite += "<h3>MAIN MENU</h3>";
  webSite += "<div id=ct>";
  webSite += "<div class='bc'><h1 id='L1'>ON</h1></div>";
  webSite += "<div class='bc'><h1 id='L2'>OFF</h1></div>";
  webSite += "</div>";
  webSite += "<div id=ct>";
  webSite += "<button onClick='FA(\"?Start=1\")' class='button'>BUTTON 1</button>";
  webSite += "<button onClick='FA(\"?Start=2\")' class='button'>BUTTON 2</button>";
  webSite += "</div>";
  webSite += "</div>";
  webSite += "<div class='ctn1'>";
  webSite += "<form action='/swifi'>";
  webSite += "<input type='submit' value='Configure Wifi'>";
  webSite += "</form>";
  webSite += "<form action='/smqtt'>";
  webSite += "<input type='submit' value='Configure MQTT'>";
  webSite += "</form>";
  webSite += "<form action='/ssms'>";
  webSite += "<input type='submit' value='Thông Tin'>";
  webSite += "</form>";
  webSite += "</div>";
  webSite += "<div class='ft'>";
  webSite += "<hr>";
  webSite += "<p>&copy; LEPA-Trợ Lý Học Tập</p>";
  webSite += "</div>";
  webSite += "</body>";
  webSite += "</html>";
  server.send(200,"text/html",webSite);
}

//AJAX WebServer
void AjaxUpdate(){
  String Ajax;
  Serial.print("URI là :");
  Serial.println(server.uri());
  String argAB = server.arg("AButton");
  Serial.println("argAB là ");
  Serial.println(argAB);
  if (argAB=="1"){
    if(status1=="OFF"){
      status1="ON";
    }else if(status1=="ON"){
      status1="OFF";
    }
    buildLed1();
  }
  if (argAB=="2"){
    if(status2=="OFF"){
      status2="ON";
    }else if(status2=="ON"){
      status2="OFF";
    }
    buildLed2();
  }
  Ajax = "{";
  Ajax += "\"led1\":\"";
  Ajax += status1;
  Ajax += "\",";
  Ajax += "\"led2\":\"";
  Ajax += status2;
  Ajax += "\"}";
  server.send(200,"text/xml",Ajax);

}
void setup() {
  Serial.begin(9600);  
  Serial.println("LePan Trợ Lý Học Tập");
  pinMode(ledPin1,OUTPUT);
  pinMode(ledPin2,OUTPUT);
  pinMode(buttonPin1,INPUT);
  pinMode(buttonPin2,INPUT);
  digitalWrite(ledPin1, HIGH);
  digitalWrite(ledPin2, HIGH);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid,password);
  while(WiFi.status()!=WL_CONNECTED){
    delay(500);
    Serial.print(".");
  }
  Serial.println("kết nối thành công");
  Serial.print("đã kết nối tới ");
  Serial.println(ssid);
  Serial.print("địa chỉ IP là: ");
  Serial.println(WiFi.localIP());
  server.on("/",handleWebsite);
  server.on("/4on",handleWebsite);
  server.on("/4off",handleWebsite);
  server.on("/5on",handleWebsite);
  server.on("/5off",handleWebsite);
  server.on("/AjaxUpdate",AjaxUpdate);
  
  server.begin();
}

void loop() {
    server.handleClient();
    reading1 = digitalRead(buttonPin1);
    reading2 = digitalRead(buttonPin2);
    if(reading1 == HIGH  && previous1 == LOW && millis() - time1 > debounce) {
        if(status1=="OFF"){
          status1="ON";
        }else if(status1=="ON"){
          status1="OFF";
        }
        time1 = millis();
        Serial.println("reading1");
        Serial.println(reading1);
        buildLed1();
    }
    
    if(reading2 == HIGH  && previous2 == LOW && millis() - time2 > debounce) {
        if(status2=="OFF"){
          status2="ON";
        }else if(status2=="ON"){
          status2="OFF";
        }
        time2 = millis();
        Serial.println("reading2");
        Serial.println(reading2);
        buildLed2();
    }
    previous1 = reading1;
    previous2 = reading2;
} 


Sau khi nạp thành công vào esp8266
Mở cửa sổ serial monitor lấy ip truy cập
Sau khi truy cập vào ip của esp thì cứa sau 1s thì ajax sẽ lấy giữ liệu từ esp8266 trong đường dẫn EventEsp. Bạn thử nhấn button trên web và trên mach esp8266 sẽ thấy sự thay đổi của trạng thái leb và cập nhật lên web


Cứa Sổ Seral Monitor


Đăng nhận xét

0 Nhận xét