From 392bb997ef2a933e0e0c63cfecb47764d8bc6566 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 26 Nov 2017 20:22:15 +0100 Subject: [PATCH] Initial commit Fully operational --- Buzzer.cpp | 53 +++++++++++++++++++++++ Buzzer.h | 10 +++++ Config.cpp | 12 ++++++ Config.h | 33 +++++++++++++++ ExposureTimer.cpp | 18 ++++++++ ExposureTimer.h | 13 ++++++ ScreenCountdown.cpp | 70 +++++++++++++++++++++++++++++++ ScreenCountdown.h | 29 +++++++++++++ ScreenManager.cpp | 38 +++++++++++++++++ ScreenManager.h | 85 +++++++++++++++++++++++++++++++++++++ ScreenMenu.cpp | 100 ++++++++++++++++++++++++++++++++++++++++++++ ScreenMenu.h | 33 +++++++++++++++ ScreenSetTime.cpp | 52 +++++++++++++++++++++++ ScreenSetTime.h | 26 ++++++++++++ UVControl.ino | 100 ++++++++++++++++++++++++++++++++++++++++++++ 15 files changed, 672 insertions(+) create mode 100644 Buzzer.cpp create mode 100644 Buzzer.h create mode 100644 Config.cpp create mode 100644 Config.h create mode 100644 ExposureTimer.cpp create mode 100644 ExposureTimer.h create mode 100644 ScreenCountdown.cpp create mode 100644 ScreenCountdown.h create mode 100644 ScreenManager.cpp create mode 100644 ScreenManager.h create mode 100644 ScreenMenu.cpp create mode 100644 ScreenMenu.h create mode 100644 ScreenSetTime.cpp create mode 100644 ScreenSetTime.h create mode 100644 UVControl.ino diff --git a/Buzzer.cpp b/Buzzer.cpp new file mode 100644 index 0000000..c9f355a --- /dev/null +++ b/Buzzer.cpp @@ -0,0 +1,53 @@ +#include "Buzzer.h" +#include +#include "Config.h" + +void buzzStartup() +{ + tone(PinBuzzer, 1000); + delay(50); + noTone(PinBuzzer); +} + + +void buzzSelect() +{ + tone(PinBuzzer, 1000); + delay(1); + noTone(PinBuzzer); +} + + +void buzzClick() +{ + tone(PinBuzzer, 1000); + delay(25); + noTone(PinBuzzer); +} + + +void buzzCompleted() +{ + for (int i = 0; i < 3; i++) + { + tone(PinBuzzer, 1000); + delay(250); + noTone(PinBuzzer); + + delay(500); + } +} + + +void buzzMemoryCleared() +{ + for (int i = 0; i < 5; i++) + { + tone(PinBuzzer, 1000); + delay(25); + noTone(PinBuzzer); + + delay(250); + } +} + diff --git a/Buzzer.h b/Buzzer.h new file mode 100644 index 0000000..bc42739 --- /dev/null +++ b/Buzzer.h @@ -0,0 +1,10 @@ +#ifndef __Buzzer +#define __Buzzer + +void buzzStartup(); +void buzzSelect(); +void buzzClick(); +void buzzCompleted(); +void buzzMemoryCleared(); + +#endif diff --git a/Config.cpp b/Config.cpp new file mode 100644 index 0000000..77acd5f --- /dev/null +++ b/Config.cpp @@ -0,0 +1,12 @@ +#include "Config.h" + +byte LCDCharArrow[8] = { + B00000, + B01000, + B01100, + B01110, + B01100, + B01000, + B00000, +}; + diff --git a/Config.h b/Config.h new file mode 100644 index 0000000..d69e990 --- /dev/null +++ b/Config.h @@ -0,0 +1,33 @@ +#ifndef __Config +#define __Config + +#include "Arduino.h" + + +static const int PinLCDRS = 7; +static const int PinLCDEN = 8; +static const int PinLCDDB4 = 9; +static const int PinLCDDB5 = 10; +static const int PinLCDDB6 = 11; +static const int PinLCDDB7 = 12; +static const int PinEncoderClock = 2; +static const int PinEncoderData = 3; +static const int PinButton = 4; +static const int PinBuzzer = 5; +static const int PinLED = 6; + +// You probably don't wanna change these without a proper review of the code, since most of it assumes 16x2 anyways +static const int LCDWidth = 16; +static const int LCDHeight = 2; + +static const int EncoderSensitivity = 4; +static const int SmallStep = 1; +static const int LargeStepTreshold = 60; +static const int LargeStep = 10; + +static const int MenuTimeout = 2000; + + +extern byte LCDCharArrow[8]; + +#endif diff --git a/ExposureTimer.cpp b/ExposureTimer.cpp new file mode 100644 index 0000000..06d4160 --- /dev/null +++ b/ExposureTimer.cpp @@ -0,0 +1,18 @@ +#include "ExposureTimer.h" +#include + +unsigned int ExposureTime = 0; +unsigned long ExposureTimerStart = 0; + +void ResetExposureTime() +{ + EEPROM.get(0, ExposureTime); +} + + +void StartExposureTimer(unsigned long currentTime) +{ + EEPROM.put(0, ExposureTime); + ExposureTimerStart = currentTime; +} + diff --git a/ExposureTimer.h b/ExposureTimer.h new file mode 100644 index 0000000..c35652d --- /dev/null +++ b/ExposureTimer.h @@ -0,0 +1,13 @@ +#ifndef __ExposureTimer +#define __ExposureTimer + +#include "Arduino.h" + +extern unsigned int ExposureTime; +extern unsigned long ExposureTimerStart; + + +void ResetExposureTime(); +void StartExposureTimer(unsigned long currentTime); + +#endif diff --git a/ScreenCountdown.cpp b/ScreenCountdown.cpp new file mode 100644 index 0000000..8779439 --- /dev/null +++ b/ScreenCountdown.cpp @@ -0,0 +1,70 @@ +#include "ScreenCountdown.h" +#include "ScreenSetTime.h" +#include "ExposureTimer.h" +#include "Config.h" +#include "Buzzer.h" + + +void ScreenCountdown::printRemainingTime() +{ + getDisplay()->setCursor(0, 1); + printTime(ExposureTime - ((getCurrentTime() - ExposureTimerStart) / 1000)); +} + + +void ScreenCountdown::onShow() +{ + mLastDisplayed = -1; + + getDisplay()->setCursor(0, 0); + getDisplay()->print("Exposing... "); + + printRemainingTime(); + digitalWrite(PinLED, HIGH); +} + + +void ScreenCountdown::onHide() +{ + digitalWrite(PinLED, LOW); +} + + +void ScreenCountdown::onButton() +{ + // TODO Confirmation? + buzzClick(); + getScreenManager()->show(); +} + + +void ScreenCountdown::onEncoder(long lastPosition, long newPosition) +{ + // TODO Allow adding / removing time? +} + + +void ScreenCountdown::onTick() +{ + int elapsed = (getCurrentTime() - ExposureTimerStart) / 1000; + + if (elapsed >= ExposureTime) + { + getDisplay()->setCursor(0, 0); + getDisplay()->print("Done! "); + + printRemainingTime(); + digitalWrite(PinLED, LOW); + + buzzCompleted(); + + ExposureTimerStart = 0; + getScreenManager()->show(); + } + else if (elapsed != mLastDisplayed) + { + printRemainingTime(); + mLastDisplayed = elapsed; + } +} + diff --git a/ScreenCountdown.h b/ScreenCountdown.h new file mode 100644 index 0000000..48cb77b --- /dev/null +++ b/ScreenCountdown.h @@ -0,0 +1,29 @@ +#ifndef __ScreenCountdown +#define __ScreenCountdown + +#include "ScreenManager.h" + +/* + * Countdown screen + * Shows the remaining time. + */ +class ScreenCountdown : public BaseScreen +{ + private: + int mLastDisplayed; + + protected: + void printRemainingTime(); + + public: + ScreenCountdown(ScreenManager* screenManager) : BaseScreen(screenManager) { } + + void onShow(); + void onHide(); + + void onButton(); + void onEncoder(long lastPosition, long newPosition); + void onTick(); +}; + +#endif diff --git a/ScreenManager.cpp b/ScreenManager.cpp new file mode 100644 index 0000000..f637161 --- /dev/null +++ b/ScreenManager.cpp @@ -0,0 +1,38 @@ +#include "ScreenManager.h" +#include "Config.h" + + +ScreenManager* BaseScreen::getScreenManager() +{ + return mScreenManager; +} + +unsigned long BaseScreen::getCurrentTime() +{ + return mScreenManager->getCurrentTime(); +} + +LiquidCrystal* BaseScreen::getDisplay() +{ + return mScreenManager->getDisplay(); +} + + +void BaseScreen::printTime(int value) +{ + String minutes = String(value / 60); + String seconds = String(value % 60); + int textLength = minutes.length() + 1 + 2; + + getDisplay()->print(minutes); + getDisplay()->print(":"); + + if (seconds.length() == 1) + getDisplay()->print("0"); + + getDisplay()->print(seconds); + + for (int space = textLength + 1; space < LCDWidth; space++) + getDisplay()->print(" "); +} + diff --git a/ScreenManager.h b/ScreenManager.h new file mode 100644 index 0000000..d0dce59 --- /dev/null +++ b/ScreenManager.h @@ -0,0 +1,85 @@ +#ifndef __ScreenManager +#define __ScreenManager + +#include + + +class ScreenManager; + + +class BaseScreen +{ + private: + ScreenManager* mScreenManager; + + protected: + ScreenManager* getScreenManager(); + unsigned long getCurrentTime(); + LiquidCrystal* getDisplay(); + + + void printTime(int value); + + public: + BaseScreen(ScreenManager* screenManager) + { + mScreenManager = screenManager; + } + + virtual void onShow() = 0; + virtual void onHide() = 0; + + virtual void onButton() = 0; + virtual void onEncoder(long lastPosition, long newPosition) = 0; + virtual void onTick() = 0; +}; + + + +class ScreenManager +{ + private: + LiquidCrystal* mDisplay; + unsigned long* mCurrentTime; + + BaseScreen* mCurrent = NULL; + + public: + ScreenManager(LiquidCrystal* display, unsigned long* currentTime) + { + mDisplay = display; + mCurrentTime = currentTime; + } + + + inline BaseScreen* getCurrent() + { + return mCurrent; + } + + + inline unsigned long getCurrentTime() + { + return *mCurrentTime; + } + + inline LiquidCrystal* getDisplay() + { + return mDisplay; + } + + + template void ScreenManager::show() + { + if (mCurrent != NULL) + { + mCurrent->onHide(); + delete(mCurrent); + } + + mCurrent = new T(this); + mCurrent->onShow(); + } +}; + +#endif diff --git a/ScreenMenu.cpp b/ScreenMenu.cpp new file mode 100644 index 0000000..e677a39 --- /dev/null +++ b/ScreenMenu.cpp @@ -0,0 +1,100 @@ +#include "ScreenMenu.h" +#include "ScreenSetTime.h" +#include "ScreenCountdown.h" +#include "ExposureTimer.h" +#include "Config.h" +#include "Buzzer.h" + + +void ScreenMenu::updateLastActivity() +{ + mLastActivity = getCurrentTime(); +} + + +void ScreenMenu::printExposureTime() +{ + getDisplay()->setCursor(0, 1); + printTime(ExposureTime); +} + + +void ScreenMenu::printMenuCursor() +{ + getDisplay()->setCursor(0, 0); + getDisplay()->write(mSelected == 0 ? (byte)0 : ' '); + + getDisplay()->setCursor(9, 0); + getDisplay()->write(mSelected == 1 ? (byte)0 : ' '); +} + + +void ScreenMenu::onShow() +{ + updateLastActivity(); + mSelected = 0; + + getDisplay()->setCursor(0, 0); + getDisplay()->print(" Start Reset "); + + printMenuCursor(); + printExposureTime(); +} + + +void ScreenMenu::onHide() +{ +} + + +void ScreenMenu::onButton() +{ + buzzClick(); + + switch (mSelected) + { + case 0: + digitalWrite(PinLED, HIGH); + + StartExposureTimer(getCurrentTime()); + getScreenManager()->show(); + break; + + case 1: + ResetExposureTime(); + getScreenManager()->show(); + } +} + + +void ScreenMenu::onEncoder(long lastPosition, long newPosition) +{ + updateLastActivity(); + + if (newPosition > lastPosition) + { + if (mSelected < 1) + { + buzzSelect(); + mSelected++; + printMenuCursor(); + } + } + else + { + if (mSelected > 0) + { + buzzSelect(); + mSelected--; + printMenuCursor(); + } + } +} + + +void ScreenMenu::onTick() +{ + if (getCurrentTime() - mLastActivity >= MenuTimeout) + getScreenManager()->show(); +} + diff --git a/ScreenMenu.h b/ScreenMenu.h new file mode 100644 index 0000000..cbf2764 --- /dev/null +++ b/ScreenMenu.h @@ -0,0 +1,33 @@ +#ifndef __ScreenMenu +#define __ScreenMenu + +#include "ScreenManager.h" + +/* + * Menu screen + * Allows starting the timer or resetting the time to the last used value. + */ +class ScreenMenu : public BaseScreen +{ + private: + int mSelected; + unsigned long mLastActivity; + + protected: + void updateLastActivity(); + + void printExposureTime(); + void printMenuCursor(); + + public: + ScreenMenu(ScreenManager* screenManager) : BaseScreen(screenManager) { } + + void onShow(); + void onHide(); + + void onButton(); + void onEncoder(long lastPosition, long newPosition); + void onTick(); +}; + +#endif diff --git a/ScreenSetTime.cpp b/ScreenSetTime.cpp new file mode 100644 index 0000000..e0c3d3c --- /dev/null +++ b/ScreenSetTime.cpp @@ -0,0 +1,52 @@ +#include "ScreenSetTime.h" +#include "ScreenMenu.h" +#include "ExposureTimer.h" +#include "Buzzer.h" +#include "Config.h" + + +void ScreenSetTime::printExposureTime() +{ + getDisplay()->setCursor(0, 1); + printTime(ExposureTime); +} + + +void ScreenSetTime::onShow() +{ + getDisplay()->setCursor(0, 0); + getDisplay()->print("Exposure time: "); + + printExposureTime(); +} + + +void ScreenSetTime::onHide() +{ +} + + +void ScreenSetTime::onButton() +{ + buzzClick(); + getScreenManager()->show(); +} + + +void ScreenSetTime::onEncoder(long lastPosition, long newPosition) +{ + buzzSelect(); + + if (newPosition > lastPosition) + ExposureTime += ExposureTime >= LargeStepTreshold ? LargeStep : SmallStep; + else if (ExposureTime > 0) + ExposureTime -= ExposureTime > LargeStepTreshold ? LargeStep : SmallStep; + + printExposureTime(); +} + + +void ScreenSetTime::onTick() +{ +} + diff --git a/ScreenSetTime.h b/ScreenSetTime.h new file mode 100644 index 0000000..fca59e9 --- /dev/null +++ b/ScreenSetTime.h @@ -0,0 +1,26 @@ +#ifndef __ScreenSetTime +#define __ScreenSetTime + +#include "ScreenManager.h" + +/* + * Time screen + * Allows changing of the exposure time. Pressing the button will switch to the menu. + */ +class ScreenSetTime : public BaseScreen +{ + protected: + void printExposureTime(); + + public: + ScreenSetTime(ScreenManager* screenManager) : BaseScreen(screenManager) { } + + void onShow(); + void onHide(); + + void onButton(); + void onEncoder(long lastPosition, long newPosition); + void onTick(); +}; + +#endif diff --git a/UVControl.ino b/UVControl.ino new file mode 100644 index 0000000..a98b0d5 --- /dev/null +++ b/UVControl.ino @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include "Config.h" +#include "ScreenManager.h" +#include "ScreenSetTime.h" +#include "Buzzer.h" +#include "ExposureTimer.h" + + +LiquidCrystal lcd(PinLCDRS, PinLCDEN, PinLCDDB4, PinLCDDB5, PinLCDDB6, PinLCDDB7); + + +// Before uploading the sketch, upload it once with ClearEEPROM defined to +// zero out the memory. +//#define ClearEEPROM +#ifndef ClearEEPROM + +ScreenManager* screenManager; +unsigned long currentTime; + +Encoder encoder(PinEncoderData, PinEncoderClock); +Bounce button = Bounce(); + + + +void setup() +{ + pinMode(PinButton, INPUT_PULLUP); + pinMode(PinBuzzer, OUTPUT); + pinMode(PinLED, OUTPUT); + + button.attach(PinButton); + button.interval(5); + + ResetExposureTime(); + + lcd.createChar(0, LCDCharArrow); + lcd.begin(LCDWidth, LCDHeight); + + screenManager = new ScreenManager(&lcd, ¤tTime); + screenManager->show(); + + buzzStartup(); +} + + +long lastPosition = 0; +bool isPressed = false; + + +void loop() +{ + currentTime = millis(); + button.update(); + + long newPosition = encoder.read(); + if (abs(newPosition - lastPosition) >= EncoderSensitivity) + { + screenManager->getCurrent()->onEncoder(lastPosition, newPosition); + lastPosition = newPosition; + } + + if (button.read() == LOW) + { + if (!isPressed) + { + screenManager->getCurrent()->onButton(); + isPressed = true; + } + } + else if (isPressed) + isPressed = false; + + screenManager->getCurrent()->onTick(); +} + +#else + +void setup() +{ + pinMode(PinBuzzer, OUTPUT); + lcd.begin(LCDWidth, LCDHeight); + + for (int i = 0 ; i < EEPROM.length() ; i++) + { + EEPROM.write(i, 0); + } + + lcd.setCursor(0, 0); + lcd.print("Memory cleared"); + + buzzMemoryCleared(); +} + +void loop() +{ +} +#endif