From 37e8f1bfca59bdb18c71dccc3e4171824a38cdc5 Mon Sep 17 00:00:00 2001 From: Dirk Jahnke Date: Tue, 29 Jan 2019 15:25:27 +0100 Subject: [PATCH] Refactoring: created Display class. --- src/Display.cpp | 198 +++++++++++++++++++++++++++++++++++ src/Display.h | 40 ++++++++ src/main.cpp | 266 +++++++----------------------------------------- 3 files changed, 274 insertions(+), 230 deletions(-) create mode 100644 src/Display.cpp create mode 100644 src/Display.h diff --git a/src/Display.cpp b/src/Display.cpp new file mode 100644 index 0000000..9aa2a63 --- /dev/null +++ b/src/Display.cpp @@ -0,0 +1,198 @@ + +#include "Display.h" +#include +#include +#include +#include "MD_RobotEyes.h" + +#define DISPLAY_CLK_PIN D5 +#define DISPLAY_DATA_PIN D7 +#define DISPLAY_CS_PIN D6 +#define VERTICAL_BAR_STARTS_TOP false + +#define HARDWARE_TYPE MD_MAX72XX::FC16_HW +#define MAX_DISPLAY_DEVICES 4 + +static MD_Parola P = MD_Parola(HARDWARE_TYPE, DISPLAY_DATA_PIN, DISPLAY_CLK_PIN, DISPLAY_CS_PIN, MAX_DISPLAY_DEVICES); +static MD_RobotEyes E; +static MD_MAX72XX *graphicDisplay = NULL; + + +void Display::begin() { + int charCode; +#if VERTICAL_BAR_STARTS_TOP + static uint8_t verticalBarFont[] = { + 1, 0x00, /* blank */ + 1, 0x01, /* 1 dot */ + 1, 0x03, /* 2 dots */ + 1, 0x07, + 1, 0x0f, + 1, 0x1f, + 1, 0x3f, + 1, 0x7f, + 1, 0xff, /* vertical bar completely set */ + }; // columns from right to left, each byte is a single column +#else + static uint8_t verticalBarFont[] = { + 1, 0x00, /* blank */ + 1, 0x80, /* 1 dot */ + 1, 0xc0, /* 2 dots */ + 1, 0xe0, + 1, 0xf0, + 1, 0xf8, + 1, 0xfc, + 1, 0xfe, + 1, 0xff, /* vertical bar completely set */ + }; // columns from right to left, each byte is a single column +#endif + static uint8_t newZero[] = {0x05, 0x3e, 0x41, 0x41, 0x41, 0x3e, 0x00}; + + P.begin(); + // P.setZoneEffect(0, true, PA_FLIP_LR); + graphicDisplay = P.getGraphicObject(); + E.begin(graphicDisplay); + + P.setIntensity(1); + for (charCode=1; charCode<=9; ++charCode) { + P.addChar(charCode, verticalBarFont+2*(charCode-1)); + } + // replace the 0 characters, we do not like the "slash" + P.addChar('0', newZero); + + char intro[] = {':', '-', ')', ' ', 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00}; + P.print(intro); +} + +typedef struct +{ + char name[7]; + MD_RobotEyes::emotion_t e; + uint16_t timePause; // in milliseconds +} sampleItem_t; + +static const sampleItem_t eSeq[] = +{ + { "Nutral", MD_RobotEyes::E_NEUTRAL, 1000 }, + { "Blink" , MD_RobotEyes::E_BLINK, 1000 }, + { "Wink" , MD_RobotEyes::E_WINK, 1000 }, + { "Left" , MD_RobotEyes::E_LOOK_L, 1000 }, + { "Right" , MD_RobotEyes::E_LOOK_R, 1000 }, + { "Up" , MD_RobotEyes::E_LOOK_U, 1000 }, + { "Down" , MD_RobotEyes::E_LOOK_D, 1000 }, + { "Angry" , MD_RobotEyes::E_ANGRY, 1000 }, + { "Sad" , MD_RobotEyes::E_SAD, 1000 }, + { "Evil" , MD_RobotEyes::E_EVIL, 1000 }, + { "Evil2" , MD_RobotEyes::E_EVIL2, 1000 }, + { "Squint", MD_RobotEyes::E_SQUINT, 1000 }, + { "Dead" , MD_RobotEyes::E_DEAD, 1000 }, + { "ScanV" , MD_RobotEyes::E_SCAN_UD, 1000 }, + { "ScanH" , MD_RobotEyes::E_SCAN_LR, 1000 }, +}; +#define DISPLAY_ANIM_NAME false + +void Display::loopEyeAnimation() { + // show startup animation + boolean animationFinished = false; + static uint32_t timeStartDelay; + static uint8_t index = ARRAY_SIZE(eSeq); + static enum { S_IDLE, S_TEXT, S_ANIM, S_PAUSE } state = S_IDLE; + + animationFinished = E.runAnimation(); + + switch (state) + { + case S_IDLE: + index++; + if (index >= ARRAY_SIZE(eSeq)) + index = 0; + P.displayClear(); + #if DISPLAY_ANIM_NAME + E.setText(eSeq[index].name); + #endif + state = S_TEXT; + break; + + case S_TEXT: // wait for the text to finish + if (animationFinished) // text animation is finished + { + E.setAnimation(eSeq[index].e, true); + state = S_ANIM; + } + break; + + case S_ANIM: // checking animation is completed + if (animationFinished) // animation is finished + { + timeStartDelay = millis(); + state = S_PAUSE; + } + break; + + case S_PAUSE: // non blocking waiting for a period between animations + if (millis() - timeStartDelay >= eSeq[index].timePause) + state = S_IDLE; + break; + + default: + state = S_IDLE; + break; + } +} + +void Display::loop() { + char minuteProgressIndicator; + char timeBuffer[10]; + + minuteProgressIndicator = currentTime->getSeconds()/6.7 + 1; // char code 1-8 show vertical bar + if (minuteProgressIndicator > 9) minuteProgressIndicator = 9; + snprintf(timeBuffer, 10, "%c %2d:%02d", minuteProgressIndicator, currentTime->getHours(), currentTime->getMinutes()); + + // standard procedure to display + static uint32_t last_clock_refresh = 0; + static uint32_t lastTimeClockNameShown = 0; + static boolean showingClockName = false; + + if (showingClockName) { + if (millis() - lastTimeClockNameShown > clockNameDurationTime_ms) { + // stop showingClockName + showingClockName = false; + } + } else { + if ((millis() - lastTimeClockNameShown > displayClockNameEvery_ms) + && (currentTime->getSeconds() < 60-doNotShowClockNameBeforeAndAfterMinuteChange_s) + && (currentTime->getSeconds() > doNotShowClockNameBeforeAndAfterMinuteChange_s)) { + // re-initialize, that fixes display problems due to electrical relais feedbacks + reInitializeDisplay(); + P.setIntensity(2); + P.print(currentTime->getClockName()); + lastTimeClockNameShown = millis(); + showingClockName = true; + } else { + // showing clock + if (millis() - last_clock_refresh > displayRefresh_ms) { + // re-initialize, that fixes display problems due to electrical relais feedbacks + reInitializeDisplay(); + P.setIntensity(1); + P.print(timeBuffer); + last_clock_refresh = millis(); + } + } + } +} + +void Display::reInitializeDisplay() { + #define REINIT_AFTER_ms 5000 + #define AVOID_REINIT_BEFORE_AND_AFTER_FULLMINUTE_FOR_s 3 + + if (last_reinit_ts == 0) last_reinit_ts = millis(); + + if (millis() - last_reinit_ts > REINIT_AFTER_ms + && currentTime->getSeconds() < 60 - AVOID_REINIT_BEFORE_AND_AFTER_FULLMINUTE_FOR_s + && currentTime->getSeconds() > AVOID_REINIT_BEFORE_AND_AFTER_FULLMINUTE_FOR_s) { + P.begin(); + last_reinit_ts = millis(); + } +} + +void Display::print(const char *s) { P.print(s); } +void Display::print(const String s) { P.print(s); } diff --git a/src/Display.h b/src/Display.h new file mode 100644 index 0000000..01da1b1 --- /dev/null +++ b/src/Display.h @@ -0,0 +1,40 @@ +#ifndef DISPLAY_h_included +#define DISPLAY_h_included + +#include +#include "Clock.h" + +class Display { +public: + Display(Clock *real, Clock *model): + last_reinit_ts(0), + realTime(real), + modelTime(model), + displayClockNameEvery_ms(16000), + clockNameDurationTime_ms(1200), + doNotShowClockNameBeforeAndAfterMinuteChange_s(2), + displayRefresh_ms(200) + { + showRealTime(); + }; + void begin(); + void loop(); + void loopEyeAnimation(); + void reInitializeDisplay(); + void showRealTime() { showRealTimeFlag=true; currentTime = realTime; }; + void showModelTime() { showRealTimeFlag=false; currentTime = modelTime; }; + void setClockNameDurationTime_ms(unsigned long val) { clockNameDurationTime_ms=val; }; + void setClockNameDisplayEvery_ms(unsigned long val) { displayClockNameEvery_ms=val; }; + void print(const char *s); + void print(const String s); +protected: + unsigned long last_reinit_ts; + Clock *realTime, *modelTime, *currentTime; + boolean showRealTimeFlag; + unsigned long displayClockNameEvery_ms; + unsigned long clockNameDurationTime_ms; + unsigned int doNotShowClockNameBeforeAndAfterMinuteChange_s; + unsigned long displayRefresh_ms; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index ad265c0..1ebe5e1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,74 +4,55 @@ #define COMPDATE __DATE__ __TIME__ #define APP_VERSION "0.2.18" -// Button pin on the esp for selecting modes. 0 for Generic devices! -#define MODEBUTTON D3 -#define RELAY1_PIN D1 -#define RELAY2_PIN D2 -#define DISPLAY_CLK_PIN D5 -#define DISPLAY_DATA_PIN D7 -#define DISPLAY_CS_PIN D6 -#define VERTICAL_BAR_STARTS_TOP false -#define DEBUG_RELAYS false -#define DEBUG_DISPLAY false -#define STARTUP1_ANIMATION_DURATION_ms 2000 -#define STARTUP2_ANIMATION_DURATION_ms 33000 - #include #include -#include -#include -#include #include #include #include -#include "MD_RobotEyes.h" #include #include "WebServer.h" #include #include "Relays.h" #include "Clock.h" +#include "Display.h" + +// Button pin on the esp for selecting modes. 0 for Generic devices! +#define MODEBUTTON D3 +#define RELAY1_PIN D1 +#define RELAY2_PIN D2 +#define DEBUG_RELAYS false +#define DEBUG_DISPLAY false +#define STARTUP1_ANIMATION_DURATION_ms 2000 +#define STARTUP2_ANIMATION_DURATION_ms 33000 IOTAppStory IAS(COMPDATE, MODEBUTTON); String deviceName = "wemosMatrixDisplay"; String chipId; -#define HARDWARE_TYPE MD_MAX72XX::FC16_HW -#define MAX_DISPLAY_DEVICES 4 Clock realTime("real"); Clock modelTime("Fremo"); -MD_Parola P = MD_Parola(HARDWARE_TYPE, DISPLAY_DATA_PIN, DISPLAY_CLK_PIN, DISPLAY_CS_PIN, MAX_DISPLAY_DEVICES); -MD_RobotEyes E; Relays R; WebServer W(&R, &realTime, &modelTime); +Display D(&realTime, &modelTime); WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); +#define mkstring(x) #x // Field default values char *clockName = "FREMO"; char *clockSpeed_modelMsPerRealSec_String = "250"; -int clockSpeed_modelMsPerRealSec = 250; -char *relay1Pin_String = "D1"; -char *relay2Pin_String = "D2"; -int relay1Pin = D1, relay2Pin = D2; +char *relay1Pin_String = mkstring(RELAY1_PIN); +char *relay2Pin_String = mkstring(RELAY2_PIN); +int relay1Pin = RELAY1_PIN, relay2Pin = RELAY2_PIN; char *relayHoldTime_ms_String = "200"; char *relayMinOffTime_ms_String = "100"; -unsigned int displayRefresh_ms = 200; // Clock Display Config Parameter static char * displayClockNameEvery_ms_String = "16000"; static char * displayClockNameDuration_ms_String = "1200"; -static uint32_t displayClockNameEvery_ms = 16000; -static uint32_t displayClockNameDuration_ms = 1200; -static uint16_t doNotShowClockNameBeforeAndAfterMinuteChange_s = 2; - -static uint16_t hours, minutes, seconds; -static char minuteProgressIndicator; -static char timeBuffer[10]; - void setupIAS(void) { #if defined ESP8266 @@ -100,41 +81,41 @@ void setupIAS(void) { IAS.onModeButtonShortPress([]() { Serial.println(F(" If mode button is released, I will enter firmware update mode.")); Serial.println(F("*----------------------------------------------------------------------*")); - P.print("|updt"); + D.print("|updt"); }); IAS.onModeButtonLongPress([]() { Serial.println(F(" If mode button is released, I will enter configuration mode.")); Serial.println(F("*----------------------------------------------------------------------*")); - P.print("|cfg"); + D.print("|cfg"); }); IAS.onFirstBoot([]() { Serial.println(F(" Manual reset necessary after serial upload!")); Serial.println(F("*----------------------------------------------------------------------*")); - P.print("|rst"); + D.print("|rst"); ESP.reset(); }); IAS.onConfigMode([]() { - P.print(" WiFi"); + D.print(" WiFi"); delay(400); - P.print(":" + chipId); + D.print(":" + chipId); Serial.print(F("Entered config mode for Wifi, device=")); Serial.println(chipId); }); IAS.onFirmwareUpdateCheck([]() { - // P.print("chk upd"); + // D.print("chk upd"); Serial.println(F("Firmware update check")); }); IAS.onFirmwareUpdateDownload([]() { - P.print("dl+instl"); + D.print("dl+instl"); Serial.println(F("Download and install new firmware")); }); IAS.onFirmwareUpdateError([]() { - // P.print("Err fwu"); + // D.print("Err fwu"); Serial.println(F("Firmware update error")); }); @@ -148,71 +129,24 @@ void setupIAS(void) { // Please change it to at least 2 hours in production IAS.setCallHomeInterval(120); IAS.callHome(true /*SPIFFS-check*/); - clockSpeed_modelMsPerRealSec = atoi(clockSpeed_modelMsPerRealSec_String); + modelTime.setSpeed_modelMsPerRealSecond(atoi(clockSpeed_modelMsPerRealSec_String)); relay1Pin = IAS.dPinConv(relay1Pin_String); relay2Pin = IAS.dPinConv(relay2Pin_String); R.setHoldTime_ms(atoi(relayHoldTime_ms_String)); R.setMinOffTime_ms(atoi(relayMinOffTime_ms_String)); - displayClockNameEvery_ms = atoi(displayClockNameEvery_ms_String); - displayClockNameDuration_ms = atoi(displayClockNameDuration_ms_String); + D.setClockNameDisplayEvery_ms(atoi(displayClockNameEvery_ms_String)); + D.setClockNameDurationTime_ms(atoi(displayClockNameDuration_ms_String)); Serial.println(F("Configuration used:")); Serial.print(F("Relay1 Pin: ")); Serial.println(relay1Pin); Serial.print(F("Relay2 Pin: ")); Serial.println(relay2Pin); - Serial.print(F("Clock speed: ")); Serial.print(clockSpeed_modelMsPerRealSec); Serial.println(F(" model ms per real sec")); Serial.print(F("Relay hold time (ms): ")); Serial.println(relayHoldTime_ms_String); Serial.print(F("Relay min off time (ms): ")); Serial.println(relayMinOffTime_ms_String); - Serial.print(F("Clock speed (model ms per real time s): ")); Serial.println(clockSpeed_modelMsPerRealSec); - Serial.print(F("Show clock name every (ms): ")); Serial.println(displayClockNameEvery_ms); - Serial.print(F("Show clock name for (ms): ")); Serial.println(displayClockNameDuration_ms); + Serial.print(F("Clock speed (model ms per real time s): ")); Serial.println(clockSpeed_modelMsPerRealSec_String); + Serial.print(F("Show clock name every (ms): ")); Serial.println(displayClockNameEvery_ms_String); + Serial.print(F("Show clock name for (ms): ")); Serial.println(displayClockNameDuration_ms_String); } -static MD_MAX72XX *graphicDisplay = NULL; - -void setupDisplay() { - int charCode; -#if VERTICAL_BAR_STARTS_TOP - static uint8_t verticalBarFont[] = { - 1, 0x00, /* blank */ - 1, 0x01, /* 1 dot */ - 1, 0x03, /* 2 dots */ - 1, 0x07, - 1, 0x0f, - 1, 0x1f, - 1, 0x3f, - 1, 0x7f, - 1, 0xff, /* vertical bar completely set */ - }; // columns from right to left, each byte is a single column -#else - static uint8_t verticalBarFont[] = { - 1, 0x00, /* blank */ - 1, 0x80, /* 1 dot */ - 1, 0xc0, /* 2 dots */ - 1, 0xe0, - 1, 0xf0, - 1, 0xf8, - 1, 0xfc, - 1, 0xfe, - 1, 0xff, /* vertical bar completely set */ - }; // columns from right to left, each byte is a single column -#endif - static uint8_t newZero[] = {0x05, 0x3e, 0x41, 0x41, 0x41, 0x3e, 0x00}; - - P.begin(); - // P.setZoneEffect(0, true, PA_FLIP_LR); - graphicDisplay = P.getGraphicObject(); - E.begin(graphicDisplay); - - P.setIntensity(1); - for (charCode=1; charCode<=9; ++charCode) { - P.addChar(charCode, verticalBarFont+2*(charCode-1)); - } - char intro[] = {':', '-', ')', ' ', 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00}; - - // replace the 0 characters, we do not like the "slash" - P.addChar('0', newZero); - P.print(intro); -} void setupFS() { if(!SPIFFS.begin()){ @@ -224,7 +158,7 @@ void setupFS() { void setup(void) { Serial.println(F("setup():")); - setupDisplay(); + D.begin(); setupFS(); setupIAS(); W.begin(); @@ -240,97 +174,6 @@ static unsigned long lastTimeOutput_ms = 0; #define TIME_BETWEEN_REALTIME_UPDATE_ms 60000 -typedef struct -{ - char name[7]; - MD_RobotEyes::emotion_t e; - uint16_t timePause; // in milliseconds -} sampleItem_t; - -static const sampleItem_t eSeq[] = -{ - { "Nutral", MD_RobotEyes::E_NEUTRAL, 1000 }, - { "Blink" , MD_RobotEyes::E_BLINK, 1000 }, - { "Wink" , MD_RobotEyes::E_WINK, 1000 }, - { "Left" , MD_RobotEyes::E_LOOK_L, 1000 }, - { "Right" , MD_RobotEyes::E_LOOK_R, 1000 }, - { "Up" , MD_RobotEyes::E_LOOK_U, 1000 }, - { "Down" , MD_RobotEyes::E_LOOK_D, 1000 }, - { "Angry" , MD_RobotEyes::E_ANGRY, 1000 }, - { "Sad" , MD_RobotEyes::E_SAD, 1000 }, - { "Evil" , MD_RobotEyes::E_EVIL, 1000 }, - { "Evil2" , MD_RobotEyes::E_EVIL2, 1000 }, - { "Squint", MD_RobotEyes::E_SQUINT, 1000 }, - { "Dead" , MD_RobotEyes::E_DEAD, 1000 }, - { "ScanV" , MD_RobotEyes::E_SCAN_UD, 1000 }, - { "ScanH" , MD_RobotEyes::E_SCAN_LR, 1000 }, -}; -#define DISPLAY_ANIM_NAME false - -void loopStartupAnimation() { - // show startup animation - boolean animationFinished = false; - static uint32_t timeStartDelay; - static uint8_t index = ARRAY_SIZE(eSeq); - static enum { S_IDLE, S_TEXT, S_ANIM, S_PAUSE } state = S_IDLE; - - animationFinished = E.runAnimation(); - - switch (state) - { - case S_IDLE: - index++; - if (index >= ARRAY_SIZE(eSeq)) - index = 0; - P.displayClear(); - #if DISPLAY_ANIM_NAME - E.setText(eSeq[index].name); - #endif - state = S_TEXT; - break; - - case S_TEXT: // wait for the text to finish - if (animationFinished) // text animation is finished - { - E.setAnimation(eSeq[index].e, true); - state = S_ANIM; - } - break; - - case S_ANIM: // checking animation is completed - if (animationFinished) // animation is finished - { - timeStartDelay = millis(); - state = S_PAUSE; - } - break; - - case S_PAUSE: // non blocking waiting for a period between animations - if (millis() - timeStartDelay >= eSeq[index].timePause) - state = S_IDLE; - break; - - default: - state = S_IDLE; - break; - } -} - -void reInitializeDisplay() { - static unsigned long last_reinit_ts = 0; - #define REINIT_AFTER_ms 5000 - #define AVOID_REINIT_BEFORE_AND_AFTER_FULLMINUTE_FOR_s 3 - - if (last_reinit_ts == 0) last_reinit_ts = millis(); - - if (millis() - last_reinit_ts > REINIT_AFTER_ms - && seconds < 60 - AVOID_REINIT_BEFORE_AND_AFTER_FULLMINUTE_FOR_s - && seconds > AVOID_REINIT_BEFORE_AND_AFTER_FULLMINUTE_FOR_s) { - P.begin(); - last_reinit_ts = millis(); - } -} - void loop(void) { static int lastMinutes = 0; @@ -358,59 +201,22 @@ void loop(void) return; } if (millis() < firstLoop_ts + STARTUP1_ANIMATION_DURATION_ms + STARTUP2_ANIMATION_DURATION_ms) { - loopStartupAnimation(); + D.loopEyeAnimation(); return; } if (timeClientInitialized) { - hours = timeClient.getHours(); - minutes = timeClient.getMinutes(); - seconds = timeClient.getSeconds(); + realTime.setTime(timeClient.getHours(), timeClient.getMinutes(), timeClient.getSeconds()); } else { - hours = (millis() / 60 * 60 * 1000) % 24; - minutes = (millis() / 60 * 1000) % 60; - seconds = (millis() / 1000) % 60; + realTime.setTime((millis() / 60 * 60 * 1000) % 24, (millis() / 60 * 1000) % 60, (millis() / 1000) % 60); } - minuteProgressIndicator = seconds/6.7 + 1; // char code 1-8 show vertical bar - if (minuteProgressIndicator > 9) minuteProgressIndicator = 9; - snprintf(timeBuffer, 10, "%c %2d:%02d", minuteProgressIndicator, hours, minutes); - // standard procedure to display - static uint32_t last_clock_refresh = 0; - static uint32_t lastTimeClockNameShown = 0; - static boolean showingClockName = false; - - if (showingClockName) { - if (millis() - lastTimeClockNameShown > displayClockNameDuration_ms) { - // stop showingClockName - showingClockName = false; - } - } else { - if ((millis() - lastTimeClockNameShown > displayClockNameEvery_ms) - && (seconds < 60-doNotShowClockNameBeforeAndAfterMinuteChange_s) - && (seconds > doNotShowClockNameBeforeAndAfterMinuteChange_s)) { - //P.begin(); // re-initialize, that fixes display problems due to electrical relais feedbacks - reInitializeDisplay(); - P.setIntensity(2); - P.print(clockName); - lastTimeClockNameShown = millis(); - showingClockName = true; - } else { - // showing clock - if (millis() - last_clock_refresh > displayRefresh_ms) { - // P.begin(); // re-initialize, that fixes display problems due to electrical relais feedbacks - reInitializeDisplay(); - P.setIntensity(1); - P.print(timeBuffer); - last_clock_refresh = millis(); - } - } - } + D.loop(); // toggle relays - if (lastMinutes != minutes) { + if (lastMinutes != realTime.getMinutes()) { R.toggle(); - lastMinutes = minutes; + lastMinutes = realTime.getMinutes(); } R.loop();