Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always use flashed options after a new flash #2149

Merged
merged 7 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/html/elrs.css
Original file line number Diff line number Diff line change
Expand Up @@ -334,4 +334,12 @@ body, input, select, textarea {
margin-right: 5px;
}

.badge {
color: white;
padding: 4px 8px;
font-weight: bold;
text-align: center;
border-radius: 5px;
}

@@include("mui.css")
12 changes: 10 additions & 2 deletions src/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,25 @@ <h1><b>ExpressLRS</b></h1>
<div class="mui-tabs__pane mui--is-active" id="pane-justified-1">
<div class="mui-panel">
<h2>Runtime Options</h2>
<div class="mui-panel" style="background-color: #eff6fc;">
This form <b>overrides</b> the options provided when the firmware was flashed. These changes will persist across reboots, but <b>will be reset</b> when the firmware is reflashed.
<br/>
<br/>
Note: The Binding phrase is <b>not</b> remembered, it is a temporary field used to generate the binding UID.
</div>
<br/>
<div class="mui-textfield">
<input type="text" id="phrase" name="phrase" placeholder="Binding Phrase" />
<label for="phrase">Binding Phrase</label>
</div>
<form id='upload_options' method='POST' action="/options">
<input type="hidden" id="flash-discriminator" name="flash-discriminator"/>
<input type="hidden" id="wifi-ssid" name="wifi-ssid"/>
<input type="hidden" id='wifi-password' name='wifi-password'/>
<span class="badge" id="uid-type"></span>&nbsp;<span id="uid-text"></span>
<br/>
<div class="mui-textfield">
<input size='40' id='uid' name='uid' type='text' class='array' readonly/>
<label>UID <b id='uid-type'></b> (Auto updated by changing the bind-phrase above)</label>
</div>
@@if sx127x:
<div class="mui-select">
Expand Down Expand Up @@ -98,7 +106,7 @@ <h2>Runtime Options</h2>
<label><input id='is-airport' name='is-airport' type='checkbox'/>Use as AirPort Serial device</label>
</div>
@@end
<button id='submit-options' class="mui-btn mui-btn--primary">Save & Reboot</button>
<button id='submit-options' class="mui-btn mui-btn--primary" disabled>Save</button>
<div id="reset-options" style="display: none;">
<a class="mui-btn mui-btn--small mui-btn--danger">Reset runtime options to defaults</a>
</div>
Expand Down
78 changes: 61 additions & 17 deletions src/html/scan.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,42 @@ function updateConfig(data) {
if (data.product_name) _('product_name').textContent = data.product_name;
if (data.reg_domain) _('reg_domain').textContent = data.reg_domain;
if (data.uid) _('uid').value = data.uid.toString();
if (data.uidtype) _('uid-type').textContent = data.uidtype;

let bg = '';
let fg = '';
let text = data.uidtype;
let desc = '';
if (!data.uidtype || data.uidtype === 'Not set') {
bg = '#D50000'; // default 'red' for 'Not set'
fg = 'white';
text = 'Not set';
desc = 'The default binding UID from the device address will be used';
}
if (data.uidtype === 'Flashed') {
bg = '#1976D2'; // blue/white
fg = 'white';
desc = 'The binding UID was generated from a binding phrase set at flash time';
}
if (data.uidtype === 'Overridden') {
bg = '#689F38'; // green
fg = 'black';
desc = 'The binding UID has been generated from a bind-phrase previously entered into the "binding phrase" field above';
}
if (data.uidtype === 'Traditional') {
bg = '#D50000'; // red
fg = 'white';
desc = 'The binding UID has been set using traditional binding method i.e. button or 3-times power cycle and bound via the Lua script';
}
if (data.uidtype === 'On loan') {
bg = '#FFA000'; // amber
fg = 'black';
desc = 'The binding UID has been set using the model-loan feature';
}
_('uid-type').style.backgroundColor = bg;
_('uid-type').style.color = fg;
_('uid-type').textContent = text;
_('uid-text').textContent = desc;

if (data.mode==='STA') {
_('stamode').style.display = 'block';
_('ssid').textContent = data.ssid;
Expand Down Expand Up @@ -561,22 +596,30 @@ function submitOptions(e) {
}));

xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
cuteAlert({
type: 'question',
title: 'Upload Succeeded',
message: 'Reboot to take effect',
confirmText: 'Reboot',
cancelText: 'Close'
}).then((e) => {
if (e == 'confirm') {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/reboot');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function() {};
xhr.send();
}
});
if (this.readyState == 4) {
if (this.status == 200) {
cuteAlert({
type: 'question',
title: 'Upload Succeeded',
message: 'Reboot to take effect',
confirmText: 'Reboot',
cancelText: 'Close'
}).then((e) => {
if (e == 'confirm') {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/reboot');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function() {};
xhr.send();
}
});
} else {
cuteAlert({
type: 'error',
title: 'Upload Failed',
message: this.responseText
});
}
}
};
}
Expand Down Expand Up @@ -623,6 +666,7 @@ function updateOptions(data) {
if (data['wifi-ssid']) _('homenet').textContent = data['wifi-ssid'];
else _('connect').style.display = 'none';
if (data['customised']) _('reset-options').style.display = 'block';
_('submit-options').disabled = false;
}

@@if isTX:
Expand Down
55 changes: 39 additions & 16 deletions src/lib/OPTIONS/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ __attribute__ ((used)) const firmware_options_t firmwareOptions = {
#elif defined(USE_SBUS_PROTOCOL)
.uart_baud = 100000,
#elif defined(USE_SUMD_PROTOCOL)
.uart_baud = 115200,
.uart_baud = 115200,
#elif defined(RCVR_UART_BAUD)
.uart_baud = RCVR_UART_BAUD,
#else
Expand Down Expand Up @@ -169,6 +169,10 @@ __attribute__ ((used)) const firmware_options_t firmwareOptions = {
char product_name[ELRSOPTS_PRODUCTNAME_SIZE+1];
char device_name[ELRSOPTS_DEVICENAME_SIZE+1];

// Discriminator value used to determine if the device has been reflashed and therefore
// the SPIFSS settings are obsolete and the flashed settings should be used in preference
uint32_t flash_discriminator;

firmware_options_t firmwareOptions;

// hardware_init prototype here as it is called by options_init()
Expand Down Expand Up @@ -208,6 +212,7 @@ void saveOptions(Stream &stream, bool customised)
doc["is-airport"] = firmwareOptions.is_airport;
doc["domain"] = firmwareOptions.domain;
doc["customised"] = customised;
doc["flash-discriminator"] = flash_discriminator;

serializeJson(doc, stream);
}
Expand All @@ -223,7 +228,7 @@ void saveOptions()
* @brief: Checks if the strmFlash currently is pointing to something that looks like
* a string (not all 0xFF). Position in the stream will not be changed.
* @return: true if appears to have a string
*/
*/
bool options_HasStringInFlash(EspFlashStream &strmFlash)
{
uint32_t firstBytes;
Expand All @@ -241,29 +246,46 @@ bool options_HasStringInFlash(EspFlashStream &strmFlash)
*/
static void options_LoadFromFlashOrFile(EspFlashStream &strmFlash)
{
Stream *strmSrc;
DynamicJsonDocument doc(1024);
File file = SPIFFS.open("/options.json", "r");
if (!file || file.isDirectory())
DynamicJsonDocument flashDoc(1024);
DynamicJsonDocument spiffsDoc(1024);
bool hasFlash = false;
bool hasSpiffs = false;

// Try OPTIONS JSON at the end of the firmware, after PRODUCTNAME DEVICENAME
constexpr size_t optionConfigOffset = ELRSOPTS_PRODUCTNAME_SIZE + ELRSOPTS_DEVICENAME_SIZE;
strmFlash.setPosition(optionConfigOffset);
if (options_HasStringInFlash(strmFlash))
{
// Try OPTIONS JSON at the end of the firmware, after PRODUCTNAME DEVICENAME
constexpr size_t optionConfigOffset = ELRSOPTS_PRODUCTNAME_SIZE + ELRSOPTS_DEVICENAME_SIZE;
strmFlash.setPosition(optionConfigOffset);
if (!options_HasStringInFlash(strmFlash))
DeserializationError error = deserializeJson(flashDoc, strmFlash);
if (error)
{
return;
}
strmSrc = &strmFlash;
hasFlash = true;
}
else

// load options.json from the SPIFFS partition
File file = SPIFFS.open("/options.json", "r");
if (file && !file.isDirectory())
{
strmSrc = &file;
DeserializationError error = deserializeJson(spiffsDoc, file);
if (!error)
{
hasSpiffs = true;
}
}

DeserializationError error = deserializeJson(doc, *strmSrc);
if (error)
DynamicJsonDocument &doc = flashDoc;
if (hasFlash && hasSpiffs)
{
if (flashDoc["flash-discriminator"] == spiffsDoc["flash-discriminator"])
{
doc = spiffsDoc;
}
}
else if (hasSpiffs)
{
return;
doc = spiffsDoc;
}

if (doc["uid"].is<JsonArray>())
Expand Down Expand Up @@ -302,6 +324,7 @@ static void options_LoadFromFlashOrFile(EspFlashStream &strmFlash)
firmwareOptions.lock_on_first_connection = doc["lock-on-first-connection"] | true;
#endif
firmwareOptions.domain = doc["domain"] | 0;
flash_discriminator = doc["flash-discriminator"] | 0U;

builtinOptions.clear();
saveOptions(builtinOptions, doc["customised"] | false);
Expand Down
2 changes: 2 additions & 0 deletions src/lib/OPTIONS/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ extern bool options_init();
extern String& getOptions();
extern String& getHardware();
extern void saveOptions();
extern uint32_t flash_discriminator;

#include "EspFlashStream.h"
extern bool options_HasStringInFlash(EspFlashStream &strmFlash);
#else
extern const firmware_options_t firmwareOptions;
extern const char device_name[];
extern const char *product_name;

#endif
16 changes: 14 additions & 2 deletions src/lib/WIFI/devWIFI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,17 @@ static void HandleReset(AsyncWebServerRequest *request)
rebootTime = millis() + 100;
}

static void UpdateSettings(AsyncWebServerRequest *request, JsonVariant &json)
{
if (flash_discriminator != json["flash-discriminator"].as<uint32_t>()) {
request->send(409, "text/plain", "Mismatched device identifier, refresh the page and try again.");
return;
}
File file = SPIFFS.open("/options.json", "w");
serializeJson(json, file);
request->send(200);
}

static void GetConfiguration(AsyncWebServerRequest *request)
{
#if defined(PLATFORM_ESP32)
Expand Down Expand Up @@ -333,7 +344,7 @@ static void GetConfiguration(AsyncWebServerRequest *request)
if (config.GetOnLoan()) json["config"]["uidtype"] = "On loan";
else
#endif
if (firmwareOptions.hasUID) json["config"]["uidtype"] = "Flashed";
if (firmwareOptions.hasUID) json["config"]["uidtype"] = (json["options"]["customised"] | false) ? "Overridden" : "Flashed";
#if defined(TARGET_RX)
else if (config.GetIsBound()) json["config"]["uidtype"] = "Traditional";
else json["config"]["uidtype"] = "Not set";
Expand Down Expand Up @@ -948,11 +959,12 @@ static void startServices()
server.on("/hardware.html", WebUpdateSendContent);
server.on("/hardware.js", WebUpdateSendContent);
server.on("/hardware.json", getFile).onBody(putFile);
server.on("/options.json", getFile).onBody(putFile);
server.on("/options.json", HTTP_GET, getFile);
server.on("/reboot", HandleReboot);
server.on("/reset", HandleReset);

server.addHandler(new AsyncCallbackJsonWebHandler("/config", UpdateConfiguration));
server.addHandler(new AsyncCallbackJsonWebHandler("/options.json", UpdateSettings));
#if defined(TARGET_TX)
server.addHandler(new AsyncCallbackJsonWebHandler("/buttons", WebUpdateButtonColors));
server.addHandler(new AsyncCallbackJsonWebHandler("/import", ImportConfiguration, 32768U));
Expand Down
12 changes: 11 additions & 1 deletion src/python/binary_configurator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/python

import os
from random import randint
import argparse
import json
from json import JSONEncoder
Expand Down Expand Up @@ -218,14 +219,23 @@ def patch_unified(args, options):
if args.uart_inverted is not None:
json_flags['uart-inverted'] = args.uart_inverted

if args.rx_baud is not None:
if args.airport_baud is not None:
json_flags['is-airport'] = True
if options.deviceType is DeviceType.RX:
json_flags['rcvr-uart-baud'] = args.airport_baud
else:
json_flags['airport-uart-baud'] = args.airport_baud
elif args.rx_baud is not None:
json_flags['rcvr-uart-baud'] = args.rx_baud

if args.lock_on_first_connection is not None:
json_flags['lock-on-first-connection'] = args.lock_on_first_connection

if args.domain is not None:
json_flags['domain'] = domain_number(args.domain)

json_flags['flash-discriminator'] = randint(1,2^32-1)

UnifiedConfiguration.doConfiguration(
args.file,
JSONEncoder().encode(json_flags),
Expand Down
3 changes: 3 additions & 0 deletions src/python/build_flags.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Import("env")
from genericpath import exists
import os
from random import randint
import sys
import hashlib
import fnmatch
Expand Down Expand Up @@ -145,6 +146,8 @@ def get_version():
build_flags.append("-DTARGET_NAME=" + re.sub("_VIA_.*", "", target_name))
condense_flags()

json_flags['flash-discriminator'] = randint(1,2^32-1)

if '-DRADIO_SX127X=1' in build_flags:
# disallow setting 2400s for 900
if fnmatch.filter(build_flags, '*-DRegulatory_Domain_ISM_2400') or \
Expand Down
1 change: 1 addition & 0 deletions src/python/serve_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"wifi-ssid": "network-ssid"
},
"config": {
"uidtype": "On loan",
"ssid":"network-ssid",
"mode":"STA",
"modelid":255,
Expand Down
2 changes: 1 addition & 1 deletion src/targets/common.ini
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ lib_deps =

# ------------------------- COMMON ESP82xx DEFINITIONS -----------------
[env_common_esp82xx]
platform = espressif8266@4.0.1
platform = espressif8266@4.2.0
board = esp8285-8285
build_flags =
-D PLATFORM_ESP8266=1
Expand Down