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
This commit is contained in:
lumapu 2023-01-21 00:34:39 +01:00
parent 76b5818f48
commit 2b3f252bbf
17 changed files with 385 additions and 483 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)
if(mConfig->plugin.display.type != 0)
everySec(std::bind(&MonoDisplayType::tickerSecond, &mMonoDisplay), "disp");
#endif
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;

View file

@ -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
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__*/

View file

@ -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

View file

@ -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.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;

View file

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

View file

@ -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)

View file

@ -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

View file

@ -1,21 +1,14 @@
#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, // ................
@ -37,10 +30,12 @@ static uint8_t bmp_logo[] PROGMEM = {
};
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;
}
mDisplay.sendBuffer();
} while( mDisplay.nextPage() );
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);
}
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 ];
mDisplay->clearBuffer();
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;
// 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);
}
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++;
}
}
/* 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"));
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 {
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")));
printText("offline", 0, 25);
if(mCfg->pwrSaveAtIvOffline) {
if ((millis() - displaySleepTimer) > displaySleepDelay)
mDisplay.setPowerSave(true);
if(mTimeout == 0)
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"));
//<=======================
snprintf(_fmtText, sizeof(_fmtText), "today: %4.0f Wh", totalYieldDay);
printText(_fmtText, 1);
snprintf(_fmtText, sizeof(_fmtText), "total: %.1f kWh", totalYieldTotal);
printText(_fmtText, 2);
IPAddress ip = WiFi.localIP();
if (!(_mExtra % 10) && (ip)) {
printText(ip.toString().c_str(), 3);
} 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"));
// Get current time
if(mIsLarge)
printText(ah::getDateTimeStr(mCE.toLocal(*mUtcTs)).c_str(), 3);
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(" -----"));
printText(ah::getTimeStr(mCE.toLocal(*mUtcTs)).c_str(), 3);
}
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++;
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;
}
}
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__*/

View file

@ -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

View file

@ -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) {

View file

@ -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>

View file

@ -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")

View file

@ -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;

View file

@ -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__*/