Arduino - Joystick

En este tutorial, vamos a aprender cómo usar un joystick con Arduino. En detalle, aprenderemos:

Diagrama de pines del joystick

Hardware Requerido

1×Arduino Uno R3
1×Cable USB 2.0 tipo A/B (para PC USB-A)
1×Cable USB 2.0 tipo C/B (para PC USB-C)
1×Joystick
1×Cables Puente
1×(Recomendado) Shield de Bloque de Terminales de Tornillo para Arduino Uno
1×(Recomendado) Sensors/Servo Expansion Shield for Arduino Uno
1×(Recomendado) Shield de Protoboard para Arduino Uno
1×(Recomendado) Carcasa para Arduino Uno
1×(Recomendado) Placa Base de Prototipado y Kit de Protoboard para Arduino Uno

Or you can buy the following kits:

1×DIYables STEM V3 Starter Kit (Arduino included)
1×DIYables Sensor Kit (18 sensors/displays)
Divulgación: Algunos de los enlaces proporcionados en esta sección son enlaces de afiliado de Amazon. Podemos recibir una comisión por las compras realizadas a través de estos enlaces sin costo adicional para usted. Apreciamos su apoyo.

Acerca del sensor del joystick

Probablemente veas el joystick en algún lugar, como un mando de videojuegos, un mando de juguete o incluso una gran máquina real, como el controlador de una excavadora.

El joystick está compuesto por dos potenciómetros entre sí y un pulsador. Por lo tanto, proporciona las siguientes salidas:

  • Un valor analógico (de 0 a 1023) correspondiente a la posición horizontal (denominada coordenada X)
  • Un valor analógico (de 0 a 1023) correspondiente a la posición vertical (denominada coordenada Y)
  • Un valor digital de un pulsador (HIGH o LOW)

La combinación de dos valores analógicos puede crear coordenadas 2-D con los valores centrales cuando el joystick está en la posición de reposo. La dirección real de las coordenadas se puede identificar fácilmente cuando ejecutes un código de prueba (en la siguiente parte).

Algunas aplicaciones pueden usar las tres salidas; algunas aplicaciones pueden usar algunas de las tres salidas.

Diagrama de pines

Un joystick tiene 5 pines:

  • Pin GND: debe conectarse a GND (0 V)
  • Pin VCC: debe conectarse a VCC (5 V)
  • Pin VRX: proporciona un valor analógico correspondiente a la posición horizontal (llamada coordenada X).
  • Pin VRY: proporciona un valor analógico correspondiente a la posición vertical (llamada coordenada Y).
  • Pin SW: es la salida del pulsador dentro del joystick. Por defecto está abierto. Si usamos una resistencia pull-up en este pin, el pin SW estará en HIGH cuando no esté presionado y en LOW cuando esté presionado.
Disposición de pines del joystick

Cómo funciona

  • Cuando empujas el pulsador del joystick hacia la izquierda/derecha, el voltaje en el pin VRX cambia. El rango de voltaje es de 0 a 5 V (0 a la izquierda y 5 V a la derecha). El valor de voltaje es proporcional a la posición del pulsador, por lo que el valor de lectura en el pin analógico del Arduino va de 0 a 1023
  • Cuando empujas el pulsador del joystick hacia arriba/abajo, el voltaje en el pin VRY cambia. El rango de voltaje va de 0 a 5 V (0 arriba y 5 V abajo). El valor de voltaje es proporcional a la posición del pulsador, por lo que el valor de lectura en el pin analógico del Arduino va de 0 a 1023
  • Cuando empujas el pulsador del joystick en cualquier dirección, el voltaje en los pines VRX y VRY cambia en proporción a la proyección de la posición en cada eje
  • Cuando empujas el pulsador del joystick de arriba hacia abajo, se cierra el pulsador interno del joystick. Si usamos una resistencia pull-up en el pin SW, la salida del pin SW cambiará de 5 V a 0 V, por lo que la lectura en el pin digital del Arduino cambia de HIGH a LOW

Diagrama de Cableado

Diagrama de cableado del joystick de Arduino

This image is created using Fritzing. Click to enlarge image

Cómo programar para un joystick

El joystick tiene dos partes: analógicas (ejes X e Y) y digitales (botón pulsador).

  • Para las partes analógicas (ejes X, Y), solo necesitas leer el valor del pin de entrada analógica usando la función analogRead().
int xValue = analogRead(A1); int yValue = analogRead(A0);
  • Para la parte digital (pulsador): es un botón. La forma más simple y conveniente es usar ezButton library. Esta biblioteca admite anti-rebote para botones y también habilita una resistencia interna de pull-up. Puedes ver más sobre el botón en Arduino - Button tutorial. El código se presentará en la próxima sesión de este tutorial.

Después de leer los valores de los pines analógicos, puede que necesitemos convertirlos a valores controlables. La siguiente parte proporcionará los códigos de ejemplo para ello.

Código de Arduino

Esta sección proporcionará los siguientes códigos de ejemplo de Arduino:

  • Ejemplo de código: lee valores analógicos del joystick
  • Ejemplo de código: lee valores analógicos y lee el estado del botón desde el joystick
  • Ejemplo de código: convierte el valor analógico en comandos MOVE_LEFT, MOVE_RIGHT, MOVE_UP, MOVE_DOWN
  • Ejemplo de código: convierte valores analógicos en ángulos para controlar dos servomotores (p. ej., en una cámara pan-tilt)

Lee valores analógicos del joystick

/* * Este código de Arduino fue desarrollado por es.newbiely.com * Este código de Arduino se proporciona al público sin ninguna restricción. * Para tutoriales completos y diagramas de cableado, visite: * https://es.newbiely.com/tutorials/arduino/arduino-joystick */ #define VRX_PIN A1 // Arduino pin connected to VRX pin #define VRY_PIN A0 // Arduino pin connected to VRY pin int xValue = 0; // To store value of the X axis int yValue = 0; // To store value of the Y axis void setup() { Serial.begin(9600) ; } void loop() { // read analog X and Y analog values xValue = analogRead(VRX_PIN); yValue = analogRead(VRY_PIN); // print data to Serial Monitor on Arduino IDE Serial.print("x = "); Serial.print(xValue); Serial.print(", y = "); Serial.println(yValue); delay(200); }

Pasos R\u00e1pidos

  • Copia el código anterior y ábrelo con Arduino IDE
  • Haz clic en el botón Subir en el IDE de Arduino para subir el código al Arduino
  • Empuja el pulsador del joystick al máximo hasta el tope y luego gíralo en un círculo (sentido horario o antihorario)
  • Mira el resultado en el Monitor Serial
Newbiely | Arduino IDE 2.3.8
──
File
Edit
Sketch
Tools
Help
Arduino Uno
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'Arduino Uno' on 'COM15')
New Line
9600 baud
Ln 11, Col 1
Arduino Uno on COM15
2
  • Mientras giras el pulgar del joystick, continúa mirando el Monitor Serie
    • Si el valor X es 0, marca o memoriza la posición actual como izquierda ⇒ la dirección opuesta es la derecha
    • Si el valor Y es 0, marca o memoriza la posición actual como arriba ⇒ la dirección opuesta es abajo

    Lee valores analógicos y lee el estado del botón de un joystick

    /* * Este código de Arduino fue desarrollado por es.newbiely.com * Este código de Arduino se proporciona al público sin ninguna restricción. * Para tutoriales completos y diagramas de cableado, visite: * https://es.newbiely.com/tutorials/arduino/arduino-joystick */ #include <ezButton.h> #define VRX_PIN A1 // Arduino pin connected to VRX pin #define VRY_PIN A0 // Arduino pin connected to VRY pin #define SW_PIN 2 // Arduino pin connected to SW pin ezButton button(SW_PIN); int xValue = 0; // To store value of the X axis int yValue = 0; // To store value of the Y axis int bValue = 0; // To store value of the button void setup() { Serial.begin(9600) ; button.setDebounceTime(50); // set debounce time to 50 milliseconds } void loop() { button.loop(); // MUST call the loop() function first // read analog X and Y analog values xValue = analogRead(VRX_PIN); yValue = analogRead(VRY_PIN); // Read the button value bValue = button.getState(); if (button.isPressed()) { Serial.println("The button is pressed"); // TODO do something here } if (button.isReleased()) { Serial.println("The button is released"); // TODO do something here } // print data to Serial Monitor on Arduino IDE Serial.print("x = "); Serial.print(xValue); Serial.print(", y = "); Serial.print(yValue); Serial.print(" : button = "); Serial.println(bValue); }

    Pasos R\u00e1pidos

    • Navega al ícono de Bibliotecas en la barra izquierda del IDE de Arduino.
    • Busca “ezButton”, luego encuentra la librería de botones de ArduinoGetStarted.com
    • Haz clic en el botón Instalar para instalar la librería ezButton.
    Librería de botones de Arduino
    • Copia el código anterior y ábrelo con el IDE de Arduino
    • Haz clic en el botón Subir del IDE de Arduino para subir el código al Arduino
    • Empuja el pulgar del joystick hacia la izquierda/derecha/arriba/abajo
    • Empuja el pulgar del joystick desde la parte superior
    • Ve el resultado en el Monitor Serial.
    Newbiely | Arduino IDE 2.3.8
    ──
    File
    Edit
    Sketch
    Tools
    Help
    Arduino Uno
    Newbiely.ino
    ···
    8 Serial.println("Hello World!");
    Output
    Serial Monitor
    Message (Enter to send message to 'Arduino Uno' on 'COM15')
    New Line
    9600 baud
    Ln 11, Col 1
    Arduino Uno on COM15
    2

    Convierte el valor analógico en comandos MOVER IZQUIERDA/DERECHA/ARRIBA/ABAJO

    /* * Este código de Arduino fue desarrollado por es.newbiely.com * Este código de Arduino se proporciona al público sin ninguna restricción. * Para tutoriales completos y diagramas de cableado, visite: * https://es.newbiely.com/tutorials/arduino/arduino-joystick */ #define VRX_PIN A1 // Arduino pin connected to VRX pin #define VRY_PIN A0 // Arduino pin connected to VRY pin #define LEFT_THRESHOLD 400 #define RIGHT_THRESHOLD 800 #define UP_THRESHOLD 400 #define DOWN_THRESHOLD 800 #define COMMAND_NO 0x00 #define COMMAND_LEFT 0x01 #define COMMAND_RIGHT 0x02 #define COMMAND_UP 0x04 #define COMMAND_DOWN 0x08 int xValue = 0 ; // To store value of the X axis int yValue = 0 ; // To store value of the Y axis int command = COMMAND_NO; void setup() { Serial.begin(9600) ; } void loop() { // read analog X and Y analog values xValue = analogRead(VRX_PIN); yValue = analogRead(VRY_PIN); // converts the analog value to commands // reset commands command = COMMAND_NO; // check left/right commands if (xValue < LEFT_THRESHOLD) command = command | COMMAND_LEFT; else if (xValue > RIGHT_THRESHOLD) command = command | COMMAND_RIGHT; // check up/down commands if (yValue < UP_THRESHOLD) command = command | COMMAND_UP; else if (yValue > DOWN_THRESHOLD) command = command | COMMAND_DOWN; // NOTE: AT A TIME, THERE MAY BE NO COMMAND, ONE COMMAND OR TWO COMMANDS // print command to serial and process command if (command & COMMAND_LEFT) { Serial.println("COMMAND LEFT"); // TODO: add your task here } if (command & COMMAND_RIGHT) { Serial.println("COMMAND RIGHT"); // TODO: add your task here } if (command & COMMAND_UP) { Serial.println("COMMAND UP"); // TODO: add your task here } if (command & COMMAND_DOWN) { Serial.println("COMMAND DOWN"); // TODO: add your task here } }

    Pasos R\u00e1pidos

    • Copia el código anterior y ábrelo con el IDE de Arduino
    • Haz clic en el botón Subir del IDE de Arduino para cargar el código en Arduino
    • Empuja el joystick hacia la izquierda/derecha/arriba/abajo o en cualquier dirección
    • Mira el resultado en el monitor serie
    Newbiely | Arduino IDE 2.3.8
    ──
    File
    Edit
    Sketch
    Tools
    Help
    Arduino Uno
    Newbiely.ino
    ···
    8 Serial.println("Hello World!");
    Output
    Serial Monitor
    Message (Enter to send message to 'Arduino Uno' on 'COM15')
    New Line
    9600 baud
    Ln 11, Col 1
    Arduino Uno on COM15
    2

    ※ Nota:

    En un momento dado, puede no haber ningún comando, un comando o dos comandos (p. ej., ARRIBA y IZQUIERDA al mismo tiempo)

    Convierte valores analógicos en ángulos para controlar dos servomotores

    El detalle se presenta en el tutorial Arduino - Controles del Joystick para el Servomotor

Video Tutorial

Estamos considerando crear tutoriales en video. Si considera que los tutoriales en video son importantes, suscríbase a nuestro canal de YouTube para motivarnos a crear los videos.

La demostración en video a continuación utiliza el siguiente código. Ten en cuenta que el video muestra el Arduino Uno R4, pero funciona de manera idéntica para el Arduino Uno R3:

/* * Este código de Arduino fue desarrollado por es.newbiely.com * Este código de Arduino se proporciona al público sin ninguna restricción. * Para tutoriales completos y diagramas de cableado, visite: * https://es.newbiely.com/tutorials/arduino/arduino-joystick */ #include "Arduino_LED_Matrix.h" // Pin definitions #define PIN_VRX A1 #define PIN_VRY A0 #define PIN_SW 2 #define PIN_BUZZER 8 // Matrix display dimensions #define MATRIX_WIDTH 12 #define MATRIX_HEIGHT 8 #define MAX_SNAKE_LENGTH 96 // Snake movement direction enum Direction { UP, DOWN, LEFT, RIGHT }; // Game state machine enum GameState { START_BOOT, // Initial boot screen scrolling "DIYables" PLAYING, // Active gameplay GAME_OVER // Game over score display }; GameState currentState = START_BOOT; ArduinoLEDMatrix matrix; uint8_t frame[8][12]; // Snake properties int snakeX[MAX_SNAKE_LENGTH]; int snakeY[MAX_SNAKE_LENGTH]; int snakeLength = 3; int score = 0; Direction dir = RIGHT; Direction nextDir = RIGHT; // Food coordinates int foodX = 0; int foodY = 0; // Movement timing unsigned long lastMoveTime = 0; int moveInterval = 250; // Text scrolling timing int startTextX = 12; int scoreTextX = 12; unsigned long lastScrollTime = 0; const int SCROLL_INTERVAL = 75; // Physics particle structure for 3s fall effect struct Particle { float x; float y; float vx; float vy; bool active; }; // Function prototypes void resetGame(); void handleInput(); void updateGameLogic(); void renderGame(); void spawnFood(); void renderText(String text, int xOffset); uint8_t getCharCol(char c, int col); void playShootingStarEffect(); void playEatSound(); void playStartSound(); bool isButtonPressed(); void clearFrame(); void setup() { pinMode(PIN_SW, INPUT_PULLUP); pinMode(PIN_BUZZER, OUTPUT); matrix.begin(); randomSeed(analogRead(A2)); } void loop() { switch (currentState) { case START_BOOT: // Show "DIYables" only on initial power-up if (millis() - lastScrollTime >= SCROLL_INTERVAL) { lastScrollTime = millis(); renderText("DIYables", startTextX); startTextX--; if (startTextX < -36) { startTextX = 12; } } if (isButtonPressed()) { resetGame(); playStartSound(); currentState = PLAYING; } break; case PLAYING: handleInput(); updateGameLogic(); renderGame(); break; case GAME_OVER: // Display score ("Score: X") if (millis() - lastScrollTime >= SCROLL_INTERVAL) { lastScrollTime = millis(); String scoreStr = "Score: " + String(score); renderText(scoreStr, scoreTextX); scoreTextX--; int strLen = scoreStr.length() * 4; if (scoreTextX < -strLen) { scoreTextX = 12; } } // Press button to restart game directly if (isButtonPressed()) { resetGame(); playStartSound(); currentState = PLAYING; } break; } } // Read Joystick axes independently for accurate 4-way directional control void handleInput() { int xVal = analogRead(PIN_VRX); int yVal = analogRead(PIN_VRY); int dx = xVal - 512; int dy = yVal - 512; // Prioritize axis with stronger deflection if (abs(dx) > abs(dy)) { if (dx < -200 && dir != RIGHT) nextDir = LEFT; else if (dx > 200 && dir != LEFT) nextDir = RIGHT; } else { if (dy < -200 && dir != DOWN) nextDir = UP; else if (dy > 200 && dir != UP) nextDir = DOWN; } } // 3.0-second shooting star fall effect void playShootingStarEffect() { const int MAX_PARTICLES = 45; Particle particles[MAX_PARTICLES]; int pCount = 0; // Convert active LEDs (snake body and food) into free-falling particles for (int r = 0; r < MATRIX_HEIGHT; r++) { for (int c = 0; c < MATRIX_WIDTH; c++) { if (frame[r][c] == 1 && pCount < 25) { particles[pCount].x = c; particles[pCount].y = r; particles[pCount].vx = (float)random(-15, 15) / 100.0; particles[pCount].vy = (float)random(-20, 5) / 100.0; particles[pCount].active = true; pCount++; } } } unsigned long startTime = millis(); unsigned long lastSpawn = 0; // 3-second physics simulation loop while (millis() - startTime < 3000) { clearFrame(); unsigned long elapsedTime = millis() - startTime; // Spawn additional falling star trails from top during the first 2.2 seconds if (elapsedTime < 2200 && millis() - lastSpawn > 80 && pCount < MAX_PARTICLES) { lastSpawn = millis(); particles[pCount].x = random(0, MATRIX_WIDTH); particles[pCount].y = -random(1, 4); particles[pCount].vx = (float)random(-5, 5) / 100.0; particles[pCount].vy = (float)random(10, 30) / 100.0; particles[pCount].active = true; pCount++; } // Update particle positions for (int i = 0; i < pCount; i++) { if (!particles[i].active) continue; particles[i].x += particles[i].vx; particles[i].y += particles[i].vy; particles[i].vy += 0.04; // Gravity acceleration int px = (int)round(particles[i].x); int py = (int)round(particles[i].y); // Draw particle if (py >= 0 && py < MATRIX_HEIGHT && px >= 0 && px < MATRIX_WIDTH) { frame[py][px] = 1; } // Draw particle trail if falling rapidly int tailY = py - 1; if (tailY >= 0 && tailY < MATRIX_HEIGHT && px >= 0 && px < MATRIX_WIDTH && particles[i].vy > 0.3) { frame[tailY][px] = 1; } // Deactivate particle once below screen if (py >= MATRIX_HEIGHT + 3) { particles[i].active = false; } } matrix.renderBitmap(frame, 8, 12); // Decreasing pitch sound from 1200Hz to 100Hz synchronized over 3 seconds int soundFreq = 1200 - (int)((elapsedTime / 3000.0) * 1100); if (soundFreq > 100) { tone(PIN_BUZZER, soundFreq, 30); } delay(35); } noTone(PIN_BUZZER); clearFrame(); matrix.renderBitmap(frame, 8, 12); } // 3x5 bitmap font data uint8_t getCharCol(char c, int col) { switch (c) { // "DIYables" characters case 'D': return (col==0)?0x1F:(col==1)?0x11:0x0E; case 'I': return (col==0)?0x11:(col==1)?0x1F:0x11; case 'Y': return (col==0)?0x03:(col==1)?0x1C:0x03; case 'a': return (col==0)?0x0C:(col==1)?0x12:0x1E; case 'b': return (col==0)?0x1F:(col==1)?0x14:0x08; case 'l': return (col==0)?0x11:(col==1)?0x1F:0x10; case 'e': return (col==0)?0x0C:(col==1)?0x16:0x14; case 's': return (col==0)?0x14:(col==1)?0x16:0x0A; // "Score:" characters case 'S': return (col==0)?0x12:(col==1)?0x15:0x09; case 'c': return (col==0)?0x0C:(col==1)?0x12:0x12; case 'o': return (col==0)?0x0C:(col==1)?0x12:0x0C; case 'r': return (col==0)?0x1E:(col==1)?0x04:0x02; case ':': return (col==0)?0x00:(col==1)?0x0A:0x00; case ' ': return 0x00; // Digits 0 - 9 case '0': return (col==0)?0x0E:(col==1)?0x11:0x0E; case '1': return (col==0)?0x12:(col==1)?0x1F:0x10; case '2': return (col==0)?0x19:(col==1)?0x15:0x12; case '3': return (col==0)?0x11:(col==1)?0x15:0x0A; case '4': return (col==0)?0x07:(col==1)?0x04:0x1F; case '5': return (col==0)?0x17:(col==1)?0x15:0x09; case '6': return (col==0)?0x0E:(col==1)?0x15:0x09; case '7': return (col==0)?0x19:(col==1)?0x05:0x03; case '8': return (col==0)?0x0A:(col==1)?0x15:0x0A; case '9': return (col==0)?0x12:(col==1)?0x15:0x0E; default: return 0x00; } } // Render string to LED matrix frame void renderText(String text, int xOffset) { clearFrame(); int curX = xOffset; int topY = 1; for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); for (int col = 0; col < 3; col++) { int screenX = curX + col; if (screenX >= 0 && screenX < MATRIX_WIDTH) { uint8_t colData = getCharCol(ch, col); for (int row = 0; row < 5; row++) { if ((colData >> row) & 1) { frame[topY + row][screenX] = 1; } } } } curX += 4; } matrix.renderBitmap(frame, 8, 12); } // Snake game logic update void updateGameLogic() { if (millis() - lastMoveTime < moveInterval) return; lastMoveTime = millis(); dir = nextDir; int newHeadX = snakeX[0]; int newHeadY = snakeY[0]; switch (dir) { case UP: newHeadY--; break; case DOWN: newHeadY++; break; case LEFT: newHeadX--; break; case RIGHT: newHeadX++; break; } // Wall collision -> Game Over if (newHeadX < 0 || newHeadX >= MATRIX_WIDTH || newHeadY < 0 || newHeadY >= MATRIX_HEIGHT) { playShootingStarEffect(); scoreTextX = 12; currentState = GAME_OVER; return; } // Self-collision -> Game Over for (int i = 0; i < snakeLength; i++) { if (snakeX[i] == newHeadX && snakeY[i] == newHeadY) { playShootingStarEffect(); scoreTextX = 12; currentState = GAME_OVER; return; } } bool ateFood = (newHeadX == foodX && newHeadY == foodY); if (ateFood) { snakeLength++; score++; playEatSound(); if (moveInterval > 100) { moveInterval -= 5; } } for (int i = snakeLength - 1; i > 0; i--) { snakeX[i] = snakeX[i - 1]; snakeY[i] = snakeY[i - 1]; } snakeX[0] = newHeadX; snakeY[0] = newHeadY; if (ateFood) { spawnFood(); } } void renderGame() { clearFrame(); frame[foodY][foodX] = 1; for (int i = 0; i < snakeLength; i++) { frame[snakeY[i]][snakeX[i]] = 1; } matrix.renderBitmap(frame, 8, 12); } void spawnFood() { bool onSnake; do { onSnake = false; foodX = random(0, MATRIX_WIDTH); foodY = random(0, MATRIX_HEIGHT); for (int i = 0; i < snakeLength; i++) { if (snakeX[i] == foodX && snakeY[i] == foodY) { onSnake = true; break; } } } while (onSnake); } void resetGame() { snakeLength = 3; score = 0; dir = RIGHT; nextDir = RIGHT; moveInterval = 250; snakeX[0] = 4; snakeY[0] = 3; snakeX[1] = 3; snakeY[1] = 3; snakeX[2] = 2; snakeY[2] = 3; spawnFood(); } bool isButtonPressed() { static bool lastState = HIGH; bool state = digitalRead(PIN_SW); if (lastState == HIGH && state == LOW) { delay(50); lastState = state; return true; } lastState = state; return false; } void clearFrame() { memset(frame, 0, sizeof(frame)); } void playEatSound() { tone(PIN_BUZZER, 1200, 60); } void playStartSound() { tone(PIN_BUZZER, 440, 80); delay(90); tone(PIN_BUZZER, 554, 80); delay(90); tone(PIN_BUZZER, 659, 120); delay(120); }

Referencias de funciones

Tutoriales Relacionados

※ NUESTROS MENSAJES

  • No dude en compartir el enlace de este tutorial. Sin embargo, por favor no use nuestro contenido en otros sitios web. Hemos invertido mucho esfuerzo y tiempo en crear el contenido, ¡por favor respete nuestro trabajo!