Felix Maocho

Para quien le interese lo que a nosotros nos interesa

5º Ejercicio de uso de pines digitales de entrada INPUT– Linterna multiuso – 2º Una posible solución

linterna-multiusoPor Félix Maocho
2/2/2017

Este post es ´continuacion del anterior sobre el mismo tema
5º Ejercicio de uso de pines digitales de entrada INPUT– Linterna multiuso – 1º Exposición de los problemas Donde se exponía un problema que planteaba una serie de problemas que resolvemos aquí

En el capítulo anterior habíamos construido un software y el hardware que mostramos a la derecha. y encontrado tres motivos de error en el software que habíamos escrito inicialmente que eran los siguientes.

  • 1º) Los leds se encendían y apagaban innecesariamente cada vez que se ejecutaba un “loop” del programa
  • 2º) La maquina entra en “catalepsia” el tiempo que dura una intermitencia de la luz roja, impidiendo observar si en ese tiempo se pulsa el botón
  • 3º) La máquina no era capaz de diferenciar entre una pulsación larga y varias pulsaciones seguidas.

Recordamos el esquema de lo que hacia el software mientras que le hard lo encuentran en la imagen que abre el post

* Sketch E7.01 41 Linterna multiuso ( erróneamente programada)>
* Inicialmente contador = 0,
* Probar si se ha apretado el el botón (PIN 2)
* Si se ha apretado, sumar uno al valor del contador
* Si contador = 0 – No hacer nada
* Si contador = 1 – Encender un Led blanco (PIN 4)
* Si contador = 2 – Encender un segundo Led blanco. (PIN 7)
* Si contador = 3 – Encender un tercer Led blanco (PIN 8)
* Si contador = 4 – Apagar los leds blancos y Encender intermitentemente el Led rojo (PIN 12)
* Si contador = 5 – Apegar el led rojo
Hacer contador = 0
Repetir el ciclo
*/

Daré una solución, pero como recuerdo con frecuencia,, ni mucho menos es la única solución posible, ni hay medio que nos indique que sea la mejor.Posiblemente haya soluciones más practicas, sencillas, eficientes y económicas en el uso de los recursos utilizados que me hayan pasado inadvertidas. Yo creo haber encontrado soluciones eficientes y prácticas pero si otro encuentra soluciones o muy diferentes o mas sencillas ruego que me lo haga saber.

Resolución de los errores

1º) Hacer que los leds se encendían y apagaban sólo una vez

Al detectar una pulsación se ejecuta la opción del menú correspondiente, que enciende y apaga determinados leds, pero una vez hecho, se busca la forma de impedir que en sucesivos “loop” sin que se hayan percibido mas pulsaciones se vuelva a repetir encender y apagar los mismos led.

La solución es llevar el control de los “loops” mediante variable que llamaremos “primeravez” inicializada a cero, que la detectar que hemos pulsado el botón toma el valor 1. Solo si “primeravez” vale 1 se ejecutan las opciones de menú y una vez ejecutadas, nuevamente se vuelve a dar el valor 0 a “primeravez” para que no repita este trabajo en los siguientes “loops”.

El esquema del programa sera por tanto algo así como

* delay (rebote)
* Ver si han apretado el botón
* Si han apretado el botón
* Aumentar el contador
* Poner la variable “primeravez” a 1
* Si “primeravez” es 1
* Encender y apagar leds en función del valor del contador
* Poner la variable “primeravez” a 0 para no repetir
* Si el contador es la opción 4
* Encender y apagar el led rojo una vez
* Repetir el proceso

Observen que separo la labor de apagar y encender los leds (incluido el led rojo) de la labor de provocar la intermitencia, porque la primera labor hay que hacerla solamente una vez, mientras que la intermitencia hay que mantenerla en tanto este vigente la opción de menú 4. “encender el led rojo intermitentemente”

Con la introducción de la variable “primeravez” tenemos una forma sencilla de diferenciar los “loop” que corresponden a la detección de una pulsación de los siguientes “loop” que se ejecuten con la misma opción de menú, con lo que resolvemos el problema.

2º) “Catalepsia” provocada por los “delay” de la intermitencia de la luz roja

Como avisamos en múltiples lugares, el uso de los “delay” son siempre peligrosos, pies hacen entrar el software en una especie de letargo invernal, razón por la cual cualquier otra acción que deseemos realiza en otro punto del “loop” no se realiza.

El problema en nuestro caso es que queremos hacer dos cosas a la vez, por un lado queremos generar una intermitencia en un led y por otro queremos estar atentos a que realicen una pulsación en el “pushbutton”. Es decir queremos hacer dos cosas a la vez.

Se cuenta del Presidente Ford, que alcanzó la Presidencia de Norteamérica de carambola, pues llegó a la presidencia por escalafón, debido a la dimisión de Nixon, que era tan simple, que no podía hacer dos cosas a la vez , como andar y macar chicle. Pues bien, lo que nosotros estamos tratando de hacer, son dos cosas a la vez, como el Presidente Ford, parpadear un led rojo, y vigilar si aprietan el botón, y siento informarte, que la Tarjeta Arduino es tan simple como el Presidente Ford, no puede hacer dos tareas simples a la vez, porque es monotarea y como eso indica, sólo puede hacer una tarea.

Simulación de multitarea

Pero hasta el Presidente Ford, hubiera podido dar un paso, a continuación dar una mascada al chicle, y seguir con otro paso, porque eso es realizar una sola tarea, en cada momento o da un paso o masca chicle y si lo hace con la debida rapidez y buen ritmo, al espectador le parecerá que el Presidente Ford, anda y mastica chicle a la vez, es decir simula que es multitarea, cuando la realidad es que, sólo hace una cosa detrás de otra, aunque sea muy deprisa.

Lo mismo pasa en informática, hasta que recientemente se han generalizado los procesadores con varios núcleos o “cores”, no importaba que tan grande y potente fuera el ordenador, tan solo podían hacer una sola cosa a la vez, pues solo tenían un procesador, y lo dedicaban a esto, o a lo otro, pero no a dos cosas a la vez, pero sin embargo todos hemos utilizado ordenadores, que por ejemplo, te permiten escribir, mientras la impresora imprime, o te dejan a la vez trabajar en una página de Excel y oír música en segundo plano.

¿Cómo lo hacían?

Pues igual que el Presidente Ford, haciendo una tarea, parando esa tarea y continuando otra, o sea avanzando todas las tareas un pasito cada vez, así hasta acabar con todas y volver a empezar. Esto es lo que hacen los PC’s internamente, pero a tal velocidad, que oyes la música sin que te des cuenta que se para entre nota y nota y es lo que vamos a hacer nosotros a mano, simular que nuestra Tarjeta Arduino es multitarea.

Para ello, vamos a repartir los bucles del programa entre las dos tareas, los bucles pares, los vamos a destinar a vigilar si aprietan el botón y los impares, a encender y apagar el led rojo, pero claro, necesitamos que tanto un bucle como otro, transcurran a alta velocidad para que el truco no se note.

En los bucles pares, no hay problema, pues sólo tenemos un “delay” muy pequeño, (rebote), pero en el segundo si estamos en la opción de menú 4, tenemos dos “delay” largos, (parada), y eso no lo podemos permitir, si pretendemos simular multitarea, necesitamos que este bucle sea también muy rápido.

¿Que podemos hacer?

Pues muy sencillo, dividir la tarea que tarda, entre tantos bucles consecutivos, que pase desapercibida. Como en el bucle par ya hay un pequeño “delay”, en el segundo bucle vamos a sustituir los “delay” existentes , por un contador que vaya sumando los tiempos que se pierden en el bucle par, y cundo el contador llegue a cierto valor, (en nuestro caso “tiempo “apagamos el led rojo y así lo tenemos hasta que el cont6ador alcance el doble de “tiempo” momento en que habremos acabado la intermitencia e iniciaremos la siguiente. Para controlar le “loop” que se ejecuta esto añadimos una nueva variable de control que llamaremos “parimpar”

Sobre el esquema anterior añadiremos estas nuevas condiciones. El nuevo esquema será:

* Si “parimpar” es cero Ver si han apretado el botón
* delay (rebote)
* Ver si han apretado el botón
* Si han apretado el botón
* Aumentar el contador
* Poner la variable “primeravez” a 1
* Si “primeravez” es 1
* Encender y apagar leds en función del valor del contador
* Poner la variable “primeravez” a 0 para no repetir
* Hacer parimpar igual a 1 para que la próxima vez haga el otro bucle
* Si “parimpar” es cero
* Si el contador es la opción 4 atender a la intermitencia
* Sumar a “contador_de_ tiempos” la cantidad “rebote”
* Si “contador_de_ tiempos” es igual que “tiempo”
* Apagar el led rojo
* Si “contador_de_ tiempos” es igual que el doble de “tiempo”
* Encender el led rojo
* Poner el “contador_de_ tiempos” a 0
* Hacer parimpar igual a 0 para que la próxima vez haga el otro bucle
*Repetir el proceso

De esta forma conseguimos que cada bucle se dedique a cosas diferentes y como hemos conseguido hacer que los bucles transcurran muy rápido, parecerá que hacemos dos cosas a la vez. El problema esta resuelto, solo que poco a poco estamos ampliando el numero de variables y la complejidad del programa, pero a mi me parece inevitable.

3º) Diferenciar las pulsaciones del botón

Abordamos por fin el último de los problemas. La primer y fundamental causa de fallo, que el “loop” transcurre a gran velocidad, Si se fijan ahora que hemos eliminado los dos “delay” que había en la intermitencia roja, tan solo nos queda una mínima parada de una décima de segundo. Algo absolutamente mínimo para que el dedo apriete y se levante del botón.

Para obviar este problema se me ocurren dos soluciones, La primea es hacer una pausa o “delay” de determinado tiempo cuando se detecte que se ha apretado el botón, para dar tiempo a que se retire el dedo, o bien otra posibilidad es detectar cuando se ha levantado el dedo y mientras no se levante, considerar sólo una pulsación, esté el dedo el tiempo que esté pulsando continuadamente el botón.

La primera solución a priori parce ms sencilla , sólo se trata de instalar un nuevo ·”deláy” y tiene la ventaja sobre la segunda que si el usuario quiere ir aumentando la intensidad de la luz, le basta con mantener pulsado el botón, pues pasado un tiempo, el sistema automáticamente pasa a la siguiente opción de menú.

Posiblemente concluido el aparato tendremos que hacer unas pruebas, hasta encontrar un valor para la “pausa” suficientemente largo, para retirar el dedo con tranquilidad, pero que no sea excesivo y haga el cambio de opción de menú muy pesado. Calculo que unos pocos experimentos variando el valor de “pausa” serán suficientes para encontrar un tiempo idóneo.

El problema es que si hemos hecho una modificación para eliminar dos “delays” que había por ahí, ¿No estaremos creando otra dificultad al poner un nuevo “delay” por el medio?.

En este caso no tendremos, como ocurría en el caso anterior, que preocuparnos con el tiempo de ejecución del bucle, pues “delay (pausa)” sólo se produce en el bucle que ha detectado la pulsación y después de encender y apagar las luces que corresponda, por tanto sólo se ejecuta una vez por opción de menú y sólo retrasara algo la ejecución de la opción 4, que es la que pone la intermitencia del led rojo y sólo afectará a la primera intermitencia. Incluso podemos compensar este problema, dando al “contador_de_ tiempos” que mide el tiempo de la intermitencia un valor inicial que compense esta pausa, pero creo que es algo sin importancia en este caso.

Macho cuidado con los “delay”

Como ven con los “delay” puede pasar de todo y hay que estudiar en cada caso las consecuencias que para el programa puede tener “anestesiar” su funcionamiento durante un tiempo.

No existe una regla fija. En este ejemplo hemos visto que en un sitio, “delay (rebote)”, pese a actuar en cada “loop impar”. es tan corto que no interfiere en el tiempo de ejecución del “loop”, pero en cambio, esta miniparada cuenta en los “loop pares” para acumular el tiempo de las intermitencias. Por tanto, de otra forma, hay que tenerlo en cuenta.

En cambio el “delay (tiempo)” que regulaba las intermitencias, era inaceptable y fue sustituido por la suma de los tiempos de  “delay (rebote)” realizados en los sucesivos “loop par”. Por último el “delay (pausa)”, que vamos a colocar para dar tiempo de retirar el dedo, pese a ser claramente perceptible, no molesta en este caso, pues sólo se ejecuta una vez por pulsación.

Como digo, no puedo daros una regla de actuación porque no la hay, pero es algo que debemos estudiar en cada caso, y cuando un programa a vuestro juicio está “perfecto”, pero no funciona como se esperaba, o peor aún, funciona unas veces si y otras no, que de todo lo que nos puede pasar, lo más peligroso, observar si hay un “delay” y revisar que no pueda ser la causa del mal funcionamiento del programa.

Veamos por fin como queda el esquema del programa definitivo incorporando esta última modificación:

* Si “parimpar” es cero Ver si han apretado el botón
* delay (rebote)
* Ver si han apretado el botón
* Si han apretado el botón
* Aumentar el contador
* Poner la variable “primeravez” a 1
* Si “primeravez” es 1
* Encender y apagar leds en función del valor del contador
* Poner la variable “primeravez” a 0 para no repetir
* Hacer un delay para poder retirar el dedo del botón.
* Hacer parimpar igual a 1 para que la próxima vez haga el otro bucle
* Si “parimpar” es cero
* Si el contador es la opción 4 atender a la intermitencia
* Sumar a “contador_de_ tiempos” la cantidad “rebote”
* Si “contador_de_ tiempos” es igual que “tiempo” Apagar el led rojo
* Si “contador_de_ tiempos” es igual que el doble de “tiempo” Encender el led rojo
* y Poner el “contador_de_ tiempos” a cero
^ Hacer parimpar igual a 0 para que la próxima vez haga el otro bucle

Es curioso que el problema que a primera vista, parecía el más complejo mas complejo, se ha resuelto únicamente en este caso añadiendo una sola línea de código, pero así es la informática hasta que no te metes a resolver un problema no tienes ni idea de la complejidad que supone.

Procedamos pues a dar escribir el nuevo código una vez tenido en cuenta todos sus errores y pasemos a probarlo,

* Sketch E7.01 42 Linterna multiuso (Con pausa para retirar el dedo?
* Inicialmente contador = 0,
**************** loop par ******************************
* Si “parimpar” es cero Ver si han apretado el botón (pin 2)
* delay (rebote)
* Ver si han apretado el botón
* Si han apretado el botón
* Aumentar el contador
* Poner la variable “primeravez” a 1
* Si “primeravez” es 1
************Encender y apagar leds en función del valor del contador
* Si contador = 0 – No hacer nada
* Si contador = 1 – Encender un Led blanco (PIN 4)
* Si contador = 2 – Encender un segundo Led blanco. (PIN 7)
* Si contador = 3 – Encender un tercer Led blanco (PIN 8)
* Si contador = 4 – Apagar los leds blancos
* Encender intermitentemente el Led rojo (PIN 12)
* Si contador = 5 – Apegar el led rojo poner j a 0
****
* Poner la variable “primeravez” a 0 para no repetir
* Hacer un delay para poder retirar el dedo del botón.
* Hacer parimpar igual a 1 para que la proxima vez haga el otro bucle
**************** loop impar ******************************
* Si “parimpar” es cero
* Si el contador es la opcion 4 atender a la intermitencia
* Sumar a “contador_de_ tiempos” la cantidad “rebote”
* Si “contador_de_ tiempos” es igual que “tiempo”
* Apagar el led rojo
* Si “contador_de_ tiempos” es igual que el doble de “tiempo”
* Encender el led rojo
* Poner el “contador_de_ tiempos” a 0
* Hacer parimpar igual a 0 para que la proxima vez haga el otro bucle
* Repetir el ciclo
*/
// Área de definición de variables y parámetros
int pinled1 = 4; // pin en OUTPUT para el LED ! (blanco)
int pinled2 = 7; // pin en OUTPUT para el LED 2 (blanco)
int pinled3 = 9; // pin en OUTPUT para el LED 3 (blanco)
int pinledrojo = 12; // pin en OUTPUT para el LED ROJO
int pininp = 2; // pin que se abre en INPUT
int parimpar = 0; // Si 0 hacer lop par si1 loop impar
int corriente = 0; // Si hay 5V toma el valor 1 , si hay 0V, 0
int primeravez = 0; // Si 1 es el primer loo`p Si 0 no es el primer loop
a 5 de las veces que se pulsa el botón
int k = 0; // Contador de 0 al doble de “tiempo” contador de tiempos
int pausa = 500; // tiempo para retirar el dedo del botón
int tiempo = 1000; // intermitencia encendida 2 segundos
int dobletiempo = 2000; // duracion total de la intermitencia 4”
int rebote = 100; // tiempo para evitar los posibles en la medición
// Función setup
void setup() {
pinMode( pinled1, OUTPUT); // enciende led 1
pinMode( pinled2, OUTPUT); // enciende led 2
pinMode( pinled3, OUTPUT); // enciende led 3
pinMode( pinledrojo, OUTPUT); // enciende led rojo
pinMode( pininp, INPUT); // Pin abierto como INPUT, lee el voltaje
}
// Función loop
void loop() {
// **************** loop par ******************************
if (parimpar == 0) {
// * Ver si han apretado el botón (pin 2)
delay (rebote) ; // Retardo para evitar “rebote” de los interruptores
corriente = digitalRead(pininp); // 1º comprobar el voltaje
// * Si han apretado el botón (corriente == 1)
// * Aumentar el contador
//* Poner la variable “primeravez” a 1
if (corriente == 1) {
j = j + 1; // Se suma al contador j una unidad
primeravez =1 ;
} // Fin (corriente == 1)
// * Si “primeravez” es 1
// ************Encender y apagar leds en función del valor del contador
// * Si contador = 0 – No hacer nada
// * Si contador = 1 – Encender un Led blanco (PIN 4)
// * Si contador = 2 – Encender un segundo Led blanco. (PIN 7)
// * Si contador = 3 – Encender un tercer Led blanco (PIN 8)
// * Si contador = 4 – Apagar los leds blancos
// * Encender intermitentemente el Led rojo (PIN 12)
//* Si contador = 5 – Apegar el led poner j a 0
if (primeravez == 1 ) {
if (j == 1) { digitalWrite(pinled1, HIGH); }
if (j == 2) { digitalWrite(pinled2, HIGH); }
if (j == 3) { digitalWrite(pinled3, HIGH); }
if (j == 4) {
digitalWrite(pinled1, LOW);
digitalWrite(pinled2, LOW);
digitalWrite(pinled3, LOW);
digitalWrite(pinledrojo, HIGH); // Encender rojo
}
if (j == 5) { digitalWrite(pinledrojo, LOW);
j = 0; // restaurar a cero los contadores
k = 0;
}
// *****
//* Hacer un delay para poder retirar el dedo del botón
//* Poner la variable “primeravez” a 0 para no repetir
delay (pausa); // parada para retirar el dedo del botón
primeravez = 0 ; // Para evitar repetirlo en otro bucles
} // Fin if (primeravez == 1 )
parimpar = 1;
} // Fin de loop par
// **************** loop impar ******************************
else {
// * Si el contador es la opcion 4 atender a la intermitencia
if (j == 4) {
k = k + rebote ; // Sumar a k “rebote”
if (k== tiempo) { digitalWrite(pinledrojo, LOW); }
if (k == dobletiempo) { digitalWrite(pinledrojo, HIGH); k = 0; }
} // Fin if (j == 4)
parimpar = 0;
} // Fin if else parimpar
} // Fin funcion loop

Les dejo una película que hecho del funcionamiento en el simulador UnoArduSim, La he hecho a velocidad normal y sin seguimiento del programa por tanto la parte de la izquierda de la pantalla del simulador no añade nada, Por eso me he centrado solo en la parte derecha.

Primero voy apretando cinco veces seguidas el botón para que se vea como se van encendiendo los leed hasta llegar a la cuarta opción que apaga los leds amarillos y enciende el led rojo. A contimuación mantengo el botón apretado hasta conseguir que se enciendan dos led y posteriormente,  enciendo el tercero.

Poner a punto este programa tan largo no es cosa que ocurra en un primer intento. Yo incialmente lo escribo tal como a mi me parece correcto e intento compilarlo. Lo habitual es que el compilador me devuelva errores de sintaxis, es decir errores que el compilador descubre porque las cosas están escritas de forma indebida y no las entiende. ¿Cuales son esos errores, pues pueden ser muchos, voy a poner una lista de los mas habituales.

  • Olvidar los signos punto y coma “:” al final de una linea.
  • Utilizar mayúsculas en lugar de minúsculas y viceversa, para la máquina “If” no es “if”.
  • Que el número de paréntesis abiertos “(“ sea diferente que el número de pqréntesis cerrados ”)”
    o analogamente que sea diferente el numero de corchetes abie5rtos y cerrados “{” y “}”.
  • Olvidar alguna “//” antes de un comentario o que solo hayamos puesto “/”.
  • Escribir una variable o no declarada en el area de variables o declarada con otra grafía.
  • Oner un sol  “=”  cuando queremos hacer una comparacion y debiamos haber puesto   “==”

Por supuesto esta no es una lista exaustivas, hay mas como mala sintaxis de las funciones y otros errores como intentar hacer operaciones con variables definidas como alfabéticas y cosas parecidas,cosas.

A medidfa que vamos compilando la compilacion se detiene en una linea dque detecta erronea  y lanza un aviso, que unas veces es acertado y otras te introduce mas problemas porque es confuso o la m`´aquina se ha confundido , por ejemplo si en un comentario no has puesto // posiblemente trate de hacer alf go con el comentario y te diga que esa variable no la tienes defindo en vez que faltan los “//”. Otra veces al llegar a un punto dice que los parentesis abiertos no coiciden con los cerrados, pero no es en ese puntyo sino muchas lineas mas arriba que olvidaste cerrar un partentesis, Por tanto los mensajes tenemos que tomarlos como error probable pero no como un dogma de fe, unas feces (las mas) acierta y otras /(las menos) fracas, y nos confunde mas.

 

Conseguido que compile todfo el texto, hemos eliminado el 90% de los errores pero tambien es cierto los errores mas obvios de eliminar, porque aqui comienza lo dificil ver si el programa actúa como estaba previsto y veremos habitualmente como la Ley de Murfi, que dice aquello de  que “Si algo puede ir mal, ira mal” sie cumple con una regularidad matemática.

Por ello llo voy probando los programas por partes; en concreto este le he probado en cuatro trozos.

1º) Sistema “parimpar”  Ver que cada loop se ejcutan sucesivamente

Para ello deje la zona de variables y la funciíon SETUP y en elsimuylador UnoArduSim probe una funcion loop como la que escribo.

// Función loop
void loop() {
// **************** loop par ******************************
if (parimpar == 0) {
delay ( 100) ;
parimpar = 1;
} // Fin de loop par
// **************** loop impar ******************************
else {
delay ( 100) ;
parimpar = 0;
} // Fin if (parimpar == 1)
} // fin funcion loop

Observara que solo es el proceso de ejecuciíon “loop `par /loop impar”. Como ven instale uunos Delay provisionales para rtelentizar el progrrama y poder observar con comodidad si se cumplian sucesivamente.

Pues en este programa tan simple había cometido un error. Inicialmente habia puesto en la primera parte “if (parimpar == 0)” y a continuación “if (parimpar == 1)” aparente mente tenia que ir bien entba con un valor se hacia lo que correpondíia al “loop” y al final cambiaba el valor para que a la vez siguiente se hiciera el otro “loop”.

Pues se me escapó que si en la ultima fila de el prime “if (parimpar == 0) hago “parimpar = 1; ” al entraen el “if (parimpar == 1)” “parimpar vale realmente 1 y se meterñá a hacer ese if cuando mi desero era que hiciera un nuevo “loop” , Si ponemos en cambio un “else” si se cumple lo que quiero pues o se hace una serie de instrucciones o la otra.

Pues bien este tipo de errores que noson ortográfico ni sintacticos, sino simple y duramente de c programación los (actuales>) compiladores no los di ¡etectan pues no son inteligentes, y si lo que escribimos se puede hacer no tiene inteligencia para saber si lo que hacen tiene sentido o es un pampirolada.

2º Probar el “lop impar”

De ambas partes del programa la segunda me parecía la mas sencilla por lo que la probé en primer lugar.  Trataba de ver si el sistema diseñado de intermitencia probaba como yo lo esperaba. Añadi a lo que ya funcionaba anteriormente la parte del “·loop  impar”, puesto que todo lo demás ya funcionaba bien, si algo iba mal, tenía que ser lo que probaba. Em este caso cambié la funcion “loop” por la siguiente:

// Función loop
void loop() {
// **************** loop par ******************************
if (parimpar == 0) {
if (j == 0) {
j=4 ;
digitalWrite(pinledrojo, HIGH);
delay ( 100) ;
} // Fin if (j == 0)
parimpar = 1;
} // Fin de loop par
// **************** loop impar ******************************
else {
// * Si el contador es la opcion 4 atender a la intermitencia
if (j == 4) {
k = k + rebote ; // Sumar a k “rebote”
if (k== tiempo) { digitalWrite(pinledrojo, LOW); }
if (k == dobletiempo) { digitalWrite(pinledrojo, HIGH); k = 0; }
} // Fin if (j == 4)
parimpar = 0;
} // Fin if else parimpar
} // Fin funcion loop

Observan que en el primer loop  cuando aun “j” vale 0, doy a j el valor 4  Por tanto nunca mas hara este “if” pero ya de paso aprovecho para encender por primera c vez el led rojo y como ante pongo una paradita para ver que pasa por estepunto solo una vez. Eb kis loop para siguentes no hara mas que poner “parimpar” a 1, Mientras que en los pares poco a poco ira aumentando el valor de k hata llegar a “tiempo” (=2000)  donde apagara el led rojo y a “dobletiempo” (=400o) donde vulve a encender el led rojo y pone nuevamente “k” a 0 , dando por acabada la intermitencia.

Esta parte del programa funcionaba muy bien sólamente me parecio que los dos segundos calculados era un poco lento y lo cambi a un segundo, o sea “tiempo” es igual a 1000 y “dobletimepo” es 2000. Anuque se cumplía descubri que hay algo que hay que tenr en cuenta que “tiempo” y “dobletiempo” han de ser múltiplos exactos de “rebote” y k comenzar con valor = porque si no, nunca se cumpliran las condiciones  “(k== tiempo)” y  (k == dobletiempo)

Otra cosa que quiza os extrañe es que aqui he escrito las funciones “if” en una sola línea de la siguiente manera

if (k== tiempo) { digitalWrite(pinledrojo, LOW); }

 Cundo hast ahora lo habíamos escrito más o meno asi

if (k== tiempo)
{ digitalWrite(pinledrojo, LOW);
}

Ambas formas de escribir son correctas, el compilador lo primero que hace es quitar saltos de linea y espacios en blanco repetidos, el solo entiendo un gigantesco churo de mandatos separdos por “;” y agrupados por “(”  “)” y por “{” “}”, nada mas, Todfo lo otro es para los humanos que somos muy lerdos y necesitamnos las cosas muy claras, por eso si es claro, fundameentalmente porque cabe en una linesa, lo escribiremos como està en priimer lugar , pero si en la agrupacion hay muchos mandatos resulta mas claro de comprensión escribirlo en varias lineas y a ser posible que mi editor de word press no me deja con pequeños indentados que indiquen lo que abarca cada agu rupacion de mandatos.

3º Lectura del pulsador

Probamos si funciona bien la lectura del pulsador y si la pausa que hemos puesto es suficiente 7y no interrumpe el programa. La funció “loop” anterior motamos lo referente a la lectura del pulsador y lo que se hace si se observa que se ha pulsado.

 

// Función loop
void loop() {
// **************** loop par ******************************
if (parimpar == 0) {
// * Ver si han apretado el botón (pin 2)
delay (rebote) ; // Retardo para evitar “rebote” de los interruptores
corriente = digitalRead(pininp); // 1º comprobar el voltaje
// * Si han apretado el botón (corriente == 1)
// * Aumentar el contador
//* Poner la variable “primeravez” a 1
if (corriente == 1) {
j = j + 1; // Se suma al contador j una unidad
primeravez =1 ;
} // Fin (corriente == 1)
delay (100 ) ;
parimpar = 1;
} // Fin de loop par
// **************** loop impar ******************************
else {
// * Si el contador es la opcion 4 atender a la intermitencia
if (j == 4) {
k = k + rebote ; // Sumar a k “rebote”
if (k== tiempo) { digitalWrite(pinledrojo, LOW); }
if (k == dobletiempo) { digitalWrite(pinledrojo, HIGH); k = 0; }
} // Fin if (j == 4)
parimpar = 0;
} // Fin if else parimpar
} // Fin funcion loop

Introfuzco además un pequeño “delay” al final del “loop par” para tener tiempo de ver si “j” ha cambiado de tiempo. No es fácil calcular si el tiempo. Todo el proceso transcurrio sin dificultad por lo que pasé directamente a el último ensayo con todas las fuhncionalidadessegún el valor de “j” colocadas.

4º Prueba final 

El software del programa al completo, probado y comprobado, listo para que Vd lo copie y pegue y comience a hacer pruebas y modificaciones-

/* Sketch E7.01 42 Linterna multiuso (Con pausa para retirar el dedo?
* Inicialmente contador = 0,
**************** loop par ******************************
* Si “parimpar” es cero Ver si han apretado el botón (pin 2)
* delay (rebote)
* Ver si han apretado el botón
* Si han apretado el botón
* Aumentar el contador
* Poner la variable “primeravez” a 1
* Si “primeravez” es 1
************Encender y apagar leds en función del valor del contador
* Si contador = 0 – No hacer nada
* Si contador = 1 – Encender un Led blanco (PIN 4)
* Si contador = 2 – Encender un segundo Led blanco. (PIN 7)
* Si contador = 3 – Encender un tercer Led blanco (PIN 8)
* Si contador = 4 – Apagar los leds blancos
* Encender intermitentemente el Led rojo (PIN 12)
* Si contador = 5 – Apegar el led rojo poner j a 0
****
* Poner la variable “primeravez” a 0 para no repetir
* Hacer un delay para poder retirar el dedo del botón.
* Hacer parimpar igual a 1 para que la proxima vez haga el otro bucle
**************** loop impar ******************************
* Si “parimpar” es cero
* Si el contador es la opcion 4 atender a la intermitencia
* Sumar a “contador_de_ tiempos” la cantidad “rebote”
* Si “contador_de_ tiempos” es igual que “tiempo”
* Apagar el led rojo
* Si “contador_de_ tiempos” es igual que el doble de “tiempo”
* Encender el led rojo
* Poner el “contador_de_ tiempos” a 0
* Hacer parimpar igual a 0 para que la proxima vez haga el otro bucle
* Repetir el ciclo
*/
// Área de definición de variables y parámetros
int pinled1 = 4; // pin en OUTPUT para el LED ! (blanco)
int pinled2 = 7; // pin en OUTPUT para el LED 2 (blanco)
int pinled3 = 9; // pin en OUTPUT para el LED 3 (blanco)
int pinledrojo = 12; // pin en OUTPUT para el LED ROJO
int pininp = 2; // pin que se abre en INPUT
int parimpar = 0; // Si 0 hacer lop par si1 loop impar
int corriente = 0; // Si hay 5V toma el valor 1 , si hay 0V, 0
int primeravez = 0; // Si 1 es el primer loo`p Si 0 no es el primer loop
int j = 0; // Contador de 0 a 5 de las veces que se pulsa el botón
int k = 0; // Contador de 0 al doble de “tiempo” contador de tiempos
int pausa = 500; // tiempo para retirar el dedo del botón
int tiempo = 1000; // intermitencia encendida 2 segundos
int dobletiempo = 2000; // duracion total de la intermitencia 4”
int rebote = 100; // tiempo para evitar los posibles en la medición
// Función setup
void setup() {
pinMode( pinled1, OUTPUT); // enciende led 1
pinMode( pinled2, OUTPUT); // enciende led 2
pinMode( pinled3, OUTPUT); // enciende led 3
pinMode( pinledrojo, OUTPUT); // enciende led rojo
pinMode( pininp, INPUT); // Pin abierto como INPUT, lee el voltaje
}
// Función loop
void loop() {
// **************** loop par ******************************
if (parimpar == 0) {
// * Ver si han apretado el botón (pin 2)
delay (rebote) ; // Retardo para evitar “rebote” de los interruptores
corriente = digitalRead(pininp); // 1º comprobar el voltaje
// * Si han apretado el botón (corriente == 1)
// * Aumentar el contador
//* Poner la variable “primeravez” a 1
if (corriente == 1) {
j = j + 1; // Se suma al contador j una unidad
primeravez =1 ;
} // Fin (corriente == 1)
// * Si “primeravez” es 1
// ************Encender y apagar leds en función del valor del contador
// * Si contador = 0 – No hacer nada
// * Si contador = 1 – Encender un Led blanco (PIN 4)
// * Si contador = 2 – Encender un segundo Led blanco. (PIN 7)
// * Si contador = 3 – Encender un tercer Led blanco (PIN 8)
// * Si contador = 4 – Apagar los leds blancos
// * Encender intermitentemente el Led rojo (PIN 12)
//* Si contador = 5 – Apegar el led poner j a 0
if (primeravez == 1 ) {
if (j == 1) { digitalWrite(pinled1, HIGH); }
if (j == 2) { digitalWrite(pinled2, HIGH); }
if (j == 3) { digitalWrite(pinled3, HIGH); }
if (j == 4) {
digitalWrite(pinled1, LOW);
digitalWrite(pinled2, LOW);
digitalWrite(pinled3, LOW);
digitalWrite(pinledrojo, HIGH); // Encender rojo
}
if (j == 5) { digitalWrite(pinledrojo, LOW);
j = 0; // restaurar a cero los contadores
k = 0;
}
// *****
//* Hacer un delay para poder retirar el dedo del botón
//* Poner la variable “primeravez” a 0 para no repetir
delay (pausa); // parada para retirar el dedo del botón
primeravez = 0 ; // Para evitar repetirlo en otro bucles
} // Fin if (primeravez == 1 )
parimpar = 1;
} // Fin de loop par
// **************** loop impar ******************************
else {
// * Si el contador es la opcion 4 atender a la intermitencia
if (j == 4) {
k = k + rebote ; // Sumar a k “rebote”
if (k== tiempo) { digitalWrite(pinledrojo, LOW); }
if (k == dobletiempo) { digitalWrite(pinledrojo, HIGH); k = 0; }
} // Fin if (j == 4)
parimpar = 0;
} // Fin if else parimpar
} // Fin funcion loop

Aun haciendo esta prueba descubrí que si pulsaba la quinta 5 vez, como estaba previsto cesaban las intermitencias pero que “k” se quedaba con el valor que tuviera en ese momento, lo que ocasionaba que de volver a llevar la linterna al menú 4 la intermitencia empezaba a medias, La verdad es que el problema no tenia excesiva importancia salvo que la primera intermitencia pudiera tener el tiempo encendido o muy breve o muy largo en función de lo que valiera “k” en ese momento, pero por si acaso, añadí inicializar la “k” a cero  a las tareas a hacer en la opcion 5.

Como digo hay muchas soluciones a los problemas planteados y esta no es mas que una de ellas. Por ejemplo , y quizá hubiera sido más sencillo, dedicar medio buclue a estudiar si se ha apretado el pulsador y el otro medio acontinuación a la intermitencia hecha en múltiples bucles de mos do que el bucle completo hubiera sido rápido, Posiblemente la cosa hubiera funcionado igual y de forma mas sencilla. El encontrar la forma mas sencilla de sresolver el miwsmo problema es lo que diferencia un buen programador de uno malo. Les dejo la labor de resolver este problema atendiendo a ambas tareas en un solo bucle.

Como ya indicamos una segunda forma de detectar las pulsaciones, es controlar cuando se deja de pulsar, de esta forma se delimita perfectamente cada pulsación independientemente del tiempo que se mantenga el dedo en el pulsador.

Así pues como sabemos cuando se ha iniciado la pulsación por la variable “primeravez” si esta con valor 1 y se detecta que el voltaje sigue siendo 5 voltios, (“corriente” igual 1), es que se continua apretando, Cuando la variable, “primeravez” tenga el valor 1 y se detecte que el voltaje es cero, (“corriente” igual 0) se ha terminado de apretar y por tanto se inicia la posibilidad de que aprieten de nuevo porque quieran cambiar la opción de menú.

Así pues pueden ocurrir las siguientes circunstancias   segun los valores de “primeravez” y “corriente ”  y la accion a tomar

  • 0 – 0 No se ha pulsado. No hacer nada.
  • 0 – 1 Se ha pulsado, Hacer lo que corresponda segun el valor de “j”.
  • 1-  0 Se ha soltado el pulsador, Preparar todo para una nueva pulsación.
  • 1 – 1 Sigue la misma pulsación , No hacer nada.

Esto s es lo que se denomia un árbol de decisiónes, todas las actividades que hay que tomar en funcion de varias circustancias concurrentes.. En este caso son solo dos las circunstancias los valores de “corriente” y “primeravez” pero hay caso que entran tres o cuatro y las actividades pueden ser muchas diferentes. El estudio detallado de los arboles de decisiones debe ser anterior al diseño del programa en la fase que se denomina Analisis Funcional ypues en en función de lo que aquí resulte que dse diseñan los programas. que se

Aquí tienen otro ejercicio que pueden intentar hacer  Será un poco lioso don una mezcla de  “if” anidados,y consecutivos y “if/else”, pero a mi juicio nada que no tengan ya conocimientos suficientes para poder resolver. Les dejo el esquema del programa a realizar

Vemos como lo hacemos

/* Sketch E7.01 43 Linterna multiuso (con deteccion de fin de pulsación)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Si “parimpar“ es 0 , hacemos el bucle par controlar el botón
* delay (rebote) ; // Retardo para evitar “rebote” de los interruptores
* Pueden ocurrir una de estas cuatro opciones
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Si primervez es 0 y corriente es 0 No se apretó el pulsador
* Si primervez es 0 y corriente es 1 Se apretó el pulsador
* Si primervez es 1 y corriente es 0 Se soltó el pulsador
* Si primervez es 1 y corriente es 1 Se sigue apretando el pulsador
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 0 0 No se apretó el pulsador – No hacer nada
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 0 1 Se apretó el pulsador – Avanzar en el menu
* Aumentar el contador j
* Poner la variable primeravez a 1
* Encender y apagar leda en función del valor de j
* Si contador = 0 – No hacer nada
* Si contador = 1 – Encender un Led blanco (PIN 4)
* Si contador = 2 – Encender un segundo Led blanco. (PIN 7)
* Si contador = 3 – Encender un tercer Led blanco (PIN 8)
* Si contador = 4
* Apagar los leds blancos
* Encender Led rojo (PIN 12)
* k=0 // Cobntador de tiempos de intermitencia
* Si contador = 5 – Apagar el led rojo
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 1 0 Se soltó el pulsador prepara la siguiente pulsacion
* Poner la variable primeravez a 0 para no repetir esto
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 1 1 Continua apretado el botón No hacer nada
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* parimpar = 1 // para hacer en el siguiente bucle impar
* Fin de el loop par
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Si “parimpar“ es 1 , hacemos el bucle impar parpadeo de el led rojo
* Si j es 4 // solo lo haremos cuando estemos en el menú 4, led rojo intermitente
* Si k es igual que parada // fin de tiempo que esta encendido el led rojo
* Apagar el led rojo // segunda parte del parpadeo
* Si k es igual al doble de parada parada
* k=o // Preparamos la siguiente intermitencia
* Encender el led rojo
* parimpar = 0 // para hacer en el siguiente bucle par
* Repetir el ciclo
*/

Habría que evaluar cual de todas las soluciones es la mejor, pues en principio para mi lo son claramente las dos primeras, sin embargo esta última solución, tiene la ventaja que sea rápida o lenta la pulsación del botón, la máquina sabe discernir sin lugar a duda cuantas veces se has pulsado, mientras que en las primeras soluciones, esto no queda tan claro.

No obstante éstas, tiene una ventaja que si el tiempo de “parada” es el adecuado, el funcionamiento sera perfecto, y eso es algo que podremos afinar en unos cuantos experimentos reales, pues dependerá no solo de la velocidad de la persona apretando el pulsador, sino además de la rapidez de la tarjeta ejecutando las instrucciones y de la velocidad con que reaccione el botón del pushbotton cuando se le suelta. Si el usuario mantiene el dedo apretado , a una velocidad razonable, la linterna va pasando de una a otra opción´sin necesidad de apretar repetidas veces el botón lo cual en principio parece más cómodo.

Usabilidad.

Entramos aquí en algo que se denomina la “USABILIDAD”, o la comodidad que las personas encuentran en el uso de un aparato. Algo que no se fácilmente mensurable. Tqampoco, como vemos se relaciona con la calida o complejidad del software, sino con la mejor aceptacion aparato por parte del público.

¿Que es mas cómodo, ¿pasar de una opción de menú manteniendo apretado un botón o apretar repetidamente el mismo? — A mi, en principio, me parece mejor solución la primera, pero lo correcto seria hacer un prototipo de cada solución, incluso hacer prototipos con dos botones uno para subir y otro para bajar y darlos a probar a una muestra de personas variadas, en la que entraran personas de diferentes niveles culturales y edades, diestros y zurdos, y que la piensen destinar a usos diferentes, como campistas, conductores, vigilantes, electricistas etc. y estudiar su respuesta y a partir de ese muestreo, tomar la decisión más, conveniente, La usabilidad es algo que nos debe preocupar también cundo estemos prototipando un aparato.

Firmware

Técnicamente Se denomina “firmware” a los  programas informáticos que manejan la lógica de más bajo nivel que controla los circuitos electrónicos de un dispositivo, por tanto en puridad al cambiar un programa que controla un hadware no cambiamos el firmware, que en nuestro caso es el programa interno que controla el funcionamiento de los distintos pin de la tarjeta. Pero en una aceptación mas genérica, es el programa podríamos llamar “firmware” estos programas que manejan los circuitos digitales de la tarjeta.

Como vemos el mismo hardware, funciona con tres programas diferentes, (aunque con el primero que pusimos lo haga mal), por tanto construida la linterna, podríamos acceder a la Tarjeta Arduino y cambiar el programa, para por ejemplo pasar del primer programa al segundo y despuès o al tercero o lña cuarto sin hacer ningún cambio en lo que es la mecáanica del aparato. Esta es una ventaja adicional de todos los aparatos electrónicos que tienen internamente un procesador, (y cada dia hay más de estos aparatos en el hogar, del ordenador, al reloj despertador, pasando por la vitrocerámica, lavadora nevera, equipo de aire acondicionado, televisión etc.) , que se puede cambiar el firmware sin cambiar el hardware, (sin modificar la parte mecánica),  para corregir a posteriori errores de diseño, como ocurre aquí, al pasar del programa con errores l al programa arreglado, o para darle mejor usabilidad mejores o mas cantidad de funcionalidades al mismo aparato, Por ejemplo podríamos utilizarle para emitir señades de Morse luminosas sin modificar la máquina. .

Si utilizais Windows, frecuentemente tendréis que padecer actualizaciones del sistema operativo, lo mismo pasa con las App del teléfono móvil , frecuentemente os pedirán que las actualices, eso técnicamente se llama actualización (si es poco) o sustitución del firmware , (si es la totalidad),

Prácticas

  • Repetir el ejercico pero resolviendo todo en el mimo “loop”
  • Repetir e programa sepando las distintas pulsaciones mediante la búsqeda del momento en que se deja de apretar el botón.

Repaso

Como repaso final te diré lo que has aprendido hoy

  • Ejercitarte en el uso de los pin digitales
  • Leer si el voltaje está alto o bajo en un punto
  • Monntajes PULL DOWN
  • Problemas que puede producir el mandato “delay”
  • Uso de la función “if” y la funcion “if /else” y distinatas formas de escribir¡rlas
  • Concepto de multitarea
  • Condepto de usabilidad
  • Concepto f de firmwarte
  • Método para poner a punto un programa

Félix Maocho

indice

 

 

Anuncios

2 febrero 2017 - Posted by | Curso de Arduino, General | , , ,

Aún no hay comentarios.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: