Home › Tag Archives › i2c

Como usar I2C no ESP8266 e especificamente no ESP-01

esp8266_esp01_automalabsUsando a IDE Arduino com Board ESP8266 é realmente muito simples. Não é preciso usar uma biblioteca I2C específica como é o caso de rede e seus programas já existentes devem funcionar exceto por um detalhe: você precisa especificar na inicialização da biblioteca Wire os pinos que você está usando para I2C no ESP8266.

Wire.begin(SDApin, SCLpin);

No ESP-01 você provavelmente vai ter que usar GPIO0 e GPIO2, por isso a inicialização fica assim:

Wire.begin(0, 2);

Em todos os casos você deve colocar resistores de pull-up de 4k7 nos GPIO que você escolheu para I2C. Atente para o fato de que alguns módulos i2C já vem com esses resistores.

No desenho SDA e SCL estão invertidos. Depois eu corrijo isso.

ESP-01_PCF8574_I2C

Display serial UART, SPI e I2C com interface destacável, compatível com LCDSmartie.

As fotos abaixo mostram um modelo com display 1602, mas também forneço com display 2004.

O endereço I2C da interface é programável. Basta entrar no modo UART e dar um comando pela serial para definir o endereço. A velocidade da serial pode ser mudada da mesma maneira, sendo que o default é 9600BPS. Cocê escolhe os modos de operação I2C e SPI por jumpers de solda. O default é o modo UART (sem jmpers).

 

Forneço plugin para LCDSmartie.

Wii nunchuck no arduino

Este produto está disponível nos modelos com fio e sem fio. Ambos acompanham adaptador wiichuck.

O Wii nunchuck é um dispositivo I2C que responde no endereço 0x52 e possui um joystick, dois botões e um acelerômetro cujas posições você pode “ler” usando rotinas adequadas no arduino.  Apenas quatro fios são necessários: VCC, GND, SCL e SDA.

Teoricamente ele é um dispositivo de 3V, mas a prática mostrou que você pode alimentá-lo com 5V no arduino sem problemas.

Existem dois tipos de nunchuck no mercado: o original (ou “OEM”) e o genérico. Embora os dois funcionem no Wii aparentemente sem diferença, quando tentamos usar no Arduino três diferenças são percebidas:

  • O genérico tem um acelerômetro menos preciso;
  • A calibração do joystick varia bastante entre genéricos;
  • O genérico pode ou não suportar criptografia na comunicação I2C.

A terceira diferença é a mais importante, pois o nunchuck que não suporta criptografia precisa ser usado de maneira diferente. O Wii se encarrega de fazer isso de forma transparente, mas uma grande quantidade de exemplos de uso do nunchuck no arduino que você encontra na internet prevê o uso de um original e por isso pode não funcionar com muitos genéricos. É preciso  procurar por exemplos que tenham sido feitos para genéricos.

Este sketch de teste funciona com originais e genéricos, usando ou não o adaptador Wiichuck:

/*
* NunchuckPrint
*
* 2007 Tod E. Kurt, http://todbot.com/blog/
*
* The Wii Nunchuck reading code is taken from Windmeadow Labs
*   http://www.windmeadow.com/node/42
*/

#include <Wire.h>

void setup()
{
Serial.begin(19200);
nunchuck_setpowerpins(); // use analog pins 2&3 as fake gnd & pwr
nunchuck_init(); // send the initilization handshake
Serial.print ("Finished setup\n");
}

void loop()
{
nunchuck_get_data();
nunchuck_print_data();
delay(100);
}

//
// Nunchuck functions
//

static uint8_t nunchuck_buf[6];   // array to store nunchuck data,

// Uses port C (analog in) pins as power & ground for Nunchuck
static void nunchuck_setpowerpins()
{
#define pwrpin PORTC3
#define gndpin PORTC2
DDRC |= _BV(pwrpin) | _BV(gndpin);
PORTC &=~ _BV(gndpin);
PORTC |=  _BV(pwrpin);
delay(100);  // wait for things to stabilize
}

// initialize the I2C system, join the I2C bus,
// and tell the nunchuck we're talking to it
void nunchuck_init()
{
Wire.begin();                    // join i2c bus as master
Wire.beginTransmission(0x52);    // transmit to device 0x52

//Só funciona com o original
//  Wire.send(0x40);        // sends memory address
//  Wire.send(0x00);        // sends sent a zero.

//Funciona com a versão xing-ling
#if (ARDUINO >= 100)
Wire.write(0xF0);
Wire.write(0x55);
#else
Wire.send(0xF0);
Wire.send(0x55);
#endif
Wire.endTransmission();
delay(1);

Wire.beginTransmission(0x52);
#if (ARDUINO >= 100)
Wire.write(0xFB);
Wire.write(0x00);
#else
Wire.send(0xFB);
Wire.send(0x00);
#endif
Wire.endTransmission();

}

// Send a request for data to the nunchuck
// was "send_zero()"
void nunchuck_send_request()
{
Wire.beginTransmission(0x52);    // transmit to device 0x52
#if (ARDUINO >= 100)
Wire.write(0x00);
#else
Wire.send(0x00);
#endif
Wire.endTransmission();    // stop transmitting
}

// Receive data back from the nunchuck,
int nunchuck_get_data()
{
int cnt=0;
Wire.requestFrom (0x52, 6);    // request data from nunchuck
while (Wire.available ()) {
// receive byte as an integer

#if (ARDUINO >= 100)
nunchuck_buf[cnt] = nunchuk_decode_byte(Wire.read());
#else
nunchuck_buf[cnt] = nunchuk_decode_byte(Wire.receive());
#endif

cnt++;
}
nunchuck_send_request();  // send request for next data payload
// If we recieved the 6 bytes, then go print them
if (cnt >= 5) {
return 1;   // success
}
return 0; //failure
}

// Print the input data we have recieved
// accel data is 10 bits long
// so we read 8 bits, then we have to add
// on the last 2 bits.  That is why I
// multiply them by 2 * 2
void nunchuck_print_data()
{
static int i=0;
int joy_x_axis = nunchuck_buf[0];
int joy_y_axis = nunchuck_buf[1];
int accel_x_axis = nunchuck_buf[2]; // * 2 * 2;
int accel_y_axis = nunchuck_buf[3]; // * 2 * 2;
int accel_z_axis = nunchuck_buf[4]; // * 2 * 2;

int z_button = 0;
int c_button = 0;

// byte nunchuck_buf[5] contains bits for z and c buttons
// it also contains the least significant bits for the accelerometer data
// so we have to check each bit of byte outbuf[5]
if ((nunchuck_buf[5] >> 0) & 1)
z_button = 1;
if ((nunchuck_buf[5] >> 1) & 1)
c_button = 1;

if ((nunchuck_buf[5] >> 2) & 1)
accel_x_axis += 2;
if ((nunchuck_buf[5] >> 3) & 1)
accel_x_axis += 1;

if ((nunchuck_buf[5] >> 4) & 1)
accel_y_axis += 2;
if ((nunchuck_buf[5] >> 5) & 1)
accel_y_axis += 1;

if ((nunchuck_buf[5] >> 6) & 1)
accel_z_axis += 2;
if ((nunchuck_buf[5] >> 7) & 1)
accel_z_axis += 1;

// Numera sequencialmente as linhas do log
//  Serial.print(i,DEC);
//  Serial.print("\t");

Serial.print(" joystick: ");
Serial.print(joy_x_axis,DEC);
Serial.print(",");
Serial.print(joy_y_axis, DEC);
Serial.print("  \t");

Serial.print("\t acc x:");
Serial.print(accel_x_axis, DEC);
Serial.print("\t\t y:");
Serial.print(accel_y_axis, DEC);
Serial.print("\t\t z:");
Serial.print(accel_z_axis, DEC);
Serial.print("\t");

Serial.print(" but:");
Serial.print(z_button, DEC);
Serial.print(",");
Serial.print(c_button, DEC);

Serial.print("\r\n");  // newline
i++;
}

// Encode data to format that most wiimote drivers except
// only needed if you use one of the regular wiimote drivers
char nunchuk_decode_byte (char x)
{
// x = (x ^ 0x17) + 0x17;
return x;
}

As diferenças são pequenas e estão em dois pontos do código: inicialização e leitura.

Inicialização

  • No original, é preciso escrever 0x00 no endereço 0x40;
  • No genérico que não suporta criptografia, é preciso escrever 0x00 no endereço 0xFB e 0x55 no endereço 0xF0.

Original

Wire.beginTransmission(0x52);// transmit to device 0x52
Wire.write(0x40);// sends memory address
Wire.write(0x00);// sends sent a zero.
Wire.endTransmission();// stop transmitting

Genérico

Wire.beginTransmission(0x52);      // endereço do nunchuck
Wire.write(0xF0);                    // registro de incialização 1
Wire.write(0x55);                  //dado
Wire.endTransmission();        //faz a transmissão
delay(1);
Wire.beginTransmission(0x52);
Wire.write(0xFB);                   // registro de incialização 2
Wire.write(0x00);                   //dado
Wire.endTransmission();             //faz a transmissão

Procure a função que faz a decodificação (geralmente procurar por “0x17” o levará direto a ela

uint8_t _nunchuk_decode_byte (uint8_t x)
{
x = (x ^ 0x17) + 0x17;
return x;
}

e desative a decodificação

uint8_t _nunchuk_decode_byte (uint8_t x)
{
//x = (x ^ 0x17) + 0x17;
return x;
}

Se você não desativar a decodificação o nunchuck parecerá funcionar, mas você encontrará dois problemas:

  • A leitura do acelerômetro ficará bagunçada;
  • O comportamento do botão Z ficará errado.

O adaptador wiichuck

A finalidade do adaptador wiichuck é meramente evitar que você precise cortar o conector original do nunchuck. Isso acelera bastante os testes com diferentes modelos de nunchuck (inclusive o que você conseguir emprestado), sem precisar danificá-lo.

Cuidado: conecte o nunchuck ao adaptador com o chanfro para cima, como mostrado na foto. Conectá-lo ao contrário vai inverter positivo e negativo da alimentação possivelmente destruindo o seu nunchuck.  Eu estou inclinado a acreditar que o nunchuck tenha proteção contra inversão de polaridade, porque é muito raro encontrar um alerta sobre isso. Porém nem todo nunchuck é igual (principalmente os genéricos) então é melhor prestar atenção à polaridade.

O wiichuck não funciona diretamente no arduino Mega. Para usá-lo no mega, faça as seguintes conexões com jumpers entre o wiichuck e o mega:

1: GND
2: 5V
3: SDA (pino 20)
4: SCL (pino 21)

Segurando o adaptador com a palavra “wiichuck” de pé, o pino 1 é o da esquerda.

Mas mesmo no Arduino UNO/Duemilanove o wiichuck depende um “truque” de programação para funcionar. Esse truque transforma as entradas analógicas A2 e A3 em pinos de alimentação. O código que faz isso se parece com este:

static void nunchuck_setpowerpins()
{
#define pwrpin PORTC3 //entrada A3
#define gndpin PORTC2 //entrada A2
DDRC |= _BV(pwrpin) | _BV(gndpin);
PORTC &=~ _BV(gndpin);
PORTC |=  _BV(pwrpin);
delay(100);  // wait for things to stabilize
}

Se você não estiver usando esse recurso, desative-o, ou não conseguirá usar as entradas A2 e A3 para outras coisas.

Links Úteis

Wiibrew – Explica a questão da criptografia, endereçamento, registros, etc.

Display LCD serial I2C 2004 com interface destacável

Este post trata apenas do específico sobre o modelo de 20 colunas e 4 linhas (2004). Para maiores informações sobre o produto, leia o post sobre o modelo 1602.

Teste básico de funcionamento:

#include <Wire.h>;
#include <LiquidCrystal_I2C.h>;
//Aqui definimos endereço, número de colunas e linhas

LiquidCrystal_I2C lcd(0x27,20,4);
void setup()
{
  lcd.init();  
 
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Linha zero 123456789");
    lcd.setCursor(0, 1);
  lcd.print("Linha um   123456789");
    lcd.setCursor(0, 2);
  lcd.print("Linha dois 123456789");
    lcd.setCursor(0, 3);
  lcd.print("Linha tres 123456789");
}

void loop()
{
}

Teclado membrana de 16 teclas com interface I2C

Este é o mesmo teclado membrana que eu já oferecia, agora com o acréscimo de um cabo que faz a interface I2C, reduzindo de oito para dois os pontos de i/o necessários.

Posso fornecer com endereços I2C de 0x20 a 0x26 (pode ser 0x27 também, mas evito usar este porque é o endereço fixo do meu display). Caso não seja especificado, será enviado com qualquer valor que eu tenha em estoque. O endereço é claramente marcado na região preta que envolve o circuito.

Minha convenção de cores na fiação que conecta ao Arduino (a interface mostrada na foto não segue esse padrão):

  • +5V: vermelho
  • GND: preto
  • SCL (CLOCK): Amarelo ou laranja
  • SDA (DATA): qualquer outra cor (geralmente branco)
  • INTERRUPT: Verde – Nem todos os meu teclados saem com esse fio. Ele sinaliza que houve uma mudança no estado das entradas. Na maioria dos casos você pode deixar solto.

O uso é bem simples.

Usando Arduino 0022:

Requer a biblioteca i2ckeypad de Angel Sancho.

Sketch adaptado do exemplo fornecido com a biblioteca.


#include <Wire.h>
#include <i2ckeypad.h>

#define linhas 4
#define colunas 4
#define endereco 0x20

i2ckeypad teclado = i2ckeypad(endereco, linhas, colunas);

void setup()
{
Serial.begin(9600);

Wire.begin();

teclado.init();

Serial.print("Testando Teclado I2C. Aperte teclas agora.\n\n");
}

void loop()
{
char key = teclado.get_key();

if(key != '\0') {
Serial.print(key);
}
}

Usando Arduino 1.0x
Você precisa da biblioteca Keypad_I2C, de Alexander Brevig
E do seguinte sketch:

/*
|| Adaptado do exemplo CustomKeypad de Alexander Brevig
*/
#include <Keypad_I2C.h>
#include <Keypad.h>
#include <Wire.h>

#define Endereco_I2C 0x20

const byte NumLinhas = 4;
const byte NumColunas = 4;
//Essa matriz tem que corresponder à aparência física do teclado
char hexaKeys[NumLinhas][NumColunas] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[NumLinhas] = {0, 1, 2, 3}; //Aqui você define que pinos estão ligados às linhas do teclado
byte colPins[NumColunas] = {4, 5, 6, 7}; //e aqui os pinos ligados às colunas

//inicializa uma instância da classe NewKeypad
Keypad_I2C customKeypad( makeKeymap(hexaKeys), rowPins, colPins, NumLinhas, NumColunas, Endereco_I2C);

void setup(){
  customKeypad.begin( );
  Serial.begin(9600);
}

void loop(){
  char customKey = customKeypad.getKey();

  if (customKey != NO_KEY){
    Serial.println(customKey);
  }
}

Cópias locais das bibliotecas:

Arduino Library I2ckeypad-20090224-3
Arduino Library I2ckeypad-20090224-3
arduino_library_i2ckeypad-20090224-3_v0.1.zip
Version: 0.1
27.3 KiB
355 Downloads
Detalhes...
Arduino Library  - Keypad I2C
Arduino Library - Keypad I2C
Arduino Library - Keypad_I2C.zip
866.3 KiB
401 Downloads
Detalhes...

Módulo de relês 8 canais I2C

Este é o mesmo módulo de oito canais que eu já oferecia (siga o link para maiores detalhes sobre o módulo), agora com o acréscimo de um cabo que faz a interface I2C, reduzindo de oito para dois os pontos de i/o necessários. Até oito módulos idênticos podem ser conectados em paralelo, dando um total de 64 relês, usando os mesmos dois fios.

Posso fornecer com endereços I2C de 0x20 a 0x27. Caso não seja especificado, será enviado com qualquer valor que eu tenha em estoque. O endereço é claramente marcado na interface.

Minha convenção de cores na fiação que conecta ao Arduino (a interface mostrada na foto não segue esse padrão):

  • +5V: vermelho
  • GND: preto
  • SCL (CLOCK): Amarelo ou laranja – Arduino UNO: A5, Arduino Mega: 21;
  • SDA (DATA): qualquer outra cor (geralmente branco) – Arduino UNO: A4, Arduino Mega: 20;

As saídas do conversor são do tipo latch. Assim não há rebote quando você escreve outro valor na interface. Porém é preciso tomar o cuidado de manter um registro em memória de que saídas estão ligadas, para evitar desligá-las acidentalmente ao ligar outro relê. Mas não é nada que um uso simples de operações bitwise como “AND”, “OR” e “XOR” não resolva.

Falando assim parece difícil, mas na verdade é bem fácil quando as rotinas estão prontas. Você consegue ligar um relê com um comando simples do tipo “LigarRele(7)” sem perturbar nenhuma das outras saídas. Exemplo a seguir:


//aqui você coloca o endereço que escrevi na interface
#define ADDRESS 0x23

//Nesta variavel ficará a representação em memória do estado das saídas
//Depois de manipular as saídas na variável jogamos a variável para a interface.
//Assim é evitada a modificação do estado das saídas erradas
byte Saidas = 0;

#include <Wire.h>

void setup()
{
Wire.begin();
//Por precaução, mas não é realmente necessário.
DesligaTudo();
}

void loop()
{

//Testa cada um dos relês, desligando os outros
for (int i=1;i<9;i++){
LigarReleUnico(i);
delay(200);
}
DesligaTudo();
delay(200);

//agora no caminho inverso
for (int i=8;i>0;i--){
LigarReleUnico(i);
delay(200);
}

DesligaTudo();
delay(200);

//Todos os relês serão ligados em sequencia até ficarem todos ligados ...
for (int i=1;i<9;i++){
LigarRele(i);
delay(500);
}

//... e serão desligados na mesma ordem
for (int i=1;i<9;i++){
DesligarRele(i);
delay(500);
}

}

void DesligaTudo()
{
Saidas=0;
sendI2Cneg(Saidas);
}

byte BitPosToDec(byte BitPos)
//Para entender esta função você precisa entender numeração binária
//e o funcionamento do operador SHIFT (<< e >>)
{
int bt=1; //isso "liga" o bit menos significativo do byte
bt = bt << BitPos-1;//move o bit ligado para a esquerda
return(bt);
}

void LigarRele(byte rele)
{
//Esta função liga um relê sem perturbar o estado dos outros
byte x=BitPosToDec(rele);
Saidas = Saidas | x;
sendI2Cneg(Saidas);
}

void DesligarRele(byte rele)
{ //Esta função desliga um relê sem perturbar o estado dos outros
byte x=BitPosToDec(rele);
 // Saidas = Saidas ^ x;
 Saidas = Saidas &(~x); //corrigido em 04/07/2017
sendI2Cneg(Saidas);
}

void LigarReleUnico(byte rele)
{
//Esta função vai desligar qualquer outro relê que já esteja ligado
sendI2Cneg(BitPosToDec(rele));
}

void sendI2Cneg(byte b)
/*Seria mais elegante chamar esta função de "AtualizarSaidas",
mas seria preciso fazer outras modificações que tornariam
o uso mais chato. Preferi deixar assim.
O "neg" vem do fato de que o módulo de relês opera
com lógica negativa e por isso todos os bits precisam ser invertidos
ao enviar. Uma única operação XOR se encarrega disso */
{
Wire.beginTransmission(ADDRESS);

//compatibilizando com o Arduino 1.x
#if (ARDUINO >= 100)
Wire.write(b ^ 255); // XOR
#else
Wire.send(b ^ 255); // XOR
#endif
Wire.endTransmission();

}

Forma alternativa da função BitPosToDec, que pode ser mais fácil de entender

byte BitPosToDec(byte BitPos)
{
switch (BitPos) {
case 1:
return(1);
break;
case 2:
return(2);
break;
case 3:
return(4);
break;
case 4:
return(8);
break;
case 5:
return(16);
break;
case 6:
return(32);
break;
case 7:
return(64);
break;
case 8:
return(128);
break;
}
}

Display LCD serial I2C 1602 com interface destacável

Precisando de pinos livres extras no seu projeto ou passar um cabo com uma dúzia de fios até o LCD está te incomodando?

  • Display serial completo. Só requer alimentação de 5V e dois fios para o bus I2C. Responde no endereço 0x27 (interface vermelha) e 0x20 (interface preta);
  • Como se trata de I2C, que é um bus (compartilhado por natureza), podemos até dizer que não ocupa pino nenhum;
  • Controle de contraste via trimpot integrado;
  • Backlight pode ser desligado/ligado com comandos de software;
  • Compatível com qualquer microcontrolador que possa se comunicar por I2C, mas somente dou suporte ao Arduino;
  • Compatível com a biblioteca LiquidCrystal_I2C do Arduino;
  • Interface destacável. Você pode usá-la em qualquer display que tenha o mesmo pinout (veja pinout na foto abaixo);
  • Posso fornecer o produto na forma de kit, sem headers soldados.

Instruções de uso

O display funcionará igualmente com o Arduino Uno/Duemilanove ou Mega sem modificações nos sketches, mas atente para o fato de que o bus i2c fica em pinos diferentes em cada modelo:

  • No Uno/Duemilanove: Pinos analógicos A4 e A5
  • No Mega: Pinos 20 e 21

O display é compatível com o modelo da DFROBOT. Baixe esta biblioteca (supostamente compatível com IDE 1.0 e 0.2x,mas só testei com 0.22), descompacte no diretório libraries (certifique-se de que os arquivos fiquem em libraries/LiquidCrystal_I2C) e rode o exemplo LiquidCrystal_I2C -> Custom Chars.

Todos os displays são testados por mim e saem com o contraste ajustado, mas se por acaso você mexer no trimpot e a imagem não estiver mais aparecendo, gire o trimpot nos dois sentidos até que apareça. Um ajuste incorreto não danificará o display, por isso pode mexer à vontade.

Cópia local da biblioteca:

Módulo display LCD 1602 serial I2C / TWI e SPI

Baseado em chip MCP23008. Endereço I2C default: 0×0, mas configurável com jumpers de solda. Este é o modelo fabricado pela Elecfreaks, que é uma variação do modelo da Adafruit

É necessário remover a biblioteca liquidcrystal e instalar a fornecida.

Backlight

O backlight pode ser controlado pelo software. Procedure setBacklight () da biblioteca.

Módulo display LCD 1602 serial I2C / TWI

Baseado em chip Philips PCF8574.

Endereço I2C: 0x27

Compatível com o modelo da DFROBOT. Biblioteca e exemplos:

http://www.robotshop.com/ProductInfo.aspx?pc=RB-Dfr-69

Backlight

O backlight pode ser controlado pelo software. Procedures backlight() e   noBackligth() da biblioteca.

O jumper serve para o controle externo do backlight. Com o jumper no lugar o backlight fica permanentemente ativo. Retirando o jumper, você pode conectar uma saída do microcontrolador para o controle do backlight.

Módulo RTC DS1307 com EEPROM I2C

A imagem abaixo mostra dois módulos iguais. Cada um com uma face da placa para cima.

E aqui está a documentação oficial

O endereço i2c do RTC é 0x68. Isso é definido no chip DS1307 e não pode ser configurado pelo fabricante do módulo, nem pelo usuário. A vantagem disso é que qualquer módulo RTC DS1307 substitui qualquer outro, sem precisar de configuração.

Funciona mesmo sem os resistores.
Funciona com a biblioteca RTCLib da Adafruit.
Para fazer o ajuste de data e hora com esta biblioteca, é preciso dar o seguinte comando no SETUP:

Execute apenas uma vez, depois remova ou comente o comando e grave o sketch de novo.

Equipado com memória I2C 24C32 de 32kbits (4KB). Datasheet.

A operação da EEPROM é independente do RTC. Lembre-se de que o Arduino tem uma EEPROM interna, por isso para usar a EEPROM do módulo é preciso usar rotinas feitas para acesso a EEPROM I2C, como esta.

Neste módulo é possível acrescentar um sensor de temperatura DS18B20. Mas não é lá grande vantagem, porque o DS18B20 não é i2C e vai requerer que você puxe mais um fio para o módulo. Além disso, você fica limitado a medir a temperatura nas proximidades do módulo.

Informações importantes sobre a operação com bateria

Segundo o datasheet do DS1307:

  • O RTC só está completamente funcional quando a tensão VCC é igual a 1.25 * Vbat (a tensão da bateria);
  • Quando VCC é menor que 1.25*Vbat mas ainda é maior que Vbat, o RTC ainda é alimentado por VCC, mas você já não pode ler ou gravar nele;
  • Quando VCC cai abaixo de Vbat+0.2V, o RTC entra em um modo de baixo consumo e passa a ser alimentado pela bateria;
  • Vbat precisa estar entre 2 e 3.5V para operação normal do RTC.

Este módulo foi projetado para usar uma bateria recarregável LIR2032, que completamente carregada chega a 4.2V e assim além de estar acima da tensão máxima de bateria para o DS1307, normalmente você precisaria fazer VCC ser 1.25*4.2= 5.25V para que ele funcionasse, o que é uma tensão “alta” e difícil de garantir. Para contornar esses problemas o projetista colocou um divisor de tensão entre a bateria e a entrada Vbat do DS1307 para que ele enxergue uma tensão menor. Com a bateria completamente carregada (4.2V) a tensão em Vbat, que você pode ler no terminal BAT de P1, é de cerca de 2.9V. Assim você precisa de um VCC de apenas 1.25*3 = 3,75V para operação total do RTC. Entretanto é recomendado alimentar o módulo sempre com 5V por causa dos componentes extras.

Um segundo problema criado por esse design é que se você quiser alimentar o módulo com uma bateria não-recarregável como a CR2032, com a bateria a 3V a tensão em Vbat será de 2.14V. Perigosamente perto do mínimo. Para fazer com que o módulo opere com a CR2032 você precisará eliminar o circuito de carga da bateria (basta remover D1) e o divisor de tensão (remova R4 e coloque um fio no lugar de R6).

O diagrama abaixo facilita a compreensão do que expliquei:

ds1307_module_TinyRTC_schematic_automalabs.com.br

A durabilidade estimada de uma bateria CR2032 decente alimentando sozinha o DS1307 é de mais de 10 anos. Segundo o datasheet do DS1307 são necessários 48mAh para isso e uma Energizer CR2032 alcança 240mAh. Isso torna a decisão de usar uma bateria recarregável no módulo bastante questionável, principalmente porque esses módulos costumam vir da China com baterias LIR2032 de baixíssima qualidade que descarregam completamente em alguns meses e depois não conseguem ser carregadas.  Eu comprei cerca de 130 módulos destes e só consegui salvar duas baterias.

Se você tiver uma fonte confiável de baterias CR2032 (que também costumam ser muito falsificadas) é melhor fazer a alteração no módulo para usá-las.

Dicas diversas

Relógio atrasando em grandes intervalos de uma vez

Talvez você tenha esquecido ativo no setup do sketch uma linha de código para inicializar a hora do RTC. Por exemplo, este é o método usado nos exemplos da RTClib:

rtc.adjust(DateTime(__DATE__, __TIME__));

__DATE__ e __TIME__ são substituídas automaticamente pela data e hora do seu computador no momento da compilação do programa e esses valores ficarão gravados no Arduino. O relógio parecerá funcionar bem enquanto energizado mas toda vez que o arduino conectado ao módulo for ligado ou resetado vai reconfigurar o relógio para esses valores. Se você estiver presente e observando a cada reset verá o relógio voltar sempre ao mesmo dia e horário e desconfiará da causa, mas se não estiver não perceberá o padrão e vai ficar propenso a confundir com um defeito.

Relógio congelado

Pode ter ocorrido o mesmo esquecimento do item anterior, mas em loop.