I am going to put the files as I got them. Comments are translated from russian
BRIGHT.ino
void backlBrightTick() {
if (BACKL_MODE == 0 && backlBrightTimer.isReady()) {
if (backlMaxBright > 0) {
if (backlBrightDirection) {
if (!backlBrightFlag) {
backlBrightFlag = true;
backlBrightTimer.setInterval((float)BACKL_STEP / backlMaxBright / 2 * BACKL_TIME);
}
backlBrightCounter += BACKL_STEP;
if (backlBrightCounter >= backlMaxBright) {
backlBrightDirection = false;
backlBrightCounter = backlMaxBright;
}
} else {
backlBrightCounter -= BACKL_STEP;
if (backlBrightCounter <= BACKL_MIN_BRIGHT) {
backlBrightDirection = true;
backlBrightCounter = BACKL_MIN_BRIGHT;
backlBrightTimer.setInterval(BACKL_PAUSE);
backlBrightFlag = false;
}
}
setPWM(BACKL, getPWM_CRT(backlBrightCounter));
} else {
digitalWrite(BACKL, 0);
}
}
}
void dotBrightTick() {
if (dotBrightFlag && dotBrightTimer.isReady()) {
if (dotBrightDirection) {
dotBrightCounter += dotBrightStep;
if (dotBrightCounter >= dotMaxBright) {
dotBrightDirection = false;
dotBrightCounter = dotMaxBright;
}
} else {
dotBrightCounter -= dotBrightStep;
if (dotBrightCounter <= 0) {
dotBrightDirection = true;
dotBrightFlag = false;
dotBrightCounter = 0;
}
}
setPWM(DOT, getPWM_CRT(dotBrightCounter));
}
}
void changeBright() {
#if (NIGHT_LIGHT == 1)
// setting the brightness of all lights depending on the time of day
if ((hrs >= NIGHT_START && hrs <= 23)
|| (hrs >= 0 && hrs < NIGHT_END)) {
indiMaxBright = INDI_BRIGHT_N;
dotMaxBright = DOT_BRIGHT_N;
backlMaxBright = BACKL_BRIGHT_N;
} else {
indiMaxBright = INDI_BRIGHT;
dotMaxBright = DOT_BRIGHT;
backlMaxBright = BACKL_BRIGHT;
}
for (byte i = 0; i < 4; i++) {
indiDimm[i] = indiMaxBright;
}
dotBrightStep = ceil((float)dotMaxBright * 2 / DOT_TIME * DOT_TIMER);
if (dotBrightStep == 0) dotBrightStep = 1;
if (backlMaxBright > 0)
backlBrightTimer.setInterval((float)BACKL_STEP / backlMaxBright / 2 * BACKL_TIME);
indiBrightCounter = indiMaxBright;
//change PWM to apply backlMaxBright in case of maximum bright mode
if (BACKL_MODE == 1) setPWM(BACKL, backlMaxBright);
#endif
}
buttonSettings.ino
void settingsTick() {
if (curMode == 1) {
if (blinkTimer.isReady()) {
sendTime(changeHrs, changeMins);
lampState = !lampState;
if (lampState) {
anodeStates[0] = 1;
anodeStates[1] = 1;
anodeStates[2] = 1;
anodeStates[3] = 1;
} else {
if (!currentDigit) {
anodeStates[0] = 0;
anodeStates[1] = 0;
} else {
anodeStates[2] = 0;
anodeStates[3] = 0;
}
}
}
}
}
void buttonsTick() {
btnSet.tick();
btnL.tick();
btnR.tick();
if (curMode == 1) {
if (btnR.isClick()) {
if (!currentDigit) {
changeHrs++;
if (changeHrs > 23) changeHrs = 0;
} else {
changeMins++;
if (changeMins > 59) {
changeMins = 0;
changeHrs++;
if (changeHrs > 23) changeHrs = 0;
}
}
sendTime(changeHrs, changeMins);
}
if (btnL.isClick()) {
if (!currentDigit) {
changeHrs--;
if (changeHrs < 0) changeHrs = 23;
} else {
changeMins--;
if (changeMins < 0) {
changeMins = 59;
changeHrs--;
if (changeHrs < 0) changeHrs = 23;
}
}
sendTime(changeHrs, changeMins);
}
} else if (curMode == 0) {
// switching digital effects
if (btnR.isClick()) {
if (++FLIP_EFFECT >= FLIP_EFFECT_NUM) FLIP_EFFECT = 0;
EEPROM.put(0, FLIP_EFFECT);
flipTimer.setInterval(FLIP_SPEED[FLIP_EFFECT]);
for (byte i = 0; i < 4; i++) {
indiDimm[i] = indiMaxBright;
anodeStates[i] = 1;
}
// show effect
newTimeFlag = true;
for (byte i = 0; i < 4; i++) indiDigits[i] = FLIP_EFFECT;
}
// switching backlight effects
if (btnL.isClick()) {
if (++BACKL_MODE >= 3) BACKL_MODE = 0;
EEPROM.put(1, BACKL_MODE);
if (BACKL_MODE == 1) {
setPWM(BACKL, backlMaxBright);
} else if (BACKL_MODE == 2) {
digitalWrite(BACKL, 0);
}
}
// glitch switch
if (btnL.isHolded()) {
GLITCH_ALLOWED = !GLITCH_ALLOWED;
EEPROM.put(2, GLITCH_ALLOWED);
}
}
if (btnSet.isHolded()) {
anodeStates[0] = 1;
anodeStates[1] = 1;
anodeStates[2] = 1;
anodeStates[3] = 1;
currentDigit = false;
if (++curMode >= 2) curMode = 0;
switch (curMode) {
case 0:
hrs = changeHrs;
mins = changeMins;
secs = 0;
rtc.adjust(DateTime(2019, 12, 05, hrs, mins, 0));
changeBright();
break;
case 1:
changeHrs = hrs;
changeMins = mins;
break;
}
}
if (btnSet.isClick()) {
if (curMode == 1) currentDigit = !currentDigit; // time settings
}
}
effects.ino
void flipTick() {
if (FLIP_EFFECT == 0) {
sendTime(hrs, mins);
newTimeFlag = false;
} else if (FLIP_EFFECT == 1) {
if (!flipInit) {
flipInit = true;
// We remember which numbers have changed and will change their brightness.
for (byte i = 0; i < 4; i++) {
if (indiDigits[i] != newTime[i]) flipIndics[i] = true;
else flipIndics[i] = false;
}
}
if (flipTimer.isReady()) {
if (!indiBrightDirection) {
indiBrightCounter--; // reduce brightness
if (indiBrightCounter <= 0) { // if the brightness is less than zero
indiBrightDirection = true; // change the direction of change
indiBrightCounter = 0; // reset the brightness
sendTime(hrs, mins); // change numbers
}
} else {
indiBrightCounter++; // increase brightness
if (indiBrightCounter >= indiMaxBright) { // reached the limit
indiBrightDirection = false; // change direction
indiBrightCounter = indiMaxBright; // set the maximum
// exit the cycle of change
flipInit = false;
newTimeFlag = false;
}
}
for (byte i = 0; i < 4; i++)
if (flipIndics[i]) indiDimm[i] = indiBrightCounter; // apply brightness
}
} else if (FLIP_EFFECT == 2) {
if (!flipInit) {
flipInit = true;
// We remember which numbers have changed and will change them.
for (byte i = 0; i < 4; i++) {
if (indiDigits[i] != newTime[i]) flipIndics[i] = true;
else flipIndics[i] = false;
}
}
if (flipTimer.isReady()) {
byte flipCounter = 0;
for (byte i = 0; i < 4; i++) {
if (flipIndics[i]) {
indiDigits[i]--;
if (indiDigits[i] < 0) indiDigits[i] = 9;
if (indiDigits[i] == newTime[i]) flipIndics[i] = false;
} else {
flipCounter++; // a counter of numbers that do not need to be changed
}
}
if (flipCounter == 4) { // If none of the 4 digits need to be changed, // exit the change loop.
flipInit = false;
newTimeFlag = false;
}
}
//byte cathodeMask[] = {1, 0, 2, 9, 3, 8, 4, 7, 5, 6}; //
order of cathodes in14 tube
} else if (FLIP_EFFECT == 3) {
if (!flipInit) {
flipInit = true;
// We remember which numbers have changed and will change them.
for (byte i = 0; i < 4; i++) {
if (indiDigits[i] != newTime[i]) {
flipIndics[i] = true;
for (byte c = 0; c < 10; c++) {
if (cathodeMask[c] == indiDigits[i]) startCathode[i] = c;
if (cathodeMask[c] == newTime[i]) endCathode[i] = c;
}
} else flipIndics[i] = false;
}
}
if (flipTimer.isReady()) {
byte flipCounter = 0;
for (byte i = 0; i < 4; i++) {
if (flipIndics[i]) {
if (startCathode[i] > endCathode[i]) {
startCathode[i]--;
indiDigits[i] = cathodeMask[startCathode[i]];
} else if (startCathode[i] < endCathode[i]) {
startCathode[i]++;
indiDigits[i] = cathodeMask[startCathode[i]];
} else {
flipIndics[i] = false;
}
} else {
flipCounter++;
}
}
if (flipCounter == 4) { // If none of the 4 digits need to be changed, // exit the change loop.
flipInit = false;
newTimeFlag = false;
}
}
}
// --- train --- //
else if (FLIP_EFFECT == 4) {
if (!flipInit) {
flipInit = true;
currentLamp = 0;
trainLeaving = true;
flipTimer.reset();
}
if (flipTimer.isReady()) {
if (trainLeaving) {
for (byte i = 3; i > currentLamp; i--) {
indiDigits[i] = indiDigits[i - 1];
}
anodeStates[currentLamp] = 0;
currentLamp++;
if (currentLamp >= 4) {
trainLeaving = false; //coming
currentLamp = 0;
//sendTime(hrs, mins);
}
} else { //trainLeaving == false
for (byte i = currentLamp; i > 0; i--) {
indiDigits[i] = indiDigits[i - 1];
}
indiDigits[0] = newTime[3 - currentLamp];
anodeStates[currentLamp] = 1;
currentLamp++;
if (currentLamp >= 4) {
flipInit = false;
newTimeFlag = false;
}
}
}
}
// --- elastic band --- //
else if (FLIP_EFFECT == 5) {
if (!flipInit) {
flipInit = true;
flipEffectStages = 0;
flipTimer.reset();
}
if (flipTimer.isReady()) {
switch (flipEffectStages++) {
case 1:
anodeStates[3] = 0;
break;
case 2:
anodeStates[2] = 0;
indiDigits[3] = indiDigits[2];
anodeStates[3] = 1;
break;
case 3:
anodeStates[3] = 0;
break;
case 4:
anodeStates[1] = 0;
indiDigits[2] = indiDigits[1];
anodeStates[2] = 1;
break;
case 5:
anodeStates[2] = 0;
indiDigits[3] = indiDigits[1];
anodeStates[3] = 1;
break;
case 6:
anodeStates[3] = 0;
break;
case 7:
anodeStates[0] = 0;
indiDigits[1] = indiDigits[0];
anodeStates[1] = 1;
break;
case 8:
anodeStates[1] = 0;
indiDigits[2] = indiDigits[0];
anodeStates[2] = 1;
break;
case 9:
anodeStates[2] = 0;
indiDigits[3] = indiDigits[0];
anodeStates[3] = 1;
break;
case 10:
anodeStates[3] = 0;
//sendTime(hrs,mins);
break;
case 11:
indiDigits[0] = newTime[3];
anodeStates[0] = 1;
break;
case 12:
anodeStates[0] = 0;
indiDigits[1] = newTime[3];
anodeStates[1] = 1;
break;
case 13:
anodeStates[1] = 0;
indiDigits[2] = newTime[3];
anodeStates[2] = 1;
break;
case 14:
anodeStates[2] = 0;
indiDigits[3] = newTime[3];
anodeStates[3] = 1;
break;
case 15:
indiDigits[0] = newTime[2];
anodeStates[0] = 1;
break;
case 16:
anodeStates[0] = 0;
indiDigits[1] = newTime[2];
anodeStates[1] = 1;
break;
case 17:
anodeStates[1] = 0;
indiDigits[2] = newTime[2];
anodeStates[2] = 1;
break;
case 18:
indiDigits[0] = newTime[1];
anodeStates[0] = 1;
break;
case 19:
anodeStates[0] = 0;
indiDigits[1] = newTime[1];
anodeStates[1] = 1;
break;
case 20:
indiDigits[0] = newTime[0];
anodeStates[0] = 1;
break;
case 21:
flipInit = false;
newTimeFlag = false;
}
}
}
}
func.ino
void burnIndicators() {
for (byte k = 0; k < BURN_LOOPS; k++) {
for (byte d = 0; d < 10; d++) {
for (byte i = 0; i < 4; i++) {
indiDigits[i]--;
if (indiDigits[i] < 0) indiDigits[i] = 9;
}
delay(BURN_TIME);
}
}
}
glitch.ino
void glitchTick() {
if (!glitchFlag && secs > 7 && secs < 55) {
if (glitchTimer.isReady()) {
glitchFlag = true;
indiState = 0;
glitchCounter = 0;
glitchMax = random(2, 6);
glitchIndic = random(0, 4);
glitchTimer.setInterval(random(1, 6) * 20);
}
} else if (glitchFlag && glitchTimer.isReady()) {
indiDimm[glitchIndic] = indiState * indiMaxBright;
indiState = !indiState;
glitchTimer.setInterval(random(1, 6) * 20);
glitchCounter++;
if (glitchCounter > glitchMax) {
glitchTimer.setInterval(random(GLITCH_MIN * 1000L, GLITCH_MAX * 1000L));
glitchFlag = false;
indiDimm[glitchIndic] = indiMaxBright;
}
}
}
isr.ino
// dynamic indication in timer interrupt 2
ISR(TIMER2_COMPA_vect) {
indiCounter[curIndi]++; // indicator counter
if (indiCounter[curIndi] >= indiDimm[curIndi]) // if the dimming threshold has been reached
setPin(opts[curIndi], 0); // turn off the current indicator
if (indiCounter[curIndi] > 25) { // reached the threshold of 25 units
indiCounter[curIndi] = 0; // nixie lamp counter reset
if (++curIndi >= 4) curIndi = 0; // nixie lamp change loop
// send a digit from the indiDigits array according to the lamp type
if (indiDimm[curIndi] > 0) {
byte thisDig = digitMask[indiDigits[curIndi]];
setPin(DECODER3, bitRead(thisDig, 0));
setPin(DECODER1, bitRead(thisDig, 1));
setPin(DECODER0, bitRead(thisDig, 2));
setPin(DECODER2, bitRead(thisDig, 3));
setPin(opts[curIndi], anodeStates[curIndi]); // turn on the anode to the current lamp
}
}
}
nixieClock_2_v2.5.ino
/*
Sketch for the project "Clock on GRI version 2"
Project page (diagrams, descriptions): https://alexgyver.ru/nixieclock_v2/
Sources on GitHub: https://github.com/AlexGyver/NixieClock_v2
Like the way the code is written? Support the author! https://alexgyver.ru/support_alex/
Author: AlexGyver Technologies, 2018
https://AlexGyver.ru/
*/
/*
Control:
- Time setting:
- Left button - selection, the rest are "greater" and "less"
- Hold "select" - time setting
- Click on "select" - change hour/minute setting
- Click "more" and "less" - change the time
- Hold "select" - return to clock mode
- Effect control IN CLOCK MODE:
- Holding the central button turns on and off the "glitches"
- Click on the central button to switch the lighting modes of the lamps
- Breath
- Permanent glow
- Disabled
- Clicking on the right button toggles the number scrolling modes
- no effect
- Smooth fade
- Rewind in numerical order
- Rewind on cathodes
- Train
- Elastic
*/
/*
Version 2.0 (together with Pavel Smelov):
- Fixed jambs with backlight
- Added switching effects "train" and "gum"
- Optimized code
Version 2.1:
- Fixed a bug with the passage of time
Version 2.2:
- Reset seconds when setting time
Version 2.3:
- Added advanced brightness settings
- Fixed freezes at zero values of some settings
Version 2.4:
- We continue to fix bugs with zero settings
Version 2.5 ***THIS VERSION***:
- Demonstration of the effect when selected
*/
// ************************** SETTINGS **************************
#define BOARD_TYPE 0
// clock board type:
// 0 - IN-12 turned (indicators are correct)
// 1 - IN-12 (indicators are upside down)
// 2 - IN-14 (regular and neon dot)
// 3 other indicators
#define DUTY 200 // PWM duty cycle. The voltage depends on the duty cycle! I have 175 volts at 180 and 145 volts at 120
// ======================= EFFECTS ========================
// clock flip effects
byte FLIP_EFFECT = 0;
// The selected one is active at the first start and is changed by buttons. Memorized in memory
// 0 - no effect
// 1 - fade in and out (recommended speed: 100-150)
// 2 - rewind in numerical order (recommended speed: 50-80)
// 3 - rewind in order of the cathodes in the lamp (recommended speed: 30-50)
// 4 - train (recommended speed: 50-170)
// 5 - elastic band (recommended speed: 50-150)
// ======================= BRIGHTNESS =======================
#define NIGHT_LIGHT 1 // Change brightness from time of day (1 on, 0 off)
#define NIGHT_START 23 // hour of transition to night illumination (BRIGHT_N)
#define NIGHT_END 7 // hour of transition to daylight (BRIGHT)
#define INDI_BRIGHT 23 // the brightness of the digits is daytime (1 - 24)! 24 may create burn-in!
#define INDI_BRIGHT_N 8 // night brightness (1 - 24)
#define DOT_BRIGHT 35 // dot brightness daytime (1 - 255)
#define DOT_BRIGHT_N 15 // dot brightness at night (1 - 255)
#define BACKL_BRIGHT 250 // Max. daytime lamp brightness (0 - 255)
#define BACKL_BRIGHT_N 50 // Max. lamp backlight brightness at night (0 - 255, 0 - backlight off)
#define BACKL_MIN_BRIGHT 20 // min. brightness of lamp illumination in breathing mode (0 - 255)
#define BACKL_PAUSE 400 // "darkness" interval between flashes of lamp illumination in breathing mode, ms
// ======================= Glitches =======================
#define GLITCH_MIN 30 // minimum time between glitches, seconds
#define GLITCH_MAX 120 // maximum time between glitches, seconds
// ====================== BLINK =======================
#define DOT_TIME 500 // dot flashing time, ms
#define DOT_TIMER 20 // dot brightness step, ms
#define BACKL_STEP 2 // backlight flashing step
#define BACKL_TIME 5000 // backlight period, ms
// ================== ANTI BURN-IN ====================
#define BURN_TIME 10 // indicator bypass period in cleaning mode, ms
#define BURN_LOOPS 3 // number of cleaning cycles for each period
#define BURN_PERIOD 15 // anti-burn-in period, minutes
// *********************** FOR DEVELOPERS ***********************
byte BACKL_MODE = 0; // The selected one is active at startup and can be changed with buttons
byte FLIP_SPEED[] = { 0, 130, 50, 40, 70, 70 }; // effects speed, ms (do not change the number)
byte FLIP_EFFECT_NUM = sizeof(FLIP_SPEED); // number of effects
boolean GLITCH_ALLOWED = 1; // 1 - enable, 0 - disable glitches. Button controlled
// --------- ALARM ---------
#define ALM_TIMEOUT 30 // alarm timeout
#define FREQ 900 // alarm frequency
// pins
#define PIEZO 2 // alarm
#define KEY0 3 // hours
#define KEY1 4 // hours
#define KEY2 5 // minutes
#define KEY3 6 // minutes
#define BTN1 7 // button 1
#define BTN2 8 // button 2
#define GEN 9 // generator
#define DOT 10 // point
#define BACKL 11 // backlight
#define BTN3 12 // button 3
// decoder
#define DECODER0 A0
#define DECODER1 A1
#define DECODER2 A2
#define DECODER3 A3
// lamp pinout
#if (BOARD_TYPE == 0)
const byte digitMask[] = { 7, 3, 6, 4, 1, 9, 8, 0, 5, 2 }; // board decoder mask IN-12-turned (normal numbers)
const byte opts[] = { KEY0, KEY1, KEY2, KEY3 }; // order of indicators from left to right
const byte cathodeMask[] = { 1, 6, 2, 7, 5, 0, 4, 9, 8, 3 }; // cathode order IN-12
#elif (BOARD_TYPE == 1)
const byte digitMask[] = { 2, 8, 1, 9, 6, 4, 3, 5, 0, 7 }; // board decoder mask IN-12 (numbers upside down)
const byte opts[] = { KEY3, KEY2, KEY1, KEY0 }; // order of indicators from right to left (for IN-12 turned) and IN-14
const byte cathodeMask[] = { 1, 6, 2, 7, 5, 0, 4, 9, 8, 3 }; // cathode order IN-12
#elif (BOARD_TYPE == 2)
const byte digitMask[] = { 9, 8, 0, 5, 4, 7, 3, 6, 2, 1 }; // IN-14 board decoder mask
const byte opts[] = { KEY3, KEY2, KEY1, KEY0 }; // order of indicators from right to left (for IN-12 turned) and IN-14
const byte cathodeMask[] = { 1, 0, 2, 9, 3, 8, 4, 7, 5, 6 }; // cathode order IN-14
#elif (BOARD_TYPE == 3)
const byte digitMask[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // here enter your order of pins
const byte opts[] = { KEY0, KEY1, KEY2, KEY3 }; // own order of indicators
const byte cathodeMask[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // and its order of cathodes
#endif
/*
ард ног ном
А0 7 4
А1 6 2
А2 4 8
А3 3 1
*/
timeFunctions.ino
// send to indicators
void sendTime(byte hours, byte minutes) {
indiDigits[0] = (byte)hours / 10;
indiDigits[1] = (byte)hours % 10;
indiDigits[2] = (byte)minutes / 10;
indiDigits[3] = (byte)minutes % 10;
}
// for effects
void setNewTime() {
newTime[0] = (byte)hrs / 10;
newTime[1] = (byte)hrs % 10;
newTime[2] = (byte)mins / 10;
newTime[3] = (byte)mins % 10;
}
}
timer2Minim.h
// Mini Timer Class, Version 2.0
// An improved millisecond timer algorithm was used
// The algorithm is slightly slower, but provides multiple intervals and protection against gaps and overflows
class timerMinim {
public:
timerMinim(uint32_t interval); // timer declaration with interval specification
void setInterval(uint32_t interval); // setting the timer interval
boolean isReady(); // Returns true when the time has come. Resets to false automatically (AUTO) or manually (MANUAL)
void reset(); // manual reset of the timer to a set interval
private:
uint32_t _timer = 0;
uint32_t _interval = 0;
};
timerMinim::timerMinim(uint32_t interval) {
_interval = (interval != 0) ? interval : 5;
_timer = millis();
}
void timerMinim::setInterval(uint32_t interval) {
_interval = (interval != 0) ? interval : 5;
_interval = interval;
}
boolean timerMinim::isReady() {
uint32_t thisMls = millis();
if (thisMls - _timer >= _interval) {
do {
_timer += _interval;
if (_timer < _interval) break; // uint32_t overflow
} while (_timer < thisMls - _interval); // step skip protection
return true;
} else {
return false;
}
}
void timerMinim::reset() {
_timer = millis();
}
timeTicker.ino
byte minsCount = 0;
void calculateTime() {
dotFlag = !dotFlag;
if (dotFlag) {
dotBrightFlag = true;
dotBrightDirection = true;
dotBrightCounter = 0;
secs++;
if (secs > 59) {
newTimeFlag = true; // flag that the time needs to be changed
secs = 0;
mins++;
minsCount++;
if (minsCount >= 15) { // every 15 minutes
minsCount = 0;
DateTime now = rtc.now(); // synchronization with RTC
secs = now.second();
mins = now.minute();
hrs = now.hour();
}
if (mins % BURN_PERIOD == 0) burnIndicators(); // we're cleaning, we're cleaning!
/*if (!alm_flag && alm_mins == mins && alm_hrs == hrs && true) {
mode = 0;
alm_flag = true;
almTimer.start();
almTimer.reset();
}*/
}
if (mins > 59) {
mins = 0;
hrs++;
if (hrs > 23) hrs = 0;
changeBright();
}
if (newTimeFlag) setNewTime(); // updating the time array
/*
if (mode == 0) sendTime(hrs, mins);
if (alm_flag) {
if (almTimer.isReady() || true ) {
alm_flag = false;
almTimer.stop();
mode = 0;
noTone(PIEZO);
}
}
*/
}
/*
// blink on the alarm clock
if (alm_flag) {
if (!dotFlag) {
noTone(PIEZO);
for (byte i = 1; i < 7; i++) digitsDraw[i] = 10;
} else {
tone(PIEZO, FREQ);
sendTime(hrs, mins);
}
}
*/
}
0_data.ino
// libraries
#include "timer2Minim.h"
#include <GyverButton.h>
#include <Wire.h>
#include <RTClib.h>
#include <EEPROM.h>
RTC_DS3231 rtc;
// timers
timerMinim dotTimer(500); // half-second timer for a watch
timerMinim dotBrightTimer(DOT_TIMER); // dot brightness step timer
timerMinim backlBrightTimer(30); // backlight brightness step timer
timerMinim almTimer((long)ALM_TIMEOUT * 1000);
timerMinim flipTimer(FLIP_SPEED[FLIP_EFFECT]);
timerMinim glitchTimer(1000);
timerMinim blinkTimer(500);
// buttons
GButton btnSet(BTN1, HIGH_PULL, NORM_OPEN);
GButton btnL(BTN2, HIGH_PULL, NORM_OPEN);
GButton btnR(BTN3, HIGH_PULL, NORM_OPEN);
// variables
volatile int8_t indiDimm[4]; // dimming value (0-24)
volatile int8_t indiCounter[4]; // counter of each indicator (0-24)
volatile int8_t indiDigits[4]; // the numbers that the indicators should show(0-10)
volatile int8_t curIndi; // current indicator (0-3)
boolean dotFlag;
int8_t hrs, mins, secs;
int8_t alm_hrs, alm_mins;
int8_t mode = 0; // 0 clock, 1 temperature, 2 alarm setting, 3 clock setting, 4 alarm
boolean changeFlag;
boolean blinkFlag;
byte indiMaxBright = INDI_BRIGHT, dotMaxBright = DOT_BRIGHT, backlMaxBright = BACKL_BRIGHT;
boolean alm_flag;
boolean dotBrightFlag, dotBrightDirection, backlBrightFlag, backlBrightDirection, indiBrightDirection;
int dotBrightCounter, backlBrightCounter, indiBrightCounter;
byte dotBrightStep;
boolean newTimeFlag;
boolean flipIndics[4];
byte newTime[4];
boolean flipInit;
byte startCathode[4], endCathode[4];
byte glitchCounter, glitchMax, glitchIndic;
boolean glitchFlag, indiState;
byte curMode = 0;
boolean currentDigit = false;
int8_t changeHrs, changeMins;
boolean lampState = false;
boolean anodeStates[] = {1, 1, 1, 1};
byte currentLamp, flipEffectStages;
bool trainLeaving;
const uint8_t CRTgamma[256] PROGMEM = {
0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 4, 4,
4, 4, 4, 5, 5, 5, 5, 6,
6, 6, 7, 7, 7, 8, 8, 8,
9, 9, 9, 10, 10, 10, 11, 11,
12, 12, 12, 13, 13, 14, 14, 15,
15, 16, 16, 17, 17, 18, 18, 19,
19, 20, 20, 21, 22, 22, 23, 23,
24, 25, 25, 26, 26, 27, 28, 28,
29, 30, 30, 31, 32, 33, 33, 34,
35, 35, 36, 37, 38, 39, 39, 40,
41, 42, 43, 43, 44, 45, 46, 47,
48, 49, 49, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 62,
63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76, 77, 79,
80, 81, 82, 83, 84, 85, 87, 88,
89, 90, 91, 93, 94, 95, 96, 98,
99, 100, 101, 103, 104, 105, 107, 108,
109, 110, 112, 113, 115, 116, 117, 119,
120, 121, 123, 124, 126, 127, 129, 130,
131, 133, 134, 136, 137, 139, 140, 142,
143, 145, 146, 148, 149, 151, 153, 154,
156, 157, 159, 161, 162, 164, 165, 167,
169, 170, 172, 174, 175, 177, 179, 180,
182, 184, 186, 187, 189, 191, 193, 194,
196, 198, 200, 202, 203, 205, 207, 209,
211, 213, 214, 216, 218, 220, 222, 224,
226, 228, 230, 232, 233, 235, 237, 239,
241, 243, 245, 247, 249, 251, 253, 255,
};
byte getPWM_CRT(byte val) {
return pgm_read_byte(&(CRTgamma[val]));
}
// fast digitalWrite
void setPin(uint8_t pin, uint8_t x) {
switch (pin) { // откл pwm
case 3: // 2B
bitClear(TCCR2A, COM2B1);
break;
case 5: // 0B
bitClear(TCCR0A, COM0B1);
break;
case 6: // 0A
bitClear(TCCR0A, COM0A1);
break;
case 9: // 1A
bitClear(TCCR1A, COM1A1);
break;
case 10: // 1B
bitClear(TCCR1A, COM1B1);
break;
case 11: // 2A
bitClear(TCCR2A, COM2A1);
break;
}
if (pin < 8) bitWrite(PORTD, pin, x);
else if (pin < 14) bitWrite(PORTB, (pin - 8), x);
else if (pin < 20) bitWrite(PORTC, (pin - 14), x);
else return;
}
// fast analogWrite
void setPWM(uint8_t pin, uint16_t duty) {
if (duty == 0) setPin(pin, LOW);
else {
switch (pin) {
case 5:
bitSet(TCCR0A, COM0B1);
OCR0B = duty;
break;
case 6:
bitSet(TCCR0A, COM0A1);
OCR0A = duty;
break;
case 10:
bitSet(TCCR1A, COM1B1);
OCR1B = duty;
break;
case 9:
bitSet(TCCR1A, COM1A1);
OCR1A = duty;
break;
case 3:
bitSet(TCCR2A, COM2B1);
OCR2B = duty;
break;
case 11:
bitSet(TCCR2A, COM2A1);
OCR2A = duty;
break;
default:
break;
}
}
}
1_setup.ino
void setup() {
//Serial.begin(9600);
// random seed for a random number generator
randomSeed(analogRead(6) + analogRead(7));
// Setting up output pins
pinMode(DECODER0, OUTPUT);
pinMode(DECODER1, OUTPUT);
pinMode(DECODER2, OUTPUT);
pinMode(DECODER3, OUTPUT);
pinMode(KEY0, OUTPUT);
pinMode(KEY1, OUTPUT);
pinMode(KEY2, OUTPUT);
pinMode(KEY3, OUTPUT);
pinMode(PIEZO, OUTPUT);
pinMode(GEN, OUTPUT);
pinMode(DOT, OUTPUT);
pinMode(BACKL, OUTPUT);
// we set the PWM frequency on pins 9 and 10 to 31 kHz
TCCR1B = TCCR1B & 0b11111000 | 1; // we put the divisor 1
// turn on PWM
setPWM(9, DUTY);
// We reconfigure the PWM frequency on pins 3 and 11 to 7.8 kHz and enable COMPA interrupts.
TCCR2B = (TCCR2B & B11111000) | 2; // divisor 8
TCCR2A |= (1 << WGM21); // enable CTC mode for COMPA
TIMSK2 |= (1 << OCIE2A); // enable COMPA match interrupts
// ---------- RTC -----------
rtc.begin();
if (rtc.lostPower()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
DateTime now = rtc.now();
secs = now.second();
mins = now.minute();
hrs = now.hour();
// EEPROM
if (EEPROM.read(1023) != 100) { // first launch
EEPROM.put(1023, 100);
EEPROM.put(0, FLIP_EFFECT);
EEPROM.put(1, BACKL_MODE);
EEPROM.put(2, GLITCH_ALLOWED);
}
EEPROM.get(0, FLIP_EFFECT);
EEPROM.get(1, BACKL_MODE);
EEPROM.get(2, GLITCH_ALLOWED);
/*if (EEPROM.read(100) != 66) { // First run check. 66 randomly
EEPROM.write(100, 66);
EEPROM.write(0, 0); // alarm clock
EEPROM.write(1, 0); // alarm minutes
}
alm_hrs = EEPROM.read(0);
alm_mins = EEPROM.read(1);*/
sendTime(hrs, mins); // send time to indicators
changeBright(); // change brightness according to the time of day
// set the brightness on the indicators
for (byte i = 0; i < 4; i++)
indiDimm[i] = indiMaxBright;
// calculation of the brightness step of a point
dotBrightStep = ceil((float)dotMaxBright * 2 / DOT_TIME * DOT_TIMER);
if (dotBrightStep == 0) dotBrightStep = 1;
// breathing backlight
if (backlMaxBright > 0)
backlBrightTimer.setInterval((float)BACKL_STEP / backlMaxBright / 2 * BACKL_TIME);
// initial period of glitches
glitchTimer.setInterval(random(GLITCH_MIN * 1000L, GLITCH_MAX * 1000L));
indiBrightCounter = indiMaxBright;
// mode speed at startup
flipTimer.setInterval(FLIP_SPEED[FLIP_EFFECT]);
//almTimer.stop();
}
2_loop.ino
void loop() {
if (dotTimer.isReady()) calculateTime(); // every 500 ms recalculation and sending of time
if (newTimeFlag && curMode == 0) flipTick(); // flipping numbers
dotBrightTick(); // smooth blinking of the dot
backlBrightTick(); // smooth blinking of lamp backlight
if (GLITCH_ALLOWED && mode == 0) glitchTick(); // glitches
buttonsTick(); // buttons
settingsTick(); // settings