Arduino UNO R4 - Joystick

Neste guia, vamos aprender a usar um joystick com o Arduino UNO R4. Vamos abordar:

Arduino UNO R4 e Joystick

Sobre o Joystick

Você já deve ter visto um joystick usado como controle de jogo, controle de brinquedo ou até em máquinas grandes, como os controles de escavadeiras.

O joystick tem dois potenciômetros dispostos em formação quadrada e um botão de pressão. Ele fornece as seguintes saídas:

  • Um valor analógico (de 0 a 1023) para a posição horizontal (coordenada X)
  • Um valor analógico (de 0 a 1023) para a posição vertical (coordenada Y)
  • Um estado digital de um botão (HIGH ou LOW)

Quando você combina os dois valores analógicos, eles formam coordenadas 2D. Essas coordenadas ficam centralizadas quando o joystick não está sendo movido. Você pode descobrir facilmente a direção real dessas coordenadas executando um código de teste, que veremos na próxima seção.

Algumas aplicações podem usar as três saídas, enquanto outras usam apenas algumas delas.

Pinagem

Um joystick usa cinco pinos.

  • Pino GND: conecte ao GND (0V)
  • Pino VCC: conecte ao VCC (5V)
  • Pino VRX: fornece um valor analógico para a posição horizontal (coordenada X).
  • Pino VRY: fornece um valor analógico para a posição vertical (coordenada Y).
  • Pino SW: vem do botão de pressão do joystick. Normalmente ele fica aberto. Usando um resistor de pull-up, este pino fica em HIGH quando não pressionado e em LOW quando pressionado.
Pinagem do Joystick

Como Funciona

  • Movimento Esquerda/Direita
    • Quando você move o joystick para a esquerda ou para a direita, ele muda o sinal elétrico (tensão) no pino VRX.
    • Mover para a esquerda reduz a tensão a 0 volts.
    • Mover para a direita aumenta a tensão a 5 volts.
    • O Arduino lê essa tensão e a converte em um número entre 0 e 1023. Assim, 0V corresponde a 0, e 5V corresponde a 1023.
  • Movimento Para Cima/Para Baixo:
    • Da mesma forma, mover o joystick para cima ou para baixo muda a tensão no pino VRY.
    • Mover para cima reduz a tensão a 0 volts.
    • Mover para baixo aumenta a tensão a 5 volts.
    • O Arduino também lê essa tensão, convertendo-a em um número entre 0 e 1023, assim como no pino VRX.
  • Movimentos Combinados:
    • Quando você move o joystick em qualquer direção (não apenas puramente esquerda/direita ou para cima/para baixo), ele muda a tensão nos pinos VRX e VRY com base na sua posição em cada eixo.
  • Pressionando o Joystick:
    • Quando você empurra o joystick para baixo (como se apertasse um botão), ele aciona um botão interno dentro do joystick.
    • Se você conectar um resistor de pull-up ao pino SW, a tensão neste pino vai mudar de 5V para 0V quando pressionado.
    • O Arduino lê essa mudança como um sinal digital, mostrando HIGH (5V) quando não pressionado e LOW (0V) quando pressionado.

    Em resumo, mover o joystick muda as tensões em VRX e VRY, que o Arduino lê como números entre 0 e 1023. Pressionar o joystick muda a tensão no pino SW, que o Arduino lê como HIGH ou LOW.

Diagrama de Ligação

Diagrama de Ligação do Arduino UNO R4 com Joystick

This image is created using Fritzing. Click to enlarge image

Veja A melhor forma de alimentar o Arduino Uno R4 e outros componentes.

Como Programar o Joystick

O joystick é formado por dois componentes: uma parte analógica (eixos X e Y) e uma parte digital (um botão de pressão).

  • Para ler os valores dos eixos X e Y, basta usar a função analogRead() no pino de entrada analógica.
int xValue = analogRead(A1); int yValue = analogRead(A0);
  • Para a parte digital, que é um botão de pressão: é só um botão. Um método fácil e eficaz é usar a biblioteca ezButton. Essa biblioteca ajuda a tratar a estabilidade do botão e também ativa um sistema interno para manter o botão estável. Você pode saber mais no tutorial Arduino UNO R4 - Botão. Mostraremos como usar o código na próxima parte deste tutorial.

Depois de obter as leituras dos pinos analógicos, talvez precisemos transformá-las em valores mais fáceis de manusear. A seção a seguir traz códigos de exemplo para isso.

Código Arduino UNO R4

Aqui estão códigos de exemplo para o Arduino UNO R4:

  • Código de exemplo: obtém valores analógicos do joystick
  • Código de exemplo: obtém valores analógicos e verifica o estado do botão do joystick
  • Código de exemplo: transforma o valor analógico em comandos: MOVE_LEFT, MOVE_RIGHT, MOVE_UP, MOVE_DOWN
  • Código de exemplo: transforma valores analógicos em ângulos para controlar dois servo motores (como em uma câmera pan-tilt)

Lê valores analógicos do joystick

/* * Este código de Arduino UNO R4 foi desenvolvido por pt.newbiely.com * Este código de Arduino UNO R4 é disponibilizado ao público sem nenhuma restrição. * Para tutoriais completos e diagramas de ligação, visite: * https://pt.newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-joystick */ #define VRX_PIN A1 // The Arduino UNO R4 pin connected to VRX pin #define VRY_PIN A0 // The Arduino UNO R4 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); }

Passos Rápidos

Siga estas instruções passo a passo:

  • Se esta é a sua primeira vez usando o Arduino Uno R4 WiFi/Minima, consulte o tutorial sobre como configurar o ambiente para o Arduino Uno R4 WiFi/Minima na Arduino IDE.
  • Ligue o joystick ao Arduino UNO R4 conforme o diagrama fornecido.
  • Conecte a placa Arduino Uno R4 ao seu computador usando um cabo USB.
  • Abra a Arduino IDE no seu computador.
  • Selecione a placa Arduino Uno R4 apropriada (por exemplo, Arduino Uno R4 WiFi) e a porta COM.
  • Copie o código acima e abra-o na Arduino IDE
  • Clique no botão Upload na Arduino IDE para carregar o código no seu Arduino UNO R4
  • Empurre o manete do joystick até a borda e depois gire-o em círculo (no sentido horário ou anti-horário)
  • Verifique os resultados no Serial Monitor.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
Arduino Uno R4 WiFi
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'Arduino Uno R4 WiFi' on 'COM15')
New Line
9600 baud
Ln 11, Col 1
Arduino Uno R4 WiFi on COM15
2
  • Ao girar o joystick, fique de olho no Serial Monitor. Se o valor de X mostrar 0, lembre-se dessa posição como esquerda; o oposto é direita. Se o valor de Y for 0, anote essa posição como para cima; o oposto é para baixo.

Lê valores analógicos e lê o estado do botão de um joystick

/* * Este código de Arduino UNO R4 foi desenvolvido por pt.newbiely.com * Este código de Arduino UNO R4 é disponibilizado ao público sem nenhuma restrição. * Para tutoriais completos e diagramas de ligação, visite: * https://pt.newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-joystick */ #include <ezButton.h> #define VRX_PIN A1 // The Arduino UNO R4 pin connected to VRX pin #define VRY_PIN A0 // The Arduino UNO R4 pin connected to VRY pin #define SW_PIN 2 // The Arduino UNO R4 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); }

Passos Rápidos

  • Vá até o ícone Libraries no lado esquerdo da Arduino IDE.
  • Procure por “ezButton” e encontre a biblioteca de botão do ArduinoGetStarted.com
  • Pressione o botão Install para adicionar a biblioteca ezButton.
biblioteca de botão do Arduino UNO R4
  • Copie o código e abra-o na Arduino IDE.
  • Clique no botão Upload na Arduino IDE para carregar o código no Arduino UNO R4.
  • Mova o manete do joystick para a esquerda, direita, para cima ou para baixo.
  • Pressione a parte de cima do joystick.
  • Verifique os resultados no Serial Monitor.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
Arduino Uno R4 WiFi
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'Arduino Uno R4 WiFi' on 'COM15')
New Line
9600 baud
Ln 11, Col 1
Arduino Uno R4 WiFi on COM15
2

Converte o valor analógico em comandos MOVE LEFT/RIGHT/UP/DOWN

/* * Este código de Arduino UNO R4 foi desenvolvido por pt.newbiely.com * Este código de Arduino UNO R4 é disponibilizado ao público sem nenhuma restrição. * Para tutoriais completos e diagramas de ligação, visite: * https://pt.newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-joystick */ #define VRX_PIN A1 // The Arduino UNO R4 pin connected to VRX pin #define VRY_PIN A0 // The Arduino UNO R4 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 } }

Passos Rápidos

  • Copie o código acima e abra-o na Arduino IDE.
  • Clique no botão Upload na Arduino IDE para carregar o código no Arduino UNO R4.
  • Mova o manete do joystick para a esquerda, direita, para cima, para baixo ou em qualquer outra direção.
  • Verifique os resultados no Serial Monitor.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
Arduino Uno R4 WiFi
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'Arduino Uno R4 WiFi' on 'COM15')
New Line
9600 baud
Ln 11, Col 1
Arduino Uno R4 WiFi on COM15
2

※ Nota:

Às vezes, pode não haver nenhum comando, haver um comando ou haver dois comandos ao mesmo tempo (por exemplo, UP e LEFT juntos).

Converte valores analógicos em ângulos para controlar dois servo motores

Você encontra informações no tutorial Arduino UNO R4 - Joystick controla Servo Motor.

Vídeo

O vídeo de demonstração abaixo usa o código abaixo:

/* * Este código de Arduino UNO R4 foi desenvolvido por pt.newbiely.com * Este código de Arduino UNO R4 é disponibilizado ao público sem nenhuma restrição. * Para tutoriais completos e diagramas de ligação, visite: * https://pt.newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-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); }

Referências de Funções

※ NOSSAS MENSAGENS

  • Fique à vontade para compartilhar o link deste tutorial. Porém, por favor, não use nosso conteúdo em outros sites. Investimos muito esforço e tempo para criar este conteúdo, por favor respeite nosso trabalho!