Hello and welcome back to our electronics blog series! This is blog 5 and we will explore a practical project: using a joystick to write and edit text on an LCD. This is an interactive way to learn electronics and programming basics.
We will use a popular I2C LCD display (the same LCD as in the Ultrasound & Display blog) and a joystick to create an interactive writing environment. Arduino and some programming let us navigate the screen and write, edit and delete text with the joystick.
This project explores input/output devices, analogue and digital signals, and Arduino libraries and functions. Join us in exploring electronics—let’s begin!
Components Used in This Project
| Arduino UNO |
| KY-023 Joystick |
| 20x4 LCD |
| Jumper cables matching the connectors |
| Jumper wires |
| Breadboard |
Here is a brief explanation of the components:
- 20x4 LCD Display: An alphanumeric screen with 4 rows of 20 characters. Its I2C backpack lets it communicate with Arduino and show text and symbols with fewer connections.
- Arduino Uno: Arduino Uno is the microcontroller board acting as our project’s brain once again. It controls the LCD and processes joystick input using programmed logic.
- LCD Connection Cables: Choose cables matching the hardware. A display with male pins usually needs female-to-male cables to connect directly to UNO’s female headers; female-to-female cables only fit when both ends have male pins.
- Other Jumper Wires: These connect Arduino Uno, the joystick and other circuit points. They are flexible and easy to connect and disconnect.
- KY-023 Joystick: The main input device uses analogue signals for x/y movement and a digital pushbutton. It is a PS2-style joystick module that is easy to connect and use.
- Breadboard: A prototyping board makes temporary connections between electronic components, keeping them organised and easy to connect during development.
These components are enough to build the writing tool and explore writing and editing with the joystick and LCD.
KY-023
Let’s look more closely at the joystick used in the project: a KY-023 joystick. It is a compact, versatile input device with two axes and a pushbutton.

It has two analogue axes, VRx and VRy, which detect horizontal and vertical movement. Their voltage levels vary with joystick position. Reading them identifies its direction.
It also has a pushbutton, called SW, used as a digital input. It indicates whether the button is pressed, allowing programmed actions when activated.
- 5V: The supply pin connects to a +5V source on the Arduino board or another suitable supply module to power the joystick.
- VRx: The analogue x-axis output provides a voltage depending on horizontal position. Connect it to an analogue input on the Arduino board.
- VRy: The analogue y-axis output varies with vertical position. Connect it to another analogue input on the Arduino board.
- SW: SW is the switch contact. With the pull-up enabled, the Arduino input reads HIGH when released and LOW when pressed. Connect SW to a digital input on the Arduino board.
Now that we understand the joystick, let’s examine how the project circuit connects.
The Project Circuit
This circuit is fairly simple. Connect the 20x4 LCD display and Arduino Uno as in the ultrasonic distance-measurement blog: GND to GND, VCC to 5V, SDA to A4 and SCL to A5 on the classic UNO. Connecting the LCD to Arduino Uno lets us write and display text in real time.
For the joystick, first connect SW to pin 2 on Arduino Uno. This detects joystick button presses. Also connect joystick GND to Arduino GND.
To power the joystick, connect VCC on the joystick to 5V on Arduino Uno. This provides the supply voltage.
Finally, connect joystick VRx to A0 on Arduino Uno and VRy to A1 on Arduino Uno. These connections read the analogue movement values. The figure below shows the circuit.

The Code
Although the circuit is simple, this is the most advanced code in the series so far. It handles joystick input and controls the LCD display in real time, using analogue inputs, conditions, loops and LCD control.
Here is the code. We will go through it line by line.
#include <Wire.h> // Inkluderer Wire biblioteket, der bruges til I2C kommunikation
#include <LiquidCrystal_I2C.h> // Inkluderer LiquidCrystal_I2C biblioteket til styring af I2C LCD
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7); // Initialiserer et LiquidCrystal_I2C objekt til at styre LCD
const int VRx = 0; // Definerer VRx pinnummeret til joystickens x-retning
const int VRy = 1; // Definerer VRy pinnummeret til joystickens y-retning
const int switchPin = 2; // Definerer switchPin pinnummeret til joystickens knap
int LCDpos80 = 0; // Initialiserer variablen LCDpos80 til at holde styr på LCD positionen
bool flip = true; // En variabel til at skifte mellem at vise og skjule markøren
char line[4][21] = {
// En todimensional array til at opbevare teksten til hver række på LCD
"|Hello! ", // Første række
"|You can ", // Anden række
"|write anything ", // Tredje række
"|on this display " // Fjerde række
};
void setup() {
lcd.begin(20, 4); // Initialiserer LCD-displayet med 20 tegn pr. linje og 4 linjer
lcd.setBacklightPin(3, POSITIVE); // Sætter bagbelysningspin til ben 3 og polaritet som positiv
lcd.setBacklight(HIGH); // Tænder for bagbelysningen
pinMode(switchPin, INPUT); // Sætter switchPin til input-tilstand
digitalWrite(switchPin, HIGH); // Aktiverer den interne pull-up modstand på switchPin
line[0][19] = '|'; // Indsætter et lodret linjetegn i slutningen af første række
line[1][19] = '|'; // Indsætter et lodret linjetegn i slutningen af anden række
line[2][19] = '|'; // Indsætter et lodret linjetegn i slutningen af tredje række
line[3][19] = '|'; // Indsætter et lodret linjetegn i slutningen af fjerde række
}
void checkJoy() {
static unsigned long buttonPressStartTime = 0;
bool buttonPressed = (digitalRead(switchPin) == LOW);
// Justér positionen til venstre eller højre baseret på VRx-værdien
if (analogRead(VRx) < 175 && LCDpos80 > 0) {
LCDpos80--;
} else if (analogRead(VRx) > 530 && LCDpos80 < 79) {
LCDpos80++;
}
// Justér værdien i den aktuelle placeholder op eller ned baseret på VRy-værdien
if (LCDpos80 >= 0 && LCDpos80 <= 79) {
if (analogRead(VRy) < 175 && line[LCDpos80 / 20][LCDpos80 % 20] < 127) {
line[LCDpos80 / 20][LCDpos80 % 20]++;
} else if (analogRead(VRy) > 530 && line[LCDpos80 / 20][LCDpos80 % 20] > 32) {
line[LCDpos80 / 20][LCDpos80 % 20]--;
}
}
// Hvis knappen er trykket ned, gemmes starttiden for knaptrykket
if (buttonPressed && buttonPressStartTime == 0) {
buttonPressStartTime = millis(); // Gem starttiden for knaptrykket
}
// Hvis knappen er sluppet og der er registreret en starttid for knaptrykket
if (!buttonPressed && buttonPressStartTime != 0) {
unsigned long buttonPressDuration = millis() - buttonPressStartTime;
buttonPressStartTime = 0; // Nulstil starttiden for knaptrykket
// Hvis knappen er holdt nede i mindst 1 sekund
if (buttonPressDuration >= 1000) {
// Ryd hele displayet
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 20; j++) {
line[i][j] = ' ';
}
}
}
// Hvis knappen slippes inden for 1 sekund og positionen er inden for gyldigt område
else if (buttonPressDuration < 1000 && LCDpos80 >= 0 && LCDpos80 <= 79) {
// Ryd den aktuelle placeholder
line[LCDpos80 / 20][LCDpos80 % 20] = ' ';
}
}
}
void LCD() { // Funktion til at opdatere LCD-displayet
int row = LCDpos80 / 20; // Beregn rækken baseret på værdien af LCDpos80
int col = LCDpos80 % 20; // Beregn kolonnen baseret på værdien af LCDpos80
lcd.setCursor(0, 0); // Sæt markøren til (0, 0) (øverste venstre hjørne)
lcd.print(line[0]); // Udskriv indholdet af line[0] i LCD-displayet
lcd.setCursor(0, 1); // Sæt markøren til (0, 1) (anden række)
lcd.print(line[1]); // Udskriv indholdet af line[1] i LCD-displayet
lcd.setCursor(0, 2); // Sæt markøren til (0, 2) (tredje række)
lcd.print(line[2]); // Udskriv indholdet af line[2] i LCD-displayet
lcd.setCursor(0, 3); // Sæt markøren til (0, 3) (fjerde række)
lcd.print(line[3]); // Udskriv indholdet af line[3] i LCD-displayet
if (flip) {
lcd.setCursor(col, row); // Sæt markøren til den aktuelle position (col, row)
lcd.print('_'); // Udskriv et understregningstegn i LCD-displayet på den aktuelle position
}
flip = !flip; // Skift værdien af flip-variablen
delay(100); // Vent i 100 millisekunder
}
void switchCategory() {
static unsigned long pressStartTime = 0;
unsigned long currentTime = millis(); // Deklarér variablen currentTime
// Tjek om knappen er trykket ned og om den aktuelle placeholder er tom
bool buttonPressed = (analogRead(VRy) > 530 && line[LCDpos80 / 20][LCDpos80 % 20] == ' ');
// Hvis knappen er trykket ned og det er første gang, gemmes starttiden for trykket
if (buttonPressed && pressStartTime == 0) {
pressStartTime = currentTime; // Gem starttiden for trykket
}
// Hvis knappen slippes og der er registreret en starttid for trykket
if (!buttonPressed && pressStartTime != 0) {
unsigned long pressDuration = currentTime - pressStartTime; // Beregn varigheden af trykket
pressStartTime = 0; // Nulstil starttiden for trykket
// Bestem kategorien baseret på trykkets varighed
if (pressDuration >= 0 && pressDuration < 1000) {
line[LCDpos80 / 20][LCDpos80 % 20] = 'A'; // Kategori A
} else if (pressDuration >= 1000 && pressDuration < 2000) {
line[LCDpos80 / 20][LCDpos80 % 20] = 'a'; // Kategori a
} else if (pressDuration >= 2000 && pressDuration < 3000) {
line[LCDpos80 / 20][LCDpos80 % 20] = '1'; // Kategori 1
} else if (pressDuration >= 3000 && pressDuration < 4000) {
line[LCDpos80 / 20][LCDpos80 % 20] = '.'; // Kategori .
// Tilføj flere betingelser for yderligere kategorier efter behov
}
}
}
void loop() { // Hovedløkken for programmet
checkJoy(); // Kald checkJoy-funktionen for at håndtere joystick-input
LCD(); // Kald LCD-funktionen for at opdatere LCD-displayet
switchCategory(); // Kald switchCategory-funktionen for at skrive hurtigere
}
Let’s Take a Closer Look at the Code...
#include <Wire.h> // Vi inkluderer Wire-biblioteket, som giver os mulighed for at kommunikere via I2C-protokollen.
#include <LiquidCrystal_I2C.h> // Vi inkluderer LiquidCrystal_I2C-biblioteket, der giver os funktioner til at styre vores I2C-LCD.
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7); // Vi initialiserer et LiquidCrystal_I2C-objekt med den specifikke I2C-adresse (0x27) og tilsluttede pins på Arduinoen (2, 1, 0, 4, 5, 6, 7). Dette objekt vil blive brugt til at styre vores LCD-display
This section includes the libraries needed for the I2C LCD and creates an object, lcd, of type LiquidCrystal_I2C. It also specifies the connected pins. With these libraries and the object, we can control the LCD and show text.
const int VRx = 0; // Vi definerer VRx som variabel og tildeler den pinnummeret (0) til joystickens x-retning.
const int VRy = 1; // Vi definerer VRy som variabel og tildeler den pinnummeret (1) til joystickens y-retning.
const int switchPin = 2; // Vi definerer switchPin som variabel og tildeler den pinnummeret (2) til joystickens knap.
int LCDpos80 = 0; // Vi initialiserer variablen LCDpos80 til 0 for at holde styr på LCD'ens position.
bool flip = true; // Vi opretter en boolsk variabel ved navn "flip" og tildeler den værdien "true". Denne variabel bruges til at skifte mellem at vise og skjule markøren.
These lines define and initialise joystick and LCD variables. VRx is the pin number for the joystick x-axis, VRy is the pin number for the joystick y-axis, and switchPin is the pin number for the joystick button.
The variable LCDpos80 tracks the LCD position and is later used to position the cursor.
flip is a Boolean variable starting at true. It will alternate between showing and hiding the LCD cursor later in the code.
char line[4][21] = {
// En todimensional array til at opbevare teksten til hver række på LCD
"|Hello! ", // Første række
"|You can ", // Anden række
"|write anything ", // Tredje række
"|on this display " // Fjerde række
};
This section declares a two-dimensional array named line. It stores the text for each LCD row.
The array line[4][21] has four rows of 21 bytes: 20 characters plus a null terminator per row, 84 bytes total. The previous 80-row declaration used 1680 bytes without using the extra rows. All row accesses are limited to 0–3, so the main example and excerpts have been corrected to four rows.
The first four rows already contain text. Each string is enclosed in quotation marks (") and entries are separated by commas (,). Each entry represents a line on the LCD.
Edit the array to customise the text. Each visible row holds at most 20 characters plus a terminating null byte.
This initialises the LCD text so we can edit and manipulate it later.
void setup() {
lcd.begin(20, 4); // Initialiserer LCD-displayet med 20 tegn pr. linje og 4 linjer
lcd.setBacklightPin(3, POSITIVE); // Sætter bagbelysningspin til ben 3 og polaritet som positiv
lcd.setBacklight(HIGH); // Tænder for bagbelysningen
pinMode(switchPin, INPUT); // Sætter switchPin til input-tilstand
digitalWrite(switchPin, HIGH); // Aktiverer den interne pull-up modstand på switchPin
line[0][19] = '|'; // Indsætter et lodret linjetegn i slutningen af første række
line[1][19] = '|'; // Indsætter et lodret linjetegn i slutningen af anden række
line[2][19] = '|'; // Indsætter et lodret linjetegn i slutningen af tredje række
line[3][19] = '|'; // Indsætter et lodret linjetegn i slutningen af fjerde række
}
2 / 2
I setup()-funktionen udføres den indledende konfiguration og initialisering af vores projekt. Lad os gennemgå koden linje for linje:
cpp
void setup() {
lcd.begin(20, 4); // Initialiserer LCD-displayet med 20 tegn pr. linje og 4 linjer
lcd.setBacklightPin(3, POSITIVE); // Sætter bagbelysningspin til ben 3 og polaritet som positiv
lcd.setBacklight(HIGH); // Tænder for bagbelysningen
pinMode(switchPin, INPUT); // Sætter switchPin til input-tilstand
digitalWrite(switchPin, HIGH); // Aktiverer den interne pull-up modstand på switchPin
line[0][19] = '|'; // Indsætter et lodret linjetegn i slutningen af første række
line[1][19] = '|'; // Indsætter et lodret linjetegn i slutningen af anden række
line[2][19] = '|'; // Indsætter et lodret linjetegn i slutningen af tredje række
line[3][19] = '|'; // Indsætter et lodret linjetegn i slutningen af fjerde række
}
In setup() function we do the following:
- lcd.begin(20, 4);: Initialise the LCD by calling begin() on the lcd object, specifying 20 characters per row and 4 rows.
- lcd.setBacklightPin(3, POSITIVE);: In this library’s mapping, 3 refers to the I2C expander’s backlight-control bit, not Arduino pin 3. POSITIVE defines the active polarity; verify the actual backpack wiring.
- lcd.setBacklight(HIGH);: Turn on the LCD backlight by calling setBacklight() with the argument HIGH.
- pinMode(switchPin, INPUT);: Set switchPin as an input using pinMode() with the pin number and mode.
- digitalWrite(switchPin, HIGH);: Enable the internal pull-up on switchPin using digitalWrite() with the pin number and value HIGH. The button input stays HIGH when released.
The final four lines in setup() demonstrate changing the initial LCD text at startup. Adding a vertical bar (|) at the end of each of the first four rows in line changes the initial display. It creates a visual divider and customises presentation. Experiment with the text and layout for your preferred effect.
void checkJoy() {
static unsigned long buttonPressStartTime = 0;
bool buttonPressed = (digitalRead(switchPin) == LOW);
// Justér positionen til venstre eller højre baseret på VRx-værdien
if (analogRead(VRx) < 175 && LCDpos80 > 0) {
LCDpos80--;
} else if (analogRead(VRx) > 530 && LCDpos80 < 79) {
LCDpos80++;
}
checkJoy() function monitors joystick movement and button presses. First it creates a static variable, buttonPressStartTime, to store when a button press starts.
Next a Boolean variable, buttonPressed, indicates whether switchPin reads LOW.
A condition then checks whether joystick VRx (x-axis) is below 175 and LCDpos80 (LCD position) is greater than 0, move LCDpos80 left by decreasing it by 1. The cursor moves left.
Similarly, if VRx is above 530 and LCDpos80 is less than 79, move LCDpos80 right by increasing it by 1. This moves the cursor right.
This lets the user move the cursor left or right with the joystick’s x-axis.
The thresholds 175 og 530 can be adjusted to the joystick’s measured centre/dead zone. Cursor repeat speed also depends on loop timing, not just these thresholds.
// Justér værdien i den aktuelle placeholder op eller ned baseret på VRy-værdien
if (LCDpos80 >= 0 && LCDpos80 <= 79)
{ if (analogRead(VRy) < 175 && line[LCDpos80 / 20][LCDpos80 % 20] < 127) { line[LCDpos80 / 20][LCDpos80 % 20]++; }
else if (analogRead(VRy) > 530 && line[LCDpos80 / 20][LCDpos80 % 20] > 32) { line[LCDpos80 / 20][LCDpos80 % 20]--;
}
}
This section of checkJoy() function processes the joystick VRy (y-axis) to change the character at the current LCD position.
First check that LCDpos80 is within the allowed position range (0-79). This limits edits to the visible display area.
The next conditions depend on the joystick VRy reading. If VRy is below 175 and the current character value (line[LCDpos80 / 20][LCDpos80 % 20]) is below 127 (the code’s upper bound; ASCII 127 is DEL, not a printable character),increase it by 1. This advances the character code. The LCD’s actual glyphs depend on its character ROM.
Similarly, if VRy is above 530 and the character value is greater than 32 (the ASCII space code used as the lower bound),decrease it by 1. This moves to the previous character code.
These changes let the user adjust the current LCD character using the y-axis.
tegn på LCD-displayet opad eller nedad ved at bevæge joystickens y-retning.
// Hvis knappen er trykket ned, gemmes starttiden for knaptrykket
if (buttonPressed && buttonPressStartTime == 0) {
buttonPressStartTime = millis(); // Gem starttiden for knaptrykket
}
// Hvis knappen er sluppet og der er registreret en starttid for knaptrykket
if (!buttonPressed && buttonPressStartTime != 0) {
unsigned long buttonPressDuration = millis() - buttonPressStartTime;
buttonPressStartTime = 0; // Nulstil starttiden for knaptrykket
// Hvis knappen er holdt nede i mindst 1 sekund
if (buttonPressDuration >= 1000) {
// Ryd hele displayet
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 20; j++) {
line[i][j] = ' ';
}
}
}
// Hvis knappen slippes inden for 1 sekund og positionen er inden for gyldigt område
else if (buttonPressDuration < 1000 && LCDpos80 >= 0 && LCDpos80 <= 79) {
// Ryd den aktuelle placeholder
line[LCDpos80 / 20][LCDpos80 % 20] = ' ';
}
}
}
The last section of checkJoy() function handles button presses and their duration:
First check whether the button is pressed (buttonPressed) and no start time is stored (start-time check: buttonPressStartTime == 0). If so, store the current time using millis().
Next check whether the button has been released (!buttonPressed) and a start time exists (buttonPressStartTime != 0). If so, calculate the press duration by subtracting the start time from the current time (millis() - buttonPressStartTime). Then reset the stored start time (buttonPressStartTime = 0).
If the button was held for at least 1 second (buttonPressDuration >= 1000), clear all displayed text by setting every position in each row to a space character (' ').
If instead the button was released within 1 second (buttonPressDuration < 1000) and the cursor position is valid (LCDpos80 >= 0 && LCDpos80 <= 79), clear only the current character by setting it to a space (' ').
This handles button presses differently according to their duration and the current cursor position.
void LCD() { // Funktion til at opdatere LCD-displayet
int row = LCDpos80 / 20; // Beregn rækken baseret på værdien af LCDpos80
int col = LCDpos80 % 20; // Beregn kolonnen baseret på værdien af LCDpos80
lcd.setCursor(0, 0); // Sæt markøren til (0, 0) (øverste venstre hjørne)
lcd.print(line[0]); // Udskriv indholdet af line[0] i LCD-displayet
lcd.setCursor(0, 1); // Sæt markøren til (0, 1) (anden række)
lcd.print(line[1]); // Udskriv indholdet af line[1] i LCD-displayet
lcd.setCursor(0, 2); // Sæt markøren til (0, 2) (tredje række)
lcd.print(line[2]); // Udskriv indholdet af line[2] i LCD-displayet
lcd.setCursor(0, 3); // Sæt markøren til (0, 3) (fjerde række)
lcd.print(line[3]); // Udskriv indholdet af line[3] i LCD-displayet
if (flip) {
lcd.setCursor(col, row); // Sæt markøren til den aktuelle position (col, row)
lcd.print('_'); // Udskriv et understregningstegn i LCD-displayet på den aktuelle position
}
flip = !flip; // Skift værdien af flip-variablen
delay(100); // Vent i 100 millisekunder
}
LCD() updates the LCD with text from line[]. Here is how:
First calculate the current row and column from LCDpos80. The row is LCDpos80 / 20, and the column is LCDpos80 % 20.
Next use lcd.setCursor() function to position the cursor. For each row, 0–3, set the cursor at its start and print the matching row of line[] using lcd.print().
After printing, check flip. If true, move the cursor to (col, row) using lcd.setCursor() and print an underscore ('_') there with lcd.print().
Finally toggle flip by negating it (flip = !flip), so the cursor alternates between visible and hidden at the same position. Wait 100 milliseconds using delay(100) to create the visual effect.
This displays the text from line[] and blinks an underscore cursor at the editable position.
void switchCategory() {
static unsigned long pressStartTime = 0;
unsigned long currentTime = millis(); // Deklarér variablen currentTime
// Tjek om knappen er trykket ned og om den aktuelle placeholder er tom
bool buttonPressed = (analogRead(VRy) > 530 && line[LCDpos80 / 20][LCDpos80 % 20] == ' ');
// Hvis knappen er trykket ned og det er første gang, gemmes starttiden for trykket
if (buttonPressed && pressStartTime == 0) {
pressStartTime = currentTime; // Gem starttiden for trykket
}
// Hvis knappen slippes og der er registreret en starttid for trykket
if (!buttonPressed && pressStartTime != 0) {
unsigned long pressDuration = currentTime - pressStartTime; // Beregn varigheden af trykket
pressStartTime = 0; // Nulstil starttiden for trykket
// Bestem kategorien baseret på trykkets varighed
if (pressDuration >= 0 && pressDuration < 1000) {
line[LCDpos80 / 20][LCDpos80 % 20] = 'A'; // Kategori A
} else if (pressDuration >= 1000 && pressDuration < 2000) {
line[LCDpos80 / 20][LCDpos80 % 20] = 'a'; // Kategori a
} else if (pressDuration >= 2000 && pressDuration < 3000) {
line[LCDpos80 / 20][LCDpos80 % 20] = '1'; // Kategori 1
} else if (pressDuration >= 3000 && pressDuration < 4000) {
line[LCDpos80 / 20][LCDpos80 % 20] = '.'; // Kategori .
// Tilføj flere betingelser for yderligere kategorier efter behov
}
}
}
switchCategory() function changes the current character category. Despite the variable name buttonPressed, this function uses VRy > 530 while the current character is a space; it does not read the SW button.
First initialise pressStartTime to store when the axis condition becomes active, and currentTime is updated with the current time using millis().
Next check whether the axis condition is active (buttonPressed) and the current character is a space (line[LCDpos80 / 20][LCDpos80 % 20] == ' ').
When that condition first becomes active (pressStartTime == 0), store the start time (pressStartTime = currentTime).
When the condition stops being active (!buttonPressed) and a start time exists (pressStartTime != 0), calculate duration by subtracting start time from current time (currentTime - pressStartTime). Then reset the start time (pressStartTime = 0).
Choose a category from that duration. If duration is between 0 og 1000 milliseconds, set the character to 'A' (category A). If duration is between 1000 og 2000 milliseconds, set it to 'a' (category a), and so on for further categories. Add conditions if more categories are needed.
This selects a starting character category by how long the y-axis condition is held, helping enter groups of characters more quickly. It is not SW-button category switching.
void loop() { // Hovedløkken for programmet
checkJoy(); // Kald checkJoy-funktionen for at håndtere joystick-input
LCD(); // Kald LCD-funktionen for at opdatere LCD-displayet
switchCategory(); // Kald switchCategory-funktionen for at skrive hurtigere
}
loop() function is the main repeating program loop:
In loop() calls three functions:
- checkJoy() function handles joystick input: x movement adjusts cursor position, y movement changes the current character, and SW-button presses delete one character or all text as described earlier.
- LCD() function updates the LCD, sets cursor positions and prints each row from line[] at the appropriate location. It also blinks the cursor at the current position.
- switchCategory() function provides faster entry by selecting a starting character category. It times the VRy-axis condition described above and writes the corresponding character at the current position.
The loop repeatedly processes joystick input, refreshes the LCD and handles category selection, creating interactive behaviour. Test the interactions and timing on the actual hardware.
This guide used a KY-023 joystick and Arduino to interact with an LCD. The simple circuit led to the most challenging code so far. We explored cursor movement, character editing and category selection through joystick input.
We hope you enjoyed the guide and found it useful. Joysticks offer many possibilities, so keep exploring and experimenting. Happy coding—see you in the next project!