Browse Source

Merge branch 'lumapu:development03' into development03

pull/1305/head
DanielR92 9 months ago
committed by GitHub
parent
commit
0d1de14747
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .github/workflows/compile_release.yml
  2. 12
      src/CHANGES.md
  3. 25
      src/app.cpp
  4. 6
      src/config/config.h
  5. 35
      src/config/settings.h
  6. 2
      src/defines.h
  7. 51
      src/web/RestApi.h
  8. 6
      src/web/html/setup.html
  9. 3
      src/web/html/system.html
  10. 78
      src/web/web.h

2
.github/workflows/compile_release.yml

@ -77,7 +77,7 @@ jobs:
VERSION: ${{ steps.rename-binary-files.outputs.name }}
- name: Create Artifact
run: zip --junk-paths ${{ steps.rename-binary-files.outputs.name }}.zip src/firmware/* rc/firmware/s3/* User_Manual.md
run: zip --junk-paths ${{ steps.rename-binary-files.outputs.name }}.zip src/firmware/* User_Manual.md
- name: Upload Release
id: upload-release

12
src/CHANGES.md

@ -1,5 +1,17 @@
# Development Changes
## 0.8.35 - 2023-12-30
* added dim option for LEDS
* changed reload time for opendtufusion after update to 5s
* fix default interval and gap for communication
* fix serial number in exported json (was decimal, now correct as hexdecimal number)
* beautified factory reset
* added second stage for erase settings
* increased maximal number of inverters to 32 for opendtufusion board (ESP32-S3)
* fixed crash if CMT inverter is enabled, but CMT isn't configured
# RELEASE 0.8.34 - 2023-12-29
## 0.8.33 - 2023-12-29
* improved communication thx @rejoe2

25
src/app.cpp

@ -102,14 +102,14 @@ void app::setup() {
//-----------------------------------------------------------------------------
void app::loop(void) {
ah::Scheduler::loop();
if(mConfig->nrf.enabled)
mNrfRadio.loop();
#if defined(ESP32)
if(mConfig->cmt.enabled)
mCmtRadio.loop();
#endif
ah::Scheduler::loop();
mCommunication.loop();
if (mMqttEnabled && mNetworkConnected)
@ -354,6 +354,9 @@ void app::tickSend(void) {
continue;
}
if(!iv->radio->isChipConnected())
continue;
iv->tickSend([this, iv](uint8_t cmd, bool isDevControl) {
if(isDevControl)
mCommunication.addImportant(iv, cmd);
@ -449,22 +452,22 @@ void app::mqttSubRxCb(JsonObject obj) {
//-----------------------------------------------------------------------------
void app::setupLed(void) {
uint8_t led_off = (mConfig->led.led_high_active) ? LOW : HIGH;
uint8_t led_off = (mConfig->led.high_active) ? 0 : 255;
if (mConfig->led.led0 != DEF_PIN_OFF) {
pinMode(mConfig->led.led0, OUTPUT);
digitalWrite(mConfig->led.led0, led_off);
analogWrite(mConfig->led.led0, led_off);
}
if (mConfig->led.led1 != DEF_PIN_OFF) {
pinMode(mConfig->led.led1, OUTPUT);
digitalWrite(mConfig->led.led1, led_off);
analogWrite(mConfig->led.led1, led_off);
}
}
//-----------------------------------------------------------------------------
void app::updateLed(void) {
uint8_t led_off = (mConfig->led.led_high_active) ? LOW : HIGH;
uint8_t led_on = (mConfig->led.led_high_active) ? HIGH : LOW;
uint8_t led_off = (mConfig->led.high_active) ? 0 : 255;
uint8_t led_on = (mConfig->led.high_active) ? (mConfig->led.luminance) : (255-mConfig->led.luminance);
if (mConfig->led.led0 != DEF_PIN_OFF) {
Inverter<> *iv;
@ -473,20 +476,20 @@ void app::updateLed(void) {
if (NULL != iv) {
if (iv->isProducing()) {
// turn on when at least one inverter is producing
digitalWrite(mConfig->led.led0, led_on);
analogWrite(mConfig->led.led0, led_on);
break;
}
else if(iv->config->enabled)
digitalWrite(mConfig->led.led0, led_off);
analogWrite(mConfig->led.led0, led_off);
}
}
}
if (mConfig->led.led1 != DEF_PIN_OFF) {
if (getMqttIsConnected()) {
digitalWrite(mConfig->led.led1, led_on);
analogWrite(mConfig->led.led1, led_on);
} else {
digitalWrite(mConfig->led.led1, led_off);
analogWrite(mConfig->led.led1, led_off);
}
}
}

6
src/config/config.h

@ -153,7 +153,11 @@
// number of configurable inverters
#if defined(ESP32)
#define MAX_NUM_INVERTERS 16
#if defined(CONFIG_IDF_TARGET_ESP32S3)
#define MAX_NUM_INVERTERS 32
#else
#define MAX_NUM_INVERTERS 16
#endif
#else
#define MAX_NUM_INVERTERS 4
#endif

35
src/config/settings.h

@ -30,7 +30,7 @@
* https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout
* */
#define CONFIG_VERSION 6
#define CONFIG_VERSION 7
#define PROT_MASK_INDEX 0x0001
@ -117,9 +117,10 @@ typedef struct {
} cfgSerial_t;
typedef struct {
uint8_t led0; // first LED pin
uint8_t led1; // second LED pin
bool led_high_active; // determines if LEDs are high or low active
uint8_t led0; // first LED pin
uint8_t led1; // second LED pin
bool high_active; // determines if LEDs are high or low active
uint8_t luminance; // luminance of LED
} cfgLed_t;
typedef struct {
@ -424,7 +425,7 @@ class settings {
mCfg.serial.showIv = false;
mCfg.serial.debug = false;
mCfg.serial.privacyLog = true;
mCfg.serial.printWholeTrace = true;
mCfg.serial.printWholeTrace = false;
mCfg.mqtt.port = DEF_MQTT_PORT;
snprintf(mCfg.mqtt.broker, MQTT_ADDR_LEN, "%s", DEF_MQTT_BROKER);
@ -440,7 +441,7 @@ class settings {
mCfg.inst.startWithoutTime = false;
mCfg.inst.rstMaxValsMidNight = false;
mCfg.inst.yieldEffiency = 1.0f;
mCfg.inst.gapMs = 2000;
mCfg.inst.gapMs = 500;
mCfg.inst.readGrid = true;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
@ -450,11 +451,10 @@ class settings {
mCfg.inst.iv[i].add2Total = true;
}
mCfg.led.led0 = DEF_LED0;
mCfg.led.led1 = DEF_LED1;
mCfg.led.led_high_active = LED_HIGH_ACTIVE;
memset(&mCfg.inst, 0, sizeof(cfgInst_t));
mCfg.led.led0 = DEF_LED0;
mCfg.led.led1 = DEF_LED1;
mCfg.led.high_active = LED_HIGH_ACTIVE;
mCfg.led.luminance = 255;
mCfg.plugin.display.pwrSaveAtIvOffline = false;
mCfg.plugin.display.contrast = 60;
@ -493,6 +493,9 @@ class settings {
mCfg.inst.gapMs = 500;
mCfg.inst.readGrid = true;
}
if(mCfg.configVersion < 7) {
mCfg.led.luminance = 255;
}
}
}
@ -667,13 +670,15 @@ class settings {
void jsonLed(JsonObject obj, bool set = false) {
if(set) {
obj[F("0")] = mCfg.led.led0;
obj[F("1")] = mCfg.led.led1;
obj[F("act_high")] = mCfg.led.led_high_active;
obj[F("0")] = mCfg.led.led0;
obj[F("1")] = mCfg.led.led1;
obj[F("act_high")] = mCfg.led.high_active;
obj[F("lum")] = mCfg.led.luminance;
} else {
getVal<uint8_t>(obj, F("0"), &mCfg.led.led0);
getVal<uint8_t>(obj, F("1"), &mCfg.led.led1);
getVal<bool>(obj, F("act_high"), &mCfg.led.led_high_active);
getVal<bool>(obj, F("act_high"), &mCfg.led.high_active);
getVal<uint8_t>(obj, F("lum"), &mCfg.led.luminance);
}
}

2
src/defines.h

@ -13,7 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 8
#define VERSION_PATCH 33
#define VERSION_PATCH 35
//-------------------------------------
typedef struct {

51
src/web/RestApi.h

@ -88,6 +88,10 @@ class RestApi {
else if(path == "html/logout") getHtmlLogout(request, root);
else if(path == "html/reboot") getHtmlReboot(request, root);
else if(path == "html/save") getHtmlSave(request, root);
else if(path == "html/erase") getHtmlErase(request, root);
else if(path == "html/erasetrue") getHtmlEraseTrue(request, root);
else if(path == "html/factory") getHtmlFactory(request, root);
else if(path == "html/factorytrue") getHtmlFactoryTrue(request, root);
else if(path == "system") getSysInfo(request, root);
else if(path == "generic") getGeneric(request, root);
else if(path == "reboot") getReboot(request, root);
@ -214,6 +218,16 @@ class RestApi {
tmp.remove(i, tmp.indexOf("\"", i)-i);
}
}
i = 0;
// convert all serial numbers to hexadecimal
while (i != -1) {
i = tmp.indexOf("\"sn\":", i);
if(-1 != i) {
i+=5;
String sn = tmp.substring(i, tmp.indexOf("\"", i)-1);
tmp.replace(sn, String(atoll(sn.c_str()), HEX));
}
}
response = request->beginResponse(200, F("application/json; charset=utf-8"), tmp);
}
@ -337,6 +351,40 @@ class RestApi {
#endif
}
void getHtmlErase(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
obj[F("html")] = F("Erase settings (not WiFi)? <a class=\"btn\" href=\"/erasetrue\">yes</a> <a class=\"btn\" href=\"/\">no</a>");
}
void getHtmlEraseTrue(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
mApp->eraseSettings(false);
mApp->setRebootFlag();
obj[F("html")] = F("Erase settings: success");
#if defined(ETHERNET) && defined(CONFIG_IDF_TARGET_ESP32S3)
obj[F("reload")] = 5;
#else
obj[F("reload")] = 20;
#endif
}
void getHtmlFactory(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
obj[F("html")] = F("Factory reset? <a class=\"btn\" href=\"/factorytrue\">yes</a> <a class=\"btn\" href=\"/\">no</a>");
}
void getHtmlFactoryTrue(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
mApp->eraseSettings(true);
mApp->setRebootFlag();
obj[F("html")] = F("Factory reset: success");
#if defined(ETHERNET) && defined(CONFIG_IDF_TARGET_ESP32S3)
obj[F("reload")] = 5;
#else
obj[F("reload")] = 20;
#endif
}
void getReboot(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
obj[F("refresh")] = 10;
@ -564,7 +612,8 @@ class RestApi {
obj[F("miso")] = mConfig->nrf.pinMiso;
obj[F("led0")] = mConfig->led.led0;
obj[F("led1")] = mConfig->led.led1;
obj[F("led_high_active")] = mConfig->led.led_high_active;
obj[F("led_high_active")] = mConfig->led.high_active;
obj[F("led_lum")] = mConfig->led.luminance;
}
#if defined(ESP32)

6
src/web/html/setup.html

@ -918,8 +918,12 @@
ml("div", { class: "row mb-3" }, [
ml("div", { class: "col-12 col-sm-3 my-2" }, "LED polarity"),
ml("div", { class: "col-12 col-sm-9" },
sel('pinLedHighActive', led_high_active, obj['led_high_active'])
sel('pinLedHighActive', led_high_active, obj.led_high_active)
)
]),
ml("div", { class: "row mb-3" }, [
ml("div", { class: "col-12 col-sm-3 my-2" }, "LED luminance (0-255)"),
ml("div", { class: "col-12 col-sm-9" }, ml("input", {class: "text", type: "number", name: "pinLedLum", value: obj.led_lum, min: 0, max: 255}, null))
])
)
}

3
src/web/html/system.html

@ -126,8 +126,7 @@
meta.httpEquiv = "refresh"
meta.content = obj.refresh + "; URL=" + obj.refresh_url;
document.getElementsByTagName('head')[0].appendChild(meta);
}
else {
} else if(null != obj.system) {
parseRadio(obj.system);
parseMqtt(obj.system.mqtt);
parseSysInfo(obj.system);

78
src/web/web.h

@ -39,7 +39,7 @@
#define WEB_SERIAL_BUF_SIZE 2048
const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq", "pinSclk", "pinMosi", "pinMiso", "pinLed0", "pinLed1", "pinLedHighActive", "pinCmtSclk", "pinSdio", "pinCsb", "pinFcsb", "pinGpio3"};
const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq", "pinSclk", "pinMosi", "pinMiso", "pinLed0", "pinLed1", "pinLedHighActive", "pinLedLum", "pinCmtSclk", "pinSdio", "pinCsb", "pinFcsb", "pinGpio3"};
template <class HMSYSTEM>
class Web {
@ -71,14 +71,15 @@ class Web {
mWeb.onNotFound ( std::bind(&Web::showNotFound, this, std::placeholders::_1));
mWeb.on("/reboot", HTTP_ANY, std::bind(&Web::onReboot, this, std::placeholders::_1));
mWeb.on("/system", HTTP_ANY, std::bind(&Web::onSystem, this, std::placeholders::_1));
mWeb.on("/erase", HTTP_ANY, std::bind(&Web::showErase, this, std::placeholders::_1));
mWeb.on("/factory", HTTP_ANY, std::bind(&Web::showFactoryRst, this, std::placeholders::_1));
mWeb.on("/erase", HTTP_ANY, std::bind(&Web::showHtml, this, std::placeholders::_1));
mWeb.on("/erasetrue", HTTP_ANY, std::bind(&Web::showHtml, this, std::placeholders::_1));
mWeb.on("/factory", HTTP_ANY, std::bind(&Web::showHtml, this, std::placeholders::_1));
mWeb.on("/factorytrue", HTTP_ANY, std::bind(&Web::showHtml, this, std::placeholders::_1));
mWeb.on("/setup", HTTP_GET, std::bind(&Web::onSetup, this, std::placeholders::_1));
mWeb.on("/save", HTTP_POST, std::bind(&Web::showSave, this, std::placeholders::_1));
mWeb.on("/live", HTTP_ANY, std::bind(&Web::onLive, this, std::placeholders::_1));
//mWeb.on("/api1", HTTP_POST, std::bind(&Web::showWebApi, this, std::placeholders::_1));
#ifdef ENABLE_PROMETHEUS_EP
mWeb.on("/metrics", HTTP_ANY, std::bind(&Web::showMetrics, this, std::placeholders::_1));
@ -197,6 +198,11 @@ class Web {
#if !defined(ETHERNET)
strncpy(mConfig->sys.stationPwd, pwd, PWD_LEN); // restore WiFi PWD
#endif
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
if((mConfig->inst.iv[i].serial.u64 != 0) && (mConfig->inst.iv[i].serial.u64 < 138999999999)) { // hexadecimal
mConfig->inst.iv[i].serial.u64 = ah::Serial2u64(String(mConfig->inst.iv[i].serial.u64).c_str());
}
}
mApp->saveSettings(true);
}
if (!mUploadFail)
@ -284,12 +290,18 @@ class Web {
bool reboot = (!Update.hasError());
String html = F("<!doctype html><html><head><title>Update</title><meta http-equiv=\"refresh\" content=\"20; URL=/\"></head><body>Update: ");
String html = F("<!doctype html><html><head><title>Update</title><meta http-equiv=\"refresh\" content=\"");
#if defined(ETHERNET) && defined(CONFIG_IDF_TARGET_ESP32S3)
html += F("5");
#else
html += F("20");
#endif
html += F("; URL=/\"></head><body>Update: ");
if (reboot)
html += "success";
else
html += "failed";
html += F("<br/><br/>rebooting ... auto reload after 20s</body></html>");
html += F("<br/><br/>rebooting ...</body></html>");
AsyncWebServerResponse *response = request->beginResponse(200, F("text/html; charset=UTF-8"), html);
response->addHeader("Connection", "close");
@ -420,39 +432,12 @@ class Web {
request->send(response);
}
void showErase(AsyncWebServerRequest *request) {
checkProtection(request);
DPRINTLN(DBG_VERBOSE, F("showErase"));
mApp->eraseSettings(false);
onReboot(request);
}
void showFactoryRst(AsyncWebServerRequest *request) {
void showHtml(AsyncWebServerRequest *request) {
checkProtection(request);
DPRINTLN(DBG_VERBOSE, F("showFactoryRst"));
String content = "";
int refresh = 3;
if (request->args() > 0) {
if (request->arg("reset").toInt() == 1) {
refresh = 10;
if (mApp->eraseSettings(true))
content = F("factory reset: success\n\nrebooting ... ");
else
content = F("factory reset: failed\n\nrebooting ... ");
} else {
content = F("factory reset: aborted");
refresh = 3;
}
} else {
content = F("<h1>Factory Reset</h1>"
"<p><a href=\"/factory?reset=1\">RESET</a><br/><br/><a href=\"/factory?reset=0\">CANCEL</a><br/></p>");
refresh = 120;
}
request->send(200, F("text/html; charset=UTF-8"), F("<!doctype html><html><head><title>Factory Reset</title><meta http-equiv=\"refresh\" content=\"") + String(refresh) + F("; URL=/\"></head><body>") + content + F("</body></html>"));
if (refresh == 10)
onReboot(request);
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), system_html, system_html_len);
response->addHeader(F("Content-Encoding"), "gzip");
request->send(response);
}
void onSetup(AsyncWebServerRequest *request) {
@ -521,7 +506,7 @@ class Web {
// pinout
uint8_t pin;
for (uint8_t i = 0; i < 14; i++) {
for (uint8_t i = 0; i < 15; i++) {
pin = request->arg(String(pinArgNames[i])).toInt();
switch(i) {
case 0: mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_NRF_CS_PIN); break;
@ -530,14 +515,15 @@ class Web {
case 3: mConfig->nrf.pinSclk = ((pin != 0xff) ? pin : DEF_NRF_SCLK_PIN); break;
case 4: mConfig->nrf.pinMosi = ((pin != 0xff) ? pin : DEF_NRF_MOSI_PIN); break;
case 5: mConfig->nrf.pinMiso = ((pin != 0xff) ? pin : DEF_NRF_MISO_PIN); break;
case 6: mConfig->led.led0 = pin; break;
case 7: mConfig->led.led1 = pin; break;
case 8: mConfig->led.led_high_active = pin; break; // this is not really a pin but a polarity, but handling it close to here makes sense
case 9: mConfig->cmt.pinSclk = pin; break;
case 10: mConfig->cmt.pinSdio = pin; break;
case 11: mConfig->cmt.pinCsb = pin; break;
case 12: mConfig->cmt.pinFcsb = pin; break;
case 13: mConfig->cmt.pinIrq = pin; break;
case 6: mConfig->led.led0 = pin; break;
case 7: mConfig->led.led1 = pin; break;
case 8: mConfig->led.high_active = pin; break; // this is not really a pin but a polarity, but handling it close to here makes sense
case 9: mConfig->led.luminance = pin; break; // this is not really a pin but a polarity, but handling it close to here makes sense
case 10: mConfig->cmt.pinSclk = pin; break;
case 11: mConfig->cmt.pinSdio = pin; break;
case 12: mConfig->cmt.pinCsb = pin; break;
case 13: mConfig->cmt.pinFcsb = pin; break;
case 14: mConfig->cmt.pinIrq = pin; break;
}
}

Loading…
Cancel
Save