Communication Between 2 Ultrasonic Sensors

I've put together some code that seems to be working on my test set up. For this, I'm using unmodified HC-SR04 sensors on both nodes, except for covering the "T" transducer on the receiver side, so it should only see the ping sent by the transmitter node.

The 433 MHz OOK sync scheme is as described above, hopefully the code is self-explanatory.

Transmit node sketch:

/*
  Transmit side of one way ultrasonic ranging using HC-SR04.
  Node sync is performed with 433 MHz OOK transmit receive pair via a sync sequence

  HC-SR04 Ping distance sensor:
  VCC to arduino 5v
  GND to arduino GND
  Echo to Arduino pin 7
  Trig to Arduino pin 8

  433 MHz Transmitter:
  Data to Arduino pin 9

  HC-SR04 technique from here: http://arduinobasics.blogspot.com.au/2012/11/arduinobasics-hc-sr04-ultrasonic-sensor.html
*/


#define echoPin 7 // Echo Pin
#define trigPin 8 // Trigger Pin
#define rfTxPin 9 // rf Transmit enable
#define LEDPin 13 // Onboard LED
#define BitPeriod 500   // Bit period for RF sync bits in microseconds

boolean syncSeq[16] = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1} ; // Bit sequence for synchronization

int maximumRange = 200; // Maximum range needed (cm)
int minimumRange = 0; // Minimum range needed
long duration, distance; // Duration used to calculate distance
long timeout = 50000;  // Ping duration timeout (uS)

void setup() {
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(rfTxPin, OUTPUT);
  pinMode(LEDPin, OUTPUT); // Use LED indicator (if required)
  digitalWrite(trigPin, LOW) ;
  digitalWrite(rfTxPin, LOW) ;
}

void loop() {
  digitalWrite(LEDPin, HIGH);
  TxRFSync() ;        // Send RF synchronization sequence
  digitalWrite(trigPin, HIGH);      // Start ping sequence
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH, timeout);
  digitalWrite(LEDPin, LOW);

  //Calculate the distance (in cm) based on the speed of sound.
  distance = duration / 58.2;

  if (distance >= maximumRange || distance <= minimumRange) {
    /* Send a negative number to computer */
    Serial.println("-1");
  }
  else {
    /* Send the distance to the computer using Serial protocol. */
    Serial.println(distance);
  }

  //Delay 100ms before next reading.
  delay(100);
}

// Function to write "syncSeq" bit sequence to RF transmit port
void TxRFSync()
{
  for (int k = 0; k < sizeof(syncSeq) ; k++)
  {
    digitalWrite(rfTxPin, syncSeq[k]) ;
    delayMicroseconds(BitPeriod) ;
  }
  digitalWrite(rfTxPin, LOW) ;      // Turn off transmit at end of sequence
}

Receive side sketch:

/*
  Receive side of one way ultrasonic ranging using HC-SR04.
  Node sync is performed with 433 MHz OOK transmit receive pair via a sync sequence

  HC-SR04 Ping distance sensor:
  VCC to arduino 5v
  GND to arduino GND
  Echo to Arduino pin 7
  Trig to Arduino pin 8

  433 MHz Receiver:
  Data to Arduino pin 9

  HC-SR04 scheme from here: http://arduinobasics.blogspot.com.au/2012/11/arduinobasics-hc-sr04-ultrasonic-sensor.html
*/
#define syncStateA 10
#define syncStateB 11
#define debugPinC 12

#define echoPin 7 // Echo Pin
#define trigPin 8 // Trigger Pin
#define rfRxPin 9 // rf Receive input pin
#define LEDPin 13 // Onboard LED

// Parameters for sync detection
#define minBitPeriod 450  // Low threshold for bit period (uS)
#define maxBitPeriod 550  // Max threshold for bit period (uS)
#define minSyncBits 8     // Min number of valid 1 plus 0 transitions before 1 plus 1

int maximumRange = 200; // Maximum range needed (cm)
int minimumRange = 0; // Minimum range needed
long duration, distance; // Duration used to calculate distance
long timeout = 50000;  // Ping duration timeout

void setup() {
  pinMode(syncStateA, OUTPUT) ;
  pinMode(syncStateB, OUTPUT) ;
  pinMode(debugPinC, OUTPUT) ;

  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(rfRxPin, INPUT);
  pinMode(LEDPin, OUTPUT) ;
  digitalWrite(LEDPin, LOW) ;
  digitalWrite(trigPin, LOW) ;
}

void loop() {
  RxRFSync() ;    // Function blocks until sync sequence is detected
  digitalWrite(LEDPin,HIGH) ;
  digitalWrite(trigPin, HIGH);    // Start ping sequence
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH, timeout);

  //Calculate the distance (in cm) based on the speed of sound.
  distance = duration / 29.1 ;

  if (distance >= maximumRange || distance <= minimumRange) {
    /* Send a negative number to computer */
    Serial.println("-1");
  }
  else {
    /* Send the distance to the computer using Serial protocol */
    Serial.println(distance);
  }
  digitalWrite(LEDPin,LOW) ;
}

// Function to detect "syncSeq" bit sequence on RF receive port
void RxRFSync()
{
  long int lastBitTime = 0 ;    // holds time of last bit transition (uS)
  int BitCount = 0 ;            // counts number of valid bits detected

  boolean synced = false ;
  boolean lastBit = LOW ;  // holds last bit detected

  digitalWrite(syncStateA, LOW) ; digitalWrite(syncStateB, LOW) ; digitalWrite(debugPinC, LOW) ;

  while (!synced)
  {
    while (digitalRead(rfRxPin) == lastBit) { } // Block until bit transition
    int currBitTime = micros() ;
    int bitPeriod = currBitTime - lastBitTime ;

    if ((bitPeriod > (2 * minBitPeriod)) && (bitPeriod < (2 * maxBitPeriod)) && (lastBit == HIGH) && (BitCount > minSyncBits))
    {
      // Valid completion of sync sequence
      synced = true ;
      digitalWrite(syncStateA, HIGH) ; digitalWrite(syncStateB, HIGH) ; digitalWrite(debugPinC, lastBit) ;
    }
    else
    {
      lastBit = !lastBit ;
      lastBitTime = currBitTime ;
      if ((bitPeriod > minBitPeriod) && (bitPeriod < maxBitPeriod))
      {
        // Valid single bit detected, increment valid bit count and look for next bit
        BitCount += 1 ;     // increment valid bit count
        digitalWrite(syncStateA, LOW) ; digitalWrite(syncStateB, HIGH) ; digitalWrite(debugPinC, lastBit) ;
      }
      else
      {
        // Invalid bit detected, reset valid bit count and look for next bit
        BitCount = 0 ;
        digitalWrite(syncStateA, HIGH) ; digitalWrite(syncStateB, LOW) ; digitalWrite(debugPinC, lastBit) ;
      }
    }
  }
}

Logic analyzer waveforms showing sync, sync debug, and ultrasound trigger and echo. The offset between HC-SR04 trigger on the two nodes is about 55 microseconds which could be improved by removing the debug in RxRFSync() and/or calibrated out with delayMicroseconds:

Wow! Problem solved. The code actually works!!! Thank you so much! (see pics)

I also tried to connect another ultrasonic receiver and just edited your code, however I can't make it work. Can you point out my mistake and correct it?

/*
  Receive side of one way ultrasonic ranging using HC-SR04.
  Node sync is performed with 433 MHz OOK transmit receive pair via a sync sequence

  HC-SR04 Ping distance sensor:
  VCC to arduino 5v
  GND to arduino GND
  Echo to Arduino pin 7
  Trig to Arduino pin 8

  433 MHz Receiveer:
  Data to Arduino pin 9

  HC-SR04 scheme from here: http://arduinobasics.blogspot.com.au/2012/11/arduinobasics-hc-sr04-ultrasonic-sensor.html
*/
#define syncStateA 10
#define syncStateB 11
#define debugPinC 12

#define echo1 5
#define trig1 6
#define echoPin 7 // Echo Pin
#define trigPin 8 // Trigger Pin
#define rfRxPin 9 // rf Receive input pin
#define LEDPin 13 // Onboard LED

// Parameters for sync detection
#define minBitPeriod 450  // Low threshold for bit period (uS)
#define maxBitPeriod 550  // Max threshold for bit period (uS)
#define minSyncBits 8     // Min number of valid 1 plus 0 transitions before 1 plus 1

int maximumRange = 200; // Maximum range needed
int minimumRange = 0; // Minimum range needed
long duration, duration2, distance, distance2; // Duration used to calculate distance

void setup() {
  pinMode(syncStateA, OUTPUT) ;
  pinMode(syncStateB, OUTPUT) ;
  pinMode(debugPinC, OUTPUT) ;

  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(trig1,OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(echo1, INPUT);
  pinMode(rfRxPin, INPUT);
  pinMode(LEDPin, OUTPUT) ;
  digitalWrite(LEDPin, LOW) ;
  digitalWrite(trigPin, LOW) ;
}

void loop() {
  RxRFSync() ;    // Function blocks until sync sequence is detected
  digitalWrite(LEDPin,HIGH) ;
  digitalWrite(trig1, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, HIGH);    // Start ping sequence
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  digitalWrite(trig1, LOW);
  duration = pulseIn(echoPin, HIGH);
  duration2 = pulseIn(echo1, HIGH);

  //Calculate the distance (in cm) based on the speed of sound.
  distance = duration / 58.2;
  distance2 = duration/ 58.2;

  if (distance >= maximumRange || distance <= minimumRange) {
    /* Send a negative number to computer */
    Serial.println("-1");
  }
  else {
    /* Send the distance to the computer using Serial protocol */
    Serial.println(distance);
  }

   if (distance2 >= maximumRange || distance2 <= minimumRange) {
    /* Send a negative number to computer */
    Serial.println("-1");
  }
  else {
    /* Send the distance to the computer using Serial protocol */
    Serial.println(distance2);
  }
  
  digitalWrite(LEDPin,LOW) ;
}

// Function to detect "syncSeq" bit sequence on RF receive port
void RxRFSync()
{
  long int lastBitTime = 0 ;    // holds time of last bit transition (uS)
  int BitCount = 0 ;            // counts number of valid bits detected

  boolean synced = false ;
  boolean lastBit = LOW ;  // holds last bit detected

  digitalWrite(syncStateA, LOW) ; digitalWrite(syncStateB, LOW) ; digitalWrite(debugPinC, LOW) ;

  while (!synced)
  {
    while (digitalRead(rfRxPin) == lastBit) { } // Block until bit transition
    int currBitTime = micros() ;
    int bitPeriod = currBitTime - lastBitTime ;

    if ((bitPeriod > (2 * minBitPeriod)) && (bitPeriod < (2 * maxBitPeriod)) && (lastBit == HIGH) && (BitCount > minSyncBits))
    {
      // Valid completion of sync sequence
      synced = true ;
      digitalWrite(syncStateA, HIGH) ; digitalWrite(syncStateB, HIGH) ; digitalWrite(debugPinC, lastBit) ;
    }
    else
    {
      lastBit = !lastBit ;
      lastBitTime = currBitTime ;
      if ((bitPeriod > minBitPeriod) && (bitPeriod < maxBitPeriod))
      {
        // Valid single bit detected, increment valid bit count and look for next bit
        BitCount += 1 ;     // increment valid bit count
        digitalWrite(syncStateA, LOW) ; digitalWrite(syncStateB, HIGH) ; digitalWrite(debugPinC, lastBit) ;
      }
      else
      {
        // Invalid bit detected, reset valid bit count and look for next bit
        BitCount = 0 ;
        digitalWrite(syncStateA, HIGH) ; digitalWrite(syncStateB, LOW) ; digitalWrite(debugPinC, lastBit) ;
      }
    }
  }
}

Hi.
Nice one MrMark.
Be interesting to see what range it has?

Tom... :slight_smile:

dXmod:
Wow! Problem solved. The code actually works!!! Thank you so much! (see pics)

I'm glad it worked for you. It was a fun diversion, though it took longer than expected as it took a while to discover a non-functional HC-SR04 I'd pulled out of the parts bin.

Since I posted it occurred to me that the range for the receive node is one way time of flight vs round trip time in the code I used as a basis. Thus the range in cm should be duration/29.1 rather than duration/58.2. Also I thought adding a timeout to pulseIn() would make the system more robust to missed pings. I've updated the code above with these changes but haven't tried them out.

dXmod:
I also tried to connect another ultrasonic receiver and just edited your code, however I can't make it work. Can you point out my mistake and correct it?

In your code for two receivers, pulseIn() is blocking, that is, the processor will not execute the next line of code until it returns from that function call after the pulse had completed. Because of this when the processor gets to the second pulseIn() the pulse has already occurred. That pulseIn() function call will timeout after 1 second (the default) and return 0. To make this work, you'd have to be able to simultaneously monitor both pins or alternate between the two on successive pings.

TomGeorge:
Be interesting to see what range it has?

I tested only over less than a meter in this configuration. The range ought to be at least double that of an HC-SR04 in its conventional mode. When I played with serial data over ultrasound per the link up thread, that worked at a distance of about 2 meters which was as much separation as I could easily get within the confines of my work room.

I'm reminded of visiting an interactive science museum as a kid where they had big parabolic dishes set up. You could stand at the focal point of one and speak at a whisper to a companion located at the focal point of the other across a large and not particularly quiet exhibition hall.

MrMark:
I'm glad it worked for you.

Yes, I owe you and everyone so much. Thank you!

MrMark:
In your code for two receivers, pulseIn() is blocking. To make this work, you'd have to be able to simultaneously monitor both pins or alternate between the two on successive pings.

So can 2 loops including the pulseIn() function can satisfy it? e.g.

void loop() {
  RxRFSync() ;    // Function blocks until sync sequence is detected
  {
  digitalWrite(LEDPin,HIGH) ;
  digitalWrite(trigPin, HIGH);    // Start ping sequence
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH, timeout);

  //Calculate the distance (in cm) based on the speed of sound.
  distance = duration / 29.1 ;

  if (distance >= maximumRange || distance <= minimumRange) {
    /* Send a negative number to computer */
    Serial.println("-1");
  }
  else {
    /* Send the distance to the computer using Serial protocol */
    Serial.println(distance);
  }
 digitalWrite(LEDPin,HIGH) ;
  digitalWrite(trigPin, HIGH);    // Start ping sequence
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration2 = pulseIn(echoPin2, HIGH, timeout);

  //Calculate the distance (in cm) based on the speed of sound.
  distanc2e = duration2 / 29.1 ;

  if (distance2 >= maximumRange || distance2 <= minimumRange) {
    /* Send a negative number to computer */
    Serial.println("-1");
  }
  else {
    /* Send the distance to the computer using Serial protocol */
    Serial.println(distance2);
  }
  digitalWrite(LEDPin,LOW) ;
}

I can't try it cause I think I got my rf module busted since when I came back to work on it again, I get -1 as values and not former results.

dXmod:
So can 2 loops including the pulseIn() function can satisfy it? e.g.

You need a "RxRFSync()" before each channel's sequence up to the pulseIn(...) function at least. The master will send a ping and the slave will detect it with one channel. The next ping the master sends, the second slave channel detects.

I can't try it cause I think I got my rf module busted since when I came back to work on it again, I get -1 as values and not former results.

You can test this by replacing the RF transmitter and receiver with a wire between transmit data and receive data (plus a ground wire). If the RF module(s) are broken the rest of the setup should work in that configuration.

Range should be more than double the basic sensor: it's not bouncing off a surface which absorbs some of the energy.

It's like the problem with ground-based radar detecting aircraft. The aircraft can detect the radar much further away than the radar can detect the aircraft. Look up HARM Missile, for fun.

MrMark:
You can test this by replacing the RF transmitter and receiver with a wire between transmit data and receive data (plus a ground wire).

I'm sorry I didn't get that well. You mean i'll just connect a wire between the designated send/receive pins of the arduino or the send/receive pins of the rf mods?

In my sketches above, pin 9 of the Arduino is connected to "Data" on both the transmitter and receiver side. You can remove the transmitter and receiver from the circuit and connect pin 9 to pin 9 with a wire. You also need to connect the ground pins. This effectively simulates a transmitter/receiver pair that perfectly reproduces the transmit data signal.

MrMark:
You need a "RxRFSync()" before each channel's sequence up to the pulseIn(...) function at least. The master will send a ping and the slave will detect it with one channel. The next ping the master sends, the second slave channel detects.You can test this by replacing the RF transmitter and receiver with a wire between transmit data and receive data (plus a ground wire). If the RF module(s) are broken the rest of the setup should work in that configuration.

Can some one share correct code for this reply?
I am working on the same kind of project.
I need to configure 2 ultra sonic reciver with one emitter and RF module.

Hi,
does anybody know of any ready-made products that do this (give distance readings on two different objects, using one beacon and one receiver)?

Sounds like a contradiction in terms.

Hi everyone,

Currently i'm too involved in a similar project. But i'm using two hc-12 modules to sync the two ultrasonic sensors (HC-SR04).

The receiver US sensor's hc-12 transmits a signal to the transmit US sensor's hc-12. Therefore on the request of the receiving side the transmitter emits the US signal.

The codes i used are attached below. Even though i get an output the results are not accurate and varies constantly.

I would greatly appreciate any comments on this. :slight_smile:

Transmitter code:

//US transmitter 
#include <SoftwareSerial.h>

SoftwareSerial HC12(6,5); // HC-12 TX Pin, HC-12 RX Pin

// defines pins numbers
const int trigPin = 10;
const int echoPin = 11;
// defines variables
long duration;
int distance;

void setup() {
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin, INPUT); // Sets the echoPin as an Input
  Serial.begin(9600); // Starts the serial communication
  HC12.begin(9600); 
}

void loop() {
    if (HC12.available()){
    
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    //delay(200);
    }
  
}

Receiver code:

//US receiver
#include <SoftwareSerial.h>
SoftwareSerial HC12(6,5);

const int trigPin = 10;
const int echoPin = 11;
// defines variables
long duration;
int distance;

void setup() {
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin, INPUT); // Sets the echoPin as an Input
  Serial.begin(9600); // Starts the serial communication
  HC12.begin(9600);
}

void loop() {
  distance=0;
  HC12.write("T");
  
  // Clears the trigPin
  digitalWrite(trigPin, LOW);
  //delayMicroseconds(2);
  
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  
  //delayMicroseconds(20);
  duration = pulseIn(echoPin, HIGH);

  // Calculating the distance
  //Serial.println(duration);
  
  distance= duration*0.034;
  if (distance>1000)
    {
        Serial.println("error");
    }
  else
    {
    // Prints the distance on the Serial Monitor
    Serial.print("Distance: ");
    Serial.println(distance);
    }  
  delay(200);
  
}

US_TX.ino (596 Bytes)

US_RX.ino (940 Bytes)

I would greatly appreciate any comments on this.

Read the forum sticky post on how to use this forum.

A lot of people are on mobile devices that know nothing of .ino files, even though they are actually just text files.

Grumpy_Mike:
Read the forum sticky post on how to use this forum.

A lot of people are on mobile devices that know nothing of .ino files, even though they are actually just text files.

Sorry for that. It's my bad. :slight_smile:

Chamzy:
Even though i get an output the results are not accurate and varies constantly.

How much variation are you seeing?

With the HC-12 the Arduino is sending/receiving data via the serial controller which introduces a timing uncertainty of at least 1/(bit rate) = 1/9600/s = 0.1 ms on both the Tx and Rx side. For ultrasound that corresponds to about 3.4 cm uncertainty at best. In addition there is a microcontroller on the HC-12 that receives the serial data then manipulates the radio and it will inevitably contribute some unknown amount of delay and delay uncertainty to synchronization.

I understand it is possible to change the baud rate on the HC-12 which would reduce uncertainty in the command timing, but if uncertainty in the delay introduced in the HC-12 microcontroller is large, this might not solve your problem.

For this application it's probably best to use a radio that is directly modulated by the Arduino as was done up thread.

I want to make an ultrasonic sensor on the LCD display I2C using HC 12. Is there anyone who wants to help me about the program code