reasonable sonar readings not triggering actions

hello , I am doing an installation for a local arts festival which involves a portaloo rigged up with 4 lv-ez1 sonars , 3 facing externally to trigger single sounds and one very basic "air therimin" inside. the readings in the serial monitor are pretty accurate but the desired actions : Keybaord.printing some letters which will play notes on a sample player are not occurring except in the case of the internal sonar module. here is the code I have.

const int pingPin = 4;
const int pingPin1 = 2;
const int pingPin2 = 13;
const int pingPin3 = 7;
int newinchvalue;
int oldinchvalue;
int newinchvalue1;
int oldinchvalue1;
int newinchvalue2;
int oldinchvalue2;
void setup() {
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{
  // establish variables for duration of the ping, 
  // and the distance result in inches and centimeters:
  long duration, inches, cm , duration1, inches1, cm1, duration2, inches2, cm2, duration3, inches3, cm3;


  // The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
  // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);

  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);

  delay (20);



  // The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
  // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
  pinMode(pingPin1, OUTPUT);
  digitalWrite(pingPin1, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin1, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin1, LOW);

  pinMode(pingPin1, INPUT);
  duration1 = pulseIn(pingPin1, HIGH);

  newinchvalue = inches;

  if (abs(oldinchvalue - newinchvalue) > 2.00)//the value has changed
  {
    if (newinchvalue > 2.00 && newinchvalue <= 30.00) //and it is in range
    {

      Keyboard.print ('d');
      oldinchvalue = newinchvalue;

    }
  }





  delay (20);

  pinMode(pingPin2, OUTPUT);
  digitalWrite(pingPin2, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin2, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin2, LOW);



  pinMode(pingPin2, INPUT);
  duration2 = pulseIn(pingPin2, HIGH);

  newinchvalue = inches2;

  if (abs(oldinchvalue1 - newinchvalue1) > 2.00)//the value has changed
  {
    if (newinchvalue1 > 2.00 && newinchvalue1 <= 30.00) //and it is in range
    {

      Keyboard.print ('f');
      oldinchvalue1 = newinchvalue1;
      //save value for next time
    }
  }

  delay (20);

  pinMode(pingPin3, OUTPUT);
  digitalWrite(pingPin3, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin3, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin3, LOW);

  pinMode(pingPin3, INPUT);
  duration3 = pulseIn(pingPin3, HIGH);

  newinchvalue2 = inches3;

  if (abs(oldinchvalue2 - newinchvalue2) > 2.00)//the value has changed
  {
    if (newinchvalue2 > 2.00 && newinchvalue2 <= 30.00) //and it is in range
    {

      Keyboard.print ('g');
      oldinchvalue2 = newinchvalue2;
      //save value for next time
    }
  }




  // The same pin is used to read the signal from the PING))): a HIGH
  // pulse whose duration is the time (in microseconds) from the sending
  // of the ping to the reception of its echo off of an object.


  // convert the time into a distance
  inches = microsecondsToInches(duration);
  cm = microsecondsToCentimeters(duration);

  inches1 = microsecondsToInches(duration1);
  cm1= microsecondsToCentimeters(duration1);


  inches2 = microsecondsToInches(duration2);
  cm2= microsecondsToCentimeters(duration2);

  inches3 = microsecondsToInches(duration3);
  cm3= microsecondsToCentimeters(duration3);

  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();

  Serial.print(inches1);
  Serial.print("in, ");
  Serial.print(cm1);
  Serial.print("cm");
  Serial.println();

  Serial.print(inches2);
  Serial.print("in, ");
  Serial.print(cm2);
  Serial.print("cm");
  Serial.println();

  Serial.print(inches3);
  Serial.print("in, ");
  Serial.print(cm3);
  Serial.print("cm");
  Serial.println();


  if (inches >16.00 && inches <=10.00) {

    Keyboard.print ('h');


  }


  if (inches >17.00 && inches <=20.00) {
    Keyboard.print ('j');


  }

  if (inches >21.00 && inches <=30.00) {
    Keyboard.press(KEY_CAPS_LOCK);
    Keyboard.print ('k');


  }


  delay(100);
}

long microsecondsToInches(long microseconds)
{
  // According to Parallax's datasheet for the PING))), there are
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
  // second).  This gives the distance travelled by the ping, outbound
  // and return, so we divide by 2 to get the distance of the obstacle.
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return microseconds / 74 / 2;
}

long microsecondsToCentimeters(long microseconds)
{
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.
  // The ping travels out and back, so to find the distance of the
  // object we take half of the distance travelled.
  return microseconds / 29 / 2;




}

.

don't really know why this is the case the abs statements work fine in tests where I have used them with just one sensor also the pingpin 4 air therminin seems to print only k and j. To explain the function of the if statements using abs is to insure the sound is triggered only once. it is an audiosample of someone singing a long note so it being repeated qiukly would sound odd ; we assume people will not be too pedantic in their interaction with this piece and will break the beam at a point more than two inches away from the previous brake as it is really designed to trigger when people walk past.

alternately I would be very grateful if anyone could explain how to code a keyboard.press keyboard.release type functionality. I orginally wanted to use this instead of the value comparison abs methodolgy but found that statements like

if (inches >0.00 && inches <=20.00) {
    Keyboard.press ('j');


  }

  if (inches >21.00 && inches <=30.00) {
  
    Keyboard.release ('j');

would simply result in j being pressed down the first time the former condition was met and not being released when the latter condition was met? at first I thought this was a peculiarity particular to sonar (not a very considered opinion but it was the one I held).

but found the same problem with a reed switch , substituting inch ranges for LOW and HIGH? this would actually be preferable to getting the abs if statements to work so i would be very grateful to anyone who could point out what I'm doing wrong here.

cheers

sam

unsigned long pingRange (byte pingPin)
{
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);

  pinMode(pingPin, INPUT);
  return pulseIn(pingPin, HIGH);
}

A single function makes stuff a lot easier to debug and write

The write function (which print would call) does this:

size_t Keyboard_::write(uint8_t c)
{	
	uint8_t p = press(c);		// Keydown
	uint8_t r = release(c);		// Keyup
	return (p);					// just return the result of press() since release() almost always returns 1
}

So you can see that a write/print is just a press/release pair.

In much the same way that you "use" the A on your keyboard by pressing and then releasing it.

  if (inches >21.00 && inches <=30.00) {
    Keyboard.press(KEY_CAPS_LOCK);
    Keyboard.print ('k');

You never release caps lock. Is this intentional? It looks to me from the code that you should be able to "OR" in a shift modifier, eg.

  if (inches >21.00 && inches <=30.00) {
    Keyboard.print (SHIFT | 'k');

I haven't used a single function for two reasons: one without labelling my pingpins ping pin-pingpin3 I am unsure how to differentiate 4 different inchvaules from them and secondly I was worried that without a delay of 20 between each pulse in that my sensors would start to read each others sound emissions which I've read and been told by people on this forum is quite common.

caps lock does need to remain pressed. I am using the caps lock keyboard in logic pro to trigger sounds and have found oddly that turning it on with the capslock on my macs keyboard and then printing capital letters via Arduino does not cause note to be played , the shift does seem like a less messy solution though.

I think I do understand the keyboard.write function (just had to look up returns) but am not entirely sure how I can use this solve my problem , should I maybe use returns in my if/release statement.

thank you very much , sam

Don't just throw in "return" without understanding what it does. Maybe a read of a C tutorial would help?

yeah it definitely would have been meaning to do so for a while but I've been constantly engaged with projects this summer. I can see how write and print are both comprised off press and release , I only thought of adding a return because the problem I seem to be having when trying

if (inches >0.00 && inches <=20.00) {
    Keyboard.press ('j');


  }

  if (inches >21.00 && inches <=30.00) {
  
    Keyboard.release ('j');

is that j won't "terminate". Do you know of a web series or tutorial site which would allow me gain an understanding of this particular machination relatively quickly ?

Perhaps if you explain a bit more.

You want "j" to be pressed when they get close, and released once they are no longer close?

So something like (pseudo code).

if is_now_close and NOT have_pressed_J then
  press "j"
  have_pressed_J = true
end 

if NOT is_now_close and have_pressed_J then
  release "j"
  have_pressed_J = false
end

I'll let you translate that into C.

yes thats it exactly , i'll try and convert that into C myself and get back to you soon.

thanks for helping out. it's basically so the note is sustained when the audience is correct proximity to the piece and ceases when they move out of that proximity.

cheers

sam

I haven't used a single function for two reasons: one without labelling my pingpins ping pin-pingpin3 I am unsure how to differentiate 4 different inchvaules from them and secondly I was worried that without a delay of 20 between each pulse in that my sensors would start to read each others sound emissions which I've read and been told by people on this forum is quite common.

The first issue can be addressed using arrays.

The second issue is a non-issue. If you are worried, anyway, put a delay(20); in the function.

I don't think you're really considering the sequence of events here (which is why the simple function I posted above would allow you to see the wood from the trees).
In "loop()" you take a range from the first Ping sensor and call the result "duration", then you take a range from the second sensor and call it "duration1".
Then you assign the value of "newinchvalue" from "inches"...but you haven't given "inches" a value yet.

okay i've read the array reference page and converted the pseudo code to c. I was a bit confused at first until I realised it was the same system of control used in the makey makey arduino code on the the arduino blog , although I will admit that that realisation was probably obstructed me comprehending it fully (which I will rectify in time probably on the grounds of necessity)

My sonars are currently installed in the project (locked up today) so I've been testing on a potentiometer.

from the reference page I thought that this might form an array but i was wrong

const int pingpin[4] = {2, 4, 13, 7,};
int newinchvalue;
int oldinchvalue;
int newinchvalue1;
int oldinchvalue1;
int newinchvalue2;
int oldinchvalue2;
void setup() {
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{
  // establish variables for duration of the ping, 
  // and the distance result in inches and centimeters:
   long duration, inches, cm , duration1, inches1, cm1, duration2, inches2, cm2, duration3, inches3, cm3;
  

  // The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
 // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
  

  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  return pulseIn(pingPin, HIGH);
pinMode(pingPin, INPUT);
  
  duration = pulseIn(pingpin [0] , HIGH);
  
  delay (20);
  

duration1 = pulseIn(pingpin [1] , HIGH);
delay (20);


duration2 = pulseIn(pingpin [2] , HIGH);
delay (20);

duration3 = pulseIn(pingpin [3] , HIGH);
delay (20);

on the pusedo conversion front I found that this worked

boolean pressed = false;

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

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // print out the value you read:
  Serial.println(sensorValue);
  delay(1);        


if ( sensorValue >= 750 && sensorValue  <= 800)  {   
    if (!pressed){
      
     Keyboard.press ('d');
      Keyboard.press(KEY_CAPS_LOCK);
     pressed = !pressed;   
    }  

    
  }
  if (sensorValue  >810) {     
    if (pressed){ 
      Keyboard.release ('d'); 
      
      pressed = !pressed;   
    }     
  }
}

but this did not , I thought this would cause d to release whenever the value was out of the condiction band but instead it caused it to only appear , be pressed , once and remain so.

boolean pressed = false;

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

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // print out the value you read:
  Serial.println(sensorValue);
  delay(1);        


if ( sensorValue >= 750 && sensorValue  <= 800)  {   
    if (!pressed){
      
     Keyboard.press ('d');
      Keyboard.press(KEY_CAPS_LOCK);
     pressed = !pressed;   
    }  

    
  }
  if (!sensorValue >= 750 && sensorValue  <= 800) {     
    if (pressed){ 
      Keyboard.release ('d'); 
      
      pressed = !pressed;   
    }     
  }
}

I also tried this as a method of creating two zones to press different keys , this was wildy ineffective and caused D to appear repeatedly without delay across my screen.

boolean pressed = false;

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

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // print out the value you read:
  Serial.println(sensorValue);
  delay(1);        


if ( sensorValue >= 750 && sensorValue  <= 800)  {   
    if (!pressed){
      
     Keyboard.press ('d');
      Keyboard.press(KEY_CAPS_LOCK);
     pressed = !pressed;   
    }  
}

if ( sensorValue >= 700 && sensorValue  <= 749)  {   
    if (!pressed){
      
     Keyboard.press ('g');
      Keyboard.press(KEY_CAPS_LOCK);
     pressed = !pressed;   
    }  
    
  }
  if (sensorValue >= 700 && sensorValue  <= 749) {     
    if (pressed){ 
      Keyboard.release ('d'); 
        pressed = !pressed;  
       
    }
  } 
         
  
if ( sensorValue >= 700 && sensorValue  <= 749)  {   
    if (!pressed){
      
     Keyboard.press ('g');
      Keyboard.press(KEY_CAPS_LOCK);
     pressed = !pressed;  
     
    }
}

if ( sensorValue >= 700 && sensorValue  <= 749) {     
    if (pressed){ 
      Keyboard.release ('g'); 
        pressed = !pressed;   
        
    }
}
}

I will carry on experimenting and trying to understand what i'm actually doing but any advice in the meantime would be great. thank you both you've been very helpful.

void loop()
{
// establish variables for duration of the ping,
// and the distance result in inches and centimeters:
long duration, inches, cm , duration1, inches1, cm1, duration2, inches2, cm2, duration3, inches3, cm3;

// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse:

pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
pinMode(pingPin, INPUT);
return pulseIn(pingPin, HIGH);
pinMode(pingPin, INPUT);

Nothing after the return statement is executed. Ever!

ah I see , I haven't made any progress on the array front but will report back when I do , This has solved the problem of creating mutually exclusive and contiguous key-press zones on my potentiometer tests

boolean pressed = false;
boolean pressed1 = false;
void setup() {

  Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue1 = analogRead(A0);
  int sensorValue2 = analogRead(A0);
  Serial.println(sensorValue1);
  Serial.println(sensorValue2);
  delay(1);        


  if ( sensorValue1 >= 750 && sensorValue1  <= 800)  {   
    if (!pressed){

      Keyboard.press ('d');
      Keyboard.press(KEY_CAPS_LOCK);
      pressed = !pressed;   
    }  


  }
  if (sensorValue1 > 810 ||sensorValue2 >= 650 && sensorValue2  <= 700 ||sensorValue1 < 649 ) {     
    if (pressed){ 
      Keyboard.release ('d'); 

      pressed = !pressed;   
    }     
  }
  if ( sensorValue2 >= 650 && sensorValue2  <= 700)  {   
    if (!pressed1){

      Keyboard.press ('g');
      Keyboard.press(KEY_CAPS_LOCK);
      pressed1 = !pressed1;   
    }  
  }
  if (sensorValue2 > 810 || sensorValue1 >= 750 && sensorValue1  <= 800 ||sensorValue2 < 649 ) {     
    if (pressed1){ 
      Keyboard.release ('g'); 

      pressed1 = !pressed1;   

    }
  }
}

so I will use this approach on my sonar , I can't think of any reason why this wouldn't work in my sonar code?

I will now try and learn more about arrays.

cheers

  if ( sensorValue1 >= 750 && sensorValue1  <= 800)
  if (sensorValue1 > 810 ||sensorValue2 >= 650 && sensorValue2  <= 700 ||sensorValue1 < 649 ) 
  if ( sensorValue2 >= 650 && sensorValue2  <= 700)
  if (sensorValue2 > 810 || sensorValue1 >= 750 && sensorValue1  <= 800 ||sensorValue2 < 649 )

How many of these statements are going to be true? If only one from each set, the other should be an else if.