Register / Login | Desktop view |
I am quite new to Arduino, but I have started an ambitious (at least for me) project. And now I found your fantastic library that could potentially solve most of the problems I would certainly have.
Short description of what I am planning: Button box for PC gaming, using a “pro micro” with a library that enables the pro micro to be recognized as HID to emulate key presses from the keyboard. This button box has 14 push buttons, 18 toggle buttons and 7 rotary encoders with push. I got 4 x the 23017 IO expander.
#ifndef MAX_KEYS
#define MAX_KEYS 5
#endif // MAX_KEYS defined
#ifndef MAX_ROTARY_ENCODERS
#define MAX_ROTARY_ENCODERS 4
#endif // MAX_ROTARY_ENCODERS
My question is: Do you think the IoAbstraction library is capable of doing what I am describing? Are there limitations and do I have to change basic values (I think I already understood that I would have to change the limitation of maximum usable buttons)?
My goal is to have complete software based debounce – included in the library if I am not wrong. Also, I want button presses to be handled by interrupts. It seems to me that this is the better way of coding and in addition, I want to update OLEDs from time to time (I got an TCA9548A as I am using multiple displays), which makes polling a bad idea.
I would be very happy if you could answer my questions and tell me potential caveats of my idea. Thanks in advance!
void onButtonDown(uint8_t pin, bool heldDown) {
if(heldDown) return;
do other stuff
}
#include <IoAbstraction.h>
#include <IoAbstractionWire.h>
#include <Wire.h>
#include <Keyboard.h>
#include <Arduino.h>
#include <U8g2lib.h>
/////////////////
// DEFINITIONS //
/////////////////
// start value and maximum encoder value before reset
uint16_t EncValList[] = {32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000};
const uint16_t MaxEncValList[] = {65000, 65000, 65000, 65000, 65000, 65000, 65000, 65000};
// Hardcoded hardware addresses
const byte TcaAddress = 0x70; // I2C address of the TCA I2C MUX
const byte ResPinsForArduino = 100; // input value for the IoAbstraction library, reserved pins for Arduino
const byte ArduinoInterruptPin = 7; //physical pin on the arduino to record interrupts
// profile, menu and displays
const byte NoOfButtons = 64;
// instantiate IoAbstraction
MultiIoAbstractionRef MultiIo = multiIoExpander(ResPinsForArduino);
///////////////////////////////
// STANDARD BUTTON CALLBACKS //
///////////////////////////////
void OnPushBtnPress(byte PinNum, bool HeldDown) {
// ignore when buttons are held
if (HeldDown == false) {
ButtonHandler(PinNum, HeldDown);
}
}
void OnToggleBtnPress(byte PinNum, bool HeldDown) {
// ignore when buttons are held
if (HeldDown == false) {
ButtonHandler(PinNum, HeldDown);
}
}
void OnToggleBtnRelease(byte PinNum) {
// Could be the same as OnToggleBtnPress, but separated in case of future changes
ButtonHandler(PinNum, 0);
}
void onEncChange0(uint16_t EncVal) {
Serial.println(F("IN: onEncChange0"));
EncoderToButton(0, EncVal, 36, 44);
}
void onEncChange1(uint16_t EncVal) {
EncoderToButton(1, EncVal, 37, 45);
}
void onEncChange2(uint16_t EncVal) {
EncoderToButton(2, EncVal, 39, 47);
}
void onEncChange3(uint16_t EncVal) {
EncoderToButton(3, EncVal, 38, 46);
}
void onEncChange4(uint16_t EncVal) {
EncoderToButton(4, EncVal, 35, 43);
}
void onEncChange5(uint16_t EncVal) {
EncoderToButton(5, EncVal, 34, 42);
}
void onEncChange6(uint16_t EncVal) {
EncoderToButton(6, EncVal, 33, 41);
}
void onEncChange7(uint16_t EncVal) {
EncoderToButton(7, EncVal, 32, 40);
}
/////////////////////////////
// ROTARY ENCODER WRAPPER //
/////////////////////////////
void EncoderToButton(byte EncAddr, uint16_t EncVal, byte BtnBack, byte BtnFwd) {
Serial.print(F("Encoder Address: "));
Serial.println(EncAddr);
Serial.print(F("Encoder Value: "));
Serial.println(EncVal);
int EncDir = GetEncDirection(EncAddr, EncVal);
if (EncDir == 1) {
ButtonHandler(ResPinsForArduino + BtnBack, false);
}
else if (EncDir == -1) {
ButtonHandler(ResPinsForArduino + BtnFwd, false);
}
}
int GetEncDirection(byte EncAddr, uint16_t NewEncVal) {
//Serial.println(" ");
//Serial.println("-------------------------");
//Serial.println("INSIDE getEncDirection");
int EncDir;
uint16_t OldEndVal = EncValList[EncAddr];
if (NewEncVal > OldEndVal) {
EncDir = 1;
}
else if (NewEncVal < OldEndVal) {
EncDir = -1;
}
else if (NewEncVal == OldEndVal) {
EncDir = 0;
Serial.println(F("ERROR: New EncoderVal = Old EncoderVal!"));
}
if (NewEncVal > 60000 || NewEncVal < 3000) {
NewEncVal = 32000;
}
EncValList[EncAddr] = NewEncVal;
//Serial.print("getEncDirection, Dir: ");
//Serial.println(EncDir);
return EncDir;
}
void ButtonHandler(byte PinNum, bool HeldDown) {
//Serial.println(" ");
//Serial.println("-------------------------");
//Serial.println("INSIDE Task Handler");
// Check whether system is on or off
if (OnOffState == 0) {
return;
}
// wake up the displays
WakeUp();
// get the real ButtonID, identical to array indices
byte BtnId = PinNum - ResPinsForArduino;
// get the number of display to be sent (via DispSelect)
byte DispAddr = RetrieveNumsFromFlash (4, BtnId); // first input is ArrayNumber = 4 for DisplayNumber
//Get the information, whether this button is used as toggle
bool IsToggle = RetrieveNumsFromFlash (6, BtnId); // ArrayNum = 6 is EnableToggleList
Serial.print(F("PinNum: "));
Serial.println(PinNum);
Serial.print(F("BtnId: "));
Serial.println(BtnId);
Serial.print(F("IsToggle: "));
Serial.println(IsToggle);
// set FontInverse
bool IsFontInverse = false;
// if this is a toggle button, change the state in BtnStateList
// and redraw the display
if (IsToggle == true) {
bool OldBtnState = BtnStateList[BtnId];
BtnStateList[BtnId] = !OldBtnState;
Serial.print(F("OldBtnState: "));
Serial.println(OldBtnState);
Serial.print(F("NewBtnState: "));
Serial.println(!OldBtnState);
if (!OldBtnState == true) {
IsFontInverse = true;
}
// draw the text to display
DispDrawFull(DispAddr);
}
//send the keystroke(s)
KeypressHandler(BtnId);
// Start the Sleep Counter
PrepareSleep();
}
///////////
// SETUP //
///////////
void setup() {
Wire.begin();
Serial.begin(9600);
//InitializeDisplays;
for (byte k = 0; k < 8; k++) {
DispSelect(k);
u8g2.begin();
u8g2.firstPage();
}
// adding all 23017 expander
multiIoAddExpander(MultiIo, ioFrom23017(0x20, ACTIVE_LOW_OPEN, ArduinoInterruptPin), 16);
multiIoAddExpander(MultiIo, ioFrom23017(0x21, ACTIVE_LOW_OPEN, ArduinoInterruptPin), 16);
multiIoAddExpander(MultiIo, ioFrom23017(0x22, ACTIVE_LOW_OPEN, ArduinoInterruptPin), 16);
multiIoAddExpander(MultiIo, ioFrom23017(0x23, ACTIVE_LOW_OPEN, ArduinoInterruptPin), 16);
switches.initialiseInterrupt(MultiIo, true); //switches.initialiseInterrupt(MultiIo, true);
// Basic controls
switches.addSwitch(ResPinsForArduino + 7, OnMainSwitchPress); // ON
switches.onRelease(ResPinsForArduino + 7, OnMainSwitchRelease); // OFF
switches.addSwitch(ResPinsForArduino + 6, OnEnableBtnPress); // Enable ON
switches.onRelease(ResPinsForArduino + 6, OnEnableBtnRelease); // Enable OFF
switches.addSwitch(ResPinsForArduino + 3, OnProfileChBtnPress); // Change Profile
switches.addSwitch(ResPinsForArduino + 2, OnSleepTimeBtnPress); // Change SleepTimer
switches.addSwitch(ResPinsForArduino + 5, OnProfileSelBtnPress);// Select Profile
switches.addSwitch(ResPinsForArduino + 4, OnDispOnOffBtnPress); // Displays On/Off
// Toggle switches, initialise in a loop
for (byte k = 16; k < 32; k++) {
switches.addSwitch(ResPinsForArduino + k, OnToggleBtnPress);
switches.onRelease(ResPinsForArduino + k, OnToggleBtnRelease);
}
// Large Toggle switches
switches.addSwitch(ResPinsForArduino + 56, OnToggleBtnPress);
switches.addSwitch(ResPinsForArduino + 57, OnToggleBtnPress);
// Push Buttons
switches.addSwitch(ResPinsForArduino + 52, OnPushBtnPress);
switches.addSwitch(ResPinsForArduino + 53, OnPushBtnPress);
switches.addSwitch(ResPinsForArduino + 54, OnPushBtnPress);
switches.addSwitch(ResPinsForArduino + 55, OnPushBtnPress);
switches.addSwitch(ResPinsForArduino + 60, OnPushBtnPress);
switches.addSwitch(ResPinsForArduino + 61, OnPushBtnPress);
switches.addSwitch(ResPinsForArduino + 62, OnPushBtnPress);
switches.addSwitch(ResPinsForArduino + 63, OnPushBtnPress);
// Color Push Buttons
switches.addSwitch(ResPinsForArduino + 51, OnPushBtnPress); // Green 0
switches.addSwitch(ResPinsForArduino + 50, OnPushBtnPress); // Green 1
switches.addSwitch(ResPinsForArduino + 58, OnPushBtnPress); // Red 0
switches.addSwitch(ResPinsForArduino + 59, OnPushBtnPress); // Red 1
// Encoder Click buttons, initialise in a loop
for (byte m = 8; m < 16; m++) {
switches.addSwitch(ResPinsForArduino + m, OnPushBtnPress);
}
// Only for testing, Encoder buttons as normal switches
//for (byte m = 32; m < 48; m++) {
// switches.addSwitch(ResPinsForArduino + m, OnPushBtnPress);
//}
// setting up first encoder
setupRotaryEncoderWithInterrupt(ResPinsForArduino + 36, ResPinsForArduino + 35, onEncChange0);
switches.changeEncoderPrecision(50000, 25000);
/* setting up extra encoders. Make sure they are all on the same expander
HardwareRotaryEncoder* EncObj1 = new HardwareRotaryEncoder(ResPinsForArduino + 37, ResPinsForArduino + 45, onEncChange1);
switches.setEncoder(1, EncObj1);
switches.changeEncoderPrecision(1, MaxEncValList[1], EncValList[1]);
HardwareRotaryEncoder* EncObj2 = new HardwareRotaryEncoder(ResPinsForArduino + 39, ResPinsForArduino + 47, onEncChange2);
switches.setEncoder(2, EncObj2);
switches.changeEncoderPrecision(2, MaxEncValList[2], EncValList[2]);
HardwareRotaryEncoder* EncObj3 = new HardwareRotaryEncoder(ResPinsForArduino + 38, ResPinsForArduino + 46, onEncChange3);
switches.setEncoder(3, EncObj3);
switches.changeEncoderPrecision(3, MaxEncValList[3], EncValList[3]);
HardwareRotaryEncoder* EncObj4 = new HardwareRotaryEncoder(ResPinsForArduino + 35, ResPinsForArduino + 43, onEncChange4);
switches.setEncoder(4, EncObj4);
switches.changeEncoderPrecision(4, MaxEncValList[4], EncValList[4]);
HardwareRotaryEncoder* EncObj5 = new HardwareRotaryEncoder(ResPinsForArduino + 34, ResPinsForArduino + 42, onEncChange5);
switches.setEncoder(5, EncObj5);
switches.changeEncoderPrecision(5, MaxEncValList[5], EncValList[5]);
HardwareRotaryEncoder* EncObj6 = new HardwareRotaryEncoder(ResPinsForArduino + 33, ResPinsForArduino + 41, onEncChange6);
switches.setEncoder(6, EncObj6);
switches.changeEncoderPrecision(6, MaxEncValList[6], EncValList[6]);
HardwareRotaryEncoder* EncObj7 = new HardwareRotaryEncoder(ResPinsForArduino + 32, ResPinsForArduino + 40, onEncChange7);
switches.setEncoder(7, EncObj7);
switches.changeEncoderPrecision(7, MaxEncValList[7], EncValList[7]);
*/
// Setting the LEDs of the color push buttons (connected to Arduino directly) to Output
ioDevicePinMode(MultiIo, 4, OUTPUT);
ioDevicePinMode(MultiIo, 5, OUTPUT);
// Getting button states at start
if (switches.isSwitchPressed(106)) {
EnableState = 1;
}
if (switches.isSwitchPressed(107)) {
OnOffState = 1;
}
uint8_t taskId = taskManager.scheduleFixedRate(2000, ReportIn);
Serial.print( "task ID: " );
Serial.println(taskId);
}
void loop() {
taskManager.runLoop();
}