Arduino, Processing and 2 HC-SR04 Ultrasonic Sensor problems. Please help....

Hi there,

I am in desperate need of help. I am doing a school project using processing, Arduino with 2 ultrasonic sensors and what I am trying to do is change between 7 images by using the sensors (left or right) unfortunately I haven’t been able to achieve this as I am really new to Arduino and processing and I don’t understand where I am going wrong.

The two sensors will be set apart so that when the user approaches the left or right sensor the image changes either going back to the previous image or go to the next image. I want to trigger the change when the distance is between 50 and 60 cm so that the images don’t get triggered when you are too close or too far.

The codes that I am using are bellow. Please take a look and help solve this. I only have 4 days to do it plus evaluate it.

Thank you so so much I am very grateful to you all.

Arduino code:

#include <NewPing.h>

int generalDist = 50;

int TRIGGER_PIN1 = 2 ; // Arduino pin tied to trigger pin on the ultrasonic sensor.
int ECHO_PIN1 = 4 ; // Arduino pin tied to echo pin on the ultrasonic sensor.
int TRIGGER_PIN2 = 7 ; // Arduino pin tied to trigger pin on the ultrasonic sensor.
int ECHO_PIN2 = 8 ; // Arduino pin tied to echo pin on the ultrasonic sensor.
//#define TRIGGER_PIN 12 // Arduino pin tied to trigger pin on the ultrasonic sensor.
//#define ECHO_PIN 11 // Arduino pin tied to echo pin on the ultrasonic sensor.

int MAX_DISTANCE1 = 200 ;// Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

int MAX_DISTANCE2 = 200 ;// Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
//#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
int dist1=0;
int dist2=0;

unsigned int uS;
int val= 0;

//int Delay_Ms = 300; //delay value for setting tempo
// int range = 0;

NewPing sonar1(TRIGGER_PIN1, ECHO_PIN1, MAX_DISTANCE1); // NewPing setup of pins and maximum distance.
NewPing sonar2(TRIGGER_PIN2, ECHO_PIN2, MAX_DISTANCE2); // NewPing setup of pins and maximum distance.

void setup() {
Serial.begin(9600); // Open serial monitor at 115200 baud to see ping results.
delay(500); // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
uS = sonar1.ping(); // Send ping, get ping time in microseconds (uS).

MAX_DISTANCE1 = ((uS / US_ROUNDTRIP_CM)); // Convert ping time to distance in cm and print result (0 = outside set distance range)
delay(100);

uS = sonar2.ping(); // Send ping, get ping time in microseconds (uS).
MAX_DISTANCE2 = ((uS / US_ROUNDTRIP_CM)); // Convert ping time to distance in cm and print result (0 = outside set distance range)
delay(500);
}

void loop() {
delay(100);
int uS = sonar1.ping(); // Send ping, get ping time in microseconds (uS).

dist1 = (uS / US_ROUNDTRIP_CM); // Convert ping time to distance in cm and print result (0 = outside set distance range)

delay (200);
uS = sonar2.ping(); // Send ping, get ping time in microseconds (uS).
dist2 = (uS / US_ROUNDTRIP_CM); // Convert ping time to distance in cm and print result (0 = outside set distance range)

//range = map(dist, 0, 50, 15, 0);

// Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
//int uS = sonar1.ping(); // Send ping, get ping time in microseconds (uS).
// Serial.print(" ");
delay (500);
Serial.write(uS / US_ROUNDTRIP_CM); // Convert ping time to distance in cm and print result (0 = outside set distance range)
//Serial.print("L");
Serial.write(dist1);

if (dist1<generalDist){
Serial.println(1);
}
else{
Serial.println(0);
}

//Serial.println ("");
//int uS = sonar2.ping(); // Send ping, get ping time in microseconds (uS).
//Serial.print(dist);
delay (100);
Serial.write(uS / US_ROUNDTRIP_CM); // Convert ping time to distance in cm and print result (0 = outside set distance range)
//Serial.print("R");
Serial.write(dist2);

if (dist2<generalDist){
Serial.println(2);
}
else{
Serial.println(0);
}
Serial.println(dist2);

}

Processing Code:
import processing.serial.*;

// curent state
int currentState=0;

// images and text to display
ArrayList displayImage= new ArrayList();

int sensorValue=0;
int sensorMin=1023;
int sensorMax=0;

int leftSensorPin=4; // the left sensor pin

int rightSensorPin=8; // the right sensor pin

boolean leftSensorTriggered=false;
boolean rightSensorTriggered=false;

Serial myPort; // Create object from Serial class
int val=0; // Data received from the serial port

void setup()
{
size(900, 700);

displayImage.add( loadImage("1.jpg"));
displayImage.add(loadImage("2.jpg"));
displayImage.add(loadImage("3.jpg"));
displayImage.add(loadImage("4.jpg"));
displayImage.add(loadImage("5.jpg"));
displayImage.add(loadImage("6.jpg"));
displayImage.add(loadImage("7.jpg"));

// etc for other images

// I know that the first port in the serial list on my mac
// is always my FTDI adaptor, so I open Serial.list()[0].
// On Windows machines, this generally opens COM1.
// Open whatever port is the one you're using.
String portName = Serial.list()[0];

println(portName);
myPort = new Serial(this, portName, 9600);

}

void draw()
{

byte[] inBuffer = new byte[2];
while (myPort.available()> 0) {
inBuffer = myPort.readBytes();
myPort.readBytes(inBuffer);
if (inBuffer != null) {
String myString = new String(inBuffer);
println(myString);

}
}

delay (500);
if ( myPort.available() > 0)

{ // If data is available,
val = myPort.read(); // read it and store it in val
}

if (currentState<displayImage.size()) {
image(displayImage.get(currentState), 0, 0);
}

int dist = 20;

println(val);
//--------------------------------- Left Sensor Triggered ---------------------------------------
if (!leftSensorTriggered && val < dist) {
// not previously triggered - but triggered this time
// i.e. just triggered

leftSensorTriggered = true ;
currentState= currentState +1;
// need to check if we're at the end:

if (currentState>= displayImage.size()) {
currentState=0;
}
}
if (leftSensorTriggered && val < dist) {
// was triggered, but now isn't
leftSensorTriggered=false;
}
//---------------------------------Right Sensor Triggered ---------------------------------------

if (!rightSensorTriggered && val < dist) {
// not previously triggered - but triggered this time
// i.e. just triggered

rightSensorTriggered = true ;
currentState= currentState -1;
// need to check if we're at the end:
if (currentState>= displayImage.size()) {
currentState=-1;
}
}
if (rightSensorTriggered && val < dist) {
// // was triggered, but now isn't
leftSensorTriggered=false;
}

}

PS only the sensor on TRIGGER_PIN2 = 7 ; ECHO_PIN2 = 8; is working properly and the other one doesnt work.

Thank you again and i am sorry for the long essay :slight_smile:

void setup() {
  Serial.begin(9600); // Open serial monitor at 115200 baud to see ping results.
  delay(500);                      // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
  uS = sonar1.ping(); // Send ping, get ping time in microseconds (uS).

  MAX_DISTANCE1 = ((uS / US_ROUNDTRIP_CM)); // Convert ping time to distance in cm and print result (0 = outside set distance range)
  delay(100);   

  uS = sonar2.ping(); // Send ping, get ping time in microseconds (uS).
  MAX_DISTANCE2 = ((uS / US_ROUNDTRIP_CM)); // Convert ping time to distance in cm and print result (0 = outside set distance range)
  delay(500);   
}

It's rather useless to ping anything in setup(). Why are you?

  Serial.write(uS / US_ROUNDTRIP_CM); // Convert ping time to distance in cm and print result (0 = outside

Serial.write() is for sending binary data.

You need to make up your mind what you are sending to Processing. Some delimiters between the values makes it easier to parse the data. Right now, you are sending "xx1xx2nnn" where x is a binary value and nnn is the distance, as a string, that the second ping got, and are carriage return and line feed.

In Processing, you are reading all the data available on the serial port, which may or may not be a full packet, and storing that data in a 2 element array. The first two bytes are binary data that you then convert to a String. You do nothing with this String except print it, with no indication what the String is.

Then, you may or no read anything, depending on how much of the packet had already been read.

You are doing all this in draw(), which is wrong.

Make your Arduino program send something like "1, dist1, 2, dist2",

Make your Processing sketch bufferUntil() the \n arrives. Add a serialEvent() method that gets called when the complete packet has arrived. Read ALL the data, as a String. Then, split the String at the commas, to create an array. If there are 4 values in the array, set global variables, that draw() uses, to useful parts of the array.

I do want to send the distance as 1 or 2 for dist1 and dist2 and want it to be sent as bytes to processing and then trigger the changes.

If you measure the distance, why not send that value? Just sending 1 seems useless.

Why do you want the data sent as bytes, when you read as strings?

If you do this:

Serial.print(1);
Serial.print(",");
Serial.print(dist1);
Serial.print(2);
Serial.print(",");
Serial.println(dist2);

Processing could receive something like "1,45,2,34".

Testing leftSensorTriggered or rightSensorTriggered is pointless, as you are getting (incorrectly now, but the above code will fix that) data from both sensors.

What you need to test is which value is smaller. That indicates which side the object being sensed (presumably a person) is closer to.

The values that you get do not distinguish between the values this time and the values last time. You need to store the current readings as the previous readings before updating the current readings. Then, the Processing side needs to compare the current reading with the previous reading to determine whether the object has moved closer, farther away, or stayed still. You need to do that for each side.

Lets work on getting the Arduino side getting, and sending, good data, correctly, only when it needs to. Then, we can work on getting Processing to collect and read that data. Then, we can work on getting Processing to use the data.

So what do I do with data now that both sensors printing out the distances?

Well, the first thing you need is another Serial.print() between dist1 and 2, to add a comma.

Then, in Processing, you need a serialEvent() function and some changes in setup():

String portName = Serial.list()[0];

println(portName);
myPort = new Serial(this, portName, 9600);
myPort.bufferUntil('\n');

void serialEvent(Serial myPort)
{
  boolean validString = true;  // whether the string you got is valid

  // read the serial buffer:
  String myString = myPort.readStringUntil('\n');

  // make sure you have a valid string:
  if (myString != null)
  {
     print("Serialized data: ");
     println(myString);

     // Use the function to convert the string to an array of ints
     int[] nums = int(split(myString, '''));

     // Then, make sure that there are 4 values in nums
     // You get to figure this out

     // Then, copy nums[1] and nums[3] somewhere
     // else (global variables) so draw() can use them
  }
}

unfortunately I am still having troubles with processing. The thing is when I added the "void serialEvent(Serial myPort)" bit it stopped working and I keep getting "Unexpected token: void" highlighting the serialEvent bit and I dont know what is going on really.

I'd guess that you are trying to put serialEvent() inside draw(), rather than after it. Of course, sans your code, it's only a guess.

You have so many problems in that code that it is hard to know where to start. NO SERIAL DATA should be handled in draw(). GET RID OF ANYTHING THAT DEALS WITH SERIAL DATA in draw().

String myString = myPort.readStringUntil('\n');
if (myString != null)
{
print("Serialized data: ");
println(myString);
// Use the function to convert the string to an array of ints
String numbers = "10 50 15 60";
int char [] nums = int(split(myString, ''));
}

Let's get some data from the serial port, and print it out. Then, lets create another String instance that we don't use. Then, lets ignore any advice about the TYPE of variable to be created. Finally, let's ignore any advice about the character to use to split the String. Where does that leave us. Thoroughly frustrated, I'm guessing.

Oh, wait. I see the problem. My last post isn't there. Ignore that.

Your serialEvent method needs to be:

void serialEvent(Serial myPort)
{
  // read the serial buffer:
  String myString = myPort.readStringUntil('\n');
  if (myString != null)
  {
    print("Serialized data: ");
    println(myString);
    // Use the function to convert the string to an array of ints
    int [] nums = int(split(myString, ','));
  }

   d1 = nums[1];
   d2 = nums[3];
}

where d1 and d2 are global variables that then get used in draw() as the distances from the Arduino sensors.

Thanks for that, So this is how processing is printing the data from the arduino

It shouldn't be. But it is because your are reading data all over the place.

Uncomment this:

  //myPort.bufferUntil('\n');

That is essential code.

Get rid of this, from draw():

  byte[] inBuffer = new byte[2];
  while (myPort.available ()> 0) {
   inBuffer = myPort.readBytes();
    myPort.readBytes(inBuffer);
    if (inBuffer != null) {
      String myString = new String(inBuffer);
      println(myString);
    }
  }

And this:

 if ( myPort.available() > 0)
   
  {  // If data is available,
   //myPort = myPort.read();         // read it and store it in myPort
  }

Remove the whole sensorTriggered() method, for now. Until you get good data into Processing, there is no need to have any code that tries to use that data.

In serialEvent(), nums is a local array. It goes out of scope before d1 and d2 are assigned values. Move those two assignments up before the closing } after the int nums[] statement.

So now I am getting this, what's next?

Verify that the values in d1 and d2 are correct.

Then, use them in draw(). It don't understand what you are trying to do with them, but, now you have them, so you can do whatever it is you are trying to do with them.

So does this code seem right to you?

Not entirely. There is too much white space. There is a delay() call. The code is not properly formatted.

There are no sensors that are triggered. There are distances that are known. I think that you need to get rid of rightSensorTriggered and leftSensorTriggered. Create two new variables that hold the previous values of d1 and d2.

Compare d1 to prevD1 to decide if one action is needed. Compare d2 to prevD2 to see if another action is needed.

How would do this by removing the booleans and replacing with something like int left, right

Yes.

When I remove the delays flicker through quickly as soon as the distance is 20 and above

Then, you need to change the frame rate. Unlike loop() on the Arduino, draw() is not called as fast as possible. You can set the frame rate, which defines how often the draw() function is called. The artificial delay is not the solution.

int left = d1;
int right = d2;

So, what values do left and right have?

Do they ever change?

I think that the value of left and right should be 10 and if d1 or d2 are greater than or equal to the left or right the image should change.

No. d1 and d2 are global variables. Therefore, they are initialized to 0. Then, left and right are also initialized to 0. You never assign new values to left and right, and you never use d1 and d2 (that you do assign values to).

I have tried different methods of doing it

the experience works better if you show us your attempts.

if (right(d2>= dist)){

"right" is a non-void function taking a boolean parameter?

Given the missing semicolons, I'm not surprised

(Generally we say something didn't work if it compiled, but didn't perform as expected.
If it didn't even compile we say it didn't compile)

Ok, thanks for letting us know.

How about the missing braces?

Have you read this?

So, it's now compiling - good.
OK, as long as you're happy to keep plugging away at it, it's all cool
If you need any help, be sure to post code.

if (d1>=dist){
   currentState=currentState +1;

else
    if (d2>=dist){
      currentState=currentState -1;
    }

I'm guessing that the error message (which you didn't post) is something like "else without previous if", which is why I asked about the missing braces.

Please read my earlier definition of working vs. compiling.

Something can only be said to work/not work if it actually runs.
It can only run if it compiles, which because of the lack of a closing brace } before the else, your code cannot.