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