# EasyJoystick

EasyJoystick is a lightweight Arduino library for reading analog joystick modules (such as the KY-023) and converting their input into simple directional states.

Instead of working directly with raw analog values, this library interprets the joystick position as one of five states:

* Up  
* Down  
* Left  
* Right  
* Middle (center)

This makes it easy to handle joystick input in games, menus, and navigation systems without writing your own thresholding or state detection logic.

## Features

* Works with any 2-axis analog joystick (two potentiometers)
* Converts analog input into discrete directional states
* Removes the need for manual analog thresholding and debounce logic.
* Built-in state change detection (`changed()`, `newUp()`, etc.)
* Axis flipping and rotation support
* Simple and beginner-friendly API

## Hardware Requirements

* Any analog joystick module (e.g., KY-023)
* Arduino board with at least 2 analog input pins

## Limitations

* This library does **not** handle the joystick’s built-in push button.
* If you need button support, use a dedicated button library (e.g., OneButton, ezButton).

## Quick Example

```cpp
#include <EasyJoystick.h>

EasyJoystick joystick;

void setup() {
  joystick.begin(A1, A2, true, true);
  Serial.begin(115200);
}

void loop() {
  joystick.loop();
  
  if (joystick.newUp()) {
    Serial.println("Moved up!");
  }
}
```

![Wiring Diagram](docs/basicLowRes.jpg)

## Functions

### begin()
```CPP
joystick.begin(xPin, yPin, flipX, flipY);
```

* Must be called once in setup().
* xPin and yPin must be analog pins (A0, A1, etc.).
* To rotate the joystick 90°, swap xPin and yPin.
* If left/right movement is reversed, toggle flipX (switch between true and false).
* If up/down movement is reversed, toggle flipY.

### loop()
```CPP
joystick.loop();
```

Call this function repeatedly in your main program loop (or within blocking loops) to update the joystick state.  

This function reads the joystick input and updates the internal state.
Most other functions simply return this stored state.

### getState()
```CPP
joystick.getState();
```
Returns the current state of the joystick as one of the constants: `UP`, `DOWN`, `LEFT`, `RIGHT`, or `MIDDLE`.  
See the [getState example](examples/003_getState_example/003_getState_example.ino).

### changed()
```CPP
joystick.changed();
```
Returns true if the joystick state has changed since the last call to loop().
Otherwise returns false.  
See the [getState example](examples/003_getState_example/003_getState_example.ino).

### isUp(), isDown(), isLeft(), isRight(), isMiddle()
```CPP
joystick.isUp();
joystick.isDown();
joystick.isLeft();
joystick.isRight();
joystick.isMiddle();
```
Each function returns true if the joystick is currently in the corresponding state.  
See the [isUp example](examples/002_isUp_example/002_isUp_example.ino).

### isUDLR()
```CPP
joystick.isUDLR();
```

Returns true if the joystick is in any directional state (up, down, left, or right), and false if it is in the middle.  

Equivalent to:
```CPP
(isUp() || isDown() || isLeft() || isRight())
```

### newUp(), newDown(), newLeft(), newRight(), newMiddle()
```CPP
joystick.newUp();     // same as (joystick.isUp() && joystick.changed())
joystick.newDown();   // same as (joystick.isDown() && joystick.changed())
joystick.newLeft();   // same as (joystick.isLeft() && joystick.changed())
joystick.newRight();  // same as (joystick.isRight() && joystick.changed())
joystick.newMiddle(); // same as (joystick.isMiddle() && joystick.changed())
```

Returns true if the joystick has just moved into that state.  
See the [newUp example](examples/001_newUp_example/001_newUp_example.ino).

### newUDLR()
```CPP
joystick.newUDLR();
```

Returns true if the joystick has just moved into a directional state (up, down, left, or right).  

Equivalent to:
```CPP
(joystick.isUDLR() && joystick.changed())
```

## More Examples

Additional examples can be found in the `examples` folder:

* [Basic state detection](examples/002_isUp_example/002_isUp_example.ino) (`isUp`, `isDown`, etc.)
* [Change detection](examples/001_newUp_example/001_newUp_example.ino) (`newUp`, `newDown`, etc.)
* [State polling](examples/003_getState_example/003_getState_example.ino) (`getState`, `changed`)
* [Wireless transmission using NRF24L01](examples/004_Transmitter_example/004_Transmitter_example.ino)
* [Wireless reception using NRF24L01](examples/005_Receiver_example/005_Receiver_example.ino)

Wiring diagrams for each example are available in the `docs` folder.

## How It Works

The joystick provides two analog values (X and Y), which represent its position.

EasyJoystick processes these values as follows:

1. Reads the analog inputs
2. Optionally flips axes (`flipX`, `flipY`)
3. Centers the values around (0, 0)
4. Determines one of five states:
   * Up
   * Down
   * Left
   * Right
   * Middle

At any given time, the joystick is always in exactly one state.

### Debouncing Strategy

Instead of using time-based debouncing, this library uses positional hysteresis.

Rather than a single boundary between states, it defines two:

* An **inner boundary** (normal zone)
* An **outer boundary** (exit threshold)

This prevents rapid state switching when the joystick is near a boundary.

For example:
* Moving from middle → up requires passing a higher threshold
* Returning from up → middle requires dropping below a lower threshold

This creates a "dead zone gap" that eliminates jitter without sacrificing responsiveness.