OTA support for ESP32 (#2812)
* Implemented otaupgrade module. * Added partition table example for otaupgrade. * Copy-paste omission. Whoops. * Updated otaupgrade docs after review.
This commit is contained in:
parent
e11087bfdf
commit
ca89bff073
|
@ -159,6 +159,14 @@ config LUA_MODULE_OW
|
||||||
help
|
help
|
||||||
Includes the 1-Wire (ow) module (recommended).
|
Includes the 1-Wire (ow) module (recommended).
|
||||||
|
|
||||||
|
config LUA_MODULE_OTAUPGRADE
|
||||||
|
bool "Over-The-Air upgrade module"
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Includes the over-the-air firmware upgrade module. Use of this requires
|
||||||
|
a partition table with at least two OTA partitions, plus the OTA data
|
||||||
|
partition. See the IDF documentation for details.
|
||||||
|
|
||||||
config LUA_MODULE_QRCODEGEN
|
config LUA_MODULE_QRCODEGEN
|
||||||
bool "QR Code Generator module"
|
bool "QR Code Generator module"
|
||||||
default "n"
|
default "n"
|
||||||
|
@ -240,7 +248,7 @@ config LUA_MODULE_WS2812
|
||||||
Includes the ws2812 module.
|
Includes the ws2812 module.
|
||||||
|
|
||||||
config LUA_MODULE_TIME
|
config LUA_MODULE_TIME
|
||||||
bool "time module"
|
bool "Time module"
|
||||||
default "n"
|
default "n"
|
||||||
help
|
help
|
||||||
Includes the time module.
|
Includes the time module.
|
||||||
|
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 Dius Computing Pty Ltd. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* - Neither the name of the copyright holders nor the names of
|
||||||
|
* its contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @author Johny Mattsson <jmattsson@dius.com.au>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "module.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_ota_ops.h"
|
||||||
|
#include "esp_partition.h"
|
||||||
|
|
||||||
|
static esp_ota_handle_t ota_handle;
|
||||||
|
static const esp_partition_t *next;
|
||||||
|
|
||||||
|
// ----------otaupgrade Lua API ------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// Lua: otaupgrade.commence() -- wipes an inactive slot and enables .write()
|
||||||
|
static int lotaupgrade_commence (lua_State* L)
|
||||||
|
{
|
||||||
|
next = esp_ota_get_next_update_partition (NULL);
|
||||||
|
if (!next)
|
||||||
|
return luaL_error (L, "no OTA partition available");
|
||||||
|
|
||||||
|
esp_err_t err = esp_ota_begin (next, OTA_SIZE_UNKNOWN, &ota_handle);
|
||||||
|
const char *msg = NULL;
|
||||||
|
switch (err) {
|
||||||
|
case ESP_OK: break;
|
||||||
|
case ESP_ERR_NO_MEM: msg = "out of memory"; break;
|
||||||
|
case ESP_ERR_OTA_PARTITION_CONFLICT: // I don't think we can get this?
|
||||||
|
msg = "can't overrite running firmware"; break;
|
||||||
|
case ESP_ERR_OTA_SELECT_INFO_INVALID:
|
||||||
|
msg = "ota data partition invalid"; break;
|
||||||
|
case ESP_ERR_OTA_ROLLBACK_INVALID_STATE:
|
||||||
|
msg = "can't upgrade from unconfirmed firmware"; break;
|
||||||
|
default: msg = "ota error"; break;
|
||||||
|
}
|
||||||
|
if (msg)
|
||||||
|
return luaL_error (L, msg);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: otaupgrade.write(data) -- writes the data block to flash
|
||||||
|
static int lotaupgrade_write (lua_State *L)
|
||||||
|
{
|
||||||
|
const char *bytes = luaL_checkstring (L, 1);
|
||||||
|
size_t len = lua_objlen (L, 1);
|
||||||
|
|
||||||
|
esp_err_t err = esp_ota_write (ota_handle, bytes, len);
|
||||||
|
const char *msg = NULL;
|
||||||
|
switch (err) {
|
||||||
|
case ESP_OK: break;
|
||||||
|
case ESP_ERR_INVALID_ARG:
|
||||||
|
msg = "write not possible, use otaupgrade.commence() first"; break;
|
||||||
|
case ESP_ERR_OTA_VALIDATE_FAILED: msg = "not a valid ota image"; break;
|
||||||
|
default: msg = "ota error"; break;
|
||||||
|
}
|
||||||
|
if (msg)
|
||||||
|
return luaL_error (L, msg);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Lua: otaupgrade.complete(optional_reboot)
|
||||||
|
static int lotaupgrade_complete (lua_State *L)
|
||||||
|
{
|
||||||
|
if (!next)
|
||||||
|
return luaL_error (L, "no upgrade in progress");
|
||||||
|
|
||||||
|
esp_err_t err = esp_ota_end(ota_handle);
|
||||||
|
const char *msg = NULL;
|
||||||
|
switch (err) {
|
||||||
|
case ESP_OK: break;
|
||||||
|
case ESP_ERR_INVALID_ARG: msg = "empty firmware image"; break;
|
||||||
|
case ESP_ERR_OTA_VALIDATE_FAILED: msg = "validation failed"; break;
|
||||||
|
default: msg = "ota error"; break;
|
||||||
|
}
|
||||||
|
if (msg)
|
||||||
|
return luaL_error (L, msg);
|
||||||
|
|
||||||
|
err = esp_ota_set_boot_partition (next);
|
||||||
|
next = NULL;
|
||||||
|
|
||||||
|
if (luaL_optint (L, 1, 0))
|
||||||
|
esp_restart ();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: otaupgrade.accept()
|
||||||
|
static int lotaupgrade_accept (lua_State *L)
|
||||||
|
{
|
||||||
|
esp_err_t err = esp_ota_mark_app_valid_cancel_rollback();
|
||||||
|
if (err != ESP_OK) // only ESP_OK defined as expected return value
|
||||||
|
return luaL_error(L, "firmware accept failed");
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: otaupgrade.rollback()
|
||||||
|
static int lotaupgrade_rollback (lua_State *L)
|
||||||
|
{
|
||||||
|
esp_err_t err = esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||||
|
const char *msg = NULL;
|
||||||
|
switch (err) {
|
||||||
|
case ESP_OK: break;
|
||||||
|
case ESP_ERR_OTA_ROLLBACK_FAILED:
|
||||||
|
msg = "no other firmware to roll back to"; break;
|
||||||
|
default:
|
||||||
|
msg = "ota error"; break;
|
||||||
|
}
|
||||||
|
if (msg)
|
||||||
|
return luaL_error(L, msg);
|
||||||
|
else
|
||||||
|
return 0; // actually, we never get here as on success the chip reboots
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Lua: t = otaupgrade.info ()
|
||||||
|
* -- running_partition, nextboot_partition, {
|
||||||
|
* .X = { name, version, secure_version, date, time, idf_version, state },
|
||||||
|
* .Y = { name, version, secure_version, date, time, idf_version, state }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
static int lotaupgrade_info (lua_State *L)
|
||||||
|
{
|
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition ();
|
||||||
|
if (running)
|
||||||
|
lua_pushstring (L, running->label);
|
||||||
|
else
|
||||||
|
lua_pushnil (L);
|
||||||
|
|
||||||
|
const esp_partition_t *boot = esp_ota_get_boot_partition ();
|
||||||
|
if (boot)
|
||||||
|
lua_pushstring (L, boot->label);
|
||||||
|
else
|
||||||
|
lua_pushnil (L);
|
||||||
|
|
||||||
|
lua_createtable (L, 0, 2);
|
||||||
|
esp_partition_iterator_t iter = esp_partition_find (
|
||||||
|
ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||||
|
while (iter) {
|
||||||
|
const esp_partition_t *part = esp_partition_get (iter);
|
||||||
|
if (part->subtype >= ESP_PARTITION_SUBTYPE_APP_OTA_MIN &&
|
||||||
|
part->subtype <= ESP_PARTITION_SUBTYPE_APP_OTA_MAX)
|
||||||
|
{
|
||||||
|
lua_pushstring (L, part->label);
|
||||||
|
lua_createtable (L, 0, 6);
|
||||||
|
|
||||||
|
esp_ota_img_states_t state;
|
||||||
|
esp_err_t err = esp_ota_get_state_partition (part, &state);
|
||||||
|
|
||||||
|
if (err == ESP_OK)
|
||||||
|
{
|
||||||
|
lua_pushliteral (L, "state");
|
||||||
|
const char *msg = "";
|
||||||
|
switch (state) {
|
||||||
|
case ESP_OTA_IMG_NEW: msg = "new"; break;
|
||||||
|
case ESP_OTA_IMG_PENDING_VERIFY: msg = "testing"; break;
|
||||||
|
case ESP_OTA_IMG_VALID: msg = "valid"; break;
|
||||||
|
case ESP_OTA_IMG_INVALID: msg = "invalid"; break;
|
||||||
|
case ESP_OTA_IMG_ABORTED: msg = "aborted"; break;
|
||||||
|
case ESP_OTA_IMG_UNDEFINED: // fall-through
|
||||||
|
default: msg = "undefined"; break;
|
||||||
|
}
|
||||||
|
lua_pushstring (L, msg);
|
||||||
|
lua_settable (L, -3);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
goto next; // just add an empty table for this slot
|
||||||
|
|
||||||
|
esp_app_desc_t desc;
|
||||||
|
err = esp_ota_get_partition_description(part, &desc);
|
||||||
|
|
||||||
|
if (err == ESP_OK)
|
||||||
|
{
|
||||||
|
lua_pushliteral (L, "name");
|
||||||
|
lua_pushstring (L, desc.project_name);
|
||||||
|
lua_settable (L, -3);
|
||||||
|
|
||||||
|
lua_pushliteral (L, "version");
|
||||||
|
lua_pushstring (L, desc.version);
|
||||||
|
lua_settable (L, -3);
|
||||||
|
|
||||||
|
lua_pushliteral (L, "secure_version");
|
||||||
|
lua_pushinteger (L, desc.secure_version);
|
||||||
|
lua_settable (L, -3);
|
||||||
|
|
||||||
|
lua_pushliteral (L, "date");
|
||||||
|
lua_pushstring (L, desc.date);
|
||||||
|
lua_settable (L, -3);
|
||||||
|
|
||||||
|
lua_pushliteral (L, "time");
|
||||||
|
lua_pushstring (L, desc.time);
|
||||||
|
lua_settable (L, -3);
|
||||||
|
|
||||||
|
lua_pushliteral (L, "idf_version");
|
||||||
|
lua_pushstring (L, desc.idf_ver);
|
||||||
|
lua_settable (L, -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
|
lua_settable (L, -3); // info table into return arg #3 table
|
||||||
|
}
|
||||||
|
iter = esp_partition_next (iter);
|
||||||
|
}
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const LUA_REG_TYPE otaupgrade_map[] =
|
||||||
|
{
|
||||||
|
{ LSTRKEY( "commence" ), LFUNCVAL( lotaupgrade_commence ) },
|
||||||
|
{ LSTRKEY( "write" ), LFUNCVAL( lotaupgrade_write) },
|
||||||
|
{ LSTRKEY( "complete" ), LFUNCVAL( lotaupgrade_complete) },
|
||||||
|
{ LSTRKEY( "accept" ), LFUNCVAL( lotaupgrade_accept) },
|
||||||
|
{ LSTRKEY( "rollback" ), LFUNCVAL( lotaupgrade_rollback) },
|
||||||
|
{ LSTRKEY( "info" ), LFUNCVAL( lotaupgrade_info) },
|
||||||
|
{ LNILKEY, LNILVAL }
|
||||||
|
};
|
||||||
|
|
||||||
|
NODEMCU_MODULE(OTAUPGRADE, "otaupgrade", otaupgrade_map, NULL);
|
|
@ -0,0 +1,232 @@
|
||||||
|
# OTA Upgrade module
|
||||||
|
| Since | Origin / Contributor | Maintainer | Source |
|
||||||
|
| :----- | :-------------------- | :---------- | :------ |
|
||||||
|
| 2019-06-24 | [DiUS](https://github.com/DiUS), [Johny Mattsson](https://github.com/jmattsson) | [Johny Mattsson](https://github.com/jmattsson) | [otaupgrade.c](../../components/modules/otaupgrade.c)|
|
||||||
|
|
||||||
|
The OTA Upgrade module provides access to the IDF Over-The-Air Upgrade
|
||||||
|
support, enabling new application firmware to be applied and booted into.
|
||||||
|
|
||||||
|
This module is not concerned with where the new application comes from.
|
||||||
|
The choice of download source and method (e.g. https, tftp) is left to
|
||||||
|
the user, as is the trigger to start an upgrade. A common approach is
|
||||||
|
to have the device periodically check in with a central server and
|
||||||
|
compare a provided version number with the currently running version,
|
||||||
|
and if necessary kick off an upgrade.
|
||||||
|
|
||||||
|
In order to use the `otaupgrade` module, there must exist at least two
|
||||||
|
OTA partitions (type `app`, subtype `ota_0` / `ota_1`), as well as the
|
||||||
|
"otadata" partition (type `data`, subtype `ota`). The IDF implements
|
||||||
|
the typical "flip-flop" approach to upgrades, in that one of the
|
||||||
|
partitions hosts the running application, and the upgrade is downloaded
|
||||||
|
into the inactive partition and only when fully downloaded and verified
|
||||||
|
is it marked as bootable. This makes the system resilient to incomplete
|
||||||
|
upgrades, be it due to power-loss, interrupted downloads, or other such
|
||||||
|
things.
|
||||||
|
|
||||||
|
An example partition table for OTA might look like:
|
||||||
|
```
|
||||||
|
# Name, Type, SubType, Offset, Size
|
||||||
|
nvs, data, nvs, 0x9000, 0x5000
|
||||||
|
otadata, data, ota, 0xe000, 0x2000
|
||||||
|
ota_0, app, ota_0, 0x10000,0x130000
|
||||||
|
ota_1, app, ota_1, 0x140000,0x130000
|
||||||
|
```
|
||||||
|
|
||||||
|
Depending on whether the installed boot loader has been built with or
|
||||||
|
without rollback support, the upgrade process itself has four or three
|
||||||
|
steps. Without rollback support, the steps are:
|
||||||
|
|
||||||
|
- `otaupgrade.commence()`
|
||||||
|
- feed the new application image into `otaupgrade.write(data)` in chunks
|
||||||
|
- `otaupgrade.complete(1)` to finalise and reboot into the new application
|
||||||
|
|
||||||
|
If the boot loader is built with rollback support, an extra step is needed
|
||||||
|
after the new application has booted (and been tested to be "good", by
|
||||||
|
whatever metric(s) the user chooses):
|
||||||
|
|
||||||
|
- `otaupgrade.accept()` to mark this image as valid, and allow it to be
|
||||||
|
booted into again.
|
||||||
|
|
||||||
|
If a new firmware is not `accept()`ed before the device reboots, the
|
||||||
|
boot loader will switch back to the previous firmware version (provided
|
||||||
|
said boot loader is built with rollback support). A common test before
|
||||||
|
marking a new firmware as valid is to ensure the upgrade server can be
|
||||||
|
reached, on the basis that as long as the firmware can be remotely
|
||||||
|
upgraded, it's "good enough" to accept.
|
||||||
|
|
||||||
|
# otaupgrade.info()
|
||||||
|
|
||||||
|
The boot info and application state and version info can be queried with
|
||||||
|
this function. Typically it will be used to check the version of the
|
||||||
|
running application, to compare against a "desired" version in order
|
||||||
|
to decide whether an upgrade is required.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
None.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
A list of three values:
|
||||||
|
- the name of the partition of the running application
|
||||||
|
- the name of the partition currently marked for boot next (typically the
|
||||||
|
same as the running application, but after `otaupgrade.complete()` it
|
||||||
|
may point to a new application partition.
|
||||||
|
- a table whose keys are the names of OTA partitions and corresponding
|
||||||
|
values are tables containing:
|
||||||
|
- `state` one of `new`, `testing`, `valid`, `invalid`, `aborted` or
|
||||||
|
possibly `undefined`. The values `invalid` and `aborted` largely
|
||||||
|
mean the same things. See the IDF documentation for specifics.
|
||||||
|
A partition in `testing` state needs to call `otaupgrade.accept()`
|
||||||
|
if it wishes to become `valid`.
|
||||||
|
- `name` the application name, typically "NodeMCU"
|
||||||
|
- `date` the build date
|
||||||
|
- `time` the build time
|
||||||
|
- `version` the build version, as set by the *PROJECT_VER* variable
|
||||||
|
during build
|
||||||
|
- `secure_version` the secure version number, if secure boot is enabled
|
||||||
|
- `idf_version` the IDF version
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
boot_part, next_part, info = otaupgrade.info()
|
||||||
|
print("Booted: "..boot_part)
|
||||||
|
print(" Next: "..next_part)
|
||||||
|
for p,t in pairs(info) do
|
||||||
|
print("@ "..p..":")
|
||||||
|
for k,v in pairs(t) do
|
||||||
|
print(" "..k..": "..v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print("Running version: "..info[boot_part].version)
|
||||||
|
```
|
||||||
|
|
||||||
|
# otaupgrade.commence()
|
||||||
|
|
||||||
|
Wipes the spare application partition and prepares to receive the new
|
||||||
|
application firmware.
|
||||||
|
|
||||||
|
If rollback support is enabled, note that the running application must
|
||||||
|
first be marked valid/accepted before it is possible to commence a
|
||||||
|
new OTA upgrade.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`otaupgrade.commence()`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
None.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
A Lua error may be raised if the OTA upgrade cannot be commenced for some
|
||||||
|
reason (such as due to incorrect partition setup).
|
||||||
|
|
||||||
|
|
||||||
|
# otaupgrade.write(data)
|
||||||
|
|
||||||
|
Write a chunk of application firmware data to the correct partition and
|
||||||
|
location. Data must be streamed sequentially, the IDF does not support
|
||||||
|
out-of-order data as would be the case from e.g. bittorrent.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`otaupgrade.write(data)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- `data` a string of binary data
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
A Lua error may be raised if the data can not be written, e.g. due to the
|
||||||
|
data not being a valid OTA image (the IDF performs some checks in this
|
||||||
|
regard).
|
||||||
|
|
||||||
|
|
||||||
|
# otaupgrade.complete(reboot)
|
||||||
|
|
||||||
|
Finalises the upgrade, and optionally reboots into the new application
|
||||||
|
firmware right away.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`otaupgrade.complete(reboot)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- `reboot` 1 to reboot into the new firmware immediately, nil to keep running
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
A Lua error may be raised if the image does not pass validation, or no data
|
||||||
|
has been written to the image at all.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
-- Quick, dirty and totally insecure "push upgrade" for development use.
|
||||||
|
-- Use netcat to push a new firmware to a device:
|
||||||
|
-- nc -q 1 your-device-ip 9999 < build/NodeMCU.bin
|
||||||
|
--
|
||||||
|
osv = net.createServer()
|
||||||
|
osv:listen(9999, function(conn)
|
||||||
|
print('Commencing OTA upgrade')
|
||||||
|
local status, err = pcall(otaupgrade.commence)
|
||||||
|
if err then
|
||||||
|
print(err)
|
||||||
|
conn:send(err)
|
||||||
|
conn:close()
|
||||||
|
end
|
||||||
|
conn:on('receive', function(sck, data)
|
||||||
|
status, err = pcall(function() otaupgrade.write(data) end)
|
||||||
|
if err then
|
||||||
|
print(err)
|
||||||
|
conn:send(err)
|
||||||
|
conn:close()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
conn:on('disconnection', function()
|
||||||
|
print('EOF, completing OTA')
|
||||||
|
status, err = pcall(function() otaupgrade.complete(1) end)
|
||||||
|
if err then
|
||||||
|
print(err)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# otaupgrade.accept()
|
||||||
|
|
||||||
|
When the installed boot loader is built with rollback support, a new
|
||||||
|
application image is by default only booted once. During this "test run"
|
||||||
|
it can perform whatever checks is appropriate (like testing whether it
|
||||||
|
can still reach the update server), and if satisfied can mark itself
|
||||||
|
as valid. Without being marked valid, upon the next reboot the system
|
||||||
|
would "roll back" to the previous version instead.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`otaupgrade.accept()`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
None.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
|
||||||
|
# otaupgrade.rollback()
|
||||||
|
|
||||||
|
A new firmware may decide that it is not performing as expected, and
|
||||||
|
request an explicit rollback to the previous version. If the call to this
|
||||||
|
function succeeds, the system will reboot without returning from the
|
||||||
|
call.
|
||||||
|
|
||||||
|
Note that it is also possible to roll back to a previous firmware
|
||||||
|
version even after the new version has called `otaupgrade.accept()`.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`otaupgrade.rollback()`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
None.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
Never. Either the system is rebooted, or a Lua error is raised (e.g. due
|
||||||
|
to there being no other firmware to roll back to).
|
Loading…
Reference in New Issue