{"id":4397,"date":"2023-07-28T12:00:47","date_gmt":"2023-07-28T19:00:47","guid":{"rendered":"https:\/\/www.cloudacm.com\/?p=4397"},"modified":"2023-07-27T06:49:08","modified_gmt":"2023-07-27T13:49:08","slug":"esp32-cam-ota-updates","status":"publish","type":"post","link":"https:\/\/www.cloudacm.com\/?p=4397","title":{"rendered":"ESP32-Cam OTA Updates"},"content":{"rendered":"<p>This post addresses the problem of servicing ESP based systems that have been installed.\u00a0 I have built systems where the ESP module is fixed, enclosed, and installed in a location that is difficult to get to once in place.\u00a0 As time progresses, so does the realization that some features will need to be updated on those installed ESP modules.\u00a0 Over The Air updates are the ideal way to update the modules without the hassle or downtime.\u00a0 Here is a detailed explanation from Andreas Spiess about the topic of OTA.<\/p>\n<p><iframe loading=\"lazy\" title=\"#332 ESP32  OTA tutorial with tricks (incl. OTA debugging)\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/1pwqS_NUG7Q?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>The Arduino IDE lists devices that are OTA ready as an available port.\u00a0 These devices can be programmed just as they would if they were connected with a serial connection.\u00a0 I was able to program ESP8266 modules this way with no trouble.\u00a0 However, I had trouble when I attempted to program ESP32-Cam modules.<\/p>\n<p>This forum had some insights as to why, <a href=\"https:\/\/forum.arduino.cc\/t\/ota-no-response-from-device\/577868\/6\">https:\/\/forum.arduino.cc\/t\/ota-no-response-from-device\/577868\/6<\/a>, but my problem persisted.<\/p>\n<p>This post on GitHub gave more insights into the ESP32-Cam module&#8217;s issue, <a href=\"https:\/\/github.com\/sigmdel\/ESP32-CAM_OTA\">https:\/\/github.com\/sigmdel\/ESP32-CAM_OTA<\/a><\/p>\n<p>&#8220;There is a well-known problem with using the ArduinoOTA library with the AI Thinker ESP32-CAM board and its many clones.&#8221;<\/p>\n<p>&#8220;The problem has nothing to do with the ArduinoOTA library. Its source is the memory partition in the ESP32-CAM board definition. The esp32cam.build.partition=huge_app setting in the board definition file (board.txt), specifies that most of the memory is allocated to a single app partition (3MiB in size) leaving no room for the uploaded firmware update.&#8221;<\/p>\n<p>Turns out it was addressed in this release of the boards.txt file, <a href=\"https:\/\/github.com\/espressif\/arduino-esp32\/blob\/master\/boards.txt\">https:\/\/github.com\/espressif\/arduino-esp32\/blob\/master\/boards.txt<\/a><\/p>\n<p>The directive &#8220;esp32cam.menu.PartitionScheme&#8221; now contains options for partition selection from the Arduino IDE that might be missing for some unaware of the problem.\u00a0 Uploading programs to the ESP32-Cam using OTA worked.\u00a0 This video has comments by viewers that point out the missing menu option.<\/p>\n<p><iframe loading=\"lazy\" title=\"How To Setup OTA (Over The Air) Updates With An ESP32-CAM Microcontroller\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/vGa59IwaOhI?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>It&#8217;s always important to verify the OTA and FS options are correct when compiling.\u00a0 If these settings are overlooked, the resulting firmware may not load properly, or worse will load but manifest as a problem that is difficult to troubleshoot.\u00a0 Take the time to verify the setting is correct for the device being flashed.<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/07\/FlashPartition.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-4426\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/07\/FlashPartition.png\" alt=\"\" width=\"782\" height=\"170\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/07\/FlashPartition.png 782w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/07\/FlashPartition-300x65.png 300w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/07\/FlashPartition-768x167.png 768w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/07\/FlashPartition-604x131.png 604w\" sizes=\"auto, (max-width: 782px) 100vw, 782px\" \/><\/a><\/p>\n<p>The idea of having OTA always on by default isn&#8217;t appealing.\u00a0 This provides some insights as to why it could be bad idea to always have it enabled, <a href=\"https:\/\/video.tau.ac.il\/events\/index.php?option=com_k2&amp;view=item&amp;id=8045:dont-let-the-cuteness-fool-you&amp;Itemid=559\">https:\/\/video.tau.ac.il\/events\/index.php?option=com_k2&amp;view=item&amp;id=8045:dont-let-the-cuteness-fool-you&amp;Itemid=559<\/a>.<\/p>\n<div style=\"width: 640px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-4397-1\" width=\"640\" height=\"360\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/video.tau.ac.il\/events\/media\/k2\/videos\/8045.mp4?_=1\" \/><a href=\"https:\/\/video.tau.ac.il\/events\/media\/k2\/videos\/8045.mp4\">https:\/\/video.tau.ac.il\/events\/media\/k2\/videos\/8045.mp4<\/a><\/video><\/div>\n<p>Fortunately, OTA is a function and it can be called as needed.\u00a0 Placing that function in a conditional call allows one to start OTA on the ESP module prior to uploading firmware.\u00a0 When the ESP module reboots with the new firmware, OTA will not be on.\u00a0 Here is code with the OTA in a function.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#include &lt;ArduinoOTA.h&gt;\r\n\r\nint programflag = 0;\r\n\r\n\r\nvoid OTABeginInstance() {\r\n\r\n  \/\/ Port defaults to 3232\r\n  ArduinoOTA.setPort(3232);\r\n\r\n  \/\/ Hostname defaults to esp3232-[MAC]\r\n  ArduinoOTA.setHostname(\"ESP32-Cam\");\r\n\r\n  \/\/ No authentication by default\r\n  \/\/ ArduinoOTA.setPassword(\"password_here\");\r\n\r\n  \/\/ Password can be set with it's md5 value as well\r\n  \/\/ MD5(admin) = md5_hash_here\r\n  \/\/ ArduinoOTA.setPasswordHash(\"md5_hash_here\");\r\n\r\n  ArduinoOTA.begin();\r\n  \r\n}\r\n\r\nvoid OTAReady() {\r\n  ArduinoOTA.handle();\r\n}\r\n\r\nvoid TriggerMyFunctions() {\r\n  \r\n  \/\/ Your meaningful code here with a condition you trigger\r\n  \/\/ if statement to check your trigger\r\n  \/\/ if true then set programflag = 1 and call function OTABeginInstance()\r\n\r\n}\r\n\r\nvoid loop() {\r\n\r\n  \/\/ Your meaningful code here\r\n  TriggerMyFunctions();\r\n  \r\n  if (programflag &gt; 0) { \/\/ this only runs if you trigger it\r\n    OTAReady();\r\n  }\r\n\r\n}<\/pre>\n<p>Rui and Sara detail steps on how to install firmware that doesn&#8217;t require the Arduino IDE.\u00a0 Here is a link, <a href=\"https:\/\/randomnerdtutorials.com\/esp32-over-the-air-ota-programming\/\">https:\/\/randomnerdtutorials.com\/esp32-over-the-air-ota-programming\/<\/a>.\u00a0 They also provide details on how to create the binary file needed in this link, <a href=\"https:\/\/randomnerdtutorials.com\/bin-binary-files-sketch-arduino-ide\/\">https:\/\/randomnerdtutorials.com\/bin-binary-files-sketch-arduino-ide\/<\/a><\/p>\n<p>The OTAWebUpdater does have some flaws you should consider before using it beyond getting familiar with OTA or debugging.\u00a0 The example OTAWebUpdater exposes the username and password, or can be bypassed.\u00a0 The connection is via HTTP and can be packet sniffed and the login can be viewed in plain text.\u00a0 The source can be viewed via the browser debug tools without any authentication and it contains the login info, &#8220;if(form.userid.value==&#8217;admin&#8217; &amp;&amp; form.pwd.value==&#8217;admin&#8217;)&#8221;<br \/>\nLastly, the login form can be bypassed altogether by opening the page directly, &#8220;window.open(&#8216;\/serverIndex&#8217;)&#8221;, in this case http:\/\/website\/serverIndex.\u00a0 Here is a video with some more insights into the topic of securing IoT devices.<\/p>\n<p><iframe loading=\"lazy\" title=\"#232 How to secure our devices using SSL (ESP8266, ESP32, Tutorial)\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/Wm1xKj4bKsY?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>For the sake of simplicity and to make the topic digestible to those new to the subject, we&#8217;ll forgo security for now.\u00a0 Just do be aware that this is only to demonstrate the fundamentals before delving into more advanced secure methods.<\/p>\n<p>This code base used the &#8220;httpUpdate&#8221; example included with the board library.\u00a0 These were the minimal required lines to get the ESP module to get its firmware directly from a web server.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\/\/ These are the Libraries and Definitions used\r\n#include &lt;HTTPUpdate.h&gt;\r\n#include &lt;WiFiClient.h&gt;\r\nWiFiClient espClient;\r\n\r\n\/\/ This line is the workhorse with messageTemp being a variable that can be set any number of ways\r\nt_httpUpdate_return ret = httpUpdate.update(espClient, \"http:\/\/&lt;website&gt;\/\" + messageTemp + \".bin\");  \r\n<\/pre>\n<p>Using SSL with the &#8220;httpUpdateSecure&#8221; only required a few more lines of code.\u00a0 Add the time library &#8220;#include &lt;time.h&gt;&#8221;.\u00a0 Setting and calling the &#8220;setClock()&#8221; function, correct time is required for cryptography.\u00a0 Setting the cert variable &#8220;const char* rootCACertificate&#8221;.\u00a0 Applying the client cert with &#8220;client.setCACert(rootCACertificate);&#8221;.\u00a0 Setting a longer timeout to tolerate ssl latency with &#8220;client.setTimeout(12000 \/ 1000);&#8221;.\u00a0 Then make the secure web request with the following lines of code.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">t_httpUpdate_return ret = httpUpdate.update(client, \"https:\/\/&lt;website&gt;\/\" + messageTemp + \".bin\");\r\n\/\/ Or:\r\n\/\/t_httpUpdate_return ret = httpUpdate.update(client, \"server\", 443, \"\/file.bin\");<\/pre>\n<p>All of these examples should satisfy most of the OTA needs developers have.\u00a0 This last example will demonstrate how to use Node-Red and MQTT to check and apply firmware updates on demand.\u00a0 Here are the nodes used.<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/08\/WebGet_OTA_Nodes.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-4411\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/08\/WebGet_OTA_Nodes.png\" alt=\"\" width=\"735\" height=\"103\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/08\/WebGet_OTA_Nodes.png 735w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/08\/WebGet_OTA_Nodes-300x42.png 300w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/08\/WebGet_OTA_Nodes-604x85.png 604w\" sizes=\"auto, (max-width: 735px) 100vw, 735px\" \/><\/a><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">[{\"id\":\"d91e2413.c522e\",\"type\":\"http in\",\"z\":\"612c5528.096aa4\",\"name\":\"\",\"url\":\"\/get-firmware-path\",\"method\":\"get\",\"upload\":false,\"swaggerDoc\":\"\",\"x\":160,\"y\":140,\"wires\":[[\"d77bfa42.a1998\"]]},{\"id\":\"d77bfa42.a1998\",\"type\":\"function\",\"z\":\"612c5528.096aa4\",\"name\":\"Uncomment FIrmware to use\",\"func\":\"\/\/ msg.payload = \\\"http:\/\/&lt;website&gt;\/ESP32-Cam_ver3.bin\\\";\\nmsg.payload = \\\"http:\/\/&lt;website&gt;\/ESP32-Cam_ver4.bin\\\";\\n\/\/ msg.payload = \\\"http:\/\/&lt;website&gt;\/ESP32-Cam_ver2.bin\\\";\\nreturn msg;\",\"outputs\":1,\"noerr\":0,\"x\":440,\"y\":140,\"wires\":[[\"f2d184ec.a85188\"]]},{\"id\":\"f2d184ec.a85188\",\"type\":\"http response\",\"z\":\"612c5528.096aa4\",\"name\":\"\",\"statusCode\":\"200\",\"headers\":{},\"x\":680,\"y\":140,\"wires\":[]}]<\/pre>\n<p>Here is the ESP module code used.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\/\/Your Domain name with URL path or IP address with path\r\nconst char* firmware_server = \"http:\/\/node-red-server:1880\/get-firmware-path\";\r\nString FirmwarePath;\r\n\r\n\/\/ MQTT Functions\r\n  if (String(topic) == \"ESP32-Cam\/HTTP-Get\") {\r\n    if(messageTemp == \"CheckFirmware\"){\r\n      FirmwarePath = httpGETRequest(firmware_server);\r\n    }\r\n  }\r\n  if (String(topic) == \"ESP32-Cam\/HTTP-Get\") {\r\n    if(messageTemp == \"UpdateFirmware\"){\r\n      FirmwarePath = httpGETRequest(firmware_server);\r\n      t_httpUpdate_return ret = httpUpdate.update(espClient, FirmwarePath);\r\n    }\r\n  }\r\n  \r\n\/\/ HTTP Get Functions\r\n\r\nUpdateStats Function\r\n    MQTTclient.publish(\"ESP32-Cam\/FirmwarePath\",FirmwarePath.c_str());<\/pre>\n<p>Here are the nodes that initiate the update.<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/08\/MQTT_OTA_Nodes.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-4413\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/08\/MQTT_OTA_Nodes.png\" alt=\"\" width=\"583\" height=\"171\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/08\/MQTT_OTA_Nodes.png 583w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2023\/08\/MQTT_OTA_Nodes-300x88.png 300w\" sizes=\"auto, (max-width: 583px) 100vw, 583px\" \/><\/a><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">[{\"id\":\"e14f9eab.e9fde\",\"type\":\"ui_button\",\"z\":\"98736f63603298d2\",\"name\":\"Firmware Check\",\"group\":\"34f13621dd653575\",\"order\":7,\"width\":\"3\",\"height\":\"1\",\"passthru\":false,\"label\":\"Firmware Check\",\"tooltip\":\"\",\"color\":\"#585858\",\"bgcolor\":\"#242424\",\"className\":\"\",\"icon\":\"\",\"payload\":\"CheckFirmware\",\"payloadType\":\"str\",\"topic\":\"ESP32-Cam\/HTTP-Get\",\"topicType\":\"str\",\"x\":220,\"y\":1140,\"wires\":[[\"2c7f498e.9a51a6\"]]},{\"id\":\"5cdad6ab.c066a\",\"type\":\"ui_button\",\"z\":\"98736f63603298d2\",\"name\":\"Reboot\",\"group\":\"34f13621dd653575\",\"order\":10,\"width\":\"3\",\"height\":\"1\",\"passthru\":false,\"label\":\"Reboot\",\"tooltip\":\"\",\"color\":\"#585858\",\"bgcolor\":\"#242424\",\"className\":\"\",\"icon\":\"\",\"payload\":\"Reboot\",\"payloadType\":\"str\",\"topic\":\"ESP32-Cam\/Reboot\",\"topicType\":\"str\",\"x\":240,\"y\":1220,\"wires\":[[\"16349782.1c7b78\"]]},{\"id\":\"16349782.1c7b78\",\"type\":\"mqtt out\",\"z\":\"98736f63603298d2\",\"name\":\"ESP32-Cam Reboot\",\"topic\":\"ESP32-Cam\/Reboot\",\"qos\":\"0\",\"retain\":\"\",\"broker\":\"e2a9f9e3.196b88\",\"x\":550,\"y\":1220,\"wires\":[]},{\"id\":\"2c7f498e.9a51a6\",\"type\":\"mqtt out\",\"z\":\"98736f63603298d2\",\"name\":\"ESP32-Cam\/HTTP-Get\",\"topic\":\"ESP32-Cam\/HTTP-Get\",\"qos\":\"0\",\"retain\":\"\",\"respTopic\":\"\",\"contentType\":\"\",\"userProps\":\"\",\"correl\":\"\",\"expiry\":\"\",\"broker\":\"e2a9f9e3.196b88\",\"x\":560,\"y\":1140,\"wires\":[]},{\"id\":\"b386b4db031f8dc8\",\"type\":\"ui_button\",\"z\":\"98736f63603298d2\",\"name\":\"Firmware Update\",\"group\":\"34f13621dd653575\",\"order\":7,\"width\":\"3\",\"height\":\"1\",\"passthru\":false,\"label\":\"Firmware Update\",\"tooltip\":\"\",\"color\":\"#585858\",\"bgcolor\":\"#242424\",\"className\":\"\",\"icon\":\"\",\"payload\":\"UpdateFirmware\",\"payloadType\":\"str\",\"topic\":\"ESP32-Cam\/HTTP-Get\",\"topicType\":\"str\",\"x\":230,\"y\":1180,\"wires\":[[\"e5bd7537571b975b\"]]},{\"id\":\"e5bd7537571b975b\",\"type\":\"mqtt out\",\"z\":\"98736f63603298d2\",\"name\":\"ESP32-Cam\/HTTP-Get\",\"topic\":\"ESP32-Cam\/HTTP-Get\",\"qos\":\"0\",\"retain\":\"\",\"respTopic\":\"\",\"contentType\":\"\",\"userProps\":\"\",\"correl\":\"\",\"expiry\":\"\",\"broker\":\"e2a9f9e3.196b88\",\"x\":560,\"y\":1180,\"wires\":[]},{\"id\":\"34f13621dd653575\",\"type\":\"ui_group\",\"name\":\"MQTT HTTP Get Updater\",\"tab\":\"e64da5d729314ca1\",\"order\":1,\"disp\":true,\"width\":\"7\",\"collapse\":false,\"className\":\"\"},{\"id\":\"e2a9f9e3.196b88\",\"type\":\"mqtt-broker\",\"name\":\"BrokerName\",\"broker\":\"BrokerServer\",\"port\":\"1883\",\"clientid\":\"\",\"usetls\":false,\"keepalive\":\"60\",\"cleansession\":true,\"birthTopic\":\"\",\"birthQos\":\"0\",\"birthPayload\":\"\",\"closeTopic\":\"\",\"closePayload\":\"\",\"willTopic\":\"\",\"willQos\":\"0\",\"willPayload\":\"\"},{\"id\":\"e64da5d729314ca1\",\"type\":\"ui_tab\",\"name\":\"Dev 1\",\"icon\":\"dashboard\",\"order\":9,\"disabled\":false,\"hidden\":false}]<\/pre>\n<p>Now when the ESP module runs, it&#8217;s firmware can be managed through Node-Red.\u00a0 All of these methods of OTA make managing updates on the ESP module easier, some more secure than others.\u00a0 In all, with proper firmware methodology, the task of updating devices is streamlined.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post addresses the problem of servicing ESP based systems that have been installed.\u00a0 I have built systems where the ESP module is fixed, enclosed, and installed in a location that is difficult to get to once in place.\u00a0 As time progresses, so does the realization that some features will need to be updated on those installed ESP modules.\u00a0 Over The Air updates are the ideal way to update the modules without the hassle or downtime.\u00a0 Here is a detailed&#8230;<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/www.cloudacm.com\/?p=4397\"> Read More<span class=\"screen-reader-text\">  Read More<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4397","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4397","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4397"}],"version-history":[{"count":21,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4397\/revisions"}],"predecessor-version":[{"id":4423,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4397\/revisions\/4423"}],"wp:attachment":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4397"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4397"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4397"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}