Ultrasonic sensor as keyboard - pause the keystrokes until distance has changed

Hey guys,

I have an ultrasonic sensor that works as a keyboard. Whenever it senses that something is closer than 10 cm, it presses a button. However, it continues to press the button over and over again until the object before the sensors moves out of the way.

I would like to make the sensor press a button once when something is closer than 10cm, and then stop. Only when the object before the sensor moved out of range and came into range again, does the button get pressed again.

Also, i would like to make it so whenever all sensors are activated at the same time, they're pressing a whole different key.

Thanks in advance!

Here's the code:

#include <Keyboard.h>

int trigPin1=8;
int echoPin1=7;

int trigPin2=10;
int echoPin2=9;

int trigPin3=6;
int echoPin3=5;

int trigPin4=12;
int echoPin4=11;

int trigPin5=10;
int echoPin5=11;

void setup() {
 Serial.begin (9600);
 pinMode(trigPin1, OUTPUT);
 pinMode(echoPin1, INPUT);
  pinMode(trigPin2, OUTPUT);
 pinMode(echoPin2, INPUT);
  pinMode(trigPin3, OUTPUT);
 pinMode(echoPin3, INPUT);
  pinMode(trigPin4, OUTPUT);
 pinMode(echoPin4, INPUT);
  pinMode(trigPin5, OUTPUT);
 pinMode(echoPin5, INPUT);
 Keyboard.begin();

}

void loop() {
 long duration1, distance1;
 digitalWrite(trigPin1, LOW);  // Added this line
 delayMicroseconds(2); // Added this line
 digitalWrite(trigPin1, HIGH);
 delayMicroseconds(10); // Added this line
 digitalWrite(trigPin1, LOW);
 duration1 = pulseIn(echoPin1, HIGH);
 distance1 = (duration1 * .0343) / 2;

  if (distance1 <= 10){
   Keyboard.press('a');
   Keyboard.release('a');
 }
 else {
   Serial.print ( "Sensor1  ");
   Serial.print ( distance1);
   Serial.println("cm");
 }
 //delay(250);
long duration2, distance2;
 digitalWrite(trigPin2, LOW);  // Added this line
 delayMicroseconds(2); // Added this line
 digitalWrite(trigPin2, HIGH);
 delayMicroseconds(10); // Added this line
 digitalWrite(trigPin2, LOW);
 duration2 = pulseIn(echoPin2, HIGH);
 distance2 = (duration2 * .0343) / 2;

  if (distance2 <= 10){
   Keyboard.press('b');
   Keyboard.release('b');
 }
 else {
   Serial.print("Sensor2  ");
   Serial.print(distance2);
   Serial.println("cm");
 }
 //delay(250);
 long duration3, distance3;
 digitalWrite(trigPin3, LOW);  // Added this line
 delayMicroseconds(2); // Added this line
 digitalWrite(trigPin3, HIGH);
 delayMicroseconds(10); // Added this line
 digitalWrite(trigPin3, LOW);
 duration3 = pulseIn(echoPin3, HIGH);
 distance3= (duration3 * .0343) / 2;

  if (distance3 <= 10){
   Keyboard.press('c');
   Keyboard.release('c');
 }
 else {
   Serial.print("Sensor3  ");
   Serial.print(distance3);
   Serial.println("cm");
 }
 //delay(250);

 long duration4, distance4;
 digitalWrite(trigPin4, LOW);  // Added this line
 delayMicroseconds(2); // Added this line
 digitalWrite(trigPin4, HIGH);
 delayMicroseconds(10); // Added this line
 digitalWrite(trigPin4, LOW);
 duration4 = pulseIn(echoPin4, HIGH);
 distance4= (duration4 * .0343) / 2;

  if (distance4 <= 10){
   Keyboard.press('d');
   Keyboard.release('d');
 }
 else {
   Serial.print("Sensor4  ");
   Serial.print(distance4);
   Serial.print("cm");
 }
 //delay(250);


 long duration5, distance5;
 digitalWrite(trigPin5, LOW);  // Added this line
 delayMicroseconds(2); // Added this line
 digitalWrite(trigPin5, HIGH);
 delayMicroseconds(10); // Added this line
 digitalWrite(trigPin5, LOW);
 duration5 = pulseIn(echoPin5, HIGH);
 distance5= (duration5/2) / 29.1;

  if (distance5 >= 500 || distance5 <= 0){
   Serial.println("Out of range");
 }
 else {
   Serial.print("Sensor5  ");
   Serial.print(distance5);
   Serial.println("cm");
 }
 delay(100);

}

Have a look at the state change detect tutorial.

Your states are "in range" vs "not in range" but the principle's exactly correct.

If currentState != previousState, and currentState is in range, that's a new arrival, do key thing.

Save current to previous, and next time through loop() they won't be different, so no repeat key.

Then eventually when it goes out of range and comes back, Voila.

12Stepper:
Have a look at the state change detect tutorial.

Your states are "in range" vs "not in range" but the principle's exactly correct.

If currentState != previousState, and currentState is in range, that's a new arrival, do key thing.

Save current to previous, and next time through loop() they won't be different, so no repeat key.

Then eventually when it goes out of range and comes back, Voila.

Unfortunately I don't have the knowledge to translate that tutorial into my code. Would you be willing to show in code what it would look like?

bananaboard:
Would you be willing to show in code what it would look like?

Based on yours, here's a standalone sketch (no keyboard stuff) and only one sensor. It prints the message "Newly in range" only once each time it comes in range; has to go back out and re-enter before it will print again.

//  https://forum.arduino.cc/index.php?topic=651762
//  4 dec 2019

int trigPin1 = 8;
int echoPin1 = 7;

void setup()
{
  Serial.begin(9600);
  Serial.println("ultrasonic in and out of range 651762");
  Serial.println(__FILE__);

  pinMode(trigPin1, OUTPUT);
  pinMode(echoPin1, INPUT);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  Serial.println("setup() done");
  Serial.println(" ");

} //setup

void loop()
{
  long duration1, distance1;
  static long distance1Previous;
  digitalWrite(trigPin1, LOW);  // Added this line
  delayMicroseconds(2); // Added this line
  digitalWrite(trigPin1, HIGH);
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin1, LOW);
  duration1 = pulseIn(echoPin1, HIGH);
  distance1 = (duration1 * .0343) / 2;
  //Serial.print ( "Sensor1  ");
  //Serial.print ( distance1);
  //Serial.print("cm");
  if (distance1 <= 10) //in range...
  {
    if (distance1Previous > 10) //.. but was out of range before
      Serial.println("Newly in range");
  }
  distance1Previous = distance1;
} //loop

12Stepper:
Based on yours, here's a standalone sketch (no keyboard stuff) and only one sensor. It prints the message "Newly in range" only once each time it comes in range; has to go back out and re-enter before it will print again.

//  https://forum.arduino.cc/index.php?topic=651762

//  4 dec 2019

int trigPin1 = 8;
int echoPin1 = 7;

void setup()
{
  Serial.begin(9600);
  Serial.println("ultrasonic in and out of range 651762");
  Serial.println(FILE);

pinMode(trigPin1, OUTPUT);
  pinMode(echoPin1, INPUT);

pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

Serial.println("setup() done");
  Serial.println(" ");

} //setup

void loop()
{
  long duration1, distance1;
  static long distance1Previous;
  digitalWrite(trigPin1, LOW);  // Added this line
  delayMicroseconds(2); // Added this line
  digitalWrite(trigPin1, HIGH);
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin1, LOW);
  duration1 = pulseIn(echoPin1, HIGH);
  distance1 = (duration1 * .0343) / 2;
  //Serial.print ( "Sensor1  ");
  //Serial.print ( distance1);
  //Serial.print("cm");
  if (distance1 <= 10) //in range...
  {
    if (distance1Previous > 10) //.. but was out of range before
      Serial.println("Newly in range");
  }
  distance1Previous = distance1;
} //loop

Thank you 12Stepper! Works flawlessly. Do you also know how to achieve my second question? When all four sensors are active, they press a new key? The idea is that each sensors trigger a different video clip thats projected on a wall. However when all sensors are used, a whole new video starts playing.

You can "and" things together with a double &&:

if (distance4 <= 10 && distance3<=10)
{

... kind of thing.

12Stepper:
You can "and" things together with a double &&:

if (distance4 <= 10 && distance3<=10)

{




... kind of thing.

I did something like this, but it doesn't seem to work. Just trigger the other letters from each sensor:

if (distance1 <= 10 && distance2 <= 10 && distance3 <= 10 && distance4 <=10)
   {
    if (distance1Previous > 10 && distance2Previous > 10 && distance3Previous > 10 && distance4Previous > 10)
    Keyboard.press('e');
    Keyboard.release('e');
  }
  {
    distance1Previous = distance1;
    distance2Previous = distance2;
    distance3Previous = distance3;
    distance4Previous = distance4;
  }

Well you presumably are still doing the existing tests.

You may have to get clever on the existing ones to test for sensor 1< and sensor 2> etc, so that the individual ones don't fire in the case where all are >.

You will need a strategy to exclude the ones you don't want.

You could (and this so off the top my head it's in the clouds (and it's pissing with rain here)) do the test for all first, then set a boolean flag say allAreInRange as true. Then not do the individual ones if allAreInRange.

I don't have 4 ultrasonics, but I have buttons. And I have leds.

The code below has 4 buttons and 5 leds. When any button is pressed, "its" led comes on.

But if all buttons are pressed, their own leds go off and led5 comes on.

A pressed button is analogous to an item being in range of one of your sensors.

Of course, on the way to becoming all pressed, when any 1, 2 or 3 buttons are pressed, the individual leds are on; as soon as the 4th is pressed, the other 3 leds go out and 5 comes on.

But my code shows the use of a flag, allButtonsArePressed, to switch between the two modes.

edit, ps: ! means "not"

// inpired by https://forum.arduino.cc/index.php?topic=651762
// 4 dec 2019
// each button turns its own light on
//    if all 4 are pressed, a 5th light comes on
//       but NOT the individual ones


const byte button1 = 2;
const byte button2 = 3;
const byte button3 = 4;
const byte button4 = 5;

const byte led1 = 14;
const byte led2 = 15;
const byte led3 = 16;
const byte led4 = 17;
const byte led5 = 13; //on board

bool allButtonsArePressed = false;

void setup()
{
  Serial.begin(9600);
  Serial.println("651762");
  Serial.println(__FILE__);

  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  pinMode(led4, OUTPUT);
  pinMode(led5, OUTPUT);

  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);
  pinMode(button4, INPUT_PULLUP);

  Serial.println("setup() done");
  Serial.println(" ");

} //setup

void loop()
{
  if (!digitalRead(button1) && !digitalRead(button2) && !digitalRead(button3) && !digitalRead(button4))
  {
    allButtonsArePressed = true;
    digitalWrite(led5, HIGH);
    digitalWrite(led1, LOW);
    digitalWrite(led2, LOW);
    digitalWrite(led3, LOW);
    digitalWrite(led4, LOW);
  }
  else
  {
    allButtonsArePressed = false;
    digitalWrite(led5, LOW);
  }

  if (!allButtonsArePressed)
  {
    if (!digitalRead(button1)) digitalWrite(led1, HIGH); else digitalWrite(led1, LOW);
    if (!digitalRead(button2)) digitalWrite(led2, HIGH); else digitalWrite(led2, LOW);
    if (!digitalRead(button3)) digitalWrite(led3, HIGH); else digitalWrite(led3, LOW);
    if (!digitalRead(button4)) digitalWrite(led4, HIGH); else digitalWrite(led4, LOW);
  }
} //loop

If all 4 are pressed (led 5 on alone) and any one is released, led5 goes off and the approriate individual ones come on.

I haven't included a test for a button to be newly pressed (= an object just having come into range).

12Stepper:
quote

I don't know what I'm doing... Now it's just spamming the e button as soon as I connect it to the computer.
I want it to work like the other sensors. One key press and wait until object moves and reappers in front of the four sensors again.

#include <Keyboard.h>

int trigPin1=8;
int echoPin1=7;

int trigPin2=10;
int echoPin2=9;

int trigPin3=6;
int echoPin3=5;

int trigPin4=12;
int echoPin4=11;

int trigPin5=10;
int echoPin5=11;

bool allSensorsAreActive = false;

void setup() {
  
  Serial.begin (9600);
  Serial.println("ultrasonic in and out of range 651762");
  Serial.println(__FILE__);
  
  pinMode(trigPin1, OUTPUT);
  pinMode(echoPin1, INPUT_PULLUP);
  
  pinMode(trigPin2, OUTPUT);
  pinMode(echoPin2, INPUT_PULLUP);
  
  pinMode(trigPin3, OUTPUT);
  pinMode(echoPin3, INPUT_PULLUP);
  
  pinMode(trigPin4, OUTPUT);
  pinMode(echoPin4, INPUT_PULLUP);
  
  pinMode(trigPin5, OUTPUT);
  pinMode(echoPin5, INPUT_PULLUP);
  
  Keyboard.begin();

  Serial.println("setup() done");
  Serial.println(" ");

}

void loop() 
  {
    if (!digitalRead(echoPin1) && !digitalRead(echoPin2) && !digitalRead(echoPin3) && !digitalRead(echoPin4))
    {
    allSensorsAreActive = true;
    Keyboard.release('a') && Keyboard.release('b') && Keyboard.release('c') && Keyboard.release('d');
    Keyboard.press('e');
    Keyboard.release('e');
  }
  else
  {
    allSensorsAreActive = false;
    Keyboard.release('e');
  }
  if (!allSensorsAreActive)
  {
   if (!digitalRead(echoPin1)) Keyboard.press('a') && Keyboard.release('a'); else Keyboard.release('a');
   if (!digitalRead(echoPin1)) Keyboard.press('b') && Keyboard.release('b'); else Keyboard.release('b');
   if (!digitalRead(echoPin1)) Keyboard.press('c') && Keyboard.release('c'); else Keyboard.release('c');
   if (!digitalRead(echoPin1)) Keyboard.press('d') && Keyboard.release('d'); else Keyboard.release('d');
   
  }
  

  
  static long distance1Previous;
  digitalWrite(trigPin1, LOW);  // Added this line
  delayMicroseconds(2); // Added this line
  digitalWrite(trigPin1, HIGH);
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin1, LOW);
  duration1 = pulseIn(echoPin1, HIGH);
  distance1 = (duration1 * .0343) / 2;

   if (distance1 <= 10)
   {
    if (distance1Previous > 10)
      Keyboard.press('a');
      Keyboard.release('a');
  }
   {
    distance1Previous = distance1;
  }
  //delay(250);
  
long duration2, distance2;
  static long distance2Previous;
  digitalWrite(trigPin2, LOW);  // Added this line
  delayMicroseconds(2); // Added this line
  digitalWrite(trigPin2, HIGH);
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin2, LOW);
  duration2 = pulseIn(echoPin2, HIGH);
  distance2 = (duration2 * .0343) / 2;

   if (distance2 <= 10)
   {
    if (distance2Previous > 10)
      Keyboard.press('b');
      Keyboard.release('b');
  }
  {
    distance2Previous = distance2;
  }
  //delay(250);
  
  long duration3, distance3;
  static long distance3Previous;
  digitalWrite(trigPin3, LOW);  // Added this line
  delayMicroseconds(2); // Added this line
  digitalWrite(trigPin3, HIGH);
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin3, LOW);
  duration3 = pulseIn(echoPin3, HIGH);
  distance3= (duration3 * .0343) / 2;

   if (distance3 <= 10)
   {
    if (distance3Previous > 10)
      Keyboard.press('c');
      Keyboard.release('c');
  }
  {
    distance3Previous = distance3;
  }
  //delay(250);

  long duration4, distance4;
  static long distance4Previous;
  digitalWrite(trigPin4, LOW);  // Added this line
  delayMicroseconds(2); // Added this line
  digitalWrite(trigPin4, HIGH);
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin4, LOW);
  duration4 = pulseIn(echoPin4, HIGH);
  distance4= (duration4 * .0343) / 2;

   if (distance4 <= 10)
   {
   if (distance4Previous > 10)
      Keyboard.press('d');
      Keyboard.release('d');
  }
  {
    distance4Previous = distance4;
  }
  //delay(250);


  

   if (distance1 <= 10 && distance2 <= 10 && distance3 <= 10 && distance4 <=10)
   {
    if (distance1Previous > 10 && distance2Previous > 10 && distance3Previous > 10 && distance4Previous > 10)
    Keyboard.press('e');
    Keyboard.release('e');
  }
  {
    distance1Previous = distance1;
    distance2Previous = distance2;
    distance3Previous = distance3;
    distance4Previous = distance4;
  }
  delay(100);

}

There's no point you just reading the echo pin like it's a button. You have to read the ech pin like before as part of the trigger/echo process....

!digitalRead(echoPin1)

I didn't say my buttons were replacements for your pins; I said a pressed button was analogous to an object in range. Don't read too much into analogies.

12Stepper:
There's no point you just reading the echo pin like it's a button. You have to read the ech pin like before as part of the trigger/echo process....

!digitalRead(echoPin1)

I didn't say my buttons were replacements for your pins; I said a pressed button was analogous to an object in range. Don't read too much into analogies.

I've only just begun to learn arduino, so I don't know what you mean here.

long duration5, distance5;
  static long distance5Previous;
  digitalWrite(trigPin1 && trigPin2 && trigPin3 && trigPin4, LOW);  // Added this line
  delayMicroseconds(2); // Added this line
  digitalWrite(trigPin1 && trigPin2 && trigPin3 && trigPin4, HIGH);
  delayMicroseconds(10); // Added this line
  digitalWrite(trigPin1 && trigPin2 && trigPin3 && trigPin4, LOW);
  duration5 = pulseIn(echoPin1 && echoPin2 && echoPin3 && echoPin4, HIGH);
  distance5= (duration5 * .0343) / 2;

   if (distance5 <= 10)
   {
   if (distance5Previous > 10)
      Keyboard.press('e');
      Keyboard.release('e');
  }
  {
    distance5Previous = distance5;

This is what I tried to add, to make it so that when all of them are triggered, it goes into its own trigger/echo process... The code errored though. I'm not sure how to proceed.

You appear to be using Pin 10 and Pin 11 for two different sensors each
trigPin5 == trigPin2 == Pin 10
echoPin5 == echoPin4 == Pin 11

You should also add a timeout for pulseIn(), otherwise it will wait a full second (a MILLION microseconds) for each sensor when there are no targets in range. A timeout of 30000UL microseconds (30 milliseconds) should be more than enough. You may also need to add a delay between sensors to avoid the echo from the previous sensor triggering the current sensor.

johnwasser:
You appear to be using Pin 10 and Pin 11 for two different sensors each
trigPin5 == trigPin2 == Pin 10
echoPin5 == echoPin4 == Pin 11

You should also add a timeout for pulseIn(), otherwise it will wait a full second (a MILLION microseconds) for each sensor when there are no targets in range. A timeout of 30000UL microseconds (30 milliseconds) should be more than enough. You may also need to add a delay between sensors to avoid the echo from the previous sensor triggering the current sensor.

I removed the trigPin5 and echoPin5 since I only have four sensors for now. Still nothing. When all sensors are covered nothing happens.

Where would i put the timeout? After each...?

duration5 = pulseIn(echoPin1 && echoPin2 && echoPin3 && echoPin4, HIGH);
  distance5= (duration5 * .0343) / 2;

The timeout is the (optional) third argument to pulseIn():

duration5 = pulseIn(echoPin1 && echoPin2 && echoPin3 && echoPin4, HIGH);[iurl=https://www.arduino.cc/reference/en/language/functions/advanced-io/pulsein/][/iurl]
  distance5= (duration5 * .0343) / 2;

No, you can't AND a bunch of pin numbers together and expect pulseIn() will be able to figure out that you mean... whatever it was you meant.

When you have three or more variables with the same name except for a trailing number, it's time to learn about arrays. When you have several such variables, all with the same numbers, it's time to learn about structures anbd arrays of structures. Here is your sketch written with an array of structures. Note how by using an array you can use a 'for' loop instead of writing almost the same code four times.

#include <Keyboard.h>


struct USProxSensor
{
  const byte trigPin;
  const byte echoPin;
  bool inProximity;
  bool wasInProximity;
  int distance;
} Sensors[] =
{
  {8, 7, false, false, 0},
  {10, 9, false, false, 0},
  {6, 5, false, false, 0},
  {12, 11, false, false, 0}
};
const char ChannelLetters[] = "abcd";
const char AllChannelsLetter = 'X';
bool AllChannelsWereInProximity = false;


const byte SensorCount = sizeof Sensors / sizeof Sensors[0];


void setup()
{
  Serial.begin (9600);
  for (int i = 0; i < SensorCount; i++)
  {
    pinMode(Sensors[i].trigPin, OUTPUT);
    pinMode(Sensors[i].echoPin, INPUT);
  }
  Keyboard.begin();
}


void loop()
{
  int proximityCount = 0;
  // Read all of the distances
  for (int i = 0; i < SensorCount; i++)
  {
    unsigned long duration;
    delay(30); // Avoid late echos from previous sensor
    digitalWrite(Sensors[i].trigPin, LOW);
    delayMicroseconds(2); // Added this line
    digitalWrite(Sensors[i].trigPin, HIGH);
    delayMicroseconds(10); // Added this line
    digitalWrite(Sensors[i].trigPin, LOW);
    duration = pulseIn(Sensors[i].echoPin, HIGH, 30000UL);
    Sensors[i].distance = (duration * .0343) / 2;  // Convert to cm


    Sensors[i].inProximity = Sensors[i].distance > 0 && Sensors[i].distance <= 10;


    if (Sensors[i].inProximity)
      proximityCount++;


    if (Sensors[i].inProximity != Sensors[i].wasInProximity)
    {
      // State has changed
      Sensors[i].wasInProximity = Sensors[i].inProximity;


      if (Sensors[i].inProximity)
      {
        // State changed to 'inProximity'
        Keyboard.press(ChannelLetters[i]);
        Keyboard.release(ChannelLetters[i]);
      }
      else  // Moved out of proximity
      {
        Serial.print ( "Sensor");
        Serial.print(i + 1);
        Serial.print ( "  ");
        Serial.print (Sensors[i].distance);
        Serial.println("cm");
      }
    }
  }


  if (proximityCount == SensorCount)
  {
    // ALL CHANNELS SHOW PROXIMITY
    if (!AllChannelsWereInProximity)
    {
      AllChannelsWereInProximity = true;
      Keyboard.press(AllChannelsLetter);
      Keyboard.release(AllChannelsLetter);
    }
  }
  else
    AllChannelsWereInProximity = false;
}