// Better Comments config file: https://gist.github.com/9d42c2d388f8fac8ea331639582147b4.git
#include <Arduino.h> // if using PlatformIO
#include <WiFi.h>
#include <time.h> // linha não é necessária, mas é boa prática
#include <FirebaseESP32.h> //?1
#include "addons/TokenHelper.h"
#include "addons/RTDBHelper.h"
#include <Adafruit_NeoPixel.h>
#include <Preferences.h>
//' variáveis para tempo //
struct tm timeinfo;
const int gmtOffset_sec = -10800, // (ex.: GMT-5 → -18000)
daylightOffset_sec = 0; // mudança no horário de verão
unsigned long long tempo = 0;
//' variáveis Firebase //
FirebaseData dados;
FirebaseConfig config;
FirebaseAuth auth;
const char* url = "https://`path`.firebaseio.com/",
* apiKey = "`key`";
bool enviar = false,
atualizarLeds = true;
//' variáveis dos leds //
const byte n_pixels = 10, pino = 22;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(n_pixels, pino, NEO_GRB + NEO_KHZ800);
//' variáveis de memória //
Preferences preferencias;
//' variáveis metas //
enum id_instancias : byte {estudu = 0, fisico, peggy, musica, cuisine, reflexao, carro, dnt, ong, debug, fisico3x = 255};
// strings para o firebase
const char* nomes_instancias[] = {"Estudu", "Fisico", "Peggy", "Musica", "Cuisine", "Reflexao", "Carro", "Dnt", "ONG"};
const char* nomes_meses[] = {"Janeiro", "Fevereiro", "Marco", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"};
const byte rotina[] = {1, 1, 1, 1, 1, 1, 1, 3, 30}; // quantidades de coisas a serem feitas
byte realizados_diarios[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}, // armazena temporariamente os dados do dia
dia, mes; // armazena dia atual, mês atual
//' componentes //
const byte botoes[] = {13, 27, 26, 25, 33, 32, 4, 21, 18, 19};
//' Protótipos de função //
void Testes(), Inicializar(), AtualizarDados(), AtualizarPreferencias(), ChecagemData(), ChecarBotoes(), LedsInstancias(std::initializer_list<byte> instancias), DebugLed(byte r, byte g, byte b);
void setup()
{
Inicializar();
Testes();
}
void loop()
{
ChecarBotoes();
delay(20);
if (millis() - tempo >= 15000 && enviar)
{
AtualizarPreferencias();
}
}
void DebugLed(byte r, byte g, byte b)
{
pixels.setPixelColor(debug, pixels.Color(r, g, b));
pixels.show();
delay(250);
}
void Inicializar()
{
Serial.begin(9600);
// # inicialização dos leds //
pixels.begin();
DebugLed(255, 255, 255); //. #FFFFFF
DebugLed(0, 0, 0); //. #000000
// # setup botões //
for (byte pino_botao : botoes) pinMode(pino_botao, INPUT_PULLDOWN);
// # conexão wifi //
DebugLed(255, 0, 0); //. #FF0000
WiFi.disconnect();
delay(1000);
Serial.println("START");
WiFi.begin("Wokwi-GUEST", "", 6); //? Wokwi
while ((!(WiFi.status() == WL_CONNECTED)))
{
delay(250);
Serial.print(".");
}
Serial.println("_—‾C͟o͟n͟e͟c͟t͟a͟d͟o‾—_");
DebugLed(0, 0, 0); //. #000000
// # configuração de tempo //
DebugLed(255, 127, 0); //. #FF7F00
configTime(gmtOffset_sec, daylightOffset_sec, "pool.ntp.org", "time.nist.gov");
DebugLed(0, 0, 0); //. #000000
Serial.println("_-Tempo configurado-_");
// # conexão firebase //
DebugLed(255, 255, 0); //. #FFFF00
config.database_url = url;
config.api_key = apiKey;
if (!Firebase.signUp(&config, &auth, "", ""))
{
Serial.println("Erro ao conectar ao Firebase");
Serial.printf("%s\n", config.signer.signupError.message.c_str());
DebugLed(255, 0, 255); //. #FF00FF
while (true) delay(9999);
}
config.token_status_callback = tokenStatusCallback;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
Serial.println("_Conectado ao Firebase_");
DebugLed(0, 0, 0); //. #000000
}
void Testes()
{
// Leds
for (byte i = 0; i <= n_pixels; i++) //? teste da cor vermelha
{
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
pixels.show();
delay(90);
}
for (byte i = 0; i < n_pixels; i++) //* teste da cor verde
{
pixels.setPixelColor(i, pixels.Color(0, 255, 0));
pixels.show();
delay(90);
}
for (byte i = 0; i < n_pixels; i++) //! teste da cor azul
{
pixels.setPixelColor(i, pixels.Color(0, 0, 255));
pixels.show();
delay(90);
}
delay(250);
pixels.clear();
pixels.show();
ChecagemData();
}
void ChecarBotoes()
{
for (byte i = 0; i < /*nº de botões*/ sizeof(botoes)/sizeof(byte); i++)
{
if (digitalRead(botoes[i]) == HIGH)
{
Serial.println("Botão " + String(i) + " pressionado");
enviar = true;
atualizarLeds = true;
tempo = millis();
if (i == debug)
{
Serial.println("Debug");
if (!preferencias.begin("metas", false))
{
Serial.println("Erro ao abrir preferências em ChecarBotoes");
DebugLed(128, 0, 128); //. #800080
return;
}
preferencias.putInt("ultimo_dia", 7);
preferencias.putInt("ultimo_mes", 13);
preferencias.end();
Testes();
// ESP.restart(); //? Wokwi debug
return;
}
realizados_diarios[i]++;
//. debounce delay
while (digitalRead(botoes[i]) == HIGH)
;
delay(25); // debounce delay
}
}
}
void AtualizarPreferencias()
{
if (!preferencias.begin("metas", false))
{
Serial.println("Erro ao abrir preferências em AtualizarPreferencias");
DebugLed(128, 0, 128); //. #800080
return;
}
bool checar = false;
for (byte i = 0; i < sizeof(realizados_diarios)/sizeof(byte); i++)
{
if (realizados_diarios[i] > 0)
{
checar = true;
byte atualizar_diario = realizados_diarios[i] + preferencias.getInt(("d_" + String(nomes_instancias[i])).c_str());
preferencias.putInt(("d_" + String(nomes_instancias[i])).c_str(), atualizar_diario);
realizados_diarios[i] = 0;
}
}
preferencias.end();
if (checar)
{
ChecagemData();
}
}
void LedsInstancias(std::initializer_list<byte> instancias)
{
if (atualizarLeds)
{
if (!preferencias.begin("metas", false))
{
Serial.println("Erro ao abrir preferências em LedsInstancias");
DebugLed(128, 0, 128); //. #800080
return;
}
for (byte instancia : instancias)
{
if (instancia == fisico3x) //*2
{
byte qnt = preferencias.getInt(("d_" + String(nomes_instancias[fisico])).c_str());
byte r = 255 - qnt * 255 / 3,
g = qnt * 255 / 3;
pixels.setPixelColor(fisico, pixels.Color(r, g, 0));
if (g > 255)
{
pixels.setPixelColor(fisico, pixels.Color(0, 0, 255));
}
pixels.show();
continue;
}
byte qnt = preferencias.getInt(("d_" + String(nomes_instancias[instancia])).c_str());
Serial.println("d_" + String(nomes_instancias[instancia]) + " : " + String(qnt));
unsigned int r = 255 - qnt * 255 / rotina[instancia],
g = qnt * 255 / rotina[instancia];
Serial.println("r: " + String(r) + " | g: " + String(g));
pixels.setPixelColor(instancia, pixels.Color(r, g, 0));
if (g > 255)
{
pixels.setPixelColor(instancia, pixels.Color(0, 0, 255));
}
}
preferencias.end();
pixels.show();
}
}
void ChecagemData()
{
if (!getLocalTime(&timeinfo))
{
Serial.println("Erro ao coletar tempo");
DebugLed(255, 127, 127); //. #FF7F7F
return;
}
dia = timeinfo.tm_wday;
mes = timeinfo.tm_mon +1;
if (!preferencias.begin("metas", false))
{
Serial.println("Erro ao abrir preferências em ChecagemData");
DebugLed(128, 0, 128); //. #800080
return;
}
byte ultimo_dia = preferencias.getInt("ultimo_dia"),
ultimo_mes = preferencias.getInt("ultimo_mes");
// # atualizando dados //
if (ultimo_dia != dia)
{
Serial.println("Ultimo_Dia da semana: " + String(ultimo_dia) + " | Dia da semana: " + String(dia));
for (byte i = 0; i < sizeof(realizados_diarios)/sizeof(byte); i++)
{
byte realizados_no_dia = preferencias.getInt(("d_" + String(nomes_instancias[i])).c_str());
if (realizados_no_dia > 0)
{
byte atualizado_mensal = realizados_no_dia + preferencias.getInt(("m_" + String(nomes_instancias[i])).c_str());
preferencias.putInt(("m_" + String(nomes_instancias[i])).c_str(), atualizado_mensal);
// # resetar instância diária //
preferencias.putInt(("d_" + String(nomes_instancias[i])).c_str(), 0);
}
}
// # atualizando dia salvo //
preferencias.putInt("ultimo_dia", dia);
// # enviando dados mensais
if (ultimo_mes != mes)
{
preferencias.end(); // fechar preferências para evitar erros ao abrir em AtualizarDados
Serial.println("Mes mudou");
Serial.println("Ultimo_Mes: " + String(ultimo_mes) + " | Mes: " + String(mes));
AtualizarDados();
}
}
preferencias.end();
// # checando instâncias //
switch (dia)
{
case 0:
Serial.println("Domingo");
for (byte i = musica; i <= ong; i++) LedsInstancias({i}); // Música(4), Cuisine(5), Reflexão(6), Carro(7), Dnt(8), ONG (9)
break;
case 1:
Serial.println("Segunda");
LedsInstancias({fisico, dnt});
break;
case 2:
Serial.println("Terça");
LedsInstancias({fisico, dnt});
break;
case 3:
Serial.println("Quarta");
LedsInstancias({estudu, fisico3x, peggy, dnt});
break;
case 4:
Serial.println("Quinta");
LedsInstancias({estudu, fisico3x, dnt});
break;
case 5:
Serial.println("Sexta");
LedsInstancias({fisico, dnt});
break;
case 6:
Serial.println("Sábado");
LedsInstancias({estudu, fisico3x, reflexao, carro, dnt});
break;
default:
DebugLed(127, 127, 127); //. #7F7F7F
Serial.println("Erro: Dia" + String(dia) + " inválido");
break;
}
atualizarLeds = false; //*1
}
void AtualizarDados()
{
if (Firebase.ready())
{
if (!preferencias.begin("metas", false))
{
Serial.println("Erro ao abrir preferências em AtualizarDados");
DebugLed(128, 0, 128); //. #800080
return;
}
Serial.println("Enviando dados...");
// # enviando dados //
for (byte i = 0; i < sizeof(realizados_diarios)/sizeof(byte); i++)
{
byte valor_mensal = preferencias.getInt(("m_" + String(nomes_instancias[i])).c_str());
if (valor_mensal == 0) continue;
if (!Firebase.setInt(dados, "Dados/" + String(timeinfo.tm_year + 1900) + "/" + String(mes) + "_" + nomes_meses[mes -1] + "/" + nomes_instancias[i], valor_mensal))
{
Serial.println("Erro ao enviar dados de " + String(nomes_instancias[i]));
Serial.println(dados.errorReason());
DebugLed(127, 0, 255); //. #7F00FF
// tentar de novo //?2
i--;
continue;
}
Serial.println("Enviado | " + String(nomes_instancias[i]) + String(" : ") + valor_mensal);
// # resetando valores mensais //
preferencias.putInt(("m_" + String(nomes_instancias[i])).c_str(), 0);
}
// # atualizando mês salvo //
preferencias.putInt("ultimo_mes", mes);
preferencias.end();
enviar = false;
Serial.println("Envio ao Firebase concluido");
DebugLed(0, 255, 0); //. #00FF00
DebugLed(0, 0, 0); //. #000000
return;
}
DebugLed(200, 80, 80); //. #C85050
}
//• V0.1.1.1.1 //
//' adicinado debug ao hardware //
/*
*1: colocado nesta posição para não interromper for loop no domingo
*2: como valores do array de rotina não muda, caso especial do if checa por dias com 3x fisico
?1: pode mudar de microcontrolador
?2: pode ficar preso em loop, tentar colocar máximo de tentativas ou derivado
__ Notas __
! add
! sleep
# verificar lightsleep + modem sleep
*/