C language - Simultaneous button push & Switch

Hey guys,

I'm having trouble with the c language programing for my arduino board. I am trying to make it so that when I push button1, a servo motor will move one way. When I push button 2, a servo motor will move in the opposite direction. Finally if you push both buttons simultaneously it will reset the servo motor back to a center position.

I'm not sure if my coding is right at all but have a look. I tried if/else statements at first and that seemed to work when I pushed button1 before button2 "simultaneously" but I want the part to work both ways.

If someone can get back to me on this, that would be great. Thanks

#include <Servo.h>

const int buttonPin = 2;
const int motorPin = 3;
const int buttonPin2 = 4;

int buttonState = 0;
int buttonState2 = 0;

byte hh = 0;
byte hl = 0;
byte lh = 0;
byte ll = 0;
byte check = 0;

Servo myservo;
void setup() {
pinMode(buttonPin, INPUT);
pinMode(motorPin, OUTPUT);
pinMode(buttonPin2, INPUT);
Serial.begin(9600);
myservo.attach(3);
myservo.write(90);
}
void loop() {
buttonState = digitalRead(buttonPin);
buttonState2 = digitalRead(buttonPin2);
hh = digitalRead(buttonPin)==HIGH && digitalRead(buttonPin2)==HIGH;
hl = digitalRead(buttonPin)==HIGH && digitalRead(buttonPin2)==LOW;
lh = digitalRead(buttonPin)==LOW && digitalRead(buttonPin2)==HIGH;
ll = digitalRead(buttonPin)==LOW & digitalRead(buttonPin2)==LOW;
check = digitalRead(buttonPin) && digitalRead(buttonPin2);
switch (check){
case hh:
myservo.write(90);
break;

case hl:
for (int i= myservo.read(); i>= 0; i-=1) {
myservo.write(i);
delay(45);
Serial.println(digitalRead(buttonPin));
if (i == 0) i = 180;
if (digitalRead(buttonPin)== LOW) break;
}

case lh:
for (int i= myservo.read(); i<= 180; i+=1){
myservo.write(i);
delay(45);
Serial.println(digitalRead(buttonPin2));
if (i == 180) i = 0;
if (digitalRead(buttonPin2)== LOW) break;
}

case ll:
break;

}

}

For starters:

Each time through the loop, you should read the buttons once and then make the decision.

If you are going to use a switch statement, you have to combine the button press information into something that has four different values, since there are four different courses of action for the four possible button conditions.

One possible way:

void loop()
{
    
    buttonState  = digitalRead(buttonPin);
    buttonState2 = digitalRead(buttonPin2);
    // Combine into a single int to use in a switch statement
    // buttonState  buttonState2    state
    //     0             0           0
    //     0             1           1
    //     1             0           2
    //     1             1           3
    //
    int state = (buttonState << 1) | buttonState2;

    switch (state) {

    case 0:
        // Both are zero.
        // Do whatever is required for this condition.
        break;

    case 1:
        // buttonState is zero and buttonState2 is one
        // Do whatever is required for this condition;
        break;

    case 2:
        // buttonState is one and buttonState2 is zero
        // Do whatever is required for this condition;
        break;

    case 3:
        // Both are one.
        // Do whatever is required for this condition;
        break;

    }
    // Do whatever you need to do after handling the buttons.


}

Note: Debouncing may be required. "Simultaneous" requirements may impose additional delay considerations.

Regards,

Dave

You don't say how you've got your buttons wired or what sort of buttons they are, but I notice you're not using the built-in pullups.
This may cause you problems.

Hey guys, I'm definitely not skilled in the c-language as its been a couple years since I have taken a programming class so bear with me (I know basic c programming, so the bitwise operators are still somewhat foreign to me since I have not used them enough). I tried something what davekw7x was doing but am still having trouble with the bit information. I put a serial print to see what values were coming out and I am only getting cases of 1 and 2 but not 0 or 3 of when I try all four different combinations. The program now seems to ignore the other button when I push a button.

AWOL: I'm not sure what you mean by built-in pullups. My buttons only have two wire connections coming out of them, they are push buttons. One end I have the 5v source and the other I have a resister to ground connection in parallel with input pins to the arduino board.

My other idea I was having a problem with was to figure out that if I push one of the buttons, that as long as it is held down, the program will continuously check to see that the 2nd button is pushed whether button1 is held down first or button 2 is in order to get the "simultaneous" action when button1 and button2 are both pushed down within each other's time interval.

Basically my criteria is:

  • On power up (void setup()), have the servo go to the center of its motion.

  • Pressing and holding down Button1 will cause servo to rotate clockwise at approximately 90 degrees per 5 seconds (18 degrees/second). When I release Button1, the servo should remain at that position.

  • Pressing and holding down Button2 does the same thing but moves in counter-clockwise direction instead.

  • The last criteria is to reset the motor to its center position by pressing both buttons down simultaneously.

davekw7x,

for this tid bit
int state = (buttonState << 1) | buttonState2;

wouldn't "&" be better in this case? For buttonState I would assume that I would get values of 1 and 2 while buttonState2 would give me 0 and 1 and with those 4 values I can get a range from 0 to 3 for the switch/cases.

I'm not sure what you mean by built-in pullups.

A bias resistor can be added externally as you've done, but the ATmega MCU used in the Arduino also has a handy feature that lets you activate built-in pull-ups inside the MCU itself to save you having to add your own.

There's more information here:

Simple inputs: buttons, switches- aht0button

Jon
Practical Arduino: www.practicalarduino.com

@T86157:

for this tid bit
int state = (buttonState << 1) | buttonState2;

wouldn't "&" be better in this case?

Each of the button variables can have a value of either zero or one.

Work it out with pencil and paper for all possible combinations of values (make a table).

Don't have a pencil? Don't have any paper? Heck, let the computer do it:

void setup()
{
    Serial.begin(9600);           
}

void loop()
{
    Serial.println("a   b   c");
    Serial.println("---------");

    for (int a = 0; a < 2; a++) {
        for (int b = 0; b < 2; b++) {
            int c = (a << 1) | b;
            Serial.print(a);
            Serial.print("   ");
            Serial.print(b);
            Serial.print("   ");
            Serial.println(c);
        }
    }
    Serial.println();
    delay(10000);
}

Output:


a   b   c
---------
0   0   0
0   1   1
1   0   2
1   1   3

Regards,

Dave

Thanks, davekw7x. I tested what you wrote in a separate program and worked fine. I figured out what was wrong with my program. It's nearly impossible to get both buttons on HIGH simultaneously to execute a function so I had at state 0 when both push buttons are on LOW so that there would be a slight delay. That way if two buttons were pressed at the same time, it would read it exactly after that delay.
I set my delay to about 250 ms but sometimes it is a bit faulty and the program misses the simultaneous part. I wish I knew a better way to read simultaneous buttons before it executes a function since mine seems very crude.

Also for some reason my switch/case was not working with the bitwise operators so I went back to if/else statements and it works okay.

'Simultaneous' is a difficult concept to convey when you're talking about mechanical devices (ie switches) and microprocessors that execute a single instruction in the time it takes a beam of light to travel 20 metres.

I wish I knew a better way to read simultaneous buttons before it executes a function since mine seems very crude.

The trick is to use an algorithm a bit like debouncing:
First button press detected.
Ignore whatever button is pressed.
Wait 100 to 250 ms (depends on what feels responsive and tolerant enough)
Check what buttons are pressed now after the delay.
Two buttons --> double button press function
One button --> correct single button press function

This way, you delay the execution of the function for one button a little, but you get instead a fair chance at detecting a double button event.

By the way, that will take care of debouncing the buttons too, so you won't have to worry about that either.

Korman

I set my delay to about 250 ms

If a program delay of 250 ms is satisfactory and your definition of simultaneous means that the all changes will occur with in 250 ms of the first change, then maybe try something like the following.

const int bounce_delay = 250;
int old_state = 0; // Both buttons up
void loop()
{

    buttonState  = digitalRead(buttonPin);
    buttonState2 = digitalRead(buttonPin2);

    // Combine into a single int to use in a switch statement
    // buttonState  buttonState2    state
    //     0             0           0
    //     0             1           1
    //     1             0           2
    //     1             1           3
    //
    int state = (buttonState << 1) | buttonState2;
    
    if (state != old_state) { // Something changed: debounce and execute
    
        delay(bounce_delay); // Whatever fits your idea of simultaneous
        state = (buttonState << 1) | buttonState2;
        old_state = state; // For comparison at next time

        switch (state) {

          case 0:
            // Both are zero.
            // Do whatever is required for this condition.
            break;

          case 1:
            // buttonState is zero and buttonState2 is one
            // Do whatever is required for this condition;
            break;

          case 2:
            // buttonState is one and buttonState2 is zero
            // Do whatever is required for this condition;
            break;

          case 3:
            // Both are one.
            // Do whatever is required for this condition;
            break;

        }
    }
    // Do whatever you need to do after handling the buttons.
}

Regards,

Dave

Thanks guy. Got it up and running last night.

Instead of making a new thread I have a new question and problem for anyone who reads this

I am trying to read inputs from Serial.read(). I figured out that if I enter in any value, the program reads it as decimal. That's not the problem (or it might be) but I'm converting any number I enter into the serial monitor into decimal value (i.e. I enter 9 into the serial monitor and the program reads it as 57 so I subtract 48 to convert my desired value from ascii into dec)

I'm having trouble reading numbers greater than 10. I thought that having an if/else statement embedded into another if/else statement with it Serial.read() reading the first value in the queue, the 2nd embedded value will be read by the next if/else statement but it doesn't seem to work.

Here's my code to clarify.

  else if(reader == 57){
    if(reader == 57){
      if(reader == 57){
      Serial.println("System deactivated");
      buttonPushCounter++;
      delay(1000);
//   or

else if(Serial.read() == 57){ /*the Serial.read() does not work but if I assign an integer such as reader = Serial.read(), it reads it and displays it on the serial monitor*/
if(Serial.read() == 57){
if(Serial.read() == 57){
Serial.println("System deactivated");
buttonPushCounter++;
delay(1000);
}
}
}

If I enter in 999 into the serial monitor it should have a certain function but what happens is the program is reading any 9 that I enter in whether it's just "9" or "99" or "09" or "90", etc. My guess is that it's not reading the next number in queue.

Again, I want to read all values from 0 - 999 but only 0 - 180 will be used along with 999. The rest should display on the Serial Monitor as an error.

It might be easier if you quit thinking in terms of 57, and think in terms of '9' (or 48 -> '0'). Six months from now, you won't remember what 57 means, but '9' will always be obvious.

As to the problem with reading numbers greater than 10, remember that a numb er like 157 is sent as '1', '5', and '7'. You need to read all the serial data, store the bytes in an array (NULL terminated after every addition), and convert the array to a number (using atoi).

Sending start of packet and end of packet markers will make collecting and parsing the serial data a lot easier. Search the forum for "started && ended" for an example of how to read data with start and end markers, and use the resulting data.

Alright I understand a little about the array and the atoi part. I am trying to figure out how to store 3-digit values in one element. Like the example you provided of 157 where it's sent as '1' '5' and '7' is there a way to put all three of those in one element?

What if I didn't use the array and just wanted to do a straight conversion which then can lead into if/else statements using the serial to int? How would it look?

For the code below I was playing around with a tutorial and was able to convert a single serial to an integer. If i did double digits, again it would print them individually like you said. I thought there was a way that could accumulate the numbers and so you can get however many digits you enter in. (i.e. '99' will convert to 99, '123' will convert to 123, etc.)

char incomingByte, *p;
int x = 0;

void setup() {
Serial.begin(9600);
9600 bps
}

void loop() {


if (Serial.available() > 0) {

incomingByte = Serial.read();
*p = incomingByte;
x = atoi(p);


Serial.print("I received: ");

Serial.println(x);

}
}

this code only does single digits

Like the example you provided of 157 where it's sent as '1' '5' and '7' is there a way to put all three of those in one element?

Yes and no. If by one element, you mean something like an int, then, no, not directly. If by one element, you mean an array of chars, then, yes.

We need to go back to your original question.

I am trying to read inputs from Serial.read().

Where is this data coming from, and what triggers that source to send data?

Serial data transmission is asynchronous. This means that the sender does not care that the receiver is ready to receive data. It sends data when it has data to send.

It also means that the sender needs to present the data in a way that the receiver can tell how to deal with the data it receives.

If the sender is going to send 4 values, 157, 213, 101, and 89, as characters, and includes no separators, the data stream will look like this: "15721310189".

Given a stream like that, it is impossible to reconstruct the data from the sender.

If the data is sent like "157;213;101;89;" instead, then the receiver has a hope of reconstructing the data.

Given this stream of data, the receiver would read every character, until it received a ';'. It could store the data in an array:

char inData[10];
byte index = 0;
int inChar = '';

void loop()
{
   while(Serial.available() && inChar != ';')
   {
      inChar = Serial.read();
      if(inChar != ';')
      {
          inData[index] = inChar;
          index++;
          inData[index] = '\0';
      }
   }

   if(inChar == ';')
   {
       int val = atoi(inData);

       index = 0;
       inData[index] = '\0';
       inChar = '';
   }
}

There are ways of reading the data and constructing the int without the need for an array. A string like '1', '5', '7' gets read, one character at a time. Each time a character is read, it's integer value is determined, by subtracting '0', and the value being constructed is multiplied by 10, and the new integer value read is added.

int val = 0;
Read a character, '1'. '1' - '0' = 1. val * 10 = 0. val + 1 = 1.
Read a character, '5'. '5' - '0' = 5. val * 10 = 10. val + 5 = 15.
Read a character, '7'. '7' - '0' = 7. val * 10 = 150. val + 7 = 157.

It should be easy enough to write the code to do this.

this code only does single digits

If you read just one digit, the atoi-part will return just values between 0 and 9. No big surprise here.

You should first read all characters and only convert them once you have them all. There are many ways to do it, most very simple.

Korman

Thank you very much, Pauls. I'll try it out.

Curious, how did most of you forum posters learn C language or programming for that matter?

Curious, how did most of you forum posters learn C language or programming for that matter?

I get paid for it.

Korman

I get paid for it.

Korman

Before you even learned it? :wink:

Lefty

Strangely enough, I did learn C whilst being paid to program in it.
No big deal; I already knew Algol and PL/M (look 'em up, young uns), so the transition was quite simple.