Button input & inconsistent beviour

Greetings! :smiley:

Have just started to play with Arduino gadgets and have used a prototyping shield to mount momentary push buttons as per attached pix.

The buttons connect GND to input pins and seem to be working using test code based on the DigitialInputPullup code from Examples.

The test code tries to turn on the pin13 LED for a second and print text corresponding to the button pushed.

The current mystery is that there seems to be something odd regarding pin 1 in that when pressed, the pin13 LED does get turned on but the Serial.println() call doesn't seem to happen (text doesn't show up in the Serial monitor).

I've even copied the pin 1 input if() test as an extra test after the others and action and it still does not print the debug text.

Is there something about pin 1 that I'm no aware of?

Thank you in advance of any help.

Regards,
Tom.

Test code:

void setup() {
  //start serial connection
  Serial.begin(9600);
  //configure pin 2 as an input and enable the internal pull-up resistor
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  pinMode(13, OUTPUT);

}

void loop() {

  //read the pushbutton values into variables
  int butn_1_Val = digitalRead(1);
  int butn_2_Val = digitalRead(2);
  int butn_3_Val = digitalRead(3);
  int butn_4_Val = digitalRead(4);
  int butn_5_Val = digitalRead(5);
  int butn_6_Val = digitalRead(6);
  
  // REMEMBER: With pushbutton inputs set to INPUT_PULLUP, the
  // logic is inverted: LOW == pressed, HIGH == off
  //
  if ( butn_1_Val == LOW ) {
    digitalWrite(13, HIGH);
    Serial.println ();
    Serial.println (F("BUTTON __ 1 ..."));
    delay(1000);
  }
  else if (butn_2_Val == LOW) {
    digitalWrite(13, HIGH);
    Serial.println ();
    Serial.println (F("BUTTON __ 2 ..."));
    delay(1000);
  }
  else if (butn_3_Val == LOW) {
    digitalWrite(13, HIGH);
    Serial.println ();
    Serial.println (F("BUTTON __ 3 ..."));
    delay(1000);
  }
  else if (butn_4_Val == LOW) {
    digitalWrite(13, HIGH);
    Serial.println ();
    Serial.println (F("BUTTON __ 4 ..."));
    delay(1000);
  }
  else if (butn_5_Val == LOW) {
    digitalWrite(13, HIGH);
    Serial.println ();
    Serial.println (F("BUTTON __ 5 ..."));
    delay(1000);
  }
  else if (butn_1_Val == LOW) {
    digitalWrite(13, HIGH);
    Serial.println ();
    Serial.println (F("BUTTON __ 1 ..."));
    delay(1000);
  }
  else {
    digitalWrite(13, LOW);
  }
}

On e.g. the uno or mega, pin 0 and 1 are the serial interface (carrying the same information as the usb). If you use serial, you can basically not use them for something else. You might be able to use pin 0 (rx) if you do not want to receive.

Bugger! But thank you for the reply.

I was afraid there might have been something special about pin 1 (and 0 as it turns out) like this and there's no real going back to rewire what I have so far as it was a pain!

My intent for pin 1 is just to use it as an input signaller so as long as I remember the serial limitation (eg. for debiugging) so hopefully I can get away with it.

Yes, I am using the shield on a Mega but may use it with a DuinoTECH Lite (LEONARDO) with the problem maybe going away?

Cheers.

On a Leonardo, pins 0 and 1 are Serial1 and the usb communication is Serial. If you don't use Serial1, you can use it as general purpose IO; in that case your problem will be solved.

I understand that you already soldered the buttons to the pins on the shield. But it should be easy to rewire :slight_smile: You only have to rewire 1 button/pin.

Regarding your code. If you decide to rewire the button to e.g. pin A0, you will have multiple places where you have to change your code due to the use of so-called magic numbers.

It's extremely advisable to 'define' your pins in the beginning. For your current code

// button pins
const byte pinB1 = 1;
const byte pinB2 = 2;
const byte pinB3 = 3;
const byte pinB4 = 4;
const byte pinB5 = 5;
const byte pinB6 = 6;

// led pin
const byte pinLed = 13;

void setup() {
  //start serial connection
  Serial.begin(9600);
  //configure pin 2 as an input and enable the internal pull-up resistor
  pinMode(pinB1, INPUT_PULLUP);
  pinMode(pinB2, INPUT_PULLUP);
  pinMode(pinB3, INPUT_PULLUP);
  pinMode(pinB4, INPUT_PULLUP);
  pinMode(pinB5, INPUT_PULLUP);
  pinMode(pinB6, INPUT_PULLUP);
  pinMode(pinLed, OUTPUT);

}

for you to modify loop()

This way, if you ever rewire the first button, you only have to change one line.

const byte pinB1 = A0;

Or if you want to use an external led on e.g. A1 instead of the built-in led

const byte pinLed = A1;

Now, if you have pin names containing sequence numbers (pinB1, pinB2), it will be better to store them in an array.

// button pins
const byte buttonPins[] = {A0, 2, 3, 4, 5, 6};

// led pin
const byte ledPin = 13;

void setup() {
  //start serial connection
  Serial.begin(9600);

  for (int cnt = 0; cnt < sizeof(buttonPins) / sizeof(buttonPins[0]); cnt++)
  {
    pinMode(buttonPins[cnt], INPUT_PULLUP);
  }

  pinMode(ledPin, OUTPUT);
}

The construction sizeof(buttonPins) / sizeof(buttonPins[0]) calculates the number of elements in the array. One usually will write a macro for that that is independent of what the array actually contains (byte, float, struct, ...).
Note that I also renamed pinLed to ledPin, just for a little consistency.

You can also use an array to store the state of the button. We will place this in loop()

void loop()
{
  byte buttonStates[sizeof(buttonPins) / sizeof(buttonPins[0])];

  // read all buttons
  for (int cnt = 0; cnt < sizeof(buttonPins) / sizeof(buttonPins[0]); cnt++)
  {
    buttonStates[cnt] = digitalRead(buttonPins[cnt]);
  }

  // REMEMBER: With pushbutton inputs set to INPUT_PULLUP, the
  // logic is inverted: LOW == pressed, HIGH == off
  //
  if ( buttonStates[0] == LOW ) {
    digitalWrite(ledPin, HIGH);
    Serial.println ();
    Serial.println (F("BUTTON __ 1 ..."));
    delay(1000);
  }
  else if (buttonStates[1] == LOW) {
    digitalWrite(ledPin, HIGH);
    Serial.println ();
    Serial.println (F("BUTTON __ 2 ..."));
    delay(1000);
  }
  ...
  ...
  else {
    digitalWrite(ledPin, LOW);
  }
}

Usually one will use a struct or class to combine related information. E.g. a buttonState and a buttonPin have a relation (just like a name and a phone number in a phone book). But that might get a little to far for now.

A last hint (for now); because you use inverted logic, you can define a macro that translates a 'name' to LOW. The below also implements a macro for the number of elements in an array.

// for easier reading of code, define ISPRESSED for the buttons
#define ISPRESSED LOW
// number of elements in an array
#define NUMELEMENTS(x) sizeof(x)/sizeof(x[0])

// button pins
const byte buttonPins[] = {A0, 2, 3, 4, 5, 6};

// led pin
const byte ledPin = 13;

void setup() {
  //start serial connection
  Serial.begin(9600);

  for (int cnt = 0; cnt < NUMELEMENTS(buttonPins); cnt++)
  {
    pinMode(buttonPins[cnt], INPUT_PULLUP);
  }

  pinMode(ledPin, OUTPUT);
}

void loop()
{
  // array with as many elements as the buttonPins array
  byte buttonStates[NUMELEMENTS(buttonPins)];

  // read all buttons
  for (int cnt = 0; cnt < NUMELEMENTS(buttonPins); cnt++)
  {
    buttonStates[cnt] = digitalRead(buttonPins[cnt]);
  }


  // REMEMBER: With pushbutton inputs set to INPUT_PULLUP, the
  // logic is inverted: LOW == pressed, HIGH == off
  //
  if ( buttonStates[0] == ISPRESSED ) {
    digitalWrite(ledPin, HIGH);
    Serial.println ();
    Serial.println (F("BUTTON __ 1 ..."));
    delay(1000);
  }
  else if (buttonStates[1] == ISPRESSED) {
    digitalWrite(ledPin, HIGH);
    Serial.println ();
    Serial.println (F("BUTTON __ 2 ..."));
    delay(1000);
  }
  ...
  ...
  else {
    digitalWrite(ledPin, LOW);
  }
}

For the rest, it depends on what your code at the end needs to do.

I was afraid there might have been something special about pin 1 (and 0 as it turns out)

The labels printed alongside them on the Arduino board do rather give the game away.

there's no real going back to rewire what I have so far

Maybe next time prototype on a breadboard before committing to solder and wire. But you are not alone. Previously others have had PCBs produced before discovering the error of their ways.

sterretje:
On a Leonardo, pins 0 and 1 are Serial1 and the usb communication is Serial. If you don't use Serial1, you can use it as general purpose IO; in that case your problem will be solved.

I understand that you already soldered the buttons to the pins on the shield. But it should be easy to rewire :slight_smile: You only have to rewire 1 button/pin.

Yeah....nah :slightly_frowning_face: It's very crowded on the other side of the prototype shield - I'd have to unpick quite a few other soldered things. I might well do this again anyway so will know better next time.

Regarding your code. If you decide to rewire the button to e.g. pin A0, you will have multiple places where you have to change your code due to the use of so-called magic numbers. ...

Thank you heaps for the hints, may very well use them. At the moment, I'm just trying to see what works and then try to optimise the coding later.

UKHeliBob:
The labels printed alongside them on the Arduino board do rather give the game away.

Very new to this - wouldn't have understood the significance if I had noticed the labels. Actually, the labels appear on the Mega board I have but not on the prototyping shield, where those pins are just labelled 0, 1, ..., but as I said, I wouldn't have understood their significance.

Maybe next time prototype on a breadboard before committing to solder and wire. But you are not alone. Previously others have had PCBs produced before discovering the error of their ways.

Yeah, that would have revealed the issue earlier. Mistakes are a good way to learn :-*

Grommot:
Yeah....nah :slightly_frowning_face: It's very crowded on the other side of the prototype shield - I'd have to unpick quite a few other soldered things. I might well do this again anyway so will know better next time.

You need to do things in stages, rather than all at once, add a bit of hardware, write some code to see if it works, then another bit of code for another bit of I/O.
Once you know your I/O is working, then begin you code, in stages, getting each stage to work before adding another feature.
That way you fix your errors as you go, rather than have a mass of hardware and software to debug at once.
It sounds long and laborious, but it pays off in the end, and you learn as you go.
Tom... :slight_smile: