아두이노와 Processing

esp32 아두이노 OTA(Over The Air, arduinoOTA)에 의한 LED ON/OFF

coding art 2021. 2. 11. 13:46
728x90

OTA(Over The Air) 기법은 최근 테슬러의 자율주행 차량 코드 업데이트 실용화에 사용됨에 따라 그 중요성이 크게 부각되고 있다. 물론 절차에 따라 OTA 기법을 적용하면 되겠으나 한편으로는 외부로부터의 해킹 차단 문제가 대단히 큰 이슈라 볼 수 있다. 개인이 작성한 아두이노 코드야 별 문제가 없겠지만 전기차의 경우 보안 문제에 대한 해결책 없이 함부로 적용할 경우 해킹을 당한다면 엄청난 위험을 수반하게 됨에 유의하자.

 

따라서 esp32 보드에서 OTA에 의한 코드 웹 업로딩 시에도 보안 차원에서 사용자 ID 와 비밀번호에 보안을 기하도록하자.

OTA web Updater를 사용하기 위해서는 다음과 같이 esp32 가 설치된 아두이노 IDE의 예제에서 OTAWebupdater 코드를 불러오자.

<Update.h>로 끝나는 헤더 영역에 이어 “xxx”“xxxx”에 사용자의 무선 공유기 ID 와 비밀번호 정보를 입력 후 OTAUpdater 코드를 USB 케이블에 의해 업로딩 하도록 하자.

OTAUpdate이어지는 코드 내용은 local ip를 사용하여 무선 호출 가능한 웹에서 login ID 와 비밀번호를 입력 받는 html 코드이다. Default ID 명과 비밀번호는 “admin” 으로 설정되어 있으므로 변경하고 싶으면 찾아서 수정해 두면 된다.

setup()에는 통신속도 설정과 WiFi 연결 및 Webserver 코드가 포함되어 있으며, 이 부분은 전혀 손을 댈 필요가 없음에 유의하자. 마지막 loop() 문을 살펴보면 아래와 같이 2줄의 간단한 코드만이 들어 있다.

보드명과 COM 포트를 설정 후 업로딩 하되 시리얼 모니터에서 local IP를 확인하도록 하자.

esp32 보드 별로 local IP가 달라질 수 있으며 그 형식은 192.168.0.32 라든지 또는 192.168.0.34 와 같은 형태로 출력된다. 이 정보를 기억하여 사용자 코드를 업데이트 한 후 크롬 웹에서 웹서버를 호출하도록 한다.

 

업로딩 완료 후에는 OTA가 가능해 졌으므로 USB 케이블을 뽑아서 차단한 후 건전지와 같은 독립전원으로 esp32 보드에 전원을 공급하자.

다음 단계로는 사용자가 업로드 하고 싶은 esp32 아두이노 코드를 헤드, setup loop 별로 작성하자. 여기서는 GPIO 23번 핀에 LED 220옴 저항을 설치하고 일정 시간 간격으로 블링킹하는 회로를 구동할 수 있도록 상기의 OTAUpdater 코드를 업데이트 해 비교해 보기로 한다.

라이브러리를 불러들이는 헤더 영역과 html 코드 사이에 LED 블링킹을 위한 GPIO 핀 지정을 위한 업데이트 내용들이 추가되었음을 알 수 있다. 이 내용에 부합하도록 아래와 같이 setup에는 pinMode 명령이 추가되어야 하면 loop에서는 digitalWrite 명령 코드 작성이 필요하다.

LED 블링킹을 위한 사용자 코드 업데이트가 완료되었으면 코드명을 부여하여 저장한 후에 아래와 같이 bin 파일(binary file)을 생성하자.

사용자 코드 OTA 업로딩을 위한 준비가 완료되었으므로 반드시 크롬 웹을 열어 앞서 기억해 둔 local IPURL 박스에 입력하면 아래의 Login 화면을 볼 수 있다. 미지 지정해 둔 IP 와 비밀번호를 입력하고 Login 버튼을 누르자.

로그 인 후 아래의 화면에서 Choose File 버튼을 누르고 파일을 탐색 선택한 후 Updatae 버튼을 눌러 progress:100% 에 도달함을 확인하자. 이로서 사용자에 의해 업데이트 되었던 LED 블링킹 코드가 업로딩 되어 실행 됨을 확인하자.

LED블링킹 사용자 코드가 업데이트 된 첨부 코드를 참조하여라.

//ThingSpeak_OTAWebUpdater

#include <WiFi.h>

#include <WiFiClient.h>

#include <WebServer.h>

#include <ESPmDNS.h>

#include <Update.h>

 

const char* host = "esp32";

const char* ssid = "droidan1234";

const char* password = "dddddddddd";

 

WebServer server(80);

//variabls to blink

const int led = 23;

unsigned long previousMillis = 0; // will store last time LED was updated

const long interval = 5000; // interval at which to blink (milliseconds)

int ledState = LOW; // ledState used to set the LED

 

//Login page

 

const char* loginIndex =

"<form name='loginForm'>"

"<table width='20%' bgcolor='A09F9F' align='center'>"

"<tr>"

"<td colspan=2>"

"<center><font size=4><b>ESP32 Login Page</b></font></center>"

"<br>"

"</td>"

"<br>"

"<br>"

"</tr>"

"<td>Username:</td>"

"<td><input type='text' size=25 name='userid'><br></td>"

"</tr>"

"<br>"

"<br>"

"<tr>"

"<td>Password:</td>"

"<td><input type='Password' size=25 name='pwd'><br></td>"

"<br>"

"<br>"

"</tr>"

"<tr>"

"<td><input type='submit' onclick='check(this.form)' value='Login'></td>"

"</tr>"

"</table>"

"</form>"

"<script>"

"function check(form)"

"{"

"if(form.userid.value=='admin' && form.pwd.value=='admin')"

"{"

"window.open('/serverIndex')"

"}"

"else"

"{"

" alert('Error Password or Username')/*displays error message*/"

"}"

"}"

"</script>";

 

/*

* Server Index Page

*/

 

const char* serverIndex =

" "

"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"

"<input type='file' name='update'>"

"<input type='submit' value='Update'>"

"</form>"

"<div id='prg'>progress: 0%</div>"

"<script>"

"$('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) + '%');"

"}"

"}, false);"

"return xhr;"

"},"

"success:function(d, s) {"

"console.log('success!')"

"},"

"error: function (a, b, c) {"

"}"

"});"

"});"

"</script>";

 

/*

* setup function

*/

void setup(void) {

Serial.begin(115200);

pinMode(led, OUTPUT);

// 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.local

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);

 

//loop to blink without delay

unsigned long currentMillis = millis();

if (currentMillis - previousMillis >= interval) {

previousMillis = currentMillis;

ledState = not(ledState);

digitalWrite(led, ledState);

}

}