#pragma once #include // Support for the Backpack FeatherWing #include // Adafruit's graphics library #include #include #include "ntp_time.h" #include "bme.h" #include "timer.h" #define DISPLAY_ADDRESS 0x70 #define MILLISECONDS(value) value*TASK_MILLISECOND #define SECONDS(value) value*TASK_SECOND #define MINUTES(value) value*TASK_MINUTE #define DISPLAY_SENSOR_ITERATIONS 2+1 #define DISPLAY_DELAY (SECONDS(2)) namespace Display { // Create display object Adafruit_7segment clockDisplay = Adafruit_7segment(); struct { operator Task&() { return *perm; } bool activate(Task& task) { if (task.isEnabled()) return false; if (temp) temp->cancel(); if (perm) perm->cancel(); if (task.getIterations() == TASK_FOREVER) { perm = &task; return task.enable(); } else { temp = &task; return task.restart(); } } void restorePerm() { if (temp && temp->isEnabled()) return; // don't enable perm when triggered by another temp on top of exsting temp if (perm) perm->enable(); } bool isPerm(Task& task) { return perm == &task; } private: Task *temp = nullptr, *perm = nullptr; } displayTask; void displayTime(); void displayDate(); void displayTemp(); void displayHumidity(); void drawTime(); void drawColon(bool); Task tDisplayTime(MILLISECONDS(500), TASK_FOREVER, displayTime, &ts, false, []() { drawTime(); return true; }, []() { drawColon(false); }); Task tDisplaySensor(SECONDS(5), DISPLAY_SENSOR_ITERATIONS, nullptr, &ts, false, []() { tDisplaySensor.setCallback(displayTemp); return true; }, []() { tDisplaySensor.setIterations(DISPLAY_SENSOR_ITERATIONS); displayTask.restorePerm(); }); Task tDisplayDate(SECONDS(5), TASK_ONCE + 1, displayDate, &ts, false, nullptr, []() { displayTask.restorePerm(); }); Task tDisplayTimer(SECONDS(10), TASK_ONCE + 1, []{ clockDisplay.print(timer, DEC); clockDisplay.writeDisplay(); }, &ts, false, nullptr, []{ if (!displayTask.isPerm(tDisplayTimer)) displayTask.restorePerm(); }); Task tTimer(MINUTES(1), TASK_FOREVER, []{ static constexpr uint8 threshold = 16; timer.decrease(); if (timer.atBeginning()) { if (timer <= threshold) tDisplayTimer.setIterations(TASK_FOREVER); displayTask.activate(tDisplayTimer); } else if (timer == threshold) { tDisplayTimer.setIterations(TASK_FOREVER); displayTask.activate(tDisplayTimer); } }, &ts, false, []{ timer.start(); return true; }, []{ tDisplayTimer.setIterations(TASK_ONCE + 1); displayTask.activate(tDisplayTime); }); struct HourFormat : public CallbackAware { operator bool() { return format24; } void operator=(bool value) { format24 = value; if (displayTask.isPerm(tDisplayTime)) drawTime(); if (callback) callback(); } private: bool format24 = false; } hourFormat24; struct Brightness : public CallbackAware { static constexpr uint8 MIN = 0; static constexpr uint8 MAX = 15; static constexpr uint8 DAY = 11; static constexpr uint8 NIGHT = MIN; void operator=(uint8 value) { current = value % (MAX + 1); clockDisplay.setBrightness(current); if (callback) callback(); } operator uint8() { return current; } private: uint8 current = NIGHT; } brightness; void drawTime() { int displayHour = hourFormat24 ? hour() : hourFormat12(); int displayMinute = minute(); int displayValue = displayHour * 100 + displayMinute; // Print the time on the display clockDisplay.print(displayValue, DEC); // Add zero padding when in 24 hour mode and it's midnight. // In this case the print function above won't have leading 0's // which can look confusing. Go in and explicitly add these zeros. if (displayHour == 0) { clockDisplay.writeDigitNum(1, 0); if (displayMinute < 10) { clockDisplay.writeDigitNum(3, 0); } } } void drawColon(bool colonOn) { if (colonOn) { static int currentHour = -1; if (currentHour != hour()) { currentHour = hour(); if (currentHour == 4) { Ntp::tUpdateTime.restart(); } if (currentHour == 7) { brightness = Brightness::DAY; Wifi::tConnect.enable(); } if (currentHour == 17) { brightness = Brightness::NIGHT; } } static int currentMin = -1; if (currentMin != minute()) { currentMin = minute(); drawTime(); } } clockDisplay.drawColon(colonOn); } void displayTime() { static bool colonOn = true; drawColon(colonOn); clockDisplay.writeDisplay(); colonOn = !colonOn; } void displayTemp() { clockDisplay.printFloat(Bme::data.temp, 2); clockDisplay.writeDigitAscii(4, 'c'); clockDisplay.writeDisplay(); tDisplaySensor.setCallback(&displayHumidity); } void displayHumidity() { clockDisplay.printFloat(Bme::data.humidity, 2); clockDisplay.writeDigitAscii(4, '%'); clockDisplay.writeDisplay(); tDisplaySensor.setCallback(&displayTemp); } void displayValue(uint8 value) { tDisplayTime.disable(); clockDisplay.print(value, HEX); clockDisplay.writeDisplay(); tDisplayTime.enableDelayed(DISPLAY_DELAY); } void displayText(const char c[]) { tDisplayTime.disable(); clockDisplay.println(c); clockDisplay.writeDisplay(); tDisplayTime.enableDelayed(DISPLAY_DELAY); } unordered_map daysOfWeek = { {1, "So"}, {2, "Mo"}, {3, "Di"}, {4, "Mi"}, {5, "Do"}, {6, "Fr"}, {7, "Sa"}, }; void displayDate() { char date[5]; sprintf(date, "%2s%2u", daysOfWeek[weekday()], day()); clockDisplay.println(date); clockDisplay.writeDisplay(); } void setup() { clockDisplay.begin(DISPLAY_ADDRESS); clockDisplay.setBrightness(brightness); displayTask.activate(tDisplayTime); } }