Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba765f2cd9 | ||
|
|
9e6ff10523 | ||
|
|
b3c44bdf5a |
@@ -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.
|
||||
|
||||
PSX Controller code is based on busslave's PSX_RECEIVER.cpp
|
||||
|
||||
# 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.
|
||||
@@ -38,6 +40,18 @@ The keypad code requires the Keypad library by Mark Stanley and Alexander Brevig
|
||||
|
||||
# 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
|
||||
|
||||
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)
|
||||
|
||||
The Arduino DUE has 3.3v logic whereas the Pop'n Music cabinet outputs (lamps, coin blocker, coin counter) use 12V.
|
||||
|
||||
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).
|
||||
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).
|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
@@ -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.
|
||||
|
||||

|
||||

|
||||
|
||||
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
@@ -1,417 +1,363 @@
|
||||
/* Arduino-HID-Lighting-Library
|
||||
|
||||
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:
|
||||
|
||||
mon's Arduino-HID-Lighting
|
||||
https://github.com/mon/Arduino-HID-Lighting
|
||||
|
||||
2018 (C) Arduino-HID-Lighting-Library, Knuckleslee
|
||||
*/
|
||||
#include "POPNHID.h"
|
||||
#include <FastLED.h>
|
||||
|
||||
/* HID DESCRIPTOR */
|
||||
static const byte PROGMEM _hidReportPOPN[] = {
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
|
||||
0x09, 0x05, /* USAGE (Game Pad) */
|
||||
0xa1, 0x01, /* COLLECTION (Application) */
|
||||
|
||||
/*Buttons */
|
||||
0x85, 0x04, /* REPORT_ID 4 */
|
||||
0x05, 0x09, /* USAGE_PAGE (Button) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
|
||||
0x29, 0x0c, /* USAGE_MAXIMUM (Button 12)*/
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
0x95, 0x0c, /* REPORT_COUNT (12) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */
|
||||
/* Reserved bits */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x04, /* REPORT_SIZE (4) */
|
||||
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
|
||||
|
||||
/*Lights */
|
||||
0x85, 0x05, /* REPORT_ID 5*/
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
/*Led 1 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x01, /* USAGE (Instance 1) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 2 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x02, /* USAGE (Instance 2) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 3 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x03, /* USAGE (Instance 3) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 4 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x04, /* USAGE (Instance 4) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 5 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x05, /* USAGE (Instance 5) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 6 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x06, /* USAGE (Instance 6) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 7 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x07, /* USAGE (Instance 7) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 8 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x08, /* USAGE (Instance 8) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 9 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x09, /* USAGE (Instance 9) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 10 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0a, /* USAGE (Instance 10) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 11 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0b, /* USAGE (Instance 11) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 12 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0c, /* USAGE (Instance 12) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 13 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0d, /* USAGE (Instance 13) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 14 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0e, /* USAGE (Instance 14) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 15 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0f, /* USAGE (Instance 15) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 16 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x10, /* USAGE (Instance 16) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 17 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x11, /* USAGE (Instance 17) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 18 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x12, /* USAGE (Instance 18) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/* Reserved 14 bits */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x0E, /* REPORT_SIZE (14) */
|
||||
0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
|
||||
/*Footer */
|
||||
0xc0 /* END_COLLECTION */
|
||||
};
|
||||
|
||||
/* PluggableUSBModule IMPLEMENTATION */
|
||||
|
||||
POPNHID_::POPNHID_(void) : PluggableUSBModule(1, 1, epType) {
|
||||
epType[0] = EP_TYPE_INTERRUPT_IN;
|
||||
PluggableUSB().plug(this);
|
||||
}
|
||||
|
||||
int POPNHID_::getInterface(byte* interfaceCount) {
|
||||
*interfaceCount += 1; // uses 1
|
||||
HIDDescriptor hidInterface = {
|
||||
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
|
||||
D_HIDREPORT(sizeof(_hidReportPOPN)),
|
||||
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) // this last parameter is the bInterval (requested polling rate)
|
||||
};
|
||||
return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
|
||||
}
|
||||
|
||||
int POPNHID_::getDescriptor(USBSetup& setup)
|
||||
{
|
||||
// Check if this is a HID Class Descriptor request
|
||||
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) {
|
||||
return 0;
|
||||
}
|
||||
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// In a HID Class Descriptor wIndex contains the interface number
|
||||
if (setup.wIndex != pluggedInterface) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return USB_SendControl(TRANSFER_PGM, _hidReportPOPN, sizeof(_hidReportPOPN));
|
||||
}
|
||||
|
||||
bool POPNHID_::setup(USBSetup& setup)
|
||||
{
|
||||
if (pluggedInterface != setup.wIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte request = setup.bRequest;
|
||||
byte requestType = setup.bmRequestType;
|
||||
|
||||
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) {
|
||||
if (request == HID_SET_REPORT) {
|
||||
if (setup.wValueH == HID_REPORT_TYPE_OUTPUT && setup.wLength == 5) {
|
||||
lastHidUpdate = millis();
|
||||
USB_RecvControl(led_data, 5);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t POPNHID_::getShortName(char *name)
|
||||
{
|
||||
name[0] = 'P';
|
||||
name[1] = 'O';
|
||||
name[2] = 'P';
|
||||
name[3] = 'N';
|
||||
return 4;
|
||||
}
|
||||
|
||||
/* CUSTOM POPN FUNCTIONS */
|
||||
|
||||
uint8_t POPNHID_::getLightMode() {
|
||||
return lightMode;
|
||||
}
|
||||
|
||||
unsigned long POPNHID_::getLastHidUpdate() {
|
||||
return lastHidUpdate;
|
||||
}
|
||||
|
||||
void POPNHID_::setLightMode(uint8_t mode) {
|
||||
if ((mode > 4) || (mode < 0)) {
|
||||
lightMode = 2;
|
||||
return;
|
||||
}
|
||||
lightMode = mode;
|
||||
}
|
||||
|
||||
void POPNHID_::updateLightMode() {
|
||||
uint32_t* bitfield = (uint32_t*) & (led_data[1]);
|
||||
if (*bitfield >> 28 & 1) {
|
||||
uint8_t mode = (*bitfield >> 24) & 0x0F;
|
||||
setLightMode(mode);
|
||||
*bitfield &= ~((uint32_t)0xFF << 24);
|
||||
}
|
||||
}
|
||||
|
||||
#define BUT_LED_PIN A0
|
||||
#define BUT_NUM_LEDS 9
|
||||
#define LEFT_LED_PIN 8
|
||||
#define LEFT_NUM_LEDS 9
|
||||
#define RIGHT_LED_PIN 9
|
||||
#define RIGHT_NUM_LEDS 9
|
||||
CRGB extern but_leds[];
|
||||
CRGB extern left_leds[];
|
||||
CRGB extern right_leds[];
|
||||
CRGB extern but_led_colors[];
|
||||
byte extern lightOrder[];
|
||||
byte extern NeonPins[];
|
||||
#define PILLAR_BRIGHTNESS 64
|
||||
#define BUTTON_BRIGHTNESS 220
|
||||
uint16_t popnhid_convert_order(uint16_t lightDesc) {
|
||||
uint16_t res = 0;
|
||||
for (int i = 0; i < BUT_NUM_LEDS; i++) {
|
||||
if ((lightDesc >> i) & 1) {
|
||||
res |= 1 << (lightOrder[i]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
/* Light up button lights according to bitfield */
|
||||
void popnhid_but_lights(uint16_t lightDesc) {
|
||||
static uint16_t prev = 0;
|
||||
if (lightDesc == prev) return;
|
||||
prev = lightDesc;
|
||||
//conversion de lightDesc
|
||||
lightDesc = popnhid_convert_order(lightDesc);
|
||||
|
||||
//allumage
|
||||
for (int i = 0; i < BUT_NUM_LEDS; i++) {
|
||||
if ((lightDesc >> i) & 1) {
|
||||
but_leds[i] = but_led_colors[i];
|
||||
} else {
|
||||
but_leds[i] = 0;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
/* Arduino-HID-Lighting-Library
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* mon's Arduino-HID-Lighting
|
||||
* https://github.com/mon/Arduino-HID-Lighting
|
||||
*
|
||||
* 2018 (C) Arduino-HID-Lighting-Library, Knuckleslee
|
||||
*/
|
||||
#include "POPNHID.h"
|
||||
|
||||
byte extern LightPins[];
|
||||
|
||||
/* HID DESCRIPTOR */
|
||||
static const byte PROGMEM _hidReportPOPN[] = {
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
|
||||
0x09, 0x05, /* USAGE (Game Pad) */
|
||||
0xa1, 0x01, /* COLLECTION (Application) */
|
||||
|
||||
/*Buttons */
|
||||
0x85, 0x04, /* REPORT_ID 4 */
|
||||
0x05, 0x09, /* USAGE_PAGE (Button) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
|
||||
0x29, 0x0c, /* USAGE_MAXIMUM (Button 12)*/
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
0x95, 0x0c, /* REPORT_COUNT (12) */
|
||||
#else
|
||||
0x95, 0x0b, /* REPORT_COUNT (11) */
|
||||
#endif
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */
|
||||
/* Reserved bits */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
0x75, 0x04, /* REPORT_SIZE (4) */
|
||||
#else
|
||||
0x75, 0x05, /* REPORT_SIZE (5) */
|
||||
#endif
|
||||
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
|
||||
|
||||
/*Lights */
|
||||
0x85, 0x05, /* REPORT_ID 5*/
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
/*Led 1 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x01, /* USAGE (Instance 1) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 2 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x02, /* USAGE (Instance 2) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 3 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x03, /* USAGE (Instance 3) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 4 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x04, /* USAGE (Instance 4) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 5 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x05, /* USAGE (Instance 5) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 6 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x06, /* USAGE (Instance 6) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 7 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x07, /* USAGE (Instance 7) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 8 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x08, /* USAGE (Instance 8) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 9 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x09, /* USAGE (Instance 9) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
/*Led 10 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0a, /* USAGE (Instance 10) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 11 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0b, /* USAGE (Instance 11) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 12 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0c, /* USAGE (Instance 12) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 13 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0d, /* USAGE (Instance 13) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 14 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0e, /* USAGE (Instance 14) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 15 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x0f, /* USAGE (Instance 15) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 16 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x10, /* USAGE (Instance 16) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 17 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x11, /* USAGE (Instance 17) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/*Led 18 */
|
||||
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
|
||||
0x09, 0x12, /* USAGE (Instance 18) */
|
||||
0xa1, 0x02, /* COLLECTION (Logical) */
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */
|
||||
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
/* Reserved 14 bits */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x0E, /* REPORT_SIZE (14) */
|
||||
0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
|
||||
#else
|
||||
/* Reserved 23 bits */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x17, /* REPORT_SIZE (23) */
|
||||
0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
|
||||
#endif
|
||||
/*Footer */
|
||||
0xc0 /* END_COLLECTION */
|
||||
};
|
||||
|
||||
/* PluggableUSBModule IMPLEMENTATION */
|
||||
|
||||
POPNHID_::POPNHID_(void) : PluggableUSBModule(1, 1, epType) {
|
||||
epType[0] = EP_TYPE_INTERRUPT_IN;
|
||||
PluggableUSB().plug(this);
|
||||
}
|
||||
|
||||
int POPNHID_::getInterface(byte* interfaceCount) {
|
||||
*interfaceCount += 1; // uses 1
|
||||
HIDDescriptor hidInterface = {
|
||||
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
|
||||
D_HIDREPORT(sizeof(_hidReportPOPN)),
|
||||
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) // this last parameter is the bInterval (requested polling rate)
|
||||
};
|
||||
return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
|
||||
}
|
||||
|
||||
int POPNHID_::getDescriptor(USBSetup& setup)
|
||||
{
|
||||
// Check if this is a HID Class Descriptor request
|
||||
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; }
|
||||
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; }
|
||||
|
||||
// In a HID Class Descriptor wIndex contains the interface number
|
||||
if (setup.wIndex != pluggedInterface) { return 0; }
|
||||
|
||||
return USB_SendControl(TRANSFER_PGM, _hidReportPOPN, sizeof(_hidReportPOPN));
|
||||
}
|
||||
|
||||
bool POPNHID_::setup(USBSetup& setup)
|
||||
{
|
||||
if (pluggedInterface != setup.wIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte request = setup.bRequest;
|
||||
byte requestType = setup.bmRequestType;
|
||||
|
||||
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) {
|
||||
if (request == HID_SET_REPORT) {
|
||||
if(setup.wValueH == HID_REPORT_TYPE_OUTPUT && setup.wLength == 5){
|
||||
lastHidUpdate = millis();
|
||||
USB_RecvControl(led_data, 5);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t POPNHID_::getShortName(char *name)
|
||||
{
|
||||
name[0] = 'P';
|
||||
name[1] = 'O';
|
||||
name[2] = 'P';
|
||||
name[3] = 'N';
|
||||
return 4;
|
||||
}
|
||||
|
||||
/* CUSTOM POPN FUNCTIONS */
|
||||
|
||||
uint8_t POPNHID_::getLightMode(){
|
||||
return lightMode;
|
||||
}
|
||||
|
||||
unsigned long POPNHID_::getLastHidUpdate(){
|
||||
return lastHidUpdate;
|
||||
}
|
||||
|
||||
void POPNHID_::setLightMode(uint8_t mode){
|
||||
if ((mode > 4) || (mode < 0)) {
|
||||
lightMode = 2;
|
||||
return;
|
||||
}
|
||||
lightMode = mode;
|
||||
}
|
||||
|
||||
void POPNHID_::updateLightMode(){
|
||||
uint32_t* bitfield = (uint32_t*)&(led_data[1]);
|
||||
if (*bitfield>>28&1){
|
||||
uint8_t mode = (*bitfield>>24) & 0x0F;
|
||||
setLightMode(mode);
|
||||
*bitfield &= ~((uint32_t)0xFF<<24);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
for(int i = 0; i < 9; i++) {
|
||||
if (leds>>i&1)
|
||||
digitalWrite(LightPins[i],HIGH);
|
||||
else
|
||||
digitalWrite(LightPins[i],LOW);
|
||||
}
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
for(int i = 9; i < 18; i++) {
|
||||
if (leds>>i&1)
|
||||
digitalWrite(LightPins[i],HIGH);
|
||||
else
|
||||
digitalWrite(LightPins[i],LOW);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
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
@@ -1,58 +1,79 @@
|
||||
#include <HID.h>
|
||||
|
||||
#define EPTYPE_DESCRIPTOR_SIZE uint8_t
|
||||
|
||||
class POPNHID_ : public PluggableUSBModule {
|
||||
|
||||
public:
|
||||
POPNHID_(void);
|
||||
|
||||
/**
|
||||
Updates the led status based on led_data (HID report received) and button states
|
||||
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)
|
||||
*/
|
||||
void updateLeds(uint32_t buttonsState, bool invert);
|
||||
|
||||
/**
|
||||
Sends the gamepad button states to the PC as an HID report
|
||||
param[in] buttonsState bitfield with currently pressed buttons
|
||||
return USB_Send() return value
|
||||
*/
|
||||
int sendState(uint32_t buttonsState);
|
||||
|
||||
/**
|
||||
Changes the lightMode if a received HID report asks for it
|
||||
*/
|
||||
void updateLightMode();
|
||||
|
||||
/**
|
||||
getter and setter for lightMode protected field.
|
||||
*/
|
||||
uint8_t getLightMode();
|
||||
void setLightMode(uint8_t mode);
|
||||
|
||||
/**
|
||||
getter for lastHidUpdate protected field.
|
||||
*/
|
||||
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;
|
||||
#include "HID.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
|
||||
#define EPTYPE_DESCRIPTOR_SIZE uint8_t
|
||||
|
||||
#elif defined(ARDUINO_ARCH_SAM)
|
||||
|
||||
#define EPTYPE_DESCRIPTOR_SIZE uint32_t
|
||||
#define USB_EP_SIZE 64
|
||||
#define TRANSFER_PGM 0x80
|
||||
#define USB_SendControl USBD_SendControl
|
||||
#define USB_RecvControl USBD_RecvControl
|
||||
#define USB_Recv USBD_Recv
|
||||
#define USB_Send USBD_Send
|
||||
#define USB_Flush USBD_Flush
|
||||
#define HID_REPORT_TYPE_OUTPUT 2
|
||||
#define HID_REPORT_TYPE_INPUT 1
|
||||
|
||||
#else
|
||||
|
||||
#error "Unsupported architecture"
|
||||
|
||||
#endif
|
||||
|
||||
class POPNHID_ : public PluggableUSBModule {
|
||||
|
||||
public:
|
||||
POPNHID_(void);
|
||||
|
||||
/**
|
||||
* Updates the led status based on led_data (HID report received) and button states
|
||||
* 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)
|
||||
*/
|
||||
void updateLeds(uint32_t buttonsState, bool invert);
|
||||
|
||||
/**
|
||||
* Sends the gamepad button states to the PC as an HID report
|
||||
* param[in] buttonsState bitfield with currently pressed buttons
|
||||
* return USB_Send() return value
|
||||
*/
|
||||
int sendState(uint32_t buttonsState);
|
||||
|
||||
/**
|
||||
* Changes the lightMode if a received HID report asks for it
|
||||
*/
|
||||
void updateLightMode();
|
||||
|
||||
/**
|
||||
* getter and setter for lightMode protected field.
|
||||
*/
|
||||
uint8_t getLightMode();
|
||||
void setLightMode(uint8_t mode);
|
||||
|
||||
/**
|
||||
* getter for lastHidUpdate protected field.
|
||||
*/
|
||||
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
@@ -1,294 +1,517 @@
|
||||
#define BOUNCE_WITH_PROMPT_DETECTION
|
||||
#include <Bounce2.h>
|
||||
#include <EEPROM.h>
|
||||
#include <FastLED.h>
|
||||
#include "POPNHID.h"
|
||||
/* 1 frame (as declared in POPNHID.cpp) on fullspeed USB spec is 1ms */
|
||||
#define REPORT_DELAY 1000
|
||||
#define MILLIDEBOUNCE 15
|
||||
POPNHID_ POPNHID;
|
||||
|
||||
/* Buttons + Lights declarations */
|
||||
#define BUT_LED_PIN A0
|
||||
#define BUT_NUM_LEDS 9
|
||||
#define LEFT_LED_PIN 8
|
||||
#define LEFT_NUM_LEDS 9
|
||||
#define RIGHT_LED_PIN 9
|
||||
#define RIGHT_NUM_LEDS 9
|
||||
CRGB but_leds[BUT_NUM_LEDS];
|
||||
CRGB left_leds[LEFT_NUM_LEDS];
|
||||
CRGB right_leds[RIGHT_NUM_LEDS];
|
||||
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 PILLAR_BRIGHTNESS 64
|
||||
#define BUTTON_BRIGHTNESS 220
|
||||
uint8_t NeonPins[] = {A1, A2, A3, A4, A5};
|
||||
uint8_t ButtonPins[] = {0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13};
|
||||
uint8_t sysPin = 12;
|
||||
//this array contains the light offset given buttons 0 to 8 as ref
|
||||
uint8_t lightOrder[] = {0, 8, 1, 7, 2, 6, 3, 5, 4};
|
||||
|
||||
const byte ButtonCount = sizeof(ButtonPins) / sizeof(ButtonPins[0]);
|
||||
const byte NeonCount = sizeof(NeonPins) / sizeof(NeonPins[0]);
|
||||
|
||||
Bounce buttons[ButtonCount];
|
||||
|
||||
/* SETUP */
|
||||
void setup() {
|
||||
// setup I/O for pins
|
||||
for (int i = 0; i < ButtonCount; i++) {
|
||||
buttons[i] = Bounce();
|
||||
buttons[i].attach(ButtonPins[i], INPUT_PULLUP);
|
||||
buttons[i].interval(MILLIDEBOUNCE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NeonCount; i++) {
|
||||
pinMode(NeonPins[i], OUTPUT);
|
||||
}
|
||||
|
||||
FastLED.addLeds<WS2812, BUT_LED_PIN, GRB>(but_leds, BUT_NUM_LEDS);
|
||||
FastLED.addLeds<WS2812, LEFT_LED_PIN, GRB>(left_leds, LEFT_NUM_LEDS);
|
||||
FastLED.addLeds<WS2812, RIGHT_LED_PIN, GRB>(right_leds, RIGHT_NUM_LEDS);
|
||||
FastLED.setBrightness( BUTTON_BRIGHTNESS );
|
||||
|
||||
uint8_t lightMode;
|
||||
EEPROM.get(0, lightMode);
|
||||
if (lightMode < 0 || lightMode > 3)
|
||||
lightMode = 2;
|
||||
POPNHID.setLightMode(lightMode);
|
||||
|
||||
//boot animation
|
||||
uint16_t anim[] = {1, 4, 16, 64, 256, 128, 32, 8, 2};
|
||||
animate(anim, 9, 100);
|
||||
animate(anim, 9, 100);
|
||||
uint16_t anim2[] = {1 + 4 + 16 + 64 + 256, 2 + 8 + 32 + 128};
|
||||
animate(anim2, 2, 500);
|
||||
animate(anim2, 2, 500);
|
||||
}
|
||||
|
||||
/* LOOP */
|
||||
unsigned long lastReport = 0;
|
||||
uint32_t prevButtonsState = 0;
|
||||
bool modeChanged = false;
|
||||
void loop() {
|
||||
/* BUTTONS */
|
||||
uint32_t buttonsState = 0;
|
||||
for (int i = 0; i < ButtonCount; i++) {
|
||||
buttons[i].update();
|
||||
int value = buttons[i].read();
|
||||
if (value != HIGH) {
|
||||
buttonsState |= (uint32_t)1 << i;
|
||||
} else {
|
||||
buttonsState &= ~((uint32_t)1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
/* USB DATA */
|
||||
if ( ( (micros() - lastReport) >= REPORT_DELAY) )
|
||||
{
|
||||
POPNHID.sendState(buttonsState);
|
||||
lastReport = micros();
|
||||
prevButtonsState = buttonsState;
|
||||
|
||||
//check for HID-requested lightmode change
|
||||
POPNHID.updateLightMode();
|
||||
}
|
||||
|
||||
/* 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);
|
||||
reactive_neon(buttonsState & 0x1ff);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
EEPROM.put(0, mode);
|
||||
}
|
||||
else if (!(buttonsState & 2)) {
|
||||
modeChanged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Light up button lights according to bitfield */
|
||||
void but_lights(uint16_t lightDesc) {
|
||||
//conversion de lightDesc
|
||||
lightDesc = convert_order(lightDesc);
|
||||
|
||||
//allumage
|
||||
for (int i = 0; i < BUT_NUM_LEDS; i++) {
|
||||
if ((lightDesc >> i) & 1) {
|
||||
but_leds[i] = but_led_colors[i];
|
||||
} else {
|
||||
but_leds[i] = 0;
|
||||
}
|
||||
}
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
/* Light up pillars and top neons according to bitfield */
|
||||
void neon_lights(uint16_t lightDesc) {
|
||||
//Top-Lamp
|
||||
for (int i = 0; i < NeonCount; 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();
|
||||
}
|
||||
|
||||
/* 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]);
|
||||
neon_lights(tab[i]);
|
||||
delay(mswait);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* BARTOP ONLY FUNCTIONS */
|
||||
uint16_t convert_order(uint16_t lightDesc) {
|
||||
uint16_t res = 0;
|
||||
for (int i = 0; i < BUT_NUM_LEDS; i++) {
|
||||
if ((lightDesc >> i) & 1) {
|
||||
res |= 1 << (lightOrder[i]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#define BOUNCE_WITH_PROMPT_DETECTION
|
||||
#include <Bounce2.h>
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
#include <Keypad.h>
|
||||
#else
|
||||
#include <Keyboard.h>
|
||||
#include <EEPROM.h>
|
||||
/* PSX DEFINE */
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <util/delay.h>
|
||||
#define SPI_PORT PORTB
|
||||
#define SPI_PINS PINB
|
||||
#define SPI_DDR DDRB
|
||||
#define SPI_PINS PINB
|
||||
#define ACK_PIN 5 //PB1
|
||||
#define ATT_PIN 0 //~SS
|
||||
#define CMD_PIN 2 //MOSI
|
||||
#define DATA_PIN 3 //MISO
|
||||
#define CLK_PIN 1 //SCK
|
||||
#define DATA_LEN 5
|
||||
/* /PSX DEFINE */
|
||||
#endif
|
||||
#include "POPNHID.h"
|
||||
/* 1 frame (as declared in POPNHID.cpp) on highspeed USB spec is 125µs */
|
||||
#define REPORT_DELAY 125
|
||||
#define MILLIDEBOUNCE 15
|
||||
POPNHID_ POPNHID;
|
||||
|
||||
/* Buttons + Lights declarations */
|
||||
#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};
|
||||
byte ButtonPins[] = {5, 6, 7, 8, 9, 10, 11, 12, 13, 4, 3, 2};
|
||||
#else
|
||||
uint8_t LightPins[] = {11,12,13,23,22,21,20,19,18};
|
||||
uint8_t ButtonPins[] = {0,1,2,3,4,5,6,7,8,9,10};
|
||||
#endif
|
||||
|
||||
const byte ButtonCount = sizeof(ButtonPins) / sizeof(ButtonPins[0]);
|
||||
const byte LightCount = sizeof(LightPins) / sizeof(LightPins[0]);
|
||||
Bounce buttons[ButtonCount];
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
/* Keypad declarations */
|
||||
const byte ROWS = 4;
|
||||
const byte COLS = 3;
|
||||
|
||||
/*
|
||||
// To use the keypad as the numpad keys (will require to send numlock for it to work)
|
||||
char numpad[ROWS][COLS] = {
|
||||
{'\347', '\350', '\351'},
|
||||
{'\344', '\345', '\346'},
|
||||
{'\341', '\342', '\343'},
|
||||
{'\352', ',', '\337'}
|
||||
};
|
||||
*/
|
||||
|
||||
/* This is to use the toprow keys instead */
|
||||
char numpad[ROWS][COLS] = {
|
||||
{'7', '8', '9'},
|
||||
{'4', '5', '6'},
|
||||
{'1', '2', '3'},
|
||||
{'0', ',', '\337'}
|
||||
};
|
||||
|
||||
/* 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
|
||||
byte colPins[COLS] = {A4, A5, A6}; //connect to the column pinouts of the keypad
|
||||
|
||||
/* For mini keypad
|
||||
byte rowPins[ROWS] = {A5, A0, A1, A3}; //connect to the row pinouts of the keypad
|
||||
byte colPins[COLS] = {A4, A6, A2}; //connect to the column pinouts of the keypad
|
||||
*/
|
||||
|
||||
Keypad kpd = Keypad( makeKeymap(numpad), rowPins, colPins, ROWS, COLS );
|
||||
#else
|
||||
/* PSX globals */
|
||||
volatile uint8_t data_buff[DATA_LEN]={0x41,0x5A,0xBF,0xFF,0xFF};//Reply.
|
||||
volatile uint8_t command_buff[DATA_LEN]={0x01,0x42,0x00,0x00,0x00};
|
||||
|
||||
volatile uint8_t curr_byte=0;
|
||||
volatile uint8_t next_byte=0;
|
||||
|
||||
byte b4;
|
||||
byte b5;
|
||||
|
||||
void convertPopn(uint32_t buttons){
|
||||
|
||||
b4 = 0xFF;
|
||||
b5 = 0xFF;
|
||||
|
||||
if (((buttons >> 9) & 1)) {
|
||||
b4 &= ~((unsigned char) 0x01);
|
||||
} else {
|
||||
b4 |= ((unsigned char) 0x01);
|
||||
}
|
||||
if (((buttons >> 10) & 1)) {
|
||||
b4 &= ~((unsigned char) 0x08);
|
||||
} else {
|
||||
b4 |= ((unsigned char) 0x08);
|
||||
}
|
||||
|
||||
if (((buttons >> 0) & 1)) {
|
||||
b5 &= ~((unsigned char) 0x10);
|
||||
} else {
|
||||
b5 |= ((unsigned char) 0x10);
|
||||
}
|
||||
if (((buttons >> 1) & 1)) {
|
||||
b5 &= ~((unsigned char) 0x20);
|
||||
} else {
|
||||
b5 |= ((unsigned char) 0x20);
|
||||
}
|
||||
if (((buttons >> 2) & 1)) {
|
||||
b5 &= ~((unsigned char) 0x08);
|
||||
} else {
|
||||
b5 |= ((unsigned char) 0x08);
|
||||
}
|
||||
if (((buttons >> 3) & 1)) {
|
||||
b5 &= ~((unsigned char) 0x40);
|
||||
} else {
|
||||
b5 |= ((unsigned char) 0x40);
|
||||
}
|
||||
if (((buttons >> 4) & 1)) {
|
||||
b5 &= ~((unsigned char) 0x04);
|
||||
} else {
|
||||
b5 |= ((unsigned char) 0x04);
|
||||
}
|
||||
if (((buttons >> 5) & 1)) {
|
||||
b5 &= ~((unsigned char) 0x80);
|
||||
} else {
|
||||
b5 |= ((unsigned char) 0x80);
|
||||
}
|
||||
if (((buttons >> 6) & 1)) {
|
||||
b5 &= ~((unsigned char) 0x02);
|
||||
} else {
|
||||
b5 |= ((unsigned char) 0x02);
|
||||
}
|
||||
if (((buttons >> 7) & 1)) {
|
||||
b4 &= ~((unsigned char) 0x10);
|
||||
} else {
|
||||
b4 |= ((unsigned char) 0x10);
|
||||
}
|
||||
if (((buttons >> 8) & 1)) {
|
||||
b5 &= ~((unsigned char) 0x01);
|
||||
} else {
|
||||
b5 |= ((unsigned char) 0x01);
|
||||
}
|
||||
// left down right always held with popn controller
|
||||
b4 &= 0x1F;
|
||||
|
||||
data_buff[2] = b4;
|
||||
data_buff[3] = b5;
|
||||
|
||||
}
|
||||
ISR(SPI_STC_vect) {
|
||||
uint8_t inbyte=SPDR;
|
||||
|
||||
if (inbyte==command_buff[curr_byte]) {
|
||||
SPDR = data_buff[curr_byte];
|
||||
curr_byte++;
|
||||
if (curr_byte<DATA_LEN) {//ACK low.
|
||||
//SPI_PORT &= ~(1<<ACK_PIN);
|
||||
PORTD &= ~(1<<5);//set HIGH
|
||||
// simulate open drain
|
||||
// SPI_DDR |= (1<<ACK_PIN);//output
|
||||
// SPI_PORT &= ~(1<<ACK_PIN);//set low (simulate open collector LOW)
|
||||
|
||||
_delay_us(5);
|
||||
//SPI_PORT |= (1<<ACK_PIN);
|
||||
PORTD |= (1<<5);//set HIGH
|
||||
|
||||
// simulate open drain
|
||||
// SPI_DDR &= ~(1<<ACK_PIN);//input
|
||||
// SPI_PORT |= (1<<ACK_PIN);//set high (simulate open collector Hi-Z)
|
||||
|
||||
} else {
|
||||
SPDR = 0xFF;
|
||||
curr_byte=0;
|
||||
}
|
||||
} else {
|
||||
SPDR = 0xFF;
|
||||
curr_byte=0;
|
||||
}
|
||||
|
||||
SPI_DDR |= (1<<ATT_PIN);//out
|
||||
SPI_PORT |= (1<<ATT_PIN);//set HIGH
|
||||
_delay_us(5);
|
||||
SPI_PORT &= ~(1<<ATT_PIN);//set HIGH
|
||||
}
|
||||
/* /PSX globals */
|
||||
#endif
|
||||
|
||||
/* SETUP */
|
||||
void setup() {
|
||||
// setup I/O for pins
|
||||
for (int i = 0; i < ButtonCount; i++) {
|
||||
buttons[i] = Bounce();
|
||||
buttons[i].attach(ButtonPins[i], INPUT_PULLUP);
|
||||
buttons[i].interval(MILLIDEBOUNCE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < LightCount; i++) {
|
||||
pinMode(LightPins[i], OUTPUT);
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
kpd.setDebounceTime(30);
|
||||
|
||||
/* activate numlock if you are not using the toprow keys */
|
||||
/* delay(2000);
|
||||
Keyboard.press(136 + 83);
|
||||
delay(500);
|
||||
Keyboard.release(136+83);
|
||||
*/
|
||||
#else
|
||||
|
||||
Keyboard.begin();
|
||||
uint8_t lightMode = 2;
|
||||
EEPROM.get(0, lightMode);
|
||||
if (lightMode < 0 || lightMode > 3)
|
||||
lightMode = 2;
|
||||
POPNHID.setLightMode(lightMode);
|
||||
|
||||
/* PSX setup */
|
||||
// use TXLED as ACK
|
||||
// DDRD |= (1<<5);//output
|
||||
PORTD |= (1<<5);//set HIGH
|
||||
|
||||
SPI_DDR |= (1<<DATA_PIN);//output
|
||||
SPI_PORT |= (1<<DATA_PIN);//set HIGH
|
||||
|
||||
SPI_DDR |= (1<<ATT_PIN);//out
|
||||
delay(500);
|
||||
SPI_PORT |= (1<<ATT_PIN);//set HIGH
|
||||
delay(5000);
|
||||
SPI_PORT |= (1<<ATT_PIN);//set HIGH
|
||||
|
||||
//SPI setup.
|
||||
// PRR &= ~(1<<PRSPI);//Set to 0 to ensure power to SPI module.
|
||||
//SPSR|=(1<<SPI2X);//Fosc/32. @16MHz==500KHz.
|
||||
SPCR|=(1<<SPR1);//Fosc/64. @16MHz==250KHz.
|
||||
SPCR|=(1<<CPHA);//Setup @ leading edge, sample @ falling edge.
|
||||
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.
|
||||
SPCR|=(1<<SPIE);//Enable Serial Transfer Complete (STC) interrupt.
|
||||
SPDR=0xFF;
|
||||
|
||||
sei();//Enable global interrupts.
|
||||
/* /PSX setup */
|
||||
#endif
|
||||
|
||||
//boot animation
|
||||
uint16_t anim[] = {1, 4, 16, 64, 256, 128, 32, 8, 2};
|
||||
animate(anim, 9, 100);
|
||||
animate(anim, 9, 100);
|
||||
uint16_t anim2[] = {1 + 4 + 16 + 64 + 256, 2 + 8 + 32 + 128};
|
||||
animate(anim2, 2, 500);
|
||||
animate(anim2, 2, 500);
|
||||
}
|
||||
|
||||
char kpdhotkeys[11] = {'1','2','3','4','5','6','7','8','9','x','0'};
|
||||
/* LOOP */
|
||||
unsigned long lastReport = 0;
|
||||
uint32_t prevButtonsState = 0;
|
||||
bool modeChanged = false;
|
||||
void loop() {
|
||||
/* BUTTONS */
|
||||
uint32_t buttonsState = 0;
|
||||
for (int i = 0; i < ButtonCount; i++) {
|
||||
buttons[i].update();
|
||||
int value = buttons[i].read();
|
||||
if (value != HIGH){
|
||||
buttonsState |= (uint32_t)1 << i;
|
||||
} else {
|
||||
buttonsState &= ~((uint32_t)1 << i);
|
||||
}
|
||||
}
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
/* PSX convert */
|
||||
convertPopn(buttonsState);
|
||||
#endif
|
||||
|
||||
/* USB DATA */
|
||||
if ( ( (micros() - lastReport) >= REPORT_DELAY) )
|
||||
{
|
||||
POPNHID.sendState(buttonsState);
|
||||
lastReport = micros();
|
||||
|
||||
//check for HID-requested lightmode change
|
||||
POPNHID.updateLightMode();
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
||||
Reference in New Issue
Block a user