An ultrasonic measurement saga finally over...

  • Surprised I'm not bald will all the hair pulling and failed attempts to get there but after a year it is finally working...
    The task was simple enough, to measure fluid depth in two tanks, one a sewage holding tank, the other a bulk water tank. Just figuring out which method worked with which board was an exercise in frustration, but the JSN-SR04-2.0 turned out the only one of the two I could get to reliably work.

    Despite the industrial look, these waterproof light switch enclosures proved ideal, as the batteries in holders fitted beneath the clear plastic. A little hacking out of the switch 'fingers' with a Dremel gives plenty of space in the base area.
    Battery holders are held in plastic conduit which are hot glued to the back of the box poking through the front section aperture.

    The top case is a 3v3 WhisperNode with onboard RTC and RFM69, and tucked under the spaghetti in the top left back corner is a level converter. The RTC wakens the Node every hour to initiate readings, and despite a few hiccups seems to be reliable. The WhisperNode is allegedly capable of draining the 2 AA cells down to 0.9v, time will tell...
    The lower box holds the 5v ProMini, a small latching relay, and the ultrasonic board, the button type ultrasonic (like parking sensor) is mounted on the end of a 3/4" collar to 1/2" galvanised steel tube drilled centrally through the roof of the 1.34m x 1.67m tank area.
    Connection between the cases is via Cat5e in 16mm conduit, 2 cores for I2C, 3 cores for relay control and 3.3v power, 2 cores for 5v and ground to the level converter.
    Despite worries over echoes in the resulting space, the tank was emptied last night, and the reading is coming in with the occasional glitch at 1620mm depth from the head, which I believe has a cone angle of 45 degrees.
    The Node control of a Relay by On/Off pins to Mosfets may seem wasteful, but I had them anyway. The 5ms on/off of this small latching signal relay boots up the ProMini and seems reliable.
    Still to incorporate a WDT/Reset on the 5v as it periodically hangs, and a time limiter on the Node to complete the task, but glad it is all all working semi-reliably, even if some of the programming side I still don't understand...
    The 5v tests for 2 consecutive identical readings, the 3.3v tests that it fits within acceptable range, and if not calls for a further reading.

    Once delivery of a second identical board from China is done, the water tank should be a lot easier. This one is the more important, as ran out of water previously due to corrosion of the level probes in the tank (3 core cable and short bolts) which stopped the borehole pump topping up the tank, and the hidrofor hit it's cut-off electrode. The level monitoring will give early warning to go investigate long before the bulk tank is 'empty'...

    Postscript 12/7/18 - Water tank worked flawlessly from first deployment, but led to concerns over dropping battery voltage.. The problem appeared to lie with the Master sinking power through the I2C lines, but a routine to null the SDA/SCL lines prior to sleeping seems to have halted the decay.
    One of the unexpected results of the Sewage Tank deployment was detecting a dramatic rise in level during a heavy storm (85mm in 24 hours) initially thought to be ground water intrusion.
    Excavation to the incoming pipe connection to the tank found a round pipe in a square hole (not exactly an unexpected building practice here, now packed out and sealed.
    Losing 30% of storage capacity would otherwise never have been detected, so it the deployment has already paid for itself handsomely...

  • Just as a follow-up to this - I am very aware a lot of folks have had problems with these ultrasonic boards and their various iterations, had plenty of WTF moments myself in the evolution of this particular end, and thought to make a few observations which might help some..

    First I should explain the environment - The button type transducer (long cable leading to the board a la parking sensor) is inserted into the tank projecting below the soffit (underside of roof) of a RC slab by 70mm on the end of a pipe, held rigidly and pointed straight down at the bottom of the tank from a central position. The tank is 1.34x1.67m reinforced concrete construction with not so shiny smooth sides, the chamber is sealed including the access hatch so zero absorption of sonic emissions, only natural decay essentially an echo chamber...
    Depth is 1.86m max to a bare earth floor when empty and it is never empty, sensor reading is on a straightforward timing routine of Low/High on the pin, the classic pulseIn...

    Went through merry hell with the early results, and grasped at a swathe of potential issues - power limitations from the power on a pin/Wire interfering with the process/the RTC causing chaos by asking "Who are you?", the alignment of the planets, I was clutching at straws..... Tried every conceivable variation with the exception of planetary alignments, every time a failure...
    Only when the eventual idea to run the ultrasonic routine in setup (one shot) and storing the value in RAM (viable on this installation), did I discover the same results and my own stupidity of having inverted level constraints, think 300<>1860 v <300||>1860.
    Once the illogic of the while statement was straightened out, it performed rock solid, spitting back results appearing on Domoticz within 25 seconds, range 1.45m, not a single failure...

    So JFS&G, ran a test to see how long it would take to establish two consecutive bogus figures out of potential range.. The first took 6.7 hours, the second <1 minute, third 35 minutes, fourth 5 hours, but what I found curious was the effect of varying delay before the next sample... My only guess was that irrespective of (unlikely) erroneous reflections, perhaps decay of the previous pulse was being detected, hence a full yet empty tank... Extending the delay between pulses with the bogus range is what drove the 6+ hours event, it took WAY longer two establish two consecutive screwups...

    So put it all back to original settings, and it has been spitting back results every hour give or take a few seconds, spot on, and very well pleased...

    It was not faulty cheap chinese anything, or physical environment which cause my headaches, it was my own stupidity... 😉

  • Hero Member

    Well done @zboblamont great news to see you have had a win. 🙂

  • @boots33 Indeed, but it took WAY longer than first thought when it all started off..

    Much of what I had read were horror stories of what could go wrong, poor QC in manufacture, bad copy designs, voltage sensitivity, echoes in installation, different libraries, different communication methods, etc., a multitude of reasons for failure and to a degree full expectation of it.
    All of these were systematically tested, as it turned out, completely erroneously... The DUH moment of realising my own illogical programming was cause for wasted months of modifications was quite chastening...
    Now that it is fully reliable I might slowly reverse interactions to see what if any effects there are, as it is simple enough to do in this particular setup.

    The one justification I could not get my head round as it simply made no sense was Echo, and it has been cited many many times as cause for bogus values, generally yielding short range results. The argument relies on a side reflection of reduced power somehow overcoming the main emission. As an echo in reality is a decaying wave it set me off looking for where it could originate...
    Examined several example sketches, and many had a PING 10 microseconds before the principle PING to be measured. In testing these sketches, the majority were strange readings with a periodic correct one. Commenting out the preceding 10microsecond PING, and leaving a 100ms silence for complete decay prior to the main PING, and BINGO, the majority reading was the correct one.
    The routine deployed here only accepts two consecutive readings fitting within a range anyway, so any bogus values picked up are discarded....
    In short, anybody thinking they have an ECHO problem would do well to examine the sketch to ensure the previous pulse has fully decayed and the environment falls completely silent before taking a reading.

  • Hero Member

    @zboblamont I would be interested to see your final sketch if you don't mind posting it.

  • @boots33 Sure... It's not finished in that want to reverse some of the mods made in desperate attempts to isolate the problem, before realising the illogic of the range checking.
    It's no different to any other pulse timer in function, and doubtless could be made more efficient by more knowledgeable people than I, but it is working flawlessly now which was the objective.
    The original idea was that the Slave would sleep as per Gammon's I2C example, constantly available to be remote commanded, but as the switched off ADC stayed off after sleep I lost the battery reading which divider was running continually. Inserting a latching relay, it ended up remote powered up with the readings taken during setup, otherwise it is completely off.
    It's a straight 5v promini chinese clone with only the power led removed, powered by 4x2AA cells via a latching signal relay, as it is only fired up every hour by the sleeping Master, it should hopefully last a while...
    Might extend the cycles to 3 or 4 hours, but for now it is feeding back accurate information on groundwater intrusion which has proven a bit of a surprise...

    // Adapted from Nick Gammon tutorials from 18th February 2011
    //ADC disabling prior to sleep failed to restart originally and written out
    #include <Wire.h>
    #include <avr/sleep.h>
    const byte MY_ADDRESS = 61;
    const byte AWAKE_LED = 13;
    #define echoPin 5
    #define trigPin 6
    #define USon 4
    #define BatteryIn A3
    long duration;
    int distance,test,test2;
    const unsigned long WAIT_TIME = 500;
    volatile unsigned long counter;
    volatile int ultradepth, batterypower;
    // various commands we might get
    enum {
        CMD_READ_TANK  = 2,
        CMD_READ_BATTERY = 3
    char command;
    void setup() 
      command = 0;
      pinMode(USon, OUTPUT);
      pinMode(trigPin, OUTPUT);
      pinMode(echoPin, INPUT);
      digitalWrite(USon, HIGH);
      delay (100); 
      delay(100);/// Allow decay of pulse
      Wire.begin (MY_ADDRESS);
      Wire.onReceive (receiveEvent);  // interrupt handler for incoming messages
      Wire.onRequest (requestEvent);  // interrupt handler for when data is wanted
      }  // end of setup
    void receiveEvent (int howMany)
      command = ();  // remember command for when we get request
      } // end of receiveEvent
    void loop (){
      if (++counter >= WAIT_TIME)
        flasher();//1 flash signals going to sleep
        set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
        digitalWrite (AWAKE_LED, LOW);
        sleep_cpu ();      
    // release TWI bus
        TWCR = bit(TWEN) | bit(TWIE) | bit(TWEA) | bit(TWINT);
         for (byte i = 0; i < 3; i++)
        {//3 flashes wakened up
    // turn it back on again
        Wire.begin (MY_ADDRESS);
     void READULTRASONIC(){//JSN-SR04-2.0
    while ((test!=test2)&&((distance<300)||(distance>1860))){// Get two consecutive readings in specified range
      digitalWrite(trigPin, LOW);
      digitalWrite(trigPin, HIGH);
      digitalWrite(trigPin, LOW);
      duration = pulseIn(echoPin, HIGH);
      distance = duration/5.82;//This is in mm
     if (test!=distance){
      delay(100);///// 500 originally but why???????
    void READTANK (){
      byte buf [2];
        buf [0] = ultradepth >> 8;
        buf [1] = ultradepth & 0xFF;
        Wire.write (buf, 2);
    }/// end of READTANK
    void READPOWER(){//Still needs cap on resistive divider
      int val, read1, read2;
     ///Get two consecutive identical readings
      val= analogRead(BatteryIn);
    void READBATTERY ()
    {// Battery reading send now
      byte buf [2];
      buf [0] = batterypower >> 8;
      buf [1] = batterypower & 0xFF;
      Wire.write (buf, 2);
      }  // end of READBATTERY
    void requestEvent ()
    {// Original which parameter removed
      switch (command) {
         case CMD_READ_TANK: READTANK (); break;  // send Tank Depth Reading
         case CMD_READ_BATTERY: READBATTERY (); break;   // send Drone battery reading
         }  // end of switch
      }  // end of requestEvent
      void flasher(){
        digitalWrite (AWAKE_LED, HIGH);
        delay (20);  
        digitalWrite (AWAKE_LED, LOW);
        delay (50);  

    EDIT - With playing around with so many variations, I had copied the wrong sketch version here, now resolved.
    One of the more frustrating aspects I found in this setup was instability, presumably due to Wire interrupts or a dodgy connection somewhere. The Slave would in isolation get a valid figure to forward, yet somehow the Master would pass completely different figures to the Gateway or everything would lock up.
    One curiosity noted was the delay between the Master powering up the Slave and requesting results over Wire, it worked fine with sleep(5000), but 1000 would introduce instability.

Log in to reply

Suggested Topics

  • 8
  • 2
  • 29
  • 2
  • 44
  • 2