Browse Source

fix wakeup issue, once wifi was lost during night the communication didn't start in the morning

reenabled FlashStringHelper because of lacking RAM
complete rewrite of monochrome display class, thx to @dAjaY85 -> displays are now configurable in setup
pull/635/head
lumapu 2 years ago
parent
commit
2b3f252bbf
  1. 2
      .github/workflows/compile_development.yml
  2. 2
      .github/workflows/compile_release.yml
  3. 5
      src/CHANGES.md
  4. 22
      src/app.cpp
  5. 14
      src/app.h
  6. 2
      src/config/config.h
  7. 64
      src/config/settings.h
  8. 2
      src/defines.h
  9. 16
      src/hm/hmInverter.h
  10. 112
      src/platformio.ini
  11. 429
      src/plugins/MonochromeDisplay/MonochromeDisplay.h
  12. 2
      src/utils/dbg.h
  13. 13
      src/web/RestApi.h
  14. 151
      src/web/html/setup.html
  15. 11
      src/web/web.h
  16. 25
      src/wifi/ahoywifi.cpp
  17. 4
      src/wifi/ahoywifi.h

2
.github/workflows/compile_development.yml

@ -47,7 +47,7 @@ jobs:
run: python convert.py
- name: Run PlatformIO
run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp8266-sh1106 --environment esp32-wroom32-release --environment esp32-wroom32-nokia5110 --environment esp32-wroom32-ssd1306 --environment esp32-wroom32-sh1106
run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp32-wroom32-release
- name: Rename Binary files
id: rename-binary-files

2
.github/workflows/compile_release.yml

@ -51,7 +51,7 @@ jobs:
run: python convert.py
- name: Run PlatformIO
run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp8266-sh1106 --environment esp32-wroom32-release --environment esp32-wroom32-nokia5110 --environment esp32-wroom32-ssd1306 --environment esp32-wroom32-sh1106
run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp32-wroom32-release
- name: Rename Binary files
id: rename-binary-files

5
src/CHANGES.md

@ -2,6 +2,11 @@
(starting from release version `0.5.66`)
## 0.5.75
* fix wakeup issue, once wifi was lost during night the communication didn't start in the morning
* reenabled FlashStringHelper because of lacking RAM
* complete rewrite of monochrome display class, thx to @dAjaY85 -> displays are now configurable in setup
## 0.5.74
* improved payload handling (retransmit all fragments on CRC error)
* improved `isAvailable`, checkes all record structs, inverter becomes available more early because version is check first

22
src/app.cpp

@ -64,9 +64,8 @@ void app::setup() {
mApi.setup(this, mSys, mWeb.getWebSrvPtr(), mConfig);
// Plugins
#if defined(ENA_NOKIA) || defined(ENA_SSD1306) || defined(ENA_SH1106)
mMonoDisplay.setup(&mConfig->plugin.display, mSys, &mTimestamp);
#endif
if(mConfig->plugin.display.type != 0)
mMonoDisplay.setup(&mConfig->plugin.display, mSys, &mTimestamp, 0xff, mVersion);
mPubSerial.setup(mConfig, mSys, &mTimestamp);
@ -133,6 +132,7 @@ void app::onWifi(bool gotIp) {
mInnerLoopCb = std::bind(&app::loopStandard, this);
mSendTickerId = every(std::bind(&app::tickSend, this), mConfig->nrf.sendInterval, "tSend");
mMqttReconnect = true;
mSunrise = 0; // needs to be set to 0, to reinstall sunrise and ivComm tickers!
once(std::bind(&app::tickNtpUpdate, this), 2, "ntp2");
}
else {
@ -146,27 +146,27 @@ void app::regularTickers(void) {
DPRINTLN(DBG_DEBUG, F("regularTickers"));
everySec(std::bind(&WebType::tickSecond, &mWeb), "webSc");
// Plugins
#if defined(ENA_NOKIA) || defined(ENA_SSD1306) || defined(ENA_SH1106)
everySec(std::bind(&MonoDisplayType::tickerSecond, &mMonoDisplay), "disp");
#endif
if(mConfig->plugin.display.type != 0)
everySec(std::bind(&MonoDisplayType::tickerSecond, &mMonoDisplay), "disp");
every(std::bind(&PubSerialType::tick, &mPubSerial), mConfig->serial.interval, "uart");
}
//-----------------------------------------------------------------------------
void app::tickNtpUpdate(void) {
uint32_t nxtTrig = 5; // default: check again in 5 sec
if (mWifi.getNtpTime(&nxtTrig)) {
if (mWifi.getNtpTime()) {
if (mMqttReconnect && mMqttEnabled) {
mMqtt.connect();
everySec(std::bind(&PubMqttType::tickerSecond, &mMqtt), "mqttS");
everyMin(std::bind(&PubMqttType::tickerMinute, &mMqtt), "mqttM");
uint32_t nxtTrig = mTimestamp - ((mTimestamp - 1) % 86400) + 86400; // next midnight
if(mConfig->mqtt.rstYieldMidNight)
onceAt(std::bind(&app::tickMidnight, this), nxtTrig, "midNi");
if(mConfig->mqtt.rstYieldMidNight) {
uint32_t midTrig = mTimestamp - ((mTimestamp - 1) % 86400) + 86400; // next midnight
onceAt(std::bind(&app::tickMidnight, this), midTrig, "midNi");
}
mMqttReconnect = false;
}
nxtTrig = 43200;
nxtTrig = 43200; // check again in 12h
if((mSunrise == 0) && (mConfig->sun.lat) && (mConfig->sun.lon)) {
mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600;

14
src/app.h

@ -46,10 +46,8 @@ typedef PubMqtt<HmSystemType> PubMqttType;
typedef PubSerial<HmSystemType> PubSerialType;
// PLUGINS
#if defined(ENA_NOKIA) || defined(ENA_SSD1306) || defined(ENA_SH1106)
#include "plugins/MonochromeDisplay/MonochromeDisplay.h"
typedef MonochromeDisplay<HmSystemType> MonoDisplayType;
#endif
#include "plugins/MonochromeDisplay/MonochromeDisplay.h"
typedef MonochromeDisplay<HmSystemType> MonoDisplayType;
class app : public IApp, public ah::Scheduler {
@ -180,10 +178,8 @@ class app : public IApp, public ah::Scheduler {
void setTimestamp(uint32_t newTime) {
DPRINTLN(DBG_DEBUG, F("setTimestamp: ") + String(newTime));
if(0 == newTime) {
uint32_t tmp;
mWifi.getNtpTime(&tmp);
}
if(0 == newTime)
mWifi.getNtpTime();
else
Scheduler::setTimestamp(newTime);
}
@ -269,9 +265,7 @@ class app : public IApp, public ah::Scheduler {
uint32_t mSunrise, mSunset;
// plugins
#if defined(ENA_NOKIA) || defined(ENA_SSD1306) || defined(ENA_SH1106)
MonoDisplayType mMonoDisplay;
#endif
};
#endif /*__APP_H__*/

2
src/config/config.h

@ -52,8 +52,6 @@
#define DEF_CE_PIN 2
#define DEF_IRQ_PIN 0
#endif
#define DEF_LED0_PIN 255 // off
#define DEF_LED1_PIN 255 // off
// default NRF24 power, possible values (0 - 3)
#define DEF_AMPLIFIERPOWER 1

64
src/config/settings.h

@ -17,6 +17,7 @@
* More info:
* https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout
* */
#define DEF_PIN_OFF 255
#define PROT_MASK_INDEX 0x0001
@ -117,10 +118,15 @@ typedef struct {
} cfgInst_t;
typedef struct {
uint8_t type;
bool pwrSaveAtIvOffline;
uint32_t wakeUp;
uint32_t sleepAt;
bool logoEn;
bool pxShift;
uint16_t wakeUp;
uint16_t sleepAt;
uint8_t contrast;
uint8_t pin0;
uint8_t pin1;
} display_t;
typedef struct {
@ -216,14 +222,15 @@ class settings {
DeserializationError err = deserializeJson(root, fp);
if(!err && (root.size() > 0)) {
mCfg.valid = true;
jsonWifi(root["wifi"]);
jsonNrf(root["nrf"]);
jsonNtp(root["ntp"]);
jsonSun(root["sun"]);
jsonSerial(root["serial"]);
jsonMqtt(root["mqtt"]);
jsonLed(root["led"]);
jsonInst(root["inst"]);
jsonWifi(root[F("wifi")]);
jsonNrf(root[F("nrf")]);
jsonNtp(root[F("ntp")]);
jsonSun(root[F("sun")]);
jsonSerial(root[F("serial")]);
jsonMqtt(root[F("mqtt")]);
jsonLed(root[F("led")]);
jsonPlugin(root[F("plugin")]);
jsonInst(root[F("inst")]);
success = true;
}
else {
@ -252,6 +259,7 @@ class settings {
jsonSerial(root.createNestedObject(F("serial")), true);
jsonMqtt(root.createNestedObject(F("mqtt")), true);
jsonLed(root.createNestedObject(F("led")), true);
jsonPlugin(root.createNestedObject(F("plugin")), true);
jsonInst(root.createNestedObject(F("inst")), true);
if(0 == serializeJson(root, fp)) {
@ -323,13 +331,17 @@ class settings {
mCfg.mqtt.rstValsNotAvail = false;
mCfg.mqtt.rstValsCommStop = false;
mCfg.led.led0 = DEF_LED0_PIN;
mCfg.led.led1 = DEF_LED1_PIN;
mCfg.led.led0 = DEF_PIN_OFF;
mCfg.led.led1 = DEF_PIN_OFF;
memset(&mCfg.inst, 0, sizeof(cfgInst_t));
mCfg.plugin.display.pwrSaveAtIvOffline = false;
mCfg.plugin.display.contrast = 60;
mCfg.plugin.display.contrast = 60;
mCfg.plugin.display.logoEn = true;
mCfg.plugin.display.pxShift = true;
mCfg.plugin.display.pin0 = DEF_PIN_OFF; // SCL
mCfg.plugin.display.pin1 = DEF_PIN_OFF; // SDA
}
void jsonWifi(JsonObject obj, bool set = false) {
@ -452,6 +464,32 @@ class settings {
}
}
void jsonPlugin(JsonObject obj, bool set = false) {
if(set) {
JsonObject disp = obj.createNestedObject("disp");
disp[F("type")] = mCfg.plugin.display.type;
disp[F("pwrSafe")] = (bool)mCfg.plugin.display.pwrSaveAtIvOffline;
disp[F("logo")] = (bool)mCfg.plugin.display.logoEn;
disp[F("pxShift")] = (bool)mCfg.plugin.display.pxShift;
disp[F("wake")] = mCfg.plugin.display.wakeUp;
disp[F("sleep")] = mCfg.plugin.display.sleepAt;
disp[F("contrast")] = mCfg.plugin.display.contrast;
disp[F("pin0")] = mCfg.plugin.display.pin0;
disp[F("pin1")] = mCfg.plugin.display.pin1;
} else {
JsonObject disp = obj["disp"];
mCfg.plugin.display.type = disp[F("type")];
mCfg.plugin.display.pwrSaveAtIvOffline = (bool) disp[F("pwrSafe")];
mCfg.plugin.display.logoEn = (bool) disp[F("logo")];
mCfg.plugin.display.pxShift = (bool) disp[F("pxShift")];
mCfg.plugin.display.wakeUp = disp[F("wake")];
mCfg.plugin.display.sleepAt = disp[F("sleep")];
mCfg.plugin.display.contrast = disp[F("contrast")];
mCfg.plugin.display.pin0 = disp[F("pin0")];
mCfg.plugin.display.pin1 = disp[F("pin1")];
}
}
void jsonInst(JsonObject obj, bool set = false) {
if(set)
obj[F("en")] = (bool)mCfg.inst.enabled;

2
src/defines.h

@ -13,7 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 5
#define VERSION_PATCH 74
#define VERSION_PATCH 75
//-------------------------------------
typedef struct {

16
src/hm/hmInverter.h

@ -315,6 +315,22 @@ class Inverter {
return true;
}
REC_TYP getChannelFieldValue(uint8_t channel, uint8_t fieldId, record_t<> *rec) {
uint8_t pos = 0;
if(NULL != rec) {
for(; pos < rec->length; pos++) {
if((rec->assign[pos].ch == channel) && (rec->assign[pos].fieldId == fieldId))
break;
}
if(pos >= rec->length)
return 0;
return rec->record[pos];
}
else
return 0;
}
REC_TYP getValue(uint8_t pos, record_t<> *rec) {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getValue"));
if(NULL == rec)

112
src/platformio.ini

@ -38,6 +38,8 @@ lib_deps =
paulstoffregen/Time
https://github.com/bertmelis/espMqttClient#v1.3.3
bblanchon/ArduinoJson
https://github.com/JChristensen/Timezone
olikraus/U8g2
;esp8266/DNSServer
;esp8266/EEPROM
;esp8266/ESP8266WiFi
@ -89,60 +91,6 @@ monitor_filters =
time ; Add timestamp with milliseconds for each new line
log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
[env:esp8266-nokia5110]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = -D RELEASE -DU8X8_NO_HW_I2C -DENA_NOKIA
monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
;log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer
nrf24/RF24
paulstoffregen/Time
https://github.com/bertmelis/espMqttClient#v1.3.3
bblanchon/ArduinoJson
https://github.com/JChristensen/Timezone
olikraus/U8g2
[env:esp8266-ssd1306]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = -D RELEASE -DENA_SSD1306
monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
;log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer
nrf24/RF24
paulstoffregen/Time
https://github.com/bertmelis/espMqttClient#v1.3.3
bblanchon/ArduinoJson
https://github.com/JChristensen/Timezone
olikraus/U8g2
[env:esp8266-sh1106]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = -D RELEASE -DENA_SH1106
monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
;log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer
nrf24/RF24
paulstoffregen/Time
https://github.com/bertmelis/espMqttClient#v1.3.3
bblanchon/ArduinoJson
https://github.com/JChristensen/Timezone
olikraus/U8g2
[env:esp32-wroom32-release]
platform = espressif32
board = lolin_d32
@ -163,59 +111,3 @@ monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
[env:esp32-wroom32-nokia5110]
platform = espressif32
board = lolin_d32
build_flags = -D RELEASE -std=gnu++14 -DU8X8_NO_HW_I2C -DENA_NOKIA
build_unflags = -std=gnu++11
monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
;log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer
nrf24/RF24
paulstoffregen/Time
https://github.com/bertmelis/espMqttClient#v1.3.3
bblanchon/ArduinoJson
https://github.com/JChristensen/Timezone
olikraus/U8g2
[env:esp32-wroom32-ssd1306]
platform = espressif32
board = lolin_d32
build_flags = -D RELEASE -std=gnu++14 -DENA_SSD1306
build_unflags = -std=gnu++11
monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
;log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer
nrf24/RF24
paulstoffregen/Time
https://github.com/bertmelis/espMqttClient#v1.3.3
bblanchon/ArduinoJson
https://github.com/ThingPulse/esp8266-oled-ssd1306.git
https://github.com/JChristensen/Timezone
olikraus/U8g2
[env:esp32-wroom32-sh1106]
platform = espressif32
board = lolin_d32
build_flags = -D RELEASE -std=gnu++14 -DENA_SH1106
build_unflags = -std=gnu++11
monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
;log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer
nrf24/RF24
paulstoffregen/Time
https://github.com/bertmelis/espMqttClient#v1.3.3
bblanchon/ArduinoJson
https://github.com/ThingPulse/esp8266-oled-ssd1306.git
https://github.com/JChristensen/Timezone
olikraus/U8g2

429
src/plugins/MonochromeDisplay/MonochromeDisplay.h

@ -1,46 +1,41 @@
#ifndef __MONOCHROME_DISPLAY__
#define __MONOCHROME_DISPLAY__
/* esp8266 : SCL = 5, SDA = 4 */
/* ewsp32 : SCL = 22, SDA = 21 */
#if defined(ENA_NOKIA) || defined(ENA_SSD1306) || defined(ENA_SH1106)
#include <U8g2lib.h>
#ifdef ENA_NOKIA
#define DISP_PROGMEM U8X8_PROGMEM
#else // ENA_SSD1306 || ENA_SH1106
#define DISP_PROGMEM PROGMEM
#endif
#include <Timezone.h>
#include "../../utils/helper.h"
#include "../../hm/hmSystem.h"
#define DISP_DEFAULT_TIMEOUT 60 // in seconds
static uint8_t bmp_logo[] PROGMEM = {
B00000000,B00000000, // ................
B11101100,B00110111, // ..##.######.##..
B11101100,B00110111, // ..##.######.##..
B11100000,B00000111, // .....######.....
B11010000,B00001011, // ....#.####.#....
B10011000,B00011001, // ...##..##..##...
B10000000,B00000001, // .......##.......
B00000000,B00000000, // ................
B01111000,B00011110, // ...####..####...
B11111100,B00111111, // ..############..
B01111100,B00111110, // ..#####..#####..
B00000000,B00000000, // ................
B11111100,B00111111, // ..############..
B11111110,B01111111, // .##############.
B01111110,B01111110, // .######..######.
B00000000,B00000000 // ................
B00000000, B00000000, // ................
B11101100, B00110111, // ..##.######.##..
B11101100, B00110111, // ..##.######.##..
B11100000, B00000111, // .....######.....
B11010000, B00001011, // ....#.####.#....
B10011000, B00011001, // ...##..##..##...
B10000000, B00000001, // .......##.......
B00000000, B00000000, // ................
B01111000, B00011110, // ...####..####...
B11111100, B00111111, // ..############..
B01111100, B00111110, // ..#####..#####..
B00000000, B00000000, // ................
B11111100, B00111111, // ..############..
B11111110, B01111111, // .##############.
B01111110, B01111110, // .######..######.
B00000000, B00000000 // ................
};
static uint8_t bmp_arrow[] DISP_PROGMEM = {
static uint8_t bmp_arrow[] PROGMEM = {
B00000000, B00011100, B00011100, B00001110, B00001110, B11111110, B01111111,
B01110000, B01110000, B00110000, B00111000, B00011000, B01111111, B00111111,
B00011110, B00001110, B00000110, B00000000, B00000000, B00000000, B00000000};
B00011110, B00001110, B00000110, B00000000, B00000000, B00000000, B00000000
};
static TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
static TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Tim
@ -48,31 +43,41 @@ static TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central Eu
template<class HMSYSTEM>
class MonochromeDisplay {
public:
#ifdef ENA_NOKIA
MonochromeDisplay() : mDisplay(U8G2_R0, 5, 4, 16), mCE(CEST, CET) {
mNewPayload = false;
mExtra = 0;
}
#else // ENA_SSD1306 || ENA_SH1106
MonochromeDisplay() : mDisplay(U8G2_R0, SCL, SDA, U8X8_PIN_NONE), mCE(CEST, CET) {
mNewPayload = false;
mExtra = 0;
}
#endif
uint8_t dispContrast = 60;
void setup(display_t *cfg, HMSYSTEM *sys, uint32_t *utcTs) {
MonochromeDisplay() : mCE(CEST, CET) {}
void setup(display_t *cfg, HMSYSTEM *sys, uint32_t *utcTs, uint8_t disp_reset, const char *version) {
mCfg = cfg;
mSys = sys;
mUtcTs = utcTs;
memset( mToday, 0, sizeof(float)*MAX_NUM_INVERTERS );
memset( mTotal, 0, sizeof(float)*MAX_NUM_INVERTERS );
mLastHour = 25;
mDisplay.begin();
ShowInfoText("booting...");
}
mNewPayload = false;
mLoopCnt = 0;
mTimeout = DISP_DEFAULT_TIMEOUT; // power off timeout (after inverters go offline)
if(mCfg->type) {
switch(mCfg->type) {
case 1:
mDisplay = new U8G2_PCD8544_84X48_F_4W_HW_SPI(U8G2_R0, mCfg->pin0, mCfg->pin1, disp_reset);
break;
case 2:
mDisplay = new U8G2_SSD1306_128X64_NONAME_F_HW_I2C(U8G2_R0, disp_reset, mCfg->pin0, mCfg->pin1);
break;
case 3:
mDisplay = new U8G2_SH1106_128X64_NONAME_F_HW_I2C(U8G2_R0, disp_reset, mCfg->pin0, mCfg->pin1);
break;
}
mDisplay->begin();
void loop(void) {
mIsLarge = ((mDisplay->getWidth() > 120) && (mDisplay->getHeight() > 60));
calcLineHeights();
mDisplay->clearBuffer();
mDisplay->setContrast(mCfg->contrast);
printText("Ahoy!", 0, 35);
printText(version, 3, 46);
mDisplay->sendBuffer();
}
}
void payloadEventListener(uint8_t cmd) {
@ -80,244 +85,136 @@ class MonochromeDisplay {
}
void tickerSecond() {
static int cnt=1;
if(mNewPayload || !(cnt % 10)) {
cnt=1;
if(mCfg->pwrSaveAtIvOffline) {
if(mTimeout != 0)
mTimeout--;
}
if(mNewPayload || ((++mLoopCnt % 10) == 0)) {
mNewPayload = false;
mLoopCnt = 0;
DataScreen();
}
else
cnt++;
}
private:
void ShowInfoText(const char *txt) {
/* u8g2_font_open_iconic_embedded_2x_t 'D' + 'G' + 'J' */
mDisplay.clear();
mDisplay.firstPage();
do {
const char *e;
const char *p = txt;
int y=10;
mDisplay.setFont(u8g2_font_5x8_tr);
while(1) {
for(e=p+1; (*e && (*e != '\n')); e++);
size_t len=e-p;
mDisplay.setCursor(2,y);
String res=((String)p).substring(0,len);
mDisplay.print(res);
if ( !*e )
break;
p=e+1;
y+=12;
void DataScreen() {
if (mCfg->type == 0)
return;
if(*mUtcTs == 0)
return;
float totalPower = 0;
float totalYieldDay = 0;
float totalYieldTotal = 0;
bool isprod = false;
Inverter<> *iv;
record_t<> *rec;
for (uint8_t i = 0; i < mSys->getNumInverters(); i++) {
iv = mSys->getInverterByPos(i);
rec = iv->getRecordStruct(RealTimeRunData_Debug);
if (iv == NULL)
continue;
if (iv->isProducing(*mUtcTs))
isprod = true;
totalPower += iv->getChannelFieldValue(CH0, FLD_PAC, rec);
totalYieldDay += iv->getChannelFieldValue(CH0, FLD_YD, rec);
totalYieldTotal += iv->getChannelFieldValue(CH0, FLD_YT, rec);
}
mDisplay->clearBuffer();
// Logos
// pxMovement +x (0 - 6 px)
uint8_t ex = (_mExtra % 7);
if (isprod) {
mDisplay->drawXBMP(5 + ex, 1, 8, 17, bmp_arrow);
if (mCfg->logoEn)
mDisplay->drawXBMP(mDisplay->getWidth() - 24 + ex, 2, 16, 16, bmp_logo);
}
if ((totalPower > 0) && isprod) {
mTimeout = DISP_DEFAULT_TIMEOUT;
mDisplay->setPowerSave(false);
mDisplay->setContrast(mCfg->contrast);
if (totalPower > 999)
snprintf(_fmtText, sizeof(_fmtText), "%2.1f kW", (totalPower / 1000));
else
snprintf(_fmtText, sizeof(_fmtText), "%3.0f W", totalPower);
printText(_fmtText, 0, 20);
} else {
printText("offline", 0, 25);
if(mCfg->pwrSaveAtIvOffline) {
if(mTimeout == 0)
mDisplay->setPowerSave(true);
}
mDisplay.sendBuffer();
} while( mDisplay.nextPage() );
}
}
snprintf(_fmtText, sizeof(_fmtText), "today: %4.0f Wh", totalYieldDay);
printText(_fmtText, 1);
snprintf(_fmtText, sizeof(_fmtText), "total: %.1f kWh", totalYieldTotal);
printText(_fmtText, 2);
void DataScreen(void) {
String timeStr = ah::getDateTimeStr(mCE.toLocal(*mUtcTs)).substring(2, 16);
int hr = timeStr.substring(9,2).toInt();
IPAddress ip = WiFi.localIP();
float totalYield = 0.0, totalYieldToday = 0.0, totalActual = 0.0;
char fmtText[32];
int ucnt=0, num_inv=0;
unsigned int pow_i[ MAX_NUM_INVERTERS ];
memset( pow_i, 0, sizeof(unsigned int)* MAX_NUM_INVERTERS );
if ( hr < mLastHour ) // next day ? reset today-values
memset( mToday, 0, sizeof(float)*MAX_NUM_INVERTERS );
mLastHour = hr;
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
Inverter<> *iv = mSys->getInverterByPos(id);
if (NULL != iv) {
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
uint8_t pos;
uint8_t list[] = {FLD_PAC, FLD_YT, FLD_YD};
for (uint8_t fld = 0; fld < 3; fld++) {
pos = iv->getPosByChFld(CH0, list[fld],rec);
int isprod = iv->isProducing(*mUtcTs);
if(fld == 1)
{
if ( isprod )
mTotal[num_inv] = iv->getValue(pos,rec);
totalYield += mTotal[num_inv];
}
if(fld == 2)
{
if ( isprod )
mToday[num_inv] = iv->getValue(pos,rec);
totalYieldToday += mToday[num_inv];
}
if((fld == 0) && isprod )
{
pow_i[num_inv] = iv->getValue(pos,rec);
totalActual += iv->getValue(pos,rec);
ucnt++;
}
}
num_inv++;
}
if (!(_mExtra % 10) && (ip)) {
printText(ip.toString().c_str(), 3);
} else {
// Get current time
if(mIsLarge)
printText(ah::getDateTimeStr(mCE.toLocal(*mUtcTs)).c_str(), 3);
else
printText(ah::getTimeStr(mCE.toLocal(*mUtcTs)).c_str(), 3);
}
mDisplay->sendBuffer();
_mExtra++;
}
void calcLineHeights() {
uint8_t yOff = 0;
for(uint8_t i = 0; i < 4; i++) {
setFont(i);
yOff += (mDisplay->getMaxCharHeight() + 1);
mLineOffsets[i] = yOff;
}
}
inline void setFont(uint8_t line) {
switch (line) {
case 0: mDisplay->setFont((mIsLarge) ? u8g2_font_ncenB14_tr : u8g2_font_lubBI14_tr); break;
case 3: mDisplay->setFont(u8g2_font_5x8_tr); break;
default: mDisplay->setFont((mIsLarge) ? u8g2_font_ncenB10_tr : u8g2_font_5x8_tr); break;
}
/* u8g2_font_open_iconic_embedded_2x_t 'D' + 'G' + 'J' */
mDisplay.clear();
mDisplay.firstPage();
do {
#ifdef ENA_NOKIA
if(ucnt) {
//=====> Actual Production
mDisplay.drawXBMP(10,1,8,17,bmp_arrow);
mDisplay.setFont(u8g2_font_logisoso16_tr);
mDisplay.setCursor(25,17);
if (totalActual>999){
sprintf(fmtText,"%2.1f",(totalActual/1000));
mDisplay.print(String(fmtText)+F(" kW"));
} else {
sprintf(fmtText,"%3.0f",totalActual);
mDisplay.print(String(fmtText)+F(" W"));
}
//<=======================
}
else
{
//=====> Offline
mDisplay.setFont(u8g2_font_logisoso16_tr );
mDisplay.setCursor(10,17);
mDisplay.print(String(F("offline")));
//<=======================
}
mDisplay.drawHLine(2,20,78);
mDisplay.setFont(u8g2_font_5x8_tr);
mDisplay.setCursor(5,29);
if (( num_inv < 2 ) || !(mExtra%2))
{
sprintf(fmtText,"%4.0f",totalYieldToday);
mDisplay.print(F("today ")+String(fmtText)+F(" Wh"));
mDisplay.setCursor(5,37);
sprintf(fmtText,"%.1f",totalYield);
mDisplay.print(F("total ")+String(fmtText)+F(" kWh"));
}
else
{
int id1=(mExtra/2)%(num_inv-1);
if( pow_i[id1] )
mDisplay.print(F("#")+String(id1+1)+F(" ")+String(pow_i[id1])+F(" W"));
else
mDisplay.print(F("#")+String(id1+1)+F(" -----"));
mDisplay.setCursor(5,37);
if( pow_i[id1+1] )
mDisplay.print(F("#")+String(id1+2)+F(" ")+String(pow_i[id1+1])+F(" W"));
else
mDisplay.print(F("#")+String(id1+2)+F(" -----"));
}
if ( !(mExtra%10) && ip ) {
mDisplay.setCursor(5,47);
mDisplay.print(ip.toString());
}
else {
mDisplay.setCursor(5,47);
mDisplay.print(timeStr);
}
#else // ENA_SSD1306
mDisplay.setContrast(mCfg->contrast);
// pxZittern in +x (0 - 8 px)
int ex = 2*( mExtra % 5 );
mDisplay.drawXBM(100+ex,2,16,16,bmp_logo);
mDisplay.setFont(u8g2_font_ncenB08_tr);
if(ucnt) {
//=====> Actual Production
mDisplay.setPowerSave(false);
displaySleep=false;
mDisplay.setFont(u8g2_font_logisoso18_tr);
mDisplay.drawXBM(10+ex,2,8,17,bmp_arrow);
mDisplay.setCursor(25+ex,20);
if (totalActual>999){
sprintf(fmtText,"%2.1f",(totalActual/1000));
mDisplay.print(String(fmtText)+F(" kW"));
} else {
sprintf(fmtText,"%3.0f",totalActual);
mDisplay.print(String(fmtText)+F(" W"));
}
//<=======================
}
else
{
//=====> Offline
if(!displaySleep) {
displaySleepTimer = millis();
displaySleep=true;
}
mDisplay.setFont(u8g2_font_logisoso18_tr);
mDisplay.setCursor(10+ex,20);
mDisplay.print(String(F("offline")));
if(mCfg->pwrSaveAtIvOffline) {
if ((millis() - displaySleepTimer) > displaySleepDelay)
mDisplay.setPowerSave(true);
}
//<=======================
}
mDisplay.drawLine(2+ex, 23, 123, 23);
mDisplay.setFont(u8g2_font_ncenB10_tr);
mDisplay.setCursor(2+ex,36);
if (( num_inv < 2 ) || !(mExtra%2))
{
//=====> Today & Total Production
sprintf(fmtText,"%5.0f",totalYieldToday);
mDisplay.print(F("today: ")+String(fmtText)+F(" Wh"));
mDisplay.setCursor(2+ex,50);
sprintf(fmtText,"%.1f",totalYield);
mDisplay.print(F("total: ")+String(fmtText)+F(" kWh"));
//<=======================
} else {
int id1=(mExtra/2)%(num_inv-1);
if( pow_i[id1] )
mDisplay.print(F("#")+String(id1+1)+F(" ")+String(pow_i[id1])+F(" W"));
else
mDisplay.print(F("#")+String(id1+1)+F(" -----"));
mDisplay.setCursor(5+ex,50);
if( pow_i[id1+1] )
mDisplay.print(F("#")+String(id1+2)+F(" ")+String(pow_i[id1+1])+F(" W"));
else
mDisplay.print(F("#")+String(id1+2)+F(" -----"));
}
mDisplay.setFont(u8g2_font_5x8_tr);
mDisplay.setCursor(5+ex,63);
if ( !(mExtra%10) && ip )
mDisplay.print(ip.toString());
else
mDisplay.print(timeStr);
#endif
mDisplay.sendBuffer();
} while( mDisplay.nextPage() );
delay(200);
mExtra++;
}
void printText(const char* text, uint8_t line, uint8_t dispX = 5) {
if(!mIsLarge)
dispX = 5;
setFont(line);
if(mCfg->pxShift)
dispX += (_mExtra % 7); // add pixel movement
mDisplay->drawStr(dispX, mLineOffsets[line], text);
}
// private member variables
#ifdef ENA_NOKIA
U8G2_PCD8544_84X48_1_4W_HW_SPI mDisplay;
#elif defined(ENA_SSD1306)
U8G2_SSD1306_128X64_NONAME_F_HW_I2C mDisplay;
#elif defined(ENA_SH1106)
U8G2_SH1106_128X64_NONAME_F_HW_I2C mDisplay;
#endif
int mExtra;
U8G2* mDisplay;
uint8_t _mExtra;
uint16_t mTimeout; // interval at which to power save (milliseconds)
char _fmtText[32];
bool mNewPayload;
float mTotal[ MAX_NUM_INVERTERS ];
float mToday[ MAX_NUM_INVERTERS ];
bool mIsLarge;
uint8_t mLoopCnt;
uint32_t *mUtcTs;
int mLastHour;
uint8_t mLineOffsets[5];
display_t *mCfg;
HMSYSTEM *mSys;
Timezone mCE;
bool displaySleep;
uint32_t displaySleepTimer;
const uint32_t displaySleepDelay= 60000;
};
#endif
#endif /*__MONOCHROME_DISPLAY__*/

2
src/utils/dbg.h

@ -5,7 +5,7 @@
#ifndef __DBG_H__
#define __DBG_H__
#if defined(F) //defined(ESP32) &&
#if defined(F) && defined(ESP32)
#undef F
#define F(sl) (sl)
#endif

13
src/web/RestApi.h

@ -14,7 +14,7 @@
#include "../appInterface.h"
#if defined(F) //defined(ESP32) &&
#if defined(F) && defined(ESP32)
#undef F
#define F(sl) (sl)
#endif
@ -354,6 +354,16 @@ class RestApi {
ah::ip2Char(mConfig->sys.ip.gateway, buf); obj[F("gateway")] = String(buf);
}
void getDisplay(JsonObject obj) {
obj[F("disp_type")] = (uint8_t)mConfig->plugin.display.type;
obj[F("disp_pwr")] = (bool)mConfig->plugin.display.pwrSaveAtIvOffline;
obj[F("logo_en")] = (bool)mConfig->plugin.display.logoEn;
obj[F("px_shift")] = (bool)mConfig->plugin.display.pxShift;
obj[F("contrast")] = (uint8_t)mConfig->plugin.display.contrast;
obj[F("pinDisp0")] = mConfig->plugin.display.pin0;
obj[F("pinDisp1")] = mConfig->plugin.display.pin1;
}
void getMenu(JsonObject obj) {
uint8_t i = 0;
uint16_t mask = (mApp->getProtection()) ? mConfig->sys.protectionMask : 0;
@ -461,6 +471,7 @@ class RestApi {
getRadio(obj.createNestedObject(F("radio")));
getSerial(obj.createNestedObject(F("serial")));
getStaticIp(obj.createNestedObject(F("static_ip")));
getDisplay(obj.createNestedObject(F("display")));
}
void getNetworks(JsonObject obj) {

151
src/web/html/setup.html

@ -165,7 +165,7 @@
<div class="s_content">
<fieldset>
<legend class="des">System Config</legend>
<p class="des">Pinout (Wemos)</p>
<p class="des">Pinout</p>
<div id="pinout"></div>
<p class="des">Radio (NRF24L01+)</p>
@ -181,6 +181,25 @@
</fieldset>
</div>
<button type="button" class="s_collapsible">Display Config</button>
<div class="s_content">
<fieldset>
<legend class="des">Display Config</legend>
<div id="dispType"></div>
<label for="logoEn">Show Logo</label>
<input type="checkbox" class="cb" name="logoEn"/><br/>
<label for="dispPwr">Turn off while inverters are offline</label>
<input type="checkbox" class="cb" name="dispPwr"/><br/>
<label for="dispPxSh">Enable pixel shifting</label>
<input type="checkbox" class="cb" name="dispPxSh"/><br/>
<label for="dispCont">Contrast</label>
<select name="dispCont" id="contrast"></select>
<p class="des">Pinout</p>
<div id="dispPins"></div>
</fieldset>
</div>
<div class="mt-3">
<label for="reboot">Reboot device after successful save</label>
<input type="checkbox" class="cb" name="reboot" checked />
@ -221,6 +240,56 @@
var highestId = 0;
var maxInv = 0;
var esp8266pins = [
[255, "off / default"],
[0, "D3 (GPIO0)"],
[1, "TX (GPIO1)"],
[2, "D4 (GPIO2)"],
[3, "RX (GPIO3)"],
[4, "D2 (GPIO4, SDA)"],
[5, "D1 (GPIO5, SCL)"],
[6, "GPIO6"],
[7, "GPIO7"],
[8, "GPIO8"],
[9, "GPIO9"],
[10, "GPIO10"],
[11, "GPIO11"],
[12, "D6 (GPIO12)"],
[13, "D7 (GPIO13)"],
[14, "D5 (GPIO14)"],
[15, "D8 (GPIO15)"],
[16, "D0 (GPIO16 - no IRQ!)"]
];
var esp32pins = [
[255, "off / default"],
[0, "GPIO0"],
[1, "TX (GPIO1)"],
[2, "GPIO2 (LED)"],
[3, "RX (GPIO3)"],
[4, "GPIO4"],
[5, "GPIO5"],
[12, "GPIO12"],
[13, "GPIO13"],
[14, "GPIO14"],
[15, "GPIO15"],
[16, "GPIO16"],
[17, "GPIO17"],
[18, "GPIO18"],
[19, "GPIO19"],
[21, "GPIO21 (SDA)"],
[22, "GPIO22 (SCL)"],
[23, "GPIO23"],
[25, "GPIO25"],
[26, "GPIO26"],
[27, "GPIO27"],
[32, "GPIO32"],
[33, "GPIO33"],
[34, "GPIO34"],
[35, "GPIO35"],
[36, "VP (GPIO36)"],
[39, "VN (GPIO39)"]
];
const re = /11[2,4,6]1.*/;
document.getElementById("btnAdd").addEventListener("click", function() {
@ -443,59 +512,7 @@
pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq'], ['led0', 'pinLed0'], ['led1', 'pinLed1']];
for(p of pins) {
e.appendChild(lbl(p[1], p[0].toUpperCase()));
if("ESP8266" == type) {
e.appendChild(sel(p[1], [
[255, "off / default"],
[0, "D3 (GPIO0)"],
[1, "TX (GPIO1)"],
[2, "D4 (GPIO2)"],
[3, "RX (GPIO3)"],
[4, "D2 (GPIO4)"],
[5, "D1 (GPIO5)"],
[6, "GPIO6"],
[7, "GPIO7"],
[8, "GPIO8"],
[9, "GPIO9"],
[10, "GPIO10"],
[11, "GPIO11"],
[12, "D6 (GPIO12)"],
[13, "D7 (GPIO13)"],
[14, "D5 (GPIO14)"],
[15, "D8 (GPIO15)"],
[16, "D0 (GPIO16 - no IRQ!)"]
], obj[p[0]]));
}
else {
e.appendChild(sel(p[1], [
[255, "off / default"],
[0, "GPIO0"],
[1, "TX (GPIO1)"],
[2, "GPIO2 (LED)"],
[3, "RX (GPIO3)"],
[4, "GPIO4"],
[5, "GPIO5"],
[12, "GPIO12"],
[13, "GPIO13"],
[14, "GPIO14"],
[15, "GPIO15"],
[16, "GPIO16"],
[17, "GPIO17"],
[18, "GPIO18"],
[19, "GPIO19"],
[21, "GPIO21"],
[22, "GPIO22"],
[23, "GPIO23"],
[25, "GPIO25"],
[26, "GPIO26"],
[27, "GPIO27"],
[32, "GPIO32"],
[33, "GPIO33"],
[34, "GPIO34"],
[35, "GPIO35"],
[36, "VP (GPIO36)"],
[39, "VN (GPIO39)"]
], obj[p[0]]));
}
e.appendChild(sel(p[1], ("ESP8266" == type) ? esp8266pins : esp32pins, obj[p[0]]));
}
}
@ -516,6 +533,29 @@
document.getElementsByName("serIntvl")[0].value = obj["interval"];
}
function parseDisplay(obj, type) {
for(var i of [["logoEn", "logo_en"], ["dispPwr", "disp_pwr"], ["dispPxSh", "px_shift"]])
document.getElementsByName(i[0])[0].checked = obj[i[1]];
var e = document.getElementById("dispPins");
pins = [['SCL / CS', 'pinDisp0'], ['SDA / DC', 'pinDisp1']];
for(p of pins) {
e.appendChild(lbl(p[1], p[0].toUpperCase()));
e.appendChild(sel(p[1], ("ESP8266" == type) ? esp8266pins : esp32pins, obj[p[1]]));
}
var opts = [[0, "None"], [1, "Nokia5110"], [2, "SSD1306 0.96\""], [3, "SH1106 1.3\""]];
document.getElementById("dispType").append(
lbl("dispType", "Type"),
sel("dispType", opts, obj["disp_type"])
);
e = document.getElementById("contrast");
for(var i = 30; i < 101; i += 2) {
e.appendChild(opt(i, i, (i == obj["contrast"])));
}
}
function parse(root) {
if(null != root) {
parseMenu(root["menu"]);
@ -529,6 +569,7 @@
parsePinout(root["pinout"], root["system"]["esp_type"]);
parseRadio(root["radio"]);
parseSerial(root["serial"]);
parseDisplay(root["display"], root["system"]["esp_type"]);
}
}
@ -555,7 +596,7 @@
hiddenInput = document.getElementById("disclaimer")
hiddenInput.value = sessionStorage.getItem("gDisclaimer");
getAjax("/api/setup", parse);
getAjax("http://10.20.3.44/api/setup", parse);
</script>
</body>

11
src/web/web.h

@ -562,6 +562,17 @@ class Web {
// Needed to log TX buffers to serial console
mSys->Radio.mSerialDebug = mConfig->serial.debug;
}
// display
mConfig->plugin.display.pwrSaveAtIvOffline = (request->arg("dispPwr") == "on");
mConfig->plugin.display.logoEn = (request->arg("logoEn") == "on");
mConfig->plugin.display.pxShift = (request->arg("dispPxSh") == "on");
mConfig->plugin.display.type = request->arg("dispType").toInt();
mConfig->plugin.display.contrast = request->arg("dispCont").toInt();
mConfig->plugin.display.pin0 = request->arg("pinDisp0").toInt();
mConfig->plugin.display.pin1 = request->arg("pinDisp1").toInt();
mApp->saveSettings();
if(request->arg("reboot") == "on")

25
src/wifi/ahoywifi.cpp

@ -11,7 +11,6 @@
// NTP CONFIG
#define NTP_PACKET_SIZE 48
#define NTP_RETRIES 5
//-----------------------------------------------------------------------------
ahoywifi::ahoywifi() : mApIp(192, 168, 4, 1) {}
@ -26,7 +25,7 @@ void ahoywifi::setup(settings_t *config, uint32_t *utcTimestamp, appWifiCb cb) {
mStaConn = DISCONNECTED;
mCnt = 0;
mScanActive = false;
mRetries = NTP_RETRIES;
mLastNtpFailed = false;
#if defined(ESP8266)
wifiConnectHandler = WiFi.onStationModeConnected(std::bind(&ahoywifi::onConnect, this, std::placeholders::_1));
@ -149,26 +148,25 @@ void ahoywifi::setupStation(void) {
//-----------------------------------------------------------------------------
bool ahoywifi::getNtpTime(uint32_t *nxtTrig) {
if(0 != mRetries) {
DPRINTLN(DBG_INFO, "try to getNtpTime");
*nxtTrig = 43200; // check again in 12h (if NTP was successful)
mRetries--;
} else if(0 != *mUtcTimestamp) { // time is availabe, but NTP not
*nxtTrig = 5; // check again 5s
mRetries = NTP_RETRIES;
bool ahoywifi::getNtpTime() {
if(mLastNtpFailed && (0 != *mUtcTimestamp)) { // time is available, but NTP not maybe it was set by "sync with browser"
mLastNtpFailed = false;
return true; // true is necessary to enable all timers even if NTP was not reachable
}
if(GOT_IP != mStaConn)
if(GOT_IP != mStaConn) {
mLastNtpFailed = true;
return false;
}
IPAddress timeServer;
uint8_t buf[NTP_PACKET_SIZE];
uint8_t retry = 0;
if (WiFi.hostByName(mConfig->ntp.addr, timeServer) != 1)
if (WiFi.hostByName(mConfig->ntp.addr, timeServer) != 1) {
mLastNtpFailed = true;
return false;
}
mUdp.begin(mConfig->ntp.port);
sendNTPpacket(timeServer);
@ -193,6 +191,7 @@ bool ahoywifi::getNtpTime(uint32_t *nxtTrig) {
}
DPRINTLN(DBG_INFO, F("[NTP]: getNtpTime failed"));
mLastNtpFailed = true;
return false;
}
@ -275,10 +274,10 @@ void ahoywifi::connectionEvent(WiFiStatus_t status) {
if(mStaConn != CONNECTING) {
mStaConn = DISCONNECTED;
mCnt = 5; // try to reconnect in 5 sec
mLastNtpFailed = false;
setupWifi(); // reconnect with AP / Station setup
mAppWifiCb(false);
DPRINTLN(DBG_INFO, "[WiFi] Connection Lost");
mRetries = NTP_RETRIES;
}
break;

4
src/wifi/ahoywifi.h

@ -25,7 +25,7 @@ class ahoywifi {
void setup(settings_t *config, uint32_t *utcTimestamp, appWifiCb cb);
void tickWifiLoop(void);
bool getNtpTime(uint32_t *nxtTrig);
bool getNtpTime();
void scanAvailNetworks(void);
void getAvailNetworks(JsonObject obj);
@ -68,7 +68,7 @@ class ahoywifi {
uint8_t mLoopCnt;
bool mScanActive;
uint8_t mRetries;
bool mLastNtpFailed;
};
#endif /*__AHOYWIFI_H__*/

Loading…
Cancel
Save