// PROYECTO DE INGENIERIA 1
// FASE 4
// JHOAN ALEXANDER PARRADO TRIANA
// --- INCLUSIONES NECESARIAS ---
#include <Arduino.h>
#include <WiFi.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <cmath>
#include <AccelStepper.h>
// --- 1. Definición de Pines y Constantes ---
// Pines Ultrasónico y Botón
const int TRIG_PIN = 5;
const int ECHO_PIN = 18;
const int DEPOSIT_BUTTON_PIN = 23;
// Pines para el Driver A4988 y Stepper
const int DIR_PIN = 17; // GPIO para la Dirección (DIR)
const int STEP_PIN = 16; // GPIO para el Pulso de Paso (STEP)
const int STEPS_TO_OPEN = 1600; // Pasos necesarios para abrir/cerrar (AJUSTAR SEGÚN CALIBRACIÓN)
// Objeto Stepper: Usando el modo DRIVER para A4988/DRV8825
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);
// Configuración de la Pantalla OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// --- 2. Variables y Conectividad ---
const char* ssid = "Wokwi-Guest";
const char* password = "";
long currentLevel_cm = 0;
const int CONTAINER_HEIGHT_CM = 30;
const int MIN_DISTANCE_FULL_CM = 2;
const int FULL_THRESHOLD_CM = 6;
bool door_open = false;
// --- Funciones de Utilidad (OLED y WiFi) ---
void oled_status(const char* line1, const char* line2 = "", const char* line3 = "") {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println(line1);
display.setCursor(0, 10);
display.println(line2);
display.setCursor(0, 20);
display.println(line3);
display.display();
}
void connect_wifi() {
oled_status("Conectando Wi-Fi...");
WiFi.begin(ssid, password);
Serial.print("Conectando a Wi-Fi...");
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n¡Wi-Fi Conectado!");
oled_status("Wi-Fi OK", "Listo para operar.");
} else {
Serial.println("\nFallo de conexión Wi-Fi.");
oled_status("ERROR:", "Fallo de Wi-Fi.");
}
delay(1000);
}
// --- FUNCIONES DE CONTROL DEL MOTOR PASO A PASO ---
void open_door() {
if (!door_open) {
oled_status("Puerta Abriendo (Stepper)...");
Serial.println("Movimiento: Abriendo puerta.");
// Mueve el motor a la posición absoluta de apertura
stepper.moveTo(STEPS_TO_OPEN);
// Ejecuta el movimiento hasta que la distancia restante sea 0
while (stepper.distanceToGo() != 0) {
stepper.run();
}
door_open = true;
delay(500);
oled_status("Puerta ABIERTA", "¡Deposite y presione", "el Boton!");
}
}
void close_door() {
if (door_open) {
oled_status("Puerta Cerrando (Stepper)...");
Serial.println("Movimiento: Cerrando puerta.");
// Mueve el motor de vuelta a la posición 0 (cerrado)
stepper.moveTo(0);
// Ejecuta el movimiento hasta que la distancia restante sea 0
while (stepper.distanceToGo() != 0) {
stepper.run();
}
door_open = false;
delay(500);
oled_status("Esperando QR...");
}
}
// --- FUNCIONES DE LÓGICA (Ultrasónico y Transacción) ---
long read_ultrasonic_level() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH);
long distance_cm = duration * 0.034 / 2;
if (distance_cm > CONTAINER_HEIGHT_CM || distance_cm <= 0) {
distance_cm = CONTAINER_HEIGHT_CM;
}
return distance_cm;
}
void send_data_to_cloud(String userId, long level_cm, int points, float weight_g) {
oled_status("Transaccion OK!",
"Puntos Ganados:",
(String(points) + " (Peso: " + String(weight_g, 1) + "g)").c_str());
int percentage = map(level_cm, CONTAINER_HEIGHT_CM, MIN_DISTANCE_FULL_CM, 0, 100);
percentage = constrain(percentage, 0, 100);
Serial.println("\n--- DATOS ENVIADOS A LA NUBE (Simulacion) ---");
Serial.println("Usuario ID: " + userId);
Serial.println("Peso Depositado: " + String(weight_g, 1) + " gramos");
Serial.println("Puntos Asignados: " + String(points));
Serial.println("Nivel de llenado: " + String(percentage) + " %");
Serial.println("----------------------------------------------");
delay(4000);
}
void check_container_level(long level_cm) {
if (level_cm <= FULL_THRESHOLD_CM) {
oled_status("¡ATENCIÓN!",
"CONTENEDOR",
("CASI LLENO (" + String(CONTAINER_HEIGHT_CM - level_cm) + "cm)").c_str());
Serial.println("!!! NOTIFICACIÓN: Contenedor necesita vaciado.");
delay(3000);
}
}
// --- SETUP (Inicialización) ---
void setup() {
Serial.begin(115200);
// Inicialización de pines
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(DEPOSIT_BUTTON_PIN, INPUT_PULLUP);
// Inicialización del Stepper
stepper.setMaxSpeed(1000); // Velocidad máxima (Pasos/segundo)
stepper.setAcceleration(500); // Aceleración suave
stepper.setCurrentPosition(0); // Establece la posición inicial (cerrado) como 0
close_door(); // Asegura que el motor esté en la posición 0 (cerrada)
// Inicialización de la pantalla OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("Fallo al iniciar SSD1306"));
for(;;);
}
display.display();
delay(500);
oled_status("Sistema ON", "Iniciando Contenedor", "Motor Stepper OK...");
connect_wifi();
oled_status("Esperando QR...", "Ingrese ID en Serial.", "Ej: 'USR123'");
}
// --- LOOP (Bucle Principal) ---
void loop() {
// --- 1. Detección de Usuario (Simulación QR) ---
if (Serial.available() > 0) {
String userId = Serial.readStringUntil('\n');
userId.trim();
if (userId.length() > 0) {
oled_status("QR Escaneado OK", ("ID: " + userId).c_str(), "Procesando...");
Serial.println("\n> Usuario identificado: " + userId);
open_door(); // ¡Abre la puerta!
// --- 2. Espera de Depósito (Simulación Botón) ---
while (digitalRead(DEPOSIT_BUTTON_PIN) == HIGH) {
oled_status("Puerta ABIERTA", "Presione el Boton", "para registrar peso.");
delay(100);
}
// --- 3. Registro y Cálculo ---
oled_status("Calculando puntos...", "Boton presionado.", "Registrando...");
float peso_depositado_gramos = 85.0; // Peso Fijo Simulado
int puntos_ganados = floor(peso_depositado_gramos / 10.0);
currentLevel_cm = read_ultrasonic_level();
close_door(); // ¡Cierra la puerta!
// --- 4. Transacción y Notificación ---
send_data_to_cloud(userId, currentLevel_cm, puntos_ganados, peso_depositado_gramos);
check_container_level(currentLevel_cm);
oled_status("Transaccion Completa.", "Esperando nuevo QR...");
delay(500);
}
}
// --- 5. Muestra Nivel de Llenado en Espera ---
if (!door_open) {
long level_check = read_ultrasonic_level();
int percentage = map(level_check, CONTAINER_HEIGHT_CM, MIN_DISTANCE_FULL_CM, 0, 100);
percentage = constrain(percentage, 0, 100);
display.setCursor(0, 30);
display.fillRect(0, 30, SCREEN_WIDTH, 10, BLACK);
display.print("Nivel: ");
display.print(percentage);
display.print("% (");
display.print(CONTAINER_HEIGHT_CM - level_check);
display.println("cm)");
display.display();
}
// Si el motor está en una posición intermedia por alguna razón, stepper.run() podría corregirlo.
stepper.run();
}