feature-requests icon indicating copy to clipboard operation
feature-requests copied to clipboard

OTA update via HTTP

Open guestisp opened this issue 4 years ago • 14 comments

Is be possible to implement automated (or semi-automated, triggered via api/mqtt command) off-site OTA updates via HTTP ?

Something similiar to this sample sketch: https://github.com/espressif/arduino-esp32/blob/master/libraries/Update/examples/AWS_S3_OTA_Update/AWS_S3_OTA_Update.ino

where a new firmware is hosted on a remote server and fetched by esphome for upgrades.

Even better would be using automated updates, like fetching a remote url with some arguments (ie https://my.remote.server/update?v=1.2.3&h=esphomenodename&key=verysecretkey) and then the remote server returns the bin file if update is available or 404 if nothing to do. This call could be placed in a loop on esphome

guestisp avatar Jul 10 '20 12:07 guestisp

What would be the use case for this?

You need to compile the binary for each device anyway, and the OTA "pushing" can easily be automated with a quick bash script.

OttoWinter avatar Jul 15 '20 10:07 OttoWinter

Use case are remote unattended upgrades without direct access to device. A firmware update is published on remote server and each nodes could update automatically

guestisp avatar Jul 25 '20 11:07 guestisp

@guestisp Yeah but the existing OTA mechanism already does most of that, no?

With one command the update is uploaded to the ESP. Then you can also create a quick bash script that performs an OTA on all devices in your network.

OttoWinter avatar Jul 25 '20 20:07 OttoWinter

Thats the point: in my network Im talking about downloading a firmware upgrade from an external server allowing unattended upgrade without the need of a PC on the same network

A customer calls, i publish and update on my server and automatically the customer esphome start the download

guestisp avatar Jul 25 '20 20:07 guestisp

Maybe I can jump in, I guess the issue is where the upgrade is initiated from, because you might not be able to reach the remote device behind a firewall or not know it's IP, but it can reach an external server and pull the update?

ajcollett avatar Jul 30 '20 12:07 ajcollett

Exactly

guestisp avatar Jul 30 '20 13:07 guestisp

I agree with this, I use this with ESPurna to update my parents' house devices, and it works great.

I use espota-server, which is a simple generic server, you drop the a file in the directory called firmwarename-version.bin, the device checks for new firmware every so often and if a newer one is found, it automatically updates.

This way I don't even need to know the IP addresses of the devices, they'll just all autoupdate whenever I compile a new firmware version.

skorokithakis avatar Nov 25 '20 19:11 skorokithakis

I have a similar use case. Devices run MQTT offsite behind a firewall where they are not (easily) reachable. Every so often I'd like to have device specific (not just generic) fw updates executed..

jperquin avatar Dec 01 '20 21:12 jperquin

@skorokithakis looks like you already cracked this nut..

Could you share an example of your .yaml for reference?

Thanks in advance.

jperquin avatar Dec 02 '20 09:12 jperquin

Sorry, I meant it's Espurna that autoupdates (ESPhome doesn't support this yet AFAICT), so I have no YAML to share.

skorokithakis avatar Dec 02 '20 10:12 skorokithakis

ESPHOME is such a versatile platform that has great potential outside the home (ie. your own network), especially when using MQTT for connecting in from remote sites (behind their own firewall).

An important shortcoming is the fact that once an ESP device with ESPHOME is deployed to a remote site, OTA firmware updates are no longer possible unless you punch a hole in the remote firewall (not possible in most cases) or physically travel to the site.

I am very appreciative of all the hard work done here by volunteers, but if there is a way to commission someone to develop this functionality, I'd be happy to pitch in..

Any thoughts from this wonderful community would be very welcome.

jperquin avatar May 14 '21 09:05 jperquin

I agree, please prioritize this. It's currently impossible to upgrade remote devices because of this, and being able to drop a firmware file on an ESPOTA-server instance and have tens of devices automatically update would be great. The ESP framework has built-in support for this, AFAIK, so it probably wouldn't even be too hard to implement.

skorokithakis avatar May 29 '21 09:05 skorokithakis

There is another request similar to this one, asking for MQTT based unattended upgrade.

Either one will work for my case, commenting so I will be notified if it's built.

wizzor avatar May 11 '22 16:05 wizzor

No news or plans on this feature?

fuzzybear62 avatar Oct 05 '22 12:10 fuzzybear62

A tutorial for implementing this feature in esp32, in case it's helpful: https://github.com/kurimawxx00/webota-esp32

Super useful feature, shouldn't be too hard to implement in esphome.

chatziko avatar Nov 27 '22 18:11 chatziko

I'm also looking for this feature. I'm going to place some esp8266-based sensors on a wifi network that "isolates" devices (an office space in my case). I cannot place the server there, nor can I control the port forwarding or any other network settings. Thus I set up a remote server, and while remote mqtt works ok, I still need to be able to OTA-upgrade the firmware.

smartynov avatar Nov 30 '22 22:11 smartynov

Same. What would be especially cool is if there was a way for the ESPHome Dashboard to push the firmware up somewhere public (configurable S3 bucket perhaps, or serve it in a way that nodes can download it through a user's external Home Assistant URL if they're using ESPHome with HA and have their instance publicly exposed through HA Cloud or otherwise) and then tell the node to update via MQTT - so the update process would remain the same one-click experience it is for nodes that are on the same network.

This might actually be a fun first contribution to ESPHome if I get the time...

javawizard avatar Nov 30 '22 23:11 javawizard

Does someone implement a custom componetn to do this?. I'm trying to implement on my own side by now but having troubles with libaries dependencies:

includes:
    - custom/brownout_disable.h
    - custom/custom_htud1d.h
    - custom/httpupdate.h
  libraries:
    - "Wire" #FOR I2C library
    - "SparkFun HTU21D Humidity and Temperature Sensor Breakout" #To read this fucking sensor 
    - "Wifi" #For http update
    - "HTTPClient"
    - "Update"
    #- "WiFiClientSecure" # for http update?
    #- "ESP32httpUpdate" #http update
    #- "HttpClient" # for http update
    #- "ArduinoJson" #for http update
    #- "PageBuilder" #for http update
    #- "ESP32-PSRamFS" #for http update
    #- "AutoConnect"
#RUN arduino-cli lib install "HTTPClient"
#RUN arduino-cli lib install "WiFiMulti"
    #- "StreamUtils"
//#include <HTTPUpdate.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <Update.h>

class HttpUpdate : public Component {
	private:
		static WiFiClientSecure wifi;
 		static WiFiClient client;;
 	
 	public:
	  	uint32_t brown_reg_temp;
	  	float get_setup_priority() const override { return esphome::setup_priority::LATE; }

	  	

	  	int updateFirmware(String server, String port, String path_to_file){
	  		
			//ESPhttpUpdate.followRedirects(true) 
			//HttpUpdate::client.setFollowRedirects(followRedirects_t::HTTPC_STRICT_FOLLOW_REDIRECTS);
			//httpUpdate.setFollowRedirects(followRedirects_t::HTTPC_STRICT_FOLLOW_REDIRECTS);
			//Api::wifi.setCACert(ca_cert);

			t_httpUpdate_return ret = httpUpdate.update( HttpUpdate::wifi, server, port.as<uint16_t>(), path_to_file, "0");
			//t_httpUpdate_return ret = httpUpdate.update(client, "server", 80, "/file.bin");

			switch (ret) {
				case HTTP_UPDATE_FAILED:
					//Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
					break;

				case HTTP_UPDATE_NO_UPDATES:
					//Serial.println("HTTP_UPDATE_NO_UPDATES");
		      		break;

		    	case HTTP_UPDATE_OK:
		      		//Serial.println("HTTP_UPDATE_OK");
		      	break;
			}
			return ret;
	  	}
  
};

HttpUpdate * htttpupdate = new HttpUpdate();
.piolibdeps/limonero/ESP32-PSRamFS/src/PSRamFS.h:30:10: fatal error: FS.h: No such file or directory

************************************************************
* Looking for FS.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:FS.h"
* Web  > https://registry.platformio.org/search?q=header:FS.h
*
************************************************************

 #include "FS.h"
          ^~~~~~
compilation terminated.
*** [.pioenvs\limonero\libfc0\ESP32-PSRamFS\PSRamFS.cpp.o] Error 1
In file included from src/httpupdate.h:6,
                 from src/main.cpp:323:
.piolibdeps/limonero/ESP32httpUpdate/src/ESP32HTTPUpdate.h:36:10: fatal error: FS.h: No such file or directory

************************************************************
* Looking for FS.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:FS.h"
* Web  > https://registry.platformio.org/search?q=header:FS.h
*
************************************************************

 #include "FS.h"
          ^~~~~~
compilation terminated.
*** [.pioenvs\limonero\src\main.cpp.o] Error 1
In file included from .piolibdeps/limonero/ESP32httpUpdate/src/ESP32httpUpdate.cpp:26:
.piolibdeps/limonero/ESP32httpUpdate/src/ESP32httpUpdate.h:33:10: fatal error: HTTPClient.h: No such file or directory

********************************************************************
* Looking for HTTPClient.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:HTTPClient.h"
* Web  > https://registry.platformio.org/search?q=header:HTTPClient.h
*
********************************************************************

 #include <HTTPClient.h>
          ^~~~~~~~~~~~~~
compilation terminated.
*** [.pioenvs\limonero\lib246\ESP32httpUpdate\ESP32httpUpdate.cpp.o] Error 1
In file included from .piolibdeps/limonero/PageBuilder/src/PageBuilder.cpp:12:
.piolibdeps/limonero/PageBuilder/src/PageBuilder.h:26:10: fatal error: WebServer.h: No such file or directory

*******************************************************************
* Looking for WebServer.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:WebServer.h"
* Web  > https://registry.platformio.org/search?q=header:WebServer.h
*
*******************************************************************

 #include <WebServer.h>
          ^~~~~~~~~~~~~
compilation terminated.
*** [.pioenvs\limonero\lib16a\PageBuilder\PageBuilder.cpp.o] Error 1
============================================= [FAILED] Took 98.70 seconds =============================================```

drunkly avatar Jun 10 '23 10:06 drunkly

@oarcher already created a external component to do this. (I will test it later) https://github.com/oarcher/piotech/tree/main/components/ota_http

Just sharing and watching here for next update on this, it would be amazing to have this feature soon.

leoddias avatar Sep 20 '23 03:09 leoddias

This is really something I would love as well. How do we support an issue this old :) ?

@leoddias, the solution by @oarcher is really awesome, but the problem is it doesn't support ESP-IDF, which means we can't have SSL. Would be really awesome if there was a port.

Alphaemef avatar Nov 09 '23 00:11 Alphaemef

@Alphaemef , I've implemented ESP-IDF, and it works for SSL. Note that for the moment, RP2040 support is missing, but I plan to work on it soon.

oarcher avatar Nov 09 '23 11:11 oarcher

@oarcher is the url: http://example.com/firmware.bin templatable? Eg. something like url: !lambda return id(text_sensor).state; so we can dynamically adjust the URL if we want?

nagyrobi avatar Nov 09 '23 12:11 nagyrobi

@nagyrobi , yes, the url is templatable.

oarcher avatar Nov 09 '23 12:11 oarcher

Looking for some docs...

nagyrobi avatar Nov 09 '23 12:11 nagyrobi

I've updated the doc on https://github.com/esphome/esphome-docs/pull/3291 .

oarcher avatar Nov 09 '23 19:11 oarcher

@Alphaemef , I've implemented ESP-IDF, and it works for SSL. Note that for the moment, RP2040 support is missing, but I plan to work on it soon.

dude this is tremendous thank you so much for the effort!!! Its a game changer for me. Thanks!

Alphaemef avatar Nov 10 '23 22:11 Alphaemef

@oarcher this is really cool, thanks for implementing this. Is there any chance it will support functionality for version checking / hash comparison in the (near) future? Instead of a button, then you could have updates triggered by a timer and only install/update if there actually is an update. Otherwise, the "remote" aspect is still limited by a remote entity having to push a button (as per your documentation).

thomasvnl avatar Nov 10 '23 22:11 thomasvnl

@thomasvnl , I think that if you put the firmware md5sum on the server (like http://exemple.com/firmware.md5), you can check for change with http_request and trigger ota_http.flash action.

You will have to save the md5 in a global variable with restore_value: true (see https://esphome.io/guides/automations.html#global-variables).

So I think there is no need to implement this in ota_http, but on the other side, I think I will put an option in ota_http for url_md5: http://exemple.com/firmware.md5, to be able to check that the md5 of the received data match the remote one. So perhaps I will also add var_md5: firmware_md5, where firwmare_md5 is the name of the global variable to store the md5 of the current firmware. This will help for those who want to check against remote md5.

oarcher avatar Nov 10 '23 23:11 oarcher

If you're going to do that, you may want to support the existing protocols (Arduino/NoFUSS) directly, so users can just run espota-server and have it work out of the box:

https://gitlab.com/stavros/espota-server

HTTP-based update has been implemented by various projects, it might be better to use one of those protocols rather than coming up with a new one.

skorokithakis avatar Nov 10 '23 23:11 skorokithakis

@skorokithakis , yes, if there is an existing protocol, ota_http should support it. But is it restricted to esp8266 ? I can see on https://registry.platformio.org/libraries/xose/NoFUSS that the device should send http headers whith names like X-ESP8266-*

header description example
X-ESP8266-MAC Device MAC address 5C:CF:7F:8B:6B:26
X-ESP8266-DEVICE Device type SENSOR
X-ESP8266-VERSION Application version 0.1.0
X-ESP8266-BUILD Application build 611cdf3

oarcher avatar Nov 10 '23 23:11 oarcher