Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON Version #6

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions code/JCPM-json/JCPM-json.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#include <HID-Project.h> // For keyboard and mouse HID functionality
#include <EEPROM.h> // For storing configurations
#include <ArduinoJson.h> // For parsing JSON configurations
#include <Adafruit_NeoPixel.h> // For NeoPixel LED control
#include <Encoder.h>

// Structure to represent a button's configuration
struct ButtonConfig {
char action[6];
char keys[10];
char modifiers[5];
int x, y;
int color[3];
int colorOnPress[3];
};

// Structure to represent an encoder configuration
struct EncoderConfig {
char action[6]; // Action type: "key", etc.
char keys[10]; // Keys to be pressed (like "UpArrow" or "DownArrow")
char modifiers[5]; // Modifiers (e.g., "Ctrl", "Shift")
};

EncoderConfig encoderConfigs[2]; // [0] for increment, [1] for decrement
ButtonConfig buttonConfigs[9]; // 9 configurable buttons

// General device settings
struct GeneralConfig {
int defaultColor[3]; // Default color for all buttons if none specified
};

GeneralConfig generalConfig; // Single global profile

void setup() {
pinMode(4, INPUT_PULLUP); //SW1 pushbutton (encoder button)
pinMode(15, INPUT_PULLUP); //SW2 pushbutton
pinMode(A0, INPUT_PULLUP); //SW3 pushbutton
pinMode(A1, INPUT_PULLUP); //SW4 pushbutton
pinMode(A2, INPUT_PULLUP); //SW5 pushbutton
pinMode(A3, INPUT_PULLUP); //SW6 pushbutton
pinMode(14, INPUT_PULLUP); //SW7 pushbutton
pinMode(16, INPUT_PULLUP); //SW8 pushbutton
pinMode(10, INPUT_PULLUP); //SW9 pushbutton
pinMode(8, INPUT_PULLUP); //SW10 pushbutton - acts as mode switch

// Initialize serial, HID, and NeoPixel
Serial.begin(9600);
while (!Serial) { /* wait for serial to initialize */ }

Serial.print("{\"status\":\"Free memory: ");
Serial.print(freeMemory());
Serial.println("\"}");

Keyboard.begin();
Mouse.begin();
setupNeoPixels();

// Load initial configuration from EEPROM. multiple profile support removed
loadConfigFromEEPROM();

Serial.println("{\"status\":\"System boot OK\"}");
}

void loop() {
// Handle serial input
handleSerialInput();

// Multiple profile support, mode button switch profile
// Ran out of RAM on a Leonardo, all multiple profile code removed

// Check button presses for actions
checkEncoderActions();

// Check button presses for actions
checkButtonActions();
}

extern unsigned int __heap_start, *__brkval;
extern unsigned int __bss_end;
extern unsigned int __data_end;

int freeMemory() {
int free_memory;

if ((int)__brkval == 0) {
free_memory = ((int)&free_memory) - ((int)&__bss_end);
} else {
free_memory = ((int)&free_memory) - ((int)__brkval);
}

return free_memory;
}
102 changes: 102 additions & 0 deletions code/JCPM-json/buttons.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Define button states (inverted logic for LOW = pressed)
bool SW1 = HIGH, SW2 = HIGH, SW3 = HIGH, SW4 = HIGH, SW5 = HIGH, SW6 = HIGH, SW7 = HIGH, SW8 = HIGH, SW9 = HIGH;
bool buttonHeld[9] = {false, false, false, false, false, false, false, false, false}; // Track button hold states

// Encoder variables
int16_t encoderPosition = 0;
Encoder myEnc(1, 0); // Pins 1 and 0 for the encoder

int buttonPins[9] = {4, 15, A0, A1, A2, A3, 14, 16, 10}; // Button pin assignments

void checkButtonActions() {
for (int i = 0; i < 9; i++) {
int currentState = digitalRead(buttonPins[i]);

// When button is pressed
if (currentState == LOW && !buttonHeld[i]) {
buttonHeld[i] = true;
Serial.print("{\"button\":");
Serial.print(i + 1);
Serial.println(", \"event\":\"pressed\"}");
setPixelColor(i - 1, buttonConfigs[i].colorOnPress[0], buttonConfigs[i].colorOnPress[1], buttonConfigs[i].colorOnPress[2]);
executeButtonAction(i); // Execute the button action
}

// When button is released
if (currentState == HIGH && buttonHeld[i]) {
buttonHeld[i] = false;
Serial.print("{\"button\":");
Serial.print(i + 1);
Serial.println(", \"event\":\"released\"}");
setPixelColor(i - 1, buttonConfigs[i].color[0], buttonConfigs[i].color[1], buttonConfigs[i].color[2]);
}
}
}

void checkEncoderActions() {
int16_t newPosition = myEnc.read();
if (newPosition > encoderPosition) {
Serial.println("{\"encoder\":\"increment\"}"); // JSON output for increment action
executeEncoderAction(encoderConfigs[0]);
encoderPosition = newPosition;
}
if (newPosition < encoderPosition) {
Serial.println("{\"encoder\":\"decrement\"}"); // JSON output for decrement action
executeEncoderAction(encoderConfigs[1]);
encoderPosition = newPosition;
}
delay(50);
}

void executeHIDAction(const char* action, const char* keys, const char* modifiers, int x = 0, int y = 0, bool hasMouseAction = false) {
// Check if the action type is "constant" and map string to HID keycode
if (strcmp(action, "const") == 0) {
if (strcmp(keys, "UpArrow") == 0) {
Keyboard.press(HID_KEYBOARD_UPARROW);
} else if (strcmp(keys, "DownArrow") == 0) {
Keyboard.press(HID_KEYBOARD_DOWNARROW);
} else if (strcmp(keys, "LeftArrow") == 0) {
Keyboard.press(HID_KEYBOARD_LEFTARROW);
} else if (strcmp(keys, "RightArrow") == 0) {
Keyboard.press(HID_KEYBOARD_RIGHTARROW);
} else if (strcmp(keys, "Enter") == 0) {
Keyboard.press(KEY_ENTER);
} else {
Serial.print("{\"error\":\"Invalid constant: ");
Serial.print(keys);
Serial.println("\"}");
return;
}
delay(100); // Short delay for key press
Keyboard.releaseAll();
}
// Handle "key" action to press any key provided in the config
else if (strcmp(action, "key") == 0) {
// Handle modifiers if any
if (strstr(modifiers, "Ctrl")) Keyboard.press(KEY_LEFT_CTRL);
if (strstr(modifiers, "Shift")) Keyboard.press(KEY_LEFT_SHIFT);
if (strstr(modifiers, "Alt")) Keyboard.press(KEY_LEFT_ALT);

// Directly press the specified key as a character
// Loop over each character in keys and press it
for (int i = 0; i < strlen(keys); i++) {
Keyboard.press(keys[i]); // Press each character in the string
delay(50); // Optional small delay between each key press
}
delay(100); // Short delay for key press
Keyboard.releaseAll();
}
// Handle mouse actions if needed
else if (hasMouseAction && strcmp(action, "mouse") == 0) {
Mouse.move(x, y);
}
}

void executeButtonAction(int buttonIndex) {
ButtonConfig config = buttonConfigs[buttonIndex];
executeHIDAction(config.action, config.keys, config.modifiers, config.x, config.y, true);
}

void executeEncoderAction(const EncoderConfig &config) {
executeHIDAction(config.action, config.keys, config.modifiers);
}
23 changes: 23 additions & 0 deletions code/JCPM-json/neopixels.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <Adafruit_NeoPixel.h>

#define PIN 5 // NeoPixel data pin
#define NUMPIXELS 11 // Total number of NeoPixel LEDs
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setupNeoPixels() {
pixels.begin();
resetAllPixels(); // Set all pixels to off initially
}

void setPixelColor(int pixel, int r, int g, int b) {
if (pixel >= 0 && pixel < pixels.numPixels()) {
pixels.setPixelColor(pixel, pixels.Color(r, g, b));
pixels.show(); // Update to show the change
}
}

void resetAllPixels() {
for (int i = 0; i < NUMPIXELS; i++) {
setPixelColor(i, 0, 0, 0); // Turn off all LEDs
}
}
86 changes: 86 additions & 0 deletions code/JCPM-json/profiles.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include <EEPROM.h>
#include <ArduinoJson.h>

// Load configuration from EEPROM
void loadConfigFromEEPROM() {
int configOffset = 0;

// Load general configuration
EEPROM.get(configOffset, generalConfig);
configOffset += sizeof(GeneralConfig);

// Load button configurations
EEPROM.get(configOffset, buttonConfigs);
configOffset += sizeof(buttonConfigs);

// Load encoder configurations
EEPROM.get(configOffset, encoderConfigs);

// Output loaded configuration for debugging
Serial.println("{\"status\":\"Loaded configuration from EEPROM.\"}");

Serial.print("{\"buttons\":[");
for (int i = 0; i < 9; i++) {
Serial.print("{\"button\":");
Serial.print(i);
Serial.print(",\"action\":\"");
Serial.print(buttonConfigs[i].action);
Serial.print("\"}");

// Set the button's color if it has a color configured
setPixelColor(i - 1, buttonConfigs[i].color[0], buttonConfigs[i].color[1], buttonConfigs[i].color[2]);


if (i < 8) {
Serial.print(","); // Add a comma after each button except the last one
}
}

Serial.println("]}"); // Close the JSON array and object

// Print encoder configurations
Serial.print("{\"encoder\":{\"increment\":{");
Serial.print("\"action\":\"");
Serial.print(encoderConfigs[0].action);
Serial.print("\",\"keys\":\"");
Serial.print(encoderConfigs[0].keys);
Serial.print("\",\"modifiers\":\"");
Serial.print(encoderConfigs[0].modifiers);
Serial.print("\"},\"decrement\":{");

Serial.print("\"action\":\"");
Serial.print(encoderConfigs[1].action);
Serial.print("\",\"keys\":\"");
Serial.print(encoderConfigs[1].keys);
Serial.print("\",\"modifiers\":\"");
Serial.print(encoderConfigs[1].modifiers);
Serial.println("\"}}}");
}

// Save configuration to EEPROM
void saveConfigToEEPROM() {
int configOffset = 0;

// Save general configuration
EEPROM.put(configOffset, generalConfig);
configOffset += sizeof(GeneralConfig);

// Save button configurations
EEPROM.put(configOffset, buttonConfigs);
configOffset += sizeof(buttonConfigs);

// Save encoder configurations
EEPROM.put(configOffset, encoderConfigs);

Serial.println("{\"response\":\"Configuration saved to EEPROM.\"}");
}

void resetEEPROM() {
int eepromSize = EEPROM.length();

for (int i = 0; i < eepromSize; i++) {
EEPROM.write(i, 0); // Write zeros to the entire EEPROM
}

Serial.println("{\"response\":\"EEPROM reset completed\"}");
}
Loading