Button Press detector

RobinNoGood

Active member
Hi,

I've got a TeensyLC button box which used to output via USB as a game controller.

And then I started tinkering before reading any manuals. It now doesn't output anything. It does work, I've been playing with example sketches to make sure I didn't completely ruin everything.

Can I sketch (and by that I mean would you please tell me how/write it for me or point me at the right links) a button press/pin detector, (probably outputting relevant info to the Serial Monitor?) to find out which buttons are connected to which pins, so that I can start rebuilding the functionality I destroyed?

Many Thanks.
 
Thank you.

I've been playing with 'DigitalRead'. Is there a way to monitor all pins and report their states?

I've also used Examples>Teensy>USB-Joystick>Complete.
In Windows Game Controller, there is no output.
 
I got this:


const int numButtons = 16;

void setup() {
Serial.begin(38400);
for (int i=0; i<numButtons; i++)
pinMode(i, INPUT);
}

void loop()
{
for (int i=0; i<numButtons; i++)
if (digitalRead(i) == HIGH) {
Serial.println("Button is not pressed...");
} else {
Serial.println("Button pressed!!!");
}
delay(2500);
}

Which prints as often as you want. But I only wnat it to print when there's a change, and which button/pins have changed. I take it some buttons are 'High' when unpressed? This doesn't acccount for those.
 
It may be difficult to help much without more information.

As I mentioned in a different thread recently. A few of us hacked up a sketch:
Code:
#ifdef ESP_PLATFORM
#define digitalWriteFast digitalWrite
#define digitalReadFast digitalRead
#endif

void setup()
{
  Serial.begin(115200);
  while (!Serial && millis() < 4000 );
  Serial.println("Compile Time:: " __FILE__ " " __DATE__ " " __TIME__);
  Serial.printf("Num Digital Pins: %d\n", NUM_DIGITAL_PINS);

  testForShorts();
  
}

uint32_t cnt = 0;
void loop() {
  cnt++;
    allPinTest( cnt );
}

uint32_t pinLast[NUM_DIGITAL_PINS];
void allPinTest( uint32_t cnt ) {
  uint32_t ii, SET;
  Serial.print("PULLDOWN Start Vals:\n  ");
  SET = 0;
  Serial.print("PULLDOWN :: TEST to 3.3V\n  ");
  for ( ii = 0; ii < NUM_DIGITAL_PINS; ii++) {
    pinMode( ii, INPUT_PULLDOWN );
    delayMicroseconds( 5 );
    pinLast[ii] = digitalReadFast( ii );
    if (pinLast[ii]) {
      Serial.print("\nd#=");
      Serial.print( ii );
      Serial.print( " val=" );
    }
    Serial.print( pinLast[ii] );
    Serial.print(',');
  }
  Serial.println();
  Serial.println();
  while ( 1 ) {
    uint32_t jj, dd = 0, cc = 0, ee=4;
    cc = 0;
    for ( ii = 0; ii < NUM_DIGITAL_PINS; ii++) {
      jj = digitalReadFast( ii );
      if ( jj != pinLast[ii] ) {
        dd = 1;
        cc++;
        pinLast[ii] = jj;
        Serial.print("d#=");
        Serial.print( ii );
        if ( pinLast[ii] ) Serial.print( "\t" );
        Serial.print( " val=" );
        Serial.print( pinLast[ii] );
        Serial.print(',');
      }
      if ( cc > 1 && ee ) {
        Serial.println(">>> MULTI CHANGE !!");
        ee--;
      }
      if ( Serial.available() ) {
        while ( Serial.available() ) Serial.read();
        if ( 0 == SET ) {
          SET = 1;
          Serial.print("PULLUP :: TEST TO GND\n  ");
        }
        else {
          SET = 0;
          Serial.print("PULLDOWN :: TEST to 3.3V\n  ");
        }
        for ( ii = 0; ii < NUM_DIGITAL_PINS; ii++) {
          if ( 0 == SET )
            pinMode( ii, INPUT_PULLDOWN );
          else
            pinMode( ii, INPUT_PULLUP );
          delayMicroseconds( 20 );
          pinLast[ii] = digitalReadFast( ii );
          if (SET != pinLast[ii]) {
            Serial.print("d#=");
            Serial.print( ii );
            Serial.print( " val=" );
            Serial.println( pinLast[ii] );
          }
        }
      }
    }
    if ( dd ) {
      dd = 0;
      Serial.println();
      delay( 50 );
    }
  }
}

void testForShorts() {
  uint32_t ii;
  Serial.print("Quick Test for Shorts to adjacent pin");
  Serial.println("First pull pins down and see if the next one follows");
  for ( ii = 0; ii < NUM_DIGITAL_PINS-1; ii++) {
    pinMode( ii+1, INPUT_PULLDOWN );
    pinMode( ii, OUTPUT);
    digitalWrite(ii, HIGH);
    delayMicroseconds( 5 );
    if (digitalRead(ii+1)) {
      Serial.printf("%d:%d ", ii, ii+1);
    }
  }
  Serial.println("\n Now try Pull up and see if setting low follow");
  for ( ii = 0; ii < NUM_DIGITAL_PINS-1; ii++) {
    pinMode( ii+1, INPUT_PULLUP );
    pinMode( ii, OUTPUT);
    digitalWrite(ii, LOW);
    delayMicroseconds( 5 );
    if (!digitalRead(ii+1)) {
      Serial.printf("%d:%d ", ii, ii+1);
    }
  }
  Serial.println();  
}
Which initializes all of the IO pins to either INPUT_PULLUP or INPUT_PULLDOWN (You can change between the two by hitting enter in Serial Monitor. It then loops through all of the IO pins seeing which ones change state.
Now assuming simple connections, like one button to one pin, you should be able to figure which. Using something like this

If it is more complex, like using some additional hardware or something like a button matrix, or resister ladder. you will probably have to try to trace through the stuff to see what connects to what.

But hard to say more without additional information.
 
Thank you.

The Teensy is part of a Thrustmaster/Sparco Steerring wheel button box.
I bought a 'All in One' conversion kit that changes Thrustmaster 6-din output to USB, complete with approriate firmware/sketch
I've had some USB bus issues, and thought this wheel might be a part of the problem, so things led to other things...

There are a number of resistors and chips in place on the Steering Wheel's board.
But if I'm only trying to read what the Teensy is seeing, does that matter?

There are a couple of guides/sketches online for a pro-micro. Also think they're using older version of the wheel, judging by the pin-out/wiring guides.

My Teensy is wired like this:
Teensy.png
 
Last edited:
I got this:


const int numButtons = 16;

void setup() {
Serial.begin(38400);
for (int i=0; i<numButtons; i++)
pinMode(i, INPUT);
}

void loop()
{
for (int i=0; i<numButtons; i++)
if (digitalRead(i) == HIGH) {
Serial.println("Button is not pressed...");
} else {
Serial.println("Button pressed!!!");
}
delay(2500);
}

Which prints as often as you want. But I only wnat it to print when there's a change, and which button/pins have changed. I take it some buttons are 'High' when unpressed? This doesn't acccount for those.

FWIW:

When this is uploaded, without pressing any of the buttons, the Serial Monitor prints:

Button is not pressed (x15)
Button is pressed (x1)
Screenshot (1035).png
 
Last edited:
Changing:

if (digitalRead(i) == HIGH)

to,

if (digitalRead(i) == INPUT)

reverses the Serial Monitor output.

Screenshot (1036).png

When I do press buttons, nothing changes, same messages in Serial Monitor.

What I'd like to see is something like

Current State=
Button1, Low
Button2, High
Button3, None
etc...

just printed once, then to only print again when there's a change.
 
Last edited:
Not seeing noted how the buttons are wired?

Are they pulled up externally to 3.3V? In this case per p#5 the pinMode should be INPUT_PULLDOWN : pinMode(i, INPUT_PULLDOWN );

Or if the buttons are wired to GND then using: pinMode(i, INPUT_PULLUP );

This would hold the pin to a known state of GND or 3.3V until the button is pressed, and pressing the button would change the state only while the button was pressed.

With pinMode(i, INPUT); the pin will not be obligated/driven to switch to a known state before or after a button is pressed.
 
I dont know how any of this works in detail .

I didn't want to add any more resistance to the buttons, as the buttons are on a pre-populated board running thrrugh x number of Resistors of unknown values.
I just wanted to see what was being read by the Teensy when a button was and wasn't being pressed.
I was expecting the rest state of various buttons to differ, some High, some Low, some 0.

Here's the board:

Buttons.jpg

I'll add the PULLUP/PULLDOWN to the sketch and see what happens.
 
That is pretty well HIDDEN :(

If you can get a meter across the button from GND testing for continuity it probably just closes a switch.

Test for Ohms and it may show there is a resistor inline with the button when pressed. Test for voltage across the button and it would show 0V in both cases and then pinMode(i, INPUT_PULLUP ); should be the answer, and the button will go LOW when pressed and stay HIGH otherwise.
 
With:

Code:
const int numButtons = 16;

void setup() {
Serial.begin(38400);
for (int i=0; i<numButtons; i++)
pinMode(i, INPUT_PULLDOWN);
}

void loop()
{
for (int i=0; i<numButtons; i++)
if (digitalRead(i) == INPUT) {
Serial.println("Button is not pressed");
} else {
Serial.println("Button pressed");
}
delay(2500);
}

Serial Monitor reports:
Code:
Button is not pressed
Button is not pressed
Button is not pressed
Button is not pressed
Button is not pressed
Button is not pressed
Button is not pressed
Button is not pressed
Button is not pressed
Button is not pressed
Button is not pressed
Button is not pressed
Button pressed
Button is not pressed
Button is not pressed
Button is not pressed

With PULLUP, I get:

Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
 
Does one of those present the expected readings of the buttons? Perhaps the first where one is noted as pressed?

This line is not clear: if (digitalRead(i) == INPUT) {

For working PULLUP it should read: if (digitalRead(i) == LOW) {

For working PULLDOWN it should read: if (digitalRead(i) == HIGH) {

Is there a DVM/DMM meter there that allows testing the button properties to the pin (and GND) as noted in p#11?

Is there a picture of how the pins get connected to the board image shown in p#10? Is there a common GND to the board with the buttons?
 
Is there a DVM/DMM meter there that allows testing the button properties to the pin (and GND) as noted in p#11?

I think this is what I wanted to use Teensy for.

At least before I start trying to map functional outputs to them.

But, no. All buttons are currently unpressed so neither of the above variants report correctly.
I want to read whatever the buttons are sending right now, and relay that to the monitor. Ideally in a "buttonNumber, buttonState [i.e Low, High etc]" format.
 
Try this - maybe as is - it was not compiled:
Code:
const int numButtons = 16;
bool bLast[numButtons];

void setup() {
	Serial.begin(38400);
	for (int i = 0; i < numButtons; i++)
		pinMode(i, INPUT_PULLUP);  // IF CHANGED TO PULLDOWN, TEST for LOW below NOT HIGH
	for (int i = 0; i < numButtons; i++)
		bLast[i] = digitalRead(i);
}

void loop()
{
	bool bRead;
	for (int i = 0; i < numButtons; i++) {
		bRead = digitalRead(i);
		if (bRead != bLast[i]) {
			if ( digitalRead(i) == HIGH ) {
				Serial.println("Button is not pressed");
			} else {
				Serial.println("Button pressed");
			}
			bLast[i] = bRead;
		}
	}
	delay(100);
}
 
Thank you. It compiles, but gives no output.

I modified mine above a little bit
Code:
const int numButtons = 16;

void setup() {
Serial.begin(38400);
for (int i=0; i<numButtons; i++)
pinMode(i, INPUT);
}

void loop()
{
for (int i=0; i<numButtons; i++)
if (digitalRead(i) == HIGH) {
Serial.println("Button is High");
  }else{
    if (digitalRead(i) == LOW) {
    Serial.println("Button is Low");
    }
delay(2500);
  }}

This gives the current HIGH/LOW state of each button.
 
Thank you. It compiles, but gives no output.

I modified mine above a little bit
...
This gives the current HIGH/LOW state of each button.

That p#15 code should only print anything on button change.

Though the delay(100) could be better handled and only delay when there was a change detected. As written any button held over 100+ milliseconds should be detected?

Not sure what is being tested there ... the pinmode is still just INPUT. Maybe he switches are internally driven HIGH and LOW on press or release? Change to pinMode should allow seeing how the switches behave.

It could print the state as stored in bLast in setup() - but that seemed like extra work.

This code will scan continuously and only print when a button is changed (press or release) - and it will delay 100 ms then to avoid button bounce doing print spasms.
Code:
const int numButtons = 16;
bool bLast[numButtons];

void setup() {
	Serial.begin(38400);
	for (int i = 0; i < numButtons; i++)
		pinMode(i, INPUT_PULLUP);  // IF CHANGED TO PULLDOWN, TEST for LOW below NOT HIGH
	for (int i = 0; i < numButtons; i++)
		bLast[i] = digitalRead(i);
}

void loop()
{
	bool bRead;
	bool bChange = false;
	for (int i = 0; i < numButtons; i++) {
		bRead = digitalRead(i);
		if (bRead != bLast[i]) {
                        bChange = true;
			if ( digitalRead(i) == HIGH ) {
				Serial.println("Button is not pressed");
			} else {
				Serial.println("Button pressed");
			}
			bLast[i] = bRead;
		}
	}
	if (bChange) delay(100);
}
 
Again, no change is being registered with that.

Maybe my buttons are fried?
I just did this:

Code:
const int numButtons = 16;
int lastButtonState;

void setup() {
  Serial.begin(9600);
  for (int i=0; i<numButtons; i++)
  lastButtonState = digitalRead(INPUT);
}

void loop() {
  // read the value of the button
  int buttonState = digitalRead(INPUT);

  if (lastButtonState != buttonState) // state changed
    delay(50); // debounce time

    if (buttonState != lastButtonState)
      Serial.println("Go");
      else
      Serial.println("Waiting");
      delay(1000);
     }

Serial Monitor shows the state as Waiting. Nothing changes on a button press.

How do I enumerate each button?
"Button 1, waiting"
etc...
 
Note: my original post, was with only the information that you were using a "TeensyLC button box" And the example program was to see if potentially the buttons were
connected simply, with a one button to a pin connection... And I mentioned, this is only one of the possible ways that they may be connected up. Again, as I mentioned, throwing darts.

With your subsequent post of being a Thrustmaster... Then maybe your best bet is to do some only queries to see if more information is available for your specific setup.
Something like: google thrustmaster to usb using teensy lc
And look through the different hits. You may try to refine the search to your specific model of Thrustmaster. For example, one of the hits I saw from a YouTube video found by the
above search showed: https://pit.uriba.org/uriba/standalone-cougar-tqs-part-i/

And in that case, the buttons were arranged in a matrix. So that would be very different!

But more interesting is your diagram showing what is connected up to the Teensy LC.

Thank you.


The Teensy is part of a Thrustmaster/Sparco Steerring wheel button box.
I bought a 'All in One' conversion kit that changes Thrustmaster 6-din output to USB, complete with approriate firmware/sketch
I've had some USB bus issues, and thought this wheel might be a part of the problem, so things led to other things...

There are a number of resistors and chips in place on the Steering Wheel's board.
But if I'm only trying to read what the Teensy is seeing, does that matter?

There are a couple of guides/sketches online for a pro-micro. Also think they're using older version of the wheel, judging by the pin-out/wiring guides.
My Teensy is wired like this:
View attachment 30150
So no, understanding how the buttons and/or pots are connected, may not help much.

The pins you show that are hooked up include:
a) USB connection, probably goes to another USB connector...
b) Power: +5v and 2 GND pins
c) Program pin
d) Pins 10, 12, 13. So probably SPI. Not sure if master or slave. But as pin 11 (MOSI) not hooked up, I might guess slave.

I have not done much with the Slave code, but you might try looking at some of the libraries out there for this, like:
https://github.com/tonton81/TSPISlave

And guessing maybe the device sends out packets of data over SPI about the current state or changes... Hopefully you can find a site that someone has documented their protocol.

Good luck
Kurt
 
Thaks Kurt.

The pro-micro sketches I mentioned are from a 'Thrustmaster Conversion' search.
Further along in that I also got a SPI sketch that prints 3 bytes of data.

Code:
Free free to modify and use for your needs.
This sketch and the documentation above provided "AS IS" under the BSD New license.
http://opensource.org/licenses/BSD-3-Clause
(c) Taras Ivaniukovich (blog@rr-m.org) April 2015

#include <SPI.h>

const int slaveSelectPin = 7;

void setup() {
  Serial.begin(9600);
  SPCR |= _BV(CPHA);
  SPCR |= _BV(CPOL);
  SPI.begin();
  pinMode(slaveSelectPin, OUTPUT);
}

void loop() {
  // tell the wheel, that we gonna read the data now (capture shift registers state to start reading)
  digitalWrite(slaveSelectPin, LOW);
  
  // read 3 bytes and output them to Arduino Serial monitor as binaries 11000001 11111111 11111111
  // For TXRW 458 Italia wheel last 17 bits are buttons. 1 - released, 0 - pressed.
  for(int i=1; i<=3; i++) {
    printBinary(SPI.transfer(0x00));
  }
  Serial.println();
  
  // release the wheel
  digitalWrite(slaveSelectPin, HIGH);
 
 // wait 1 second, then read data from wheel again
  delay(1000);
}

// print byte as binary, zero padded if needed "127" -> "01111111"
void printBinary(byte data) {
  for(int i=7; i>0; i--) {
    if (data >> i == 0) {
      Serial.print("0");
    } else {
      break;
    }
  }
  
  Serial.print(data,BIN);
  Serial.print(" ");
}

which prints:

11111111 11111111 11111111

But, that is "slaveSelectPin 7", which isn't connected on my board, so probably not a correct output.
Would I modify this to 'program pin' for mine?
 
Last edited:
Insert Homer Drooling

mmmm functions!
Screenshot (1038).png

OK, cooking on gas.

Pin 10, provides functional repsonses.

12 & 13 just read 0's or 1;'s with no change on button press.

All the buttons are single momentary switches, but there is also a 5 way hat-like switch which doesn't register a 'push'. Up, Down, Left and Right all show bit changes.

I take it this means the Push is the alternate reading I was getting from #8 and #12? Or are they compeltely seperate things?

Now to tun this into something useful.
 
Now wondering about:
Code:
  Serial.begin(9600);
[COLOR="#FF0000"]  SPCR |= _BV(CPHA);
  SPCR |= _BV(CPOL);[/COLOR]
  SPI.begin();
We have some stuff in place to try to emulate the AVR register SPCR, but not to say it works overly well.

CPHA - Clock Phase.
CPOL - Clock Polarity

And I don't think we ever implemented it in the T4 port.

Wondering what would happen if you changed this code to:

Code:
  SPI.begin(); // start up SPI
  SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE3));
(AND REMOVE THOSE TWO LINES IN RED)
And see if it gives you anything different.
Note: this sets up for 4mhz transfer which I think is sort of default on AVR, MSBFIRST (maybe LSBFIRST)
MODE3 is with those two bits on I think...
 
That works as well.

I've no idea what AVR, MSBFirst/LSBFirst are though ¯\_(ツ)_/¯

If the CPHA/CPOL arent necessary then good riddance.

I'm just working through one of those pro-micro sketches to see what I can modify/bring in here.
Might also need to do some soldering, the poor board has taken a pounding.
 
Here's the Pro-Micro sketch I'm working through.

Having changed the 'slaveSelectPin', and deleted '#include <Joystick.h>'
there's only a 'setButton' that comes back as an error.

Code:
/*
 * sketch heavily based on code provided at:
 * April 2015 © blog@rr-m.org 
*/

/*
Also see comments from Ignacio Hernandez-Ros & mispeaced under https://www.youtube.com/watch?v=CJjLUAgAFnQ
* On Arduino Pro Micro side it must be connected as follows:
 * 
 * Green -> not used (or can be connected to arduino MOSI pin 16)
 * Blue - GND -> arduino uno GND pin
 * White - MISO -> arduino uno pin 14
 * Orange – SS -> arduino uno pin 7
 * Red – SCK -> arduino uno pin 15
 * Black +VCC -> arduino +5V pin (or RAW if USB current is +5V already)
 * 
* Byte 1
 * 7 - 1
 * 6 - 0
 * 5 - 0
 * 4 - 0
 * 3 - 0
 * 2 - 1
 * 1 - 0
 * 0 - Button 6
 * 
 * Byte 2
 * 7 - Button 2
 * 6 - Button 5
 * 5 - Button 9
 * 4 - 1 = Hat-Push?
 * 3 - Button 7
 * 2 - Button 13
 * 1 - Button 8
 * 0 - 1 = Hat-Push?       
 * 
 * Byte 3
 * 7 - Button 3
 * 6 - Button 10
 * 5 - Hat-Left
 * 4 - Hat-Up
 * 3 - Hat-Right
 * 2 - Hat-Down
 * 1 - Button 4
 * 0 - Button 1
*/

#include <SPI.h>


const int slaveSelectPin = 10;
byte pos[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
byte currBytes[] = {0x00, 0x00, 0x00, 0x00};
byte prevBytes[] = {0x00, 0x00, 0x00};
bool btnState, joyBtnState, prevJoyBtnState;
int bit2btn[] = {-1,-1,-1,-1,-1,-1,-1,8,  -1,2,1,-1,5,7,6,5,  4,0,11,9,12,10,3,-1};              //numbers the buttons from top left to bottom right (main is D,U,press)

void setup() {
  //input from wheel
  Serial.begin(9600);
  SPI.beginTransaction(SPISettings(1000, MSBFIRST, SPI_MODE0));
  SPI.begin();                     
  pinMode(slaveSelectPin, OUTPUT);

  //output to joystick
  Joystick.begin();
}

void loop() {
  // tell the wheel, that we are ready to read the data now
  digitalWrite(slaveSelectPin, LOW);
  
  //read the next 4 bytes (get 2's compliment)
  for(int i=0; i<4; i++) 
    currBytes[i] = ~SPI.transfer(0x00);
  
  //deal with the buttons first
  for(int i=0; i<3; i++)      //process the three bytes
    for(int b=0; b<8; b++)     //one bit at a time
      if((btnState=currBytes[i] & pos[b])!=(prevBytes[i] & pos[b]))
        [COLOR="#FF0000"]Joystick.setButton(bit2btn[(i*8)+b], btnState);      //if the bit has changed send the update     [/COLOR]
  
  joyBtnState = (currBytes[3] & pos[0]) && !(currBytes[2] & 0x3c);  
  
  if(joyBtnState != prevJoyBtnState)
    Joystick.setButton(13, joyBtnState);
  
  for(int i=0;i<3;i++)
    prevBytes[i] = currBytes[i];   //finally update the just read input to the the previous input for the next cycle

  prevJoyBtnState = joyBtnState;

  // release the wheel
  digitalWrite(slaveSelectPin, HIGH);
}

This is the error returned:

Code:
sparco: In function 'void loop()':
sparco:81: error: 'class usb_joystick_class' has no member named 'setButton'
         Joystick.setButton(bit2btn[(i*8)+b], btnState);      //if the bit has changed send the update     
                  ^
sparco:86: error: 'class usb_joystick_class' has no member named 'setButton'
     Joystick.setButton(13, joyBtnState);
              ^
'class usb_joystick_class' has no member named 'setButton'
 
Last edited:
Back
Top