1

12 Commits

Author SHA1 Message Date
eleksir
2dc7e9ec79 Init battery for ds3231 explicitly, code style fixes 2025-02-07 19:14:46 +03:00
eleksir
24d457f63c Remove .gitignore, it does not contain project-specific settings 2025-02-07 18:40:29 +03:00
eleksir
0c91c05486 Migrate Grove RTC DS1307 -> uRTCLib 2019-10-20 00:05:50 +03:00
eleksir
acc53eaa74 Implement simple dim 2019-10-07 03:35:01 +03:00
eleksir
ccb12d2076 Move Time Compensation feature under define
* move Time Compensation feature under define
* translte last few eol to unix style
* make correction: in my case with bad chrystal rtc runs faster about
  9.5 seconds per day
2019-10-07 03:02:25 +03:00
eleksir
ee1dc7bbea Adjusted brightness table, simplify display drawing algo. 2019-08-17 11:20:26 +03:00
eleksir
bd1f945ad3 Update display text only by interrupt or on button press
* Re-calculate displayed time only ater rtcsync or when time adjusted by
  buttons
2019-08-04 21:32:34 +03:00
eleksir
ea293ec335 Implement ISR and timers
* Poll RTC by trigger, set via ISR
* Change blink state by trigger, set via ISR
* Set time compensation trigger in ISR
* Refresh clock by timer
* Poll photoresistor by same timer as above
* Stop continuously calculate brightness, do it by the same timer as above
2019-07-27 15:34:03 +03:00
eleksir
0afc0383c4 Cleanup after moving from mercurial to git 2019-06-12 12:47:14 +03:00
eleksir
732ade0819 Add ability to correct 0.5 second per day
+ Added ability to fix timw skew of 1 second per two
   days
* Removed text of license as it shipped as file
* Fixed some grammar mistekes
* compflag now boolean
* Coding style fixes
* Tinker a bit values for brightness, again. Make display
   more dusk.

--HG--
branch : phtoresistor
2019-06-02 15:06:04 +03:00
eleksir
edeb2d3501 Added tag v1.3 for changeset 20da600eb0cc
--HG--
branch : phtoresistor
2019-05-21 16:18:38 +03:00
eleksir
352dc5fa4c Added photoresistor and amb. light compensation
+ Added ambient light compensation logic
+ Added photocell on scheme
+ Added photoresitor to README.md

--HG--
branch : phtoresistor
2019-05-21 16:00:01 +03:00
4 changed files with 199 additions and 80 deletions

6
.gitignore vendored
View File

@@ -1,6 +0,0 @@
.vscode/*
*.swp
*.bak
*.old
*.orig
*.clang-format

View File

@@ -12,6 +12,8 @@
* usb phone charger as power supply * usb phone charger as power supply
* 2 push buttons * 2 push buttons
* 1 on/off switch * 1 on/off switch
* 1 photoresistor
* 1 resistor to make secund shoulder of photosensitive "bridge", 10 kOhms, 0.25 watts
* Gainta G521G enclosure (92x66.5x28мм) * Gainta G521G enclosure (92x66.5x28мм)
* 5m power cord * 5m power cord
@@ -24,12 +26,3 @@
### Additional Notes: ### Additional Notes:
My RTC module is not very accurate due to bad quartz chrystal calibration, looks like it is not exactly 32768 Hz and clock runs faster than it should be, giving about 5-8 seconds runaway per day. So i have to implement time correction in software (temperature changes during day/month/year is not very big - about 10 degrees Celsius, so we can use software compensation without thermal compensation). My RTC module is not very accurate due to bad quartz chrystal calibration, looks like it is not exactly 32768 Hz and clock runs faster than it should be, giving about 5-8 seconds runaway per day. So i have to implement time correction in software (temperature changes during day/month/year is not very big - about 10 degrees Celsius, so we can use software compensation without thermal compensation).
This project split into 2 branches:
* **default** that contains generic clock without any additional elements
* **photoresistor** that contains additional hardware - photocell bridge - and software algorithm that compensates ambient light sensed by photocell basing on LED display charachteristics.
### Stable tags for branches:
v1.6 - default
v1.7 - photoresistor

Binary file not shown.

View File

@@ -1,20 +1,35 @@
/* /* 7 Segment 4 Digits clock with digit point separator.
* 7 Segment 4 Digits clock with digit point separator
* *
* It uses display module with common anode and DS1307 RTC module to enhance * It uses display module with common anode and DS1307/DS3231 RTC module to
* time keeping ability of Atmega 328p based Arduino Nano microcontroller and * enhance time keeping ability of Atmega 328p based Arduino Nano
* 2 buttons for adjusting time. * microcontroller and 2 buttons for adjusting time. */
*/
/* DS1307 lib by FrankieChu for Seeed RTC module, in my case /* uRTCLib
* Grove___RTC_DS1307-1.0.0.zip */ * https://github.com/Naguissa/uRTCLib
#include <DS1307.h> * it has support for both DS1307 and DS3231, that I have.
* It is more fat than ds1307-dedicated or ds3231-dedicated lib, but I do not
* want to re-invent wheel if I have one. */
#include <uRTCLib.h>
/* SevSeg
* https://github.com/DeanIsMe/SevSeg
* it has simple api and ideally works in my case. */
#include <SevSeg.h> #include <SevSeg.h>
DS1307 clock; /* Disable time compensation.
* At last we borrow more accurate chrystal and solder it to rtc breakboard.
*/
//#define TIMECOMP
// 0.5 second/day compensation flag.
//#define OFIVECOMP
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
uRTCLib clock;
SevSeg Display; SevSeg Display;
/* push button stuff */ // Push button stuff.
const long initial_delay = 600; // wait a bit before spin numbers const long initial_delay = 600; // wait a bit before spin numbers
const long adjust_interval = 200; // delay between consecutive number increments const long adjust_interval = 200; // delay between consecutive number increments
const long bounce_delay = 20; // contact bounce compensation const long bounce_delay = 20; // contact bounce compensation
@@ -23,131 +38,187 @@ const int min_btn = A1; // put minute adjusting button on A1 lane
volatile bool btn_pushed = false; // is button pushed? volatile bool btn_pushed = false; // is button pushed?
unsigned long previousPushTimer = 0; unsigned long previousPushTimer = 0;
/* dot blinking stuff */ // Dot blinking stuff.
volatile bool dot = true; volatile bool dot = true;
/* time inaccuracy compensation flag */ #ifdef TIMECOMP
// Time inaccuracy compensation flag.
volatile bool compflag1 = true; volatile bool compflag1 = true;
volatile bool compflag2 = false; volatile bool compflag2 = false;
const uint8_t timeSkew = 8; // assume that clock runs faster than should const uint8_t timeSkew = 9; // Assume that clock runs faster than should.
volatile bool timeSkewEven = false; // comp. 1 additional second per 2 days #ifdef OFIVECOMP
volatile bool timeSkewEven = false; // Comp. 1 additional second per 2 days.
#endif
#endif
/* time vars */ // Ambient light compensation aka night mode.
const int photoPin = A2;
volatile bool measureAmbLight = true;
unsigned int ambLight = 0; // Actual value.
unsigned long previousLightTimer = 0;
const long refresh_interval = 40; // Refresh rate 25Hz.
// Time vars.
volatile uint8_t mysecond; volatile uint8_t mysecond;
volatile uint8_t myminute; volatile uint8_t myminute;
volatile uint8_t myhour; volatile uint8_t myhour;
/* rtc sync flag */ // RTC sync flag.
volatile bool rtcsync = true; volatile bool rtcsync = true;
void setup() void setup()
{ {
// setup display // Setup display.
byte hardwareConfig = COMMON_ANODE; byte hardwareConfig = COMMON_ANODE;
byte numDigits = 4; byte numDigits = 4;
byte digitPins[] = {9, 10, 11, 12}; byte digitPins[] = {9, 10, 11, 12};
byte segmentPins[] = {2, 3, 4, 5, 6, 7, 8, 13}; byte segmentPins[] = {2, 3, 4, 5, 6, 7, 8, 13};
bool resistorsOnSegments = true; // 8 resistors, 330 Ohms each bool resistorsOnSegments = true; // 8 resistors, 330 Ohms each
bool updateWithDelays = false; // Default. Recommended /* updateWithDelays defaults to false and it is recommended value,
* but I was unable to make my display to change brightness notably with
* recommended value, so i have to use updateWithDelays=true,
* in this case brightness gets changed. */
bool updateWithDelays = true;
/* without it there is visual bug at 0 o'clock, and in current version /* Without it there is visual bug at 0 o'clock, and in current version
* of SevSeg' setChars() does not dots at all */ * of SevSeg' setChars() does not dots at all. */
bool leadingZeros = false; bool leadingZeros = false;
Display.begin(hardwareConfig, numDigits, digitPins, segmentPins, Display.begin(hardwareConfig, numDigits, digitPins, segmentPins,
resistorsOnSegments, updateWithDelays, leadingZeros); resistorsOnSegments, updateWithDelays, leadingZeros);
Display.setBrightness(100); Display.setBrightness(100);
// setup clock // Init i2c bus and setup clock address.
clock.begin(); Wire.begin();
clock.set_rtc_address(0x68);
// if rtc returns incorrect time, reset it to some default value // If rtc returns incorrect time, reset it to some default values.
clock.getTime(); clock.refresh();
if ((clock.hour > 23) || (clock.minute > 59)) { if (clock.lostPower()) {
clock.fillByYMD(2019, 2, 23); // 23 Feb 2019 clock.set(30, 52, 15, 6, 23, 2, 19); // Saturday, 23 Feb 2019 15:52:30
clock.fillByHMS(15, 52, 30); // 15:52:30" clock.lostPowerClear();
clock.fillDayOfWeek(SAT); // Saturday
clock.setTime(); // put date to rtc
} }
// setup control buttons if ((clock.hour() > 23) || (clock.minute() > 59)) {
clock.set(30, 52, 15, 6, 23, 2, 19); // Saturday, 23 Feb 2019 15:52:30
}
/* In case of ds1307 this will be noop, but ds3231/ds3232 sometimes does
* not enable battery by default. */
clock.enableBattery();
// Setup control buttons.
pinMode(hrs_btn, INPUT_PULLUP); pinMode(hrs_btn, INPUT_PULLUP);
pinMode(min_btn, INPUT_PULLUP); pinMode(min_btn, INPUT_PULLUP);
/* Timer stuff /* Timer stuff.
* timer0 being used in millis() micros() delay() etc, it has 8 bit resolution * timer0 being used in millis() micros() delay() etc, it has 8 bit resolution.
* timer2 being used in analogWrite() tone() notone(), it has 8 bit resolution * timer2 being used in analogWrite() tone() notone(), it has 8 bit resolution.
* timer1 being used in servo lib has 16 bit resolution * timer1 being used in servo lib has 16 bit resolution.
* We'll use timer1, since we have no mechanical parts and don't need servo lib * We'll use timer1, since we have no mechanical parts and don't need servo lib */
*/
// initialize timer1 // Initialize timer1.
noInterrupts(); // disable all interrupts noInterrupts(); // Disable all interrupts.
TCCR1A = 0; TCCR1A = 0;
TCCR1B = 0; TCCR1B = 0;
TCNT1 = 34286; // preload timer 65536-16MHz/256/2Hz TCNT1 = 34286; // Preload timer 65536-16MHz/256/2Hz
TCCR1B |= (1 << CS12); // 256 prescaler TCCR1B |= (1 << CS12); // 256 prescaler
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt TIMSK1 |= (1 << TOIE1); // Enable timer overflow interrupt.
interrupts(); // enable all interrupts interrupts(); // Enable all interrupts.
/* Increase sampling rate of analogRead()
* by setting an ADC prescale from 128 to 16, so it takes less time to read
* sensor. */
sbi(ADCSRA, ADPS2);
cbi(ADCSRA, ADPS1);
cbi(ADCSRA, ADPS0);
// Setup ambient brigtness sensor.
pinMode(photoPin, INPUT);
ambLight = analogRead(photoPin);
} }
ISR(TIMER1_OVF_vect) ISR(TIMER1_OVF_vect)
{ {
/* Feed clock twice per second */ // Feed clock twice per second.
TCNT1 = 34286; // preload timer TCNT1 = 34286; // Preload timer.
// do not read rtc if time beng adjusted by buttons // Do not read rtc if time beng adjusted by buttons.
(btn_pushed) ? rtcsync : rtcsync = true; (btn_pushed) ? rtcsync : rtcsync = true;
/* Also switch dot */ // Also switch dot.
if (dot) if (dot)
dot = false; dot = false;
else else
dot = true; dot = true;
/* time compensation magic */ #ifdef TIMECOMP
// Time compensation magic.
if ((myhour == 21) and (myminute == 21) and (mysecond == 21) if ((myhour == 21) and (myminute == 21) and (mysecond == 21)
and (compflag2)) { and (compflag2)) {
compflag1 = true; compflag1 = true;
#ifdef OFIVECOMP
(timeSkewEven) ? timeSkewEven = false : timeSkewEven; (timeSkewEven) ? timeSkewEven = false : timeSkewEven;
#endif
} }
if ((myhour == 21) and (myminute == 21) and (mysecond == 23)) { if ((myhour == 21) and (myminute == 21) and (mysecond == 23)) {
compflag1 = false; compflag1 = false;
compflag2 = true; compflag2 = true;
} }
#endif
} }
void loop() void loop()
{ {
unsigned long currentMillis = millis(); unsigned long currentMillis = millis();
/* #ifdef TIMECOMP
* Let's implement time compenstion for individual rtc module time /* Let's implement time compensation for individual rtc module time
* charachteristics. In in my case quartz resonator as inaccurate as * characteristics. In in my case quartz resonator as inaccurate as
* about +8.5 seconds per 24 hours * about +9.5 seconds per 24 hours. */
*/
if (compflag1 && compflag2) { if (compflag1 && compflag2) {
#ifdef OFIVECOMP
if (timeSkewEven) { if (timeSkewEven) {
clock.fillByHMS(myhour, myminute, (mysecond - timeSkew - 1)); clock.set(
(mysecond - timeSkew - 1),
clock.minute(),
clock.hour(),
clock.dayOfWeek(),
clock.day(),
clock.month(),
clock.year()
);
} else { } else {
clock.fillByHMS(myhour, myminute, (mysecond - timeSkew)); #endif
clock.set(
(mysecond - timeSkew),
clock.minute(),
clock.hour(),
clock.dayOfWeek(),
clock.day(),
clock.month(),
clock.year()
);
#ifdef OFIVECOMP
} }
#endif
clock.setTime();
compflag2 = false; compflag2 = false;
} }
#endif
if (rtcsync) { if (rtcsync) {
clock.getTime(); clock.refresh();
mysecond = clock.second; mysecond = clock.second();
myminute = clock.minute; myminute = clock.minute();
myhour = clock.hour; myhour = clock.hour();
updateTimeStr(myhour, myminute); updateTimeStr(myhour, myminute);
rtcsync = false; rtcsync = false;
} }
// push button magic // Push button magic.
if ((!btn_pushed) and if ((!btn_pushed) and
((digitalRead(hrs_btn) == LOW) or (digitalRead(min_btn) == LOW))) { ((digitalRead(hrs_btn) == LOW) or (digitalRead(min_btn) == LOW))) {
if (currentMillis - previousPushTimer >= initial_delay) { if (currentMillis - previousPushTimer >= initial_delay) {
@@ -171,21 +242,81 @@ void loop()
if (digitalRead(hrs_btn) == LOW) { if (digitalRead(hrs_btn) == LOW) {
myhour++; myhour++;
(myhour > 23) ? myhour = 0 : myhour; (myhour > 23) ? myhour = 0 : myhour;
clock.fillByHMS(myhour, myminute, 0); clock.set(
clock.setTime(); 0,
myminute,
myhour,
clock.dayOfWeek(),
clock.day(),
clock.month(),
clock.year()
);
} }
if (digitalRead(min_btn) == LOW) { if (digitalRead(min_btn) == LOW) {
myminute++; myminute++;
(myminute > 59) ? myminute = 0 : myminute; (myminute > 59) ? myminute = 0 : myminute;
clock.fillByHMS(myhour, myminute, 0); clock.set(
clock.setTime(); 0,
myminute,
myhour,
clock.dayOfWeek(),
clock.day(),
clock.month(),
clock.year()
);
} }
} }
updateTimeStr(myhour, myminute); updateTimeStr(myhour, myminute);
} }
if ((currentMillis - previousLightTimer >= refresh_interval) or
(previousLightTimer > currentMillis)) { // millis() reset every ~49 days
previousLightTimer = currentMillis;
ambLight = analogRead(photoPin);
/* Ambient light compensation.
* ambLight can be in range of 0..1023, if it 910+, it has point to use
* fullbright.
* Use table comparsion (not function of ambLight) because of non-linear
* characteristics of led indicator, that cannot be described via simple
* formula. */
if (ambLight >= 910) {
Display.setBrightness(100);
} else if ((ambLight < 900) and (ambLight >= 860)) {
Display.setBrightness(75);
} else if ((ambLight < 850) and (ambLight >= 810)) {
Display.setBrightness(45);
} else if ((ambLight < 800) and (ambLight >= 760)) {
Display.setBrightness(25);
} else if ((ambLight < 750) and (ambLight >= 710)) {
Display.setBrightness(18);
} else if ((ambLight < 700) and (ambLight >= 660)) {
Display.setBrightness(12);
} else if ((ambLight < 650) and (ambLight >= 610)) {
Display.setBrightness(10);
} else if ((ambLight < 600) and (ambLight >= 560)) {
Display.setBrightness(7);
} else if ((ambLight < 550) and (ambLight >= 410)) {
Display.setBrightness(4);
} else if ((ambLight < 400) and (ambLight >= 360)) {
Display.setBrightness(0);
} else if ((ambLight < 350) and (ambLight >= 310)) {
Display.setBrightness(-3);
} else if ((ambLight < 300) and (ambLight >= 260)) {
Display.setBrightness(-6);
} else if ((ambLight < 250) and (ambLight >= 210)) {
Display.setBrightness(-10);
} else if ((ambLight < 200) and (ambLight >= 160)) {
Display.setBrightness(-14);
} else if ((ambLight < 150) and (ambLight >= 110)) {
Display.setBrightness(-18);
} else if (ambLight < 100) {
Display.setBrightness(-22);
}
}
Display.refreshDisplay(); Display.refreshDisplay();
} }
@@ -202,4 +333,5 @@ void updateTimeStr(uint8_t h, uint8_t m)
} }
} }
/* vim: set filetype=cpp: */ /* vim: set filetype=cpp: */