Buenos días,
Me encuentro programando una maquina expendedora en un Arduino UNO hasta hoy no había tenido ningún problema, definí una maquina de estados y las condiciones necesarias para cada transición.
Hoy quería mejorarla añadiendo estructuras y optimizar el consumo de memoria, tras modificarla actualmente en el estado de entrega no me realiza la transición al siguiente estado(de entrega a recogida) a pesar de que cumple todos los requisitos. He probado cambiando el estado al que avanzar (en lugar de la recogida por ejemplo a la elección nuevamente) y en ese caso la transición funciona sin fallo. También he revisado el cableado y sustituido los cables que pudieran estar en peor estado.
Mi código es el siguiente:
// ---------------------------- Definiciones de Pines ----------------------------
#define PIN_BOTONA 6
#define PIN_BOTONB 7
#define PIN_FIN_CARRERA 11
// ---------------------------- Estructura de Producto ----------------------------
struct Producto {
int id;
char nombre[50];
float precio;
int stockDeseado;
};
// ---------------------------- Estructura de Compartimentos ----------------------------
struct Compartimento {
int id;
int productoId; // Identificador del producto que contiene
int stock;
};
// ---------------------------- Estructura de Maquina ----------------------------
#define NUM_COMPARTIMENTOS 3
#define NUM_PRODUCTOS 2
struct MaquinaExpendedora {
Compartimento compartimentos[NUM_COMPARTIMENTOS];
Producto productos[NUM_PRODUCTOS];
int numProductos;
float temperaturaIdeal;
};
// ---------------------------- Definiciones de LEDs y Sensor ----------------------------
const int ledPins[] = {2, 3, 4, 5};
const int LedEntrega = 8;
const int LedFallo = 9;
const int sensorPin = A0;
const int sensorPinTemp = A1;
// ---------------------------- Variables de Tiempo para los Botones ----------------------------
unsigned long tiempoInicioPulsacionA = 0;
unsigned long tiempoPulsacionA = 0;
unsigned long int inicioPulsacionA, botonPulsadoA;
unsigned long int inicioPulsacionB, botonPulsadoB;
// ---------------------------- Variables Misceláneas ----------------------------
unsigned long int valor;
unsigned long int cantidadAIntroducir;
unsigned long int tiempoRecogida;
unsigned long int tiempoReentrega;
bool botonesSoltados = false;
bool devuelto = false;
bool aviso = false;
int Misaldo = 0;
int finCarrera;
int cantidadAAnadir = 0;
int compartimentoSeleccionado = 0;
// ---------------------------- Definición de la Contraseña del Modo Técnico ----------------------------
const int claveTecnico[] = {1, 2, 3, 4, 15};
const int longitudClave = 5;
int claveIntroducida[longitudClave];
int indiceClave = 0;
bool introduciendoClave = false;
bool ignorarSoltarA = false;
// ---------------------------- Enumeraciones de Estados ----------------------------
enum estadoMaquina {
ELECCION,
ENTREGA,
RECOGIDA,
TECNICO,
PRECIOS,
RELLENARMAQUINA,
INTRODUCIR_CLAVE
} maquina = ELECCION, estadoAnterior = 7;
// Define los posibles estados de un botón
enum Estadoboton {
PULSADO = 1,
SUELTO = 2,
TR_PULS = 3,
TR_SOLT = 4
} botonA = SUELTO, botonB = SUELTO;
// ---------------------------- Instancia de la Máquina Expendedora (Puntero) ----------------------------
MaquinaExpendedora miMaquina;
MaquinaExpendedora* ptrMaquina = &miMaquina; // Puntero a la instancia de la máquina
// ---------------------------- Funciones ----------------------------
float leerTemperatura() {
int valorPotenciometro = analogRead(sensorPinTemp);
float temperatura = (float)valorPotenciometro * (40.0 / 1023.0);
return temperatura;
}
Estadoboton gestorBoton(Estadoboton* estadoAnteriorBoton, bool lectura) {
switch (*estadoAnteriorBoton) {
case PULSADO:
if (lectura == 1) {
*estadoAnteriorBoton = TR_SOLT;
}
break;
case SUELTO:
if (lectura == 0) {
*estadoAnteriorBoton = TR_PULS;
}
break;
case TR_PULS:
*estadoAnteriorBoton = PULSADO;
break;
case TR_SOLT:
*estadoAnteriorBoton = SUELTO;
break;
}
return *estadoAnteriorBoton;
}
unsigned long int MuestraSaldo(unsigned long int *valor) {
int cantidadAIntroducir = *valor;
for (int i = 0; i < 4; i++) {
int bit = (*valor >> i) & 1; // Usando puntero para acceder a valor
digitalWrite(ledPins[i], bit);
}
}
void retirarDinero() {
Serial.println("Se ha devuelto su saldo");
Serial.print("Saldo actual: ");
Misaldo = 0;
devuelto = true;
Serial.println(Misaldo);
}
int seleccionarProducto() {
Serial.print("Seleccione el compartimento (1-");
Serial.print(NUM_COMPARTIMENTOS);
Serial.println("):");
if (Serial.available() > 0) {
compartimentoSeleccionado = Serial.read() - '1';
if (compartimentoSeleccionado >= 0 && compartimentoSeleccionado < NUM_COMPARTIMENTOS) {
Serial.print("Compartimento seleccionado: ");
Serial.println(compartimentoSeleccionado + 1);
} else {
Serial.println("Selección inválida.");
compartimentoSeleccionado = 0;
}
}
return compartimentoSeleccionado;
}
int seleccionarCompartimento() {
int maxStock = -1;
int mejorCompartimento = -1;
for (int i = 0; i < NUM_COMPARTIMENTOS; i++) {
for (int j = 0; j < ptrMaquina->numProductos; j++) { // Usando puntero para acceder a numProductos
if (ptrMaquina->compartimentos[i].productoId == ptrMaquina->productos[j].id) { // Usando puntero para acceder a los arrays
if (ptrMaquina->compartimentos[i].stock > maxStock) { // Accediendo directamente al miembro
maxStock = ptrMaquina->compartimentos[i].stock;
mejorCompartimento = i;
}
break;
}
}
}
return mejorCompartimento;
}
// ---------------------------- Función de Configuración ----------------------------
void setup() {
pinMode(PIN_BOTONA, INPUT_PULLUP);
pinMode(PIN_BOTONB, INPUT_PULLUP);
for (int i = 0; i < 4; i++) {
pinMode(ledPins[i], OUTPUT);
}
pinMode(LedFallo, OUTPUT);
pinMode(LedEntrega, OUTPUT);
pinMode(PIN_FIN_CARRERA, INPUT_PULLUP);
Serial.begin(9600);
Serial.println("Aqui empezamos:");
// ---------------------------- Inicialización de la Máquina ----------------------------
ptrMaquina->numProductos = NUM_PRODUCTOS;
ptrMaquina->temperaturaIdeal = 25.0;
ptrMaquina->productos[0].id = 1;
strcpy(ptrMaquina->productos[0].nombre, "Refresco A");
ptrMaquina->productos[0].precio = 2;
ptrMaquina->productos[0].stockDeseado = 10;
ptrMaquina->productos[1].id = 2;
strcpy(ptrMaquina->productos[1].nombre, "Snack B");
ptrMaquina->productos[1].precio = 1;
ptrMaquina->productos[1].stockDeseado = 15;
ptrMaquina->compartimentos[0].id = 1;
ptrMaquina->compartimentos[0].productoId = 1;
ptrMaquina->compartimentos[0].stock = 5;
ptrMaquina->compartimentos[1].id = 2;
ptrMaquina->compartimentos[1].productoId = 2;
ptrMaquina->compartimentos[1].stock = 10;
ptrMaquina->compartimentos[2].id = 3;
ptrMaquina->compartimentos[2].productoId = 1;
ptrMaquina->compartimentos[2].stock = 3;
}
// ---------------------------- Bucle Principal ----------------------------
void loop() {
cantidadAIntroducir = (analogRead(A0) >> 6);
MuestraSaldo(&cantidadAIntroducir);
bool lecturaA = digitalRead(PIN_BOTONA);
bool lecturaB = digitalRead(PIN_BOTONB);
finCarrera = digitalRead(PIN_FIN_CARRERA);
botonA = gestorBoton(&botonA, lecturaA);
if (botonA == TR_PULS) {
inicioPulsacionA = millis();
}
if (botonA == PULSADO) {
botonPulsadoA = millis() - inicioPulsacionA;
}
botonB = gestorBoton(&botonB, lecturaB);
if (botonB == TR_PULS) {
inicioPulsacionB = millis();
}
if (botonB == PULSADO) {
botonPulsadoB = millis() - inicioPulsacionB;
}
// ---------------------------- Máquina de Estados Principal ----------------------------
switch (maquina) {
case ELECCION:
if (estadoAnterior != ELECCION) {
Serial.println("En eleccion, A(mas cerca pot) para introducir saldo, B para elegir producto");
Serial.println(finCarrera);
estadoAnterior = maquina;
botonesSoltados = false;
devuelto = false;
indiceClave = 0;
introduciendoClave = false;
}
if (!botonesSoltados && botonA == SUELTO && botonB == SUELTO) {
botonesSoltados = true;
}
if (botonA == TR_SOLT && botonesSoltados == true && devuelto == false) {
Serial.print("Tu Saldo:");
Misaldo = cantidadAIntroducir + Misaldo;
Serial.println(Misaldo);
}
if (botonB == TR_SOLT && botonesSoltados == true) {
maquina = ENTREGA;
estadoAnterior = 7;
}
if (botonA == PULSADO && botonPulsadoA > 3000 && botonesSoltados == true && devuelto == false) {
retirarDinero();
}
if (botonA == PULSADO && botonB == PULSADO && botonPulsadoA > 2000 && botonPulsadoB > 2000 && botonesSoltados == true) {
maquina = INTRODUCIR_CLAVE;
Serial.println("Introduce la clave tecnica:");
indiceClave = 0;
for (int i = 0; i < longitudClave; i++) {
claveIntroducida[i] = -1;
}
introduciendoClave = true;
ignorarSoltarA = true;
estadoAnterior = 7;
}
break;
case INTRODUCIR_CLAVE:
if (estadoAnterior != INTRODUCIR_CLAVE) {
Serial.print("Introduce el digito ");
Serial.println(indiceClave + 1);
Serial.println("Usa el potenciometro (0-15) y A para confirmar.");
estadoAnterior = maquina;
}
if (botonA == TR_SOLT && introduciendoClave && ignorarSoltarA) {
ignorarSoltarA = false;
} else if (botonA == TR_SOLT && introduciendoClave && !ignorarSoltarA) {
claveIntroducida[indiceClave] = cantidadAIntroducir;
Serial.print("Digit ");
Serial.print(indiceClave + 1);
Serial.print(" introducido: ");
Serial.println(claveIntroducida[indiceClave]);
indiceClave++;
if (indiceClave == longitudClave) {
bool claveCorrecta = true;
for (int i = 0; i < longitudClave; i++) {
if (claveIntroducida[i] != claveTecnico[i]) {
claveCorrecta = false;
break;
}
}
if (claveCorrecta) {
Serial.println("Clave correcta. Accediendo al modo tecnico.");
maquina = TECNICO;
estadoAnterior = 7;
} else {
Serial.println("Clave incorrecta. Volviendo a Eleccion.");
maquina = ELECCION;
estadoAnterior = 7;
}
introduciendoClave = false;
indiceClave = 0;
ignorarSoltarA = false;
} else {
Serial.print("Introduce el digito ");
Serial.println(indiceClave + 1);
}
}
break;
case ENTREGA:
if (estadoAnterior != ENTREGA) {
Serial.println("Estoy en entrega");
estadoAnterior = maquina;
}
digitalWrite(LedEntrega, LOW);
int compartimentoSeleccionadoEntrega = seleccionarCompartimento();
if (compartimentoSeleccionadoEntrega != -1) {
int productoIdSeleccionado = ptrMaquina->compartimentos[compartimentoSeleccionadoEntrega].productoId;
float precioProducto = 0;
for (int i = 0; i < ptrMaquina->numProductos; i++) {
if (ptrMaquina->productos[i].id == productoIdSeleccionado) {
precioProducto = ptrMaquina->productos[i].precio;
Serial.println(precioProducto);
break;
}
}
if (botonB == TR_PULS && (Misaldo >= precioProducto) && ptrMaquina->compartimentos[compartimentoSeleccionadoEntrega].stock > 0) {
ptrMaquina->compartimentos[compartimentoSeleccionadoEntrega].stock--;
Serial.print("Entregando producto del compartimento ");
Serial.println(compartimentoSeleccionadoEntrega + 1);
digitalWrite(LedEntrega, HIGH);
Misaldo -= precioProducto;
Serial.print("Tu nuevo saldo es:");
Serial.println(Misaldo);
maquina = RECOGIDA;
Serial.println("TRANSICION A RECOGIDA (SALDO OK)");
estadoAnterior = 7;
} else if (botonB == TR_PULS) {
Serial.println("No hay suficiente saldo.");
Serial.print("Saldo: ");
Serial.println(Misaldo);
digitalWrite(LedFallo, HIGH);
Serial.println(finCarrera);
maquina = RECOGIDA;
Serial.println("TRANSICION A RECOGIDA (SIN SALDO)");
estadoAnterior = 7;
}
} else if (botonB == TR_PULS) {
Serial.println("No hay productos disponibles.");
digitalWrite(LedFallo, HIGH);
maquina = RECOGIDA;
Serial.println("TRANSICION A RECOGIDA (SIN PRODUCTOS)");
estadoAnterior = 7;
}
break;
case RECOGIDA:
Serial.print("ESTADO: RECOGIDA - Valor de estadoAnterior: ");
Serial.println(estadoAnterior);
Serial.print("ESTADO: RECOGIDA - Valor de finCarrera: ");
Serial.println(finCarrera);
if (estadoAnterior != RECOGIDA) {
tiempoRecogida = millis();
estadoAnterior = maquina;
Serial.println("ESTOY EN RECOGIDA");
}
if (finCarrera == 0) {
Serial.println("Vuelvo a Eleccion");
maquina = ELECCION;
digitalWrite(LedFallo, LOW);
digitalWrite(LedEntrega, LOW);
estadoAnterior = 7;
}
if (estadoAnterior == RECOGIDA) {
tiempoReentrega = millis() - tiempoRecogida;
}
if (tiempoReentrega > 10000 && aviso == false) {
Serial.println("Se ha reintentado entrega de producto");
tiempoReentrega = millis();
aviso = true;
}
break;
case TECNICO:
if (estadoAnterior != TECNICO) {
Serial.println("Estoy en el modo Tecnico, pulse A para cambiar el precio y B para añadir existencias");
estadoAnterior = maquina;
botonesSoltados = false;
}
if (botonA == TR_SOLT && botonesSoltados == true) {
maquina = PRECIOS;
estadoAnterior = 7;
}
if (botonB == TR_SOLT && botonesSoltados == true) {
maquina = RELLENARMAQUINA;
estadoAnterior = 7;
}
if (!botonesSoltados && botonA == SUELTO && botonB == SUELTO) {
botonesSoltados = true;
}
if (botonA == PULSADO && botonB == PULSADO && botonPulsadoA > 2000 && botonPulsadoB > 2000 && botonesSoltados == true) {
maquina = ELECCION;
devuelto = true;
estadoAnterior = 7;
}
break;
case RELLENARMAQUINA:
if (estadoAnterior != RELLENARMAQUINA) {
Serial.println("Modo Rellenar Maquina:");
Serial.print("Compartimento: ");
Serial.println(compartimentoSeleccionado + 1);
Serial.println("Use A para cambiar, B para añadir.");
estadoAnterior = maquina;
}
if (botonA == TR_SOLT) {
compartimentoSeleccionado = (compartimentoSeleccionado + 1) % NUM_COMPARTIMENTOS;
Serial.print("Compartimento: ");
Serial.println(compartimentoSeleccionado + 1);
}
if (botonB == TR_SOLT) {
cantidadAAnadir = cantidadAIntroducir;
ptrMaquina->compartimentos[compartimentoSeleccionado].stock += cantidadAAnadir;
Serial.print("Añadido ");
Serial.print(cantidadAAnadir);
Serial.print(" al compartimento ");
Serial.println(compartimentoSeleccionado + 1);
Serial.print("El compartimento seleccionado tiene:");
Serial.println(ptrMaquina->compartimentos[compartimentoSeleccionado].stock);
maquina = TECNICO;
estadoAnterior = 7;
}
break;
case PRECIOS:
if (estadoAnterior != PRECIOS) {
Serial.println("Estoy en Precios. Pulse A para establecer un nuevo precio y B para volver a Tecnico.");
estadoAnterior = maquina;
}
if (botonA == TR_PULS) {
Serial.print("Estableciendo nuevo precio para el producto del compartimento ");
Serial.print(compartimentoSeleccionado + 1);
Serial.print(" a: ");
int productoIdPrecio = ptrMaquina->compartimentos[compartimentoSeleccionado].productoId;
for (int i = 0; i < ptrMaquina->numProductos; i++) {
if (ptrMaquina->productos[i].id == productoIdPrecio) {
ptrMaquina->productos[i].precio = cantidadAIntroducir;
Serial.println(ptrMaquina->productos[i].precio);
break;
}
}
}
if (botonB == TR_PULS) {
maquina = TECNICO;
estadoAnterior = 7;
}
break;
}
// Aquí termina el switch de la máquina de estados
}
Espero haber realizado bien el post pues es el primero que hago.