Browse Source

0.8.4

* introduced tabs in WebGUI (inverter settings)
* added inverter-wise power level and frequency
pull/1225/head
lumapu 11 months ago
parent
commit
efdac9634f
  1. 2
      src/CHANGES.md
  2. 2
      src/app.cpp
  3. 3
      src/config/config.h
  4. 43
      src/config/settings.h
  5. 2
      src/hm/Communication.h
  6. 2
      src/hm/hmInverter.h
  7. 7
      src/hm/hmRadio.h
  8. 4
      src/hms/cmt2300a.h
  9. 16
      src/hms/hmsRadio.h
  10. 12
      src/web/RestApi.h
  11. 42
      src/web/html/api.js
  12. 129
      src/web/html/setup.html
  13. 51
      src/web/html/style.css
  14. 2
      src/web/html/system.html
  15. 2
      src/web/web.h

2
src/CHANGES.md

@ -3,6 +3,8 @@
## 0.8.4 - 2023-11-10
* changed MqTT alarm topic, removed retained flag #1212
* reduce last_success MQTT messages (#1124)
* introduced tabs in WebGUI (inverter settings)
* added inverter-wise power level and frequency
## 0.8.3 - 2023-11-09
* fix yield day reset during day #848

2
src/app.cpp

@ -34,7 +34,7 @@ void app::setup() {
DBGPRINTLN(F("false"));
if(mConfig->nrf.enabled) {
mNrfRadio.setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs, mConfig->nrf.pinSclk, mConfig->nrf.pinMosi, mConfig->nrf.pinMiso);
mNrfRadio.setup(mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs, mConfig->nrf.pinSclk, mConfig->nrf.pinMosi, mConfig->nrf.pinMiso);
mNrfRadio.enableDebug();
}
#if defined(ESP32)

3
src/config/config.h

@ -132,9 +132,6 @@
#define LED_HIGH_ACTIVE false
#endif
// default NRF24 power, possible values (0 - 3)
#define DEF_AMPLIFIERPOWER 1
// number of packets hold in buffer
#define PACKET_BUFFER_SIZE 30

43
src/config/settings.h

@ -30,6 +30,8 @@
* https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout
* */
#define CONFIG_VERSION 1
#define PROT_MASK_INDEX 0x0001
#define PROT_MASK_LIVE 0x0002
@ -88,7 +90,6 @@ typedef struct {
uint8_t pinMiso;
uint8_t pinMosi;
uint8_t pinSclk;
uint8_t amplifierPower;
} cfgNrf24_t;
typedef struct {
@ -142,6 +143,8 @@ typedef struct {
uint16_t chMaxPwr[6];
double yieldCor[6]; // YieldTotal correction value
char chName[6][MAX_NAME_LENGTH];
uint8_t frequency;
uint8_t powerLevel;
} cfgIv_t;
typedef struct {
@ -188,6 +191,7 @@ typedef struct {
cfgInst_t inst;
plugins_t plugin;
bool valid;
uint16_t configVersion;
} settings_t;
class settings {
@ -284,6 +288,7 @@ class settings {
if(root.containsKey(F("led"))) jsonLed(root[F("led")]);
if(root.containsKey(F("plugin"))) jsonPlugin(root[F("plugin")]);
if(root.containsKey(F("inst"))) jsonInst(root[F("inst")]);
getConfigVersion(root.as<JsonObject>());
}
else {
Serial.println(F("failed to parse json, using default config"));
@ -299,6 +304,7 @@ class settings {
DynamicJsonDocument json(MAX_ALLOWED_BUF_SIZE);
JsonObject root = json.to<JsonObject>();
json[F("version")] = CONFIG_VERSION;
jsonNetwork(root.createNestedObject(F("wifi")), true);
jsonNrf(root.createNestedObject(F("nrf")), true);
#if defined(ESP32)
@ -391,7 +397,6 @@ class settings {
mCfg.nrf.pinMosi = DEF_NRF_MOSI_PIN;
mCfg.nrf.pinSclk = DEF_NRF_SCLK_PIN;
mCfg.nrf.amplifierPower = DEF_AMPLIFIERPOWER & 0x03;
mCfg.nrf.enabled = true;
#if defined(ESP32)
@ -436,6 +441,11 @@ class settings {
mCfg.inst.rstMaxValsMidNight = false;
mCfg.inst.yieldEffiency = 0.955f;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
mCfg.inst.iv[i].powerLevel = 0xff; // impossible high value
mCfg.inst.iv[i].frequency = 0x12; // 863MHz (minimum allowed frequency)
}
mCfg.led.led0 = DEF_LED0;
mCfg.led.led1 = DEF_LED1;
mCfg.led.led_high_active = LED_HIGH_ACTIVE;
@ -446,13 +456,30 @@ class settings {
mCfg.plugin.display.contrast = 60;
mCfg.plugin.display.pxShift = true;
mCfg.plugin.display.rot = 0;
mCfg.plugin.display.disp_data = DEF_PIN_OFF; // SDA
mCfg.plugin.display.disp_clk = DEF_PIN_OFF; // SCL
mCfg.plugin.display.disp_data = DEF_PIN_OFF; // SDA
mCfg.plugin.display.disp_clk = DEF_PIN_OFF; // SCL
mCfg.plugin.display.disp_cs = DEF_PIN_OFF;
mCfg.plugin.display.disp_reset = DEF_PIN_OFF;
mCfg.plugin.display.disp_busy = DEF_PIN_OFF;
mCfg.plugin.display.disp_dc = DEF_PIN_OFF;
}
}
void loadAddedDefaults() {
if(0 == mCfg.configVersion) {
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
mCfg.inst.iv[i].powerLevel = 0xff; // impossible high value
mCfg.inst.iv[i].frequency = 0x12; // 863MHz (minimum allowed frequency)
}
}
}
void getConfigVersion(JsonObject obj) {
getVal<uint16_t>(obj, F("version"), &mCfg.configVersion);
DPRINT(DBG_INFO, F("Config Version: "));
DBGPRINTLN(String(mCfg.configVersion));
if(CONFIG_VERSION != mCfg.configVersion)
loadAddedDefaults();
}
void jsonNetwork(JsonObject obj, bool set = false) {
if(set) {
@ -506,7 +533,6 @@ class settings {
obj[F("sclk")] = mCfg.nrf.pinSclk;
obj[F("mosi")] = mCfg.nrf.pinMosi;
obj[F("miso")] = mCfg.nrf.pinMiso;
obj[F("pwr")] = mCfg.nrf.amplifierPower;
obj[F("en")] = (bool) mCfg.nrf.enabled;
} else {
getVal<uint16_t>(obj, F("intvl"), &mCfg.nrf.sendInterval);
@ -516,7 +542,6 @@ class settings {
getVal<uint8_t>(obj, F("sclk"), &mCfg.nrf.pinSclk);
getVal<uint8_t>(obj, F("mosi"), &mCfg.nrf.pinMosi);
getVal<uint8_t>(obj, F("miso"), &mCfg.nrf.pinMiso);
getVal<uint8_t>(obj, F("pwr"), &mCfg.nrf.amplifierPower);
#if !defined(ESP32)
mCfg.nrf.enabled = true; // ESP8266, read always as enabled
#else
@ -707,6 +732,8 @@ class settings {
obj[F("en")] = (bool)cfg->enabled;
obj[F("name")] = cfg->name;
obj[F("sn")] = cfg->serial.u64;
obj[F("freq")] = cfg->frequency;
obj[F("pa")] = cfg->powerLevel;
for(uint8_t i = 0; i < 6; i++) {
obj[F("yield")][i] = cfg->yieldCor[i];
obj[F("pwr")][i] = cfg->chMaxPwr[i];
@ -716,6 +743,8 @@ class settings {
getVal<bool>(obj, F("en"), &cfg->enabled);
getChar(obj, F("name"), cfg->name, MAX_NAME_LENGTH);
getVal<uint64_t>(obj, F("sn"), &cfg->serial.u64);
getVal<uint8_t>(obj, F("freq"), &cfg->frequency);
getVal<uint8_t>(obj, F("pa"), &cfg->powerLevel);
uint8_t size = 4;
if(obj.containsKey(F("pwr")))
size = obj[F("pwr")].size();

2
src/hm/Communication.h

@ -92,7 +92,7 @@ class Communication : public CommQueue<> {
q->iv->radioStatistics.rxFailNoAnser++; // got nothing
mHeu.setGotNothing(q->iv);
if((IV_HMS == q->iv->ivGen) || (IV_HMT == q->iv->ivGen)) {
q->iv->radio->switchFrequency(q->iv, HOY_BOOT_FREQ_KHZ, WORK_FREQ_KHZ);
q->iv->radio->switchFrequency(q->iv, HOY_BOOT_FREQ_KHZ, (q->iv->config->frequency*FREQ_STEP_KHZ + HOY_BASE_FREQ_KHZ));
mWaitTimeout = millis() + 1000;
}
} else

2
src/hm/hmInverter.h

@ -129,6 +129,7 @@ class Inverter {
statistics_t radioStatistics; // information about transmitted, failed, ... packets
int8_t txRfQuality[5]; // heuristics tx quality (check 'Heuristics.h')
uint8_t txRfChId; // RF TX channel id
uint8_t curCmtFreq; // current used CMT frequency, used to check if freq. was changed during runtime
static uint32_t *timestamp; // system timestamp
static cfgInst_t *generalConfig; // general inverter configuration from setup
@ -194,6 +195,7 @@ class Inverter {
initAssignment(&recordConfig, SystemConfigPara);
initAssignment(&recordAlarm, AlarmData);
toRadioId();
curCmtFreq = this->config->frequency; // update to frequency read from settings
}
uint8_t getPosByChFld(uint8_t channel, uint8_t fieldId, record_t<> *rec) {

7
src/hm/hmRadio.h

@ -45,7 +45,7 @@ class HmRadio : public Radio {
}
~HmRadio() {}
void setup(uint8_t ampPwr = RF24_PA_LOW, uint8_t irq = IRQ_PIN, uint8_t ce = CE_PIN, uint8_t cs = CS_PIN, uint8_t sclk = SCLK_PIN, uint8_t mosi = MOSI_PIN, uint8_t miso = MISO_PIN) {
void setup(uint8_t irq = IRQ_PIN, uint8_t ce = CE_PIN, uint8_t cs = CS_PIN, uint8_t sclk = SCLK_PIN, uint8_t mosi = MOSI_PIN, uint8_t miso = MISO_PIN) {
DPRINTLN(DBG_VERBOSE, F("hmRadio.h:setup"));
pinMode(irq, INPUT_PULLUP);
@ -81,9 +81,7 @@ class HmRadio : public Radio {
// enable all receiving interrupts
mNrf24.maskIRQ(false, false, false);
DPRINT(DBG_INFO, F("RF24 Amp Pwr: RF24_PA_"));
DPRINTLN(DBG_INFO, String(rf24AmpPowerNames[ampPwr]));
mNrf24.setPALevel(ampPwr & 0x03);
mNrf24.setPALevel(1); // low is default
if(mNrf24.isChipConnected()) {
DPRINTLN(DBG_INFO, F("Radio Config:"));
@ -269,6 +267,7 @@ class HmRadio : public Radio {
}
void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) {
mNrf24.setPALevel(iv->config->powerLevel & 0x03);
updateCrcs(&len, appendCrc16);
// set TX and RX channels

4
src/hms/cmt2300a.h

@ -422,6 +422,10 @@ class Cmt2300a {
return HOY_BASE_FREQ_KHZ + (mCurCh * FREQ_STEP_KHZ);
}
uint8_t getCurrentChannel(void) {
return mCurCh;
}
void setPaLevel(int8_t level) {
if(level < -10)
level = -10;

16
src/hms/hmsRadio.h

@ -66,6 +66,11 @@ class CmtRadio : public Radio {
uint8_t fromCh = mCmt.freq2Chan(fromkHz);
uint8_t toCh = mCmt.freq2Chan(tokHz);
return switchFrequencyCh(iv, fromCh, toCh);
}
private:
bool switchFrequencyCh(Inverter<> *iv, uint8_t fromCh, uint8_t toCh) {
if((0xff == fromCh) || (0xff == toCh))
return false;
@ -75,8 +80,17 @@ class CmtRadio : public Radio {
return true;
}
private:
void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) {
// frequency was changed during runtime
if(iv->curCmtFreq != iv->config->frequency) {
if(switchFrequencyCh(iv, iv->curCmtFreq, iv->config->frequency))
iv->curCmtFreq = iv->config->frequency;
} else {
// inverters have maybe different settings regarding frequency
if(mCmt.getCurrentChannel() != iv->config->frequency)
mCmt.switchChannel(iv->config->frequency);
}
updateCrcs(&len, appendCrc16);
if(mSerialDebug) {

12
src/web/RestApi.h

@ -346,7 +346,14 @@ class RestApi {
obj2[F("name")] = String(iv->config->name);
obj2[F("serial")] = String(iv->config->serial.u64, HEX);
obj2[F("channels")] = iv->channels;
obj2[F("version")] = String(iv->getFwVersion());
obj2[F("freq")] = iv->config->frequency;
if(0xff == iv->config->powerLevel) {
if((IV_HMT == iv->ivGen) || (IV_HMS == iv->ivGen))
obj2[F("pa")] = 30; // 20dBm
else
obj2[F("pa")] = 1; // low
} else
obj2[F("pa")] = iv->config->powerLevel;
for(uint8_t j = 0; j < iv->channels; j ++) {
obj2[F("ch_yield_cor")][j] = (double)iv->config->yieldCor[j];
@ -529,7 +536,6 @@ class RestApi {
void getRadioNrf(JsonObject obj) {
obj[F("en")] = (bool) mConfig->nrf.enabled;
obj[F("isconnected")] = mRadioNrf->isChipConnected();
obj[F("power_level")] = mConfig->nrf.amplifierPower;
obj[F("dataRate")] = mRadioNrf->getDataRate();
//obj[F("isPVariant")] = mRadioNrf->isPVariant();
}
@ -736,6 +742,8 @@ class RestApi {
case 0x61: iv->type = INV_TYPE_4CH; iv->channels = 4; break;
default: break;
}
iv->config->frequency = jsonIn[F("freq")];
iv->config->powerLevel = jsonIn[F("pa")];
mApp->saveSettings(false); // without reboot
} else {
jsonOut[F("error")] = F("unknown cmd");

42
src/web/html/api.js

@ -192,6 +192,34 @@ function badge(success, text, second="error") {
return ml("span", {class: "badge badge-" + ((success) ? "success" : second)}, text);
}
function tabChange(id) {
var els = document.getElementsByClassName("nav-link");
[].forEach.call(els, function(e) {
if(e.id != id)
e.classList.remove('active');
else
e.classList.add('active');
});
els = document.getElementsByClassName("tab-content");
[].forEach.call(els, function(e) {
if(e.id == ("div"+id.substring(3)))
e.classList.remove('hide');
else
e.classList.add('hide');
});
}
function tabs(items) {
var li = [];
var cl = " active";
for(it of items) {
li.push(ml("li", {class: "nav-item"},ml("a", {id: "tab"+it, class: "nav-link" + cl, href: "#", onclick: function(){tabChange(this.id)}}, it)))
cl = "";
}
return ml("ul", {class: "nav nav-tabs mb-4"}, li);
}
function des(val) {
e = document.createElement('p');
e.classList.add("subdes");
@ -223,13 +251,11 @@ function inp(name, val, max=32, cl=["text"], id=null, type=null, pattern=null, t
}
function sel(name, options, selId) {
e = document.createElement('select');
e.name = name;
var o = [];
for(it of options) {
o = opt(it[0], it[1], (it[0] == selId));
e.appendChild(o);
o.push(opt(it[0], it[1], (it[0] == selId)));
}
return e;
return ml("select", {name: name}, o);
}
function selDelAllOpt(sel) {
@ -240,9 +266,7 @@ function selDelAllOpt(sel) {
}
function opt(val, html, sel=false) {
o = document.createElement('option');
o.value = val;
o.innerHTML = html;
var o = ml("option", {value: val}, html);
if(sel)
o.selected = true;
return o;
@ -301,7 +325,7 @@ function svg(data=null, w=24, h=24, cl=null, tooltip=null) {
function modal(title, body) {
if(null == document.getElementById("modal")) {
document.getElementById("wrapper").append(
ml("div", {id: "modal-wrapper", class: "modal", onclick: modalClose}),
ml("div", {id: "modal-wrapper", onclick: modalClose}),
ml("div", {id: "modal", class: "modal"},
ml("div", {class: "modal-content"}, [
ml("div", {class: "modal-header"}, [

129
src/web/html/setup.html

@ -438,6 +438,23 @@
[47, "GPIO47"],
[48, "GPIO48"],
];
var nrfPa = [
[0, "MIN (recommended)"],
[1, "LOW"],
[2, "HIGH"],
[3, "MAX (experimental)"]
];
var esp32cmtPa = [];
var esp32cmtFreq = [];
var freqFmt = new Intl.NumberFormat('en-US', {
minimumIntegerDigits: 3,
minimumFractionDigits: 2
});
for(var i = 0; i < 31; i++) {
esp32cmtPa.push([i, String(i-10) + " dBm"]);
if(i < 29)
esp32cmtFreq.push([i, freqFmt.format(860 + i*0.25) + " MHz"]);
}
/*ENDIF_ESP32*/
var led_high_active = [
[0, "low active"],
@ -647,6 +664,7 @@
ml("th", {}, "Name (optional)"),
ml("th", {}, "Yield Correction [kWh] (optional)")
]));
for(let i = 0; i < 6; i++) {
lines.push(ml("tr", {id: "ch"+i}, [
ml("td", {}, String(i+1)),
@ -655,29 +673,54 @@
ml("td", {}, ml("input", {name: "yld_c"+i, class: "text", type: "number", max: 999999, value: obj.ch_yield_cor[i]}, null))
]));
}
var cbEn = ml("input", {name: "enable", type: "checkbox"}, null);
if(obj.enabled)
cbEn.checked = true;
var ser = ml("input", {name: "ser", class: "text", type: "number", max: 138999999999, value: obj.serial}, null);
var html = ml("div", {}, [
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4"}, "Serial"),
ml("div", {class: "col-8"}, ser)
tabs(["General", "Inputs", "Radio"]),
ml("div", {id: "divGeneral", class: "tab-content"}, [
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4"}, "Enable"),
ml("div", {class: "col-8"}, cbEn)
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4 mt-2"}, "Serial"),
ml("div", {class: "col-8"}, ser)
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4 mt-2"}, "Name"),
ml("div", {class: "col-8"}, ml("input", {name: "name", class: "text", type: "text", value: obj.name}, null))
])
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4"}, "Name"),
ml("div", {class: "col-8"}, ml("input", {name: "name", class: "text", type: "text", value: obj.name}, null))
ml("div", {id: "divInputs", class: "tab-content hide"}, [
ml("div", {class: "row mb-3"},
ml("table", {class: "table"},
ml("tbody", {}, lines)
)
)
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4"}, "Enable"),
ml("div", {class: "col-8"}, cbEn)
ml("div", {id: "divRadio", class: "tab-content hide"}, [
ml("input", {type: "hidden", name: "isnrf"}, null),
ml("div", {id: "setcmt"}, [
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4 mt-2"}, "Frequency"),
ml("div", {class: "col-8"}, sel("freq", esp32cmtFreq, obj.freq))
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4 mt-2"}, "Power Level"),
ml("div", {class: "col-8"}, sel("cmtpa", esp32cmtPa, obj.pa))
]),
]),
ml("div", {id: "setnrf"},
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4 mt-2"}, "Power Level"),
ml("div", {class: "col-8"}, sel("nrfpa", nrfPa, obj.pa))
]),
),
]),
ml("div", {class: "row mb-3"},
ml("table", {class: "table"},
ml("tbody", {}, lines)
)
),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "row mt-5"}, [
ml("div", {class: "col-8", id: "res"}, ""),
ml("div", {class: "col-4 a-r"}, ml("input", {type: "button", value: "save", class: "btn", onclick: function() { ivSave(); }}, null))
])
@ -685,28 +728,34 @@
['keyup', 'change'].forEach(function(evt) {
ser.addEventListener(evt, (e) => {
var serial = ser.value.substring(0,4);
var sn = parseInt(ser.value, 16);
sn = Math.floor(sn / Math.pow(2, 32));
var max = 1;
switch(sn & 0x00f0) {
case 0x0010: max = 1; break;
case 0x0040: max = 2; break;
case 0x0060: max = 4; break;
case 0x0080: max = 6; break;
}
for(var i = 0; i < 6; i++) {
setHide("ch"+i, true);
setHide("ch"+i, (i >= max));
}
if(serial.charAt(0) == 1) {
if((serial.charAt(1) == 0) || (serial.charAt(1) == 1) || (serial.charAt(1) == 3)) {
if((serial.charAt(3) == 1) || (serial.charAt(3) == 2) || (serial.charAt(3) == 4)) {
switch(serial.charAt(2)) {
default:
case "2": max = 1; break;
case "4": max = 2; break;
case "6": max = 4; break;
case "8": max = 6; break;
}
var nrf = true;
switch(sn & 0xff00) {
case 0x1000: nrf = true; break;
case 0x1100:
switch(sn & 0x000f) {
case 0x0004: nrf = false; break;
default: nrf = true; break;
}
}
}
for(var i = 0; i < max; i++) {
setHide("ch"+i, false);
break;
case 0x1300: nrf = false; break;
}
setHide("setcmt", nrf);
setHide("setnrf", !nrf);
document.getElementsByName("isnrf")[0].value = nrf;
})
});
@ -728,6 +777,11 @@
q.yld = document.getElementsByName("yld_c"+i)[0].value;
o.ch.push(q);
}
if("true" == document.getElementsByName("isnrf")[0].value)
o.pa = document.getElementsByName("nrfpa")[0].value;
else
o.pa = document.getElementsByName("cmtpa")[0].value;
o.freq = document.getElementsByName("freq")[0].value;
getAjax("/api/setup", cb, "POST", JSON.stringify(o));
}
@ -845,19 +899,6 @@
])
);
}
e.append(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, "Power Level"),
ml("div", {class: "col-12 col-sm-9"},
sel("rf24Power", [
[0, "MIN (recommended)"],
[1, "LOW"],
[2, "HIGH"],
[3, "MAX (experimental)"]
], obj["power_level"])
)
])
);
}
/*IF_ESP32*/

51
src/web/html/style.css

@ -686,7 +686,7 @@ div.hr {
margin: 1.75rem auto;
}
.modal {
.modal, #modal-wrapper {
position: fixed;
top: 0;
right: 0;
@ -695,6 +695,10 @@ div.hr {
display: block;
}
.modal {
height: calc(100% - 3.5rem);
}
#modal-wrapper {
background-color: #000;
opacity: 0.5;
@ -710,6 +714,8 @@ div.hr {
background-clip: padding-box;
border: 1px solid var(--fg);
flex-direction: column;
max-height: 100%;
overflow: hidden;
}
.modal-header {
@ -717,7 +723,7 @@ div.hr {
align-items: flex-start;
justify-content: space-between;
padding: 1rem;
border-bottom: 1px solid #e9ecef;
border-bottom: 1px solid var(--table-border);
}
.modal-header .close {
@ -726,7 +732,8 @@ div.hr {
}
.modal-body {
padding: 1rem 1rem 2rem 1rem;
padding: 1rem;
overflow-y: auto;
}
.close {
@ -779,3 +786,41 @@ h5 {
vertical-align: baseline;
border-radius: .25rem;
}
ul {
margin-top: 0;
}
.nav {
display: flex;
flex-wrap: wrap;
padding-left: 0;
list-style: none;
}
.nav-tabs {
border-bottom: 1px solid var(--fg);
}
.nav-tabs .nav-link {
margin-bottom: -1px;
border: 1px solid transparent;
border-top-left-radius: .25rem;
border-top-right-radius: .25rem;
}
.nav-link {
display: block;
padding: .5rem 1rem;
text-decoration: none;
color: var(--fg);
}
.nav-tabs .nav-link.active {
border-color: var(--fg) var(--fg) var(--bg);
}
.nav-link:hover, .nav-link:visited {
background-color: var(--input-bg);
color: var(--fg);
}

2
src/web/html/system.html

@ -45,13 +45,11 @@
}
function parseRadio(obj) {
const pa = ["MIN (recommended)", "LOW", "HIGH", "MAX"];
const dr = ["1 M", "2 M", "250 k"]
if(obj.radioNrf.en) {
lines = [
tr("NRF24L01", badge(obj.radioNrf.isconnected, ((obj.radioNrf.isconnected) ? "" : "not ") + "connected")),
tr("NRF24 Power Level", pa[obj.radioNrf.power_level]),
tr("NRF24 Data Rate", dr[obj.radioNrf.dataRate] + "bps")
];
} else

2
src/web/web.h

@ -519,8 +519,6 @@ class Web {
}
}
// nrf24 amplifier power
mConfig->nrf.amplifierPower = request->arg("rf24Power").toInt() & 0x03;
mConfig->nrf.enabled = (request->arg("nrfEnable") == "on");
// cmt

Loading…
Cancel
Save