3

I have a keypad which is connected to arduino (with one analog pin).In the arduino code, I have created a circular queue which stores the inputs by pressing on keypad. Now when a key is pressed on keypad, it is stored as the first element in the queue. If an another key is pressed, it is stored as second element in the queue until is queue is full.(The keypad pins are connected using a voltage divider for each pin, so each and every key pressed on keypad will be recognized by arduino based on voltage for the key. The keys and voltages are stored in an array, as follows):

int keyboardPin = 0;    // analog pin to which keypad is connected
int keyboardValue = 0;  // Initial reading of analog pin 0 (A0) (it is zero as no key is pressed initially)
char keypad[16] = {'1','2','3','4','5','6','7','8','9'};
int treshold[16] = {590,568,548,426,415,404,334,326,320}; // Volatge values corresponding to each pin
int front=0,rear=0,size=6; // implementing circular queue using array
char cQueue[6];
int speed = 6,timer=0;
void setup(){
  cQueue[0] = '1'; // the first element of queue is '1' by default at the start
}
void loop(){
  keyboardValue = analogRead(keyboardPin); // Reading the value from A0 when key is pressed
  while(keyboardValue > 5) {  // Runs when a key is pressed
    for(int i=0;i<8;i++){
      if((treshold[i] - keyboardValue) == 0){
        Serial.println(keypad[i]); // Printing the key pressed
        enQueue(keypad[i]); // Storing the key pressed in the queue
        break;
      }
    break; 
    } // end of for
  keyboardValue = 0;
  liftStops(cQueue[front+1]);// function to be run in background (front + 1) because  the key pressed by the user should be the argument of function
  break;   
  } // end of while
}        // end of loop
int isFull(){  // to check if queue is full
  if( (f1 == r1 + 1) || (f1 == 0 && r1 == stops1-1)) {
  return 1;
}
  return 0;
}
void enQueue(char element){  // adding key pressed to queue
  if(isFull()){
    Serial.println("Lift full");
  }else{
    r1 = (r1 + 1) % stops1;
    qlift1[r1] = element;
  }
}
int deQueue(){  // removing the front element of queue
  char element;
  element = qlift1[f1];
  f1 = (f1 + 1) % stops1;
  return(element);
}   

At the start of program, 1st element of queue is loaded with '1' (in setup function). When key is pressed, it is added as 2nd element of the queue.

There is a function which should run in the background as soon as a key is pressed. The function is:

void liftStops(char keyPress){  // defined after the loop() function
  while(timer<100){
    distance = (speed*timer);
    motorClockwise();
    Serial.println(distance);
    if(keyPress == '1' && distance == 0){
      motorStop();
      deQueue();
      return;
    }else if(keyPress == '2' && distance == 30){
      motorStop();
      deQueue();
      return;
    }else if(keyPress == '3' && distance == 60){
      motorStop();
      deQueue();
      return;
    }else if(keyPress == '4' && distance == 90){
      motorStop();
      deQueue();
      return;
    }
    timer = timer+0.167;
    delay(200);
  }
}

The element hold indicated by front variable in the queue is passed as a parameter to the liftStops() function. There is a motor in the circuit and the motorClockwise() rotates motor clockwise,motorStop() will stop the motor.

When the program is run for first time , if I press '2', it is stored as second element in the queue. The function liftStops() starts with '2' as its argument. Now the liftStops() function runs till distance == 30 and motor stops. Now deQueue() function is called and the element indicated by front gets eliminated('1' gets eliminated in the above case). After this,if I press '3', liftStops() runs till distance == 60 and the element indicated by front gets eliminated('2' in this case).

If I press a key when the liftStops() execution didn't finish, nothing is added to queue. I want the liftStops() function to run in background so that each and every key I press at any point of time gets stored in queue and the liftStops() function runs for every element in queue one after another.

Thanks.

2
  • 1
    If you use millis() instead of delay() (see the Blink Without Delay Arduino tutorial), then you won't need to run anything in the background. Jun 20, 2017 at 13:13
  • Thanks for the reply. I have tried a lot using millis(), but I couldn't achieve the desired output.
    – Sri Harsha
    Jun 20, 2017 at 15:38

4 Answers 4

3

I wrote in a comment to your question:

If you use millis() instead of delay() [...], then you won't need to run anything in the background.

and you answered:

I have tried a lot using millis(), but I couldn't achieve the desired output.

From this, I assume the real problem is that you haven't tried hard enough. The general approach is quite simple:

  • Is there any work that has to be performed right now?
    • if yes, then just do it
    • if no, then don't do it

It works as long as you do not have any blocking code in your program. Especially no calls to delay().

In your particular case, it would seem that liftStops() has work only when:

  • timer is less than 100
  • and it hasn't done useful work during the past 200 ms (that's the delay(200) in your code, and it's not useful work).

You may try the following:

void liftStops(char keyPress) {

    // Manage the execution timing.
    static uint32_t last_time;
    uint32_t now = millis();
    if (timer >= 100 || now - last_time < 200)
        return;  // nothing to do this time
    last_time = now;

    // Do the actual work.
    distance = (speed*timer);
    motorClockwise();
    Serial.println(distance);
    if(keyPress == '1' && distance == 0){
      motorStop();
      deQueue();
      return;
    }else if(keyPress == '2' && distance == 30){
      motorStop();
      deQueue();
      return;
    }else if(keyPress == '3' && distance == 60){
      motorStop();
      deQueue();
      return;
    }else if(keyPress == '4' && distance == 90){
      motorStop();
      deQueue();
      return;
    }
    timer = timer+0.167;
}

Just make sure it is called often. I.e. you call it in loop() and you make sure there is no blocking code elsewhere in your program.

0
1

Typically this is done via an interrupt.

The use of an analog keyboard makes that more difficult or less convintional as the detection of a press in the analog sense is more difficult. One way to go is likely to scan the keyboard on a constant rate. But then detecting releases needs to be done creatively.

4
  • Thanks for the reply. I think it's easy the other way. The arduino runs the liftStops() function in the background and always checks if any key is pressed,stores it in queue and run the liftStops() for every function in queue.
    – Sri Harsha
    Jun 20, 2017 at 15:41
  • 1
    You probably want to think through it more. The term "background" can be confusing. It generally means a high priority short task that can interrupt other low priority tasks.
    – dannyf
    Jun 21, 2017 at 11:33
  • Your code has a serial print in it and then delay. That's the sort of things that you should not put in the interrupt.
    – dannyf
    Jun 21, 2017 at 11:34
  • 1
    Obviously the whole thing is moot if you have a different distribution of "background".
    – dannyf
    Jun 21, 2017 at 11:34
1

Use loop function already provided for repetitive tasks

 void loop() {
 //this itself should be the function which you want keep running
 //check for keystrokes here and handle characters as they are
 //received. For handling characters, you can write a function outside
 //of the main loop which should return control to loop when task is
 //finished
 delay(200);
 }
1

I have solved my problem using attachInterrupt() function.The loop() runs liftStops() continuously and if there is a keyPress, it is recorded by the interrupt.

void setup(){
  attachInterrupt(digitalPinToInterrupt(3), floor1, CHANGE);
}
loop(){
  liftStops();
}
void liftStops(){
  if(qlift1[f1+1] != NULL){
    while(timer<100){
      distance = (speed*timer);
      motorClockwise();
      Serial.println(distance);
      if(qlift1[f1+1] == '1' && distance == 0){
        motorStop();
        deQueue();
        delay(5000);
        return;
      }else if(qlift1[f1+1] == '2' && distance == 30){
        motorStop();
        deQueue();
        delay(5000);
        return;
      }else if(qlift1[f1+1] == '3' && distance == 60){
        motorStop();
        deQueue();
        delay(5000);
        return;
      }else if(qlift1[f1+1] == '4' && distance == 90){
        motorStop();
        deQueue();
        delay(5000);
        return;
      }else if(qlift1[f1+1] == '5' && distance == 120){
        motorStop();
        deQueue();
        delay(5000);
        return;
      }else if(qlift1[f1+1] == '6' && distance == 150){
        motorStop();
        deQueue();
        delay(5000);
        return;
      }
      timer = timer+0.167;
      delay(200);
    }
  }
}
void floor1(){
  keyboardValue = analogRead(keyboardPin);
  while(keyboardValue > 5) {  // Runs when a key is pressed
    for(int i=0;i<8;i++){
      if((treshold[i] - keyboardValue) == 0){
        Serial.println(keypad[i]); // Printing the key pressed
        enQueue(keypad[i]); // Storing the key pressed in the queue
        break;
      }
    break; 
    } // end of for
  keyboardValue = 0;
  break;   
  } // end of while
}

If any key is pressed during delay(5000) even that gets recorded.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.