//projeto de uma estação meterológica com NTP
// Engenheiro José Cesar Ribeiro 
//======================================================
// Bibliotecas utilizadas
//======================================================
#include <WiFi.h>              // Biblioteca para conexão WiFi do ESP32
#include <DHT.h>               // Biblioteca do sensor DHT11/DHT22
#include "time.h"              // Funções de data e hora via servidor NTP
#include <MD_Parola.h>         // Biblioteca para efeitos de texto no MAX7219
#include <MD_MAX72xx.h>        // Biblioteca de controle do MAX7219
#include <SPI.h>               // Comunicação SPI utilizada pelo MAX7219
#include <HardwareSerial.h>    // Comunicação Serial do ESP32

//======================================================
// Configuração do display de matriz de LEDs
//======================================================

#define MAX_DEVICES  4         // Número de módulos MAX7219 em cascata
#define DATA_PIN     23        // Pino DIN
#define CLK_PIN      18        // Pino CLK
#define CS_PIN        5        // Pino CS (LOAD)

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
// Tipo do módulo utilizado.
// Caso o texto apareça invertido pode ser necessário alterar
// para PAROLA_HW ou outro tipo.

//======================================================
// Configuração do sensor DHT11
//======================================================

#define DHTPIN 15              // Pino conectado ao DHT11
#define DHTTYPE DHT11          // Tipo do sensor

//======================================================
// Configuração da rede WiFi
//======================================================

const char *ssid = "            ";     // Coloque o nome da rede WiFi
const char *password = "       ";      // Coloque a senha da rede

//======================================================
// Configuração do servidor NTP
//======================================================

const char* ntpServer = "pool.ntp.org"; // Servidor de horário

const long gmtOffset = -10800;
// Fuso horário em segundos
// -10800 = UTC-3 (Brasil)

const int dstOffset = 0;
// Horário de verão
// Como atualmente não existe mais no Brasil,
// permanece igual a zero.

//======================================================
// Criação dos objetos das bibliotecas
//======================================================

MD_Parola myDisplay =
MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

DHT dht(DHTPIN, DHTTYPE);

//======================================================
// Variáveis globais
//======================================================

char msg[60];
// Buffer onde será montada a mensagem que será exibida.

uint8_t etapa = 0;
// Controla qual informação será mostrada:
//
// 0 = Hora
// 1 = Data
// 2 = Temperatura
// 3 = Umidade

uint16_t pausa = 2000;
// Tempo de pausa entre animações (ms)

textEffect_t efeitoAtual = PA_SCROLL_LEFT;
// Efeito inicial da animação

//======================================================
// Setup
//======================================================

void setup()
{
    // Inicializa a Serial
    Serial.begin(115200);

    // Inicializa o display
    myDisplay.begin();

    // Ajusta o brilho (0 até 15)
    myDisplay.setIntensity(6);

    // Limpa o display
    myDisplay.displayClear();

    // Inicializa o sensor DHT
    dht.begin();

    //==========================================
    // Conecta ao WiFi
    //==========================================

    WiFi.begin(ssid, password);

    // Aguarda até conectar
    while (WiFi.status() != WL_CONNECTED)
        delay(500);

    //==========================================
    // Configura o relógio via NTP
    //==========================================

    configTime(gmtOffset,
               dstOffset,
               ntpServer);

    struct tm t;

    // Aguarda receber a primeira hora válida
    while (!getLocalTime(&t))
        delay(500);
}

//======================================================
// Configura uma nova animação
//======================================================

void efeito()
{
    myDisplay.displayText(
        msg,             // Texto
        PA_CENTER,       // Alinhamento
        50,              // Velocidade
        pausa,           // Tempo parado
        efeitoAtual,     // Efeito de entrada
        efeitoAtual      // Efeito de saída
    );
}

//======================================================
// Loop principal
//======================================================

void loop()
{
    // displayAnimate() deve ser chamado continuamente.
    // Ele retorna TRUE somente quando a animação termina.
    if (myDisplay.displayAnimate())
    {
        //------------------------------------------------
        // Lê data e hora
        //------------------------------------------------

        struct tm timeinfo;

        if (!getLocalTime(&timeinfo))
        {
            // Caso ocorra erro na sincronização NTP
            myDisplay.displayText(
                "Erro NTP",
                PA_CENTER,
                50,
                1000,
                PA_SCROLL_LEFT,
                PA_SCROLL_LEFT);

            myDisplay.displayReset();

            return;
        }

        //------------------------------------------------
        // Lê temperatura e umidade
        //------------------------------------------------

        float temp = dht.readTemperature();

        float hum = dht.readHumidity();

        //------------------------------------------------
        // Seleciona qual informação será mostrada
        //------------------------------------------------

        switch (etapa)
        {

            //------------------------------------------------
            // ETAPA 0
            // Mostra Hora
            //------------------------------------------------

            case 0:

                sprintf(msg,
                        "%02d:%02d",
                        timeinfo.tm_hour,
                        timeinfo.tm_min);

                efeitoAtual = PA_SCROLL_DOWN;

                pausa = 2000;

                break;

            //------------------------------------------------
            // ETAPA 1
            // Mostra Data
            //------------------------------------------------

            case 1:

                sprintf(msg,
                        "%02d/%02d/%04d",
                        timeinfo.tm_mday,
                        timeinfo.tm_mon + 1,
                        timeinfo.tm_year + 1900);

                efeitoAtual = PA_SCROLL_LEFT;

                pausa = 0;

                break;

            //------------------------------------------------
            // ETAPA 2
            // Mostra Temperatura
            //------------------------------------------------

            case 2:

                sprintf(msg,
                        "T:%.1fC",
                        temp);

                efeitoAtual = PA_FADE;

                pausa = 1000;

                break;

            //------------------------------------------------
            // ETAPA 3
            // Mostra Umidade
            //------------------------------------------------

            case 3:

                sprintf(msg,
                        "U:%.0f%%",
                        hum);

                efeitoAtual = PA_SCROLL_UP;

                pausa = 1000;

                break;
        }

        //------------------------------------------------
        // Passa para a próxima etapa
        //------------------------------------------------

        etapa = (etapa + 1) % 4;

        /*
        Funcionamento:

        etapa = 0 → Hora

        etapa = 1 → Data

        etapa = 2 → Temperatura

        etapa = 3 → Umidade

        Depois volta para 0.

        O operador % (módulo) faz esse retorno automático.
        */

        //------------------------------------------------
        // Inicia a nova animação
        //------------------------------------------------

        efeito();
    }
}