Skip to content

Instantly share code, notes, and snippets.

@roooodcastro
Last active August 24, 2020 17:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roooodcastro/6f19eb074a45002526e02bc6fcf5c32a to your computer and use it in GitHub Desktop.
Save roooodcastro/6f19eb074a45002526e02bc6fcf5c32a to your computer and use it in GitHub Desktop.
PIU game controller tutorial
#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();
}
}
#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();
}
#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);
}
}
#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);
}
#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);
}
#include <Arduino.h>
#include <Joystick.h>
Joystick_ Joystick;
void setup() {
Joystick.begin();
}
void loop() {
Joystick.setButton(0, true);
}
Joystick_ Joystick(
JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, // Device ID and type
5, 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
);
#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();
}
#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();
}
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();
}
}
#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