Position-recognized audio player

Status
Not open for further replies.

byungjun

Member
Hello
I'm trying to build audio-player using teensy audio shield with local positioning system with DWM1000 module.
15 units will be moving around in the space and users will listen to the relevant sound in regards to the position in the space.
By now I figured out the wav file play and mix using the audio shield.
But for the localization, I still don't know what the best option if for Teensy 3.2.
I'd like to use Posyx library (https://www.pozyx.io/Documentation/Datasheet/arduino), but it wouldn't compile under Teensy.
So my choice of hardware for this project would be,

Audio player Units(x15)
Teensy3.2 -- Audio shield
+dwm1000

Anchor
Teensy3.2
+dwm1000

There 're several libraries which may do the similar job to Pozyx but in my opinion Pozyx is way more well-documented.
Is there a way to use this library in Teensy since it's also open source?

Any comments regarding it would be appreciated!

jun
 
I looked at this library briefly. I see 3 issues.

1: There's an illegal character in the code, for an #ifdef case they probably never tested.
2: Which pin to use for the interrupt is a good question....
3: Their code wants abort(), which Teensyduino doesn't currently support.

Please give this copy a try to fix the first two:

https://github.com/PaulStoffregen/Pozyx-Arduino-library

For the 3rd issue, please add this to the code in Arduino:

Code:
void abort() { while(1); }

This library also has an incredible number of compiler warnings, mostly due to the authors using "NULL" where they should have used the number 0. You can safely ignore those. The authors probably never turned on compiler warnings, because these same warnings print for compiling on Arduino Uno if they're enabled in File > Preferences. I considered fixing all these, but for now it's probably best to just focus on getting this working. When/if it works, I'll submit a pull request. Hopefully if the changes are to only a small number of lines, they might merge it?
 
On issue #2, try connecting the interrupt signal to pin 2 on Teensy (the same as you'd do on Arduino Uno).

Thank you very much Paul for your quick and detailed help!
Before I really test it, I have one more question for you.
For trying out, I've already designed PCB and the pin for IRQ is being connected to pin16 of Teensy.
Obviously it's my mistake not considering Interrupt.
So my question is,

- Can I still use pin16 as a interrupt to use Pozyx?
Otherwise I'll cut the pcb line and connnect to pin2.
- It doen'st show where CS pin goes to in the example sketch.
Would you show me how to connect CS pin of DWM1000?

Again, I appreciate your help, jun
 
Last edited:
Thank you very much Paul for your quick and detailed help!
Before I really test it, I have one more question for you.
For trying out, I've already designed PCB and the pin for IRQ is being connected to pin16 of Teensy.
Obviously it's my mistake not considering Interrupt.
So my question is, Can I still use pin16 as a interrupt to use Pozyx?
Otherwise I'll cut the pcb line and connnect to pin2.

Again, I appreciate your help, jun
Pin 16 on the Teensy 3.x/LC is also analog input pin A2. As long as you don't use pin 16/A2 for anything else, you can use it for an interrupt pin on the 3.1/3.2/3.5/3.6. If you had the Teensy LC, you would not be able to use it as an interrupt pin.

On the Teensy 3.2, all analog input pins can be use for digital input/output/interrupt except for analog input pins A10/A11 (on the inside row of pins), A12/A13 (on solder pads underneath the Teensy) and A14 (pin on the back row).

You do want to use digitalPinToInterrupt to map pin 16 to the interrupt number.
 
- Can I still use pin16 as a interrupt to use Pozyx?
Otherwise I'll cut the pcb line and connnect to pin2.

You'll almost certainly have the edit the library code. Their begin() function only allows 0 and 1 as possible settings for the interrupt. You'll see I added code similar to what they had for the SAMD21 chip, where it adds an offset of 2 for use with attachInterrupt.

Honestly, this was just my best guess to make it work, based on what I saw in the code and of the shield they sell. I don't have this hardware, and before yesterday I had never even heard of this library. I was kinda hoping you would confirm if it actually works! ;)
 
You'll almost certainly have the edit the library code. Their begin() function only allows 0 and 1 as possible settings for the interrupt. You'll see I added code similar to what they had for the SAMD21 chip, where it adds an offset of 2 for use with attachInterrupt.

Honestly, this was just my best guess to make it work, based on what I saw in the code and of the shield they sell. I don't have this hardware, and before yesterday I had never even heard of this library. I was kinda hoping you would confirm if it actually works! ;)

IIRC, in the AVR 328p (i.e. Arudino Uno), only pins 2 and 3 could be interrupt pins. The AVR 32u4 (Leonardo, etc.) added a few more depending on which pins are brought out (4-8 I think), with pin 2 being interrupt 1 and pin 3 being interrupt 0. So, anything being written for those chips, probably wants to use pin 2/3 as the interrupt pins.
 
Thank you so much for your feedbacks!
I've been working with DWM module and got some positive data using Decaduino library.
The Pozyx library which Paul edited complies and I need some time to work on it since hardware is more fitting Decaduino for the moment.
I'll post the Pozyx library test result as soon as I get Decaduino test done.
Again I appreciate your help!
jun
 
Hello,
Now I realize the Pozyx only works as a shield which is running it's own firmware.
That means I need Pozyx hardware to make use of the library they offer.
And moreover what I need is teensy directly communicating with DWM 1000 without the MCU in between.
To achieve that I was trying it with Decaduino library.
https://www.irit.fr/~Adrien.Van-Den-Bossche/decaduino/
It works like charm with teensy and I'm happy to get started around there.
But the problem now is that it doesn't work with Wav file play in Audio Library.
As I assume this is SPI bus issue I opened another thread to ask specifically about the multiple slaves in 1 spi bus.
https://forum.pjrc.com/threads/4719...ield)-conflicts-with-Other-SPI-device(DWM1000)
Would you have a look and guide me how to resolve this issue?
In the worst case I'm thinking of adding 1MCU(ATMEGA328p) dedicated to communicating with DWM1000 and passing the result to Teensy.
I wish I just play wav file and get data from DWM1000 without additional MCU.

Best jun
 
It works like charm with teensy and I'm happy to get started around there.
But the problem now is that it doesn't work with Wav file play in Audio Library.

Try adding:

Code:
  SPI.usingInterrupt(DW1000_IRQ0_PIN);

Or if using a different pin for the interrupt signal, substitute the pin number. The idea is to tell the SPI library that DecaDuino using using SPI from the interrupt triggered by that pin.
 
Try adding:

Code:
  SPI.usingInterrupt(DW1000_IRQ0_PIN);

Or if using a different pin for the interrupt signal, substitute the pin number. The idea is to tell the SPI library that DecaDuino using using SPI from the interrupt triggered by that pin.

Yes that worked out! Now I can play wav file while I get the distance from DWM1000 module.
Thank you again for your big help!

But as soon as I start reading wav file, the distance data gets unreliable.
I assume this is coming from the interrupt from the audio.

I used AudioNoInterrupts(); while I communicate with remote DWM100 like following
Code:
// check all the anchors and the the average
    if (ranging_flag == true) {
    // check Range with all the anchors   
    AudioNoInterrupts();
    getRangeFromAnchor();
    AudioInterrupts();
    }
and getRangeFromAnchor() function is like following
Code:
void getRangeFromAnchor() {
  
   // from_address = anchor_address[anchor_number]; // when receive
   // destination_adress = anchor_address[anchor_number]; // when send
  switch (state) {

    case TWR_ENGINE_STATE_INIT:
      decaduino.plmeRxDisableRequest();
      state = TWR_ENGINE_STATE_WAIT_NEW_CYCLE;
      break;

    case TWR_ENGINE_STATE_WAIT_NEW_CYCLE:
      state = TWR_ENGINE_STATE_SEND_START;
      break;

    case TWR_ENGINE_STATE_SEND_START:

      // destination_address, my_address,
      txData[0] = anchor_address[anchor_number];
      txData[1] = my_short_address;
      txData[2] = TWR_MSG_TYPE_START;  // 1

      decaduino.pdDataRequest(txData, 3);
      state = TWR_ENGINE_STATE_WAIT_SEND_START;
      break;

    case TWR_ENGINE_STATE_WAIT_SEND_START:
      if ( decaduino.hasTxSucceeded() ) { ////////////////// sent data
        state = TWR_ENGINE_STATE_MEMORISE_T1;
      }
      break;

    case TWR_ENGINE_STATE_MEMORISE_T1:
      t1 = decaduino.lastTxTimestamp;
      state = TWR_ENGINE_STATE_WATCHDOG_FOR_ACK;
      break;

    case TWR_ENGINE_STATE_WATCHDOG_FOR_ACK:
      timeout = millis() + TIMEOUT;  // 20ms is the timeout
      state = TWR_ENGINE_STATE_RX_ON_FOR_ACK;
      break;

    case TWR_ENGINE_STATE_RX_ON_FOR_ACK:

      decaduino.plmeRxEnableRequest(); //Sets transceiver mode to receive mode.
      state = TWR_ENGINE_STATE_WAIT_ACK;
      break;

    case TWR_ENGINE_STATE_WAIT_ACK:

      if ( millis() > timeout ) { ///////////////////////////////// all anchors are off
        // state = TWR_ENGINE_STATE_INIT;

        ///////////////////////////////////////////////////// timeout! go to the beginning.
        if (num_ranging_error < ranging_error_threshold) {
          //blinkLED(1);
          num_ranging_error++;

          state = TWR_ENGINE_STATE_INIT;
        } else { ////////////////////////////////////////////// if it fails 6times
          state = TWR_ENGINE_STATE_UNABLE_TO_REACH_ANCHOR;
        }


        ///////////////////////////////////////////////////////////// // we got Ack, one or more anchors are On
      } else {
        //blinkLED(1);
        if ( decaduino.rxFrameAvailable() ) {


          /////////////////////////////// check if it's the one from the anchor we want
          if ( rxData[0] == my_short_address && rxData[1] == anchor_address[anchor_number] && rxData[2] == TWR_MSG_TYPE_ACK ) {

            //
            state = TWR_ENGINE_STATE_MEMORISE_T4;
          } else {
            //////////////////////////////// it's not the one from the anchor we want
            // go back and wait for another one
            state = TWR_ENGINE_STATE_RX_ON_FOR_ACK;
          }

        }
      }
      break;

    case TWR_ENGINE_STATE_UNABLE_TO_REACH_ANCHOR:
      // blinkLED(1);
      ranging_error[anchor_number] = true;
      state = TWR_ENGINE_STATE_CHECK_ONE_CYCLE;
      break;

    case TWR_ENGINE_STATE_MEMORISE_T4:
      t4 = decaduino.lastRxTimestamp;
      state = TWR_ENGINE_STATE_RX_ON_FOR_DATA_REPLY;
      break;

    case TWR_ENGINE_STATE_WATCHDOG_FOR_DATA_REPLY:
      timeout = millis() + TIMEOUT;
      state = TWR_ENGINE_STATE_RX_ON_FOR_DATA_REPLY;
      break;

    case TWR_ENGINE_STATE_RX_ON_FOR_DATA_REPLY:
      decaduino.plmeRxEnableRequest();        //Sets transceiver mode to receive mode.
      state = TWR_ENGINE_STATE_WAIT_DATA_REPLY;
      break;

    case TWR_ENGINE_STATE_WAIT_DATA_REPLY:
      if ( millis() > timeout ) {
        state = TWR_ENGINE_STATE_INIT;
      } else {
        if ( decaduino.rxFrameAvailable() ) {
          if ( rxData[0] == my_short_address && rxData[1] == anchor_address[anchor_number] && rxData[2] == TWR_MSG_TYPE_DATA_REPLY ) {
            state = TWR_ENGINE_STATE_EXTRACT_T2_T3;
          } else state = TWR_ENGINE_STATE_RX_ON_FOR_DATA_REPLY;
        }
      }
      break;

    case TWR_ENGINE_STATE_EXTRACT_T2_T3:
      t2 = decaduino.decodeUint64(&rxData[3]);
      t3 = decaduino.decodeUint64(&rxData[9]);
      tof = (t4 - t1 - (t3 - t2)) / 2;
      distance[anchor_number] = tof * COEFF * X_CORRECTION + Y_CORRECTION;

      // if the distance is out of range then ignore this reading by making it 0
      if (distance[anchor_number] > max_distance[anchor_number] || distance[anchor_number] < min_distance[anchor_number]) {
        distance[anchor_number] = 0;
        num_ranging_error++;
      }
      state = TWR_ENGINE_STATE_GET_SUM;

      break;

    case  TWR_ENGINE_STATE_GET_SUM:
      // check if it's time to get the sum
      //  check_time 0~9 total_check_time = 10
      if (check_time < (total_check_time - 1)) {  // it did't reach 10 readings
        // still need to ranging the anchor
        sum +=  distance[anchor_number];
        check_time++;
        state = TWR_ENGINE_STATE_INIT;

      } else { // it reached 10 readings

        state = TWR_ENGINE_STATE_CHECK_NUM_ERROR;

      }


      break;

    case TWR_ENGINE_STATE_CHECK_NUM_ERROR:
      // it reached 10times. check if the ranging_error > 5. it means we got more than 5 success.
      if ( num_ranging_error >= ranging_error_threshold) {

        ranging_error[anchor_number] = true;



      } else {
        ///////////////////////////////////////////////////////////////////// get the average
        average[anchor_number] = sum / (division_factor - num_ranging_error);
        ranging_error[anchor_number] = false;

      }
      state = TWR_ENGINE_STATE_CHECK_ONE_CYCLE;

      break;




    case TWR_ENGINE_STATE_CHECK_ONE_CYCLE:
      // anchor_number 0~3
      if (anchor_number < (total_anchor_num - 1)) {
        ///////////////////////////////////////////// move on to the next anchor
        anchor_number++;
        check_time = 0;
        num_ranging_error = 0;
        sum = 0;
        state = TWR_ENGINE_STATE_INIT;

      } else {
        //////////////////////////////////////////// we finished whole(4) anchors reading
        state = TWR_ENGINE_STATE_DISPLAY;


      }

      break;

    case TWR_ENGINE_STATE_DISPLAY:
      // We checked 1 anchor and averaged the value
      // Let's display the result and then move on to the next anchor
      // we got all the average and print them
      
      Serial.print("Bat : ");
      Serial.print(bat_voltage);
      //Serial.print(reading);
      Serial.println("V");
      Serial.println();
      for (int i = 0; i < total_anchor_num; i++) {
        Serial.print("anchor");
        Serial.print(i + 1);
        Serial.print(": ");
        if (ranging_error[i] == true) {
          Serial.println("Error");
        } else {
          Serial.print(average[i]);
          Serial.print("m");
        }
        Serial.print(" num_error: ");
        Serial.println("Error");
      }
      Serial.print("one cycle time : ");
      Serial.print( millis() - last_time_check_range);
      
      /*
      AudioNoInterrupts();
      
      display.clearDisplay();
      display.setCursor(0, 0);
      display.print("Bat : ");
      display.print(bat_voltage);
      //display.print(reading);
      display.println("V");
      display.println();

      for (int i = 0; i < total_anchor_num; i++) {
        display.print("anchor");
        display.print(i + 1);
        display.print(": ");
        if (ranging_error[i] == true) {
          display.println("Error");
        } else {
          display.print(average[i]);
          display.println("m");
        }

      }
      
      //t4 - t1
      display.print("one cycle time : ");
      display.print( millis() - last_time_check_range);
      display.display();

      AudioInterrupts();
      */
      state = TWR_ENGINE_STATE_FINISH;

      break;

    case TWR_ENGINE_STATE_FINISH:
      ////////////////////////////////////////////////////////////// we checked all the sensor so change the flag and get ready
      ranging_flag = false;
      break;


    default:
      state = TWR_ENGINE_STATE_INIT;
      break;
  }
}

Do you have any suggestion that I may try out?
I tried using USE_TEENSY3_OPTIMIZED_CODE for the fast reading of SD card but it didn't help.

jun
 
Status
Not open for further replies.
Back
Top