Hello, I need some direction as to what is possible. I have been using Arduino Pro Micro boards to create button boxes etc. In American Truck Simulator and Euro Truck Simulator, the Electrics on programing (ignition) in their programing does not turn on an off with a latched switch. For example I can use a key switch to turn on the electrics, but because the programs don't recognize the latched state on the initial activation, to turn off the electrics again I have to turn the switch off, and then on again. So the question is is there a way to program the switch to activate and then shut off the joystick output until I turn the switch off again, which would again cause a momentary activation of the (button) so that ATS and ETS would recognize it as a button press even though I am actually turning the switch off (open). If possible can i be directed to a sample code and how to wire the switch. Thanks in advance
You need to detect when the switch becomes closed rather than when it is closed
See the StateChangeDetection example in the IDE
Hi,
You need to get momentary spring return toggle switches.
They are sprung so they are OFF, you toggle them ON, when you release the toggle it returns to the OFF position.
google;
sprung spdt switch non-latching
Tom...
Hello
You may design a common button handler for debouncing, change state detection and to start the desired action for the button.
This task is quite easy to code by using a structure array and a methode to handle a.m. actions. This data structure contains the information about button pin address, current state and the timing information for debouncing.
Have a nice day and enjoy coding in C++.
Are you saying you want the same effect when the switch goes OFF as when it goes ON?
See this
Sounds like what you trying to do maybe.
a7
Hi sorry for delayed response Yes I want a momentary on when activating a switch and a momentary on again when i turn the switch off. Thanks in advance
Hi alto777, I see the code and it checks out ok in my code, but how do I wire the switch? I am using this Nano Pro Micro board as joystick functions using pins A3, A2,A1,A0,15 as my rows. 14, 16,10,9,8 as my columns. I have rotary encoders on TX, Rx; 2,3 ; 4,5; 6,7 Can I remove one encoder from 6,7 and use pin 7 or 6 (const int toggle1 = 8;) do I change this to pin 7? and then do I use 5v - Switch - pin7 with a 10k resistor to ground as well? Hope you can shed light. I'm good at breaking the code down when I see it, just not good at creating it. Thanks in advance
OK not sure what that means given your question about how to wire the switch.
You can place the switch at any crosspoint in your switch matrix, just like all the other switches are.
But. Since it is a toggle, not momentarily, switch, you will be subject to risk of “ghosting”, where pressing multiple buttons will result in spurious readings.
This is solved with diodes.
diode ghosting matrix
and poke around a bit. Essentially a diode is added to every switch path.
Easy if you have real switches, hard to impossible if you are using a matrix keypad that doesn’t have them build in.
HTH
a7
Well I have a headache now, i'm just not understanding how this code works. Below is my button box code that has 24 joystick buttons and 3 rotary encoders. I added remarks above the code added re toggle1State, but I cant seem to get the switch to work. I have tried Switch on Pin 6 with 5v thru 10K resistor through Diode to toggle (on/off) switch then to ground but I get no indication of activity when using the game controller settings box in windows. I tried hooking this up to one of my buttons in the array example pin6 & 5v 10k through diode then to pin 10 but that didn't work either, tried using the switch matrix using A3, 14 (btn #1) connecting switch from A3 adding the 5v through 10k resistor then diode to switch then to pin 14 but that didnt work either, so about ready to give it up as a lost cause.
//ATS-ETS BUTTON BOX
// #include <Keyboard.h>
#include <Keypad.h>
#include <Joystick.h>
#include <Bounce2.h>
#include <elapsedMillis.h>
#define ENABLE_PULLUPS
#define NUMROTARIES 3
#define NUMBUTTONS 24
#define NUMROWS 5
#define NUMCOLS 5
Bounce pbrake= Bounce(2,10);
elapsedMillis timeElapsed;
unsigned int t_interval = 100; //interval to hold buttons on when needed
byte buttons[NUMROWS][NUMCOLS] = {
{0,1,2,3,4},
{5,6,7,8,9},
{10,11,12,13,14},
{15,16,17,18,19},
{20,21,22,23},
};
struct rotariesdef {
byte pin1;
byte pin2;
int ccwchar;
int cwchar;
volatile unsigned char state;
};
rotariesdef rotaries[NUMROTARIES] {
{0,1,24,25,0},
{2,3,26,27,0},
{4,5,28,29,0},
// {6,7,30,31,0}, Removed to free pins 6,7
};
#define DIR_CCW 0x10
#define DIR_CW 0x20
#define R_START 0x0
#ifdef HALF_STEP
#define R_CCW_BEGIN 0x1
#define R_CW_BEGIN 0x2
#define R_START_M 0x3
#define R_CW_BEGIN_M 0x4
#define R_CCW_BEGIN_M 0x5
const unsigned char ttable[6][4] = {
// R_START (00)
{R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START},
// R_CCW_BEGIN
{R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START},
// R_CW_BEGIN
{R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START},
// R_START_M (11)
{R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START},
// R_CW_BEGIN_M
{R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW},
// R_CCW_BEGIN_M
{R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW},
};
#else
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6
const unsigned char ttable[7][4] = {
// R_START
{R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START},
// R_CW_FINAL
{R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW},
// R_CW_BEGIN
{R_CW_NEXT, R_CW_BEGIN, R_START, R_START},
// R_CW_NEXT
{R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START},
// R_CCW_BEGIN
{R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START},
// R_CCW_FINAL
{R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW},
// R_CCW_NEXT
{R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};
#endif
// Added these 3 lines for the switch toggle1 function also changed 8 to 6 assumed pin #
const int toggle1 = 6;
int toggle1State = 0;
int lastToggle1State = 0;
byte rowPins[NUMROWS] = {21,20,19,18,15};
byte colPins[NUMCOLS] = {14,16,10,9,8};
Keypad buttbx = Keypad( makeKeymap(buttons), rowPins, colPins, NUMROWS, NUMCOLS);
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
JOYSTICK_TYPE_JOYSTICK, 32, 0,
false, false, false, false, false, false,
false, false, false, false, false);
void setup() {
Joystick.begin();
rotary_init();
// Added these lines for Toggle function
pinMode(toggle1, INPUT_PULLUP);
Serial.begin(9600);
// Keyboard.begin();
}
void loop() {
CheckAllEncoders();
CheckAllButtons();
// added below section re Switch Toggle1
toggle1State = digitalRead(toggle1);
if (toggle1State != lastToggle1State)
{
if (toggle1State == HIGH)
{
Serial.write('1');
//Keyboard.press('1');
delay(500);
}
else if (toggle1State == LOW)
{
Serial.write('2');
//Keyboard.press('2');
delay(500);
}
delay(50);
//Keyboard.releaseAll();
}
lastToggle1State = toggle1State;
}
void CheckAllButtons(void) {
if (buttbx.getKeys())
{
for (int i=0; i<LIST_MAX; i++)
{
if ( buttbx.key[i].stateChanged )
{
switch (buttbx.key[i].kstate) {
case PRESSED:
case HOLD:
Joystick.setButton(buttbx.key[i].kchar, 1);
break;
case RELEASED:
case IDLE:
Joystick.setButton(buttbx.key[i].kchar, 0);
break;
}
}
}
}
}
void rotary_init() {
for (int i=0;i<NUMROTARIES;i++) {
pinMode(rotaries[i].pin1, INPUT);
pinMode(rotaries[i].pin2, INPUT);
#ifdef ENABLE_PULLUPS
digitalWrite(rotaries[i].pin1, HIGH);
digitalWrite(rotaries[i].pin2, HIGH);
#endif
}
}
unsigned char rotary_process(int _i) {
unsigned char pinstate = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1);
rotaries[_i].state = ttable[rotaries[_i].state & 0xf][pinstate];
return (rotaries[_i].state & 0x30);
}
void CheckAllEncoders(void) {
for (int i=0;i<NUMROTARIES;i++) {
unsigned char result = rotary_process(i);
if (result == DIR_CCW) {
Joystick.setButton(rotaries[i].ccwchar, 1); delay(50); Joystick.setButton(rotaries[i].ccwchar, 0);
};
if (result == DIR_CW) {
Joystick.setButton(rotaries[i].cwchar, 1); delay(50); Joystick.setButton(rotaries[i].cwchar, 0);
};
}
}
Open to any suggestions (sure wished that ATS would change their programming so this would just work! lol
Just a thought - the bounce2 library can return switch.rose() and switch.fell(). These return the HIGH/LOW transition of the input. You could or these together and use the result to indicate any time the switch changes.
Ok, high enough time to ask you to draw and post a schematic of all the wiring. Just use a pencil and paper and shoot a picture of it if you don’t want to get spidertracked learning a schematic capture program. I wouldn’t bother, pen and paper is good.
I think you may be off the track on how the matrix works and how the diodes must enter into it when using non-momentarily switches.
Draw the wiring from before the idea of using the toggle switches and anything that needs be added to address the implications of having done.
a7
Hi alto777,
So this is my electrical connections, Trial 1 on LH side (purplish color) Pin 6 to gnd wired as shown.
Trial 2 Pin 6 to Pin 7 with GND removed, Trial 3 Pin 6 to pin 10 (brownish color) still using power, resistor and diode. Trial 4 - light blue on RH side tried connecting to button in my Joystick matrix.
The only change I made to the code you referenced, which was added to code above was "const int toggle1 =6 (instead of 8) as i assumed 8 referenced pin # I also tried code with and without the #include <keyboard.h> So thats where i left it, hope you can shed some light to a coding challenged grandpa. Thanks
Please post the code you had before your attempt to add the toggle idea.
At a glance, you have not u dets told how to take the example code and fit it to your circumstances.
Don’t worry about the diode(s) at this moment. Sry I mentioned it, it will,become a problem but won’t be necessary yet.
The switch should just wired between any two matrix cross points, you can even just use an existing button and test by holding it down for a period and then letting it go, to kinda simulate toggle the to-be.
Once we can “see” that one switch opening and closing through rather normal function that handles the matrix, it will be easier and a second small step to start adding the logic.
Then the diodes…
a7
This my code I have put the added sections under ALL CAP HEADERS, Code works fine for all the joystick functions, was wondering though if I need to create a Keyboard Matrix for this function? as maybe wont work with the Joystick Library, just a thought.
//ATS-ETS BUTTON BOX
// #include <Keyboard.h> ADDED FOR SOME OF THE TESTING
#include <Keypad.h>
#include <Joystick.h>
#include <Bounce2.h>
#include <elapsedMillis.h>
#define ENABLE_PULLUPS
#define NUMROTARIES 3
#define NUMBUTTONS 24
#define NUMROWS 5
#define NUMCOLS 5
Bounce pbrake= Bounce(2,10);
elapsedMillis timeElapsed;
unsigned int t_interval = 100; //interval to hold buttons on when needed
byte buttons[NUMROWS][NUMCOLS] = {
{0,1,2,3,4},
{5,6,7,8,9},
{10,11,12,13,14},
{15,16,17,18,19},
{20,21,22,23},
};
struct rotariesdef {
byte pin1;
byte pin2;
int ccwchar;
int cwchar;
volatile unsigned char state;
};
rotariesdef rotaries[NUMROTARIES] {
{0,1,24,25,0},
{2,3,26,27,0},
{4,5,28,29,0},
// {6,7,30,31,0}, Removed to free pins 6,7
};
#define DIR_CCW 0x10
#define DIR_CW 0x20
#define R_START 0x0
#ifdef HALF_STEP
#define R_CCW_BEGIN 0x1
#define R_CW_BEGIN 0x2
#define R_START_M 0x3
#define R_CW_BEGIN_M 0x4
#define R_CCW_BEGIN_M 0x5
const unsigned char ttable[6][4] = {
// R_START (00)
{R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START},
// R_CCW_BEGIN
{R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START},
// R_CW_BEGIN
{R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START},
// R_START_M (11)
{R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START},
// R_CW_BEGIN_M
{R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW},
// R_CCW_BEGIN_M
{R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW},
};
#else
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6
const unsigned char ttable[7][4] = {
// R_START
{R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START},
// R_CW_FINAL
{R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW},
// R_CW_BEGIN
{R_CW_NEXT, R_CW_BEGIN, R_START, R_START},
// R_CW_NEXT
{R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START},
// R_CCW_BEGIN
{R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START},
// R_CCW_FINAL
{R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW},
// R_CCW_NEXT
{R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};
#endif
// ADDED THE 3 LINES BELOW WHEN TRYING THE TOGGLE FUNCTION
const int toggle1 = 6;
int toggle1State = 0;
int lastToggle1State = 0;
byte rowPins[NUMROWS] = {21,20,19,18,15};
byte colPins[NUMCOLS] = {14,16,10,9,8};
Keypad buttbx = Keypad( makeKeymap(buttons), rowPins, colPins, NUMROWS, NUMCOLS);
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
JOYSTICK_TYPE_JOYSTICK, 32, 0,
false, false, false, false, false, false,
false, false, false, false, false);
void setup() {
Joystick.begin(); // Original code
rotary_init(); // Original code
// ADDED THESE LINES BELOW FOR THE TOGGLE FUNCTION PROGRAMING
pinMode(toggle1, INPUT_PULLUP);
Serial.begin(9600);
// Keyboard.begin();
}
void loop() {
CheckAllEncoders();
CheckAllButtons();
// ADDED BELOW SECTION FOR THE TOGGLE1 FUNCTION
toggle1State = digitalRead(toggle1);
if (toggle1State != lastToggle1State)
{
if (toggle1State == HIGH)
{
Serial.write('1');
//Keyboard.press('1');
delay(500);
}
else if (toggle1State == LOW)
{
Serial.write('2');
//Keyboard.press('2');
delay(500);
}
delay(50);
//Keyboard.releaseAll();
}
lastToggle1State = toggle1State;
}
void CheckAllButtons(void) {
if (buttbx.getKeys())
{
for (int i=0; i<LIST_MAX; i++)
{
if ( buttbx.key[i].stateChanged )
{
switch (buttbx.key[i].kstate) {
case PRESSED:
case HOLD:
Joystick.setButton(buttbx.key[i].kchar, 1);
break;
case RELEASED:
case IDLE:
Joystick.setButton(buttbx.key[i].kchar, 0);
break;
}
}
}
}
}
void rotary_init() {
for (int i=0;i<NUMROTARIES;i++) {
pinMode(rotaries[i].pin1, INPUT);
pinMode(rotaries[i].pin2, INPUT);
#ifdef ENABLE_PULLUPS
digitalWrite(rotaries[i].pin1, HIGH);
digitalWrite(rotaries[i].pin2, HIGH);
#endif
}
}
unsigned char rotary_process(int _i) {
unsigned char pinstate = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1);
rotaries[_i].state = ttable[rotaries[_i].state & 0xf][pinstate];
return (rotaries[_i].state & 0x30);
}
void CheckAllEncoders(void) {
for (int i=0;i<NUMROTARIES;i++) {
unsigned char result = rotary_process(i);
if (result == DIR_CCW) {
Joystick.setButton(rotaries[i].ccwchar, 1); delay(50); Joystick.setButton(rotaries[i].ccwchar, 0);
};
if (result == DIR_CW) {
Joystick.setButton(rotaries[i].cwchar, 1); delay(50); Joystick.setButton(rotaries[i].cwchar, 0);
};
}
}
// #include <Keyboard.h> ADDED FOR SOME OF THE TESTING
// ADDED THE 3 LINES BELOW WHEN TRYING THE TOGGLE FUNCTION
const int toggle1 = 6;
int toggle1State = 0;
int lastToggle1State = 0;
// ADDED THESE LINES BELOW FOR THE TOGGLE FUNCTION PROGRAMING
pinMode(toggle1, INPUT_PULLUP);
Serial.begin(9600);
// Keyboard.begin();
// ADDED BELOW SECTION FOR THE TOGGLE1 FUNCTION
toggle1State = digitalRead(toggle1);
if (toggle1State != lastToggle1State)
{
if (toggle1State == HIGH)
{
Serial.write('1');
//Keyboard.press('1');
delay(500);
}
else if (toggle1State == LOW)
{
Serial.write('2');
//Keyboard.press('2');
delay(500);
}
delay(50);
//Keyboard.releaseAll();
}
lastToggle1State = toggle1State;
You can see the sections i added these to in the code, maybe i have them in wrong places?
Thanks again
Hi,
What are the circled components?
If switches, you would advised not to use Tx and Rx pins as these are used for programming the Nano.
Tom..
They are rotary encoders
rotariesdef rotaries[NUMROTARIES] {
{0,1,24,25,0}, // 0,1 are Tx Rx pins
{2,3,26,27,0},
{4,5,28,29,0},
Hi, @AustinTheMech
How are you going to program the Nano, if both of the encoder outputs are switched to Gnd?
Tom...
Nano Pro Micro has USB
What is a "Nano Pro Micro"? I've never heard of it.