Lập trình ESP32 FOTA nạp firmware từ xa bằng local wifi

lap trinh esp32 FOTA nap firmware tu xa

Trong bài này chúng ta sẽ học cách sử dụng ESP32 FOTA để nạp firmware từ xa cho thiết bị của mình. Đây là một quy trình không thể thiếu cho các sản phẩm IOT hiện nay bởi tính tiện dụng của nó

Bài 8 Wifi trong Serie Học ESP32 từ A tới Z

FOTA là gì?

FOTA (Firmware Over The Air) là thuật ngữ chỉ việc nạp firmware từ xa mà không cần sử dụng mạch nạp, chỉ cần có 1 kết nối không dây là có thể sử dụng được. FOTA được sử dụng rộng rãi trong các sản phẩm IOT.

Các bạn đã từng cập nhật hệ điều hành điện thoại hay chưa? Đó chính là FOTA đó. Hệ điều hành sẽ được cập nhật thông qua internet, điện thoại sau khi cập nhật xong sẽ restart và chạy hệ điều hành mới.

fota esp32

Tại sao chúng ta phải sử dụng FOTA

FOTA đem lại rất nhiều lợi ích khi lập trình IOT

  • Giúp việc nạp firmware trong quá trình R&D dễ dàng hơn, bởi nhiều khi sản phẩm IOT bị đóng kín, không có các chân để giao tiếp với bên ngoài mà chỉ giao tiếp qua các chuẩn không dây
  • Thuận tiện trong việc bảo trì, nâng cấp firmware.
  • Cập nhật các tính năng mới của sản phẩm và app

Tuy nhiên khi FOTA chúng ta sẽ gặp những vấn đề sau:

  • Tốn bộ nhớ khi sử dụng
  • Cần bảo mật các giao thức nạp hoặc sử dụng user dành riêng cho việc nạp

 

Lập trình ESP32 FOTA nạp firmware từ xa trong mạng local wifi

Trong bài này chúng ta sử dụng ESP32 FOTA, nạp chương trình blynk led bằng wifi cho ESP32, sử dụng webserver được tạo ra bởi ESP32.

esp32 fota

Thiết lập trên server ESP32

Thiết lập style, màu sắc của web page

style fota

Thiết lập giao diện login

Để đổi tài khoản và mật khẩu login các bạn sửa câu lệnh: "if(form.userid.value=='admin' && form.pwd.value=='khuenguyencreator')"

Trong bài này ta sử dụng:

  • ID: admin
  • Password: khuenguyencreator
login fota

Thiết lập giao diện upload

login scrip

Cập nhập firmware trên ESP32

Kết nối với wifi, quá quen thuộc với bạn rồi.

connect wifi

Sử dụng mDNS để truy cập đơn giản hơn, không cần sử dụng ip

local mdns

Nạp firmware với file upload lên. Phần này chúng ta sẽ thêm các cách xử lý cho ESP32 mỗi event sảy ra

  • Event khi vào trang login
  • Event khi đăng nhập, vào trang upload
  • Event khi upload firmware và nạp vào esp32

Để nạp firmware vào ESP32 ta sử dụng thư viện Update, với các hàm Update.begin , Update.buf , Update.end 

upload firmware

Trong loop chúng ta bật hàm xử lý các event. Và delay 1ms.

Full code

#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>

const char* host = "esp32";
const char* ssid = "hellovtag";
const char* password = "12345678";

WebServer server(80);

/* Style */
String style =
"<style>#file-input,input{width:100%;height:44px;border-radius:4px;margin:10px auto;font-size:15px}"
"input{background:#f1f1f1;border:0;padding:0 15px}body{background:#3498db;font-family:sans-serif;font-size:14px;color:#777}"
"#file-input{padding:0;border:1px solid #ddd;line-height:44px;text-align:left;display:block;cursor:pointer}"
"#bar,#prgbar{background-color:#f1f1f1;border-radius:10px}#bar{background-color:#3498db;width:0%;height:10px}"
"form{background:#fff;max-width:258px;margin:75px auto;padding:30px;border-radius:5px;text-align:center}"
".btn{background:#3498db;color:#fff;cursor:pointer}</style>";

/* Login page */
String loginIndex = 
"<form name=loginForm>"
"<h1>ESP32 FOTA</h1>"
"<input name=userid placeholder='User ID'> "
"<input name=pwd placeholder=Password type=Password> "
"<input type=submit onclick=check(this.form) class=btn value=Login></form>"
"<script>"
"function check(form) {"
"if(form.userid.value=='admin' && form.pwd.value=='khuenguyencreator')"
"{window.open('/serverIndex')}"
"else"
"{alert('Error Password or Username')}"
"}"
"</script>" + style;
 
/* Server Index Page */
String serverIndex = 
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
"<input type='file' name='update' id='file' onchange='sub(this)' style=display:none>"
"<label id='file-input' for='file'>   Choose file...</label>"
"<input type='submit' class=btn value='Update'>"
"<br><br>"
"<div id='prg'></div>"
"<br><div id='prgbar'><div id='bar'></div></div><br></form>"
"<script>"
"function sub(obj){"
"var fileName = obj.value.split('\\\\');"
"document.getElementById('file-input').innerHTML = '   '+ fileName[fileName.length-1];"
"};"
"$('form').submit(function(e){"
"e.preventDefault();"
"var form = $('#upload_form')[0];"
"var data = new FormData(form);"
"$.ajax({"
"url: '/update',"
"type: 'POST',"
"data: data,"
"contentType: false,"
"processData:false,"
"xhr: function() {"
"var xhr = new window.XMLHttpRequest();"
"xhr.upload.addEventListener('progress', function(evt) {"
"if (evt.lengthComputable) {"
"var per = evt.loaded / evt.total;"
"$('#prg').html('progress: ' + Math.round(per*100) + '%');"
"$('#bar').css('width',Math.round(per*100) + '%');"
"}"
"}, false);"
"return xhr;"
"},"
"success:function(d, s) {"
"console.log('success!') "
"},"
"error: function (a, b, c) {"
"}"
"});"
"});"
"</script>" + style;

/* setup function */
void setup(void) {
  Serial.begin(115200);

  // Connect to WiFi network
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  /*use mdns for host name resolution*/
  if (!MDNS.begin(host)) { //http://esp32
    Serial.println("Error setting up MDNS responder!");
    while (1) {
      delay(1000);
    }
  }
  Serial.println("mDNS responder started");

  /*return index page which is stored in serverIndex */
  server.on("/", HTTP_GET, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", loginIndex);
  });
  server.on("/serverIndex", HTTP_GET, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", serverIndex);
  });
  /*handling uploading firmware file */
  server.on("/update", HTTP_POST, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
    ESP.restart();
  }, []() {
    HTTPUpload& upload = server.upload();
    if (upload.status == UPLOAD_FILE_START) {
      Serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE) {
      /* flashing firmware to ESP*/
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_END) {
      if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
      } else {
        Update.printError(Serial);
      }
    }
  });
  server.begin();
}

void loop(void) {
  server.handleClient();
  delay(1);
}

 

Kết quả

Sau khi nạp code vào ESP32, chung ta sử dụng Arduino, mở example blink led. Tạo bin file như sau:

tao bin file bang arduino

Nếu sử dụng platformio, các bạn nhấn build project, bin file sẽ nằm trong thư mục .pio theo đường dẫn

tao firmware bang platformio

Để upload firmware, chúng ta mở trình duyệt, vào ip hoặc mDNS của ESP32.

UPLOAD FOTA 1

Login vào đúng account đã tạo

UPLOAD FOTA

Nhấn update, tìm tới file bin đã tạo ra.

CHON FILE

Nhấn update, kết quả ESP32 sẽ reboot và chạy firmware của blink, nháy led liên tục.

fota success

reboot

Lưu ý: Vì firmware blink không có fota, vậy nên chúng ta không thể fota cho esp32 sau khi nạp code blink. Để có thể sử dụng fota tiếp thì bắt buộc bản firmware bạn nạp vào cũng cần có chức năng fota

Kết

FOTA là chức năng quan trọng trong lập trình IOT, tuy rằng FOTA sử dụng mạng nội bộ không được sử dụng nhiều trong các sản phẩm thực tế, tuy nhiên chúng vẫn có nhiều ứng dụng. Thông thường firmware sẽ được lưu tại 1 server trên internet và ESP32 sẽ kết nối và download firmware từ đó. Giúp lập trình viên quản lí version dễ dàng hơn, chi tiết chúng ta sẽ học trong bài sau.

Cám ơn các bạn đã đọc bài viết, cùng vào hội Anh Em Nghiện Lập Trình để cùng trao đổi nhé

 

5/5 - (2 bình chọn)

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *