Close

Intel Edison my friend

A project log for Precision Indoor Positioning

Performing high precision indoor positioning using Vive Lighthouses, Arduino Due, and TS3633-CM1

simon-trendelSimon Trendel 11/06/2016 at 23:075 Comments

Hi everyone, I'm part of a student team in Munich, Germany. We are trying to use the vive tracking system for our humanoid legs. I joined this project recently and wanted to share some insides we had using the sensors with different hardware than the arduino due and the TS3633-CM1 modules. Namely Intel Edison and Genuino 101 and sensors we got from disassemblying a vive controller. This log entry serves as additonal information for the interested reader, or people who might have some microcontroller flying around. In the future we will work closer with Mike and in particular try to use the same hardware.

We wanted to order the TS3633-CM1, but currently they are sold-out. We couldn't wait to get started and disassembled one of the vive controllers. Each controller contains 24 sensor. At a price of 130$ per controller, this is a lot cheaper than the 6.95$ triad wants (we pay 100$ extra shipping to Germany).

As you can see, connecting the sensors is quite difficult, because the contacts are tiny.

For measuring the pulse width, we wanted to use an Intel Edison, because we had one lying around. After hacking for a while and scanning the forums, it became apparant, that the interrupts on the Intel are in fact threaded and simply not fast enough for measuring 62us - 135us.

#include <iostream>
#include <vector>

#include <chrono>
#include <mraa.h>
#include <mraa/gpio.h>

using namespace std::chrono; 

static volatile int 		counter_falling = 0, counter_rising = 0; 
static  high_resolution_clock::time_point t1, t2; 
static std::vector<microseconds> 	timeElapsed; 
static bool 			valid; 


// Interrupt routine for the input data from the TS3633 sensor
void IntrValveFalling(void* arg){
    counter_falling +=1;  
}

// Interrupt routine for the input data from the TS3633 sensor
void IntrValveRising(void *arg){
    counter_rising += 1;  
}

void IntrValveBoth(void *arg){
    if(valid){
	counter_falling++; 
    	valid = false; 
	t2 = high_resolution_clock::now();
	duration<double> d = duration<double>(t2-t1);
	microseconds us = duration_cast<microseconds>(d);
	timeElapsed.push_back(us.count()); 
    }else{
	counter_rising++; 
	t1 = high_resolution_clock::now();
	valid = true; 
    }
}


int main(int argc, char** argv){
    mraa_result_t rv; 
    mraa_init(); 
    const char* board_name = mraa_get_platform_name();
    fprintf(stdout, "Version: %s\n Running on %s\n", mraa_get_version(), board_name);
    
    mraa_gpio_context m_gpio; 
    gpio_edge_t edge_r = MRAA_GPIO_EDGE_RISING;
    gpio_edge_t edge_f = MRAA_GPIO_EDGE_FALLING; 
    gpio_edge_t edge_b = MRAA_GPIO_EDGE_BOTH;            
 
    // J17-7 on the Intel Edison Breakout Board
    m_gpio = mraa_gpio_init(6); 
    if(m_gpio == NULL){
        std::cout << " cannot open J17-7 pin...\t closing" << std::endl; 
    }

    mraa_gpio_dir(m_gpio, MRAA_GPIO_IN); 

    rv = mraa_gpio_isr(m_gpio, edge_b , &IntrValveBoth, NULL);
    if(rv != MRAA_SUCCESS) std::cout << "MRRA return code: " << rv << std::endl;  

    // J17-8 on the Intel Edison Breakout Board
    mraa_gpio_context m_gpio_2; 
    m_gpio_2= mraa_gpio_init(7); 
    if(m_gpio_2 == NULL){
        std::cout << " cannot open J17-7 pin...\t closing" << std::endl; 
    }

    mraa_gpio_dir(m_gpio_2, MRAA_GPIO_IN); 

    std::cout << "reading GPIO J17-7: " << mraa_gpio_read(m_gpio) << std::endl; 
    std::cout << "reading GPIO J17-8: " << mraa_gpio_read(m_gpio_2) << std::endl; 

    sleep(5);
    std::cout << "Counter falling= " << counter_falling << std::endl; 
    std::cout << "Counter rising= " << counter_rising << std::endl; 

    counter_rising = 0; 
    for(auto e : timeElapsed){
	    std::cout << "counter: " << counter_rising << " E: " << e << std::endl; 
	    counter_rising++; 
    }
    
    mraa_gpio_isr_exit(m_gpio); 
    mraa_gpio_close(m_gpio); 

    mraa_gpio_isr_exit(m_gpio_2); 
    mraa_gpio_close(m_gpio_2); 
    mraa_deinit();
    return MRAA_SUCCESS;
}

There is a way via busy wait loops, which unfortunately can't be used when using multiple sensors. Here is the code:

#include <stdio.h>
#include <unistd.h> 
#include <mraa.h>

int main() {
	 mraa_init();
	 mraa_gpio_context pin = mraa_gpio_init(6);
	 mraa_gpio_dir(pin, MRAA_GPIO_IN);
	 mraa_gpio_use_mmaped(pin,1);
	 int i, counter = 0, times[100];
	 // wait for 0
	 for(;;){
		 if(mraa_gpio_read(pin)==0)break;
	 }
	 while(counter<100){
	 	for(i=0;i<10000;i++){
			 if(mraa_gpio_read(pin)==1)break;
	 	}
	 	for(i=1;i<10000;i++){
			 if(mraa_gpio_read(pin)==0)break;
	 	}
		times[counter++] = i;
	 }
	 for(i=0; i<100;i++){
	 	//printf("%f us\n",0.66*times[i]+0.548);
	 	printf("%d \n",times[i]);
	 }
	 return MRAA_SUCCESS;
}

The next idea was to use the Edisons MCU, which unfortunately wasn't fast enough either.

Here is the MCU code (which cannot cope with the fast interrupts):

#include "mcu_api.h"
#include "mcu_errno.h"
#include <string.h>

volatile long counter = 0;

volatile long t0, t1;
volatile int rising = 1;
volatile int times[1000];
volatile int measure_time = 1;


int rising_irq(int req)
{
	 t0 = time_us();
	 return IRQ_HANDLED;
}

int falling_irq(int req)
{
 	 t1 = time_us();
 	 times[counter++] = t1-t0;
 	 return IRQ_HANDLED;
}

 void mcu_main()
 {
    /* your configuration code starts here */
    gpio_setup(27, 0);
    gpio_setup(20, 0);
    gpio_register_interrupt(27, 1, rising_irq);
    gpio_register_interrupt(20, 0, falling_irq);
    while (1)
    {
    	counter = 0;
		debug_print(DBG_INFO, "measuring...\n");
		mcu_sleep(100);         /* sleep 1000 milliseconds */
		int i=0;
		debug_print(DBG_INFO, "%d interrupts:\n", counter);
		for( i = 0; i<counter; i++)
			debug_print(DBG_INFO, "%d\n", times[i]);
    }
 }

So eventually we used a Genuino 101, which has of course direct interrupts. With this little fellow we could finally get good measurements. Here is the code:

volatile unsigned long t1 = 0, t2 = 0;
volatile unsigned long lens[1000] = {0};
volatile unsigned long counter = 0;

void setup(){
    Serial.begin(9600);
    while(!Serial);
    pinMode(8,INPUT); 
}

bool decodeDuration( int duration, bool &skip, bool &rotor, bool& data){
  int timing_tolerance = 5;
  if( duration < 40 )
    return false;
  if( abs(duration - 63) <= timing_tolerance){
    skip = false;
    rotor = false;
    data = false;
  }else if( abs(duration - 82) <= timing_tolerance){
    skip = false;
    rotor = false;
    data = true;
  }else if( abs(duration - 73) <= timing_tolerance){
    skip = false;
    rotor = true;
    data = false;
  }else if( abs(duration - 94) <= timing_tolerance){
    skip = false;
    rotor = true;
    data = true;
  }else if( abs(duration - 104) <= timing_tolerance){
    skip = true;
    rotor = false;
    data = false;
  }else if( abs(duration - 125) <= timing_tolerance){
    skip = true;
    rotor = false;
    data = true;
  }else if( abs(duration - 115) <= timing_tolerance){
    skip = true;
    rotor = true;
    data = false;
  }else if( abs(duration - 135) <= timing_tolerance){
    skip = true;
    rotor = true;
    data = true;
  }else{
    return false;
  }

  return true;
}

void loop(){
  while(counter < 1000){
    lens[counter] = pulseIn(8,HIGH);
    counter++;
  }
    
  for(int i = 0; i < 999; ++i){
    bool rotor, skip, data;
    Serial.print(lens[i]);
    if(decodeDuration(lens[i], skip, rotor, data)){
      Serial.print("\tskip: ");
      Serial.print(skip);
      Serial.print(" rotor: ");
      Serial.print(rotor);
      Serial.print(" data: ");
      Serial.print(data);
      Serial.println();
    }else{
       Serial.print("\tsweep"); 
       Serial.println();
    }
  }
  counter = 0;
}

Here is a screenshot from the measurement:

When you look closely, it makes kind of sense :D. From the top ignoring data for now:
Lighthouse 0 measures with rotor 1 while Lighthouse 1 skips

sweep

Lighthouse 1 measures with rotor 0 while Lighthouse 0 skips

sweep

Lighthouse 1 measures with rotor 1 while Lighthouse 0 skips

sweep

Lighthouse 0 measures with rotor 0 while Lighthouse 1 skips

sweep

repeat

Apparently the two sync signals from the lighthouses are queued, which makes sense, because otherwise the signals would interfere and the sensors wouldn't know which lighthouse sent what information.

Discussions

Simon Trendel wrote 11/07/2016 at 17:55 point

Hi,

hm I agree this can be confusing. I guess not everyone has an arduino due lying around (we dont :(..). Our effort was to get on the same page as Mike, i.e. get correct measurements from the sensors. There are many ways to get them and this log examplyfies three (of which only one is working). 

But I think Mike is right, we should communicate the progress and discuss project logs. 

  Are you sure? yes | no

Lee Cook wrote 11/07/2016 at 12:40 point

Hi,

I'm curious as to why you didn't continue with the Due for measurements then hand-off to the Edison for the ePnP?

Also, not sure if it matters, but the logic ladder within decodeDuration is out of sequence, you'll miss 73 and 115 events.

Lee

  Are you sure? yes | no

Mike Turvey wrote 11/07/2016 at 16:12 point

Lee-- Yes, this is very confusing.  Simon and his classmates have separately investigating positioning using the Vive lighthouse, and he recently asked to join this project.  We've had a few exchanges so far, and from this post that he made (this is not my post), I can see a lot more of what they've been working on.  They've clearly done some awesome work.  I'm really impressed with the resourcefulness of getting the sensors from the controller to work.  That said, this reads very much like a project log for a different effort.  I find it very interesting to see the different approaches to the problem.  But if we're going to be working on this single project together, we're going to have to do a better job communicating  and presenting project log updates as a single project, not two different projects with a common goal but different approaches.  

To be clear Lee, I'm still very much using the Due for now.  And as we had discussed, the Teensy 3.2 seems like a very good longer-term candidate, especially because of the floating point unit on it.  I do have a spare Arduino 101 lying around, so I'll give the existing code a try on it.  I don't know yet what the interrupt performance will look like on that platform for capturing the critical timing of the laser sweeps.  My goal very much continues to be to enable running this on the simplest hardware possible.  I'm still very hopeful to get everything running on a single off-the-shelf piece of hardware with little-to-no glue hardware to make things fit together.

  Are you sure? yes | no

Simon Trendel wrote 11/07/2016 at 22:46 point

I think the else if statements should work, because each statement checks the distance of the duration to each slot (taking a timing tolerance into account). why do you think it shouldn't work?

  Are you sure? yes | no

Lee Cook wrote 11/07/2016 at 23:02 point

Apologies :)  Just noticed the ABS...

  Are you sure? yes | no