commit 392bb997ef2a933e0e0c63cfecb47764d8bc6566 Author: Mark van Renswoude Date: Sun Nov 26 20:22:15 2017 +0100 Initial commit Fully operational 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