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