Code for Generating Precise Stereo Sound Positions on Arduino

Blogged by Mathieu as Electronics — Mathieu Sun 31 Aug 2008 11:03

This Arduino code takes a heading from an HMC6343 electronic compass and generates a low-frequency beep that seems to always come from the south.

Explanations and hardware photos here.


// Generating a low frequency beep that seems to always come from the South
// by Mathieu Glachant @ http://ban-sidhe.com
// with shameless pilfering of much good code from the trailblazers at arduino.cc
// Public release v0.1 Labor day 2008

#include <wire.h> ;

int ASCIIoutputOn =false; //Toggles Serial ouput of measurements, note that this uses pins 0 and 1
int AudioOutputOn =true; //Toggles audio ouput of measurements

int HMC6343Address = 0x32;
int slaveAddress; // This is calculated in the setup() function

byte inByte;

byte headingData[6];
int i;

long time = 0;

int headingValue =0;
int tiltValue =0;
int rollValue =0;

int audioPinR =9; // This is the pin for the right channel audio bit-banging
int audioPinL =10; // This is the pin for the right channel audio bit-banging
// Note that these should not be in output mode unless needed,
// so they are not included in setup()

void setup()
{

TCCR1B = 0x09; // Make PWM frequency more audio friendly on pins 9 and 10

// Shift the device's documented slave address (0x32) 1 bit right
// This compensates for how the TWI library only wants the
// 7 most significant bits (with the high bit padded with 0)

slaveAddress = HMC6343Address >> 1; // This results in the 7 bit address to pass to TWI

while( millis() < 500) { delay(10); } // The HMC6343 needs a half second to start from power up

Wire.begin();

if (ASCIIoutputOn)
{
Serial.begin(9600);
Serial.print(10,BYTE);
Serial.println("Ready to start!");
Serial.print(10,BYTE);
}
}

void getSensorData()
{

Wire.beginTransmission(slaveAddress);
Wire.send(0x50); // Send a "Get Data" (0x50) command to the HMC6343
Wire.endTransmission();

delay(2); // The HMC6343 needs at least a 1ms (microsecond) delay
// after this command. Using 2ms just makes it safe

// Read the 6 heading bytes, MSB first
// The resulting 3 16bit words are the compass heading, the tilt and roll in 10th's of a degree
// For example: a heading of 1345 would be 134.5 degrees

Wire.requestFrom(slaveAddress, 6); // Request the 6 bytes of data (MSB comes first)
i = 0;
while(Wire.available() && i < 6)
{
headingData[i] = Wire.receive();
i++;
}
}

void convertDataToValues()
{
headingValue = headingData[0]*256 + headingData[1]; // Put the MSB and LSB together
tiltValue = headingData[2]*256 + headingData[3] + 900; // Put the MSB and LSB together
rollValue = headingData[4]*256 + headingData[5] + 1800; // Put the MSB and LSB together
}

void sendSerialValues()
{
Serial.print("$HTR,");
Serial.print(int (headingValue / 10)); // The whole number part of the heading
Serial.print(".");
Serial.print(int (abs (headingValue % 10))); // The fractional part of the heading
Serial.print(",");
Serial.print(int (tiltValue / 10)); // The whole number part of the tilt
Serial.print(".");
Serial.print(int (abs (tiltValue % 10))); // The fractional part of the tilt
Serial.print(",");
Serial.print(int (rollValue / 10)); // The whole number part of the roll
Serial.print(".");
Serial.print(int (abs (rollValue % 10))); // The fractional part of the roll
//Serial.print(",");
//Serial.print(ratioPin); // The remainder of the division by the number of output pins, scaled to 256
Serial.print(",A ");
//Serial.print(10,BYTE);
Serial.print(13,BYTE);
}

void stereoSound(int freq, int t, int phase, boolean phaseSign, int volumeR, int volumeL)
// freq in hz, t in ms, phase in us, phaseSign true if positive, volume from 0 to 255
{
int hperiod; //calculate 1/2 period in us
int audioPin1 =audioPinL;
int audioPin2 =audioPinR;
int volume1 =volumeL;
int volume2 =volumeR;
long cycles, i;

//pinMode(audioPinR, OUTPUT); // turn on output pin, not needed for analogWrite
//pinMode(audioPinL, OUTPUT); // turn on output pin, not needed for analogWrite

hperiod = (500000 / freq) - 7; // subtract 7 us to make up for digitalWrite overhead

cycles = ((long)freq * (long)t) / 1000; // calculate cycles

phase = max(3,phase);

if (phaseSign)
{
audioPin1 =audioPinR;
audioPin2 =audioPinL;
volume1 =volumeR;
volume2 =volumeL;
}

analogWrite(audioPin1, volume1);
for (i=0; i<= cycles; i++){ // play note for t ms
delayMicroseconds(phase);
analogWrite(audioPin2, volume2);
delayMicroseconds(hperiod-phase);
analogWrite(audioPin1, 0);
delayMicroseconds(phase);
analogWrite(audioPin2, 0);
delayMicroseconds(hperiod -phase - 1); // - 1 to make up for fractional us
analogWrite(audioPin1, volume1);
}
delayMicroseconds(hperiod);
analogWrite(audioPin1, 0);

//pinMode(audioPinR, INPUT); // shut off pin to avoid noise from other operations, not needed for analogWrite
//pinMode(audioPinL, INPUT);

}

void delayLoop(int duration)
{
while( (millis() - time) < duration) { delay(1); }
time = millis();
}

void loop()
{
getSensorData();

convertDataToValues();

if (ASCIIoutputOn) { sendSerialValues(); }

if (AudioOutputOn)
{
stereoSound(
150, // frequency of beep
40, // duration of beep
abs(int(1200*sin(float(headingValue-1800)*0.001745))), // phase shift in us
(headingValue < 1800), // phase shift sign (true if right before left)
constrain(map(headingValue,2700,1800,0,255),0,255),
constrain(map(headingValue,1800,900,255,0),0,255)
);
}

delayLoop(200); // The HMC6343 needs 200ms between measurement
// this function will make sure loop() lasts at least that long

}

1 Comment »

  1. Comment by schuey — October 24, 2008 at 17:48

    on te laisse un peu seul et voila le résultat. des bises

RSS feed for comments on this post.

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Click on smiley to insert!

) (w) (u) p (y) (n) d (*) o) 8) ( (f) (g) (t) o (8) (l) (i) x (~) (e) $ (&amp) (c) ( s (d) (o) (@) (p) (^) (b) [

Proudly powered by wordpress - Theme Back in Black 2 by neuro