blog

Learn Wiring and Coding of 5-Way Navigation Button in 3 Minutes

5-way Navigation Button Cover

Want to create your own personalized devices? The 5-way navigation button module, 5D Joystick and independent keyboard switches are indispensable helpers. Whether it’s a game controller or a smart home device, they make the DIY process full of fun and challenges. This article will take you deep into the 5-way Navigation Key Module, allowing you to get started easily and become a DIY expert!

The 5-way navigation button module can be said to be a “little elf” in the DIY world. It usually consists of a central key and four direction keys, enabling up, down, left, right, and confirmation functions. Although it is a small module, it can play a big role in many scenarios.

What is navigation button?What is 5-way navigation button?

Navigation buttons are a type of interactive control used to guide users in directional or hierarchical movement within interfaces (such as software, webpages, and device systems). Their core function is to help users quickly switch pages, return to the previous level, enter the next level, or adjust the view position.

A 5-way navigation button, which is the internal structure of the joystick we commonly see, has five key values (up, down, left, right, and the middle). The 5 way navigation button module can be connected to devices such as Arduino, microcontrollers, and Raspberry Pi. This module is equivalent to 7 independent key switches, with one end of each of the 7 switches connected to the common terminal COM.

5-Way Navigation Button Working Principle

The biggest difference of a 5-way switch lies in its five contact points. When the device needs to form a circuit path, the contact reeds can be directly connected to the common contact. The movable contact reeds can be linked together, and under the control of the operating lever, a single movable contact reed can be directly connected to its corresponding fixed contact.

  1. When no key is pressed

When no key is pressed, each functional pin (UP, DOWN, LEFT, RIGHT, MID) is in an open state relative to the COM pin. At this time, the level of these pins is determined by external pull-up or pull-down resistors. It is common to connect an external pull-up resistor to the positive power supply, so these pins are at a high level when no key is pressed.

  1. When a key is pressed

When a certain direction of the 5-way navigation key is pressed, the metal dome structure inside the key will connect the corresponding direction pin to the COM pin through conductive rubber or metal contacts.

For example:

When the “Up” direction is pressed, the UP pin is connected to the COM pin. If an external pull-up resistor is connected from the UP pin to VCC in the external circuit, the UP pin will be pulled to a low level after connection (since COM is usually grounded or connected to ground through a pull-down resistor). An external control device (such as a microcontroller) can determine that the user has pressed the “Up” direction key by detecting the level of the UP pin changing from high to low.

Similarly, when the “Down”, “Left”, “Right”, or “Middle” direction is pressed, the corresponding DOWN, LEFT, RIGHT, and MID pins are respectively connected to the COM pin, causing the level of the corresponding pin to change. After detecting the level change, the external device recognizes the corresponding key operation.

  1. SET and RST pinsoperation mechanism

① SET pin: When the SET pin is pulled to a specific level and held for a certain period of time, the module may enter the setting mode. In this mode, pressing other direction keys can set the parameters of the module. After the setting is completed, operating the SET pin again or waiting for a period of time, the module will save the settings and exit the setting mode.

② RST pin: When the RST pin is pulled to the trigger level, the internal programs and parameters of the module will be reset to the factory default settings, which is equivalent to reinitializing the module and returning it to the initial working state.

5-Way Navigation Button Principle

5-Way Navigation Button Feature

Key Direction Front, Back, Left, Right, Vertical Down
Input Voltage 3.3V-5V
Output Digital level (Low level when pressed, High level when released)
Platform Arduino, Microcontroller, ARM, Raspberry Pi
  • COM can be connected to VCC and GND, and its interrupt mode will vary depending on the wiring.

      1. If COM is connected to VCC, the interrupt mode is rising-edge trigger mode.

      2. If COM is connected to GND, the interrupt mode is falling-edge trigger mode.

5-Way Navigation Button Pin Function

5-way Navigation Key Pin Function
Pin Function Description
COM Common terminal Connect to the VCC or GND of the microcontroller
UP “Up” direction key Connect to the I/O pin of the microcontroller
DWN “Down” direction key Connect to the I/O pin of the microcontroller
LFT “Left” direction key Connect to the I/O pin of the microcontroller
RHT “Right” direction key Connect to the I/O pin of the microcontroller
MID “Middle” direction key Connect to the I/O pin of the microcontroller
SET “SET” key Connect to the I/O pin of the microcontroller
RST “RST” key Connect to the I/O pin of the microcontroller

5-Way Navigation Button Wiring Instruction

5-Way Navigation Button Wiring Instruction

5-Way Navigation Button Arduino

5-Way Navigation Button Pinout

5-Way Navigation ButtonArduino
COMGND
UP2
DWM3
LFT4
RHT5
MID6
SET7
RST8
OLEDArduino
VCC3.3V
GNDGND
SDAA4
SCLA5

5-Way Navigation Button Code

				
					#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// OLED Screen Configuration (ZJY_M2420: 128x32)
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C // Change to 0x3D if not displaying
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// 5-Way Key Pin Definitions
#define KEY_UP     2
#define KEY_DOWN   3
#define KEY_LEFT   4
#define KEY_RIGHT  5
#define KEY_SET    7
#define KEY_RST    8

// Game Constants (Optimized for Memory & Slow Speed)
#define GRID_SIZE 4
#define GRID_WIDTH (SCREEN_WIDTH / GRID_SIZE)
#define GRID_HEIGHT (SCREEN_HEIGHT / GRID_SIZE)
#define GAME_SPEED_INIT 500  // Slow initial speed (500ms, bigger = slower)
#define MAX_SNAKE_LENGTH 30
#define MAX_SPEED 200        // Slow max speed (200ms, not too fast)
#define SPEED_UP_STEP 2      // Small speed up step (slow difficulty increase)

// Game State Enumeration
enum GameState {
  GAME_READY,
  GAME_PLAYING,
  GAME_PAUSED,
  GAME_OVER
};
GameState currentState = GAME_READY;

// Snake Direction Enumeration
enum Direction {
  DIR_UP,
  DIR_DOWN,
  DIR_LEFT,
  DIR_RIGHT
};

// Snake Structure (Optimized)
struct SnakeSegment {
  uint8_t x;
  uint8_t y;
};
SnakeSegment snake[MAX_SNAKE_LENGTH];
uint8_t snakeLength = 3;
Direction currentDir = DIR_RIGHT;
Direction nextDir = DIR_RIGHT;

// Food Variables
uint8_t foodX, foodY;
uint8_t score = 0;
unsigned long gameTimer = 0;
uint16_t gameSpeed = GAME_SPEED_INIT;

// Key Debounce Variables
unsigned long lastKeyCheck = 0;
const unsigned long debounceDelay = 200;

// Initialize Game
void setup() {
  // Disable Serial Debug (Save memory)
  // Serial.begin(9600);

  // Initialize Key Pins
  pinMode(KEY_UP, INPUT_PULLUP);
  pinMode(KEY_DOWN, INPUT_PULLUP);
  pinMode(KEY_LEFT, INPUT_PULLUP);
  pinMode(KEY_RIGHT, INPUT_PULLUP);
  pinMode(KEY_SET, INPUT_PULLUP);
  pinMode(KEY_RST, INPUT_PULLUP);

  // Initialize OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    for (;;);
  }

  // Global smallest font
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  drawWelcomeScreen();
}

// Main Game Loop
void loop() {
  // Key Scan
  char pressedKey = '\0';
  if (millis() - lastKeyCheck >= debounceDelay) {
    pressedKey = scanKeys();
    lastKeyCheck = millis();
  }

  // Handle Game States
  switch (currentState) {
    case GAME_READY:
      handleReadyState(pressedKey);
      break;
    case GAME_PLAYING:
      handlePlayingState(pressedKey);
      break;
    case GAME_PAUSED:
      handlePausedState(pressedKey);
      break;
    case GAME_OVER:
      handleGameOverState(pressedKey);
      break;
  }
}

// Scan Pressed Key
char scanKeys() {
  if (digitalRead(KEY_UP) == LOW) return 'U';
  if (digitalRead(KEY_DOWN) == LOW) return 'D';
  if (digitalRead(KEY_LEFT) == LOW) return 'L';
  if (digitalRead(KEY_RIGHT) == LOW) return 'R';
  if (digitalRead(KEY_SET) == LOW) return 'S';
  if (digitalRead(KEY_RST) == LOW) return 'T';
  return '\0';
}

// Handle Ready State
void handleReadyState(char pressedKey) {
  if (pressedKey == 'S') {
    initGame();
    currentState = GAME_PLAYING;
    gameTimer = millis();
  }
}

// Handle Playing State
void handlePlayingState(char pressedKey) {
  handleKeyInput(pressedKey);

  if (millis() - gameTimer >= gameSpeed) {
    updateDirection();
    moveSnake();

    if (checkCollision()) {
      currentState = GAME_OVER;
      drawGameOverScreen();
      return;
    }

    if (snake[0].x == foodX && snake[0].y == foodY) {
      eatFood();
    }

    drawGameScreen();
    gameTimer = millis();
  }
}

// Handle Paused State
void handlePausedState(char pressedKey) {
  if (pressedKey == 'S') {
    currentState = GAME_PLAYING;
    gameTimer = millis();
    drawGameScreen();
  } else if (pressedKey == 'T') {
    currentState = GAME_READY;
    drawWelcomeScreen();
  }
}

// Handle Game Over State
void handleGameOverState(char pressedKey) {
  if (pressedKey == 'T') {
    currentState = GAME_READY;
    drawWelcomeScreen();
  }
}

// Initialize Game Parameters
void initGame() {
  // Reset Snake
  snakeLength = 3;
  currentDir = DIR_RIGHT;
  nextDir = DIR_RIGHT;
  snake[0].x = 5; snake[0].y = GRID_HEIGHT / 2;
  snake[1].x = 4; snake[1].y = GRID_HEIGHT / 2;
  snake[2].x = 3; snake[2].y = GRID_HEIGHT / 2;

  // Spawn Food
  spawnFood();

  // Reset Score and Slow Speed
  score = 0;
  gameSpeed = GAME_SPEED_INIT;

  drawGameScreen();
}

// Handle Key Input
void handleKeyInput(char pressedKey) {
  if (pressedKey == 'S') {
    if (currentState == GAME_PLAYING) {
      currentState = GAME_PAUSED;
      drawPausedScreen();
    }
    return;
  }

  // Direction Control
  if (pressedKey == 'U' && currentDir != DIR_DOWN) {
    nextDir = DIR_UP;
  } else if (pressedKey == 'D' && currentDir != DIR_UP) {
    nextDir = DIR_DOWN;
  } else if (pressedKey == 'L' && currentDir != DIR_RIGHT) {
    nextDir = DIR_LEFT;
  } else if (pressedKey == 'R' && currentDir != DIR_LEFT) {
    nextDir = DIR_RIGHT;
  }
}

// Update Direction
void updateDirection() {
  currentDir = nextDir;
}

// Move Snake
void moveSnake() {
  uint8_t prevX = snake[0].x;
  uint8_t prevY = snake[0].y;

  // Update Head
  switch (currentDir) {
    case DIR_UP:
      snake[0].y--;
      break;
    case DIR_DOWN:
      snake[0].y++;
      break;
    case DIR_LEFT:
      snake[0].x--;
      break;
    case DIR_RIGHT:
      snake[0].x++;
      break;
  }

  // Update Body
  for (uint8_t i = 1; i < snakeLength; i++) {
    uint8_t tempX = snake[i].x;
    uint8_t tempY = snake[i].y;
    snake[i].x = prevX;
    snake[i].y = prevY;
    prevX = tempX;
    prevY = tempY;
  }
}

// Check Collision
bool checkCollision() {
  // Wall Collision
  if (snake[0].x >= GRID_WIDTH || snake[0].y >= GRID_HEIGHT) {
    return true;
  }

  // Self Collision
  for (uint8_t i = 1; i < snakeLength; i++) {
    if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
      return true;
    }
  }

  return false;
}

// Spawn Food
void spawnFood() {
  bool foodOnSnake;
  do {
    foodOnSnake = false;
    foodX = random(GRID_WIDTH);
    foodY = random(GRID_HEIGHT);

    for (uint8_t i = 0; i < snakeLength; i++) {
      if (snake[i].x == foodX && snake[i].y == foodY) {
        foodOnSnake = true;
        break;
      }
    }
  } while (foodOnSnake);
}

// Eat Food (Slow Speed Up)
void eatFood() {
  // Grow Snake
  if (snakeLength < MAX_SNAKE_LENGTH) {
    snake[snakeLength].x = snake[snakeLength - 1].x;
    snake[snakeLength].y = snake[snakeLength - 1].y;
    snakeLength++;
  }

  // Update Score
  score += 10;

  // Slow Speed Up (Only if faster than max slow speed)
  if (gameSpeed > MAX_SPEED) {
    gameSpeed -= SPEED_UP_STEP;
  }

  spawnFood();
}

// Draw Welcome Screen
void drawWelcomeScreen() {
  display.clearDisplay();

  display.setCursor(40, 8);
  display.print(F("Snake Game"));

  display.setCursor(15, 18);
  display.print(F("SET: Start/Pause"));
  display.setCursor(15, 28);
  display.print(F("RST: Reset Game"));

  display.display();
}

// Draw Game Screen
void drawGameScreen() {
  display.clearDisplay();

  // Draw Snake
  for (uint8_t i = 0; i < snakeLength; i++) {
    display.fillRect(snake[i].x * GRID_SIZE, snake[i].y * GRID_SIZE,
                     GRID_SIZE - 1, GRID_SIZE - 1, SSD1306_WHITE);
  }

  // Draw Food
  display.fillRect(foodX * GRID_SIZE, foodY * GRID_SIZE,
                   GRID_SIZE - 1, GRID_SIZE - 1, SSD1306_WHITE);

  // Draw Score
  display.setCursor(0, 0);
  display.print(F("Score: "));
  display.print(score);

  display.display();
}

// Draw Paused Screen
void drawPausedScreen() {
  display.clearDisplay();

  display.setCursor(50, 12);
  display.print(F("PAUSED"));

  display.setCursor(35, 22);
  display.print(F("SET to resume"));

  display.display();
}

// Draw Game Over Screen
void drawGameOverScreen() {
  display.clearDisplay();

  display.setCursor(45, 10);
  display.print(F("GAME OVER"));

  display.setCursor(35, 20);
  display.print(F("Final Score: "));
  display.print(score);

  display.display();
}
				
			

5-Way Navigation Button Effect Demonstration

5-Way Navigation Button Application

1. Embedded development, DIY robot movement/mode switching

2. TV remote control

3. Game console direction control

4. Smart wearable devices

Purchase Link

FAQ

Why is my 5-way navigation button not responding correctly?

There are several possible reasons :

  1. Loose wiring

Check whether the module’s pins (COM, UP, DOWN, etc.) are securely connected to the controller. Poor contact will cause detection failure.

  1. Power supply issues

Ensure that the supply voltage meets the module specifications (usually 3.3 V or 5 V) and that the power supply is stable.

  1. Signal interference or noise

Electrical noise may cause misreading; hardware debouncing can be achieved by adding pull-up/pull-down resistors or capacitors.

  1. Software logic errors

If using a microcontroller, confirm that the input pin configuration is correct (e.g., INPUT_PULLUP) and that the reading/debouncing code is functioning properly.

  1. Mechanical aging of keys

Wear or contamination of the internal structure of the keys can lead to poor contact, requiring cleaning or replacement of the keys.

Is a 5-way navigation button better than a joystick or touchpad for embedded projects?

It depends on application requirements:

  1. 5-way button

Simple, low-cost, and reliable discrete direction selection, suitable for menu navigation and limited motion control.

  1. Joystick

Provides analog or continuous direction input, more suitable for variable speed or angle control (such as robot steering).

  1. Touchpad

Supports multi-touch and large-area interaction, but requires more complex drivers and processing.

Leave a Reply

Your email address will not be published. Required fields are marked *