#include "Display.h" #include #include #include #include "MD_RobotEyes.h" extern "C" { #include "user_interface.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, 1); // start at 2nd module, count starts with 0 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() { uint32_t free = system_get_free_heap_size(); #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(); Serial.print("reinit display, free="); Serial.println(free); last_reinit_ts = millis(); } } void Display::print(const char *s) { P.print(s); } void Display::print(const String s) { P.print(s); }