Merge branch 'feature/platformio' into develop

This commit is contained in:
Mark van Renswoude 2017-12-08 22:32:59 +01:00
commit 3035fd189b
41 changed files with 1238 additions and 532 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.sublime-workspace
.pioenvs
.piolibdeps

View File

@ -1,53 +0,0 @@
#include "Buzzer.h"
#include <Arduino.h>
#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);
}
}

View File

@ -1,10 +0,0 @@
#ifndef __Buzzer
#define __Buzzer
void buzzStartup();
void buzzSelect();
void buzzClick();
void buzzCompleted();
void buzzMemoryCleared();
#endif

View File

@ -1,12 +0,0 @@
#include "Config.h"
byte LCDCharArrow[8] = {
B00000,
B01000,
B01100,
B01110,
B01100,
B01000,
B00000,
};

View File

@ -1,33 +0,0 @@
#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

View File

@ -1,18 +0,0 @@
#include "ExposureTimer.h"
#include <EEPROM.h>
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;
}

View File

@ -1,13 +0,0 @@
#ifndef __ExposureTimer
#define __ExposureTimer
#include "Arduino.h"
extern unsigned int ExposureTime;
extern unsigned long ExposureTimerStart;
void ResetExposureTime();
void StartExposureTimer(unsigned long currentTime);
#endif

View File

@ -1,70 +0,0 @@
#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<ScreenSetTime>();
}
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<ScreenSetTime>();
}
else if (elapsed != mLastDisplayed)
{
printRemainingTime();
mLastDisplayed = elapsed;
}
}

View File

@ -1,29 +0,0 @@
#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

View File

@ -1,38 +0,0 @@
#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(" ");
}

View File

@ -1,100 +0,0 @@
#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<ScreenCountdown>();
break;
case 1:
ResetExposureTime();
getScreenManager()->show<ScreenSetTime>();
}
}
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<ScreenSetTime>();
}

View File

@ -1,33 +0,0 @@
#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

View File

@ -1,52 +0,0 @@
#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<ScreenMenu>();
}
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()
{
}

View File

@ -1,26 +0,0 @@
#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

View File

@ -0,0 +1,9 @@
{
"folders":
[
{
"path": ".",
"file_exclude_patterns": ["*.sublime-project"]
}
]
}

1
build.ps1 Normal file
View File

@ -0,0 +1 @@
& platformio run

24
platformio.ini Normal file
View File

@ -0,0 +1,24 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html
[env:attiny85]
platform = atmelavr
board = pro16MHzatmega328
framework = arduino
;upload_protocol = stk500v1
;upload_flags = -P$UPLOAD_PORT -b$UPLOAD_SPEED
;upload_speed = 19200
board_f_cpu = 16000000L
lib_deps =
Bounce2
Encoder

57
src/Buzzer.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "buzzer.h"
#include <Arduino.h>
#include "config.h"
#include "state.h"
void Buzzer::playNote(uint16_t frequency, uint16_t duration)
{
tone(PinBuzzer, frequency);
delay(duration);
noTone(PinBuzzer);
}
void Buzzer::startup()
{
if (GetBuzzer() == BuzzerSetting::CompletedButtonStartup)
playNote(1000, 50);
}
void Buzzer::select()
{
if (GetBuzzer() <= BuzzerSetting::CompletedButton)
playNote(1000, 1);
}
void Buzzer::click()
{
if (GetBuzzer() <= BuzzerSetting::CompletedButton)
playNote(1000, 25);
}
void Buzzer::completed()
{
if (GetBuzzer() <= BuzzerSetting::Completed)
{
for (int i = 0; i < 3; i++)
{
playNote(1000, 250);
delay(500);
}
}
}
void Buzzer::memoryCleared()
{
for (int i = 0; i < 5; i++)
{
playNote(1000, 25);
delay(250);
}
}

19
src/Buzzer.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __buzzer
#define __buzzer
#include <Arduino.h>
class Buzzer
{
protected:
static void playNote(uint16_t frequency, uint16_t duration);
public:
static void startup();
static void select();
static void click();
static void completed();
static void memoryCleared();
};
#endif

71
src/Config.cpp Normal file
View File

@ -0,0 +1,71 @@
#include "Config.h"
uint8_t LCDCharArrowRightMap[8] = {
B00000,
B01000,
B01100,
B01110,
B01100,
B01000,
B00000,
};
uint8_t LCDCharArrowLeftMap[8] = {
B00000,
B00100,
B01100,
B11100,
B01100,
B00100,
B00000,
};
uint8_t LCDCharArrowRightHollowMap[8] = {
B00000,
B01000,
B00100,
B00010,
B00100,
B01000,
B00000,
};
uint8_t LCDCharArrowLeftHollowMap[8] = {
B00000,
B00100,
B01000,
B10000,
B01000,
B00100,
B00000,
};
uint8_t LCDCharUpDownMap[8] = {
B00000,
B00100,
B01110,
B00000,
B01110,
B00100,
B00000,
};
uint8_t LCDCharUpMap[8] = {
B00000,
B00100,
B01110,
B00000,
B00000,
B00000,
B00000,
};
uint8_t LCDCharDownMap[8] = {
B00000,
B00000,
B00000,
B00000,
B01110,
B00100,
B00000,
};

48
src/Config.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef __Config
#define __Config
#include <Arduino.h>
const uint8_t PinLCDRS = 7;
const uint8_t PinLCDEN = 8;
const uint8_t PinLCDDB4 = 9;
const uint8_t PinLCDDB5 = 10;
const uint8_t PinLCDDB6 = 11;
const uint8_t PinLCDDB7 = 12;
const uint8_t PinEncoderClock = 2;
const uint8_t PinEncoderData = 3;
const uint8_t PinButton = 4;
const uint8_t PinBuzzer = 5;
const uint8_t PinLED = 6;
// Note: an LCD size of at least 16x2 is assumed for all text to fit
const uint8_t LCDWidth = 16;
const uint8_t LCDHeight = 2;
const uint8_t EncoderSensitivity = 4;
const uint8_t SmallStep = 1;
const uint8_t LargeStepTreshold = 60;
const uint8_t LargeStep = 10;
const uint8_t IntensityStep = 5;
const uint32_t DefaultExposureTime = 60;
const uint8_t DefaultExposureIntensity = 100;
const uint8_t LCDCharArrowRight = 0;
const uint8_t LCDCharArrowLeft = 1;
const uint8_t LCDCharArrowRightHollow = 2;
const uint8_t LCDCharArrowLeftHollow = 3;
const uint8_t LCDCharUpDown = 4;
const uint8_t LCDCharUp = 5;
const uint8_t LCDCharDown = 6;
extern uint8_t LCDCharArrowRightMap[8];
extern uint8_t LCDCharArrowLeftMap[8];
extern uint8_t LCDCharArrowRightHollowMap[8];
extern uint8_t LCDCharArrowLeftHollowMap[8];
extern uint8_t LCDCharUpDownMap[8];
extern uint8_t LCDCharUpMap[8];
extern uint8_t LCDCharDownMap[8];
#endif

153
src/display.cpp Normal file
View File

@ -0,0 +1,153 @@
#include "display.h"
#include "config.h"
void LCDPrintLine(LiquidCrystal* display, uint8_t y, const char* value, uint8_t margin)
{
display->setCursor(margin, y);
uint8_t width = LCDWidth - (2 * margin);
if (value != NULL)
{
uint8_t length = strlen(value);
if (length >= width)
{
char* character = (char*)value;
for (uint8_t i = 0; i < width; i++)
{
display->write(byte(*character));
character++;
}
}
else
{
display->print(value);
width -= length;
while (width > 0)
{
display->write(' ');
width--;
}
}
}
else
{
for (uint8_t i = 0; i < width; i++)
display->write(' ');
}
}
void LCDPrintLineCentered(LiquidCrystal* display, uint8_t y, const char* value, uint8_t margin)
{
display->setCursor(margin, y);
uint8_t width = LCDWidth - (2 * margin);
if (value != NULL)
{
uint8_t length = strlen(value);
if (length >= width)
{
char* character = (char*)value;
for (uint8_t i = 0; i < width; i++)
{
display->write(byte(*character));
character++;
}
}
else
{
uint8_t offset = (width - length) / 2;
width -= offset;
for (uint8_t i = 0; i < offset; i++)
display->write(' ');
display->print(value);
width -= length;
while (width > 0)
{
display->write(' ');
width--;
}
}
}
else
{
for (uint8_t i = 0; i < width; i++)
display->write(' ');
}
}
const char* UniqueString(const char* value)
{
char* result = new char[strlen(value) + 1];
return strcpy(result, value);
}
#define ASCII0 0x30
const char* FormatTime(uint16_t time)
{
uint16_t minutes = time / 60;
uint8_t seconds = time % 60;
char* value = new char[9];
itoa(minutes, value, 10);
uint8_t length = strlen(value);
value[length] = ':'; length++;
value[length] = (seconds / 10) + ASCII0; length++;
value[length] = (seconds % 10) + ASCII0; length++;
value[length] = 0;
return value;
}
const char* FormatPercentage(uint8_t percentage)
{
char* value = new char[6];
itoa(percentage, value, 10);
uint8_t length = strlen(value);
value[length] = '%';
value[length + 1] = 0;
return value;
}
const char* FormatPercentageFixedWidth(uint8_t percentage)
{
char* value = new char[5];
if (percentage > 99)
{
value[0] = '1';
value[1] = '0';
}
else
{
value[0] = ' ';
value[1] = percentage > 9 ? (percentage / 10) + ASCII0 : ' ';
}
value[2] = (percentage % 10) + ASCII0;
value[3] = '%';
value[4] = 0;
return value;
}

16
src/display.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef __display
#define __display
#include <Arduino.h>
#include <LiquidCrystal.h>
void LCDPrintLine(LiquidCrystal* display, uint8_t y, const char* value, uint8_t margin = 0);
void LCDPrintLineCentered(LiquidCrystal* display, uint8_t y, const char* value, uint8_t margin = 0);
const char* UniqueString(const char* value);
const char* FormatTime(uint16_t time);
const char* FormatPercentage(uint8_t percentage);
const char* FormatPercentageFixedWidth(uint8_t percentage);
#endif

View File

@ -1,24 +1,24 @@
#include <Arduino.h>
#include <Bounce2.h> #include <Bounce2.h>
#include <Encoder.h> #include <Encoder.h>
#include <LiquidCrystal.h> #include <LiquidCrystal.h>
#include <EEPROM.h> #include "config.h"
#include "Config.h" #include "screen.h"
#include "ScreenManager.h" #include "screen/menu.h"
#include "ScreenSetTime.h" #include "buzzer.h"
#include "Buzzer.h" #include "state.h"
#include "ExposureTimer.h"
LiquidCrystal lcd(PinLCDRS, PinLCDEN, PinLCDDB4, PinLCDDB5, PinLCDDB6, PinLCDDB7); LiquidCrystal lcd(PinLCDRS, PinLCDEN, PinLCDDB4, PinLCDDB5, PinLCDDB6, PinLCDDB7);
// Before uploading the sketch, upload it once with ClearEEPROM defined to // Before uploading the sketch, upload it once with ResetEEPROM defined to
// zero out the memory. // write the default values to the EEPROM
//#define ClearEEPROM //#define ResetEEPROM
#ifndef ClearEEPROM #ifndef ResetEEPROM
ScreenManager* screenManager; ScreenManager* screenManager;
unsigned long currentTime; uint32_t currentTime;
Encoder encoder(PinEncoderData, PinEncoderClock); Encoder encoder(PinEncoderData, PinEncoderClock);
Bounce button = Bounce(); Bounce button = Bounce();
@ -34,19 +34,25 @@ void setup()
button.attach(PinButton); button.attach(PinButton);
button.interval(5); button.interval(5);
ResetExposureTime(); LoadSettings();
lcd.createChar(0, LCDCharArrow); lcd.createChar(LCDCharArrowRight, LCDCharArrowRightMap);
lcd.createChar(LCDCharArrowLeft, LCDCharArrowLeftMap);
lcd.createChar(LCDCharArrowRightHollow, LCDCharArrowRightHollowMap);
lcd.createChar(LCDCharArrowLeftHollow, LCDCharArrowLeftHollowMap);
lcd.createChar(LCDCharUpDown, LCDCharUpDownMap);
lcd.createChar(LCDCharUp, LCDCharUpMap);
lcd.createChar(LCDCharDown, LCDCharDownMap);
lcd.begin(LCDWidth, LCDHeight); lcd.begin(LCDWidth, LCDHeight);
screenManager = new ScreenManager(&lcd, &currentTime); screenManager = new ScreenManager(&lcd, &currentTime);
screenManager->show<ScreenSetTime>(); screenManager->show<MenuScreen>();
buzzStartup(); Buzzer::startup();
} }
long lastPosition = 0; int32_t lastPosition = 0;
bool isPressed = false; bool isPressed = false;
@ -55,7 +61,7 @@ void loop()
currentTime = millis(); currentTime = millis();
button.update(); button.update();
long newPosition = encoder.read(); int32_t newPosition = encoder.read();
if (abs(newPosition - lastPosition) >= EncoderSensitivity) if (abs(newPosition - lastPosition) >= EncoderSensitivity)
{ {
screenManager->getCurrent()->onEncoder(lastPosition, newPosition); screenManager->getCurrent()->onEncoder(lastPosition, newPosition);
@ -78,23 +84,31 @@ void loop()
#else #else
#include <EEPROM.h>
void setup() void setup()
{ {
pinMode(PinBuzzer, OUTPUT); pinMode(PinBuzzer, OUTPUT);
lcd.begin(LCDWidth, LCDHeight); lcd.begin(LCDWidth, LCDHeight);
for (int i = 0 ; i < EEPROM.length() ; i++) for (uint16_t i = 0 ; i < EEPROM.length() ; i++)
{ {
EEPROM.write(i, 0); EEPROM.update(i, 0);
} }
SetExposureTime(DefaultExposureTime);
SetExposureIntensity(DefaultExposureIntensity);
SetBuzzer(BuzzerSetting::CompletedButtonStartup);
SaveSettings();
lcd.setCursor(0, 0); lcd.setCursor(0, 0);
lcd.print("Memory cleared"); lcd.print("Memory cleared");
buzzMemoryCleared(); Buzzer::memoryCleared();
} }
void loop() void loop()
{ {
} }
#endif #endif

41
src/menu/intensity.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "intensity.h"
#include "config.h"
#include "state.h"
#include "display.h"
#include "buzzer.h"
const char* IntensityMenuItem::getTitle()
{
return UniqueString("Intensity");
}
const char* IntensityMenuItem::getValue()
{
return FormatPercentageFixedWidth(GetExposureIntensity());
}
bool IntensityMenuItem::canIncrement()
{
return GetExposureIntensity() < 100;
}
bool IntensityMenuItem::canDecrement()
{
return GetExposureIntensity() > IntensityStep;
}
void IntensityMenuItem::incrementValue()
{
SetExposureIntensity(GetExposureIntensity() + IntensityStep);
}
void IntensityMenuItem::decrementValue()
{
SetExposureIntensity(GetExposureIntensity() - IntensityStep);
}

22
src/menu/intensity.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef __intensitymenuitem
#define __intensitymenuitem
#include "screen/menu.h"
class IntensityMenuItem : public MenuItem
{
public:
IntensityMenuItem() : MenuItem() { }
const char* getTitle();
const char* getValue();
bool editable() { return true; }
bool canIncrement();
bool canDecrement();
void incrementValue();
void decrementValue();
};
#endif

56
src/menu/sound.cpp Normal file
View File

@ -0,0 +1,56 @@
#include "sound.h"
#include "config.h"
#include "state.h"
#include "display.h"
#include "buzzer.h"
const char* SoundMenuItem::getTitle()
{
return UniqueString("Sound");
}
const char* SoundMenuItem::getValue()
{
switch (GetBuzzer())
{
case BuzzerSetting::CompletedButtonStartup:
return UniqueString("All");
case BuzzerSetting::CompletedButton:
return UniqueString("Alarm/button");
case BuzzerSetting::Completed:
return UniqueString("Alarm only");
case BuzzerSetting::None:
return UniqueString("None");
}
return NULL;
}
bool SoundMenuItem::canIncrement()
{
return GetBuzzer() > BuzzerSetting::First;
}
bool SoundMenuItem::canDecrement()
{
return GetBuzzer() < BuzzerSetting::Last;
}
void SoundMenuItem::incrementValue()
{
SetBuzzer((BuzzerSetting)((uint8_t)GetBuzzer() - 1));
}
void SoundMenuItem::decrementValue()
{
SetBuzzer((BuzzerSetting)((uint8_t)GetBuzzer() + 1));
}

22
src/menu/sound.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef __soundmenuitem
#define __soundmenuitem
#include "screen/menu.h"
class SoundMenuItem : public MenuItem
{
public:
SoundMenuItem() : MenuItem() { }
const char* getTitle();
const char* getValue();
bool editable() { return true; }
bool canIncrement();
bool canDecrement();
void incrementValue();
void decrementValue();
};
#endif

42
src/menu/start.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "start.h"
#include "state.h"
#include "display.h"
#include "screen/countdown.h"
const char* StartMenuItem::getTitle()
{
return UniqueString("Start");
}
const char* StartMenuItem::getValue()
{
const char* time = FormatTime(GetExposureTime());
const char* intensity = FormatPercentage(GetExposureIntensity());
uint8_t timeLength = strlen(time);
uint8_t intensityLength = strlen(intensity);
char* value = new char[timeLength + 3 + intensityLength + 1];
strcpy(value, time);
delete[] time;
uint8_t offset = timeLength;
value[offset] = ' '; offset++;
value[offset] = '@'; offset++;
value[offset] = ' '; offset++;
strcpy(value + offset, intensity);
delete[] intensity;
return value;
}
void StartMenuItem::execute(ScreenManager* screenManager, uint32_t currentTime)
{
StartExposureTimer(currentTime);
screenManager->show<CountdownScreen>();
}

19
src/menu/start.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __startmenuitem
#define __startmenuitem
#include "screen/menu.h"
class StartMenuItem : public MenuItem
{
public:
StartMenuItem() : MenuItem() { }
const char* getTitle();
const char* getValue();
bool editable() { return false; }
void execute(ScreenManager* screenManager, uint32_t currentTime);
};
#endif

42
src/menu/time.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "time.h"
#include "config.h"
#include "state.h"
#include "display.h"
#include "buzzer.h"
const char* TimeMenuItem::getTitle()
{
return UniqueString("Time");
}
const char* TimeMenuItem::getValue()
{
return FormatTime(GetExposureTime());
}
bool TimeMenuItem::canIncrement()
{
return GetExposureTime() < (uint16_t)-1;
}
bool TimeMenuItem::canDecrement()
{
return GetExposureTime() > SmallStep;
}
void TimeMenuItem::incrementValue()
{
uint16_t exposureTime = GetExposureTime();
SetExposureTime(exposureTime + (exposureTime >= LargeStepTreshold ? LargeStep : SmallStep));
}
void TimeMenuItem::decrementValue()
{
uint16_t exposureTime = GetExposureTime();
SetExposureTime(exposureTime - (exposureTime > LargeStepTreshold ? LargeStep : SmallStep));
}

22
src/menu/time.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef __timemenuitem
#define __timemenuitem
#include "screen/menu.h"
class TimeMenuItem : public MenuItem
{
public:
TimeMenuItem() : MenuItem() { }
const char* getTitle();
const char* getValue();
bool editable() { return true; }
bool canIncrement();
bool canDecrement();
void incrementValue();
void decrementValue();
};
#endif

19
src/screen.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "screen.h"
#include "config.h"
ScreenManager* BaseScreen::getScreenManager()
{
return mScreenManager;
}
uint32_t BaseScreen::getCurrentTime()
{
return mScreenManager->getCurrentTime();
}
LiquidCrystal* BaseScreen::getDisplay()
{
return mScreenManager->getDisplay();
}

View File

@ -1,9 +1,8 @@
#ifndef __ScreenManager #ifndef __screen
#define __ScreenManager #define __screen
#include <LiquidCrystal.h> #include <LiquidCrystal.h>
class ScreenManager; class ScreenManager;
@ -14,23 +13,22 @@ class BaseScreen
protected: protected:
ScreenManager* getScreenManager(); ScreenManager* getScreenManager();
unsigned long getCurrentTime(); uint32_t getCurrentTime();
LiquidCrystal* getDisplay(); LiquidCrystal* getDisplay();
void printTime(int value);
public: public:
BaseScreen(ScreenManager* screenManager) BaseScreen(ScreenManager* screenManager)
{ {
mScreenManager = screenManager; mScreenManager = screenManager;
} }
virtual ~BaseScreen() {}
virtual void onShow() = 0; virtual void onShow() = 0;
virtual void onHide() = 0; virtual void onHide() = 0;
virtual void onButton() = 0; virtual void onButton() = 0;
virtual void onEncoder(long lastPosition, long newPosition) = 0; virtual void onEncoder(int32_t lastPosition, int32_t newPosition) = 0;
virtual void onTick() = 0; virtual void onTick() = 0;
}; };
@ -40,12 +38,12 @@ class ScreenManager
{ {
private: private:
LiquidCrystal* mDisplay; LiquidCrystal* mDisplay;
unsigned long* mCurrentTime; uint32_t* mCurrentTime;
BaseScreen* mCurrent = NULL; BaseScreen* mCurrent = NULL;
public: public:
ScreenManager(LiquidCrystal* display, unsigned long* currentTime) ScreenManager(LiquidCrystal* display, uint32_t* currentTime)
{ {
mDisplay = display; mDisplay = display;
mCurrentTime = currentTime; mCurrentTime = currentTime;
@ -58,7 +56,7 @@ class ScreenManager
} }
inline unsigned long getCurrentTime() inline uint32_t getCurrentTime()
{ {
return *mCurrentTime; return *mCurrentTime;
} }
@ -69,12 +67,12 @@ class ScreenManager
} }
template<class T> void ScreenManager::show() template<class T> void show()
{ {
if (mCurrent != NULL) if (mCurrent != NULL)
{ {
mCurrent->onHide(); mCurrent->onHide();
delete(mCurrent); delete mCurrent;
} }
mCurrent = new T(this); mCurrent = new T(this);

78
src/screen/countdown.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "countdown.h"
#include "screen/menu.h"
#include "display.h"
#include "state.h"
#include "config.h"
#include "buzzer.h"
inline uint32_t intDivCeil(uint32_t x, uint32_t y)
{
return x / y + (x % y != 0);
}
void CountdownScreen::printRemainingTime()
{
LCDPrintLineCentered(getDisplay(), 1, FormatTime(mLastDisplayed));
}
void CountdownScreen::onShow()
{
LCDPrintLineCentered(getDisplay(), 0, "Exposing...");
uint32_t remaining = GetExposureTimeRemaining(getCurrentTime());
mLastDisplayed = intDivCeil(remaining, 1000);
printRemainingTime();
analogWrite(PinLED, map(GetExposureIntensity(), 0, 100, 0, 255));
}
void CountdownScreen::onHide()
{
digitalWrite(PinLED, LOW);
}
void CountdownScreen::onButton()
{
// TODO Confirmation?
Buzzer::click();
getScreenManager()->show<MenuScreen>();
}
void CountdownScreen::onEncoder(int32_t lastPosition, int32_t newPosition)
{
// TODO Allow adding / removing time?
}
void CountdownScreen::onTick()
{
uint32_t remaining = GetExposureTimeRemaining(getCurrentTime());
remaining = intDivCeil(remaining, 1000);
if (remaining == 0)
{
mLastDisplayed = 0;
LCDPrintLineCentered(getDisplay(), 0, "Done!");
printRemainingTime();
digitalWrite(PinLED, LOW);
Buzzer::completed();
ResetExposureTimer();
getScreenManager()->show<MenuScreen>();
}
else if (remaining != mLastDisplayed)
{
mLastDisplayed = remaining;
printRemainingTime();
}
}

29
src/screen/countdown.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef __countdown
#define __countdown
#include "screen.h"
/*
* Countdown screen
* Shows the remaining time.
*/
class CountdownScreen : public BaseScreen
{
private:
uint32_t mLastDisplayed;
protected:
void printRemainingTime();
public:
CountdownScreen(ScreenManager* screenManager) : BaseScreen(screenManager) { }
void onShow();
void onHide();
void onButton();
void onEncoder(int32_t lastPosition, int32_t newPosition);
void onTick();
};
#endif

195
src/screen/menu.cpp Normal file
View File

@ -0,0 +1,195 @@
#include "screen/menu.h"
#include "config.h"
#include "buzzer.h"
#include "display.h"
#include "state.h"
#include "menu/start.h"
#include "menu/time.h"
#include "menu/intensity.h"
#include "menu/sound.h"
MenuScreen::MenuScreen(ScreenManager* screenManager) : BaseScreen(screenManager)
{
mCount = 4;
mItems = new MenuItem*[mCount];
mItems[0] = new StartMenuItem();
mItems[1] = new TimeMenuItem();
mItems[2] = new IntensityMenuItem();
mItems[3] = new SoundMenuItem();
}
MenuScreen::~MenuScreen()
{
for (uint8_t i = 0; i < mCount; i++)
delete mItems[i];
delete[] mItems;
}
void MenuScreen::onShow()
{
printFullUpdate();
}
void MenuScreen::onHide()
{
}
void MenuScreen::printFullUpdate()
{
printTitle();
printScrollIndicators();
printValue();
}
void MenuScreen::printTitle()
{
const char* title = mItems[mSelected]->getTitle();
LCDPrintLineCentered(getDisplay(), 0, title, 1);
if (title != NULL)
delete[] title;
}
void MenuScreen::printScrollIndicators()
{
LiquidCrystal* display = getDisplay();
display->setCursor(0, 0);
if (mSelected > 0)
display->write(mEditing ? LCDCharArrowLeftHollow : LCDCharArrowLeft);
else
display->write(' ');
display->setCursor(LCDWidth - 1, 0);
if (mSelected < mCount - 1)
display->write(mEditing ? LCDCharArrowRightHollow : LCDCharArrowRight);
else
display->write(' ');
}
void MenuScreen::printValue()
{
LiquidCrystal* display = getDisplay();
const char* value = mItems[mSelected]->getValue();
if (mEditing && value != NULL)
{
uint8_t valueLength = strlen(value);
char* editingValue = new char[valueLength + 5];
editingValue[0] = ' ';
editingValue[1] = ' ';
strcpy(editingValue + 2, value);
editingValue[valueLength + 2] = ' ';
bool canIncrement = mItems[mSelected]->canIncrement();
bool canDecrement = mItems[mSelected]->canDecrement();
if (canIncrement && canDecrement)
editingValue[valueLength + 3] = LCDCharUpDown;
else if (canIncrement)
editingValue[valueLength + 3] = LCDCharUp;
else if (canDecrement)
editingValue[valueLength + 3] = LCDCharDown;
else
editingValue[valueLength + 3] = ' ';
editingValue[valueLength + 4] = 0;
LCDPrintLineCentered(display, 1, editingValue);
delete[] editingValue;
}
else
LCDPrintLineCentered(display, 1, value);
if (value != NULL)
delete[] value;
}
void MenuScreen::onButton()
{
if (mItems[mSelected]->editable())
{
Buzzer::select();
if (mEditing)
SaveSettings();
mEditing = !mEditing;
printScrollIndicators();
printValue();
}
else
{
mItems[mSelected]->execute(getScreenManager(), getCurrentTime());
}
}
void MenuScreen::onEncoder(int32_t lastPosition, int32_t newPosition)
{
if (mEditing)
{
if (newPosition > lastPosition)
{
if (mItems[mSelected]->canIncrement())
{
mItems[mSelected]->incrementValue();
Buzzer::select();
}
}
else
{
if (mItems[mSelected]->canDecrement())
{
mItems[mSelected]->decrementValue();
Buzzer::select();
}
}
printValue();
}
else
{
if (newPosition > lastPosition)
{
if (mSelected < mCount - 1)
{
Buzzer::select();
mSelected++;
printFullUpdate();
}
}
else
{
if (mSelected > 0)
{
Buzzer::select();
mSelected--;
printFullUpdate();
}
}
}
}
void MenuScreen::onTick()
{
}

53
src/screen/menu.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef __menuscreen
#define __menuscreen
#include "screen.h"
class MenuItem
{
public:
virtual ~MenuItem() { }
virtual const char* getTitle() = 0;
virtual const char* getValue() { return NULL; }
virtual bool editable() { return false; }
// Editable = true
virtual bool canIncrement() { return true; }
virtual bool canDecrement() { return true; }
virtual void incrementValue() { }
virtual void decrementValue() { }
// Editable = false
virtual void execute(ScreenManager* screenManager, uint32_t currentTime) { }
};
class MenuScreen : public BaseScreen
{
private:
uint8_t mCount;
MenuItem** mItems;
uint8_t mSelected = 0;
bool mEditing = false;
protected:
void printFullUpdate();
void printTitle();
void printScrollIndicators();
void printValue();
public:
MenuScreen(ScreenManager* screenManager);
~MenuScreen();
void onShow();
void onHide();
void onButton();
void onEncoder(int32_t lastPosition, int32_t newPosition);
void onTick();
};
#endif

105
src/state.cpp Normal file
View File

@ -0,0 +1,105 @@
#include "state.h"
#include "config.h"
#include <EEPROM.h>
uint16_t ExposureTime = DefaultExposureTime;
uint8_t ExposureIntensity = DefaultExposureIntensity;
uint32_t ExposureTimerStart = 0;
BuzzerSetting Buzzer = BuzzerSetting::CompletedButtonStartup;
uint16_t GetExposureTime()
{
return ExposureTime;
}
void SetExposureTime(uint16_t value)
{
if (value < SmallStep)
ExposureTime = SmallStep;
else
ExposureTime = value;
}
uint8_t GetExposureIntensity()
{
return ExposureIntensity;
}
void SetExposureIntensity(uint8_t value)
{
if (value > 100)
ExposureIntensity = 100;
else if (value < IntensityStep)
ExposureIntensity = IntensityStep;
else
ExposureIntensity = value;
}
BuzzerSetting GetBuzzer()
{
return Buzzer;
}
void SetBuzzer(BuzzerSetting value)
{
Buzzer = value;
}
void LoadSettings()
{
uint16_t offset = 0;
EEPROM.get(offset, ExposureTime);
SetExposureTime(ExposureTime);
offset += sizeof(ExposureTime);
EEPROM.get(offset, ExposureIntensity);
SetExposureIntensity(ExposureIntensity);
offset += sizeof(ExposureIntensity);
EEPROM.get(offset, Buzzer);
if (Buzzer < BuzzerSetting::First || Buzzer > BuzzerSetting::Last)
Buzzer = BuzzerSetting::CompletedButtonStartup;
}
void SaveSettings()
{
uint16_t offset = 0;
EEPROM.put(offset, ExposureTime);
offset += sizeof(ExposureTime);
EEPROM.put(offset, ExposureIntensity);
offset += sizeof(ExposureIntensity);
EEPROM.put(offset, (uint8_t)Buzzer);
}
void StartExposureTimer(uint32_t currentTime)
{
ExposureTimerStart = currentTime;
}
void ResetExposureTimer()
{
ExposureTimerStart = 0;
}
uint16_t GetExposureTimeRemaining(uint32_t currentTime)
{
uint32_t elapsed = (currentTime - ExposureTimerStart);
uint32_t exposureTimeMs = ExposureTime * 1000;
return elapsed <= exposureTimeMs ? exposureTimeMs - elapsed : 0;
}

34
src/state.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef __state
#define __state
#include <Arduino.h>
enum BuzzerSetting
{
CompletedButtonStartup = 0,
CompletedButton = 1,
Completed = 2,
None = 3,
First = CompletedButtonStartup,
Last = None
};
uint16_t GetExposureTime();
void SetExposureTime(uint16_t value);
uint8_t GetExposureIntensity();
void SetExposureIntensity(uint8_t value);
BuzzerSetting GetBuzzer();
void SetBuzzer(BuzzerSetting value);
void LoadSettings();
void SaveSettings();
void StartExposureTimer(uint32_t currentTime);
void ResetExposureTimer();
uint16_t GetExposureTimeRemaining(uint32_t currentTime);
#endif

1
upload.ps1 Normal file
View File

@ -0,0 +1 @@
& platformio run --target upload