Ultrasonic sensor JSN-SR04T to Electron

Thanks a lot for sharing your code! I’m a total newbie and I was only able to obtain relevant data from the JSN-SR04T-2.0 sensor after understanding and adapting your code. I did some refactoring of the code, in case you or somebody else is interested.

/*

Code to measure the distance using ultrasonic sensor JSN-SR04T-2.0 using a Particle Electron.

The measurement principle is as follows: 

    (1) A HIGH signal is sent to the TRIGPIN.
    (2) The JSN-SR04T-2.0 will send 8 40khz square waves and will 
        automatically detect the return signal (if there is any).
    (3) To report the time between sending and receiving the ultrasonic 
        wave, a HIGH signal is sent by the ECHO port of which the 
        duration equals the latter.

Note that if no echo signal was received, the ECHO port will automatically 
become LOW after 60MS, marking the End of measurement, whether successful 
or not.


*/

int TRIGPIN = D0;
int ECHOPIN = D1;

volatile unsigned long startPulse = 0;
volatile unsigned long endPulse = 0;

double distance = 0.0;

double measureDistance(void);

int triggerMeasurementOnce(void);
int triggerMeasurement(void);

void ultrasonicISR(void);

void setup() {

    pinMode(TRIGPIN, OUTPUT);
    pinMode(ECHOPIN, INPUT_PULLDOWN);

    Serial.begin(9600);

}

void loop() {

    attachInterrupt(ECHOPIN, ultrasonicISR, CHANGE);

    distance = measureDistance();
    
    Serial.printlnf("Distance (in cm): %f", distance);

    delay(250);

}

double measureDistance(void) {
    
    int duration;

    duration = triggerMeasurement();

    return duration / 34.029 / 2.0 * 1.25; // local calibration factor 1.25

}

int triggerMeasurement() {

    int duration;
    
    int attempts = 1;
    
    while(attempts < 10) {
        duration = triggerMeasurementOnce();
        if (duration > 0 && duration < 60000) {
            break;
        }
        delay(200);
    }
    
    return duration;
    
}

int triggerMeasurementOnce(void) {

    int duration;
    
    endPulse = startPulse = 0;

    digitalWrite(TRIGPIN, HIGH);
    delay(5);
    digitalWrite(TRIGPIN, LOW);
    delay(20);

    duration = endPulse - startPulse;    

    return duration;

}

void ultrasonicISR(void) {

    if (digitalRead(ECHOPIN) == HIGH)
        startPulse = micros();
    else
        endPulse = micros();

}

Just some hints

  • if you don’t call detachInterrupt() you should not call attachInterrupt() over and over
  • if you want faster execution - especially inside the ISR - you can use pinReadFast() and its fast write siblings
  • when doing calculations (without dedicated floating point coprocessor) try to reduce the number of divisions to the bare minimum and perform them as late as possible
    // return duration / 34.029 / 2.0 * 1.25; // local calibration factor 1.25
    // better as
    return  (duration * 1.25) / (34.029 * 2.0);
1 Like

Thank you, @ScruffR.

Maybe just one follow-up question: Is it good practice to call attachInterrupt() in the setup() function and thus never call the detachInterrupt() ?

That's perfectly fine, but whether or not you want to call detachInterrupt() depends on the use-case.

But since you are not calling it in your code above, re-attaching an already attached interrupt isn't really good practice.

True, but that was unintentional. In my head, it was in the setup() function. I just didn’t understand why the attachInterrupt() / detachtInterrupt combo was needed for every measurement cycle in the original code, so I moved it “upward” so that the ECHO pin is monitored continuously. I didn’t spot my mistake because the code ran as expected, I guess.

Thanks again for helping me out!

1 Like

Aren’t the delays after pulling TRIG high/low supposed to be 5 and 20us rather than 5 and 20ms?

e.g. delayMicroseconds(5) instead of delay(5)

based on: https://www.jahankitshop.com/getattach.aspx?id=4635&Type=Product, where it’s stated a 10us width trigger pulse should be sent.

You could also make this even easier by using The NewPing library written by Tim Eckel.

#include <NewPing.h>

#define trigPin 2
#define echoPin 3

#define MAX_DISTANCE 400
NewPing sonar = NewPing(trigPin, echoPin, MAX_DISTANCE);

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

void loop() {

  delay(50); 
  Serial.print(sonar.ping_cm()); 
}

Hi stewsterl,

I’m very anxious to use your code for my application, but I want to assign the distance value returned by the NewPing library to a variable (‘distance’ in my case) rather than print it on a serial monitor. I tried “distance = sonar.ping_in();” but all I get back is a ‘0’. Can you please advise how to do that?

Oops; disregard. I started with pins 6 & 7 (with appropriate assignments in code, of course). Moved to pins 2 & 3 and it works!

If the volume/dimensions of the tank are known, could you use a water level sensor to achieve the same goal, or make a digital switch, the n ultrasonic sensor start working.