Prop Shield Motion data usage for a beginner

Status
Not open for further replies.

Ein

Member
I'm trying to make a prop/cosplay thing that's basically a jetpack with a left and right thruster on it. I'm working on making it play sounds, and I want it to do so in response to the wearer's motion; particularly, I want it to detect if there's a sudden acceleration, play sounds, and increase the intensity of the lights in the thrusters in response.

I've already worked out the basics of the lights and the sounds I'm trying to build in. I have a Teensy 3.2 and a Prop Shield on it. The Prop Shield has motion sensing components and the NXPMotionSense library has given me a pretty decent starting point for tapping into that, I think - I was able to get everything calibrated and outputting information for me.

The sketch I'm running right now is here:

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <play_sd_mp3.h> // https://github.com/FrankBoesing/Arduino-Teensy-Codec-lib
//#include <play_sd_aac.h>
#include <FastLED.h>
#include "easing.h"
#include <NXPMotionSense.h>
#include <EEPROM.h>

//AudioPlaySdWav           playSdWav1;     //xy=154,422
AudioPlaySdMp3           playMp31;
AudioMixer4              mixer1;
AudioOutputAnalog        dac1;
AudioConnection          patchCord1(playMp31, 0, mixer1, 0);
AudioConnection          patchCord2(playMp31, 1, mixer1, 1);
AudioConnection          patchCord3(mixer1, dac1);

NXPMotionSense imu;
NXPSensorFusion filter;

#define LEFT_RING_NEO_PIN 2
#define RIGHT_RING_NEO_PIN 3
#define N_LEDS 16

CRGB ringL[N_LEDS];
CRGB ringR[N_LEDS];

#define PROP_AMP_ENABLE    5
#define FLASH_CHIP_SELECT  6
//#define FLASH_CHIP_SELECT 21  // Arduino 101 built-in SPI Flash

uint8_t max_bright = 128;                                      // Overall brightness definition. 

void setup() {
  Serial.begin(115200);
  AudioMemory(8); //4
  delay(2000);
  imu.begin();
  filter.begin(100);

  FastLED.addLeds<NEOPIXEL, LEFT_RING_NEO_PIN>(ringL, N_LEDS);
  FastLED.addLeds<NEOPIXEL, RIGHT_RING_NEO_PIN>(ringR, N_LEDS);
  FastLED.setBrightness(max_bright);

  // Start SerialFlash
  if (!SerialFlash.begin(FLASH_CHIP_SELECT)) {
    while (1) {
      Serial.println ("Cannot access SPI Flash chip");
      delay (1000);
    }
  }

  listFiles();

  //Set Volume
  mixer1.gain(0, 2);
  mixer1.gain(1, 2);

  //Start Amplifier
  pinMode(PROP_AMP_ENABLE, OUTPUT);
  digitalWrite(PROP_AMP_ENABLE, HIGH);    // Enable Amplifier

  fill_solid(ringL, N_LEDS, CRGB::Green);
  fill_solid(ringR, N_LEDS, CRGB::Green);
  FastLED.show();

  //Startup sound
  playFile("WELC.MP3");
}

void listFiles(){
  Serial.println("All Files on SPI Flash chip:");

  if (!SerialFlash.begin(FLASH_CHIP_SELECT)) {
    error("Unable to access SPI Flash chip");
  }

  SerialFlash.opendir();
  while (1) {
    char filename[64];
    uint32_t filesize;

    if (SerialFlash.readdir(filename, sizeof(filename), filesize)) {
      Serial.print("  ");
      Serial.print(filename);
      spaces(20 - strlen(filename));
      Serial.print("  ");
      Serial.print(filesize);
      Serial.print(" bytes");
      Serial.println();
    } else {
      break; // no more files
    }
  }
}

void spaces(int num) {
  for (int i=0; i < num; i++) {
    Serial.print(" ");
  }
}

void error(const char *message) {
  while (1) {
    Serial.println(message);
    delay(2500);
  }
}

void playFile(const char *filename)
{
  SerialFlashFile ff = SerialFlash.open(filename);
  Serial.print("Playing file: ");
  Serial.println(filename);

  uint32_t sz = ff.size();
  uint32_t pos = ff.getFlashAddress();

  // Start playing the file.  This sketch continues to
  // run while the file plays.
  playMp31.play(pos,sz);

  // Simply wait for the file to finish playing.
  //while (playMp31.isPlaying()) {
   //yield();
  //}
}


void loop() {
  if(!playMp31.isPlaying()){
    playFile("R301.MP3");
  }
  readMotionSensor();
}

void readMotionSensor(){
  float ax, ay, az;
  float gx, gy, gz;
  float mx, my, mz;
  float roll, pitch, heading;

  if (imu.available()) {
    // Read the motion sensors
    imu.readMotionSensor(ax, ay, az, gx, gy, gz, mx, my, mz);

    // Update the SensorFusion filter
    filter.update(gx, gy, gz, ax, ay, az, mx, my, mz);

    //print the data
    roll = filter.getRoll();
    pitch = filter.getPitch();
    heading = filter.getYaw();
    Serial.print("Orientation: ");
    Serial.print(heading);
    Serial.print(" ");
    Serial.print(pitch);
    Serial.print(" ");
    Serial.println(roll);
    Serial.print("Accel X: ");
    Serial.println(ax);
    Serial.print("Gyro X: ");
    Serial.println(gx);
    Serial.print("Accel Y: ");
    Serial.println(ay);
    Serial.print("Gyro Y: ");
    Serial.println(gy);
    Serial.print("Accel Z: ");
    Serial.println(az);
    Serial.print("Gyro Z: ");
    Serial.println(gz);
    Serial.println("=============");
  }
}

The output I'm getting seems about in line with what I'd expect.

Code:
Orientation: 58.26 -1.85 -2.94
Accel X: 0.03
Gyro X: -0.88
Accel Y: -0.06
Gyro Y: -0.56
Accel Z: 1.03
Gyro Z: 0.13
=============
Orientation: 58.26 -1.85 -2.94
Accel X: 0.03
Gyro X: -0.63
Accel Y: -0.06
Gyro Y: -0.50
Accel Z: 1.03
Gyro Z: 0.25
=============
Orientation: 58.27 -1.85 -2.93
Accel X: 0.03
Gyro X: -0.81
Accel Y: -0.06
Gyro Y: -0.56
Accel Z: 1.04
Gyro Z: 0.13
=============
Orientation: 58.27 -1.85 -2.93
Accel X: 0.03
Gyro X: -0.88
Accel Y: -0.06
Gyro Y: -0.38
Accel Z: 1.03
Gyro Z: 0.44
=============
Orientation: 58.27 -1.85 -2.93
Accel X: 0.03
Gyro X: -1.06
Accel Y: -0.06
Gyro Y: -0.31
Accel Z: 1.03
Gyro Z: 0.00
=============

And so on.

I can see the numbers jump in a relative sense if I move the device around; for instance, in the instant I pick it straight up off my desk:

Code:
Orientation: 40.70 -8.13 -0.81
Accel X: 0.06
Gyro X: 37.75
Accel Y: -0.02
Gyro Y: -2.44
Accel Z: 0.72
Gyro Z: -8.94
=============
Orientation: 40.74 -8.09 -0.73
Accel X: -0.12
Gyro X: 13.94
Accel Y: -0.18
Gyro Y: -3.44
Accel Z: 0.52
Gyro Z: 4.63
=============
Orientation: 40.93 -8.02 -0.81
Accel X: -0.12
Gyro X: -2.00
Accel Y: -0.18
Gyro Y: 2.31
Accel Z: 0.52
Gyro Z: 18.88
=============
Orientation: 40.98 -7.83 -0.50
Accel X: 0.01
Gyro X: 12.94
Accel Y: 0.44
Gyro Y: 18.88
Accel Z: 0.52
Gyro Z: 12.56
=============
Orientation: 41.02 -7.53 -0.09
Accel X: 0.01
Gyro X: 28.50
Accel Y: 0.44
Gyro Y: 28.63
Accel Z: 0.52
Gyro Z: 9.31

All of this is great, I think, but I'm at a bit of a loss as to how to effectively use this data to bridge the gap to my goal. I'd like to come up with some way of calculating an overall 'activity' value, so I can set the intensity of things like the LEDs to ramp up and decay based on the wearer's movements.

I'm guessing that I need to keep a rolling average of values to compare each update against, and if the variation is big enough, assume that the wearer has moved in a sufficient way to trigger the 'jetpack' effect? But I'm at a loss as to how to establish bounds for these values and then compare them. The orientation values are straightforward enough - the SensorFusion filter handles those calculations - but there doesn't seem to be an equivalent simplification of the acceleration/gyroscope values, and frankly, I'm nowhere near math-smart enough to work through this without spending many many days on it.

Is there a... more general tool or function or library that I can feed the gyro/accel data into to simplify this task? Failing that, can I get any advice as to where to start picking through this problem? I started looking at code relevant to lightsabers, but a lot of those are more based on orientation/swinging than acceleration, and I felt like I started to go cross-eyed.
 
Status
Not open for further replies.
Back
Top