584 lines
12 KiB
C++
584 lines
12 KiB
C++
/*
|
|
Macro Board
|
|
Made by Bozarre
|
|
*/
|
|
|
|
/*
|
|
* saving presets in eeprom
|
|
*/
|
|
#include <EEPROM.h>
|
|
|
|
struct MemSlot
|
|
{
|
|
String displayName;
|
|
int address;
|
|
byte value;
|
|
};
|
|
|
|
MemSlot memory[] = //128 bytes for the Teensy LC
|
|
{
|
|
{"prst", 1, 0}, //preset
|
|
{"bt1", 2, 0}, //button mode (0 = default, 1 = toggle)
|
|
{"bt2", 3, 0},
|
|
{"bt3", 4, 0},
|
|
{"bt4", 5, 0},
|
|
{"bt5", 6, 0},
|
|
{"bt6", 7, 0},
|
|
{"bt7", 8, 0},
|
|
{"bt8", 9, 0},
|
|
{"bt9", 10, 0},
|
|
{"bt10", 11, 0},
|
|
{"bt11", 12, 0},
|
|
{"bt12", 13, 0},
|
|
{"effc", 14, 1},
|
|
{"reset", 0, 0}, // != 0 -> reset eeprom
|
|
};
|
|
|
|
|
|
/*
|
|
* Menu
|
|
*/
|
|
bool isInMenu = false;
|
|
|
|
struct MenuItem
|
|
{
|
|
String path;
|
|
bool isValue;
|
|
String displayName;
|
|
};
|
|
|
|
MenuItem menuEntries[] =
|
|
{
|
|
{"", true, "prst"},
|
|
{"", false, "butt"},
|
|
{"", true, "effc"},
|
|
{"", true, "reset"},
|
|
{"/butt", true, "bt1"},
|
|
{"/butt", true, "bt2"},
|
|
{"/butt", true, "bt3"},
|
|
{"/butt", true, "bt4"},
|
|
{"/butt", true, "bt5"},
|
|
{"/butt", true, "bt6"},
|
|
{"/butt", true, "bt7"},
|
|
{"/butt", true, "bt8"},
|
|
{"/butt", true, "bt9"},
|
|
{"/butt", true, "bt10"},
|
|
{"/butt", true, "bt11"},
|
|
{"/butt", true, "bt12"},
|
|
};
|
|
|
|
String menuCurrentPath = "";
|
|
unsigned int menuSelectionIndex = 0;
|
|
unsigned int menuCurrentPathItems = 0;
|
|
MenuItem menuCurrentItem;
|
|
bool menuIsSettingValue = false;
|
|
|
|
/*
|
|
* 7 Segment 4 digits display
|
|
*/
|
|
#include "SevSeg.h"
|
|
|
|
SevSeg sevseg;
|
|
|
|
/*
|
|
* key button matrix
|
|
*/
|
|
#include <Keypad.h>
|
|
|
|
const byte rows = 4; //four rows
|
|
const byte cols = 3; //three columns
|
|
//Hardware inputs ordered from the bottom left = 1 to the top right = 12 on the switch matrix,
|
|
char keys[rows][cols] = { // assiging arbitraty uid to each button
|
|
{9,5,1},
|
|
{10,6,2},
|
|
{11,7,3},
|
|
{12,8,4}
|
|
};
|
|
byte rowPins[rows] = {1, 2, 3, 0}; //connect to the row pinouts of the keypad
|
|
byte colPins[cols] = {21, 22, 23}; //connect to the column pinouts of the keypad
|
|
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, rows, cols );
|
|
|
|
bool buttonsVirtualState[] =
|
|
{
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
};
|
|
|
|
/*
|
|
* Rotary Encoder
|
|
*/
|
|
#include <SimpleRotary.h>
|
|
|
|
SimpleRotary rotaryEncoder(19, 20, 4);
|
|
byte rotaryEncoderState = 0;
|
|
|
|
// Rotary push button
|
|
const int BUTTON_Rotary = 4;
|
|
|
|
/*
|
|
* mode presets and action
|
|
*/
|
|
#include "modeAction.h"
|
|
|
|
/*
|
|
* custom display animations
|
|
*/
|
|
#include "animations.h"
|
|
|
|
int animationFrame = 0;
|
|
bool animationPlaying = false;
|
|
unsigned int animationLastFrameMillis = 0;
|
|
unsigned int animationFrameDelayMillis = 64;
|
|
bool animationLooping = false;
|
|
bool animationEnabled = true;
|
|
|
|
/*
|
|
* time cooldown managment
|
|
*/
|
|
unsigned int lastInputMillis = 0;
|
|
unsigned int inputCoolDown = 10;
|
|
unsigned int sevSegCoolDown = 5000;
|
|
|
|
/*
|
|
* INIT
|
|
*/
|
|
void setup()
|
|
{
|
|
//debug
|
|
Serial.begin(9600);
|
|
Serial.println("setup start");
|
|
|
|
//memory
|
|
initEepromMemory();
|
|
|
|
applyMemoryChange();
|
|
|
|
//display
|
|
initSevSegDisplay();
|
|
|
|
//Init rotary encoders
|
|
rotaryEncoder.setDebounceDelay(2);
|
|
rotaryEncoder.setErrorDelay(100);
|
|
|
|
pinMode(BUTTON_Rotary, INPUT_PULLUP);
|
|
|
|
// Init keyboard output
|
|
Keyboard.begin();
|
|
|
|
//init cooldowns
|
|
lastInputMillis = millis();
|
|
|
|
Serial.println("setup complete");
|
|
|
|
playAnimation(0);
|
|
|
|
}
|
|
|
|
void initEepromMemory()
|
|
{
|
|
//write eeprom first init
|
|
byte memValue = EEPROM.read(0);
|
|
if (memValue > 0) // cannot find settings, writes default ones
|
|
{
|
|
for (byte i = 0; i < (sizeof(memory)/sizeof(MemSlot)); i++)
|
|
{
|
|
EEPROM.write(memory[i].address, memory[i].value);
|
|
}
|
|
EEPROM.write(getMemorySlot("reset")->address,0);
|
|
}
|
|
//read eeprom
|
|
for (byte i = 0; i < (sizeof(memory)/sizeof(MemSlot)); i++)
|
|
{
|
|
memory[i].value = EEPROM.read(memory[i].address);
|
|
}
|
|
}
|
|
|
|
void initSevSegDisplay()
|
|
{
|
|
//Init display
|
|
byte numDigits = 4;
|
|
byte digitPins[] = {7, 8, 9, 6};
|
|
byte segmentPins[] = {10, 12, 14, 16, 17, 11, 13, 15};
|
|
|
|
bool resistorsOnSegments = true;
|
|
//bool updateWithDelaysIn = true;
|
|
byte hardwareConfig = COMMON_CATHODE;
|
|
sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments);
|
|
sevseg.setBrightness(90);
|
|
}
|
|
|
|
/*
|
|
* Tick Loop
|
|
*/
|
|
void loop()
|
|
{
|
|
//animation draw tick
|
|
if (animationEnabled && animationPlaying && millis() > (animationLastFrameMillis + animationFrameDelayMillis))
|
|
{
|
|
drawAnimation(animationLooping);
|
|
animationLastFrameMillis = millis();
|
|
}
|
|
|
|
//INPUTS
|
|
if (millis() > lastInputMillis + inputCoolDown)
|
|
{
|
|
char customKey = keypad.getKey();
|
|
|
|
if (customKey)
|
|
{
|
|
sendInputOuput(customKey);
|
|
}
|
|
|
|
if (rotaryEncoder.push() == 1) // = pressed
|
|
{
|
|
sendInputOuput(15);
|
|
}
|
|
|
|
rotaryEncoderState = rotaryEncoder.rotate();
|
|
|
|
if (rotaryEncoderState == 1 || rotaryEncoderState == 2)
|
|
{
|
|
if ( rotaryEncoderState == 1 ) //Turned Clockwise
|
|
{
|
|
sendInputOuput(14);
|
|
}
|
|
|
|
if ( rotaryEncoderState == 2 ) //Turned Counter-Clockwise
|
|
{
|
|
sendInputOuput(13);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*//clear screen after no input delay
|
|
if (millis() > lastInputMillis + sevSegCoolDown)
|
|
{
|
|
//sevseg.blank();
|
|
}*/
|
|
|
|
if(!animationPlaying && !isInMenu)
|
|
{
|
|
drawCustomSegments();
|
|
}
|
|
|
|
sevseg.refreshDisplay();
|
|
|
|
}
|
|
|
|
void printMenu()
|
|
{
|
|
Serial.println("printMenu");
|
|
Serial.print(menuCurrentPath + "/ ");
|
|
if (isInMenu)
|
|
{
|
|
if(menuIsSettingValue)
|
|
{
|
|
sevseg.setNumber(menuGetValue(menuCurrentItem.displayName));
|
|
Serial.println(menuGetValue(menuCurrentItem.displayName));
|
|
}
|
|
else
|
|
{
|
|
updateMenu();
|
|
sevseg.setChars(menuCurrentItem.displayName.c_str());
|
|
Serial.println(menuCurrentItem.displayName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void updateMenu()
|
|
{
|
|
unsigned int correspondingPathItemIndex = 0;
|
|
for (unsigned int i = 0; i < (sizeof(menuEntries)/sizeof(MenuItem)); i++)
|
|
{
|
|
if (menuEntries[i].path == menuCurrentPath)
|
|
{
|
|
if(correspondingPathItemIndex == menuSelectionIndex)
|
|
{
|
|
menuCurrentItem = menuEntries[i];
|
|
}
|
|
correspondingPathItemIndex++;
|
|
}
|
|
}
|
|
menuCurrentPathItems = correspondingPathItemIndex;
|
|
//Serial.println(menuCurrentPathItems);
|
|
}
|
|
|
|
void menuBack()
|
|
{
|
|
if (isInMenu)
|
|
{
|
|
if (menuCurrentPath == "/" || menuCurrentPath == "" )
|
|
{
|
|
Serial.println("menuExit");
|
|
isInMenu = false;
|
|
menuSelectionIndex = 0;
|
|
menuCurrentPath = "";
|
|
sevseg.blank();
|
|
playAnimation(3);
|
|
}
|
|
else
|
|
{
|
|
Serial.println("menuBack");
|
|
menuSelectionIndex = menuGetSelectionIndexFromPath(menuCurrentPath);
|
|
menuCurrentPath = menuCurrentPath.substring(0,menuCurrentPath.lastIndexOf('/'));
|
|
menuIsSettingValue = false;
|
|
printMenu();
|
|
}
|
|
}
|
|
}
|
|
|
|
void menuEnter()
|
|
{
|
|
Serial.println("menuEnter");
|
|
if (isInMenu)
|
|
{
|
|
menuCurrentPath = menuCurrentItem.path + '/';
|
|
menuCurrentPath+= menuCurrentItem.displayName;
|
|
menuSelectionIndex = 0;
|
|
if (menuCurrentItem.isValue)
|
|
{
|
|
if (menuIsSettingValue)
|
|
{
|
|
for (unsigned int i = 0; i < (sizeof(memory)/sizeof(MemSlot)); i++)
|
|
{
|
|
if (memory[i].displayName == menuCurrentItem.displayName)
|
|
{
|
|
EEPROM.write(memory[i].address, memory[i].value);
|
|
Serial.println("value saved in eeprom");
|
|
menuBack();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
menuIsSettingValue = true;
|
|
Serial.println("menuIsSettingValue");
|
|
}
|
|
}
|
|
printMenu();
|
|
}
|
|
}
|
|
|
|
void menuPrev()
|
|
{
|
|
if (isInMenu)
|
|
{
|
|
if (menuIsSettingValue)
|
|
{
|
|
menuSetValue(menuCurrentItem.displayName, menuGetValue(menuCurrentItem.displayName) - 1);
|
|
}
|
|
else if (menuSelectionIndex > 0)
|
|
{
|
|
menuSelectionIndex = menuSelectionIndex - 1;
|
|
}
|
|
printMenu();
|
|
}
|
|
}
|
|
|
|
void menuNext()
|
|
{
|
|
if (isInMenu)
|
|
{
|
|
if (menuIsSettingValue)
|
|
{
|
|
menuSetValue(menuCurrentItem.displayName, menuGetValue(menuCurrentItem.displayName) + 1);
|
|
}
|
|
else if (menuSelectionIndex < menuCurrentPathItems-1)
|
|
{
|
|
menuSelectionIndex = menuSelectionIndex + 1;
|
|
}
|
|
printMenu();
|
|
}
|
|
}
|
|
|
|
byte menuGetValue(String variableName)
|
|
{
|
|
MemSlot* memorySlot = getMemorySlot(variableName);
|
|
if (memorySlot != NULL)
|
|
{
|
|
return memorySlot->value;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool menuSetValue(String variableName, byte newValue)
|
|
{
|
|
MemSlot* memorySlot = getMemorySlot(variableName);
|
|
if (memorySlot != NULL)
|
|
{
|
|
memorySlot->value = newValue;
|
|
applyMemoryChange();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void applyMemoryChange()
|
|
{
|
|
animationEnabled = (getMemorySlot("effc")->value > 0);
|
|
}
|
|
|
|
int menuGetSelectionIndexFromPath(String path)
|
|
{
|
|
int correspondingPathItemIndex = 0;
|
|
String makePath = "";
|
|
String parentPath = path.substring(0,path.lastIndexOf('/'));
|
|
for (unsigned int i = 0; i < (sizeof(menuEntries)/sizeof(MenuItem)); i++)
|
|
{
|
|
if (menuEntries[i].path == parentPath)
|
|
{
|
|
makePath = menuEntries[i].path + '/';
|
|
makePath += menuEntries[i].displayName;
|
|
if(makePath == path)
|
|
{
|
|
return correspondingPathItemIndex;
|
|
}
|
|
correspondingPathItemIndex++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
MemSlot* getMemorySlot(String memSlotName)
|
|
{
|
|
for (unsigned int i = 0; i < (sizeof(memory)/sizeof(MemSlot)); i++)
|
|
{
|
|
if (memory[i].displayName == memSlotName)
|
|
{
|
|
return &memory[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void sendInputOuput(int input)
|
|
{
|
|
String inputName = "bt";
|
|
inputName += input;
|
|
MemSlot* buttonInputMem = getMemorySlot(inputName);
|
|
Serial.println(inputName);
|
|
|
|
if (isInMenu)
|
|
{
|
|
animationPlaying = false;
|
|
}
|
|
else
|
|
{
|
|
playAnimation(1);
|
|
|
|
if (buttonInputMem != NULL)
|
|
{
|
|
if (buttonInputMem->value == 1)
|
|
{
|
|
buttonsVirtualState[input-1] = !buttonsVirtualState[input-1];
|
|
}
|
|
if (buttonInputMem->value == 0)
|
|
{
|
|
buttonsVirtualState[input-1] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
lastInputMillis = millis();
|
|
|
|
int numberOfModes = sizeof(modes)/(sizeof(Action)*15);
|
|
|
|
int currentMode = (getMemorySlot("prst")->value) % numberOfModes;
|
|
//Hardware inputs ordered from the bottom left = 1 to the top right = 12 on the switch matrix,
|
|
Action actionToRun = modes[currentMode][input-1]; //getting the virtual inputs from the modes array.
|
|
//Inputs uids starts with 1, so input - 1 to get the index 0 of the array
|
|
|
|
if (actionToRun.keyID == -1) //menu input, input mapping can be changed in modeAction.h
|
|
{
|
|
if (isInMenu)
|
|
{
|
|
menuBack();
|
|
//playAnimation(3);
|
|
}
|
|
else
|
|
{
|
|
isInMenu = true;
|
|
sevseg.blank(); // clear display
|
|
playAnimation(2);
|
|
printMenu();
|
|
}
|
|
}
|
|
else if (isInMenu && input == 13) //menu prev, hardcoded input
|
|
{
|
|
menuPrev();
|
|
}
|
|
else if (isInMenu && input == 14) //menu next, hardcoded input
|
|
{
|
|
menuNext();
|
|
}
|
|
else if (isInMenu && input == 15) //menu enter, hardcoded input
|
|
{
|
|
menuEnter();
|
|
}
|
|
else
|
|
{
|
|
clickKey(actionToRun.keyID, actionToRun.keyModifierID);
|
|
}
|
|
|
|
}
|
|
|
|
void clickKey(int key, int modifier)
|
|
{
|
|
Keyboard.set_modifier(modifier);
|
|
Keyboard.press(key);
|
|
Keyboard.release(key);
|
|
Keyboard.set_modifier(0);
|
|
//Keyboard.send_now(); //can't remember what's that's for
|
|
}
|
|
|
|
void playAnimation(int animID)
|
|
{
|
|
currentAnimationID = animID;
|
|
animationFrame = 0;
|
|
animationPlaying = true;
|
|
}
|
|
|
|
void drawAnimation(bool looping)
|
|
{
|
|
int currentAnimationFrameTotal = animData[currentAnimationID].animationFrameCount;
|
|
sevseg.setSegments(animData[currentAnimationID].animationPtr + (animationFrame * (sizeof(const uint8_t)*4)));
|
|
animationFrame++;
|
|
if (animationFrame == currentAnimationFrameTotal)
|
|
{
|
|
animationFrame = 0;
|
|
if (!looping)
|
|
{
|
|
animationPlaying = false;
|
|
sevseg.blank();
|
|
if (isInMenu) //not great
|
|
{
|
|
printMenu();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void drawCustomSegments()
|
|
{
|
|
uint8_t seg0 = (0b00001000 * buttonsVirtualState[0]) + (0b01000000 * buttonsVirtualState[4]) + (0b00000001 * buttonsVirtualState[8]);
|
|
uint8_t seg1 = (0b00001000 * buttonsVirtualState[1]) + (0b01000000 * buttonsVirtualState[5]) + (0b00000001 * buttonsVirtualState[9]);
|
|
uint8_t seg2 = (0b00001000 * buttonsVirtualState[2]) + (0b01000000 * buttonsVirtualState[6]) + (0b00000001 * buttonsVirtualState[10]);
|
|
uint8_t seg3 = (0b00001000 * buttonsVirtualState[3]) + (0b01000000 * buttonsVirtualState[7]) + (0b00000001 * buttonsVirtualState[11]);
|
|
|
|
sevseg.setSegmentsDigit(0,seg0);
|
|
sevseg.setSegmentsDigit(1,seg1);
|
|
sevseg.setSegmentsDigit(2,seg2);
|
|
sevseg.setSegmentsDigit(3,seg3);
|
|
}
|