E' GIUNTA LA TUA ORA


torna alla Home Page


CHE ORE SONO ?

Troppo facile guardare cronografo al polso, voltarsi verso l'orologio a parete o sbirciare nell'angolo in basso a destra dello schermo... Arduino ce lo deve dire lui, con tutto quello che gli abbiamo insegnato !

Beh, probabilmente avrà bisogno di un pò di aiuto, il suo clock interno non è in grado di mantenere la precisione richiesta per più di qualche ora, e poi se manca la corrente, jè tutto da rifare.

Ok, prendiamo il canotto e andiamo a sguazzare per Internet alla ricerca della soluzione: mica sono il primo a fare questa cosa, anzi trovo tutto più o meno già pronto, e scopro che la parolina magica è RTC cioè Real Time Clock, che mi dà l'ora quando la voglio, e con una batteria tampone evito pure i blackout. Tiè. 

E' l'ora delle prove: acquisto un paio di DS1307 (il chip che implementa l'RTC), quarzini da 32KHz e usando questo schema assemblo una basetta veloce da provare con arduino. Il collegameneto avviene sulle linee di input analogiche 4 e 5, che tramite la libreria Wire sono in grado di dialogare con il protocollo I2C, e mi ritrovo il mio bell'orologio ticchettante, che anche dopo avere rimosso l'alimentazione (tranne la batteria tampone) continua a mantenere l'ora corretta... grandioso !

Devo dire però che mi dispiace sacrificare il bell'arduino per un'applicazione fissa, per quanto utile, come un orologio. D'altronde non ha molto senso fare un orologio per poi smontarlo dopo un giorno che funziona. E qui il gesto atletico si manifesta in tutta la sua grandezza: me lo faccio tutto da solo, su basetta millefori !

MILLE E NON PIU' MILLE

Magari qualcosa di meno, ma basetta millefori sarà. Sicuramente di display lo manterrò separato per poterlo piazzare con comodità dove voglio; anche l'interfaccia verso la RS232 non è necessario lasciarla a bordo, anzi tenendola a parte magari mi viene comoda per altri progetti. L'arduino (ATMega168, con il suo bootloader originale) e L'RTC (aka DS1307) li teniamo assieme, batteria di backup compresa, e sulla stessa basetta ci metto pure lo stadio di alimentazione. Ok, l'idea c'è, ma tra il dire e il fare c'è di mezzo il saldare...

IL DISPLAY

L'integrato da usare per pilotare i display 7 segmenti è il classico Maxim 7219, già impiegato in altri progettini: usa 3 fili per dialogare (protocollo SPI) ed è in grado di pilotare fino a 8 digit a 7 segmenti, nel nostro caso ne useremo solo sei. 

Inoltre, per poter regolare ore e minuti ho aggiunto un paio di bottoni che chiudono il contatto verso massa, e che dovranno essere interpretati opportunamente dall'arduino quando si tratta di regolare l'orario. (schema elettrico)

L'aspetto che ho dato alla basetta mantiene l'altezza minima imposta dai display, e ho già trovato un contenitore (l'astuccio cilindrico di un rapid) ideale per questo oggettino, e usando i connettori a pettine che fanno tanto professionale, diventa un modulo che posso usare anche per altre cose (quali ? boh, adesso ci faccio l'orologio, magari domani lo uso per contare le stelle cadenti...)


ARDUINO & RTC

E qui viene il bello... l'idea è sempre quella di creare una basetta la più piccola possibile, che possa venire infilata nel contenitore finale: avevo pensato a qualcosa tipo un tubo, quindi la basetta dovrà  essere lunga e stretta.

Il procedimento è fondamentalmente empirico: essendo pochi i componenti da piazzare, definisco un'area grosso modo rettangolare e stretta, poi provo un pò tutte le combinazioni fino a quando trovo la giusta armonia tra geometria e cablaggio logico (mmazza, che paroloni !). 

Sulla basetta ho previsto i connettori per il display, l'alimentazione, i segnali dal programmatore, e una manciata di pin extra dove attestare un paio di input analogici e altrettanti i/o digitali di tipo PWM. Del resto se dovessi aggiungere qualche fuzionalità o bottone extra, ci saranno già dei pin a disposizione.

Anche se sembrerà superfluo, a quel punto scatto qualche foto all'opera "teorica", da usare come appunto visuale per l'assemblaggio finale (e non sapete quanto sia utile). La mano che  afferra il saldatore è il passaggio naturale successivo, e come materiale di cablaggio uso i fili  di una vecchia piattina dati per hard disk IDE: i-de-a-le. (schema elettrico)

Nota speciale per quanto riguarda il chip ATMega168: si tratta dell'integrato presente su un arduino originale, che è stato programmato e poi messo su questa scheda. Per poterlo riprogrammare si potrà procedere rimettendo il chip sull'arduino, oppure collegando il programmatore (descritto qui sotto) alla porta predisposta, e procedere tramite RS232 a caricare il nuovo programma senza più staccare l'ATMega dal nostro RTCDuino..

IL PROGRAMMATORE

Avevo comprato un chip MAX232 più per sfizio che per necessità, ma in questo progetto viene usato proprio per il suo scopo: per quei due o tre che non lo conoscono, si tratta di un convertitrore di livelli logici tra RS232 e TTL, e richiede solo qualche condensatore di contorno per funzionare.

L'unico svantaggio è che richiede la porta RS232, e oramai cominciano a scarseggiare le macchine  con questo tipo di interfaccia, ma tanto il mio pc ce l'ha, e non avevo in giro qualcosa di tipo FTDI per fare una cosa così (che non saprei neanche come fare...)

L'immagine a lato la dice lunga sull'ottimizzazione, o meglio sullo spreco, di spazio, ma la scatolina da impiegare era la più piccola che avevo in giro, pazienza. (schema elettrico)

IL CODICE

Qui comincia il divertimento, ma non dura molto: se date un'occhiata al codice, non c'è molto da raccontarsela, a parte l'animazione iniziale per scrivere un benvenuto, e un pizzico di follia per fare ballare i numeri ogni tanto (crazyDigit routine)

Andiamo con ordine: è fondamentale la libreria per poter colloquiare con il chip RTC, che trovate a questo indirizzo (sempre che non me lo spostino) e l'inclusione della libreria WIRE che è alla base del protocollo I2C. 

Con questi elementi possiamo leggere e impostare l'orario sul DS1307 a piacimento, e per avere una sequenza casuale sempre diversa all'accensione, sfrutto l'ora appena letta (secondi+minuti x 60) come "seme" inizale della funzione random.

Resta da inizializzare il display, che con la libreria di Eberhard Fahle si può ribaltare come un calzino, e infatti uso sia la scrittura diretta dei 10 digit classici, che la visualizzazione di simboli più o meno sensati usando l'indirizzamento bitmap (metodo .setRow).

Tutto il resto è noia, verrebbe da dire: ciclo continuo che legge l'orario e lo riscrive sul display, frammezzato da un evento che ogni tanto fa ballare un digit a caso per qualche centinaio di millisecondi, giusto per dare all'orologio un aspetto un pò più strano (anche il saluto iniziale potrebbe essere reso più strano)

Qui sotto potete vedere l'orologio in azione e il relativo codice, migliorabile in assoluto e aperto a tutte le modifiche.

/*
* --------------------------------------------------
* RTCDUINO
* RTC CONTROLLED ARDUINO CLOCK
* AUTHOR: M.NICOLATO
* DATE: OCTOBER, 4, 2009
* --------------------------------------------------
*/

// LEDControl Library - (c) by Eberhard Fahle
// http://www.arduino.cc/playground/Main/LedControl
#include "LedControl.h"

// WIRE Library, to use I2C protocol
#include <Wire.h>

// DS1307 Library written by mattt on the Arduino forum and modified by D. Sjunnesson
// http://code.google.com/p/sjunnesson/wiki/DS1307
#include <DS1307.h>

boolean dp=false;
unsigned long oldMillis = 0;

// Pins used by MAXIM 7219 (7 segment display driver)
#define DSP_DIN_PIN 12
#define DSP_LOAD_PIN 11
#define DSP_CLK_PIN 10

#define DSP_HH_PIN 8 // HOURS Button pin
#define DSP_MM_PIN 9 // MIN Button pin

#define LED_PIN 13 // Monitor LED pin

#define H1DIGIT 5 // tens hours digit#
#define H2DIGIT 4 // uinits hours digit#
#define M1DIGIT 3 // tens min digit#
#define M2DIGIT 2 // uinits min digit#
#define S1DIGIT 1 // tens secs digit#
#define S2DIGIT 0 // uinits secs digit#

// RTC structure
int regTime[4]={0,
DS1307_HR,
DS1307_MIN,
DS1307_SEC};

// Display object
LedControl lc=LedControl(DSP_DIN_PIN,
DSP_CLK_PIN,
DSP_LOAD_PIN, 1);


void setup() {

// setup I/O pins
pinMode(LED_PIN, OUTPUT);
pinMode(DSP_HH_PIN, INPUT);
pinMode(DSP_MM_PIN, INPUT);

// Pullup H/M button pins
digitalWrite(DSP_HH_PIN, HIGH);
digitalWrite(DSP_MM_PIN, HIGH);

// Wake up display driver
lc.shutdown(0, false);
// Set the brightness to a medium values
lc.setIntensity(0,8);

// Get random seed using current time, to start everytime differently
int mm = RTC.get(DS1307_MIN,true);
int ss = RTC.get(DS1307_SEC,false);
randomSeed((mm*60) + ss);

// Welcome message
crazyDigit(H2DIGIT, 400);
lc.setRow(0, H2DIGIT, 0b00110111); // H
crazyDigit(M1DIGIT, 400);
lc.setRow(0, M1DIGIT, 0b01001111); // E
crazyDigit(M2DIGIT, 400);
lc.setRow(0, M2DIGIT, 0b00001110); // L
crazyDigit(S1DIGIT, 400);
lc.setRow(0, S1DIGIT, 0b00001110); // L
crazyDigit(S2DIGIT, 400);
lc.setRow(0, S2DIGIT, 0b01111110); // 0
delay(3000UL);

// Read current time
int hh = RTC.get(DS1307_HR,true);
mm = RTC.get(DS1307_MIN,false);
ss = RTC.get(DS1307_SEC,false);

int h1 = hh / 10;
int h2 = hh % 10;
int m1 = mm / 10;
int m2 = mm % 10;
int s1 = ss / 10;
int s2 = ss % 10;

// Show current time, changhing each digit at time
crazyDigit(5, 300);
lc.setDigit(0, H1DIGIT, h1, false);
crazyDigit(4, 300);
lc.setDigit(0, H2DIGIT, h2, true);
crazyDigit(3, 300);
lc.setDigit(0, M1DIGIT, m1, false);
crazyDigit(2, 300);
lc.setDigit(0, M2DIGIT, m2, true);
crazyDigit(1, 300);
lc.setDigit(0, S1DIGIT, s1, false);
crazyDigit(0, 300);
lc.setDigit(0, S2DIGIT, s2, false);

}


void loop() {

// every 500ms toggle decimal point
if (oldMillis < millis())
{
oldMillis = millis() + 500;
dp = !dp;
}

// update status led
digitalWrite(LED_PIN, dp);

// Read current time from RTC
int hh = RTC.get(DS1307_HR,true);
int mm = RTC.get(DS1307_MIN,false);
int ss = RTC.get(DS1307_SEC,false);

// Apply to each digit
int h1 = hh / 10;
int h2 = hh % 10;
int m1 = mm / 10;
int m2 = mm % 10;
int s1 = ss / 10;
int s2 = ss % 10;


// Reset display and show new time
lc.clearDisplay(0);
if(h1>0)
lc.setDigit(0, H1DIGIT, h1, false);
lc.setDigit(0, H2DIGIT, h2, dp);
lc.setDigit(0, M2DIGIT, m2, dp);
lc.setDigit(0, M1DIGIT, m1, false);
lc.setDigit(0, S2DIGIT, s2, false);
lc.setDigit(0, S1DIGIT, s1, false);

// show radomly some crazy digit
if (random(0,1000) > 900)
crazyDigit(random(0,6), 200 );

// Verify key pressed
readKey(DSP_HH_PIN);
readKey(DSP_MM_PIN);

delay(200);

}

// Read if "nInput" has been grounded - return 0 if nothing, else evaluate "nInput"
int readKey(int nInput)
{
if (digitalRead(nInput) == 0)
{
delay(10); // debounce
if (digitalRead(nInput) == 0)
evalKey(nInput);
}
return(0);
}


// Evaluate H/M buttons
// setup=toggle
// 0=up, 1=down
void evalKey(int key)
{

if (key == DSP_HH_PIN) // increase hours
{
int hh = RTC.get(DS1307_HR,true) + 1;
if(hh > 23) hh = 0;
RTC.stop();
RTC.set(DS1307_HR, hh);
RTC.start();
}


if (key == DSP_MM_PIN) // increase mins
{
int mm = RTC.get(DS1307_MIN,true) + 1;
if(mm > 59) mm = 0;
RTC.stop();
RTC.set(DS1307_MIN, mm);
RTC.start();
}

}



// Show crazy segments on this "digit" for "ms" millisecs
void crazyDigit(int digit, int ms)
{
for(int r = 0; r < (ms/30); r++)
{
lc.setRow(0, digit, random(0,255));
delay(30);
}
}


E ORA BASTA

Non ho finito... per questo orologio avevo l'intenzione di racchiuderlo in un falso candelotto rosso di dinamite (con tanto di scritta TNT), e mettere qualche sensore di tilt che facesse visualizzare qualcosa di allarmante al display (tipo conteggio 5..4..3..) al minimo scuotimento. 

Avevo anche pensato di mettere un ricevitore IR con cui poter regolare l'orario usando il telecomando. E avevo pure pensato a collegare un paio di microamperometri alle uscite PWM per emulare il chronulator, vedi che bella accoppiata: analogico e digitale !

Beh, intanto pubblico questo mio lavoro, magari non sarà completissimo, ma è funzionante, e vi assicuro che è una soddisfazione farsi un "arduino DIY", e anche il nome (RTCDuino) mi suona bene.

Leggero' con piacere i vostri commenti, se vorrete scrivermi al seguente indirizzo: mnicolato@hotmail.com

Aggiornato il 10 ottobre 2009