The Matrixclock project
This little project was born to build a present for my son’s birthday, I had a couple of components laying around and I decided to make something new.Goals
- build an easy-to-use alarm clock with a personal touch
- use only components I already had in stock
- use as few components as possible
- low power consumption
- learn to solder some SOIC components
Design decisions
- project a 3.3V circuit
- use a 64 led matrix for the display…
- a rotary encoder with integrated pushbutton for the controls
- an RGB led to change the clock color while the time passes (60′ cicle)
- sometimes print some random messages addressed to my son
- play a small song once every hour at his birthday
- play a custom alarm music
- an LDR sensor to adjust display and RGB brightness in the dark
- ATMEGA328P-PU for the MCU, clocked @ 8MHz using internal oscillator and running @ 3.3V
- DS1388 SOIC for the RTC (unfortunately DS1307 doesn’t run @ 3.3V)
- an 8OHM speaker for the sound
- an AS1107Â SOIC to drive the matrix (MAX7219 does not officialy run @ 3.3V even if it seemed ok to me when I tested it on the breadboard)
And my beloved jungle-breadboard 🙂
Building the board
Placing components was the most difficult and tedious work, I had to keep the board small and I only had 100x70mm boards available.
… and HCL + H2O2 to etch the board:
And finally: some more photos on Flickr and a short and ugly video showing the clock running.
Follow the source Luke! (The code)
The code is very patchy but Just Works (TM) , I used a couple of Arduino libraries and I merged the Pong game I’ve already written as a standalone sketch.
/**
* Matrix Clock
*/
#define DEBUG_CLOCK 0
#define ENABLE_RGB 1 /* rgb led */
// Pins
#define ENCODER_PIN_1 2 // INT0
#define ENCODER_PIN_2 3 // INT1
#define SET_PIN 4 // digital pull up, connected to ENCODER push button
#define LC_PIN_1 8
#define LC_PIN_2 7
#define LC_PIN_3 6
#define ALM_SWITCH_PIN A3
#define BUZZER_PIN 5 // digital (PWM?) -> BUZZER -> GND
#define RED_PIN 9
#define GREEN_PIN 10
#define BLUE_PIN 11
#define LDR_PIN A2
// Includes
#include "LedControl.h"
#include "8x8Font.h"
#include "3x5Font.h"
#include <EEPROM.h>
#include "EEPROMAnything.h"
#include <Wire.h>
#include "RTClib.h"
#include "Timer.h"
#include <Encoder.h>
#include "note.h"
#include "messages.h"
#define NO_PORTB_PINCHANGES // to indicate that port b will not be used for pin change interrupts
#define NO_PORTC_PINCHANGES // to indicate that port c will not be used for pin change interrupts
// if there is only one PCInt vector in use the code can be inlined
// reducing latency and code size
// define DISABLE_PCINT_MULTI_SERVICE below to limit the handler to servicing a single interrupt per invocation.
#define DISABLE_PCINT_MULTI_SERVICE
#include <PinChangeInt.h>
// FSM status
#define CLK_IDLE 0
#define CLK_PLAY 1
#define CLK_SET_ALM_H 2
#define CLK_SET_ALM_M 3
#define CLK_SET_TIM_H 4
#define CLK_SET_TIM_M 5
#define CLK_SET_TIM_S 6
#define CLK_SET_DAT_D 7
#define CLK_SET_DAT_M 8
#define CLK_SET_DAT_Y 9
#define CLK_SET_SPEED 10
#define CLK_SET_LAST 10 // Must be the last: check main code for status changes.
#define POS_CENTER 1
#define POS_LEFT 0
#define POS_RIGHT 2
#define SYM_ALM 0
#define SYM_DATE 1
#define SYM_TIME 2
#define SYM_SETUP 3
// Config
#define BIRTHDAY_M 4
#define BIRTHDAY_D 12
#define CHAR_SPEED 20
// Random message every
#define RANDOM_MSG_EVERY 6 * 60 * 1000 // 13 minutes
#define PRINT_DATE_EVERY_CICLES 5
// Alarm FSM
#define ALM_PLAY 1
#define ALM_STOP 0
#define ALM_CLEARED 2
// Pong
#define PADSIZE 3
#define BALL_DELAY 100
#define GAME_DELAY 9
#define BOUNCE_VERTICAL 1
#define BOUNCE_HORIZONTAL -1
#define HIT_NONE 0
#define HIT_CENTER 1
#define HIT_LEFT 2
#define HIT_RIGHT 3
// Timers
#define TIMEOUT 5000 // millis
#define DELAYED_TIMEOUT 20000 // millis
#define DEBOUNCE 200 // millis between pulses
#define ALM_RESET_AFTER_MINUTES 2 // timer for alarm reset
// Ugly globals...
uint8_t timeH;
uint8_t timeM;
volatile uint8_t timeS;
uint8_t dateD;
uint8_t dateM;
uint8_t dateY;
uint8_t dateW;
uint8_t almH;
uint8_t almM;
//int almDays;
uint8_t brightness = 8;
const uint8_t eeprom_id = 0x99;
DateTime now;
uint8_t print_date;
uint8_t char_speed = CHAR_SPEED;
// store clock status
int clockStatus = CLK_IDLE;
// Used in ISR, keep one-byte-long to avoid disabling INTs
byte alarmStatus = ALM_STOP;
volatile boolean setButtonPressed = false;
volatile unsigned long setButtonBounceTime=0; // variable to hold ms count to debounce a pressed switch
int oldStatus = CLK_IDLE;
// Timers
Timer timer;
volatile int idleTimer = 1000; // set to out of range value
volatile int alarmTimer = 1000;
volatile int storeConfigTimer = 1000;
volatile int ballTimer = 1000;
// Pong
byte direction; // Wind rose, 0 is north
int xball;
int yball;
int yball_prev;
int xpad;
// Sound
//char noteNames[] = {'C','D','E','F','G','a','b'};
//unsigned int frequencies[] = {262,294,330,349,392,440,494};
//const byte noteCount = sizeof(noteNames); // the number of notes
// (7 in this example)
//notes, a space represents a rest
// char song[] PROGMEM = "CCGGaaGFFEEDDC GGFFEEDGGFFEED CCGGaaGFFEEDDC ";
unsigned int alarm_song[] PROGMEM = {NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4, NOTE_A4, NOTE_A4, NOTE_G4, SILENCE, NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_C4, SILENCE, SILENCE, NOTE_G4, NOTE_G4, NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4, SILENCE, NOTE_G4, NOTE_G4, NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4, SILENCE, SILENCE, NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4, NOTE_A4, NOTE_A4, NOTE_G4, SILENCE, NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_C4, SILENCE, END_SONG};
//do do re do fa mi do do re do sol fa do do do# la fa mi re do do re do fa mi
// # G G A G C B | G G A G D C | G G + G E C B A | F F E C D C
unsigned int birthday_song[] PROGMEM = {NOTE_G4, NOTE_A4, NOTE_G4, NOTE_C5, NOTE_B4, SILENCE, NOTE_G4, NOTE_A4, NOTE_G4, NOTE_D5, NOTE_C5, SILENCE, NOTE_G4, NOTE_G5, NOTE_E5, NOTE_C5, NOTE_B4, NOTE_A4, SILENCE, NOTE_F4, NOTE_E4, NOTE_C4, NOTE_D4, NOTE_C4, SILENCE, END_SONG};
// Encoder
int encoderCount = 0;
Encoder myEnc(ENCODER_PIN_2, ENCODER_PIN_1);
#if DEBUG_CLOCK
RTC_DS1307 RTC;
// variables created by the build process when compiling the sketch
extern int __bss_end;
extern void *__brkval;
// function to return the amount of free RAM
int memoryFree() {
int freeValue;
if((int)__brkval == 0)
freeValue = ((int)&freeValue) - ((int)&__bss_end);
else
freeValue = ((int)&freeValue) - ((int)__brkval);
return freeValue;
}
#else
RTC_DS1388 RTC;
#endif
LedControl lc = LedControl(LC_PIN_1, LC_PIN_2, LC_PIN_3,1);
char char_buffer[8];
char char_buffer2[8];
char buf[64];
#if ENABLE_RGB
void setColor(int red, int green, int blue)
{
analogWrite(RED_PIN, 255-red);
analogWrite(GREEN_PIN, 255-green);
analogWrite(BLUE_PIN, 255-blue);
}
//Convert a given HSV (Hue Saturation Value) to RGB(Red Green Blue) and set the led to the color
// h is hue value, integer between 0 and 360
// s is saturation value, double between 0 and 1
// v is value, double between 0 and 1
//http://splinter.com.au/blog/?p=29
void setColorHsv(int h, double s, double v)
{
byte rgb[3];
// Make sure our arguments stay in-range
h = max(0, min(360, h));
s = max(0, min(1.0, s));
v = max(0, min(1.0, v));
if(s == 0)
{
// Achromatic (grey)
rgb[0] = rgb[1] = rgb[2] = round(v * 255);
} else {
double hs = h / 60.0; // sector 0 to 5
int i = floor(hs);
double f = hs - i; // factorial part of h
double p = v * (1 - s);
double q = v * (1 - s * f);
double t = v * (1 - s * (1 - f));
double r, g, b;
switch(i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default: // case 5:
r = v;
g = p;
b = q;
}
rgb[0] = round(r * 255.0);
rgb[1] = round(g * 255.0);
rgb[2] = round(b * 255.0);
}
setColor(rgb[0], rgb[1], rgb[2]);
}
void setColorHueValue(int hueValue, uint8_t brightness)
{
setColorHsv(hueValue, 1, constrain((double)brightness/15, 0.1, 1));
}
#endif
/*
* Play a note for a given time
*
*
void playNote(char note, int duration)
{
// play the tone corresponding to the note name
for (int i = 0; i < noteCount; i++)
{
// try and find a match for the noteName to get the index to the note
if (noteNames[i] == note) // find a matching note name in the array
// play the note using the frequency:
tone(BUZZER_PIN, frequencies[i], duration);
}
// if there is no match then the note is a rest, so just do the delay
delay(duration);
}
*/
/**
* Play a note for a given time
*
*/
void playFrequency(unsigned int note, int duration)
{
if(note != SILENCE){
tone(BUZZER_PIN, note, duration);
}
delay(duration);
}
/**
* Set IDLE status
*/
void setIdle()
{
clockStatus = CLK_IDLE;
}
/**
* Get encoder move
*/
void readEncoder(){
encoderCount = myEnc.read()/4;
if(encoderCount){
myEnc.write(0);
}
}
/**
* String message handling
*/
char reverseByte(char b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
void setSprite(char* font){
for(int r = 0; r < 8; r++){
lc.setRow(0, r, reverseByte(*(font + r)));
}
}
void load_char(char i, char* c){
for(int r = 0; r < 8; r++){
c[r] = pgm_read_byte((char*)font_data + (i * 8) + r);
}
}
void merge_char(char* c1, char* c2, char pos){
for(int r = 0; r < 8; r++){
c1[r] = (c1[r] << 1) | (c2[r] >> (8 - pos)) ;
}
}
void print_string(char* s){
load_char(32, char_buffer);
bool valid = 1;
while (valid){
setSprite(char_buffer);
load_char(*s ? *s : 32, char_buffer2);
for(int r = 1; r <= 8; r++){
if(setButtonPressed){
return;
}
merge_char(char_buffer, char_buffer2, r);
setSprite(char_buffer);
delay(char_speed);
}
valid = (char) *s;
s++;
}
}
void load_digits(char* c, uint8_t digits, uint8_t pos, uint8_t sym){
for(uint8_t i=0; i<5; i++){
c[i] = pgm_read_byte((char*)small_font_data + digits / 10 * 5 + i) << 4 | pgm_read_byte((char*)small_font_data + digits % 10 * 5 + i);
}
c[5] = 0;
uint8_t shift = constrain(6 - 3 * pos, 0, 5);
switch(sym){
case SYM_ALM:
c[6] = B00000010 << shift;
c[7] = B00000101 << shift;
break;
case SYM_TIME:
c[6] = B00000111 << shift;
c[7] = B00000010 << shift;
break;
case SYM_DATE:
c[6] = B00000111 << shift;
c[7] = B00000101 << shift;
break;
case SYM_SETUP:
c[6] = 0;
c[7] = B00000111 << shift;
break;
}
}
/**
* Pong game
*/
void newGame() {
lc.clearDisplay(0);
// initial position
xball = random(1, 7);
yball = 1;
direction = random(3, 6); // Go south
load_char(1, char_buffer);
setSprite(char_buffer);
delay(700);
lc.clearDisplay(0);
}
void setPad() {
readEncoder();
xpad = constrain(xpad + encoderCount, 0, 8 - PADSIZE);
}
/**
* Checks if the ball is in contact with the walls
*/
int checkBounce() {
if(!xball || !yball || xball == 7 || yball == 6){
int bounce = (yball == 0 || yball == 6) ? BOUNCE_HORIZONTAL : BOUNCE_VERTICAL;
return bounce;
}
return 0;
}
int getHit() {
if(yball != 6 || xball < xpad || xball > xpad + PADSIZE){
return HIT_NONE;
}
if(xball == xpad + PADSIZE / 2){
return HIT_CENTER;
}
return xball < xpad + PADSIZE / 2 ? HIT_LEFT : HIT_RIGHT;
}
bool checkLoose() {
return yball == 6 && getHit() == HIT_NONE;
}
void hitSound(){
playFrequency(NOTE_B4, 2);
}
void moveBall() {
int bounce = checkBounce();
if(bounce) {
switch(direction){
case 0:
direction = 4;
break;
case 1:
direction = (bounce == BOUNCE_VERTICAL) ? 7 : 3;
break;
case 2:
direction = 6;
break;
case 6:
direction = 2;
break;
case 7:
direction = (bounce == BOUNCE_VERTICAL) ? 1 : 5;
break;
case 5:
direction = (bounce == BOUNCE_VERTICAL) ? 3 : 7;
break;
case 3:
direction = (bounce == BOUNCE_VERTICAL) ? 5 : 1;
break;
case 4:
direction = 0;
break;
}
}
// Check hit
switch(getHit()){
case HIT_LEFT:
if(direction == 0){
direction = 7;
} else if (direction == 1){
direction = 0;
}
hitSound();
break;
case HIT_RIGHT:
if(direction == 0){
direction = 1;
} else if(direction == 7){
direction = 0;
}
hitSound();
break;
case HIT_CENTER:
hitSound();
break;
}
// Check orthogonal directions and borders ...
// should never happen
if((direction == 0 && xball == 0) || (direction == 4 && xball == 7)){
direction++;
}
if(direction == 0 && xball == 7){
direction = 7;
}
if(direction == 4 && xball == 0){
direction = 3;
}
if(direction == 2 && yball == 0){
direction = 3;
}
if(direction == 2 && yball == 6){
direction = 1;
}
if(direction == 6 && yball == 0){
direction = 5;
}
if(direction == 6 && yball == 6){
direction = 7;
}
// Corner case:
if(xball == 0 && yball == 0){
direction = 3;
}
if(xball == 0 && yball == 6){
direction = 1;
}
if(xball == 7 && yball == 6){
direction = 7;
}
if(xball == 7 && yball == 0){
direction = 5;
}
yball_prev = yball;
if(2 < direction && direction < 6) {
yball++;
} else if(direction != 6 && direction != 2) {
yball--;
}
if(0 < direction && direction < 4) {
xball++;
} else if(direction != 0 && direction != 4) {
xball--;
}
xball = max(0, min(7, xball));
yball = max(0, min(6, yball));
}
void gameOver() {
load_char(33, char_buffer);
setSprite(char_buffer);
delay(1500);
lc.clearDisplay(0);
}
void drawGame() {
if(yball_prev != yball){
lc.setRow(0, yball_prev, 0);
}
lc.setRow(0, yball, byte(1 << (xball)));
byte padmap = byte(0xFF >> (8 - PADSIZE) << xpad) ;
lc.setRow(0, 7, padmap);
}
void initGame(){
randomSeed(analogRead(0));
newGame();
ballTimer = timer.every(BALL_DELAY, moveBall);
}
void playGame(){
while(!setButtonPressed){
timer.update();
setPad();
drawGame();
if(checkLoose()) {
gameOver();
newGame();
}
delay(GAME_DELAY);
}
timer.stop(ballTimer);
}
/**
* Wake up effect, this is not returning
* check for status from button ISR
*
*/
void wakeUp(const unsigned int* song)
{
while (pgm_read_word(song) != END_SONG)
{
if(alarmStatus != ALM_PLAY || setButtonPressed){
alarmStatus = ALM_CLEARED;
setButtonPressed = false;
return;
}
playFrequency(pgm_read_word(song++), 333); // play the note
}
delay(4000); // wait four seconds before repeating the song
}
/**
* Birthday
*/
void check_birthday(){
unsigned int *song;
song = birthday_song;
if(dateD == BIRTHDAY_D && dateM == BIRTHDAY_M && timeH > 9 && timeH < 21){
load_char(3, char_buffer);
setSprite(char_buffer);
while (pgm_read_word(song) != END_SONG)
{
playFrequency(pgm_read_word(song++), 333); // play the note
}
delay(4000);
}
}
/**
* Turn off alarm
*/
void stopAlarm(){
alarmStatus = ALM_CLEARED;
//digitalWrite(BUZZER_PIN, LOW);
//digitalWrite(ALM_LED_PIN, LOW);
}
/**
* ISR on FALLING push button
*/
void setSetButtonPressed(){
// this is the interrupt handler for button presses
// it ignores presses that occur in intervals less then the bounce time
if (abs(millis() - setButtonBounceTime) > DEBOUNCE)
{
setButtonPressed = true;
setButtonBounceTime = millis(); // set whatever bounce time in ms is appropriate
}
}
/**
* Changes clock status
*/
void changeStatus()
{
clockStatus++;
if(clockStatus > CLK_SET_LAST){
setIdle();
}
// Start timer
timer.stop(idleTimer);
idleTimer = timer.after(TIMEOUT, setIdle);
setButtonPressed = false;
}
/**
* Store config to EEPROM
*/
void store_config(){
// Read EEPROM
int write = 0;
write += EEPROM_writeAnything(write, eeprom_id);
write += EEPROM_writeAnything(write, char_speed);
write += EEPROM_writeAnything(write, almH);
EEPROM_writeAnything(write, almM);
}
/**
* Delayed store config
*/
void store_config_delayed(){
timer.stop(storeConfigTimer);
storeConfigTimer = timer.after(DELAYED_TIMEOUT, store_config);
}
/**
* Adjust clock
*/
void storeDateTime(){
RTC.adjust(DateTime(dateY, dateM, dateD, timeH, timeM, timeS));
}
/**
* Adjust brightness
*/
void adjustBrightness(){
brightness = map(analogRead(LDR_PIN), 0, 1023, 0, 15);
lc.setIntensity(0, brightness);
#if ENABLE_RGB
setColorHueValue((uint16_t)timeM * 6, brightness);
#endif
}
/**
* Random message
*/
void randomMessage(){
if(clockStatus == CLK_IDLE){
strcpy_P(buf, (char*)pgm_read_word(&(messages[random(0, sizeof(messages)/sizeof(char *))])));
print_string(buf);
}
}
/**
* Setup
*/
void setup() {
// Read Alm from EEPROM
// Read EEPROM
if(EEPROM.read(0) == eeprom_id){
int read = 1;
read += EEPROM_readAnything(read, char_speed);
read += EEPROM_readAnything(read, almH);
EEPROM_readAnything(read, almM);
} else {
store_config();
}
pinMode(LDR_PIN, INPUT);
adjustBrightness();
// LCD
// The MAX72XX is in power-saving mode on startup,
// we have to do a wakeup call
lc.shutdown(0,false);
/* Set the brightness to a medium values range (0-15) */
lc.setIntensity(0, brightness);
/* and clear the display */
lc.clearDisplay(0);
// RTC
Wire.begin();
RTC.begin();
if (! RTC.isrunning()) {
// following line sets the RTC to the date & time this sketch was compiled
RTC.adjust(DateTime(__DATE__, __TIME__));
}
// Encoder & push
pinMode(SET_PIN, INPUT);
digitalWrite(SET_PIN, HIGH);
PCintPort::attachInterrupt(SET_PIN, &setSetButtonPressed, FALLING);
// Alm enable pin
pinMode(ALM_SWITCH_PIN, INPUT);
digitalWrite(ALM_SWITCH_PIN, HIGH); // pull up
pinMode(BUZZER_PIN, OUTPUT);
timer.every(1000, adjustBrightness);
timer.every(RANDOM_MSG_EVERY, randomMessage);
print_date = 0;
}
/**
* Loop
*/
void loop() {
if(setButtonPressed && alarmStatus != ALM_PLAY){
changeStatus();
}
// Read encoder
readEncoder();
// Read clock
now = RTC.now();
timeH = now.hour();
timeM = now.minute();
timeS = now.second();
dateD = now.day();
dateM = now.month();
dateY = now.yoff();
boolean alarmSwitchClosed;
alarmSwitchClosed = (digitalRead(ALM_SWITCH_PIN) == LOW); // pull-up, closed is low
// Check alarm, buttons pressed stop alarm
int minutes_after_alarm;
minutes_after_alarm = ((int)timeH * 60 + (int)timeM) - ((int)almH * 60 + (int)almM);
if(clockStatus == CLK_IDLE && alarmSwitchClosed && alarmStatus == ALM_STOP && minutes_after_alarm == 0){
alarmStatus = ALM_PLAY;
}
if(alarmStatus == ALM_PLAY){
if(clockStatus == CLK_IDLE && alarmSwitchClosed && !setButtonPressed){
load_char(13, char_buffer);
setSprite(char_buffer);
wakeUp(alarm_song);
} else {
stopAlarm();
}
}
if(clockStatus == CLK_PLAY){
timer.stop(idleTimer);
initGame();
playGame();
idleTimer = timer.after(TIMEOUT, setIdle);
}
if(clockStatus > CLK_PLAY){
uint8_t digits;
uint8_t pos;
uint8_t sym;
switch(clockStatus) {
case CLK_SET_ALM_H:
pos = POS_LEFT;
sym = SYM_ALM;
digits = almH;
break;
case CLK_SET_ALM_M:
pos = POS_CENTER;
sym = SYM_ALM;
digits = almM;
break;
case CLK_SET_TIM_H:
pos = POS_LEFT;
sym = SYM_TIME;
digits = timeH;
break;
case CLK_SET_TIM_M:
pos = POS_CENTER;
sym = SYM_TIME;
digits = timeM;
break;
case CLK_SET_TIM_S:
pos = POS_RIGHT;
sym = SYM_TIME;
digits = timeS;
break;
case CLK_SET_DAT_D:
pos = POS_LEFT;
sym = SYM_DATE;
digits = dateD;
break;
case CLK_SET_DAT_M:
pos = POS_CENTER;
sym = SYM_DATE;
digits = dateM;
break;
case CLK_SET_DAT_Y:
pos = POS_RIGHT;
sym = SYM_DATE;
pos = 2;
digits = dateY;
break;
case CLK_SET_SPEED:
digits = char_speed;
pos = POS_CENTER;
sym = SYM_SETUP;
break;
}
if(encoderCount){
// reset timeout
timer.stop(idleTimer);
idleTimer = timer.after(TIMEOUT, setIdle);
// Update...
switch(clockStatus) {
case CLK_SET_ALM_H:
almH += encoderCount;
if(almH > 23) almH = 0;
store_config_delayed();
break;
case CLK_SET_ALM_M:
almM += encoderCount;
if(almM > 59) almM = 0;
store_config_delayed();
break;
case CLK_SET_TIM_H:
timeH += encoderCount;
if(timeH > 23) timeH = 0;
storeDateTime();
break;
case CLK_SET_TIM_M:
timeM += encoderCount;
if(timeM > 59) timeM = 0;
storeDateTime();
break;
case CLK_SET_TIM_S:
timeS += encoderCount;
if(timeS > 59) timeS = 0;
storeDateTime();
break;
case CLK_SET_DAT_D:
dateD += encoderCount;
if(dateD > 31)dateD = 1;
storeDateTime();
break;
case CLK_SET_DAT_M:
dateM += encoderCount;
if(dateM > 12) dateM= 1;
storeDateTime();
break;
case CLK_SET_DAT_Y:
dateY += encoderCount;
if(dateY > 99) dateY = 0;
storeDateTime();
break;
case CLK_SET_SPEED:
char_speed+= encoderCount;
if(char_speed > 30) char_speed = 10;
if(char_speed < 10) char_speed = 30;
store_config_delayed();
break;
}
}
load_digits(char_buffer, digits, pos, sym);
setSprite(char_buffer);
} else {
//strcpy_P(buf, PSTR("Ciao Leo... sono le"));
//print_string((char*) buf);
#if DEBUG_CLOCK
//sprintf(buf, "E: %d S: %d", encoderCount, clockStatus);
//print_string(buf);
//sprintf_P(buf, PSTR("F:%d"), memoryFree());
//print_string(buf);
sprintf_P(buf, PSTR("B:%d"), brightness);
print_string(buf);
#endif
sprintf_P(buf, PSTR("%02d:%02d:%02d"), now.hour(), now.minute(), now.second());
print_string(buf);
if(print_date++ == PRINT_DATE_EVERY_CICLES){
print_date = 0;
sprintf_P(buf, PSTR("%02d-%02d-%d"), now.day(), now.month(), now.year());
print_string(buf);
}
// Show alarm
if(alarmSwitchClosed && alarmStatus == ALM_STOP){
sprintf_P(buf, PSTR("%c %02d:%02d"), 13, almH, almM);
print_string(buf);
}
}
if(timeM == 0){
check_birthday();
}
// Hand timer
if(alarmStatus != ALM_STOP && minutes_after_alarm >= ALM_RESET_AFTER_MINUTES){
alarmStatus = ALM_STOP;
}
timer.update();
}