Last active
August 24, 2020 17:29
-
-
Save roooodcastro/6f19eb074a45002526e02bc6fcf5c32a to your computer and use it in GitHub Desktop.
PIU game controller tutorial
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#define DEBOUNCE_INTERVAL 40 // This is in milliseconds | |
#define INPUT_PIN 2 | |
bool buttonPressed; | |
unsigned long lastChangeTimestamp; | |
void setup() { | |
lastChangeTimestamp = 0; | |
buttonPressed = false; | |
pinMode(INPUT_PIN, INPUT_PULLUP); | |
} | |
void loop() { | |
// Constantly read the "raw" button state | |
bool newState = digitalRead(INPUT_PIN) == LOW; | |
// Calculate the time since the button state was last changed | |
unsigned long timeSinceLastDebounce = abs(millis() - lastChangeTimestamp); | |
// Button state is different from what we know? | |
bool stateChanged = newState != buttonPressed; | |
// Has enough time passed so that we can consider the input debounced? | |
bool inputDebounced = timeSinceLastDebounce >= DEBOUNCE_INTERVAL; | |
// If both conditions are true, we can change the button's state and store the | |
// current timestamp, which tells us when the state was last changed. | |
// | |
// If the button changes state but not enough time has passed since the last | |
// "accepted" state change, then this new change is discarded. | |
if (stateChanged && inputDebounced) { | |
buttonPressed = newState; | |
lastChangeTimestamp = millis(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include <Joystick.h> | |
#include <FastLED.h> | |
// The number of arrows the controller has. For PIU pads, this will be 5 | |
#define NUMBER_OF_ARROWS 5 | |
// Debounce interval, in milliseconds | |
#define DEBOUNCE_INTERVAL 40 | |
// The number of LEDs per arrow | |
#define LEDS_PER_ARROW 8 | |
// Initializes the Joystick library. It creates only the exact number of buttons | |
// needed to map all pads. This initializer defines all types of input methods | |
// a controller might have, such as accelerator, brake, rudder, or analog axis. | |
// Because our controller only uses momentary button switches, everything else | |
// must be set to false, because their default value is true. | |
Joystick_ Joystick( | |
JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, // Device ID and type | |
NUMBER_OF_ARROWS, 0, // Button Count, Hat Switch Count | |
false, false, false, // No X, Y, or Z Axis | |
false, false, false, // No Rx, Ry, or Rz | |
false, false, // No rudder or throttle | |
false, false, false); // No accelerator, brake, or steering | |
// Pin configuration | |
int arrowPins[NUMBER_OF_ARROWS]; | |
// Pins for the LEDs. This needs to be a constant to work with FastLED library | |
constexpr int LED_PINS[] = { | |
3, // Top Left | |
5, // Top Right | |
7, // Center | |
9, // Bottom Left | |
11 // Bottom Right | |
}; | |
// Debouncing configuration | |
bool isArrowPressed[NUMBER_OF_ARROWS]; | |
unsigned long lastChanges[NUMBER_OF_ARROWS]; | |
// This needs to be a 2D array, as each of the 5 arrows have 8 LEDs | |
CRGB leds[NUMBER_OF_ARROWS][LEDS_PER_ARROW]; | |
// Colors to set each arrow when pressed | |
CRGB colors[NUMBER_OF_ARROWS]; | |
void setup() { | |
Joystick.begin(); | |
// We still have to configure the pins one by one | |
arrowPins[0] = 2; | |
arrowPins[1] = 4; | |
arrowPins[2] = 6; | |
arrowPins[3] = 8; | |
arrowPins[4] = 10; | |
// We have to define the colours for the arrows | |
colors[0] = CRGB(255, 128, 128); // Red(-ish) | |
colors[1] = CRGB(255, 128, 128); // Red(-ish) | |
colors[2] = CRGB(255, 255, 0); // Yellow(-ish) | |
colors[3] = CRGB(128, 128, 255); // Blue(-ish) | |
colors[4] = CRGB(128, 128, 255); // Blue(-ish) | |
// Also initialize the library (this cannot be done in the for loop below) | |
FastLED.addLeds<WS2812B, LED_PINS[0], GRB>(leds[0], LEDS_PER_ARROW); | |
FastLED.addLeds<WS2812B, LED_PINS[1], GRB>(leds[1], LEDS_PER_ARROW); | |
FastLED.addLeds<WS2812B, LED_PINS[2], GRB>(leds[2], LEDS_PER_ARROW); | |
FastLED.addLeds<WS2812B, LED_PINS[3], GRB>(leds[3], LEDS_PER_ARROW); | |
FastLED.addLeds<WS2812B, LED_PINS[4], GRB>(leds[4], LEDS_PER_ARROW); | |
// Iterate over all buttons to set them up | |
for (int i = 0; i < NUMBER_OF_ARROWS; i++) { | |
pinMode(arrowPins[i], INPUT_PULLUP); | |
pinMode(LED_PINS[i], OUTPUT); | |
isArrowPressed[i] = false; | |
lastChanges[i] = 0; | |
} | |
} | |
void loop() { | |
// Iterate over all buttons to read them | |
for (int i = 0; i < NUMBER_OF_ARROWS; i++) { | |
readButton(i); | |
} | |
} | |
void readButton(int index) { | |
bool newState = digitalRead(arrowPins[index]) == LOW; | |
unsigned long timeSinceLastDebounce = abs(millis() - lastChanges[index]); | |
bool stateChanged = newState != isArrowPressed[index]; | |
bool inputDebounced = timeSinceLastDebounce >= DEBOUNCE_INTERVAL; | |
if (stateChanged && inputDebounced) { | |
isArrowPressed[index] = newState; | |
lastChanges[index] = millis(); | |
Joystick.setButton(index, isArrowPressed[index]); | |
// If arrow is pressed, set the configured colour, otherwise set black | |
CRGB newColor = isArrowPressed[index] ? colors[index] : CRGB::Black; | |
setStripColor(index, newColor); | |
} | |
} | |
// New function to change the colour of an arrow | |
void setStripColor(int arrowIndex, CRGB color) { | |
for (int ledIndex = 0; ledIndex < LEDS_PER_ARROW; ledIndex++) { | |
leds[arrowIndex][ledIndex] = color; | |
} | |
FastLED.show(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include <Joystick.h> | |
// The number of input buttons the controller has. For PIU pads, this will be 5 | |
#define NUMBER_OF_BUTTONS 5 | |
// Debounce interval, in milliseconds | |
#define DEBOUNCE_INTERVAL 40 | |
// Which pins are assigned to which buttons | |
#define BUTTON_1_PIN 2 | |
#define BUTTON_2_PIN 4 | |
#define BUTTON_3_PIN 6 | |
#define BUTTON_4_PIN 8 | |
#define BUTTON_5_PIN 10 | |
// Initializes the Joystick library. It creates only the exact number of buttons | |
// needed to map all pads. This initializer defines all types of input methods | |
// a controller might have, such as accelerator, brake, rudder, or analog axis. | |
// Because our controller only uses momentary button switches, everything else | |
// must be set to false, because their default value is true. | |
Joystick_ Joystick( | |
JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, // Device ID and type | |
NUMBER_OF_BUTTONS, 0, // Button Count, Hat Switch Count | |
false, false, false, // No X, Y, or Z Axis | |
false, false, false, // No Rx, Ry, or Rz | |
false, false, // No rudder or throttle | |
false, false, false); // No accelerator, brake, or steering | |
bool isButton1Pressed; | |
bool isButton2Pressed; | |
bool isButton3Pressed; | |
bool isButton4Pressed; | |
bool isButton5Pressed; | |
unsigned long lastChangeTimeButton1; | |
unsigned long lastChangeTimeButton2; | |
unsigned long lastChangeTimeButton3; | |
unsigned long lastChangeTimeButton4; | |
unsigned long lastChangeTimeButton5; | |
void setup() { | |
Joystick.begin(); | |
pinMode(BUTTON_1_PIN, INPUT_PULLUP); | |
pinMode(BUTTON_2_PIN, INPUT_PULLUP); | |
pinMode(BUTTON_3_PIN, INPUT_PULLUP); | |
pinMode(BUTTON_4_PIN, INPUT_PULLUP); | |
pinMode(BUTTON_5_PIN, INPUT_PULLUP); | |
isButton1Pressed = false; | |
isButton2Pressed = false; | |
isButton3Pressed = false; | |
isButton4Pressed = false; | |
isButton5Pressed = false; | |
lastChangeTimeButton1 = 0; | |
lastChangeTimeButton2 = 0; | |
lastChangeTimeButton3 = 0; | |
lastChangeTimeButton4 = 0; | |
lastChangeTimeButton5 = 0; | |
} | |
void loop() { | |
readButton1(); | |
readButton2(); | |
readButton3(); | |
readButton4(); | |
readButton5(); | |
} | |
void readButton1() { | |
bool newState = digitalRead(BUTTON_1_PIN) == LOW; | |
unsigned long timeSinceLastDebounce = abs(millis() - lastChangeTimeButton1); | |
bool stateChanged = newState != isButton1Pressed; | |
bool inputDebounced = timeSinceLastDebounce >= DEBOUNCE_INTERVAL; | |
if (stateChanged && inputDebounced) { | |
isButton1Pressed = newState; | |
lastChangeTimeButton1 = millis(); | |
Joystick.setButton(0, isButton1Pressed); | |
} | |
} | |
void readButton2() { | |
bool newState = digitalRead(BUTTON_2_PIN) == LOW; | |
unsigned long timeSinceLastDebounce = abs(millis() - lastChangeTimeButton2); | |
bool stateChanged = newState != isButton2Pressed; | |
bool inputDebounced = timeSinceLastDebounce >= DEBOUNCE_INTERVAL; | |
if (stateChanged && inputDebounced) { | |
isButton2Pressed = newState; | |
lastChangeTimeButton2 = millis(); | |
Joystick.setButton(1, isButton2Pressed); | |
} | |
} | |
void readButton3() { | |
bool newState = digitalRead(BUTTON_3_PIN) == LOW; | |
unsigned long timeSinceLastDebounce = abs(millis() - lastChangeTimeButton3); | |
bool stateChanged = newState != isButton3Pressed; | |
bool inputDebounced = timeSinceLastDebounce >= DEBOUNCE_INTERVAL; | |
if (stateChanged && inputDebounced) { | |
isButton3Pressed = newState; | |
lastChangeTimeButton3 = millis(); | |
Joystick.setButton(2, isButton3Pressed); | |
} | |
} | |
void readButton4() { | |
bool newState = digitalRead(BUTTON_4_PIN) == LOW; | |
unsigned long timeSinceLastDebounce = abs(millis() - lastChangeTimeButton4); | |
bool stateChanged = newState != isButton5Pressed; | |
bool inputDebounced = timeSinceLastDebounce >= DEBOUNCE_INTERVAL; | |
if (stateChanged && inputDebounced) { | |
isButton4Pressed = newState; | |
lastChangeTimeButton4 = millis(); | |
Joystick.setButton(3, isButton4Pressed); | |
} | |
} | |
void readButton5() { | |
bool newState = digitalRead(BUTTON_5_PIN) == LOW; | |
unsigned long timeSinceLastDebounce = abs(millis() - lastChangeTimeButton5); | |
bool stateChanged = newState != isButton5Pressed; | |
bool inputDebounced = timeSinceLastDebounce >= DEBOUNCE_INTERVAL; | |
if (stateChanged && inputDebounced) { | |
isButton5Pressed = newState; | |
lastChangeTimeButton5 = millis(); | |
Joystick.setButton(4, isButton5Pressed); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include <Joystick.h> | |
// The number of input buttons the controller has. For PIU pads, this will be 5 | |
#define NUMBER_OF_BUTTONS 5 | |
// Which pins are assigned to which buttons | |
#define BUTTON_1_PIN 2 | |
#define BUTTON_2_PIN 4 | |
#define BUTTON_3_PIN 6 | |
#define BUTTON_4_PIN 8 | |
#define BUTTON_5_PIN 10 | |
// Initializes the Joystick library. It creates only the exact number of buttons | |
// needed to map all pads. This initializer defines all types of input methods | |
// a controller might have, such as accelerator, brake, rudder, or analog axis. | |
// Because our controller only uses momentary button switches, everything else | |
// must be set to false, because their default value is true. | |
Joystick_ Joystick( | |
JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, // Device ID and type | |
NUMBER_OF_BUTTONS, 0, // Button Count, Hat Switch Count | |
false, false, false, // No X, Y, or Z Axis | |
false, false, false, // No Rx, Ry, or Rz | |
false, false, // No rudder or throttle | |
false, false, false); // No accelerator, brake, or steering | |
void setup() { | |
Joystick.begin(); | |
pinMode(BUTTON_1_PIN, INPUT_PULLUP); | |
pinMode(BUTTON_2_PIN, INPUT_PULLUP); | |
pinMode(BUTTON_3_PIN, INPUT_PULLUP); | |
pinMode(BUTTON_4_PIN, INPUT_PULLUP); | |
pinMode(BUTTON_5_PIN, INPUT_PULLUP); | |
} | |
void loop() { | |
// Because we're using the PULLUP mode, the LOW value is actually the button's | |
// pressed state, and not the HIGH value. | |
bool isButton1Pressed = digitalRead(BUTTON_1_PIN) == LOW; | |
bool isButton2Pressed = digitalRead(BUTTON_2_PIN) == LOW; | |
bool isButton3Pressed = digitalRead(BUTTON_3_PIN) == LOW; | |
bool isButton4Pressed = digitalRead(BUTTON_4_PIN) == LOW; | |
bool isButton5Pressed = digitalRead(BUTTON_5_PIN) == LOW; | |
// Write data to the host PC | |
Joystick.setButton(0, isButton1Pressed); | |
Joystick.setButton(1, isButton2Pressed); | |
Joystick.setButton(2, isButton3Pressed); | |
Joystick.setButton(3, isButton4Pressed); | |
Joystick.setButton(4, isButton5Pressed); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include <Joystick.h> | |
// The number of input buttons the controller has. For PIU pads, this will be 5 | |
#define NUMBER_OF_BUTTONS 5 | |
// Initializes the Joystick library. It creates only the exact number of buttons | |
// needed to map all pads. This initializer defines all types of input methods | |
// a controller might have, such as accelerator, brake, rudder, or analog axis. | |
// Because our controller only uses momentary button switches, everything else | |
// must be set to false. | |
Joystick_ Joystick( | |
JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, // Device ID and type | |
NUMBER_OF_BUTTONS, 0, // Button Count, Hat Switch Count | |
false, false, false, // No X, Y, or Z Axis | |
false, false, false, // No Rx, Ry, or Rz | |
false, false, // No rudder or throttle | |
false, false, false); // No accelerator, brake, or steering | |
int buttonIndex; | |
void setup() { | |
Joystick.begin(); | |
buttonIndex = 0; | |
} | |
void loop() { | |
// For values from 0 to 4, we're setting the buttons in the "pressed" state | |
// For values from 5 to 9, we're setting the buttons in the "released" state | |
bool isButtonPressed = buttonIndex < 5; | |
// Write data to the host PC | |
Joystick.setButton(buttonIndex % 5, isButtonPressed); | |
// Code needed to loop through all 5 buttons continuously | |
buttonIndex += 1; | |
if (buttonIndex == 10) | |
buttonIndex = 0; | |
// 100ms is slow enough to see what's happening and fast enough for it to be | |
// interesting :) | |
delay(100); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include <Joystick.h> | |
Joystick_ Joystick; | |
void setup() { | |
Joystick.begin(); | |
} | |
void loop() { | |
Joystick.setButton(0, true); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include <FastLED.h> | |
// Pin 3 goes to the Din connection on the LED strip | |
#define LED_PIN 3 | |
// We want to control 8 LEDs | |
#define LED_COUNT 8 | |
// As a requirement from the FastLED library, we need to store the LED colours | |
// in an array structure. This array must have the same size as the number of | |
// LEDs we specify in the addLeds function call below | |
CRGB leds[LED_COUNT]; | |
void setup() { | |
// Inform Arduino that we're going to use pin 3 to write data | |
pinMode(LED_PIN, OUTPUT); | |
// Inform the FastLED library that: | |
// * There is a WS2812B strip on pin LED_PIN (pin 3) | |
// * The strip's colour order is GRB (found this by trial and error) | |
// * I'm using the array called "leds" to set the colours I want to show | |
// * There are LED_COUNT (8) LEDs in the strip | |
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, LED_COUNT); | |
} | |
void loop() { | |
// Create a custom colour, but there are a lot of predefined colours already: | |
// https://github.com/FastLED/FastLED/blob/master/pixeltypes.h#L579 | |
CRGB cyan = CRGB(0, 128, 255); | |
// Set the colour to all LEDs in the strip to the cyan we just created. | |
// Note that we're simply changing the "leds" array, we're not calling the | |
// library directly for this | |
for (int i = 0; i < LED_COUNT; i++) { | |
leds[i] = cyan; | |
} | |
// Calls the library and tells it to "show" the LEDs, which will read the | |
// contents of the "leds" array and actually change the colours of the strip | |
// itself. It's only at this moment that the data is written to the strip. | |
FastLED.show(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include <FastLED.h> | |
// Pin 3 goes to the Din connection on the LED strip | |
#define LED_PIN 3 | |
// We want to control 8 LEDs | |
#define LED_COUNT 8 | |
// As a requirement from the FastLED library, we need to store the LED colours | |
// in an array structure. This array must have the same size as the number of | |
// LEDs we specify in the addLeds function call below | |
CRGB leds[LED_COUNT]; | |
CRGB color; | |
void setup() { | |
// Inform Arduino that we're going to use pin 3 to write data | |
pinMode(LED_PIN, OUTPUT); | |
// Inform the FastLED library that: | |
// * There is a WS2812B strip on pin LED_PIN (pin 3) | |
// * The strip's colour order is GRB (found this by trial and error) | |
// * I'm using the array called "leds" to set the colours I want to show | |
// * There are LED_COUNT (8) LEDs in the strip | |
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, LED_COUNT); | |
} | |
void loop() { | |
// You can also use HSV to set the colour. HSV stands for: | |
// * Hue | |
// * Saturation | |
// * Value (aka lightness, or brightness) | |
color = CHSV(128, 255, 255); | |
// Handy new function to change the strip colour | |
setStripColor(color); | |
} | |
void setStripColor(CRGB color) { | |
for (int i = 0; i < LED_COUNT; i++) { | |
leds[i] = cyan; | |
} | |
FastLED.show(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void loop() { | |
CRGB cyan = CRGB(0, 128, 255); | |
for (int i = 0; i < LED_COUNT; i++) { | |
leds[i] = cyan; | |
// Adds a delay and calls the show function once for every LED | |
delay(200); | |
FastLED.show(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include <Joystick.h> | |
// The number of input buttons the controller has. For PIU pads, this will be 5 | |
#define NUMBER_OF_BUTTONS 5 | |
// Debounce interval, in milliseconds | |
#define DEBOUNCE_INTERVAL 40 | |
// Initializes the Joystick library. It creates only the exact number of buttons | |
// needed to map all pads. This initializer defines all types of input methods | |
// a controller might have, such as accelerator, brake, rudder, or analog axis. | |
// Because our controller only uses momentary button switches, everything else | |
// must be set to false, because their default value is true. | |
Joystick_ Joystick( | |
JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, // Device ID and type | |
NUMBER_OF_BUTTONS, 0, // Button Count, Hat Switch Count | |
false, false, false, // No X, Y, or Z Axis | |
false, false, false, // No Rx, Ry, or Rz | |
false, false, // No rudder or throttle | |
false, false, false); // No accelerator, brake, or steering | |
int buttonPins[NUMBER_OF_BUTTONS]; | |
bool isButtonPressed[NUMBER_OF_BUTTONS]; | |
unsigned long lastChanges[NUMBER_OF_BUTTONS]; | |
void setup() { | |
Joystick.begin(); | |
// We still have to configure the pins one by one | |
buttonPins[0] = 2; | |
buttonPins[1] = 4; | |
buttonPins[2] = 6; | |
buttonPins[3] = 8; | |
buttonPins[4] = 10; | |
// Iterate over all buttons to set them up | |
for (int i = 0; i < NUMBER_OF_BUTTONS; i++) { | |
pinMode(buttonPins[i], INPUT_PULLUP); | |
isButtonPressed[i] = false; | |
lastChanges[i] = 0; | |
} | |
} | |
void loop() { | |
// Iterate over all buttons to read them | |
for (int i = 0; i < NUMBER_OF_BUTTONS; i++) { | |
readButton(i); | |
} | |
} | |
void readButton(int index) { | |
bool newState = digitalRead(buttonPins[index]) == LOW; | |
unsigned long timeSinceLastDebounce = abs(millis() - lastChanges[index]); | |
bool stateChanged = newState != isButtonPressed[index]; | |
bool inputDebounced = timeSinceLastDebounce >= DEBOUNCE_INTERVAL; | |
if (stateChanged && inputDebounced) { | |
isButtonPressed[index] = newState; | |
lastChanges[index] = millis(); | |
Joystick.setButton(index, isButtonPressed[index]); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment