Skip to content

Instantly share code, notes, and snippets.

@BenJuan26
Last active November 4, 2022 06:47
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?
An Arduino sketch for Elite Dangerous that receives data from the game and keeps its switches in sync with the game state.
#include <ArduinoJson.h>
#include <Key.h>
#include <Keypad.h>
// Allow 5 seconds of lag from the device, to the game, and back to the device.
#define BUTTON_SYNC_TIME 5000
// Each button press will last about 100ms.
#define BUTTON_HOLD_TIME 100
#define BUTTON_PRESSED LOW
#define BUTTON_RELEASED HIGH
// Masks for the flags I'm interested in.
#define FLAG_LANDING_GEAR 0x00000004
#define FLAG_HARDPOINTS 0x00000040
#define FLAG_SHIP_LIGHTS 0x00000100
#define FLAG_CARGO_SCOOP 0x00000200
#define FLAG_SILENT_RUNNING 0x00000400
#define FLAG_NIGHT_VISION 0x10000000
#define NO_FLAGS 65535
long currentFlags = NO_FLAGS;
// Setting up the button matrix.
const byte rows = 5;
const byte cols = 5;
char keys[rows][cols] = {
{'A', 'B', 'C', 'D', 'E'},
{'F', 'G', 'H', 'I', 'J'},
{'K', 'L', 'M', 'N', 'O'},
{'P', 'Q', 'R', 'S', 'T'},
{'U', 'V', 'W', 'X', 'Y'}
};
// The ASCII value of the first button in the matrix.
// This will allow any key code to be turned into a zero-based
// index for use with arrays.
const int keyOffset = keys[0][0];
byte rowPins[rows] = {14, 15, 16, 17, 18};
byte colPins[cols] = {19, 20, 21, 22, 23};
Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, rows, cols);
struct Toggleswitch {
byte currState; // State of the physical switch.
byte lastState; // Last known state of the physical switch.
byte buttonState; // Whether the joystick button is pressed.
byte joyButton; // Which joystick button the switch belongs to.
unsigned long pressedTime; // The time after which to stop pressing the button.
long flag; // The flag assigned to this switch.
// Must have a default constructor because of the array.
Toggleswitch() {}
// Constructor supports an initial state.
Toggleswitch(byte state, byte button, long theFlag) {
currState = state;
lastState = state;
buttonState = BUTTON_RELEASED;
joyButton = button;
pressedTime = 0L;
flag = theFlag;
}
// Is the game in the same state as the switch?
bool isInSync() {
// Always treat as in sync if there's no flag info.
if (currentFlags == NO_FLAGS) {
return true;
}
bool buttonPressed = lastState == BUTTON_PRESSED;
bool flagSet = currentFlags & flag;
// Need to account for the rollover of the millis() value.
// In that case this value will be huge, which is fine, as it
// will just try to sync up a little early.
unsigned long timeSinceReleased = millis() - pressedTime;
// Always assume things are in sync for a while after the button is pressed
// to give the game time to catch up.
return timeSinceReleased < BUTTON_SYNC_TIME || buttonPressed == flagSet;
}
// This will be called periodically for the switch to take care of its own updates.
void update() {
// If the switch is flipped, or the game comes out of sync, trigger a button press.
// If the button is pressed and it's time to release it, release it.
if (currState != lastState || !isInSync()) {
lastState = currState;
buttonState = BUTTON_PRESSED;
Joystick.button(joyButton, true);
pressedTime = millis();
} else if (buttonState == BUTTON_PRESSED && millis() - pressedTime >= BUTTON_HOLD_TIME) {
buttonState = BUTTON_RELEASED;
Joystick.button(joyButton, false);
}
}
};
#define NUM_SWITCHES 6
Toggleswitch switches[NUM_SWITCHES];
void setup() {
Serial.begin(9600);
Joystick.X(512);
Joystick.Y(512);
Joystick.Z(512);
Joystick.Zrotate(512);
Joystick.sliderLeft(512);
Joystick.sliderRight(512);
Joystick.hat(-1);
// Get the initial state of the switches.
// First, assume they're all switched off.
byte initialStates[NUM_SWITCHES];
for (int i = 0; i < NUM_SWITCHES; i++) {
initialStates[i] = BUTTON_RELEASED;
}
// Check the state of each switch and update the initial states accordingly.
if (kpd.getKeys()) {
for (int i = 0; i < LIST_MAX; i++) {
int keyIndex = (int)kpd.key[i].kchar - keyOffset;
KeyState state = kpd.key[i].kstate;
if (state == PRESSED || state == HOLD) {
initialStates[keyIndex] = BUTTON_PRESSED;
}
}
}
// Create the Toggleswitch objects with the given initial states.
int flags[NUM_SWITCHES] = {FLAG_HARDPOINTS, FLAG_LANDING_GEAR, FLAG_CARGO_SCOOP,
FLAG_SHIP_LIGHTS, FLAG_NIGHT_VISION, FLAG_SILENT_RUNNING};
for (int i = 0; i < NUM_SWITCHES; i++) {
switches[i] = Toggleswitch(initialStates[i], i, flags[i]);
}
}
// Handle all Serial I/O.
void serialRx() {
if (!Serial.available()) {
return;
}
DynamicJsonBuffer jsonBuffer(512);
JsonObject &root = jsonBuffer.parseObject(Serial);
if (!root.success()) {
return;
}
long flags = root["Flags"];
currentFlags = flags;
}
// Get the states of the physical switches and write them to the Toggleswitch objects.
void updateKeys() {
if (!kpd.getKeys()) {
return;
}
for (int i = 0; i < LIST_MAX; i++) {
if (kpd.key[i].kchar == NO_KEY) {
continue;
}
// Turn the key code (e.g. 'F') into a zero-based array index.
int keyIndex = (int)kpd.key[i].kchar - keyOffset;
KeyState state = kpd.key[i].kstate;
if (state == PRESSED) {
switches[keyIndex].currState = BUTTON_PRESSED;
} else if (state == RELEASED) {
switches[keyIndex].currState = BUTTON_RELEASED;
}
}
}
// Main loop.
void loop() {
serialRx();
updateKeys();
for (int i = 0; i < NUM_SWITCHES; i++) {
switches[i].update();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment