1

24 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
eleksir
c7ed04e987 Added tag v1.2 for changeset 109112211e45 2019-05-15 18:28:34 +03:00
eleksir
ac646f6ac0 Replaced clock_schem.pdf with more correct 2019-05-15 18:28:09 +03:00
eleksir
d501cce0f1 Updated README.md 2019-05-15 18:25:59 +03:00
eleksir
be82b11537 Time adjust algo changed
+ Added 600ms pause before spin numbers if button pressed
* Changed increment delay 500ms -> 200ms
* Button bounce compensation code
2019-05-14 20:52:37 +03:00
eleksir
757210bfc5 Now compensation hack should work. 2019-05-12 07:55:17 +03:00
eleksir
2900e78ca4 Bits of cleanup, fix indentaion and coding style 2019-05-12 07:52:44 +03:00
eleksir
825c3096f2 Implement software time inaccuracy compensation hack
32768 Hz quartz chrystal resonator tends to be a bit inaccurate giving
about 5-10 seconds per day time skew
2019-04-20 12:04:05 +03:00
eleksir
f46a294535 Converted to unix EOL file 2019-04-20 11:52:00 +03:00
eleksir
72bd8ec064 Added hgignore file :) 2019-04-20 11:48:24 +03:00
eleksir
a93fcbfa80 Added tag v1.1 for changeset 1167bd4b6227 2019-03-01 01:27:27 +03:00
eleksir
f18e0a1166 Make it possible to display time without explicit leading zeroes 2019-03-01 01:27:07 +03:00
eleksir
5440f7852a Added tag v1.0 for changeset ee4999ebdd2d 2019-02-28 22:33:36 +03:00
3 changed files with 352 additions and 140 deletions

View File

@@ -4,13 +4,25 @@
### Component base is very simple: ### Component base is very simple:
* Arduino NANO v3 chinese replica * Arduino NANO v3 chinese replica (on Atmega-168 chip)
* 7 segment 4 digit LED indicator with common anode, FYQ-5641BG-21 * 7 segment 4 digit LED indicator with common anode, FYQ-5641BD-21 (red LEDs)
* 8 resistors that limiting current on LED segments, 330 Ohms, 0.25 watts * 8 resistors that limiting current on LED segments, 330 Ohms, 0.25 watts
* RTC Module GEEGROW with DS1307 chip * RTC Module GEEGROW with DS1307 chip (very basic, only RTC chip, quartz resonator, button battery, no additional memory chip)
* 5x7 cm single side prototyping board * 5x7 cm single side prototyping board
* 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
### Optionally you may need:
* Capasitor with well capacity if your charger has small capasitor on output
* Fuse, to protect your device
* DC/DC transformer if you have power supply that gives more than 5 volts or less than 4.5 volts (this will give additional surge protection if your power supply dies, this can happen after 3-5 years of uninterrupted work)
### 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).

Binary file not shown.

View File

@@ -1,137 +1,337 @@
/* /* 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 * It uses display module with common anode and DS1307/DS3231 RTC module to
* RTC module to enhance time keeping ability of * enhance time keeping ability of Atmega 328p based Arduino Nano
* Atmega 328p based Arduino nano microcontroller and * microcontroller and 2 buttons for adjusting time. */
* 2 buttons for adjusting time.
*/
/* This software distributed under BSD 2 Clause License /* uRTCLib
* https://github.com/Naguissa/uRTCLib
Copyright (c) Sergei Fedosoff, All rights reserved. * 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
Redistribution and use in source and binary forms, with or without modification, * want to re-invent wheel if I have one. */
are permitted provided that the following conditions are met: #include <uRTCLib.h>
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer. Redistributions in binary form must
reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the
distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
/* SevSeg
* https://github.com/DeanIsMe/SevSeg
* it has simple api and ideally works in my case. */
#include <SevSeg.h> #include <SevSeg.h>
#include <Wire.h>
#include <TimeLib.h>
// DS1307 lib by FrankieChu for Seeed RTC module
#include <DS1307.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;
unsigned long currentMillis; // Push button stuff.
unsigned long previousMillis = 0; const long initial_delay = 600; // wait a bit before spin numbers
const long interval = 500; const long adjust_interval = 200; // delay between consecutive number increments
const int hrs_btn = A0; const long bounce_delay = 20; // contact bounce compensation
const int min_btn = A1; const int hrs_btn = A0; // put hour adjusting button on A0 lane
bool dot = true; const int min_btn = A1; // put minute adjusting button on A1 lane
uint8_t minutes; volatile bool btn_pushed = false; // is button pushed?
uint8_t hours; unsigned long previousPushTimer = 0;
// Dot blinking stuff.
volatile bool dot = true;
#ifdef TIMECOMP
// Time inaccuracy compensation flag.
volatile bool compflag1 = true;
volatile bool compflag2 = false;
const uint8_t timeSkew = 9; // Assume that clock runs faster than should.
#ifdef OFIVECOMP
volatile bool timeSkewEven = false; // Comp. 1 additional second per 2 days.
#endif
#endif
// 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 myminute;
volatile uint8_t myhour;
// RTC sync flag.
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,
/* without it there is visual bug at 0 o'clock, and in current version of * but I was unable to make my display to change brightness notably with
* SevSeg' setChars() does not dots at all */ * recommended value, so i have to use updateWithDelays=true,
bool leadingZeros = true; * in this case brightness gets changed. */
bool updateWithDelays = true;
/* Without it there is visual bug at 0 o'clock, and in current version
* of SevSeg' setChars() does not dots at all. */
bool leadingZeros = false;
Display.begin(hardwareConfig, numDigits, digitPins, segmentPins, Display.begin(hardwareConfig, numDigits, digitPins, segmentPins,
resistorsOnSegments, updateWithDelays, leadingZeros); resistorsOnSegments, updateWithDelays, leadingZeros);
Display.setBrightness(90); 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.set(30, 52, 15, 6, 23, 2, 19); // Saturday, 23 Feb 2019 15:52:30
clock.fillByYMD(2019,2,23); // 23 Feb 2013 clock.lostPowerClear();
clock.fillByHMS(15,52,30); // 15:52 30"
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.
* 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.
* 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 */
// Initialize timer1.
noInterrupts(); // Disable all interrupts.
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 34286; // Preload timer 65536-16MHz/256/2Hz
TCCR1B |= (1 << CS12); // 256 prescaler
TIMSK1 |= (1 << TOIE1); // Enable timer overflow interrupt.
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)
{
// Feed clock twice per second.
TCNT1 = 34286; // Preload timer.
// Do not read rtc if time beng adjusted by buttons.
(btn_pushed) ? rtcsync : rtcsync = true;
// Also switch dot.
if (dot)
dot = false;
else
dot = true;
#ifdef TIMECOMP
// Time compensation magic.
if ((myhour == 21) and (myminute == 21) and (mysecond == 21)
and (compflag2)) {
compflag1 = true;
#ifdef OFIVECOMP
(timeSkewEven) ? timeSkewEven = false : timeSkewEven;
#endif
}
if ((myhour == 21) and (myminute == 21) and (mysecond == 23)) {
compflag1 = false;
compflag2 = true;
}
#endif
}
void loop() void loop()
{ {
unsigned long currentMillis = millis(); unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) #ifdef TIMECOMP
{ /* Let's implement time compensation for individual rtc module time
previousMillis = currentMillis; * characteristics. In in my case quartz resonator as inaccurate as
* about +9.5 seconds per 24 hours. */
if (dot == false) if (compflag1 && compflag2) {
{ #ifdef OFIVECOMP
dot = true; if (timeSkewEven) {
clock.set(
if (digitalRead(hrs_btn) == LOW) (mysecond - timeSkew - 1),
{ clock.minute(),
clock.getTime(); clock.hour(),
hours = clock.hour + 1; clock.dayOfWeek(),
(hours > 23)? hours = 0: hours; clock.day(),
clock.fillByHMS(hours, clock.minute, 0); clock.month(),
clock.setTime(); clock.year()
} );
if (digitalRead(min_btn) == LOW)
{
clock.getTime();
minutes = clock.minute + 1;
(minutes > 59)? minutes = 0: minutes;
clock.fillByHMS(clock.hour, minutes, 0);
clock.setTime();
}
}
else
{
dot = false;
}
}
clock.getTime();
// make blinking dot
if (dot == true) {
Display.setNumber((clock.hour * 100 + clock.minute), 2);
} else { } else {
Display.setNumber((clock.hour * 100 + clock.minute)); #endif
clock.set(
(mysecond - timeSkew),
clock.minute(),
clock.hour(),
clock.dayOfWeek(),
clock.day(),
clock.month(),
clock.year()
);
#ifdef OFIVECOMP
}
#endif
compflag2 = false;
}
#endif
if (rtcsync) {
clock.refresh();
mysecond = clock.second();
myminute = clock.minute();
myhour = clock.hour();
updateTimeStr(myhour, myminute);
rtcsync = false;
}
// Push button magic.
if ((!btn_pushed) and
((digitalRead(hrs_btn) == LOW) or (digitalRead(min_btn) == LOW))) {
if (currentMillis - previousPushTimer >= initial_delay) {
previousPushTimer = currentMillis;
btn_pushed = true;
}
}
if (btn_pushed and
(digitalRead(hrs_btn) != LOW) and (digitalRead(min_btn) != LOW)) {
// compensate possible contact bounce with delay
if (currentMillis - previousPushTimer >= bounce_delay) {
btn_pushed = false;
}
}
if (btn_pushed) {
if (currentMillis - previousPushTimer >= adjust_interval) {
previousPushTimer = currentMillis;
if (digitalRead(hrs_btn) == LOW) {
myhour++;
(myhour > 23) ? myhour = 0 : myhour;
clock.set(
0,
myminute,
myhour,
clock.dayOfWeek(),
clock.day(),
clock.month(),
clock.year()
);
}
if (digitalRead(min_btn) == LOW) {
myminute++;
(myminute > 59) ? myminute = 0 : myminute;
clock.set(
0,
myminute,
myhour,
clock.dayOfWeek(),
clock.day(),
clock.month(),
clock.year()
);
}
}
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();
} }
void updateTimeStr(uint8_t h, uint8_t m)
{
if (dot) {
char str[5];
sprintf(str, "%2d.%02d", h, m);
Display.setChars(str);
} else {
char str[4];
sprintf(str, "%2d%02d", h, m);
Display.setChars(str);
}
}
/* vim: set filetype=cpp: */ /* vim: set filetype=cpp: */