1

16 Commits

Author SHA1 Message Date
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
5 changed files with 312 additions and 164 deletions

6
.gitignore vendored Normal file
View File

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

View File

@@ -1 +0,0 @@
ee4999ebdd2d1b54512facafdd03c8a530ea4bfa v1.0

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,53 +1,54 @@
/* /*
* 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 RTC module to enhance
* RTC module to enhance time keeping ability of * time keeping ability of Atmega 328p based Arduino Nano microcontroller and
* Atmega 328p based Arduino nano microcontroller and * 2 buttons for adjusting time.
* 2 buttons for adjusting time.
*/ */
/* This software distributed under BSD 2 Clause License /* DS1307 lib by FrankieChu for Seeed RTC module, in my case
* Grove___RTC_DS1307-1.0.0.zip */
Copyright (c) Sergei Fedosoff, All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
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.
*/
#include <SevSeg.h>
#include <Wire.h>
#include <TimeLib.h>
// DS1307 lib by FrankieChu for Seeed RTC module
#include <DS1307.h> #include <DS1307.h>
#include <SevSeg.h>
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
DS1307 clock; DS1307 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;
/* time inaccuracy compensation flag */
volatile bool compflag1 = true;
volatile bool compflag2 = false;
const uint8_t timeSkew = 8; // assume that clock runs faster than should
volatile bool timeSkewEven = false; // comp. 1 additional second per 2 days
/* ambient light compensation aka night mode */
const int photoPin = A2;
volatile bool measureAmbLight = true;
unsigned int ambLight = 0;
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()
{ {
@@ -56,15 +57,21 @@ void setup()
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,
* 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; 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 // setup clock
clock.begin(); clock.begin();
@@ -72,89 +79,213 @@ void setup()
// if rtc returns incorrect time, reset it to some default value // if rtc returns incorrect time, reset it to some default value
clock.getTime(); clock.getTime();
if (( clock.hour > 23 ) || (clock.minute > 59)) if ((clock.hour > 23) || (clock.minute > 59)) {
{ clock.fillByYMD(2019, 2, 23); // 23 Feb 2019
clock.fillByYMD(2019,2,23); // 23 Feb 2013 clock.fillByHMS(15, 52, 30); // 15:52:30"
clock.fillByHMS(15,52,30); // 15:52 30" clock.fillDayOfWeek(SAT); // Saturday
clock.fillDayOfWeek(SAT); // Saturday clock.setTime(); // put date to rtc
clock.setTime(); // put date to rtc
} }
// setup control buttons // 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;
/* time compensation magic */
if ((myhour == 21) and (myminute == 21) and (mysecond == 21)
and (compflag2)) {
compflag1 = true;
(timeSkewEven) ? timeSkewEven = false : timeSkewEven;
}
if ((myhour == 21) and (myminute == 21) and (mysecond == 23)) {
compflag1 = false;
compflag2 = true;
}
}
void loop() void loop()
{ {
unsigned long currentMillis = millis(); unsigned long currentMillis = millis();
char str[4];
float d;
if (currentMillis - previousMillis >= interval) /*
{ * Let's implement time compenstion for individual rtc module time
previousMillis = currentMillis; * charachteristics. In in my case quartz resonator as inaccurate as
* about +8.5 seconds per 24 hours
if (dot == false) */
{ if (compflag1 && compflag2) {
dot = true; if (timeSkewEven) {
clock.fillByHMS(myhour, myminute, (mysecond - timeSkew - 1));
if (digitalRead(hrs_btn) == LOW) } else {
{ clock.fillByHMS(myhour, myminute, (mysecond - timeSkew));
clock.getTime();
hours = clock.hour + 1;
(hours > 23)? hours = 0: hours;
clock.fillByHMS(hours, clock.minute, 0);
clock.setTime();
}
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
{ clock.setTime();
dot = false; compflag2 = false;
}
if (rtcsync) {
clock.getTime();
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;
} }
} }
clock.getTime(); if (btn_pushed and
(digitalRead(hrs_btn) != LOW) and (digitalRead(min_btn) != LOW)) {
// make blinking dot // compensate possible contact bounce with delay
if (clock.hour > 0) if (currentMillis - previousPushTimer >= bounce_delay) {
{ btn_pushed = false;
if (dot == true)
{
Display.setNumber((clock.hour * 100 + clock.minute), 2);
}
else
{
Display.setNumber((clock.hour * 100 + clock.minute));
} }
} }
else
{ if (btn_pushed) {
/* 0 o'clock is an exception, we dont want to see "00.00", we want see if (currentMillis - previousPushTimer >= adjust_interval) {
* "0.00" or "0 00", so we have to use this "hack" */ previousPushTimer = currentMillis;
if (dot == true)
{ if (digitalRead(hrs_btn) == LOW) {
d = float(clock.minute) / 100; myhour++;
Display.setNumber(d, 2); (myhour > 23) ? myhour = 0 : myhour;
clock.fillByHMS(myhour, myminute, 0);
clock.setTime();
}
if (digitalRead(min_btn) == LOW) {
myminute++;
(myminute > 59) ? myminute = 0 : myminute;
clock.fillByHMS(myhour, myminute, 0);
clock.setTime();
}
} }
else
{ updateTimeStr(myhour, myminute);
sprintf(str, "%2d%02d", clock.hour, clock.minute); }
Display.setChars(str);
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
* charachteristics 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(-5);
} else if ((ambLight < 250) and (ambLight >= 210)) {
Display.setBrightness(-7);
} else if ((ambLight < 200) and (ambLight >= 160)) {
Display.setBrightness(-9);
} else if ((ambLight < 150) and (ambLight >= 110)) {
Display.setBrightness(-12);
} else if (ambLight < 100) {
Display.setBrightness(-15);
} }
} }
Display.refreshDisplay(); Display.refreshDisplay();
} }
void updateTimeStr(uint8_t h, uint8_t m)
{
char str[4];
float d;
// make blinking dot
if (h > 0) {
if (dot) {
Display.setNumber((h * 100 + m), 2);
} else {
Display.setNumber((h * 100 + m));
}
} else {
/* 0 o'clock is an exception, we don't want to see "00.00", we
want see "0.00" or "0 00", so we have to use this "hack" */
if (dot) {
d = float(m) / 100;
Display.setNumber(d, 2);
} else {
sprintf(str, "%2d%02d", h, m);
Display.setChars(str);
}
}
}
/* vim: set filetype=cpp: */ /* vim: set filetype=cpp: */