3 Commits
Author SHA1 Message Date
CrazyRedMachine ba765f2cd9 keypad hotkey 2020-11-17 01:21:06 +01:00
CrazyRedMachine 9e6ff10523 Update README.md 2020-11-10 22:57:01 +01:00
CrazyRedMachine b3c44bdf5a working 2020-11-10 22:54:43 +01:00
5 changed files with 986 additions and 773 deletions
+27 -4
View File
@@ -28,6 +28,8 @@ The keypad code uses the Keypad library by Mark Stanley and Alexander Brevig.
Switch debouncing is done with Bounce2 library by Thomas O Fredericks. Switch debouncing is done with Bounce2 library by Thomas O Fredericks.
PSX Controller code is based on busslave's PSX_RECEIVER.cpp
# Supported devices and requirements # Supported devices and requirements
This code was designed for Arduino Due. It will compile for Leonardo as well but some features are stripped due to lack of gpio (only 11 buttons and 9 lights, no keypad). The HID descriptor is updated accordingly depending on the selected target board. This code was designed for Arduino Due. It will compile for Leonardo as well but some features are stripped due to lack of gpio (only 11 buttons and 9 lights, no keypad). The HID descriptor is updated accordingly depending on the selected target board.
@@ -38,6 +40,18 @@ The keypad code requires the Keypad library by Mark Stanley and Alexander Brevig
# Features # Features
## Playstation Controller (Leonardo only, Due coming soon)
This controller can be used as a playstation controller for Pop'n Music CS versions.
Note that this requires a couple hardware mods in order to work as we are using two extra pins (SS and ACK, on the RX and TX leds aka PB0 and PD5) which are not readily available on the pre-soldered headers.
Of course you'll need either a dualshock breakout board or a dualshock extension cable for it to work.
Refer to the pinout to know how to wire the controller plug to the arduino.
**Note:** This is PoC code, I'll try to port a more complete dualshock implementation soon and make it Due compatible as well
## I/O ## I/O
This controller has 12 buttons (9 buttons + coin + service + test) and 18 lights (9 buttons + 5 top neon + 4 side pillar). This controller has 12 buttons (9 buttons + coin + service + test) and 18 lights (9 buttons + 5 top neon + 4 side pillar).
@@ -90,11 +104,18 @@ I included pre-compiled binaries and sources in the "ModeSwitch" folder. Refer t
# Pinout (DUE) # Pinout (DUE)
The Arduino DUE has 3.3v logic whereas the Pop'n Music cabinet outputs (lamps, coin blocker, coin counter) use 12V. The Arduino DUE has 3.3v logic whereas the Pop'n Music cabinet lamps use 12V. Therefore I'm using mosfet transistors to do level shifting (the parts I used were three ULN2003APG chips). You can also buy pre-made level shifters such as this one https://www.tindie.com/products/ddebeer/12-channel-level-shifter-and-buffer/ (you'd need two of them since there are 18 lamps to control for a Pop'n cabinet).
Therefore I'm using transistors to do level shifting (the parts I used were three ULN2003APG or TBD62003APG chips). You can also buy pre-made level shifters such as this one https://www.tindie.com/products/ddebeer/12-channel-level-shifter-and-buffer/ (you'd need two of them since there are 20 outputs to control for a Pop'n cabinet).
Refer to ```pinout.png``` to see how it is all wired to a Pop'n Music cabinet. Refer to ```pinout.png``` to see how it is all wired to a Pop'n Music cabinet.
LightPins 36 to 52 = Button lights 1 to 9
LightPins 37 to 53 = Top Neon 1 to 5, Left 1 (blue) 2 (red), Right 1 (blue) 2 (red)
connect pin to mosfet gate then mosfet drain to - terminal of LED
connect ground to mosfet ground (daisychain)
connect +12V to + terminal of LED (daisychain)
ButtonPins 5 to 13 = Button input 1 to 9
ButtonPins 4 3 2 = Test, Service, Coin
connect button pin to ground to trigger button press
![pinout](https://github.com/CrazyRedMachine/UltimatePopnController/blob/master/pinout.png?raw=true) ![pinout](https://github.com/CrazyRedMachine/UltimatePopnController/blob/master/pinout.png?raw=true)
@@ -102,4 +123,6 @@ Refer to ```pinout.png``` to see how it is all wired to a Pop'n Music cabinet.
Arduino Leonardo has 5V logic therefore one can directly connect 5V leds to it. Arduino Leonardo has 5V logic therefore one can directly connect 5V leds to it.
![pinout](https://github.com/CrazyRedMachine/UltimatePopnController/blob/master/pinout_leonardo.png?raw=true) ![pinout](https://github.com/CrazyRedMachine/UltimatePopnController/blob/PSX/pinout_leonardo.png?raw=true)
For ACK (TXLED aka PD5) and SS (RXLED aka PB0) you have to solder new headers or cables directly on the leonardo PCB. For the PSX controller function to work properly you need to short SS to ground (leonardo doesn't work well as SPI slave otherwise, which is why we cannot use the "attention" line from the controller cable unfortunately).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

After

Width:  |  Height:  |  Size: 687 KiB

+363 -417
View File
@@ -1,417 +1,363 @@
/* Arduino-HID-Lighting-Library /* Arduino-HID-Lighting-Library
*
This Arduino-HID-Lighting-Library is derived from Arduino-HID-Lighting, whose copyriht owner is mon. * This Arduino-HID-Lighting-Library is derived from Arduino-HID-Lighting, whose copyriht owner is mon.
More information about Arduino-HID-Lighting you can find under: * More information about Arduino-HID-Lighting you can find under:
*
mon's Arduino-HID-Lighting * mon's Arduino-HID-Lighting
https://github.com/mon/Arduino-HID-Lighting * https://github.com/mon/Arduino-HID-Lighting
*
2018 (C) Arduino-HID-Lighting-Library, Knuckleslee * 2018 (C) Arduino-HID-Lighting-Library, Knuckleslee
*/ */
#include "POPNHID.h" #include "POPNHID.h"
#include <FastLED.h>
byte extern LightPins[];
/* HID DESCRIPTOR */
static const byte PROGMEM _hidReportPOPN[] = { /* HID DESCRIPTOR */
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ static const byte PROGMEM _hidReportPOPN[] = {
0x09, 0x05, /* USAGE (Game Pad) */ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0xa1, 0x01, /* COLLECTION (Application) */ 0x09, 0x05, /* USAGE (Game Pad) */
0xa1, 0x01, /* COLLECTION (Application) */
/*Buttons */
0x85, 0x04, /* REPORT_ID 4 */ /*Buttons */
0x05, 0x09, /* USAGE_PAGE (Button) */ 0x85, 0x04, /* REPORT_ID 4 */
0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ 0x05, 0x09, /* USAGE_PAGE (Button) */
0x29, 0x0c, /* USAGE_MAXIMUM (Button 12)*/ 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 0x29, 0x0c, /* USAGE_MAXIMUM (Button 12)*/
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x95, 0x0c, /* REPORT_COUNT (12) */ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x01, /* REPORT_SIZE (1) */ #if defined(ARDUINO_ARCH_SAM)
0x81, 0x02, /* INPUT (Data,Var,Abs) */ 0x95, 0x0c, /* REPORT_COUNT (12) */
/* Reserved bits */ #else
0x95, 0x01, /* REPORT_COUNT (1) */ 0x95, 0x0b, /* REPORT_COUNT (11) */
0x75, 0x04, /* REPORT_SIZE (4) */ #endif
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
/*Lights */ /* Reserved bits */
0x85, 0x05, /* REPORT_ID 5*/ 0x95, 0x01, /* REPORT_COUNT (1) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */ #if defined(ARDUINO_ARCH_SAM)
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 0x75, 0x04, /* REPORT_SIZE (4) */
/*Led 1 */ #else
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x75, 0x05, /* REPORT_SIZE (5) */
0x09, 0x01, /* USAGE (Instance 1) */ #endif
0xa1, 0x02, /* COLLECTION (Logical) */ 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ /*Lights */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x85, 0x05, /* REPORT_ID 5*/
0x95, 0x01, /* REPORT_COUNT (1) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0xc0, /* END_COLLECTION */ /*Led 1 */
/*Led 2 */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x09, 0x01, /* USAGE (Instance 1) */
0x09, 0x02, /* USAGE (Instance 2) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0xc0, /* END_COLLECTION */
0xc0, /* END_COLLECTION */ /*Led 2 */
/*Led 3 */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x09, 0x02, /* USAGE (Instance 2) */
0x09, 0x03, /* USAGE (Instance 3) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0xc0, /* END_COLLECTION */
0xc0, /* END_COLLECTION */ /*Led 3 */
/*Led 4 */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x09, 0x03, /* USAGE (Instance 3) */
0x09, 0x04, /* USAGE (Instance 4) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0xc0, /* END_COLLECTION */
0xc0, /* END_COLLECTION */ /*Led 4 */
/*Led 5 */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x09, 0x04, /* USAGE (Instance 4) */
0x09, 0x05, /* USAGE (Instance 5) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0xc0, /* END_COLLECTION */
0xc0, /* END_COLLECTION */ /*Led 5 */
/*Led 6 */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x09, 0x05, /* USAGE (Instance 5) */
0x09, 0x06, /* USAGE (Instance 6) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0xc0, /* END_COLLECTION */
0xc0, /* END_COLLECTION */ /*Led 6 */
/*Led 7 */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x09, 0x06, /* USAGE (Instance 6) */
0x09, 0x07, /* USAGE (Instance 7) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0xc0, /* END_COLLECTION */
0xc0, /* END_COLLECTION */ /*Led 7 */
/*Led 8 */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x09, 0x07, /* USAGE (Instance 7) */
0x09, 0x08, /* USAGE (Instance 8) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0xc0, /* END_COLLECTION */
0xc0, /* END_COLLECTION */ /*Led 8 */
/*Led 9 */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x09, 0x08, /* USAGE (Instance 8) */
0x09, 0x09, /* USAGE (Instance 9) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0xc0, /* END_COLLECTION */
0xc0, /* END_COLLECTION */ /*Led 9 */
/*Led 10 */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x09, 0x09, /* USAGE (Instance 9) */
0x09, 0x0a, /* USAGE (Instance 10) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0xc0, /* END_COLLECTION */
0xc0, /* END_COLLECTION */ #if defined(ARDUINO_ARCH_SAM)
/*Led 11 */ /*Led 10 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x0b, /* USAGE (Instance 11) */ 0x09, 0x0a, /* USAGE (Instance 10) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */ 0xc0, /* END_COLLECTION */
/*Led 12 */ /*Led 11 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x0c, /* USAGE (Instance 12) */ 0x09, 0x0b, /* USAGE (Instance 11) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */ 0xc0, /* END_COLLECTION */
/*Led 13 */ /*Led 12 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x0d, /* USAGE (Instance 13) */ 0x09, 0x0c, /* USAGE (Instance 12) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */ 0xc0, /* END_COLLECTION */
/*Led 14 */ /*Led 13 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x0e, /* USAGE (Instance 14) */ 0x09, 0x0d, /* USAGE (Instance 13) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */ 0xc0, /* END_COLLECTION */
/*Led 15 */ /*Led 14 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x0f, /* USAGE (Instance 15) */ 0x09, 0x0e, /* USAGE (Instance 14) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */ 0xc0, /* END_COLLECTION */
/*Led 16 */ /*Led 15 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x10, /* USAGE (Instance 16) */ 0x09, 0x0f, /* USAGE (Instance 15) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */ 0xc0, /* END_COLLECTION */
/*Led 17 */ /*Led 16 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x11, /* USAGE (Instance 17) */ 0x09, 0x10, /* USAGE (Instance 16) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */ 0xc0, /* END_COLLECTION */
/*Led 18 */ /*Led 17 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x12, /* USAGE (Instance 18) */ 0x09, 0x11, /* USAGE (Instance 17) */
0xa1, 0x02, /* COLLECTION (Logical) */ 0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x75, 0x01, /* REPORT_SIZE (1) */ 0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */ 0xc0, /* END_COLLECTION */
/* Reserved 14 bits */ /*Led 18 */
0x95, 0x01, /* REPORT_COUNT (1) */ 0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x75, 0x0E, /* REPORT_SIZE (14) */ 0x09, 0x12, /* USAGE (Instance 18) */
0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */ 0xa1, 0x02, /* COLLECTION (Logical) */
/*Footer */ 0x05, 0x08, /* USAGE_PAGE (LEDs) */
0xc0 /* END_COLLECTION */ 0x09, 0x4b, /* USAGE (Generic Indicator 1) */
}; 0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */
/* PluggableUSBModule IMPLEMENTATION */ 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */
POPNHID_::POPNHID_(void) : PluggableUSBModule(1, 1, epType) { /* Reserved 14 bits */
epType[0] = EP_TYPE_INTERRUPT_IN; 0x95, 0x01, /* REPORT_COUNT (1) */
PluggableUSB().plug(this); 0x75, 0x0E, /* REPORT_SIZE (14) */
} 0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
#else
int POPNHID_::getInterface(byte* interfaceCount) { /* Reserved 23 bits */
*interfaceCount += 1; // uses 1 0x95, 0x01, /* REPORT_COUNT (1) */
HIDDescriptor hidInterface = { 0x75, 0x17, /* REPORT_SIZE (23) */
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE), 0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
D_HIDREPORT(sizeof(_hidReportPOPN)), #endif
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) // this last parameter is the bInterval (requested polling rate) /*Footer */
}; 0xc0 /* END_COLLECTION */
return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); };
}
/* PluggableUSBModule IMPLEMENTATION */
int POPNHID_::getDescriptor(USBSetup& setup)
{ POPNHID_::POPNHID_(void) : PluggableUSBModule(1, 1, epType) {
// Check if this is a HID Class Descriptor request epType[0] = EP_TYPE_INTERRUPT_IN;
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { PluggableUSB().plug(this);
return 0; }
}
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { int POPNHID_::getInterface(byte* interfaceCount) {
return 0; *interfaceCount += 1; // uses 1
} HIDDescriptor hidInterface = {
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
// In a HID Class Descriptor wIndex contains the interface number D_HIDREPORT(sizeof(_hidReportPOPN)),
if (setup.wIndex != pluggedInterface) { D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) // this last parameter is the bInterval (requested polling rate)
return 0; };
} return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
}
return USB_SendControl(TRANSFER_PGM, _hidReportPOPN, sizeof(_hidReportPOPN));
} int POPNHID_::getDescriptor(USBSetup& setup)
{
bool POPNHID_::setup(USBSetup& setup) // Check if this is a HID Class Descriptor request
{ if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; }
if (pluggedInterface != setup.wIndex) { if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; }
return false;
} // In a HID Class Descriptor wIndex contains the interface number
if (setup.wIndex != pluggedInterface) { return 0; }
byte request = setup.bRequest;
byte requestType = setup.bmRequestType; return USB_SendControl(TRANSFER_PGM, _hidReportPOPN, sizeof(_hidReportPOPN));
}
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
{ bool POPNHID_::setup(USBSetup& setup)
return true; {
} if (pluggedInterface != setup.wIndex) {
return false;
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) { }
if (request == HID_SET_REPORT) {
if (setup.wValueH == HID_REPORT_TYPE_OUTPUT && setup.wLength == 5) { byte request = setup.bRequest;
lastHidUpdate = millis(); byte requestType = setup.bmRequestType;
USB_RecvControl(led_data, 5);
return true; if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
} {
} return true;
} }
return false; if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) {
} if (request == HID_SET_REPORT) {
if(setup.wValueH == HID_REPORT_TYPE_OUTPUT && setup.wLength == 5){
uint8_t POPNHID_::getShortName(char *name) lastHidUpdate = millis();
{ USB_RecvControl(led_data, 5);
name[0] = 'P'; return true;
name[1] = 'O'; }
name[2] = 'P'; }
name[3] = 'N'; }
return 4;
} return false;
}
/* CUSTOM POPN FUNCTIONS */
uint8_t POPNHID_::getShortName(char *name)
uint8_t POPNHID_::getLightMode() { {
return lightMode; name[0] = 'P';
} name[1] = 'O';
name[2] = 'P';
unsigned long POPNHID_::getLastHidUpdate() { name[3] = 'N';
return lastHidUpdate; return 4;
} }
void POPNHID_::setLightMode(uint8_t mode) { /* CUSTOM POPN FUNCTIONS */
if ((mode > 4) || (mode < 0)) {
lightMode = 2; uint8_t POPNHID_::getLightMode(){
return; return lightMode;
} }
lightMode = mode;
} unsigned long POPNHID_::getLastHidUpdate(){
return lastHidUpdate;
void POPNHID_::updateLightMode() { }
uint32_t* bitfield = (uint32_t*) & (led_data[1]);
if (*bitfield >> 28 & 1) { void POPNHID_::setLightMode(uint8_t mode){
uint8_t mode = (*bitfield >> 24) & 0x0F; if ((mode > 4) || (mode < 0)) {
setLightMode(mode); lightMode = 2;
*bitfield &= ~((uint32_t)0xFF << 24); return;
} }
} lightMode = mode;
}
#define BUT_LED_PIN A0
#define BUT_NUM_LEDS 9 void POPNHID_::updateLightMode(){
#define LEFT_LED_PIN 8 uint32_t* bitfield = (uint32_t*)&(led_data[1]);
#define LEFT_NUM_LEDS 9 if (*bitfield>>28&1){
#define RIGHT_LED_PIN 9 uint8_t mode = (*bitfield>>24) & 0x0F;
#define RIGHT_NUM_LEDS 9 setLightMode(mode);
CRGB extern but_leds[]; *bitfield &= ~((uint32_t)0xFF<<24);
CRGB extern left_leds[]; }
CRGB extern right_leds[]; }
CRGB extern but_led_colors[];
byte extern lightOrder[]; void POPNHID_::updateLeds(uint32_t buttonsState, bool invert){
byte extern NeonPins[]; uint32_t* bitfield = (uint32_t*)&(led_data[1]);
#define PILLAR_BRIGHTNESS 64 uint32_t leds = (*bitfield|buttonsState);
#define BUTTON_BRIGHTNESS 220 if (invert)
uint16_t popnhid_convert_order(uint16_t lightDesc) { leds = ~leds;
uint16_t res = 0; for(int i = 0; i < 9; i++) {
for (int i = 0; i < BUT_NUM_LEDS; i++) { if (leds>>i&1)
if ((lightDesc >> i) & 1) { digitalWrite(LightPins[i],HIGH);
res |= 1 << (lightOrder[i]); else
} digitalWrite(LightPins[i],LOW);
} }
return res; #if defined(ARDUINO_ARCH_SAM)
} for(int i = 9; i < 18; i++) {
/* Light up button lights according to bitfield */ if (leds>>i&1)
void popnhid_but_lights(uint16_t lightDesc) { digitalWrite(LightPins[i],HIGH);
static uint16_t prev = 0; else
if (lightDesc == prev) return; digitalWrite(LightPins[i],LOW);
prev = lightDesc; }
//conversion de lightDesc #endif
lightDesc = popnhid_convert_order(lightDesc);
}
//allumage
for (int i = 0; i < BUT_NUM_LEDS; i++) { int POPNHID_::sendState(uint32_t buttonsState){
if ((lightDesc >> i) & 1) { uint8_t data[3];
but_leds[i] = but_led_colors[i]; data[0] = (uint8_t) 4; //report id
} else { data[1] = (uint8_t) (buttonsState & 0xFF);
but_leds[i] = 0; data[2] = (uint8_t) (buttonsState >> 8) & 0xFF;
} return USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, 3);
} }
FastLED.show();
}
/* Light up pillars and top neons according to bitfield */
void popnhid_neon_lights(uint16_t lightDesc) {
static uint16_t prev = 0;
if (lightDesc == prev) return;
prev = lightDesc;
//Top-Lamp
for (int i = 0; i < 5; i++) {
if ((lightDesc >> i) & 1) {
digitalWrite(NeonPins[i], HIGH);
} else {
digitalWrite(NeonPins[i], LOW);
}
}
//LEFT
CRGB left_color = 0;
if ((lightDesc >> 5) & 1) left_color.b += PILLAR_BRIGHTNESS;
if ((lightDesc >> 6) & 1) left_color.r += PILLAR_BRIGHTNESS;
for (int i = 1; i < 8; i++) {
left_leds[i] = left_color;
}
//RIGHT
CRGB right_color = 0;
if ((lightDesc >> 7) & 1) right_color.b += PILLAR_BRIGHTNESS;
if ((lightDesc >> 8) & 1) right_color.r += PILLAR_BRIGHTNESS;
for (int i = 1; i < 8; i++) {
right_leds[i] = right_color;
}
FastLED.show();
}
void POPNHID_::updateLeds(uint32_t buttonsState, bool invert) {
uint32_t* bitfield = (uint32_t*) & (led_data[1]);
uint32_t leds = (*bitfield | buttonsState);
if (invert)
leds = ~leds;
//ici on allume les boutons
popnhid_but_lights((uint16_t) leds);
popnhid_neon_lights(leds >> 9);
}
int POPNHID_::sendState(uint32_t buttonsState) {
uint8_t data[3];
data[0] = (uint8_t) 4; //report id
data[1] = (uint8_t) (buttonsState & 0xFF);
data[2] = (uint8_t) (buttonsState >> 8) & 0xFF;
return USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, 3);
}
+79 -58
View File
@@ -1,58 +1,79 @@
#include <HID.h> #include "HID.h"
#define EPTYPE_DESCRIPTOR_SIZE uint8_t #if defined(ARDUINO_ARCH_AVR)
class POPNHID_ : public PluggableUSBModule { #define EPTYPE_DESCRIPTOR_SIZE uint8_t
public: #elif defined(ARDUINO_ARCH_SAM)
POPNHID_(void);
#define EPTYPE_DESCRIPTOR_SIZE uint32_t
/** #define USB_EP_SIZE 64
Updates the led status based on led_data (HID report received) and button states #define TRANSFER_PGM 0x80
param[in] buttonState bitfield with currently pressed buttons (used to force additional lights for mixed mode) #define USB_SendControl USBD_SendControl
param[in] invert set to true to invert on/off status (used for invert lightmode) #define USB_RecvControl USBD_RecvControl
*/ #define USB_Recv USBD_Recv
void updateLeds(uint32_t buttonsState, bool invert); #define USB_Send USBD_Send
#define USB_Flush USBD_Flush
/** #define HID_REPORT_TYPE_OUTPUT 2
Sends the gamepad button states to the PC as an HID report #define HID_REPORT_TYPE_INPUT 1
param[in] buttonsState bitfield with currently pressed buttons
return USB_Send() return value #else
*/
int sendState(uint32_t buttonsState); #error "Unsupported architecture"
/** #endif
Changes the lightMode if a received HID report asks for it
*/ class POPNHID_ : public PluggableUSBModule {
void updateLightMode();
public:
/** POPNHID_(void);
getter and setter for lightMode protected field.
*/ /**
uint8_t getLightMode(); * Updates the led status based on led_data (HID report received) and button states
void setLightMode(uint8_t mode); * param[in] buttonState bitfield with currently pressed buttons (used to force additional lights for mixed mode)
* param[in] invert set to true to invert on/off status (used for invert lightmode)
/** */
getter for lastHidUpdate protected field. void updateLeds(uint32_t buttonsState, bool invert);
*/
unsigned long getLastHidUpdate(); /**
* Sends the gamepad button states to the PC as an HID report
protected: * param[in] buttonsState bitfield with currently pressed buttons
/* current lightMode (0 = reactive, 1 = HID only, 2 = mixed (HID+reactive auto-switch), 3 = combined (HID+button presses), 4 = combined invert) */ * return USB_Send() return value
uint8_t lightMode = 2; */
/* timestamp of last received HID report for lightMode 3 */ int sendState(uint32_t buttonsState);
unsigned long lastHidUpdate = 0;
/* byte array to receive HID reports from the PC */ /**
byte led_data[5]; * Changes the lightMode if a received HID report asks for it
*/
/* Implementation of the PUSBListNode */ void updateLightMode();
EPTYPE_DESCRIPTOR_SIZE epType[1];
uint8_t protocol; /**
uint8_t idle; * getter and setter for lightMode protected field.
int getInterface(uint8_t* interfaceCount); */
int getDescriptor(USBSetup& setup); uint8_t getLightMode();
bool setup(USBSetup& setup); void setLightMode(uint8_t mode);
uint8_t getShortName(char *name);
}; /**
* getter for lastHidUpdate protected field.
extern POPNHID_ POPNHID; */
unsigned long getLastHidUpdate();
protected:
/* current lightMode (0 = reactive, 1 = HID only, 2 = mixed (HID+reactive auto-switch), 3 = combined (HID+button presses), 4 = combined invert) */
uint8_t lightMode = 2;
/* timestamp of last received HID report for lightMode 3 */
unsigned long lastHidUpdate = 0;
/* byte array to receive HID reports from the PC */
byte led_data[5];
/* Implementation of the PUSBListNode */
EPTYPE_DESCRIPTOR_SIZE epType[1];
uint8_t protocol;
uint8_t idle;
int getInterface(uint8_t* interfaceCount);
int getDescriptor(USBSetup& setup);
bool setup(USBSetup& setup);
uint8_t getShortName(char *name);
};
extern POPNHID_ POPNHID;
+517 -294
View File
@@ -1,294 +1,517 @@
#define BOUNCE_WITH_PROMPT_DETECTION #define BOUNCE_WITH_PROMPT_DETECTION
#include <Bounce2.h> #include <Bounce2.h>
#include <EEPROM.h> #if defined(ARDUINO_ARCH_SAM)
#include <FastLED.h> #include <Keypad.h>
#include "POPNHID.h" #else
/* 1 frame (as declared in POPNHID.cpp) on fullspeed USB spec is 1ms */ #include <Keyboard.h>
#define REPORT_DELAY 1000 #include <EEPROM.h>
#define MILLIDEBOUNCE 15 /* PSX DEFINE */
POPNHID_ POPNHID; #include <avr/io.h>
#include <avr/interrupt.h>
/* Buttons + Lights declarations */ #include <util/delay.h>
#define BUT_LED_PIN A0 #define SPI_PORT PORTB
#define BUT_NUM_LEDS 9 #define SPI_PINS PINB
#define LEFT_LED_PIN 8 #define SPI_DDR DDRB
#define LEFT_NUM_LEDS 9 #define SPI_PINS PINB
#define RIGHT_LED_PIN 9 #define ACK_PIN 5 //PB1
#define RIGHT_NUM_LEDS 9 #define ATT_PIN 0 //~SS
CRGB but_leds[BUT_NUM_LEDS]; #define CMD_PIN 2 //MOSI
CRGB left_leds[LEFT_NUM_LEDS]; #define DATA_PIN 3 //MISO
CRGB right_leds[RIGHT_NUM_LEDS]; #define CLK_PIN 1 //SCK
CRGB but_led_colors[BUT_NUM_LEDS] = {CRGB::White, CRGB::Green, CRGB::Red, CRGB::Green, CRGB::White, CRGB::Yellow, CRGB::Blue, CRGB::Blue, CRGB::Yellow}; #define DATA_LEN 5
#define PILLAR_BRIGHTNESS 64 /* /PSX DEFINE */
#define BUTTON_BRIGHTNESS 220 #endif
uint8_t NeonPins[] = {A1, A2, A3, A4, A5}; #include "POPNHID.h"
uint8_t ButtonPins[] = {0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13}; /* 1 frame (as declared in POPNHID.cpp) on highspeed USB spec is 125µs */
uint8_t sysPin = 12; #define REPORT_DELAY 125
//this array contains the light offset given buttons 0 to 8 as ref #define MILLIDEBOUNCE 15
uint8_t lightOrder[] = {0, 8, 1, 7, 2, 6, 3, 5, 4}; POPNHID_ POPNHID;
const byte ButtonCount = sizeof(ButtonPins) / sizeof(ButtonPins[0]); /* Buttons + Lights declarations */
const byte NeonCount = sizeof(NeonPins) / sizeof(NeonPins[0]); #if defined(ARDUINO_ARCH_SAM)
byte LightPins[] = {36, 38, 40, 42, 44, 46, 48, 50, 52, 37, 39, 41, 43, 45, 47, 49, 51, 53};
Bounce buttons[ButtonCount]; byte ButtonPins[] = {5, 6, 7, 8, 9, 10, 11, 12, 13, 4, 3, 2};
#else
/* SETUP */ uint8_t LightPins[] = {11,12,13,23,22,21,20,19,18};
void setup() { uint8_t ButtonPins[] = {0,1,2,3,4,5,6,7,8,9,10};
// setup I/O for pins #endif
for (int i = 0; i < ButtonCount; i++) {
buttons[i] = Bounce(); const byte ButtonCount = sizeof(ButtonPins) / sizeof(ButtonPins[0]);
buttons[i].attach(ButtonPins[i], INPUT_PULLUP); const byte LightCount = sizeof(LightPins) / sizeof(LightPins[0]);
buttons[i].interval(MILLIDEBOUNCE); Bounce buttons[ButtonCount];
}
#if defined(ARDUINO_ARCH_SAM)
for (int i = 0; i < NeonCount; i++) { /* Keypad declarations */
pinMode(NeonPins[i], OUTPUT); const byte ROWS = 4;
} const byte COLS = 3;
FastLED.addLeds<WS2812, BUT_LED_PIN, GRB>(but_leds, BUT_NUM_LEDS); /*
FastLED.addLeds<WS2812, LEFT_LED_PIN, GRB>(left_leds, LEFT_NUM_LEDS); // To use the keypad as the numpad keys (will require to send numlock for it to work)
FastLED.addLeds<WS2812, RIGHT_LED_PIN, GRB>(right_leds, RIGHT_NUM_LEDS); char numpad[ROWS][COLS] = {
FastLED.setBrightness( BUTTON_BRIGHTNESS ); {'\347', '\350', '\351'},
{'\344', '\345', '\346'},
uint8_t lightMode; {'\341', '\342', '\343'},
EEPROM.get(0, lightMode); {'\352', ',', '\337'}
if (lightMode < 0 || lightMode > 3) };
lightMode = 2; */
POPNHID.setLightMode(lightMode);
/* This is to use the toprow keys instead */
//boot animation char numpad[ROWS][COLS] = {
uint16_t anim[] = {1, 4, 16, 64, 256, 128, 32, 8, 2}; {'7', '8', '9'},
animate(anim, 9, 100); {'4', '5', '6'},
animate(anim, 9, 100); {'1', '2', '3'},
uint16_t anim2[] = {1 + 4 + 16 + 64 + 256, 2 + 8 + 32 + 128}; {'0', ',', '\337'}
animate(anim2, 2, 500); };
animate(anim2, 2, 500);
} /* This follows the Pop'n Music cabinet numpad pins order */
byte rowPins[ROWS] = {A3, A2, A1, A0}; //connect to the row pinouts of the keypad
/* LOOP */ byte colPins[COLS] = {A4, A5, A6}; //connect to the column pinouts of the keypad
unsigned long lastReport = 0;
uint32_t prevButtonsState = 0; /* For mini keypad
bool modeChanged = false; byte rowPins[ROWS] = {A5, A0, A1, A3}; //connect to the row pinouts of the keypad
void loop() { byte colPins[COLS] = {A4, A6, A2}; //connect to the column pinouts of the keypad
/* BUTTONS */ */
uint32_t buttonsState = 0;
for (int i = 0; i < ButtonCount; i++) { Keypad kpd = Keypad( makeKeymap(numpad), rowPins, colPins, ROWS, COLS );
buttons[i].update(); #else
int value = buttons[i].read(); /* PSX globals */
if (value != HIGH) { volatile uint8_t data_buff[DATA_LEN]={0x41,0x5A,0xBF,0xFF,0xFF};//Reply.
buttonsState |= (uint32_t)1 << i; volatile uint8_t command_buff[DATA_LEN]={0x01,0x42,0x00,0x00,0x00};
} else {
buttonsState &= ~((uint32_t)1 << i); volatile uint8_t curr_byte=0;
} volatile uint8_t next_byte=0;
}
byte b4;
/* USB DATA */ byte b5;
if ( ( (micros() - lastReport) >= REPORT_DELAY) )
{ void convertPopn(uint32_t buttons){
POPNHID.sendState(buttonsState);
lastReport = micros(); b4 = 0xFF;
prevButtonsState = buttonsState; b5 = 0xFF;
//check for HID-requested lightmode change if (((buttons >> 9) & 1)) {
POPNHID.updateLightMode(); b4 &= ~((unsigned char) 0x01);
} } else {
b4 |= ((unsigned char) 0x01);
/* LAMPS */ }
uint8_t mode = POPNHID.getLightMode(); if (((buttons >> 10) & 1)) {
/* mixed mode will behave sometimes like HID, sometimes like reactive */ b4 &= ~((unsigned char) 0x08);
if (mode == 2) { } else {
if ((millis() - POPNHID.getLastHidUpdate()) > 3000) b4 |= ((unsigned char) 0x08);
mode = 0; }
else
mode = 1; if (((buttons >> 0) & 1)) {
} b5 &= ~((unsigned char) 0x10);
switch (mode) } else {
{ b5 |= ((unsigned char) 0x10);
/* Reactive mode, locally determined lamp data */ }
case 0: if (((buttons >> 1) & 1)) {
but_lights(buttonsState & 0x1ff); b5 &= ~((unsigned char) 0x20);
reactive_neon(buttonsState & 0x1ff); } else {
break; b5 |= ((unsigned char) 0x20);
/* HID mode, only based on received HID data */ }
case 1: if (((buttons >> 2) & 1)) {
POPNHID.updateLeds(0, false); b5 &= ~((unsigned char) 0x08);
break; } else {
/* Combined inverse mode, received HID data and button state are combined then inverted */ b5 |= ((unsigned char) 0x08);
case 4: }
POPNHID.updateLeds(buttonsState & 0x1ff, true); if (((buttons >> 3) & 1)) {
break; b5 &= ~((unsigned char) 0x40);
/* Combined mode, received HID data and button state are combined */ } else {
case 3: b5 |= ((unsigned char) 0x40);
POPNHID.updateLeds(buttonsState & 0x1ff, false); }
break; if (((buttons >> 4) & 1)) {
default: b5 &= ~((unsigned char) 0x04);
break; } else {
} b5 |= ((unsigned char) 0x04);
}
/* MANUAL LIGHTMODE UPDATE */ if (((buttons >> 5) & 1)) {
if ( buttonsState & 1024 ) { b5 &= ~((unsigned char) 0x80);
if ( (buttonsState & 2) && (modeChanged == false)) { } else {
modeChanged = true; b5 |= ((unsigned char) 0x80);
uint8_t mode = POPNHID.getLightMode() + 1; }
if (mode > 4) mode = 0; if (((buttons >> 6) & 1)) {
POPNHID.setLightMode(mode); b5 &= ~((unsigned char) 0x02);
EEPROM.put(0, mode); } else {
} b5 |= ((unsigned char) 0x02);
else if (!(buttonsState & 2)) { }
modeChanged = false; if (((buttons >> 7) & 1)) {
} b4 &= ~((unsigned char) 0x10);
} } else {
} b4 |= ((unsigned char) 0x10);
}
/* Light up button lights according to bitfield */ if (((buttons >> 8) & 1)) {
void but_lights(uint16_t lightDesc) { b5 &= ~((unsigned char) 0x01);
//conversion de lightDesc } else {
lightDesc = convert_order(lightDesc); b5 |= ((unsigned char) 0x01);
}
//allumage // left down right always held with popn controller
for (int i = 0; i < BUT_NUM_LEDS; i++) { b4 &= 0x1F;
if ((lightDesc >> i) & 1) {
but_leds[i] = but_led_colors[i]; data_buff[2] = b4;
} else { data_buff[3] = b5;
but_leds[i] = 0;
} }
} ISR(SPI_STC_vect) {
FastLED.show(); uint8_t inbyte=SPDR;
}
if (inbyte==command_buff[curr_byte]) {
/* Light up pillars and top neons according to bitfield */ SPDR = data_buff[curr_byte];
void neon_lights(uint16_t lightDesc) { curr_byte++;
//Top-Lamp if (curr_byte<DATA_LEN) {//ACK low.
for (int i = 0; i < NeonCount; i++) { //SPI_PORT &= ~(1<<ACK_PIN);
if ((lightDesc >> i) & 1) { PORTD &= ~(1<<5);//set HIGH
digitalWrite(NeonPins[i], HIGH); // simulate open drain
} else { // SPI_DDR |= (1<<ACK_PIN);//output
digitalWrite(NeonPins[i], LOW); // SPI_PORT &= ~(1<<ACK_PIN);//set low (simulate open collector LOW)
}
} _delay_us(5);
//SPI_PORT |= (1<<ACK_PIN);
//LEFT PORTD |= (1<<5);//set HIGH
CRGB left_color = 0;
if ((lightDesc >> 5) & 1) left_color.b += PILLAR_BRIGHTNESS; // simulate open drain
if ((lightDesc >> 6) & 1) left_color.r += PILLAR_BRIGHTNESS; // SPI_DDR &= ~(1<<ACK_PIN);//input
for (int i = 1; i < 8; i++) { // SPI_PORT |= (1<<ACK_PIN);//set high (simulate open collector Hi-Z)
left_leds[i] = left_color;
} } else {
//RIGHT SPDR = 0xFF;
CRGB right_color = 0; curr_byte=0;
if ((lightDesc >> 7) & 1) right_color.b += PILLAR_BRIGHTNESS; }
if ((lightDesc >> 8) & 1) right_color.r += PILLAR_BRIGHTNESS; } else {
for (int i = 1; i < 8; i++) { SPDR = 0xFF;
right_leds[i] = right_color; curr_byte=0;
} }
SPI_DDR |= (1<<ATT_PIN);//out
FastLED.show(); SPI_PORT |= (1<<ATT_PIN);//set HIGH
} _delay_us(5);
SPI_PORT &= ~(1<<ATT_PIN);//set HIGH
/* Display animation on the cab according to a bitfield array */ }
void animate(uint16_t* tab, uint8_t n, int mswait) { /* /PSX globals */
for (int i = 0; i < n; i++) { #endif
but_lights(tab[i]);
neon_lights(tab[i]); /* SETUP */
delay(mswait); void setup() {
} // setup I/O for pins
} for (int i = 0; i < ButtonCount; i++) {
buttons[i] = Bounce();
/* Manage pillars and top neons in reactive mode */ buttons[i].attach(ButtonPins[i], INPUT_PULLUP);
uint16_t neon_anim[] = {16, 24, 28, 30, 31, 30, 28, 24}; buttons[i].interval(MILLIDEBOUNCE);
int neon_anim_index = 0; }
uint16_t pillar_state[] = {0, 0x140, 0xA0, 0x1E0};
int pillar_state_index = 0; for (int i = 0; i < LightCount; i++) {
bool pillar_lit = false; pinMode(LightPins[i], OUTPUT);
uint16_t prevState = 0; }
unsigned long lastBlink = 0;
unsigned long lastNeonUpdate = 0; #if defined(ARDUINO_ARCH_SAM)
unsigned long lastButtonAction = 0; kpd.setDebounceTime(30);
unsigned long actionRate = 0;
unsigned long neonRate = 200; /* activate numlock if you are not using the toprow keys */
void reactive_neon(uint16_t buttonsState) { /* delay(2000);
uint16_t neons = 0; Keyboard.press(136 + 83);
unsigned long currTime = millis(); delay(500);
Keyboard.release(136+83);
/* */
SIDE PILLARS #else
When pressing any button the side pillars will blink for half a second
The color is randomly chosen with blue being predominant, red rare and purple super rare Keyboard.begin();
*/ uint8_t lightMode = 2;
if ( buttonsState != prevState ) EEPROM.get(0, lightMode);
{ if (lightMode < 0 || lightMode > 3)
if ( buttonsState != 0 ) { lightMode = 2;
long randNumber = random(21); POPNHID.setLightMode(lightMode);
if (randNumber == 0)
{ /* PSX setup */
pillar_state_index = 3; // use TXLED as ACK
} // DDRD |= (1<<5);//output
else if (randNumber < 3) PORTD |= (1<<5);//set HIGH
{
pillar_state_index = 1; SPI_DDR |= (1<<DATA_PIN);//output
} else { SPI_PORT |= (1<<DATA_PIN);//set HIGH
pillar_state_index = 2;
} SPI_DDR |= (1<<ATT_PIN);//out
pillar_lit = true; delay(500);
if (buttonsState != 0) lastButtonAction = currTime; SPI_PORT |= (1<<ATT_PIN);//set HIGH
} delay(5000);
prevState = buttonsState; SPI_PORT |= (1<<ATT_PIN);//set HIGH
} else { /* no state change, continue to blink for 50ms */
if (currTime - lastBlink > 50) { //SPI setup.
pillar_lit = !pillar_lit; // PRR &= ~(1<<PRSPI);//Set to 0 to ensure power to SPI module.
lastBlink = currTime; //SPSR|=(1<<SPI2X);//Fosc/32. @16MHz==500KHz.
} SPCR|=(1<<SPR1);//Fosc/64. @16MHz==250KHz.
if (currTime - lastButtonAction > 500) { SPCR|=(1<<CPHA);//Setup @ leading edge, sample @ falling edge.
pillar_state_index = 0; SPCR|=(1<<CPOL);//Leading edge is falling edge, trailing edge is rising edge.
} SPCR &= ~(1<<MSTR);//MSTR bit is zero, SPI is slave.
} SPCR|=(1<<DORD);//Byte is transmitted LSB first, MSB last.
SPCR|=(1<<SPE);//Enable SPI.
if (pillar_lit) SPCR|=(1<<SPIE);//Enable Serial Transfer Complete (STC) interrupt.
neons |= pillar_state[pillar_state_index]; SPDR=0xFF;
/* sei();//Enable global interrupts.
Adjusting top neon animation speed (should go faster when you hit buttons quickly) /* /PSX setup */
*/ #endif
actionRate = currTime - lastButtonAction;
//boot animation
if (15 * actionRate < neonRate) uint16_t anim[] = {1, 4, 16, 64, 256, 128, 32, 8, 2};
neonRate *= 0.99995; animate(anim, 9, 100);
else if (actionRate > 10 * neonRate) { animate(anim, 9, 100);
neonRate = neonRate * 1.01; uint16_t anim2[] = {1 + 4 + 16 + 64 + 256, 2 + 8 + 32 + 128};
if (neonRate < 100) neonRate++; animate(anim2, 2, 500);
} animate(anim2, 2, 500);
if (neonRate > 400) }
neonRate = 400;
char kpdhotkeys[11] = {'1','2','3','4','5','6','7','8','9','x','0'};
if (neonRate < 40) /* LOOP */
neonRate = 40; unsigned long lastReport = 0;
uint32_t prevButtonsState = 0;
/* bool modeChanged = false;
Cycling through the top neon animation void loop() {
*/ /* BUTTONS */
if ((currTime - lastNeonUpdate) > neonRate) uint32_t buttonsState = 0;
{ for (int i = 0; i < ButtonCount; i++) {
neon_anim_index++; buttons[i].update();
if (neon_anim_index > 7) neon_anim_index = 0; int value = buttons[i].read();
lastNeonUpdate = currTime; if (value != HIGH){
} buttonsState |= (uint32_t)1 << i;
} else {
neons |= neon_anim[neon_anim_index]; buttonsState &= ~((uint32_t)1 << i);
}
/* }
Light the leds #if defined(ARDUINO_ARCH_AVR)
*/ /* PSX convert */
neon_lights(neons); convertPopn(buttonsState);
} #endif
/* BARTOP ONLY FUNCTIONS */ /* USB DATA */
uint16_t convert_order(uint16_t lightDesc) { if ( ( (micros() - lastReport) >= REPORT_DELAY) )
uint16_t res = 0; {
for (int i = 0; i < BUT_NUM_LEDS; i++) { POPNHID.sendState(buttonsState);
if ((lightDesc >> i) & 1) { lastReport = micros();
res |= 1 << (lightOrder[i]);
} //check for HID-requested lightmode change
} POPNHID.updateLightMode();
return res; }
}
/* LAMPS */
uint8_t mode = POPNHID.getLightMode();
/* mixed mode will behave sometimes like HID, sometimes like reactive */
if (mode == 2){
if ((millis()-POPNHID.getLastHidUpdate()) > 3000)
mode = 0;
else
mode = 1;
}
switch (mode)
{
/* Reactive mode, locally determined lamp data */
case 0:
but_lights(buttonsState & 0x1ff);
#if defined(ARDUINO_ARCH_SAM)
reactive_neon(buttonsState & 0x1ff);
#endif
break;
/* HID mode, only based on received HID data */
case 1:
POPNHID.updateLeds(0, false);
break;
/* Combined inverse mode, received HID data and button state are combined then inverted */
case 4:
POPNHID.updateLeds(buttonsState & 0x1ff, true);
break;
/* Combined mode, received HID data and button state are combined */
case 3:
POPNHID.updateLeds(buttonsState & 0x1ff, false);
break;
default:
break;
}
#if defined(ARDUINO_ARCH_SAM)
/* KEYPAD */
if (kpd.getKeys())
{
for (int i = 0; i < LIST_MAX; i++) // Scan the whole key list.
{
if ( kpd.key[i].stateChanged ) // Only find keys that have changed state.
{
switch (kpd.key[i].kstate) { // Report active key state : IDLE, PRESSED, HOLD, or RELEASED
case PRESSED:
Keyboard.press(kpd.key[i].kchar);
break;
case HOLD:
break;
case RELEASED:
Keyboard.release(kpd.key[i].kchar);
break;
case IDLE:
break;
}
}
}
}
#endif
/* KEYPAD HOTKEY */
if ( buttonsState & 512 ) {
if ( prevButtonsState != buttonsState )
{
for (int i=0; i<11; i++)
{
if (i==9) continue;
if ( ((buttonsState>>i) & 1) && !((prevButtonsState>>i) & 1) )
{
Keyboard.press(kpdhotkeys[i]);
}
else if ( !((buttonsState>>i) & 1) && ((prevButtonsState>>i) & 1) )
{
Keyboard.release(kpdhotkeys[i]);
}
}
}
}
/* MANUAL LIGHTMODE UPDATE */
if ( buttonsState & 1024 ) {
if ( (buttonsState & 2) && (modeChanged == false)) {
modeChanged = true;
uint8_t mode = POPNHID.getLightMode()+1;
if (mode > 4) mode = 0;
POPNHID.setLightMode(mode);
#if defined(ARDUINO_ARCH_AVR)
EEPROM.put(0, mode);
#endif
}
else if (!(buttonsState&2)) {
modeChanged = false;
}
}
prevButtonsState = buttonsState;
}
/* Light up button lights according to bitfield */
void but_lights(uint16_t lightDesc) {
for (int i = 0; i < 9; i++) {
if ((lightDesc >> i) & 1) {
digitalWrite(LightPins[i], HIGH);
} else {
digitalWrite(LightPins[i], LOW);
}
}
}
/* Light up pillars and top neons according to bitfield */
void neon_lights(uint16_t lightDesc) {
for (int i = 0; i < 9; i++) {
if ((lightDesc >> i) & 1) {
digitalWrite(LightPins[i + 9], HIGH);
} else {
digitalWrite(LightPins[i + 9], LOW);
}
}
}
/* Display animation on the cab according to a bitfield array */
void animate(uint16_t* tab, uint8_t n, int mswait) {
for (int i = 0; i < n; i++) {
but_lights(tab[i]);
#if defined(ARDUINO_ARCH_SAM)
neon_lights(tab[i]);
#endif
delay(mswait);
}
}
/* ARDUINO DUE ONLY FUNCTIONS */
#if defined(ARDUINO_ARCH_SAM)
/* Manage pillars and top neons in reactive mode */
uint16_t neon_anim[] = {16, 24, 28, 30, 31, 30, 28, 24};
int neon_anim_index = 0;
uint16_t pillar_state[] = {0, 0x140, 0xA0, 0x1E0};
int pillar_state_index = 0;
bool pillar_lit = false;
uint16_t prevState = 0;
unsigned long lastBlink = 0;
unsigned long lastNeonUpdate = 0;
unsigned long lastButtonAction = 0;
unsigned long actionRate = 0;
unsigned long neonRate = 200;
void reactive_neon(uint16_t buttonsState) {
uint16_t neons = 0;
unsigned long currTime = millis();
/*
* SIDE PILLARS
* When pressing any button the side pillars will blink for half a second
* The color is randomly chosen with blue being predominant, red rare and purple super rare
*/
if ( buttonsState != prevState )
{
if ( buttonsState != 0 ){
long randNumber = random(21);
if (randNumber == 0)
{
pillar_state_index = 3;
}
else if (randNumber < 3)
{
pillar_state_index = 1;
} else {
pillar_state_index = 2;
}
pillar_lit = true;
if (buttonsState != 0) lastButtonAction = currTime;
}
prevState = buttonsState;
} else { /* no state change, continue to blink for 50ms */
if (currTime - lastBlink > 50) {
pillar_lit = !pillar_lit;
lastBlink = currTime;
}
if (currTime - lastButtonAction > 500) {
pillar_state_index = 0;
}
}
if (pillar_lit)
neons |= pillar_state[pillar_state_index];
/*
* Adjusting top neon animation speed (should go faster when you hit buttons quickly)
*/
actionRate = currTime - lastButtonAction;
if (15*actionRate < neonRate)
neonRate *= 0.99995;
else if (actionRate > 10*neonRate){
neonRate = neonRate*1.01;
if (neonRate < 100) neonRate++;
}
if (neonRate > 400)
neonRate = 400;
if (neonRate < 40)
neonRate = 40;
/*
* Cycling through the top neon animation
*/
if ((currTime - lastNeonUpdate) > neonRate)
{
neon_anim_index++;
if (neon_anim_index > 7) neon_anim_index = 0;
lastNeonUpdate = currTime;
}
neons |= neon_anim[neon_anim_index];
/*
* Light the leds
*/
neon_lights(neons);
}
#endif