Community project

Joystick-Controlled Relay Switches

Arduino
Photo of Joystick-Controlled Relay Switches
Generated with AI

Mohamed Mediouni

Published September 21, 2026

This project builds a two-channel relay controller using a joystick to switch mains-powered appliances on and off. The KY-023 joystick module provides directional input to select between two relay channels, while an LCD1602 display shows the current state of each relay in real time.

The guide includes a complete wiring diagram, parts list, and Arduino firmware with debouncing and relay-state management. Assembly steps keep high-voltage terminals safely disconnected until testing is complete, and the firmware handles the active-LOW relay logic common to most 5V relay boards. Builders will learn relay switching fundamentals and gain a reusable controller template for home automation projects.

Wiring diagram

Wiring diagram for Joystick-Controlled Relay Switches

Gather all the parts

QtyComponent
1

KY-023 Dual Axis Joystick Module

KY-023

Dual-axis analog joystick breakout (PSP/PS2-style thumbstick) with two perpendicular 10 kOhm potentiometers and an integrated push-button. Outputs analog voltages on VRx and VRy proportional to stick position, plus an active-low digital switch (SW) that is pulled LOW when the stick is pressed. Operates 3.3 V to 5 V; on 5 V boards the VRx/VRy swing matches the wider ADC range. SW should be read with INPUT_PULLUP.

1

2-Channel 5V Relay Module

2-channel 5 V relay module

A ready-made two-relay board that lets the Arduino switch two separate external circuits.

1

LCD1602 blue-backlight character display (AIP31066)

16×2, blue backlight, 5 V

A 5 V blue-backlit screen that can show two lines of 16 characters for the relay controller status.

Assemble it in 7 steps

1. Keep the 220 V terminals disconnected while building

Leave the relay board screw terminals labelled COM, NO, and NC empty while connecting the Arduino, joystick, and screen. Put the relay board on a non-metal surface and keep its screw-terminal side away from loose jumper wires.

  • You can safely test the controls first by listening for each relay click.
  • Those screw terminals can carry dangerous 220 V electricity. Do not touch or wire them while anything is connected to wall power.

2. Wire the joystick

Connect joystick_1 VCC to Uno 5V (power), joystick_1 GND to Uno GND (ground), joystick_1 VRx to A0 (spare position signal), joystick_1 VRy to A1 (selection signal), and joystick_1 SW to D2 (press signal).

  • The pin labels are normally printed on the back of the joystick board.
  • Make sure VCC and GND are not swapped — swapped power can damage the joystick board.

3. Wire the relay board's low-voltage header

Connect relay_2ch_1 VCC to Uno 5V (power), relay_2ch_1 GND to Uno GND (ground), relay_2ch_1 IN1 to D7 (relay 1 control signal), and relay_2ch_1 IN2 to D8 (relay 2 control signal).

  • Keep these small header wires well away from the relay screw terminals.
  • Do not power an appliance from the Uno 5V pin. The Uno only controls the relay board; appliance power belongs on the relay screw terminals.

4. Wire the LCD power and contrast pins

With the LCD pins numbered 1 to 16 from left to right when you face the display and its pins point downward, connect lcd1602_1 VSS/pin 1 to Uno GND (ground), VDD/pin 2 to Uno 5V (power), VO/pin 3 to Uno GND (fixed text contrast), R/W/pin 5 to Uno GND (write-only setting), LED_A/pin 15 to Uno 5V (blue backlight power), and LED_K/pin 16 to Uno GND (blue backlight ground).

  • If you see the blue glow but no letters, check the pin order first; the screen pin labels are usually printed on its circuit board.
  • Make sure VDD and VSS are not swapped — swapped power can damage the screen.

5. Wire the LCD text-control pins

Connect lcd1602_1 RS/pin 4 to D3 (text or command signal), E/pin 6 to D4 (send signal), DB4/pin 11 to D5 (text data), DB5/pin 12 to D6 (text data), DB6/pin 13 to D9 (text data), and DB7/pin 14 to D10 (text data). Leave DB0 through DB3, pins 7 through 10, unconnected because this project uses the screen's four-wire mode.

  • Count slowly from pin 1: the LCD has 16 closely-spaced pins, so one shifted wire prevents the text from appearing.
  • Unplug USB before moving any wire. A misplaced wire can stop the screen or relays from working.

6. Test the controller before connecting an appliance

Plug the Uno into USB. The screen should say which relay is selected and show R1 and R2 as OFF. Push the joystick up for relay 1 or down for relay 2, then press it once to switch that relay on or off; the screen changes and the selected relay clicks.

  • If the screen lights but shows no letters, recheck its pin 3 VO connection to GND and the six text-control wires.
  • Do not rely on software alone as a safety switch for a dangerous appliance; unplug power before moving wires.

7. Connect a mains appliance only if qualified

After the low-voltage controller works, unplug all power. For each appliance, route only its live wire through that relay channel's COM and NO screw terminals so it is normally off. Keep neutral and protective earth continuous according to local electrical rules, and place all mains wiring in a closed insulated enclosure.

  • If you are not trained for mains wiring, have a qualified electrician make or inspect this connection.
  • 220 V can cause fatal electric shock or fire. Never work on live wiring, never leave exposed mains terminals accessible, and do not exceed the relay board's marked voltage and current rating.

Review all connections

1. Connections between "joystick_1" and "Arduino"

Functionjoystick_1Arduino
powerVCC5V
groundGNDGND
adcVRxGPIO 14
adcVRyGPIO 15
digitalSWGPIO 2

2. Connections between "relay_2ch_1" and "Arduino"

Functionrelay_2ch_1Arduino
powerVCC5V
groundGNDGND
digitalIN1GPIO 7
digitalIN2GPIO 8

3. Connections between "lcd1602_1" and "Arduino"

Functionlcd1602_1Arduino
groundVSSGND
powerVDD5V
groundVOGND
digitalRSGPIO 3
groundR/WGND
digitalEGPIO 4
digitalDB4GPIO 5
digitalDB5GPIO 6
digitalDB6GPIO 9
digitalDB7GPIO 10
powerLED_A5V
groundLED_KGND

Deploy the firmware

#include <Arduino.h>
#include <LiquidCrystal.h>

// Two-channel relay controller with a KY-023 joystick and a 16x2 AIP31066 LCD.
// Many 5 V relay boards are active LOW: LOW turns the relay on.

enum SelectedRelay { CHANNEL_1, CHANNEL_2 };


// Forward declarations
void applyRelayOutputs();
void printStatus();
void updateDisplay();
void selectFromJoystick();
void handleButton();

const uint8_t JOYSTICK_X_PIN = A0;
const uint8_t JOYSTICK_Y_PIN = A1;
const uint8_t JOYSTICK_BUTTON_PIN = 2;
const uint8_t LCD_RS_PIN = 3;
const uint8_t LCD_ENABLE_PIN = 4;
const uint8_t LCD_D4_PIN = 5;
const uint8_t LCD_D5_PIN = 6;
const uint8_t RELAY_1_PIN = 7;
const uint8_t RELAY_2_PIN = 8;
const uint8_t LCD_D6_PIN = 9;
const uint8_t LCD_D7_PIN = 10;

const bool RELAY_ON_LEVEL = LOW;
const bool RELAY_OFF_LEVEL = HIGH;
const int JOYSTICK_LOW = 300;
const int JOYSTICK_HIGH = 700;
const unsigned long DEBOUNCE_MS = 35;
const unsigned long SELECTION_REPEAT_MS = 180;

LiquidCrystal lcd(LCD_RS_PIN, LCD_ENABLE_PIN, LCD_D4_PIN, LCD_D5_PIN,
                  LCD_D6_PIN, LCD_D7_PIN);

SelectedRelay selected = CHANNEL_1;
bool relay1On = false;
bool relay2On = false;
bool lastButtonReading = HIGH;
bool stableButtonState = HIGH;
unsigned long lastDebounceTime = 0;
unsigned long lastSelectionTime = 0;

void applyRelayOutputs() {
  digitalWrite(RELAY_1_PIN, relay1On ? RELAY_ON_LEVEL : RELAY_OFF_LEVEL);
  digitalWrite(RELAY_2_PIN, relay2On ? RELAY_ON_LEVEL : RELAY_OFF_LEVEL);
}

void printStatus() {
  Serial.print(F("Selected channel: "));
  Serial.print(selected == CHANNEL_1 ? 1 : 2);
  Serial.print(F(" | Relay 1: "));
  Serial.print(relay1On ? F("ON") : F("OFF"));
  Serial.print(F(" | Relay 2: "));
  Serial.println(relay2On ? F("ON") : F("OFF"));
}

void updateDisplay() {
  // Redraw only after selection or relay state changes, not on every loop.
  lcd.setCursor(0, 0);
  lcd.print(F("Selected: Relay "));
  lcd.print(selected == CHANNEL_1 ? 1 : 2);
  lcd.print(F(" "));

  lcd.setCursor(0, 1);
  lcd.print(F("R1:"));
  lcd.print(relay1On ? F("ON ") : F("OFF"));
  lcd.print(F(" R2:"));
  lcd.print(relay2On ? F("ON ") : F("OFF"));
  lcd.print(F("  "));
}

void selectFromJoystick() {
  int yValue = analogRead(JOYSTICK_Y_PIN);
  SelectedRelay newSelection = selected;

  if (yValue < JOYSTICK_LOW) {
    newSelection = CHANNEL_1;
  } else if (yValue > JOYSTICK_HIGH) {
    newSelection = CHANNEL_2;
  }

  if (newSelection != selected && millis() - lastSelectionTime >= SELECTION_REPEAT_MS) {
    selected = newSelection;
    lastSelectionTime = millis();
    printStatus();
    updateDisplay();
  }
}

void handleButton() {
  bool reading = digitalRead(JOYSTICK_BUTTON_PIN);

  if (reading != lastButtonReading) {
    lastDebounceTime = millis();
  }

  if (millis() - lastDebounceTime > DEBOUNCE_MS && reading != stableButtonState) {
    stableButtonState = reading;
    if (stableButtonState == LOW) {
      if (selected == CHANNEL_1) {
        relay1On = !relay1On;
      } else {
        relay2On = !relay2On;
      }
      applyRelayOutputs();
      printStatus();
      updateDisplay();
    }
  }

  lastButtonReading = reading;
}

void setup() {
  pinMode(JOYSTICK_X_PIN, INPUT);
  pinMode(JOYSTICK_Y_PIN, INPUT);
  pinMode(JOYSTICK_BUTTON_PIN, INPUT_PULLUP);
  pinMode(RELAY_1_PIN, OUTPUT);
  pinMode(RELAY_2_PIN, OUTPUT);

  // Keep both relays off before the controller begins reading the joystick.
  applyRelayOutputs();

  Serial.begin(9600);
  lcd.begin(16, 2);
  updateDisplay();

  Serial.println(F("Relay controller ready."));
  Serial.println(F("Push up for relay 1, down for relay 2, then press to toggle."));
  printStatus();
}

void loop() {
  selectFromJoystick();
  handleButton();
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik