Está en la página 1de 6

En esta ocasión vamos a ampliar el proyecto anterior para incluir un semáforo para peatones y un

pulsador para peatones para solicitar el cruce de la vía. El Arduino reaccionará cuando se presione
el botón cambiando el estado de las luces para hacer que los autos se detengan y permitan que el
peatón cruce de manera segura.

Por primera vez podemos interactuar con el Arduino y hacer que haga algo cuando cambiamos el
estado de un botón que el Arduino está observando (es decir, presionarlo para cambiar el estado
de abierto a cerrado). En este proyecto también aprenderemos a crear nuestras propias funciones.
De ahora en adelante, cuando conectemos los componentes, ya no incluiremos la placa ni los
cables de puente. Tómelo como si estuviera claro que siempre necesitará ambos.

Que necesitarás

- 2 x LED rojos difusos


- LED amarillo difuso
- 2 x LED verdes difusos
- 6 resistencias de 150Ω
- Interruptor táctil
Conéctalo

Conecte los LED y el interruptor como en el diagrama de la página anterior. Deberá pasar los cables
desde los pines 8, 9 y 10 en el proyecto anterior hasta los pines 10, 11 y 12 para permitirle
conectar las luces para peatones a los pines 8 y 9.

Ingrese el código

Ingrese el código en la página siguiente, verifíquelo y cárguelo. Cuando ejecutes el programa verás
que el semáforo de automóviles comienza en verde para permitir el paso de los automóviles y el
semáforo de peatones está en rojo. Al pulsar el botón, el programa comprueba que hayan
transcurrido al menos 5 segundos desde la última vez que se cambiaron las luces (para permitir
que el tráfico se mueva), y si es así pasa la ejecución de código a la función que hemos creado
llamada changeLights() . En esta función, las luces del automóvil pasan de verde a ámbar, luego a
rojo y luego las luces de peatones se vuelven verdes. Después de un período de tiempo establecido
en la variable crossTime (tiempo suficiente para permitir que los peatones crucen), la luz verde
para peatones se encenderá y apagará intermitentemente como advertencia a los peatones para
que se apresuren ya que las luces están a punto de volver a cambiar a roja. . Luego, el semáforo
para peatones vuelve a cambiar a rojo y las luces del vehículo pasan de rojo a ámbar y luego a
verde y el tráfico puede reanudarse. El código de este proyecto es similar al del proyecto anterior.
Sin embargo, se han introducido algunas declaraciones y conceptos nuevos, así que echémosles un
vistazo.

// Project 4 - Interactive Traffic Lights

int carRed = 12; // assign the car lights


int carYellow = 11;
int carGreen = 10;
int pedRed = 9; // assign the pedestrian lights int pedGreen = 8;
int button = 2; // button pin int crossTime = 5000; // time allowed to cross
unsigned long changeTime; // time since button pressed
void setup() {
pinMode(carRed, OUTPUT);
pinMode(carYellow, OUTPUT);
pinMode(carGreen, OUTPUT);
pinMode(pedRed, OUTPUT);
pinMode(pedGreen, OUTPUT);
pinMode(button, INPUT); // button on pin 2
// turn on the green light
digitalWrite(carGreen, HIGH);
digitalWrite(pedRed, HIGH);
}

void loop() {
int state = digitalRead(button);
/* check if button is pressed and it is
over 5 seconds since last button press */
if (state == HIGH && (millis() - changeTime) > 5000) {
// Call the function to change the lights
changeLights();
}
}

void changeLights() {
digitalWrite(carGreen, LOW); // green off
digitalWrite(carYellow, HIGH); // yellow on
delay(2000); // wait 2 seconds
digitalWrite(carYellow, LOW); // yellow off
digitalWrite(carRed, HIGH); // red on
delay(1000); // wait 1 second till its safe
digitalWrite(pedRed, LOW); // ped red off
digitalWrite(pedGreen, HIGH); // ped green on
delay(crossTime); // wait for preset time period
// flash the ped green
for (int x=0; x<10; x++) {
digitalWrite(pedGreen, HIGH);
delay(250);
digitalWrite(pedGreen, LOW);
delay(250);
}

// turn ped red on


digitalWrite(pedRed, HIGH);
delay(500);
digitalWrite(carYellow, HIGH); // yellow on
digitalWrite(carRed, LOW); // red off
delay(1000);
digitalWrite(carGreen, HIGH);
digitalWrite(carYellow, LOW); // yellow off
// record the time since last change of lights
changeTime = millis();
// then return to the main program loop
}

La mayor parte del código de este proyecto lo comprenderá y reconocerá de proyectos anteriores.
Sin embargo, echemos un vistazo a algunas palabras clave y conceptos nuevos que se han
introducido en este boceto.

unsigned long changeTime;

Aquí tenemos un nuevo tipo de datos para una variable. Anteriormente hemos creado tipos de
datos enteros, que pueden almacenar un número entre -32,768 y 32,767. Esta vez hemos creado
un tipo de datos largo, que puede almacenar un número desde -2.147.483.648 hasta
2.147.483.647. Sin embargo, hemos especificado un largo sin signo, lo que significa que la variable
no puede almacenar números negativos, lo que nos da un rango de 0 a 4.294.967.295. Si usáramos
un número entero para almacenar el tiempo transcurrido desde el último cambio de luces, solo
obtendríamos un tiempo máximo de 32 segundos antes de que la variable entera alcanzara un
número superior al que podía almacenar.

Como es poco probable que se use un paso de peatones cada 32 segundos, no queremos que
nuestro programa falle debido a que nuestra variable se "desborde" cuando intenta almacenar un
número demasiado alto para el tipo de datos de la variable. Es por eso que utilizamos un tipo de
datos largos sin firmar, ya que ahora tenemos una gran cantidad de tiempo entre las pulsaciones
de botones.

4294967295 * 1 ms = 4294967 segundos


4294967 segundos = 71582 minutos
71582 minutos - 1193 horas
1193 horas - 49 días

Como es bastante inevitable que se presione el botón de un paso de peatones al menos una vez
cada 49 días, no deberíamos tener problemas con este tipo de datos. Quizás se pregunte por qué
no tenemos un solo tipo de datos que pueda almacenar cantidades enormes todo el tiempo y
terminar con ello. Bueno, la razón por la que no hacemos eso es porque las variables ocupan
espacio en la memoria y cuanto mayor es el número, más memoria se utiliza para almacenar
variables. En el PC o portátil de tu casa no tendrás que preocuparte mucho por eso, pero en un
microcontrolador pequeño como el Atmega328 que usa Arduino es esencial que usemos solo el
tipo de datos variable más pequeño necesario para nuestro propósito.

Hay varios tipos de datos que podemos usar como nuestros bocetos y estos son:
Data type RAM Number Range
Void keyword N/A N/A
Boolean 1 byte 0 to 1 (True or False)
Byte 1 byte 0 to 255
Char 1 byte -128 to 127
Unsigned char 1byte 0 to 255
Int 2 byte -32,768 to 32,767
Unsigned int 2 byte 0 to 65,535
Word 2 byte 0 to 65,535
Long 4 byte -2,147,483,648 to
2,147,483,647
Unigned long 4 byte 0 to 4,294,967,295
Float 4 byte -3.4028235E+38 to
3.4028235E+38
Double 4 byte -3.4028235E+38 to
3.4028235E+38
String 1 byte + x Arrays of chars
Array 1 byte + x Collection of variables

Cada tipo de datos utiliza una cierta cantidad de memoria en Arduino, como puede ver en el
cuadro anterior. Algunas variables usan sólo 1 byte de memoria y otras usan 4 o más (no te
preocupes por lo que es un byte por ahora, ya que discutiremos esto más adelante). No puede
copiar datos de un tipo de datos a otro, p. Si x fuera un int e y fuera una cadena, entonces x = y no
funcionaría ya que los dos tipos de datos son diferentes.

El Atmega168 tiene 1Kb (1000 bytes) y el Atmega328 tiene 2Kb (2000 bytes) de SRAM. Esto no es
mucho y en programas grandes con muchas variables, fácilmente podría quedarse sin memoria si
no optimiza el uso de los tipos de datos correctos. En la lista anterior podemos ver claramente que
nuestro uso del tipo de datos int es un desperdicio ya que utiliza 2 bytes y puede almacenar un
número de hasta 32,767. Como hemos utilizado int para almacenar el número de nuestro pin
digital, que sólo llegará hasta 13 en nuestro Arduino (y hasta 54 en el Arduino Mega), hemos
consumido más memoria de la necesaria. Podríamos haber ahorrado memoria usando el tipo de
datos byte, que puede almacenar un número entre 0 y 255, que es más que suficiente para
almacenar el número de un pin de E/S.

A continuación tenemos

pinMode(button, INPUT);

Esto le dice al Arduino que queremos usar el Pin Digital 2 (botón = 2) como en INPUT. Usaremos el
pin 2 para escuchar las pulsaciones de botones, por lo que su modo debe configurarse en entrada.
En el bucle principal del programa comprobamos el estado del pin digital 2 con esta declaración:

int state = digitalRead(button);

Esto inicializa un número entero (sí, es un desperdicio y deberíamos usar un booleano) llamado
"estado" y luego establece el valor del estado para que sea el valor del pin digital 2. La declaración
digitalRead lee el estado del pin digital dentro del paréntesis y lo devuelve. al número entero al
que se lo hemos asignado. Luego podemos verificar el valor en el estado para ver si el botón se ha
presionado o no.

if (state == HIGH && (millis() - changeTime) > 5000) { // Call the function to change the lights
changeLights(); }

La declaración if es un ejemplo de una estructura de control y su propósito es verificar si se ha


cumplido o no una determinada condición y, de ser así, ejecutar el código dentro de su bloque de
código. Por ejemplo, si quisiéramos encender un LED si una variable llamada x supera el valor de
500, podríamos escribir

if (x>500) {digitalWrite(ledPin, HIGH);

Cuando leemos un pin digital usando el comando digitalRead, el estado del pin será ALTO o BAJO.
Entonces el comando if en nuestro boceto se ve así

if (state == HIGH && (millis() - changeTime) > 5000)

Lo que estamos haciendo aquí es comprobar que se han cumplido dos condiciones. La primera es
que la variable llamada estado es alta. Si se ha presionado el botón, el estado será alto, ya que ya
lo hemos configurado para que sea el valor leído desde el pin digital 2. También estamos
verificando que el valor de millis()-changeTime sea mayor que 5000 (usando el comando lógico
AND &&). La función millis() está integrada en el lenguaje Arduino y devuelve el número de
milisegundos desde que Arduino comenzó a ejecutar el programa actual. Nuestra variable
changeTime inicialmente no tendrá ningún valor, pero después de que se haya ejecutado la
función changeLights), la configuramos al final de esa función con el valor actual de millis().By
subtracting the value in the changeTime variable from the current millis() value we can check if 5
seconds have passed since changeTime was last set. The calculation of millis()-changeTime is put
inside itʼs own set of parenthesis to ensure that we compare the value of state and the result of
this calculation and not the value of millis() on its own.

The symbol ʻ&&’ in between

state == HIGH

y el cálculo es un ejemplo de operador booleano. En este caso significa Y. Para ver qué queremos
decir con esto, echemos un vistazo a todos los operadores booleanos.

- && Logical AND


- || Logical OR
- ! NOT

Estas son declaraciones lógicas y se pueden usar para probar varias condiciones en declaraciones
if. && significa verdadero si ambos operandos son verdaderos, p. :

if (x==5 && y==10) {....

Esta declaración if ejecutará su código sólo si x es 5 y también y es 10. || significa verdadero si


cualquiera de los operandos es verdadero, p. :
if (x==5 || y==10) {..... This will run if x is 5 or if y is 10. The ! or NOT statement means true if the
operand is false, e.g. :

if (!x) {....... Will run if x is false, i.e. equals zero. You can also ʻnestʼ conditions with parenthesis, for
example if (x==5 && (y==10 || z==25)) {.......

En este caso, las condiciones entre paréntesis se procesan por separado y se tratan como una
condición única y luego se comparan con la segunda condición. Entonces, si dibujamos una tabla
de verdad simple para esta afirmación, podemos ver cómo funciona.

El comando dentro de la declaración if es changeLights(); y este es un ejemplo de una llamada a


función. Una función es simplemente un bloque de código separado al que se le ha dado un
nombre. Sin embargo, a las funciones también se les pueden pasar parámetros y/o devolver datos.
En este caso no hemos pasado ningún dato a la función ni la función ha devuelto ninguna fecha.
Más adelante entraremos en más detalles sobre cómo pasar parámetros y devolver datos de
funciones.

Cuando changeLights(); se llama, la ejecución del código salta de la línea actual a la función,
ejecuta el código dentro de esa función y luego regresa al punto en el código después de donde se
llamó la función.

Entonces, en este caso, si se cumplen las condiciones en la declaración if, entonces el programa
ejecuta el código dentro de la función y luego regresa a la siguiente línea después de
changeLights(); en la declaración if. El código dentro de la función simplemente cambia las luces
del vehículo a rojo, pasando a ámbar, y luego enciende la luz verde para peatones. Después de un
período de tiempo establecido por la variable crossTime la luz parpadea unas cuantas veces para
advertir al peatón que su tiempo está por terminar, luego el semáforo para peatones se pone rojo
y la luz del vehículo pasa de rojo a verde, pasando por ámbar y vuelve a es estado normal.

El bucle del programa principal simplemente verifica continuamente si el botón de peatón ha sido
presionado o no y si es así, y (&&) el tiempo desde la última vez que se cambiaron las luces es
mayor a 5 segundos, llama nuevamente a la función changeLights().

En este programa no hubo ningún beneficio al poner el código en su propia función aparte de
hacer que el código pareciera más limpio. Sólo cuando a una función se le pasan parámetros y/o
devuelve datos, sus verdaderos beneficios salen a la luz y lo veremos más adelante. A
continuación, vamos a utilizar muchos más LED para crear un efecto de persecución de LED estilo
"Knight Rider".

También podría gustarte