Push button controlling optocoupler weird behavior

Hi,
this is my first time here.
I'd like to know if somebody could give me a hand with the following.

I'm using a Arduino Uno board with two push-buttons (digital pins 3 & 4) to control 2 pairs of relays (a 4 module relay) (digital pins 10-13).

In addition I'm sending the button state, though Firmata to PD.

Sometimes it works Ok. But sometime not. It's too unstable. It suddenly begins to change the button state even if I'm not pushing any button.

Below I include a drawing of the schematic and the code.

Any hint will be really helpful.

Thanks in advance,

Alexis




//#include <Firmata.h>
//#include "utility/SerialFirmata.h"

const int buttonPin1 = 3; // Botón para optoacopladores 1 y 2
const int buttonPin2 = 4; // Botón para optoacopladores 3 y 4

const int opto1Pin = 10;  // Pin del primer optoacoplador
const int opto2Pin = 11;  // Pin del segundo optoacoplador
const int opto3Pin = 12;  // Pin del tercer optoacoplador
const int opto4Pin = 13;  // Pin del cuarto optoacoplador

byte optoStates[4] = {LOW, LOW, LOW, LOW}; // Estados de los optoacopladores
byte prevButtonState1 = LOW; // Estado previo del botón 1
byte prevButtonState2 = LOW; // Estado previo del botón 2

byte myArray[6]; // Array para almacenar los estados de los optoacopladores

void setup() {
 pinMode(buttonPin1, INPUT);
  pinMode(buttonPin2, INPUT);
  
 pinMode(opto1Pin, OUTPUT);
  pinMode(opto2Pin, OUTPUT);
  pinMode(opto3Pin, OUTPUT);
  pinMode(opto4Pin, OUTPUT);

  Serial.begin(9600);
 // Firmata.begin(57600);
}

void loop() {
 // Firmata.update(); // Actualizar la comunicación Firmata
  
  byte buttonState1 = digitalRead(buttonPin1);
  byte buttonState2 = digitalRead(buttonPin2);

  // Cambiar estado de optoacopladores 1 y 2 con el botón 1
  if (buttonState1 != prevButtonState1) {
    if (buttonState1 == HIGH) {
      optoStates[0] = !optoStates[0];
      optoStates[1] = !optoStates[1];
    }
    prevButtonState1 = buttonState1;
  }

  // Cambiar estado de optoacopladores 3 y 4 con el botón 2
  if (buttonState2 != prevButtonState2) {
    if (buttonState2 == HIGH) {
      optoStates[2] = !optoStates[2];
      optoStates[3] = !optoStates[3];
    }
    prevButtonState2 = buttonState2;
  }

  digitalWrite(opto1Pin, optoStates[0]);
  digitalWrite(opto2Pin, optoStates[1]);
  digitalWrite(opto3Pin, optoStates[2]);
  digitalWrite(opto4Pin, optoStates[3]);

  // Almacenar estados de optoacopladores en el array
  for (int i = 0; i < 4; i++) {
    myArray[i] = optoStates[i];
  }

  // Enviar array a través de Serial.write
  Serial.write(myArray, sizeof(myArray));

  delay(100); // Pequeña pausa para evitar rebotes de los botones
}

Your buttons are wired in a very strange way. Why do you have a resistor in series with the left hand one ?

This diagram may be helpful

Dear @UKHeliBob
I'm not sure why do I have a resistor in series there. Have I tried to avoid a short circuit actually causing it?
I'll try what you've suggested and let you know how does it goes.
Thanks for your help.

Dear @UKHeliBob I've just tried all the possibilities offered by your diagram with similar results.
S3 and S2 behave in a slightly more stable manner.
However it still does some random weird actions.
The board might actually be failing. Tomorrow I'll try another board.

Thanks again.

Best,

Alexis

Is the button on Pin 3 using a pull-down resistor, or a voltage divider?

If both buttons are to use a pull-down, try wiring the buttons like this:

PIN3--+--resistor----GND  (Pin3 is pulled low if button is open/not pressed)
      |
     /  button (normally open)
      |
      + --------------VCC (Pin3 senses HIGH when button is pressed)

buttons are typically connected between the pin and ground, the pin configured as INPUT_PULLUP to use the internal pullup resistor which pulls the pin HIGH and when pressed, the button pulls the pin LOW.

1 Like

When wired as S3, did you change the pinMode to INPUT_PULLUP?

Dear @xfpd
thanks for your help!
Tried what you've suggested with same luck to prior attemps thou.
Best,
Alexis

Dear @gcjr
thanks for your time
I did that on the code
Got a more reliable behavior but with an uncontrolled overall result
Best
Alexis

Dear @evanmars

thanks for your help
Did that as well with similar luck

Best

Alexis

how are you handling debouncing ??

dear @gcjr

what you mean by 'debouning'?

I'm using that part of the code to change (alternate between) the state of the relay according to its actual state

Hello alexis_perepelycia

Due to their mechanical and physical construction, push-buttons often generate multiple transitions when opening/closing: These transitions can be read as several actuations in a very short time and deceive the sketch.

looks like it's processing a button press
what if you did

  if (buttonState1 != prevButtonState1) {
    if (buttonState1 == HIGH) {
      optoStates[0] = !optoStates[0];
      optoStates[1] = !optoStates[1];
    }
    prevButtonState1 = buttonState1;
    delay (20);
  }

Due to mechanical factors, when you first press a button, the metallic contact often will make contact, bounce open, make contact, etc. several times. If your code is scanning quickly, these bounces may be recorded as individual button presses. Consequently, your software may see 1, 2, 3, 4, or more button close/open series, while you believe you pushed the button once. Generally, a first order fix for this is to insert a delay as others have suggested, so that your software should see the initial closure, then sense again that it is closed(therefore no change).
HTH

Dear @paulpaulson

thanks for your help.
I'm implementing a delal at the very bottom of the code : ```
delay(100);

Best,

Alexis

Dear @gcjr

thanks again.
I'll try that.
So far I've been using a delay at the end of the code : ```
delay(100);


Best,

Alexis

Dear @camsysca ,

thanks for your help.
I'm indeed using a delay at the end of the code : ```
delay(100);

Perhaps I should move that isntance to a different section of the code.

Best,

Alexis

This is a picture of a digital storage oscilloscope that can record voltage over time very very fast.
Fast enough to make visible that within 0,01 seconds the contact of a mechanical button opens / closes multiple times.

microcontroller can execute code so fast that this open/closing within 0,01 seconds is detected as multiple button-presses.

This is called bouncing.
And debouncing means write code tat takes care of this effect to filter it out to avoid "false alarms"

Here is your code with additional serial printing to make visible what is going on in your code.

This is done by a tricky so called "macro" which enables to write a single line of code that has a functionality of

  • printing the value only in case the value has CHANGED
  • printing an ID-Text
  • printing the name of the variable
  • printing the value of the variable

There are two modifications:

it used baudrate 115200 instead of 9600.
This means you have to adjust the baudrate in the serial monitor
it has a #define for the configuration as INPUT or INPUT_PULLUP
and does print the actual configuration to the serial monitor

#define inputConfiguration INPUT_PULLUP
//#define inputConfiguration INPUT

as a variant you can de-comment this line of code

//printIO_PinStates();

If you de-comment this line
it will print the IO-pins states onces EVERY second regardless if the value has changed or not

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName,timeInterval) \
  { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  }

#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

#define dbgcf(myFixedText, variableName) \
  { \
    static float lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *



//#include <Firmata.h>
//#include "utility/SerialFirmata.h"

const int buttonPin1 = 3; // Botón para optoacopladores 1 y 2
const int buttonPin2 = 4; // Botón para optoacopladores 3 y 4

const int opto1Pin = 10;  // Pin del primer optoacoplador
const int opto2Pin = 11;  // Pin del segundo optoacoplador
const int opto3Pin = 12;  // Pin del tercer optoacoplador
const int opto4Pin = 13;  // Pin del cuarto optoacoplador

byte optoStates[4] = {LOW, LOW, LOW, LOW}; // Estados de los optoacopladores
byte prevButtonState1 = LOW; // Estado previo del botón 1
byte prevButtonState2 = LOW; // Estado previo del botón 2

byte myArray[6]; // Array para almacenar los estados de los optoacopladores

#define inputConfiguration INPUT_PULLUP
//#define inputConfiguration INPUT

void setup() {
  Serial.begin(115200);
  Serial.print("io-pins defined as ");
  Serial.println(inputConfiguration);
  
  pinMode(buttonPin1, inputConfiguration);
  pinMode(buttonPin2, inputConfiguration);

  pinMode(opto1Pin, OUTPUT);
  pinMode(opto2Pin, OUTPUT);
  pinMode(opto3Pin, OUTPUT);
  pinMode(opto4Pin, OUTPUT);

  //Serial.begin(9600);
  // Firmata.begin(57600);
}

void loop() {
  // Firmata.update(); // Actualizar la comunicación Firmata

  //printIO_PinStates();
  
  byte buttonState1 = digitalRead(buttonPin1);
  // only in case the value of variable "buttonState1" has CHANGED print ONCE
  dbgc("0A", buttonState1);

  byte buttonState2 = digitalRead(buttonPin2);
  // only in case the value of variable "buttonState2" has CHANGED print ONCE
  dbgc("0B", buttonState2);

  // Cambiar estado de optoacopladores 1 y 2 con el botón 1
  if (buttonState1 != prevButtonState1) {
    dbgc("0C", prevButtonState1);
    if (buttonState1 == HIGH) {
      optoStates[0] = !optoStates[0];
      dbgc("0D", optoStates[0]);

      optoStates[1] = !optoStates[1];
      dbgc("0E", optoStates[1]);
    }
    prevButtonState1 = buttonState1;
  }

  // Cambiar estado de optoacopladores 3 y 4 con el botón 2
  if (buttonState2 != prevButtonState2) {
    dbgc("2C", prevButtonState2);
    if (buttonState2 == HIGH) {
      optoStates[2] = !optoStates[2];
      dbgc("2D", optoStates[2]);
      optoStates[3] = !optoStates[3];
      dbgc("2E", optoStates[3]);
    }
    prevButtonState2 = buttonState2;
  }

  digitalWrite(opto1Pin, optoStates[0]);
  digitalWrite(opto2Pin, optoStates[1]);
  digitalWrite(opto3Pin, optoStates[2]);
  digitalWrite(opto4Pin, optoStates[3]);

  // Almacenar estados de optoacopladores en el array
  for (int i = 0; i < 4; i++) {
    myArray[i] = optoStates[i];
  }

  // Enviar array a través de Serial.write
  //Serial.write(myArray, sizeof(myArray));

  //delay(100); // Pequeña pausa para evitar rebotes de los botones
}


void printIO_PinStates() {
  // print value of digitalRead(buttonPin1) only ONCE per second
  dbgi("IO1",digitalRead(buttonPin1),1000);
  dbgi("IO2",digitalRead(buttonPin2),1000);
}

best regards Stefan