#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
// Definição dos pinos do Display ST7735
#define TFT_CS 15
#define TFT_RST 4
#define TFT_DC 2
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
// Configuração do Servo Motor
const int pinoServo = 13;
const int frequenciaPWM = 50; // 50Hz para servo
const int resolucaoPWM = 16; // Resolução de 16 bits
// Configuração do Botão Físico para liberar a bebida
const int pinoBotao = 14;
// Configuração da rede Wi-Fi padrão do Wokwi
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// O seu Access Token do Mercado Pago
const String accessToken = "APP_USR-5597491384836501-072612-f1bc0315e689ffaca264cdc477aa1a0d-3569723008";
// Pinos dos Relés / Bombas
const int pinoBombaGin = 21;
const int pinoBombaVodka = 22;
const int pinoBombaEnergetico = 19;
enum EstadoMaquina {
MENU,
GERANDO_PIX,
AGUARDANDO_PAGAMENTO,
LIBERANDO_GELO,
AGUARDANDO_BOTAO_BEBIDA,
SERVINDO
};
EstadoMaquina estadoAtual = MENU;
int opcaoEscolhida = 0;
float valorPix = 0.0;
String idPagamento = "";
const int tempoDose = 3000;
const int tempoEnergetico = 8000;
// Função auxiliar atualizada para o novo padrão PWM do ESP32
void moverServo(int angulo) {
int duty = map(angulo, 0, 180, 1638, 7864);
ledcWrite(pinoServo, duty);
}
void setup() {
Serial.begin(115200);
// Nova forma padrão de iniciar o PWM no ESP32 (Frequência e Resolução direto no pino)
ledcAttach(pinoServo, frequenciaPWM, resolucaoPWM);
moverServo(0); // Posição inicial (fechado)
pinMode(pinoBotao, INPUT_PULLUP);
tft.initR(INITR_BLACKTAB);
tft.fillScreen(ST7735_BLACK);
tft.setRotation(1);
tft.setTextColor(ST7735_WHITE);
tft.setTextSize(1);
tft.setCursor(10, 10);
tft.println("Iniciando Wokwi...");
pinMode(pinoBombaGin, OUTPUT);
pinMode(pinoBombaVodka, OUTPUT);
pinMode(pinoBombaEnergetico, OUTPUT);
digitalWrite(pinoBombaGin, LOW);
digitalWrite(pinoBombaVodka, LOW);
digitalWrite(pinoBombaEnergetico, LOW);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
tft.fillScreen(ST7735_BLACK);
exibirMenuTela();
}
void loop() {
switch (estadoAtual) {
case MENU:
lerComandosMenu();
break;
case GERANDO_PIX:
criarPagamentoMercadoPago();
break;
case AGUARDANDO_PAGAMENTO:
checarStatusPagamento();
break;
case LIBERANDO_GELO:
acionarDispenserGelo();
break;
case AGUARDANDO_BOTAO_BEBIDA:
verificarBotaoBebida();
break;
case SERVINDO:
break;
}
}
void exibirMenuTela() {
tft.fillScreen(ST7735_BLACK);
tft.setCursor(5, 5);
tft.setTextColor(ST7735_YELLOW);
tft.println("=== MENU COPAO ===");
tft.setTextColor(ST7735_WHITE);
tft.setCursor(5, 25);
tft.println("[1] Dose Gin - R$ 10");
tft.setCursor(5, 45);
tft.println("[2] Dose Vodka - R$ 10");
tft.setCursor(5, 65);
tft.println("[3] Copao Gin+Ener - R$ 25");
tft.setCursor(5, 85);
tft.println("[4] Copao Vod+Ener - R$ 25");
tft.setCursor(5, 115);
tft.setTextColor(ST7735_CYAN);
tft.println("Envie 1, 2, 3 ou 4 no Serial");
estadoAtual = MENU;
}
void lerComandosMenu() {
if (Serial.available() > 0) {
char comando = Serial.read();
if (comando == '1') { opcaoEscolhida = 1; valorPix = 10.00; }
else if (comando == '2') { opcaoEscolhida = 2; valorPix = 10.00; }
else if (comando == '3') { opcaoEscolhida = 3; valorPix = 25.00; }
else if (comando == '4') { opcaoEscolhida = 4; valorPix = 25.00; }
else return;
tft.fillScreen(ST7735_BLACK);
tft.setCursor(10, 50);
tft.println("Gerando Pix...");
estadoAtual = GERANDO_PIX;
}
}
void criarPagamentoMercadoPago() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin("https://api.mercadopago.com/v1/payments");
http.addHeader("Authorization", "Bearer " + accessToken);
http.addHeader("Content-Type", "application/json");
String payload = "{";
payload += "\"transaction_amount\":" + String(valorPix, 2) + ",";
payload += "\"description\":\"Maquina de Copao\"," ;
payload += "\"payment_method_id\":\"pix\",";
payload += "\"payer\":{\"email\":\"[email protected]\"}";
payload += "}";
int httpResponseCode = http.POST(payload);
if (httpResponseCode > 0) {
String response = http.getString();
DynamicJsonDocument doc(4096);
deserializeJson(doc, response);
idPagamento = doc["id"].as<String>();
desenharQRCodeSimulado();
estadoAtual = AGUARDANDO_PAGAMENTO;
} else {
tft.fillScreen(ST7735_BLACK);
tft.setCursor(10, 50);
tft.println("Erro na API Pix!");
delay(2000);
exibirMenuTela();
}
http.end();
}
}
void desenharQRCodeSimulado() {
tft.fillScreen(ST7735_WHITE);
tft.fillRect(30, 20, 100, 100, ST7735_BLACK);
tft.fillRect(36, 26, 88, 88, ST7735_WHITE);
tft.fillRect(45, 35, 30, 30, ST7735_BLACK);
tft.fillRect(85, 35, 20, 20, ST7735_BLACK);
tft.fillRect(45, 75, 20, 20, ST7735_BLACK);
tft.setCursor(5, 2);
tft.setTextColor(ST7735_BLACK);
tft.setTextSize(1);
tft.println("Aguardando Pags...");
}
void checarStatusPagamento() {
if (Serial.available() > 0) {
char comando = Serial.read();
if (comando == 'P' || comando == 'p') {
estadoAtual = LIBERANDO_GELO;
return;
}
}
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = "https://api.mercadopago.com/v1/payments/" + idPagamento;
http.begin(url);
http.addHeader("Authorization", "Bearer " + accessToken);
int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
String response = http.getString();
DynamicJsonDocument doc(2048);
deserializeJson(doc, response);
String status = doc["status"];
if (status == "approved") {
estadoAtual = LIBERANDO_GELO;
return;
}
}
http.end();
}
delay(4000);
}
void acionarDispenserGelo() {
tft.fillScreen(ST7735_BLACK);
tft.setCursor(10, 30);
tft.setTextColor(ST7735_GREEN);
tft.println("PAGAMENTO APROVADO!");
tft.setCursor(10, 60);
tft.setTextColor(ST7735_YELLOW);
tft.println("Liberando Gelo...");
moverServo(90);
delay(1000);
moverServo(0);
tft.fillScreen(ST7735_BLACK);
tft.setCursor(5, 20);
tft.setTextColor(ST7735_WHITE);
tft.println("1. Pegue o gelo");
tft.setCursor(5, 45);
tft.println("2. Posicione o copo");
tft.setCursor(5, 70);
tft.setTextColor(ST7735_CYAN);
tft.println("3. Aperte o BOTAO");
tft.setCursor(5, 100);
tft.setTextColor(ST7735_YELLOW);
tft.println("para servir a bebida!");
estadoAtual = AGUARDANDO_BOTAO_BEBIDA;
}
void verificarBotaoBebida() {
if (digitalRead(pinoBotao) == LOW) {
delay(50);
if (digitalRead(pinoBotao) == LOW) {
estadoAtual = SERVINDO;
liberarBebida();
exibirMenuTela();
}
}
}
void liberarBebida() {
tft.fillScreen(ST7735_BLACK);
tft.setCursor(10, 50);
tft.setTextColor(ST7735_YELLOW);
tft.println("Preparando seu drink...");
if (opcaoEscolhida == 1) {
digitalWrite(pinoBombaGin, HIGH);
delay(tempoDose);
digitalWrite(pinoBombaGin, LOW);
} else if (opcaoEscolhida == 2) {
digitalWrite(pinoBombaVodka, HIGH);
delay(tempoDose);
digitalWrite(pinoBombaVodka, LOW);
} else if (opcaoEscolhida == 3) {
digitalWrite(pinoBombaGin, HIGH);
digitalWrite(pinoBombaEnergetico, HIGH);
delay(tempoDose);
digitalWrite(pinoBombaGin, LOW);
delay(tempoEnergetico - tempoDose);
digitalWrite(pinoBombaEnergetico, LOW);
} else if (opcaoEscolhida == 4) {
digitalWrite(pinoBombaVodka, HIGH);
digitalWrite(pinoBombaEnergetico, HIGH);
delay(tempoDose);
digitalWrite(pinoBombaVodka, LOW);
delay(tempoEnergetico - tempoDose);
digitalWrite(pinoBombaEnergetico, LOW);
}
delay(2000);
}