Help with an IR sensor reading loop issue

Hello everyone,

I made a basic interaction in which one audio file is triggered on a wave shield using a Sharp IR distance sensor. It uses Wave_HC.h, WaveUtil.h & SharpDistSensor.h.

Strangely, I had it working fine.. but when I revisited the project a week later, there is now a slight issue: when triggered, the audio plays three times in a loop before returning to reading the distance data. It seems to repeat the initial reading (<800mm) and use it to re-trigger the audio two more times. The IR has no obstruction at this point.

Any ideas? I've tried tweaking and adjusting code and hardware connections for hours and so confused.. perhaps someone with more coding knowledge might know of an additional line or two that could skip over this glitch?

Thanks.

/*
   This example plays every .WAV file it finds on the SD card in a loop
*/
#include <WaveHC.h>
#include <WaveUtil.h>
#include <SharpDistSensor.h>

SdReader card;    // This object holds the information for the card
FatVolume vol;    // This holds the information for the partition on the card
FatReader root;   // This holds the information for the volumes root directory
WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time

uint8_t dirLevel; // indent level for file/dir names    (for prettyprinting)
dir_t dirBuf;     // buffer for directory reads



// Analog pin to which the sensor is connected
const byte sensorPin = A0;

// Window size of the median filter (odd number, 1 = no filtering)
const byte medianFilterWindowSize = 5;

// Create an object instance of the SharpDistSensor class
SharpDistSensor sensor(sensorPin, medianFilterWindowSize);


/*
   Define macro to put error messages in flash memory
*/
#define error(msg) error_P(PSTR(msg))

// Function definitions (we define them here, but the code is below)
void play(FatReader &dir);

//////////////////////////////////// SETUP
void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps for debugging

  putstring_nl("\nWave test!");  // say we woke up!

  putstring("Free RAM: ");       // This can help with debugging, running out of RAM is bad
  Serial.println(FreeRam());

  //  if (!card.init(true)) { //play with 4 MHz spi if 8MHz isn't working for you
  if (!card.init()) {         //play with 8 MHz spi (default faster!)
    error("Card init. failed!");  // Something went wrong, lets print out why
  }

  // enable optimize read - some cards may timeout. Disable if you're having problems
  card.partialBlockRead(true);

  // Now we will look for a FAT partition!
  uint8_t part;
  for (part = 0; part < 5; part++) {   // we have up to 5 slots to look in
    if (vol.init(card, part))
      break;                           // we found one, lets bail
  }
  if (part == 5) {                     // if we ended up not finding one  :(
    error("No valid FAT partition!");  // Something went wrong, lets print out why
  }

  // Lets tell the user about what we found
  putstring("Using partition ");
  Serial.print(part, DEC);
  putstring(", type is FAT");
  Serial.println(vol.fatType(), DEC);     // FAT16 or FAT32?

  // Try to open the root directory
  if (!root.openRoot(vol)) {
    error("Can't open root dir!");      // Something went wrong,
  }

  // Whew! We got past the tough parts.
  putstring_nl("Files found (* = fragmented):");

  // Print out all of the files in all the directories.
  root.ls(LS_R | LS_FLAG_FRAGMENTED);
}

//////////////////////////////////// LOOP
void loop() {
  // 5v
 // Get distance from sensor
   int distance = sensor.getDist();
   
  // Print distance to Serial
  Serial.println(distance);

  if (distance <= 800) {
    //Serial.println(distance);   // print the distance
    root.rewind();          
    play(root);       //play the track on the SD Card
  }
}

/////////////////////////////////// HELPERS
/*
   print error message and halt
*/
void error_P(const char *str) {
  PgmPrint("Error: ");
  SerialPrint_P(str);
  sdErrorCheck();
  while (1);
}
/*
   print error message and halt if SD I/O error, great for debugging!
*/
void sdErrorCheck(void) {
  if (!card.errorCode()) return;
  PgmPrint("\r\nSD I/O error: ");
  Serial.print(card.errorCode(), HEX);
  PgmPrint(", ");
  Serial.println(card.errorData(), HEX);
  while (1);
}
/*
   play recursively - possible stack overflow if subdirectories too nested
*/
void play(FatReader &dir) {
  FatReader file;
  while (dir.readDir(dirBuf) > 0) {    // Read every file in the directory one at a time

    // Skip it if not a subdirectory and not a .WAV file
    if (!DIR_IS_SUBDIR(dirBuf)
        && strncmp_P((char *)&dirBuf.name[8], PSTR("WAV"), 3)) {
      continue;
    }

    Serial.println();            // clear out a new line

    for (uint8_t i = 0; i < dirLevel; i++) {
      Serial.write(' ');       // this is for prettyprinting, put spaces in front
    }
    if (!file.open(vol, dirBuf)) {        // open the file in the directory
      error("file.open failed");          // something went wrong
    }

    if (file.isDir()) {                   // check if we opened a new directory
      putstring("Subdir: ");
      printEntryName(dirBuf);
      Serial.println();
      dirLevel += 2;                      // add more spaces
      // play files in subdirectory
      play(file);                         // recursive!
      dirLevel -= 2;
    }
    else {
      // Aha! we found a file that isnt a directory
      putstring("Playing ");
      printEntryName(dirBuf);              // print it out
      if (!wave.create(file)) {            // Figure out, is it a WAV proper?
        putstring(" Not a valid WAV");     // ok skip it
      } else {
        Serial.println();                  // Hooray it IS a WAV proper!
        wave.play();                       // make some noise!

        uint8_t n = 0;
        while (wave.isplaying) {// playing occurs in interrupts, so we print dots in realtime
          putstring(".");
          if (!(++n % 32))Serial.println();
          delay(100);
        }
        sdErrorCheck();                    // everything OK?
        // if (wave.errors)Serial.println(wave.errors);     // wave decoding errors
      }
    }
  }
}

Hi @sounds_shapes,

did I understand it right that the sound is played three times although the if clause (distance <= 800) was only triggered once?

  • How long does play(root) take?
  • What happens if you return very quickly to sensor.getDist()? Does the sensor return the last value it had acquired?

To check it I would do the following:

void loop() {
  // 5v
 // Get distance from sensor
   int distance = sensor.getDist();
   
  // Print distance to Serial
  Serial.println(distance);

  if (distance <= 800) {
  // set distance to a value above the threshold (paranoia)
   distance = 1000;
    //Serial.println(distance);   // print the distance
    root.rewind();          
    play(root);       //play the track on the SD Card
   // put a delay here to avoid to return too quick to sensor.getDist()
   delay(1000);
  }
}

If the delay helps but you won't work with delay, you might restrict the sensor.getDist() by a checking the millis() between last measurement and the next valid measurement.

To be more clear, something like this

void CheckDistance(){
  static unsigned long lastMillis = 0;
  if (millis()- lastMillis > 500) {
      lastMillis = millis();
      int distance = sensor.getDist();
      Serial.println(distance);
      if (distance <= 800) {
         root.rewind();          
         play(root);       
      }
   }
}

void loop(){
   CheckDistance();
}

(Have not compiled it ...)

Hello ec2021

did I understand it right that the sound is played three times although the if clause (distance <= 800) was only triggered once?

Yes this is what happens

  • How long does play(root) take?

one .wav which plays around 2minutes

  • What happens if you return very quickly to sensor.getDist()? Does the sensor return the last value it had acquired?

yes it uses that initially read value to repeat the audio two more times.

I had tried using delay like you have shown (and in every other opportune point) but it just delays the same event occurring. I'll try your addition of setting the distance up to 1000. The sensor reads up to 1100 ish max. so that would suit.

I had tried / thought to use mathematics at one point. I tried using a - while <800 function after the if { play(root) } section. It locked in and repeated the initial reading but I couldn't figure out how to bump it (the distance) up over 800 to break the out of it. [not sure if that made sense].

Thanks for the suggestions though! I'll try them asap and let you know how it works.

Sorry if it looked as if I would underestimate your skills ... You did already a good job!

haha no it didn't at all. My skills are actually very basic and I'm learning as I go. I usually use Arduino connected to Max/MSP so I'm still new to writing stand alone code.

Okay, if you tried that we might have to see what sensor.getDist() is really doing ... Does a proper action require to trigger the measurment with a specific function call?

I'll check with github ...

No it obviously does not need a specific trigger; and from the examples there it should return a valid distance even every 50 msec ....

Is you sensor broken????

If not: Is (something like) this required:

sensor.setModel(SharpDistSensor::GP2Y0A710K0F_5V_DS);  // Set sensor model

(Unfortunately a lot calls for "broken sensor" as source code usually :innocent: does not get defect over time ... Unless that libs or compiler changes lead to unexpected behavior ... ).

Hello ec2021! Apologies for being awol there. I had tried your suggestions (and variations trying the code lines in slightly differing order/placements) but had no success.
Interestingly the CheckDistance void made it play twice on first go instead of three but then went back to playing three times before reading.
with the distance=1000, it always seem to still read the initial reading under 800, then read it immediately as 1000 before playing.

The sensor is ok and I had tried more than one but I had been thinking it related to hardware. I had rewired and connected everything properly before I tested again and had this happen so I wondered if it was due to that. I changed back similar to my original bread board set-up but didn't seem to be any change.

At this stage, I was creating it for another artist and had to hand it over, but will be doing similar for myself for an installation. If you managed to look into the sensor.getDist any further let me know, otherwise I'll dig around and explore myself and see what I can figure out. Thanks for all your help! :raised_hands:

I recon there is no much other chance than investigating by some trial and error as the reason for the strange behavior does not seem to be obvious. And it is always tricky to assist just by "remote diagnosis" ...

What you could do is to change the value of 500 in CheckDistance in the line

 if (millis()- lastMillis > 500) {

to values like 1000 or 2000 which defines the time interval in micro seconds between measurements. So your sketch will only measure and take action (if <= 800) every interval/1000 seconds. That is probably not what you want in real life, but may help to find out whether the time between measurements has an effect.

A second possibility is to calculate an average of several measurements and only check this value after n measurements. A code would look like this (but here emulating the sensor.getDist() by reading data from a slide potentiometer :wink: )

#include <Streaming.h>
Print &cout = Serial;

int sensorgetDist(){
   int in = analogRead(A0);
   return in;
}

#define MinCountForCheck  10
#define MinTimeForMeasurement 100

unsigned long average = 0;
unsigned long  sum     = 0;
int Count   = 0;


void CheckDistance(){
  static unsigned long lastMillis = 0;
  if (millis()- lastMillis > MinTimeForMeasurement) {
      lastMillis = millis();
      int distance = sensorgetDist();
      Count++;
      sum += distance;
      average = sum/Count;
   }
}

void PlaySound(){
   if (average <= 800 && Count > MinCountForCheck) {
      //root.rewind();          
      //play(root);       
      cout << average << "\n";
      Count = 0;
      sum   = 0;
   }
}

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

void loop(){
   CheckDistance();
   PlaySound();
}

There are two defines for minimum time between measurements and minimum number of counts before the average is "taken serious" ...

You may also play around with those data ... Of course you have to do the required modifications to make it run in your environment (uncommenting, deleting the additional parts which you do not need ...).

Actually, I have made this sketch available at Wokwi, so you can test it there if you like:

https://wokwi.com/arduino/projects/323313054131421779

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.