Compare commits

...

10 Commits

Author SHA1 Message Date
s.aydarov
47cb78f6c6 Added distance corrected switch time for LEDs. 2025-01-20 19:13:29 -08:00
Lis Glitchrain
914b1d6588 Create LICENSE 2024-12-15 05:55:11 -08:00
s.aydarov
fbb3fa4a13 Minor code cleaning. 2024-12-15 05:49:21 -08:00
s.aydarov
8c15a865ee renamed .ino file according to folder. 2024-12-15 05:39:46 -08:00
glitchrain
ffa1564a45 Added README and some useful info. 2024-12-15 05:26:19 -08:00
Author glitchrain
0ff9bdd0d2 Stylistic clean ups. 2024-12-15 03:50:11 -08:00
Author glitchrain
2bbfeb8940 Defined up downbuttons' hold constants. 2024-12-15 03:50:11 -08:00
Author glitchrain
11b4909f39 Fixed probleb with copy assignment of settings in LEDRunner and DisplayHelper classes. Cleaned code up a bit. 2024-12-15 03:50:11 -08:00
Author glitchrain
031a19b107 Added manual mode and time mode settings. 2024-12-15 03:50:11 -08:00
Author glitchrain
7861a6d207 Added ping pong mode (with minor errors). 2024-12-15 03:50:11 -08:00
17 changed files with 3161 additions and 370 deletions

View File

@@ -13,12 +13,27 @@
#define PIN_DISPLAY_CLK 7
#define PIN_DISPLAY_DIO 8
#define PIN_LED_DATA 5
#define PIN_LED_LATCH 4
#define PIN_LED_CLOCK 3
#define PIN_LED_SHIFT_REGISTER_DATA 5
#define PIN_LED_SHIFT_REGISTER_LATCH 4
#define PIN_LED_SHIFT_REGISTER_CLOCK 3
#define REGISTER_SIZE 2
#define LONG_CLICK_TIME_MS 500
#define BTN_UP_SHORT_CLICKED 1
#define BTN_DOWN_SHORT_CLICKED -1
#define BTN_DOWN_UP_NO_SHORT_CLICKED 0
#define BTN_UP_HOLDED 1
#define BTN_DOWN_HOLDED -1
#define BTN_DOWN_UP_NOT_HOLDED 0
#define LED_MAX_DISTANCE 1312
#define LED_DISTANCE_ARRAY LedDistances [MAX_LED_INDEX + 1] = { 0.0, 0.013, 0.029, 0.049, 0.073, 0.101, 0.135, 0.176, 0.225, 0.285, 0.356, 0.442, 0.546, 0.670, 0.820, 1.0 }
#define MAX_SWITCH_TIME 15000
#define MIN_SWITCH_TIME 500
#define DEFAULT_SWITCH_TIME 5000
#endif

50
DisplayDefines.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef EYE_TRAINER_DISPLAYDEFINES
#define EYE_TRAINER_DISPLAYDEFINES 0
#include <TM1637Display.h>
//SEG_A - Up
//SEG_B..SEG_F - Clockwise
//SEG_G - middle
#define NONE 0
#define A SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G
#define B SEG_F | SEG_C | SEG_D | SEG_E | SEG_G
#define C SEG_A | SEG_F | SEG_E | SEG_D
#define D SEG_B | SEG_C | SEG_D | SEG_E | SEG_G
#define E SEG_A | SEG_D | SEG_E | SEG_F | SEG_G
#define F SEG_A | SEG_F | SEG_G | SEG_E
#define G SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G
#define H SEG_F | SEG_C | SEG_E | SEG_G
#define I SEG_B | SEG_C
#define J SEG_B | SEG_C | SEG_D
#define K SEG_A
#define L SEG_F | SEG_E | SEG_D
#define M SEG_C | SEG_G | SEG_E
#define N SEG_C | SEG_G | SEG_E
#define O SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F
#define P SEG_A | SEG_B | SEG_G | SEG_F | SEG_E
#define Q SEG_A | SEG_B | SEG_C | SEG_G | SEG_F
#define R SEG_E | SEG_G
#define S SEG_A | SEG_F | SEG_G | SEG_C | SEG_D
#define T SEG_F | SEG_E | SEG_D | SEG_G
#define U SEG_C | SEG_D | SEG_E
#define V SEG_C | SEG_D | SEG_E | SEG_F | SEG_B
#define W SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G
#define X SEG_B | SEG_C | SEG_E | SEG_F | SEG_G
#define Y SEG_F | SEG_G | SEG_B | SEG_C
#define Z SEG_A | SEG_B | SEG_G | SEG_E | SEG_D
#define WORD(a, b, c, d) (const uint8_t[]) {a, b, c, d}
#define WORD_DONE WORD( D, O, N, E )
#define WORD_RUN WORD( R, U, N, NONE )
#define WORD_MODE WORD( R, M, O, D )
#define WORD_TIME_SWITCH WORD( T, I, M, E )
#define WORD_TIME_MODE WORD( T, M, O, D )
#define WORD_BRIGHTNESS WORD( B, R, G, H )
#define WORD_TEST WORD( T, E, S, T )
#define WORD_ WORD( SEG_G, SEG_G, SEG_G, SEG_G )
#endif

View File

@@ -5,6 +5,7 @@
#include "Defines.h"
#include "Settings.h"
#include "Enums.h"
#include "DisplayDefines.h"
class DisplayHelper
{
@@ -13,71 +14,11 @@ class DisplayHelper
public:
DisplayHelper() { }
DisplayHelper(Settings* _Settings)
{
m_Settings = _Settings;
}
static const uint8_t NONE = 0;
static const uint8_t A = SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G;
static const uint8_t B = SEG_F | SEG_C | SEG_D | SEG_E | SEG_G;
static const uint8_t C = SEG_A | SEG_F | SEG_E | SEG_D;
static const uint8_t D = SEG_B | SEG_C | SEG_D | SEG_E | SEG_G;
static const uint8_t E = SEG_A | SEG_D | SEG_E | SEG_F | SEG_G;
static const uint8_t F = SEG_A | SEG_F | SEG_G | SEG_E;
static const uint8_t G = SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
static const uint8_t H = SEG_F | SEG_C | SEG_E | SEG_G;
static const uint8_t I = SEG_B | SEG_C;
static const uint8_t J = SEG_B | SEG_C | SEG_D;
static const uint8_t K = SEG_A;
static const uint8_t L = SEG_F | SEG_E | SEG_D;
static const uint8_t M = SEG_C | SEG_G | SEG_E;
static const uint8_t N = SEG_C | SEG_G | SEG_E;
static const uint8_t O = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F;
static const uint8_t P = SEG_A | SEG_B | SEG_G | SEG_F | SEG_E;
static const uint8_t Q = SEG_G;
static const uint8_t R = SEG_E | SEG_G;
static const uint8_t S = SEG_A | SEG_F | SEG_G | SEG_C | SEG_D;
static const uint8_t T = SEG_F | SEG_E | SEG_D | SEG_G;
static const uint8_t U = SEG_C | SEG_D | SEG_E;
static const uint8_t V = SEG_C | SEG_D | SEG_E | SEG_F | SEG_B;
static const uint8_t W = SEG_D;
static const uint8_t X = SEG_B | SEG_C | SEG_E | SEG_F | SEG_G;
static const uint8_t Y = SEG_F | SEG_G | SEG_B | SEG_C;
static const uint8_t Z = SEG_A | SEG_B | SEG_G | SEG_E | SEG_D;
void setup()
{
display.setBrightness(0x0f);
}
void buttonsTest()
{
bool btnMainState = digitalRead(PIN_BTN_MAIN);
bool btnUpState = digitalRead(PIN_BTN_UP);
bool btnDownState = digitalRead(PIN_BTN_DOWN);
int numChanged = currentTestNum;
if(btnMainState == HIGH)
{
numChanged = 0;
}
else if (btnUpState == HIGH)
{
numChanged++;
}
else if (btnDownState == HIGH)
{
numChanged--;
}
if(numChanged != currentTestNum)
{
currentTestNum = numChanged;
display.clear();
display.showNumberDec(currentTestNum, false);
}
m_DrawTitle = true;
}
void runTest()
@@ -187,106 +128,113 @@ public:
display.setSegments(WORD_DONE);
}
void DrawRunInfo(bool _Title, bool _Run)
void UpdateModeDisplay(bool _RunnerActive, DeviceMode _DeviceMode)
{
if(_Title)
switch(_DeviceMode)
{
case DeviceMode::RUN: DrawRunInfo(_RunnerActive); break;
case DeviceMode::SETTINGS_MODE: DrawSettingsRunModeInfo(); break;
case DeviceMode::SETTINGS_BRIGHTNESS: DrawSettingsBrightnessInfo(); break;
case DeviceMode::SETTINGS_TIME_SWITCH: DrawSettingsTimeSwitchInfo(); break;
case DeviceMode::SETTINGS_TIME_MODE: DrawSettingsTimeModeInfo(); break;
case DeviceMode::TEST: DrawTestInfo(); break;
}
}
void DrawRunInfo(bool _Run)
{
if(m_DrawTitle)
{
display.setSegments(WORD_RUN);
}
else if(_Run)
{
uint8_t forward[4] = {P, L, A, NONE};
display.setSegments(forward);
display.setSegments(WORD(P, L, A, Y));
}
else
{
uint8_t forward[4] = {P, A, U, S};
display.setSegments(forward);
display.setSegments(WORD(P, A, U, S));
}
}
void DrawSettingsRunModeInfo(bool _Title, RunMode _RunMode)
void DrawSettingsRunModeInfo()
{
if(_Title)
if(m_DrawTitle)
{
display.setSegments(WORD_MODE);
}
else
{
switch(_RunMode)
switch(Settings::getInstance().RunModeState)
{
case RunMode::FORWARD: display.setSegments((const uint8_t[]) {F, W, R, D}); break;
case RunMode::BACKWARD: display.setSegments((const uint8_t[]) {B, A, C, K}); break;
case RunMode::BOTH: display.setSegments((const uint8_t[]) {B, O, T, H}); break;
case RunMode::RANDOM: display.setSegments((const uint8_t[]) {R, N, D, NONE}); break;
case RunMode::FORWARD: display.setSegments(WORD(F, R, W, D )); break;
case RunMode::BACKWARD: display.setSegments(WORD(B, A, C, D )); break;
case RunMode::PING_PONG: display.setSegments(WORD(P, I, N, G )); break;
case RunMode::RANDOM: display.setSegments(WORD(R, N, D, NONE)); break;
}
}
}
void DrawSettingsTimeInfo(bool _Title, int _SwitchTime)
void DrawSettingsTimeSwitchInfo()
{
if(_Title)
if(m_DrawTitle)
{
display.setSegments(WORD_TIME);
display.setSegments(WORD_TIME_SWITCH);
}
else
{
display.showNumberDec(_SwitchTime, false);
display.showNumberDec(Settings::getInstance().getDisplaySwitchTime(), false);
}
}
void DrawSettingsTimeModeInfo()
{
if(m_DrawTitle)
{
display.setSegments(WORD_TIME_MODE);
}
else
{
switch(Settings::getInstance().TimeModeState)
{
case TimeMode::CONST: display.setSegments(WORD(C, N, S, T )); break;
case TimeMode::MANUAL: display.setSegments(WORD(M, A, N, L )); break;
case TimeMode::DECREASING: display.setSegments(WORD(D, E, C, R )); break;
}
}
}
void DrawSettingsBrightnessInfo(bool _Title, int _Brightness)
void DrawSettingsBrightnessInfo()
{
if(_Title)
if(m_DrawTitle)
{
display.setSegments(WORD_BRIGHTNESS);
}
else
{
display.showNumberDec(_Brightness, false);
display.showNumberDec(Settings::getInstance().Brightness, false);
}
}
void DrawTestInfo(bool _Title)
void DrawTestInfo()
{
display.setSegments(WORD_TEST);
}
//DOES NOT WORK!
// uint8_t* encodeNumber(uint8_t _Num)
// {
// uint8_t data[] = { 0xff, 0xff, 0xff, 0xff };
// // data[3] = display.encodeDigit(_Num % 10);
// // _Num = _Num / 10;
// // data[2] = display.encodeDigit(_Num % 10);
// // _Num = _Num / 10;
// // data[1] = display.encodeDigit(_Num % 10);
// // _Num = _Num / 10;
// // data[0] = display.encodeDigit(_Num % 10);
void setDrawTitle(bool _Value)
{
m_DrawTitle = _Value;
}
// data[3] = display.encodeDigit(1);
// data[2] = display.encodeDigit(2);
// data[1] = display.encodeDigit(3);
// data[0] = display.encodeDigit(4);
// return data;
// }
void getDrawTitle()
{
return m_DrawTitle;
}
private:
TM1637Display display = TM1637Display::TM1637Display(PIN_DISPLAY_CLK, PIN_DISPLAY_DIO);
Settings* m_Settings;
int currentTestNum = 0;
//seg A = upper ground
//seg B = upper right
//seg C = lower right
uint8_t WORD_DONE[4] = { D, O, N, E };
uint8_t WORD_RUN[4] = { R, U, N, NONE };
uint8_t WORD_MODE[4] = { M, O, D, E };
uint8_t WORD_TIME[4] = { T, I, M, E };
uint8_t WORD_BRIGHTNESS[4] = { B, R, G, H };
uint8_t WORD_TEST[4] = { T, E, S, T };
uint8_t WORD_[4] = { SEG_G, SEG_G, SEG_G, SEG_G };
bool m_DrawTitle = true;
};
#endif

View File

@@ -6,7 +6,8 @@ enum DeviceMode
RUN = 0,
SETTINGS_MODE,
SETTINGS_BRIGHTNESS,
SETTINGS_TIME,
SETTINGS_TIME_SWITCH,
SETTINGS_TIME_MODE,
TEST
};
@@ -14,7 +15,7 @@ enum RunMode
{
FORWARD = 0,
BACKWARD = 1,
BOTH = 2,
PING_PONG = 2,
RANDOM = 3
};
@@ -22,7 +23,7 @@ enum TimeMode
{
CONST = 0,
MANUAL,
DECREASING
DECREASING // not implemented
};
#endif

2559
EyeTrainerLayout.diy Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,27 +4,11 @@
#include "LEDRunner.h"
#include "InputHelper.h"
#include "Enums.h"
#include "WrapHelper.h"
class EyeTrainerMain
{
public:
//==============================================================
//state
//==============================================================
enum DeviceMode m_deviceMode = DeviceMode::RUN;
Settings m_Settings = Settings();
LEDRunner m_LEDRunner = LEDRunner(&m_Settings);
DisplayHelper m_DisplayHelper = DisplayHelper(&m_Settings);
InputHelper m_InputHelper = InputHelper();
bool m_DrawTitle = true;
unsigned long m_PrevTime = 0;
//==============================================================
//end of state
//==============================================================
EyeTrainerMain() { }
void setup()
@@ -33,26 +17,32 @@ public:
m_LEDRunner.setup();
m_DisplayHelper.setup();
m_InputHelper.setup();
UpdateModeDisplay();
updateModeDisplay();
}
void loop()
{
bool inputEvent = m_InputHelper.readInputs(millis());
switch(m_deviceMode)
if(inputEvent)
{
case DeviceMode::RUN: ProcessRun(); break;
case DeviceMode::SETTINGS_MODE: ProcessSettingsRunMode(); break;
case DeviceMode::SETTINGS_TIME: ProcessSwitchTime(); break;
case DeviceMode::SETTINGS_BRIGHTNESS: ProcessBrightness(); break;
case DeviceMode::TEST: ProcessTest(); break;
processChangeMode();
int upDownButtonsShortClickState = m_InputHelper.getUpDownButtonsShortClicked();
int upDownButtonsHoldState = m_InputHelper.getUpDownButtonsHold();
switch(m_deviceMode)
{
case DeviceMode::RUN: processRun(upDownButtonsShortClickState, upDownButtonsHoldState); break;
case DeviceMode::SETTINGS_MODE: processSettingsRunMode(upDownButtonsShortClickState, upDownButtonsHoldState); break;
case DeviceMode::SETTINGS_TIME_SWITCH: processSwitchTime(upDownButtonsShortClickState, upDownButtonsHoldState); break;
case DeviceMode::SETTINGS_BRIGHTNESS: processBrightness(upDownButtonsShortClickState, upDownButtonsHoldState); break;
case DeviceMode::SETTINGS_TIME_MODE: processTimeMode(upDownButtonsShortClickState, upDownButtonsHoldState); break;
case DeviceMode::TEST: processTest(upDownButtonsShortClickState, upDownButtonsHoldState); break;
}
}
unsigned long deltaTime = millis() - m_PrevTime;
m_PrevTime = millis();
m_LEDRunner.update(deltaTime, m_Settings.SwitchTime, m_Settings.m_RunMode, m_Settings.m_TimeMode);
unsigned long currentTime = millis();
unsigned long deltaTime = currentTime - m_PrevTime;
m_PrevTime = currentTime;
m_LEDRunner.update(deltaTime);
}
void runTest()
@@ -62,154 +52,115 @@ public:
}
private:
enum DeviceMode m_deviceMode = DeviceMode::RUN;
static const uint8_t BTN_UP_SHORT_CLICKED = 1;
static const uint8_t BTN_DOWN_SHORT_CLICKED = -1;
static const uint8_t BTN_DOWN_UP_NO_SHORT_CLICKED = 0;
LEDRunner m_LEDRunner = LEDRunner();
DisplayHelper m_DisplayHelper = DisplayHelper();
InputHelper m_InputHelper = InputHelper();
unsigned long m_PrevTime = 0;
int wrapInt(int _Num, int _Max)
void updateModeDisplay()
{
if(_Num < 0)
{
return (_Max - _Num) % _Max;
}
if(_Num >= _Max)
{
return _Num % _Max;
}
return _Num;
m_DisplayHelper.UpdateModeDisplay(m_LEDRunner.getRun(), m_deviceMode);
}
int wrapInt(int _Num, int _Min, int _Max)
void processChangeMode()
{
int num = _Num - _Min;
int max = _Max - _Min;
return wrapInt(num, max) + _Min;
}
void ProcessChangeMode()
{
bool mainButtonShortClicked = m_InputHelper.getButtonMain().IsNowShortClicked();
bool mainButtonShortClicked = m_InputHelper.getButtonMain().isNowShortClicked();
if(!mainButtonShortClicked)
return;
m_DrawTitle = true;
m_deviceMode = static_cast<DeviceMode>(wrapInt(static_cast<int>(m_deviceMode) + 1, 5));
UpdateModeDisplay();
m_DisplayHelper.setDrawTitle(true);
m_deviceMode = static_cast<DeviceMode>(wrapInt(static_cast<int>(m_deviceMode) + 1, 6));
updateModeDisplay();
if(m_deviceMode != DeviceMode::RUN)
{
m_LEDRunner.setRun(false);
}
}
void UpdateModeDisplay()
void processRun(int upDownButtons, int upDownButtonsHold)
{
switch(m_deviceMode)
if(Settings::getInstance().TimeModeState == TimeMode::MANUAL && upDownButtons != BTN_DOWN_UP_NO_SHORT_CLICKED)
{
case DeviceMode::RUN: m_DisplayHelper.DrawRunInfo(m_DrawTitle, m_LEDRunner.getRun()); break;
case DeviceMode::SETTINGS_MODE: m_DisplayHelper.DrawSettingsRunModeInfo(m_DrawTitle, m_Settings.m_RunMode); break;
case DeviceMode::SETTINGS_TIME: m_DisplayHelper.DrawSettingsTimeInfo(m_DrawTitle, m_Settings.SwitchTime / 10); break;
case DeviceMode::SETTINGS_BRIGHTNESS: m_DisplayHelper.DrawSettingsBrightnessInfo(m_DrawTitle, m_Settings.Brightness); break;
case DeviceMode::TEST: m_DisplayHelper.DrawTestInfo(m_DrawTitle); break;
}
}
int GetUpDownButtonsShortClicked()
{
bool upButtonShortClicked = m_InputHelper.getButtonUp().IsNowShortClicked();
bool downButtonShortClicked = m_InputHelper.getButtonUp().IsNowShortClicked();
if(upButtonShortClicked)
return BTN_UP_SHORT_CLICKED;
else if(downButtonShortClicked)
return BTN_DOWN_SHORT_CLICKED;
return BTN_DOWN_UP_NO_SHORT_CLICKED;
}
int GetUpDownButtonsHold()
{
bool upButtonHold = m_InputHelper.getButtonUp().IsHolded();
bool downButtonHold = m_InputHelper.getButtonDown().IsHolded();
return (upButtonHold - downButtonHold);
}
void ProcessRun()
{
ProcessChangeMode();
int upDownButtons = GetUpDownButtonsShortClicked();
if(upDownButtons == BTN_UP_SHORT_CLICKED)
{
m_LEDRunner.setRun(true);
UpdateModeDisplay();
m_LEDRunner.manualUpdate(Settings::getInstance().RunModeState, upDownButtons);
}
if(upDownButtons == BTN_DOWN_SHORT_CLICKED)
else
{
m_LEDRunner.setRun(false);
UpdateModeDisplay();
if(upDownButtons == BTN_UP_SHORT_CLICKED)
{
m_DisplayHelper.setDrawTitle(false);
m_LEDRunner.setRun(true);
updateModeDisplay();
}
if(upDownButtons == BTN_DOWN_SHORT_CLICKED)
{
m_DisplayHelper.setDrawTitle(false);
m_LEDRunner.setRun(false);
updateModeDisplay();
}
}
}
void ProcessSettingsRunMode()
void processSettingsRunMode(int upDownButtons, int upDownButtonsHold)
{
ProcessChangeMode();
int upDownButtons = GetUpDownButtonsShortClicked();
if(upDownButtons != BTN_DOWN_UP_NO_SHORT_CLICKED)
{
m_DrawTitle = false;
m_Settings.switchRunMode(upDownButtons);
UpdateModeDisplay();
m_DisplayHelper.setDrawTitle(false);
Settings::getInstance().switchRunMode(upDownButtons);
updateModeDisplay();
}
}
void ProcessSwitchTime()
void processSwitchTime(int upDownButtons, int upDownButtonsHold)
{
ProcessChangeMode();
int upDownButtons = GetUpDownButtonsShortClicked();
int upDownHoldButtons = GetUpDownButtonsHold();
if(upDownButtons != BTN_DOWN_UP_NO_SHORT_CLICKED)
{
m_DrawTitle = false;
m_Settings.setSwitchTime(m_Settings.SwitchTime + upDownButtons * 50);
UpdateModeDisplay();
m_DisplayHelper.setDrawTitle(false);
Settings::getInstance().setSwitchTime(Settings::getInstance().SwitchTime + upDownButtons * 50);
updateModeDisplay();
}
if(upDownHoldButtons != 0)
if(upDownButtonsHold != BTN_DOWN_UP_NOT_HOLDED)
{
m_DrawTitle = false;
m_Settings.setSwitchTime(m_Settings.SwitchTime + upDownHoldButtons * 50);
UpdateModeDisplay();
m_DisplayHelper.setDrawTitle(false);
Settings::getInstance().setSwitchTime(Settings::getInstance().SwitchTime + upDownButtonsHold * 50);
updateModeDisplay();
}
}
void ProcessBrightness()
void processBrightness(int upDownButtons, int upDownButtonsHold)
{
ProcessChangeMode();
int upDownButtons = GetUpDownButtonsShortClicked();
int upDownHoldButtons = GetUpDownButtonsHold();
if(upDownButtons != BTN_DOWN_UP_NO_SHORT_CLICKED)
{
m_LEDRunner.setAllHigh();
m_DrawTitle = false;
m_Settings.setBrightness(m_Settings.Brightness + upDownButtons);
m_LEDRunner.updateBrightness(m_Settings.Brightness);
UpdateModeDisplay();
m_DisplayHelper.setDrawTitle(false);
Settings::getInstance().setBrightness(Settings::getInstance().Brightness + upDownButtons);
m_LEDRunner.setBrightness(Settings::getInstance().Brightness);
updateModeDisplay();
}
if(upDownHoldButtons != 0)
if(upDownButtonsHold != BTN_DOWN_UP_NOT_HOLDED)
{
m_LEDRunner.setAllHigh();
m_DrawTitle = false;
m_Settings.setBrightness(m_Settings.Brightness + upDownHoldButtons);
m_LEDRunner.updateBrightness(m_Settings.Brightness);
UpdateModeDisplay();
m_DisplayHelper.setDrawTitle(false);
Settings::getInstance().setBrightness(Settings::getInstance().Brightness + upDownButtonsHold);
m_LEDRunner.setBrightness(Settings::getInstance().Brightness);
updateModeDisplay();
}
}
void ProcessTest()
void processTimeMode(int upDownButtons, int upDownButtonsHold)
{
if(upDownButtons != BTN_DOWN_UP_NO_SHORT_CLICKED)
{
m_DisplayHelper.setDrawTitle(false);
Settings::getInstance().switchTimeMode(upDownButtons);
updateModeDisplay();
}
}
void processTest(int upDownButtons, int upDownButtonsHold)
{
ProcessChangeMode();
int upDownButtons = GetUpDownButtonsShortClicked();
if(upDownButtons != BTN_DOWN_UP_NO_SHORT_CLICKED)
{
runTest();

BIN
GeneralViewBreadBoard.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

View File

@@ -42,11 +42,33 @@ public:
return m_DownButton;
}
int getUpDownButtonsShortClicked()
{
bool upButtonShortClicked = m_UpButton.isNowShortClicked();
bool downButtonShortClicked = m_DownButton.isNowShortClicked();
if(upButtonShortClicked)
return BTN_UP_SHORT_CLICKED;
else if(downButtonShortClicked)
return BTN_DOWN_SHORT_CLICKED;
return BTN_DOWN_UP_NO_SHORT_CLICKED;
}
int getUpDownButtonsHold()
{
bool upButtonHold = m_UpButton.isHolded();
bool downButtonHold = m_DownButton.isHolded();
if(upButtonHold)
return BTN_UP_HOLDED;
else if(downButtonHold)
return BTN_DOWN_HOLDED;
return BTN_DOWN_UP_NOT_HOLDED;
}
private:
bool m_InputEvent;
SimpleButton m_MainButton = SimpleButton(PIN_BTN_MAIN, LONG_CLICK_TIME_MS);
SimpleButton m_UpButton = SimpleButton(PIN_BTN_UP, LONG_CLICK_TIME_MS);
SimpleButton m_UpButton = SimpleButton(PIN_BTN_UP, LONG_CLICK_TIME_MS);
SimpleButton m_DownButton = SimpleButton(PIN_BTN_DOWN, LONG_CLICK_TIME_MS);
};

View File

@@ -1,3 +1,6 @@
#ifndef EYE_TRAINER_LEDRUNNER
#define EYE_TRAINER_LEDRUNNER 0
/*
ShiftRegister74HC595 - Library for simplified control of 74HC595 shift registers.
Developed and maintained by Timo Denk and contributers, since Nov 2014.
@@ -7,52 +10,42 @@
#include "Defines.h"
#include <ShiftRegister74HC595.h>
#include "Settings.h"
#include "WrapHelper.h"
class LEDRunner
{
public:
// LEDRunner()
// {
// }
LEDRunner(Settings* _Settings)
{
this->m_Settings = _Settings;
}
LEDRunner() { }
void setup()
{
pinMode(PIN_LED_GND_BUS, OUTPUT);
pinMode(PIN_LED_DATA, OUTPUT);
pinMode(PIN_LED_CLOCK, OUTPUT);
pinMode(PIN_LED_LATCH, OUTPUT);
pinMode(PIN_LED_SHIFT_REGISTER_DATA, OUTPUT);
pinMode(PIN_LED_SHIFT_REGISTER_CLOCK, OUTPUT);
pinMode(PIN_LED_SHIFT_REGISTER_LATCH, OUTPUT);
}
void nextLED(RunMode _RunMode)
void manualUpdate(RunMode _RunMode, int _NextLEDIndexIncrement)
{
switch(_RunMode)
{
case RunMode::FORWARD: m_CurrentLED = wrapInt(m_CurrentLED + 1, MAX_LED_INDEX + 1); break;
case RunMode::BACKWARD: m_CurrentLED = wrapInt(m_CurrentLED - 1, MAX_LED_INDEX + 1); break;
//TODO: ADD PING PONG
case RunMode::BOTH: m_CurrentLED = wrapInt(m_CurrentLED - 1, MAX_LED_INDEX + 1); break;
case RunMode::RANDOM: m_CurrentLED = random(0, MAX_LED_INDEX + 1); break;
}
nextLED(_RunMode, _NextLEDIndexIncrement);
m_LEDs.setAllLow();
m_LEDs.set(m_CurrentLED, HIGH);
}
void update(unsigned long _DeltaTime, int _SwitchTime, RunMode _RunMode, TimeMode _TimeMode)
void update(unsigned long _DeltaTime)
{
if(!m_Run)
if(!m_Run || Settings::getInstance().TimeModeState == TimeMode::MANUAL)
return;
m_CurrentTime += _DeltaTime;
if(m_CurrentTime > _SwitchTime)
//Settings::getInstance().SwitchTime
if(m_CurrentTime > Settings::getInstance().getDistanceCorrectedSwitchTime(m_CurrentLED))
{
m_CurrentTime = 0;
nextLED(_RunMode);
sr.setAllLow();
sr.set(m_CurrentLED, HIGH);
nextLED(Settings::getInstance().RunModeState, 1);
m_LEDs.setAllLow();
m_LEDs.set(m_CurrentLED, HIGH);
}
}
@@ -61,7 +54,7 @@ public:
m_Run = _Run;
}
void updateBrightness(float _Brightness)
void setBrightness(float _Brightness)
{
analogWrite(PIN_LED_GND_BUS, (MAX_BRIGHTNESS - _Brightness) / MAX_BRIGHTNESS * 255);
}
@@ -73,10 +66,6 @@ public:
void runTest()
{
// // setting all pins at the same time to either HIGH or LOW
// sr.setAllHigh(); // set all pins HIGH
// delay(500);
analogWrite(PIN_LED_GND_BUS, 120);
for(int i = 0; i < 4; i++)
@@ -87,13 +76,13 @@ public:
delay(250); // wait for a second
}
sr.setAllHigh();
m_LEDs.setAllHigh();
delay(500);
sr.setAllLow();
m_LEDs.setAllLow();
delay(500);
sr.setAllHigh();
m_LEDs.setAllHigh();
delay(500);
sr.setAllLow();
m_LEDs.setAllLow();
delay(500);
for(int i = 0; i < 4; i++)
@@ -109,29 +98,29 @@ public:
// setting single pins
for (int i = 0; i < MAX_LED_INDEX; i++)
{
sr.set(i, HIGH);
m_LEDs.set(i, HIGH);
delay(25);
sr.set(i, LOW);
m_LEDs.set(i, LOW);
}
// setting single pins
// setting single pins
for (int i = MAX_LED_INDEX; i > 0; i--)
{
sr.set(i, HIGH);
m_LEDs.set(i, HIGH);
delay(25);
sr.set(i, LOW);
m_LEDs.set(i, LOW);
}
}
sr.set(0, HIGH);
m_LEDs.set(0, HIGH);
delay(50);
for (int i = 0; i < MAX_LED_INDEX / 2; i++)
{
sr.set(i, HIGH);
sr.set(MAX_LED_INDEX - i, HIGH);
m_LEDs.set(i, HIGH);
m_LEDs.set(MAX_LED_INDEX - i, HIGH);
delay(150);
sr.set(i, LOW);
sr.set(MAX_LED_INDEX - i, LOW);
m_LEDs.set(i, LOW);
m_LEDs.set(MAX_LED_INDEX - i, LOW);
}
@@ -139,15 +128,15 @@ public:
{
for (int i = MAX_LED_INDEX / 2; i >= 0; i--)
{
sr.set(i, HIGH);
sr.set(MAX_LED_INDEX - i, HIGH);
m_LEDs.set(i, HIGH);
m_LEDs.set(MAX_LED_INDEX - i, HIGH);
delay(50);
sr.set(i, LOW);
sr.set(MAX_LED_INDEX - i, LOW);
m_LEDs.set(i, LOW);
m_LEDs.set(MAX_LED_INDEX - i, LOW);
}
}
sr.setAllHigh(); // set all pins HIGH
m_LEDs.setAllHigh(); // set all pins HIGH
for(int i = 10; i > 0; i--)
{
@@ -160,58 +149,53 @@ public:
void blink(const uint8_t _LedIndex, unsigned long _Time)
{
sr.setAllLow();
sr.set(_LedIndex, HIGH);
m_LEDs.setAllLow();
m_LEDs.set(_LedIndex, HIGH);
delay(_Time);
sr.setAllLow();
m_LEDs.setAllLow();
}
void setAndClearOthers(const uint8_t _LedIndex, const uint8_t _Value)
{
sr.setAllLow();
sr.set(_LedIndex, _Value);
m_LEDs.setAllLow();
m_LEDs.set(_LedIndex, _Value);
}
void set(const uint8_t _LedIndex, const uint8_t _Value)
{
sr.set(_LedIndex, _Value);
m_LEDs.set(_LedIndex, _Value);
}
void setAllHigh()
{
sr.setAllHigh();
m_LEDs.setAllHigh();
}
void setAllLow()
{
sr.setAllLow();
m_LEDs.setAllLow();
}
private:
Settings* m_Settings;
bool m_Run = false;
unsigned long m_CurrentTime = 0;
int m_CurrentLED = 0;
ShiftRegister74HC595<REGISTER_SIZE> sr = ShiftRegister74HC595<REGISTER_SIZE>::ShiftRegister74HC595(PIN_LED_DATA, PIN_LED_CLOCK, PIN_LED_LATCH);
private:
bool m_Run = true;
unsigned long m_CurrentTime = 0;
int m_CurrentLED = 0;
int m_CurrentLEDPingPong = 0;
ShiftRegister74HC595<REGISTER_SIZE> m_LEDs = ShiftRegister74HC595<REGISTER_SIZE>::ShiftRegister74HC595(PIN_LED_SHIFT_REGISTER_DATA, PIN_LED_SHIFT_REGISTER_CLOCK, PIN_LED_SHIFT_REGISTER_LATCH);
int wrapInt(int _Num, int _Max)
void nextLED(RunMode _RunMode, int _NextLEDIndexIncrement)
{
if(_Num < 0)
switch(_RunMode)
{
return (_Max - _Num) % _Max;
case RunMode::FORWARD: m_CurrentLED = wrapInt(m_CurrentLED + _NextLEDIndexIncrement, MAX_LED_INDEX + 1); break;
case RunMode::BACKWARD: m_CurrentLED = wrapInt(m_CurrentLED - _NextLEDIndexIncrement, MAX_LED_INDEX + 1); break;
case RunMode::PING_PONG:
m_CurrentLEDPingPong = wrapInt(m_CurrentLEDPingPong + _NextLEDIndexIncrement, MAX_LED_INDEX * 2);
m_CurrentLED = pingPong(m_CurrentLEDPingPong, MAX_LED_INDEX);
break;
case RunMode::RANDOM: m_CurrentLED = random(0, MAX_LED_INDEX + 1); break;
}
if(_Num >= _Max)
{
return _Num % _Max;
}
return _Num;
}
int wrapInt(int _Num, int _Min, int _Max)
{
int num = _Num - _Min;
int max = _Max - _Min;
return wrapInt(num, max) + _Min;
}
};
#endif

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Lis Glitchrain
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

91
LogDistanceCalc.ipynb Normal file

File diff suppressed because one or more lines are too long

97
README.md Normal file
View File

@@ -0,0 +1,97 @@
# Eye Trainer
## Description
This is a simple eye trainer which is supposed to reduce muscle stress after focusing on same distance for a long time. The constraction is long stick (1.0-1.5m) with LEDs placed along. LEDs turn on with choosen order after selected switch time is passed. Display helps to control the device.
## Controls
**Buttons:**
1. Main
2. Up
3. Down
## Usage
Device starts in "run" mode. but inactive. Click "Up" button to start with default settings. Click "Down" button to pause. Click "Main" button to switch displayed info. On other screens than "run" press "Up" or "Down" buttons to change settings.
On "Test" screen any button except "Main" will trigger test sequence to check display and LEDs.
## Device Settings
**LEDS order modes (rnod):**
1. Forward (FrWd)
2. Backward (bAC-)
3. Ping-Pong (PInG)
4. Random (rnd) (default)
**Brightness (brgh):**
Number from 25 to 255.
**Switch Time (time):**
Number between 25 and 1500 measured in milliseconds x10, i.e. 25 means 250 ms and 1500 means 15000 ms (15 seconds).
Default value 300 (3 seconds).
**Time mode (tmod):**
1. Constant (CnSt)
2. Manusl (nAnL)
3. Decremental (dECr) (not implemented)
## Components Used
1. Arduino Nano (ATMega328PB) x1
2. ShiftRegister 74HC595N x2
3. LED x16
4. Resistor 220Ohm x16
5. Resistor 10kOhm x3
6. Segment Display TM1637 x1
7. Tact Button x3
8. Solderless breadboards x3
9. Wires (a lot)
## Connection Scheme
To be added. Hints are in file EyeTrainerLayout.diy (can be opened with DIY Layout Creator).
### Text description
**Arduino's BreadBoard:**
1. Contains Arduino Nano, TM1637 Display, 3 tact buttons with 10kOhm resistors, connectors
2. Pins D7, D8 controlls display (see Defines.h)
3. Pins D9, D10, D11 receives buttons signals (see Defines.h)
4. Pins D3, D4, D5 controlls shift registers (see Defines.h)
5. Ping 6 controlls brightness of LEDs (see Defines.h)
6. D6, D3, D4, D5, GND and 5V pins are passed to ShiftRegisters' BreadBoard
**ShiftRegisters' BreadBoard:**
1. Contains two 74HC595N shift registers, x16 220Ohm resistors and connectors
2. Pins Q0..Q7 of both shift registers are used to controll LEDs.
3. Two 74HC595N are connected sequentially. Latch, Data and Clock connections to Arduino board are defined in Defines.h
**IMPORTANT: Arduino's pins are different in EyeTrainer project!**
![Shift registers sequential connection](ShiftRegisterSequentialConnection.png)
Source: https://blog.timodenk.com/shift-register-arduino-library/
## General View
![GeneralView image](GeneralViewBreadBoard.jpg)
## Notes
Python script (LogDistanceCalc.ipynb, Jupyter Notebook) can be used to define distances between LEDs on stick.
## Known Problems
1. Sometimes buttons click are not registered or skipped.
## Possible Improvements
1. Lighten small printed and high contrast images with white LEDs. Probably it would be easier and healthier to focus eyes on, than watching LEDs directly.

View File

@@ -7,26 +7,40 @@
class Settings
{
public:
enum RunMode m_RunMode = RunMode::RANDOM;
enum TimeMode m_TimeMode = TimeMode::CONST;
int SwitchTime = 3000; //In milliseconds
// float Brightness = MAX_BRIGHTNESS;
float Brightness = 195;
enum RunMode RunModeState = RunMode::PING_PONG;
enum TimeMode TimeModeState = TimeMode::CONST;
int SwitchTime = DEFAULT_SWITCH_TIME; //In milliseconds
float Brightness = MAX_BRIGHTNESS / 2;
float LED_DISTANCE_ARRAY;
Settings()
//=======================================
//Singleton. AVOID COPY ASSIGNMENTS
//=======================================
static Settings& getInstance()
{
static Settings instance; // Guaranteed to be destroyed. Instantiated on first use.
return instance;
}
Settings() {}
Settings(Settings const&) = delete;
void operator = (Settings const&) = delete;
//=======================================
//Singleton. AVOID COPY ASSIGNMENTS
//=======================================
void setSwitchTime(int _SwitchTime)
{
if(_SwitchTime > 15000)
if(_SwitchTime > MAX_SWITCH_TIME)
{
SwitchTime = 15000;
SwitchTime = MAX_SWITCH_TIME;
return;
}
if(_SwitchTime < 250)
if(_SwitchTime < MIN_SWITCH_TIME)
{
SwitchTime = 250;
SwitchTime = MIN_SWITCH_TIME;
return;
}
@@ -50,45 +64,40 @@ public:
void resetRunMode(RunMode _RunMode)
{
m_RunMode = RunMode::FORWARD;
RunModeState = RunMode::FORWARD;
}
void switchRunMode(int _NextOrBack)
void switchRunMode(int _ModeIncrement)
{
if(m_RunMode == RunMode::FORWARD)
{
m_RunMode = RunMode::BACKWARD;
}
else if(m_RunMode == RunMode::BACKWARD)
{
m_RunMode = RunMode::BOTH;
}
else if(m_RunMode == RunMode::BOTH)
{
m_RunMode = RunMode::RANDOM;
}
else if(m_RunMode == RunMode::RANDOM)
{
m_RunMode = RunMode::FORWARD;
}
// RunMode newRunMode = static_cast<RunMode>(wrapInt(static_cast<int>(m_RunMode) + _NextOrBack, 4));
// m_RunMode = newRunMode;
RunModeState = static_cast<RunMode>(wrapInt(static_cast<int>(RunModeState) + _ModeIncrement, 4));
}
void setTimeMode(enum TimeMode _TimeMode)
{
m_TimeMode = _TimeMode;
TimeModeState = _TimeMode;
}
void switchTimeMode(int _ModeIncrement)
{
TimeModeState = static_cast<TimeMode>(wrapInt(static_cast<int>(TimeModeState) + _ModeIncrement, 3));
}
int getDisplaySwitchTime()
{
return SwitchTime / 10;
}
int getDistanceCorrectedSwitchTime(int _LedIndex)
{
return (int) (min(max(SwitchTime * LedDistances[_LedIndex], MIN_SWITCH_TIME), MAX_SWITCH_TIME));
}
private:
int wrapInt(int _Num, int _Max)
{
if(_Num < 0)
{
return (_Max - _Num) % _Max;
return (_Max + _Num % _Max);
}
if(_Num >= _Max)
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

View File

@@ -58,37 +58,37 @@ public:
return m_IsNowHolded | (prevState != m_State);
}
bool IsNowClicked()
bool isNowClicked()
{
return m_IsNowClicked;
}
bool IsNowPressed()
bool isNowPressed()
{
return m_IsNowPressed;
}
bool IsNowReleased()
bool isNowReleased()
{
return m_IsNowReleased;
}
bool IsHolded()
bool isHolded()
{
return m_IsNowHolded;
}
bool IsNowLongClicked()
bool isNowLongClicked()
{
return m_IsNowLongClicked;
}
bool IsNowShortClicked()
bool isNowShortClicked()
{
return m_IsNowShortClicked;
}
unsigned long GetPressedTime()
unsigned long getPressedTime()
{
return m_PressedTime;
}

44
WrapHelper.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef EYE_TRAINER_WRAPHELPER
#define EYE_TRAINER_WRAPHELPER 0
int wrapInt(int _Num, int _Max)
{
if(_Num < 0)
{
return (_Max + _Num % _Max);
}
if(_Num >= _Max)
{
return _Num % _Max;
}
return _Num;
}
int pingPong(int _Num, int _MaxHalfIndex)
{
const int maxHalfCount = _MaxHalfIndex + 1;
const int maxFullIndex = _MaxHalfIndex * 2;
const int maxFullCount = (_MaxHalfIndex + 1) * 2;
int numInRange = _Num < 0
? _Num % maxFullCount == 0
? 0
: _Num % maxFullCount + maxFullCount
: _Num % maxFullCount;
if(numInRange > _MaxHalfIndex)
{
return maxFullIndex - numInRange;
}
return numInRange;
}
int wrapInt(int _Num, int _Min, int _Max)
{
int num = _Num - _Min;
int max = _Max - _Min;
return wrapInt(num, max) + _Min;
}
#endif

View File

@@ -1,4 +1,3 @@
#include "EyeTrainerMain.h"
EyeTrainerMain m_EyeTrainerMain;