#include <EEPROM.h> // Biblioteca para acessar a EEPROM
#include <Wire.h>
#include <Servo.h>
#include "RTClib.h"
#include <LiquidCrystal_I2C.h>
#include <DHT22.h>
// Definir os animais
const char* animais[] = {"Galinha", "Codorna", "Ganso", "Marreco", "Pato", "Cisne"};
int **setPoints; // Ponteiro para os setpoints
Servo servo1;
int posicao = 80;
int pwmluz;
int Hor;
int Min;
int Sec;
int Data;
int falta;
int i;
uint8_t date;
int estado = LOW; // Define a variável para controlar o estado ligado/desligado
int estadoAnterior = HIGH; // Define a variável para armazenar o estado anterior do botão
int bt_start = 0;
const int valorTecla5 = 0; // Valor analógico da tecla 5 pressionada
unsigned long INICIO_CLIQUE = 0; // Armazena o tempo de início do pressionamento do botão
bool CLIQUE_LONGO_DETECTADO = false; // Flag para indicar se um clique longo foi detectado
bool menuSetup = false; // Flag para indicar que usuario entrou no menu de configurações
int valorTeclaH;
int valorTeclaV;
int startOp = 0;
int ovoSel = 0;
float setPointT = 0.0; // Set-point de temperatura em graus Celsius
int setPointU = 0; // Set-point de umidade em %
int diasSetPoint = 0; // Defina o set-point de dias
int currentAnimal = -1; // Índice do animal atual (-1 para tela inicial)
int AnimalOp = 0;
int currentSetPointIndex = 0; // Índice do setpoint atual
bool adjustingSetPoint = false; // Flag para indicar se está ajustando o setpoint
/* define of commands of LCD */
#define LINE_UP 0
#define LINE_DOWN 1
#define LINE_BEGIN 0
#define NUM_KEYS 6
#define LIM_CLIQUE_CURTO 100 // Limiar para clique curto
#define LIM_CLIQUE_LONGO 800 // Limiar para clique longo115200
#define DURACAO_CLIQUE_LONGO 5000 // Duração do pressionamento longo em milissegundos
#define mosfet 6
#define pinTecladoH A2 // Pino analógico onde o teclado está conectado
#define pinTecladoV A3 // Pino analógico onde o teclado está conectado
#define SEL_PIN 2
#define vento 12
#define DHT22_PIN 10
DHT22 dht22(DHT22_PIN);
#define Kp 1.0 // Ganho proporcional
#define Ki 0.1 // Ganho integral
#define Kd 0.2 // Ganho derivativo
double setPoint = 25.0; // Valor desejado
double atual = 0.0; // Valor atual
double erroAnterior = 0.0; // Erro anterior
double erroIntegral = 0.0; // Erro acumulado
RTC_DS1307 rtc;
String MinString;
String SecString;
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display
// Define o caractere especial para o símbolo de grau (º)
byte grau[8] = {
B00111,
B00101,
B00111,
B00000,
B00000,
B00000,
B00000,
B00000
};
byte down[8] = {
B00100,
B00100,
B00100,
B00100,
B10101,
B01110,
B00100,
};
byte rigth[8] = {
B01000,
B00100,
B00010,
B11111,
B00010,
B00100,
B01000,
};
byte bar[8] = {
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
};
const int numRows = 2;
const int numCols = 16;
/* define pin to read buttons of the shield */
const int Analog_Button_Pin = A0;
/* Key Messages */
char messages[][17] =
{
" ",
" RIGHT Key OK ",
" UP Key OK ",
" DOWN Key OK ",
" LEFT Key OK ",
" SELECT Key OK "
};
/* Maybe this values need some adjusts
int adc_key_values[] =
{
30, // button RIGHT 0.00V
150, // button UP 0.65V
360, // button DOWN 1.50V
535, // button LEFT 2.35V
760, // button SELECT 3.52V
1023 // button NOT_PRESS 5.00V
};
*/
// Values for Simulation
int adc_key_values[] =
{
0, // button RIGHT 0.00V
134, // button UP 0.65V
309, // button DOWN 1.50V
481, // button LEFT 2.35V
722, // button SELECT 3.52V
1023 // button NOT_PRESS 5.00V
};
int get_key( int input ) {
int k;
for ( k=1; k<NUM_KEYS; k++) {
if ( input < adc_key_values[k]) {
return k;
}
}
if ( k>= NUM_KEYS ) {
k = 0;
}
return k;
}
void setup() {
Serial.begin(115200);
rtc.begin();
rtc.adjust(DateTime(2024, 3, 20, 23, 55, 30));
DateTime now = rtc.now();
Hor = rtc.now().hour();
Min = rtc.now().minute();
Sec = rtc.now().second();
Data = rtc.now().day();
Serial.print(Hor);
Serial.print(":");
MinString = (Min < 10) ? "0" + String(Min) : String(Min); // Adiciona zero à esquerda se o minuto for menor que 10
Serial.print(MinString);
Serial.print(":");
SecString = (Sec < 10) ? "0" + String(Sec) : String(Sec); // Adiciona zero à esquerda se o minuto for menor que 10
Serial.println(SecString);
pinMode(mosfet, OUTPUT);
pinMode(vento, OUTPUT);
servo1.attach(5);
lcd.init();
lcd.backlight();
lcd.createChar(0, grau); // Define o caractere especial º no LCD
lcd.createChar(1, down); // Define o caractere especial Seta Down no LCD
lcd.createChar(2, rigth); // Define o caractere especial Seta Rigth no LCD
lcd.createChar(3, bar);
pinMode(pinTecladoH, INPUT);
pinMode(pinTecladoV, INPUT);
pinMode(SEL_PIN, INPUT_PULLUP);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" INICIALIZANDO ");
lcd.setCursor(0, 1);
lcd.print(" AGUARDE ");
// Limpar a EEPROM na primeira inicialização
if (EEPROM.read(0) != 'C' || EEPROM.read(1) != 'L') {
clearEEPROM();
EEPROM.write(0, 'C');
EEPROM.write(1, 'L');
}
// Alocar memória para os setpoints
setPoints = new int*[sizeof(animais) / sizeof(animais[0])];
for (int i = 0; i < sizeof(animais) / sizeof(animais[0]); i++) {
setPoints[i] = new int[3];
}
lcd.setCursor(0,2);
for (int i = 1; i <= 20; i++) {
//lcd.print(".");
lcd.write((byte)3);
lcd.setCursor(i,2);
delay(100);
}
// Ler os valores dos setpoints da EEPROM
readSetPointsFromEEPROM();
//Posiciona o servo conforme a hora do dia que máquina é ligada
if (Hor >= 6 && Hor < 12) {
posicao = 1;
pos_servo(posicao);
}
if (Hor >= 12 && Hor < 17) {
posicao = 2;
pos_servo(posicao);
}
if ( Hor >= 17) {
posicao = 3;
pos_servo(posicao);
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" INICIALIZACAO ");
lcd.setCursor(0, 1);
lcd.print(" COMPLETA ");
delay(2000);
// Exibir tela inicial
displayWelcomeScreen();
}
void loop() {
DateTime now = rtc.now();
Data = rtc.now().day();
valorTeclaH = analogRead(pinTecladoH);
valorTeclaV = analogRead(pinTecladoV);
bt_start = digitalRead(SEL_PIN); // Lê o estado atual do botão
if (bt_start != estadoAnterior && bt_start == HIGH && ovoSel > 0) { // Verifica se houve uma transição do estado do botão
estado = !estado; // Inverte o estado
if (estado){
setPointU = setPoints[AnimalOp][0];
setPointT = setPoints[AnimalOp][1];
diasSetPoint = setPoints[AnimalOp][2];
imp_Op();
startOp = 1;
Serial.println("Ligou");
} else{
imp_Sel();
startOp = 0;
}
}
estadoAnterior = bt_start; // Atualiza o estado anterior do botão
delay(50);
if (valorTeclaV < 200 and startOp == 0){
ovoSel++;
if(ovoSel == 7){
ovoSel = 1;
}
imp_Sel();
valorTeclaV = analogRead(pinTecladoV);
while (valorTeclaV < 200) {
valorTeclaV = analogRead(pinTecladoV);
delay(50);
}
}
if (valorTeclaV > 600 and startOp == 0){
ovoSel--;
if(ovoSel <= 0){
ovoSel = 6;
}
imp_Sel();
valorTeclaV = analogRead(pinTecladoV);
while (valorTeclaV < 200) {
valorTeclaV = analogRead(pinTecladoV);
delay(50);
}
}
if (valorTeclaH < LIM_CLIQUE_CURTO) { // Verifica se foi um clique curto
INICIO_CLIQUE = millis(); // Registra o tempo de início do pressionamento
delay(50); // Delay para debounce
// Verifica se o botão ainda está pressionado após o delay
if (analogRead(pinTecladoH) < LIM_CLIQUE_CURTO) { // Botão ainda pressionado, aguarda o pressionamento longo
while (analogRead(pinTecladoH) < LIM_CLIQUE_CURTO && millis() - INICIO_CLIQUE < DURACAO_CLIQUE_LONGO) {
} // Espera até que o botão seja solto ou o tempo de pressionamento longo seja atingido
// Verifica se o pressionamento foi longo o suficiente
if (millis() - INICIO_CLIQUE >= DURACAO_CLIQUE_LONGO && !CLIQUE_LONGO_DETECTADO) {
CLIQUE_LONGO_DETECTADO = true; // Pressionamento longo detectado
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" SETUP ");
lcd.setCursor(0, 1);
lcd.print(" SET-POINTs ");
lcd.setCursor(0, 2);
lcd.print(" PRESSIONE ");
lcd.write((byte)1); // Escreve o caractere especial do símbolo de grau
menuSetup = true;
}
}
while (analogRead(pinTecladoH) < LIM_CLIQUE_CURTO) { // Aguarda até que o botão seja solto
}
CLIQUE_LONGO_DETECTADO = false; // Reinicia a flag de clique longo
}
while (menuSetup) {
valorTeclaH = analogRead(pinTecladoH);
valorTeclaV = analogRead(pinTecladoV);
if (currentAnimal == -1) { // Verificar botões e atualizar o menu
if (valorTeclaV == 0) {
currentAnimal = 0;
displayMenu();
delay(200);
}
} else if (!adjustingSetPoint) {
if (valorTeclaH == 1023) {
if (currentAnimal > 0) {
currentAnimal--;
displayMenu();
}
delay(200);
} else if (valorTeclaH == 0) {
if (currentAnimal < sizeof(animais) / sizeof(animais[0]) - 1) {
currentAnimal++;
displayMenu();
} else { // Opção de sair do menu de setpoint
currentAnimal = -1;
menuSetup = false;
readSetPointsFromEEPROM();
displayWelcomeScreen();
}
delay(200);
} else if (valorTeclaV == 1023) {
if (currentSetPointIndex > 0) {
currentSetPointIndex--;
displayMenu();
}
delay(200);
} else if (valorTeclaV == 0) {
if (currentSetPointIndex < 2) {
currentSetPointIndex++;
displayMenu();
}
delay(200);
} else if (digitalRead(SEL_PIN) == LOW) {
adjustingSetPoint = true;
displayMenu();
delay(200);
}
} else {
if (valorTeclaV == 1023) {
setPoints[currentAnimal][currentSetPointIndex]++;
displayMenu();
delay(200);
} else if (valorTeclaV == 0) {
setPoints[currentAnimal][currentSetPointIndex]--;
displayMenu();
delay(200);
} else if (digitalRead(SEL_PIN) == LOW) {
adjustingSetPoint = false;
saveSetPointsToEEPROM(); // Salvar os novos valores na EEPROM
displayMenu();
delay(200);
}
}
}
while (startOp == 1) {
DateTime now = rtc.now();
Hor = rtc.now().hour();
Min = rtc.now().minute();
Sec = rtc.now().second();
Data = rtc.now().day();
DateTime dataLimite = now + TimeSpan(diasSetPoint, 0, 0, 0); // Adiciona o set-point de dias à data atual
TimeSpan diferenca = dataLimite - now; // Calcula quantos dias faltam até a data limite
falta = diferenca.days();
// Se faltar mais de 4 dias para a choca move os motores as 6:00, 12:00, 17:56 e 23:59
if (falta > 4) {
if ( Hor == 6 && Min == 00 && Sec == 01) {
posicao = 1;
pos_servo(posicao);
}
if ( Hor == 12 && Min == 00 && Sec == 01) {
posicao = 2;
pos_servo(posicao);
}
if ( Hor == 17 && Min == 00 && Sec == 01) {
posicao = 3;
pos_servo(posicao);
}
if ( Hor == 23 && Min == 59 && Sec == 59) {
posicao = 4;
pos_servo(posicao);
}
}
// Se faltar 4 dias ou menos para chocar a chocadeira para na posição 80
if (falta <= 4 && ( Hor == 6 && Min == 00 && Sec == 01)) {
posicao = 5;
pos_servo(posicao);
}
uint32_t start = micros();
uint32_t stop = micros();
lcd.setCursor(0, 2); // Imprime as informações de temperatura e umidade no display
lcd.print("RUN U:");
lcd.print(dht22.getHumidity(), 0);
lcd.print("%");
lcd.print(" T:");
lcd.print(dht22.getTemperature(), 0);
lcd.write((byte)0); // Escreve o caractere especial do símbolo de grau
lcd.print("C");
lcd.setCursor(0, 3); // Imprime as informações de hora e tempo que falta para a eclosão
lcd.print(Hor);
lcd.print(":");
MinString = (Min < 10) ? "0" + String(Min) : String(Min); // Adiciona zero à esquerda se o minuto for menor que 10
lcd.print(MinString);
lcd.print(":");
SecString = (Sec < 10) ? "0" + String(Sec) : String(Sec); // Adiciona zero à esquerda se o minuto for menor que 10
lcd.print(SecString);
lcd.print(" D:");
lcd.print(diasSetPoint);
lcd.print("/");
lcd.print(falta);
float temperatura = dht22.getTemperature(); // Lê a temperatura atual do sensor DHT
// Calcula o valor do PWM com base na diferença entre a temperatura atual e o set-point
atual = temperatura; // Leia o valor atual do sensor, como um termômetro ou um sensor de posição
double erro = setPointT - atual; // Calcule o erro
double P = Kp * erro; // Componente Proporcional
erroIntegral += erro; // Componente Integral
double I = Ki * erroIntegral;
double dErro = erro - erroAnterior; // Componente Derivativo
double D = Kd * dErro;
double sinalControle = P + I + D; // Calcule o sinal de controle
int valorPWM = map(sinalControle, 0, 100, 0, 255); // Aplique o sinal de controle ao sistema
analogWrite(mosfet, valorPWM); // Aplica o valor do PWM
erroAnterior = erro; // Atualize o erro anterior para a próxima iteração
float umidade = dht22.getHumidity(); // Lê a umidade atual do sensor DHT
if (umidade > setPointU){
digitalWrite(vento, HIGH);
} else {
digitalWrite(vento, LOW);
}
bt_start = digitalRead(SEL_PIN); // Lê o estado atual do botão
if (bt_start != estadoAnterior && bt_start == HIGH && ovoSel > 0) { // Verifica se houve uma transição do estado do botão
estado = !estado; // Inverte o estado
if (estado){
setPointU = setPoints[AnimalOp][0];
setPointT = setPoints[AnimalOp][1];
diasSetPoint = setPoints[AnimalOp][2];
imp_Op();
startOp = 1;
Serial.println("Ligou");
} else{
imp_Sel();
digitalWrite(mosfet, LOW);
digitalWrite(vento, LOW);
Serial.println("Desligou");
startOp = 0;
}
}
estadoAnterior = bt_start; // Atualiza o estado anterior do botão
delay(50);
}
}
void displayWelcomeScreen() {
lcd.clear();
lcd.noCursor();
lcd.setCursor(0, 0);
lcd.print("CHOCADEIRA AUT. V1.0");
lcd.setCursor(0, 1);
lcd.print(" MENU INICIAL ");
lcd.setCursor(0, 2);
lcd.print(" ESPECIE - TECLA ");
lcd.write((byte)1); // Escreve o caractere especial do símbolo de grau
lcd.setCursor(0, 3);
lcd.print(" SETUP - TECLA ");
lcd.write((byte)2); // Escreve o caractere especial do símbolo de grau
}
void imp_Sel(){
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ESPECIE SELECIONADA");
lcd.setCursor(6, 1);
switch (ovoSel) {
case 1:
AnimalOp = 0;
lcd.print("GALINHA");
break;
case 2:
AnimalOp = 1;
lcd.print("CODORNA");
break;
case 3:
AnimalOp = 2;
lcd.print("GANSO");
break;
case 4:
AnimalOp = 3;
lcd.print("MARRECO");
break;
case 5:
AnimalOp = 4;
lcd.print("PATO");
break;
case 6:
AnimalOp = 5;
lcd.print("CISNE");
break;
default:
break;
}
lcd.setCursor(0, 2);
lcd.print("START - TECLA: OK");
lcd.setCursor(0, 3);
lcd.print("SETUP - TECLA: ");
lcd.write((byte)2); // Escreve o caractere especial do símbolo de grau
}
void imp_Op(){
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("CHOC.OVO DE ");
switch (ovoSel) {
case 1:
lcd.print("GALINHA");
break;
case 2:
lcd.print("CODORNA");
break;
case 3:
lcd.print("GANSO");
break;
case 4:
lcd.print("MARRECO");
break;
case 5:
lcd.print("PATO");
break;
case 6:
lcd.print("CISNE");
break;
default:
break;
}
lcd.setCursor(0, 1);
lcd.print("SET U:");
lcd.print(setPointU);
lcd.print("%");
lcd.print(" T:");
lcd.print(setPointT, 0);
lcd.write((byte)0); // Escreve o caractere especial do símbolo de grau
lcd.print("C");
}
void displayMenu() {
lcd.clear();
if (currentAnimal != -1) {
lcd.setCursor(1, 0);
lcd.print("Animal: ");
lcd.print(animais[currentAnimal]);
lcd.setCursor(1, 1);
lcd.print("SetPoint U: ");
lcd.print(setPoints[currentAnimal][0]);
lcd.setCursor(1, 2);
lcd.print("SetPoint T: ");
lcd.print(setPoints[currentAnimal][1]);
lcd.setCursor(1, 3);
lcd.print("SetPoint D: ");
lcd.print(setPoints[currentAnimal][2]);
if (!adjustingSetPoint) {
// Indicar qual setpoint está selecionado
lcd.setCursor(0, currentSetPointIndex + 1);
lcd.print(">");
} else {
// Mostrar cursor para indicar que está ajustando o setpoint
lcd.setCursor(13, currentSetPointIndex + 1);
lcd.cursor();
}
}
delay(100); // Evitar debouncing
}
void pos_servo(int pos){
Serial.println("Posicionando Servo... Aguarde...");
switch (pos) {
case 1:
for (int i = 40; i <= 80; i++) {
servo1.write(i);
delay(100);
}
break;
case 2:
for (int i = 80; i >= 40; i--) {
servo1.write(i);
delay(100);
}
break;
case 3:
for (int i = 40; i <= 80; i++) {
servo1.write(i);
delay(100);
}
break;
case 4:
for (int i = 80; i <= 120; i++) {
servo1.write(i);
delay(100);
}
break;
case 5:
for (int i = 120; i >= 40; i--) {
servo1.write(i);
delay(100);
}
break;
default:
break;
}
}
void readSetPointsFromEEPROM() {
int address = 0;
for (int i = 0; i < sizeof(animais) / sizeof(animais[0]); i++) {
for (int j = 0; j < 3; j++) {
setPoints[i][j] = EEPROM.read(address);
address++;
}
}
}
void saveSetPointsToEEPROM() {
int address = 0;
for (int i = 0; i < sizeof(animais) / sizeof(animais[0]); i++) {
for (int j = 0; j < 3; j++) {
EEPROM.write(address, setPoints[i][j]);
address++;
}
}
}
void clearEEPROM() {
for (int i = 0; i < EEPROM.length(); i++) {
EEPROM.write(i, 0);
}
}