Projetos

Relógio de Fases Lunares com o Arduino

Eletrogate 11 de abril de 2024

Introdução

Neste tutorial, utilizando um Arduino UNO, um módulo RTC (Real Time Clock) e um Display OLED, vamos aprender a construir um relógio que mostra a fase lunar atual e a taxa de iluminação. Além disso, discutiremos os métodos para calcular a fase da lua a partir da data atual. Também exploraremos como configurar o display de LEDs e entenderemos como funcionam os bitmap para representar as diferentes fases lunares. No final, você terá um relógio como o da imagem a seguir.


Montando o Projeto

Antes de falarmos sobre as diferentes formas de calcularmos a fase lunar atual, iremos realizar a montagem das partes que compõem o relógio. O módulo RTC é responsável por fornecer a data atual com precisão para o Arduino, sendo o modelo DS1307 de alta precisão e baixo consumo energético, podendo fornecer informações como segundos, minutos, dia, data, mês e ano. Você pode ler mais sobre esse componente neste outro tutorial do blog.

Para a montagem do circuito, é importante entender a função dos pinos SCL (Serial Clock) e SDA (Serial Data) tanto no módulo OLED quanto no RTC DS1307. Esses pinos são responsáveis pela comunicação I2C, que permite a transferência de dados entre os dispositivos e o Arduino de forma serial. No Arduino Uno, os pinos A4 e A5 são designados como SDA e SCL, respectivamente, para essa finalidade. Portanto, para conectar o módulo OLED e o RTC DS1307 ao Arduino, é necessário conectar o pino SCL de cada dispositivo ao pino A5 do Arduino e o pino SDA de cada dispositivo ao pino A4 do Arduino.

Essa conexão estabelece a comunicação serial entre os dispositivos e o microcontrolador. Além disso, também é necessário conectar os pinos de alimentação (VCC) de cada dispositivo ao pino 5V do Arduino para fornecer energia e os pinos de terra (GND) de cada dispositivo ao GND do Arduino, como demonstrado no esquema a seguir.

Ao seguir essas conexões corretamente, garantimos o funcionamento adequado do circuito, permitindo que o Arduino comunique-se com o módulo OLED e o RTC por meio do protocolo I2C. Depois de realizada a montagem, podemos carregar os códigos ao Arduino através do Cabo USB. Antes disso, vamos entender melhor as fases lunares e as diferentes formas de calcular a fase da lua a partir de uma data.


Fases da Lua

A Lua passa por todas as suas fases em um período de aproximadamente 29,5 dias, conhecido como lunação ou período sinódico lunar. Esse ciclo difere do período em que a lua circunda a Terra, que é cerca de 27,3 dias, chamado de mês sideral. Essa discrepância está relacionada ao movimento orbital da Terra ao redor do Sol.

Além disso, as fases da lua podem ser descritas em termos de porcentagem de iluminação da Lua visível da Terra. Elas são frequentemente divididas em oito fases igualmente espaçadas: lua nova, crescente, quarto crescente, lua gibosa crescente, lua cheia, minguante, quarto minguante e lua gibosa minguante. Cada fase representa aproximadamente 12.5% da jornada lunar, resultando em uma divisão uniforme. Na imagem a seguir, podemos visualizar melhor esses conceitos.

Esse sistema facilita a compreensão das diferentes fases da Lua ao longo de seu ciclo. Para determinar os tipos de lua, basta observar a quantidade de dias no período sinódico (29,5 dias) e dividir pela quantidade de fases lunares (8, neste caso), resultando em aproximadamente 3,7. Portanto, a cada 3,7 dias, ocorre um novo tipo de lua. No entanto, existem diferentes formas de obter a fase lunar correspondente a partir de uma data, variando de acordo com a precisão e custo computacional.


Método 1: Mês Sinódico

A abordagem mais simples corresponde a aplicação das informações explicadas anteriormente. Para isso, podemos usar uma data com uma lua cheia como referência. Nesse exemplo, utilizaremos arbitrariamente o dia 1 de janeiro de 1972. Assim, considerando o ciclo de 29,5 dias, calculamos a diferença entre essa data e o resto da divisão pelo período sinódico para obter o número de dias lunares desde a última lua cheia.

No entanto, para isso, precisamos normalizar as datas para a data Juliana, uma forma de contagem contínua do número de dias sem a distinção de semanas, meses ou anos. A partir da data Juliana, subtraímos o nosso dia de referência de lua cheia para obter o número de dias decorridos desde aquela dia. As datas Julianas são um método direto para representar datas contando os dias desde 1º de janeiro de 4713 a.C.

Ou seja, para convertermos uma certa data para o seu equivalente na data Juliana, precisamos obter o número de dias que se passaram desde a data de referência (1º de janeiro de 4713 a.C). Considerando o nosso calendário, percebe-se que diversos fatores devem ser levados em conta nessa conversão, como os anos bissextos e a adição de alguns fatores de ajustes (por exemplo, o ano zero também conta como um ano, dentre outros diversos fatores a serem considerados). Por isso, você pode usar a função implementada a seguir para realizar a conversão e, caso fique interessado pelo assunto, pode ler mais sobre a conversão da data nas referências.

double dataJuliana(int ano, int mes, int dia){
  // Função para calcular a data juliana com base na data fornecida
  // As variáveis a seguir são usadas nas diferentes etapas da conversão, 
  // para a realização de ajustes 

  int ajuste_mes,ajuste_ano;
  double dias_ano, dias_meses, ajuste_bissexto;
  double data_juliana;
  
  // Ajuste do ano e mês para o cálculo. 
  // A fórmula (12 - meses)/10 serve para determinar se o mês atual é janeiro ou fevereiro. 
  // Se o mês atual for janeiro ou fevereiro, o valor será 0 ou 1, respectivamente. Esse valor é então convertido em um
  // número inteiro (truncando qualquer parte decimal) e o ano original é subtraído por ele. 
  // Este ajuste garante que, para as datas de janeiro e fevereiro, o cálculo as considere como parte do ano anterior e   
  // não do ano atual.
  ajuste_ano = ano - int((12 - mes)/10);
  ajuste_mes = mes + 9;
  if(ajuste_mes >= 12) {
    ajuste_mes = ajuste_mes - 12;
  }
  
  // conversão dos anos em dias
  // conforme citado no texto, a data Juliana é em relação a ano de 4713 a.C. Após realizados os ajustes
  // no ano no passo anterior, para os anos bissextos, e outras considerações na conversão de calendários, obtemos uma
  // diferença de 542 anos já descontados do valor.
  // 365.25 é o número médio de dias em um ano, também considerando os anos bissextos 
  dias_ano = 365.25 * (ajuste_ano + 4172);
  // converte a variável mm, com o ajuste dos meses, em dias (adicionando 0.5 para representar o meio dia)
  dias_meses = int((30.6001 * ajuste_mes) + 0.5);
  // realiza a conversão do ajuste dos anos para dias, considerando os anos bissextos
  ajuste_bissexto = int((((ajuste_ano/100) + 4) * 0.75) - 38);
  
  // cálculo da data juliana, somando o total de dias e removendo o desvio dos anos bissextos.
  data_juliana = dias_ano + dias_meses + dia + 59;
  data_juliana = data_juliana - ajuste_bissexto; // j é a data juliana às 12h UT (Tempo Universal)

  return data_juliana;
}

Em seguida, para normalizá-lo para uma fração do ciclo lunar, precisamos dividir esse tempo decorrido em dias pelo comprimento de um mês sinódico. Essa divisão resulta em um valor entre 0 e 1, representando o progresso do ciclo lunar atual. Dessa forma, ao multiplicarmos esse valor por 8, obtemos um número no intervalo de 0 a 7, representando as oito fases distintas. Como estamos considerando o dia 1º de janeiro de 1972 como referência para lua cheia, precisamos encontrar a diferença entre a data Juliana atual e a do dia em questão (que, após conversão, resulta em 2244116.75). Podemos implementar isso através da seguinte função:

int faseLunarSinodico(){
  // Função para calcular a fase lunar sinódica (0 a 7)
  DateTime now = RTC.now(); // Obtendo a data atual pelo módulo RTC
  double data_juliana = 0; // Data Juliana
  double dias_lunares = 0; // Dias decorridos desde o início do ciclo lunar atual
  int fase_lunar = 0; 

  // Calcula a Data Juliana com base na data atual
  data_juliana = dataJuliana(now.year(), now.month(), now.day());
  // Ajusta a Data Juliana para iniciar em 1º de janeiro de 1972
  data_juliana = int(data_juliana - 2244116.75);
  // Divide pela duração do ciclo lunar (aproximadamente 29.53 dias)
  data_juliana /= 29.53;

  // Calcula os dias lunares decorridos neste mês
  fase_lunar = data_juliana;
  data_juliana -= fase_lunar; // Deixa a parte fracionária da variável
  dias_lunares = data_juliana * 29.53; 

  // Calcula e retorna a fase lunar (0 a 7)
  fase_lunar = data_juliana * 8 + 0.5;
  fase_lunar = fase_lunar & 7;
  return fase_lunar;
}

Note que obtemos a data atual a partir do módulo RTC, o que necessita da biblioteca RTClib e outras configurações que serão explicadas quando finalizarmos o resto do código. Além disso, na função anterior, perceba que, para garantir que nosso valor de fase esteja dentro do intervalo, aplicamos uma operação AND bit a bit com 7. Essa operação efetivamente redefine quaisquer bits fora do intervalo, garantindo que o valor de fase resultante seja uma representação válida de uma das oito fases lunares, caso o valor final exceda o intervalo.

Dessa forma, com as duas funções, já temos uma forma simples de obter um valor entre 0 e 7 correspondendo a fase lunar a partir de uma data. Vamos então dar uma olhada em outras formas de fazer isso.


Método 2: Algoritmo de Conway

O algoritmo de Conway é um método simples e eficaz para calcular as fases da lua, desenvolvido por John Conway, um matemático britânico. O funcionamento do algoritmo de Conway é baseado em uma série de operações matemáticas simples que podem ser facilmente realizadas manualmente. Ele permite determinar a fase lunar para uma determinada data usando apenas o ano, mês e dia correspondentes.

O algoritmo de Conway é um método matemático e, para calcular a fase lunar, são aplicadas diversas transformações pré-definidas aos números de uma data. Primeiramente, realizamos transformações para o ano, nas quais são consideradas apenas os dois últimos dígitos do ano desejado. Em seguida, calcula-se o resto da divisão desse número por 19. Se o resultado for maior que 9, subtrai-se 19. Então, esse valor é multiplicado por 11 e é calculado o resto da divisão por 30. Depois, subtrai-se 4 para datas no século XX ou 8 para datas no século XXI e calcula-se novamente o resto da divisão por 30. Adiciona-se a isso o número do dia e do mês, ajustando para adicionar 2 caso seja janeiro ou fevereiro. É importante notar que o método só funciona para os dois séculos indicados, então, caso esteja lendo esse artigo em algum outro século, será necessário utilizar algum dos outros método para obter a fase da lua.

A seguir, podemos ver a implementação desse algoritmo em C++:

int diaLunarConway(DateTime data) {
  // Extrai o ano, mês e dia do objeto DateTime fornecido
  int ano = data.year();
  int mes = data.month();
  int dia = data.day();

  // Verifica se o ano está dentro do intervalo aceito
  if (ano < 1900 || ano >= 2100) {
      Serial.println("A data deve ser maior que 1900 e menor que 2100");
      return -1; // Retorna -1 em caso de erro
  }
  
  // Define o valor do século (valores fixos, pois o método só é aplicável aos séculos XX e XXI)
  double seculo = -4.0;
  if (ano > 2000) {
      seculo = -8.3;
  }
  
  // Etapas do método de Conway para obter o valor do ano
  int digitos = ano % 100;
  int valor = digitos % 19;
  if (valor > 9) {
      valor -= 19;
  }
  valor *= 11;
  valor %= 30;
  valor += seculo;
  // Adiciona o mês e o dia ao valor obtido a partir do ano
  valor += mes + dia;
  // Ajusta para adicionar 2 caso o mês seja janeiro ou fevereiro
  if (mes < 3) {
      valor += 2;
  }

  // Calcula o valor final, arredondando para o número inteiro mais próximo
  valor = round(valor) % 30;
  // Garante que o resultado seja positivo, ajustando se necessário
  return (valor < 0) ? valor + 30 : valor;
}

A partir do valor retornado pela algoritmo de Conway, implementado na função anterior, podemos novamente implementar uma correspondência entre o intervalo 0-7 e as fases correspondentes. A conversão é feita utilizando o valor resultando e a seguinte tabela:

  • Lua Nova: 0, 1, 29
  • Crescente: de 2 a 6
  • Quarto crescente: 7 e 8
  • Crescente gibosa: de 9 a 13
  • Cheia: de 14 a 16
  • Minguante gibosa: de 17 a 21
  • Quarto minguante: 22 e 23
  • Minguante: de 24 a 28

Implementando essa conversão através de uma função, obtemos a transformação desejada da seguinte forma:

int faseLunarConway() {
  DateTime now = RTC.now();
  int lunarDay = diaLunarConway(now);
  if (lunarDay <= 28) {
      if (lunarDay >= 24) {
          return 3; // MINGUANTE
      }
      if (lunarDay >= 22) {
          return 2; // QUARTO MINGUANTE
      }
      if (lunarDay >= 17) {
          return 1; // MINGUANTE GIBOSA
      }
      if (lunarDay >= 14) {
          return 0; // LUA CHEIA
      }
      if (lunarDay >= 9) {
          return 7; // CRESCENTE GIBOSA
      }
      if (lunarDay >= 7) {
          return 6; // QUARTO CRESCENTE
      }
      if (lunarDay >= 2) {
          return 5; // CRESCENTE
      }
  }
  return 4; // LUA NOVA
}

Ambos os métodos utilizados até agora envolvem operações matemáticas simples e algoritmos mais práticos, resultando em aproximações da fase lunar e não dependendo de medidas exatas. No entanto, existem formas mais complexas de fazer isso.


Método 3: Taxa de Iluminação

Além do método de Conway, existem outras abordagens para calcular a fase da lua, que são mais facilmente implementadas a partir de bibliotecas. Nesse tutorial, utilizaremos a biblioteca moonPhaser, adaptada para arquiteturas AVR. Essa biblioteca utiliza uma forma mais complexa para obter a fase da lua e a taxa de iluminação.

O método implementado por essa e outras bibliotecas é baseado em cálculos astronômicos precisos. Nesse método, é também inicialmente feita a conversão das datas para a data Juliana, o que é comum em cálculos astronômicos. Em seguida, são determinadas as posições do Sol e da Lua em relação à Terra usando modelos matemáticos complexos que levam em consideração suas respectivas órbitas. Com base nessas posições, o ângulo lunar é calculado, representando a separação angular entre o Sol e a Lua, observada da Terra.

Finalmente, para tornar isso mais acessível, traduzimos o ângulo em uma porcentagem de iluminação da superfície lunar, variando de 0% (lua nova) a 100% (lua cheia). Abordaremos a implementação da biblioteca no decorrer do artigo, mas utilizaremos a seguinte função para obter o valor com a fase lunar correspondente:

int faseLunarAngulo() {
    moonData_t moon; // instância da biblioteca para armazenar os dados da lua
    DateTime now = RTC.now(); 
    moon = moonPhase.getPhase(now.year(), now.month(), now.day(), now.hour()); 
    int moon_angle = moon.angle; // Obtém o ângulo da lua

    // Verifica o ângulo da lua e retorna o número correspondente à fase lunar
    if (moon_angle == 0 || moon_angle == 360) {
        return 4; // Lua Nova
    } else if (moon_angle > 0 && moon_angle < 90) {
        return 5; // Crescente Crescente
    } else if (moon_angle == 90) {
        return 6; // Primeiro Quarto
    } else if (moon_angle > 90 && moon_angle < 180) {
        return 7; // Crescente Crescente
    } else if (moon_angle == 180) {
        return 0; // Lua Cheia
    } else if (moon_angle > 180 && moon_angle < 270) {
        return 1; // Gibosa Decrescente
    } else if (moon_angle == 270) {
        return 2; // Último Quarto
    } else if (moon_angle > 270 && moon_angle < 360) {
        return 3; // Crescente Minguante
    }
}

Além disso, utilizaremos também a seguinte função para retornar a taxa de iluminação e mostrá-la no Display:

int taxaIluminacao() {
  moonData_t moon;            
  DateTime now = RTC.now();
  moon = moonPhase.getPhase(now.year(), now.month(), now.day(), now.hour());
  float percentLit = moon.percentLit * 100;
  return percentLit;
}

Configurando o Display

Agora, vamos definir como cada fase lunar será apresentada no display e as outras informações utilizadas pelo relógio. Para exibir as diferentes fases da lua no display de LEDs, precisaremos configurar os bitmap correspondentes a cada fase. Isso envolve mapear os LEDs de acordo com o desenho da lua em cada fase, garantindo uma representação visual aproximada de cada uma das 8 fases.

Um bitmap é uma representação gráfica de dados binários, onde cada bit corresponde a um pixel na tela. Por exemplo, consideremos um bitmap para a lua cheia:

static unsigned char lua_cheia_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0xff, 0x3f, 0x00,
0x80, 0xff, 0x7f, 0x00, 0xe0, 0xff, 0xff, 0x01, 0xf0, 0xff, 0xff, 0x03,
0xf0, 0xff, 0xff, 0x03, 0xf8, 0xff, 0xff, 0x07, 0xfc, 0xff, 0xff, 0x0f,
0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f, 0xfe, 0xff, 0xff, 0x1f,
0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f,
0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f,
0xfe, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f,
0xfc, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0x03,
0xf0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x01, 0x80, 0xff, 0x7f, 0x00,
0x00, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 };

Cada valor dentro desses arrays corresponde a um byte, que por sua vez representa uma linha de pixels na tela. Cada byte neste array representa uma linha de pixels da imagem da lua cheia gerada no Display. Ao percorrer os bytes, cada bit dentro de um byte indica se um pixel correspondente na linha está ativado (1) ou desativado (0). O código que processa esses bitmaps usa essas informações para exibir a fase lunar adequada no display LED, acendendo ou apagando os pixels conforme necessário.

No nosso projeto, utilizaremos os LED acesos para gerar a imagem, mas, se você inverter os valor, poderá gerar a imagem negativa e explorar diferentes formas de realizar o projeto, ou adaptar para outros tipos de Display disponíveis na loja. A seguir, temos os bitmaps para cada fase.

static unsigned char lua_cheia_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0xff, 0x3f, 0x00,
0x80, 0xff, 0x7f, 0x00, 0xe0, 0xff, 0xff, 0x01, 0xf0, 0xff, 0xff, 0x03,
0xf0, 0xff, 0xff, 0x03, 0xf8, 0xff, 0xff, 0x07, 0xfc, 0xff, 0xff, 0x0f,
0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f, 0xfe, 0xff, 0xff, 0x1f,
0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f,
0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f,
0xfe, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f,
0xfc, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0x03,
0xf0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x01, 0x80, 0xff, 0x7f, 0x00,
0x00, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 };
 
static unsigned char minguante_gibosa_bits[] = {
0x00, 0xf8, 0x0f, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xff, 0x00,
0xe0, 0xff, 0xff, 0x00, 0xf0, 0xff, 0x7f, 0x00, 0xf8, 0xff, 0x7f, 0x00,
0xf8, 0xff, 0x3f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfe, 0xff, 0x1f, 0x00,
0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xff, 0xff, 0x0f, 0x00,
0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x07, 0x00,
0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x07, 0x00,
0xff, 0xff, 0x07, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x0f, 0x00,
0xfe, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xf8, 0xff, 0x1f, 0x00,
0xf8, 0xff, 0x3f, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0xe0, 0xff, 0xff, 0x00,
0x80, 0xff, 0xff, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0x1f, 0x00 };
 
static unsigned char quarto_minguante_bits[] = {
0x00, 0xf8, 0x0f, 0x00, 0x00, 0xff, 0x07, 0x00, 0x80, 0xff, 0x01, 0x00,
0xe0, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00,
0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00,
0xfe, 0x1f, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
0xff, 0x1f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00,
0xfe, 0x1f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00,
0xf8, 0x7f, 0x00, 0x00, 0xf0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x01, 0x00,
0x80, 0xff, 0x01, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, 0xf8, 0x0f, 0x00 };
 
static unsigned char minguante_bits[] = {
0x00, 0xf8, 0x07, 0x00, 0x00, 0xff, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00,
0xe0, 0x0f, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00,
0xf8, 0x01, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
0x3f, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
0x7e, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00,
0xf8, 0x01, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00,
0x80, 0x1f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00 };
 
static unsigned char lua_nova_bits[] = {
0x00, 0xf8, 0x07, 0x00, 0x00, 0x07, 0x38, 0x00, 0x80, 0x00, 0x40, 0x00,
0x60, 0x00, 0x80, 0x01, 0x10, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x04,
0x08, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x10,
0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x20,
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
0x01, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10,
0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04,
0x08, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x02, 0x60, 0x00, 0x80, 0x01,
0x80, 0x00, 0x40, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0xf8, 0x07, 0x00 };
 
static unsigned char crescente_bits[] = {
0x00, 0xf8, 0x07, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x7e, 0x00,
0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xe0, 0x07,
0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x80, 0x1f,
0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00, 0x3f,
0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f,
0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f,
0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x80, 0x1f,
0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0xe0, 0x07,
0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xfc, 0x01,
0x00, 0x00, 0x7e, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0xf8, 0x07, 0x00 };
 
static unsigned char quarto_crescente_bits[] = {
0x00, 0xfc, 0x07, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xe0, 0x7f, 0x00,
0x00, 0xe0, 0xff, 0x01, 0x00, 0xc0, 0xff, 0x03, 0x00, 0x80, 0xff, 0x07,
0x00, 0x00, 0xff, 0x07, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x1f,
0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0xfc, 0x3f,
0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f,
0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f,
0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfe, 0x1f,
0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x07,
0x00, 0x80, 0xff, 0x07, 0x00, 0xc0, 0xff, 0x03, 0x00, 0xe0, 0xff, 0x01,
0x00, 0xe0, 0x7f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x07, 0x00 };
 
static unsigned char crescente_gibosa_bits[] = {
0x00, 0xfc, 0x07, 0x00, 0x00, 0xff, 0x3f, 0x00, 0xc0, 0xff, 0x7f, 0x00,
0xc0, 0xff, 0xff, 0x01, 0x80, 0xff, 0xff, 0x03, 0x80, 0xff, 0xff, 0x07,
0x00, 0xff, 0xff, 0x07, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x1f,
0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfc, 0xff, 0x3f,
0x00, 0xfc, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f,
0x00, 0xf8, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f,
0x00, 0xf8, 0xff, 0x3f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfc, 0xff, 0x1f,
0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x07,
0x00, 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x03, 0xc0, 0xff, 0xff, 0x01,
0xc0, 0xff, 0x7f, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xfe, 0x07, 0x00 };
//

Código Final

Antes de juntarmos todas as informações, precisamos definir uma função responsável por chamar os diferentes métodos de cálculo da fase lunar descritos e gerar o desenho correspondente a partir dos bitmaps anteriormente definidos, como a seguir:

void desenharLua(void){
  // Define a fonte para o texto
  u8g.setFont(u8g_font_profont15);
  u8g.setFont(u8g_font_5x7);

  // Desenha o título "Relógio Lunar" e uma linha separadora
  u8g.drawStr(15,10, "Relógio Lunar");
  u8g.drawLine(0,13,128,13);

  // Calcula a taxa de iluminação lunar e a exibe
  float percentLit = taxaIluminacao();
  char buffer[10];
  dtostrf(percentLit, 3, 0, buffer);
  strcat(buffer, "%");
  u8g.drawStr(100, 30, buffer);

  // Determina a fase lunar atual e a desenha junto com o nome correspondente
  // int mp = faseLunarConway();
  // int mp = faseLunarSinodico();
  int mp = faseLunarAngulo();
  switch (mp){
    case 0:
      u8g.drawStr(15,61, "Lua Cheia");
      u8g.drawXBM(45,18,30,30,lua_cheia_bits);
      break;
    case 1:
      u8g.drawStr(15,61, "Minguante Gibosa");
      u8g.drawXBM(45,18,30,30,minguante_gibosa_bits);
      break;
    case 2:
      u8g.drawStr(15,61, "Quarto Minguante");
      u8g.drawXBM(45,18,30,30,quarto_minguante_bits);
      break;
    case 3:
      u8g.drawStr(15,61, "Lua Minguante");
      u8g.drawXBM(45,18,30,30,minguante_bits);
      break;
    case 4:
      u8g.drawStr(15,61, "Lua Nova");
      u8g.drawXBM(45,18,30,30,lua_nova_bits);
      break;
    case 5:
      u8g.drawStr(15,61, "Lua Crescente");
      u8g.drawXBM(45,18,30,30,crescente_bits);
      break;
    case 6:
      u8g.drawStr(15,61, "Quarto Crescente");
      u8g.drawXBM(45,18,30,30,quarto_crescente_bits);
      break;
    case 7:
      u8g.drawStr(15,61, "Crescente Gibosa");
      u8g.drawXBM(45,18,30,30,crescente_gibosa_bits);
      break;
  }
}

Agora, precisamos instalar e importar as bibliotecas utilizadas até aqui. Na Arduino IDE, você pode instalar as bibliotecas da seguinte forma:

Então, busque pelas seguintes bibliotecas e realize a instalação:

Para as bibliotecas Wire e moonPhaser, o método mais rápido é instalar apenas os arquivos utilizados. Primeiramente, dentro da pasta de bibliotecas do Arduino (libraries), crie uma pasta chamada MoonPhaser. O caminho das pastas é o mesmo da imagem a seguir:

Então, dentro da pasta, você deve instalar os arquivos moonPhaser.h e moonPhaser.cpp a partir do repositório no GitHub. Para isso, é só acessar o link de cada arquivo e fazer o download como indicado a seguir:

Repita o processo para ambos os arquivos. Agora, faremos o mesmo para a biblioteca Wire.h. Acesse o link e realize o download dentro de uma pasta nomeada Wire:

Além disso, você também pode utilizar o mesmo código do projeto no Wokwi, que contém os arquivos indicados para cada biblioteca. Finalmente, podemos incluir as bibliotecas e definir as instâncias utilizadas:

#include "U8glib.h" 
// Biblioteca para controlar o display OLED
#include <Wire.h> 
// Biblioteca para comunicação I2C
#include "RTClib.h" 
// Biblioteca para interagir com módulos de relógio em tempo real (RTC)

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); // cria objeto u8g para o Display
RTC_DS1307 RTC; // iniciando módulo RTC

Com isso, você poderá interagir com o RTC através do módulo que nomeamos RTC, da forma como implementamos nas funções de obtenção de fase lunar. Então, após feita a inicialização do módulo através da função begin(), podemos utilizar a função now() para obtermos a data atual, retornada na forma de uma estrutura DateTime:

RTC_DS1307 RTC;
RTC.begin();
DateTime agora = RTC.now();

A inicialização do módulo RTC pode ser feita apenas uma vez, dentro da função setup(), enquanto a obtenção da data atual foi realizada dentro das funções de obtenção da fase lunar correspondente, abordadas anteriormente. Em relação à implementação da biblioteca moonPhaser, conforme já utilizamos até agora, você pode utilizar a função getPhase() para obter informações como a fase lunar e o ângulo da lua (assumindo que já tenha recebido os dados da data atual conforme o código anterior):

#include <moonPhaser.h>

ano = agora.year();
mes = agora.month();
dia = agora.day();
hora = agora.hour();
moonPhaser moonPhase; // iniciar uma instância da biblioteca

moonData_t moon; 
// definir uma variável para guardar as informações retornadas, conforme o tipo de dado utilizado e definido pela biblioteca

moon = moonPhase.getPhase(ano, mes, dia, hora);

Dessa forma, o objeto moon pode ser acessado, retornando as informações a partir da data inserida:

angulo = moon.angle; 
iluminacao = moon.percentLit; // valor entre 0 e 1 com a taxa de iluminação da lua

Finalmente, temos o código completo, no qual você pode escolher utilizar um dos três métodos comentados (acessados na função desenharLua):

#include "U8glib.h"   
// Biblioteca para controlar o display OLED
#include <Wire.h>     
// Biblioteca para comunicação I2C
#include "RTClib.h"   
// Biblioteca para interagir com módulos de relógio em tempo real (RTC)
#include "moonPhaser.h"
// Biblioteca para o Método 3
moonPhaser moonPhase; // iniciar instância para a lua

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); // cria objeto u8g para o Display
RTC_DS1307 RTC; // iniciando módulo RTC
 
static unsigned char lua_cheia_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0xff, 0x3f, 0x00,
0x80, 0xff, 0x7f, 0x00, 0xe0, 0xff, 0xff, 0x01, 0xf0, 0xff, 0xff, 0x03,
0xf0, 0xff, 0xff, 0x03, 0xf8, 0xff, 0xff, 0x07, 0xfc, 0xff, 0xff, 0x0f,
0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f, 0xfe, 0xff, 0xff, 0x1f,
0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f,
0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f,
0xfe, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f,
0xfc, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0x03,
0xf0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x01, 0x80, 0xff, 0x7f, 0x00,
0x00, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 };
 
static unsigned char minguante_gibosa_bits[] = {
0x00, 0xf8, 0x0f, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xff, 0x00,
0xe0, 0xff, 0xff, 0x00, 0xf0, 0xff, 0x7f, 0x00, 0xf8, 0xff, 0x7f, 0x00,
0xf8, 0xff, 0x3f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfe, 0xff, 0x1f, 0x00,
0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xff, 0xff, 0x0f, 0x00,
0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x07, 0x00,
0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x07, 0x00,
0xff, 0xff, 0x07, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x0f, 0x00,
0xfe, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xf8, 0xff, 0x1f, 0x00,
0xf8, 0xff, 0x3f, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0xe0, 0xff, 0xff, 0x00,
0x80, 0xff, 0xff, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xf8, 0x1f, 0x00 };
 
static unsigned char quarto_minguante_bits[] = {
0x00, 0xf8, 0x0f, 0x00, 0x00, 0xff, 0x07, 0x00, 0x80, 0xff, 0x01, 0x00,
0xe0, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00,
0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00,
0xfe, 0x1f, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
0xff, 0x1f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00,
0xfe, 0x1f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00,
0xf8, 0x7f, 0x00, 0x00, 0xf0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x01, 0x00,
0x80, 0xff, 0x01, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, 0xf8, 0x0f, 0x00 };
 
static unsigned char minguante_bits[] = {
0x00, 0xf8, 0x07, 0x00, 0x00, 0xff, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00,
0xe0, 0x0f, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00,
0xf8, 0x01, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
0x3f, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
0x7e, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00,
0xf8, 0x01, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00,
0x80, 0x1f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00 };
 
static unsigned char lua_nova_bits[] = {
0x00, 0xf8, 0x07, 0x00, 0x00, 0x07, 0x38, 0x00, 0x80, 0x00, 0x40, 0x00,
0x60, 0x00, 0x80, 0x01, 0x10, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x04,
0x08, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x10,
0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x20,
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
0x01, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x10,
0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04,
0x08, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x02, 0x60, 0x00, 0x80, 0x01,
0x80, 0x00, 0x40, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0xf8, 0x07, 0x00 };
 
static unsigned char crescente_bits[] = {
0x00, 0xf8, 0x07, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x7e, 0x00,
0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xe0, 0x07,
0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x80, 0x1f,
0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00, 0x3f,
0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f,
0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f,
0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x80, 0x1f,
0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0xe0, 0x07,
0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xfc, 0x01,
0x00, 0x00, 0x7e, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0xf8, 0x07, 0x00 };
 
static unsigned char quarto_crescente_bits[] = {
0x00, 0xfc, 0x07, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xe0, 0x7f, 0x00,
0x00, 0xe0, 0xff, 0x01, 0x00, 0xc0, 0xff, 0x03, 0x00, 0x80, 0xff, 0x07,
0x00, 0x00, 0xff, 0x07, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x1f,
0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0xfc, 0x3f,
0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f,
0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f,
0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfe, 0x1f,
0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x07,
0x00, 0x80, 0xff, 0x07, 0x00, 0xc0, 0xff, 0x03, 0x00, 0xe0, 0xff, 0x01,
0x00, 0xe0, 0x7f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x07, 0x00 };
 
static unsigned char crescente_gibosa_bits[] = {
0x00, 0xfc, 0x07, 0x00, 0x00, 0xff, 0x3f, 0x00, 0xc0, 0xff, 0x7f, 0x00,
0xc0, 0xff, 0xff, 0x01, 0x80, 0xff, 0xff, 0x03, 0x80, 0xff, 0xff, 0x07,
0x00, 0xff, 0xff, 0x07, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x1f,
0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfc, 0xff, 0x3f,
0x00, 0xfc, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f,
0x00, 0xf8, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f,
0x00, 0xf8, 0xff, 0x3f, 0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfc, 0xff, 0x1f,
0x00, 0xfc, 0xff, 0x1f, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0xfe, 0xff, 0x07,
0x00, 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x03, 0xc0, 0xff, 0xff, 0x01,
0xc0, 0xff, 0x7f, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xfe, 0x07, 0x00 };
 
void setup() {
  // Inicia a comunicação serial com uma taxa de transmissão de 9600 bits por segundo
  Serial.begin(9600);
  // Inicia o módulo RTC
  RTC.begin();
  // Verifica se o RTC está funcionando corretamente
  if (!RTC.isrunning()) {
    Serial.println("RTC não está ligado.");
  }
  // Inicia a comunicação com dispositivos externos usando o protocolo I2C
  Wire.begin();
}

void loop() {
  u8g.firstPage(); // Inicia a primeira página do Display
  // Loop para desenhar a fase lunar
  do {
    desenharLua(); // Chama a função para desenhar a fase lunar
  } while (u8g.nextPage()); // Vai para a próxima página do Display
  // Reconstrói a o desenho após um pequeno intervalo
  delay(50);
}

void desenharLua(void){
  // Define a fonte para o texto
  u8g.setFont(u8g_font_profont15);
  u8g.setFont(u8g_font_5x7);

  // Desenha o título "Relógio Lunar" e uma linha separadora
  u8g.drawStr(15,10, "Relógio Lunar");
  u8g.drawLine(0,13,128,13);

  // Calcula a taxa de iluminação lunar e a exibe
  float percentLit = taxaIluminacao();
  char buffer[10];
  dtostrf(percentLit, 3, 0, buffer);
  strcat(buffer, "%");
  u8g.drawStr(100, 30, buffer);

  // Determina a fase lunar atual e a desenha junto com o nome correspondente
  int mp = faseLunarConway();
  // int mp = faseLunarSinodico();
  // int mp = faseLunarAngulo();
  // int mp = fase;
  switch (mp){
    case 0:
      u8g.drawStr(15,61, "Lua Cheia");
      u8g.drawXBM(45,18,30,30,lua_cheia_bits);
      break;
    case 1:
      u8g.drawStr(15,61, "Minguante Gibosa");
      u8g.drawXBM(45,18,30,30,minguante_gibosa_bits);
      break;
    case 2:
      u8g.drawStr(15,61, "Quarto Minguante");
      u8g.drawXBM(45,18,30,30,quarto_minguante_bits);
      break;
    case 3:
      u8g.drawStr(15,61, "Lua Minguante");
      u8g.drawXBM(45,18,30,30,minguante_bits);
      break;
    case 4:
      u8g.drawStr(15,61, "Lua Nova");
      u8g.drawXBM(45,18,30,30,lua_nova_bits);
      break;
    case 5:
      u8g.drawStr(15,61, "Lua Crescente");
      u8g.drawXBM(45,18,30,30,crescente_bits);
      break;
    case 6:
      u8g.drawStr(15,61, "Quarto Crescente");
      u8g.drawXBM(45,18,30,30,quarto_crescente_bits);
      break;
    case 7:
      u8g.drawStr(15,61, "Crescente Gibosa");
      u8g.drawXBM(45,18,30,30,crescente_gibosa_bits);
      break;
  }
}

int taxaIluminacao() {
  moonData_t moon;            
  DateTime now = RTC.now();
  moon = moonPhase.getPhase(now.year(), now.month(), now.day(), now.hour());
  float percentLit = moon.percentLit * 100;
  return percentLit;
}

int faseLunarAngulo() {
    moonData_t moon; // instância da biblioteca para armazenar os dados da lua
    DateTime now = RTC.now(); 
    moon = moonPhase.getPhase(now.year(), now.month(), now.day(), now.hour()); 
    int moon_angle = moon.angle; // Obtém o ângulo da lua

    // Verifica o ângulo da lua e retorna o número correspondente à fase lunar
    if (moon_angle == 0 || moon_angle == 360) {
        return 4; // Lua Nova
    } else if (moon_angle > 0 && moon_angle < 90) {
        return 5; // Crescente Crescente
    } else if (moon_angle == 90) {
        return 6; // Primeiro Quarto
    } else if (moon_angle > 90 && moon_angle < 180) {
        return 7; // Crescente Crescente
    } else if (moon_angle == 180) {
        return 0; // Lua Cheia
    } else if (moon_angle > 180 && moon_angle < 270) {
        return 1; // Gibbosa Decrescente
    } else if (moon_angle == 270) {
        return 2; // Último Quarto
    } else if (moon_angle > 270 && moon_angle < 360) {
        return 3; // Crescente Minguante
    }
}
 
int faseLunarSinodico(){
  // Função para calcular a fase lunar sinódica (0 a 7)
  DateTime now = RTC.now(); // Obtendo a data atual pelo módulo RTC
  double data_juliana = 0; // Data Juliana
  double dias_lunares = 0; // Dias decorridos desde o início do ciclo lunar atual
  int fase_lunar = 0; 

  // Calcula a Data Juliana com base na data atual
  data_juliana = dataJuliana(now.year(), now.month(), now.day());
  // Ajusta a Data Juliana para iniciar em 1º de janeiro de 1972
  data_juliana = int(data_juliana - 2244116.75);
  // Divide pela duração do ciclo lunar (aproximadamente 29.53 dias)
  data_juliana /= 29.53;

  // Calcula os dias lunares decorridos neste mês
  fase_lunar = data_juliana;
  data_juliana -= fase_lunar; // Deixa a parte fracionária da variável
  dias_lunares = data_juliana * 29.53; 

  // Calcula e retorna a fase lunar (0 a 7)
  fase_lunar = data_juliana * 8 + 0.5;
  fase_lunar = fase_lunar & 7;
  return fase_lunar;
}

double dataJuliana(int ano, int mes, int dia){
  // Função para calcular a data juliana com base na data fornecida
  // As variáveis a seguir são usadas nas diferentes etapas da conversão, 
  // para a realização de ajustes 

  int ajuste_mes,ajuste_ano;
  double dias_ano, dias_meses, ajuste_bissexto;
  double data_juliana;
  
  // Ajuste do ano e mês para o cálculo
  ajuste_ano = ano - int((12 - mes)/10);
  ajuste_mes = mes + 9;
  if(ajuste_mes >= 12) {
    ajuste_mes = ajuste_mes - 12;
  }
  
  // conversão dos anos em dias
  dias_ano = 365.25 * (ajuste_ano + 4172);
  // converte a variável mm, com o ajuste dos meses, em dias (adicionando 0.5 para representar o meio dia)
  dias_meses = int((30.6001 * ajuste_mes) + 0.5);
  // realiza a conversão do ajuste dos anos para dias, considerando os anos bissextos
  ajuste_bissexto = int((((ajuste_ano/100) + 4) * 0.75) - 38);
  
  // cálculo da data juliana, somando o total de dias e removendo o desvio dos anos bissextos.
  data_juliana = dias_ano + dias_meses + dia + 59;
  data_juliana = data_juliana - ajuste_bissexto; // j é a data juliana às 12h UT (Tempo Universal)

  return data_juliana;
}

int diaLunarConway(DateTime data) {
  // Extrai o ano, mês e dia do objeto DateTime fornecido
  int ano = data.year();
  int mes = data.month();
  int dia = data.day();

  // Verifica se o ano está dentro do intervalo aceito
  if (ano < 1900 || ano >= 2100) {
      Serial.println("A data deve ser maior que 1900 e menor que 2100");
      return -1; // Retorna -1 em caso de erro
  }
  
  // Define o valor do século (valores fixos, pois o método só é aplicável aos séculos XX e XXI)
  double seculo = -4.0;
  if (ano > 2000) {
      seculo = -8.3;
  }
  
  // Etapas do método de Conway para obter o valor do ano
  int digitos = ano % 100;
  int valor = digitos % 19;
  if (valor > 9) {
      valor -= 19;
  }
  valor *= 11;
  valor %= 30;
  valor += seculo;
  // Adiciona o mês e o dia ao valor obtido a partir do ano
  valor += mes + dia;
  // Ajusta para adicionar 2 caso o mês seja janeiro ou fevereiro
  if (mes < 3) {
      valor += 2;
  }

  // Calcula o valor final, arredondando para o número inteiro mais próximo
  valor = round(valor) % 30;
  // Garante que o resultado seja positivo, ajustando se necessário
  return (valor < 0) ? valor + 30 : valor;
}

int faseLunarConway() {
  DateTime now = RTC.now();
  int lunarDay = diaLunarConway(now);
  if (lunarDay <= 28) {
      if (lunarDay >= 24) {
          return 3; // MINGUANTE
      }
      if (lunarDay >= 22) {
          return 2; // QUARTO MINGUANTE
      }
      if (lunarDay >= 17) {
          return 1; // MINGUANTE GIBOSA
      }
      if (lunarDay >= 14) {
          return 0; // LUA CHEIA
      }
      if (lunarDay >= 9) {
          return 7; // CRESCENTE GIBOSA
      }
      if (lunarDay >= 7) {
          return 6; // QUARTO CRESCENTE
      }
      if (lunarDay >= 2) {
          return 5; // CRESCENTE
      }
  }
  return 4; // LUA NOVA
}

 


Resultados

Agora, temos uma forma que visualizar a fase lunar atual e a taxa de iluminação. No vídeo a seguir, é possível visualizar um loop por todos os bitmaps definidos para as 8 fases:

Além disso, você também pode visualizar e interagir com o projeto no Wokwi. Para iterar por todas as fases como no vídeo, você pode usar a seguinte modificação da função loop():

static int fase = 0;
void loop() { 
  u8g.firstPage(); 
  do {
    desenharLua(fase); 
  } while (u8g.nextPage()); 
  // 
  fase++;
  delay(100);
  
  if (fase == 8) { // Se atingir 8 desenhos
    fase = 0; // Reinicia o número de desenhos para 0
  }
}

No entanto, note que a função desenharLua passa a receber o número da fase. Para isso, essa função deve ser modificada da seguinte forma:

void desenharLua(int fase){
  // Define a fonte para o texto
  u8g.setFont(u8g_font_profont15);
  u8g.setFont(u8g_font_5x7);

  // Desenha o título "Relógio Lunar" e uma linha separadora
  u8g.drawStr(15,10, "Relógio Lunar");
  u8g.drawLine(0,13,128,13);

  // Calcula a taxa de iluminação lunar e a exibe
  float percentLit = taxaIluminacao();
  char buffer[10];
  dtostrf(percentLit, 3, 0, buffer);
  strcat(buffer, "%");
  u8g.drawStr(100, 30, buffer);

  // Determina a fase lunar atual e a desenha junto com o nome correspondente
  // int mp = faseLunarConway();
  // int mp = faseLunarSinodico();
  // int mp = faseLunarAngulo();
  int mp = fase;
  switch (mp){
    case 0:
      u8g.drawStr(15,61, "Lua Cheia");
      u8g.drawXBM(45,18,30,30,lua_cheia_bits);
      break;
    case 1:
      u8g.drawStr(15,61, "Minguante Gibosa");
      u8g.drawXBM(45,18,30,30,minguante_gibosa_bits);
      break;
    case 2:
      u8g.drawStr(15,61, "Quarto Minguante");
      u8g.drawXBM(45,18,30,30,quarto_minguante_bits);
      break;
    case 3:
      u8g.drawStr(15,61, "Lua Minguante");
      u8g.drawXBM(45,18,30,30,minguante_bits);
      break;
    case 4:
      u8g.drawStr(15,61, "Lua Nova");
      u8g.drawXBM(45,18,30,30,lua_nova_bits);
      break;
    case 5:
      u8g.drawStr(15,61, "Lua Crescente");
      u8g.drawXBM(45,18,30,30,crescente_bits);
      break;
    case 6:
      u8g.drawStr(15,61, "Quarto Crescente");
      u8g.drawXBM(45,18,30,30,quarto_crescente_bits);
      break;
    case 7:
      u8g.drawStr(15,61, "Crescente Gibosa");
      u8g.drawXBM(45,18,30,30,crescente_gibosa_bits);
      break;
  }
}

 


Conclusão

Neste tutorial, aprendemos como criar um Relógio de Fase Lunar com o Arduino Uno e exploramos os métodos para calcular a fase da lua a partir da data, incluindo o algoritmo de Conway e outras opções alternativas. Além disso, discutimos como configurar o display OLED e outras curiosidades sobre o ciclo lunar.

Espero que este projeto tenha sido informativo e inspirador para suas próprias explorações na área da robótica e da astronomia. Caso tenha alguma dúvida ou sugestão, deixe um comentário abaixo! E, caso execute o projeto, não deixe de nos mostrar e marcar no Instagram @eletrogate.


Sobre o Autor


Barbara Leidens

Graduanda em Estatística pela UFSM, analista de dados freelancer e mentora do grupo de robótica ASTERIA.


Eletrogate

11 de abril de 2024

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!