mirror of https://github.com/lumapu/ahoy.git
32 changed files with 1577 additions and 973 deletions
@ -0,0 +1,285 @@ |
|||||
|
#ifndef __MONOCHROME_DISPLAY__ |
||||
|
#define __MONOCHROME_DISPLAY__ |
||||
|
|
||||
|
#if defined(ENA_NOKIA) || defined(ENA_SSD1306) |
||||
|
#ifdef ENA_NOKIA |
||||
|
#include <U8g2lib.h> |
||||
|
#define DISP_PROGMEM U8X8_PROGMEM |
||||
|
#else // ENA_SSD1306
|
||||
|
/* esp8266 : SCL = 5, SDA = 4 */ |
||||
|
/* ewsp32 : SCL = 22, SDA = 21 */ |
||||
|
#include <Wire.h> |
||||
|
#include <SSD1306Wire.h> |
||||
|
#define DISP_PROGMEM PROGMEM |
||||
|
#endif |
||||
|
|
||||
|
#include <Timezone.h> |
||||
|
|
||||
|
#include "../../utils/helper.h" |
||||
|
#include "../../hm/hmSystem.h" |
||||
|
|
||||
|
static uint8_t bmp_arrow[] DISP_PROGMEM = { |
||||
|
B00000000, B00011100, B00011100, B00001110, B00001110, B11111110, B01111111, |
||||
|
B01110000, B01110000, B00110000, B00111000, B00011000, B01111111, B00111111, |
||||
|
B00011110, B00001110, B00000110, B00000000, B00000000, B00000000, B00000000} ; |
||||
|
|
||||
|
template<class HMSYSTEM> |
||||
|
class MonochromeDisplay { |
||||
|
public: |
||||
|
#if defined(ENA_NOKIA) |
||||
|
MonochromeDisplay() : mDisplay(U8G2_R0,5,4,16) { |
||||
|
mNewPayload = false; |
||||
|
mExtra = 0; |
||||
|
} |
||||
|
#else // ENA_SSD1306
|
||||
|
MonochromeDisplay() : mDisplay(0x3c, SDA, SCL) { |
||||
|
mNewPayload = false; |
||||
|
mExtra = 0; |
||||
|
mRx = 0; |
||||
|
mUp = 1; |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
void setup(HMSYSTEM *sys, uint32_t *utcTs) { |
||||
|
mSys = sys; |
||||
|
mUtcTs = utcTs; |
||||
|
#if defined(ENA_NOKIA) |
||||
|
mDisplay.begin(); |
||||
|
ShowInfoText("booting..."); |
||||
|
#else |
||||
|
mDisplay.init(); |
||||
|
mDisplay.flipScreenVertically(); |
||||
|
mDisplay.setContrast(63); |
||||
|
mDisplay.setBrightness(63); |
||||
|
|
||||
|
mDisplay.clear(); |
||||
|
mDisplay.setFont(ArialMT_Plain_24); |
||||
|
mDisplay.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); |
||||
|
|
||||
|
mDisplay.drawString(64,22,"Starting..."); |
||||
|
mDisplay.display(); |
||||
|
mDisplay.setTextAlignment(TEXT_ALIGN_LEFT); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
void loop(void) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
void payloadEventListener(uint8_t cmd) { |
||||
|
mNewPayload = true; |
||||
|
} |
||||
|
|
||||
|
void tickerSecond() { |
||||
|
if(mNewPayload) { |
||||
|
mNewPayload = false; |
||||
|
DataScreen(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
#if defined(ENA_NOKIA) |
||||
|
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() ); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
void DataScreen(void) { |
||||
|
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
|
||||
|
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
|
||||
|
Timezone CE(CEST, CET); |
||||
|
String timeStr = ah::getDateTimeStr(CE.toLocal(*mUtcTs)).substring(2, 22); |
||||
|
IPAddress ip = WiFi.localIP(); |
||||
|
float totalYield = 0.000, totalYieldToday = 0.000, 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 ); |
||||
|
|
||||
|
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}; |
||||
|
|
||||
|
num_inv++; |
||||
|
|
||||
|
if ( !iv->isProducing(*mUtcTs,rec) ) |
||||
|
continue; |
||||
|
|
||||
|
for (uint8_t fld = 0; fld < 3; fld++) { |
||||
|
pos = iv->getPosByChFld(CH0, list[fld],rec); |
||||
|
|
||||
|
if(fld == 1) |
||||
|
totalYield += iv->getValue(pos,rec); |
||||
|
if(fld == 2) |
||||
|
totalYieldToday += iv->getValue(pos,rec); |
||||
|
if(fld == 0) |
||||
|
{ |
||||
|
pow_i[num_inv-1] = iv->getValue(pos,rec); |
||||
|
totalActual += iv->getValue(pos,rec); |
||||
|
} |
||||
|
} |
||||
|
ucnt++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* u8g2_font_open_iconic_embedded_2x_t 'D' + 'G' + 'J' */ |
||||
|
mDisplay.clear(); |
||||
|
#if defined(ENA_NOKIA) |
||||
|
mDisplay.firstPage(); |
||||
|
do { |
||||
|
if(ucnt) { |
||||
|
mDisplay.drawXBMP(10,0,8,17,bmp_arrow); |
||||
|
mDisplay.setFont(u8g2_font_logisoso16_tr); |
||||
|
mDisplay.setCursor(25,16); |
||||
|
sprintf(fmtText,"%3.0f",totalActual); |
||||
|
mDisplay.print(String(fmtText)+F(" W")); |
||||
|
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 |
||||
|
{ |
||||
|
if( pow_i[0] ) |
||||
|
mDisplay.print(F("#1 ")+String(pow_i[0])+F(" W")); |
||||
|
else |
||||
|
mDisplay.print(F("#1 -----")); |
||||
|
mDisplay.setCursor(5,37); |
||||
|
if( pow_i[1] ) |
||||
|
mDisplay.print(F("#2 ")+String(pow_i[1])+F(" W")); |
||||
|
else |
||||
|
mDisplay.print(F("#2 -----")); |
||||
|
} |
||||
|
} |
||||
|
else { |
||||
|
mDisplay.setFont(u8g2_font_logisoso16_tr); |
||||
|
mDisplay.setCursor(30,30); |
||||
|
mDisplay.print(F("off")); |
||||
|
mDisplay.setFont(u8g2_font_5x8_tr); |
||||
|
} |
||||
|
if ( !(mExtra%10) && ip ) { |
||||
|
mDisplay.setCursor(5,47); |
||||
|
mDisplay.print(ip.toString()); |
||||
|
} |
||||
|
else { |
||||
|
mDisplay.setCursor(0,47); |
||||
|
mDisplay.print(timeStr); |
||||
|
} |
||||
|
|
||||
|
mDisplay.sendBuffer(); |
||||
|
} while( mDisplay.nextPage() ); |
||||
|
mExtra++; |
||||
|
#else // ENA_SSD1306
|
||||
|
if(mUp) { |
||||
|
mRx += 2; |
||||
|
if(mRx >= 20) |
||||
|
mUp = 0; |
||||
|
} else { |
||||
|
mRx -= 2; |
||||
|
if(mRx <= 0) |
||||
|
mUp = 1; |
||||
|
} |
||||
|
int ex = 2*( mExtra % 5 ); |
||||
|
|
||||
|
if(ucnt) { |
||||
|
mDisplay.setBrightness(63); |
||||
|
mDisplay.drawXbm(10+ex,5,8,17,bmp_arrow); |
||||
|
mDisplay.setFont(ArialMT_Plain_24); |
||||
|
sprintf(fmtText,"%3.0f",totalActual); |
||||
|
mDisplay.drawString(25+ex,0,String(fmtText)+F(" W")); |
||||
|
mDisplay.setFont(ArialMT_Plain_16); |
||||
|
|
||||
|
if (( num_inv != 2 ) || !(mExtra%2)) |
||||
|
{ |
||||
|
sprintf(fmtText,"%4.0f",totalYieldToday); |
||||
|
mDisplay.drawString(5,22,F("today ")+String(fmtText)+F(" Wh")); |
||||
|
sprintf(fmtText,"%.1f",totalYield); |
||||
|
mDisplay.drawString(5,35,F("total ")+String(fmtText)+F(" kWh")); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if( pow_i[0] ) |
||||
|
mDisplay.drawString(15,22,F("#1 ")+String(pow_i[0])+F(" W")); |
||||
|
else |
||||
|
mDisplay.drawString(15,22,F("#1 -----")); |
||||
|
if( pow_i[1] ) |
||||
|
mDisplay.drawString(15,35,F("#2 ")+String(pow_i[1])+F(" W")); |
||||
|
else |
||||
|
mDisplay.drawString(15,35,F("#2 -----")); |
||||
|
} |
||||
|
mDisplay.drawLine(2,23,123,23); |
||||
|
} |
||||
|
else { |
||||
|
mDisplay.setBrightness(1); |
||||
|
mDisplay.setFont(ArialMT_Plain_24); |
||||
|
mDisplay.drawString(mRx+50, 10, F("off")); |
||||
|
mDisplay.setFont(ArialMT_Plain_16); |
||||
|
} |
||||
|
if ( (!(mExtra%10) && ip )|| (timeStr.length()<16)) |
||||
|
{ |
||||
|
mDisplay.drawString(5,49,ip.toString()); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int w=mDisplay.getStringWidth(timeStr.c_str(),timeStr.length(),0); |
||||
|
if ( w>127 ) |
||||
|
{ |
||||
|
String tt=timeStr.substring(9,17); |
||||
|
w=mDisplay.getStringWidth(tt.c_str(),tt.length(),0); |
||||
|
mDisplay.drawString(127-w-mRx,49,tt); |
||||
|
} |
||||
|
else |
||||
|
mDisplay.drawString(0,49,timeStr); |
||||
|
} |
||||
|
|
||||
|
mDisplay.display(); |
||||
|
mExtra++; |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
// private member variables
|
||||
|
#if defined(ENA_NOKIA) |
||||
|
U8G2_PCD8544_84X48_1_4W_HW_SPI mDisplay; |
||||
|
#else // ENA_SSD1306
|
||||
|
SSD1306Wire mDisplay; |
||||
|
int mRx; |
||||
|
char mUp; |
||||
|
#endif |
||||
|
int mExtra; |
||||
|
bool mNewPayload; |
||||
|
uint32_t *mUtcTs; |
||||
|
HMSYSTEM *mSys; |
||||
|
}; |
||||
|
#endif |
||||
|
|
||||
|
#endif /*__MONOCHROME_DISPLAY__*/ |
@ -0,0 +1,42 @@ |
|||||
|
//-----------------------------------------------------------------------------
|
||||
|
// 2022 Ahoy, https://github.com/lumpapu/ahoy
|
||||
|
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
|
||||
|
#include "helper.h" |
||||
|
|
||||
|
namespace ah { |
||||
|
void ip2Arr(uint8_t ip[], const char *ipStr) { |
||||
|
memset(ip, 0, 4); |
||||
|
char *tmp = new char[strlen(ipStr)+1]; |
||||
|
strncpy(tmp, ipStr, strlen(ipStr)+1); |
||||
|
char *p = strtok(tmp, "."); |
||||
|
uint8_t i = 0; |
||||
|
while(NULL != p) { |
||||
|
ip[i++] = atoi(p); |
||||
|
p = strtok(NULL, "."); |
||||
|
} |
||||
|
delete[] tmp; |
||||
|
} |
||||
|
|
||||
|
// note: char *str needs to be at least 16 bytes long
|
||||
|
void ip2Char(uint8_t ip[], char *str) { |
||||
|
if(0 == ip[0]) |
||||
|
str[0] = '\0'; |
||||
|
else |
||||
|
snprintf(str, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); |
||||
|
} |
||||
|
|
||||
|
double round3(double value) { |
||||
|
return (int)(value * 1000 + 0.5) / 1000.0; |
||||
|
} |
||||
|
|
||||
|
String getDateTimeStr(time_t t) { |
||||
|
char str[20]; |
||||
|
if(0 == t) |
||||
|
sprintf(str, "n/a"); |
||||
|
else |
||||
|
sprintf(str, "%04d-%02d-%02d %02d:%02d:%02d", year(t), month(t), day(t), hour(t), minute(t), second(t)); |
||||
|
return String(str); |
||||
|
} |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
//-----------------------------------------------------------------------------
|
||||
|
// 2022 Ahoy, https://ahoydtu.de
|
||||
|
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
|
||||
|
#ifndef __HELPER_H__ |
||||
|
#define __HELPER_H__ |
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <cstdint> |
||||
|
#include <cstring> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <TimeLib.h> |
||||
|
|
||||
|
namespace ah { |
||||
|
void ip2Arr(uint8_t ip[], const char *ipStr); |
||||
|
void ip2Char(uint8_t ip[], char *str); |
||||
|
double round3(double value); |
||||
|
String getDateTimeStr(time_t t); |
||||
|
} |
||||
|
|
||||
|
#endif /*__HELPER_H__*/ |
@ -0,0 +1,108 @@ |
|||||
|
//-----------------------------------------------------------------------------
|
||||
|
// 2022 Ahoy, https://ahoydtu.de
|
||||
|
// Lukas Pusch, lukas@lpusch.de
|
||||
|
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
#ifndef __LIST_H__ |
||||
|
#define __LIST_H__ |
||||
|
|
||||
|
template<class T, class... Args> |
||||
|
struct node_s { |
||||
|
typedef T dT; |
||||
|
node_s *pre; |
||||
|
node_s *nxt; |
||||
|
uint32_t id; |
||||
|
dT d; |
||||
|
node_s() : pre(NULL), nxt(NULL), d() {} |
||||
|
node_s(Args... args) : id(0), pre(NULL), nxt(NULL), d(args...) {} |
||||
|
}; |
||||
|
|
||||
|
template<int MAX_NUM, class T, class... Args> |
||||
|
class llist { |
||||
|
typedef node_s<T, Args...> elmType; |
||||
|
typedef T dataType; |
||||
|
public: |
||||
|
llist() : root(mPool) { |
||||
|
root = NULL; |
||||
|
elmType *p = mPool; |
||||
|
for(uint32_t i = 0; i < MAX_NUM; i++) { |
||||
|
p->id = i; |
||||
|
p++; |
||||
|
} |
||||
|
mFill = mMax = 0; |
||||
|
} |
||||
|
|
||||
|
elmType *add(Args... args) { |
||||
|
elmType *p = root, *t; |
||||
|
if(NULL == (t = getFreeNode())) |
||||
|
return NULL; |
||||
|
if(++mFill > mMax) |
||||
|
mMax = mFill; |
||||
|
|
||||
|
if(NULL == root) { |
||||
|
p = root = t; |
||||
|
p->pre = p; |
||||
|
p->nxt = p; |
||||
|
} |
||||
|
else { |
||||
|
p = root->pre; |
||||
|
t->pre = p; |
||||
|
p->nxt->pre = t; |
||||
|
t->nxt = p->nxt; |
||||
|
p->nxt = t; |
||||
|
} |
||||
|
t->d = dataType(args...); |
||||
|
return p; |
||||
|
} |
||||
|
|
||||
|
elmType *getFront() { |
||||
|
return root; |
||||
|
} |
||||
|
|
||||
|
elmType *get(elmType *p) { |
||||
|
p = p->nxt; |
||||
|
return (p == root) ? NULL : p; |
||||
|
} |
||||
|
|
||||
|
elmType *rem(elmType *p) { |
||||
|
if(NULL == p) |
||||
|
return NULL; |
||||
|
elmType *t = p->nxt; |
||||
|
p->nxt->pre = p->pre; |
||||
|
p->pre->nxt = p->nxt; |
||||
|
if(root == p) |
||||
|
root = NULL; |
||||
|
p->nxt = NULL; |
||||
|
p->pre = NULL; |
||||
|
p = NULL; |
||||
|
mFill--; |
||||
|
return (NULL == root) ? NULL : ((t == root) ? NULL : t); |
||||
|
} |
||||
|
|
||||
|
uint16_t getFill(void) { |
||||
|
return mFill; |
||||
|
} |
||||
|
|
||||
|
uint16_t getMaxFill(void) { |
||||
|
return mMax; |
||||
|
} |
||||
|
|
||||
|
protected: |
||||
|
elmType *root; |
||||
|
|
||||
|
private: |
||||
|
elmType *getFreeNode(void) { |
||||
|
elmType *n = mPool; |
||||
|
for(uint32_t i = 0; i < MAX_NUM; i++) { |
||||
|
if(NULL == n->nxt) |
||||
|
return n; |
||||
|
n++; |
||||
|
} |
||||
|
return NULL; |
||||
|
} |
||||
|
|
||||
|
elmType mPool[MAX_NUM]; |
||||
|
uint16_t mFill, mMax; |
||||
|
}; |
||||
|
|
||||
|
#endif /*__LIST_H__*/ |
Loading…
Reference in new issue