Estoy haciendo una maquina que funciona con monedas. El tema es que "acepto" 3 tipos de monedas que me dan un crédito, , por poner un ejemplo, la "moneda 1" me da 1 minuto, la "moneda 2" me da 2 minutos y la "moneda 3" me da 3 minutos... ¿fácil verdad?
Esta parte ya la tengo controlada. Todas las interrupciones (monedas) convergen con uno u otro valor en una variable llamada crédito, que se multiplica por un tiempo base (así puedo modificarlo fácilmente según lo quiera el cliente) y consigo tener una variable que me almacena el tiempo que hay disponible.
Pues el caso es que no se como hacer el "descontador" de tiempo para que la maquina esté habilitada mientras tenga tiempo de crédito disponible. Con un delay, imposible, con for no he sido capaz.
Un detalle añadido... mientras esta descontando y la maquina en uso, si insertan otra moneda, debe sumarse, al tiempo que resta. Esta parte creo que ya lo hará (cuando empiece a descontar, claro).
Obviamente, como va a ser una maquina cara al público y desatendido, no puedo tener muchos delays, o muy largos para evitar problemas por la interactuación de las personas
Alguna recomendación?
Otra pregunta añadida, voy a poner un contador de monedas... ¿como hago para que se mantenga si apagan la maquina o si la reinician, o simplemente para saber yo cuanto uso le han dado?... ¿donde o como guardo esa información?
cefere0:
Pues el caso es que no se como hacer el "descontador" de tiempo para que la maquina esté habilitada mientras tenga tiempo de crédito disponible. Con un delay, imposible, con for no he sido capaz.
Un detalle añadido... mientras esta descontando y la maquina en uso, si insertan otra moneda, debe sumarse, al tiempo que resta. Esta parte creo que ya lo hará (cuando empiece a descontar, claro).
Obviamente, como va a ser una maquina cara al público y desatendido, no puedo tener muchos delays, o muy largos para evitar problemas por la interactuación de las personas
Alguna recomendación?
Que le eches un ojo el ejemplo "BlinkWithoutDelay". Sería un buen punto de partida para comprender el uso de la función millis.
Otra pregunta añadida, voy a poner un contador de monedas... ¿como hago para que se mantenga si apagan la maquina o si la reinician, o simplemente para saber yo cuanto uso le han dado?... ¿donde o como guardo esa información?
Eso dependiendo de con y cuanta frecuencia se van a guardar los datos. Las dos opciones más sencillas suelen ser: EEPROM del microcontrolador o una tarjeta SD.
Gracias, me resistía un poco al milis() ya que me preocupa que pasa cuando lleve 49 días encendida la maquina.
Imagino la situación, un cliente pone 5 € y tiene 10 minutos con la maquina habilitada... ¿que pasa si cuando van 4 minutos acaban los 49 dias del milis()?
Esta hiper-aclarado que si restas un tiempo antes de los 49 dias y luego de ellos, el resultado será correcto.
Asi que no te preocupes.
Lee esto (en inglés). En este foro, noter hizo la misma aclaración por otra vía pero similar en efecto. Timing Rollover
Gracias, por aclararme la duda de los 49 días. Hice la lectura del link del maestro sur byte y quedó claro claro.
Sobre el código, ahora no puedo poner nada ya que estoy desde el telf.
Pero la idea es que cuando meto una moneda x debe empezar a contar hasta que pasen 3 minutos (por decir algo) pero claro, si no ha terminado el tiempo y meten otra moneda debe sumar ese tiempo al que resta.
Para que os hagáis una idea, el funcionamiento es el de un centro de lavado de coches, de esos de las gasolineras
Comienza por ahi entonces?
Porque no escribes un simple sketch que haga eso que comentas.
Inserta una moneda que simulamos con un pulsador.... arrancas el timer, luego ingresa otra moneda que agrega 3 minutos.. simple, entonces la variable que iba a cortar en millis() + T1 (de la primer moneda) ahora tiene que sumarsele 3601000 mseg y listo.
Ya me he liado a hacer el sketch y como no podía ser de otra manera... no me va bien del todo.
Aquí va el código:
nt ledPin = 13; // pin del led
int ledPin2 = 12; // pin del led 2
const int moneda1 = 2; //entrada moneda 1
const long tMoneda1 = 10000; // tiempo que da la moneda 1 (10 segundos)
int mon1 = 0;
unsigned long millisPrim = 0; //millis al pulsar cualquier moneda
unsigned long millisFin = 0; // tiempo en que debe parar (aqui se van sumando los tiempos)
void setup() {
// put your setup code here, to run once:
pinMode(ledPin, OUTPUT); // configuro el pin como salida
pinMode(ledPin2, OUTPUT); // configuro el pin como salida
pinMode(moneda1, INPUT); // configuro el pin como entrada
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
mon1 = digitalRead(moneda1);
unsigned long currentMillis = millis();
if (mon1 == HIGH) {
millisFin = (currentMillis + tMoneda1);
if (currentMillis >= millisFin) {
digitalWrite(ledPin, LOW);
} else {
digitalWrite(ledPin, HIGH);
}
} else {
digitalWrite(ledPin2, HIGH);
}
Serial.println(ledPin);
Serial.println(millisFin);
Serial.println(currentMillis);
}
Aquí en reposo tengo el led del pin 13 apagado. y millisFin me da 0, currentMillis empieza a contar.
Cuando pulso el pulsador del pin2, millisFin coge el valor de currentmillis + 10.000. currentMillis sigue contando, pero ledPin se enciende.
Pasados los 10.000, es decir, currentmillis es mayor que millisFin, el led deberia apagarse... y sigue encendido.
Me da que en este caso el tema del temporizador con millis funciona, y falla el if. Por otro lado, el ledPin2 siempre está encendido.
Mira a ver si entiendes por dónde va este código. No lo he probado pero compila, así que debe andar cerca.
const int ledPin = 13; // pin del led
const int moneda1 = 2; //entrada moneda 1
const unsigned long tMoneda1 = 10000; // tiempo que da la moneda 1 (10 segundos)
unsigned long millisPrim = 0; //millis desde los que calculamos los transcurridos
unsigned long millisRestan = 0; // milisegundos de crédito que quedan
void setup() {
// put your setup code here, to run once:
pinMode(ledPin, OUTPUT); // configuro el pin como salida
pinMode(moneda1, INPUT); // configuro el pin como entrada
Serial.begin(9600);
}
void loop() {
if (digitalRead(moneda1) == HIGH) {
if (millisRestan == 0) {
digitalWrite(ledPin, HIGH);
millisPrim = millis(); // si no había crédito tomamos inicio de partida
}
millisRestan += tMoneda1; // En todo caso sumamos crédito
}
if (millisRestan > 0) {
unsigned long transcurridos = millis() - millisPrim; // Operación primordial con millis. Así calculamos el tiempo transcurrido
if (millisRestan>transcurridos) { // Si hay más crédito que transcurridos
millisRestan-=transcurridos; // Restamos los transcurridos del crédito
millisPrim += transcurridos; // y tomamos el millis actual para calcular el próximo transcurrido
}
else { // Si ha transcurrido tiempo restante o más
millisRestan=0;
digitalWrite(ledPin, LOW);
}
Serial.println(millisRestan);
}
}
Muchas gracias, este código va fetén. El único problema es que como cada ciclo lo repite, a la que dejo pulsado un momento el millisRestan crece de una forma descontrolada.
Ahora voy a implementarlo usando una interrupción, ya que es lo que usaré en la práctica.... no me puedo permitir el lujo de que entre una moneda y el programa ande por otros lares. Luego lo posteo aquí para que quede reflejado para la posteridad. Así, además, podéis corregirlo.
Muchas gracias
const int ledPin = 13; // pin del led
const int moneda1 = 2; //entrada moneda 1
const unsigned long tMoneda1 = 10000; // tiempo que da la moneda 1 (10 segundos)
unsigned long millisPrim = 0; //millis desde los que calculamos los transcurridos
unsigned long millisRestan = 0; // milisegundos de crédito que quedan
unsigned long antirebote1 = 0;
int estado = 0;
void setup() {
// put your setup code here, to run once:
pinMode(ledPin, OUTPUT); // configuro el pin como salida
pinMode(moneda1, INPUT); // configuro el pin como entrada
attachInterrupt((0), contador_m1, RISING);
Serial.begin(9600);
}
void contador_m1(){
if(millis() > antirebote1 + 1000)
estado = 1;
antirebote1 = millis();
}
void loop() {
if (estado == 1) {
estado= 0;
if (millisRestan == 0) {
digitalWrite(ledPin, HIGH);
millisPrim = millis(); // si no había crédito tomamos inicio de partida
}
millisRestan += tMoneda1; // En todo caso sumamos crédito
}
if (millisRestan > 0) {
unsigned long transcurridos = millis() - millisPrim; // Operación primordial con millis. Así calculamos el tiempo transcurrido
if (millisRestan>transcurridos) { // Si hay más crédito que transcurridos
millisRestan-=transcurridos; // Restamos los transcurridos del crédito
millisPrim += transcurridos; // y tomamos el millis actual para calcular el próximo transcurrido
}
else { // Si ha transcurrido tiempo restante o más
millisRestan=0;
digitalWrite(ledPin, LOW);
}
Serial.println(millisRestan);
Serial.println(estado);
}
}
a parte de montarlo en una interrupción he añadido antirebote, ya que en las pruebas que he hecho cruzando cables y pinchando en protoboard chinai me aparecian rebotes raros. Se supone que el monedero que tendré será electrónico y me dará señales limpias. En ese momento bajaré de 1000millis a 100 millis, aunque se supone que lo debería poder quitar. creo que nunca está demás
lo mismo con 2 entradas para aplicar 2 tiempos diferentes (con interrupciones)
const int ledPin = 12; // pin del led
const int moneda1 = 2; //entrada moneda 1
const int moneda2 = 3; //entrada moneda 1
const unsigned long tMoneda1 = 10000; // tiempo que da la moneda 1 (10 segundos)
const unsigned long tMoneda2 = 20000; // tiempo que da la moneda 1 (10 segundos)
unsigned long millisPrim = 0; //millis desde los que calculamos los transcurridos
unsigned long millisRestan = 0; // milisegundos de crédito que quedan
unsigned long antirebote = 0;
int est_mon1 = 0;
int est_mon2 = 0;
void setup() {
// put your setup code here, to run once:
pinMode(ledPin, OUTPUT); // configuro el pin como salida
pinMode(moneda1, INPUT); // configuro el pin como entrada
attachInterrupt((0), contador_m1, RISING);
attachInterrupt((1), contador_m2, RISING);
Serial.begin(9600);
}
void contador_m1(){
if(millis() > antirebote + 1000) // esto es para eliminar rebotes raros en la entrada
est_mon1 = 1;
antirebote = millis();
}
void contador_m2(){
if(millis() > antirebote + 1000) // esto es para eliminar rebotes raros en la entrada
est_mon2 = 1;
antirebote = millis();
}
void loop() {
if (est_mon1 == 1) {
est_mon1= 0;
if (millisRestan == 0) {
digitalWrite(ledPin, HIGH);
millisPrim = millis(); // si no había crédito tomamos inicio de partida
}
millisRestan += tMoneda1; // En todo caso sumamos crédito
}
if (est_mon2 == 1) {
est_mon2= 0;
if (millisRestan == 0) {
digitalWrite(ledPin, HIGH);
millisPrim = millis(); // si no había crédito tomamos inicio de partida
}
millisRestan += tMoneda2; // En todo caso sumamos crédito
}
if (millisRestan > 0) {
unsigned long transcurridos = millis() - millisPrim; // Operación primordial con millis. Así calculamos el tiempo transcurrido
if (millisRestan>transcurridos) { // Si hay más crédito que transcurridos
millisRestan-=transcurridos; // Restamos los transcurridos del crédito
millisPrim += transcurridos; // y tomamos el millis actual para calcular el próximo transcurrido
}
else { // Si ha transcurrido tiempo restante o más
millisRestan=0;
digitalWrite(ledPin, LOW);
}
}
Serial.println(millisRestan);
}
En mi proyecto necesito mas interrupciones, por lo que usaré una due.
Si veis bien el código lo marcaré como SOLUCIONADO. Si se os ocurre como limpiar el código, o simplificarlo un poco, bienvenido será
Para que interrupción?
La gente tiene un mal concepto de la interrupción y su uso. Para un evento como una moneda que entra y debe detectarse no hace falta una interrupción si tu código esta bien hecho.
Si no vas a usar delays no hay razón para su uso.
El simple digitalRead(pin) es suficiente para detectar lo que sea.
Si mides cuantas veces se cumple un loop te sorprenderías, y esa es tu velocidad de consulta del pin digital.
En lo personal considero que continues en este hilo hasta resolver todas tus dudas.
Si quieres lo movemos a Proyecto ya que tiene forma de tal.
Unas cuantas sugerencias respecto a las variables globales:
unsigned long millisPrim = 0; //millis desde los que calculamos los transcurridos
unsigned long millisRestan = 0; // milisegundos de crédito que quedan
unsigned int antirebote = 0; // no creo que el "antirebote" dure más de 65 segundos, entonces unsigned int sería lo más óptimo utilizar.
// Además, se usa dentro de una ISR; en la medida de lo posible, debe ser del tipo que menos ciclos de reloj se tome en trabajarlo.
volatile int est_mon1 = 0; // Toda variable global que sea modificada durante una ISR, debe llevar el prefijo "volatile"
volatile int est_mon2 = 0; // Toda variable global que sea modificada durante una ISR, debe llevar el prefijo "volatile"
// Si la variable solamente va a ser leída, entonces no es necesaria esa palabra
surbyte:
Para que interrupción?
La gente tiene un mal concepto de la interrupción y su uso. Para un evento como una moneda que entra y debe detectarse no hace falta una interrupción si tu código esta bien hecho.
Si no vas a usar delays no hay razón para su uso.
El simple digitalRead(pin) es suficiente para detectar lo que sea.
No lo he calculado, la verdad, pero pensé que sería lo mejor para evitar posibles fallos al tratarse de una maquina que trabajará sin asistencia y con gente de a pie, que por lo general tienen poca paciencia al introducir dinero si la maquina hace el mínimo fallo
surbyte:
no hace falta una interrupción si tu código esta bien hecho.
Esto me preocupa, como soy 100% inexperto igual no está muy bien hecho mi código
surbyte:
En lo personal considero que continues en este hilo hasta resolver todas tus dudas.
Si quieres lo movemos a Proyecto ya que tiene forma de tal.
Estaría muy agradecido si se pasa a proyecto, era impensable para mi que mi "aparato" pudiera aparecer aquí como proyecto. En tal caso el nombre debería ser "centro de lavado AP full-equipe" o algo parecido.
Lucario448:
Unas cuantas sugerencias respecto a las variables globales:
unsigned long millisPrim = 0; //millis desde los que calculamos los transcurridos
unsigned long millisRestan = 0; // milisegundos de crédito que quedan
unsigned int antirebote = 0; // no creo que el "antirebote" dure más de 65 segundos, entonces unsigned int sería lo más óptimo utilizar.
// Además, se usa dentro de una ISR; en la medida de lo posible, debe ser del tipo que menos ciclos de reloj se tome en trabajarlo.
volatile int est_mon1 = 0; // Toda variable global que sea modificada durante una ISR, debe llevar el prefijo "volatile"
volatile int est_mon2 = 0; // Toda variable global que sea modificada durante una ISR, debe llevar el prefijo "volatile"
// Si la variable solamente va a ser leída, entonces no es necesaria esa palabra
Añadido a lo que comentaba surbyte sobre las interrupciones, en este caso son incluso más perjudiciales que beneficiosas, principalmente por el tema rebotes.
El loop que te propuse (sin interrupciones) puede ser fácilmente modificable para distintos tipos de moneda. Tan sólo encadena los digitalread correspondientes para calcular el tiempo de incremento dependiendo de la moneda introducida.
const int ledPin = 13; // pin del led
const int moneda1 = 2; //entrada moneda 1
const int moneda2 = 3; //entrada moneda 1
const unsigned long tMoneda1 = 10000; // tiempo que da la moneda 1 (10 segundos)
const unsigned long tMoneda2 = 15000; // tiempo que da la moneda 2 (15 segundos)
unsigned long millisPrim = 0; //millis al pulsar cualquier moneda
unsigned long millisRestan = 0; // milisegundos de crédito que quedan
void setup() {
// put your setup code here, to run once:
pinMode(ledPin, OUTPUT); // configuro el pin como salida
pinMode(moneda1, INPUT); // configuro el pin como entrada
Serial.begin(9600);
}
void loop() {
unsigned long tiempoAgregado=0;
if (digitalRead(moneda1) == HIGH) tiempoAgregado+=tMoneda1;
if (digitalRead(moneda2) == HIGH) tiempoAgregado+=tMoneda2;
if (tiempoAgregado>0) {
if (millisRestan == 0) {
digitalWrite(ledPin, HIGH);
millisPrim = millis(); // si no había crédito tomamos inicio de partida
}
millisRestan += tiempoAgregado; // En todo caso sumamos crédito
}
if (millisRestan > 0) {
unsigned long transcurridos = millis() - millisPrim; // Operación primordial con millis. Así calculamos el tiempo transcurrido
if (millisRestan>transcurridos) { // Si hay más crédito que transcurridos
millisRestan-=transcurridos; // Restamos los transcurridos del crédito
millisPrim += transcurridos; // y tomamos el millis actual para calcular el próximo transcurrido
}
else { // Si ha transcurrido tiempo restante o más
millisRestan=0;
digitalWrite(ledPin, LOW);
}
Serial.println(millisRestan);
}
}
Una vez añadidas todas las funcionalidades al loop, si se siguen produciendo rebotes, tan sólo será cuestión de añadir al final del loop un delay de duración adecuada.
noter:
void loop() {
unsigned long tiempoAgregado=0;
if (digitalRead(moneda1) == HIGH) tiempoAgregado+=tMoneda1;
if (digitalRead(moneda2) == HIGH) tiempoAgregado+=tMoneda2;
Ves.. Esta es la diferencia entre quien programa (vosotros y en este caso noter) y los que somos unos pipiolos y aprendices, que por situaciones de la vida no hemos podido tener una buena base de programación.
Yo para implementar la segunda moneda había copiado toda la sección del primer if.
Todos hemos comenzado de cero. Aprender a programar mejor sólo se consigue viendo (y comprendiendo, no sólo copiar y pegar) códigos de otros (y a veces autocriticando los propios).
Por ello me gusta más proponer (y ver propuestas de) alternativas a códigos que considero mejorables que dar solución directa a quien sólo quiere que le hagan su trabajo.
Por ello siempre pedimos a quien viene pidiendo ayuda que muestre lo que está intentando. Para tomar el punto desde el que ayudar a avanzar.