Sound Source Triangulation - More Efficient Interrupt

Hello,

Long winded, but I gather people on this forum like a good amount of information.

As part of and Army military career course (Electronics Engineering Degree), we have built a sound source triangulation system based on Angle of Arrival for our project. The system operates with two nodes, each with two microphones, which return a bearing to a central unit via RF, and the central unit calculates the distance to source, and bearing from its known position in relation to the two nodes.

The system operates well, with good accuracy from 20-160 degrees. The only issues in it are the interrupts which take the time delays not quite being fast enough at the acute angle <20 & > 160 degrees. Otherwise we have tested strong results for bearings with results being correct, or +-5 degrees, and reasonable results for distance +-0.5m at short range. Long range tests are still TBC.

My requests is mainly to do with the interrupt side, and creating low level ISR's via the register. I have made attempts to get it done, but I will not lie that I am capable, and due it being that layer deeper, the examples available are not very often. We've done 4 weeks Zero to Hero Arduino coding, and are hobbyists at best.

We are using Arduino Mega 2650's, on pins 2, & 3 (INT4, INT5) attachInterrupts on changing state to catch the first rise out of Schmitt trigger which works off the analogue input. The results are good in the goldilocks zoon 20-260deg due to the lesser requirement for accurate time delay measurements

The code below is the parts that matter.

#include <eRCaGuy_Timer2_Counter.h>
bool mic1trigger = 0;
bool mic2trigger = 0;

volatile int mic1substate = 0;
volatile int mic2substate = 0;

const int soundmic1pin = 2;
const int soundmic2pin = 3;

int unsigned long mic1soundtimemic;
int unsigned long mic2soundtimemic;

void setup(){
 pinMode(soundmic1pin, INPUT_PULLUP); 
  pinMode(soundmic2pin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(soundmic1pin), mic1listen, CHANGE);
  attachInterrupt(digitalPinToInterrupt(soundmic2pin), mic2listen, CHANGE);

}


Void loop (){

anglesolution(); //runs the triangulation

}

void mic1listen()
{
  if (!mic1substate)  {
    
    mic1soundtimemic = timer2.get_count();
    mic1substate = 1;
    mic1trigger = 1;
  }
}



void mic2listen()
{
  if (!mic2substate)  {
  
    mic2soundtimemic = timer2.get_count();
    mic2substate = 1;
    mic2trigger = 1;
  }
}

I have had minimal success getting INT4 to interrupt via the below code, but I am afraid I don't understand the ATMEGA2560 datasheet enough to make progress on INT5. It seems so simple, yet far away.


#include <avr/interrupt.h>
#include <avr/io.h>
#include <eRCaGuy_Timer2_Counter.h>

bool mic1trigger = 0;
bool mic2trigger = 0;

volatile int mic1substate = 0;
volatile int mic2substate = 0;

const int soundmic1pin = 2;
const int soundmic2pin = 3;

int unsigned long mic1soundtimemic;
int unsigned long mic2soundtimemic;

void setup() {
  // put your setup code here, to run once:
  
  pinMode(soundmic1pin, INPUT_PULLUP);
  pinMode(soundmic2pin, INPUT_PULLUP);

  EICRB = 0x0A;
  EIMSK = 0x10;
  sei();
}

Void loop (){

anglesolution(); //runs the triangulation

}

ISR(INT4_vect)
{
  if (!mic1substate)  {
    mic1soundtimemic = timer2.get_count();
    mic1substate = 1;
    mic1trigger = 1;
  }
}


ISR(INT5_vect)
{
  if (!mic2substate)  {
    mic2soundtimemic = timer2.get_count();
    mic2substate = 1;
    mic2trigger = 1;
  }
}

My request is simple. Is there anyone who can help us with low level ISR code for INT4, and INT5 on CHANGING state? Our project works, and is fully presentable, this would just be an added bonus if we could refine the Interrupts ever so slightly more.

I am also aware of using input capture to take a reading from Timer1 of the exact moment the interrupt happened, but that also proved unsuccessful, again, being limited to very few examples make it more challenging. Nick Gammon's forum has proven educational, and informative, but I can't take the time right now to fully get my head around the deeper level of understanding required to achieve an input capture.

Any help is obviously fully appreciated.

Maybe this would be useful.

Available in the Library Manager.

1 Like

You are setting bit 1 and 3 which sets IS41 and IS51. That sets the interrupts to FALLING. Is that what you want? For CHANGE, use EICRB = 0x05 and for RISING use EICRB = 0x0F;

You are enabling only INT4. To enable both 4 and 5, use EIMSK = 0x30;.

1 Like

@dougp many thanks. After some reading it’s clear I’ve misunderstood pin interrupts in the past. I’ve setup a stand-alone to test the response time and I’ll report back. Appreciate the input, thank you.

@johnwasser thank you. I will be digging back into the documentation to better understand this. Sometimes it’s easier to understand when someone can show you first. Really appreciate this, and again I’ve got a stand-alone to test for the response time, and calculate the time delay.

If you want to try a method without interrupts, here is a sketch I wrote that records the timer count each time any of a set of pins changes.

// Differential Time of Arrival
// Use multiple sensors to calculate the relative arrival time
// (in 16ths of a microsecond) of signals. Microphones to locate a
// pulse noise in 2D or 3D space. Knock sensors to locate impacts on
// a 2D surface.

// Put all of the sensors on one port.
// For example, on an UNO: PORTD (Pins 0-7) or PORTB (Pins 8-13)
#define INPUT_PORT PORTD
const uint8_t INPUT_MASK = 0b00011100; // Pins 2, 3, and 4

// Can record up to 256 state changes
const size_t MaxSamples = 256;

// Allow for bursts up to MaxSamples input changes
uint16_t TimeBuffer[MaxSamples];
uint8_t InputBuffer[MaxSamples];
size_t Index = 0;
uint8_t PreviousInput = INPUT_PORT & INPUT_MASK;

void setup()
{
  // Run Timer1 at 16 MHz.
  TCCR1A = 0;  // WGM 0, "Normal" mode (just counts)
  TCCR1B = 1;  // WGM 0, prescale = 1 (16 MHz)
  PreviousInput = INPUT_PORT & INPUT_MASK;
}



void loop()
{
  // Read the port and mask the used bits.
  uint8_t inputs = INPUT_PORT & INPUT_MASK;

  // If the value has changed, record the time and value.
  if (inputs != PreviousInput)
  {
    uint16_t time = TCNT1;
    PreviousInput = inputs;
    if (Index < MaxSamples)
    {
      TimeBuffer[Index] = time;
      InputBuffer[Index] = inputs;
      Index++;
    }
  }

  // Do we have more than 2 milliseconds of data
  if (Index > 0 && TCNT1 - TimeBuffer[0] > 32000u)
  {
    // Process the data
    for (size_t i = 0; i < Index; i++)
    {
      Serial.print(TimeBuffer[i] - TimeBuffer[0]);
      Serial.print(',');
      Serial.println(InputBuffer[i]);
    }

    // Prepare for the next burst of inputs
    Index = 0;
    PreviousInput = 0;
  }
}

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