Relógio de 7 Segmentos com Arduino Nano, DS3231 RTC e Brilho Automático LDR
Componentes e Materiais Necessários
| Componente | Quantidade | Observações |
|---|---|---|
| Arduino Nano R3 | 1 | Funciona bem com Arduino Uno ou Elegoo Uno |
| Adafruit 7-Segment Backpack - Dígitos de 1.2" | 1 | Dígitos altos de 1.2" no meu caso (opcional se comprar o kit) |
| Adafruit Display 7-Segmentos 4 Dígitos 1.2" c/ Backpack I2C - Verde | 1 | |
| Adafruit DS3231 | 1 | Ou DS1307 se preferir |
| Resistor 100k ohm | 1 | |
| Fotorresistor (LDR) | 1 | |
| Protoboard (genérica) | 1 | |
| Jumpers (genéricos) | 1 | 10-14 peças |
| Bateria de Moeda CR1220 | 1 |
Aplicativos e Serviços Online
- Arduino IDE
Sobre este Projeto
Introdução
Em tempos em que sempre temos um relógio no pulso — seja um relógio de pulso ou smartphone — o Arduino Uno/Nano parece um pouco sem graça. Caso contrário, temos dispositivos sincronizados com servidores NTP neste artigo, que estão constantemente online via conexão à Internet.
História
Um amigo meu recusou, por razões pessoais, um dispositivo com conexão WiFi no quarto dele, então equipei o Arduino com um relógio separado.
Escolhendo o Produto Certo
Fiquei surpreso com quantos produtos diferentes a Adafruit tinha disponíveis: dependendo da tensão desejada, bem como com diferentes precisões! Aqui decidi por dois produtos: o mais barato DS1307 e o provavelmente mais preciso DS3231.
Para ser claro: ambos os produtos podem ser usados neste relógio. Para usar o DS1307, atente para a atribuição correta dos pinos e a restrição à opção de exibição de temperatura: comente a função displayTemp(); dentro do loop().
Adaptando o Sketch
Procurei um sketch e encontrei algo de outro membro. Reestruturei e adaptei este sketch, mas mantive seus comentários em tcheco e adicionei observações em inglês.
O display deveria ser o maior possível, então decidi por um display de 7 segmentos: afinal, eu só queria ter a hora em mente.
A próxima surpresa positiva ao revisar os sketches: aqui, o DS3231 oferece a opção de usar a temperatura, que o DS1307 — além de sua imprecisão — não possui.
Inicializando os Relógios
Se você tem uma bateria de botão CR1220, pode seguir o breve tutorial da Adafruit sobre a configuração dos relógios. Em resumo, a hora não é baixada da Internet, mas obtida do PC local. O timestamp do upload é transferido para os temporizadores.
Funções
Depois, adicionei a matriz de LED ao circuito para saída de hora e temperatura. O período para alternância entre hora e temperatura pode ser definido via variáveis.
Em particular, como o controle dos pontos e dígitos individuais — piscando — na matriz de LED funciona e é controlado, é bem descrito no sketch.
Aqui me refiro a mais fontes da Adafruit.
O sketch é completado pelo ajuste de horário de verão/inverno e pelo brilho da matriz de LED dependente da luz ambiente (à noite o display deve ser mais escuro que à luz do dia).
No vídeo, você certamente notou a alternância entre hora e temperatura. Prestou atenção ao ponto à esquerda na imagem? Este muda de posição (cima ou baixo) dependendo dos segundos.
Me dê feedback se souber como exibir "ERR" na matriz de LED em caso de falha do relógio. No momento "bEEF" aparecerá:
// oh no, no data!
Serial.println("DS3231 Sensor Error - cant read temperature");
matrix.print(0xBEEF, HEX); // informa o usuário com BEEF
matrix.writeDisplay();
delay(5000);
Expansão
- Um alarme ou hora de alarme ainda não está integrado.
- Possivelmente também uma extensão via módulo Bluetooth e Blynk.
Atualização 01.02.2019:
A transferência da protoboard para uma placa perfurada permanente foi concluída hoje. Algumas fotos para os interessados:
Código
MyClock v1.0
// MyClock v1.0
// Ingo Lohs
// Hardware: testado com Arduino Uno e Arduino Nano (ATmega328P [Old Bootloader]) usando Arduino IDE v1.8.8
/* Teoria para a Matriz ht16k33
A maneira mais fácil é apenas chamar print - assim como você faz com Serial
print(variable,HEX) - isso imprimirá um número hexadecimal, de 0000 até FFFF
print(variable,DEC) ou print(variable) - isso imprimirá um inteiro decimal, de 0000 até 9999
Se você precisar de mais controle, pode chamar writeDigitNum(location, number) - isso escreverá o número (0-9) em uma única posição.
Posição #0 é a mais à esquerda, posição #2 são os pontos dos dois pontos, então você provavelmente vai querer pular, posição #4 é a mais à direita.
Para controlar os dois pontos e pontos decimais, use a função writeDigitRaw(location, bitmap).
(Note que ambos os pontos do dois pontos central estão ligados internamente no display, então não é possível endereçá-los separadamente.)
Especifique 2 para a localização e os bits são mapeados da seguinte forma:
0x02 - dois pontos central (ambos os pontos)
0x04 - dois pontos esquerdo - ponto inferior
0x08 - dois pontos esquerdo - ponto superior
0x10 - ponto decimal
Se você quiser um ponto decimal, chame writeDigitNum(location, number, true) que pintará o ponto decimal.
Para desenhar os dois pontos, use drawColon(true or false)
Se você quiser controle total dos segmentos em todos os dígitos, pode chamar writeDigitRaw(location,bitmask) para desenhar uma máscara bruta de 8 bits (como armazenada em um uint8_t) em qualquer localização.
Todas as rotinas de desenho apenas alteram a memória do display mantida pelo Arduino.
Não se esqueça de chamar writeDisplay() após desenhar para 'salvar' a memória na matriz via I2C.
Fonte: https://www.mouser.com/ds/2/737/adafruit-led-backpack-932846.pdf
*/
#include <Wire.h> // Habilite esta linha se usando Arduino Uno, Mega, etc.
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#define DS3231_I2C_ADDRESS 0x68
int ldr_sensor = A0; // LDR Fotoresistor com 100k Ohm para GND, outra perna 5V
int ldr_value = 0; // Valor LDR - var armazena valor
int brightness_matrix; // Brilho da Matriz LED - var armazena valor
int threshold_brightness = 400; // Limiar
int blinkrate_value = 0; // Taxa de piscar da Matriz LED
int delay_matrix_time = 20; // quanto tempo exibir hora (20 seg)
int delay_matrix_temp = 2000; // quanto tempo exibir temp (2 seg)
int blinky_dot = 2; // pisca nada - no lado esquerdo 2 pontos únicos
Adafruit_7segment matrix = Adafruit_7segment();
byte decToBcd(byte val) {
return ((val/10*16) + (val%10));
}
byte bcdToDec(byte val) {
return ((val/16*10) + (val%16));
}
void setup() {
Serial.begin(9600);
Wire.begin();
matrix.begin(0x70);
}
void loop() {
lightBrightness(); // Ajuste de Brilho da Matriz
matrixBlinkrate(); // Ajuste de Taxa de Piscar da Matriz
displayTime(); // exibe dados do relógio em tempo real
displayTemp(); // exibe a temperatura
}
void lightBrightness() {
// setBrightness(brilho) - permite alterar o brilho geral de todo o display. 0 é menos brilhante, 15 é mais brilhante e é o que é inicializado pelo display quando você inicia
ldr_value = analogRead(ldr_sensor);
Serial.print("LDR: ");
Serial.println(ldr_value);
if (ldr_value <= threshold_brightness) {
// medição de brilho vs Limiar
brightness_matrix = 0; // brilhante à luz do dia
} else {
brightness_matrix = 15; // escuro à noite
}
}
void matrixBlinkrate() {
// blinkRate(taxa) - Você pode piscar todo o display. 0 é sem piscar. 1, 2 ou 3 é para display piscando.
matrix.blinkRate(blinkrate_value);
}
void displayTime() {
matrix.setBrightness(brightness_matrix);
matrix.clear();
// quanto tempo exibir hora na matriz em segundos
for (uint16_t i = 0; i < delay_matrix_time; i++) {
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs3231(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
byte se = second;
byte mi = minute;
byte ho = hour;
byte we = dayOfWeek;
byte dm = dayOfMonth;
byte mo = month;
byte ye = year;
// mudança para horário de verão - último domingo de março às 2 horas
// >> Change to summer time - last Sunday in March at 2 o'clock
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 3) && (hour == 2)) {
// definir relógio para 3 horas
setDS3231time(se, mi, 3, we, dm, mo, ye);
}
// mudança para horário de inverno
// >> Change to winter time
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 1) && (year != 1)) {
// ano usado como indicação de que era verão
setDS3231time(se, mi, ho, we, dm, mo, 1);
}
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 3) && (year == 1)) {
// definir relógio para 2 horas e flag ano para 0
setDS3231time(se, mi, 2, we, dm, mo, 0);
}
/* Especifique 2 para a localização e os bits são mapeados da seguinte forma:
0x02 - dois pontos central (ambos os pontos)
0x04 - dois pontos esquerdo - ponto inferior
0x08 - dois pontos esquerdo - ponto superior
0x10 - ponto decimal */
/* localização #2 controla também os 2 pontos únicos esquerdos assim (=blinky_dot):
2 = pisca nada
3 = pisca nada
4 = pisca superior esquerdo (ponto superior único)
5 = pisca superior esquerdo (ponto superior único)
6 = pisca superior esquerdo (ponto superior único) + ambos Dois Pontos no meio
7 = pisca superior esquerdo (ponto superior único) + ambos Dois Pontos no meio
8 = pisca inferior esquerdo (ponto inferior único)
9 = pisca inferior esquerdo (ponto inferior único)
10 = pisca inferior esquerdo (ponto inferior único) + ambos Dois Pontos no meio
11 = pisca inferior esquerdo (ponto inferior único) + ambos Dois Pontos no meio
12 = pisca ambos Dois Pontos (ambos pontos únicos esquerdos) sem ambos Dois Pontos no meio */
// formato horas: _0:mm
if (hour > 9) {
matrix.writeDigitNum(0, (hour / 10), false);
matrix.writeDigitNum(1, (hour % 10), false);
} else {
matrix.writeDigitNum(1, hour, false);
}
// matrix.drawColon(true); // ambos Dois Pontos no meio ativos: caso não queira piscar os pontos esquerdos, pode ativar ambos Dois Pontos aqui
// dar chance aos segundos mostrarem nos pontos da localização esquerda
if (se <= 30) {
blinky_dot = 10; // pisca inferior esquerdo (ponto inferior único) + ambos Dois Pontos no meio
} else if (se > 30) {
blinky_dot = 6; // pisca superior esquerdo (ponto superior único) + ambos Dois Pontos no meio
}
matrix.writeDigitRaw(2, blinky_dot);
matrix.writeDigitNum(3, (minute / 10), false);
matrix.writeDigitNum(4, (minute % 10), false);
matrix.writeDisplay();
// pisca dois pontos duplos para meio segundo
delay(500);
matrix.drawColon(false);
matrix.writeDisplay();
delay(500);
}
}
void displayTemp() {
matrix.setBrightness(brightness_matrix);
matrix.clear();
byte temp = get3231Temp();
int abs_temp = abs(temp); // valor absoluto
matrix.writeDigitNum(1, (abs_temp % 10), false); // posição 1, valor 9, mostra decimal
if (temp < 0) matrix.writeDigitRaw(0, 64);
if (temp <= -10) matrix.writeDigitRaw(2, 12); // se temperatura negativa, plota sinal de menos na primeira posição
if (temp >= 10) matrix.writeDigitNum(0, (abs_temp/10), false); // posição 0, valor 1, mostra decimal
if (temp <= -10) matrix.writeDigitNum(0, (abs_temp/10), false); // posição 0, valor 1, mostra decimal
// matrix.writeDigitRaw(2,0x10); // ponto decimal
matrix.writeDigitRaw(3, 99); // 99 = "°"
matrix.writeDigitRaw(4, 57); // 57 = "C"
matrix.writeDisplay();
// exibe valores de temperatura na linha serial
Serial.print("Temperatura em C: ");
Serial.println(get3231Temp());
// +/- 3 Graus Celsius
// quanto tempo exibir temp na matriz em segundos
delay(delay_matrix_temp);
}
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) {
// define dados de hora e data no DS3231
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // define próxima entrada para começar no registrador de segundos
Wire.write(decToBcd(second)); // define segundos
Wire.write(decToBcd(minute)); // define minutos
Wire.write(decToBcd(hour)); // define horas
Wire.write(decToBcd(dayOfWeek)); // define dia da semana (1=Domingo, 7=Sábado)
Wire.write(decToBcd(dayOfMonth)); // define data (1 a 31)
Wire.write(decToBcd(month)); // define mês
Wire.write(decToBcd(year)); // define ano (0 a 99)
Wire.endTransmission();
}
void getDateDs3231(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) {
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
float get3231Temp() {
byte tMSB, tLSB;
float temp3231;
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0x11);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
if (Wire.available()) {
tMSB = Wire.read(); // complemento de 2 para parte inteira
tLSB = Wire.read(); // parte fracionária
temp3231 = (tMSB & B01111111); // faz matemática de complemento de 2 no Tmsb
temp3231 += ((tLSB >> 6) * 0.25); // só liga para bits 7 & 8
return temp3231;
} else {
// oh no, no data!
Serial.println("DS3231 Sensor Error - cant read temperature");
matrix.print(0xBEEF, HEX); // informa o usuário com BEEF
matrix.writeDisplay();
delay(5000);
}
}
MyClock v1.1
Atualização Horário de Verão/Inverno
// MyClock v1.1
// Inho Lohs
// Hardware: testado com Arduino Uno e Arduino Nano (ATmega328P [Old Bootloader]) usando Arduino IDE v1.8.8
// Mudança v1.0 > v1.1
// dayOfWeek == 1 em vez de 7 alterado em displayTime() para correta determinação do Domingo para a mudança de horário
/* Teoria para a Matriz ht16k33 ... (mesmo comentário da v1.0) */
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#define DS3231_I2C_ADDRESS 0x68
int ldr_sensor = A0;
int ldr_value = 0;
int brightness_matrix;
int threshold_brightness = 400;
int blinkrate_value = 0;
int delay_matrix_time = 20;
int delay_matrix_temp = 2000;
int blinky_dot = 2;
Adafruit_7segment matrix = Adafruit_7segment();
byte decToBcd(byte val) { return ((val/10*16) + (val%10)); }
byte bcdToDec(byte val) { return ((val/16*10) + (val%16)); }
void setup() {
Serial.begin(9600);
Wire.begin();
matrix.begin(0x70);
}
void loop() {
lightBrightness();
matrixBlinkrate();
displayTime();
displayTemp();
}
void lightBrightness() {
ldr_value = analogRead(ldr_sensor);
Serial.print("LDR: ");
Serial.println(ldr_value);
if (ldr_value <= threshold_brightness) {
brightness_matrix = 0;
} else {
brightness_matrix = 15;
}
}
void matrixBlinkrate() {
matrix.blinkRate(blinkrate_value);
}
void displayTime() {
matrix.setBrightness(brightness_matrix);
matrix.clear();
for (uint16_t i = 0; i < delay_matrix_time; i++) {
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs3231(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
byte se = second;
byte mi = minute;
byte ho = hour;
byte we = dayOfWeek;
byte dm = dayOfMonth;
byte mo = month;
byte ye = year;
// mudança para horário de verão - último domingo de março às 2 horas
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 3) && (hour == 2)) {
setDS3231time(se, mi, 3, we, dm, mo, ye);
}
// mudança para horário de inverno
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 1) && (year != 1)) {
setDS3231time(se, mi, ho, we, dm, mo, 1);
}
if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 3) && (year == 1)) {
setDS3231time(se, mi, 2, we, dm, mo, 0);
}
if (hour > 9) {
matrix.writeDigitNum(0, (hour / 10), false);
matrix.writeDigitNum(1, (hour % 10), false);
} else {
matrix.writeDigitNum(1, hour, false);
}
if (se <= 30) {
blinky_dot = 10;
} else if (se > 30) {
blinky_dot = 6;
}
matrix.writeDigitRaw(2, blinky_dot);
matrix.writeDigitNum(3, (minute / 10), false);
matrix.writeDigitNum(4, (minute % 10), false);
matrix.writeDisplay();
delay(500);
matrix.drawColon(false);
matrix.writeDisplay();
delay(500);
}
}
void displayTemp() {
matrix.setBrightness(brightness_matrix);
matrix.clear();
byte temp = get3231Temp();
int abs_temp = abs(temp);
matrix.writeDigitNum(1, (abs_temp % 10), false);
if (temp < 0) matrix.writeDigitRaw(0, 64);
if (temp <= -10) matrix.writeDigitRaw(2, 12);
if (temp >= 10) matrix.writeDigitNum(0, (abs_temp/10), false);
if (temp <= -10) matrix.writeDigitNum(0, (abs_temp/10), false);
matrix.writeDigitRaw(3, 99);
matrix.writeDigitRaw(4, 57);
matrix.writeDisplay();
Serial.print("Temperatur in C: ");
Serial.println(get3231Temp());
delay(delay_matrix_temp);
}
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) {
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0);
Wire.write(decToBcd(second));
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour));
Wire.write(decToBcd(dayOfWeek));
Wire.write(decToBcd(dayOfMonth));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.endTransmission();
}
void getDateDs3231(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) {
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
float get3231Temp() {
byte tMSB, tLSB;
float temp3231;
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0x11);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
if (Wire.available()) {
tMSB = Wire.read();
tLSB = Wire.read();
temp3231 = (tMSB & B01111111);
temp3231 += ((tLSB >> 6) * 0.25);
return temp3231;
} else {
Serial.println("DS3231 Sensor Error - cant read temperature");
matrix.print(0xBEEF, HEX);
matrix.writeDisplay();
delay(5000);
}
}
GitHub: ILohs/MySketches
Esquemáticos
Caminho LDR corrigido


Processo de manufatura
- Relógio POV com Arduino: Construa um Display LED de Persistência de Visão
- Construa um Relógio Retrô Numitron com Arduino: Simples, Confiável e Eficiente
- Relógio de Parede Estiloso com Anel Neopixel 60 LEDs Adafruit e Arduino
- Relógio em Tempo Real com Arduino para Horários de Oração Islâmica
- Construa um Relógio Mestre LED Personalizado com Alarme – Visível a 12 Metros
- Monitor de Temperatura e Relógio Tempo Real com Arduino Due e Display TFT 3.2"
- Controle Dois Motores de Passo com Arduino Nano e Joystick – Tutorial Simples
- Smartwatch Arduino DIY: Construa Seu Próprio Relógio Estilo Apple Watch
- Relógio Preciso de 7 Segmentos com Arduino Pro Mini e RTC DS1302
- Construa um Relógio Despertador Confiável com RTC DS1302 e Arduino UNO